From f6b5935afde6934794d12b15d54c97edefd2df74 Mon Sep 17 00:00:00 2001 From: Daniel Sieger <dsieger@posteo.de> Date: Sun, 10 Dec 2017 20:02:22 +0100 Subject: [PATCH] revamp docs --- README.md | 25 +-- docs/Doxyfile.in | 6 +- docs/codingstyle.md | 124 +++++++++++++++ docs/contributing.md | 52 ++++++ docs/development.md | 118 -------------- docs/footer.html | 1 - docs/header.html | 56 +++++++ docs/images/pmp-logo-text-smaller.png | Bin 0 -> 25288 bytes docs/installation.md | 80 ++++++---- docs/mainpage.md | 38 +++-- docs/overview.md | 188 +++++++--------------- docs/quickstart.md | 35 +++- docs/style.css | 53 ++++++- docs/tutorial.md | 220 ++++++++++++++++++++++++++ docs/userguide.md | 10 ++ 15 files changed, 683 insertions(+), 323 deletions(-) create mode 100644 docs/codingstyle.md create mode 100644 docs/contributing.md delete mode 100644 docs/development.md create mode 100644 docs/header.html create mode 100644 docs/images/pmp-logo-text-smaller.png create mode 100644 docs/tutorial.md create mode 100644 docs/userguide.md diff --git a/README.md b/README.md index 29a649e..8a2c8e0 100644 --- a/README.md +++ b/README.md @@ -6,26 +6,5 @@ <img src="docs/images/pmp-logo-text.png" alt="logo" width="750px"/> The pmp-library is a modern C++ open-source library for digital geometry -processing. It provides a set of geometric data structures frequently used -in geometry processing tasks as well as implementations of canonical -algorithms. It has been designed and implemented with a focus on ease of -use and performance while maintaining high flexibility. - -## Quickstart - -Fetch the repository: - - $ git clone --recursive https://github.com/pmp-library/pmp-library.git - -Configure and build: - - $ cd pmp-library && mkdir build && cd build && cmake .. && make - -See the [installation guide](docs/installation.md) file for more -detailed installation instructions. - -## License - -The pmp-library is provided under a flexible 3-clause -BSD [license](./LICENSE.txt), thereby allowing for both open-source and -commercial usage. +processing. See the [home page](http://pmp-library.github.io/pmp-library/) for +details. diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index a01ac1f..5dd473e 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -425,7 +425,7 @@ CASE_SENSE_NAMES = NO # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. -HIDE_SCOPE_NAMES = NO +HIDE_SCOPE_NAMES = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation @@ -878,7 +878,7 @@ HTML_FILE_EXTENSION = .html # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! -HTML_HEADER = +HTML_HEADER = ${CMAKE_CURRENT_SOURCE_DIR}/header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a @@ -912,7 +912,7 @@ HTML_EXTRA_STYLESHEET = ${CMAKE_CURRENT_SOURCE_DIR}/style.css # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = ${CMAKE_CURRENT_SOURCE_DIR}/images/favicon.ico # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images diff --git a/docs/codingstyle.md b/docs/codingstyle.md new file mode 100644 index 0000000..f067947 --- /dev/null +++ b/docs/codingstyle.md @@ -0,0 +1,124 @@ +# Coding Style {#codingstyle} + +If you would like to contribute to the pmp-library please make sure your source +code adheres to the following coding conventions. Please use +the [clang-format](https://clang.llvm.org/docs/ClangFormat.html) tool and the +corresponding `.clang-format` configuration file from the repository to properly +format your code. + +## Naming + +### Types + +The names of user-defined types such as classes, structs and enums use +`CamelCase` notation. The names of persons such as Cholesky or Delaunay are +properly capitalized as well. + +~~~~{.cpp} + class PolyLine { ... }; + enum RGBColors { red, green, blue }; + class SparseCholeskySolver { ... }; +~~~~ + +### Functions + +Function names are written using `camelCase` notation starting with a lowercase +letter. + +~~~~{.cpp} + class ExampleClassName + { + double exampleFunctionName(void); + }; +~~~~ + +### Variables + +Variables are named in `camelCase` notation. Class member variables are prefixed +with `m_`. + +~~~~{.cpp} + int globalsConsideredHarmful; + + class ExampleClass + { + protected: + double m_memberVariable; + static double m_staticVariable; + }; +~~~~ + +### File Names + +File names follow the naming rules for user-defined types. Implementation files +end with `.cpp` and header files end with `.h`. + +## Formatting + +### Blocks + +The expressions following an `if, else, while, do ... while` or `for` statement +should always be enclosed in braces. The braces enclosing a block should be +placed in the same column, on separate lines. + +~~~~{.cpp} + if (fooBar == baz) + { + std::cout << "hurz" << std::endl; + } + else + { + std::cout << "asdf" << std::endl; + } +~~~~ + +### Comments + +Use C++-style comments, i.e., `// My important comment.` Use `//!` for doxygen +special comments. + +### Line Length + +Lines should not exceed more than 80 characters. There should only be one +statement per line. + +### Indentation + +Use spaces instead of tabs. Indent the code by four spaces for each +level of indentation. Avoid trailing whitespaces at the end of a +line as well as on empty lines. + +## Miscellaneous + +This section describes some basic programming conventions developers should +adhere to. + +- Use the <tt>\#pragma once</tt> compiler directive at the beginning of each + header file in order to protect against multiple inclusion. + +- Use the `pmp` namespace in order to avoid conflicts. In source files, do not + add an additional level of indentation due to the namespace: + +~~~~{.cpp} + namespace pmp { + + class ExampleClass + { + ... + }; + + } +~~~~ + +- Use meaningful prefixes for `bool` variables or functions returning booleans, + e.g., `hasColors()` or `isDone`. +- Consistently name dynamic properties, e.g., "v:scalar" for vertex scalars or + "f:normal" for face normals. +- Consistently name iterators and circulators by their type +- Use `std::size_t` where appropriate, e.g., storing values from STL functions + such as the `size()` member function of a `std::vector` +- Localize variable scope and avoid declaring all variables at the beginning of + a function or code block. +- In case you want to preserve the special formatting of a particular code block + such as a matrix intialization add the `// clang-format off` and + `// clang-format on` directives around this block. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..8e6ced2 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,52 @@ +# Contributing {#contributing} + +Contributions to the pmp-library are generally welcome. However, please keep in +mind that we develop the library besides our daily jobs and therefore might not +always find the time to quickly react to your requests and suggestions. + +## Reporting Issues + +In case you run into trouble using the pmp-libray, please check for +existing [issues](https://github.com/pmp-library/pmp-library/issues). If your +problem is not already reported file a new issue and also provide some piece of +code and data to reproduce the behavior in question. + +## Contributing Code + +If you would like to contribute to the development of the pmp-library you should +start by [forking](https://help.github.com/articles/fork-a-repo) and creating +a [pull request](https://help.github.com/articles/creating-a-pull-request). Make +sure that your code follows the [Coding Style](coding_style.html) guidelines. In +case you want to contribute a new algorithm make sure the code is properly +documented using doxygen and is accompanied by a unit test, see below. + +## Unit Testing + +The pmp-library has a suite of unit tests that aims at making sure everything +works as expected. The unit tests are written +using [Google Test](https://github.com/google/googletest) and run during +continuous integration builds. See the also the `tests` sub-directory of the +repository. You can locally run the test suite from your build directory by +invoking the + + $ make test + +target. To obtain more detailed test output we recommend to invoke the Google +Test runner directly: + + $ cd tests + $ ./gtest_runner + + +## Code Coverage + +We track the overall code coverage rate of our unit tests +using [gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html), which is part +of [GCC](https://gcc.gnu.org/). To check the code coverage locally run + + $ make coverage + +This will run the test suite and collect the coverage data. A HTML report of the +results will be generated to `coverage/index.html`. We generally try to maintain +a high coverage rate of above 90%. Any code that you would like to contribute +should not decrease the coverage rate. diff --git a/docs/development.md b/docs/development.md deleted file mode 100644 index a915127..0000000 --- a/docs/development.md +++ /dev/null @@ -1,118 +0,0 @@ -# Development {#development} - -[TOC] - -# Contribution Guidelines {#contribution} - -- criteria -- git best practices -- how to submit - -# Testing and Code Coverage {#testing} - -- how to write tests - -# Coding Conventions {#codingConventions} - -If you would like to contribute to pmp please make sure -your code adheres to the following coding conventions. - -## Naming {#naming} - -### Types {#types} - -The names of user-defined types such as classes, structs and enums use -`CamelCase` notation. This applies to acronyms as well. The names of persons -such as Cholesky or Delaunay are properly capitalized as well. - - class PolyLine { ... }; - enum RgbColors { red, green, blue }; - class SparseCholeskySolver { ... }; - -### Functions {#functions} - -Function names are written using `camelCase` notation. - - class ExampleClassName - { - double exampleFunctionName(void); - }; - -### Variables {#variables} - -Variables are named in `camelCase` notation. Class member -variables are prefixed with `m_`. - - int globalsConsideredHarmful; - - class ExampleClass - { - protected: - double m_memberVariable; - static double m_staticVariable; - }; - -### File Names {#fileNames} - -File names follow the naming rules for user-defined types. Implementation files -end with `.cpp` and header files end with `.h`. - -## Formatting {#formatting} - -### Blocks {#blocks} - -The expressions following an `if, else, while, do ... while` or `for` statement -should always be enclosed in braces. The braces enclosing a block should be -placed in the same column, on separate lines. - - if (fooBar == baz) - { - std::cout << "hurz" << std::endl; - } - else - { - std::cout << "asdf" << std::endl; - } - -### Comments {#comments} - -C++-style comments should be used, i.e., `// My important comment.` - -### Line Length {#lineLength} - -Lines should not exceed more than 80 characters. There should only be one -statement per line. - -### Indentation {#indentation} - -Use spaces instead of tabs. Indent the code by four spaces for each -level of indentation. Avoid trailing whitespaces at the end of a -line as well as on empty lines. - -## Misc {#misc} - -This section describes some basic programming conventions developers should -adhere to. - -- Use the <tt>\#pragma once</tt> compiler directive at the beginning of each - header file in order to protect against multiple inclusion. - -- Use the `pmp` namespace in order to avoid conflicts. In source files, do not - add an additional level of indentation due to the namespace: - - namespace pmp { - - class ExampleClass - { - ... - }; - - } - -- use is and has prefixes for boolean predicates -- consistent property naming -- consistent iterator / circulator naming -- using size_t where approriate -- localized variable scope -- const auto -- clang-format on / off diff --git a/docs/footer.html b/docs/footer.html index e662fae..4fb2a1d 100644 --- a/docs/footer.html +++ b/docs/footer.html @@ -1,5 +1,4 @@ <div id="footer"> - <hr> <address> Copyright © 2017 The pmp-library developers </address> diff --git a/docs/header.html b/docs/header.html new file mode 100644 index 0000000..836bfe6 --- /dev/null +++ b/docs/header.html @@ -0,0 +1,56 @@ +<!-- HTML header for doxygen 1.8.11--> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/> +<meta http-equiv="X-UA-Compatible" content="IE=9"/> +<meta name="generator" content="Doxygen $doxygenversion"/> +<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME--> +<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME--> +<link rel="shortcut icon" type="image/x-icon" href="favicon.ico"> +<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/> +<script type="text/javascript" src="$relpath^jquery.js"></script> +<script type="text/javascript" src="$relpath^dynsections.js"></script> +$treeview +$search +$mathjax +<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" /> +$extrastylesheet +</head> +<body> +<div id="top"><!-- do not remove this div, it is closed by doxygen! --> + +<!--BEGIN TITLEAREA--> +<div id="titlearea"> +<table cellspacing="0" cellpadding="0"> + <tbody> + <tr style="height: 56px;"> + <!--BEGIN PROJECT_LOGO--> + <td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td> + <!--END PROJECT_LOGO--> + <!--BEGIN PROJECT_NAME--> + <td id="projectalign" style="padding-left: 0.5em;"> + <div id="projectname">$projectname + <!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER--> + </div> + <!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF--> + </td> + <!--END PROJECT_NAME--> + <!--BEGIN !PROJECT_NAME--> + <!--BEGIN PROJECT_BRIEF--> + <td style="padding-left: 0.5em;"> + <div id="projectbrief">$projectbrief</div> + </td> + <!--END PROJECT_BRIEF--> + <!--END !PROJECT_NAME--> + <!--BEGIN DISABLE_INDEX--> + <!--BEGIN SEARCHENGINE--> + <td>$searchbox</td> + <!--END SEARCHENGINE--> + <!--END DISABLE_INDEX--> + </tr> + </tbody> +</table> +</div> +<!--END TITLEAREA--> +<!-- end header part --> diff --git a/docs/images/pmp-logo-text-smaller.png b/docs/images/pmp-logo-text-smaller.png new file mode 100644 index 0000000000000000000000000000000000000000..7443ad82bc2400cc27271a449f82e6aff4c814cc GIT binary patch literal 25288 zcmXtf19WA<(r)aDZD(TJb}~sO=ETOt*2K1L=fpNAI1}6EiH(<g|99738>{!~uIg$P zzKZyvB>fEm9{~&u?3=8Ngen*qMAz4MI2_E^6MC9w=IaGxEH5nq_W7SHzq>5ys|4Or zM%x(-jEwI;H#nc=&B<3Gtc$FoB<v0n0xA<&b^dS<7#Jy-tc2)KkJa-WcMrYcg|Mxa zh1y8(Q6rP}6Dm}><ApyW$CvYe{vg9Z77T^_vt!{DIZij$IfnPa6g@7`G^qr9Yr|o% zDM@hyJ&y7c=x=n>+ofpCb^(iEyJk1p+%G2`rn8OfO(iVls9bSC@FE`82c~6i8|Oy7 zI~akz5G5}b_ageCVYTDi4l%^NLct&&k)NMgOe^csM5q(npo#EN^K|emhLIg~qX56& zmK0(qj=?h^4uD~mZwALYkT(HvsPP)A!+;$%oTw0C7lgE+7raaa`8JiT@ki@^j`L3I zlgEgCO-!2=R}0b>4ykD>fi(|p8-5MxtA;V)38P*yUTCKx1?*3$#sky`TykLhckDV6 zsBGyoZK^dsr=gqk<<hZq!vF&EqN&82Fxy-0|Ijjqc0f)xxTBjnG2Y+|6$a}(uA@_^ zH}#q-P5?px*HRmrNRh9UOsk(^MNFdsjK%=1uUc4LD;Q^#mnmQ5boW@0zdOHs*?OE# zyaw|^0@}3MhOL`zwUFPS^L=8QFaO)4|4{i;rlIQpA@rxLR~114s^wb6R&&(v#g^7F z#SRb+xI|iUviKsCe;bUP0B{>N`u~J@;~mXNFVEo#Q+9z|oLGX?S;WESV1~;YjhN}} zeN<rTqHDPS6@XTM7UT{sdO3Ek&I%6v&$G3mJmpy5&qLJc3|n0$lj;<1_UVXdPW1z4 zLR`C=&i~b+I>70XyW16n*Z+YBCIueGeVf8$RD0PpdUX^KW{0SQFa-HEGTMC|Z2#%_ zf5c@!uOXA4N&AZwpD-EbwZ`{tfi*c<3A)mjUgu`gdRr_2fuP*{|8w7tntQ``jrd3= z(A<DDc->cO<<|_AZFQJd64kF~uREJ)@qvzKbLj51T(GL+&iG%Zg1B)CaX^w!(WCuN zQt>^m-2MNwTsA@iBXe6J^HvvavCn$5h#m54+0GVMM(Y2|6~d;^ziw~Z5j})I>~H4q zEKDzRM+8O?X)B>k`w_PgyR%@UC3YK(T<lk<+p<QiSCCf}S4=GPLYrLIZ#s#7ecgTN znugDCTTSuUQ6({R@H`}11)x`yK76B3pPc-m7Jzb<;T3uFPP`MLMRGqc%fw(c38~O9 zy!%U0h5jp#W4WES{34GYqEDB7wiDY2wI2LiCDu?9_#rq=rx0RA{K?TXKR<SfpWj?y z4japlc?z>>O$cE!EA*{7i<wk7a&4#e3@5op77;B5ct4cvQ?E3_ZF)-o50n~5!NEH( zE_Yvz&>^e0u-N8XJTNaX!Fw9f5Ueg}a*hkCMa8wOn<#_IaA-*GBc2t|F2l!f<~f^x zibH3Q(0psyEvEhCNUahhnYO{Ct>6!(uGJwM=X(<wjQ7t69Tcs&9$;;VPm^3#C6H`= zNBXO<T5*SsEC*S?w_Ub5^0w}q<0@GqS9c5TS2w|if&vi(?wtkn9HMyzd+ZO=j4qpM z*5+3O+U&xT^^klZ`)afqpK9WopmXMd5JKR@P@!wo02h(Bk%28R6P+hMH^`Bd(O=LG zRj9cyY+wae%yA+<vN!#1^KWdqMvhz!uEw%9Z-DQgFu76DoPZl%<hK>m*WA{{PW#dH z0kpZ0!mYTuNd#wxD+ES|g$AuiS5^<Ce<dN)OY+0^4r_OeVXjs(S7}S(N?Ym{kj@m( zYafh*P}C!7Q~pf<Y_@F)j~rS6j8>}9>x6hV*kVeO)Y#-7StrD#$Vd`FaO)C*AU0G0 zOu7S!iKrH=fTFekB7&)I6XHL08Vv`u)BC`+_?m@R+aw)6o=z}nNbcsoc0Wi$v#fQ5 zO^t#sayckCWnWK1<k!RJD_!GBry=83G`Q$loQc_h;0?FOJ*Is0{PSO7ndkCwc@pNS zi*Ux;4^pFkkhY}|v`H2=<HAug4*X37_3Xyu8yL9f&%P)|l}ri-ty9(~eJ<cqke?7e z%-fzX6oFlCk5wUnZ^&Q!9C}6rcL8nP9hkF~sFLyI@>3=BJ4>K<U~|ht_k~GT-ylxk z`IwfPIx7tg01+KPGQJIK+PN;jP8B})UIB<}AcdNH>*VAM`~*)Am+<q(3$;CZsQ)6k zeGWsYZ8X&k>#5Le1_<J&I4uQLWX@+GAki&XGT{o1SLj@mr^~IK5?2%bQ~Wu6*$K8# zbDO$KCG)zcXgcx0n70)J-BPb;3SdbXIPfxvdmDz;uHK%C{>K6T6H&Ql9X|cXHW%-D zMa?RBwr+q~%d56$O3)|RBUm!f$*9SWP=50Xye-^x!rOv!$)UoITpqR%`)T%e1-lQ; z^spnPb`$?XTZ^v}y7IiJKB%=`!8^0V%DaEE3__;x$N*Z%Bwpky`zaFIFu)Vy({3*| zviTZef@K{W&&H3bcVV3B$CR<`@Mxc!P#!4S)u!G>p#K$NSM2aGu5Zy(XbEfq&t6Cz ziBRSBd-XQscQd}0uq8KM@aqjLc-L!sh(IT$YBy|E=y79NRM@YX%8C-4LYx`|K=2qp zx{U!0J24O`Loz~`dkTS~p1O}39j8|)%gDUZJ%R-^FdB<X2hFYEl)W=DtvXS24S2(O zMrVINKlVXQ$JKpYj~mtXb3>#VH=1dk)z<&RWrF<fKSpM(j|bMGPPVAKm1G!ii{_w` z9?<p?Ze|f+qo7rD^u~Na0mmHckGF6?wq8<zq6%-1n)tzxZ+FL@=>B$#ojKYu=P_E1 zP_9NHRl6sjK0^DlE5l;s>>?@Wg(5Cj0(0Mi4VjnNm6EH>CBasS^iNU&3_kS(4?uUA z7(!YM!MLp>Qj&cUKop3!3In56@c5yW0Jq7vtxcrn_WS+PqgZ4@-jfGtF4IEDod@1H zu8bww+tXUpleW<es#$pa@HO%z26Cb<BP@%&>L_f*fdWzHd^8(=)yFQJQ5>Y(xLn^D zUL0v$QHLw7(X+bbAiZ^jC~qx5$(M~RGV!x6mjESf>*08a4DJ@eS~cTX4Iv~(Tr2^) zm5cEJKH1b0N*foJ8g;Z64oBX4Hk2a)Vq6p?NuNhKtFb>n>VOtRi5+KBIUK8A{?3+N zRSOY!4Z?r!Ruq1a?b$OI1+i2fX~h<|bwZBs=)s*fGvB#x%;{`<kuD*m`9mlzv^f2I zQ?fa1<0n5bZdKb+In1=duv4o^x-sdi>4=|5v6pFynW2NA6L{w-J*t)b?JNwxo;&>7 zNRar~KL6ttk+M;=+2Wc5k4FSrv*ICZ=r)qHr|J=6sp+N@b>6}$O!^9+vhTcGAiSkY z|1nM|tjtx#LxdP~@Yo0FfV<fDWX3SDfO4{VflchE(v^awIZj`Z-HOTYp^0!-uQ=}J zR7Ql2kM!_|3AH3aIScB4x9_fU9)u+_xC$-PK+ox+`BG54!NTJGBJ+IMsDEgB)9@tq z3vBH`*KwLp*CgO1M}`qr^4D$)BDJa3Kr2GW+|7Z59I*@>4p>C>M8ykY4)Ogc1docW z0Tp&d7lc>sOJ13+p|P_M_!VNc?GN~Hwv}S%#M^B8Jo{NV0li*AS#Vu&3}gL49Gj!0 z338NoP^D*{oyDwmw&eq+iAuA6c6cyKu*43BS~TT-gfd?J8jxsbx3qLfAEGY@8~j#l zNRHhY?|}~YTS)=E<^L4baZAD?Z{*{BX2ZxA+ZVHabESRO{6Q}po2y486tC%7Dt&&N z*TikSagAGU9gfSYuLh(sJ*~`Fm64+%c-c1POfUXLM{HJI(y*jGvt>v1S42UWe`Ogc zv(C1PTol@5M_Z7O2l+2{{wy;}tKLOh*2p_S1ZEXR341ff^iNi9JSaj9dQNL$K=KtX zySkY9l|&Z)vQk<lBU1QOhJ8`UfL=$j4YD0BnY*Im8zU)N4DPXOntF$D(M;s?ppMq+ z{0On7NVT}yO+AG;gcz#iX#jh44vsTgQ=;j%U6Gb7$l|{S*0ZCo<O-#PGVZ&4*97qV zq;UZO1I(!(iRm;P3j0@cnat>X>=z&NZ9+lS?qpgJ0^pPLkL1~h?+#@HRTlKU7*PSd zl)7tml<lXc)OvfKnw6-U5h0!(mq~#Z0s3h4{(K!w0b0`jQRf=(bW_fDRZ;Kn&Ar9! zFi_3!eqmY$kH8<^8sM-Hb!5ZN9i^WB`T;fx`k9GC+Z+8*pyQ!WB89W11QHF^#WRjQ z9t*yaH>N4@Lc!CVSUs~T@Z9KVR%Wn2M?4bz2Iiq&rZ&#}Ie##!XTg6*AWvtJeRLV{ zRY990&OQRV6>+*Y-R)~48{AP1C(0w6-_^m?+hFDuT~Rk<ThtXw8x0!MK$hPHcCk8f zAk<U|9V(PEg)P1B#e8149-r5`)j2*Q-d-MSC0dR?_g|``l;WKmJcM%MA;346W6xw- z?(QYl36ZZ(mg1^#pYZ{^4-$IEBc`Nms?RSEFy^GX%YJXg?@u&D7B6JaW?|27b_2f0 zjt%k&&G5!G8#C-J@8yVtoE@mk*no2{JS2f?ke1C&Q)JaOF{^(e!m~%{=xZYo0z`iy z{cM!@AXo<%O;#&Wcg(I;_HM@AE$n`!M^apP<hH7;He)#4Yxof9e($q&=1fu!0#4_$ zc9$J(oj9WJTF(OSwR2*5x7uHTT(q&i7*||ZzeZiG(ly%Akfkl!3K=oehBcTwg#Vdr zZw2)JI<c%Ko3@JZm*6+YZ}@f(oAYCHQS=QuuXh6pFzGenkD?|o&G!+Y<|5`CwNb9q zF|ADYV!tSQk8Q-91~7th%wx)5LwHBiWMcN~i3G>8@H2iiF-80w#}qR{Z7A{R8b!Y& z5ZufthGQBJ!rg6rr2!uCp8=imh(X*O4@VNOd)z`7jh&lCS<3f~k3O;b6}u;tSVHzT zj!=NY-mqkEKMeo0Xtp8cW##jpQpDsX)%CT-zF|6_b)cCr*1xg}E#M#RT6c1l%s1l; z3Ii!J?B|_YV06p#uRdYj>y7eR=vk&(R`~8TZ<p&FCbQMg;FuBb_D3J6USja<y&@Ch zC4eIU=zcjHGHSN!j?NFEfA6i!eSID`2VI;M!k7AzLe(oFd1`7ZT^+|{O~<asL%6FW z^8N?vW$R!rbR&Uqk0z;Wjum6MZm^9b**Y+yK(iMD8J4W{&6qSCesg;Wg54#c(S_jy zAI~8*P1q7com8VuO+CLWe%C=rI*2M)_Sc1F?1t-h?N#^&hI)Wx=F{aPpSG~13H??% z7o`V%)BD>xg24k?7Xf*y8uKH?;_7HtdY7NbO-Af(Mz++MM5=PpgnM|%wD<c;arU|- z4v&F+!lRIvWu9xJ$nd$hBB5|?RM~hgaKe?XS1d%6?sHENRal!jt?IjHO@y=mpQj3g zb7x_i#B+yCWGW_b4txNnfA+3J-qQ1<H%Xw!Z?rB7<=1H861pa~UkEY!xrCJLgBzZ0 zBV^&9uKsyP{#potETiU@TLz#<YesCKACib3^?D{i3g@$q+o_g{?r$q(?5sOg7Fb^! z6OUBobpiFTuc8Jk2<yTdVvsL%J4Zvd#@tNnDJscvo|ziib@8JO2TG}QZx#(}tMkh; z9O>mpPH-aHjPp!T3gRxiZmmklkAM~m{;ftO`mJHOE#c^vUfr~KLM3<DYFWAsvuasT zf|1$4K(sp1fQf45Vvl#5fwKE2pFoi5#?^1^-OfyA>%iVupB~{|;{>Lx9TL;K(6}~G zK~hf^tPFEFTWIm%4Z_~LCEurwFjna=5>Y?+6Ie|}@weFS$-lqJZpe&Fe5qr4-?6Xj zs_(n<vdHj=b+Xn1J#&E{A}>^7++qxGWWXRlo!nXwoc>*#Cmg`BbRy(sv-4=v`RY-a zM5%#(lBk#jwF7JgUN`l?+qV{<ZWi9-9oAU#fvrOvZj;S7yJXJbvUo8xVc0!o+O=Bu z1ExXHQqtkIj0teXcNQ31>l#6(Yv5pdm2>1vP3c1o&iy*OJj%j~YGK`8A=LuhtBvq7 zxiQS3H3Z4w?Rq&;8)w|3FTigS2zHaj_~MDSU^<Atxv>ML1w?<l1^)_P_-?4N$&(m7 zNB?EPxs7e}ti9YN>B=CRvxTpPX5kM-rd_69py&DZvMR#L+9}+`4rtPORI%GOp51$G zM1vv!z2d2I>Ccoc4dIu0sA0OddR^SM8)7V)wqT7%_NU@yX+j^zPo$?nbI0UHqg2-u zh&lpI7p=qkyO$A%PXxHn%%VF#lyvaOrIm!!$3L!I%w9q}j2jvjn7`x_IuADT#IAtj zzfna+?<na{tw4$Uj~oAMXcDxKj^+fV0-ee;{_WIs3hx=h74Y_ud!AKR44>OmI;a6D zI~#Hzy?Zp3+;sa_&}ZRYWg_#?=<Xgw87ks9$<9Axd(|&J@_<6qzg0dkc6*P^2%l@r zn>B_j(zfQ}rK)IO63#DM6mYaQ{m`$_IO!9J!d<kb8w@So@pWM~TyD_#BR-z&urKa2 zA|dP4pMZG8N3TdYq7mMYk)`zyTgKZuiI6bv(mtzM#+sBAf!<`TFcBzYuvtmxhJGk3 zC9dAFrd4O6wAe`oPHU=&Z*gM+RO_Qc?@kh(C^~072Hz$EVbj#_h8&4AG|{bPlcp-B zD@pR;T+ZBiW9McdmiZE0-GxHwCrI4G39T_uDReDYq9vnHqcS@Q?JGHc2_{q{803yM zfNFlArKM+2SZX6W7@G30)@?)^C>v%fb*5iMWd-&cZuAR59NZJuJ|hls8euG1c$fF- zCX~5Vh%;Did1G@I#WN63MFhpDS9=T(#nFyb>yV&jN05y_z4;PVFuLZ*ATKLWuEv?K zm;+B5-K6_Lg;8xXFd)bH$>5D1@RmoyGR=f&d12DED;#f^L5?A;VdgAJFrLA=R8l52 z=i8=#8|z2i{*mXmy8aPu43xi}U$J&|DujLY@?xa)$S;8fpb~FjM#E5<;7hZ2f9G5B zJG`Rzl*oHO52JP9cqsAE8S@YnaHN7H%{a(D&6u0yUU&@^2i2kitCJVwTKl<7&a)9M zrl?kwVWQ-zOx8LUzd0JoNpT)h_i7V^m!r`PPxbv+>Wu;@?>myW5I!N&!XOre%zX{_ z2aWE>o~LzIUc$VE|1DfaFsjXACiSX|1AZB~ys2Wj9KcBHxDj=nlG|A`af2K?kuS2B zef(q0uo!j!<i^krqg94pbLTb>;&IO!U-OL}Gfm|brZ{rFrPhWp_UT;t_W6|}A%~JX zYJA;}4y)UDb0YmA&Ck?8!iH$K62qObYf+vo@@U2bwnkyif}Zjp$PPj6CpCe!&td57 z1EWWoVo~9~$LU$`CK!#Q6G59XWOQVm-jhWBQs2qYDak3zY{{NwuRjD(Vg#@34Loej zgWk)TG*?vlbPu3J33L9bzB9hNF77iV14#Nl{)UPkk^7ge5dhrRR&-lCgxA=1__cv_ zZ8w>=9dmrG^eAw9E%Rf)jJ9Wqq$2U~kQOVsLaQ30vsb?FQ)-Pn$ESrL{v6?!>%$%` zbie#<NQ^w~W>xq_uPRsz9g)$)L{f-AB7PFq*|MGkCL!72afU}xALT-L)9KK-c*srY z__WcnPW;V%$!z5#%X{-aKX>$^vFGPe?S|EP&^$cAl0+u>^G#PKcHNR*h+7y3X|5L9 zfwri;$5MPijh>lQ@UzA^pPh$hq{{!<IlpOcZ<}xxEm<BGa9Imc8yn4uUOq$CXw-_Y z?*nCpa&zWRz%c9msU*3!_Hqk9B`yCNljgdA=!H7G)zJ~yqOVU3@?_d=^(j1Sv=3K* zqfgXFlZ$upFdzY0Sn)lcT2oHVKXYi&NmHcm^Jlh?AW)`klEwx2@k{DLsS2cV?h`+% z98mD-8jlt-RzH146rU3ucnVKTvvE`6D*pa=PD2-cOJifYe#5lx4!q?#JW6Gk#(Bnh ziFOsgKXTNE@~IhkJTb?PW?ZdXy+|g=)sVet%7Ig^TE`2+SY1AFkTzXF`{~k|ptWJJ z&s{!fy}4+D9r_6V_mD?IO83kj5xvj}pQa2f>EaE$E00PB%)ShBF-8YVEP`<&<t4|Z zhRM%FNw8frxk|~Y)<-ysQzm~!C6|j&1^QGq4|>Ka7iduAi4$v$1+k|;(9E8Q=z@y= z)HLfJohH!H`{?7xUvayH{}x3hIku76GIEr2Y#$c$>BO{qsk)0EQWT%;!d!(aFl(XW zEA(75fLjchkR}@e96W(o=E*-kgvp>SUY=!{hYpO@hL+y6DD^j{IC2O&BrRKmxcTgW z67Fq1-!?61(<!nSgnO(z9y&2bGv+VDk{+1dx4?-bqFI~4I8ADn?SAcYY_f67%=|eU z11{<+&;+F#GbV0v#qZkvy^le_K5heW08>ylCG`l_``v>RDDUDrYKKXCBD_6%J_$c6 zKwUne`^??)!_C1@*SR6d=kJ);Dz#G8&bs9yt9`d`xe{-cS(O7=GBc&^9l`Uj;bj3t zTpyz};lrCb-Nul`4O<gqN(rq9D~<bGR72NY;!>~AR2|{$o;-ePojmHRK*JfyO%+N| zBxSjXUXj$P@eWt_bmpR0_=W0KXsL!Ox3BWo#8swx_c|CJIaKo9z}<5cPUP{$;6H4# z;{ALa)4HV+c@{@bVAZ&4VQHpxFwvw~8*R4J8^a08#2jei7=55qytYLE?7yQu2@ge{ z`t2x5`8ZS5+VCyjZZHREvU|Y9k=etsTYWE`frod*mlHn>AsvUN_%}RZnh7LV$tqM7 z=WS6BiR-HY3`$WjK~Vo&j_gc9=<W7d1vwNSjpm#TQ10HYgi*r&z>X|uf~pfSCT$U& zMI?^?tPPlf-VRxH!}%vyp}BT|P;38^o2*cC9>NpVsvpfu*<-EZ0MWm4VSt+1r4<*M z!fQGEp7>h5Y)9os*6!FB>WPYQkzz&*<&Y)-N6##e_;lG@8HLZ?Nv2yHfde2I=M6KI zesg5k_7Di!2aA20Y%GyEgsfYBN2cxg%`v4}6ItfLgheaiLjBY(#{j4krAVbX2->jy zgPF0saQn=NIpWSK_8UykU)7?FmJ-d^-B3g?B$U*i_>r5;5-0=UsSn~N_>w*bx0|5d z7Oa&Z(q2wxbDb*G2Xkt*R`}qTL$i@EpTU-(7>kA;Bmux1Ca_#w^*N#tn~1kt*ON#w z{9F5MnyV$GFC^V!*qyB;KD`c~8v;}gH{-!T+l0Pe%Mlg-Sj|#@>I#9nVOad^LDCDc z4UChxp)E5}j44AM_8nuKrjTd=RRp*F(!zIcEYtrRZ|Ch!SJxbF^LG^@yEk$l(}iHh z#jHmRW9g$)ZWyF8Ry%V4WYNbfzlBEc;Y>wWi_}SRaJ?ExJ4JZ;laVA6UO=QFq_TU4 zSTPSpdL+aD)1j#$(7I-rJhAo7D=`~#ft-QVlfMNqap1e|%Z{PsnRtWzw=^bTf*$Lc zWpSS<D$l}^S1xe-sU+Ot$nyonw@o?Zx=Tr^xZ*g3&P{l2A)p-M;Tt;Og%u;;Uud_= z9QLphWIE9;{yV}vsx3KtWpZm$tN{}<DCOxj#X;1mt(@A;8i!(WT}`It;ZpXJADu|| z%V)%)GX%7(!IBWoRmtBke##sM1$%bqAd{;Q$*k(i9(<A`e<V+x$b{@iCjP`2_ONwS zqN5Z1ycE4zw4=6#vORe|r$Hb&e<Y!-hb5Wmgp3KcUnCE-&v-J3mAJ{L_Rxq^fvXJU z7RaMg6*HTtr7Y{vQ5Bh4Om#v%`;@2k`y@g~)h>rXa+QfMb{FIQv%V37(BHjE{KSx3 zvpuE5s^1j`w^fm1L7|&+F`nFlaX%yIXzV`Ikgy#>8LE{~>j*)`Jnw&(2XxA2pFf=; zW>`53fTb{*I$A$iu0|T}U!!z@&kNV`VjQ2me&Y{M4xFMc^MqTD*e`xlPWbF27S0tz zkZ9vYU-5HgM;Y(T9QVBtF-Z$x3AwGP7TW_CIF~lZ+FXB2HBAc-vwc*SW!!B&8=b-w z0-bA8zLt={Akh!BRQ3y_vM=J}%rQYZ3^;HL9Hhj!KvY?}*J+VDy4fBCfn3b+9QNEB ze`xxJ;kIpTXJef=Z&FOhIR{Vre;Of`QKaasHVi)s_X&e_tl6dxi<Lakb8p0>7p&OB zx|O^ij{8kYQ^poMHN3m5U<1m%JpT+l@p*aTUmv+P5;^`k+j0%RcM*jd{q_Pk^^Qie zV|cQgk7GO|2UT;x29k0`);guwbpN~Sfe}z6n72SR!W6}foLG8J4Lqs?12zSuXGJ~z z^=ikZ70mvYgV1&b+HEu{w~m2bI-E;PF?^y+IMFd3;yI#NGrvgiqekT$j#yZ?4KnMD z=X=y=y=6!Dc|>tM48}VP7f$Xf?oZs}?Pq#q{oFfKs0)OhEjc1_#0`G>mdWLow<I}| zdHNOMsX}R9+#vszgHWw6h%rbjQ#;=&D}12Qju0}_0oEP8UKx)?!K#n6e)>`^7IN14 zq>KNKTMpq^9=H5n9CBHrAqjfmFr|d^pnfI^^kx&VC%Ot=5-51|4q~eBQ4f5HQo2*8 zyQykYmb%-D*d?8M7G{L7FLuH%WJ$cVN&przVP&1UdcFITkaoWf68SQ;OXS^;y!bd! zu4s0l4ykLANSWllye9<ISFz(p1<CxKqeuvtr?=E&TA6xzVPd}0hAT71P3)MOI>TJF z=zT}nw9IJI{fdl#FV1y-Wn8wNyuMbC0--!?9ve{780ki^7zRsF1Gz>r&Unn3S-2~j z$0j5O4tm}FwA~OV4uM?3t)7G?AK`<dm`ABGc`_b!Y>NUYsDN6e;SupZA<D9X4oTR% zFxeKUj+5BpCO(mn-vfSI>cis2In7J@e0Z08c<j%t|HA^j*oG@r3aa1L+8VQMfWvR% zA3blNZ^I<ATVN)>qMhBP(n<`$%Jk`!f?n6gxNhO{Bxk+?r<nBW5<U@6@4Zo&m9pyA z?f4!)jyf;y*`+@}ttmqfgo|6UX==~NmmV<~NblXcOIV|t9a>>Prx5R|_(v^5Br7Uy zYOlYWB82K$(M8-4zV#Q2-|63%7M}(G)Z|CMR_+x)x+_J3%1Ia??7j9x(WQFy%)$=a zv0_GhKif=JsyIKC8P)CW?)_Ct>7wp)@pmSWNf3qFlfDvnc&j%MmUX&}$J~3A)%~dp z%%H<~vOU>VX#Lr0lW6GdvtKGp*w~mbY><rd6&xF5ixhs8Hg8X>U)N`7fi0=L?Z6yi z7x?6WMKa-P#H1qHDb%3You|XMPFT8_;IO9cy-*SFzigtoiu=^5lW8Dboo#cMQztUZ zVFynr@p~rQdWi4Y4RLD%fzo79CK1u7T9QuGZ17PgHI%k*+LSnOQ_a6cimve;Y#ml_ zZpe6-0IRf>?7285a&x1(JMYK!ThV4#5rnDlP|6-EzUm|uU3HDvUv=iDL5ZwwWM7oe z<L5VJz81llb3MjIccy&be3Hb4UnG>9<5Rh-{;3|=wWFlN+HwJd&3VFq+9G@_{0)Pj znjYuR_#ogBn4Inb?g{xyHak<v$4%V_cAN?#iFoc{lPyZV_o6(4rN)yOm>`W$n?53# z+rZ$)Z?Wz86*W2V;;exaaH0M{6lW4gIr>_*cUVC=B7d>U4ouz87;sJLkxUYP;o~1> z!}fq1!G?SHl1NSN-o-rpobTvCtppYCd#Pl)pNOkg-cyps6y{cIloF~q%`h?IEj*f? zP!9fY><85<Rr4FgEk@XhS+3}Au$#PIalnJCP5&r~W6&wuvYBZvg)6FRN+pT0ue@1_ zx613adbk-A>-L@cj5EaU?lxA}rZK%#$ThT#V_u0}JwBjgm%=N|W=kenKpAe6iDMdX zT}G8G1){r?(5$1+9oTwCW;Hr;(%HC7<>5`%X#^?gYfF|G>!5AJsg;G*RbZ7eueg?U zHUC#JUS@5Ys3eU5qm8G<dd96k7m`GCLY4Iz+EFl_yMJY%za46vKSSR5oCqGOwW#(% z#NuQ9RS^`JbTa>pcFK#f3<$sMMsFj^|C`GG-Azc9)rs7A0pS(thM>eLdlZ#^?e?Op zd-DglYiexk=7OI8yH)K}okVq9je3ilkg>K<GE7=MABV3|Dq&<Y^rp_yJP6f22G~sE zs&Ty~>)%0c)#qiIWtVBpdM}NY58MVV5lFMiF~CksuG@Brxf$UveUk4YTxSzuuM$SN z6tJQ2gzba<HOlneR;T=Nt&#_=!MU49ICDKqRJ@jU*AVOza4yp&N3ZeIE-9$>w&|!M zvxP=aRa=b6Bue3~2})m))B_J{I`qkbHa2^3!t|d@E*LvRw$<2!PMyHR!aE$4yQ9Y2 z6FU7Ak$GP@fafii$6xlggo(1bPdfoH^0m-VtzMz2-`H~YO;qr%c$IJ(qV$-l+9|E6 z5vv^L#cl+?HE+*F{QN*0AAW{HC%rR46o?8?&T8f$flT@t5F@&dHM`RDiF(cJ8#x(N z03ifyq;DT(vfZ`<4u=o(X7%{+?5-xDR)eaZsufrPuuW5c^^MzTb`M=)#zTHkll#q9 zkbe?kp>2_NV(rQJ(_C;4<=yZmLV_|0qB{4jY`;GWM5!X^Qgff52xC!pwl;D6%6t5t zlz21|aCYZO9MOhetoGvwhjMRJ*cI(b5ySV~W=I|6DL9(WG5VLph4mS3s?3I+HrH1+ z6=aUni_s*=<U_!;k?=K>^b_<FE;ikldI|V?5$|y8p?Nf$cGc%0x$*YacX>+VYQY{A z23M2l<(;zq)zEvm>UXx)l^ALE9Jy+6#@ww;tJ}+o?wN#z7t~Eti{|6(`|MYRjrm|| z=45>pLq}IJW!<qJv;-OeEf2F!rj)y7oe9XtqRoz!e+T@kkF>-l%*>X<V1%cjY_61# zH&yS{cG~cEtN3MxEhB`0!*uq-|71ZlY$!h~MD(Cw+F#@(a0piHqll|998s1`{qWHH zQfML7Cuy+n5z!DmT7U;|LlBp>Qrwzr+(db!0X>=oq>G1Bs7o=^t8xA_M^_;ad(bZ+ zg*i*R@M4iOv0#V8-`lcmt()&K*N+N!7wMa~{LrVZS;x%;OK<lHEz&js-d#FONBFK! ziq6^T2(93LugL<LNqO8Ab?wk|5ueIW{YsEEHKiMM%l!3{Z)|z)9{o_*8@n&_Ed1d& z@U`P7-6I{acf^&9aTkFt@wx@8#vpEvlEjXvN6NRQznf6$fjp}2KYI{ePVQ}jN*odh z$=1;YLUzarX%G%=o1gAV4sQ5Ie?DzWmt8h0)cI{x9R1Fj*GUBJIkl7%IV`FR?Eerf z)qlE#QHw@thatedAQ?H*Hs{lZ@p;J<xA$a;L8z&(`nKz})!a0#;Zu++*;6}}W5_jQ z;JRwi4Bhr<`yv<QPjQlkK41A=FIRFa>(7(Gm$F-9xMF?^SEVI1cE8d}2gmNaNY<Ph zq9+xQPD~?a|1n1$@~c2d8OW`)^Ly$w1}{XwkZZ|wtZToC<bV!WUJqn8$r;P**>*Xk zmBACXhfiPCNH`Z<3o={7?(uD!YUh~O@3JV=cDvj*#xaudxrk&B7KF_`9x`EW-S!lQ zcFu&qslYgLp%f`(W^~a12Jy~nj=o?s!n0sg>j)Evi&1VyL-?We+r^`R2jJGFz4VM0 zR!Om@n6FciSjO)u;6_GG2MhR7dj<w1H(b(Kkh=7Bpyb<_B5aV4F^^BSvDZk_)#2;? zcoR<@D2^R~hUW9svu$?SvbS?uOI*S%sJBAjgZ6m5UHA{Tit0Lu%D&p4y1b|n4~>2M zPpee&Rv8ylRDFPZ>O1;q_Q?odl06=^go~I(q&Yg#mKA%pE&uzT+c|D(p#Vlg9$A_K zu^X1excExCH9W>t@<SecRVS+Sgu&}BzsrsBn!gqUoI_K$O~&zY!?U@TYG2+8U*-Wd zL*!s*mX#D0+qh(2*O`pu<d2d7`X#t8?4vDeefdy@H%Zu<RrYEbE=!*|s{cyY>%!g} z2S^eENmNb*dm!=08BaDr-BQZF6W6zlVb$XEfc-pIHQ6~ysmuW~UN8sm24JRIq^X(X zkw%^P&GpE|UZWOY`Zly9Av@e;$-@jMx~De}J<fSJyu!GJTGhX9qERGB;y4T+1leDW zW(1aL!7S#vw|iZPIg)t_{SgjfTJkrvK0xl!JZl=A0viA(uZc{ob9%<!(^LIXqfp<0 zOz9uC%&mI_(oQw;c#)2zu06z>^u~E(t2pC|Db!q5?Z6$r#3TmYZH&2Q5H7}d&&+lC zt6K4v6XroyVUuh`RN;-5ziio0&_jhNS%JFI5Vts#Y+Tu2Kl^@djqx-#%uyhp#Woc# z8PlqAL0*cI|HbeLHr*c^_R@*mXA)dJa(5u3?cEe1-!bv3mH21g9xEC~zJk|9n@lYA zmsD4pHi}BQ2_DhG(%MWc&%t5XUQUX~CQXrpnuRmWq*}7&Iacy-#AUG4VT%(0@gFQk zTTEhuoW;(O&2BFPFWh)9Um62r&&bZetd|COwXPL^-mvNrA(ZXGvn_S|>&POx)tJE* zp0t}-;=O-(aoUTF<W(o%rBB#CmC!VWn@zxqOocJM8)K?~>f3#Osi30I%Xcv~`c8Zk z;^M1k{WUtOJ}?@C&v5uOcTv@qV)+>4k;cCDKeq21G4r}8%|%?N%A_926;&$%2Z)!P zIUT}MvSS4WslFZSp*z!+@TH}@qG25-HLm}WPAEB6IZUUJE_ilyvEtv9aD=64K5kbs z&m#^Q@xTcCCz_?Wa?~D#ad<sDlAPg0|Ae3O$ul|lc%mP0(_9Y;oa0jSwFL5#kDY<t zGPPm{qhGsqxttLy&m(C+&IK-eYQr=v-vrLOLemR2yXDy^T)K(Va@xG>2nGX~{TGVU z=5L%!IkClX?U#UT3&?ME@o(a%$#2PN6ag?5i4UkO#P9+H0Z(@t9Jk%W;m}X6Lk*pM zLTphbBJBRGhI$@<=_QyqgRh#>!gkXit@H+0c7Z^sAROq9`z=Bmp^Av!S6mN559kW1 z-w@-?%lr^q=y4lgP7DW_XhON{q9!8Mlq`WTo-ZRVtK%kYO%ncBREZ!vW`tGvsZT3{ z6Z?0{_XKN|!2}LV_$qr5N%wEYW*^|==+{Dvg*E^e+x}PK9scGUm16UDDk8$82$8hS zMq1iVOu{;wK_ANWT`Ee4{REk932j)1X7})gE%BpGP`ZIHH`NG0rqGL<8PY#In)8;P z*tJ|}AX5JeFU%zx*%=-_JX=e<n%_K2`$^B{gu8OYb)G+g%T8y|Y)pZKGK-oHz+U;w zk00iq(AT7EB~vgrvl3`Dt|*~lc*u}Wz%nyt8k8M9p95PV?(c0{oq_V>J;RRWt8p}q za~*~n+L*}a-M=3%sz)BAS9FOz!!uaFBYbU7wtej7`1P&mycGJJQX#o1bZx>YKDr53 zxa7vI36IpH+n11VuQ{DYc8sET8ctcw?f&aFT<oy<AHEkglP9U;m0FGKz4^y_goBi9 z`QrUqYO@?av{2sl^U(NCJ8qv7#LyWe>Zi~@-ygKs9W`jJA%f$A(hkzQ7KBGw17_~9 zp-5hhFJNlJ`mr_thrN|2C_1h#*|}aq?FKQ3VOSv7Wptb^m6UC0o*Bg!-=9X{+;o9c zUZjoh@j{AVT`#Lu3m5hrMw8J`nEP@fXFJJQ6Ux8bji$esS9z9xwT#uZR8Xx@`TPGs zz*hX2H5GImPUa<$?#aonUX%ti7wnW%QNA!!Y6x#twlnMEHZ99%wvHXM6|XsgKgvcE zBE?fDpKx3O!8X$yO`BXHUyyAr1`XQ-(f#TC%N}Pq#cWSP$8V^dt}|$NYnH-^XQ{ex z+M#>dN?T+gHN#u5dPcxY4rDj1UBuqxkd7z&FEo}~^VzW_JGK9^H0ws2%kWKFsM`3k zl33<>`%=V^22+UH>-|YOK=WcxE{eO5TOnQCcS^#DzYmom<DB975{>ex$Rz7KV_{zs zJV4j&KCMOazYu4uBFvBwZl8IE!p0IQG0WXa+P>#DH8Z?W=*}8Le3YwpUsJ_~01qD| zUl`t@GZF}xcM!MIdA>*-c(W04k<*1+l(3=XueGP*1a$>xqs(tV8~?*%NsTHY_NMy> zja#eK?@O*}#4|@byM@U1zWaxSl49Z~_q$Md2aG!5mV{+!mi~#e9$ygAwr6!pEAJxs z++LEDMu^2!qw&&z#FomXR0Z6p!Z82g|H%vA@6D*1B!CyT1^t5TxfZc73F);aoi@bH zb1qkR-#X%yEBYJTn>VZWofriwh=i1h0^9#Xo!x35WXt}4q7$-oMRX8552cx2CmF}e zlFI}})){CaPCq{YsL-J8*(h4-2Arr%P&>hJ_$PYf$g!3fKB@!ytiVb%fz36){s;9& z%Vfa_NQ7ti;$Z<_u8$BV5!K%~nSP-#hjdt#UJ8#ZI&*FB*Y3H3GwP-Os7->`)H5%< zXAi8iYkdB#K2^N4l`{1bx!TAL_`fFQ`Sb7f@MF8+iFqaefQB+m0{H`P%zK(KP2AW7 zmq?=b-bjX%%?j_sgPw_eVSPpQFZ=@vru+sYK-kB;_5887$#y+bZ>>z2FN){$WG#o? z|DACc;TjmxW(TTubR549MYff#!u10-d8RoUcfywfI?wkYxASJsM!RM7FRZL0^uaLF zvGiNdKLadmZ{Op*j8wl`0?gX<HvEspLUo2ND(R_wzX#rA#WpTXb^u2IIOXerwPP%Y zFD!GB&<nHCJM!DxBAD)+$-Dh_R}6Qj<XkrKvEZ@i>6wsdWuvQ$`Jw{8r_zzO;SWwL zwKe?zV&I!7bcP3Xg>@koj7$CCc#_!Pyxduic&jvhU=LG~Ki-%k421oJcr?wwyMccJ zM|FOFGY|Fs_Joja17111YWaVt_%EzJMzR8`FIFZ`BcYSoGE3Cpxy%JKM#ZrY_mw@Y zuUj7feRJ!dvY*88(SLVe-y?>ZN(*0I``(a0h_gYcFYp1f!58KKO+=9AFLDi99MQ9v zlap)V9O+U##nhR)M!sG~D+Pwa?(%RSI%ac4Hu_hu!PG%PP78ENQqNjojS%7=_w<8h zKlu{f5w)T4)O;=21vj30tyQXDMh-@}SF~4=c8S;vxFK{dF1Wk1PtaGt@%{500~Pd? zIgMQ!vGn-isA`;Me|*P;U>a8`wwEWpvY6`zrA${+!9oy~{|NA$@45xz1i$9}c0xAB z$>)z{pZY@=>r$xB4Dsqx|Iv%k#zp2PZ{aI>QH)0P3t|;59bw+|s=HhXRTz8=k^~I2 zMmDdyZwoaE=2s;)Dq5*Xobv0lm1F8%gk}j>4fc(otqAPs6j1=#wi3S-yBuod%u@{j z&C+)d3O~za)VMm#FsUQg!(l-tlyO_T%C+GR8|n3WbeeN0>I@%kg?(`eu)rRSP;`C} zwj&(^js-uhP`BJw1w(Yh=qpv%7Qt`ILk<Z;m$$tTRMLDq`~KLS+v9(Y)RR;nITV=w z@pW!$x8r#XBA2C-Qy^tS5$-&{!41*z2oAdUC1$FKpe*&;1b%7WOF12u%5A1@Fvt+K zFiH-)-q*$LO^%Be@@TUSLS628^1cyjFnVca6!uc}`zZH_ngY;W|ImojP1*#!28V!T zeu6&|!o=Mmm0T+|{o{68kmnx_p>wNqn`ANbJDkIAELmzFg*Wo@KmNpnjU(g>8>@Q~ z0qSi{!hXu5u|g~}4uh124xNBBqIs-Es7y)X0fx^4ovKF6IY5R~a?~yn7W3(%>lU$E zLSlxR)hLnin7-DFH_s{VnU1xAIRm_qHj;fCk(Hqe17FPk*|mUSqWmrCz$|O(n72d! zyf;sNetZtw=h7D|IRQ{0RQv~8*5R}&VRC}kL<dfdw#RrP<A&@70$xMV((LJ%Ww>Mg z3>vcGf?}Gy-FZcWBPQ^FTtPuAyL)ka>tZ6q{eT~BC%nlNt|BxU8Pa@59Zg^37wPS# zQF{9X8(3OHnE1;at~)T-*O;4k6fcqo3``Nh`sn#By3Mr>Wy-%|Y^CT2qpF_Z34b>I z5BphSlvEg7g@+sAYrDD<>he}7y8LLoC%LvkbeWzpFPh4wMD2?7uvIp?b$hBdC^p~t zI%wZq2rc#?2>sPgO-4zv&XJ*7?8|&VY>eDoW~puDEPQ1zP6jW&lls7B7h=dcpCd(s zBTKI1cVVXy1bDija#P69+)yNj94v7Z_eNJ3@hu^2a>)uh(mIyJ3QFajaQAVn38a>O zZwuiTC`SY<fY*$hg>PVa`~^Y7zvcHMnAM$pCha2Pd#p1Bfvrx^UN#hTWvtO;|1}n( z_b-S>S5zy9p}-Z+B&i;lAN*D({16<(h_e@(4y65L-lW*SdV7Ae|6BOV7fJDi;KBF6 z^gv06Jcsv*{-$Leg{wc54JHuMrm_JW4Q(*kQK6+He-#?n9(pWjp~hw}MLW`7+4tB$ zzwr`{QmgCCex<_it%QOa)1}aL>GNA&e36jAc#>7A=>?i4<)Xf~7h)aqhu?+3>%-A> zE8n%i;caIDwBEK3L&g=IBc<_4PWV*kYN{-Q>IXC-ge?l;S?28p`N%9^P-~CeEMaDT z29)h0#G|v@#wr$MpB_@R4Gqu|4bZW+Y_zA?&kN-=$-MotLU>-S&qp};F5)exUk}k% z^tDAn7gY69{yvoXq8oM~lRL-k<LBOBNBo~GvnHE<-QV*_yK(v=$K@Hp33}m-D)4sV z7<7W1Uhi#30T!#{K@Xy3YhHQiRs0yZ%Mdi@V*iPxG0Sg`q~ol~3dccyo_<4tNDd8p zm;y3`$gIY@Y~v)<b^oJy%nCs}yq`P&taC!E7e(Bh8~+-+i|{?Y@Sn5R6F$51OPkhm z6Av_xVniABacY{K^w&>zeTi=WfJcy;Oqm?Bw0WFybOhxZaGT(%FFrjbcKH8gnG1S+ z7M0JM+O?4eBa_D&Q}w2mO3|83VSD!Y8^!~2-bbGTWnm*DdcV`9Nw8vN_~Ki_h`KF8 zEIbs=s3w!Z_kTA3Ft*<UHR!~1V66lF=N0guc|}YUUCq9uZjq%dy?6<2!9B$tHA?J6 z4&BNg_{~E;ecNp{s6<?TM172Kp*a7R*lbo$T!#eTpc|Yln=60HA^n`s%z~~(0>6t$ zD%vcMMsn<PFnku-7V}NycLGd;>c6OaVw{8P+{HQPd+AEO;ecit{Ceg9GhX6e!g2I3 zyfhh$7xP3shsl;1ybKgLH+tkCDrk6iF~B`sigW@)g8^s%zx&_Ol+IO=szE{&nf z4D^2JYTAbH2|XpdoHS|v`vy@ywzMk!;Y64_Pd#g5UHi|qXKBhPMXq;#)<a$yxzS2Y z|0RMIytN;nepFW(vEC;S2~&5k-S?||UfB-uiC4|qP44Z)Rqc<zzSlGUQ|^6azbcIt z-neIf^iEf99nY;bA-F)f|H{)EHI&`RNrEThZj9zrS|D(+dO%Rj=0%=#-6kv{YOT2r zbTV#L(d0*4ZpE4w4idStJhidUFTMlQ8Ob5v;^~8R8f*qTSem5xo%)sYWvmz(*pw9q zCW!eIg|NYL<-QG`KXUJWL?%iLUw}Km^$DT(M`De8c%)CIPf2F{+Me$u`55X)RnoK^ z)*oe3m8}SyNwZxfuwGbXO~9Id$S>o8@+EJ>ZwwL;j>MCygck_WYJtmYG+nCQp?rcs z1T2X-h`1xixK{`a7w{Qz5N+{jgtd3=zXu-)5Pg!fT__akMRAg=re@Yx^h7V_eEAKr zjXP%CyZoVK>K<3Ldt^qDp~(x<f^69L^v*LU($vk-8Obr7Y0Rr!1pd^0k^JMLjx0se zS=-UJ&i7ff){WNrzQkG|iAn6Y`IgdcM&$2{y&73z--nz#;^@c^_<ih7llX=?lb=@) zjxl)f2^~L&kL-%m23Y%d`tV-5c4hvKoYZcGB{ao{RcKulf;3_xM|54X6^4mrj1I9o zu1+h-IaxOs+D5W0?q25XsxJE|x50dY;L}haXQ$%bfl&)0Gijkg%xL;hc|pnkLd$cH zA628%A>c2u-~K%?E<JAJ`@$m9cubI!Wi+4i#Lk-$=?38i4sQve89Iz<*9NxM)FaUN z<;?k8k`&Ge&*@ZXMDz0Zm|qVD8hnMrr`H#&yHvKbk25H2=xZ~io3T1WilO{*XnP^B zAg#_%S#)5_^IU>Fe+)}mkeo@9B-V@>oNb%Yr7q3wagHMQeaOUbxNO%DuKwcRcCbD; zo*UYt@@s95O&SGTI_oYQB;7`Mx8E|~oa*<|_XZz@^p`ALox>8;4<JBmt?ZeKmLB|> ze^ItrOxqV`m2HENDEbR^3Wsq8f0^hB!;$w2Nd3{$h%j7(A5*8;s#y^UvJ(v2mB(7< zw_bCo#TF5Msa)QaqIs5;K}%DB17}zNj%2%=>D|6rk&|E@#z_mM&zNZQKFuYtn0(^f zG54K`x;1CNK*oMc&~+#MGy_?*!S}ofF{)}#;6vm+w=-^|a%n^QbxsT4t!MqcwT$WQ z{Rzvl=VAOqu}M_3`vdKz6}A#gnZ`sGcUc0MVw8;V5&m)Mci}IZcT0^#avPv%MYwcL zPdNcR(-vw~#d-<z6zW}~cOwG|eQD*u{KNjnFT0Xgi^Ra@aA!SVNRO-JZp;Z-abL=) zO|=z$zjjAkI8$V`L5^<*C9bB`xvOEyUtHrP{gwOc0QNHtsvdLwRCVXJJ<qUiiKsyM z+d(+V0(_5i<n4U?Dz`sWgRdLguAEBhVKYH@s7m<3Q#3=3_+FrYmB(dLzkBBirAwny z$5c#~H-$ZUkt?}^J6T7rk_*Zy)K0Od9djk_McU!++OaE9dD4pPRQ4HDG3t=xY~8;l zamlwFnS!L!k+~NR>|ay97Uhw{YN+(jmx|%~by~Qw=SJsAzw$irpBkC8<k+4zQ#+2Y zKHl&FxhU3qCNX=rNN0@#u|;;bT|F3rb591SE25c&h_x=^uMCskr)};4CJFjXmUV_3 z&rzkN9uFSl4F%0B_gU6`6;oI`qAn&Rf^t7S6c1G#w~Farg}kMtvCBC3>)&q3z6FSh z_;XWbW7Cm!>eLSNTi!~${&8Y0t2$Va7E9}1_jaX6FA*0rEq!z}2L3868qLs-I%s;p z9l%TGT2KS@Mk+RP6eQF%<o7JjIQ*}J%Cbd)?B=ladgF4e<=l)8ArrrCX$SP#!rGw7 zLpuB@B-v7|c|ysKyBxA_Oh7=6mBn{*Uo0+y^YmES_P{q}EudM~9i=1bb`3XXKeqT> zCd1|{{IKv?KVTu|i$$B!DFU2Rqj9dg{$;XFr!T<K`g$KuJUD?9jyTp@D><Rm=OF0c zDx}ycu3s>);VoQsx1MLYbsVAPhH@32Xpg6`+Wrq?g<LRq#Ga2+rteUhFV8-4KxRf= z5=0#?FX_Bq16=J?_19T@rx$cb8S?Ut)&XV~;XiF6Ykqn=xPvoYMeEBa=tq>$jb(b3 z(wsK-k_|eC-M@~W7MAj+dW=l;R&L+VIW~t^^{KvpijFa9L;%x01$PGkd{K{NwJ<@4 z)Jukzj(~RMwT7s($Idg4H(zX^1FWx*eJ+J0V`vxr=9jfy-up@T<=?sdlb#+`uqzAq z<bC&Y{Tin1zwKbs#Zd4+EWoO4WQN!Fuf~&0!-oA%|M>IIWj;h$liDc1*AI=If;FMC z#j4#=N3Z*g!74Co+pj19tz-)>zo(F4PU&`yNOhI;oY9FSY)z|c-+3wI0dQ=w!F|{B zuk`sMM5B7!$2JAA7~e8b#OL2S#^00h-nH|?rG+=TkY)XEdQn>TnjO`0QvMy40UZIM zc0K-;R6}t)FWz-LK6uv^uRrVhui?rX0}=mU0I4BX-jPLXf`o7D+$JWPF(k(i{EYaw zK1AaHl*B!C*M<7IDi-D=E*^etqjQD#el9~?OJbhmC;Hmq`uoB9SxXoOj?nos@r=Jg ze951pv9&R-ZJTgKaL<E%kRa+4n>p9%$kuwmBX|y0A>(xqc|p%N6`1U4&$uoeKGVg? z$N1;W?ub+%J1U7?g}3yXzCmM)ri1JlU9SDryI4OC*;#r&GkYAr_30xY>Ty=+_%Kqh z^KIAPY=Z2bl=v({uh20fO5@<LetK^wdA_rs&gqYkS#{NN9Z$CR=$HR!U#rnY@K*ZS zkNP)vXdiV8-p+bm=Vti)$`H3EH;a%0Q?Y^x-q3`Z)ZIUsz_=?D{uG-RQK#M~G;!tu zl$6~-ea*2HcktB;jB^1?zTX|Ok{1gjL%sT+WQd>P5SU8~l%&p)=WEU!`AFHd^Qf@} zCR&1tx=Q!*PzHnNUup2o{ZuVhAEx>JTM*wmH>;5C=LbELzjuLow~CfZ8v7OZLtG=? zrs+%y;S^%`GabZVZ}NQ?vHbvWiHiD5kboyrw5QwEBc`7n>k?gl1AP_Z=l8WvI0<&6 z!Kc@cvpoM^rs8={6X#m4!fO^1ocIQ*1E?nWEK+8uI8Q~jE<zqu;X5C(cDBT#F2NOg zjj1w)e69EXD9P0ZkJdI^*Tf`#q{rA1S7GvrOTgd}WN5qhMtpd~uHU`jdG3}5(HYi0 zb(1atQ@P%Ji|j<4r_bNJa2u1M;O(W~NlZ|Q;H@{)`0m@<|EIbZ_EV6+tyDprKOwGK zYdj0z!~*LXQfH3vV~CC)i5;>>bi8dYgGH^5cjvhJXh{3~6#RY9Q?Wi09PH-xrpDZy zg-iK_E2+DGG62C~aE~GrYUo6Cb)aU=D#vhs5x;yf94=#nP2%88E4GVs;PU$Ai&p7G zR?I|kWQ)j(-4<aF6-IsqCaz8M7@d${r;rMyUuPh8cSo$lZowH*L6U9&*h<8e!aG>+ zsA9e;jwg9bZN6GD(hbD;QLRLWkvg^13CNbdoA*@Q)TMcz*@(5-&A#~OO72ACAeDrW zOSer|Bfhy!iP3Fn?6VtlP2$Z_uVVilS}xl_or;w6X+AAp9C3~L&S!ztlzZK7K8t{R zNIxUMR2px8C){l;{!}6EJ?Ey`kFL}OuHSRZB<FY!m)Lzy#q^tLe%DcfK3(6j?erdM z)9Ammea^EJ>G`9(*@Olw?KOJ+>85MD$wyrG(lIby0wlYFtVMRaekqvXjRa-)Bs5`W zUtPWSF2YVdH1^B#NrCci&maL9#mMK;8^&MHx?jfVL|bf>nUa#L94XtpjN)YqoAX22 zH60}-Wj@z5X$yy-TA)TF7E(Xo!COdnJ!p<)D*7_X6M33PY&*!-<jq-}`*fer_x2IA zZVS=*K0@q4BnT#-oI~TS&(%nTcqD^k)aiHJ0Y2N(IC|V|!SM>XAM<!Wy-0R}`8mV; zX=x?`4`=ZApCFcg$J1w;if<X}vv_<ogL5v|_b%=_pDq59deJn8#_7S*;e4M(=2kun z(scL|30nIhi}7ljF1SC-;Co*n)?_c<c!G3!k<5e6env8uxZjH|NYHP(bRs!e?StmV z-JAq(=%|@+?`etGYL5&rxFrdJIWzvl=bZ=yt|GgL&Vs&p2F?sme{FqmE-T^z;#g<R zqD?X8>>|CskBq|&HT#XE#-<?4;vJ!vS|0lP{Pafxx;91p86QS0Y<>~nAJg=<bW@^k zc?N$!7_kxtJi_dfEZg8ok}Yz-N+AeAtjk^(skx*MILnrONVfXTN460&Wd#lShC$rg zwjen>g5xt+^v~lx`B_N&P~4@c-^%hk5s%<)DldF@8oN(G=D|GQdfGQk+btYaNFqqO zy-Zn2avt!qCKkp`85tfRw={^^@Eysn1Dj+ac;iTrZ8lu(W7rsm1f{0K5j1xDsYfz1 zEGU@Zt(z^b455iLH$s9YikZFy15-_D*EcD?9E6Y6UO&Fl#Op64^R0yz##|eytNJIh z^;}Fi#$j?GUep$%VrGC^F78B<l6!ydK_bN8*TH_j6vz2g7rvQRFPo>U{&)u8*~>>D zEkpcqvmxdqR?Cr?1i!KJP@m7{lK-Hf-+2T}l+1IwCRm5K0k|20q!)6t(C1lfhFH`~ z^KehANZ!^gMFNBR7U?^ZjcELVhGhK-!P`_ZraGT?&s2y6e1ug!7JS+@oW=L*5H|!b z!%3Yl*N+cL9iUoBR*)Pg&@YRAb{E-a{P>t;*NT3m=YC^IP96C*k^(WK7b9)L6K9Ip zJ!)nM@k!dL$UAola7cW?r(01HzLorz+RTMDjuSgKJoT0B9SM>tZX9-~uUd1oDG8lL zaaHR1dpy7!>n4wM%B?#?HgrX{S`R@20lYs6U(d28tR>GNK91Q4zK6)XO42qY=dArt zE^BxswU?KXNZv^=-oiR-@!u}Y6>mzR7~&h-4S#EVs-HfH%UO-jMB~i}vxel<K0T0t zolB7D>@GRp*M}r;4WEcql-ig4T9jLhbw1-{QwxTrND!5qOf84QXzbIz0!u!L>B8Pi ze|<;N<#RHt)|M}7ecrol7P0q{e*AzRm-z_UU(>t`&Q3HgB;x?@k|GsZt}9kCG2Ay9 z$N7fRqn;S%BuD)fD}4zD&yB8KHZfe@?GDlpkSHXVmTbKHqS(@J?`uOZ{JF8!i}wt4 z?tKe5Cl$f<e23x2+A~Mi0JeN^%MYIKs-@y~vagN)GnreVHu4$6KFZ-<oD>#|0V)(1 zdOjcF`F9nmgU)hc1CRBk1o5BVI}4+QL0nVZbRjt(F*-gz>p4dS5|DQ!lEe2Mowz?E zvj}UnzZ^ia5BT9o%BE+LZOTve_{-bi`w_?AJtP$$sX%q-g*}EetXIllZBOIs)0K9Z z$~&uRNHLh8SB8tgHLY~*5pv%}NQ~LJ7`<Ln!S5EhLlAd`rWOL{_$(6dPv?X=`Sp~I zeKhEVO3tL|Et+Cu_t3hvhnVi`-R0Cq4j{FEUQ6&<VC{;VVoSe0mLe$BkpUZ0Uwby8 zvRTwc4kdXzw57Sqi925dlwJ2FHD_#WhvULd;7br6($A4h0WZ=}fFN6f84kK8#pLpH zj@J>@$*j9)BJ);$>*Zq=sYTNN<dfbcr#uTGZVtKNo~%poFPQNhl0(<eFfa&7yY?rY zoIX<V`zHR@FF)oX0Yf*C?8Z=`qWVb0=kP;aSiPm^Xe(RvnF@(d^v6Aj{SFD3YtASV z*7oe>d0%g{!&csLbewQobUTrmrL>UgJ%hTi?W<!*SG~R<5)|JxPv^*$D-b_scOTvj zaSibsO9ztdPWKt`e+3h~q48Hy_rSTtLZMv@&L7)YN9|4^@Q3)qc`urNgYLm`HY0UO zze2w=7sLl{TtRVIn#u4*Ju<#>{-c4e{jbD{w`QucCmndA_N<L#ov!_cb!QB-LveQe zjFhST-e+8hsSu1HDPLCVVDvqiyCLL@&MC&P)w#ZudXL>b;;S@i97(or)YC*toGhuJ zFhzXa$6P|{ZSPC<cV{6Mb-zh(8ya$vex~B|5w+kn$HUsM&O?fq_%TF(4o9NeKSr#- z_mi2=cU$3FHGuSYjkhxqOaDE<VxRA9g#-|d)&)_5`*fjT$^XKCok#GN<Vgf(%W%Yi zx0NnP`|0&{SFu~J<5)2TFg}g!oVkO4w?T?w_{CbCOyk|CDvw~!zu;xP^$3l=EC|k3 z6n_x{KXxG)Jc`mzw@_2{dr}AB6p}}x*H3slJhk#hlD8P!0)2_qpXtE42FBrEezedm z1^!VxY2*@sb#?2o<kXh;P#7Q7LM^)dgkW8c$8~&;;dicJitpxIfcX`7SchzL$MPVC z4PvcNl@_TEvB18ESQ?KYbt&Hp9S62Y+%29!syj88X6|XkCwX5z{_aiu?Z*1EH&Uf) zGR;J0w?W=Qih=x{hM=l`Y8W|4+v<C5*KSBEv(0Jj%8)qDU$C?ku~t}s1&|nv!x3Na z0ko|h_jO3YjG3;_x*`F|kI*={E{tdmcOPFw23c`37QEq!Gl*VuG@*$z|IkVzCm)nx z%tU9wXV04ThTcp(wlysq8N7~o{Z--VmA@v=xHj5`2F&ixszp1QUK?Ibtp1@^xQ2U8 zApRmi>8yFw+7iOK3ZJ$_eTvDYQy$u(+~-_g;TcUa`GoU6RwUnN9b)0EX~Fh>3$d)u zR&jNp{-nygJ`BlgygpJ<tvTi)<$9k%EXlvs<8PYc?|UQe4#U(Mw6vYQk9Q;1tE*`U zm@G%E;5#FMZE?h!>$i>m6p|lxu^rk9CN#Y+Rgo0qLZR3h*%{fs8IfD1i^7%24p=WF zXK;BDw4;M${h8`%&)pE$ix*s<jia&qWE9yUie~cAG9r54Ih0PkDp3NnEwd#h;bZF- zfAuW>k=cdh+MYKa{XTInC9|l`VxtE*$2iri=Yn~xh0pz8;NHj?n?wOCb~~QvU6*2@ z%QcT{faHt?zmQxI!B1d?fS!52&(%I+J4kY2h?byQh3P*L7l&U12dg;q6W@udV(D5L zV~Dl+6~uRV6p|4Ca{b(oZOAT{YO#|j8-06<g<S>W*5ehs-y#Y5|D+2DOW_1>H=lR4 zrM4N5QK5K{kJx<=v6vo-1cYs^i^9=J%E*7vdI58OEF-z}^+2SGr+X*u1dl<X8n`~w zhn6;>i8BC*6S$JbQKW5^V@m?z?FodY({gFb=2Ba8_sVInOwg5?Y1>U`e&bZH9%X_h zchGX_Z>oX&YbK5S6rlXNxpw&NnE2)T9h~DaD@e8yFC*DEx0>WUvgP>ux#r438v6?e zk({`#X_Z-}BJ>&H6vTgUR*Lo<OXfi`RXQfWfmEXUwTd{upu{6cZ3%A6+=N)k6Z7=V zNCm^URoMQdqV^SS`~865=mKPk9Ucm581T!tyO`W#2-4vY#0|h(jOI>V0PY7&L1HpK z)xNS&-<`EOmKBrldlY|v-BHMH61wmm;nGWSy}yh!NBV+y!u8bMcMhS6GqwbdZ6_VK z4}stb=CjK0kn)X%-;Qv&F}(hUOK_YseO8EVkupqRbZpha6&Ukq3!U?&dIGltl+Bt) z<cv+QbV$A=+48zW4#x}Pp9As-{L5*3Q=oOFael~r@ez%)5`-w8tS)gC|BDc-?*x*A z`TFGeTxyV%WVay}<u82%$f$PKw`T&!(Xaz}k}fPx2lhm&;|<gPx<9hD{UWmSV2ex? zmJ*}_qu)4DXxExy`kuM9LoMRNdy0+~t8`q9H6hrGjfKdLh8uU;^W#b04Zk|mlcX<r z?@yGOH#d=gKeg86n0UXzd&L4~DuuW5zDU)YN25;3q;_VJ_%*Tm-y>TrUlsUVKL9t^ zPa0kgP-9&gIx_E)yfwEY+54u2QV6HdQ%q#u{HCQ8Ce7gsG!CQ-Apu^+$O9zjj17~& zA`<T+)*x@CsDbUMU+;`;M<@F6*N9~|5l~s9f+q2kC?w$6A)!SG=_13+Jf8)4zS+`e zv3mis(^Auh+jKR_ZVe#8saqnu;qI8TS9Dx1kl+PE6K75Uwxi8&!+X~|X0ll@H$;G$ z1?)66IL@66+o}tWGlt@D7|&xH2FAr#{!oW8PZn_Ctq1;9Gil_%?U-u;UMAV{T1xgi zP~8w8H#dt(odn&_Z+$4a=g9T)tdFqo4;)pb&&6AIJl~~lKlTy4v3At252A4pb-dlp zxow+%`i$IsgQVmuB%MgMEU!ZXN~7(dUB6Hv>t+YUcej`4^O@P4pjp9NGEuC?fr(dD zAbufb?XUyyVJf;F9KCk@%?0eVkDJ=a(?p|Z(LVjnhG2pdsH=Gd_@F@Nn2VMpEW6=L zJM3E4BG%8SXH1)%#W+=h<T3T;<bIpfsoFj#ImhXSz|l0$g_`bIT!DYOvlx;c!SRUm zT->5Uy67&1kn+ZN)RKEa5bwgon_*~vJL=bcXdH|-61WD**3eCbPZ?r450QD%7K@Oo zcuiA;f%APnf46}1p8K8kZtGWNv6$?V#rV_`{7iC+vB7GD=;c{lWM%YfR`Ax}cQyb) z0vFOY^Tnpi^w7A=2kK;*?`?@i#AA0k12*0koZq$M@#E;&>YuxUd4%M8@c9roH`PoU zxu|T`JZet+|Mt#2NQ&x?<DZ^mPgq#k)s-6$4vDuh9?>Kvrh<4>Vo-rW%On`7ATo(J zo=M6mEUvepNi>QOHAWLGBPk6P55$8giijA9#uzXl$Fj%lG1K|u_Z#V*={dJ&U3cEM zY6^Cae%-HMzxRE=<NGt!)C>Sg-bf5Z8i{1@W-GgBAsNBfxQw~wq)!0)xyyT#kvO|- zNk};oN^uL~!gIrXk`puzLo{-8DV$?z|6G6+l{h5LcdHPKb*>4@lM?>kjk}e+OwVN~ zxp^B;a#{Fa?n7hxza(oH&qbuAmMi(a5)t7)5BMIEsB{dXQM|wQX*FMLk%8VGVo?s< zK(h5P75s?i?aYMtcL6IXyz)VmWEF^g$xWH&8%P{@ZxkJfA=c{k3GWX|An5b+Z6NI= z6AX#fI1pMm0V!?R7p=Q-48tFs-G0Zkza!ayUOxAK?x-8_3ek>>bb{}zsI&qlm)OnQ zp90o+JaMjygvTxh6Sr&71^-55sUisr(+?51*KpETKdJ$~i%4xHVmYGom<~?^&m=wv zh!yB^((7aT5Ossg^cb^|Y<stgW1s2b980*)5R%L_A;bJ!szhiClAya72rJ1>75{pp z64l=V$B|j%xCBX@IU-?^ejWF0>S>4t{b{`i*(|~n5q)0g0{9-u34~u}EXUnq?L*=> zFGO@p8xYyD$B;76uWDbsseO7Y6819w3s}1?eYQp*9?g7S6ZH3mvPQ=H^HJUSr7H`( z50a4idza5v;2z%OHCm8@BZ)Yj%$DkbIu;&Gcs_d@HX{WmQeh{sEZ>8t-2IiGyO373 z?poNFlPrPJ7jbED`hsOXxI88nm%$z!b3L)xy}?dS!~592-iXufw&q8yib3~cS%>0{ z62&&~T?tUu@BxwVL5AiTIMJXB)&&WR*(JK5J%EHNH6q~;ekE_i)MDdS7-2QAfMn@0 zH(ZRkoeyxlHvp*<f1*y%n-Pm~Oo?F?BDr%uT?_aetb}eg$#JQ7>vzvfAQsb<kUpg3 zayw!nv~&@#My$bS==YpB8A*Uklm$8mu_~hH^DV?`{ujiezDwJP3-H(J!h5aDhU8Y4 zK05+QwyD$CACRz(M-dsN?YfDHAr|Z!L~`p&x=O#<z`Q;-o?k<(@3WC4$XxaEs}Swg z!;yIQk9AY{0Pbodg+LHd)Ht5}tFe{>f7Nlar2yw>Bi`JTb;G$>pS#Z-W4oUksm{{P zVj|WdUZ$oV=+mffK${abpR@Ftd<yXnw6Hfsss@qsI$P%&pMIwd?={1{Km^InxDfY5 z*SqM3)^SS08z~b*LNHW+CgLvf(f8rQvVIl0W7?#WIZJOM57~<Zv|-!J7(QVuExT_g z_jsW%X)gw@jehpgWPi=5l{)ElH@<9fBz(|Y{qEdA)5V7D8l)thmQL!<@ph!XB8b9A zvo6N_lDfL$Y9&_LS`9ZNZq7xzxHvIcUt4vd>D0w)2<}B^UO=j3rAos3iC$kQ;W|en zRzDnsG@uK1Dq}@EQcilM%V&vU3303SY+w~)fo#&v!+uC|$*6?iG$QWDcU+GDRg$gW zGxW6zQG3~?gm;i`XpV9@&fR$Kk?R$BM*HXp_NpRn)%(yd$2oO4V#(hhvFf+>!1-HB zysNr-oFNo0EMq%TgkhdOBfrq+a}yGt5YVxmN@3(f#0&CD4~~oXfJc-NI(ghJx-ng& zgx#-XcO2p|DtG*To^JBb>ZN1<EyT-lVZ!?zI$z}-D&snkf-@G<@!-!WnO<LQi)*m1 zh!)Q81Vi;_1^N%VyK~Qu8M;f(7i;Z6;C`#MaVk<9x>zs)e{i}Ti{51U{3msDC%G1) zHv=>^2Qe^6fo3G8aTB81m{@?K46(wdLkA+Obvxn~&IK!hA0dgo6J0*9%P^1b(YEg( zIfl)NCbj5nLacYAT)s1m%pSRSbn$oq_W~ZjQo@>;Fw&pwG;cSn!C+B}&!1G{Hd7a6 zCyv)wIy55g{OkJQSe~R~`4lC)TM<|E8;Iv%fZm649oLomca`3&(J3}_=P8+3^d;(x zD20gywsix1jcz=DtRyjz#XNzM+?#cC+LPFU#O;5@<rz7V^!nV-C^3EkQQL~qI}MqQ zNcGzgUGo!`A>N9-;YxTr9+prB+u~frl~ruGCGz3hD*`n`ufVoe<avx%Z2LB=c4P%@ zt#gX411-zCDz@XJ$Ne??|4yw6`QoRM@WCx*4I7Ar4=}XO8@TNp`jI}?)k~CQ`CjhN zoRmCYg?JWDMTFWjVW$$-r;ymkoq4wZB4Qwzic~{7HjCeUg1GShgeXolW=ZaEL=v^) z*O^VJ;U>g-{X9~n;EgojeINLle)kMScXUXW*AtI@UYX{5+mKL)_`N!fy;YUcp#^bs zKZe9jW|Cv_>)3Xpiu4vzAMS~O`H+JE$yUyOX}*81l9G6W_$DL)^3NHbr#R`Y*3Hh* zh&R9)7hSI7vJix~P~OW5mwxxT^l5)GTfLqAZaFr!S0Keqen4ge*9IMTkK*pR$YsvU zH?De}-rF!;^_o-RB_v1T2_(cQK3C0jjE{83uu^<pNV4pvBym4o^~iJQB}sQBR8RYo zO9_VR$09<&Clo0uxT#~wrSnSWEM2M#ST2YGE2tha1?=mQq?BUBWB$t8*>*6vhWv!# z7e~TlFDh#Y5eY9bG}WWM2$Cpuwqx?}BQbI-5Vw1waf}kEcqV$O63w-Er>9zuxWNxX zl6Lk-JPl>q#%?8N>k;?)2L0QsJ|2f8-5jdtP@@xgJK{?HkGB0!C71ceR#qxe9D~IF zj@Iie(YAJKTkj(YFaOdub3wKC<#=stl%tJKZQ~=ofA8wQjX4~@RQqb2_Sb>B5Lf72 z+NAeposx{TIgHbNb*v55`)=um;Xaa;5S)wcDSEw~j&r=7+%xeHP%`f%H@j1i&Z7{k zb)0nBx|w-4&oOw4Zls)TzoF;jb?M<t@IEmvpwH&Bbk$uRfW+%Nlg##L-=*6C&C=h- zV|SlPcoq)Pjl$tN#;bJv?bh-ArarT8le_Ni*N|A@sN?>u(M@i?sL|sdqvu|$_n=ju zkBx{$e?7_73CneSjM3-#ZA9<ePBF*C3qW)@$okznmwAohdTgiG@!yaV;yb%LG4I8c z8{qL9s17akHDO<k#IzJEcE#XWf6dSUF?$uc#Uiz0b3C=f0z{)P(zn$uGEo2MMBkv{ zLy&};T(A|(Ix;eM%$Bl-jYPs@3{~}(^adQ0o2CCRl>5-lGT={WKT6^1yH_ZQDRZ>j zuEb|ofv>d;$&fD7>y9eXYQp<Om$;R$RIfAMRx`<k8uG<JM;kuv%Qij!PLHl(fc971 z1KEyPc6X4w=BUM9om$)RZ>Hn;9oH8>PG=s^N4)(+xV3*BV!4mwIbHO-Qw-^Ox`xyJ z15w2IcLB(0A?B%=%O*INK6N|CO!qP#dtBm}EB55LiOXiri+QxcFV{_Jx#PKSR-)dT z<#}Z~7q6a&Z18;~e8D-U&Os7eW1bDtV8uW>fF|r?5c7U9W1tTfwBI)MjFLG^Z$=9q zWQpH!;L+>Z@yQ>Gn(PAj{CC-{Ek|1A6&C_u&*7JIBXh@WDX)K*Rzr5ZhD|qJL*3m5 z6_DJzp#|@Cp|eE<iNWj%G}Da?=4=fC1>PiheGU0md5d~PMzU?cgygW44IcGpPmIUT z9(<<qy{Fm!rm>skwx{R3R`O3o%=WyzdLA-iA0))d$xYuuJQLmSzt3YZf}#0z{BAD( zP<_0-=4itU_+~J);B<neOUOQ6XS?n5U(b&JP9nMdU@>Bx-L`8MmNh$v-z-P9O<PAC zY<keZz@V?AgycBS?ykXcomnTj*+AbYLu=4CF&+uGbK+r=iwkv+@OCV@JpRVfNXDW8 z^atxk%bxr(qRE*N7S_Dj-~(F0ZZO^VIes2%-1;eSZx-L}z~-7rc&xZ!&o&h^4GawW zJeo)@&M_I$PtF(j(6tKJr*uQK-@u^27=VN`I5&P<5XA;hHfcV9l8|t@U}#|-Saa|u zW4f<kOzeMmUTXCp{4|**NY=JZ82xL1bkAIjh1`isqR|BeE3YMkYuU!S-nOibrppWr z42le|xscvx$-Q9F07S*YSpv5cNsxNZbhAM(#R!r^Y5#~M-+2OGaQZ^pmt00^Xuc2Z z6Tv>i^m_tX!<rfU=j#D1YcXkJWo+z=O14Dy!|sxJU?7+l+qQW;mUTs%@4RVYFKr8t ziIg@N&TC*`P*ivv$wqf#AfjOM3UCpURbPrIemGx)kpfZoAQ}2o*z3WKAr<&$nJzZy zrKr#`>y!g*LnH`2%Nh9GUmOh857UE>H+`RkPGFJm*t5g-#+UBGJ>=t|x;=sOw~nm+ zJvQf<E_2xrz^m5a(UYub+l#nrZrb0AgvUK;y2`-7phz(qDF$*9>2+ndBHprqlHJM_ z-?7ZLt^fc6F-b&0RBKbRd?wx}Vi_2Cg^@^NT8-nqKOq$~KlZ8-A3$JweGsW?VaTpK z0>I?h%a0s^FYq9uW$8q--Pw87rz2{=VaRUU&DD|iXCIzp`2udb#1DY=0zi4gd#1Mx z3=E19|3XT>E&={W>df>yMAczL3bGqPEWHbm;!%d|8uV5Kb<Ec!yuZRrvJ0wZ=RfS* z2O*V10;ca%q8gvSU&qHA@AMDeua>UauUA|B2EIU`<Ozza40Ar?ZH#T(cUghbIz((9 z9n@Dx!ecl3?s%V$x#LVv85kH8D_W7#%WK%Hts2vRQ`J|u5ec$wN}87;ArQu6X3*PF ziB!leL9~NgkT9P6@J8GFa|BAKE(pZ1ZUDQ~^m|533|7~Emq5vH=xm>eR5)4Z+p}#^ zta`s$bT6Z}STKsn?wM2$Y#`csDpKxov0cGxfXa676!HxW3=F=+SgEf%M8o_L#PwT? z#PP<9XhaZe@CS%ncqKini)&!e9Ylfoy3w<cFr0Nrp&3tM1w-|xA-bmq+2J{R&-N+4 zQHNcCZT}M6o<Vip+gQHUNGz2>W_*G_IKx-9&#Bn<alrYJ@Yqddx4lnf?pV`11_lNO z1_lP6A^@C#lpl;C?h;=Qwe6#4<<Ek2E$x@@IagWz1|Me!%OA4ZTS{qZe4fEWr()Y1 zaW8l12Ai&>IeVh7gQQP_C`4bULX6J#iIzWjpWWI1d?Y+}6JW@$fq{X6fq_9m@PAez VbySehWc~mE002ovPDHLkV1i3Z6s-UN literal 0 HcmV?d00001 diff --git a/docs/installation.md b/docs/installation.md index 22f4399..d2adec3 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,28 +1,48 @@ # Installation {#installation} -# System Requirements {#system_requirements} +In this section, we describe how to configure, build, and install the +pmp-library in detail. -pmp uses [CMake](http://www.cmake.org) as its build configuration -system. Version 3.1 or greater is required. +## System Requirements -pmp requires a C++11-compliant compiler. For the current release pmp has -been tested to build with the following compilers: +The pmp-library uses [CMake](http://www.cmake.org) as its build configuration +system. Version 3.0.2 or greater is required. The pmp-library requires a +C++11-compliant compiler. We continuously build and test the pmp-library +with the following compilers and operating systems: Operating System | Compiler -----------------|-------------------- -Linux | gcc version 4.8.4 -Mac OS-X | LLVM version 5.0 -Windows | Visual Studio 2012 +Linux | gcc 4.8.4, clang 3.9.0 +Mac OS-X | AppleClang 8.1.0 +Windows | Visual Studio 2015, 2017 -# Configuration {#configuration} +## Dependencies -pmp relies on [CMake](http://www.cmake.org) as its build and configuration -system. `CMake` is a cross-platform build-system capable of generating different -build files (so-called _generators_) for a specific platform, e.g., Makefiles -for Linux/Unix, Xcode projects for Mac OS-X and Visual Studio projects for -Windows. +Some parts of the pmp-library depends on the following third-party libraries: -On the command line change to the top-level pmp directory, create a +Library | Description | Version +------------------------------------------|-----------------------------------|-------------- +[Eigen](http://eigen.tuxfamily.org) | C++ linear algebra library | ≥ 3.3.4 +[OpenGL](http://opengl.org) | Open Graphics Library | ≥ 3.3 +[GLEW](http://glew.sourceforge.net) | OpenGL Extension Wrangler Library | ≥ 3.3 +[GLFW](http://glfw.org) | Graphics Library Framework | ≥ 3.2.1 +[ImGui](https://github.com/ocornut/imgui) | Immediate Mode GUI | ≥ 1.51 +[Google Test](https://github.com/google/googletest) | C++ Test Framework | ≥ 1.8.0 + +By default, we include the corresponding libraries using git submodules. Note +that OpenGL and related dependencies are optional. They are only needed if you +want to use the viewer classes. Google Test is optional as well and only +required if you want to run the unit test suite. + +## Configuration + +The pmp-library relies on [CMake](http://www.cmake.org) as its build and +configuration system. `CMake` is a cross-platform build-system capable of +generating different build files (so-called _generators_) for a specific +platform, e.g., Makefiles for Linux/Unix, Xcode projects for Mac OS-X and Visual +Studio projects for Windows. + +On the command line change to the top-level pmp-library directory, create a build directory and run `cmake`: $ cd pmp @@ -55,33 +75,31 @@ customizing its configuration see the [CMake documentation](http://cmake.org/cmake/help/documentation.html). -# Building pmp {#building} +## Building -After successful configuration pmp can be build using -the chosen build system. For a Unix-like environment the default -generator is Makefiles. In order to build pmp just call +After successful configuration pmp-library can be build using the chosen build +system. For a Unix-like environment the default generator is Makefiles. In order +to build pmp-library just call $ make -from the top-level build directory. In order to build -pmp in parallel use the `-j` option of -`make`: +from the top-level build directory. In order to build pmp in parallel use the +`-j` option of `make`: $ make -j -The resulting library is named <code>pmp.so</code> and +The resulting library is named <code>libpmp.so</code> and located in the current working directory. -In order to build the full HTML manual and reference -documentation call +In order to build the full HTML manual and reference documentation call $ make docs -The resulting HTML documentation can be found in the `doc/` sub-directory. +The resulting HTML documentation can be found in the `docs/` sub-directory. -# Installation {#make_installation} +## Installation -In order to install pmp just call +In order to install pmp-library just call $ sudo make install @@ -92,10 +110,10 @@ prefix during build configuration: $ cmake -DCMAKE_INSTALL_PREFIX=<your custom path> .. -# Build Options {#build_options} +## Build Options -By default, pmp uses 32-bit unsigned integers as internal index type to -reference entities. However, if you need to process very large data sets this +By default, the pmp-libray uses 32-bit unsigned integers as internal index type +to reference entities. However, if you need to process very large data sets this might not be sufficient. In this case, you can change the index type to be 64-bit by specifying diff --git a/docs/mainpage.md b/docs/mainpage.md index 79fadc2..b7f0ddc 100644 --- a/docs/mainpage.md +++ b/docs/mainpage.md @@ -1,20 +1,40 @@ -# User Manual {#mainpage} +# Introduction {#mainpage} The pmp-library is a modern C++ open-source library for digital geometry processing. It provides a set of geometric data structures frequently used in geometry processing tasks as well as implementations of canonical algorithms. It has been designed and implemented with a focus on ease of -use and performance while maintaining high flexibility. +use and performance while maintaining high flexibility. In addition, it provides +mesh viewers and visualization utilities based on OpenGL® as well as the +possibility to compile the library into JavaScript +using [emscripten](https://github.com/kripken/emscripten). -This manual is organized into the following sections: +The [User Guide](./userguide.html) describes how to +quickly [get started](./quickstart.html) using the pmp-library, provides +an [overview](./overview.html) of the library and its capabilities, +a [tutorial](./tutorial.html) as well as guidelines +for [contributing](./contributing.html) patches or new code. -- @subpage installation -- How compile and install pmp -- @subpage quickstart -- How easily to setup your own project using pmp -- @subpage overview -- An overview of the library components -- @subpage development -- Guidelines for pmp development and contributions +The [Reference Documentation](./annotated.html) provides detailed information on +the classes and functions provided by the pmp-library. -The [Reference Manual](./annotated.html) provides detailed information on the -classes and functions provided by pmp. +## License + +The pmp-library is provided under a flexible 3-clause BSD +[license](https://github.com/pmp-library/pmp-library/blob/master/LICENSE.txt), +thereby allowing for both open-source and commercial usage. + +## Acknowledgment + +If you are using the pmp-library for research projects, please acknowledge its +use by referencing + + @misc{pmp-library, + title = {The Polygon Mesh Processing Library}, + author = {Daniel Sieger and Mario Botsch}, + note = {http://pmp-library.github.io/pmp-library/}, + year = {2017}, + } ## Heritage diff --git a/docs/overview.md b/docs/overview.md index c90317f..e4f2e03 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -1,137 +1,55 @@ # Overview {#overview} -[TOC] - -# Introduction {#introduction} - -In general, a polygonal surface mesh is composed of vertices, edges and faces as -well as the incidence relationships between them. SurfaceMesh stores the -connectivity information based on halfedges, i.e., pairs of directed edges with -opposing direction. To be more precise: - -- Each vertex stores an outgoing halfedge. -- Each face stores an incident halfedge. -- Each halfedge stores its incident face, its target vertex, and its previous - and next halfedges within the face. - -The halfedge connectivity is illustrated in the figure below: - - - -In the following sections we describe the basic usage of SurfaceMesh by means of -simple example programs and code excerpts. - -# Basics {#basics} - -The very basic usage of SurfaceMesh is demonstrated in the example below. The -program first instantiates a SurfaceMesh object as well as four vertex -handles. These handles, as well as the handles for the other mesh entities -`Halfedge`, `Edge` and `Face` basically indices. Four vertices are added to the -mesh, as well as four triangular faces composing a tetrahedron. Finally, the -number of vertices, edges, and faces is printed to standard output. - -\snippet SurfaceMeshBasics.cpp basics - -# File I/O {#fileio} - -SurfaceMesh currently supports reading OFF, OBJ, and STL files. Write support is -currently limited to OFF files. All I/O operations are handled by the -pmp::SurfaceMesh::read() and pmp::SurfaceMesh::write() member functions, with -the target file name being their only argument. An example is given below. - -\snippet SurfaceMeshIO.cpp io - - -# Iterators and Circulators {#iterators} - -In order to sequentially access mesh entities SurfaceMesh provides iterators for -each entity type, namely pmp::PointSet::VertexIterator, -pmp::EdgeSet::HalfedgeIterator, pmp::EdgeSet::EdgeIterator, and -pmp::SurfaceMesh::FaceIterator. Similar to iterators, SurfaceMesh also provides -circulators for the ordered enumeration of all incident vertices, halfedges, or -faces around a given face or vertex. The example below demonstrates the use of -iterators and circulators for computing the mean valence of a mesh. - -\snippet SurfaceMeshIterators.cpp iterators - -# Dynamic Properties {#properties} - -Attaching additional attributes to mesh entities is important for many -applications. SurfaceMesh supports properties by means of synchronized -arrays that can be (de-)allocated dynamically at run-time. Property arrays -are also used internally, e.g., to store vertex coordinates. The example -program below shows how to access vertex coordinates through the -(pre-defined) point property. - -\snippet SurfaceMeshBarycenter.cpp barycenter - -The dynamic (de-)allocation of properties at run-time is managed by a set -of four different functions: - -- add[_EntityType_]Property<_PropertyType_>(_PropertyName_) allocates a new - property for the given _EntityType_ of the type _PropertyType_ labeled by the - _PropertyName_ string. -- get[_EntityType_]Property<_PropertyType_>(_PropertyName_) returns a handle - to an existing property. -- _EntityType_Property<_PropertyType_>(_PropertyName_) returns a handle to - an existing property if the specified property already exists. If not, a new - property is allocated and its handle is returned. -- remove[_EntityType_]Property(_PropertyHandle_) removes and the vertex - property referenced by _PropertyHandle_. - -Functions that allocate a new property take a default value for the property as -an optional second argument. The code excerpt below demonstrates how to -allocate, use and remove a custom edge property. - - SurfaceMesh mesh; - - // allocate property storing a point per edge - auto edgePoints = mesh.addEdgeProperty<Point>("propertyName"); - - // access the edge property like an array - SurfaceMesh::Edge e; - edgePoints[e] = Point(x,y,z); - - // remove property and free memory - mesh.removeEdgeProperty(edgePoints); - - -# Connectivity Queries {#connectivity} - -Commonly used connectivity queries such as retrieving the next -halfedge or the target vertex of an halfedge are illustrated below. - - SurfaceMesh::Halfedge h; - auto h0 = mesh.nextHalfedge(h); - auto h1 = mesh.prevHalfedge(h); - auto h2 = mesh.oppositeHalfedge(h); - auto f = mesh.face(h); - auto v0 = mesh.fromVertex(h); - auto v1 = mesh.toVertex(h); - - - -# Topological Operations {#topology} - -SurfaceMesh also offers higher-level topological operations, such as performing -edge flips, edge splits, face splits, or halfedge collapses. The figure below -illustrates some of these operations. - - - -The corresponding member functions and their syntax is demonstrated in the -pseudo-code below. - - SurfaceMesh::Vertex v; - SurfaceMesh::Edge e; - SurfaceMesh::Halfedge h; - SurfaceMesh::Face f; - - mesh.split(f, v); - mesh.split(e, v); - mesh.flip(e); - mesh.collapse(h); - -When entities are removed from the mesh due to topological changes, the member -function `garbageCollection()` has to be called in order to ensure the -consistency of the data structure. +This section provides a high-level overview of the pmp-library. We describe its +design as well as the capabilities provided by the library. The pmp-library is +organized into different modules. At the core of the library is the @ref +geometry module providing data structures for point sets, edge sets, and +polygonal surface meshes. On top of the @ref geometry module the @ref algorithms +module provides implementations of canonical geometry processing algorithms such +as remeshing, simplification, subdivision, and smoothing. The @ref io module +provides classes for reading and writing data structures of the @ref geometry +module to common file formats. The optional @ref gl module provides +OpenGL®-based viewers and visualization tools. + +## The `geometry` Module + +The core of the library is the geometry module providing a set of data +structures for common geometry processing tasks. At this point, pmp-library +provides data structures pmp::PointSet, pmp::EdgeSet, and +pmp::SurfaceMesh. These classes form a simple inheritance hierarchy. The +pmp::GeometryObject base class contains functionality common to all types of +geometry representations, such as having certain spatial bounds and giving +access to the underlying points. Furthermore, it also defines the base class for +entity handles and functions for handling global object properties. + +## The `algorithms` Module + +The @ref algorithms module provides implementations of canonical geometry +processing algorithms such as remeshing or mesh simplification. The class +structure and naming follows a simple and straightforward scheme: Provide one +class for one type of tasks, and name it according to the data structure it is +operating on. Example: the pmp::SurfaceRemeshing class provides remeshing +algorithms operating on surface meshes. Similarly, the pmp::PointSetSmoothing +class can be used to smooth point sets. + +## The `io` Module + +This module provides readers and writers for common file formats. For each data +structure of the @ref geometry module a corresponding IO class is provided, +e.g., pmp::SurfaceMeshIO and pmp::PointSetIO. For convenience, the IO +functionality is easily accessible using the `read()` and `write()` member +functions. The only required argument is a file name. The corresponding file +format is automatically determined by the file extension. Additional +pmp::IOOptions can be used to control _what_ data is written, e.g., including +vertex normals or not, and _how_ it is written, i.e., as plain text ASCII or +binary files. + +## The `gl` Module + +In order to easily create visualizations the library contains an optional @ref +gl module including basic viewers for all @ref geometry structures, i.e., a +pmp::PointSetViewer, pmp::EdgeSetViewer, and a pmp::MeshViewer. Similar to the +@ref io module, the corresponding OpenGL® code for rendering the data is +implemented using one class for each data type, e.g., pmp::EdgeSetGL. For +simplicity, the @ref gl classes inherit from their corresponding @ref geometry +classes in order to access internal data structures. diff --git a/docs/quickstart.md b/docs/quickstart.md index cf5d232..26e95e7 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -1,4 +1,35 @@ # Quickstart {#quickstart} -Write me: How to setup a simple cmake-based project using pmp, creating a -custom mesh viewer to implement some algorithm. Provide ready-to use template. +This section briefly describes how to get up and running using the +pmp-library. See [Installation](installation.html) for more detailed +information such as system requirements and different ways of installing and +using pmp-library. + +## Building the Library + +Fetch the repository: + + $ git clone --recursive https://github.com/pmp-library/pmp-library.git + +Configure and build: + + $ cd pmp-library && mkdir build && cd build && cmake .. && make + +Load and view a mesh: + + $ ./mview ../external/pmp-data/off/bunny.off + +## Using the Project Template + +We also provide a simple project template for writing your own algorithms and +applications using the pmp-library. It directly includes the pmp-library +repository as a git submodule. To get started, just clone the repository +recursively: + + $ git clone --recursive https://github.com/pmp-library/pmp-template.git + +Configure and build: + + $ cd pmp-template && mkdir build && cd build && cmake .. && make + +This will automatically build the pmp-library and its dependecies. diff --git a/docs/style.css b/docs/style.css index 7a44c6c..39a6d8f 100644 --- a/docs/style.css +++ b/docs/style.css @@ -28,8 +28,59 @@ body, table, div, p, dl #footer address { + border-top: 2px solid #09a8ff; padding-top: 10px; padding-bottom: 10px; font-size: normal; text-align: center; -} \ No newline at end of file +} + +div.header +{ + background-image:none; + background-repeat:repeat-x; + background-color: #ffffff; + margin: 0px; + margin-left: 12px; + border-bottom: none; +} + +#nav-tree +{ + background-image:none; + background-repeat:repeat-x; + background-color: #ffffff; + -webkit-overflow-scrolling : touch; /* iOS 5+ */ +} + +div.contents { + margin-top: 10px; + margin-left: 24px; + margin-right: 8px; + width: 60em; +} + +.ui-resizable-e { + background:url("splitbar.png") repeat scroll right center transparent; + background: #09a8ff; + cursor:e-resize; + height:100%; + right:0; + top:0; + width:2px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 2px solid #09a8ff; +} + +.title { + font: 400 14px/28px Lato, Open Sans, sans-serif; + font-size: 200%; + font-weight: bold; + margin: 10px 2px 0px 2px; +} diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 0000000..e152335 --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,220 @@ +# Turorial {#tutorial} + +This section provides a hands-on tutorial on the basic usage of the +pmp-library. Since working with polygon meshes is the focus of the library, this +tutorial uses the pmp::SurfaceMesh class throughout the examples. However, the +general concepts such as iterating through the entities of a data set naturally +transfer to other representations as well. + +## Introduction + +In general, a polygonal surface mesh is composed of vertices, edges and faces as +well as the incidence relationships between them. pmp::SurfaceMesh stores the +connectivity information based on halfedges, i.e., pairs of directed edges with +opposing direction. To be more precise: + +- Each vertex stores an outgoing halfedge. +- Each face stores an incident halfedge. +- Each halfedge stores its incident face, its target vertex, and its previous + and next halfedges within the face. + +The halfedge connectivity is illustrated in the figure below: + + + +In the following sections we describe the basic usage of pmp::SurfaceMesh by +means of simple example programs and code excerpts. + +## Basics + +The very basic usage of pmp::SurfaceMesh is demonstrated in the example below. The +program first instantiates a pmp::SurfaceMesh object as well as four vertex +handles. These handles, as well as the handles for the other mesh entities +`Halfedge`, `Edge` and `Face` basically indices. Four vertices are added to the +mesh, as well as four triangular faces composing a tetrahedron. Finally, the +number of vertices, edges, and faces is printed to standard output. + +~~~~{.cpp} + // instantiate a SurfaceMesh object + SurfaceMesh mesh; + + // instantiate 4 vertex handles + SurfaceMesh::Vertex v0,v1,v2,v3; + + // add 4 vertices + v0 = mesh.addVertex(Point(0,0,0)); + v1 = mesh.addVertex(Point(1,0,0)); + v2 = mesh.addVertex(Point(0,1,0)); + v3 = mesh.addVertex(Point(0,0,1)); + + // add 4 triangular faces + mesh.addTriangle(v0,v1,v3); + mesh.addTriangle(v1,v2,v3); + mesh.addTriangle(v2,v0,v3); + mesh.addTriangle(v0,v2,v1); + + std::cout << "vertices: " << mesh.nVertices() << std::endl; + std::cout << "edges: " << mesh.nEdges() << std::endl; + std::cout << "faces: " << mesh.nFaces() << std::endl; +~~~~ + +## Iterators and Circulators + +In order to sequentially access mesh entities pmp::SurfaceMesh provides +iterators for each entity type, namely pmp::PointSet::VertexIterator, +pmp::EdgeSet::HalfedgeIterator, pmp::EdgeSet::EdgeIterator, and +pmp::SurfaceMesh::FaceIterator. Similar to iterators, pmp::SurfaceMesh also +provides circulators for the ordered enumeration of all incident vertices, +halfedges, or faces around a given face or vertex. The example below +demonstrates the use of iterators and circulators for computing the mean valence +of a mesh. + +~~~~{.cpp} + SurfaceMesh mesh; + + if (argc > 1) + mesh.read(argv[1]); + + float meanValence = 0.0f; + + // loop over all vertices + for (auto v : mesh.vertices()) + { + // sum up vertex valences + meanValence += mesh.valence(v); + } + + meanValence /= mesh.nVertices(); + + std::cout << "mean valence: " << meanValence << std::endl; +~~~~ + +## Dynamic Properties + +Attaching additional attributes to mesh entities is important for many +applications. pmp::SurfaceMesh supports properties by means of synchronized arrays +that can be (de-)allocated dynamically at run-time. Property arrays are also +used internally, e.g., to store vertex coordinates. The example program below +shows how to access vertex coordinates through the (pre-defined) point property. + +~~~~{.cpp} + SurfaceMesh mesh; + + if (argc > 1) + mesh.read(argv[1]); + + // get (pre-defined) property storing vertex positions + auto points = mesh.getVertexProperty<Point>("v:point"); + + Point p(0,0,0); + + for (auto v : mesh.vertices()) + { + // access point property like an array + p += points[v]; + } + + p /= mesh.nVertices(); + + std::cout << "barycenter: " << p << std::endl; +~~~~ + +The dynamic (de-)allocation of properties at run-time is managed by a set +of four different functions: + +- `addEntityTypeProperty<PropertyType>("PropertyName")` allocates a new property + for the given _EntityType_ of the type _PropertyType_ labeled by the + _PropertyName_ string. +- `getEntityTypeProperty<PropertyType>("PropertyName")` returns a handle to an + existing property. +- `EntityTypeProperty<PropertyType>("PropertyName")` returns a handle to an + existing property if the specified property already exists. If not, a new + property is allocated and its handle is returned. +- `removeEntityTypeProperty(PropertyHandle)` removes and the property referenced + by `PropertyHandle`. + +Functions that allocate a new property take a default value for the property as +an optional second argument. The code excerpt below demonstrates how to +allocate, use and remove a custom edge property. + +~~~~{.cpp} + SurfaceMesh mesh; + + // allocate property storing a point per edge + auto edgePoints = mesh.addEdgeProperty<Point>("propertyName"); + + // access the edge property like an array + SurfaceMesh::Edge e; + edgePoints[e] = Point(x,y,z); + + // remove property and free memory + mesh.removeEdgeProperty(edgePoints); +~~~~ + +## Connectivity Queries + +Commonly used connectivity queries such as retrieving the next +halfedge or the target vertex of an halfedge are illustrated below. + +~~~~{.cpp} + SurfaceMesh::Halfedge h; + auto h0 = mesh.nextHalfedge(h); + auto h1 = mesh.prevHalfedge(h); + auto h2 = mesh.oppositeHalfedge(h); + auto f = mesh.face(h); + auto v0 = mesh.fromVertex(h); + auto v1 = mesh.toVertex(h); +~~~~ + + + +## Topological Operations + +pmp::SurfaceMesh also offers higher-level topological operations, such as +performing edge flips, edge splits, face splits, or halfedge collapses. The +figure below illustrates some of these operations. + + + +The corresponding member functions and their syntax is demonstrated in the +pseudo-code below. + +~~~~{.cpp} + SurfaceMesh::Vertex v; + SurfaceMesh::Edge e; + SurfaceMesh::Halfedge h; + SurfaceMesh::Face f; + + mesh.split(f, v); + mesh.split(e, v); + mesh.flip(e); + mesh.collapse(h); +~~~~ + +When entities are removed from the mesh due to topological changes, the member +function pmp::GeometryObject::garbageCollection() has to be called in order to +ensure the consistency of the data structure. + +## File I/O + +pmp::SurfaceMesh currently supports reading OFF, OBJ, and STL files. Write +support is currently limited to OFF files. All I/O operations are handled by the +pmp::SurfaceMesh::read() and pmp::SurfaceMesh::write() member functions, with +the target file name being their only argument. An example is given below. + +~~~~{.cpp} + // instantiate a SurfaceMesh object + SurfaceMesh mesh; + + // read a mesh specified as the first command line argument + if (argc > 1) + mesh.read(argv[1]); + + // ... + // do fancy stuff with the mesh + // ... + + // write the mesh to the file specified as second argument + if (argc > 2) + mesh.write(argv[2]); +~~~~ diff --git a/docs/userguide.md b/docs/userguide.md new file mode 100644 index 0000000..e50d2af --- /dev/null +++ b/docs/userguide.md @@ -0,0 +1,10 @@ +# User Guide {#guide} + +This User Guide is organized into the following sections + +- @subpage quickstart -- how to quickly using the pmp-library +- @subpage installation -- detailed installation instructions +- @subpage overview -- an overview of the library and its capabilities +- @subpage tutorial -- how to perform basic tasks +- @subpage contributing -- guidelines for contributions +- @subpage codingstyle -- contains our coding guidelines