From f5e65197f449b58419d6a33363625332fa7b44db Mon Sep 17 00:00:00 2001 From: erwincoumans Date: Sat, 24 Sep 2016 11:25:05 -0700 Subject: [PATCH 1/9] fix pybullet.c compilation for Windows add lego.urdf, duck.urdf (optimized using VHACD convex decomposition) optimize Kiva shelf collision model (by hand, using boxes/Blender) physics timescale toggle between 1 -> 0,25 -> 0 --- data/duck.mtl | 13 + data/duck.obj | 8604 +++++++++++++++++ data/duckCM.png | Bin 0 -> 32504 bytes data/duck_vhacd.obj | 609 ++ data/duck_vhacd.urdf | 32 + data/kiva_shelf/model.sdf | 182 +- data/lego/lego.obj | 3751 +++++++ data/lego/lego.urdf | 32 + data/lego/lego_vhacd.obj | 3072 ++++++ .../Importers/ImportURDFDemo/URDF2Bullet.cpp | 5 +- .../PhysicsServerCommandProcessor.cpp | 36 +- .../SharedMemory/PhysicsServerExample.cpp | 14 +- examples/pybullet/pybullet.c | 21 +- 13 files changed, 16333 insertions(+), 38 deletions(-) create mode 100644 data/duck.mtl create mode 100644 data/duck.obj create mode 100644 data/duckCM.png create mode 100644 data/duck_vhacd.obj create mode 100644 data/duck_vhacd.urdf create mode 100644 data/lego/lego.obj create mode 100644 data/lego/lego.urdf create mode 100644 data/lego/lego_vhacd.obj diff --git a/data/duck.mtl b/data/duck.mtl new file mode 100644 index 000000000..43e9be2ae --- /dev/null +++ b/data/duck.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl blinn3 +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Ka duckCM.png +map_Kd duckCM.png \ No newline at end of file diff --git a/data/duck.obj b/data/duck.obj new file mode 100644 index 000000000..b4a71e1f9 --- /dev/null +++ b/data/duck.obj @@ -0,0 +1,8604 @@ +# Blender v2.68 (sub 0) OBJ File: '' +# www.blender.org +mtllib duck.mtl +o LOD3sp_LOD3spShape +v -0.350226 0.893874 -0.233732 +v -0.195676 0.897173 -0.224879 +v -0.092291 0.915427 -0.171037 +v -0.043305 0.887008 -0.045773 +v -0.450571 0.894178 -0.198240 +v 0.305196 0.116272 -0.251326 +v 0.156992 0.114278 -0.342321 +v -0.518411 0.177055 -0.365602 +v -0.657206 0.183720 -0.270862 +v -0.560117 0.114345 -0.226963 +v 0.232343 0.181488 -0.410429 +v 0.409218 0.186322 -0.296382 +v -0.624487 0.113989 -0.129806 +v -0.602326 0.281944 -0.395949 +v -0.712984 0.290359 -0.293335 +v 0.329737 0.296914 -0.434770 +v 0.489500 0.289358 -0.314102 +v -0.738118 0.417425 -0.298584 +v -0.652513 0.413955 -0.398840 +v -0.726597 0.550030 -0.292468 +v -0.646263 0.554849 -0.382648 +v -0.665829 0.664165 -0.279218 +v -0.555179 0.677340 -0.357358 +v -0.434971 0.756992 -0.318699 +v -0.569340 0.750037 -0.257495 +v -0.147601 0.738701 -0.351574 +v 0.121248 0.739991 -0.289191 +v -0.147016 0.788465 -0.288886 +v 0.033796 0.780576 -0.231953 +v 0.247824 0.782304 -0.076512 +v -0.049422 0.814267 -0.158195 +v 0.547257 0.899761 -0.088449 +v 0.536566 0.747375 -0.269735 +v 0.441714 0.778938 -0.261268 +v 0.647587 0.738997 -0.071530 +v 0.615691 0.656958 -0.252253 +v 0.429296 0.612502 -0.394496 +v 0.649663 0.572673 -0.217398 +v 0.489596 0.492561 -0.398218 +v 0.583698 0.439468 -0.280360 +v 0.317993 0.708398 -0.334366 +v 0.331153 0.823349 -0.086247 +v -0.482452 0.810789 -0.223270 +v -0.341225 0.805718 -0.266473 +v -0.155527 0.813807 -0.244230 +v -0.006560 0.810723 -0.039938 +v -0.151798 0.114100 -0.364164 +v -0.172973 0.173674 -0.432976 +v -0.438649 0.677941 -0.398677 +v -0.559835 0.561625 -0.427808 +v -0.564187 0.417648 -0.444371 +v -0.469566 0.290085 -0.442399 +v -0.180410 0.213370 -0.454158 +v 0.242634 0.297596 -0.464694 +v 0.371346 0.446474 -0.444312 +v 0.358668 0.578953 -0.427333 +v 0.241626 0.668013 -0.396298 +v 0.142645 0.434767 -0.518892 +v -0.101522 0.438267 -0.539252 +v 0.103735 0.363160 -0.518336 +v 0.145047 0.522745 -0.510455 +v -0.093544 0.521796 -0.535056 +v -0.245685 0.369247 -0.524691 +v -0.112191 0.356243 -0.525988 +v -0.270248 0.445585 -0.528835 +v -0.254374 0.550852 -0.520724 +v -0.092313 0.592580 -0.512115 +v -0.212751 0.616417 -0.503945 +v 0.110645 0.573325 -0.503033 +v 0.228339 0.538174 -0.486047 +v 0.223883 0.433299 -0.498488 +v 0.133437 0.344469 -0.507719 +v 0.150193 0.597488 -0.480109 +v -0.123031 0.316369 -0.514681 +v -0.282000 0.349703 -0.514911 +v -0.349314 0.441559 -0.512583 +v -0.337281 0.558993 -0.500475 +v -0.248495 0.633700 -0.486114 +v -0.099616 0.628873 -0.484038 +v -0.067334 0.845266 -0.028379 +v -0.102211 0.849641 -0.131445 +v -0.190500 0.850093 -0.204734 +v -0.352584 0.849759 -0.220089 +v -0.448651 0.851990 -0.187578 +v -0.541484 0.901992 -0.131393 +v -0.258400 0.893162 -0.235593 +v -0.145318 0.904728 -0.205795 +v -0.059928 0.895698 -0.101098 +v -0.591034 0.900324 -0.033769 +v 0.239364 0.115353 -0.306125 +v 0.114590 0.100413 -0.298243 +v 0.245326 0.101147 -0.222069 +v 0.351528 0.116836 -0.177191 +v -0.615472 0.142726 -0.252082 +v -0.436854 0.114300 -0.300164 +v -0.477677 0.141162 -0.336212 +v 0.194590 0.143090 -0.379267 +v 0.360670 0.145054 -0.276816 +v 0.329292 0.185574 -0.367248 +v 0.461733 0.186604 -0.205246 +v -0.733418 0.183468 -0.149194 +v -0.688732 0.234107 -0.284208 +v -0.557803 0.224988 -0.385465 +v 0.270171 0.234671 -0.430366 +v 0.452302 0.236628 -0.309246 +v 0.410731 0.293325 -0.394763 +v -0.794615 0.291894 -0.160672 +v -0.769844 0.234678 -0.155749 +v -0.728865 0.351223 -0.298369 +v -0.632406 0.345574 -0.398307 +v -0.818608 0.421140 -0.167292 +v -0.809318 0.353877 -0.164090 +v -0.660817 0.485555 -0.392449 +v -0.739987 0.485347 -0.296857 +v -0.801688 0.551853 -0.169480 +v -0.815894 0.488387 -0.169628 +v -0.611927 0.620057 -0.372950 +v -0.701700 0.610152 -0.286395 +v -0.748647 0.663268 -0.166010 +v -0.777874 0.610196 -0.167611 +v -0.617637 0.710370 -0.268557 +v -0.671835 0.754493 -0.166366 +v -0.716321 0.711772 -0.166670 +v 0.074004 0.761581 -0.263759 +v -0.148335 0.763145 -0.320471 +v 0.209470 0.750059 -0.213995 +v 0.093096 0.780917 -0.176635 +v -0.023331 0.812658 -0.107014 +v 0.553055 0.814823 -0.201865 +v 0.441025 0.828784 -0.210800 +v 0.483998 0.772606 -0.269595 +v 0.638341 0.698359 -0.177050 +v 0.582579 0.706396 -0.264175 +v 0.581459 0.604109 -0.315845 +v 0.501918 0.687831 -0.330970 +v 0.535557 0.461592 -0.349928 +v 0.630690 0.527290 -0.240553 +v 0.636495 0.607883 -0.233658 +v 0.544261 0.285392 -0.216382 +v 0.124228 0.392454 -0.519419 +v 0.521766 0.341006 -0.309987 +v 0.393582 0.371753 -0.428245 +v 0.632047 0.333562 -0.077735 +v 0.149689 0.480973 -0.515712 +v 0.132184 0.681588 -0.385488 +v 0.208410 0.719312 -0.315875 +v 0.340814 0.736573 -0.231685 +v -0.390864 0.776536 -0.294959 +v -0.522860 0.782905 -0.242985 +v -0.286752 0.750467 -0.347400 +v -0.245284 0.794248 -0.290977 +v -0.582456 0.816454 -0.149372 +v -0.625867 0.788584 -0.161198 +v 0.400580 0.733058 -0.290169 +v 0.624832 0.427738 -0.196053 +v 0.662542 0.577677 -0.160138 +v 0.463972 0.555620 -0.403341 +v -0.461403 0.832928 -0.200590 +v -0.528472 0.875160 -0.123304 +v -0.243957 0.812413 -0.264145 +v -0.175457 0.832299 -0.216568 +v -0.350308 0.869496 -0.219178 +v -0.056606 0.830193 -0.029728 +v -0.392717 0.100042 -0.259897 +v -0.497414 0.099968 -0.197068 +v -0.146156 0.100124 -0.317958 +v -0.282119 0.114278 -0.345791 +v -0.551790 0.100309 -0.114214 +v -0.161259 0.139798 -0.402177 +v -0.330793 0.177336 -0.413765 +v -0.189195 0.362041 -0.525892 +v -0.106044 0.396524 -0.534314 +v -0.182523 0.443116 -0.537955 +v -0.259535 0.399994 -0.527745 +v -0.097355 0.480914 -0.538815 +v -0.169236 0.536009 -0.532898 +v -0.268721 0.498693 -0.526952 +v -0.091260 0.558675 -0.527181 +v -0.159783 0.608706 -0.508542 +v -0.232428 0.592083 -0.511997 +v -0.152006 0.709348 -0.384909 +v 0.380814 0.666122 -0.376368 +v 0.314745 0.632943 -0.412950 +v 0.129337 0.554359 -0.506592 +v 0.298093 0.429681 -0.465354 +v 0.186612 0.311764 -0.478255 +v 0.302660 0.558423 -0.447033 +v -0.362897 0.312217 -0.480605 +v -0.465110 0.427776 -0.477899 +v -0.456636 0.564673 -0.462574 +v -0.328524 0.661703 -0.442955 +v -0.123579 0.672602 -0.432175 +v 0.191490 0.381518 -0.504005 +v 0.236628 0.488825 -0.492194 +v -0.211045 0.327401 -0.514785 +v -0.324268 0.389651 -0.514600 +v -0.352465 0.500413 -0.507771 +v -0.179201 0.637837 -0.484371 +v -0.300180 0.605970 -0.492453 +v 0.204236 0.576424 -0.481257 +v 0.071135 0.605511 -0.482081 +v 0.055476 0.574675 -0.506459 +v 0.062067 0.514293 -0.524750 +v 0.059546 0.433855 -0.531660 +v 0.046697 0.357333 -0.522177 +v 0.057025 0.324450 -0.511085 +v 0.083205 0.178723 -0.433717 +v 0.026968 0.113425 -0.364653 +v 0.272981 0.428866 -0.480687 +v 0.278897 0.550430 -0.465161 +v 0.164436 0.322248 -0.493054 +v 0.185848 0.623861 -0.455990 +v -0.136265 0.280639 -0.499986 +v -0.413922 0.434352 -0.494685 +v -0.323868 0.327171 -0.498711 +v -0.404699 0.563449 -0.480345 +v -0.289733 0.650389 -0.463945 +v -0.110523 0.656624 -0.456909 +v 0.379272 0.117800 -0.069973 +v 0.502675 0.183890 -0.073635 +v -0.758256 0.183928 -0.029395 +v -0.642756 0.116576 -0.026599 +v -0.828128 0.291368 -0.029395 +v -0.858726 0.421384 -0.029395 +v -0.849147 0.552091 -0.032575 +v -0.799360 0.662007 -0.032575 +v -0.729207 0.756569 -0.031167 +v 0.132799 0.782816 -0.067808 +v 0.424217 0.915301 -0.090221 +v 0.662519 0.423750 -0.075912 +v 0.679758 0.585114 -0.069980 +v -0.087642 0.815601 -0.202429 +v -0.086500 0.831534 -0.137124 +v -0.061663 0.863527 -0.033376 +v -0.103479 0.874003 -0.139949 +v -0.079197 0.847475 -0.081836 +v -0.195238 0.870994 -0.206484 +v -0.138638 0.850248 -0.172846 +v -0.351961 0.830430 -0.235111 +v -0.260550 0.849410 -0.223278 +v -0.444291 0.871742 -0.186599 +v -0.551672 0.837325 -0.132216 +v -0.533737 0.855572 -0.123674 +v 0.187264 0.101080 -0.266814 +v 0.285504 0.101785 -0.157995 +v 0.286467 0.144120 -0.339793 +v 0.409463 0.146426 -0.192219 +v -0.686182 0.142022 -0.140757 +v 0.369819 0.237436 -0.385844 +v -0.485084 0.721529 -0.339029 +v 0.147042 0.766096 -0.197209 +v 0.495150 0.838778 -0.207678 +v 0.603339 0.763635 -0.190618 +v 0.545522 0.648765 -0.329243 +v 0.608158 0.559957 -0.285728 +v 0.506442 0.234975 -0.212438 +v 0.451383 0.351824 -0.390803 +v 0.577470 0.335446 -0.215100 +v 0.275531 0.736877 -0.228942 +v -0.266207 0.769559 -0.319144 +v 0.393930 0.779086 -0.222647 +v 0.655928 0.516658 -0.172594 +v 0.448336 0.713336 -0.321213 +v 0.655690 0.635509 -0.165565 +v -0.253055 0.831594 -0.237810 +v -0.121192 0.832358 -0.180750 +v -0.259631 0.100257 -0.300393 +v -0.305288 0.140969 -0.382233 +v -0.184628 0.399920 -0.532994 +v -0.176910 0.489863 -0.538221 +v -0.161429 0.576002 -0.523171 +v -0.322615 0.714100 -0.379727 +v -0.514408 0.626760 -0.414618 +v -0.576406 0.489151 -0.438433 +v -0.529177 0.350586 -0.445773 +v -0.362170 0.231483 -0.439056 +v 0.317681 0.366178 -0.457769 +v 0.378300 0.515724 -0.436927 +v 0.259576 0.365644 -0.472776 +v 0.312662 0.496491 -0.456583 +v -0.267542 0.275116 -0.481161 +v -0.232102 0.678126 -0.435534 +v 0.098115 0.645785 -0.425347 +v 0.057351 0.546841 -0.518166 +v 0.063446 0.475345 -0.530926 +v 0.051828 0.393551 -0.528687 +v 0.130679 0.235531 -0.456457 +v 0.052198 0.140791 -0.402548 +v 0.006794 0.100168 -0.318299 +v 0.287520 0.492710 -0.472864 +v 0.238534 0.366519 -0.487626 +v 0.246964 0.595316 -0.458889 +v 0.071594 0.293978 -0.497079 +v -0.381551 0.375127 -0.497814 +v -0.239612 0.295453 -0.499126 +v -0.422701 0.499123 -0.488776 +v -0.358278 0.616884 -0.471419 +v -0.205055 0.662430 -0.459037 +v 0.089566 0.632617 -0.455834 +v 0.311142 0.101703 -0.063849 +v 0.441714 0.146552 -0.072783 +v -0.706407 0.143913 -0.029283 +v -0.799353 0.234130 -0.029395 +v -0.845803 0.353544 -0.029395 +v -0.859631 0.488595 -0.032575 +v -0.828254 0.610226 -0.032583 +v -0.767546 0.713433 -0.032553 +v 0.192017 0.778115 -0.073361 +v 0.485489 0.922656 -0.090651 +v 0.601596 0.844057 -0.082814 +v 0.557570 0.230430 -0.077306 +v 0.292013 0.796561 -0.080857 +v 0.371064 0.881752 -0.089146 +v 0.680046 0.515783 -0.071537 +v 0.669147 0.656180 -0.070774 +v -0.570756 0.101525 -0.022855 +v -0.066400 0.830734 -0.086410 +v -0.076706 0.867590 -0.086381 +v -0.143983 0.872706 -0.178748 +v -0.262100 0.869437 -0.221654 +v -0.623057 0.925874 -0.029313 +v -0.013848 0.692606 -0.385873 +v -0.015627 0.654519 -0.430262 +v -0.013225 0.641537 -0.456605 +v -0.049555 0.099983 -0.323904 +v -0.046308 0.113477 -0.370599 +v -0.044817 0.139220 -0.409628 +v -0.040821 0.173859 -0.439419 +v -0.030293 0.214474 -0.458963 +v -0.030752 0.282293 -0.499356 +v -0.031101 0.316361 -0.513265 +v -0.030300 0.354574 -0.524675 +v -0.026133 0.394033 -0.533009 +v -0.021136 0.434389 -0.538266 +v -0.017577 0.475190 -0.537873 +v -0.016376 0.513863 -0.533032 +v -0.017029 0.548295 -0.524802 +v -0.017674 0.580161 -0.510099 +v -0.015220 0.615001 -0.483586 +v -0.019364 0.728387 -0.338763 +v -0.036476 0.759142 -0.305480 +v -0.053181 0.784269 -0.269639 +v -0.585778 0.875953 -0.022336 +v -0.591501 0.858263 -0.021172 +v -0.615583 0.842063 -0.025176 +v -0.649644 0.821214 -0.028920 +v -0.690133 0.791475 -0.030099 +v -0.327991 0.897151 0.304233 +v -0.183501 0.894171 0.279721 +v -0.086752 0.895668 0.209938 +v -0.023931 0.903705 0.098568 +v -0.413633 0.901718 0.287054 +v 0.311060 0.114775 0.329686 +v 0.161893 0.114130 0.418242 +v 0.378441 0.117199 0.179362 +v -0.511145 0.178270 0.437764 +v -0.654167 0.183720 0.346672 +v -0.555386 0.114663 0.302365 +v 0.227101 0.183816 0.481723 +v 0.412258 0.186322 0.372200 +v 0.499064 0.181281 0.194546 +v -0.620944 0.114196 0.205393 +v -0.593140 0.284606 0.465397 +v -0.709277 0.290352 0.368544 +v 0.331991 0.298730 0.507109 +v 0.493615 0.288780 0.390246 +v -0.734812 0.417559 0.373453 +v -0.646226 0.415905 0.465931 +v -0.729066 0.550971 0.370309 +v -0.639264 0.552231 0.455736 +v -0.673080 0.664328 0.361160 +v -0.558026 0.667664 0.441694 +v -0.439561 0.746649 0.398150 +v -0.578297 0.752380 0.341393 +v -0.155401 0.731894 0.425115 +v 0.122174 0.738819 0.364385 +v -0.153978 0.787998 0.355147 +v 0.020421 0.784120 0.294750 +v 0.247876 0.765792 0.197267 +v 0.125340 0.782897 0.176678 +v -0.043446 0.811227 0.241642 +v 0.558163 0.872424 0.220756 +v 0.437036 0.883494 0.223158 +v 0.541303 0.747924 0.346324 +v 0.444888 0.778752 0.336648 +v 0.655053 0.725896 0.185049 +v 0.619613 0.657536 0.329553 +v 0.434634 0.604480 0.474894 +v 0.651954 0.570723 0.295143 +v 0.592654 0.279119 0.202361 +v 0.492087 0.491998 0.473723 +v 0.589689 0.437199 0.355858 +v 0.663446 0.421948 0.194391 +v -0.155861 0.700443 0.460785 +v 0.331287 0.693852 0.414713 +v 0.344892 0.800254 0.215618 +v -0.486990 0.811865 0.308689 +v -0.342382 0.805703 0.342498 +v -0.165759 0.811153 0.318950 +v -0.007279 0.811872 0.124148 +v 0.680180 0.582133 0.178183 +v -0.147802 0.113848 0.440174 +v -0.169221 0.174452 0.508066 +v 0.130864 0.433447 0.588481 +v -0.104495 0.439097 0.610858 +v 0.093266 0.366667 0.590505 +v 0.142200 0.519030 0.580496 +v -0.094560 0.519980 0.600277 +v -0.242460 0.370634 0.605519 +v -0.113896 0.357436 0.602776 +v -0.265918 0.446467 0.610071 +v -0.254626 0.544929 0.595273 +v -0.090067 0.589948 0.572808 +v -0.213366 0.605555 0.572059 +v 0.111972 0.572725 0.569708 +v 0.222438 0.531256 0.560203 +v 0.203457 0.430689 0.573067 +v 0.121122 0.350371 0.581689 +v 0.149222 0.593774 0.552218 +v -0.122904 0.317184 0.591973 +v -0.277581 0.352002 0.594961 +v -0.338971 0.444969 0.595273 +v -0.327486 0.555991 0.579969 +v -0.245692 0.625262 0.558246 +v -0.096903 0.623164 0.552589 +v -0.063101 0.846215 0.095907 +v -0.099409 0.849232 0.200693 +v -0.190219 0.850345 0.274843 +v -0.347653 0.850426 0.294209 +v -0.441941 0.851190 0.267866 +v -0.508728 0.906708 0.236459 +v -0.250482 0.895646 0.299540 +v -0.129607 0.894230 0.249078 +v -0.052550 0.898204 0.162020 +v -0.032473 0.885844 0.027243 +v -0.569280 0.897848 0.105352 +v 0.243405 0.114359 0.383603 +v 0.120514 0.099939 0.374698 +v 0.251190 0.099294 0.301860 +v 0.354745 0.116324 0.256774 +v 0.309667 0.101577 0.169998 +v -0.610000 0.143030 0.326913 +v -0.429826 0.115093 0.374209 +v -0.469722 0.142133 0.407944 +v 0.195197 0.143549 0.453897 +v 0.364880 0.144595 0.353605 +v 0.331887 0.185722 0.442309 +v 0.441233 0.144447 0.186976 +v 0.466619 0.183928 0.282843 +v -0.730378 0.183468 0.225011 +v -0.685692 0.234107 0.360025 +v -0.548395 0.227093 0.457182 +v 0.268814 0.236917 0.501401 +v 0.455350 0.236628 0.385063 +v 0.413718 0.293444 0.467703 +v -0.791575 0.291894 0.236481 +v -0.766804 0.234678 0.231566 +v -0.725633 0.351268 0.373883 +v -0.625170 0.348109 0.466606 +v -0.815568 0.421140 0.243110 +v -0.806278 0.353877 0.239899 +v -0.652083 0.485511 0.461149 +v -0.738400 0.485629 0.373000 +v -0.806871 0.553477 0.248070 +v -0.816740 0.488988 0.246283 +v -0.607961 0.614452 0.450316 +v -0.707794 0.611190 0.367907 +v -0.755475 0.665218 0.246246 +v -0.786126 0.612650 0.248040 +v -0.629885 0.710490 0.351951 +v -0.668647 0.754648 0.244251 +v -0.714756 0.712009 0.244489 +v 0.068569 0.761307 0.332993 +v -0.153904 0.761240 0.389186 +v 0.206749 0.749933 0.288188 +v 0.186590 0.771776 0.189697 +v 0.083190 0.783549 0.242702 +v -0.019646 0.812562 0.183514 +v 0.558430 0.813140 0.285504 +v 0.496254 0.893466 0.224648 +v 0.444451 0.826048 0.289686 +v 0.486808 0.772324 0.345316 +v 0.643131 0.696573 0.258984 +v 0.617107 0.818990 0.209783 +v 0.587835 0.707123 0.341097 +v 0.584469 0.603879 0.391736 +v 0.505077 0.687809 0.406817 +v 0.541830 0.459931 0.422824 +v 0.638549 0.525711 0.320063 +v 0.639335 0.607290 0.311655 +v 0.549570 0.228131 0.199766 +v 0.551520 0.282115 0.295499 +v 0.112032 0.394137 0.590290 +v 0.530760 0.342815 0.385249 +v 0.397533 0.373377 0.501979 +v 0.631283 0.334630 0.201464 +v 0.142141 0.477244 0.584915 +v 0.300695 0.773155 0.205683 +v 0.215757 0.712928 0.393886 +v 0.342579 0.739026 0.308400 +v -0.392161 0.776454 0.370836 +v -0.527939 0.784766 0.328685 +v -0.297051 0.737336 0.420511 +v -0.252180 0.793136 0.358083 +v -0.579394 0.817173 0.232211 +v -0.623879 0.789177 0.242442 +v 0.403947 0.733170 0.365831 +v 0.386256 0.846334 0.221512 +v 0.631328 0.425522 0.278513 +v 0.680395 0.512550 0.183870 +v 0.666864 0.575891 0.246884 +v 0.467190 0.553544 0.480974 +v 0.670838 0.651042 0.179110 +v -0.460958 0.832499 0.286417 +v -0.509529 0.873143 0.206038 +v -0.252513 0.810708 0.333601 +v -0.179216 0.832039 0.290153 +v -0.340372 0.871164 0.290287 +v -0.051772 0.830067 0.104967 +v -0.387965 0.100502 0.336129 +v -0.493922 0.100272 0.273241 +v -0.141870 0.099360 0.395629 +v -0.277425 0.114278 0.421534 +v -0.550359 0.100176 0.190023 +v -0.156372 0.139546 0.478631 +v -0.327953 0.177996 0.488099 +v -0.188513 0.362745 0.605645 +v -0.108610 0.397903 0.608997 +v -0.183879 0.443219 0.613282 +v -0.255709 0.401514 0.609249 +v -0.099846 0.480417 0.607803 +v -0.171720 0.530256 0.601664 +v -0.267053 0.497018 0.606231 +v -0.090245 0.556280 0.588755 +v -0.159709 0.599690 0.573289 +v -0.232776 0.582326 0.582090 +v -0.441377 0.664765 0.474420 +v -0.329162 0.700487 0.455996 +v 0.391943 0.648054 0.457138 +v -0.555401 0.555242 0.497715 +v -0.512480 0.616595 0.487462 +v -0.560065 0.419561 0.512803 +v -0.570734 0.488091 0.505693 +v -0.465132 0.292250 0.515050 +v -0.525663 0.353329 0.516029 +v -0.175056 0.214015 0.531414 +v -0.357529 0.232580 0.514249 +v 0.248654 0.303579 0.528774 +v 0.375950 0.446801 0.517631 +v 0.323879 0.370560 0.526379 +v 0.363175 0.565451 0.506094 +v 0.380947 0.508613 0.514190 +v 0.250582 0.659138 0.472878 +v 0.130315 0.552283 0.575699 +v 0.295994 0.427331 0.540504 +v 0.177196 0.321054 0.553997 +v 0.310452 0.549518 0.521567 +v -0.144814 0.256447 0.559766 +v -0.356595 0.316265 0.556473 +v -0.450786 0.432372 0.554835 +v -0.442682 0.561306 0.540718 +v -0.326130 0.653459 0.518098 +v -0.122638 0.666664 0.503239 +v 0.168788 0.385536 0.577812 +v 0.222779 0.481633 0.566920 +v -0.209614 0.327638 0.593975 +v -0.317892 0.392914 0.596303 +v -0.342463 0.501711 0.589927 +v -0.177651 0.631379 0.554049 +v -0.293959 0.599275 0.567944 +v 0.199928 0.570804 0.554805 +v 0.072388 0.603598 0.552040 +v 0.056580 0.575512 0.570761 +v 0.060065 0.513551 0.590490 +v 0.052221 0.434715 0.598809 +v 0.039639 0.360409 0.595080 +v 0.051561 0.328988 0.585686 +v 0.135854 0.239342 0.526112 +v 0.090641 0.181577 0.505723 +v 0.032588 0.113870 0.439714 +v 0.262193 0.426871 0.556006 +v 0.285919 0.544558 0.538865 +v 0.150875 0.333422 0.567928 +v 0.188250 0.619909 0.528915 +v -0.133618 0.277844 0.577174 +v -0.398055 0.439520 0.575439 +v -0.318122 0.331212 0.576826 +v -0.389151 0.561492 0.560878 +v -0.285307 0.643375 0.538731 +v -0.108825 0.652576 0.528604 +v 0.387872 0.117814 0.103899 +v 0.513804 0.182081 0.111306 +v -0.759308 0.182734 0.106190 +v -0.643713 0.115264 0.101319 +v -0.826956 0.291294 0.107480 +v -0.856139 0.421370 0.115317 +v -0.847924 0.553455 0.124133 +v -0.796343 0.663979 0.129219 +v -0.716684 0.756332 0.127061 +v 0.252621 0.791542 0.112952 +v 0.143624 0.783676 0.105812 +v 0.554174 0.911379 0.151558 +v 0.431913 0.920224 0.149719 +v 0.655402 0.747902 0.111558 +v 0.610434 0.280454 0.113123 +v 0.326111 0.846875 0.125304 +v 0.672833 0.424839 0.109141 +v 0.688802 0.586841 0.104374 +v -0.005433 0.811205 0.062216 +v -0.095242 0.810708 0.287966 +v -0.083757 0.831824 0.211125 +v -0.055308 0.866678 0.092444 +v -0.063553 0.843783 0.037215 +v -0.099194 0.869399 0.199543 +v -0.074630 0.848157 0.151358 +v -0.191716 0.869829 0.271848 +v -0.137244 0.849789 0.242465 +v -0.350315 0.830667 0.312656 +v -0.261633 0.850641 0.293275 +v -0.428321 0.872394 0.265835 +v -0.544673 0.836613 0.215529 +v -0.521903 0.853592 0.204029 +v 0.191772 0.099701 0.345627 +v 0.290093 0.100524 0.239684 +v 0.289966 0.143787 0.416404 +v 0.414341 0.144313 0.271351 +v -0.682653 0.142141 0.216559 +v 0.371724 0.237510 0.458917 +v -0.491935 0.709503 0.423484 +v 0.145648 0.766133 0.270075 +v 0.498916 0.836109 0.289872 +v 0.611227 0.761114 0.272671 +v 0.550601 0.650337 0.405186 +v 0.613029 0.558348 0.362984 +v 0.512017 0.231727 0.290783 +v 0.458464 0.354359 0.463143 +v 0.586945 0.336906 0.294149 +v 0.273062 0.738878 0.303291 +v -0.271279 0.767275 0.388845 +v 0.396428 0.780687 0.298339 +v 0.661326 0.513373 0.258257 +v 0.451368 0.712869 0.396934 +v 0.658464 0.634174 0.250094 +v -0.258474 0.831357 0.308770 +v -0.121451 0.832039 0.255855 +v -0.254849 0.099383 0.379992 +v -0.300780 0.141043 0.457664 +v -0.184480 0.400625 0.612347 +v -0.179876 0.487616 0.610605 +v -0.162348 0.567727 0.588674 +v 0.322767 0.620154 0.492051 +v 0.139368 0.678282 0.459206 +v 0.248684 0.370434 0.548511 +v 0.315250 0.488313 0.531740 +v -0.264258 0.275071 0.556637 +v -0.232554 0.670170 0.508845 +v 0.101563 0.644547 0.500444 +v 0.057574 0.547064 0.582557 +v 0.058419 0.475145 0.595984 +v 0.043791 0.395768 0.598409 +v 0.059761 0.142111 0.475917 +v 0.009262 0.099627 0.395747 +v 0.285177 0.485533 0.547769 +v 0.216936 0.374949 0.562842 +v 0.254638 0.591765 0.531910 +v 0.068028 0.301029 0.572111 +v -0.370326 0.381125 0.577227 +v -0.237499 0.294563 0.576722 +v -0.406137 0.501599 0.570286 +v -0.348580 0.611842 0.549200 +v -0.204387 0.657863 0.531414 +v 0.091835 0.631068 0.528225 +v 0.317926 0.101666 0.099777 +v 0.452866 0.145017 0.108326 +v -0.707816 0.142259 0.105137 +v -0.799368 0.233648 0.106420 +v -0.843683 0.353670 0.110661 +v -0.858037 0.489262 0.120255 +v -0.827112 0.612146 0.126921 +v -0.760168 0.714278 0.133949 +v 0.202604 0.779924 0.112248 +v 0.493845 0.929811 0.153182 +v 0.608729 0.856046 0.142238 +v 0.564851 0.228762 0.112211 +v 0.643531 0.342467 0.111677 +v 0.291309 0.810871 0.114450 +v 0.373177 0.893021 0.145323 +v 0.690093 0.516317 0.106220 +v 0.676547 0.661837 0.104878 +v -0.057155 0.830356 0.044281 +v -0.570919 0.100917 0.095929 +v -0.060372 0.830742 0.161330 +v -0.054597 0.861214 0.032603 +v -0.071205 0.868962 0.150149 +v -0.138867 0.869385 0.240596 +v -0.259809 0.870964 0.289590 +v 0.116043 1.227810 -0.086848 +v 0.112981 1.226920 -0.096968 +v 0.109770 1.223660 -0.106888 +v 0.106656 1.218200 -0.115860 +v 0.103765 1.210780 -0.123445 +v 0.101237 1.201750 -0.129302 +v 0.099183 1.191560 -0.133142 +v 0.097722 1.180670 -0.134803 +v 0.096877 1.169600 -0.134188 +v 0.096706 1.158870 -0.131341 +v 0.097233 1.148990 -0.126358 +v 0.098582 1.140210 -0.118907 +v 0.107227 1.129140 -0.087189 +v 0.111023 1.279440 -0.084156 +v 0.104921 1.277670 -0.104464 +v 0.098597 1.271220 -0.124230 +v 0.092465 1.260370 -0.142114 +v 0.086808 1.245610 -0.157276 +v 0.081863 1.227640 -0.168946 +v 0.077896 1.207330 -0.176635 +v 0.074931 1.185630 -0.179719 +v 0.073344 1.163630 -0.178451 +v 0.073085 1.142330 -0.172735 +v 0.074182 1.122730 -0.162733 +v 0.076999 1.105340 -0.147860 +v 0.082775 1.090330 -0.124727 +v 0.093578 1.080800 -0.085424 +v 0.099561 1.329980 -0.079626 +v 0.090493 1.327370 -0.109810 +v 0.081114 1.317810 -0.139103 +v 0.072032 1.301730 -0.165602 +v 0.063661 1.279880 -0.188075 +v 0.056402 1.253280 -0.205468 +v 0.050582 1.223180 -0.216961 +v 0.046482 1.190990 -0.222025 +v 0.044228 1.158240 -0.220238 +v 0.043976 1.126500 -0.211845 +v 0.045629 1.097460 -0.196875 +v 0.049707 1.072110 -0.173891 +v 0.074249 1.033830 -0.082177 +v 0.082048 1.378790 -0.073369 +v 0.070141 1.375370 -0.112983 +v 0.057855 1.362840 -0.151374 +v 0.045956 1.341760 -0.186110 +v 0.034983 1.313130 -0.215559 +v 0.025463 1.278270 -0.238351 +v 0.017841 1.238830 -0.253417 +v 0.012465 1.196640 -0.260060 +v 0.009589 1.153670 -0.257954 +v 0.009322 1.112020 -0.247018 +v 0.011568 1.074110 -0.226977 +v 0.017196 1.040940 -0.196809 +v 0.049099 0.990215 -0.077476 +v 0.058745 1.425160 -0.065480 +v 0.044176 1.420980 -0.113954 +v 0.029162 1.405660 -0.160879 +v 0.014608 1.379890 -0.203341 +v 0.001196 1.344890 -0.239337 +v -0.010437 1.302290 -0.267200 +v -0.019757 1.254070 -0.285617 +v -0.026326 1.202490 -0.293728 +v -0.029840 1.149980 -0.291163 +v -0.030130 1.098970 -0.278025 +v -0.027223 1.052480 -0.253810 +v -0.020039 1.013150 -0.214907 +v 0.019034 0.951527 -0.070885 +v 0.029985 1.468400 -0.056071 +v 0.012969 1.463530 -0.112694 +v -0.004558 1.445640 -0.167478 +v -0.021544 1.415560 -0.217035 +v -0.037203 1.374700 -0.259059 +v -0.050786 1.324970 -0.291585 +v -0.061663 1.268690 -0.313079 +v -0.069336 1.208480 -0.322555 +v -0.073429 1.147180 -0.319552 +v -0.073770 1.087640 -0.304219 +v -0.070256 1.033350 -0.275927 +v -0.061596 0.988146 -0.229217 +v -0.013106 0.915316 -0.059222 +v -0.003809 1.507890 -0.045284 +v -0.023019 1.502390 -0.109231 +v -0.042808 1.482200 -0.171066 +v -0.061981 1.448250 -0.227007 +v -0.079650 1.402130 -0.274444 +v -0.094982 1.346000 -0.311152 +v -0.107268 1.282460 -0.335419 +v -0.115920 1.214500 -0.346110 +v -0.120547 1.145310 -0.342722 +v -0.120932 1.078100 -0.325417 +v -0.116995 1.016280 -0.294492 +v -0.107461 0.964739 -0.241724 +v -0.042141 1.543050 -0.033265 +v -0.063271 1.537000 -0.103604 +v -0.085032 1.514810 -0.171593 +v -0.106111 1.477490 -0.233102 +v -0.125537 1.426770 -0.285254 +v -0.142397 1.365050 -0.325617 +v -0.155905 1.295190 -0.352301 +v -0.165418 1.220480 -0.364053 +v -0.170504 1.144390 -0.360331 +v -0.170919 1.070500 -0.341306 +v -0.166589 1.002580 -0.307400 +v -0.157233 0.947279 -0.257910 +v -0.084454 1.573380 -0.020208 +v -0.107201 1.566870 -0.095908 +v -0.130608 1.542990 -0.169057 +v -0.153288 1.502830 -0.235229 +v -0.174189 1.448270 -0.291348 +v -0.192332 1.381860 -0.334774 +v -0.206856 1.306700 -0.363481 +v -0.217103 1.226310 -0.376123 +v -0.222575 1.144450 -0.372119 +v -0.223019 1.064950 -0.351648 +v -0.218259 0.992209 -0.314888 +v -0.208250 0.933985 -0.265962 +v -0.130133 1.598420 -0.006284 +v -0.154163 1.591540 -0.086247 +v -0.178875 1.566330 -0.163489 +v -0.202830 1.523920 -0.233369 +v -0.224903 1.466310 -0.292623 +v -0.244054 1.396180 -0.338481 +v -0.259394 1.316820 -0.368790 +v -0.270211 1.231930 -0.382151 +v -0.275987 1.145490 -0.377917 +v -0.276461 1.061530 -0.356297 +v -0.271561 0.984202 -0.318069 +v -0.262026 0.923353 -0.267949 +v -0.178512 1.617810 0.008292 +v -0.203468 1.610670 -0.074755 +v -0.229136 1.584490 -0.154970 +v -0.254011 1.540450 -0.227534 +v -0.276929 1.480630 -0.289072 +v -0.296814 1.407800 -0.336686 +v -0.312747 1.325390 -0.368168 +v -0.323972 1.237240 -0.382032 +v -0.330378 1.147320 -0.377776 +v -0.331394 1.060040 -0.355511 +v -0.325863 0.979694 -0.315919 +v -0.319650 0.913737 -0.262299 +v -0.280317 1.627610 -0.054322 +v -0.306652 1.600740 -0.136627 +v -0.332180 1.555560 -0.211096 +v -0.355698 1.494160 -0.274236 +v -0.376109 1.419430 -0.323103 +v -0.392458 1.334870 -0.355408 +v -0.403779 1.244400 -0.368931 +v -0.410778 1.151820 -0.363645 +v -0.413685 1.062510 -0.343441 +v -0.405714 0.980124 -0.300964 +v -0.394830 0.908428 -0.245869 +v -0.254700 1.634940 0.030935 +v -0.332647 1.639700 0.053801 +v -0.358181 1.632410 -0.031167 +v -0.384420 1.605640 -0.113176 +v -0.409852 1.560620 -0.187370 +v -0.455605 1.501640 -0.241539 +v -0.469596 1.435120 -0.285313 +v -0.487731 1.361430 -0.309839 +v -0.481273 1.251120 -0.341698 +v -0.495294 1.162890 -0.342469 +v -0.499179 1.071130 -0.324260 +v -0.486893 0.988206 -0.278254 +v -0.472243 0.916280 -0.221780 +v -0.384532 1.634550 0.068822 +v -0.409503 1.627420 -0.014292 +v -0.435171 1.601230 -0.094507 +v -0.477967 1.551730 -0.173906 +v -0.531913 1.293920 -0.304605 +v -0.574434 1.201870 -0.303530 +v -0.577555 1.088810 -0.291971 +v -0.565010 1.003490 -0.248901 +v -0.544406 0.934897 -0.186510 +v -0.435401 1.623220 0.083413 +v -0.459453 1.616360 0.003354 +v -0.504154 1.581240 -0.099519 +v -0.484521 1.605890 0.097345 +v -0.507305 1.599390 0.021512 +v -0.543182 1.576140 -0.062314 +v -0.638033 1.195650 -0.253861 +v -0.639101 1.100600 -0.247226 +v -0.629010 1.022190 -0.209027 +v -0.607679 0.960876 -0.147059 +v -0.531171 1.582800 0.110416 +v -0.552347 1.576750 0.039921 +v -0.581900 1.558720 -0.028075 +v -0.672969 1.195730 -0.210355 +v -0.677537 1.112500 -0.200049 +v -0.673733 1.039200 -0.171459 +v -0.650430 0.981407 -0.115222 +v -0.574671 1.554290 0.122450 +v -0.593940 1.548790 0.058316 +v -0.621915 1.529820 -0.001035 +v -0.696977 1.198480 -0.165973 +v -0.708076 1.122690 -0.156490 +v -0.704413 1.060390 -0.128968 +v -0.685447 1.007250 -0.081991 +v -0.614382 1.520770 0.133260 +v -0.631457 1.515900 0.076422 +v -0.657800 1.494270 0.020437 +v -0.729933 1.135600 -0.101899 +v -0.726093 1.091230 -0.072301 +v -0.714208 1.026920 0.059369 +v -0.661158 1.463140 0.145560 +v -0.676306 1.456560 0.088737 +v -0.689370 1.446410 0.047313 +v -0.711249 1.402380 0.038720 +v -0.729303 1.354320 0.032537 +v -0.739550 1.298720 -0.009562 +v -0.738222 1.240510 -0.068876 +v -0.699913 1.401690 0.155436 +v -0.718760 1.387490 0.096907 +v 0.113759 1.176340 -0.087782 +v -0.633644 1.322440 -0.197795 +v -0.587861 1.447770 -0.194836 +v -0.670122 1.297640 -0.168798 +v -0.706155 1.388550 -0.058347 +v -0.634756 1.483830 -0.094952 +v -0.628195 1.331840 -0.174640 +v -0.653848 1.313230 -0.150306 +v -0.588061 1.417700 -0.168434 +v -0.601659 1.370600 -0.184575 +v -0.673103 1.313110 -0.121873 +v -0.598997 1.446270 -0.132097 +v -0.686945 1.336970 -0.096301 +v -0.626779 1.445730 -0.100134 +v -0.685944 1.377830 -0.079419 +v -0.661373 1.419720 -0.080323 +v -0.635846 1.334490 -0.177798 +v -0.661373 1.315230 -0.154970 +v -0.596002 1.423930 -0.173936 +v -0.680977 1.314720 -0.124801 +v -0.695345 1.338850 -0.096486 +v -0.633399 1.452410 -0.101906 +v -0.694233 1.382730 -0.077617 +v -0.668358 1.426310 -0.079953 +v -0.703049 1.281940 -0.127300 +v -0.725233 1.329830 -0.067081 +v -0.708876 1.397010 -0.038848 +v -0.573232 1.381680 -0.244749 +v -0.623546 1.311570 -0.227541 +v -0.673926 1.462790 -0.044134 +v -0.626852 1.515750 -0.081636 +v -0.585355 1.521520 -0.147897 +v -0.560087 1.473270 -0.216990 +v -0.674163 1.282030 -0.185732 +v -0.617689 1.387380 -0.169517 +v -0.601192 1.420660 -0.159826 +v -0.641066 1.356200 -0.164201 +v -0.659460 1.333900 -0.147222 +v -0.672903 1.329390 -0.128657 +v -0.682793 1.350090 -0.114955 +v -0.677633 1.383550 -0.107993 +v -0.656517 1.416850 -0.109157 +v -0.628610 1.439670 -0.117343 +v -0.606115 1.441970 -0.136175 +v -0.609733 1.373500 -0.188824 +v -0.608999 1.453300 -0.136701 +v -0.654233 1.320970 -0.149091 +v -0.641940 1.320520 -0.163667 +v -0.630381 1.339840 -0.171452 +v -0.605626 1.375760 -0.180038 +v -0.592072 1.394850 -0.180623 +v -0.591790 1.418150 -0.165439 +v -0.614122 1.348870 -0.181943 +v -0.672154 1.320190 -0.124453 +v -0.664087 1.310470 -0.136130 +v -0.590411 1.435720 -0.151678 +v -0.601096 1.444640 -0.133409 +v -0.685151 1.343060 -0.103248 +v -0.680821 1.321890 -0.108290 +v -0.611490 1.449740 -0.114258 +v -0.627831 1.443630 -0.105976 +v -0.682882 1.380590 -0.089658 +v -0.689548 1.356240 -0.086166 +v -0.643898 1.435340 -0.088650 +v -0.660150 1.418920 -0.090614 +v -0.675801 1.399480 -0.077291 +v -0.625229 1.433430 -0.132497 +v -0.610674 1.439460 -0.138533 +v -0.648184 1.411260 -0.131519 +v -0.668854 1.381870 -0.129695 +v -0.673889 1.336510 -0.131986 +v -0.667994 1.346350 -0.141328 +v -0.655909 1.372420 -0.149847 +v -0.634504 1.401090 -0.152998 +v -0.614471 1.425870 -0.149150 +v -0.695049 1.297050 -0.125735 +v -0.721177 1.363200 -0.047730 +v -0.715801 1.333930 -0.082043 +v -0.592932 1.342010 -0.238217 +v -0.599546 1.375790 -0.212023 +v -0.650801 1.493590 -0.059719 +v -0.673192 1.443950 -0.062766 +v -0.569459 1.504090 -0.185398 +v -0.605959 1.487290 -0.145628 +v -0.561303 1.429470 -0.237928 +v -0.652135 1.292830 -0.209902 +v -0.689562 1.277830 -0.157988 +v -0.692988 1.429880 -0.036957 +v -0.605470 1.526100 -0.111863 +v -0.717477 1.300250 -0.095671 +v -0.729533 1.316260 -0.043348 +v -0.713592 1.253400 -0.126099 +v -0.708958 1.402880 -0.008005 +v -0.604142 1.284850 -0.253476 +v -0.533566 1.373680 -0.280990 +v -0.601785 1.544540 -0.056561 +v -0.668521 1.481520 -0.015463 +v -0.504599 1.493210 -0.234570 +v -0.544777 1.551900 -0.140957 +v -0.668083 1.256100 -0.196379 +v -0.607657 1.404450 -0.166648 +v -0.629418 1.371000 -0.168738 +v -0.651157 1.343610 -0.156497 +v -0.666044 1.328320 -0.138036 +v -0.679094 1.337280 -0.120538 +v -0.682460 1.366220 -0.110736 +v -0.668721 1.400780 -0.107355 +v -0.642593 1.430150 -0.112634 +v -0.616205 1.444010 -0.124393 +v -0.600361 1.433450 -0.149402 +v -0.622167 1.351300 -0.185205 +v -0.600235 1.442530 -0.157061 +v -0.599835 1.399270 -0.185190 +v -0.649258 1.322800 -0.167923 +v -0.671776 1.312360 -0.140179 +v -0.689044 1.323350 -0.109928 +v -0.697696 1.359520 -0.085068 +v -0.683794 1.405730 -0.075586 +v -0.649874 1.442120 -0.089287 +v -0.619957 1.456110 -0.117513 +v -0.643364 1.328660 -0.161250 +v -0.596128 1.397500 -0.175893 +v -0.617458 1.355830 -0.178214 +v -0.663694 1.317910 -0.136738 +v -0.593318 1.434570 -0.150788 +v -0.679657 1.328940 -0.113154 +v -0.613218 1.447720 -0.117684 +v -0.686738 1.360940 -0.095144 +v -0.644194 1.433740 -0.096968 +v -0.673615 1.400390 -0.087849 +v -0.617844 1.440050 -0.131407 +v -0.636091 1.423650 -0.132305 +v -0.659438 1.396930 -0.130347 +v -0.674912 1.366730 -0.129257 +v -0.669492 1.337140 -0.138169 +v -0.663464 1.358610 -0.145969 +v -0.645685 1.386720 -0.152167 +v -0.623546 1.414470 -0.151878 +v -0.608028 1.433410 -0.146881 +v -0.707571 1.310990 -0.103129 +v -0.714393 1.360840 -0.066659 +v -0.615272 1.345310 -0.206551 +v -0.652209 1.467150 -0.076386 +v -0.595052 1.473310 -0.173142 +v -0.588328 1.412040 -0.208056 +v -0.653699 1.307060 -0.186303 +v -0.683045 1.293870 -0.147556 +v -0.691838 1.417040 -0.056301 +v -0.619527 1.490980 -0.118158 +v -0.726026 1.275660 -0.084757 +v -0.723965 1.360970 -0.017050 +v -0.565247 1.326140 -0.276949 +v -0.636929 1.516850 -0.030878 +v -0.520220 1.530090 -0.189305 +v -0.511249 1.442260 -0.268356 +v -0.639865 1.265080 -0.225154 +v -0.692202 1.252380 -0.165350 +v -0.690541 1.443170 -0.006929 +v -0.572187 1.556610 -0.095612 +v -0.677410 1.352820 -0.128723 +v -0.677907 1.342830 -0.127419 +v -0.716609 1.361110 0.159358 +v -0.723201 1.059690 0.152641 +v -0.740417 1.171570 0.161597 +v -0.752065 1.065250 0.162769 +v -0.798634 1.073080 0.176552 +v -0.913867 1.094570 0.211013 +v -0.883001 1.061640 0.200804 +v -0.792991 1.142950 0.176596 +v -0.835950 1.070640 0.187273 +v -0.841132 1.134610 0.189875 +v -0.853967 1.064880 0.192404 +v -0.743568 1.358830 0.167173 +v -0.789611 1.343050 0.180244 +v -0.829529 1.331260 0.191618 +v -0.849244 1.342480 0.197638 +v -0.869900 1.366830 0.204237 +v -0.904947 1.394330 0.215114 +v -0.937133 1.394470 0.224507 +v -0.949967 1.339680 0.226969 +v -0.954994 1.380230 0.229393 +v -0.920955 1.305360 0.217701 +v -0.883638 1.268320 0.205942 +v -0.856599 1.249600 0.197608 +v -0.814537 1.230620 0.184885 +v -0.748654 1.158130 0.163999 +v -0.750456 1.191620 0.164370 +v -0.773477 1.069010 0.169108 +v -0.904702 1.070670 0.207351 +v -0.768324 1.149440 0.169597 +v -0.818660 1.069770 0.182320 +v -0.818267 1.137090 0.183173 +v -0.875994 1.131720 0.200641 +v -0.728517 1.364630 0.164526 +v -0.766211 1.351680 0.173616 +v -0.810749 1.335600 0.186235 +v -0.841259 1.334780 0.195125 +v -0.857941 1.352220 0.200404 +v -0.955972 1.360050 0.229201 +v -0.937963 1.322090 0.223054 +v -0.901255 1.286040 0.211495 +v -0.870197 1.256940 0.201745 +v -0.837714 1.241690 0.191907 +v -0.778207 1.215290 0.173927 +v -0.909181 1.111960 0.210087 +v -0.725032 1.082230 0.020948 +v -0.752258 1.283800 0.027554 +v -0.730089 1.347230 0.116266 +v -0.749870 1.208080 -0.062003 +v -0.759205 1.175270 0.094261 +v -0.749633 1.165530 0.097389 +v -0.781707 1.271100 0.044585 +v -0.764373 1.098760 -0.033851 +v -0.758359 1.081570 0.044555 +v -0.817666 1.276210 0.059139 +v -0.766804 1.140450 -0.071975 +v -0.766345 1.202050 -0.060772 +v -0.823917 1.199360 -0.032813 +v -0.807256 1.087850 0.071291 +v -0.835075 1.176850 -0.024687 +v -0.829974 1.152770 -0.017673 +v -0.924261 1.110420 0.148022 +v -0.903783 1.106340 0.091577 +v -0.954037 1.294020 0.075814 +v -0.960984 1.367890 0.174832 +v -0.823405 1.195790 0.092659 +v -0.808413 1.153690 0.096537 +v -0.852625 1.245870 0.018108 +v -0.848272 1.292100 0.074805 +v -0.857451 1.178060 0.035258 +v -0.868232 1.214140 0.095276 +v -0.859097 1.188860 -0.003252 +v -0.871990 1.317760 0.091606 +v -0.887516 1.344160 0.111099 +v -0.901003 1.213410 0.057886 +v -0.942515 1.269910 0.096262 +v -0.899564 1.236240 0.102795 +v -0.927753 1.268380 0.118772 +v -0.908128 1.225360 0.029371 +v -0.848317 1.088560 0.096381 +v -0.860135 1.147460 0.027673 +v -0.865792 1.081620 0.110609 +v -0.907371 1.250970 0.025367 +v -0.730430 1.068750 0.103143 +v -0.750849 1.163660 0.090413 +v -0.759716 1.160370 0.099236 +v -0.746763 1.172260 0.133023 +v -0.766107 1.274950 0.038134 +v -0.758715 1.073380 0.109986 +v -0.742945 1.317870 0.068385 +v -0.776673 1.309200 0.079721 +v -0.799509 1.273830 0.051265 +v -0.764239 1.173640 -0.074970 +v -0.796098 1.203600 -0.048864 +v -0.795468 1.143050 -0.059066 +v -0.783479 1.084710 0.058005 +v -0.801169 1.081170 0.136233 +v -0.813351 1.306110 0.090620 +v -0.834830 1.161490 -0.026288 +v -0.830011 1.140260 -0.035037 +v -0.917610 1.086720 0.141260 +v -0.921266 1.098890 0.180081 +v -0.938845 1.316450 0.074546 +v -0.961799 1.340370 0.114887 +v -0.819698 1.177060 0.065760 +v -0.789833 1.186590 0.093363 +v -0.782737 1.157730 0.097916 +v -0.816235 1.160260 0.053727 +v -0.798693 1.144930 0.148934 +v -0.835372 1.280100 0.067851 +v -0.835372 1.168960 0.030972 +v -0.862686 1.193130 0.066583 +v -0.847701 1.204770 0.093734 +v -0.857155 1.178140 0.010983 +v -0.828336 1.160650 0.002435 +v -0.840376 1.183100 -0.012490 +v -0.824806 1.220500 0.115955 +v -0.871182 1.244050 0.120018 +v -0.880999 1.332290 0.102009 +v -0.922897 1.238180 0.072025 +v -0.934241 1.268200 0.108934 +v -0.915661 1.253000 0.110513 +v -0.899394 1.222500 0.079002 +v -0.904806 1.214590 0.040559 +v -0.934426 1.253580 0.045556 +v -0.950537 1.278610 0.084207 +v -0.866067 1.335980 0.115288 +v -0.885707 1.358520 0.129256 +v -0.898207 1.264650 0.126372 +v -0.929117 1.288170 0.139117 +v -0.950404 1.303440 0.132941 +v -0.829299 1.090110 0.083621 +v -0.856702 1.133760 0.020926 +v -0.839642 1.149270 0.007343 +v -0.832265 1.151220 0.103417 +v -0.857510 1.151290 0.073130 +v -0.841511 1.079940 0.146242 +v -0.846693 1.138300 0.160203 +v -0.886597 1.139500 0.056433 +v -0.881600 1.123140 0.052066 +v -0.876284 1.146160 0.084474 +v -0.911709 1.122570 0.101186 +v -0.892610 1.078960 0.127825 +v -0.844929 1.314250 0.102884 +v -0.884714 1.224120 0.097908 +v -0.884424 1.201480 0.010650 +v -0.859995 1.213230 -0.005602 +v -0.861099 1.304340 0.082605 +v -0.878345 1.188720 0.044251 +v -0.911968 1.334190 0.090101 +v -0.888191 1.282750 0.045667 +v -0.815976 1.228730 -0.008286 +v -0.769644 1.222050 -0.029432 +v -0.814612 1.109020 0.002969 +v -0.857392 1.107230 0.036363 +v -0.759894 1.341760 0.124407 +v -0.801251 1.328420 0.135803 +v -0.839442 1.324850 0.146398 +v -0.859460 1.340220 0.153694 +v -0.880213 1.363270 0.164059 +v -0.913103 1.385820 0.169330 +v -0.943976 1.383990 0.171755 +v -0.953962 1.327640 0.178798 +v -0.928436 1.300180 0.175358 +v -0.892928 1.269740 0.163569 +v -0.864413 1.249100 0.156051 +v -0.824754 1.230100 0.145108 +v -0.918960 1.124860 0.149156 +v -0.828751 1.156580 0.005445 +v -0.862805 1.151880 0.045000 +v -0.761644 1.165720 0.088314 +v -0.754185 1.158820 0.138516 +v -0.760488 1.194380 0.126624 +v -0.756543 1.314570 0.074183 +v -0.794986 1.176560 -0.067296 +v -0.790322 1.100080 -0.016598 +v -0.780128 1.076350 0.120211 +v -0.795268 1.307580 0.084778 +v -0.821877 1.171110 -0.046662 +v -0.913177 1.072670 0.176789 +v -0.943523 1.358040 0.113701 +v -0.791990 1.163970 0.077482 +v -0.774160 1.150640 0.143959 +v -0.841318 1.185710 0.063610 +v -0.833881 1.170340 0.003228 +v -0.850712 1.232250 0.118706 +v -0.918070 1.243320 0.090338 +v -0.929703 1.240470 0.055906 +v -0.875542 1.347820 0.123095 +v -0.913940 1.278080 0.132948 +v -0.940891 1.294640 0.138324 +v -0.836906 1.110700 0.020748 +v -0.836929 1.139090 -0.000968 +v -0.836402 1.155860 0.062298 +v -0.822292 1.081450 0.140185 +v -0.822626 1.141080 0.153812 +v -0.859475 1.074390 0.153597 +v -0.882578 1.134560 0.171125 +v -0.882163 1.140840 0.112463 +v -0.889763 1.066660 0.167455 +v -0.831168 1.306140 0.098160 +v -0.885233 1.253320 0.122287 +v -0.885596 1.227180 0.009901 +v -0.881236 1.205680 0.071432 +v -0.915424 1.365450 0.122591 +v -0.901959 1.307080 0.064655 +v -0.831835 1.233750 0.005660 +v -0.795957 1.227830 -0.019964 +v -0.743672 1.172950 -0.078803 +v -0.875631 1.100720 0.063758 +v -0.741744 1.347690 0.119588 +v -0.778467 1.335640 0.129456 +v -0.820565 1.323880 0.141586 +v -0.851416 1.331140 0.149897 +v -0.868728 1.351080 0.158513 +v -0.960880 1.347210 0.176366 +v -0.943716 1.312540 0.179443 +v -0.909752 1.284780 0.168885 +v -0.878886 1.258170 0.159581 +v -0.846456 1.240170 0.151551 +v -0.787980 1.215280 0.136567 +v -0.916357 1.115800 0.179377 +v -0.842971 1.155190 0.025011 +v -0.837426 1.206250 -0.017754 +v -0.885833 1.144510 0.069719 +v -0.880828 1.189760 0.022616 +v -0.924098 1.280850 0.044659 +v -0.909233 1.131980 0.108133 +v -0.958359 1.319120 0.124400 +v -0.888584 1.091040 0.092718 +v -0.756269 1.218570 -0.036572 +v -0.785266 1.204520 0.113783 +v -0.857325 1.324790 0.107807 +v -0.872109 1.262270 0.030335 +v -0.823865 1.157920 0.033997 +v -0.855323 1.146830 0.107629 +v -0.885351 1.139060 0.143610 +v -0.851809 1.141350 0.135766 +v -0.827372 1.146180 0.130324 +v -0.803171 1.148150 0.124815 +v -0.778096 1.152650 0.120804 +v -0.757054 1.158950 0.117089 +v -0.747512 1.169600 0.112522 +v -0.757440 1.187320 0.108711 +v -0.698430 1.418360 0.093082 +v -0.679679 1.431890 0.150231 +v -0.728213 1.199410 -0.109157 +v -0.716506 1.049860 -0.006840 +v -0.694560 1.007270 0.144700 +v -0.739950 1.142900 -0.069995 +v -0.731142 1.108500 -0.035630 +v -0.729748 1.362880 0.106843 +v -0.713295 1.377320 0.158765 +v -0.713963 1.041770 0.151099 +v -0.741581 1.331030 0.052763 +v -0.752109 1.288250 0.014001 +v -0.750730 1.221230 -0.062114 +v -0.742901 1.186160 -0.088383 +v -0.740083 1.141330 -0.079930 +v -0.734597 1.103470 -0.049183 +v -0.728198 1.069300 0.009679 +v -0.824554 1.132900 -0.021817 +v -0.795720 1.115210 -0.032642 +v -0.801607 1.123040 -0.041465 +v 0.101429 1.132540 -0.107089 +v -0.727842 1.053820 0.072581 +v 0.058308 1.049900 -0.140223 +v 0.028562 1.011430 -0.153198 +v -0.006908 0.979167 -0.163207 +v -0.046730 0.948688 -0.169057 +v -0.663361 0.952358 -0.001532 +v 0.118920 1.226450 -0.076727 +v 0.121529 1.222740 -0.066800 +v 0.123643 1.216850 -0.057806 +v 0.125340 1.209090 -0.050184 +v 0.126260 1.199780 -0.044290 +v 0.126460 1.189380 -0.040405 +v 0.125956 1.178400 -0.038714 +v 0.124777 1.167340 -0.039285 +v 0.122953 1.156740 -0.042080 +v 0.120595 1.147070 -0.047003 +v 0.117659 1.138620 -0.054336 +v 0.116777 1.276720 -0.063856 +v 0.121989 1.269340 -0.044090 +v 0.126304 1.257650 -0.026199 +v 0.129514 1.242190 -0.011030 +v 0.131465 1.223690 0.000715 +v 0.132072 1.203010 0.008485 +v 0.131242 1.181130 0.011881 +v 0.128996 1.159100 0.010739 +v 0.125437 1.137980 0.005119 +v 0.120803 1.118760 -0.004683 +v 0.115131 1.102010 -0.019067 +v 0.107279 1.086840 -0.043556 +v 0.108109 1.325960 -0.049450 +v 0.115843 1.315020 -0.020157 +v 0.122226 1.297690 0.006350 +v 0.126979 1.274790 0.028830 +v 0.129870 1.247380 0.046231 +v 0.130768 1.216740 0.057745 +v 0.129633 1.184310 0.062824 +v 0.126504 1.151600 0.061237 +v 0.121492 1.120180 0.053008 +v 0.114627 1.091680 0.038394 +v 0.106360 1.066940 0.017434 +v 0.093274 1.373520 -0.033762 +v 0.103401 1.359190 0.004629 +v 0.111772 1.336480 0.039373 +v 0.118000 1.306460 0.068830 +v 0.121796 1.270540 0.091636 +v 0.122968 1.230390 0.106717 +v 0.121477 1.187890 0.113375 +v 0.117385 1.145020 0.111299 +v 0.110883 1.103790 0.100578 +v 0.101881 1.066490 0.081286 +v 0.091138 1.034150 0.053949 +v 0.072476 1.418710 -0.017013 +v 0.084858 1.401190 0.029919 +v 0.095090 1.373440 0.072381 +v 0.102704 1.336740 0.108392 +v 0.107339 1.292830 0.136270 +v 0.108777 1.243750 0.154702 +v 0.106953 1.191790 0.162843 +v 0.101948 1.139400 0.160300 +v 0.093800 1.089160 0.146924 +v 0.082404 1.044100 0.122576 +v 0.068117 1.005500 0.086587 +v 0.046030 1.460870 0.000544 +v 0.060480 1.440420 0.055328 +v 0.072432 1.408020 0.104893 +v 0.081314 1.365190 0.146932 +v 0.086727 1.313930 0.179473 +v 0.088402 1.256640 0.200997 +v 0.086274 1.195990 0.210494 +v 0.080432 1.134820 0.207529 +v 0.070860 1.076250 0.191759 +v 0.057796 1.023530 0.163577 +v 0.041270 0.978226 0.122769 +v 0.014311 1.499390 0.018657 +v 0.030623 1.476310 0.080492 +v 0.044109 1.439740 0.136441 +v 0.054141 1.391390 0.183892 +v 0.060250 1.333530 0.220622 +v 0.062141 1.268860 0.244919 +v 0.059739 1.200400 0.255640 +v 0.053140 1.131360 0.252289 +v 0.042538 1.065090 0.234776 +v 0.028147 1.005140 0.203577 +v 0.008573 0.953878 0.159247 +v -0.022211 1.533710 0.037059 +v -0.004276 1.508320 0.105049 +v 0.010552 1.468120 0.166565 +v 0.021585 1.414960 0.218739 +v 0.028295 1.351340 0.259124 +v 0.030378 1.280240 0.285838 +v 0.027731 1.204970 0.297627 +v 0.020488 1.129060 0.293942 +v 0.008973 1.056060 0.274954 +v -0.006582 0.989955 0.241115 +v -0.026697 0.937269 0.197623 +v -0.063004 1.563320 0.055484 +v -0.043705 1.536020 0.128633 +v -0.027750 1.492750 0.194821 +v -0.015887 1.435560 0.250954 +v -0.008665 1.367110 0.294409 +v -0.006426 1.290610 0.323147 +v -0.009266 1.209630 0.335833 +v -0.017066 1.127960 0.331866 +v -0.029455 1.049410 0.311440 +v -0.046434 0.979398 0.274910 +v -0.066934 0.928958 0.234650 +v -0.107483 1.587800 0.073664 +v -0.087101 1.558960 0.150906 +v -0.070256 1.513280 0.220800 +v -0.057718 1.452890 0.280077 +v -0.050096 1.380610 0.325957 +v -0.047731 1.299830 0.356311 +v -0.050734 1.214310 0.369701 +v -0.058971 1.128070 0.365512 +v -0.072050 1.045130 0.343944 +v -0.090549 0.972925 0.305560 +v -0.112406 0.925807 0.269734 +v -0.154979 1.606780 0.091332 +v -0.133818 1.576840 0.171547 +v -0.116321 1.529400 0.244125 +v -0.103309 1.466690 0.305671 +v -0.095390 1.391630 0.353323 +v -0.092936 1.307750 0.384834 +v -0.096057 1.218950 0.398743 +v -0.104606 1.129390 0.394398 +v -0.118189 1.043260 0.372000 +v -0.140588 0.969514 0.333467 +v -0.166434 0.924725 0.299718 +v -0.230545 1.623620 0.116177 +v -0.208829 1.592890 0.198483 +v -0.190871 1.544210 0.272967 +v -0.177525 1.479860 0.336129 +v -0.169392 1.402840 0.385019 +v -0.166871 1.316750 0.417360 +v -0.170074 1.225630 0.431632 +v -0.178853 1.133730 0.427169 +v -0.192791 1.045350 0.404185 +v -0.212106 0.970107 0.362628 +v -0.234200 0.927149 0.324103 +v -0.308573 1.628430 0.138754 +v -0.286938 1.597810 0.220763 +v -0.269055 1.549310 0.294973 +v -0.279242 1.487490 0.362635 +v -0.268335 1.418960 0.404148 +v -0.271308 1.344030 0.431558 +v -0.248332 1.231900 0.453060 +v -0.257073 1.140330 0.448611 +v -0.270960 1.052270 0.425709 +v -0.289770 0.977766 0.383173 +v -0.310100 0.931798 0.337501 +v -0.360984 1.623520 0.151936 +v -0.339816 1.593580 0.232144 +v -0.333826 1.540150 0.319877 +v -0.312043 1.277970 0.451888 +v -0.412721 1.612610 0.163458 +v -0.395527 1.572520 0.272589 +v -0.341114 1.180810 0.467154 +v -0.352740 1.069580 0.447195 +v -0.368836 0.993018 0.394702 +v -0.389366 0.942630 0.337738 +v -0.463034 1.595830 0.173163 +v -0.448502 1.568540 0.262038 +v -0.425748 1.175210 0.466924 +v -0.429789 1.085910 0.452964 +v -0.445010 1.009510 0.406187 +v -0.472910 0.948665 0.334268 +v -0.511197 1.573450 0.180904 +v -0.499772 1.552130 0.253275 +v -0.482059 1.177080 0.457612 +v -0.487168 1.099610 0.440908 +v -0.503568 1.025090 0.400270 +v -0.534827 0.967468 0.329174 +v -0.556498 1.545780 0.186568 +v -0.548409 1.523920 0.250769 +v -0.527864 1.182090 0.432581 +v -0.536228 1.113510 0.412941 +v -0.552413 1.046560 0.380407 +v -0.587364 0.994700 0.318202 +v -0.598286 1.513230 0.190083 +v -0.590656 1.488880 0.250487 +v -0.586371 1.126470 0.372162 +v -0.600213 1.083580 0.343455 +v -0.667364 1.012150 0.232055 +v -0.643461 1.453930 0.201264 +v -0.632939 1.441710 0.242324 +v -0.646093 1.395630 0.258976 +v -0.660209 1.342400 0.272240 +v -0.648265 1.281370 0.310498 +v -0.614092 1.213440 0.360211 +v -0.684454 1.382920 0.215291 +v -0.454530 1.308090 0.415454 +v -0.416524 1.434010 0.392100 +v -0.501010 1.284330 0.409145 +v -0.590129 1.379570 0.337931 +v -0.509314 1.473760 0.334765 +v -0.462433 1.318540 0.392708 +v -0.497303 1.300610 0.384737 +v -0.431271 1.405110 0.368678 +v -0.434601 1.357180 0.387740 +v -0.529147 1.301380 0.370872 +v -0.459668 1.435090 0.345197 +v -0.554430 1.326360 0.357653 +v -0.500284 1.435570 0.333201 +v -0.562208 1.367840 0.344582 +v -0.540432 1.410010 0.334001 +v -0.467260 1.320910 0.399847 +v -0.501181 1.302450 0.393182 +v -0.434927 1.410990 0.377857 +v -0.534219 1.302910 0.377879 +v -0.561362 1.328070 0.362509 +v -0.504828 1.442080 0.338546 +v -0.570422 1.372900 0.348170 +v -0.546430 1.416530 0.337723 +v -0.550842 1.269610 0.390439 +v -0.600643 1.318070 0.352485 +v -0.603282 1.388730 0.324200 +v -0.378104 1.365990 0.423736 +v -0.429500 1.296200 0.436504 +v -0.570126 1.454490 0.312322 +v -0.509410 1.506320 0.320693 +v -0.438723 1.509750 0.354420 +v -0.380876 1.458890 0.396926 +v -0.495108 1.268020 0.426850 +v -0.456050 1.374390 0.384211 +v -0.446923 1.408280 0.368618 +v -0.478930 1.343160 0.391277 +v -0.503776 1.321340 0.386198 +v -0.525225 1.317500 0.377278 +v -0.540743 1.338690 0.371666 +v -0.539713 1.372480 0.364482 +v -0.520865 1.405960 0.355540 +v -0.492647 1.428750 0.348407 +v -0.463531 1.430520 0.352278 +v -0.439086 1.359780 0.395785 +v -0.465533 1.441790 0.354761 +v -0.498356 1.308460 0.384441 +v -0.479983 1.307600 0.390142 +v -0.466052 1.326560 0.391566 +v -0.440339 1.362490 0.386168 +v -0.428387 1.381700 0.380111 +v -0.436017 1.405640 0.368188 +v -0.446634 1.335350 0.391929 +v -0.526931 1.308510 0.373023 +v -0.513763 1.298450 0.378198 +v -0.442029 1.423820 0.356615 +v -0.460758 1.433370 0.347362 +v -0.549106 1.332140 0.362776 +v -0.542990 1.310780 0.363859 +v -0.479753 1.439170 0.337041 +v -0.498059 1.433210 0.338591 +v -0.554037 1.370250 0.351736 +v -0.561837 1.345990 0.351328 +v -0.521021 1.425470 0.332281 +v -0.533870 1.408780 0.341972 +v -0.554482 1.389730 0.338331 +v -0.481733 1.421900 0.359091 +v -0.466141 1.427850 0.356607 +v -0.501892 1.399520 0.369649 +v -0.520658 1.369970 0.377961 +v -0.524194 1.324470 0.380845 +v -0.514089 1.333980 0.385931 +v -0.499030 1.359830 0.387555 +v -0.478930 1.388610 0.379940 +v -0.463798 1.413770 0.366995 +v -0.545637 1.284860 0.385583 +v -0.608235 1.353050 0.335811 +v -0.585718 1.323820 0.360552 +v -0.398159 1.326270 0.428845 +v -0.417955 1.361190 0.410087 +v -0.541663 1.484830 0.314153 +v -0.559539 1.434770 0.326654 +v -0.405373 1.490920 0.376693 +v -0.457748 1.475390 0.362109 +v -0.371201 1.414210 0.413319 +v -0.463145 1.278040 0.436823 +v -0.523171 1.264770 0.409857 +v -0.591168 1.421330 0.315495 +v -0.474993 1.515620 0.335084 +v -0.582159 1.289070 0.373334 +v -0.619319 1.302800 0.335996 +v -0.559843 1.242140 0.398068 +v -0.619357 1.395120 0.298517 +v -0.400547 1.271590 0.448752 +v -0.325262 1.356930 0.432641 +v -0.501396 1.536480 0.287343 +v -0.580699 1.474570 0.286001 +v -0.324395 1.478740 0.382750 +v -0.407865 1.540900 0.328062 +v -0.485485 1.238910 0.442502 +v -0.448917 1.391710 0.377145 +v -0.466541 1.357910 0.389245 +v -0.491683 1.330760 0.389875 +v -0.514356 1.316090 0.381690 +v -0.534767 1.325690 0.373890 +v -0.542530 1.354980 0.368633 +v -0.532321 1.389830 0.359892 +v -0.507090 1.419260 0.351558 +v -0.478345 1.432940 0.347866 +v -0.451675 1.421520 0.359951 +v -0.451713 1.337540 0.399113 +v -0.447331 1.430250 0.366728 +v -0.432421 1.385830 0.388326 +v -0.483979 1.309670 0.398120 +v -0.518085 1.300090 0.386079 +v -0.548980 1.312140 0.369842 +v -0.569414 1.349260 0.355317 +v -0.562052 1.395950 0.341460 +v -0.525633 1.432140 0.336329 +v -0.485055 1.445280 0.344611 +v -0.482504 1.315670 0.389341 +v -0.434311 1.384510 0.378428 +v -0.451446 1.342460 0.390580 +v -0.513088 1.305730 0.378872 +v -0.444980 1.422670 0.357379 +v -0.539312 1.317670 0.367632 +v -0.479390 1.436980 0.340771 +v -0.554586 1.350330 0.357586 +v -0.516817 1.423510 0.339384 +v -0.546942 1.390210 0.346087 +v -0.476009 1.428670 0.354487 +v -0.491112 1.412010 0.364341 +v -0.512183 1.385110 0.374098 +v -0.526196 1.354790 0.380192 +v -0.517129 1.324880 0.383810 +v -0.507646 1.346090 0.387822 +v -0.488984 1.374140 0.384633 +v -0.470130 1.402150 0.373683 +v -0.459490 1.421480 0.361946 +v -0.568747 1.300030 0.373653 +v -0.592710 1.351220 0.348148 +v -0.434222 1.330630 0.413875 +v -0.534219 1.457680 0.327803 +v -0.433933 1.460370 0.378806 +v -0.410259 1.397750 0.401938 +v -0.477811 1.293120 0.415937 +v -0.523542 1.281200 0.397631 +v -0.579468 1.407520 0.330391 +v -0.483905 1.480090 0.346435 +v -0.594660 1.261010 0.368574 +v -0.627690 1.349180 0.311499 +v -0.353370 1.309170 0.447544 +v -0.545184 1.509490 0.283414 +v -0.361436 1.517340 0.354628 +v -0.312473 1.426300 0.412578 +v -0.444825 1.247500 0.450680 +v -0.522230 1.237670 0.420800 +v -0.603461 1.437350 0.290309 +v -0.455294 1.547230 0.304826 +v -0.528762 1.340880 0.380496 +v -0.529993 1.330950 0.379288 +v -0.654323 1.075280 0.269171 +v -0.679487 1.268330 0.285074 +v -0.705081 1.343190 0.202732 +v -0.626749 1.196160 0.357438 +v -0.726589 1.172780 0.230980 +v -0.722444 1.164420 0.223254 +v -0.713889 1.257950 0.283703 +v -0.655901 1.090290 0.335959 +v -0.693603 1.076370 0.266398 +v -0.750515 1.267570 0.292103 +v -0.635342 1.131200 0.369642 +v -0.642148 1.192060 0.364696 +v -0.705985 1.189580 0.371681 +v -0.749121 1.083190 0.270453 +v -0.719998 1.167420 0.369872 +v -0.719612 1.143910 0.360381 +v -0.888094 1.105780 0.269527 +v -0.841169 1.101310 0.306072 +v -0.872695 1.287490 0.354465 +v -0.930867 1.365470 0.278016 +v -0.775760 1.189130 0.266205 +v -0.762215 1.149810 0.253972 +v -0.756780 1.238170 0.346450 +v -0.783012 1.286860 0.298368 +v -0.770845 1.170970 0.331740 +v -0.812906 1.208220 0.288656 +v -0.751464 1.180210 0.365460 +v -0.811720 1.312930 0.298079 +v -0.834949 1.339940 0.291154 +v -0.819364 1.206850 0.337567 +v -0.874282 1.264440 0.330020 +v -0.842007 1.231620 0.299962 +v -0.873956 1.264050 0.303062 +v -0.809881 1.217470 0.365927 +v -0.797217 1.084460 0.271440 +v -0.768458 1.137370 0.337746 +v -0.819683 1.077920 0.268548 +v -0.806767 1.242890 0.369998 +v -0.701714 1.066450 0.201493 +v -0.720220 1.162410 0.229037 +v -0.728888 1.158840 0.226383 +v -0.733892 1.171410 0.190068 +v -0.697533 1.258950 0.281175 +v -0.729185 1.071010 0.211147 +v -0.691965 1.304840 0.248144 +v -0.726997 1.298970 0.254795 +v -0.731742 1.262940 0.288100 +v -0.631531 1.163580 0.373542 +v -0.673688 1.193590 0.370517 +v -0.667935 1.132860 0.375877 +v -0.721970 1.079770 0.268719 +v -0.778994 1.079390 0.212215 +v -0.763201 1.298800 0.264826 +v -0.719026 1.152250 0.370598 +v -0.710463 1.130660 0.374483 +v -0.879791 1.083690 0.270824 +v -0.903568 1.097870 0.241197 +v -0.858919 1.310030 0.348340 +v -0.899668 1.335380 0.327744 +v -0.753807 1.172610 0.286335 +v -0.742589 1.182730 0.248974 +v -0.745081 1.155550 0.239173 +v -0.747186 1.152010 0.292771 +v -0.782730 1.144140 0.202702 +v -0.769377 1.273370 0.295447 +v -0.749877 1.161900 0.323036 +v -0.792280 1.186100 0.308741 +v -0.797455 1.195900 0.277520 +v -0.757618 1.170150 0.351966 +v -0.728947 1.152680 0.342906 +v -0.730867 1.174450 0.362917 +v -0.788617 1.221500 0.251110 +v -0.827305 1.240790 0.270743 +v -0.824725 1.327770 0.294795 +v -0.845114 1.231930 0.338509 +v -0.874141 1.263380 0.314821 +v -0.859520 1.248490 0.302847 +v -0.829247 1.216870 0.319314 +v -0.813232 1.207240 0.354257 +v -0.840399 1.246030 0.367655 +v -0.874445 1.272500 0.344856 +v -0.819223 1.332220 0.275740 +v -0.843016 1.355090 0.275510 +v -0.853181 1.261040 0.280604 +v -0.885803 1.284690 0.287521 +v -0.900231 1.299410 0.304819 +v -0.774300 1.085700 0.272025 +v -0.763149 1.126250 0.341430 +v -0.741210 1.141130 0.344307 +v -0.785236 1.147520 0.260741 +v -0.792235 1.148610 0.297308 +v -0.818400 1.078090 0.225419 +v -0.828432 1.134770 0.217471 +v -0.806945 1.131980 0.327484 +v -0.801006 1.116670 0.328137 +v -0.814167 1.144450 0.297768 +v -0.852891 1.116530 0.301957 +v -0.851586 1.075670 0.268363 +v -0.795016 1.310240 0.273879 +v -0.827149 1.219010 0.295647 +v -0.780128 1.193110 0.367914 +v -0.750649 1.204450 0.368974 +v -0.797863 1.299260 0.299221 +v -0.793229 1.181870 0.335803 +v -0.844402 1.328770 0.321553 +v -0.801110 1.275760 0.343959 +v -0.712569 1.219890 0.347844 +v -0.662834 1.209580 0.339154 +v -0.718315 1.101290 0.332852 +v -0.772358 1.100400 0.327655 +v -0.734782 1.338840 0.211310 +v -0.776228 1.324640 0.222276 +v -0.813655 1.322770 0.234746 +v -0.834260 1.338200 0.240033 +v -0.857036 1.361410 0.243458 +v -0.887308 1.383750 0.257679 +v -0.914667 1.381630 0.272159 +v -0.927583 1.325520 0.269171 +v -0.904562 1.298270 0.257152 +v -0.868669 1.267800 0.246669 +v -0.840844 1.247210 0.236771 +v -0.801888 1.228340 0.223988 +v -0.884224 1.119800 0.266420 +v -0.730971 1.148720 0.340422 +v -0.780365 1.142570 0.324652 +v -0.726559 1.163660 0.236348 +v -0.740565 1.157380 0.188756 +v -0.743546 1.193710 0.203332 +v -0.706786 1.302520 0.249797 +v -0.663234 1.165590 0.384240 +v -0.687427 1.091820 0.335877 +v -0.752695 1.074150 0.214172 +v -0.745162 1.299230 0.260103 +v -0.697229 1.160440 0.380771 +v -0.895331 1.071240 0.237920 +v -0.883401 1.353200 0.319677 +v -0.741388 1.159500 0.260645 +v -0.759612 1.149480 0.194391 +v -0.771416 1.181140 0.300044 +v -0.733922 1.162310 0.345642 +v -0.809955 1.231210 0.261556 +v -0.850815 1.237930 0.320715 +v -0.842148 1.233440 0.355836 +v -0.831264 1.344270 0.274776 +v -0.869811 1.274540 0.284118 +v -0.895220 1.290980 0.294795 +v -0.746652 1.103460 0.329938 +v -0.734634 1.130870 0.349460 +v -0.768228 1.148880 0.295521 +v -0.798915 1.079560 0.220252 +v -0.806122 1.140730 0.210487 +v -0.837573 1.072620 0.228645 +v -0.864999 1.131950 0.228837 +v -0.833651 1.136980 0.276845 +v -0.870649 1.065130 0.232930 +v -0.781729 1.300270 0.268719 +v -0.840191 1.249700 0.276585 +v -0.780395 1.218740 0.370279 +v -0.809955 1.199530 0.315310 +v -0.864391 1.361350 0.297404 +v -0.822626 1.300710 0.336418 +v -0.733188 1.226300 0.345204 +v -0.689993 1.215980 0.345597 +v -0.611201 1.162880 0.364563 +v -0.802549 1.094850 0.314116 +v -0.716743 1.343560 0.207640 +v -0.753384 1.332980 0.216337 +v -0.795527 1.320540 0.227740 +v -0.825547 1.329060 0.238513 +v -0.844528 1.349140 0.241434 +v -0.931861 1.344880 0.275777 +v -0.919487 1.310590 0.262461 +v -0.885515 1.282830 0.251888 +v -0.854827 1.256240 0.241983 +v -0.823397 1.238330 0.230520 +v -0.766708 1.214560 0.211288 +v -0.898690 1.114570 0.239848 +v -0.752791 1.144710 0.331310 +v -0.725418 1.197120 0.366595 +v -0.813907 1.138760 0.315584 +v -0.783679 1.181960 0.355399 +v -0.830864 1.273360 0.364037 +v -0.854308 1.125410 0.295180 +v -0.902145 1.314600 0.316971 +v -0.829166 1.086270 0.296277 +v -0.647835 1.205210 0.337531 +v -0.752428 1.208610 0.232937 +v -0.807976 1.320830 0.276852 +v -0.779572 1.254840 0.347339 +v -0.741796 1.150130 0.314161 +v -0.809021 1.146380 0.267177 +v -0.851483 1.133120 0.252630 +v -0.820676 1.140190 0.240982 +v -0.796802 1.143580 0.233746 +v -0.773173 1.146690 0.226413 +v -0.752124 1.151610 0.217041 +v -0.735880 1.158370 0.210027 +v -0.727523 1.168610 0.209560 +v -0.734137 1.185810 0.218902 +v -0.664762 1.416040 0.208641 +v -0.583546 1.187430 0.382809 +v -0.636573 1.031640 0.296181 +v -0.612017 1.134640 0.352759 +v -0.626030 1.100790 0.318602 +v -0.699438 1.358910 0.211621 +v -0.682393 1.316700 0.262038 +v -0.674378 1.271360 0.295202 +v -0.628091 1.206870 0.359907 +v -0.605767 1.176530 0.373935 +v -0.607761 1.132470 0.361998 +v -0.620921 1.095560 0.331525 +v -0.652417 1.059350 0.281842 +v -0.713073 1.123940 0.360092 +v -0.683164 1.106170 0.352945 +v -0.683445 1.113310 0.363747 +v 0.113552 1.131330 -0.066333 +v -0.684684 1.048050 0.225375 +v 0.094504 1.044010 -0.019541 +v 0.075605 1.004920 0.006275 +v 0.050686 0.972087 0.034694 +v 0.018849 0.939531 0.063855 +v -0.568080 0.933547 0.233279 +v -0.619223 0.966682 0.233508 +v -0.594081 1.405660 -0.196623 +v -0.604639 1.374640 -0.200420 +v -0.618763 1.348310 -0.195882 +v -0.634845 1.328570 -0.187674 +v -0.591931 1.435850 -0.184382 +v -0.651653 1.315030 -0.176961 +v -0.597648 1.457920 -0.165098 +v -0.665740 1.306540 -0.161776 +v -0.607479 1.470300 -0.141165 +v -0.677426 1.303220 -0.143856 +v -0.619742 1.473540 -0.117839 +v -0.687983 1.305810 -0.125261 +v -0.634081 1.468120 -0.098429 +v -0.698230 1.317360 -0.106896 +v -0.651046 1.454640 -0.082837 +v -0.706207 1.336360 -0.089984 +v -0.670790 1.435100 -0.071374 +v -0.706489 1.359760 -0.077083 +v -0.687916 1.411090 -0.066273 +v -0.700306 1.385290 -0.068787 +v -0.460899 1.314510 0.407647 +v -0.442964 1.334080 0.406498 +v -0.428521 1.360480 0.402939 +v -0.421344 1.391790 0.395132 +v -0.480895 1.301390 0.407032 +v -0.425725 1.422500 0.384974 +v -0.501107 1.293390 0.401160 +v -0.440636 1.445310 0.372763 +v -0.520814 1.290650 0.391855 +v -0.461640 1.458590 0.358431 +v -0.539920 1.293840 0.381720 +v -0.484484 1.462680 0.345523 +v -0.558478 1.306200 0.371918 +v -0.507075 1.457920 0.336656 +v -0.572832 1.326390 0.362405 +v -0.529926 1.444920 0.332066 +v -0.580113 1.350020 0.352811 +v -0.552984 1.425650 0.332185 +v -0.579772 1.375690 0.343714 +v -0.570771 1.401720 0.335929 +v -0.013818 0.689774 0.459569 +v -0.014789 0.650285 0.502728 +v -0.010964 0.639090 0.528700 +v -0.045989 0.099768 0.400908 +v -0.045662 0.114122 0.445415 +v -0.042793 0.140176 0.483510 +v -0.036239 0.175846 0.512515 +v -0.025518 0.216054 0.533934 +v -0.028180 0.259316 0.560077 +v -0.030196 0.283271 0.575284 +v -0.033214 0.318267 0.589081 +v -0.034549 0.356754 0.599047 +v -0.031420 0.396169 0.604303 +v -0.026356 0.436332 0.605215 +v -0.021514 0.476250 0.602798 +v -0.018074 0.514352 0.596459 +v -0.016428 0.549177 0.586553 +v -0.015449 0.581214 0.571999 +v -0.012461 0.612169 0.552299 +v -0.022026 0.728765 0.412252 +v -0.040999 0.758378 0.374454 +v -0.061870 0.785203 0.334350 +v -0.564936 0.874619 0.100800 +v -0.577629 0.856098 0.103506 +v -0.599983 0.840135 0.107406 +v -0.634852 0.820057 0.115406 +v -0.675801 0.791089 0.122650 +v 0.101652 1.082620 -0.062158 +v 0.110734 1.129650 -0.075763 +v 0.004814 0.918178 0.005030 +v 0.086200 1.037050 -0.047389 +v 0.064513 0.994990 -0.029506 +v 0.037318 0.956969 -0.009858 +v 0.601092 0.282359 -0.079278 +v 0.088499 1.083950 -0.104182 +v 0.104380 1.130060 -0.096753 +v -0.027386 0.925778 -0.109750 +v 0.067205 1.038920 -0.109765 +v 0.040121 0.996658 -0.113213 +v 0.008061 0.958341 -0.113643 +v -0.643527 0.948043 0.125052 +v -0.600369 0.914033 0.110824 +v 0.115390 0.101510 -0.199241 +v 0.054986 0.101436 -0.218177 +v 0.159268 0.101577 -0.163800 +v 0.193300 0.101651 -0.116357 +v 0.225381 0.101681 -0.044015 +v -0.327190 0.101303 -0.185309 +v -0.402660 0.101244 -0.136679 +v -0.142263 0.101377 -0.230462 +v -0.227994 0.101333 -0.217028 +v -0.027016 0.101370 -0.231159 +v -0.444157 0.101281 -0.075697 +v -0.068209 0.101370 -0.236438 +v -0.465807 0.101555 -0.008041 +v 0.227316 0.101651 0.087988 +v -0.465392 0.101481 0.083562 +v 0.065737 0.101422 0.300845 +v 0.125993 0.101510 0.282717 +v 0.184966 0.101644 0.256115 +v 0.216988 0.101659 0.205282 +v 0.216462 0.101607 0.142928 +v -0.406619 0.101362 0.219510 +v -0.329614 0.101496 0.271618 +v -0.229625 0.101599 0.318320 +v -0.138408 0.101637 0.336470 +v -0.021166 0.101488 0.324430 +v -0.447679 0.101347 0.155147 +v -0.060432 0.101488 0.325260 +v -0.125907 0.101733 0.038053 +v -0.591887 0.918044 -0.089524 +v 0.371383 0.889529 0.029771 +v 0.318022 0.100858 0.019710 +v 0.251531 0.101622 0.022068 +v 0.489670 0.943801 0.034331 +v 0.427435 0.927505 0.032448 +v 0.452458 0.145410 0.019814 +v 0.388006 0.116880 0.019443 +v 0.515643 0.182037 0.021067 +v 0.568395 0.229229 0.020429 +v 0.613273 0.281670 0.020007 +v 0.646830 0.341592 0.019280 +v 0.677244 0.426760 0.018895 +v 0.692985 0.520358 0.019836 +v 0.690782 0.590674 0.019858 +v 0.680128 0.665870 0.019636 +v 0.663149 0.754782 0.021186 +v 0.616254 0.866775 0.029586 +v 0.554464 0.924777 0.033871 +v 0.328632 0.835116 0.019525 +v 0.291664 0.803716 0.016796 +v 0.250219 0.786923 0.018220 +v 0.197310 0.779019 0.019443 +v 0.138211 0.783245 0.018998 +v -0.005996 0.810967 0.011139 +v -0.056880 0.830274 0.007276 +v -0.065095 0.844391 0.009671 +v -0.057251 0.862104 0.003317 +v -0.037381 0.885303 -0.011089 +v -0.004424 0.915093 -0.028201 +v 0.028213 0.951602 -0.040472 +v 0.056677 0.991208 -0.052742 +v 0.080350 1.034650 -0.063404 +v 0.097982 1.081280 -0.072242 +v 0.109333 1.129310 -0.080279 +v 0.267479 0.607542 -0.436698 +v 0.201774 0.638423 -0.425992 +v 0.084280 0.274619 -0.482347 +v -0.149477 0.257121 -0.483904 +v -0.030945 0.258960 -0.484757 +v -0.426645 0.365214 -0.480598 +v -0.477344 0.496380 -0.471827 +v -0.405551 0.622549 -0.452372 +v 0.204769 0.631994 0.503046 +v 0.276280 0.601306 0.511217 +v 0.083361 0.281588 0.557549 +v -0.416947 0.370493 0.556696 +v -0.461069 0.497648 0.549594 +v -0.396610 0.615616 0.529449 +v 0.282701 0.615994 -0.426785 +v 0.210960 0.645792 -0.417131 +v 0.106093 0.655757 -0.412676 +v -0.015805 0.668139 -0.412357 +v 0.206037 0.306107 -0.470640 +v 0.279498 0.364747 -0.466355 +v 0.320076 0.433195 -0.457562 +v 0.333289 0.501288 -0.448568 +v 0.098419 0.261273 -0.471530 +v 0.321367 0.564888 -0.438329 +v -0.401118 0.302660 -0.465139 +v -0.299349 0.259004 -0.465236 +v -0.164046 0.239305 -0.470581 +v -0.031234 0.240639 -0.472805 +v -0.468217 0.358170 -0.465762 +v -0.507757 0.422756 -0.463478 +v -0.520569 0.493051 -0.457651 +v -0.501277 0.563990 -0.447582 +v -0.450638 0.625551 -0.435645 +v -0.137088 0.687742 -0.411082 +v -0.264851 0.692584 -0.412994 +v -0.371171 0.669377 -0.423456 +v -0.015383 0.662289 0.487276 +v 0.111349 0.654890 0.486957 +v 0.217418 0.640098 0.492903 +v 0.292399 0.607943 0.502557 +v 0.337448 0.492984 0.523954 +v 0.322894 0.431438 0.531362 +v 0.278334 0.368602 0.538620 +v 0.203917 0.312343 0.542831 +v 0.100503 0.265855 0.545085 +v 0.328298 0.553737 0.514331 +v -0.027290 0.241781 0.548289 +v -0.158893 0.239164 0.547310 +v -0.295034 0.260458 0.540829 +v -0.396795 0.305618 0.538806 +v -0.462530 0.362100 0.537901 +v -0.500128 0.425536 0.535439 +v -0.511390 0.492547 0.529597 +v -0.493210 0.558341 0.520848 +v -0.446241 0.616202 0.510127 +v -0.371431 0.657944 0.498316 +v -0.267320 0.680261 0.487847 +v -0.136339 0.678667 0.486371 +vt 0.877975 0.397658 +vt 0.873951 0.397664 +vt 0.900103 0.397679 +vt 0.870831 0.397671 +vt 0.867889 0.397674 +vt 0.931273 0.397662 +vt 0.929288 0.397637 +vt 0.865490 0.397668 +vt 0.867711 0.397671 +vt 0.868707 0.397667 +vt 0.866606 0.398924 +vt 0.871384 0.397619 +vt 0.874160 0.398826 +vt 0.878048 0.397558 +vt 0.866060 0.397625 +vt 0.860570 0.399008 +vt 0.856321 0.399060 +vt 0.862375 0.397683 +vt 0.860024 0.397676 +vt 0.853776 0.399148 +vt 0.932361 0.401286 +vt 0.944998 0.401429 +vt 0.936097 0.404570 +vt 0.948825 0.405179 +vt 0.939922 0.398832 +vt 0.928618 0.398828 +vt 0.862287 0.401557 +vt 0.870712 0.401462 +vt 0.855482 0.401642 +vt 0.858360 0.405349 +vt 0.851030 0.405417 +vt 0.867250 0.404976 +vt 0.851008 0.401767 +vt 0.848050 0.401779 +vt 0.846214 0.405443 +vt 0.842460 0.405195 +vt 0.951482 0.401365 +vt 0.945825 0.398800 +vt 0.955814 0.405156 +vt 0.939709 0.408955 +vt 0.951716 0.409790 +vt 0.943792 0.414167 +vt 0.953940 0.414936 +vt 0.854643 0.410095 +vt 0.863781 0.409841 +vt 0.847079 0.410021 +vt 0.850892 0.415208 +vt 0.843668 0.414845 +vt 0.858319 0.415536 +vt 0.959154 0.409842 +vt 0.961426 0.415077 +vt 0.955396 0.420505 +vt 0.946551 0.419988 +vt 0.948394 0.426244 +vt 0.956245 0.426562 +vt 0.962774 0.420748 +vt 0.963626 0.426902 +vt 0.947821 0.439136 +vt 0.949156 0.432796 +vt 0.955188 0.438695 +vt 0.956416 0.432776 +vt 0.963377 0.433055 +vt 0.962074 0.438861 +vt 0.939469 0.450342 +vt 0.944673 0.445101 +vt 0.949616 0.449137 +vt 0.952905 0.444195 +vt 0.959891 0.444199 +vt 0.957210 0.449055 +vt 0.928445 0.457630 +vt 0.933041 0.454385 +vt 0.940767 0.456994 +vt 0.945196 0.453364 +vt 0.954246 0.453493 +vt 0.950166 0.457401 +vt 0.877438 0.456074 +vt 0.890333 0.455013 +vt 0.881771 0.458050 +vt 0.891902 0.457826 +vt 0.885457 0.459788 +vt 0.893433 0.460126 +vt 0.902160 0.458193 +vt 0.902038 0.460510 +vt 0.902093 0.455956 +vt 0.875073 0.458463 +vt 0.870949 0.459563 +vt 0.869348 0.456995 +vt 0.865831 0.459946 +vt 0.876379 0.459992 +vt 0.880020 0.459819 +vt 0.890696 0.462723 +vt 0.889158 0.462546 +vt 0.893089 0.462870 +vt 0.245158 0.423975 +vt 0.250110 0.468112 +vt 0.148569 0.498875 +vt 0.150442 0.543685 +vt 0.595914 0.442419 +vt 0.701364 0.364720 +vt 0.595493 0.387203 +vt 0.704143 0.312403 +vt 0.757075 0.288915 +vt 0.749567 0.262372 +vt 0.302841 0.416173 +vt 0.302959 0.373132 +vt 0.224027 0.289653 +vt 0.235580 0.359702 +vt 0.134156 0.320572 +vt 0.143764 0.429085 +vt 0.298228 0.319321 +vt 0.288073 0.263566 +vt 0.342229 0.232442 +vt 0.353643 0.285267 +vt 0.355112 0.336459 +vt 0.409212 0.301844 +vt 0.416748 0.239090 +vt 0.412385 0.174765 +vt 0.371257 0.129527 +vt 0.316583 0.183819 +vt 0.312010 0.090946 +vt 0.278107 0.146712 +vt 0.258385 0.177707 +vt 0.272238 0.213152 +vt 0.842115 0.409869 +vt 0.837426 0.409453 +vt 0.838646 0.414482 +vt 0.833434 0.414204 +vt 0.345064 0.636086 +vt 0.339502 0.673242 +vt 0.283208 0.642063 +vt 0.260986 0.672117 +vt 0.331126 0.714598 +vt 0.241009 0.715530 +vt 0.847163 0.420560 +vt 0.840709 0.419570 +vt 0.839445 0.430603 +vt 0.835030 0.428579 +vt 0.843659 0.433436 +vt 0.852464 0.422383 +vt 0.835601 0.419062 +vt 0.830596 0.418889 +vt 0.831257 0.427506 +vt 0.827802 0.427141 +vt 0.326898 0.757163 +vt 0.328393 0.797118 +vt 0.233372 0.762934 +vt 0.238410 0.805793 +vt 0.866399 0.449489 +vt 0.869211 0.447456 +vt 0.876435 0.450731 +vt 0.878828 0.448367 +vt 0.861778 0.461251 +vt 0.863290 0.455790 +vt 0.869445 0.454182 +vt 0.798670 0.019963 +vt 0.720188 0.090411 +vt 0.815355 0.094326 +vt 0.722678 0.141004 +vt 0.586596 0.164171 +vt 0.591566 0.221736 +vt 0.919848 0.462088 +vt 0.924400 0.459418 +vt 0.932799 0.462552 +vt 0.936505 0.460001 +vt 0.914853 0.457033 +vt 0.912969 0.458780 +vt 0.911051 0.461039 +vt 0.945951 0.460521 +vt 0.941970 0.463070 +vt 0.896594 0.462992 +vt 0.902819 0.462828 +vt 0.775513 0.183818 +vt 0.714664 0.225413 +vt 0.594393 0.311859 +vt 0.240207 0.064107 +vt 0.220228 0.127313 +vt 0.137886 0.045291 +vt 0.134162 0.116701 +vt 0.209620 0.176711 +vt 0.132836 0.176022 +vt 0.803506 0.201004 +vt 0.853165 0.100422 +vt 0.393776 0.367451 +vt 0.346805 0.380154 +vt 0.214240 0.228892 +vt 0.133514 0.241035 +vt 0.939147 0.464980 +vt 0.930869 0.464578 +vt 0.937502 0.466649 +vt 0.929700 0.466322 +vt 0.911763 0.464455 +vt 0.904647 0.464520 +vt 0.910929 0.462701 +vt 0.906026 0.466148 +vt 0.912450 0.466086 +vt 0.920833 0.464349 +vt 0.920890 0.466118 +vt 0.899670 0.464526 +vt 0.896490 0.464450 +vt 0.897930 0.466107 +vt 0.901270 0.466162 +vt 0.924570 0.397524 +vt 0.934172 0.397517 +vt 0.901960 0.397532 +vt 0.912366 0.397543 +vt 0.902477 0.398810 +vt 0.914428 0.398827 +vt 0.887934 0.397535 +vt 0.886084 0.398748 +vt 0.939158 0.397548 +vt 0.916553 0.401268 +vt 0.903345 0.401161 +vt 0.904419 0.404260 +vt 0.918892 0.404595 +vt 0.883770 0.401252 +vt 0.880927 0.404722 +vt 0.600870 0.640918 +vt 0.595916 0.679782 +vt 0.517366 0.634965 +vt 0.510699 0.676291 +vt 0.593633 0.724091 +vt 0.505796 0.719121 +vt 0.905488 0.424961 +vt 0.912357 0.424967 +vt 0.905294 0.428912 +vt 0.913340 0.429138 +vt 0.911087 0.422154 +vt 0.905906 0.421495 +vt 0.587548 0.772055 +vt 0.501279 0.762876 +vt 0.497144 0.804816 +vt 0.579229 0.819401 +vt 0.904076 0.437412 +vt 0.904780 0.433190 +vt 0.911884 0.438770 +vt 0.913199 0.433998 +vt 0.570764 0.860428 +vt 0.494669 0.842655 +vt 0.495808 0.877438 +vt 0.568980 0.893979 +vt 0.903210 0.444063 +vt 0.903361 0.441071 +vt 0.908067 0.444769 +vt 0.909872 0.442542 +vt 0.918142 0.453706 +vt 0.902496 0.453271 +vt 0.928783 0.450398 +vt 0.935730 0.445715 +vt 0.939896 0.439756 +vt 0.941415 0.433124 +vt 0.940295 0.426582 +vt 0.937084 0.420446 +vt 0.931618 0.414911 +vt 0.921769 0.409549 +vt 0.905101 0.407893 +vt 0.876573 0.409920 +vt 0.866307 0.415598 +vt 0.859425 0.421874 +vt 0.854503 0.429219 +vt 0.853866 0.435555 +vt 0.855666 0.441340 +vt 0.846009 0.439206 +vt 0.849189 0.444410 +vt 0.859693 0.446280 +vt 0.853635 0.449316 +vt 0.859396 0.453185 +vt 0.333511 0.830512 +vt 0.255441 0.838224 +vt 0.335539 0.859065 +vt 0.275714 0.857682 +vt 0.889827 0.451740 +vt 0.862926 0.421743 +vt 0.859205 0.428005 +vt 0.137030 0.645509 +vt 0.099669 0.709478 +vt 0.114209 0.644610 +vt 0.072436 0.710311 +vt 0.217381 0.600094 +vt 0.193334 0.589335 +vt 0.869662 0.416377 +vt 0.857993 0.434235 +vt 0.859086 0.440054 +vt 0.083907 0.774978 +vt 0.093256 0.834193 +vt 0.056643 0.778851 +vt 0.067488 0.842396 +vt 0.916008 0.412068 +vt 0.903601 0.410265 +vt 0.543475 0.557405 +vt 0.557800 0.533271 +vt 0.655545 0.572596 +vt 0.685834 0.551732 +vt 0.910530 0.415402 +vt 0.913091 0.413541 +vt 0.918257 0.418305 +vt 0.921836 0.416936 +vt 0.925340 0.416062 +vt 0.879532 0.412275 +vt 0.318062 0.571086 +vt 0.304302 0.551225 +vt 0.931494 0.421140 +vt 0.923547 0.422692 +vt 0.927682 0.421785 +vt 0.926515 0.428111 +vt 0.931209 0.427509 +vt 0.935120 0.427050 +vt 0.936295 0.433481 +vt 0.927320 0.434037 +vt 0.932331 0.433786 +vt 0.925669 0.439922 +vt 0.930432 0.440034 +vt 0.934525 0.439972 +vt 0.912845 0.451737 +vt 0.922595 0.449614 +vt 0.907361 0.448978 +vt 0.915126 0.447877 +vt 0.909842 0.450414 +vt 0.918684 0.448912 +vt 0.515560 0.943140 +vt 0.618068 0.949096 +vt 0.529717 0.959537 +vt 0.647404 0.965201 +vt 0.901128 0.451294 +vt 0.929882 0.445604 +vt 0.921412 0.444811 +vt 0.925747 0.445329 +vt 0.862632 0.444730 +vt 0.194162 0.909528 +vt 0.176891 0.924466 +vt 0.127886 0.880246 +vt 0.105635 0.892789 +vt 0.298575 0.918513 +vt 0.289302 0.932026 +vt 0.410040 0.927663 +vt 0.412647 0.940987 +vt 0.152917 0.714025 +vt 0.188039 0.660899 +vt 0.251002 0.622891 +vt 0.148083 0.821618 +vt 0.139091 0.770988 +vt 0.624566 0.605374 +vt 0.529124 0.594060 +vt 0.907910 0.418325 +vt 0.914417 0.420366 +vt 0.333860 0.602351 +vt 0.918293 0.424021 +vt 0.920590 0.428770 +vt 0.920879 0.434155 +vt 0.919487 0.439514 +vt 0.911345 0.446349 +vt 0.904990 0.446728 +vt 0.503730 0.914675 +vt 0.590033 0.923870 +vt 0.916084 0.443813 +vt 0.174218 0.860862 +vt 0.232830 0.882474 +vt 0.318561 0.890705 +vt 0.412205 0.900441 +vt 0.414872 0.864697 +vt 0.414169 0.832004 +vt 0.413463 0.796679 +vt 0.414763 0.757000 +vt 0.418623 0.715141 +vt 0.424042 0.673740 +vt 0.428561 0.633258 +vt 0.429426 0.594048 +vt 0.429051 0.559095 +vt 0.429259 0.535162 +vt 0.891421 0.410387 +vt 0.891335 0.407994 +vt 0.892300 0.404278 +vt 0.892667 0.401108 +vt 0.892803 0.398753 +vt 0.893101 0.397518 +vt 0.953337 0.401538 +vt 0.958091 0.405199 +vt 0.947500 0.399036 +vt 0.961860 0.409791 +vt 0.964499 0.415028 +vt 0.966120 0.420717 +vt 0.967305 0.426924 +vt 0.967388 0.433074 +vt 0.966427 0.438883 +vt 0.964510 0.444202 +vt 0.961861 0.448940 +vt 0.958944 0.453645 +vt 0.955428 0.457592 +vt 0.951844 0.460785 +vt 0.948132 0.463506 +vt 0.945008 0.465414 +vt 0.942800 0.466896 +vt 0.940897 0.397660 +vt 0.894646 0.464377 +vt 0.893748 0.464327 +vt 0.895819 0.465909 +vt 0.894732 0.465707 +vt 0.898046 0.468336 +vt 0.895591 0.467749 +vt 0.894211 0.467377 +vt 0.892528 0.469525 +vt 0.894053 0.470321 +vt 0.897020 0.472125 +vt 0.906461 0.468060 +vt 0.901761 0.468217 +vt 0.901883 0.471147 +vt 0.906501 0.470456 +vt 0.912592 0.467918 +vt 0.912253 0.470089 +vt 0.920674 0.470154 +vt 0.920682 0.467923 +vt 0.929876 0.470181 +vt 0.929300 0.468129 +vt 0.937020 0.468441 +vt 0.938213 0.470896 +vt 0.942275 0.468514 +vt 0.942757 0.470744 +vt 0.890006 0.449500 +vt 0.859393 0.397599 +vt 0.852976 0.399065 +vt 0.847065 0.401675 +vt 0.841270 0.405026 +vt 0.953466 0.401386 +vt 0.958188 0.405089 +vt 0.947588 0.398916 +vt 0.961862 0.409747 +vt 0.964391 0.415022 +vt 0.965926 0.420729 +vt 0.967068 0.426923 +vt 0.967242 0.433135 +vt 0.966314 0.439008 +vt 0.964406 0.444378 +vt 0.961584 0.449120 +vt 0.954279 0.457569 +vt 0.958267 0.453722 +vt 0.870463 0.459645 +vt 0.869977 0.459728 +vt 0.865611 0.460368 +vt 0.865391 0.460791 +vt 0.875883 0.460032 +vt 0.875386 0.460071 +vt 0.889107 0.462568 +vt 0.889055 0.462590 +vt 0.044392 0.517280 +vt 0.043999 0.560029 +vt 0.579657 0.457140 +vt 0.472222 0.467543 +vt 0.576570 0.399149 +vt 0.470583 0.402538 +vt 0.055194 0.327681 +vt 0.048040 0.442488 +vt 0.836433 0.409343 +vt 0.832578 0.414030 +vt 0.832317 0.414141 +vt 0.829542 0.419704 +vt 0.829240 0.419624 +vt 0.456894 0.171519 +vt 0.545104 0.178866 +vt 0.459246 0.232082 +vt 0.554780 0.242428 +vt 0.861810 0.461905 +vt 0.861843 0.462560 +vt 0.950530 0.460750 +vt 0.946775 0.463400 +vt 0.468306 0.320445 +vt 0.572701 0.325840 +vt 0.057144 0.041836 +vt 0.056343 0.115320 +vt 0.056323 0.176259 +vt 0.056512 0.244825 +vt 0.943577 0.465237 +vt 0.941527 0.466697 +vt 0.893773 0.464335 +vt 0.893798 0.464342 +vt 0.894526 0.465626 +vt 0.894385 0.465571 +vt 0.940912 0.397604 +vt 0.893807 0.467247 +vt 0.893563 0.467166 +vt 0.891985 0.469369 +vt 0.891535 0.469419 +vt 0.940762 0.470517 +vt 0.940364 0.468392 +vt 0.866236 0.398834 +vt 0.873711 0.398813 +vt 0.870971 0.397493 +vt 0.877506 0.397514 +vt 0.860032 0.398872 +vt 0.865522 0.397455 +vt 0.856026 0.399014 +vt 0.861954 0.397568 +vt 0.853853 0.399094 +vt 0.860159 0.397664 +vt 0.948546 0.405179 +vt 0.944496 0.401457 +vt 0.935431 0.404681 +vt 0.931632 0.401375 +vt 0.939488 0.398861 +vt 0.927974 0.398901 +vt 0.870657 0.401504 +vt 0.861966 0.401526 +vt 0.855096 0.401600 +vt 0.850751 0.405417 +vt 0.858121 0.405363 +vt 0.867731 0.405188 +vt 0.850561 0.401575 +vt 0.848094 0.401586 +vt 0.842791 0.404957 +vt 0.845766 0.405198 +vt 0.945500 0.398818 +vt 0.951158 0.401375 +vt 0.955535 0.405156 +vt 0.953600 0.414936 +vt 0.951437 0.409790 +vt 0.942950 0.414410 +vt 0.938847 0.409148 +vt 0.863906 0.410047 +vt 0.854469 0.410101 +vt 0.846800 0.410021 +vt 0.843291 0.414792 +vt 0.850618 0.415218 +vt 0.858112 0.415702 +vt 0.958875 0.409842 +vt 0.961147 0.415077 +vt 0.955100 0.420509 +vt 0.945887 0.420220 +vt 0.955942 0.426574 +vt 0.947818 0.426423 +vt 0.962495 0.420748 +vt 0.963347 0.426902 +vt 0.947179 0.438896 +vt 0.955415 0.438781 +vt 0.948355 0.432791 +vt 0.956270 0.432802 +vt 0.963455 0.433109 +vt 0.962550 0.439010 +vt 0.939730 0.449457 +vt 0.950281 0.449152 +vt 0.944309 0.444589 +vt 0.953464 0.444290 +vt 0.960647 0.444424 +vt 0.957836 0.449233 +vt 0.928866 0.456683 +vt 0.941589 0.457208 +vt 0.933669 0.453285 +vt 0.946319 0.453375 +vt 0.954103 0.453514 +vt 0.949874 0.457416 +vt 0.877353 0.455968 +vt 0.882269 0.458025 +vt 0.890577 0.455047 +vt 0.892317 0.457757 +vt 0.886684 0.460112 +vt 0.894230 0.460211 +vt 0.902677 0.460467 +vt 0.902670 0.458019 +vt 0.902808 0.455334 +vt 0.865825 0.458435 +vt 0.871446 0.458982 +vt 0.869597 0.456984 +vt 0.875200 0.458466 +vt 0.877063 0.460000 +vt 0.880928 0.460060 +vt 0.890358 0.462714 +vt 0.889224 0.462652 +vt 0.892541 0.462593 +vt 0.253473 0.420340 +vt 0.198325 0.471074 +vt 0.257191 0.464291 +vt 0.201641 0.514357 +vt 0.644363 0.422582 +vt 0.643061 0.366324 +vt 0.703499 0.366966 +vt 0.703370 0.314326 +vt 0.745969 0.267070 +vt 0.753820 0.293297 +vt 0.305266 0.371658 +vt 0.304409 0.414784 +vt 0.230885 0.286168 +vt 0.167912 0.306331 +vt 0.242536 0.354324 +vt 0.188981 0.401339 +vt 0.300813 0.317790 +vt 0.290984 0.262458 +vt 0.343946 0.231017 +vt 0.355397 0.284532 +vt 0.356783 0.335152 +vt 0.414764 0.292810 +vt 0.419941 0.236001 +vt 0.413771 0.173263 +vt 0.370421 0.125545 +vt 0.313385 0.086563 +vt 0.319456 0.180460 +vt 0.282902 0.142155 +vt 0.261679 0.175104 +vt 0.275739 0.211488 +vt 0.841603 0.409572 +vt 0.838159 0.409243 +vt 0.834209 0.413908 +vt 0.837981 0.414182 +vt 0.352720 0.639241 +vt 0.294558 0.645659 +vt 0.348213 0.675519 +vt 0.274212 0.673847 +vt 0.253789 0.714174 +vt 0.339072 0.715472 +vt 0.846514 0.420792 +vt 0.839885 0.419736 +vt 0.834481 0.428371 +vt 0.838869 0.430451 +vt 0.843430 0.433385 +vt 0.852102 0.422532 +vt 0.834732 0.419195 +vt 0.830666 0.418987 +vt 0.827717 0.426976 +vt 0.830662 0.427303 +vt 0.241492 0.801977 +vt 0.330569 0.796358 +vt 0.241562 0.759109 +vt 0.332348 0.756954 +vt 0.878346 0.448288 +vt 0.868619 0.446935 +vt 0.875777 0.450429 +vt 0.865577 0.448677 +vt 0.860982 0.459109 +vt 0.863516 0.455972 +vt 0.868771 0.453598 +vt 0.797949 0.022112 +vt 0.816816 0.093012 +vt 0.715991 0.092651 +vt 0.720595 0.147032 +vt 0.636546 0.210325 +vt 0.627672 0.148444 +vt 0.919955 0.462087 +vt 0.933215 0.462650 +vt 0.924520 0.459410 +vt 0.936971 0.460171 +vt 0.915797 0.455832 +vt 0.913434 0.458571 +vt 0.911682 0.460937 +vt 0.945768 0.460575 +vt 0.941689 0.463136 +vt 0.897291 0.462545 +vt 0.903757 0.462585 +vt 0.772502 0.189202 +vt 0.711355 0.231084 +vt 0.641706 0.289174 +vt 0.247515 0.059560 +vt 0.175872 0.043369 +vt 0.230266 0.122298 +vt 0.166908 0.113803 +vt 0.162066 0.173298 +vt 0.220575 0.173418 +vt 0.800674 0.205793 +vt 0.855305 0.094200 +vt 0.399643 0.347472 +vt 0.348371 0.378515 +vt 0.223314 0.226619 +vt 0.162858 0.235961 +vt 0.938505 0.464915 +vt 0.930828 0.464538 +vt 0.929084 0.466249 +vt 0.936418 0.466469 +vt 0.904992 0.464496 +vt 0.911714 0.462545 +vt 0.912260 0.464434 +vt 0.906001 0.466171 +vt 0.912549 0.466198 +vt 0.920438 0.466178 +vt 0.920682 0.464371 +vt 0.899694 0.464496 +vt 0.896238 0.464476 +vt 0.897673 0.466070 +vt 0.901142 0.466120 +vt 0.933851 0.397545 +vt 0.924135 0.397565 +vt 0.901567 0.397461 +vt 0.902111 0.398787 +vt 0.911927 0.397463 +vt 0.913998 0.398826 +vt 0.885568 0.398789 +vt 0.887707 0.397486 +vt 0.939027 0.397536 +vt 0.902897 0.401138 +vt 0.916139 0.401275 +vt 0.904075 0.404332 +vt 0.918631 0.404656 +vt 0.883077 0.401373 +vt 0.880245 0.404983 +vt 0.600135 0.641640 +vt 0.519216 0.636196 +vt 0.595756 0.680499 +vt 0.513483 0.677706 +vt 0.509018 0.719971 +vt 0.595108 0.724198 +vt 0.912942 0.429219 +vt 0.912006 0.425106 +vt 0.905419 0.428922 +vt 0.905474 0.425025 +vt 0.910791 0.422281 +vt 0.905844 0.421559 +vt 0.590769 0.769750 +vt 0.503982 0.762367 +vt 0.498249 0.802950 +vt 0.581926 0.813498 +vt 0.904305 0.436885 +vt 0.911907 0.438227 +vt 0.905052 0.432984 +vt 0.913047 0.433844 +vt 0.571758 0.851938 +vt 0.493564 0.840197 +vt 0.493370 0.874734 +vt 0.568900 0.884732 +vt 0.903203 0.443238 +vt 0.908124 0.443775 +vt 0.903445 0.440313 +vt 0.909903 0.441649 +vt 0.902850 0.452456 +vt 0.918742 0.452460 +vt 0.929033 0.449192 +vt 0.935553 0.444785 +vt 0.939489 0.439171 +vt 0.940895 0.433027 +vt 0.939916 0.426757 +vt 0.936762 0.420698 +vt 0.931211 0.415109 +vt 0.904610 0.407951 +vt 0.921344 0.409650 +vt 0.876099 0.410269 +vt 0.865754 0.416146 +vt 0.858856 0.422274 +vt 0.854080 0.429250 +vt 0.848700 0.443676 +vt 0.855252 0.440105 +vt 0.845714 0.439016 +vt 0.853622 0.434905 +vt 0.852614 0.447663 +vt 0.858958 0.445110 +vt 0.858176 0.451853 +vt 0.254385 0.836095 +vt 0.333265 0.830744 +vt 0.334347 0.859927 +vt 0.274272 0.857068 +vt 0.889824 0.451480 +vt 0.858947 0.427844 +vt 0.863033 0.422095 +vt 0.160452 0.654158 +vt 0.126019 0.649524 +vt 0.111374 0.707428 +vt 0.074711 0.707898 +vt 0.232092 0.611555 +vt 0.203545 0.598866 +vt 0.869857 0.416948 +vt 0.858451 0.439034 +vt 0.857612 0.433475 +vt 0.086444 0.767612 +vt 0.053832 0.770468 +vt 0.085639 0.828171 +vt 0.059040 0.833261 +vt 0.915613 0.412200 +vt 0.903128 0.410252 +vt 0.540600 0.554531 +vt 0.653253 0.571690 +vt 0.552741 0.532581 +vt 0.682275 0.551691 +vt 0.910336 0.415321 +vt 0.917730 0.418674 +vt 0.912790 0.413538 +vt 0.921258 0.417307 +vt 0.924944 0.416332 +vt 0.879341 0.412694 +vt 0.321928 0.578317 +vt 0.305300 0.558373 +vt 0.930972 0.421500 +vt 0.922517 0.423241 +vt 0.926792 0.422268 +vt 0.925060 0.428584 +vt 0.929895 0.427930 +vt 0.934420 0.427304 +vt 0.935453 0.433435 +vt 0.925801 0.434263 +vt 0.930838 0.433902 +vt 0.924243 0.439743 +vt 0.929152 0.439726 +vt 0.933786 0.439455 +vt 0.922618 0.448568 +vt 0.913071 0.450609 +vt 0.907300 0.448560 +vt 0.909883 0.449686 +vt 0.914720 0.447235 +vt 0.918464 0.448158 +vt 0.513713 0.938992 +vt 0.528694 0.953445 +vt 0.617347 0.944413 +vt 0.647892 0.957038 +vt 0.901060 0.450464 +vt 0.929479 0.444749 +vt 0.920523 0.444350 +vt 0.924927 0.444695 +vt 0.861743 0.443993 +vt 0.191556 0.905473 +vt 0.119561 0.876603 +vt 0.173642 0.917872 +vt 0.096090 0.886393 +vt 0.296111 0.916924 +vt 0.285563 0.930752 +vt 0.407594 0.925155 +vt 0.411737 0.936637 +vt 0.175067 0.711347 +vt 0.212659 0.665019 +vt 0.264355 0.628942 +vt 0.154483 0.814520 +vt 0.154113 0.763611 +vt 0.623018 0.605621 +vt 0.528987 0.594894 +vt 0.907779 0.418347 +vt 0.914012 0.420577 +vt 0.339791 0.607004 +vt 0.917709 0.424320 +vt 0.919641 0.429082 +vt 0.919962 0.434273 +vt 0.918588 0.439240 +vt 0.911088 0.445577 +vt 0.904848 0.446138 +vt 0.588358 0.917245 +vt 0.500784 0.908814 +vt 0.915514 0.443200 +vt 0.178893 0.855096 +vt 0.233880 0.878659 +vt 0.317203 0.888741 +vt 0.409219 0.897533 +vt 0.412456 0.865776 +vt 0.413517 0.832907 +vt 0.415303 0.797177 +vt 0.419030 0.758087 +vt 0.424284 0.717135 +vt 0.429773 0.675932 +vt 0.433166 0.635493 +vt 0.431719 0.596003 +vt 0.426262 0.535529 +vt 0.428450 0.560105 +vt 0.890897 0.408138 +vt 0.891059 0.410492 +vt 0.891880 0.404459 +vt 0.892481 0.401195 +vt 0.892744 0.398811 +vt 0.892774 0.397499 +vt 0.852987 0.399150 +vt 0.859402 0.397672 +vt 0.847027 0.401638 +vt 0.841440 0.405030 +vt 0.139391 0.505963 +vt 0.140772 0.546375 +vt 0.105327 0.324975 +vt 0.131454 0.436403 +vt 0.836758 0.409301 +vt 0.826856 0.427241 +vt 0.103269 0.042003 +vt 0.100779 0.113050 +vt 0.099208 0.173793 +vt 0.099637 0.242841 +vt 0.893304 0.464316 +vt 0.894343 0.465793 +vt 0.894093 0.464377 +vt 0.895401 0.465971 +vt 0.893628 0.467665 +vt 0.890751 0.471053 +vt 0.897653 0.467915 +vt 0.895086 0.467874 +vt 0.893376 0.470550 +vt 0.896513 0.470318 +vt 0.906138 0.467954 +vt 0.901291 0.467913 +vt 0.900442 0.470186 +vt 0.905385 0.470181 +vt 0.912382 0.468058 +vt 0.911527 0.470316 +vt 0.918635 0.470453 +vt 0.919770 0.468076 +vt 0.926488 0.470872 +vt 0.927835 0.468189 +vt 0.935282 0.468257 +vt 0.935209 0.471328 +vt 0.878197 0.500625 +vt 0.878935 0.505269 +vt 0.877915 0.500707 +vt 0.878376 0.505430 +vt 0.878490 0.500327 +vt 0.879515 0.504678 +vt 0.878776 0.499827 +vt 0.880077 0.503685 +vt 0.879041 0.499148 +vt 0.880596 0.502336 +vt 0.879273 0.498323 +vt 0.881050 0.500691 +vt 0.879461 0.497390 +vt 0.881414 0.498833 +vt 0.879595 0.496393 +vt 0.881685 0.496848 +vt 0.879673 0.495381 +vt 0.881831 0.494834 +vt 0.879688 0.494400 +vt 0.881855 0.492886 +vt 0.879641 0.493495 +vt 0.881754 0.491093 +vt 0.879517 0.492692 +vt 0.881496 0.489502 +vt 0.879256 0.491990 +vt 0.880966 0.488128 +vt 0.878724 0.491679 +vt 0.879976 0.487255 +vt 0.878985 0.491764 +vt 0.880441 0.487544 +vt 0.880259 0.509816 +vt 0.879427 0.510054 +vt 0.881118 0.508941 +vt 0.881951 0.507469 +vt 0.882719 0.505470 +vt 0.883385 0.503037 +vt 0.883918 0.500283 +vt 0.884295 0.497337 +vt 0.884501 0.494341 +vt 0.884524 0.491438 +vt 0.884373 0.488781 +vt 0.883999 0.486461 +vt 0.883210 0.484429 +vt 0.882394 0.483424 +vt 0.882124 0.514208 +vt 0.881033 0.514520 +vt 0.883251 0.513061 +vt 0.884343 0.511133 +vt 0.885349 0.508513 +vt 0.886222 0.505324 +vt 0.886921 0.501715 +vt 0.887414 0.497854 +vt 0.887677 0.493924 +vt 0.887702 0.490113 +vt 0.887496 0.486645 +vt 0.886980 0.483610 +vt 0.885938 0.480910 +vt 0.884878 0.479558 +vt 0.883170 0.518762 +vt 0.884506 0.518380 +vt 0.885883 0.516979 +vt 0.887217 0.514621 +vt 0.888447 0.511419 +vt 0.889514 0.507521 +vt 0.890369 0.503109 +vt 0.890971 0.498390 +vt 0.891293 0.493586 +vt 0.891320 0.488919 +vt 0.891054 0.484665 +vt 0.890395 0.481066 +vt 0.889190 0.477958 +vt 0.887817 0.476052 +vt 0.885807 0.522719 +vt 0.887367 0.522273 +vt 0.888975 0.520637 +vt 0.890533 0.517885 +vt 0.891968 0.514147 +vt 0.893214 0.509596 +vt 0.894212 0.504446 +vt 0.894915 0.498938 +vt 0.895291 0.493329 +vt 0.895322 0.487882 +vt 0.895000 0.482915 +vt 0.894205 0.478779 +vt 0.892842 0.475169 +vt 0.891068 0.473073 +vt 0.888906 0.526332 +vt 0.890668 0.525829 +vt 0.892483 0.523982 +vt 0.894241 0.520876 +vt 0.895861 0.516656 +vt 0.897267 0.511520 +vt 0.898394 0.505707 +vt 0.899187 0.499489 +vt 0.899612 0.493158 +vt 0.899646 0.487009 +vt 0.899286 0.481354 +vt 0.898411 0.476638 +vt 0.892421 0.529549 +vt 0.894359 0.528996 +vt 0.896354 0.526965 +vt 0.898288 0.523550 +vt 0.900069 0.518911 +vt 0.901615 0.513263 +vt 0.902854 0.506872 +vt 0.903726 0.500036 +vt 0.904193 0.493075 +vt 0.904231 0.486314 +vt 0.903834 0.480100 +vt 0.902975 0.475040 +vt 0.896302 0.532323 +vt 0.898388 0.531728 +vt 0.900534 0.529543 +vt 0.902614 0.525869 +vt 0.904531 0.520877 +vt 0.906194 0.514801 +vt 0.907527 0.507925 +vt 0.908466 0.500570 +vt 0.908967 0.493081 +vt 0.909009 0.485806 +vt 0.908572 0.479151 +vt 0.907654 0.473824 +vt 0.900491 0.534614 +vt 0.902694 0.533986 +vt 0.904960 0.531679 +vt 0.907157 0.527799 +vt 0.909181 0.522528 +vt 0.910937 0.516111 +vt 0.912345 0.508850 +vt 0.913336 0.501084 +vt 0.913866 0.493175 +vt 0.913909 0.485494 +vt 0.913460 0.478418 +vt 0.912585 0.472851 +vt 0.904927 0.536388 +vt 0.907215 0.535736 +vt 0.909569 0.533340 +vt 0.911850 0.529311 +vt 0.913952 0.523838 +vt 0.915776 0.517175 +vt 0.917237 0.509635 +vt 0.918266 0.501570 +vt 0.918854 0.493342 +vt 0.918947 0.485357 +vt 0.918440 0.478006 +vt 0.917870 0.471971 +vt 0.911914 0.537955 +vt 0.914263 0.537286 +vt 0.916678 0.534827 +vt 0.919019 0.530693 +vt 0.921175 0.525076 +vt 0.923047 0.518239 +vt 0.924546 0.510502 +vt 0.925585 0.502224 +vt 0.926227 0.493754 +vt 0.926493 0.485583 +vt 0.925762 0.478045 +vt 0.924764 0.471485 +vt 0.919062 0.538392 +vt 0.921403 0.537724 +vt 0.923810 0.535275 +vt 0.926142 0.531156 +vt 0.930338 0.525761 +vt 0.931620 0.519674 +vt 0.933283 0.512932 +vt 0.932692 0.502839 +vt 0.933977 0.494768 +vt 0.934333 0.486371 +vt 0.933206 0.478784 +vt 0.931863 0.472204 +vt 0.923820 0.537920 +vt 0.926110 0.537268 +vt 0.928464 0.534872 +vt 0.932388 0.530343 +vt 0.937335 0.506755 +vt 0.941235 0.498334 +vt 0.941521 0.487989 +vt 0.940371 0.480184 +vt 0.938481 0.473907 +vt 0.928485 0.536884 +vt 0.930691 0.536256 +vt 0.934789 0.533043 +vt 0.932989 0.535298 +vt 0.935078 0.534703 +vt 0.938368 0.532576 +vt 0.947164 0.489068 +vt 0.947066 0.497764 +vt 0.946239 0.481893 +vt 0.944283 0.476284 +vt 0.937267 0.533185 +vt 0.939209 0.532632 +vt 0.941919 0.530982 +vt 0.950271 0.497772 +vt 0.950689 0.490156 +vt 0.950340 0.483450 +vt 0.948203 0.478162 +vt 0.941256 0.530577 +vt 0.943023 0.530074 +vt 0.945589 0.528338 +vt 0.953490 0.491089 +vt 0.952472 0.498024 +vt 0.953154 0.485389 +vt 0.951415 0.480527 +vt 0.944898 0.527510 +vt 0.946464 0.527065 +vt 0.948879 0.525086 +vt 0.955336 0.498108 +vt 0.955494 0.492270 +vt 0.955142 0.488211 +vt 0.954263 0.484425 +vt 0.949389 0.475505 +vt 0.954052 0.482326 +vt 0.947570 0.475109 +vt 0.943613 0.471998 +vt 0.945693 0.473082 +vt 0.949187 0.522238 +vt 0.950577 0.521636 +vt 0.951775 0.520708 +vt 0.950886 0.519379 +vt 0.952605 0.518141 +vt 0.952742 0.516616 +vt 0.954470 0.515316 +vt 0.878125 0.495997 +vt 0.699705 0.808301 +vt 0.767751 0.813767 +vt 0.710393 0.859091 +vt 0.770203 0.857847 +vt 0.670460 0.860010 +vt 0.652167 0.800156 +vt 0.732575 0.902022 +vt 0.706983 0.910753 +vt 0.776574 0.894725 +vt 0.709069 0.706824 +vt 0.772884 0.721001 +vt 0.699610 0.756109 +vt 0.769128 0.766819 +vt 0.650715 0.739559 +vt 0.665169 0.685443 +vt 0.747389 0.629544 +vt 0.781976 0.645207 +vt 0.726035 0.663915 +vt 0.777571 0.679447 +vt 0.692510 0.641696 +vt 0.725600 0.608173 +vt 0.800844 0.613722 +vt 0.782354 0.622030 +vt 0.800842 0.598447 +vt 0.770640 0.606739 +vt 0.761516 0.586356 +vt 0.801211 0.578120 +vt 0.843418 0.585771 +vt 0.884582 0.608278 +vt 0.833474 0.606525 +vt 0.861681 0.629270 +vt 0.821482 0.621769 +vt 0.826927 0.645008 +vt 0.841154 0.721344 +vt 0.835351 0.679466 +vt 0.901508 0.709495 +vt 0.885190 0.664947 +vt 0.920582 0.644640 +vt 0.946246 0.692573 +vt 0.840491 0.814502 +vt 0.842749 0.767471 +vt 0.905321 0.811872 +vt 0.908491 0.759591 +vt 0.957769 0.748636 +vt 0.953229 0.808266 +vt 0.832824 0.894364 +vt 0.836470 0.858222 +vt 0.876480 0.901039 +vt 0.893987 0.860506 +vt 0.934495 0.863509 +vt 0.904941 0.908789 +vt 0.808557 0.928096 +vt 0.833115 0.918674 +vt 0.811165 0.939260 +vt 0.850764 0.928355 +vt 0.866017 0.939324 +vt 0.815782 0.955298 +vt 0.767445 0.930389 +vt 0.781351 0.919619 +vt 0.756778 0.947206 +vt 0.943537 0.514246 +vt 0.944979 0.511457 +vt 0.944004 0.514141 +vt 0.945299 0.511731 +vt 0.946866 0.510467 +vt 0.945612 0.512005 +vt 0.946775 0.509925 +vt 0.944125 0.524447 +vt 0.943125 0.523168 +vt 0.944265 0.522893 +vt 0.943363 0.521760 +vt 0.943213 0.518650 +vt 0.943600 0.520352 +vt 0.942839 0.519741 +vt 0.942465 0.520831 +vt 0.942508 0.517563 +vt 0.943036 0.516979 +vt 0.944472 0.514036 +vt 0.943564 0.516394 +vt 0.948503 0.507957 +vt 0.948316 0.508687 +vt 0.946664 0.509365 +vt 0.949207 0.508705 +vt 0.948096 0.509397 +vt 0.949608 0.507910 +vt 0.950009 0.507096 +vt 0.951195 0.506751 +vt 0.950679 0.507606 +vt 0.950161 0.508443 +vt 0.951005 0.508659 +vt 0.951647 0.507843 +vt 0.951744 0.509448 +vt 0.952587 0.508900 +vt 0.952323 0.510866 +vt 0.953318 0.510638 +vt 0.953444 0.508317 +vt 0.952296 0.507042 +vt 0.954069 0.512878 +vt 0.953344 0.512779 +vt 0.954198 0.510416 +vt 0.952538 0.512757 +vt 0.952220 0.514880 +vt 0.952777 0.515115 +vt 0.952001 0.518020 +vt 0.951641 0.517475 +vt 0.953314 0.515413 +vt 0.951263 0.516984 +vt 0.949848 0.518868 +vt 0.950071 0.519673 +vt 0.948367 0.522605 +vt 0.948260 0.521460 +vt 0.950291 0.520481 +vt 0.948152 0.520315 +vt 0.946642 0.521256 +vt 0.946704 0.522693 +vt 0.945370 0.524785 +vt 0.945389 0.523190 +vt 0.946766 0.524131 +vt 0.945409 0.521595 +vt 0.944404 0.521338 +vt 0.678283 0.626368 +vt 0.715348 0.591757 +vt 0.648199 0.671421 +vt 0.648584 0.866073 +vt 0.628808 0.796368 +vt 0.694447 0.921544 +vt 0.627873 0.728326 +vt 0.756739 0.569509 +vt 0.801790 0.560389 +vt 0.753465 0.960832 +vt 0.819304 0.969461 +vt 0.896919 0.591012 +vt 0.849419 0.567507 +vt 0.878442 0.952645 +vt 0.925282 0.919811 +vt 0.970524 0.680324 +vt 0.939853 0.628765 +vt 0.962143 0.870229 +vt 0.978626 0.806509 +vt 0.983346 0.741752 +vt 0.947425 0.509189 +vt 0.948517 0.508521 +vt 0.946164 0.510225 +vt 0.942852 0.515989 +vt 0.943731 0.513771 +vt 0.942484 0.518080 +vt 0.944874 0.511783 +vt 0.949456 0.508269 +vt 0.950283 0.508511 +vt 0.942699 0.519730 +vt 0.943487 0.520694 +vt 0.950990 0.509314 +vt 0.951552 0.510694 +vt 0.944633 0.521012 +vt 0.946034 0.520645 +vt 0.951791 0.512457 +vt 0.951461 0.514432 +vt 0.947604 0.519694 +vt 0.949207 0.518265 +vt 0.950530 0.516413 +vt 0.954352 0.507334 +vt 0.955064 0.510040 +vt 0.953029 0.505660 +vt 0.954691 0.513093 +vt 0.953563 0.516187 +vt 0.945738 0.508370 +vt 0.942931 0.511155 +vt 0.941124 0.514785 +vt 0.948237 0.525023 +vt 0.946042 0.527051 +vt 0.950358 0.522206 +vt 0.939919 0.523165 +vt 0.940778 0.525984 +vt 0.942236 0.527579 +vt 0.940031 0.519157 +vt 0.950380 0.505668 +vt 0.948360 0.506655 +vt 0.951792 0.505283 +vt 0.952106 0.519194 +vt 0.944080 0.527997 +vt 0.955136 0.505085 +vt 0.955458 0.508799 +vt 0.953996 0.503048 +vt 0.954947 0.512890 +vt 0.953571 0.516724 +vt 0.940392 0.509704 +vt 0.943959 0.505926 +vt 0.937487 0.514053 +vt 0.946965 0.527152 +vt 0.943743 0.529685 +vt 0.949863 0.523920 +vt 0.936263 0.528363 +vt 0.934831 0.524989 +vt 0.938515 0.530358 +vt 0.935440 0.520327 +vt 0.947235 0.504116 +vt 0.949823 0.503295 +vt 0.952034 0.502955 +vt 0.951882 0.520410 +vt 0.941028 0.530790 +vt 0.956376 0.507194 +vt 0.956255 0.501869 +vt 0.955436 0.512282 +vt 0.953780 0.516679 +vt 0.874321 0.260529 +vt 0.874570 0.258357 +vt 0.870642 0.262514 +vt 0.875205 0.262091 +vt 0.870232 0.260129 +vt 0.874180 0.274349 +vt 0.878088 0.275377 +vt 0.875282 0.284248 +vt 0.879128 0.284194 +vt 0.876451 0.268146 +vt 0.871760 0.267630 +vt 0.879196 0.270887 +vt 0.875496 0.265391 +vt 0.893101 0.284420 +vt 0.892287 0.271326 +vt 0.879947 0.284131 +vt 0.877309 0.242158 +vt 0.869772 0.241647 +vt 0.862255 0.219399 +vt 0.855575 0.218438 +vt 0.849440 0.210845 +vt 0.862432 0.239621 +vt 0.849901 0.240235 +vt 0.863194 0.266321 +vt 0.867315 0.244511 +vt 0.876212 0.265675 +vt 0.880423 0.283451 +vt 0.866718 0.283089 +vt 0.870498 0.254293 +vt 0.863904 0.253791 +vt 0.870234 0.270504 +vt 0.864805 0.270607 +vt 0.878645 0.270230 +vt 0.879852 0.254050 +vt 0.894641 0.243235 +vt 0.885819 0.242521 +vt 0.884908 0.221559 +vt 0.874780 0.219802 +vt 0.853855 0.205189 +vt 0.868038 0.204565 +vt 0.856654 0.209577 +vt 0.870975 0.210424 +vt 0.882303 0.208527 +vt 0.885027 0.212810 +vt 0.869343 0.207181 +vt 0.855354 0.205901 +vt 0.879862 0.246326 +vt 0.872817 0.221463 +vt 0.859409 0.218500 +vt 0.885826 0.225344 +vt 0.891813 0.248223 +vt 0.886742 0.266822 +vt 0.897889 0.269881 +vt 0.890434 0.283337 +vt 0.902191 0.283202 +vt 0.887267 0.269985 +vt 0.888486 0.253806 +vt 0.897865 0.269734 +vt 0.897003 0.253871 +vt 0.890889 0.214313 +vt 0.890567 0.213820 +vt 0.887336 0.211472 +vt 0.935827 0.245027 +vt 0.948386 0.259727 +vt 0.940502 0.247339 +vt 0.952135 0.261243 +vt 0.935892 0.257903 +vt 0.929477 0.246937 +vt 0.955141 0.271935 +vt 0.951237 0.271687 +vt 0.956107 0.282705 +vt 0.951697 0.282445 +vt 0.941535 0.282520 +vt 0.939971 0.271031 +vt 0.963745 0.266939 +vt 0.955791 0.248173 +vt 0.971434 0.266214 +vt 0.963770 0.246708 +vt 0.948554 0.235961 +vt 0.955224 0.234835 +vt 0.886107 0.251776 +vt 0.887306 0.257140 +vt 0.896396 0.245174 +vt 0.901578 0.253513 +vt 0.893935 0.234459 +vt 0.893306 0.241625 +vt 0.884880 0.259333 +vt 0.895682 0.256287 +vt 0.898524 0.274248 +vt 0.887363 0.275119 +vt 0.897217 0.265981 +vt 0.885952 0.267220 +vt 0.888292 0.284017 +vt 0.899782 0.283788 +vt 0.893557 0.224473 +vt 0.903384 0.244263 +vt 0.909834 0.245212 +vt 0.904117 0.226402 +vt 0.898452 0.232315 +vt 0.905360 0.242289 +vt 0.908475 0.231470 +vt 0.914901 0.241090 +vt 0.912117 0.251402 +vt 0.921110 0.249824 +vt 0.894105 0.223477 +vt 0.905103 0.223634 +vt 0.892209 0.224715 +vt 0.891627 0.223783 +vt 0.894785 0.217728 +vt 0.904032 0.218825 +vt 0.916745 0.259188 +vt 0.905292 0.260920 +vt 0.925679 0.257542 +vt 0.919312 0.270264 +vt 0.927598 0.269904 +vt 0.909165 0.270372 +vt 0.923018 0.231732 +vt 0.931448 0.236491 +vt 0.922229 0.248254 +vt 0.927474 0.250713 +vt 0.931475 0.252998 +vt 0.939132 0.243721 +vt 0.941395 0.236759 +vt 0.941774 0.243180 +vt 0.953028 0.242625 +vt 0.951181 0.247570 +vt 0.943442 0.249961 +vt 0.949719 0.251412 +vt 0.935522 0.249090 +vt 0.932269 0.241398 +vt 0.930136 0.234395 +vt 0.929446 0.228394 +vt 0.942151 0.230847 +vt 0.929372 0.224433 +vt 0.942787 0.227016 +vt 0.954848 0.237908 +vt 0.922857 0.256527 +vt 0.927955 0.258098 +vt 0.925166 0.269641 +vt 0.929776 0.270263 +vt 0.933130 0.259066 +vt 0.935431 0.270899 +vt 0.945703 0.257403 +vt 0.938091 0.256867 +vt 0.953024 0.257866 +vt 0.948714 0.269472 +vt 0.957575 0.269677 +vt 0.940805 0.269454 +vt 0.944953 0.253898 +vt 0.950208 0.269278 +vt 0.957956 0.256418 +vt 0.964661 0.269455 +vt 0.961307 0.253711 +vt 0.968960 0.268209 +vt 0.902892 0.249987 +vt 0.897745 0.228849 +vt 0.908596 0.231834 +vt 0.912735 0.252197 +vt 0.894850 0.221810 +vt 0.906240 0.226903 +vt 0.897122 0.224227 +vt 0.908607 0.228742 +vt 0.903082 0.242361 +vt 0.906809 0.256103 +vt 0.919494 0.264606 +vt 0.908311 0.265315 +vt 0.917237 0.255133 +vt 0.907454 0.269025 +vt 0.916487 0.269043 +vt 0.911531 0.283044 +vt 0.919593 0.282900 +vt 0.909419 0.273405 +vt 0.911476 0.283360 +vt 0.920571 0.273041 +vt 0.922157 0.283218 +vt 0.922114 0.255039 +vt 0.920062 0.238863 +vt 0.921052 0.234472 +vt 0.923775 0.235380 +vt 0.934895 0.263753 +vt 0.929369 0.253983 +vt 0.927988 0.282737 +vt 0.925160 0.269608 +vt 0.937386 0.272947 +vt 0.938515 0.283177 +vt 0.905634 0.254510 +vt 0.912156 0.254648 +vt 0.906903 0.269654 +vt 0.915624 0.269303 +vt 0.934262 0.269581 +vt 0.931993 0.256855 +vt 0.928513 0.249010 +vt 0.928512 0.223212 +vt 0.917128 0.220406 +vt 0.916725 0.220767 +vt 0.904100 0.217973 +vt 0.914088 0.228391 +vt 0.916363 0.246441 +vt 0.918617 0.232270 +vt 0.923487 0.240784 +vt 0.938248 0.227770 +vt 0.892821 0.216321 +vt 0.844542 0.206031 +vt 0.874322 0.212263 +vt 0.864795 0.285932 +vt 0.870581 0.286403 +vt 0.877377 0.285736 +vt 0.887924 0.285532 +vt 0.898823 0.285311 +vt 0.908672 0.285115 +vt 0.917425 0.284957 +vt 0.921216 0.269224 +vt 0.922914 0.284906 +vt 0.926665 0.284912 +vt 0.930758 0.284928 +vt 0.936388 0.284960 +vt 0.952837 0.284936 +vt 0.967867 0.284724 +vt 0.971593 0.266723 +vt 0.976556 0.284338 +vt 0.976161 0.284497 +vt 0.973686 0.284223 +vt 0.968029 0.284169 +vt 0.960033 0.284154 +vt 0.950775 0.284138 +vt 0.942494 0.284121 +vt 0.936182 0.284123 +vt 0.929809 0.284158 +vt 0.920968 0.284223 +vt 0.910111 0.284292 +vt 0.952943 0.272204 +vt 0.950019 0.262148 +vt 0.953976 0.282879 +vt 0.900909 0.229617 +vt 0.889642 0.217102 +vt 0.912068 0.234085 +vt 0.913562 0.243735 +vt 0.925227 0.239763 +vt 0.923110 0.245509 +vt 0.916786 0.225007 +vt 0.940372 0.249840 +vt 0.963568 0.250138 +vt 0.888081 0.264217 +vt 0.918118 0.254988 +vt 0.953969 0.514386 +vt 0.955477 0.513065 +vt 0.954272 0.512902 +vt 0.955509 0.511632 +vt 0.956562 0.510151 +vt 0.957528 0.506237 +vt 0.957401 0.500104 +vt 0.957541 0.505830 +vt 0.957323 0.498901 +vt 0.956684 0.496896 +vt 0.956425 0.492794 +vt 0.955922 0.489330 +vt 0.956413 0.492938 +vt 0.955605 0.489791 +vt 0.954030 0.483685 +vt 0.955302 0.484788 +vt 0.952251 0.480529 +vt 0.844126 0.209259 +vt 0.844951 0.221286 +vt 0.955335 0.486204 +vt 0.955539 0.486154 +vt 0.954877 0.485325 +vt 0.956687 0.508947 +vt 0.956754 0.495687 +vt 0.955045 0.487387 +vt 0.886769 0.216308 +vt 0.872985 0.215718 +vt 0.877848 0.505182 +vt 0.877652 0.500582 +vt 0.877370 0.504506 +vt 0.877412 0.500243 +vt 0.876974 0.503437 +vt 0.877218 0.499703 +vt 0.876680 0.502023 +vt 0.877063 0.498994 +vt 0.876501 0.500330 +vt 0.876979 0.498142 +vt 0.876446 0.498438 +vt 0.876960 0.497191 +vt 0.876522 0.496436 +vt 0.877006 0.496186 +vt 0.876728 0.494420 +vt 0.877115 0.495174 +vt 0.877281 0.494204 +vt 0.877054 0.492488 +vt 0.877498 0.493320 +vt 0.877479 0.490730 +vt 0.877999 0.489197 +vt 0.877767 0.492546 +vt 0.878719 0.487808 +vt 0.878144 0.491879 +vt 0.879235 0.487423 +vt 0.878402 0.491725 +vt 0.878643 0.509686 +vt 0.877934 0.508686 +vt 0.877348 0.507101 +vt 0.876912 0.505005 +vt 0.876647 0.502497 +vt 0.876565 0.499694 +vt 0.876669 0.496727 +vt 0.876956 0.493734 +vt 0.877416 0.490859 +vt 0.878045 0.488251 +vt 0.878803 0.485988 +vt 0.879891 0.483890 +vt 0.880652 0.483253 +vt 0.880004 0.514038 +vt 0.879075 0.512727 +vt 0.878307 0.510649 +vt 0.877736 0.507903 +vt 0.877388 0.504616 +vt 0.877280 0.500942 +vt 0.877417 0.497054 +vt 0.877793 0.493132 +vt 0.878389 0.489360 +vt 0.879214 0.485947 +vt 0.880199 0.482989 +vt 0.881623 0.480314 +vt 0.882641 0.479405 +vt 0.881911 0.518172 +vt 0.880775 0.516569 +vt 0.879836 0.514030 +vt 0.879138 0.510673 +vt 0.878713 0.506655 +vt 0.878582 0.502165 +vt 0.878749 0.497412 +vt 0.879208 0.492618 +vt 0.879955 0.488021 +vt 0.881001 0.483899 +vt 0.882310 0.480367 +vt 0.883909 0.477310 +vt 0.885135 0.475926 +vt 0.884336 0.522030 +vt 0.883011 0.520159 +vt 0.881915 0.517195 +vt 0.881100 0.513276 +vt 0.880604 0.508586 +vt 0.880450 0.503344 +vt 0.880646 0.497795 +vt 0.881181 0.492199 +vt 0.882059 0.486840 +vt 0.883257 0.482017 +vt 0.884772 0.477872 +vt 0.886828 0.474331 +vt 0.888116 0.472377 +vt 0.887245 0.525554 +vt 0.885749 0.523442 +vt 0.884512 0.520096 +vt 0.883592 0.515673 +vt 0.883032 0.510379 +vt 0.882858 0.504462 +vt 0.883079 0.498199 +vt 0.883683 0.491882 +vt 0.884656 0.485819 +vt 0.885976 0.480334 +vt 0.887771 0.475644 +vt 0.888962 0.472096 +vt 0.890594 0.528694 +vt 0.888949 0.526372 +vt 0.887589 0.522693 +vt 0.886578 0.517830 +vt 0.885962 0.512009 +vt 0.885771 0.505504 +vt 0.886014 0.498617 +vt 0.886678 0.491672 +vt 0.887734 0.484992 +vt 0.889161 0.478945 +vt 0.891005 0.474124 +vt 0.894335 0.531403 +vt 0.892565 0.528905 +vt 0.891102 0.524947 +vt 0.890014 0.519714 +vt 0.889351 0.513452 +vt 0.889146 0.506453 +vt 0.889407 0.499043 +vt 0.890122 0.491571 +vt 0.891258 0.484384 +vt 0.892815 0.477978 +vt 0.894695 0.473363 +vt 0.898413 0.533643 +vt 0.896544 0.531004 +vt 0.894999 0.526825 +vt 0.893850 0.521300 +vt 0.893151 0.514687 +vt 0.892934 0.507295 +vt 0.893209 0.499472 +vt 0.893965 0.491581 +vt 0.895164 0.483992 +vt 0.896860 0.477387 +vt 0.898865 0.473076 +vt 0.902769 0.535380 +vt 0.900829 0.532640 +vt 0.899224 0.528300 +vt 0.898031 0.522562 +vt 0.897305 0.515695 +vt 0.897079 0.508020 +vt 0.897366 0.499895 +vt 0.898150 0.491701 +vt 0.899396 0.483821 +vt 0.901449 0.477074 +vt 0.903819 0.472976 +vt 0.909699 0.536920 +vt 0.907707 0.534109 +vt 0.906061 0.529655 +vt 0.904836 0.523767 +vt 0.904091 0.516721 +vt 0.903860 0.508844 +vt 0.904153 0.500507 +vt 0.904958 0.492099 +vt 0.906237 0.484012 +vt 0.908008 0.477129 +vt 0.910034 0.473198 +vt 0.916854 0.537360 +vt 0.914870 0.534559 +vt 0.913230 0.530122 +vt 0.914164 0.524465 +vt 0.913164 0.518196 +vt 0.913437 0.511340 +vt 0.911330 0.501080 +vt 0.912131 0.492703 +vt 0.913405 0.484646 +vt 0.915130 0.477830 +vt 0.916994 0.473623 +vt 0.921660 0.536911 +vt 0.919719 0.534172 +vt 0.919170 0.529284 +vt 0.917172 0.505296 +vt 0.919838 0.496406 +vt 0.920904 0.486229 +vt 0.922380 0.479225 +vt 0.924263 0.474614 +vt 0.926405 0.535913 +vt 0.924828 0.532245 +vt 0.931019 0.534378 +vt 0.929686 0.531880 +vt 0.927970 0.487724 +vt 0.927599 0.495894 +vt 0.929366 0.480734 +vt 0.931925 0.475167 +vt 0.935435 0.532330 +vt 0.934387 0.530379 +vt 0.933232 0.488977 +vt 0.932764 0.496065 +vt 0.934736 0.482159 +vt 0.937602 0.476887 +vt 0.939590 0.529799 +vt 0.938848 0.527798 +vt 0.937731 0.490249 +vt 0.936964 0.496523 +vt 0.939215 0.484123 +vt 0.942421 0.479379 +vt 0.943421 0.526821 +vt 0.942722 0.524593 +vt 0.942329 0.491435 +vt 0.942070 0.497013 +vt 0.943599 0.487511 +vt 0.946933 0.482758 +vt 0.945342 0.476815 +vt 0.949756 0.480976 +vt 0.940652 0.473783 +vt 0.947564 0.521395 +vt 0.946600 0.520277 +vt 0.949518 0.517928 +vt 0.951324 0.514898 +vt 0.699423 0.809253 +vt 0.710111 0.860044 +vt 0.767468 0.814720 +vt 0.769921 0.858800 +vt 0.669688 0.860514 +vt 0.651885 0.801109 +vt 0.732293 0.902975 +vt 0.706453 0.911484 +vt 0.776292 0.895678 +vt 0.708787 0.707777 +vt 0.699329 0.757062 +vt 0.772602 0.721954 +vt 0.768846 0.767772 +vt 0.650433 0.740512 +vt 0.664887 0.686395 +vt 0.747107 0.630497 +vt 0.725753 0.664868 +vt 0.781693 0.646160 +vt 0.777288 0.680400 +vt 0.692228 0.642648 +vt 0.725318 0.609127 +vt 0.770358 0.607692 +vt 0.782072 0.622983 +vt 0.800560 0.599400 +vt 0.800562 0.614675 +vt 0.761234 0.587309 +vt 0.800928 0.579073 +vt 0.861399 0.630223 +vt 0.884300 0.609231 +vt 0.833192 0.607478 +vt 0.843136 0.586725 +vt 0.821200 0.622722 +vt 0.826645 0.645961 +vt 0.884907 0.665900 +vt 0.835069 0.680418 +vt 0.901226 0.710448 +vt 0.840872 0.722297 +vt 0.945964 0.693526 +vt 0.920299 0.645594 +vt 0.908209 0.760544 +vt 0.842467 0.768423 +vt 0.905039 0.812825 +vt 0.840209 0.815454 +vt 0.952947 0.809219 +vt 0.957487 0.749588 +vt 0.893705 0.861459 +vt 0.836187 0.859175 +vt 0.876198 0.901993 +vt 0.832541 0.895317 +vt 0.904659 0.909742 +vt 0.934213 0.864462 +vt 0.850482 0.929308 +vt 0.832833 0.919627 +vt 0.810883 0.940213 +vt 0.808275 0.929049 +vt 0.815494 0.955807 +vt 0.865735 0.940277 +vt 0.767163 0.931342 +vt 0.781069 0.920572 +vt 0.757688 0.947696 +vt 0.926885 0.512910 +vt 0.927854 0.512845 +vt 0.928377 0.510114 +vt 0.929178 0.510430 +vt 0.931406 0.509225 +vt 0.930823 0.508639 +vt 0.929980 0.510746 +vt 0.930534 0.523359 +vt 0.930891 0.521821 +vt 0.928350 0.521984 +vt 0.928964 0.520607 +vt 0.928441 0.517467 +vt 0.927597 0.518520 +vt 0.929579 0.519229 +vt 0.926753 0.519573 +vt 0.926179 0.516255 +vt 0.927195 0.515710 +vt 0.928823 0.512781 +vt 0.928211 0.515165 +vt 0.932374 0.506682 +vt 0.930239 0.508053 +vt 0.932657 0.507439 +vt 0.934517 0.507536 +vt 0.934510 0.506707 +vt 0.932940 0.508196 +vt 0.934502 0.505878 +vt 0.936567 0.505592 +vt 0.936317 0.506456 +vt 0.936067 0.507320 +vt 0.937546 0.507578 +vt 0.938070 0.506748 +vt 0.938901 0.508422 +vt 0.940036 0.509880 +vt 0.939772 0.507879 +vt 0.941088 0.509726 +vt 0.940713 0.507314 +vt 0.938594 0.505926 +vt 0.942911 0.511998 +vt 0.942269 0.509491 +vt 0.941755 0.511888 +vt 0.940774 0.511818 +vt 0.940867 0.513981 +vt 0.941724 0.514237 +vt 0.941696 0.517149 +vt 0.942674 0.514592 +vt 0.940898 0.516619 +vt 0.940099 0.516090 +vt 0.938667 0.517973 +vt 0.939267 0.518807 +vt 0.937547 0.521739 +vt 0.939868 0.519641 +vt 0.937153 0.520570 +vt 0.936759 0.519402 +vt 0.934851 0.520311 +vt 0.935057 0.521760 +vt 0.932933 0.523788 +vt 0.935263 0.523209 +vt 0.932985 0.522196 +vt 0.933038 0.520604 +vt 0.931248 0.520284 +vt 0.715065 0.592710 +vt 0.678001 0.627321 +vt 0.647916 0.672373 +vt 0.628526 0.797321 +vt 0.648988 0.865355 +vt 0.693916 0.922274 +vt 0.627590 0.729278 +vt 0.801508 0.561342 +vt 0.756457 0.570463 +vt 0.753635 0.961316 +vt 0.820182 0.969676 +vt 0.896636 0.591965 +vt 0.849136 0.568460 +vt 0.878160 0.953598 +vt 0.925000 0.920764 +vt 0.970242 0.681277 +vt 0.939570 0.629718 +vt 0.961861 0.871181 +vt 0.978343 0.807461 +vt 0.983064 0.742705 +vt 0.932573 0.508007 +vt 0.934161 0.507367 +vt 0.930964 0.509008 +vt 0.927841 0.514787 +vt 0.928411 0.512543 +vt 0.928106 0.516928 +vt 0.929515 0.510546 +vt 0.935671 0.507169 +vt 0.937081 0.507438 +vt 0.929093 0.518640 +vt 0.930710 0.519671 +vt 0.938351 0.508298 +vt 0.939400 0.509723 +vt 0.932552 0.520045 +vt 0.934434 0.519715 +vt 0.940079 0.511519 +vt 0.940113 0.513519 +vt 0.936336 0.518792 +vt 0.938116 0.517377 +vt 0.939405 0.515521 +vt 0.943638 0.508965 +vt 0.941943 0.506311 +vt 0.939071 0.504531 +vt 0.943880 0.515430 +vt 0.944335 0.512165 +vt 0.927944 0.506963 +vt 0.925069 0.509715 +vt 0.923231 0.513349 +vt 0.935271 0.526189 +vt 0.938229 0.524222 +vt 0.940839 0.521447 +vt 0.923484 0.521848 +vt 0.925731 0.524779 +vt 0.928789 0.526502 +vt 0.922597 0.517761 +vt 0.933960 0.504385 +vt 0.931029 0.505302 +vt 0.936534 0.504088 +vt 0.942769 0.518413 +vt 0.932116 0.527039 +vt 0.945351 0.507568 +vt 0.943089 0.503744 +vt 0.939896 0.502018 +vt 0.946118 0.511811 +vt 0.945354 0.516015 +vt 0.925289 0.504712 +vt 0.920962 0.508150 +vt 0.918385 0.512520 +vt 0.938552 0.526478 +vt 0.934537 0.528948 +vt 0.941809 0.523283 +vt 0.921702 0.527196 +vt 0.918305 0.523665 +vt 0.925959 0.529352 +vt 0.917212 0.518867 +vt 0.933078 0.501722 +vt 0.929349 0.502508 +vt 0.936447 0.501609 +vt 0.943896 0.519878 +vt 0.930309 0.529931 +vt 0.948005 0.505607 +vt 0.944871 0.499392 +vt 0.947806 0.516062 +vt 0.949100 0.511191 +vt 0.879346 0.309871 +vt 0.875833 0.306046 +vt 0.875654 0.308144 +vt 0.879007 0.306407 +vt 0.876295 0.294136 +vt 0.878977 0.293033 +vt 0.876175 0.301094 +vt 0.879812 0.300399 +vt 0.882198 0.297454 +vt 0.880254 0.303450 +vt 0.893175 0.297687 +vt 0.859181 0.350621 +vt 0.872916 0.327332 +vt 0.865817 0.349629 +vt 0.880252 0.326495 +vt 0.865713 0.330422 +vt 0.852822 0.359205 +vt 0.877097 0.301439 +vt 0.864055 0.301093 +vt 0.869257 0.322943 +vt 0.852819 0.327816 +vt 0.872679 0.316230 +vt 0.866118 0.317194 +vt 0.865658 0.301152 +vt 0.871305 0.301562 +vt 0.879516 0.300927 +vt 0.881998 0.315802 +vt 0.878301 0.348969 +vt 0.888479 0.326114 +vt 0.888262 0.347411 +vt 0.897047 0.325511 +vt 0.857019 0.363938 +vt 0.860381 0.359999 +vt 0.872015 0.364197 +vt 0.874655 0.358691 +vt 0.888629 0.355800 +vt 0.886097 0.359632 +vt 0.872907 0.361011 +vt 0.858128 0.362290 +vt 0.881706 0.320824 +vt 0.875903 0.346079 +vt 0.862423 0.349295 +vt 0.893556 0.318638 +vt 0.888714 0.341972 +vt 0.887564 0.300041 +vt 0.898554 0.296744 +vt 0.888147 0.300674 +vt 0.898715 0.300283 +vt 0.890480 0.315686 +vt 0.898830 0.315390 +vt 0.894383 0.353795 +vt 0.894065 0.354129 +vt 0.890921 0.356254 +vt 0.952900 0.304272 +vt 0.949521 0.305533 +vt 0.942171 0.318343 +vt 0.937705 0.320863 +vt 0.937122 0.307590 +vt 0.931259 0.318903 +vt 0.955735 0.293528 +vt 0.951772 0.293299 +vt 0.940544 0.294181 +vt 0.964624 0.302435 +vt 0.972337 0.302695 +vt 0.957594 0.320997 +vt 0.965634 0.321964 +vt 0.957663 0.333351 +vt 0.950951 0.332759 +vt 0.904388 0.314564 +vt 0.887892 0.312338 +vt 0.897685 0.323309 +vt 0.888936 0.316243 +vt 0.878641 0.308129 +vt 0.895710 0.326063 +vt 0.896265 0.333541 +vt 0.887647 0.308911 +vt 0.896958 0.311973 +vt 0.897963 0.301935 +vt 0.887703 0.301029 +vt 0.898883 0.293285 +vt 0.887878 0.292930 +vt 0.896731 0.344468 +vt 0.905567 0.324686 +vt 0.906991 0.342485 +vt 0.911791 0.324251 +vt 0.900908 0.335596 +vt 0.911043 0.336295 +vt 0.907054 0.325968 +vt 0.917143 0.326675 +vt 0.923285 0.318080 +vt 0.915184 0.316034 +vt 0.908088 0.344187 +vt 0.897103 0.344536 +vt 0.895142 0.343144 +vt 0.894607 0.344154 +vt 0.898106 0.350443 +vt 0.907260 0.349181 +vt 0.907871 0.308374 +vt 0.918398 0.309598 +vt 0.927049 0.310818 +vt 0.928305 0.298442 +vt 0.920004 0.298182 +vt 0.909927 0.298243 +vt 0.929162 0.318872 +vt 0.933826 0.332572 +vt 0.924036 0.321254 +vt 0.925629 0.337191 +vt 0.941158 0.325550 +vt 0.933051 0.316658 +vt 0.943727 0.330971 +vt 0.955075 0.325269 +vt 0.943791 0.324629 +vt 0.952983 0.320359 +vt 0.951332 0.316568 +vt 0.945126 0.317960 +vt 0.937248 0.318798 +vt 0.934373 0.326359 +vt 0.932584 0.333273 +vt 0.932192 0.339300 +vt 0.932318 0.343421 +vt 0.944776 0.336887 +vt 0.945607 0.340893 +vt 0.957129 0.330060 +vt 0.924262 0.313255 +vt 0.925922 0.300164 +vt 0.929283 0.311724 +vt 0.930502 0.299579 +vt 0.936126 0.298969 +vt 0.934410 0.310774 +vt 0.939442 0.311396 +vt 0.947027 0.310849 +vt 0.954323 0.310334 +vt 0.958291 0.298597 +vt 0.949440 0.298819 +vt 0.941532 0.298832 +vt 0.946483 0.315703 +vt 0.950981 0.300512 +vt 0.969751 0.300159 +vt 0.965388 0.298807 +vt 0.962811 0.314479 +vt 0.959326 0.311738 +vt 0.904541 0.316598 +vt 0.900452 0.338160 +vt 0.914268 0.314088 +vt 0.911146 0.334819 +vt 0.910827 0.338483 +vt 0.909045 0.340214 +vt 0.900043 0.343367 +vt 0.897918 0.345703 +vt 0.905082 0.324826 +vt 0.907714 0.311836 +vt 0.909052 0.301920 +vt 0.918749 0.311513 +vt 0.920236 0.301846 +vt 0.908155 0.297332 +vt 0.917180 0.297035 +vt 0.909931 0.293440 +vt 0.920410 0.293444 +vt 0.923496 0.310879 +vt 0.922253 0.327378 +vt 0.923469 0.332076 +vt 0.925923 0.331262 +vt 0.930583 0.312153 +vt 0.934974 0.302503 +vt 0.925817 0.296140 +vt 0.937580 0.293425 +vt 0.916398 0.300537 +vt 0.913653 0.315103 +vt 0.907706 0.300101 +vt 0.907277 0.314776 +vt 0.934984 0.298714 +vt 0.933344 0.311407 +vt 0.930312 0.318902 +vt 0.931529 0.345055 +vt 0.920283 0.347817 +vt 0.919853 0.347075 +vt 0.907379 0.350402 +vt 0.916863 0.340467 +vt 0.918259 0.323025 +vt 0.921166 0.335346 +vt 0.925584 0.327014 +vt 0.941044 0.340685 +vt 0.896265 0.352186 +vt 0.847118 0.363087 +vt 0.877923 0.355511 +vt 0.921992 0.300554 +vt 0.972463 0.301867 +vt 0.953466 0.293583 +vt 0.950828 0.303658 +vt 0.892951 0.350759 +vt 0.903260 0.337983 +vt 0.915596 0.322974 +vt 0.914170 0.333036 +vt 0.927312 0.326701 +vt 0.925040 0.320904 +vt 0.919699 0.342662 +vt 0.941871 0.316003 +vt 0.965254 0.318221 +vt 0.889956 0.306147 +vt 0.919598 0.314755 +vt 0.953215 0.511264 +vt 0.952698 0.512701 +vt 0.951135 0.508840 +vt 0.950868 0.504414 +vt 0.946032 0.497811 +vt 0.950400 0.504692 +vt 0.946155 0.498790 +vt 0.944108 0.496015 +vt 0.945966 0.489086 +vt 0.945497 0.488606 +vt 0.944681 0.492183 +vt 0.944291 0.491984 +vt 0.951345 0.484260 +vt 0.845891 0.359181 +vt 0.847322 0.346695 +vt 0.948386 0.485293 +vt 0.952907 0.485943 +vt 0.952013 0.507755 +vt 0.944606 0.494766 +vt 0.948560 0.486751 +vt 0.890112 0.351330 +vt 0.876360 0.352039 +vt 0.889967 0.448966 +vt 0.878531 0.491695 +vt 0.879572 0.487300 +vt 0.881189 0.483034 +vt 0.883359 0.479059 +vt 0.885970 0.475436 +vt 0.826451 0.427416 +vt 0.889758 0.472115 +vt 0.881748 0.482959 +vt 0.884054 0.478968 +vt 0.886811 0.475429 +vt 0.942835 0.472365 +vt 0.883515 0.397651 +vt 0.925482 0.397634 +vt 0.918562 0.397639 +vt 0.909465 0.397642 +vt 0.901603 0.397646 +vt 0.891034 0.397645 +vt 0.894812 0.397645 +vt 0.931235 0.397655 +vt 0.877003 0.397658 +vt 0.882529 0.397650 +vt 0.871595 0.397671 +vt 0.868659 0.397671 +vt 0.925845 0.397644 +vt 0.918784 0.397657 +vt 0.909614 0.397666 +vt 0.901249 0.397670 +vt 0.890498 0.397656 +vt 0.929610 0.397643 +vt 0.894099 0.397656 +vt 0.484696 0.402538 +vt 0.483058 0.467543 +vt 0.026409 0.041836 +vt 0.027211 0.115320 +vt 0.027231 0.176259 +vt 0.027041 0.244825 +vt 0.028360 0.327681 +vt 0.035514 0.442488 +vt 0.039161 0.517280 +vt 0.039555 0.560029 +vt 0.486973 0.320445 +vt 0.496033 0.232082 +vt 0.498386 0.171519 +vt 0.166927 0.932027 +vt 0.089131 0.901460 +vt 0.412839 0.954955 +vt 0.280654 0.942250 +vt 0.172265 0.583534 +vt 0.092604 0.643694 +vt 0.048602 0.713913 +vt 0.034269 0.783775 +vt 0.288973 0.537530 +vt 0.047197 0.849027 +vt 0.429574 0.516361 +vt 0.573602 0.514995 +vt 0.720327 0.535205 +vt 0.544365 0.975068 +vt 0.682918 0.980037 +vt 0.412382 0.948959 +vt 0.274953 0.941360 +vt 0.159929 0.926191 +vt 0.078614 0.893197 +vt 0.045546 0.712112 +vt 0.029763 0.775258 +vt 0.174568 0.589926 +vt 0.093871 0.647648 +vt 0.286715 0.542233 +vt 0.039682 0.837587 +vt 0.425296 0.517534 +vt 0.568016 0.514851 +vt 0.715649 0.536695 +vt 0.543555 0.965760 +vt 0.685594 0.967391 +usemtl blinn3 +s off +f 1988/1 1990/2 2015/3 +f 1990/2 1991/4 2015/3 +f 1991/4 1992/5 2015/3 +f 2000/6 1998/7 2015/3 +f 1992/5 2019/8 2015/3 +f 2019/8 2001/9 2015/3 +f 2001/9 2007/10 2015/3 +s 1 +f 90/11 244/12 7/13 +f 7/13 244/12 91/14 +f 244/12 90/11 92/15 +f 92/15 90/11 6/16 +f 93/17 245/18 6/16 +f 6/16 245/18 92/15 +f 245/18 93/17 300/19 +f 300/19 93/17 219/20 +f 96/21 94/22 8/23 +f 8/23 94/22 9/24 +f 94/22 96/21 10/25 +f 10/25 96/21 95/26 +f 246/27 90/11 97/28 +f 97/28 90/11 7/13 +f 90/11 246/27 6/16 +f 6/16 246/27 98/29 +f 246/27 99/30 98/29 +f 98/29 99/30 12/31 +f 99/30 246/27 11/32 +f 11/32 246/27 97/28 +f 247/33 93/17 98/29 +f 98/29 93/17 6/16 +f 93/17 247/33 219/20 +f 219/20 247/33 301/34 +f 247/33 100/35 301/34 +f 301/34 100/35 220/36 +f 100/35 247/33 12/31 +f 12/31 247/33 98/29 +f 248/37 94/22 13/38 +f 13/38 94/22 10/25 +f 94/22 248/37 9/24 +f 9/24 248/37 101/39 +f 103/40 102/41 14/42 +f 14/42 102/41 15/43 +f 102/41 103/40 9/24 +f 9/24 103/40 8/23 +f 249/44 99/30 104/45 +f 104/45 99/30 11/32 +f 99/30 249/44 12/31 +f 12/31 249/44 105/46 +f 249/44 106/47 105/46 +f 105/46 106/47 17/48 +f 106/47 249/44 16/49 +f 16/49 249/44 104/45 +f 108/50 102/41 101/39 +f 101/39 102/41 9/24 +f 102/41 108/50 15/43 +f 15/43 108/50 107/51 +f 109/52 110/53 15/43 +f 15/43 110/53 14/42 +f 110/53 109/52 19/54 +f 19/54 109/52 18/55 +f 112/56 109/52 107/51 +f 107/51 109/52 15/43 +f 109/52 112/56 18/55 +f 18/55 112/56 111/57 +f 21/58 113/59 20/60 +f 20/60 113/59 114/61 +f 114/61 113/59 18/55 +f 18/55 113/59 19/54 +f 116/62 114/61 111/57 +f 111/57 114/61 18/55 +f 116/62 115/63 114/61 +f 114/61 115/63 20/60 +f 23/64 117/65 22/66 +f 22/66 117/65 118/67 +f 117/65 21/58 118/67 +f 118/67 21/58 20/60 +f 115/63 120/68 20/60 +f 20/60 120/68 118/67 +f 120/68 119/69 118/67 +f 118/67 119/69 22/66 +f 24/70 250/71 25/72 +f 25/72 250/71 121/73 +f 250/71 23/64 121/73 +f 121/73 23/64 22/66 +f 119/69 123/74 22/66 +f 22/66 123/74 121/73 +f 123/74 122/75 121/73 +f 121/73 122/75 25/72 +f 27/76 340/77 124/78 +f 124/78 340/77 341/79 +f 124/78 341/79 29/80 +f 29/80 341/79 342/81 +f 341/79 125/82 342/81 +f 342/81 125/82 28/83 +f 125/82 341/79 26/84 +f 26/84 341/79 340/77 +f 251/85 308/86 126/87 +f 126/87 308/86 30/88 +f 308/86 251/85 228/89 +f 228/89 251/85 127/90 +f 251/85 124/78 127/90 +f 127/90 124/78 29/80 +f 124/78 251/85 27/76 +f 27/76 251/85 126/87 +f 127/90 128/91 228/89 +f 228/89 128/91 46/92 +f 127/90 29/80 128/91 +f 128/91 29/80 31/93 +f 129/94 252/95 32/96 +f 32/96 252/95 309/97 +f 309/98 252/99 229/100 +f 229/100 252/99 130/101 +f 252/99 131/102 130/101 +f 130/101 131/102 34/103 +f 252/95 129/94 131/104 +f 131/104 129/94 33/105 +f 132/106 253/107 35/108 +f 35/108 253/107 310/109 +f 253/107 129/94 310/109 +f 310/109 129/94 32/96 +f 129/94 253/107 33/105 +f 33/105 253/107 133/110 +f 253/107 132/106 133/110 +f 133/110 132/106 36/111 +f 134/112 254/113 36/111 +f 36/111 254/113 133/110 +f 254/113 135/114 133/110 +f 133/110 135/114 33/105 +f 135/114 254/113 37/115 +f 37/115 254/113 157/116 +f 254/113 134/112 157/116 +f 157/116 134/112 39/117 +f 136/118 255/119 40/120 +f 40/120 255/119 137/121 +f 137/121 255/119 38/122 +f 38/122 255/119 138/123 +f 255/119 134/112 138/123 +f 138/123 134/112 36/111 +f 255/119 136/118 134/112 +f 134/112 136/118 39/117 +f 256/124 100/35 105/46 +f 105/46 100/35 12/31 +f 100/35 256/124 220/36 +f 220/36 256/124 311/125 +f 256/124 139/126 311/125 +f 311/125 139/126 1979/127 +f 139/126 256/124 17/48 +f 17/48 256/124 105/46 +f 205/128 286/129 60/130 +f 60/130 286/129 140/131 +f 286/129 204/132 140/131 +f 140/131 204/132 58/133 +f 106/47 257/134 17/48 +f 17/48 257/134 141/135 +f 257/134 136/136 141/135 +f 141/135 136/136 40/137 +f 136/136 257/134 39/138 +f 39/138 257/134 142/139 +f 257/134 106/47 142/139 +f 142/139 106/47 16/49 +f 139/126 258/140 1979/127 +f 1979/127 258/140 143/141 +f 258/140 155/142 143/141 +f 143/141 155/142 230/143 +f 155/142 258/140 40/137 +f 40/137 258/140 141/135 +f 258/140 139/126 141/135 +f 141/135 139/126 17/48 +f 285/144 203/145 144/146 +f 144/146 203/145 61/147 +f 204/132 285/144 58/133 +f 58/133 285/144 144/146 +f 57/148 2066/149 145/150 +f 145/150 2066/149 2067/151 +f 312/152 259/153 30/88 +f 30/88 259/153 126/87 +f 259/153 146/154 126/87 +f 126/87 146/154 27/76 +f 146/155 259/156 41/157 +f 41/157 259/156 147/158 +f 259/156 312/159 147/158 +f 147/158 312/159 42/160 +f 44/161 148/162 43/163 +f 43/163 148/162 149/164 +f 148/162 24/70 149/164 +f 149/164 24/70 25/72 +f 150/165 260/166 26/84 +f 26/84 260/166 125/82 +f 260/166 151/167 125/82 +f 125/82 151/167 28/83 +f 151/167 260/166 44/161 +f 44/161 260/166 148/162 +f 260/166 150/165 148/162 +f 148/162 150/165 24/70 +f 122/75 153/168 25/72 +f 25/72 153/168 149/164 +f 153/168 152/169 149/164 +f 149/164 152/169 43/163 +f 232/170 31/93 342/81 +f 342/81 31/93 29/80 +f 342/81 28/83 232/170 +f 232/170 28/83 45/171 +f 154/172 261/173 34/103 +f 34/103 261/173 130/101 +f 261/173 313/174 130/101 +f 130/101 313/174 229/100 +f 313/174 261/173 42/160 +f 42/160 261/173 147/158 +f 261/173 154/172 147/158 +f 147/158 154/172 41/157 +f 155/175 262/176 230/177 +f 230/177 262/176 314/178 +f 262/176 156/179 314/178 +f 314/178 156/179 231/180 +f 156/179 262/176 38/122 +f 38/122 262/176 137/121 +f 262/176 155/175 137/121 +f 137/121 155/175 40/120 +f 263/181 182/182 154/172 +f 154/172 182/182 41/157 +f 182/183 263/184 37/115 +f 37/115 263/184 135/114 +f 263/184 131/104 135/114 +f 135/114 131/104 33/105 +f 131/102 263/181 34/103 +f 34/103 263/181 154/172 +f 138/123 264/185 38/122 +f 38/122 264/185 156/179 +f 156/179 264/185 231/180 +f 231/180 264/185 315/186 +f 264/185 132/106 315/186 +f 315/186 132/106 35/108 +f 132/106 264/185 36/111 +f 36/111 264/185 138/123 +f 152/169 242/187 43/163 +f 43/163 242/187 158/188 +f 242/187 243/189 158/188 +f 158/188 243/189 84/190 +f 265/191 161/192 160/193 +f 160/193 161/192 45/171 +f 161/192 265/191 82/194 +f 82/194 265/191 240/195 +f 265/191 239/196 240/195 +f 240/195 239/196 83/197 +f 239/196 265/191 44/161 +f 44/161 265/191 160/193 +f 83/197 239/196 84/190 +f 84/190 239/196 158/188 +f 158/188 239/196 43/163 +f 43/163 239/196 44/161 +f 161/192 266/198 45/171 +f 45/171 266/198 232/170 +f 266/198 233/199 232/170 +f 232/170 233/199 31/93 +f 233/199 266/198 81/200 +f 81/200 266/198 238/201 +f 266/198 161/192 238/201 +f 238/201 161/192 82/194 +f 164/202 165/203 95/26 +f 95/26 165/203 10/25 +f 166/204 267/205 47/206 +f 47/206 267/205 167/207 +f 267/205 164/202 167/207 +f 167/207 164/202 95/26 +f 289/208 208/209 91/14 +f 91/14 208/209 7/13 +f 165/203 168/210 10/25 +f 10/25 168/210 13/38 +f 268/211 169/212 167/207 +f 167/207 169/212 47/206 +f 169/212 268/211 48/213 +f 48/213 268/211 170/214 +f 268/211 96/21 170/214 +f 170/214 96/21 8/23 +f 96/21 268/211 95/26 +f 95/26 268/211 167/207 +f 208/209 288/215 7/13 +f 7/13 288/215 97/28 +f 288/215 207/216 97/28 +f 97/28 207/216 11/32 +f 171/217 269/218 64/219 +f 64/219 269/218 172/220 +f 269/218 173/221 172/220 +f 172/220 173/221 59/222 +f 269/223 174/224 173/225 +f 173/225 174/224 65/226 +f 174/224 269/223 63/227 +f 63/227 269/223 171/228 +f 173/221 270/229 59/222 +f 59/222 270/229 175/230 +f 175/230 270/229 62/231 +f 62/231 270/229 176/232 +f 176/233 270/234 66/235 +f 66/235 270/234 177/236 +f 270/234 173/225 177/236 +f 177/236 173/225 65/226 +f 271/237 178/238 176/232 +f 176/232 178/238 62/231 +f 178/238 271/237 67/239 +f 67/239 271/237 179/240 +f 179/241 271/242 68/243 +f 68/243 271/242 180/244 +f 271/242 176/233 180/244 +f 180/244 176/233 66/235 +f 272/245 150/165 181/246 +f 181/246 150/165 26/84 +f 150/165 272/245 24/70 +f 24/70 272/245 250/71 +f 272/245 49/247 250/71 +f 250/71 49/247 23/64 +f 151/167 160/193 28/83 +f 28/83 160/193 45/171 +f 160/193 151/167 44/161 +f 49/247 273/248 23/64 +f 23/64 273/248 117/65 +f 273/248 50/249 117/65 +f 117/65 50/249 21/58 +f 50/249 274/250 21/58 +f 21/58 274/250 113/59 +f 113/59 274/250 19/54 +f 19/54 274/250 51/251 +f 275/252 110/53 51/251 +f 51/251 110/53 19/54 +f 110/53 275/252 14/42 +f 14/42 275/252 52/253 +f 276/254 53/255 170/214 +f 170/214 53/255 48/213 +f 276/254 103/40 52/253 +f 52/253 103/40 14/42 +f 103/40 276/254 8/23 +f 8/23 276/254 170/214 +f 207/216 287/256 11/32 +f 11/32 287/256 104/45 +f 287/256 54/257 104/45 +f 104/45 54/257 16/49 +f 54/257 277/258 16/49 +f 16/49 277/258 142/139 +f 277/258 55/259 142/139 +f 142/139 55/259 39/138 +f 278/260 56/261 157/262 +f 157/262 56/261 37/263 +f 55/259 278/260 39/138 +f 39/138 278/260 157/262 +f 183/264 182/265 56/261 +f 56/261 182/265 37/263 +f 182/265 183/264 41/266 +f 41/266 183/264 57/148 +f 284/267 184/268 203/145 +f 203/145 184/268 61/147 +f 202/269 69/270 284/267 +f 284/267 69/270 184/268 +f 145/150 146/154 57/148 +f 57/148 146/154 41/266 +f 322/271 340/77 145/150 +f 340/77 27/76 145/150 +f 145/150 27/76 146/154 +f 2070/272 2071/273 277/258 +f 277/258 2071/273 55/259 +f 291/274 209/275 279/276 +f 279/276 209/275 185/277 +f 211/278 291/274 186/279 +f 186/279 291/274 279/276 +f 2069/280 2070/272 54/257 +f 54/257 2070/272 277/258 +f 2072/281 2074/282 278/260 +f 278/260 2074/282 56/261 +f 290/283 210/284 280/285 +f 280/285 210/284 187/286 +f 209/275 290/283 185/277 +f 185/277 290/283 280/285 +f 2071/273 2072/281 55/259 +f 55/259 2072/281 278/260 +f 276/254 2076/287 53/255 +f 53/255 2076/287 2077/288 +f 213/289 2054/290 295/291 +f 295/291 2054/290 281/292 +f 295/293 281/294 215/295 +f 215/295 281/294 188/296 +f 2076/287 276/254 2075/297 +f 2075/297 276/254 52/253 +f 2073/298 2069/280 287/256 +f 287/256 2069/280 54/257 +f 293/299 211/278 2053/300 +f 2053/300 211/278 186/279 +f 275/252 2079/301 52/253 +f 52/253 2079/301 2075/297 +f 215/295 188/296 294/302 +f 294/302 188/296 2056/303 +f 294/302 2056/303 214/304 +f 214/304 2056/303 189/305 +f 2079/301 275/252 2080/306 +f 2080/306 275/252 51/251 +f 274/250 2081/307 51/251 +f 51/251 2081/307 2080/306 +f 214/304 189/305 296/308 +f 296/308 189/305 2057/309 +f 216/310 296/308 190/311 +f 190/311 296/308 2057/309 +f 2082/312 2081/307 50/249 +f 50/249 2081/307 274/250 +f 2085/313 2086/314 272/245 +f 272/245 2086/314 49/247 +f 298/315 217/316 282/317 +f 282/317 217/316 191/318 +f 218/319 298/320 192/321 +f 192/321 298/320 282/322 +f 2084/323 2085/313 181/246 +f 181/246 2085/313 272/245 +f 2083/324 2082/312 273/248 +f 273/248 2082/312 50/249 +f 297/325 216/310 2058/326 +f 2058/326 216/310 190/311 +f 217/316 297/325 191/318 +f 191/318 297/325 2058/326 +f 2086/314 2083/324 49/247 +f 49/247 2083/324 273/248 +f 183/264 2065/327 57/148 +f 57/148 2065/327 2066/149 +f 212/328 2052/329 292/330 +f 292/330 2052/329 2051/331 +f 292/330 2051/331 210/284 +f 210/284 2051/331 187/286 +f 2065/327 183/264 2074/282 +f 2074/282 183/264 56/261 +f 299/332 283/333 212/328 +f 212/328 283/333 2052/329 +f 324/334 323/335 299/332 +f 299/332 323/335 283/333 +f 71/336 193/337 58/133 +f 58/133 193/337 140/131 +f 60/130 140/131 72/338 +f 72/338 140/131 193/337 +f 70/339 194/340 61/147 +f 61/147 194/340 144/146 +f 58/133 144/146 71/336 +f 71/336 144/146 194/340 +f 195/341 171/217 74/342 +f 74/342 171/217 64/219 +f 171/228 195/343 63/227 +f 63/227 195/343 75/344 +f 72/338 206/345 60/130 +f 60/130 206/345 205/128 +f 63/227 75/344 174/224 +f 174/224 75/344 196/346 +f 174/224 196/346 65/226 +f 65/226 196/346 76/347 +f 65/226 76/347 177/236 +f 177/236 76/347 197/348 +f 66/235 177/236 77/349 +f 77/349 177/236 197/348 +f 78/350 198/351 68/243 +f 68/243 198/351 179/241 +f 67/239 179/240 79/352 +f 79/352 179/240 198/353 +f 77/349 199/354 66/235 +f 66/235 199/354 180/244 +f 68/243 180/244 78/350 +f 78/350 180/244 199/354 +f 200/355 184/268 73/356 +f 73/356 184/268 69/270 +f 70/339 61/147 200/355 +f 200/355 61/147 184/268 +f 73/356 69/270 201/357 +f 201/357 69/270 202/269 +f 201/357 202/269 339/358 +f 339/358 202/269 338/359 +f 338/359 67/239 339/358 +f 339/358 67/239 79/352 +f 284/267 337/360 202/269 +f 202/269 337/360 338/359 +f 178/238 337/360 62/231 +f 62/231 337/360 336/361 +f 335/362 336/361 285/144 +f 285/144 336/361 203/145 +f 335/362 334/363 175/230 +f 175/230 334/363 59/222 +f 333/364 334/363 286/129 +f 286/129 334/363 204/132 +f 333/364 332/365 172/220 +f 172/220 332/365 64/219 +f 332/365 205/128 331/366 +f 331/366 205/128 206/345 +f 332/365 331/366 64/219 +f 64/219 331/366 74/342 +f 330/367 293/299 2055/368 +f 2055/368 293/299 2053/300 +f 2078/369 329/370 2077/288 +f 2077/288 329/370 53/255 +f 329/370 328/371 53/255 +f 53/255 328/371 48/213 +f 327/372 328/371 288/215 +f 288/215 328/371 207/216 +f 169/212 327/372 47/206 +f 47/206 327/372 326/373 +f 325/374 326/373 289/208 +f 289/208 326/373 208/209 +f 290/283 209/275 194/340 +f 194/340 209/275 71/336 +f 210/284 290/283 70/339 +f 70/339 290/283 194/340 +f 211/278 293/299 72/338 +f 72/338 293/299 206/345 +f 209/275 291/274 71/336 +f 71/336 291/274 193/337 +f 291/274 211/278 193/337 +f 193/337 211/278 72/338 +f 200/355 292/330 70/339 +f 70/339 292/330 210/284 +f 292/330 200/355 212/328 +f 212/328 200/355 73/356 +f 293/299 330/367 206/345 +f 206/345 330/367 331/366 +f 2054/290 213/289 2055/368 +f 2055/368 213/289 330/367 +f 196/346 294/302 76/347 +f 76/347 294/302 214/304 +f 294/302 196/346 215/295 +f 215/295 196/346 75/344 +f 295/291 195/341 213/289 +f 213/289 195/341 74/342 +f 195/343 295/293 75/344 +f 75/344 295/293 215/295 +f 296/308 197/348 214/304 +f 214/304 197/348 76/347 +f 296/308 216/310 197/348 +f 197/348 216/310 77/349 +f 216/310 297/325 77/349 +f 77/349 297/325 199/354 +f 297/325 217/316 199/354 +f 199/354 217/316 78/350 +f 324/334 218/319 323/335 +f 323/335 218/319 192/321 +f 218/319 324/334 79/352 +f 79/352 324/334 339/358 +f 217/316 298/315 78/350 +f 78/350 298/315 198/351 +f 298/320 218/319 198/353 +f 198/353 218/319 79/352 +f 201/357 299/332 73/356 +f 73/356 299/332 212/328 +f 248/37 302/375 101/39 +f 101/39 302/375 221/376 +f 302/375 248/37 222/377 +f 222/377 248/37 13/38 +f 108/50 303/378 107/51 +f 107/51 303/378 223/379 +f 303/378 108/50 221/376 +f 221/376 108/50 101/39 +f 112/56 304/380 111/57 +f 111/57 304/380 224/381 +f 304/380 112/56 223/379 +f 223/379 112/56 107/51 +f 305/382 225/383 116/62 +f 116/62 225/383 115/63 +f 224/381 305/382 111/57 +f 111/57 305/382 116/62 +f 306/384 226/385 120/68 +f 120/68 226/385 119/69 +f 225/383 306/384 115/63 +f 115/63 306/384 120/68 +f 307/386 227/387 123/74 +f 123/74 227/387 122/75 +f 226/385 307/386 119/69 +f 119/69 307/386 123/74 +f 347/388 346/389 153/168 +f 153/168 346/389 152/169 +f 153/168 122/75 347/388 +f 347/388 122/75 227/387 +f 345/390 344/391 242/187 +f 242/187 344/391 243/189 +f 242/187 152/169 345/390 +f 345/390 152/169 346/389 +f 168/210 316/392 13/38 +f 13/38 316/392 222/377 +f 317/393 128/91 233/199 +f 233/199 128/91 31/93 +f 128/91 317/393 46/92 +f 46/92 317/393 163/394 +f 317/393 236/395 163/394 +f 163/394 236/395 80/396 +f 317/393 233/199 236/395 +f 236/395 233/199 81/200 +f 235/397 318/398 81/200 +f 81/200 318/398 236/395 +f 318/398 234/399 236/395 +f 236/395 234/399 80/396 +f 234/399 318/398 4/400 +f 4/400 318/398 88/401 +f 318/398 235/397 88/401 +f 88/401 235/397 3/402 +f 237/403 319/404 82/194 +f 82/194 319/404 238/201 +f 319/404 235/397 238/201 +f 238/201 235/397 81/200 +f 235/397 319/404 3/402 +f 3/402 319/404 87/405 +f 319/404 237/403 87/405 +f 87/405 237/403 2/406 +f 320/407 237/403 240/195 +f 240/195 237/403 82/194 +f 237/403 320/407 2/406 +f 2/406 320/407 86/408 +f 86/408 320/407 1/409 +f 1/409 320/407 162/410 +f 320/407 240/195 162/410 +f 162/410 240/195 83/197 +f 1/409 162/410 5/411 +f 5/411 162/410 241/412 +f 162/410 83/197 241/412 +f 241/412 83/197 84/190 +f 243/189 159/413 84/190 +f 84/190 159/413 241/412 +f 241/412 159/413 5/411 +f 5/411 159/413 85/414 +f 344/391 343/415 243/189 +f 243/189 343/415 159/413 +f 343/415 89/416 159/413 +f 159/413 89/416 85/414 +f 145/150 2067/151 322/271 +f 322/271 2067/151 2068/417 +f 322/271 181/246 340/77 +f 340/77 181/246 26/84 +f 2068/417 2084/323 322/271 +f 322/271 2084/323 181/246 +f 326/373 325/374 47/206 +f 47/206 325/374 166/204 +f 326/373 327/372 208/209 +f 208/209 327/372 288/215 +f 48/213 328/371 169/212 +f 169/212 328/371 327/372 +f 328/371 329/370 207/216 +f 207/216 329/370 287/256 +f 287/256 329/370 2073/298 +f 2073/298 329/370 2078/369 +f 331/366 330/367 74/342 +f 74/342 330/367 213/289 +f 332/365 333/364 205/128 +f 205/128 333/364 286/129 +f 334/363 333/364 59/222 +f 59/222 333/364 172/220 +f 334/363 335/362 204/132 +f 204/132 335/362 285/144 +f 62/231 336/361 175/230 +f 175/230 336/361 335/362 +f 203/145 336/361 284/267 +f 284/267 336/361 337/360 +f 67/239 338/359 178/238 +f 178/238 338/359 337/360 +f 339/358 324/334 201/357 +f 201/357 324/334 299/332 +f 300/19 219/20 2018/418 +f 2018/418 219/20 2023/419 +f 219/20 301/34 2023/419 +f 2023/419 301/34 2022/420 +f 301/34 220/36 2022/420 +f 2022/420 220/36 2024/421 +f 302/375 675/422 221/376 +f 221/376 675/422 593/423 +f 222/377 594/424 302/375 +f 302/375 594/424 675/422 +f 303/378 676/425 223/379 +f 223/379 676/425 595/426 +f 303/378 221/376 676/425 +f 676/425 221/376 593/423 +f 304/380 677/427 224/381 +f 224/381 677/427 596/428 +f 223/379 595/426 304/380 +f 304/380 595/426 677/427 +f 305/382 678/429 225/383 +f 225/383 678/429 597/430 +f 224/381 596/428 305/382 +f 305/382 596/428 678/429 +f 306/384 679/431 226/385 +f 226/385 679/431 598/432 +f 225/383 597/430 306/384 +f 306/384 597/430 679/431 +f 227/387 307/386 599/433 +f 599/433 307/386 680/434 +f 226/385 598/432 307/386 +f 307/386 598/432 680/434 +f 2038/435 681/436 2037/437 +f 2037/437 681/436 600/438 +f 2039/439 601/440 2038/435 +f 2038/435 601/440 681/436 +f 2040/441 609/442 2039/439 +f 2039/439 609/442 601/440 +f 32/96 309/97 2034/443 +f 2034/443 309/97 2020/444 +f 682/445 2020/446 603/447 +f 603/447 2020/446 2021/448 +f 35/108 310/109 2032/449 +f 2032/449 310/109 2033/450 +f 310/109 32/96 2033/450 +f 2033/450 32/96 2034/443 +f 220/36 311/125 2024/421 +f 2024/421 311/125 2025/451 +f 605/452 2026/453 685/454 +f 685/454 2026/453 2027/455 +f 2036/456 686/457 2035/458 +f 2035/458 686/457 606/459 +f 2037/437 600/438 2036/460 +f 2036/460 600/438 686/461 +f 347/388 1972/462 346/389 +f 346/389 1972/462 1971/463 +f 227/387 599/433 347/388 +f 347/388 599/433 1972/462 +f 2017/464 687/465 2021/448 +f 2021/448 687/465 603/447 +f 2035/458 606/459 2017/464 +f 2017/464 606/459 687/465 +f 230/177 314/178 2028/466 +f 2028/466 314/178 2029/467 +f 314/178 231/180 2029/467 +f 2029/467 231/180 2030/468 +f 231/180 315/186 2030/468 +f 2030/468 315/186 2031/469 +f 315/186 35/108 2031/469 +f 2031/469 35/108 2032/449 +f 345/390 1970/470 344/391 +f 344/391 1970/470 1969/471 +f 346/389 1971/463 345/390 +f 345/390 1971/463 1970/470 +f 2041/472 690/473 2040/441 +f 2040/441 690/473 609/442 +f 2042/474 613/475 2041/472 +f 2041/472 613/475 690/473 +f 316/392 691/476 222/377 +f 222/377 691/476 594/424 +f 2043/477 693/478 2042/474 +f 2042/474 693/478 613/475 +f 2044/479 435/480 2043/477 +f 2043/477 435/480 693/478 +f 89/416 343/415 436/481 +f 436/481 343/415 1968/482 +f 343/415 344/391 1968/482 +f 1968/482 344/391 1969/471 +f 437/483 354/484 623/485 +f 623/485 354/484 438/486 +f 353/487 437/483 439/488 +f 439/488 437/483 623/485 +f 440/489 353/487 624/490 +f 624/490 353/487 439/488 +f 355/491 440/489 441/492 +f 441/492 440/489 624/490 +f 357/493 442/494 356/495 +f 356/495 442/494 444/496 +f 442/494 358/497 444/496 +f 444/496 358/497 443/498 +f 354/484 437/483 445/499 +f 445/499 437/483 625/500 +f 437/483 353/487 625/500 +f 625/500 353/487 446/501 +f 360/502 447/503 446/501 +f 446/501 447/503 625/500 +f 447/503 359/504 625/500 +f 625/500 359/504 445/499 +f 353/487 440/489 446/501 +f 446/501 440/489 626/505 +f 440/489 355/491 626/505 +f 626/505 355/491 448/506 +f 361/507 449/508 448/506 +f 448/506 449/508 626/505 +f 449/508 360/502 626/505 +f 626/505 360/502 446/501 +f 358/497 442/494 362/509 +f 362/509 442/494 627/510 +f 442/494 357/493 627/510 +f 627/510 357/493 450/511 +f 364/512 451/513 363/514 +f 363/514 451/513 452/515 +f 451/513 357/493 452/515 +f 452/515 357/493 356/495 +f 359/504 447/503 453/516 +f 453/516 447/503 628/517 +f 447/503 360/502 628/517 +f 628/517 360/502 454/518 +f 366/519 455/520 454/518 +f 454/518 455/520 628/517 +f 455/520 365/521 628/517 +f 628/517 365/521 453/516 +f 357/493 451/513 450/511 +f 450/511 451/513 457/522 +f 451/513 364/512 457/522 +f 457/522 364/512 456/523 +f 458/524 364/512 459/525 +f 459/525 364/512 363/514 +f 367/526 458/524 368/527 +f 368/527 458/524 459/525 +f 364/512 458/524 456/523 +f 456/523 458/524 461/528 +f 458/524 367/526 461/528 +f 461/528 367/526 460/529 +f 370/530 369/531 462/532 +f 462/532 369/531 463/533 +f 463/533 367/526 462/532 +f 462/532 367/526 368/527 +f 367/526 463/533 460/529 +f 460/529 463/533 465/534 +f 369/531 464/535 463/533 +f 463/533 464/535 465/534 +f 372/536 371/537 466/538 +f 466/538 371/537 467/539 +f 369/531 370/530 467/539 +f 467/539 370/530 466/538 +f 464/535 369/531 469/540 +f 469/540 369/531 467/539 +f 371/537 468/541 467/539 +f 467/539 468/541 469/540 +f 373/542 374/543 629/544 +f 629/544 374/543 470/545 +f 371/537 372/536 470/545 +f 470/545 372/536 629/544 +f 468/541 371/537 472/546 +f 472/546 371/537 470/545 +f 374/543 471/547 470/545 +f 470/545 471/547 472/546 +f 376/548 473/549 1965/550 +f 1965/550 473/549 1966/551 +f 473/549 378/552 1966/551 +f 1966/551 378/552 1967/553 +f 377/554 474/555 1967/553 +f 1967/553 474/555 1966/551 +f 474/555 375/556 1966/551 +f 1966/551 375/556 1965/550 +f 379/557 476/558 475/559 +f 475/559 476/558 630/560 +f 476/558 380/561 630/560 +f 630/560 380/561 477/562 +f 378/552 473/549 477/562 +f 477/562 473/549 630/560 +f 473/549 376/548 630/560 +f 630/560 376/548 475/559 +f 477/562 380/561 478/563 +f 478/563 380/561 400/564 +f 477/562 478/563 378/552 +f 378/552 478/563 381/565 +f 479/566 382/567 631/568 +f 631/568 382/567 480/569 +f 480/570 383/571 631/572 +f 631/572 383/571 481/573 +f 385/574 482/575 481/573 +f 481/573 482/575 631/572 +f 384/576 479/566 482/577 +f 482/577 479/566 631/568 +f 483/578 386/579 632/580 +f 632/580 386/579 484/581 +f 382/567 479/566 484/581 +f 484/581 479/566 632/580 +f 479/566 384/576 632/580 +f 632/580 384/576 485/582 +f 387/583 483/578 485/582 +f 485/582 483/578 632/580 +f 486/584 387/583 633/585 +f 633/585 387/583 485/582 +f 384/576 487/586 485/582 +f 485/582 487/586 633/585 +f 487/586 388/587 633/585 +f 633/585 388/587 512/588 +f 391/589 486/584 512/588 +f 512/588 486/584 633/585 +f 488/590 392/591 634/592 +f 634/592 392/591 489/593 +f 489/593 389/594 634/592 +f 634/592 389/594 490/595 +f 387/583 486/584 490/595 +f 490/595 486/584 634/592 +f 391/589 488/590 486/584 +f 486/584 488/590 634/592 +f 360/502 449/508 454/518 +f 454/518 449/508 635/596 +f 449/508 361/507 635/596 +f 635/596 361/507 491/597 +f 390/598 492/599 491/597 +f 491/597 492/599 635/596 +f 492/599 366/519 635/596 +f 635/596 366/519 454/518 +f 576/600 406/601 660/602 +f 660/602 406/601 493/603 +f 404/604 575/605 493/603 +f 493/603 575/605 660/602 +f 455/520 366/519 636/606 +f 636/606 366/519 494/607 +f 392/608 488/609 494/607 +f 494/607 488/609 636/606 +f 488/609 391/610 636/606 +f 636/606 391/610 495/611 +f 365/521 455/520 495/611 +f 495/611 455/520 636/606 +f 492/599 390/598 637/612 +f 637/612 390/598 496/613 +f 393/614 509/615 496/613 +f 496/613 509/615 637/612 +f 509/615 392/608 637/612 +f 637/612 392/608 494/607 +f 366/519 492/599 494/607 +f 494/607 492/599 637/612 +f 407/616 574/617 497/618 +f 497/618 574/617 659/619 +f 575/605 404/604 659/619 +f 659/619 404/604 497/618 +f 2088/620 2089/621 652/622 +f 652/622 2089/621 553/623 +f 498/624 379/557 638/625 +f 638/625 379/557 475/559 +f 376/548 499/626 475/559 +f 475/559 499/626 638/625 +f 499/627 395/628 638/629 +f 638/629 395/628 500/630 +f 396/631 498/632 500/630 +f 500/630 498/632 638/629 +f 398/633 397/634 501/635 +f 501/635 397/634 502/636 +f 374/543 373/542 502/636 +f 502/636 373/542 501/635 +f 503/637 375/556 639/638 +f 639/638 375/556 474/555 +f 377/554 504/639 474/555 +f 474/555 504/639 639/638 +f 504/639 398/633 639/638 +f 639/638 398/633 501/635 +f 373/542 503/637 501/635 +f 501/635 503/637 639/638 +f 471/547 374/543 506/640 +f 506/640 374/543 502/636 +f 397/634 505/641 502/636 +f 502/636 505/641 506/640 +f 610/642 1967/553 381/565 +f 381/565 1967/553 378/552 +f 399/643 377/554 610/642 +f 610/642 377/554 1967/553 +f 507/644 385/574 640/645 +f 640/645 385/574 481/573 +f 383/571 508/646 481/573 +f 481/573 508/646 640/645 +f 508/646 396/631 640/645 +f 640/645 396/631 500/630 +f 395/628 507/644 500/630 +f 500/630 507/644 640/645 +f 509/647 393/648 641/649 +f 641/649 393/648 510/650 +f 401/651 511/652 510/650 +f 510/650 511/652 641/649 +f 511/652 389/594 641/649 +f 641/649 389/594 489/593 +f 392/591 509/647 489/593 +f 489/593 509/647 641/649 +f 507/644 395/628 642/653 +f 642/653 395/628 539/654 +f 388/587 487/586 539/655 +f 539/655 487/586 642/656 +f 384/576 482/577 487/586 +f 487/586 482/577 642/656 +f 482/575 385/574 642/653 +f 642/653 385/574 507/644 +f 389/594 511/652 490/595 +f 490/595 511/652 643/657 +f 511/652 401/651 643/657 +f 643/657 401/651 513/658 +f 386/579 483/578 513/658 +f 513/658 483/578 643/657 +f 483/578 387/583 643/657 +f 643/657 387/583 490/595 +f 505/641 397/634 621/659 +f 621/659 397/634 514/660 +f 430/661 622/662 514/660 +f 514/660 622/662 621/659 +f 399/643 517/663 516/664 +f 516/664 517/663 644/665 +f 517/663 428/666 644/665 +f 644/665 428/666 619/667 +f 619/667 429/668 644/665 +f 644/665 429/668 618/669 +f 618/669 398/633 644/665 +f 644/665 398/633 516/664 +f 429/668 430/661 618/669 +f 618/669 430/661 514/660 +f 397/634 398/633 514/660 +f 514/660 398/633 618/669 +f 517/663 399/643 645/670 +f 645/670 399/643 610/642 +f 381/565 611/671 610/642 +f 610/642 611/671 645/670 +f 611/671 427/672 645/670 +f 645/670 427/672 617/673 +f 428/666 517/663 617/673 +f 617/673 517/663 645/670 +f 358/497 521/674 443/498 +f 443/498 521/674 520/675 +f 522/676 402/677 646/678 +f 646/678 402/677 523/679 +f 443/498 520/675 523/679 +f 523/679 520/675 646/678 +f 354/484 580/680 438/486 +f 438/486 580/680 662/681 +f 521/674 358/497 524/682 +f 524/682 358/497 362/509 +f 402/677 525/683 523/679 +f 523/679 525/683 647/684 +f 525/683 403/685 647/684 +f 647/684 403/685 526/686 +f 356/495 444/496 526/686 +f 526/686 444/496 647/684 +f 444/496 443/498 647/684 +f 647/684 443/498 523/679 +f 580/680 354/484 661/687 +f 661/687 354/484 445/499 +f 359/504 579/688 445/499 +f 445/499 579/688 661/687 +f 527/689 410/690 648/691 +f 648/691 410/690 528/692 +f 405/693 529/694 528/692 +f 528/692 529/694 648/691 +f 411/695 530/696 529/697 +f 529/697 530/696 648/698 +f 530/696 409/699 648/698 +f 648/698 409/699 527/700 +f 529/694 405/693 649/701 +f 649/701 405/693 531/702 +f 408/703 532/704 531/702 +f 531/702 532/704 649/701 +f 532/705 412/706 649/707 +f 649/707 412/706 533/708 +f 533/708 411/695 649/707 +f 649/707 411/695 529/697 +f 532/704 408/703 650/709 +f 650/709 408/703 534/710 +f 534/710 413/711 650/709 +f 650/709 413/711 535/712 +f 535/713 414/714 650/715 +f 650/715 414/714 536/716 +f 412/706 532/705 536/716 +f 536/716 532/705 650/715 +f 375/556 503/637 394/717 +f 394/717 503/637 538/718 +f 503/637 373/542 538/718 +f 538/718 373/542 629/544 +f 372/536 537/719 629/544 +f 629/544 537/719 538/718 +f 377/554 399/643 504/639 +f 504/639 399/643 516/664 +f 516/664 398/633 504/639 +f 537/719 372/536 541/720 +f 541/720 372/536 466/538 +f 370/530 540/721 466/538 +f 466/538 540/721 541/720 +f 540/721 370/530 543/722 +f 543/722 370/530 462/532 +f 462/532 368/527 543/722 +f 543/722 368/527 542/723 +f 368/527 459/525 542/723 +f 542/723 459/525 545/724 +f 459/525 363/514 545/724 +f 545/724 363/514 544/725 +f 403/685 546/726 526/686 +f 526/686 546/726 547/727 +f 363/514 452/515 544/725 +f 544/725 452/515 547/727 +f 452/515 356/495 547/727 +f 547/727 356/495 526/686 +f 579/688 359/504 578/728 +f 578/728 359/504 453/516 +f 365/521 548/729 453/516 +f 453/516 548/729 578/728 +f 548/729 365/521 550/730 +f 550/730 365/521 495/611 +f 391/610 549/731 495/611 +f 495/611 549/731 550/730 +f 388/732 551/733 512/734 +f 512/734 551/733 552/735 +f 549/731 391/610 552/735 +f 552/735 391/610 512/734 +f 388/732 539/736 551/733 +f 551/733 539/736 651/737 +f 539/736 395/738 651/737 +f 651/737 395/738 553/623 +f 407/616 554/739 574/617 +f 574/617 554/739 658/740 +f 573/741 658/740 415/742 +f 415/742 658/740 554/739 +f 395/738 499/626 553/623 +f 553/623 499/626 652/622 +f 1946/743 652/622 1965/550 +f 652/622 499/626 1965/550 +f 499/626 376/548 1965/550 +f 549/731 2092/744 550/730 +f 550/730 2092/744 2093/745 +f 664/746 653/747 581/748 +f 581/748 653/747 555/749 +f 583/750 556/751 664/746 +f 664/746 556/751 653/747 +f 2094/752 548/729 2093/745 +f 2093/745 548/729 550/730 +f 551/733 2096/753 552/735 +f 552/735 2096/753 2091/754 +f 663/755 654/756 582/757 +f 582/757 654/756 557/758 +f 581/748 555/749 663/755 +f 663/755 555/749 654/756 +f 2092/744 549/731 2091/754 +f 2091/754 549/731 552/735 +f 547/727 546/726 2099/759 +f 2099/759 546/726 2098/760 +f 585/761 668/762 558/763 +f 558/763 668/762 655/764 +f 668/765 587/766 655/767 +f 655/767 587/766 559/768 +f 544/725 547/727 2100/769 +f 2100/769 547/727 2099/759 +f 548/729 2094/752 578/728 +f 578/728 2094/752 2095/770 +f 666/771 2061/772 583/750 +f 583/750 2061/772 556/751 +f 545/724 544/725 2101/773 +f 2101/773 544/725 2100/769 +f 587/766 667/774 559/768 +f 559/768 667/774 2062/775 +f 667/774 586/776 2062/775 +f 2062/775 586/776 560/777 +f 542/723 545/724 2102/778 +f 2102/778 545/724 2101/773 +f 543/722 542/723 2103/779 +f 2103/779 542/723 2102/778 +f 586/776 669/780 560/777 +f 560/777 669/780 2063/781 +f 588/782 561/783 669/780 +f 669/780 561/783 2063/781 +f 2104/784 540/721 2103/779 +f 2103/779 540/721 543/722 +f 537/719 2106/785 538/718 +f 538/718 2106/785 2107/786 +f 671/787 656/788 589/789 +f 589/789 656/788 562/790 +f 590/791 563/792 671/793 +f 671/793 563/792 656/794 +f 2108/795 394/717 2107/786 +f 2107/786 394/717 538/718 +f 540/721 2104/784 541/720 +f 541/720 2104/784 2105/796 +f 670/797 2064/798 588/782 +f 588/782 2064/798 561/783 +f 589/789 562/790 670/797 +f 670/797 562/790 2064/798 +f 2106/785 537/719 2105/796 +f 2105/796 537/719 541/720 +f 651/737 553/623 2090/799 +f 2090/799 553/623 2089/621 +f 584/800 665/801 2059/802 +f 2059/802 665/801 2060/803 +f 665/801 582/757 2060/803 +f 2060/803 582/757 557/758 +f 551/733 651/737 2096/753 +f 2096/753 651/737 2090/799 +f 672/804 584/800 657/805 +f 657/805 584/800 2059/802 +f 1948/806 672/804 1947/807 +f 1947/807 672/804 657/805 +f 417/808 404/604 564/809 +f 564/809 404/604 493/603 +f 564/809 493/603 418/810 +f 418/810 493/603 406/601 +f 416/811 407/616 565/812 +f 565/812 407/616 497/618 +f 565/812 497/618 417/808 +f 417/808 497/618 404/604 +f 566/813 420/814 527/689 +f 527/689 420/814 410/690 +f 527/700 409/699 566/815 +f 566/815 409/699 421/816 +f 418/810 406/601 577/817 +f 577/817 406/601 576/600 +f 567/818 421/816 530/696 +f 530/696 421/816 409/699 +f 530/696 411/695 567/818 +f 567/818 411/695 422/819 +f 568/820 422/819 533/708 +f 533/708 422/819 411/695 +f 568/820 533/708 423/821 +f 423/821 533/708 412/706 +f 424/822 414/714 569/823 +f 569/823 414/714 535/713 +f 569/824 535/712 425/825 +f 425/825 535/712 413/711 +f 423/821 412/706 570/826 +f 570/826 412/706 536/716 +f 570/826 536/716 424/822 +f 424/822 536/716 414/714 +f 571/827 419/828 554/739 +f 554/739 419/828 415/742 +f 554/739 407/616 571/827 +f 571/827 407/616 416/811 +f 419/828 572/829 415/742 +f 415/742 572/829 573/741 +f 572/829 1964/830 573/741 +f 573/741 1964/830 1963/831 +f 425/825 413/711 1964/830 +f 1964/830 413/711 1963/831 +f 658/740 573/741 1962/832 +f 1962/832 573/741 1963/831 +f 408/703 1961/833 534/710 +f 534/710 1961/833 1962/832 +f 574/617 1961/833 659/619 +f 659/619 1961/833 1960/834 +f 405/693 1959/835 531/702 +f 531/702 1959/835 1960/834 +f 575/605 1959/835 660/602 +f 660/602 1959/835 1958/836 +f 410/690 1957/837 528/692 +f 528/692 1957/837 1958/836 +f 577/817 576/600 1956/838 +f 1956/838 576/600 1957/837 +f 420/814 1956/838 410/690 +f 410/690 1956/838 1957/837 +f 2061/772 666/771 1954/839 +f 1954/839 666/771 1955/840 +f 546/726 1953/841 2098/760 +f 2098/760 1953/841 2097/842 +f 403/685 1952/843 546/726 +f 546/726 1952/843 1953/841 +f 579/688 1952/843 661/687 +f 661/687 1952/843 1951/844 +f 525/683 402/677 1951/844 +f 1951/844 402/677 1950/845 +f 580/680 1950/845 662/681 +f 662/681 1950/845 1949/846 +f 417/808 581/748 565/812 +f 565/812 581/748 663/755 +f 582/757 416/811 663/755 +f 663/755 416/811 565/812 +f 583/750 418/810 666/771 +f 666/771 418/810 577/817 +f 581/748 417/808 664/746 +f 664/746 417/808 564/809 +f 418/810 583/750 564/809 +f 564/809 583/750 664/746 +f 571/827 416/811 665/801 +f 665/801 416/811 582/757 +f 419/828 571/827 584/800 +f 584/800 571/827 665/801 +f 666/771 577/817 1955/840 +f 1955/840 577/817 1956/838 +f 1955/840 585/761 1954/839 +f 1954/839 585/761 558/763 +f 567/818 422/819 667/774 +f 667/774 422/819 586/776 +f 421/816 567/818 587/766 +f 587/766 567/818 667/774 +f 420/814 566/813 585/761 +f 585/761 566/813 668/762 +f 566/815 421/816 668/765 +f 668/765 421/816 587/766 +f 422/819 568/820 586/776 +f 586/776 568/820 669/780 +f 423/821 588/782 568/820 +f 568/820 588/782 669/780 +f 588/782 423/821 670/797 +f 670/797 423/821 570/826 +f 424/822 589/789 570/826 +f 570/826 589/789 670/797 +f 1948/806 1947/807 590/791 +f 590/791 1947/807 563/792 +f 590/791 425/825 1948/806 +f 1948/806 425/825 1964/830 +f 589/789 424/822 671/787 +f 671/787 424/822 569/823 +f 425/825 590/791 569/824 +f 569/824 590/791 671/793 +f 572/829 419/828 672/804 +f 672/804 419/828 584/800 +f 591/847 355/491 673/848 +f 673/848 355/491 441/492 +f 355/491 591/847 448/506 +f 448/506 591/847 674/849 +f 592/850 361/507 674/849 +f 674/849 361/507 448/506 +f 627/510 450/511 675/422 +f 675/422 450/511 593/423 +f 362/509 627/510 594/424 +f 594/424 627/510 675/422 +f 457/522 456/523 676/425 +f 676/425 456/523 595/426 +f 450/511 457/522 593/423 +f 593/423 457/522 676/425 +f 460/529 596/428 461/528 +f 461/528 596/428 677/427 +f 456/523 461/528 595/426 +f 595/426 461/528 677/427 +f 464/535 597/430 465/534 +f 465/534 597/430 678/429 +f 596/428 460/529 678/429 +f 678/429 460/529 465/534 +f 468/541 598/432 469/540 +f 469/540 598/432 679/431 +f 597/430 464/535 679/431 +f 679/431 464/535 469/540 +f 471/547 599/433 472/546 +f 472/546 599/433 680/434 +f 598/432 468/541 680/434 +f 680/434 468/541 472/546 +f 476/558 379/557 681/436 +f 681/436 379/557 600/438 +f 380/561 476/558 601/440 +f 601/440 476/558 681/436 +f 380/561 601/440 400/564 +f 400/564 601/440 609/442 +f 382/567 602/851 480/569 +f 480/569 602/851 682/852 +f 603/447 383/571 682/445 +f 682/445 383/571 480/570 +f 386/579 604/853 484/581 +f 484/581 604/853 683/854 +f 602/851 382/567 683/854 +f 683/854 382/567 484/581 +f 361/507 592/850 491/597 +f 491/597 592/850 684/855 +f 605/452 390/598 684/855 +f 684/855 390/598 491/597 +f 390/598 605/452 496/613 +f 496/613 605/452 685/454 +f 607/856 393/614 685/454 +f 685/454 393/614 496/613 +f 396/631 606/459 498/632 +f 498/632 606/459 686/457 +f 600/438 379/557 686/461 +f 686/461 379/557 498/624 +f 505/641 1971/463 506/640 +f 506/640 1971/463 1972/462 +f 506/640 1972/462 471/547 +f 471/547 1972/462 599/433 +f 508/646 383/571 687/465 +f 687/465 383/571 603/447 +f 396/631 508/646 606/459 +f 606/459 508/646 687/465 +f 393/648 607/857 510/650 +f 510/650 607/857 688/858 +f 608/859 401/651 688/858 +f 688/858 401/651 510/650 +f 401/651 608/859 513/658 +f 513/658 608/859 689/860 +f 604/853 386/579 689/860 +f 689/860 386/579 513/658 +f 622/662 1969/471 621/659 +f 621/659 1969/471 1970/470 +f 621/659 1970/470 505/641 +f 505/641 1970/470 1971/463 +f 400/564 609/442 519/861 +f 519/861 609/442 690/473 +f 613/475 426/862 690/473 +f 690/473 426/862 519/861 +f 524/682 362/509 691/476 +f 691/476 362/509 594/424 +f 381/565 478/563 611/671 +f 611/671 478/563 692/863 +f 478/563 400/564 692/863 +f 692/863 400/564 519/861 +f 426/862 615/864 519/861 +f 519/861 615/864 692/863 +f 615/864 427/672 692/863 +f 692/863 427/672 611/671 +f 426/862 613/475 612/865 +f 612/865 613/475 693/478 +f 351/866 612/865 435/480 +f 435/480 612/865 693/478 +f 427/672 615/864 614/867 +f 614/867 615/864 694/868 +f 615/864 426/862 694/868 +f 694/868 426/862 612/865 +f 351/866 434/869 612/865 +f 612/865 434/869 694/868 +f 350/870 614/867 434/869 +f 434/869 614/867 694/868 +f 616/871 428/666 695/872 +f 695/872 428/666 617/673 +f 617/673 427/672 695/872 +f 695/872 427/672 614/867 +f 350/870 433/873 614/867 +f 614/867 433/873 695/872 +f 433/873 349/874 695/872 +f 695/872 349/874 616/871 +f 619/667 428/666 696/875 +f 696/875 428/666 616/871 +f 349/874 432/876 616/871 +f 616/871 432/876 696/875 +f 432/876 348/877 696/875 +f 696/875 348/877 518/878 +f 429/668 619/667 518/878 +f 518/878 619/667 696/875 +f 348/877 352/879 518/878 +f 518/878 352/879 620/880 +f 430/661 429/668 620/880 +f 620/880 429/668 518/878 +f 622/662 430/661 515/881 +f 515/881 430/661 620/880 +f 352/879 431/882 620/880 +f 620/880 431/882 515/881 +f 1969/471 622/662 1968/482 +f 1968/482 622/662 515/881 +f 431/882 436/481 515/881 +f 515/881 436/481 1968/482 +f 698/883 711/884 697/885 +f 697/885 711/884 710/886 +f 699/887 712/888 698/883 +f 698/883 712/888 711/884 +f 700/889 713/890 699/887 +f 699/887 713/890 712/888 +f 701/891 714/892 700/889 +f 700/889 714/892 713/890 +f 702/893 715/894 701/891 +f 701/891 715/894 714/892 +f 703/895 716/896 702/893 +f 702/893 716/896 715/894 +f 704/897 717/898 703/895 +f 703/895 717/898 716/896 +f 705/899 718/900 704/897 +f 704/897 718/900 717/898 +f 705/899 706/901 718/900 +f 718/900 706/901 719/902 +f 706/901 707/903 719/902 +f 719/902 707/903 720/904 +f 708/905 721/906 707/903 +f 707/903 721/906 720/904 +f 1332/907 722/908 708/905 +f 708/905 722/908 721/906 +f 709/909 723/910 1981/911 +f 1981/911 723/910 1980/912 +f 711/884 725/913 710/886 +f 710/886 725/913 724/914 +f 712/888 726/915 711/884 +f 711/884 726/915 725/913 +f 713/890 727/916 712/888 +f 712/888 727/916 726/915 +f 714/892 728/917 713/890 +f 713/890 728/917 727/916 +f 715/894 729/918 714/892 +f 714/892 729/918 728/917 +f 716/896 730/919 715/894 +f 715/894 730/919 729/918 +f 717/898 731/920 716/896 +f 716/896 731/920 730/919 +f 718/900 732/921 717/898 +f 717/898 732/921 731/920 +f 718/900 719/902 732/921 +f 732/921 719/902 733/922 +f 719/902 720/904 733/922 +f 733/922 720/904 734/923 +f 721/906 735/924 720/904 +f 720/904 735/924 734/923 +f 1334/925 722/908 1983/926 +f 1983/926 722/908 1980/912 +f 725/913 738/927 724/914 +f 724/914 738/927 737/928 +f 726/915 739/929 725/913 +f 725/913 739/929 738/927 +f 727/916 740/930 726/915 +f 726/915 740/930 739/929 +f 728/917 741/931 727/916 +f 727/916 741/931 740/930 +f 729/918 742/932 728/917 +f 728/917 742/932 741/931 +f 730/919 743/933 729/918 +f 729/918 743/933 742/932 +f 731/920 744/934 730/919 +f 730/919 744/934 743/933 +f 732/921 745/935 731/920 +f 731/920 745/935 744/934 +f 732/921 733/922 745/935 +f 745/935 733/922 746/936 +f 734/923 747/937 733/922 +f 733/922 747/937 746/936 +f 735/924 748/938 734/923 +f 734/923 748/938 747/937 +f 1335/939 1334/925 1984/940 +f 1984/940 1334/925 1983/926 +f 737/928 738/927 750/941 +f 750/941 738/927 751/942 +f 739/929 752/943 738/927 +f 738/927 752/943 751/942 +f 740/930 753/944 739/929 +f 739/929 753/944 752/943 +f 741/931 754/945 740/930 +f 740/930 754/945 753/944 +f 742/932 755/946 741/931 +f 741/931 755/946 754/945 +f 743/933 756/947 742/932 +f 742/932 756/947 755/946 +f 744/934 757/948 743/933 +f 743/933 757/948 756/947 +f 745/935 758/949 744/934 +f 744/934 758/949 757/948 +f 745/935 746/936 758/949 +f 758/949 746/936 759/950 +f 747/937 760/951 746/936 +f 746/936 760/951 759/950 +f 748/938 761/952 747/937 +f 747/937 761/952 760/951 +f 1336/953 1335/939 1985/954 +f 1985/954 1335/939 1984/940 +f 750/941 751/942 763/955 +f 763/955 751/942 764/956 +f 752/943 765/957 751/942 +f 751/942 765/957 764/956 +f 753/944 766/958 752/943 +f 752/943 766/958 765/957 +f 754/945 767/959 753/944 +f 753/944 767/959 766/958 +f 755/946 768/960 754/945 +f 754/945 768/960 767/959 +f 756/947 769/961 755/946 +f 755/946 769/961 768/960 +f 757/948 770/962 756/947 +f 756/947 770/962 769/961 +f 758/949 771/963 757/948 +f 757/948 771/963 770/962 +f 758/949 759/950 771/963 +f 771/963 759/950 772/964 +f 760/951 773/965 759/950 +f 759/950 773/965 772/964 +f 761/952 774/966 760/951 +f 760/951 774/966 773/965 +f 1337/967 1336/953 1982/968 +f 1982/968 1336/953 1985/954 +f 763/955 764/956 776/969 +f 776/969 764/956 777/970 +f 765/957 778/971 764/956 +f 764/956 778/971 777/970 +f 766/958 779/972 765/957 +f 765/957 779/972 778/971 +f 767/959 780/973 766/958 +f 766/958 780/973 779/972 +f 768/960 781/974 767/959 +f 767/959 781/974 780/973 +f 769/961 782/975 768/960 +f 768/960 782/975 781/974 +f 770/962 783/976 769/961 +f 769/961 783/976 782/975 +f 771/963 784/977 770/962 +f 770/962 784/977 783/976 +f 771/963 772/964 784/977 +f 784/977 772/964 785/978 +f 772/964 773/965 785/978 +f 785/978 773/965 786/979 +f 774/966 787/980 773/965 +f 773/965 787/980 786/979 +f 3/402 1337/967 88/401 +f 88/401 1337/967 1982/968 +f 776/969 777/970 788/981 +f 788/981 777/970 789/982 +f 778/971 790/983 777/970 +f 777/970 790/983 789/982 +f 779/972 791/984 778/971 +f 778/971 791/984 790/983 +f 780/973 792/985 779/972 +f 779/972 792/985 791/984 +f 781/974 793/986 780/973 +f 780/973 793/986 792/985 +f 782/975 794/987 781/974 +f 781/974 794/987 793/986 +f 783/976 795/988 782/975 +f 782/975 795/988 794/987 +f 784/977 796/989 783/976 +f 783/976 796/989 795/988 +f 784/977 785/978 796/989 +f 796/989 785/978 797/990 +f 785/978 786/979 797/990 +f 797/990 786/979 798/991 +f 787/980 799/992 786/979 +f 786/979 799/992 798/991 +f 788/981 789/982 800/993 +f 800/993 789/982 801/994 +f 790/983 802/995 789/982 +f 789/982 802/995 801/994 +f 791/984 803/996 790/983 +f 790/983 803/996 802/995 +f 792/985 804/997 791/984 +f 791/984 804/997 803/996 +f 793/986 805/998 792/985 +f 792/985 805/998 804/997 +f 794/987 806/999 793/986 +f 793/986 806/999 805/998 +f 795/988 807/1000 794/987 +f 794/987 807/1000 806/999 +f 796/989 808/1001 795/988 +f 795/988 808/1001 807/1000 +f 796/989 797/990 808/1001 +f 808/1001 797/990 809/1002 +f 797/990 798/991 809/1002 +f 809/1002 798/991 810/1003 +f 799/992 811/1004 798/991 +f 798/991 811/1004 810/1003 +f 800/993 801/994 812/1005 +f 812/1005 801/994 813/1006 +f 802/995 814/1007 801/994 +f 801/994 814/1007 813/1006 +f 803/996 815/1008 802/995 +f 802/995 815/1008 814/1007 +f 804/997 816/1009 803/996 +f 803/996 816/1009 815/1008 +f 805/998 817/1010 804/997 +f 804/997 817/1010 816/1009 +f 806/999 818/1011 805/998 +f 805/998 818/1011 817/1010 +f 807/1000 819/1012 806/999 +f 806/999 819/1012 818/1011 +f 808/1001 820/1013 807/1000 +f 807/1000 820/1013 819/1012 +f 808/1001 809/1002 820/1013 +f 820/1013 809/1002 821/1014 +f 809/1002 810/1003 821/1014 +f 821/1014 810/1003 822/1015 +f 811/1004 823/1016 810/1003 +f 810/1003 823/1016 822/1015 +f 812/1005 813/1006 824/1017 +f 824/1017 813/1006 825/1018 +f 814/1007 826/1019 813/1006 +f 813/1006 826/1019 825/1018 +f 815/1008 827/1020 814/1007 +f 814/1007 827/1020 826/1019 +f 816/1009 828/1021 815/1008 +f 815/1008 828/1021 827/1020 +f 817/1010 829/1022 816/1009 +f 816/1009 829/1022 828/1021 +f 818/1011 830/1023 817/1010 +f 817/1010 830/1023 829/1022 +f 819/1012 831/1024 818/1011 +f 818/1011 831/1024 830/1023 +f 819/1012 820/1013 831/1024 +f 831/1024 820/1013 832/1025 +f 820/1013 821/1014 832/1025 +f 832/1025 821/1014 833/1026 +f 821/1014 822/1015 833/1026 +f 833/1026 822/1015 834/1027 +f 822/1015 823/1016 834/1027 +f 834/1027 823/1016 835/1028 +f 824/1017 825/1018 847/1029 +f 847/1029 825/1018 836/1030 +f 826/1019 837/1031 825/1018 +f 825/1018 837/1031 836/1030 +f 827/1020 838/1032 826/1019 +f 826/1019 838/1032 837/1031 +f 828/1021 839/1033 827/1020 +f 827/1020 839/1033 838/1032 +f 829/1022 840/1034 828/1021 +f 828/1021 840/1034 839/1033 +f 830/1023 841/1035 829/1022 +f 829/1022 841/1035 840/1034 +f 831/1024 842/1036 830/1023 +f 830/1023 842/1036 841/1035 +f 831/1024 832/1025 842/1036 +f 842/1036 832/1025 843/1037 +f 832/1025 833/1026 843/1037 +f 843/1037 833/1026 844/1038 +f 834/1027 845/1039 833/1026 +f 833/1026 845/1039 844/1038 +f 834/1027 835/1028 845/1039 +f 845/1039 835/1028 846/1040 +f 847/1029 836/1030 848/1041 +f 848/1041 836/1030 849/1042 +f 836/1030 837/1031 849/1042 +f 849/1042 837/1031 850/1043 +f 837/1031 838/1032 850/1043 +f 850/1043 838/1032 851/1044 +f 838/1032 839/1033 851/1044 +f 851/1044 839/1033 852/1045 +f 840/1034 853/1046 839/1033 +f 839/1033 853/1046 852/1045 +f 841/1035 854/1047 840/1034 +f 840/1034 854/1047 853/1046 +f 842/1036 855/1048 841/1035 +f 841/1035 855/1048 854/1047 +f 842/1036 843/1037 855/1048 +f 855/1048 843/1037 856/1049 +f 844/1038 857/1050 843/1037 +f 843/1037 857/1050 856/1049 +f 845/1039 858/1051 844/1038 +f 844/1038 858/1051 857/1050 +f 846/1040 859/1052 845/1039 +f 845/1039 859/1052 858/1051 +f 848/1041 849/1042 860/1053 +f 860/1053 849/1042 861/1054 +f 849/1042 850/1043 861/1054 +f 861/1054 850/1043 862/1055 +f 850/1043 851/1044 862/1055 +f 862/1055 851/1044 863/1056 +f 854/1047 855/1048 864/1057 +f 856/1049 865/1058 855/1048 +f 855/1048 865/1058 864/1057 +f 857/1050 866/1059 856/1049 +f 856/1049 866/1059 865/1058 +f 858/1051 867/1060 857/1050 +f 857/1050 867/1060 866/1059 +f 859/1052 868/1061 858/1051 +f 858/1051 868/1061 867/1060 +f 860/1053 861/1054 869/1062 +f 869/1062 861/1054 870/1063 +f 861/1054 862/1055 870/1063 +f 870/1063 862/1055 871/1064 +f 869/1062 870/1063 872/1065 +f 872/1065 870/1063 873/1066 +f 871/1064 874/1067 870/1063 +f 870/1063 874/1067 873/1066 +f 876/1068 875/1069 866/1059 +f 866/1059 875/1069 865/1058 +f 867/1060 877/1070 866/1059 +f 866/1059 877/1070 876/1068 +f 868/1061 878/1071 867/1060 +f 867/1060 878/1071 877/1070 +f 872/1065 873/1066 879/1072 +f 879/1072 873/1066 880/1073 +f 874/1067 881/1074 873/1066 +f 873/1066 881/1074 880/1073 +f 882/1075 875/1069 883/1076 +f 883/1076 875/1069 876/1068 +f 876/1068 877/1070 883/1076 +f 883/1076 877/1070 884/1077 +f 878/1071 885/1078 877/1070 +f 877/1070 885/1078 884/1077 +f 879/1072 880/1073 886/1079 +f 886/1079 880/1073 887/1080 +f 880/1073 881/1074 887/1080 +f 887/1080 881/1074 888/1081 +f 890/1082 889/1083 883/1076 +f 883/1076 889/1083 882/1075 +f 883/1076 884/1077 890/1082 +f 890/1082 884/1077 891/1084 +f 885/1078 892/1085 884/1077 +f 884/1077 892/1085 891/1084 +f 886/1079 887/1080 893/1086 +f 893/1086 887/1080 894/1087 +f 887/1080 888/1081 894/1087 +f 894/1087 888/1081 895/1088 +f 889/1083 890/1082 1314/1089 +f 1314/1089 890/1082 896/1090 +f 890/1082 891/1084 896/1090 +f 896/1090 891/1084 897/1091 +f 891/1084 892/1085 897/1091 +f 897/1091 892/1085 1315/1092 +f 892/1085 1338/1093 1315/1092 +f 1315/1092 1338/1093 898/1094 +f 1986/1095 1338/1093 1987/1096 +f 1987/1096 1338/1093 321/1097 +f 893/1086 894/1087 899/1098 +f 899/1098 894/1087 900/1099 +f 894/1087 895/1088 900/1099 +f 900/1099 895/1088 901/1100 +f 1313/1101 1312/1102 906/1103 +f 906/1103 1312/1102 907/1104 +f 698/883 697/885 908/1105 +f 699/887 698/883 908/1105 +f 700/889 699/887 908/1105 +f 701/891 700/889 908/1105 +f 702/893 701/891 908/1105 +f 703/895 702/893 908/1105 +f 704/897 703/895 908/1105 +f 705/899 704/897 908/1105 +f 706/901 705/899 908/1105 +f 707/903 706/901 908/1105 +f 708/905 707/903 908/1105 +f 708/905 908/1105 1332/907 +f 709/909 1981/911 908/1105 +f 942/1106 981/1107 1008/1108 +f 1008/1108 981/1107 1045/1109 +f 1008/1108 1029/1110 942/1106 +f 942/1106 1029/1110 957/1111 +f 943/1112 959/1113 1008/1108 +f 1008/1108 959/1113 1029/1110 +f 1008/1108 1045/1109 943/1112 +f 943/1112 1045/1109 982/1114 +f 944/1115 980/1116 1009/1117 +f 1009/1117 980/1116 1044/1118 +f 1009/1117 1030/1119 944/1115 +f 944/1115 1030/1119 956/1120 +f 942/1106 957/1111 1009/1117 +f 1009/1117 957/1111 1030/1119 +f 1009/1117 1044/1118 942/1106 +f 942/1106 1044/1118 981/1107 +f 945/1121 979/1122 1010/1123 +f 1010/1123 979/1122 1043/1124 +f 1010/1123 1028/1125 945/1121 +f 945/1121 1028/1125 954/1126 +f 944/1115 956/1120 1010/1123 +f 1010/1123 956/1120 1028/1125 +f 1010/1123 1043/1124 944/1115 +f 944/1115 1043/1124 980/1116 +f 978/1127 1042/1128 946/1129 +f 946/1129 1042/1128 1011/1130 +f 1011/1130 1031/1131 946/1129 +f 946/1129 1031/1131 961/1132 +f 945/1121 954/1126 1011/1130 +f 1011/1130 954/1126 1031/1131 +f 1011/1130 1042/1128 945/1121 +f 945/1121 1042/1128 979/1122 +f 1033/1133 965/1134 1012/1135 +f 1012/1135 965/1134 947/1136 +f 961/1132 1033/1133 946/1129 +f 946/1129 1033/1133 1012/1135 +f 1012/1135 947/1136 1068/1137 +f 1068/1137 947/1136 1067/1138 +f 977/1139 1041/1140 948/1141 +f 948/1141 1041/1140 1013/1142 +f 1035/1143 969/1144 1013/1142 +f 1013/1142 969/1144 948/1141 +f 965/1134 1035/1143 947/1136 +f 947/1136 1035/1143 1013/1142 +f 1041/1140 1067/1138 1013/1142 +f 1013/1142 1067/1138 947/1136 +f 976/1145 1040/1146 949/1147 +f 949/1147 1040/1146 1014/1148 +f 1037/1149 972/1150 1014/1148 +f 1014/1148 972/1150 949/1147 +f 969/1144 1037/1149 948/1141 +f 948/1141 1037/1149 1014/1148 +f 1040/1146 977/1139 1014/1148 +f 1014/1148 977/1139 948/1141 +f 974/1151 1039/1152 950/1153 +f 950/1153 1039/1152 1015/1154 +f 1036/1155 968/1156 1015/1154 +f 1015/1154 968/1156 950/1153 +f 972/1150 1036/1155 949/1147 +f 949/1147 1036/1155 1015/1154 +f 1039/1152 976/1145 1015/1154 +f 1015/1154 976/1145 949/1147 +f 975/1157 1038/1158 951/1159 +f 951/1159 1038/1158 1016/1160 +f 1034/1161 964/1162 1016/1160 +f 1016/1160 964/1162 951/1159 +f 968/1156 1034/1161 950/1153 +f 950/1153 1034/1161 1016/1160 +f 1038/1158 974/1151 1016/1160 +f 1016/1160 974/1151 950/1153 +f 943/1112 982/1114 1017/1163 +f 1017/1163 982/1114 1046/1164 +f 1017/1163 1032/1165 943/1112 +f 943/1112 1032/1165 959/1113 +f 951/1159 964/1162 1017/1163 +f 1017/1163 964/1162 1032/1165 +f 1017/1163 1046/1164 951/1159 +f 951/1159 1046/1164 975/1157 +f 1039/1152 974/1151 1045/1109 +f 1045/1109 974/1151 982/1114 +f 987/1166 1049/1167 1907/1168 +f 1907/1168 1049/1167 1908/1169 +f 924/1170 1018/1171 1909/1172 +f 1909/1172 1018/1171 1908/1169 +f 991/1173 1051/1174 1914/1175 +f 1914/1175 1051/1174 1912/1176 +f 926/1177 1019/1178 1910/1179 +f 1910/1179 1019/1178 1912/1176 +f 910/1180 1052/1181 1910/1179 +f 1910/1179 1052/1181 1906/1182 +f 952/1183 1020/1184 1907/1168 +f 1907/1168 1020/1184 1906/1182 +f 1053/1185 1911/1186 909/1187 +f 909/1187 1911/1186 1909/1172 +f 925/1188 1021/1189 1913/1190 +f 1913/1190 1021/1189 1911/1186 +f 911/1191 1054/1192 1913/1190 +f 1913/1190 1054/1192 1915/1193 +f 1022/1194 1915/1193 927/1195 +f 927/1195 1915/1193 1917/1196 +f 1023/1197 1919/1198 928/1199 +f 928/1199 1919/1198 1921/1200 +f 1047/1201 1919/1198 983/1202 +f 983/1202 1919/1198 1917/1196 +f 1048/1203 1923/1204 985/1205 +f 985/1205 1923/1204 1921/1200 +f 1024/1206 1923/1204 930/1207 +f 930/1207 1923/1204 1925/1208 +f 1055/1209 1924/1210 912/1211 +f 912/1211 1924/1210 1925/1208 +f 1025/1212 1924/1210 931/1213 +f 931/1213 1924/1210 1922/1214 +f 1050/1215 1920/1216 989/1217 +f 989/1217 1920/1216 1922/1214 +f 1026/1218 1920/1216 929/1219 +f 929/1219 1920/1216 1918/1220 +f 1056/1221 1916/1222 913/1223 +f 913/1223 1916/1222 1918/1220 +f 1027/1224 1916/1222 953/1225 +f 953/1225 1916/1222 1914/1175 +f 1028/1125 955/1226 954/1126 +f 954/1126 955/1226 915/1227 +f 955/1226 1028/1125 914/1228 +f 914/1228 1028/1125 956/1120 +f 1029/1110 958/1229 957/1111 +f 957/1111 958/1229 917/1230 +f 958/1229 1029/1110 916/1231 +f 916/1231 1029/1110 959/1113 +f 1030/1119 960/1232 956/1120 +f 956/1120 960/1232 914/1228 +f 960/1232 1030/1119 917/1230 +f 917/1230 1030/1119 957/1111 +f 1031/1131 962/1233 961/1132 +f 961/1132 962/1233 918/1234 +f 962/1233 1031/1131 915/1227 +f 915/1227 1031/1131 954/1126 +f 1032/1165 963/1235 959/1113 +f 959/1113 963/1235 916/1231 +f 963/1235 1032/1165 919/1236 +f 919/1236 1032/1165 964/1162 +f 965/1134 1033/1133 920/1237 +f 920/1237 1033/1133 966/1238 +f 1033/1133 961/1132 966/1238 +f 966/1238 961/1132 918/1234 +f 964/1162 1034/1161 919/1236 +f 919/1236 1034/1161 967/1239 +f 1034/1161 968/1156 967/1239 +f 967/1239 968/1156 921/1240 +f 969/1144 1035/1143 922/1241 +f 922/1241 1035/1143 970/1242 +f 1035/1143 965/1134 970/1242 +f 970/1242 965/1134 920/1237 +f 968/1156 1036/1155 921/1240 +f 921/1240 1036/1155 971/1243 +f 1036/1155 972/1150 971/1243 +f 971/1243 972/1150 923/1244 +f 972/1150 1037/1149 923/1244 +f 923/1244 1037/1149 973/1245 +f 1037/1149 969/1144 973/1245 +f 973/1245 969/1144 922/1241 +f 955/1246 1021/1189 915/1247 +f 915/1247 1021/1189 925/1188 +f 1021/1189 955/1246 924/1170 +f 924/1170 955/1246 914/1248 +f 958/1249 1020/1184 917/1250 +f 917/1250 1020/1184 952/1183 +f 926/1177 1020/1184 916/1251 +f 916/1251 1020/1184 958/1249 +f 924/1170 914/1248 1018/1171 +f 1018/1171 914/1248 960/1252 +f 917/1250 952/1183 960/1252 +f 960/1252 952/1183 1018/1171 +f 962/1253 1022/1194 918/1254 +f 918/1254 1022/1194 927/1195 +f 915/1247 925/1188 962/1253 +f 962/1253 925/1188 1022/1194 +f 916/1251 963/1255 926/1177 +f 926/1177 963/1255 1019/1178 +f 953/1225 1019/1178 919/1256 +f 919/1256 1019/1178 963/1255 +f 966/1257 1023/1197 920/1258 +f 920/1258 1023/1197 928/1199 +f 918/1254 927/1195 966/1257 +f 966/1257 927/1195 1023/1197 +f 919/1256 967/1259 953/1225 +f 953/1225 967/1259 1027/1224 +f 929/1219 1027/1224 921/1260 +f 921/1260 1027/1224 967/1259 +f 970/1261 1024/1206 922/1262 +f 922/1262 1024/1206 930/1207 +f 920/1258 928/1199 970/1261 +f 970/1261 928/1199 1024/1206 +f 921/1260 971/1263 929/1219 +f 929/1219 971/1263 1026/1218 +f 931/1213 1026/1218 923/1264 +f 923/1264 1026/1218 971/1263 +f 931/1213 923/1264 1025/1212 +f 1025/1212 923/1264 973/1265 +f 922/1262 930/1207 973/1265 +f 973/1265 930/1207 1025/1212 +f 1047/1201 997/1266 985/1205 +f 985/1205 997/1266 933/1267 +f 1047/1201 983/1202 997/1266 +f 997/1266 983/1202 932/1268 +f 1048/1203 984/1269 912/1211 +f 912/1211 984/1269 934/1270 +f 984/1269 1048/1203 933/1267 +f 933/1267 1048/1203 985/1205 +f 909/1187 1049/1167 936/1271 +f 936/1271 1049/1167 986/1272 +f 1049/1167 987/1166 986/1272 +f 986/1272 987/1166 935/1273 +f 1050/1215 988/1274 913/1223 +f 913/1223 988/1274 938/1275 +f 988/1274 1050/1215 937/1276 +f 937/1276 1050/1215 989/1217 +f 910/1180 1051/1174 940/1277 +f 940/1277 1051/1174 990/1278 +f 990/1278 1051/1174 939/1279 +f 939/1279 1051/1174 991/1173 +f 987/1166 1052/1181 935/1273 +f 935/1273 1052/1181 992/1280 +f 1052/1181 910/1180 992/1280 +f 992/1280 910/1180 940/1277 +f 911/1191 1053/1185 941/1281 +f 941/1281 1053/1185 993/1282 +f 1053/1185 909/1187 993/1282 +f 993/1282 909/1187 936/1271 +f 983/1202 1054/1192 932/1268 +f 932/1268 1054/1192 994/1283 +f 1054/1192 911/1191 994/1283 +f 994/1283 911/1191 941/1281 +f 1055/1209 995/1284 989/1217 +f 989/1217 995/1284 937/1276 +f 995/1284 1055/1209 934/1270 +f 934/1270 1055/1209 912/1211 +f 1056/1221 996/1285 991/1173 +f 991/1173 996/1285 939/1279 +f 996/1285 1056/1221 938/1275 +f 938/1275 1056/1221 913/1223 +f 1057/1286 998/1287 997/1266 +f 997/1266 998/1287 933/1267 +f 999/1288 1057/1286 932/1268 +f 932/1268 1057/1286 997/1266 +f 984/1269 1058/1289 934/1270 +f 934/1270 1058/1289 1000/1290 +f 1058/1289 984/1269 998/1287 +f 998/1287 984/1269 933/1267 +f 1059/1291 1001/1292 986/1272 +f 986/1272 1001/1292 936/1271 +f 1002/1293 1059/1291 935/1273 +f 935/1273 1059/1291 986/1272 +f 988/1274 1060/1294 938/1275 +f 938/1275 1060/1294 1003/1295 +f 1060/1294 988/1274 1004/1296 +f 1004/1296 988/1274 937/1276 +f 990/1278 1061/1297 940/1277 +f 940/1277 1061/1297 1005/1298 +f 1061/1297 990/1278 1006/1299 +f 1006/1299 990/1278 939/1279 +f 1062/1300 1002/1293 992/1280 +f 992/1280 1002/1293 935/1273 +f 1005/1298 1062/1300 940/1277 +f 940/1277 1062/1300 992/1280 +f 1063/1301 1007/1302 993/1282 +f 993/1282 1007/1302 941/1281 +f 1001/1292 1063/1301 936/1271 +f 936/1271 1063/1301 993/1282 +f 1064/1303 999/1288 994/1283 +f 994/1283 999/1288 932/1268 +f 1007/1302 1064/1303 941/1281 +f 941/1281 1064/1303 994/1283 +f 995/1284 1065/1304 937/1276 +f 937/1276 1065/1304 1004/1296 +f 1065/1304 995/1284 1000/1290 +f 1000/1290 995/1284 934/1270 +f 996/1285 1066/1305 939/1279 +f 939/1279 1066/1305 1006/1299 +f 1066/1305 996/1285 1003/1295 +f 1003/1295 996/1285 938/1275 +f 1038/1158 975/1157 974/1151 +f 974/1151 975/1157 982/1114 +f 975/1157 1046/1164 982/1114 +f 981/1107 1044/1118 976/1145 +f 976/1145 1044/1118 1040/1146 +f 1045/1109 981/1107 1039/1152 +f 1039/1152 981/1107 976/1145 +f 1044/1118 980/1116 1040/1146 +f 1040/1146 980/1116 977/1139 +f 1042/1128 978/1127 979/1122 +f 1067/1138 979/1122 1068/1137 +f 979/1122 978/1127 1068/1137 +f 1043/1124 1041/1140 980/1116 +f 980/1116 1041/1140 977/1139 +f 1068/1137 978/1127 1012/1135 +f 1012/1135 978/1127 946/1129 +f 979/1122 1067/1138 1043/1124 +f 1043/1124 1067/1138 1041/1140 +f 998/1287 1057/1286 904/1306 +f 904/1306 1057/1286 905/1307 +f 1057/1286 999/1288 905/1307 +f 905/1307 999/1288 1314/1089 +f 1058/1289 903/1308 1000/1290 +f 1000/1290 903/1308 902/1309 +f 1059/1291 864/1057 1001/1292 +f 1001/1292 864/1057 865/1058 +f 1059/1291 1002/1293 864/1057 +f 864/1057 1002/1293 854/1047 +f 1060/1294 888/1081 1003/1295 +f 1003/1295 888/1081 881/1074 +f 1004/1296 895/1088 1060/1294 +f 1060/1294 895/1088 888/1081 +f 1005/1298 1061/1297 852/1045 +f 852/1045 1061/1297 863/1056 +f 1061/1297 1006/1299 863/1056 +f 863/1056 1006/1299 871/1064 +f 1062/1300 853/1046 1002/1293 +f 1002/1293 853/1046 854/1047 +f 1062/1300 1005/1298 853/1046 +f 853/1046 1005/1298 852/1045 +f 1007/1302 1063/1301 882/1075 +f 882/1075 1063/1301 875/1069 +f 1063/1301 1001/1292 875/1069 +f 875/1069 1001/1292 865/1058 +f 1064/1303 1007/1302 889/1083 +f 889/1083 1007/1302 882/1075 +f 1004/1296 1065/1304 895/1088 +f 895/1088 1065/1304 901/1100 +f 1000/1290 902/1309 1065/1304 +f 1065/1304 902/1309 901/1100 +f 1006/1299 1066/1305 871/1064 +f 871/1064 1066/1305 874/1067 +f 1066/1305 1003/1295 874/1067 +f 874/1067 1003/1295 881/1074 +f 998/1287 904/1306 1058/1289 +f 1058/1289 904/1306 903/1308 +f 871/1064 862/1055 863/1056 +f 863/1056 851/1044 852/1045 +f 1064/1303 889/1083 999/1288 +f 999/1288 889/1083 1314/1089 +f 1117/1310 1238/1311 1118/1312 +f 1238/1311 1153/1313 1152/1314 +f 1152/1314 1153/1313 1118/1312 +f 1154/1315 1239/1316 1071/1317 +f 1071/1317 1239/1316 1093/1318 +f 1239/1316 1154/1315 1309/1319 +f 1309/1319 1154/1315 1310/1320 +f 1240/1321 1311/1322 1154/1315 +f 1154/1315 1311/1322 1310/1320 +f 1111/1323 1288/1324 1094/1325 +f 1094/1325 1288/1324 1240/1321 +f 1240/1321 1154/1315 1094/1325 +f 1094/1325 1154/1315 1071/1317 +f 1119/1326 1155/1327 1220/1328 +f 1220/1328 1155/1327 1298/1329 +f 1116/1330 1298/1329 1114/1331 +f 1114/1331 1298/1329 1155/1327 +f 1113/1332 1151/1333 1121/1334 +f 1121/1334 1151/1333 1156/1335 +f 1072/1336 1156/1335 1070/1337 +f 1070/1337 1156/1335 1151/1333 +f 1155/1327 1241/1338 1114/1331 +f 1114/1331 1241/1338 1157/1339 +f 1241/1338 1278/1340 1157/1339 +f 1157/1339 1278/1340 1115/1341 +f 1278/1340 1241/1338 1223/1342 +f 1223/1342 1241/1338 1158/1343 +f 1241/1338 1155/1327 1158/1343 +f 1158/1343 1155/1327 1119/1326 +f 1122/1344 1159/1345 1219/1346 +f 1219/1346 1159/1345 1275/1347 +f 1220/1328 1275/1347 1119/1326 +f 1119/1326 1275/1347 1159/1345 +f 1160/1348 1242/1349 1124/1350 +f 1124/1350 1242/1349 1161/1351 +f 1242/1349 1246/1352 1161/1351 +f 1161/1351 1246/1352 1125/1353 +f 1246/1352 1242/1349 1162/1354 +f 1162/1354 1242/1349 1123/1355 +f 1123/1355 1242/1349 1160/1348 +f 1163/1356 1243/1357 1121/1334 +f 1121/1334 1243/1357 1120/1358 +f 1243/1357 1163/1356 1221/1359 +f 1221/1359 1163/1356 1126/1360 +f 1163/1356 1244/1361 1126/1360 +f 1126/1360 1244/1361 1164/1362 +f 1244/1361 1095/1363 1164/1362 +f 1164/1362 1095/1363 1073/1364 +f 1095/1363 1244/1361 1072/1336 +f 1072/1336 1244/1361 1156/1335 +f 1244/1361 1163/1356 1156/1335 +f 1156/1335 1163/1356 1121/1334 +f 1279/1365 1245/1366 1224/1367 +f 1224/1367 1245/1366 1165/1368 +f 1165/1368 1245/1366 1122/1344 +f 1122/1344 1245/1366 1159/1345 +f 1245/1366 1158/1343 1159/1345 +f 1159/1345 1158/1343 1119/1326 +f 1245/1366 1279/1365 1158/1343 +f 1158/1343 1279/1365 1223/1342 +f 1125/1353 1246/1352 1127/1369 +f 1127/1369 1246/1352 1166/1370 +f 1166/1370 1246/1352 1167/1371 +f 1130/1372 1168/1373 1209/1374 +f 1209/1374 1168/1373 1129/1375 +f 1168/1373 1130/1372 1210/1376 +f 1210/1376 1130/1372 1297/1377 +f 1169/1378 1247/1379 1074/1380 +f 1074/1380 1247/1379 1096/1381 +f 1096/1381 1247/1379 1075/1382 +f 1075/1382 1247/1379 1267/1383 +f 1247/1379 1168/1373 1267/1383 +f 1267/1383 1168/1373 1210/1376 +f 1247/1379 1169/1378 1168/1373 +f 1168/1373 1169/1378 1129/1375 +f 1229/1384 1248/1385 1132/1386 +f 1132/1386 1248/1385 1171/1387 +f 1248/1385 1170/1388 1171/1387 +f 1171/1387 1170/1388 1131/1389 +f 1249/1390 1173/1391 1172/1392 +f 1172/1392 1173/1391 1133/1393 +f 1249/1390 1238/1311 1173/1391 +f 1173/1391 1238/1311 1117/1310 +f 1302/1394 1175/1395 1172/1392 +f 1172/1392 1175/1395 1249/1390 +f 1249/1390 1174/1396 1238/1311 +f 1238/1311 1174/1396 1153/1313 +f 1174/1396 1249/1390 1134/1397 +f 1134/1397 1249/1390 1175/1395 +f 1176/1398 1250/1399 1307/1400 +f 1307/1400 1250/1399 1308/1401 +f 1250/1399 1239/1316 1308/1401 +f 1308/1401 1239/1316 1309/1319 +f 1239/1316 1250/1399 1093/1318 +f 1093/1318 1250/1399 1097/1402 +f 1250/1399 1176/1398 1097/1402 +f 1097/1402 1176/1398 1076/1403 +f 1219/1346 1274/1404 1122/1344 +f 1122/1344 1274/1404 1177/1405 +f 1136/1406 1177/1405 1135/1407 +f 1135/1407 1177/1405 1274/1404 +f 1178/1408 1251/1409 1137/1410 +f 1137/1410 1251/1409 1179/1411 +f 1251/1409 1180/1412 1179/1411 +f 1179/1411 1180/1412 1138/1413 +f 1180/1412 1251/1409 1133/1393 +f 1133/1393 1251/1409 1172/1392 +f 1252/1414 1178/1408 1181/1415 +f 1181/1415 1178/1408 1137/1410 +f 1175/1395 1302/1394 1236/1416 +f 1182/1417 1236/1416 1302/1394 +f 1182/1417 1252/1414 1127/1369 +f 1127/1369 1252/1414 1183/1418 +f 1252/1414 1181/1415 1183/1418 +f 1183/1418 1181/1415 1139/1419 +f 1253/1420 1180/1412 1184/1421 +f 1184/1421 1180/1412 1133/1393 +f 1180/1412 1253/1420 1138/1413 +f 1138/1413 1253/1420 1185/1422 +f 1253/1420 1287/1423 1185/1422 +f 1185/1422 1287/1423 1233/1424 +f 1287/1423 1253/1420 1234/1425 +f 1234/1425 1253/1420 1184/1421 +f 1218/1426 1273/1427 1140/1428 +f 1140/1428 1273/1427 1186/1429 +f 1141/1430 1186/1429 1217/1431 +f 1217/1431 1186/1429 1273/1427 +f 1187/1432 1254/1433 1143/1434 +f 1143/1434 1254/1433 1188/1435 +f 1254/1433 1189/1436 1188/1435 +f 1188/1435 1189/1436 1145/1437 +f 1189/1436 1254/1433 1144/1438 +f 1144/1438 1254/1433 1190/1439 +f 1254/1433 1187/1432 1190/1439 +f 1190/1439 1187/1432 1142/1440 +f 1191/1441 1255/1442 1146/1443 +f 1146/1443 1255/1442 1192/1444 +f 1255/1442 1193/1445 1192/1444 +f 1192/1444 1193/1445 1131/1389 +f 1255/1442 1187/1432 1193/1445 +f 1193/1445 1187/1432 1143/1434 +f 1187/1432 1255/1442 1142/1440 +f 1142/1440 1255/1442 1191/1441 +f 1194/1446 1256/1447 1226/1448 +f 1226/1448 1256/1447 1282/1449 +f 1256/1447 1195/1450 1282/1449 +f 1282/1449 1195/1450 1227/1451 +f 1195/1450 1256/1447 1141/1430 +f 1141/1430 1256/1447 1186/1429 +f 1256/1447 1194/1446 1186/1429 +f 1186/1429 1194/1446 1140/1428 +f 1257/1452 1189/1436 1196/1453 +f 1196/1453 1189/1436 1144/1438 +f 1189/1436 1257/1452 1145/1437 +f 1145/1437 1257/1452 1197/1454 +f 1257/1452 1285/1455 1197/1454 +f 1197/1454 1285/1455 1231/1456 +f 1285/1455 1257/1452 1232/1457 +f 1232/1457 1257/1452 1196/1453 +f 1217/1431 1272/1458 1141/1430 +f 1141/1430 1272/1458 1195/1450 +f 1228/1459 1227/1451 1272/1458 +f 1272/1458 1227/1451 1195/1450 +f 1258/1460 1284/1461 1198/1462 +f 1198/1462 1284/1461 1230/1463 +f 1284/1461 1258/1460 1231/1456 +f 1231/1456 1258/1460 1197/1454 +f 1197/1454 1258/1460 1145/1437 +f 1145/1437 1258/1460 1188/1435 +f 1258/1460 1198/1462 1188/1435 +f 1188/1435 1198/1462 1143/1434 +f 1199/1464 1259/1465 1126/1360 +f 1126/1360 1259/1465 1221/1359 +f 1259/1465 1199/1464 1222/1466 +f 1222/1466 1199/1464 1147/1467 +f 1260/1468 1200/1469 1201/1470 +f 1201/1470 1200/1469 1148/1471 +f 1175/1395 1261/1472 1134/1397 +f 1134/1397 1261/1472 1202/1473 +f 1305/1474 1306/1475 1303/1476 +f 1303/1476 1306/1475 1202/1473 +f 1199/1464 1262/1477 1147/1467 +f 1147/1467 1262/1477 1204/1478 +f 1262/1477 1098/1479 1204/1478 +f 1204/1478 1098/1479 1077/1480 +f 1098/1479 1262/1477 1073/1364 +f 1073/1364 1262/1477 1164/1362 +f 1262/1477 1199/1464 1164/1362 +f 1164/1362 1199/1464 1126/1360 +f 1263/1481 1176/1398 1306/1475 +f 1306/1475 1176/1398 1307/1400 +f 1176/1398 1263/1481 1076/1403 +f 1076/1403 1263/1481 1099/1482 +f 1263/1481 1205/1483 1099/1482 +f 1099/1482 1205/1483 1078/1484 +f 1205/1483 1263/1481 1305/1474 +f 1305/1474 1263/1481 1306/1475 +f 1149/1485 1277/1486 1147/1467 +f 1147/1467 1277/1486 1222/1466 +f 1200/1469 1207/1487 1148/1471 +f 1148/1471 1207/1487 1206/1488 +f 1304/1489 1305/1474 1266/1490 +f 1266/1490 1305/1474 1303/1476 +f 1079/1491 1264/1492 1077/1480 +f 1077/1480 1264/1492 1204/1478 +f 1264/1492 1149/1485 1204/1478 +f 1204/1478 1149/1485 1147/1467 +f 1205/1483 1265/1493 1078/1484 +f 1078/1484 1265/1493 1100/1494 +f 1265/1493 1205/1483 1304/1489 +f 1304/1489 1205/1483 1305/1474 +f 1207/1487 1130/1372 1206/1488 +f 1206/1488 1130/1372 1209/1374 +f 1075/1382 1267/1383 1079/1491 +f 1079/1491 1267/1383 1264/1492 +f 1267/1383 1210/1376 1264/1492 +f 1264/1492 1210/1376 1149/1485 +f 1268/1495 1211/1496 1280/1497 +f 1280/1497 1211/1496 1225/1498 +f 1211/1496 1268/1495 1136/1406 +f 1136/1406 1268/1495 1177/1405 +f 1268/1495 1165/1368 1177/1405 +f 1177/1405 1165/1368 1122/1344 +f 1165/1368 1268/1495 1224/1367 +f 1224/1367 1268/1495 1280/1497 +f 1286/1499 1269/1500 1233/1424 +f 1233/1424 1269/1500 1185/1422 +f 1269/1500 1212/1501 1185/1422 +f 1185/1422 1212/1501 1138/1413 +f 1212/1501 1269/1500 1144/1438 +f 1144/1438 1269/1500 1196/1453 +f 1269/1500 1286/1499 1196/1453 +f 1196/1453 1286/1499 1232/1457 +f 1150/1502 1270/1503 1146/1443 +f 1146/1443 1270/1503 1213/1504 +f 1270/1503 1214/1505 1213/1504 +f 1213/1504 1214/1505 1139/1419 +f 1135/1407 1301/1506 1136/1406 +f 1136/1406 1301/1506 1215/1507 +f 1140/1428 1215/1507 1218/1426 +f 1218/1426 1215/1507 1301/1506 +f 1216/1508 1271/1509 1142/1440 +f 1142/1440 1271/1509 1190/1439 +f 1271/1509 1212/1501 1190/1439 +f 1190/1439 1212/1501 1144/1438 +f 1212/1501 1271/1509 1138/1413 +f 1138/1413 1271/1509 1179/1411 +f 1271/1509 1216/1508 1179/1411 +f 1179/1411 1216/1508 1137/1410 +f 1228/1459 1272/1458 1229/1384 +f 1229/1384 1272/1458 1248/1385 +f 1272/1458 1217/1431 1248/1385 +f 1248/1385 1217/1431 1170/1388 +f 1217/1431 1273/1427 1170/1388 +f 1170/1388 1273/1427 1294/1510 +f 1273/1427 1218/1426 1294/1510 +f 1294/1510 1218/1426 1150/1502 +f 1135/1407 1274/1404 1214/1505 +f 1214/1505 1274/1404 1291/1511 +f 1274/1404 1219/1346 1291/1511 +f 1291/1511 1219/1346 1125/1353 +f 1219/1346 1275/1347 1125/1353 +f 1125/1353 1275/1347 1161/1351 +f 1275/1347 1220/1328 1161/1351 +f 1161/1351 1220/1328 1124/1350 +f 1276/1512 1160/1348 1116/1330 +f 1116/1330 1160/1348 1124/1350 +f 1331/1513 1162/1354 1123/1355 +f 1260/1468 1259/1465 1200/1469 +f 1200/1469 1259/1465 1222/1466 +f 1277/1486 1207/1487 1222/1466 +f 1222/1466 1207/1487 1200/1469 +f 1297/1377 1130/1372 1277/1486 +f 1277/1486 1130/1372 1207/1487 +f 1115/1341 1278/1340 1069/1514 +f 1069/1514 1278/1340 1101/1515 +f 1101/1515 1278/1340 1080/1516 +f 1080/1516 1278/1340 1223/1342 +f 1279/1365 1102/1517 1223/1342 +f 1223/1342 1102/1517 1080/1516 +f 1102/1517 1279/1365 1081/1518 +f 1081/1518 1279/1365 1224/1367 +f 1280/1497 1103/1519 1224/1367 +f 1224/1367 1103/1519 1081/1518 +f 1280/1497 1225/1498 1103/1519 +f 1103/1519 1225/1498 1082/1520 +f 1225/1498 1281/1521 1082/1520 +f 1082/1520 1281/1521 1104/1522 +f 1281/1521 1226/1448 1104/1522 +f 1104/1522 1226/1448 1083/1523 +f 1226/1448 1282/1449 1083/1523 +f 1083/1523 1282/1449 1105/1524 +f 1282/1449 1227/1451 1105/1524 +f 1105/1524 1227/1451 1084/1525 +f 1227/1451 1228/1459 1084/1525 +f 1084/1525 1228/1459 1085/1526 +f 1228/1459 1229/1384 1085/1526 +f 1085/1526 1229/1384 1086/1527 +f 1283/1528 1106/1529 1132/1386 +f 1132/1386 1106/1529 1088/1530 +f 1106/1529 1283/1528 1087/1531 +f 1087/1531 1283/1528 1230/1463 +f 1107/1532 1284/1461 1089/1533 +f 1089/1533 1284/1461 1231/1456 +f 1284/1461 1107/1532 1230/1463 +f 1230/1463 1107/1532 1087/1531 +f 1285/1455 1108/1534 1231/1456 +f 1231/1456 1108/1534 1089/1533 +f 1108/1534 1285/1455 1090/1535 +f 1090/1535 1285/1455 1232/1457 +f 1109/1536 1286/1499 1091/1537 +f 1091/1537 1286/1499 1233/1424 +f 1286/1499 1109/1536 1232/1457 +f 1232/1457 1109/1536 1090/1535 +f 1287/1423 1110/1538 1233/1424 +f 1233/1424 1110/1538 1091/1537 +f 1110/1538 1287/1423 1092/1539 +f 1092/1539 1287/1423 1234/1425 +f 1288/1324 1111/1323 1234/1425 +f 1234/1425 1111/1323 1092/1539 +f 1169/1378 1289/1540 1129/1375 +f 1129/1375 1289/1540 1235/1541 +f 1289/1540 1265/1493 1235/1541 +f 1235/1541 1265/1493 1304/1489 +f 1265/1493 1289/1540 1100/1494 +f 1100/1494 1289/1540 1112/1542 +f 1289/1540 1169/1378 1112/1542 +f 1112/1542 1169/1378 1074/1380 +f 1086/1527 1229/1384 1088/1530 +f 1088/1530 1229/1384 1132/1386 +f 1290/1543 1236/1416 1201/1470 +f 1201/1470 1236/1416 1128/1544 +f 1236/1416 1290/1543 1175/1395 +f 1175/1395 1290/1543 1261/1472 +f 1290/1543 1237/1545 1261/1472 +f 1261/1472 1237/1545 1203/1546 +f 1237/1545 1290/1543 1148/1471 +f 1148/1471 1290/1543 1201/1470 +f 1214/1505 1291/1511 1139/1419 +f 1139/1419 1291/1511 1183/1418 +f 1183/1418 1291/1511 1127/1369 +f 1127/1369 1291/1511 1125/1353 +f 1292/1547 1237/1545 1206/1488 +f 1206/1488 1237/1545 1148/1471 +f 1237/1545 1292/1547 1203/1546 +f 1203/1546 1292/1547 1208/1548 +f 1216/1508 1293/1549 1137/1410 +f 1137/1410 1293/1549 1181/1415 +f 1181/1415 1293/1549 1139/1419 +f 1139/1419 1293/1549 1213/1504 +f 1293/1549 1191/1441 1213/1504 +f 1213/1504 1191/1441 1146/1443 +f 1293/1549 1216/1508 1191/1441 +f 1191/1441 1216/1508 1142/1440 +f 1295/1550 1292/1547 1209/1374 +f 1209/1374 1292/1547 1206/1488 +f 1292/1547 1295/1550 1208/1548 +f 1208/1548 1295/1550 1266/1490 +f 1170/1388 1294/1510 1131/1389 +f 1131/1389 1294/1510 1192/1444 +f 1294/1510 1150/1502 1192/1444 +f 1192/1444 1150/1502 1146/1443 +f 1235/1541 1295/1550 1129/1375 +f 1129/1375 1295/1550 1209/1374 +f 1283/1528 1296/1551 1230/1463 +f 1230/1463 1296/1551 1198/1462 +f 1296/1551 1193/1445 1198/1462 +f 1198/1462 1193/1445 1143/1434 +f 1193/1445 1296/1551 1131/1389 +f 1131/1389 1296/1551 1171/1387 +f 1296/1551 1283/1528 1171/1387 +f 1171/1387 1283/1528 1132/1386 +f 1210/1376 1297/1377 1149/1485 +f 1149/1485 1297/1377 1277/1486 +f 1220/1328 1298/1329 1124/1350 +f 1124/1350 1298/1329 1116/1330 +f 1127/1369 1166/1370 1182/1417 +f 1166/1370 1128/1544 1182/1417 +f 1236/1416 1182/1417 1128/1544 +f 1299/1552 1288/1324 1184/1421 +f 1184/1421 1288/1324 1234/1425 +f 1288/1324 1299/1552 1240/1321 +f 1240/1321 1299/1552 1311/1322 +f 1173/1391 1299/1552 1133/1393 +f 1133/1393 1299/1552 1184/1421 +f 1211/1496 1300/1553 1225/1498 +f 1225/1498 1300/1553 1281/1521 +f 1300/1553 1194/1446 1281/1521 +f 1281/1521 1194/1446 1226/1448 +f 1194/1446 1300/1553 1140/1428 +f 1140/1428 1300/1553 1215/1507 +f 1300/1553 1211/1496 1215/1507 +f 1215/1507 1211/1496 1136/1406 +f 1218/1426 1301/1506 1150/1502 +f 1150/1502 1301/1506 1270/1503 +f 1301/1506 1135/1407 1270/1503 +f 1270/1503 1135/1407 1214/1505 +f 1302/1394 1178/1408 1182/1417 +f 1182/1417 1178/1408 1252/1414 +f 1251/1409 1178/1408 1172/1392 +f 1172/1392 1178/1408 1302/1394 +f 1203/1546 1208/1548 1303/1476 +f 1303/1476 1208/1548 1266/1490 +f 1261/1472 1203/1546 1202/1473 +f 1202/1473 1203/1546 1303/1476 +f 1266/1490 1295/1550 1304/1489 +f 1304/1489 1295/1550 1235/1541 +f 1306/1475 1307/1400 1202/1473 +f 1202/1473 1307/1400 1134/1397 +f 1308/1401 1174/1396 1307/1400 +f 1307/1400 1174/1396 1134/1397 +f 1174/1396 1308/1401 1153/1313 +f 1153/1313 1308/1401 1309/1319 +f 1309/1319 1310/1320 1153/1313 +f 1153/1313 1310/1320 1118/1312 +f 1311/1322 1117/1310 1310/1320 +f 1310/1320 1117/1310 1118/1312 +f 1299/1552 1173/1391 1311/1322 +f 1311/1322 1173/1391 1117/1310 +f 1320/1554 1319/1555 1069/1556 +f 1069/1556 1319/1555 1115/1557 +f 1322/1558 1319/1555 903/1308 +f 903/1308 1319/1555 907/1104 +f 903/1308 907/1104 902/1309 +f 902/1309 907/1104 1312/1102 +f 900/1099 901/1100 1312/1102 +f 1312/1102 901/1100 902/1309 +f 899/1098 900/1099 1313/1101 +f 1313/1101 900/1099 1312/1102 +f 1323/1559 1324/1560 1114/1561 +f 1114/1561 1324/1560 1116/1562 +f 1325/1563 1324/1560 1314/1089 +f 1314/1089 1324/1560 905/1307 +f 1326/1564 1327/1565 1317/1566 +f 1317/1566 1327/1565 1318/1567 +f 1321/1568 1333/1569 1316/1570 +f 1316/1570 1333/1569 898/1094 +f 1323/1559 1322/1558 904/1306 +f 904/1306 1322/1558 903/1308 +f 1326/1564 1325/1563 896/1090 +f 896/1090 1325/1563 1314/1089 +f 1276/1512 1317/1571 1160/1348 +f 1160/1348 1317/1571 1123/1355 +f 1121/1334 1120/1358 1113/1332 +f 1113/1332 1120/1358 1318/1572 +f 1328/1573 1327/1565 1315/1092 +f 1315/1092 1327/1565 897/1091 +f 1120/1358 1123/1355 1318/1572 +f 1318/1572 1123/1355 1317/1571 +f 1319/1555 1320/1554 907/1104 +f 907/1104 1320/1554 906/1103 +f 1333/1569 1321/1568 1151/1574 +f 1151/1574 1321/1568 1070/1575 +f 1115/1557 1319/1555 1157/1576 +f 1157/1576 1319/1555 1322/1558 +f 1157/1576 1322/1558 1114/1561 +f 1114/1561 1322/1558 1323/1559 +f 905/1307 1324/1560 904/1306 +f 904/1306 1324/1560 1323/1559 +f 1324/1560 1325/1563 1116/1562 +f 1116/1562 1325/1563 1276/1577 +f 1325/1563 1326/1564 1276/1577 +f 1276/1577 1326/1564 1317/1566 +f 1327/1565 1326/1564 897/1091 +f 897/1091 1326/1564 896/1090 +f 1327/1565 1328/1573 1318/1567 +f 1318/1567 1328/1573 1113/1578 +f 1328/1573 1333/1569 1113/1578 +f 1113/1578 1333/1569 1151/1574 +f 1329/1579 1260/1468 1128/1544 +f 1128/1544 1260/1468 1201/1470 +f 1259/1465 1260/1468 1221/1359 +f 1221/1359 1260/1468 1329/1579 +f 1243/1357 1221/1359 1330/1580 +f 1330/1580 1221/1359 1329/1579 +f 1329/1579 1128/1544 1167/1371 +f 1167/1371 1128/1544 1166/1370 +f 1330/1580 1329/1579 1331/1513 +f 1331/1513 1329/1579 1167/1371 +f 1330/1580 1331/1513 1120/1358 +f 1120/1358 1331/1513 1123/1355 +f 1120/1358 1243/1357 1330/1580 +f 1331/1513 1167/1371 1162/1354 +f 1162/1354 1167/1371 1246/1352 +f 1315/1092 898/1094 1328/1573 +f 1328/1573 898/1094 1333/1569 +f 722/908 1334/925 721/906 +f 721/906 1334/925 735/924 +f 1334/925 1335/939 735/924 +f 735/924 1335/939 748/938 +f 1335/939 1336/953 748/938 +f 748/938 1336/953 761/952 +f 1336/953 1337/967 761/952 +f 761/952 1337/967 774/966 +f 774/966 1337/967 787/980 +f 787/980 1337/967 3/402 +f 3/402 87/405 787/980 +f 787/980 87/405 799/992 +f 885/1078 321/1097 892/1085 +f 892/1085 321/1097 1338/1093 +f 710/886 1350/1581 697/885 +f 697/885 1350/1581 1339/1582 +f 1350/1581 1351/1583 1339/1582 +f 1339/1582 1351/1583 1340/1584 +f 1351/1583 1352/1585 1340/1584 +f 1340/1584 1352/1585 1341/1586 +f 1352/1585 1353/1587 1341/1586 +f 1341/1586 1353/1587 1342/1588 +f 1353/1587 1354/1589 1342/1588 +f 1342/1588 1354/1589 1343/1590 +f 1354/1589 1355/1591 1343/1590 +f 1343/1590 1355/1591 1344/1592 +f 1355/1591 1356/1593 1344/1592 +f 1344/1592 1356/1593 1345/1594 +f 1356/1593 1357/1595 1345/1594 +f 1345/1594 1357/1595 1346/1596 +f 1346/1596 1357/1595 1347/1597 +f 1347/1597 1357/1595 1358/1598 +f 1347/1597 1358/1598 1348/1599 +f 1348/1599 1358/1598 1359/1600 +f 1359/1600 1360/1601 1348/1599 +f 1348/1599 1360/1601 1349/1602 +f 1360/1601 1361/1603 1349/1602 +f 1349/1602 1361/1603 1898/1604 +f 1361/1603 1973/1605 1898/1604 +f 1898/1604 1973/1605 1974/1606 +f 724/914 1362/1607 710/886 +f 710/886 1362/1607 1350/1581 +f 1362/1607 1363/1608 1350/1581 +f 1350/1581 1363/1608 1351/1583 +f 1363/1608 1364/1609 1351/1583 +f 1351/1583 1364/1609 1352/1585 +f 1364/1609 1365/1610 1352/1585 +f 1352/1585 1365/1610 1353/1587 +f 1365/1610 1366/1611 1353/1587 +f 1353/1587 1366/1611 1354/1589 +f 1366/1611 1367/1612 1354/1589 +f 1354/1589 1367/1612 1355/1591 +f 1367/1612 1368/1613 1355/1591 +f 1355/1591 1368/1613 1356/1593 +f 1368/1613 1369/1614 1356/1593 +f 1356/1593 1369/1614 1357/1595 +f 1357/1595 1369/1614 1358/1598 +f 1358/1598 1369/1614 1370/1615 +f 1358/1598 1370/1615 1359/1600 +f 1359/1600 1370/1615 1371/1616 +f 1371/1616 1372/1617 1359/1600 +f 1359/1600 1372/1617 1360/1601 +f 1900/1618 1976/1619 1361/1603 +f 1361/1603 1976/1619 1973/1605 +f 737/928 1373/1620 724/914 +f 724/914 1373/1620 1362/1607 +f 1373/1620 1374/1621 1362/1607 +f 1362/1607 1374/1621 1363/1608 +f 1374/1621 1375/1622 1363/1608 +f 1363/1608 1375/1622 1364/1609 +f 1375/1622 1376/1623 1364/1609 +f 1364/1609 1376/1623 1365/1610 +f 1376/1623 1377/1624 1365/1610 +f 1365/1610 1377/1624 1366/1611 +f 1377/1624 1378/1625 1366/1611 +f 1366/1611 1378/1625 1367/1612 +f 1378/1625 1379/1626 1367/1612 +f 1367/1612 1379/1626 1368/1613 +f 1379/1626 1380/1627 1368/1613 +f 1368/1613 1380/1627 1369/1614 +f 1369/1614 1380/1627 1370/1615 +f 1370/1615 1380/1627 1381/1628 +f 1370/1615 1381/1628 1371/1616 +f 1371/1616 1381/1628 1382/1629 +f 1371/1616 1382/1629 1372/1617 +f 1372/1617 1382/1629 1383/1630 +f 1901/1631 1977/1632 1900/1618 +f 1900/1618 1977/1632 1976/1619 +f 737/928 750/941 1373/1620 +f 1373/1620 750/941 1384/1633 +f 1384/1633 1385/1634 1373/1620 +f 1373/1620 1385/1634 1374/1621 +f 1385/1634 1386/1635 1374/1621 +f 1374/1621 1386/1635 1375/1622 +f 1386/1635 1387/1636 1375/1622 +f 1375/1622 1387/1636 1376/1623 +f 1387/1636 1388/1637 1376/1623 +f 1376/1623 1388/1637 1377/1624 +f 1388/1637 1389/1638 1377/1624 +f 1377/1624 1389/1638 1378/1625 +f 1389/1638 1390/1639 1378/1625 +f 1378/1625 1390/1639 1379/1626 +f 1390/1639 1391/1640 1379/1626 +f 1379/1626 1391/1640 1380/1627 +f 1380/1627 1391/1640 1381/1628 +f 1381/1628 1391/1640 1392/1641 +f 1392/1641 1393/1642 1381/1628 +f 1381/1628 1393/1642 1382/1629 +f 1382/1629 1393/1642 1383/1630 +f 1383/1630 1393/1642 1394/1643 +f 1902/1644 1978/1645 1901/1631 +f 1901/1631 1978/1645 1977/1632 +f 750/941 763/955 1384/1633 +f 1384/1633 763/955 1395/1646 +f 1395/1646 1396/1647 1384/1633 +f 1384/1633 1396/1647 1385/1634 +f 1396/1647 1397/1648 1385/1634 +f 1385/1634 1397/1648 1386/1635 +f 1397/1648 1398/1649 1386/1635 +f 1386/1635 1398/1649 1387/1636 +f 1398/1649 1399/1650 1387/1636 +f 1387/1636 1399/1650 1388/1637 +f 1399/1650 1400/1651 1388/1637 +f 1388/1637 1400/1651 1389/1638 +f 1400/1651 1401/1652 1389/1638 +f 1389/1638 1401/1652 1390/1639 +f 1401/1652 1402/1653 1390/1639 +f 1390/1639 1402/1653 1391/1640 +f 1391/1640 1402/1653 1392/1641 +f 1392/1641 1402/1653 1403/1654 +f 1403/1654 1404/1655 1392/1641 +f 1392/1641 1404/1655 1393/1642 +f 1404/1655 1405/1656 1393/1642 +f 1393/1642 1405/1656 1394/1643 +f 1903/1657 1975/1658 1902/1644 +f 1902/1644 1975/1658 1978/1645 +f 763/955 776/969 1395/1646 +f 1395/1646 776/969 1406/1659 +f 1406/1659 1407/1660 1395/1646 +f 1395/1646 1407/1660 1396/1647 +f 1407/1660 1408/1661 1396/1647 +f 1396/1647 1408/1661 1397/1648 +f 1408/1661 1409/1662 1397/1648 +f 1397/1648 1409/1662 1398/1649 +f 1409/1662 1410/1663 1398/1649 +f 1398/1649 1410/1663 1399/1650 +f 1410/1663 1411/1664 1399/1650 +f 1399/1650 1411/1664 1400/1651 +f 1411/1664 1412/1665 1400/1651 +f 1400/1651 1412/1665 1401/1652 +f 1412/1665 1413/1666 1401/1652 +f 1401/1652 1413/1666 1402/1653 +f 1402/1653 1413/1666 1403/1654 +f 1403/1654 1413/1666 1414/1667 +f 1414/1667 1415/1668 1403/1654 +f 1403/1654 1415/1668 1404/1655 +f 1415/1668 1416/1669 1404/1655 +f 1404/1655 1416/1669 1405/1656 +f 2045/1670 1975/1658 2044/479 +f 2044/479 1975/1658 435/480 +f 776/969 788/981 1406/1659 +f 1406/1659 788/981 1417/1671 +f 1417/1671 1418/1672 1406/1659 +f 1406/1659 1418/1672 1407/1660 +f 1418/1672 1419/1673 1407/1660 +f 1407/1660 1419/1673 1408/1661 +f 1419/1673 1420/1674 1408/1661 +f 1408/1661 1420/1674 1409/1662 +f 1420/1674 1421/1675 1409/1662 +f 1409/1662 1421/1675 1410/1663 +f 1421/1675 1422/1676 1410/1663 +f 1410/1663 1422/1676 1411/1664 +f 1422/1676 1423/1677 1411/1664 +f 1411/1664 1423/1677 1412/1665 +f 1423/1677 1424/1678 1412/1665 +f 1412/1665 1424/1678 1413/1666 +f 1413/1666 1424/1678 1414/1667 +f 1414/1667 1424/1678 1425/1679 +f 1414/1667 1425/1679 1415/1668 +f 1415/1668 1425/1679 1426/1680 +f 1426/1680 1427/1681 1415/1668 +f 1415/1668 1427/1681 1416/1669 +f 788/981 800/993 1417/1671 +f 1417/1671 800/993 1428/1682 +f 1428/1682 1429/1683 1417/1671 +f 1417/1671 1429/1683 1418/1672 +f 1429/1683 1430/1684 1418/1672 +f 1418/1672 1430/1684 1419/1673 +f 1430/1684 1431/1685 1419/1673 +f 1419/1673 1431/1685 1420/1674 +f 1431/1685 1432/1686 1420/1674 +f 1420/1674 1432/1686 1421/1675 +f 1432/1686 1433/1687 1421/1675 +f 1421/1675 1433/1687 1422/1676 +f 1433/1687 1434/1688 1422/1676 +f 1422/1676 1434/1688 1423/1677 +f 1434/1688 1435/1689 1423/1677 +f 1423/1677 1435/1689 1424/1678 +f 1424/1678 1435/1689 1425/1679 +f 1425/1679 1435/1689 1436/1690 +f 1436/1690 1437/1691 1425/1679 +f 1425/1679 1437/1691 1426/1680 +f 1437/1691 1438/1692 1426/1680 +f 1426/1680 1438/1692 1427/1681 +f 800/993 812/1005 1428/1682 +f 1428/1682 812/1005 1439/1693 +f 1439/1693 1440/1694 1428/1682 +f 1428/1682 1440/1694 1429/1683 +f 1440/1694 1441/1695 1429/1683 +f 1429/1683 1441/1695 1430/1684 +f 1441/1695 1442/1696 1430/1684 +f 1430/1684 1442/1696 1431/1685 +f 1442/1696 1443/1697 1431/1685 +f 1431/1685 1443/1697 1432/1686 +f 1443/1697 1444/1698 1432/1686 +f 1432/1686 1444/1698 1433/1687 +f 1444/1698 1445/1699 1433/1687 +f 1433/1687 1445/1699 1434/1688 +f 1445/1699 1446/1700 1434/1688 +f 1434/1688 1446/1700 1435/1689 +f 1435/1689 1446/1700 1436/1690 +f 1436/1690 1446/1700 1447/1701 +f 1447/1701 1448/1702 1436/1690 +f 1436/1690 1448/1702 1437/1691 +f 1448/1702 1449/1703 1437/1691 +f 1437/1691 1449/1703 1438/1692 +f 812/1005 824/1017 1439/1693 +f 1439/1693 824/1017 1450/1704 +f 1450/1704 1451/1705 1439/1693 +f 1439/1693 1451/1705 1440/1694 +f 1451/1705 1452/1706 1440/1694 +f 1440/1694 1452/1706 1441/1695 +f 1452/1706 1453/1707 1441/1695 +f 1441/1695 1453/1707 1442/1696 +f 1453/1707 1454/1708 1442/1696 +f 1442/1696 1454/1708 1443/1697 +f 1454/1708 1455/1709 1443/1697 +f 1443/1697 1455/1709 1444/1698 +f 1455/1709 1456/1710 1444/1698 +f 1444/1698 1456/1710 1445/1699 +f 1456/1710 1457/1711 1445/1699 +f 1445/1699 1457/1711 1446/1700 +f 1446/1700 1457/1711 1447/1701 +f 1447/1701 1457/1711 1458/1712 +f 1447/1701 1458/1712 1448/1702 +f 1448/1702 1458/1712 1459/1713 +f 1448/1702 1459/1713 1449/1703 +f 1449/1703 1459/1713 1460/1714 +f 824/1017 847/1029 1450/1704 +f 1450/1704 847/1029 1461/1715 +f 1461/1715 1462/1716 1450/1704 +f 1450/1704 1462/1716 1451/1705 +f 1462/1716 1463/1717 1451/1705 +f 1451/1705 1463/1717 1452/1706 +f 1463/1717 1464/1718 1452/1706 +f 1452/1706 1464/1718 1453/1707 +f 1464/1718 1465/1719 1453/1707 +f 1453/1707 1465/1719 1454/1708 +f 1465/1719 1466/1720 1454/1708 +f 1454/1708 1466/1720 1455/1709 +f 1466/1720 1467/1721 1455/1709 +f 1455/1709 1467/1721 1456/1710 +f 1456/1710 1467/1721 1457/1711 +f 1457/1711 1467/1721 1468/1722 +f 1457/1711 1468/1722 1458/1712 +f 1458/1712 1468/1722 1469/1723 +f 1458/1712 1469/1723 1459/1713 +f 1459/1713 1469/1723 1470/1724 +f 1459/1713 1470/1724 1460/1714 +f 1460/1714 1470/1724 1471/1725 +f 847/1029 848/1041 1461/1715 +f 1461/1715 848/1041 1472/1726 +f 1461/1715 1472/1726 1462/1716 +f 1462/1716 1472/1726 1473/1727 +f 1462/1716 1473/1727 1463/1717 +f 1463/1717 1473/1727 1474/1728 +f 1463/1717 1474/1728 1464/1718 +f 1464/1718 1474/1728 1475/1729 +f 1475/1729 1476/1730 1464/1718 +f 1464/1718 1476/1730 1465/1719 +f 1476/1730 1477/1731 1465/1719 +f 1465/1719 1477/1731 1466/1720 +f 1477/1731 1478/1732 1466/1720 +f 1466/1720 1478/1732 1467/1721 +f 1467/1721 1478/1732 1468/1722 +f 1468/1722 1478/1732 1479/1733 +f 1468/1722 1479/1733 1469/1723 +f 1469/1723 1479/1733 1480/1734 +f 1469/1723 1480/1734 1470/1724 +f 1470/1724 1480/1734 1481/1735 +f 1470/1724 1481/1735 1471/1725 +f 1471/1725 1481/1735 1482/1736 +f 848/1041 860/1053 1472/1726 +f 1472/1726 860/1053 1483/1737 +f 1472/1726 1483/1737 1473/1727 +f 1473/1727 1483/1737 1484/1738 +f 1473/1727 1484/1738 1474/1728 +f 1474/1728 1484/1738 1485/1739 +f 1477/1731 1486/1740 1478/1732 +f 1486/1740 1489/1741 1478/1732 +f 1478/1732 1489/1741 1479/1733 +f 1489/1741 1490/1742 1479/1733 +f 1479/1733 1490/1742 1480/1734 +f 1490/1742 1491/1743 1480/1734 +f 1480/1734 1491/1743 1481/1735 +f 1481/1735 1491/1743 1482/1736 +f 1482/1736 1491/1743 1492/1744 +f 860/1053 869/1062 1483/1737 +f 1483/1737 869/1062 1487/1745 +f 1483/1737 1487/1745 1484/1738 +f 1484/1738 1487/1745 1488/1746 +f 869/1062 872/1065 1487/1745 +f 1487/1745 872/1065 1493/1747 +f 1493/1747 1494/1748 1487/1745 +f 1487/1745 1494/1748 1488/1746 +f 1496/1749 1490/1742 1495/1750 +f 1495/1750 1490/1742 1489/1741 +f 1496/1749 1497/1751 1490/1742 +f 1490/1742 1497/1751 1491/1743 +f 1491/1743 1497/1751 1492/1744 +f 1492/1744 1497/1751 1498/1752 +f 872/1065 879/1072 1493/1747 +f 1493/1747 879/1072 1499/1753 +f 1499/1753 1500/1754 1493/1747 +f 1493/1747 1500/1754 1494/1748 +f 1496/1749 1495/1750 1502/1755 +f 1502/1755 1495/1750 1501/1756 +f 1496/1749 1502/1755 1497/1751 +f 1497/1751 1502/1755 1503/1757 +f 1497/1751 1503/1757 1498/1752 +f 1498/1752 1503/1757 1504/1758 +f 879/1072 886/1079 1499/1753 +f 1499/1753 886/1079 1505/1759 +f 1499/1753 1505/1759 1500/1754 +f 1500/1754 1505/1759 1506/1760 +f 1508/1761 1502/1755 1507/1762 +f 1507/1762 1502/1755 1501/1756 +f 1502/1755 1508/1761 1503/1757 +f 1503/1757 1508/1761 1509/1763 +f 1503/1757 1509/1763 1504/1758 +f 1504/1758 1509/1763 1510/1764 +f 886/1079 893/1086 1505/1759 +f 1505/1759 893/1086 1511/1765 +f 1505/1759 1511/1765 1506/1760 +f 1506/1760 1511/1765 1512/1766 +f 1513/1767 1508/1761 1883/1768 +f 1883/1768 1508/1761 1507/1762 +f 1508/1761 1513/1767 1509/1763 +f 1509/1763 1513/1767 1514/1769 +f 1509/1763 1514/1769 1510/1764 +f 1510/1764 1514/1769 1884/1770 +f 1510/1764 1884/1770 1905/1771 +f 1905/1771 1884/1770 1515/1772 +f 1905/1771 1986/1095 1904/1773 +f 1904/1773 1986/1095 1987/1096 +f 893/1086 899/1098 1511/1765 +f 1511/1765 899/1098 1516/1774 +f 1511/1765 1516/1774 1512/1766 +f 1512/1766 1516/1774 1517/1775 +f 1313/1101 906/1103 1882/1776 +f 1882/1776 906/1103 1522/1777 +f 1339/1582 908/1105 697/885 +f 1340/1584 908/1105 1339/1582 +f 1341/1586 908/1105 1340/1584 +f 1342/1588 908/1105 1341/1586 +f 1343/1590 908/1105 1342/1588 +f 1344/1592 908/1105 1343/1590 +f 1345/1594 908/1105 1344/1592 +f 1346/1596 908/1105 1345/1594 +f 1347/1597 908/1105 1346/1596 +f 1348/1599 908/1105 1347/1597 +f 1349/1602 908/1105 1348/1599 +f 1349/1602 1898/1604 908/1105 +f 1974/1606 908/1105 1898/1604 +f 1556/1778 1622/1779 1595/1780 +f 1595/1780 1622/1779 1659/1781 +f 1622/1779 1556/1778 1643/1782 +f 1643/1782 1556/1778 1571/1783 +f 1557/1784 1622/1779 1573/1785 +f 1573/1785 1622/1779 1643/1782 +f 1622/1779 1557/1784 1659/1781 +f 1659/1781 1557/1784 1596/1786 +f 1558/1787 1623/1788 1594/1789 +f 1594/1789 1623/1788 1658/1790 +f 1623/1788 1558/1787 1644/1791 +f 1644/1791 1558/1787 1570/1792 +f 1556/1778 1623/1788 1571/1783 +f 1571/1783 1623/1788 1644/1791 +f 1623/1788 1556/1778 1658/1790 +f 1658/1790 1556/1778 1595/1780 +f 1559/1793 1624/1794 1593/1795 +f 1593/1795 1624/1794 1657/1796 +f 1624/1794 1559/1793 1642/1797 +f 1642/1797 1559/1793 1568/1798 +f 1558/1787 1624/1794 1570/1792 +f 1570/1792 1624/1794 1642/1797 +f 1624/1794 1558/1787 1657/1796 +f 1657/1796 1558/1787 1594/1789 +f 1625/1799 1656/1800 1560/1801 +f 1560/1801 1656/1800 1592/1802 +f 1625/1799 1560/1801 1645/1803 +f 1645/1803 1560/1801 1575/1804 +f 1559/1793 1625/1799 1568/1798 +f 1568/1798 1625/1799 1645/1803 +f 1625/1799 1559/1793 1656/1800 +f 1656/1800 1559/1793 1593/1795 +f 1561/1805 1579/1806 1626/1807 +f 1626/1807 1579/1806 1647/1808 +f 1626/1807 1647/1808 1560/1801 +f 1560/1801 1647/1808 1575/1804 +f 1626/1807 1682/1809 1561/1805 +f 1561/1805 1682/1809 1681/1810 +f 1627/1811 1655/1812 1562/1813 +f 1562/1813 1655/1812 1591/1814 +f 1562/1813 1583/1815 1627/1811 +f 1627/1811 1583/1815 1649/1816 +f 1627/1811 1649/1816 1561/1805 +f 1561/1805 1649/1816 1579/1806 +f 1561/1805 1681/1810 1627/1811 +f 1627/1811 1681/1810 1655/1812 +f 1628/1817 1654/1818 1563/1819 +f 1563/1819 1654/1818 1590/1820 +f 1563/1819 1586/1821 1628/1817 +f 1628/1817 1586/1821 1651/1822 +f 1628/1817 1651/1822 1562/1813 +f 1562/1813 1651/1822 1583/1815 +f 1562/1813 1591/1814 1628/1817 +f 1628/1817 1591/1814 1654/1818 +f 1629/1823 1653/1824 1564/1825 +f 1564/1825 1653/1824 1588/1826 +f 1564/1825 1582/1827 1629/1823 +f 1629/1823 1582/1827 1650/1828 +f 1629/1823 1650/1828 1563/1819 +f 1563/1819 1650/1828 1586/1821 +f 1563/1819 1590/1820 1629/1823 +f 1629/1823 1590/1820 1653/1824 +f 1630/1829 1652/1830 1565/1831 +f 1565/1831 1652/1830 1589/1832 +f 1565/1831 1578/1833 1630/1829 +f 1630/1829 1578/1833 1648/1834 +f 1630/1829 1648/1834 1564/1825 +f 1564/1825 1648/1834 1582/1827 +f 1564/1825 1588/1826 1630/1829 +f 1630/1829 1588/1826 1652/1830 +f 1557/1784 1631/1835 1596/1786 +f 1596/1786 1631/1835 1660/1836 +f 1631/1835 1557/1784 1646/1837 +f 1646/1837 1557/1784 1573/1785 +f 1565/1831 1631/1835 1578/1833 +f 1578/1833 1631/1835 1646/1837 +f 1631/1835 1565/1831 1660/1836 +f 1660/1836 1565/1831 1589/1832 +f 1596/1786 1588/1826 1659/1781 +f 1659/1781 1588/1826 1653/1824 +f 1601/1838 1928/1839 1663/1840 +f 1663/1840 1928/1839 1927/1841 +f 1538/1842 1926/1843 1632/1844 +f 1632/1844 1926/1843 1927/1841 +f 1605/1845 1935/1846 1665/1847 +f 1665/1847 1935/1846 1933/1848 +f 1540/1849 1931/1850 1633/1851 +f 1633/1851 1931/1850 1933/1848 +f 1524/1852 1931/1850 1666/1853 +f 1666/1853 1931/1850 1929/1854 +f 1566/1855 1928/1839 1634/1856 +f 1634/1856 1928/1839 1929/1854 +f 1667/1857 1523/1858 1930/1859 +f 1930/1859 1523/1858 1926/1843 +f 1539/1860 1932/1861 1635/1862 +f 1635/1862 1932/1861 1930/1859 +f 1525/1863 1932/1861 1668/1864 +f 1668/1864 1932/1861 1934/1865 +f 1636/1866 1541/1867 1934/1865 +f 1934/1865 1541/1867 1936/1868 +f 1637/1869 1542/1870 1938/1871 +f 1938/1871 1542/1870 1940/1872 +f 1661/1873 1597/1874 1938/1871 +f 1938/1871 1597/1874 1936/1868 +f 1662/1875 1599/1876 1942/1877 +f 1942/1877 1599/1876 1940/1872 +f 1638/1878 1544/1879 1942/1877 +f 1942/1877 1544/1879 1944/1880 +f 1669/1881 1526/1882 1945/1883 +f 1945/1883 1526/1882 1944/1880 +f 1639/1884 1545/1885 1945/1883 +f 1945/1883 1545/1885 1943/1886 +f 1664/1887 1603/1888 1941/1889 +f 1941/1889 1603/1888 1943/1886 +f 1640/1890 1543/1891 1941/1889 +f 1941/1889 1543/1891 1939/1892 +f 1670/1893 1527/1894 1937/1895 +f 1937/1895 1527/1894 1939/1892 +f 1641/1896 1567/1897 1937/1895 +f 1937/1895 1567/1897 1935/1846 +f 1529/1898 1569/1899 1568/1798 +f 1568/1798 1569/1899 1642/1797 +f 1569/1899 1528/1900 1642/1797 +f 1642/1797 1528/1900 1570/1792 +f 1531/1901 1572/1902 1571/1783 +f 1571/1783 1572/1902 1643/1782 +f 1572/1902 1530/1903 1643/1782 +f 1643/1782 1530/1903 1573/1785 +f 1528/1900 1574/1904 1570/1792 +f 1570/1792 1574/1904 1644/1791 +f 1574/1904 1531/1901 1644/1791 +f 1644/1791 1531/1901 1571/1783 +f 1532/1905 1576/1906 1575/1804 +f 1575/1804 1576/1906 1645/1803 +f 1576/1906 1529/1898 1645/1803 +f 1645/1803 1529/1898 1568/1798 +f 1530/1903 1577/1907 1573/1785 +f 1573/1785 1577/1907 1646/1837 +f 1577/1907 1533/1908 1646/1837 +f 1646/1837 1533/1908 1578/1833 +f 1579/1806 1534/1909 1647/1808 +f 1647/1808 1534/1909 1580/1910 +f 1532/1905 1575/1804 1580/1910 +f 1580/1910 1575/1804 1647/1808 +f 1578/1833 1533/1908 1648/1834 +f 1648/1834 1533/1908 1581/1911 +f 1535/1912 1582/1827 1581/1911 +f 1581/1911 1582/1827 1648/1834 +f 1583/1815 1536/1913 1649/1816 +f 1649/1816 1536/1913 1584/1914 +f 1534/1909 1579/1806 1584/1914 +f 1584/1914 1579/1806 1649/1816 +f 1582/1827 1535/1912 1650/1828 +f 1650/1828 1535/1912 1585/1915 +f 1537/1916 1586/1821 1585/1915 +f 1585/1915 1586/1821 1650/1828 +f 1586/1821 1537/1916 1651/1822 +f 1651/1822 1537/1916 1587/1917 +f 1536/1913 1583/1815 1587/1917 +f 1587/1917 1583/1815 1651/1822 +f 1569/1918 1529/1919 1635/1862 +f 1635/1862 1529/1919 1539/1860 +f 1635/1862 1538/1842 1569/1918 +f 1569/1918 1538/1842 1528/1920 +f 1572/1921 1531/1922 1634/1856 +f 1634/1856 1531/1922 1566/1855 +f 1572/1921 1634/1856 1530/1923 +f 1530/1923 1634/1856 1540/1849 +f 1574/1924 1528/1920 1632/1844 +f 1632/1844 1528/1920 1538/1842 +f 1632/1844 1566/1855 1574/1924 +f 1574/1924 1566/1855 1531/1922 +f 1576/1925 1532/1926 1636/1866 +f 1636/1866 1532/1926 1541/1867 +f 1636/1866 1539/1860 1576/1925 +f 1576/1925 1539/1860 1529/1919 +f 1530/1923 1540/1849 1577/1927 +f 1577/1927 1540/1849 1633/1851 +f 1577/1927 1633/1851 1533/1928 +f 1533/1928 1633/1851 1567/1897 +f 1580/1929 1534/1930 1637/1869 +f 1637/1869 1534/1930 1542/1870 +f 1637/1869 1541/1867 1580/1929 +f 1580/1929 1541/1867 1532/1926 +f 1533/1928 1567/1897 1581/1931 +f 1581/1931 1567/1897 1641/1896 +f 1581/1931 1641/1896 1535/1932 +f 1535/1932 1641/1896 1543/1891 +f 1584/1933 1536/1934 1638/1878 +f 1638/1878 1536/1934 1544/1879 +f 1638/1878 1542/1870 1584/1933 +f 1584/1933 1542/1870 1534/1930 +f 1535/1932 1543/1891 1585/1935 +f 1585/1935 1543/1891 1640/1890 +f 1585/1935 1640/1890 1537/1936 +f 1537/1936 1640/1890 1545/1885 +f 1587/1937 1537/1936 1639/1884 +f 1639/1884 1537/1936 1545/1885 +f 1639/1884 1544/1879 1587/1937 +f 1587/1937 1544/1879 1536/1934 +f 1547/1938 1611/1939 1599/1876 +f 1599/1876 1611/1939 1661/1873 +f 1546/1940 1597/1874 1611/1939 +f 1611/1939 1597/1874 1661/1873 +f 1548/1941 1598/1942 1526/1882 +f 1526/1882 1598/1942 1662/1875 +f 1598/1942 1547/1938 1662/1875 +f 1662/1875 1547/1938 1599/1876 +f 1523/1858 1550/1943 1663/1840 +f 1663/1840 1550/1943 1600/1944 +f 1549/1945 1601/1838 1600/1944 +f 1600/1944 1601/1838 1663/1840 +f 1552/1946 1602/1947 1527/1894 +f 1527/1894 1602/1947 1664/1887 +f 1602/1947 1551/1948 1664/1887 +f 1664/1887 1551/1948 1603/1888 +f 1524/1852 1554/1949 1665/1847 +f 1665/1847 1554/1949 1604/1950 +f 1604/1950 1553/1951 1665/1847 +f 1665/1847 1553/1951 1605/1845 +f 1601/1838 1549/1945 1666/1853 +f 1666/1853 1549/1945 1606/1952 +f 1554/1949 1524/1852 1606/1952 +f 1606/1952 1524/1852 1666/1853 +f 1525/1863 1555/1953 1667/1857 +f 1667/1857 1555/1953 1607/1954 +f 1550/1943 1523/1858 1607/1954 +f 1607/1954 1523/1858 1667/1857 +f 1597/1874 1546/1940 1668/1864 +f 1668/1864 1546/1940 1608/1955 +f 1555/1953 1525/1863 1608/1955 +f 1608/1955 1525/1863 1668/1864 +f 1551/1948 1609/1956 1603/1888 +f 1603/1888 1609/1956 1669/1881 +f 1609/1956 1548/1941 1669/1881 +f 1669/1881 1548/1941 1526/1882 +f 1553/1951 1610/1957 1605/1845 +f 1605/1845 1610/1957 1670/1893 +f 1610/1957 1552/1946 1670/1893 +f 1670/1893 1552/1946 1527/1894 +f 1547/1938 1612/1958 1611/1939 +f 1611/1939 1612/1958 1671/1959 +f 1613/1960 1546/1940 1671/1959 +f 1671/1959 1546/1940 1611/1939 +f 1598/1942 1548/1941 1672/1961 +f 1672/1961 1548/1941 1614/1962 +f 1547/1938 1598/1942 1612/1958 +f 1612/1958 1598/1942 1672/1961 +f 1550/1943 1615/1963 1600/1944 +f 1600/1944 1615/1963 1673/1964 +f 1616/1965 1549/1945 1673/1964 +f 1673/1964 1549/1945 1600/1944 +f 1602/1947 1552/1946 1674/1966 +f 1674/1966 1552/1946 1617/1967 +f 1551/1948 1602/1947 1618/1968 +f 1618/1968 1602/1947 1674/1966 +f 1604/1950 1554/1949 1675/1969 +f 1675/1969 1554/1949 1619/1970 +f 1553/1951 1604/1950 1620/1971 +f 1620/1971 1604/1950 1675/1969 +f 1549/1945 1616/1965 1606/1952 +f 1606/1952 1616/1965 1676/1972 +f 1619/1970 1554/1949 1676/1972 +f 1676/1972 1554/1949 1606/1952 +f 1555/1953 1621/1973 1607/1954 +f 1607/1954 1621/1973 1677/1974 +f 1615/1963 1550/1943 1677/1974 +f 1677/1974 1550/1943 1607/1954 +f 1546/1940 1613/1960 1608/1955 +f 1608/1955 1613/1960 1678/1975 +f 1621/1973 1555/1953 1678/1975 +f 1678/1975 1555/1953 1608/1955 +f 1609/1956 1551/1948 1679/1976 +f 1679/1976 1551/1948 1618/1968 +f 1548/1941 1609/1956 1614/1962 +f 1614/1962 1609/1956 1679/1976 +f 1610/1957 1553/1951 1680/1977 +f 1680/1977 1553/1951 1620/1971 +f 1552/1946 1610/1957 1617/1967 +f 1617/1967 1610/1957 1680/1977 +f 1652/1830 1588/1826 1589/1832 +f 1588/1826 1596/1786 1589/1832 +f 1596/1786 1660/1836 1589/1832 +f 1595/1780 1590/1820 1658/1790 +f 1658/1790 1590/1820 1654/1818 +f 1659/1781 1653/1824 1595/1780 +f 1595/1780 1653/1824 1590/1820 +f 1658/1790 1654/1818 1594/1789 +f 1594/1789 1654/1818 1591/1814 +f 1656/1800 1593/1795 1592/1802 +f 1681/1810 1682/1809 1593/1795 +f 1593/1795 1682/1809 1592/1802 +f 1591/1814 1655/1812 1594/1789 +f 1594/1789 1655/1812 1657/1796 +f 1560/1801 1592/1802 1626/1807 +f 1626/1807 1592/1802 1682/1809 +f 1593/1795 1657/1796 1681/1810 +f 1681/1810 1657/1796 1655/1812 +f 1612/1958 1520/1978 1671/1959 +f 1671/1959 1520/1978 1521/1979 +f 1671/1959 1521/1979 1613/1960 +f 1613/1960 1521/1979 1883/1768 +f 1518/1980 1519/1981 1614/1962 +f 1614/1962 1519/1981 1672/1961 +f 1489/1741 1486/1740 1615/1963 +f 1615/1963 1486/1740 1673/1964 +f 1673/1964 1486/1740 1616/1965 +f 1616/1965 1486/1740 1477/1731 +f 1500/1754 1506/1760 1617/1967 +f 1617/1967 1506/1760 1674/1966 +f 1506/1760 1512/1766 1674/1966 +f 1674/1966 1512/1766 1618/1968 +f 1619/1970 1475/1729 1675/1969 +f 1675/1969 1475/1729 1485/1739 +f 1675/1969 1485/1739 1620/1971 +f 1620/1971 1485/1739 1488/1746 +f 1477/1731 1476/1730 1616/1965 +f 1616/1965 1476/1730 1676/1972 +f 1676/1972 1476/1730 1619/1970 +f 1619/1970 1476/1730 1475/1729 +f 1621/1973 1501/1756 1677/1974 +f 1677/1974 1501/1756 1495/1750 +f 1677/1974 1495/1750 1615/1963 +f 1615/1963 1495/1750 1489/1741 +f 1678/1975 1507/1762 1621/1973 +f 1621/1973 1507/1762 1501/1756 +f 1618/1968 1512/1766 1679/1976 +f 1679/1976 1512/1766 1517/1775 +f 1517/1775 1518/1980 1679/1976 +f 1679/1976 1518/1980 1614/1962 +f 1620/1971 1488/1746 1680/1977 +f 1680/1977 1488/1746 1494/1748 +f 1680/1977 1494/1748 1617/1967 +f 1617/1967 1494/1748 1500/1754 +f 1519/1981 1520/1978 1672/1961 +f 1672/1961 1520/1978 1612/1958 +f 1488/1746 1485/1739 1484/1738 +f 1485/1739 1475/1729 1474/1728 +f 1678/1975 1613/1960 1507/1762 +f 1507/1762 1613/1960 1883/1768 +f 1808/1982 1688/1983 1722/1984 +f 1688/1983 1723/1985 1722/1984 +f 1722/1984 1723/1985 1808/1982 +f 1724/1986 1071/1317 1809/1987 +f 1809/1987 1071/1317 1093/1318 +f 1880/1988 1724/1986 1879/1989 +f 1879/1989 1724/1986 1809/1987 +f 1724/1986 1880/1988 1810/1990 +f 1810/1990 1880/1988 1881/1991 +f 1094/1325 1810/1990 1111/1323 +f 1111/1323 1810/1990 1858/1992 +f 1071/1317 1724/1986 1094/1325 +f 1094/1325 1724/1986 1810/1990 +f 1868/1993 1725/1994 1790/1995 +f 1790/1995 1725/1994 1689/1996 +f 1725/1994 1868/1993 1684/1997 +f 1684/1997 1868/1993 1686/1998 +f 1726/1999 1721/2000 1691/2001 +f 1691/2001 1721/2000 1683/2002 +f 1072/1336 1070/1337 1726/1999 +f 1726/1999 1070/1337 1721/2000 +f 1725/1994 1684/1997 1811/2003 +f 1811/2003 1684/1997 1727/2004 +f 1685/2005 1848/2006 1727/2004 +f 1727/2004 1848/2006 1811/2003 +f 1848/2006 1793/2007 1811/2003 +f 1811/2003 1793/2007 1728/2008 +f 1689/1996 1725/1994 1728/2008 +f 1728/2008 1725/1994 1811/2003 +f 1845/2009 1729/2010 1789/2011 +f 1789/2011 1729/2010 1692/2012 +f 1790/1995 1689/1996 1845/2009 +f 1845/2009 1689/1996 1729/2010 +f 1730/2013 1694/2014 1812/2015 +f 1812/2015 1694/2014 1731/2016 +f 1695/2017 1816/2018 1731/2016 +f 1731/2016 1816/2018 1812/2015 +f 1816/2018 1732/2019 1812/2015 +f 1732/2019 1693/2020 1812/2015 +f 1812/2015 1693/2020 1730/2013 +f 1733/2021 1691/2001 1813/2022 +f 1813/2022 1691/2001 1690/2023 +f 1696/2024 1733/2021 1791/2025 +f 1791/2025 1733/2021 1813/2022 +f 1733/2021 1696/2024 1814/2026 +f 1814/2026 1696/2024 1734/2027 +f 1073/1364 1095/1363 1734/2027 +f 1734/2027 1095/1363 1814/2026 +f 1095/1363 1072/1336 1814/2026 +f 1814/2026 1072/1336 1726/1999 +f 1691/2001 1733/2021 1726/1999 +f 1726/1999 1733/2021 1814/2026 +f 1849/2028 1794/2029 1815/2030 +f 1815/2030 1794/2029 1735/2031 +f 1735/2031 1692/2012 1815/2030 +f 1815/2030 1692/2012 1729/2010 +f 1689/1996 1728/2008 1729/2010 +f 1729/2010 1728/2008 1815/2030 +f 1793/2007 1849/2028 1728/2008 +f 1728/2008 1849/2028 1815/2030 +f 1695/2017 1697/2032 1816/2018 +f 1816/2018 1697/2032 1736/2033 +f 1736/2033 1737/2034 1816/2018 +f 1699/2035 1738/2036 1779/2037 +f 1779/2037 1738/2036 1700/2038 +f 1738/2036 1780/2039 1700/2038 +f 1700/2038 1780/2039 1867/2040 +f 1739/2041 1074/1380 1817/2042 +f 1817/2042 1074/1380 1096/1381 +f 1096/1381 1075/1382 1817/2042 +f 1817/2042 1075/1382 1837/2043 +f 1780/2039 1738/2036 1837/2043 +f 1837/2043 1738/2036 1817/2042 +f 1699/2035 1739/2041 1738/2036 +f 1738/2036 1739/2041 1817/2042 +f 1799/2044 1702/2045 1818/2046 +f 1818/2046 1702/2045 1741/2047 +f 1701/2048 1740/2049 1741/2047 +f 1741/2047 1740/2049 1818/2046 +f 1703/2050 1743/2051 1742/2052 +f 1742/2052 1743/2051 1819/2053 +f 1687/2054 1808/1982 1743/2051 +f 1743/2051 1808/1982 1819/2053 +f 1819/2053 1745/2055 1742/2052 +f 1742/2052 1745/2055 1872/2056 +f 1723/1985 1744/2057 1808/1982 +f 1808/1982 1744/2057 1819/2053 +f 1744/2057 1704/2058 1819/2053 +f 1819/2053 1704/2058 1745/2055 +f 1877/2059 1878/2060 1746/2061 +f 1746/2061 1878/2060 1820/2062 +f 1878/2060 1879/1989 1820/2062 +f 1820/2062 1879/1989 1809/1987 +f 1809/1987 1093/1318 1820/2062 +f 1820/2062 1093/1318 1097/1402 +f 1076/1403 1746/2061 1097/1402 +f 1097/1402 1746/2061 1820/2062 +f 1789/2011 1692/2012 1844/2063 +f 1844/2063 1692/2012 1747/2064 +f 1844/2063 1747/2064 1705/2065 +f 1705/2065 1747/2064 1706/2066 +f 1748/2067 1707/2068 1821/2069 +f 1821/2069 1707/2068 1749/2070 +f 1708/2071 1750/2072 1749/2070 +f 1749/2070 1750/2072 1821/2069 +f 1750/2072 1703/2050 1821/2069 +f 1821/2069 1703/2050 1742/2052 +f 1707/2068 1748/2067 1751/2073 +f 1751/2073 1748/2067 1822/2074 +f 1745/2055 1806/2075 1872/2056 +f 1752/2076 1872/2056 1806/2075 +f 1752/2076 1697/2032 1822/2074 +f 1822/2074 1697/2032 1753/2077 +f 1709/2078 1751/2073 1753/2077 +f 1753/2077 1751/2073 1822/2074 +f 1703/2050 1750/2072 1754/2079 +f 1754/2079 1750/2072 1823/2080 +f 1750/2072 1708/2071 1823/2080 +f 1823/2080 1708/2071 1755/2081 +f 1803/2082 1857/2083 1755/2081 +f 1755/2081 1857/2083 1823/2080 +f 1857/2083 1804/2084 1823/2080 +f 1823/2080 1804/2084 1754/2079 +f 1756/2085 1843/2086 1710/2087 +f 1710/2087 1843/2086 1788/2088 +f 1843/2086 1756/2085 1787/2089 +f 1787/2089 1756/2085 1711/2090 +f 1757/2091 1713/2092 1824/2093 +f 1824/2093 1713/2092 1758/2094 +f 1715/2095 1759/2096 1758/2094 +f 1758/2094 1759/2096 1824/2093 +f 1759/2096 1714/2097 1824/2093 +f 1824/2093 1714/2097 1760/2098 +f 1712/2099 1757/2091 1760/2098 +f 1760/2098 1757/2091 1824/2093 +f 1761/2100 1716/2101 1825/2102 +f 1825/2102 1716/2101 1762/2103 +f 1701/2048 1763/2104 1762/2103 +f 1762/2103 1763/2104 1825/2102 +f 1713/2092 1757/2091 1763/2104 +f 1763/2104 1757/2091 1825/2102 +f 1757/2091 1712/2099 1825/2102 +f 1825/2102 1712/2099 1761/2100 +f 1764/2105 1796/2106 1826/2107 +f 1826/2107 1796/2106 1852/2108 +f 1797/2109 1765/2110 1852/2108 +f 1852/2108 1765/2110 1826/2107 +f 1765/2110 1711/2090 1826/2107 +f 1826/2107 1711/2090 1756/2085 +f 1710/2087 1764/2105 1756/2085 +f 1756/2085 1764/2105 1826/2107 +f 1714/2097 1759/2096 1766/2111 +f 1766/2111 1759/2096 1827/2112 +f 1759/2096 1715/2095 1827/2112 +f 1827/2112 1715/2095 1767/2113 +f 1801/2114 1855/2115 1767/2113 +f 1767/2113 1855/2115 1827/2112 +f 1855/2115 1802/2116 1827/2112 +f 1827/2112 1802/2116 1766/2111 +f 1787/2089 1711/2090 1842/2117 +f 1842/2117 1711/2090 1765/2110 +f 1798/2118 1842/2117 1797/2109 +f 1797/2109 1842/2117 1765/2110 +f 1800/2119 1854/2120 1768/2121 +f 1768/2121 1854/2120 1828/2122 +f 1854/2120 1801/2114 1828/2122 +f 1828/2122 1801/2114 1767/2113 +f 1767/2113 1715/2095 1828/2122 +f 1828/2122 1715/2095 1758/2094 +f 1713/2092 1768/2121 1758/2094 +f 1758/2094 1768/2121 1828/2122 +f 1769/2123 1696/2024 1829/2124 +f 1829/2124 1696/2024 1791/2025 +f 1717/2125 1769/2123 1792/2126 +f 1792/2126 1769/2123 1829/2124 +f 1718/2127 1770/2128 1771/2129 +f 1771/2129 1770/2128 1830/2130 +f 1745/2055 1704/2058 1831/2131 +f 1831/2131 1704/2058 1772/2132 +f 1772/2132 1876/2133 1873/2134 +f 1873/2134 1876/2133 1875/2135 +f 1769/2123 1717/2125 1832/2136 +f 1832/2136 1717/2125 1774/2137 +f 1077/1480 1098/1479 1774/2137 +f 1774/2137 1098/1479 1832/2136 +f 1098/1479 1073/1364 1832/2136 +f 1832/2136 1073/1364 1734/2027 +f 1696/2024 1769/2123 1734/2027 +f 1734/2027 1769/2123 1832/2136 +f 1877/2059 1746/2061 1876/2133 +f 1876/2133 1746/2061 1833/2138 +f 1746/2061 1076/1403 1833/2138 +f 1833/2138 1076/1403 1099/1482 +f 1078/1484 1775/2139 1099/1482 +f 1099/1482 1775/2139 1833/2138 +f 1775/2139 1875/2135 1833/2138 +f 1833/2138 1875/2135 1876/2133 +f 1719/2140 1717/2125 1847/2141 +f 1847/2141 1717/2125 1792/2126 +f 1770/2128 1718/2127 1777/2142 +f 1777/2142 1718/2127 1776/2143 +f 1873/2134 1875/2135 1836/2144 +f 1836/2144 1875/2135 1874/2145 +f 1079/1491 1077/1480 1834/2146 +f 1834/2146 1077/1480 1774/2137 +f 1717/2125 1719/2140 1774/2137 +f 1774/2137 1719/2140 1834/2146 +f 1775/2139 1078/1484 1835/2147 +f 1835/2147 1078/1484 1100/1494 +f 1875/2135 1775/2139 1874/2145 +f 1874/2145 1775/2139 1835/2147 +f 1777/2142 1776/2143 1700/2038 +f 1700/2038 1776/2143 1779/2037 +f 1075/1382 1079/1491 1837/2043 +f 1837/2043 1079/1491 1834/2146 +f 1719/2140 1780/2039 1834/2146 +f 1834/2146 1780/2039 1837/2043 +f 1795/2148 1781/2149 1850/2150 +f 1850/2150 1781/2149 1838/2151 +f 1781/2149 1706/2066 1838/2151 +f 1838/2151 1706/2066 1747/2064 +f 1692/2012 1735/2031 1747/2064 +f 1747/2064 1735/2031 1838/2151 +f 1735/2031 1794/2029 1838/2151 +f 1838/2151 1794/2029 1850/2150 +f 1856/2152 1803/2082 1839/2153 +f 1839/2153 1803/2082 1755/2081 +f 1708/2071 1782/2154 1755/2081 +f 1755/2081 1782/2154 1839/2153 +f 1782/2154 1714/2097 1839/2153 +f 1839/2153 1714/2097 1766/2111 +f 1802/2116 1856/2152 1766/2111 +f 1766/2111 1856/2152 1839/2153 +f 1720/2155 1716/2101 1840/2156 +f 1840/2156 1716/2101 1783/2157 +f 1709/2078 1784/2158 1783/2157 +f 1783/2157 1784/2158 1840/2156 +f 1705/2065 1706/2066 1871/2159 +f 1871/2159 1706/2066 1785/2160 +f 1710/2087 1788/2088 1785/2160 +f 1785/2160 1788/2088 1871/2159 +f 1786/2161 1712/2099 1841/2162 +f 1841/2162 1712/2099 1760/2098 +f 1714/2097 1782/2154 1760/2098 +f 1760/2098 1782/2154 1841/2162 +f 1782/2154 1708/2071 1841/2162 +f 1841/2162 1708/2071 1749/2070 +f 1707/2068 1786/2161 1749/2070 +f 1749/2070 1786/2161 1841/2162 +f 1798/2118 1799/2044 1842/2117 +f 1842/2117 1799/2044 1818/2046 +f 1740/2049 1787/2089 1818/2046 +f 1818/2046 1787/2089 1842/2117 +f 1787/2089 1740/2049 1843/2086 +f 1843/2086 1740/2049 1864/2163 +f 1720/2155 1788/2088 1864/2163 +f 1864/2163 1788/2088 1843/2086 +f 1705/2065 1784/2158 1844/2063 +f 1844/2063 1784/2158 1861/2164 +f 1695/2017 1789/2011 1861/2164 +f 1861/2164 1789/2011 1844/2063 +f 1789/2011 1695/2017 1845/2009 +f 1845/2009 1695/2017 1731/2016 +f 1694/2014 1790/1995 1731/2016 +f 1731/2016 1790/1995 1845/2009 +f 1694/2014 1730/2013 1686/1998 +f 1686/1998 1730/2013 1846/2165 +f 1897/2166 1693/2020 1732/2019 +f 1830/2130 1770/2128 1829/2124 +f 1829/2124 1770/2128 1792/2126 +f 1770/2128 1777/2142 1792/2126 +f 1792/2126 1777/2142 1847/2141 +f 1777/2142 1700/2038 1847/2141 +f 1847/2141 1700/2038 1867/2040 +f 1069/1514 1101/1515 1685/2005 +f 1685/2005 1101/1515 1848/2006 +f 1101/1515 1080/1516 1848/2006 +f 1848/2006 1080/1516 1793/2007 +f 1080/1516 1102/1517 1793/2007 +f 1793/2007 1102/1517 1849/2028 +f 1102/1517 1081/1518 1849/2028 +f 1849/2028 1081/1518 1794/2029 +f 1081/1518 1103/1519 1794/2029 +f 1794/2029 1103/1519 1850/2150 +f 1082/1520 1795/2148 1103/1519 +f 1103/1519 1795/2148 1850/2150 +f 1795/2148 1082/1520 1851/2167 +f 1851/2167 1082/1520 1104/1522 +f 1083/1523 1796/2106 1104/1522 +f 1104/1522 1796/2106 1851/2167 +f 1796/2106 1083/1523 1852/2108 +f 1852/2108 1083/1523 1105/1524 +f 1084/1525 1797/2109 1105/1524 +f 1105/1524 1797/2109 1852/2108 +f 1085/1526 1798/2118 1084/1525 +f 1084/1525 1798/2118 1797/2109 +f 1086/1527 1799/2044 1085/1526 +f 1085/1526 1799/2044 1798/2118 +f 1088/1530 1106/1529 1702/2045 +f 1702/2045 1106/1529 1853/2168 +f 1106/1529 1087/1531 1853/2168 +f 1853/2168 1087/1531 1800/2119 +f 1107/1532 1089/1533 1854/2120 +f 1854/2120 1089/1533 1801/2114 +f 1087/1531 1107/1532 1800/2119 +f 1800/2119 1107/1532 1854/2120 +f 1089/1533 1108/1534 1801/2114 +f 1801/2114 1108/1534 1855/2115 +f 1108/1534 1090/1535 1855/2115 +f 1855/2115 1090/1535 1802/2116 +f 1109/1536 1091/1537 1856/2152 +f 1856/2152 1091/1537 1803/2082 +f 1090/1535 1109/1536 1802/2116 +f 1802/2116 1109/1536 1856/2152 +f 1091/1537 1110/1538 1803/2082 +f 1803/2082 1110/1538 1857/2083 +f 1110/1538 1092/1539 1857/2083 +f 1857/2083 1092/1539 1804/2084 +f 1092/1539 1111/1323 1804/2084 +f 1804/2084 1111/1323 1858/1992 +f 1739/2041 1699/2035 1859/2169 +f 1859/2169 1699/2035 1805/2170 +f 1874/2145 1835/2147 1805/2170 +f 1805/2170 1835/2147 1859/2169 +f 1835/2147 1100/1494 1859/2169 +f 1859/2169 1100/1494 1112/1542 +f 1074/1380 1739/2041 1112/1542 +f 1112/1542 1739/2041 1859/2169 +f 1086/1527 1088/1530 1799/2044 +f 1799/2044 1088/1530 1702/2045 +f 1698/2171 1806/2075 1771/2129 +f 1771/2129 1806/2075 1860/2172 +f 1806/2075 1745/2055 1860/2172 +f 1860/2172 1745/2055 1831/2131 +f 1773/2173 1807/2174 1831/2131 +f 1831/2131 1807/2174 1860/2172 +f 1807/2174 1718/2127 1860/2172 +f 1860/2172 1718/2127 1771/2129 +f 1784/2158 1709/2078 1861/2164 +f 1861/2164 1709/2078 1753/2077 +f 1695/2017 1861/2164 1697/2032 +f 1697/2032 1861/2164 1753/2077 +f 1718/2127 1807/2174 1776/2143 +f 1776/2143 1807/2174 1862/2175 +f 1807/2174 1773/2173 1862/2175 +f 1862/2175 1773/2173 1778/2176 +f 1786/2161 1707/2068 1863/2177 +f 1863/2177 1707/2068 1751/2073 +f 1751/2073 1709/2078 1863/2177 +f 1863/2177 1709/2078 1783/2157 +f 1716/2101 1761/2100 1783/2157 +f 1783/2157 1761/2100 1863/2177 +f 1712/2099 1786/2161 1761/2100 +f 1761/2100 1786/2161 1863/2177 +f 1776/2143 1862/2175 1779/2037 +f 1779/2037 1862/2175 1865/2178 +f 1778/2176 1836/2144 1862/2175 +f 1862/2175 1836/2144 1865/2178 +f 1740/2049 1701/2048 1864/2163 +f 1864/2163 1701/2048 1762/2103 +f 1716/2101 1720/2155 1762/2103 +f 1762/2103 1720/2155 1864/2163 +f 1805/2170 1699/2035 1865/2178 +f 1865/2178 1699/2035 1779/2037 +f 1853/2168 1800/2119 1866/2179 +f 1866/2179 1800/2119 1768/2121 +f 1713/2092 1763/2104 1768/2121 +f 1768/2121 1763/2104 1866/2179 +f 1763/2104 1701/2048 1866/2179 +f 1866/2179 1701/2048 1741/2047 +f 1702/2045 1853/2168 1741/2047 +f 1741/2047 1853/2168 1866/2179 +f 1780/2039 1719/2140 1867/2040 +f 1867/2040 1719/2140 1847/2141 +f 1790/1995 1694/2014 1868/1993 +f 1868/1993 1694/2014 1686/1998 +f 1697/2032 1752/2076 1736/2033 +f 1736/2033 1752/2076 1698/2171 +f 1806/2075 1698/2171 1752/2076 +f 1804/2084 1858/1992 1754/2079 +f 1754/2079 1858/1992 1869/2180 +f 1858/1992 1810/1990 1869/2180 +f 1810/1990 1881/1991 1869/2180 +f 1743/2051 1703/2050 1869/2180 +f 1869/2180 1703/2050 1754/2079 +f 1781/2149 1795/2148 1870/2181 +f 1870/2181 1795/2148 1851/2167 +f 1796/2106 1764/2105 1851/2167 +f 1851/2167 1764/2105 1870/2181 +f 1764/2105 1710/2087 1870/2181 +f 1870/2181 1710/2087 1785/2160 +f 1706/2066 1781/2149 1785/2160 +f 1785/2160 1781/2149 1870/2181 +f 1788/2088 1720/2155 1871/2159 +f 1871/2159 1720/2155 1840/2156 +f 1784/2158 1705/2065 1840/2156 +f 1840/2156 1705/2065 1871/2159 +f 1872/2056 1752/2076 1748/2067 +f 1748/2067 1752/2076 1822/2074 +f 1872/2056 1748/2067 1742/2052 +f 1742/2052 1748/2067 1821/2069 +f 1773/2173 1873/2134 1778/2176 +f 1778/2176 1873/2134 1836/2144 +f 1873/2134 1773/2173 1772/2132 +f 1772/2132 1773/2173 1831/2131 +f 1836/2144 1874/2145 1865/2178 +f 1865/2178 1874/2145 1805/2170 +f 1704/2058 1877/2059 1772/2132 +f 1772/2132 1877/2059 1876/2133 +f 1878/2060 1877/2059 1744/2057 +f 1744/2057 1877/2059 1704/2058 +f 1744/2057 1723/1985 1878/2060 +f 1878/2060 1723/1985 1879/1989 +f 1688/1983 1880/1988 1723/1985 +f 1723/1985 1880/1988 1879/1989 +f 1881/1991 1880/1988 1687/2054 +f 1687/2054 1880/1988 1688/1983 +f 1869/2180 1881/1991 1743/2051 +f 1743/2051 1881/1991 1687/2054 +f 1685/2182 1887/2183 1069/1556 +f 1069/1556 1887/2183 1320/1554 +f 1522/1777 1887/2183 1519/1981 +f 1519/1981 1887/2183 1888/2184 +f 1519/1981 1518/1980 1522/1777 +f 1522/1777 1518/1980 1882/1776 +f 1518/1980 1517/1775 1882/1776 +f 1882/1776 1517/1775 1516/1774 +f 899/1098 1313/1101 1516/1774 +f 1516/1774 1313/1101 1882/1776 +f 1684/2185 1686/2186 1889/2187 +f 1889/2187 1686/2186 1890/2188 +f 1883/1768 1521/1979 1891/2189 +f 1891/2189 1521/1979 1890/2188 +f 1886/2190 1893/2191 1885/2192 +f 1885/2192 1893/2191 1892/2193 +f 1515/1772 1899/2194 1316/1570 +f 1316/1570 1899/2194 1321/1568 +f 1519/1981 1888/2184 1520/1978 +f 1520/1978 1888/2184 1889/2187 +f 1883/1768 1891/2189 1513/1767 +f 1513/1767 1891/2189 1892/2193 +f 1730/2013 1693/2020 1846/2165 +f 1846/2165 1693/2020 1885/2195 +f 1691/2001 1683/2002 1690/2023 +f 1690/2023 1683/2002 1886/2196 +f 1514/1769 1893/2191 1884/1770 +f 1884/1770 1893/2191 1894/2197 +f 1885/2195 1693/2020 1886/2196 +f 1886/2196 1693/2020 1690/2023 +f 906/1103 1320/1554 1522/1777 +f 1522/1777 1320/1554 1887/2183 +f 1070/1575 1321/1568 1721/2198 +f 1721/2198 1321/1568 1899/2194 +f 1685/2182 1727/2199 1887/2183 +f 1887/2183 1727/2199 1888/2184 +f 1727/2199 1684/2185 1888/2184 +f 1888/2184 1684/2185 1889/2187 +f 1521/1979 1520/1978 1890/2188 +f 1890/2188 1520/1978 1889/2187 +f 1846/2200 1891/2189 1686/2186 +f 1686/2186 1891/2189 1890/2188 +f 1885/2192 1892/2193 1846/2200 +f 1846/2200 1892/2193 1891/2189 +f 1513/1767 1892/2193 1514/1769 +f 1514/1769 1892/2193 1893/2191 +f 1683/2201 1894/2197 1886/2190 +f 1886/2190 1894/2197 1893/2191 +f 1721/2198 1899/2194 1683/2201 +f 1683/2201 1899/2194 1894/2197 +f 1895/2202 1698/2171 1830/2130 +f 1830/2130 1698/2171 1771/2129 +f 1895/2202 1830/2130 1791/2025 +f 1791/2025 1830/2130 1829/2124 +f 1895/2202 1791/2025 1896/2203 +f 1896/2203 1791/2025 1813/2022 +f 1736/2033 1698/2171 1737/2034 +f 1737/2034 1698/2171 1895/2202 +f 1737/2034 1895/2202 1897/2166 +f 1897/2166 1895/2202 1896/2203 +f 1693/2020 1897/2166 1690/2023 +f 1690/2023 1897/2166 1896/2203 +f 1690/2023 1896/2203 1813/2022 +f 1816/2018 1737/2034 1732/2019 +f 1732/2019 1737/2034 1897/2166 +f 1884/1770 1894/2197 1515/1772 +f 1515/1772 1894/2197 1899/2194 +f 1372/1617 1900/1618 1360/1601 +f 1360/1601 1900/1618 1361/1603 +f 1383/1630 1901/1631 1372/1617 +f 1372/1617 1901/1631 1900/1618 +f 1394/1643 1902/1644 1383/1630 +f 1383/1630 1902/1644 1901/1631 +f 1405/1656 1903/1657 1394/1643 +f 1394/1643 1903/1657 1902/1644 +f 1405/1656 1416/1669 1903/1657 +f 1903/1657 1416/1669 351/866 +f 1427/1681 434/869 1416/1669 +f 1416/1669 434/869 351/866 +f 1498/1752 1904/1773 431/882 +f 1504/1758 1510/1764 1904/1773 +f 1904/1773 1510/1764 1905/1771 +f 1052/1181 987/1166 1906/1182 +f 1906/1182 987/1166 1907/1168 +f 1049/1167 909/1187 1908/1169 +f 1908/1169 909/1187 1909/1172 +f 1018/1171 952/1183 1908/1169 +f 1908/1169 952/1183 1907/1168 +f 1020/1184 926/1177 1906/1182 +f 1906/1182 926/1177 1910/1179 +f 1021/1189 924/1170 1911/1186 +f 1911/1186 924/1170 1909/1172 +f 1051/1174 910/1180 1912/1176 +f 1912/1176 910/1180 1910/1179 +f 1053/1185 911/1191 1911/1186 +f 1911/1186 911/1191 1913/1190 +f 1019/1178 953/1225 1912/1176 +f 1912/1176 953/1225 1914/1175 +f 1022/1194 925/1188 1915/1193 +f 1915/1193 925/1188 1913/1190 +f 991/1173 1914/1175 1056/1221 +f 1056/1221 1914/1175 1916/1222 +f 983/1202 1917/1196 1054/1192 +f 1054/1192 1917/1196 1915/1193 +f 929/1219 1918/1220 1027/1224 +f 1027/1224 1918/1220 1916/1222 +f 927/1195 1917/1196 1023/1197 +f 1023/1197 1917/1196 1919/1198 +f 913/1223 1918/1220 1050/1215 +f 1050/1215 1918/1220 1920/1216 +f 985/1205 1921/1200 1047/1201 +f 1047/1201 1921/1200 1919/1198 +f 931/1213 1922/1214 1026/1218 +f 1026/1218 1922/1214 1920/1216 +f 928/1199 1921/1200 1024/1206 +f 1024/1206 1921/1200 1923/1204 +f 989/1217 1922/1214 1055/1209 +f 1055/1209 1922/1214 1924/1210 +f 912/1211 1925/1208 1048/1203 +f 1048/1203 1925/1208 1923/1204 +f 930/1207 1925/1208 1025/1212 +f 1025/1212 1925/1208 1924/1210 +f 1663/1840 1927/1841 1523/1858 +f 1523/1858 1927/1841 1926/1843 +f 1666/1853 1929/1854 1601/1838 +f 1601/1838 1929/1854 1928/1839 +f 1632/1844 1927/1841 1566/1855 +f 1566/1855 1927/1841 1928/1839 +f 1635/1862 1930/1859 1538/1842 +f 1538/1842 1930/1859 1926/1843 +f 1634/1856 1929/1854 1540/1849 +f 1540/1849 1929/1854 1931/1850 +f 1667/1857 1930/1859 1525/1863 +f 1525/1863 1930/1859 1932/1861 +f 1665/1847 1933/1848 1524/1852 +f 1524/1852 1933/1848 1931/1850 +f 1636/1866 1934/1865 1539/1860 +f 1539/1860 1934/1865 1932/1861 +f 1633/1851 1933/1848 1567/1897 +f 1567/1897 1933/1848 1935/1846 +f 1597/1874 1668/1864 1936/1868 +f 1936/1868 1668/1864 1934/1865 +f 1605/1845 1670/1893 1935/1846 +f 1935/1846 1670/1893 1937/1895 +f 1541/1867 1637/1869 1936/1868 +f 1936/1868 1637/1869 1938/1871 +f 1543/1891 1641/1896 1939/1892 +f 1939/1892 1641/1896 1937/1895 +f 1599/1876 1661/1873 1940/1872 +f 1940/1872 1661/1873 1938/1871 +f 1527/1894 1664/1887 1939/1892 +f 1939/1892 1664/1887 1941/1889 +f 1542/1870 1638/1878 1940/1872 +f 1940/1872 1638/1878 1942/1877 +f 1545/1885 1640/1890 1943/1886 +f 1943/1886 1640/1890 1941/1889 +f 1526/1882 1662/1875 1944/1880 +f 1944/1880 1662/1875 1942/1877 +f 1603/1888 1669/1881 1943/1886 +f 1943/1886 1669/1881 1945/1883 +f 1544/1879 1639/1884 1944/1880 +f 1944/1880 1639/1884 1945/1883 +f 652/622 1946/743 2088/620 +f 2088/620 1946/743 2087/2204 +f 375/556 394/717 1965/550 +f 1965/550 394/717 1946/743 +f 2087/2204 1946/743 2108/795 +f 2108/795 1946/743 394/717 +f 402/677 522/676 1950/845 +f 1950/845 522/676 1949/846 +f 661/687 1951/844 580/680 +f 580/680 1951/844 1950/845 +f 403/685 525/683 1952/843 +f 1952/843 525/683 1951/844 +f 578/728 1953/841 579/688 +f 579/688 1953/841 1952/843 +f 578/728 2095/770 1953/841 +f 1953/841 2095/770 2097/842 +f 585/761 1955/840 420/814 +f 420/814 1955/840 1956/838 +f 660/602 1958/836 576/600 +f 576/600 1958/836 1957/837 +f 528/692 1958/836 405/693 +f 405/693 1958/836 1959/835 +f 659/619 1960/834 575/605 +f 575/605 1960/834 1959/835 +f 531/702 1960/834 408/703 +f 408/703 1960/834 1961/833 +f 574/617 658/740 1961/833 +f 1961/833 658/740 1962/832 +f 413/711 534/710 1963/831 +f 1963/831 534/710 1962/832 +f 672/804 1948/806 572/829 +f 572/829 1948/806 1964/830 +f 1471/1725 1482/1736 432/876 +f 432/876 1482/1736 348/877 +f 1460/1714 1471/1725 349/874 +f 349/874 1471/1725 432/876 +f 1449/1703 1460/1714 433/873 +f 433/873 1460/1714 349/874 +f 1438/1692 1449/1703 350/870 +f 350/870 1449/1703 433/873 +f 352/879 348/877 1492/1744 +f 1492/1744 348/877 1482/1736 +f 431/882 352/879 1498/1752 +f 1498/1752 352/879 1492/1744 +f 431/882 1904/1773 436/481 +f 436/481 1904/1773 1987/1096 +f 436/481 1987/1096 89/416 +f 89/416 1987/1096 321/1097 +f 434/869 1427/1681 350/870 +f 350/870 1427/1681 1438/1692 +f 2050/2205 1974/1606 2049/2206 +f 2049/2206 1974/1606 1973/1605 +f 351/866 435/480 1903/1657 +f 1903/1657 435/480 1975/1658 +f 2048/2207 2049/2206 1976/1619 +f 1976/1619 2049/2206 1973/1605 +f 2047/2208 2048/2207 1977/1632 +f 1977/1632 2048/2207 1976/1619 +f 2047/2208 1977/1632 2046/2209 +f 2046/2209 1977/1632 1978/1645 +f 2045/1670 2046/2209 1975/1658 +f 1975/1658 2046/2209 1978/1645 +f 685/454 2027/455 607/856 +f 607/856 2027/455 2028/2210 +f 311/125 1979/127 2025/451 +f 2025/451 1979/127 2026/453 +f 859/1052 5/411 868/1061 +f 868/1061 5/411 85/414 +f 859/1052 846/1040 5/411 +f 5/411 846/1040 1/409 +f 722/908 1332/907 1980/912 +f 1980/912 1332/907 1981/911 +f 4/400 88/401 775/2211 +f 775/2211 88/401 1982/968 +f 736/2212 1983/926 723/910 +f 723/910 1983/926 1980/912 +f 749/2213 1984/940 736/2212 +f 736/2212 1984/940 1983/926 +f 762/2214 1985/954 749/2213 +f 749/2213 1985/954 1984/940 +f 775/2211 1982/968 762/2214 +f 762/2214 1982/968 1985/954 +f 811/1004 799/992 2/406 +f 2/406 799/992 87/405 +f 2/406 86/408 811/1004 +f 811/1004 86/408 823/1016 +f 823/1016 86/408 835/1028 +f 835/1028 86/408 1/409 +f 846/1040 835/1028 1/409 +f 89/416 321/1097 2016/2215 +f 2016/2215 85/414 89/416 +f 1515/1772 1316/1570 1905/1771 +f 1905/1771 1316/1570 1986/1095 +f 1338/1093 1986/1095 898/1094 +f 898/1094 1986/1095 1316/1570 +f 244/12 1988/1 91/14 +f 91/14 1988/1 1989/2216 +f 92/15 1990/2 244/12 +f 244/12 1990/2 1988/1 +f 245/18 1991/4 92/15 +f 92/15 1991/4 1990/2 +f 300/19 1992/5 245/18 +f 245/18 1992/5 1991/4 +f 165/203 164/202 1994/2217 +f 1994/2217 164/202 1993/2218 +f 267/205 166/204 1996/2219 +f 1996/2219 166/204 1995/2220 +f 164/202 267/205 1993/2218 +f 1993/2218 267/205 1996/2219 +f 91/14 1989/2216 289/208 +f 289/208 1989/2216 1997/2221 +f 168/210 165/203 1998/7 +f 1998/7 165/203 1994/2217 +f 289/208 1997/2221 325/374 +f 325/374 1997/2221 1999/2222 +f 316/392 168/210 2000/6 +f 2000/6 168/210 1998/7 +f 325/374 1999/2222 166/204 +f 166/204 1999/2222 1995/2220 +f 1992/5 300/19 2019/8 +f 2019/8 300/19 2018/418 +f 316/392 2000/6 691/476 +f 691/476 2000/6 2002/2223 +f 623/485 438/486 2004/2224 +f 2004/2224 438/486 2003/2225 +f 439/488 623/485 2005/2226 +f 2005/2226 623/485 2004/2224 +f 624/490 439/488 2006/2227 +f 2006/2227 439/488 2005/2226 +f 624/490 2006/2227 441/492 +f 441/492 2006/2227 2007/10 +f 521/674 2008/2228 520/675 +f 520/675 2008/2228 2009/2229 +f 646/678 2010/2230 522/676 +f 522/676 2010/2230 2011/2231 +f 520/675 2009/2229 646/678 +f 646/678 2009/2229 2010/2230 +f 438/486 662/681 2003/2225 +f 2003/2225 662/681 2012/2232 +f 524/682 2013/2233 521/674 +f 521/674 2013/2233 2008/2228 +f 662/681 1949/846 2012/2232 +f 2012/2232 1949/846 2014/2234 +f 441/492 2007/10 673/848 +f 673/848 2007/10 2001/9 +f 691/476 2002/2223 524/682 +f 524/682 2002/2223 2013/2233 +f 1949/846 522/676 2014/2234 +f 2014/2234 522/676 2011/2231 +f 1989/2216 1988/1 2015/3 +f 1994/2217 1993/2218 2015/3 +f 1996/2219 1995/2220 2015/3 +f 1993/2218 1996/2219 2015/3 +f 1997/2221 1989/2216 2015/3 +f 1998/7 1994/2217 2015/3 +f 1999/2222 1997/2221 2015/3 +f 1995/2220 1999/2222 2015/3 +f 2002/2223 2000/6 2015/3 +f 2004/2224 2003/2225 2015/3 +f 2005/2226 2004/2224 2015/3 +f 2006/2227 2005/2226 2015/3 +f 2007/10 2006/2227 2015/3 +f 2009/2229 2008/2228 2015/3 +f 2011/2231 2010/2230 2015/3 +f 2010/2230 2009/2229 2015/3 +f 2003/2225 2012/2232 2015/3 +f 2008/2228 2013/2233 2015/3 +f 2012/2232 2014/2234 2015/3 +f 2013/2233 2002/2223 2015/3 +f 2014/2234 2011/2231 2015/3 +f 321/1097 885/1078 2016/2215 +f 2016/2215 885/1078 878/1071 +f 868/1061 85/414 878/1071 +f 878/1071 85/414 2016/2215 +f 1498/1752 1504/1758 1904/1773 +f 2001/9 2019/8 673/848 +f 673/848 2019/8 2018/418 +f 229/100 2021/2235 309/98 +f 309/98 2021/2235 2020/2236 +f 674/849 591/847 2022/420 +f 2022/420 591/847 2023/419 +f 591/847 673/848 2023/419 +f 2023/419 673/848 2018/418 +f 592/850 674/849 2024/421 +f 2024/421 674/849 2022/420 +f 592/850 2024/421 684/855 +f 684/855 2024/421 2025/451 +f 684/855 2025/451 605/452 +f 605/452 2025/451 2026/453 +f 1979/127 143/141 2026/453 +f 2026/453 143/141 2027/455 +f 143/141 230/143 2027/455 +f 2027/455 230/143 2028/2210 +f 607/857 2028/2237 688/858 +f 688/858 2028/2237 2029/2238 +f 688/858 2029/2238 608/859 +f 608/859 2029/2238 2030/2239 +f 608/859 2030/2239 689/860 +f 689/860 2030/2239 2031/2240 +f 689/860 2031/2240 604/853 +f 604/853 2031/2240 2032/2241 +f 683/854 604/853 2033/2242 +f 2033/2242 604/853 2032/2241 +f 602/851 683/854 2034/2243 +f 2034/2243 683/854 2033/2242 +f 602/851 2034/2243 682/852 +f 682/852 2034/2243 2020/2244 +f 2021/2235 229/100 2017/2245 +f 2017/2245 229/100 313/174 +f 42/160 2035/2246 313/174 +f 313/174 2035/2246 2017/2245 +f 312/159 2036/2247 42/160 +f 42/160 2036/2247 2035/2246 +f 312/152 30/88 2036/460 +f 2036/460 30/88 2037/437 +f 308/86 2038/435 30/88 +f 30/88 2038/435 2037/437 +f 228/89 2039/439 308/86 +f 308/86 2039/439 2038/435 +f 228/89 46/92 2039/439 +f 2039/439 46/92 2040/441 +f 46/92 163/394 2040/441 +f 2040/441 163/394 2041/472 +f 163/394 80/396 2041/472 +f 2041/472 80/396 2042/474 +f 234/399 2043/477 80/396 +f 80/396 2043/477 2042/474 +f 4/400 2044/479 234/399 +f 234/399 2044/479 2043/477 +f 775/2211 2045/1670 4/400 +f 4/400 2045/1670 2044/479 +f 762/2214 2046/2209 775/2211 +f 775/2211 2046/2209 2045/1670 +f 762/2214 749/2213 2046/2209 +f 2046/2209 749/2213 2047/2208 +f 749/2213 736/2212 2047/2208 +f 2047/2208 736/2212 2048/2207 +f 736/2212 723/910 2048/2207 +f 2048/2207 723/910 2049/2206 +f 723/910 709/909 2049/2206 +f 2049/2206 709/909 2050/2205 +f 908/1105 2050/2205 709/909 +f 1974/1606 2050/2205 908/1105 +f 908/1105 1981/911 1332/907 +f 2052/329 2066/2248 2051/331 +f 2051/331 2066/2248 2065/2249 +f 323/335 2068/2250 283/333 +f 283/333 2068/2250 2067/2251 +f 283/333 2067/2251 2052/329 +f 2052/329 2067/2251 2066/2248 +f 186/279 279/276 2069/2252 +f 2069/2252 279/276 2070/2253 +f 185/277 280/285 2071/2254 +f 2071/2254 280/285 2072/2255 +f 279/276 185/277 2070/2253 +f 2070/2253 185/277 2071/2254 +f 2053/300 186/279 2073/2256 +f 2073/2256 186/279 2069/2252 +f 2051/331 2065/2249 187/286 +f 187/286 2065/2249 2074/2257 +f 280/285 187/286 2072/2255 +f 2072/2255 187/286 2074/2257 +f 281/294 2076/287 188/296 +f 188/296 2076/287 2075/297 +f 2055/368 2078/2258 2054/290 +f 2054/290 2078/2258 2077/2259 +f 2054/290 2077/2259 281/292 +f 281/292 2077/2259 2076/2260 +f 188/296 2075/297 2056/303 +f 2056/303 2075/297 2079/301 +f 2055/368 2053/300 2078/2258 +f 2078/2258 2053/300 2073/2256 +f 2056/303 2079/301 189/305 +f 189/305 2079/301 2080/306 +f 189/305 2080/306 2057/309 +f 2057/309 2080/306 2081/307 +f 190/311 2057/309 2082/312 +f 2082/312 2057/309 2081/307 +f 2058/326 190/311 2083/324 +f 2083/324 190/311 2082/312 +f 192/321 282/322 2084/2261 +f 2084/2261 282/322 2085/2262 +f 191/318 2058/326 2086/314 +f 2086/314 2058/326 2083/324 +f 282/317 191/318 2085/313 +f 2085/313 191/318 2086/314 +f 323/335 192/321 2068/2250 +f 2068/2250 192/321 2084/2261 +f 1947/807 657/805 2087/2263 +f 2087/2263 657/805 2088/2264 +f 2059/802 2060/803 2089/2265 +f 2089/2265 2060/803 2090/2266 +f 657/805 2059/802 2088/2264 +f 2088/2264 2059/802 2089/2265 +f 555/749 2092/2267 654/756 +f 654/756 2092/2267 2091/2268 +f 556/751 2094/2269 653/747 +f 653/747 2094/2269 2093/2270 +f 653/747 2093/2270 555/749 +f 555/749 2093/2270 2092/2267 +f 2061/772 2095/2271 556/751 +f 556/751 2095/2271 2094/2269 +f 2060/803 557/758 2090/2266 +f 2090/2266 557/758 2096/2272 +f 654/756 2091/2268 557/758 +f 557/758 2091/2268 2096/2272 +f 1954/839 558/763 2097/2273 +f 2097/2273 558/763 2098/2274 +f 655/767 559/768 2099/759 +f 2099/759 559/768 2100/769 +f 558/763 655/764 2098/2274 +f 2098/2274 655/764 2099/2275 +f 559/768 2062/775 2100/769 +f 2100/769 2062/775 2101/773 +f 1954/839 2097/2273 2061/772 +f 2061/772 2097/2273 2095/2271 +f 2062/775 560/777 2101/773 +f 2101/773 560/777 2102/778 +f 560/777 2063/781 2102/778 +f 2102/778 2063/781 2103/779 +f 561/783 2104/784 2063/781 +f 2063/781 2104/784 2103/779 +f 2064/798 2105/796 561/783 +f 561/783 2105/796 2104/784 +f 562/790 2106/785 2064/798 +f 2064/798 2106/785 2105/796 +f 563/792 2108/2276 656/794 +f 656/794 2108/2276 2107/2277 +f 656/788 2107/786 562/790 +f 562/790 2107/786 2106/785 +f 1947/807 2087/2263 563/792 +f 563/792 2087/2263 2108/2276 +f 1687/2054 1688/1983 1808/1982 +f 1118/1312 1238/1311 1152/1314 diff --git a/data/duckCM.png b/data/duckCM.png new file mode 100644 index 0000000000000000000000000000000000000000..62d9200baee32f84118cfd55e04e4a4dda9cc699 GIT binary patch literal 32504 zcmYg%1y~f(yZ7$WAdPfMOLs}9bV+xE(j~o1NjFG02uLX1UD7Ef-Ju}e>>dC2e&4-3 z@SK^QWp_@@`@X+8k?N}QnCPVFAP@*s;jN4&2m}ERAt2O$jUTCW70`g)HRYv1)f420 zzzLemTYYyB2m}A$9~hLGO$@w<;-R1-i?R&CLZidaJ7iV?fv7!&~~nO$ZDjEImK?`2X#jCbrN5A8N5x(O#uoSgie(OAsgo>B~t%{hJMx^m34 z0WV)g09?^JL*I;RYlW_8=re4@flS=r4`TqHfOePLIkdsgUtEfHqeN^Jfuy?jktVQ%-Bs9GH2&K{x`Fm zwEL*%eYYm4yKVzvqhmw#hv%z>$lsDXSk!eW8GnBKmfNGXYNY*zEVhB#{qMt*=uGh6 z$I1RVD|>_1z@zT>Vn)7)49y0u!5g^+%SucJj&iUb+$o+9H-6C`Zw2#wizc7ISnm-LN+2b|H@6y@|I;zmEv6FE(B6cvmqZ^|Qa6Z^;r zq048=)ybj{e7*wzJ$m?=#f*@7 zZ>!)?B<7i$bdtp6X7z~%>zSoB7sV|O^l!2si|xS%&xEuSl$|UtxAgVvk<~wk89@se zKY&~RY}LP%ehy)P`|B0KH-wd+g}$^WJbJqP>say+&5w8tPcO6rg9gPvMxTk#-0r8J zV<_K~9H}b)<`X7Tcj$c@b+A_grjntY%kBpUSiNr40JJ>Nwp#{BONkcxs)a&62O6DHA~ zh~`Lgweatl-4jtA&BavG3+N+#!ljk6KTdMsXC}VJ^O#Zv@xFeP0UXX7jxLfq6XSHdxMa3J;22S)=2$Ha6;1eV! zhSQ{k9EXriT-a-;{3uYrw)K8WiTy=Yli5~R57{47iH)TVdU1Pw))%@@$HlBkxuLW= zW^>w3^dk&K)xEeZ3AsC{`Sl|XGwlkJ=k8l{I^{y7@|7~%rp>8Lb-d3=aeDf}@7olq zL6nZBxcQ8PCu}n?ODskY;C{7o<@;Vc(c`_sEHxXsaFyS{R|ARk#u<&El+@F)eu7eb zc@rspB=%_-9=Dv~M4u!x%)@oJu5nXit^gWDcV0R({=iYkvalN?BRXsK#ToO%r8rtL zG;aA2c<&H+mg{c+EH*`f4xHWtW~9=`u#I(%YEBD%!HhKTSk+<~Tw9(oS!k1K7#XTK z&Z`{A&#+w?bp-NIp#ReEANl^(}oqSffjm%y!?B*Ub0?GJpCSuV>$3t)7J1t(+*Z zIEWtg(lMh>K=jK&`eoaX!#)qyD-MnC$I9U7B+_^VmL!pHD+)DpIiU(H38bd6h}}hw zMINa#AGh4kINwG-n8dZW8u%!~r8vI)9nFq1Ajf={?X8mDOpaOFmRbg^77F~dz7Jp7 zArXahT2hrZgdn?o=TCEz*6Y~K1RW+VNnV&aGchk0J)}VK(XAx?6Gc4FnGnonb_?grf;fdG=Q$r+bexjsWMU|h-g1@9 z0EUv~qwxV0yTe(h83_mF9_6POyN+KA`i)SJ%wH`Cz9rSbiaA7?==GJ`t>tGz(+}UM zq3Y?wt`e0=xe<}rG<*|PGO2IhlQW1#P~=J5d_=;7Rqux<7nl+hkB9XHB%TI`3n>VW zH)-uynro_|>Mo%d9F*6;lu`(my-p@rCDr9J;wWm3no#6=Jk6!4**++fDXNQ@U) z3RdaC)3`#^tyfy9+3wB$f2!JPWFs;cVSH)lILJ0~YfAvY@}C8ZA^ zj&bG5xvWQ06p|TKaz%WO=E}#%$8-0!uV=htiQ%*kaMoJC~HNvdECn2ICvjHC3tQts?=X_9zwto@DoQ=zQ z(x^u^G8A?D=HrR~e$GdJ=e2hG*)NX28vp&ScLo4II&r4Dx&lrs&AJEh>(~h>C2)$`ruQF_M z+8&I}5^#2McIH@GGBhx-naCFMyZ$|^CjNw%XcAvcG z%hIf@ES8N(+s19RQp*jsmr9_XJa_4a=ipzAWR0TOzjSMZ@U}Ac{&4;*(CgOPDR+sA zG;d{Ms^TRQq1dsuYQhfdYX_0Q9GYc%ZZ?%c$vS2`)(V~7adC0FrU9N;M?X0_E@wWo z8RGfn@YswAKK!mDV%2k5wl9q(7vi4A?%3A&ZIHwX$*+LRj`(RD*A!9?6db~FLj7A* z`!v3wooHm^vEuNkG{s9qoNq}-j+!#{z_BKlFi~gQ#P%`7fQR`o$0s}0R?@;~6k>-G zkJbGgW~x5`tE)oyn@Nqu0L!=bq+u&&56{`d%M9Dx7OUYTBn-kBvb6S*qvxBm(%IJ} zlJu`YZ2M|GeOf6CRGI4}nYWYLx%4etllI;HSFYh}Oz)B;`w=MV7}&YZG@bHUBk1U= zU-EQMDgC4Y?V(DT4z5Rj{>E?=)#$Kv{F03169lunU0?OtRQ;%yp^|Bju=AK?93lo-$df;jnRXu8GVvfU$ z+Ah(F^8_hG5s!+&p44SW(dJL7sw^<4sYKSAH|uW?m4L?wUKW++1q!3-i@GXlY0b(K z8DExg1iXID>*CGkHHJdPL?etqP{wR9>gBA#4eedsp_0Q4ucuwZres;SZ z&jJ7W`~HK+BdZT0&T=>78{W0(?+5Hi_n1+!$x~KxBwDFVx9a)`&woTIAR9W#+$fm1 zCl?S&(hL{Ge3o)T@;lP^qlfWpP0s7_(8q1R(~gg)YaPVZ+<}j`$KSf1qfH+zGNk*T z4F8sDB|4_zNE{t66n0iCALNZ5Kgb3`QEfK&dL>LN8kJa}d8z37>C`ghxKGk(u!`){ zX=fc(*+MII#cchiQ19k)_^#94-9b_O^Z5GXxR#pe&(+mfbJo?>)qmZ|@O|EYH$Q8m|SZ8Z<$_$&cq5pxCap7wbo=q&7I`6RC2Jqj(8 z(0QfL9de9>L1Ek8t_l1o;qqO~vtolYlbm;T@rimPj*^|DAXIGBVxH$N7U zws=-Tosh^kKBk>vq*6ytNyz$3j(iS*!l-USt~#=SG!&lmaZLya=BX^Yt>HK+Kr@jg za60kk^!Mp0N4Nnen2o!EI+qwkR0P%7vUzo#g<1SacJOc(#?znn1U*`nM?B71|E zX(zzOe*-J1g3-e?_XUb*{H9F7vMY{SbmoIQ1cfsP-4MN>M=D%H=+eEk-B1%Q&(5}s z_G@_=h^t;#KXp%+8%Ej0jlKRXdje-1hK-d#GA?GMY$0eEN(}{3>%c?)h&(BZB0W_o zrxPf*pqsQfFGk}9r&fJ!@PDoR`>d2aYKdAy9uJAc)A99Bcr2N~M4mXj^WnrD!fqes^AsG0Ue9Ck#D za^iBM_2u>`#V!+aqcn6=emkg%ob>~rv+li(8NRKt6DzQm4h%QGTFQ6h<1!Yh*`Tc^ z(5?C<(eB-kY10L)jhQ_hTw|(v*Qm(IS=dnokNJ*JPR!@8*hS^$st$pRy05eg-xvLI zYWow#QoY#W*JOBgQ0sSd!cyJ5)%eSaB^8$wtEie;2z+~SQzsoA7~({Q>K0hY@;n^+ z>dX6xZ*#}e@;xp&oBT#bq=R-Q&j()VJr{0w!1QZ30XUIfP)%2N0aIY*@3?bdok&ZJ zHZxG`tUvAvI5%&8!n;x=^`zIVI>UjXdbpXlibIsCC84_tKG}Mc+rJs;6#m?x2ELMG za-`+I+S!@0u*|k*rX<}{cVbY9R(DrkVO#|TIT4r|hS8|&k0n%JtXsdn<(BqQ zkgfg^*2`;K7x5^?I;KKLBd&TdQLK$+qw$0y)G9$@Bjg|R=~La5i(;ki?KTy>*=4iW z6n?*eRkZ#2UyDJ+{s_EGeDTH|opgd34NNe8G4ko^DpYWBwCdiD0H`z;(qn) z`=z?C1HaIHgHsxm5P?m*HKvtjo@@=M&+hi~y3G5K!zW4-G0FI|q0e_*YRZLuEA>zigh)AOz%FJ@bl-Hxon@wa{_C(zz6j3bK-T<>3X9ZugL+c{O|5_3$b#%bGO&qYKV7MV zONg3t|LW&GeZa2Nn|^(N`kj7c*OyZMpNvbC`bww&+PacAx3&2#)mj5FzgBwld}NfP zs~yP6&3i|@k0hjG6s1Vi&cDfJ*sEIDU1wn@i?3VuOHt2LI{tVswj-*ZB=z^PhnQ5$ zI&hCDm)7(29*o>ls$AVd2?uuCpOzOo_qfIIX@G@v4;&Lt)^tcoO4KhzWPOFVe5bp# zDipU?iX=mk_B*vRW@dCAbfmj-Kcu$6xrZo-7^7yLjq+w;=-1CM>$c1B<`4}9Id?r> z=RNK)V)-P~ir)>ZIRWS19uXHCd^CijH2XY|s00DVzmL>=!6r1#>3n=z^HXx)7HQT9 zh&(<`1#48P;!(@AWWb~ZRmyFH%Z9(F-nW#A_S|da`JWF1lm64(X=VqQ^nmkW#y?a| z*NAhIvPKu6DAEihlpZ2;m85CYV;HV<1F~(Y$4-+IODdhUYz3j$`Ghzzw^nkjeO{wYd?sdCb zO0Q)#i4Iqzfz*Ob+_Rg1n%_*eH|^AI*Kf+&s;WPkAF7w?Y~u}AtwwT1{i<8`NFR8t zNZ~{max&B)b_SR_wwfyWQKkCkjN)Ftme|N&e2d;rDq$bQt<9~TGdz~i=|}#;_IxH4 z?oW|(<25JnyZeQ%lO!>Gb~1j4Tj1E~F28}Xm~f1IG0*Zp zgen;!tm|S@BL5INctcjE2vs6MnEJzs-@(6_uDb)}W2$81E!(I^en}voG4qr5oFF6T zW4aK`Wk;BItiiK+JWBw2U6(E&h0inxp z)WCR7stBxFxiZA-Lfc=1?e6e{BPL5;g@$&Ez8k5u@>^xcINu4VsxdOKHXRJVvg{~D zPvw!+haDg1(_=!BokOcaR^2B>IokgQ8_ta+5t(>z=K7t~93a;2Lzunq>LhIJxLo8! z?zx;+#eLUGb@Y;Or;J9OV1WUwwCfs*D=t!NNfG6$RSq|UK&6?j?2Aa7w&?bp?X#D0 zjj!}oV-N@$0Ls#@v&jLLrq&nb3{GZavxBi@ zH>c}V$5-`@jh*KsEIQl`-SB-Jx&!Qwk3*+Ut=w1R)7O4y@D73N+OG{2t0nb6c%P37ZNf zGkEQ1(z+gZE53F5KHi?`U^N4EHwvZqhAI5Y^y8mp>l;nmvGlX$58oQ<>t*ObMbzJQ zoU;o%yt8Rd3m?Mjr^H+LOYM;Ar`CUe&frDT;^vZD>S4Qt8Fi5^1qGJzW;^Y2eq0Y| zl7_3_0lkCIC~*XAldXRbdj<{0Q^8}M{kMUB)H=C2F4gXyw4FABUN8{C%8)!bk=0#L zc8dd8l8c^M{FV%d0$P8Cx`AWz$>N-(fb8qGr*`s;L*B zxbVGU`wJVir}1_!NeE>OHcIlF)FLM7Q>9#@D{-BL-ZnzxH7IObbumI%IOt#ZM25G)BgU2?m z3PB8jU1S1ItU_SBpf5}_S@oR~1_ym?6tgstC=vc#?+uq}>~mDa5?t}pr7)}$&yql| zOUo6{1!LgHiS6m2%WZdrsay=1+i|%bfCI~0uo*vfN669*GMBbriCJk8L`zWPS~lY{H9xv}T)}~bo@ zbH9_UCU>^_unSy|vOw&&2lrEK$MzXQw8^;Hu#?=3-kfcmi)ErdEEUDh_fEBb-$-U2 z6(JcymnJMA>rD|{>uaG_oQU%^_0`Xi|46g%!gziP`ZR_9sr-UlkyO2eiJwyk_4_i;YU!5D>Q z?N}0?Js{;r24;I6&pR%DFHj}~A(~H`C2Ck)K)0rX$K9A(-q{KeE69aDr)OSBQXS9#CO!H~Rphh}1ljFzk3?OGpL!*ir zf`4ZGxjzD;>%&Q#6B>&3W^b4_)aMj91zscN0jrB$O-*a8%xt8be}%X22o89#BHJ7e z;<~KMwgifKP?zCjVwh#0VeHSrgJUS(T+eB#shq#e^*t(|?vOynUX4D>C0-&*QU0k$ zIr61xh-+$I_JZ_q6RcPxqp09H5xcY3^9;}yrk^30!e_Ykn z=(K7e24TA2NLGKe@7Rl`KeVuD(D-cI3^7+F zd(tyRj|ODTO;Z!1qTJkF;yr#@_s*wYThoHK&egw)O+WPOM)Q+%UnZztGRU3MRlPY` zF%2LV@U%cNsIUkES@$mz6>QvPx={)*ceiG17F&Cl-fMkrh}9q95Gauoz|q^*m_I(L zu^fu~xaKnid@mN2m`El2Ilu`m)mX}-v4L=D>I&YPcTiL7tj=0+)34_Bc^V|Ron>aR zWiGjIX1+SZvjyq$gXc*69|6_EY4=eR7h#&!#LCa_jRxA^7vkQQSK0cGhy;1!K}%Z0 z`t`<&I+Dh=dCm->g@V@o!)(0~_WSmS^Dn$@0SYJBH-=7WI@%bV=+4JZ?z1q`s znhaVyBi6ZT0WQ8Yc|Re3yMfT;AX^JmNSj`XRQ`X#^jPGEq)cR_paioLl4pj3Z?Nh* zLk#j3j`bA$%~?0Ay4Y$hI03=UMas{x)*#Cn;29=!MgMgW&?W%cMEU$Bkfvvy&QAc0 z(&)TyB835xJ}W_`)q6pSww>~_6`jgDi>Y$3Bxmo114Btu)CZHSgQNp|mK+L+q!{+N z%bS}vZI+~`hnueJGYiZ=V`e-K zIo1{8EUF`ZlpE$P(KpvHAYvp2_hd5>y-;sw?l8#GmFcw9w*u}qUhLkE``e#sd8&qE zctO_-lXuklkCB)ZPS!OfbX=JuydgM*zt+P~%EpQsN=j$#S+}GY3R#>+n)}qVUzawS&xMUDr;-q@nRqq$X;|bKdgEvucg&J+X$QQ32oY`)J zt=uZY)|@9uz}%37`Q>&RU&<7vf65h5>=hgTJl`E{a{NU&l=zxM`URoH)M~thar#Tz zkkqe#Q$HNF4?6NmtAL#z;+ji_#@RYJCvfR#VX9T**c8G$^U5>;VT*m(r>RH{s8COv z*ihox9^i8vs;JvJW6Xlmzw3wCtxjatY?-0OE0P&5 zIHcNLRgS6^flenZ(}{^LoQKI$(wIq)j-;b@ZEek@$wsl|ZZFFe`gC<}(g)D#ffwTf zL%!%Eqc~YeoZb!`zcFkf9Fk}_$%z4q+1)-OdtALn?jJ3Tgijb|jPRdwkf;+YP0Sa% zyJ3brCG`yrY{sqiF)GPow;LfPN@&b`^ni{zd+ft^ zzf_nbyRcG9us(4`bHgZ5?C@bGzTOPUt{>zQ0g=zdZJ6G=JnL6yzyp1nk!G+S@2>S< z#mlGgYxD*~;z}ihl^Hg7ztLTJ=$CRyMs#J2#zbj&8P%Cs6ED~BCrmOzHUk1J^e=D! z&Ai=oAy8V-S>bLCLo7P`SjfPUg*|7lx6#5WDSvH$52SHYxcNv^5Ayop8S=Jt)@i(6 z(Cqo~?s(xF$N1s=H&cLCeM~A4-?fhDK?>!xUZ=vw5rQ6wayT|{^J}dYeVpE$xoz>B z+OJ8(Oe=(1>$9OXfV^lcHeXklm%D!WLJyFVYr#kuit5z=79pTwwco7y(YNsdV>94~ z#@Y=|v9~}rXzz&|>$^OXg_%U3*;J#(q-vt!EUc4x{Ytc-l~qQoJ3m;kA5E+f?JNNF{0a7;C;3 zL42b+*4B0^D>5%b29R`m*)lRjt`{wB^8&8_G_5<;aDM>8bJKd@0|0lQ7A@mbpW~P~ zLNMcB;WO2(5cOG!y#Yx-fG$7`gr3QJkd=C+mm-x5u{#1W{^V#*%gjEr^q-PyDx<+N zX=7A}zxet1T99}?8@G9_0%{k4QK$YdczyrB;PstiUVP7JLfqLXc2zFL4vYqI-~3H& zzp`K?wpBD?K?Fn}an9z;7(tF8h2dgq=lG?gd@SiMA#35kVH8;RzxW?nLpGXlJm<|v zpa$p<_z188flv1Url|&Q00_^!9k?T-360l>Q8Az$+&oN*x1)Wq+_1!;D{!Mm3~VD$ zuU-zfVEd)dM%PGIl-r=@SNNDuEU&eMWz6uwbkS}pA#^E4iM&Rx9ybc>PK zN+jVmxsQt8Yh-O6x)kA69c&{^c4+X5egk&vrWQ^Cc95Ng` z@c6gfZ#X5`bPC_FWWVcBH$nD8%y($lLOd3!648)GB_HaC_R=PsUVlo*kRYA zZg|7uz;=h;=LBoFNNL@$9}{pN9_Wk${}$1LGlnl1EOk~G@q!LnlHdosW|>eBSxQf2 zgk#vj4RM$OUg3{6Vp!0n6P?q)@^g9Fpvg@2{f7^XfRn%pB4I}EAEkczAq=4qWR1%% z&zc&AIxAxEAawV0nGcEjs<>v6@cU5-6vG>P`Y4anJlK$v5tt387L2z$n5erTpJ;VO zGin{)>5<)dw;2ecp@1vh3ZXBU@G791MD-&gby`r>0i)+@Vj;I3t|9Vm!m(kX;c?sf zfv5+P^#b8D!X-BDZRy7_3PFSmuU@cIexem(rOo5Q%A!T*^^*(tJox5qLUP%mV(@N| z94tse2O3NtIOVJL)dRVzO;MCZASkXYr;8Ddb=!TfhLhSY6|y-B1&bUGaOQLgzfQ{a z?8g*;K8tVr=_cimX+aIrfRc4>ouVO!zf!)D*Lx=+@F6)#ezHWgtSB$6qB81$L~Nx0 z)$vcS^K!Av{@LsYo06FaKWe3=zoGc5E)~0>x1{~+nDwYcBI^#IFXtCOFpw-}$UT!e zs>&`D8NQz&F$+t1(89_OBm+r=hQOMKP_m~}{I18(p9!1H`t%J93`P_|IQXD**Nqf{ z%Lc)GE4dfOY4Mg3mgM9(uEI7hmt-`0WN`>q(eA_@&YJw$+}2S%knDT4N;D1y9KC+9 ziJ4ydj~eQISVX5EdZqNfn$d5(NN-Hnx6^ZAgoSojBtCcYeA@7ZB*N4h0C%+gB%R>) zyLM1SBsQzT1!;j1FIqxG&VG9%(hPdph6jDNo>UG24+f#L6B893hra3+sH^cH;MU4S z64GU5NXUgtop4(B@l1-$R?WK*9$|Fzlqe!#{=wKmvKhD??#7UY_K$&k{8gN!4Vzwm z)43+n#6w`rHfXAqd=``n7iC(ZIKjtCer7xPCA0MRpDdI0g2$nxygV`;l5!l-6uK(0 z>rNv((&O-wDY1Bck1A@wv2DVIuUeeS4GIUnP;u z^JqQvjkS{M%skOp8;eZ7Ij*c*d5@_k1hgXYkl!M+XVG|s!ICRzabaQL;3Y{s%U*UA z0?r5|P}-1-KZ9!F{*0*`sW02r0H3)_K5LXI$90B398HNqH{X*!2hI+8308xoH zP=nA6Q^*O}^Zo=FsW{PVJz&MC0PUiX``%RLu<7~AX3`v(;OQHq%BguUi8-E%5HWe2 zSiE{aSXn+YuGuN+vyUq+ZK)*Z?42oEsW2p8Y2p2d4q#%s*c*4R;9Ic*Ofsgz-Nz$q@PW!2elirI2 zsi;E?Lnq|2{p%0v7C(I>DkC$V)Z!QUQ$HIg{_G9(P#$P+XI00woqIq345fd!4OAfw4kYao7dMy_!A@m55@yw^$XI`JhuXj z5jnV=D|66HIAeHqDw1e>rRD~x>r<9y!_(^#Qf|9V5_++*@T2z2&JiP9ePT8S2W!n> zc3R)WeK9w(1%8K;{icIEj*(A9#vU5NIbIn2^*8%nQy0IiaJuse1oQuL zP?&{nh^raUIpiSF#S!_=S}I=0m1dZbxOxS%6Kc_Pj3g$rOL8SyP2Uk%w*ga^9e95T ztk{r$?c8JDL@9dJKu57rCs7&N+q-8U3vLNL8w~=igm0R4XZY(mD1She?qN)O*M?fx9t)RqjEaiRqD-&MMO53 zed(q3T}g@<9gL&?B7P(>(-e2;Z68stWh3-<0*N5}#Ut)2Typ8}0`KmoN|pi1R>u_Ta}{U=<hYEACs+zuj&(!bfl&x?m6Z&Ji zCf(j7SMtZJ6Nl-|*WrP3K79s;hH0$&TRIi3*278kfT&LwDtuW-L-B@W>U8M-UAnC} z{rs6M7f1nz?+a5+k8Aqv7bcW~H|E~3&rCPO%XxjbzTRUMqLsz_)|c(7OMgdLzLAUi z)*F6*pE_M6Z|bv~TK{p+^Riu;7NpP@A}F>nRg)-+=@;?Z{lF5Ei>*c($JDVYDLd_W#uWYcRH0nnJTK7dPXLn=F+KL)z;?P$7a--&VpWw`_2)BT` za1F@_HDs4j{}FU_fCxjTQl+tJ+Tv?B0WZVBV5oKmFCX8k_jW9=vAJnk-;~mpW2CSM zkq}hV9F4hv+-$*C<@Y4($EU2ExQCUq`2rpLFEXZbHkS;7iEnNEG)%(#QJWvnM}h7p z8wK9&T3x|ye>g1A!Q4o|cw%P3T)l;Y;4lQH<%*hp#&^-BWK>D?7Qizr8XJDh8V4Wr zd9lq*0Z!=r^N`?J>fjA%Wz$JWIqzVArnKfUtHOKLMG=02@f1A*VOk&}UjC)OLxJuF zW31-TQr*SGk|T13m3@UBdn-ju4I?lMF;aye=C{Cqax!IZe=$ds2Mx}^DTOJ&I0y#Z zU;eA?oO-@WcRucN;M>9rx6LoRh@QhJ{1J*W?U`MMJLNPK95q6o3mI~Rs0A*eb)9VP z7N$ZPJgI4iBOk1ze+)0;S=0xc%a#4Im@&Mt!=kcKPT%)=cl@8Qsz?} zJH5x|PoH%&Kh}u~(;>}zi5z}3?a|d$wx?ggD%7e-IYwraXbNJ)qurQ7NWldBm`)v6 zI!$J&>`iOFEND+)a$kw!=MYjvoBB1YMmvd=Aa-4EQ7Z43==~x^gf=vynfQ&^D*U^d zVpNDL>d5x|=lExW)21>ASX0}{z@~^L=_MC{O(!yP2{QaXl8V9JYzCYbtMK?u1vc%R z?yyp~ok<{wB`nY*A%J8nH!vHp)1z#5swoSz-uBa6Mz+Q9Clb%_%lDrU$sSCiMdJ^o zyB~EK4?7I?v?;^~*PI64w6T*K70fIz->;qqrg1IryZ$|y+Mx=7z%v7Nns%@f@$7#CZxJ4;~Lqii&pr? z!2D*U3K;fFs~6!;t;8J=C5Wj zhVlT4q`Ud|c=gN#(*EKJ8o4QL&#ZBcqz^vCm*KT>W#ar=q7-u8{?m6^7P8g>4HT*e zGj)_GY4}YYM_()cSp%AEhSNR(2f$gOQ-F(*?|gEnhUY82vKN6F{mx>F7z))hX}Ej4 zuzbBrb%M_n1`Nn<8G1|`BORj1NGc=2+|%qPu#24+Bk!&;FZ5q}SVnA}&+s7{YY-c2 zZoBRX&n5ZxH#Icm0CNDi2V6!!>jApl1O`XDgv!bhlnN?LvD7hwX&+{WHyV__GclZt zi9o@_i%9^XQZ;X0%%{LHpq8VmWeT>`u!s+mtJId3K$~XK(v_7%UU+@`{f$fPK4x%b z9kN~0YV^|_UEcisJfK>wBO&0i01&4c{`|l<<8`wxB?(7m&)=vvIx5!b4Y<7J?C*d{ zu^@|>8rrL^NMQwqz~-s73a9ZuZU%`F(x6_Ox=hplDF4KJpcD0aNMVq)o$WS^9FwW! zLcv2sam5Re8Ubzgql!wbVPmm^ipn@(SOcIg>k$@3eBp%xrs3H?ljX#_hrKNtTI)Yf z1W&kM=P2%)Ut05l-UWea)`rPuNJ8@AC>$<1vhRBAM2e5c&S+c=JxF0n_%v_Vk$FuK zoe8Xe#RvShJoNAdJk?H1c=YRc6CVNTQTS*+RI#_nN6X63mK zk^sH{*B;o;4}dng={OBgvkVtqDB9P?#4Hfsl1P`dOZE=jQGG>u_n=FPm~=T+h#B=B zNrY>V%e5NULqcB$S=r#{SU+;qO6tz(=cA99)(Ft6Av|Jp?NOSQyI>})prHtrE#%#Y zb@+2{7{+QXy(A&K(e2YI)YLLu8ascRYqd@$7xnXqJM2f@Sjm(7Z#NifmDI(02%&_= zjXDRX<6?3zRYzAsbrQ>0^7*JHX&hZtZr(GWR0@9z6m_fh@q-8Ft;X2c*wHi&@qb{T zHdS{*__z`<4-4?~C#BxO2RpMcRjn-Ptb7mV2d}v!xpz; zpv0&2+4<*r(D?Yn)ABU!AVa(n4F*1hp(_-C4ZncG5Ym|?f8@PlCS{rQARS)vjF&mA z$7Ka$ObjRP1R%!`j&d>laD~;7Kl3d=M|R$&yw>`sPyvj|QkBu+E+7sE9+j5@W{$!d zwO`_!f4_~Z+S)U5FtjXiaT7;sS(FXju_)#DHpjunaz#87hUEG>;<5l=zseRZBMqNI-9-=5)c5;{5xo_#?H){ND~fS0uo9)Dny z{(C%5XIr2DwacwgFDft7u(6YfPO=Kk?K%W0vMX3O%Ag{B2c=3s_&HCs=k4YcGuX#f zU!TB=Q15XAoyXvVxXtox7c2ROza-6J0KT6y;^fp#I=A%*Fy@DD(w3=V7(`-#Lhl<0 z&Fxtun|`o)H>6T5#hJ(vf%$}$p+{@8MU~P?A&l}?3Nqr$#W4nh@m_?ZSztHLSy`0u zmN2R5BaNS9zjGtp!@#ENCvf4jkSgu@B|%8LH+^I0;2?6^et|_Pa`3)?E8(9#8yKzv zRd0^Bzr>kJ1IpLKo&{0!^WW_EGDi|)-WN^5+G)#~4D%wJthV0k;esWEt7od?zR7}8 zmct77^)5G>VLy`jA6MNMX%ceRsYTJQNGG0_mzOUl-h69p^jdB8TymIaD|lI6RrPcY zY|a24?|9+u5^9t*%XN57{)>KD&<|iQVGzTW8#nCmyBr%9ECr!?k+#1dl_@;1UyDT| z#!tzCT1_!o_7x6$vr1yQhDk^_pc`P_@*UX}dVQK#was=N1QK79(`svVHG5Mit~Ic| z7kCuAp6`zvZhrd``cVU6v|I@J0O3$18;ai6n;H(BbVe<=%aG0^u7<1sL3@AjCr(QR-`ZWwy3z1kw zo*i-`86n>tu2m1#64IppOUp$uf%^4PnNMRh4~~Di84(eSD@P&23TEMAdP6J0jsWeI zkDlt%{@4X1v42}1{&ph(yO`W6-lhS&nM8pzU|1K@Pc##rt8VRZk)N$rRq(sKw@A6z zv?L1cGxZsj$%+4-i^c^{87+2;TddJGCD{lMg8NI5m4)Mo%?-NB+DXjSLTBY#vq;z{ zP~S#eIuA@-aD5*)Tj22D#OS&m;MkkYJJm^~x!9kC4{!vYg>m@zM0{;B-@ikCm-7Nb z%nQ*B-U1E76>lX*Ul;T3PGjF67APc0YUP*N=;<>ImMG`*8c9{5Yv8>v3)@r}TIM`# ze(AJ>QE2Aj{}$LLIu6dNxq$Fs2?BBqK&dZLJ#L_o=8S&Iiva>Fiui3$-K3b`RwQF) zAsp#ooqVWa;4>0|3=1|f4nj7!0>O^!4rit$Ljjn+eFUXcVyZt6clj;OnEEl4?J!yK)&+<_H3;f5Dpj0 zMpOF#?Ezr4c&Z{vrX5vId}q^kb7OiPENYzqB!Myo_5gQu!*ROpFRpr%BB-w*NYL%_h;X!852?ukv;%C(K`gE5s+Z%L@@w6Ze4Mw=zaNz>`7Vl+ zRekp|Y;`L2%*%Fkin0N5ibbc=8+b`o_^=ptaLIILNGScl>_0xp3c`|A#|~N3-HbW z0qGu?_UJ?rOVMwZ3;SGuhiz*JkZ8Z9?>~I&GNPV4c#Zj;`{lk6w1<1KITO_iu8Y=!KOPf54&uc=cn4sG$DE2%c~hFVm2*YQ1m#+~60Wj&Ae( z1-X$`vuTN{$)&AB6oe5egaX?7TCgwb-VPHRUL;@lz=0ZCS%gkJt_7Pl3F_FTP&Y0t z!@G#<8HLzWyT7j;kn+Uur{i-05@%CgXk~5=V1|eS4wX#rcNoPr0(r17zRxf>q^VqF-c3M2fq% z*aUda-glK?G|l}_41&85`%{BB0-r1j8;=JWDAt9ww%p;Yu7xWijt1#OfB%EJUwdlE zpI=SRz$UzZ%X>Ffmpel_j9o!EM-14<1rSTMW<8J(NI3sh*;|IS@kNiilLU8ncXuxm z+`Uk=NTFzPC{D2A6n9E+FRsO~c;Ft_jQ+oE5ov*>kPszNxwf)OrK;%0*Xsw@$`EMk-?_Wr~y zcjOPE4{~i@|J{#*P$O!n_7mYaQ%i3w>LQpCb{9D2?vN)pgy+IjGe}*&v>aP6+`LWs zl8vkKy*>fJcx|Xg1S+A9P`3Cg5q~RT(u-s0dRBobN3H9*J3L&fw(vhFdKPK1TJn1X z25GxXJDnDc@9X@bXZdjk{?OUxskqg7W+04$cL~GE0fk+xfid9%p~9W|NNn?~tG7Qk zo)Bd>Exor>u891{tcaSzva&Knl;FRiJVF5Xm*p~RKYuzuQ$3p2U!Tr)mziC&vB0R@ z@b<5QCOd|FgW1$9%%$zk&#u0Ju;6gYP2&7CiLH>Rvi3AR_!pvGh~(` zf72VT`|7LLjTuiHBDZV80|po zyUT-5q(6Rm?$aOlB4_8@24;c5WB?8jBt2R|cgZVPN|c1+7<5pg+KR&V<^D@+WFvCs zNkzcD$$kM8tnvkbVhMP#e+{M-Rvz(BL7W(oM~q0vu|wS2e__J^DX{&4C=*^;u^Tbf z``=gCQZep79Hd$C~%+AjK{3(fumStpQAbNJj zL@{ebI26&d=ScdUFIMU!yxwn=AL!xt&$CyxMwF5YbTAq?_vvMn_G4XHKZS4~8oYsO z87RRBuzzJ!_QrHnyRh_0zbri6=^*c!ReZh+t@b&()7XW1alQ@x-GS3}4j8Q{ zME0>xx)PETdgrm%98IN=^!xp|DTJRLMASlNeP z3%!Ixruf?p`zOgP&(7Q47Ay(c!O#%?tZp+oa0TQFoLxWDepp^rc22#22>T8YlF3_L ziW{qOuftH-D$J?(PZysXN28nge?KGOC~Il}^g3j#?>TCam-z~e>m5dY$F1aZ?-4Aw zhEJjkLsiNk3}R%4#rNwqet1B7BM9TnSr{nBhp;|irLRFlG$y?c^6pMc=nx2%^ENW~ ztH0)^0T3)qUH?}l~NJjzaE!|;tk;?L^Vu6gzNpfjoDv%)sQr)_cIL{U~X$HTy z$!n-NY&dSi7OK7|ntN84nRC9=d<%CLL;WF&D;LDSB*hoeGw!wdl0T29UiW9lqdUTq z`_L8w?DURCwlv)bWW{Pfx6cmM)6s|ilD@MB>?btK*)5@!^zi@XyxFPKG zcV)n<*>>z^QJoIU9!UuUi2Ou5CLAC=(rj%ujzoO;@C&rIF-%9u_OD=Ql-b@SBB!|O z&oYX%!it(#4fHgZ`2C+8q$HtNt`7Ea9D9&si7e)u+`bd3#==n4vN5EtqDC3N=#_rt zU_$N#bbCqMsZ8skY)kL-Al@$AC=1!W?fqbyY$;-YLO<{*{?7%URC(V#sHM3hj0CQg zPW|#V#q8l9!b33um0NDt#u^K~oOCCxaRMJ+uT1Ktqt-VN+!?Q0rPB{; zAvi){wot>-Z4h!?3fSa4^38a9Ogg$z-p3fiJVl1wVxdt=lbeiK!=cR*t^r5g5DbcE zs)P%jYxMdYq@#Pt{ylZ)O3}T{+`WWvh_@sN463)!Dk8qv0duE&FlfC{BswEa%wF>? z>S9ZaIYcWVb_XGiXaYrUX|Ku-hP0e}VjUKz zImf6iK#L4k1`Oyb8!B=WK4=#{EmTP13oEBD3T#5iZtxymi?7@E{UAY<$!iwFaeo+L z(e<`cQ4vm0pFV#z>7P}Ci_7?H#N5XeflRd#kpXT>N4EVk{3wzsWaTXWgOkrs)s90> za#IZzQZ(MIg+3^bA7$^@d>L!Kb2rI%B$96N9%9mco}{r2x#XAGI%_KOpHy?la+P_$W2>Ev~?iC$QLA!Rkb-4riO*EG*M=+xiE97NgT3HUG_T0X;{M6G-@&qY2E?$sE45_4vgYs zT^1~n=AW_}U+m6q8qK(5cI1n>6KDY%$d6~jsSPIpzad$Ra~zc$+5TAq^}D zhQ5e~w*W&a;@sr=fJ4QFSIzfD%e-heB5EH&LsB7u@!zRi54(pH=~gw!RfbU#6JCP; z^i>3+(ksD1O7cQ)Sn4TX>f~s4#)>oQr!73@i^Z&fXXj6fAuj0lutt7T2;uo|omb5m zfEua`?H2RLm$>a7rJ%#nEVjcQ#(xy9j@B8dG@oKz!a3bv5#|3FdP;7tJ2J5kxeJ6mY3mSTG= z{#!qZP@w%ADs&Ay7>?!TY^8vrn>H4%P8L)h^LP41VEX=P7gQ@R%Lp*2yeF*Fd5u9= z*vFvrdtNL15lT8s^|xv4Q4VEHu0OX*Cm~B+FwA#pt&Wh@mIc7bd9|@J%rMh!1**rn zizb_eECSeIb7>((waO<#Z17r?%E6V{%L?bUTb_n*Oy*#7QaV}qo-76tczUnA~ zNDK+k4Fzh9*|P@)G=fB)5BX@8;f3l87D#$tBc|xu90J}X2j-g%t3FVFb4KOKXOeU= z2cSy8KyZFq#U!}oOGO(UVr|{YFt&Vp=-+`Ok??IKCG^KqP6WP-i3bz9iXlgCy)=JN zrP>8xUjoK(_TIl{ov?T_9)MAR&)0^ImZRsBmRVogf26D!H2`JTa{Q0DRVD`u1lJNV z`0IT{yi%skV5i>%iqWe!+vl?jiecxxA&kS4OCVsG_79uhDVR+;4x8HqBRE_}O~7Pq zXm(``5HLuW%8sQ1S(S{ng=xW8MYUPl4ae2^*RG08zJid%)H|k&npA>ycyeqsymTM` z2W?hF04yA@TGsQoH5)T~KsiovD1^{ZJmnWa*og8jrZ7e37`;2X1OK-**Q6=+i97MD zg%0gpe`gRf5&+N2g>0`&4+Zn4t3xz~L4V!*eg$e6#r!McgR#Z-+4E4~mr#h_mkbdX zNiOR!((S2#!Kj^70Xe`?RKx|`wo0Ob_?8kj!msRB@?|;PpNwiUosBmE;1P;pPnQN` zHx30iUN#4f6WOkBb zsN*+rw)FP*KpJ*_+Pb2%)O5H!zWpd;bl4>qle*iZeX7LxfZdK?@3N@`p$rpC-_X2Z zsN!j8Z@G?zw7!Zlby-BPT3AA~fBFwkli*B&nW(ss-&Kgn;tRT5)W;g!$H#FsU1Jp7 zuBHB^-%ylMfNFpI`d>A|ph=7%X;lq^;oLOSZnbpHppiUQ!GUq#6R?PAH*>fzYoo#`W34d zM>@b(3oQ<()2IKk%1=ninuHqhXqI(`aIQi+m*Z7!$zX*0h6Ey;NYn*dOP65FPvw)L zU7j1y>d7%s8!_uU`A~ETm=YYn!qalAM1l*DxKlo*uNnrW?7M>S5g< zQnmd8t7%tgKDY60<|Z^ydz(a^i+mSKanMd?%bhi^S!ckB=P#sI_d8k;U4i0`PV)-6 zex2lw(&EqNPQhTG3fy7;7!`4sF#wx`cqdwg@M1lN7}WR$_hDP|1S>MX0U@&1Yf%Q; zQIPpGcHX9$01d&|1*f1&(xZI7q~voslM0F$;00kk&o=*5E$OCrKmrrE9r@Pl8ov?` zA7l~XfFCdLCAC5D;Gkz*u1(&QOuUKAd;*f+Ap5U6!c_PS5(HJFyK9dS`MF-3*xrmw zP|$5D8xed7?9CIos5*=fvo~SVmknW~;-t?_&Lu|G{Y0qb!aQ|L7~2uvg=NG9wMU8&*fKQihGD;LYbveWM4t;~E3tGBc>5+GaYD!IEUe(^pYHiKR*U|yd`7q>mX?bOo}F(L&n! z;CCLBMVogshA-~!UAr*%r>|)>Zv~SgNA@`-?pSGw##2ca%oK(6AerI(Dyvmq94^)B0oig8)&o=U^wgykPrQltL zTJEU6E9%A@oh$NvHh$oxiJNV2wn>3Vgu1}-$#z)?#bQ)|Qlhu__J!ay}Pk?CG%0Oq(cN4`a=#y3z$GoW*TjKNHHOQ*!6^23%nq(zc_R`9WC-^c-u zN`@@d;Ull*g$|2!h$ODMj|%=O>l&k1%$tBY>P(F>XTl3hFG7(;BZ?+Y@k0Syj@bJZ z(Z%bHMY`jXu3Ub2>PegBjp^1GtBe;)0)2-K_SwVN>2nDgKur2$34@GG9tt{ND}Qw7 z@6%w(pxPjlDbQlg(tZ;dB~dhtEDsNIu2?Q0q(oIDfu=POs^M(s;3}RFoeu>M>ov;Z z&f>)YM!S=iXCqPK36SgX7a$-VegjvIgBbPm^Owp!J zcMyrXP@A%g`~BOg_`g+6^}J!3ff3#$_7D`<9FM^wdOi>@c$Qjouij(R+c-9C&cWV< zI&MvgJOV^Z5{&x^bPho1c+N51!z8%uBKP-Q&y4x154CD+W)Y6CS{{Mp2svPI+~kt; zi9Kf~R1s5MZ}FMUPnL$>=@a_hh^{?Km;;iTB@dlWA@)F*g@XEcY(<>gc|ZGdVP3qt zJVdJRwPlSufj2=lW^`__2Rc6Q#xJ^ZeTlxB^*YdJ;)K0_XfEWc1-Sk`GOY5gfw{9p z6lJOjbD5o^k^*9BBp*NMHM)#2bzVa5VeSaFWEMWu)?kE;@Ee?Ut=JasDIheo6Ai4s z=r7raI3&@j1aQT3?2mtmK*2JTn3KwwoWCdvsBXVuH_9%e;(+u?Bd!J8gW&VyVb`jL z`oacndLE($!Q!%B1#6%eA`a+3P-G zB!kqKELYB>`7&NVq8AL=t?hF@4;igowtSx*-&WNa?s=Pne6?q)EYz7%r1gMgxyz{Q zKV!UQ9D4s7)shXBk3xY$jJ76MGm@VR%}fSU|5y0S{^1^9nqLD7u5szd+T&k|s+ws2 zkRE85{VR0?u`At1qeKzB2Wvu8i$Sb~jPxzA4{k4^tqzv2)bZ>(g4$7i-ssm*5LK5z?_&jGezI29xA@}oW+Bskr#|y-0euSnB zxbMGLg(z&tX(MAGr@ifQ2`(glMSywVPuPb|*iOwxq_qchyoioj4MVQ_Q6&2PO~e*W{|xv_Yu3kkOZKv>rgvt+X~y}@3KtW3}xFPdGsW6rsgLgJJ1NVasu)F=%G;3 zcVoJby%V`0n10Vi^)po@GchKWt+OPhRf-+;Z{Y_pqa+cH4~EuX7kyYdeit;Ub32Wj zHa@y9?5B97l9lno0tLYeKO^@H7 zR+fp#ZM12ru9GVAN}baMynXp*wB!`G>H2%LK#aImOH0bQ5!{s+cOUe1TM*N6$chdU zkD?^@lByM{?^3rfp3K=#i>(YL)dg<587JK;z#^ubF^%c8XDoC`4gM&Iv|p^i&;7WF z`9td4h8%gTtxzuXJvMvW*C7#2FY|~we^QEO{%c-ynOPJYGuAmP6+q(wXUvqYUMx9| zE${6)=LK7uG@j~1!m3k@)$hW$9H$98A?(T-POF7MQrWSRX&ov3mN0^?7!DcHmVh+e z{BNRZpB}~saX2Z?NQQfd&QIV}eB z!e`$*^j$KMpsaY!c}+y_RQCbkZv!{ZH0T9tE|Vw-~vK zCyZNq3W2I(2`k(71yzhG>nO z8%2DIRtVxQ>cATZO|HW&S%yi+G732lR7LgWB$4P&Vj6N|Hb_{?5;kYnXXDBv8dK@w ze|HGz%&^~OvzCF>=6=G-=RlX@c~pixbhf1RI=$S4S>3boE_1yYwKk5moU-4zM;BD3 z|Xq?yP`HL2X%RQJ@L<5O7)7UP3YH<;5%QuoEC<$scU;xO|vAe z=>ZRIdHf`F9#|wXE~$$QsULHTQN-M*_R*wWFvCl5XUV>TaV(eGkhRTaRGn7)^1t|M zL(-(iV?rzP;6hiMA;ZWtYI5vZZj#4|eB!rSK-T%^MrumN2fg) z==#Ir`Jn)Xu5P?FStJ{V8qdnDP7GE*T4!%yR^~h!-8?ZqL)@j8mY6U73ZdQ?r1{Y# zRx^{E;5uoW-7@NNPKjm5SOr zD}!>Er|3edEa^VZSX>ywMm$Dr^!ZSUyedGALRtP_Bl}1dpF2B1x*`41?q?)%Q@FGU zfpuvV<|188T%Af`p;$i-#2cCrK2_BW>^xZ)6^=V>bg8tf?V^U}(fQ7?e`bx{%iO>C zCG-)aAvYK4kvz0u(u6^OUv_I5mt$LFR%?l{7gwXokBvC+b9S$Vk2PIl0v7kz3`y{8 zRUI>?m>6B%2tM>5K0xS%trn4pOzao-oc~GdxmS8!>S|%u+X;6+H6Bu;YR;vQ0jtP2xafJZlrgO1TmL{Q zA=t>Wi~CyqL=pS9SjQe{0k=c2pUB#D!KB9POOY8CV?7vmvH_2n)$@ChI3xTH4JOzx z`a9-Lml=kt*(O^O9(u{F(&R3wasOM#_6!pyimp=OpOTjom-bIQwUi}McOK9v6{y|ODyJBEFLize>|XG^&nI&QlciG9>=t#66~Zqoh4x8CUNV1CtHY7%Pn&ZwZd4eL(A~AP z+VyuN_rwztEu~G6J`#&1(Vq1E5L%ts2mmy1|C=L#g9z6_Bqx-ow5zT^4UvLV=8pz_ zkC!#-x=ky9Tx_Dy8B%~RZCUzh0=?4v{6O8g!VUtK@lE_Hq_M;jFK}yEH1BT>wUUT~ zM^>36QfTW8XmSZz-Ib+yWEj_AKY69o&y~y8=vrC{ZZ=o#qP{->>Gh!@%yldPzq<ehHyh^en(5v->3ivOabc7@GuI0LL#{HM#Jf?-!q`6u7Kz@QROu8M zD?j!KBbRkQV_#5}5C5AM{+BEZrg~9dXw1b@6taev{cLdHNu?7+0u&SL$))F4M!fnQ z{+_@KDCEli^vpVjZZ*%Td&Qs5x~}{ELaz6cbO+RH5@b|(2;l%5qLW8`O-TDBx|J*c zzI~xvl;C|4H;ZKi|6-blmr~OJ_MyZ*fFdM@Q(+H=wSORth!DKZEyw(lHc&lFsJNQ{ z^mRgZ;W#UPGmAmwiXTQsM$L42p4F^nbo-MX^m^-*UDUQz)PilQss3_9EKoMya&DaS z;Zcj8giJu=Oz8?jqjOr{JkJf-TG->=X{bv3@@Ppc=e*!`2pA~kZct$1%NJ(@I9AG; zXz7O_KGDN7Nz=c!b_y9fsq>oeirM3O+rMEGeD@qEniw9(XVT?yab&O`hA23(RUWRi zWWUslfcm2nNV1Y9sHatP>3XeloJY^!+VCnQGy~a=L(5G6U z65Sg#XW?H|rA1DSd>}F&3!39pzsx$iLsDQP(a2%=z3ud?5eWfPBG*6_-e*MG(a}2l zp%Ruc_u>!VlmSO5kv*MVWjXyCia?oma?S>B31^8u9_kg?7)~n&ApS8cT(hW`SfzHa zZ-44w=J7TFVub@b5hdf7FPJ}{^^wWNsN#4S+7Cf;gbm8rQcRlCWSy?-E^+3>m0l(& z4pEmmu4!ajF+8UlW=2Nru?x_p?*Wi*>C0qx!gl6VmN>@|zGlPZnD?|hu-H+IsoiI6 zt;~r#u%I{{eFAd}FPlV+ z03;z%<0RXg5fGv7kiOvcuo)rjCl%YsA|C6mqjVOp0aCfI;KT&Cr$OL^G;jQV)>O0c z>R{{OUfvl7*ORYoZmZnBt~ZM`1-R0HG~qh}O~cjK4g1%M!vcAhYm}n@gf5U}9ix4P z!avNT3^M*iW1Rfc2)3D=cUMrS$3TG|?<9+YcCUDrCe6vK+4~Yal$iVd%D|60N7=|Y za_C~%fN(k`VZv6(O!zPL_!z@#y3vbs^*#g0Y4(axY?~a)m ztO=H^bV;dq|14)4T== zfv`mVCT`fdsn<@w3Ut5@rT#(D|28B%I%`UhN2(V=k|qqEED9OVOn1=Aw*~8@ zV)drmYou_Zx7h9WlA4E0qANF(V?Hd=@kUpS5*C1MrJGidp+(-zhp!xGm6u@;SqVSb z$xZwua>S$p3@*V@Jq-%3^r&~2tX65mlN@eP#|liG9>2h%(h33ufl3sPwWRm<)Gy4> z2JUb|B)0I!?*M%gT6QkzTQw73y=cc^Hjm9{PPYv%R;Z=~DIS$$y-oUNlxG>Jfa>ly z^Jl&t8RKu-LQL!!yV;oaGRJPa57F4OOoD>}(w0|k(& zn0PbCBt2@sYK#mRbG|RuW`0A-r`d~pX6mE z*EE~3*-kc?K=R701<$Kul|nygxfQpAFsPDtNbr%hH1{r~e>F8{)v2nwRVnYLhQjf% zB5FLfC|R*RNXpLCx!4u-ikMhM$5M_qQ&z$%{jCX`!rAa#H@RXH*G!nrO_X3%?rdun z3Z%}Z|0sDmow3)$h;fnv?=|kw1uJtR18o6dD7%{sm`z)p8TCzF8DUln<|pfKd!W9Wm&8*(|Zc zL9f{jUWnhMW+L+Ddn4QP!p)aGRFj06t@Vd2K<)d?d7lFCQOX^i5+xSq_x2aHF3D*q zJ}5fV4r_&0#gFm|Cxyl63ulV6tba$#F_EV5tK&7G@7L~#;a(HlYpCU!W-BVz=)u7eRFhTGRe;}^ z#4KRMWR_bpt^RWlr)ug&h<~6~;XpbnCCw{YlBDnM_-FYdVq&&$NkoNj3SifhP>WEqbv`Mqt3F|gokMb*P6@1_rIQV9o- z^hIp{B<1jdCJep!Wo#k)ukH?qc5Iw(15OSi`mdeby}W(ya_c5QX1i!$zo#!YPZ7pJ zH=AF{Eq33nqQgfMGNxaIIKuF6`+-1&&+Bo1bcg`>*dC^P19M@rNXWfUn;@?)^G@Y&b}XKog;JQaQ2nxzmz>XXL9(X?57ys zJ7X`B6_<;MVyD_AWupK$VszV#D~A}!<=njXf2}+zqrwu@7Nhew6)37bEOu*z@Juua z&E*X$wU2{pj_YFFm(wlU4beu|in}RKtR zU16b5r2CC&T^=}S)%6fYs)TG4Xb2Y745{z{!$X{P1Et49|KTq^>>F!nRH3>*+I+e> zfBa*J>PPNX5IK4d|0|R!vb&-f5=+V>>@|SCW>gX%BfJpu zs674qOFgr~Ln@i$1(C2Ahh>sxle{>vWCcod!f{lPhX24ud67d*Vs!e|If?q^mCZAo zNW#xw#m@ahNEr-m4d68XJe`&@K0(#uv<}~9ocVZ!>oU}uh&L%@!`WI-E>4c(Ah3#H zW&|S8qW}*JHMo_MP1VgF5wbW!qA?a4&s(Sd7@fuSTe!^@2~QMLD;m;KX~&13ONK`h z^gu+lvWu?fEyRe+TNL0C+eIb*cpbW`u0!(JqvL9z^vx=iw2=>80CjPPS0D{i6$>yW zV!mExeW`ZS>H&>St-2%(n&8X}48QbC(xBkS$>&!ckv6Ta3S1!Tcd|Ja*p;RgE!9R} z2^@@B2v>~Sis1)8XpdYNvj&suaL|64P9m)pK!dG&ve372(CWET=lV*%qH*U{x_?UT zBT37#>R6_ABzH?kX36m$KJNBrK-$?Ut+&@!Axk_+Lxb{JsalhhU0?DP-WME*e9JquGC;rQGP^`6Xtzw;ucdeXX z3bttwTzkil`vJ?m%BW_3I3*AVB`%+)jh8vwB6)a%Lqv)Id4BkVyl4+quui+xtY72% z=AG1c%^om&j|nsH6^AKDuG=u7X8SxF2@-R>Tmb0fJUM9;Z}c# z=iCYJu8mU+)JClFjW4bB;mPP|-VnRV?9m|On7?*$buQ<3kZT)`0WG(+K9g&Ya4-71 zQQNe1p1e1Ho^2y36LWcBH_z*H*(I~#5Q_8j=^WLmFA2O`QKK?<48KedISRy(njNB* ztUbHm<4}z?0!~XOFC5C9mt0qzc72N1oCc+}e%kyL;XGwa$Y7A0ZZhs8b?&*G{^5HE zI_wlSIcHt&4pbJ?Sz5d&Zymfpb^@O?3HvGIT8A9!7EO7pSz zfs!qhW^;P5xy3cb=IS6>eKS6Bw(O{)^j%M@VYm|<>6JQXrPE4|7jO5GP|J~a>{dZH zA>GOZ*uUd1i74-jFKENH**v2^;=ZuAblRg<)Ynm;(omgL{EMVsz8t;bht^|gs69@` z9Sod-Mu$Pf5&^%XS*vc}Xup%n|D+Il>3!`ekR`Yhu1vS@WM&)m7I(8ERRT99ih?x8 z`hp?k`wg>y+$lbhM`r{{X4@Y={k0=XTGGPLbCXM;{l~ToG#W09FRor(a(}V?>caJFbh=Wcr7y1>*Wgc? z@Rf=?a@q(t8A|j7kkoNCSE&Uk^IcGD=2JWf}WItqRg;rl5ENg?4URJk9sXWl1 zf60tZMGbTDaC}gR3-21-DqwaMdtYQ({{52?U6U}^AfeNal!j@}NaK{+ zo@2?j-(52wmxUtSt_p|On}@TscQi)HKid9kx6moAX{?7jk!T)edc_bX1ytR0rlbpR z(@z!q%w&TKFqMk7K*xV*cdkKuU#rO)fMgcCZ9*;E@ovd###y>cUO>z>|Ao<9PKU*aj1L(FVw`bv@E96n4=*Q*W z#3C~@env=SGNy1;l@ci_6EB+UnH#>T@MDyI_G6+n5MZzGbPRW2tCrk7*g7UeYc$>& zL3yNSNiMKDTT$kP`1^XECDOwUej>XE_|P!@zG!{Lv2xyzYs`eK^oEx{J^vB@hrPj@S(QR(;&A-ZbVfhzVo!za%7lwFR3(0oaYuq|es8o%d?6A*xMdbNx;zhmn z6k71h#4Nj&bKQ-}vT|Lab0Xn;sdU&>&*+*F#QEVU78TH{pwEggSuu%XW`CYWtGA*3 z{e69f;@iiguxj20X}PVFlT>j<@yy|z&jaTY>o@whB0)t4twDPx+4-eXb`{>)0eYy& zAUS5f=E|@xWhxnhq!`|O+c1%gwsd^TcMV^s6Q@%2qQBm94Qn>+nrB$&lp(uxY>w97 z?DA->%=Sh0{Mkp+Snq4n_%1-;)cway$`cFZ`{KA;+pF3q(WGM+m*$6rZf4bE&ztE7 z7Pz6r_CH#~=3x@C5`*`aiObdhI5r}w5k++u!mdp%jSb54{{$Ij?hY)=cUj!I<&ZYJ?nV&G=GD9rEf)xhZ>=MP@Fge!ZX5 z`627I=+o`(^$Z$7_4Pzqi0;W7GLtl`c+*{A>2dH;aEI3wGa1?Nc3Fp4rwD5u%@hUDzDpfNnUAA1qQB>R9%s z%E>7fl+;3@w5yiGY<9$1>egium51fyF!ish^JhyGVUN$}3PMl%X7|U;_~*L^RZ+t@ z!Rv5&U^9t}y< z#+wxKt+yw7bO4Yr7rrB^xi;0v{q&ub_N`01(qIO&b(t^;%e>Ke*X}>|MwYWsBmy6^ zp>)-EhYH=2NJ84TnSlvbqHM%(nV9+v!{|TLOcqOsV17huO9+0_UfQWKaflI`({ND{ z-sBMyZCt{pF(%GfB~&NB+bP0+qp)VgEd~LM7SNLxKW_*qHIe1lIifaOJJV<3TeL{E z>BZ4wzBfq-Jhb$wS*n>#F>zWuZ`ELl@opjxIC=cCfC)Ij-A21Z&g%%t?f0t|-GHQ; z?WMnz%Q@oQ1S*0C?}_4VUUOy>R2{v$swNTfK&$%_kCfFr#7%tE=1%-g`?>CV0X6I| zuxY6UYgg`3#jcQub*@m1eKo&)a?R|+M8{svDiCemXlBsBXj;FJX`r0)r|Y&o0aaeB zF`P!8^WB3C8qgO!&ab-fjK)PZax%2woAM`)n;FZbK<*GX^YbCJ?ZGB^X2Oa*s!yN^ zbP@z>Py0})#N+_zOk+F8Onid>~5JC}|2C*G^+!tWow zm*y_swe2#khCDs>2HZZZy9Y~w0oN*${_NA-s=1UP+d-sTOT3>BLApQ9R=%%(k6@^^ zI5gM^tj(p*l=C@F2xdLc71DvVx|+z3={0B$92%}y@+a;Lena`Xc=#+VU9l!&DmtY; zx2$GXx-4*VE#8ra+qQWI+t|LrMJObtBg|P+S$`$K?cJlJ0ejsBLvw3rh2d-XBkI+O z!=0_=?p{CspW5D-hvb(?B){(MKxQaciBz4^_bT~jYt@|J`@o6rwE z*ymXA=|X+S>A}<&0P5yof)qJL>CR$*W{q9Qz{oTgb8kTk+JRa$XN-U`&G(PY<f zDkG-({q{J>(`KJMclGXd+QMrF-^(>OIhwN_uWd?RQQ+L+F(l?enCG#jfh>3n3)u+@#-#{(J$xyHuv^M4F+N>w%{CSrS8?x@{$^C# z=SzC>=q=8U_J{o5LajM`+mKHtDSCrjyzx&er00$Wk2D7*9smUDKXn*Ob1~mM7$k^0 z$s}96>BbY!IVs%hw7loxk`;7O zqKy*@!fE#GRq`BXPJUmz0`%%uct*gt$y2f421)n)-mUuk^XB;y+2UGucctVB69kAw ziR3+QH`{KZzK|0QFwZ&QxBEw{Y|%M7sQDOVc}c6G_w-M1nHJHI*l+>U-hR!+O7Sd&BF0LHHmnvW@&iEIn%OtN+#{yJP2C<1Fs*3t;O5x^O> z;c2n<{2R-DNhHLI@-Ts9Z@D*W481;nCbVuB^=BCa7T~%iWe>*?wR@~)&_!)~8eH;S z4qQXU38nt6`FUz@egYj}B@Tz5O;O|D+cpe47swlj9Oc98?4nO!o;>efjTfwY0Da7V zdeY+qbRo?)chyRAG~@E~vh1)@Bxqd@vW-2^_v84{Ntn*zOxTvc`5>Pv^at0eV8>%` zEQ%W%*550N`V-H?)eJe*-+wbCj$4f4k7{Uu7eTP=Mc9X|56_!7Kw~)7gH}z~m4F*= z$zxtf8ara1$lAP`$Q02Y}6dVfY_seN$SvvU!Bd zG17xj!iFqjby_eC>uO-4V8_cVyl0lS5_!(xo}C^5+;m*&qPT-&2Gur8eqJwYzv9tS z5UWLTLjiy{oUP`grnt|^Q3-JGdimv4uXcF=u*|1!aOL-2+6%m8U233pJB7%$jeqpy zz=k5WWz#R@JQNgLv4d{NBL<`|x?leLTg-zG0QM`u+6^dbUi=Ry@ZnZQn-%J0R*-5B zMD+o9^|qXeST7pCuR?)O%$B$YNsBr+p80nah>5_}ea3x=<7C>NW@HK&LM|69Y2Kc{ z`AM~S5Mh!QLF)Lm01W_YijZF2DSX_VDIb+leDYoeC3)fj04%h%Ti;a(q1C`T5fXry zOB}KM_lejBKoQIT+X}Kb|8PYNu`Pr4k&)`^?+5#vE<&p>P&p@1VL)GtdFYmc2VjIN z2dz)At$UYpB1t9%^g&?yE{Bn5(f|!$`ABJbj|)Qu_L`oYA3%uCPxJa|!s>rkx!T<| zJORD{>>n!PGU}gzdX@;-CYciL(SJLP2tT&&E~E$`A-*4(ep3yn#y&ptepBpRDLS@J z3xHt4bJ=SJ6MZ=B4~G7!Mj?npw9dnrr~D8HdKDoEYny}RCph!9fBc8q2Z24Bg-VIu zYYp0I$RMujxE545fN19nxFKO1onRP!tFG@?e{%=I1H>}0jK<7IvjYY%Aqbo3NUqX} z{O+Leg1k2?7;y?5Zis>5dc%{1$RmIdfI}J`0kuakX)!M1hvH9g`CBs6|GxgH1Os`r zhX|4Wf64a$EdBqB7XP~k_kXeL|MB7fKgR%BqW}N7k4--C-`g<;0O+DFC^)}8eIA{` iRR;loUq4)joQK?^-tp@uhPMBwZmNn}3ZLaH!~Pe9lxp1o literal 0 HcmV?d00001 diff --git a/data/duck_vhacd.obj b/data/duck_vhacd.obj new file mode 100644 index 000000000..54b8990c7 --- /dev/null +++ b/data/duck_vhacd.obj @@ -0,0 +1,609 @@ +o convex_0 +v -0.086018 0.502143 0.615873 +v -0.225720 0.427101 -0.544625 +v -0.268729 0.362660 -0.533833 +v 0.623016 0.276638 -0.017993 +v 0.601512 0.867587 -0.082516 +v -0.601818 0.867587 0.003477 +v -0.677007 0.126174 0.228965 +v 0.386620 0.244455 0.476150 +v 0.300756 0.137003 -0.351171 +v 0.569331 0.835252 0.282696 +v -0.677007 0.190691 -0.286648 +v -0.709188 0.631101 0.379366 +v -0.472792 0.706295 -0.383432 +v 0.483314 0.577413 -0.404901 +v -0.859718 0.545154 -0.050254 +v 0.150225 0.104745 0.432983 +v -0.386774 0.867587 0.293487 +v -0.537305 0.244455 0.486828 +v -0.247225 0.094067 -0.329701 +v 0.623016 0.588166 0.368574 +v -0.666179 0.534402 -0.394110 +v -0.257901 0.867587 -0.232917 +v -0.107523 0.620348 -0.512363 +v -0.290081 0.620348 0.572820 +v 0.386620 0.104745 0.121503 +v 0.666025 0.523649 -0.189864 +v -0.838214 0.287390 0.121503 +v 0.515647 0.856758 -0.200656 +v 0.161053 0.373413 -0.523041 +v 0.225414 0.577413 0.562029 +v -0.257901 0.362660 0.615873 +v -0.279405 0.094067 0.400949 +v 0.687529 0.502295 0.207382 +v -0.655503 0.405595 0.476150 +v 0.461810 0.330402 -0.394110 +v -0.633999 0.104745 -0.146925 +v 0.526323 0.222949 0.293374 +v -0.677007 0.770811 -0.168508 +v -0.161207 0.169261 -0.447841 +v 0.515647 0.867587 0.271904 +v -0.838214 0.620424 0.132295 +v 0.118045 0.341079 0.594290 +v -0.805881 0.287390 -0.168508 +v 0.311431 0.094067 -0.157716 +v -0.601818 0.287466 -0.415579 +v 0.676853 0.749306 0.024946 +v 0.483314 0.566660 0.486828 +v -0.193388 0.169261 0.519089 +v -0.752196 0.341154 0.357896 +v -0.644674 0.792241 0.250435 +v 0.128721 0.577413 -0.512250 +v 0.633845 0.652607 -0.254387 +v -0.580314 0.663359 0.454567 +v 0.472638 0.180014 -0.222239 +v -0.419107 0.566660 -0.490894 +v -0.118199 0.663359 0.540559 +v -0.504972 0.158508 -0.372527 +v -0.773701 0.179938 -0.039576 +v 0.289927 0.094067 0.304165 +v -0.397603 0.523649 0.583612 +v -0.483620 0.867587 -0.179186 +v 0.547827 0.362660 0.400949 +v -0.655503 0.169186 0.357896 +v -0.773701 0.727800 -0.039576 +f 41 50 64 +f 5 6 17 +f 6 5 22 +f 13 22 23 +f 22 5 28 +f 23 22 28 +f 3 2 29 +f 24 1 30 +f 4 26 33 +f 18 31 34 +f 9 29 35 +f 29 14 35 +f 32 7 36 +f 19 32 36 +f 25 4 37 +f 4 33 37 +f 13 21 38 +f 9 19 39 +f 3 29 39 +f 29 9 39 +f 10 5 40 +f 5 17 40 +f 30 10 40 +f 15 27 41 +f 16 8 42 +f 30 1 42 +f 1 31 42 +f 15 21 43 +f 27 15 43 +f 19 9 44 +f 32 19 44 +f 11 43 45 +f 43 21 45 +f 5 10 46 +f 10 20 46 +f 20 33 46 +f 33 26 46 +f 20 10 47 +f 10 30 47 +f 42 8 47 +f 30 42 47 +f 31 18 48 +f 32 16 48 +f 16 42 48 +f 42 31 48 +f 34 12 49 +f 12 41 49 +f 41 27 49 +f 17 6 50 +f 41 12 50 +f 2 23 51 +f 28 14 51 +f 23 28 51 +f 29 2 51 +f 14 29 51 +f 28 5 52 +f 14 28 52 +f 26 35 52 +f 35 14 52 +f 5 46 52 +f 46 26 52 +f 24 17 53 +f 12 34 53 +f 17 50 53 +f 50 12 53 +f 4 25 54 +f 26 4 54 +f 9 35 54 +f 35 26 54 +f 44 9 54 +f 25 44 54 +f 2 3 55 +f 21 13 55 +f 23 2 55 +f 13 23 55 +f 3 45 55 +f 45 21 55 +f 17 24 56 +f 24 30 56 +f 40 17 56 +f 30 40 56 +f 36 11 57 +f 19 36 57 +f 3 39 57 +f 39 19 57 +f 45 3 57 +f 11 45 57 +f 7 27 58 +f 11 36 58 +f 36 7 58 +f 43 11 58 +f 27 43 58 +f 8 16 59 +f 16 32 59 +f 37 8 59 +f 25 37 59 +f 44 25 59 +f 32 44 59 +f 1 24 60 +f 31 1 60 +f 34 31 60 +f 24 53 60 +f 53 34 60 +f 6 22 61 +f 22 13 61 +f 38 6 61 +f 13 38 61 +f 33 20 62 +f 8 37 62 +f 37 33 62 +f 47 8 62 +f 20 47 62 +f 27 7 63 +f 7 32 63 +f 18 34 63 +f 48 18 63 +f 32 48 63 +f 49 27 63 +f 34 49 63 +f 21 15 64 +f 6 38 64 +f 38 21 64 +f 15 41 64 +f 50 6 64 +o convex_1 +v -0.193580 1.125601 0.443872 +v -0.236532 1.179294 -0.383444 +v -0.236532 1.275851 -0.383444 +v -0.741519 1.222234 0.368646 +v -0.150542 1.619669 0.089200 +v -0.032381 0.867587 -0.007315 +v -0.709262 1.114847 -0.179140 +v -0.601710 1.544392 -0.125539 +v 0.139599 1.254419 -0.018086 +v -0.526501 0.867587 0.207508 +v -0.451206 1.533714 0.347105 +v -0.365302 0.867587 -0.232909 +v 0.021352 1.297359 0.314961 +v -0.139847 1.512131 -0.243679 +v -0.000124 1.104094 -0.275907 +v -0.043162 0.942863 0.261193 +v -0.440511 1.061079 0.454558 +v -0.505025 1.200802 -0.351133 +v -0.677005 0.953692 -0.028772 +v -0.300960 1.437006 0.422331 +v -0.741519 1.372559 0.175280 +v -0.215056 0.867587 0.293336 +v 0.053609 1.458514 -0.071770 +v -0.386864 1.641176 -0.028772 +v -0.365388 1.501453 -0.286593 +v -0.741519 1.093340 0.336418 +v 0.107342 1.039647 0.067827 +v -0.000124 1.480021 0.207340 +v -0.000124 1.340298 -0.265136 +v -0.612577 1.533714 0.164510 +v -0.150628 0.932109 -0.265136 +v -0.505025 1.050325 -0.329676 +v -0.537196 0.867587 -0.136226 +v -0.741519 1.275851 -0.082541 +v -0.558758 1.447760 -0.254366 +v -0.440511 1.190048 0.476184 +v -0.279570 1.608991 0.228881 +v 0.075085 1.028817 -0.125455 +v 0.064304 1.125601 0.261193 +v 0.118037 1.286605 0.153739 +v -0.193580 1.608991 -0.114684 +v -0.096809 1.404745 0.368646 +v -0.451206 1.630422 0.099886 +v -0.225837 1.050325 -0.361903 +v 0.096561 1.254419 -0.168453 +v -0.741519 1.082662 -0.050229 +v -0.515806 0.953692 0.347105 +v -0.644748 1.211556 -0.265136 +v -0.107590 1.028893 0.379332 +v -0.182799 1.555222 0.282734 +v -0.096895 0.867587 -0.157683 +v -0.397559 1.350976 -0.361903 +v -0.139847 1.232987 0.433185 +v -0.698481 1.447760 -0.050229 +v -0.666224 1.018139 -0.189910 +v -0.193580 1.404745 -0.340446 +v -0.096809 1.232987 -0.351133 +v -0.032381 1.555222 -0.028772 +v -0.580234 0.867587 0.110741 +v -0.354693 1.576729 -0.200765 +v -0.558758 1.523036 0.282650 +v 0.075085 1.437006 0.089200 +v -0.397559 1.480021 0.400874 +v -0.053943 0.867587 0.153655 +f 91 80 128 +f 70 74 76 +f 67 66 82 +f 74 70 86 +f 81 68 90 +f 68 85 90 +f 78 87 93 +f 82 66 96 +f 76 74 97 +f 96 76 97 +f 90 85 98 +f 72 89 99 +f 68 81 100 +f 81 65 100 +f 73 91 102 +f 91 70 102 +f 80 91 103 +f 103 91 104 +f 91 73 104 +f 92 77 104 +f 77 103 104 +f 88 69 105 +f 77 92 106 +f 69 88 107 +f 88 72 107 +f 72 94 107 +f 75 101 107 +f 101 69 107 +f 95 76 108 +f 79 95 108 +f 96 66 108 +f 76 96 108 +f 87 73 109 +f 79 93 109 +f 93 87 109 +f 73 102 109 +f 102 79 109 +f 83 90 110 +f 98 71 110 +f 90 98 110 +f 74 86 111 +f 86 81 111 +f 81 90 111 +f 90 74 111 +f 82 96 112 +f 71 98 112 +f 99 82 112 +f 65 81 113 +f 86 80 113 +f 81 86 113 +f 80 103 113 +f 92 69 114 +f 101 75 114 +f 69 101 114 +f 84 106 114 +f 106 92 114 +f 70 76 115 +f 76 95 115 +f 95 79 115 +f 79 102 115 +f 102 70 115 +f 67 82 116 +f 82 99 116 +f 99 89 116 +f 100 65 117 +f 84 100 117 +f 103 77 117 +f 77 106 117 +f 106 84 117 +f 65 113 117 +f 113 103 117 +f 85 94 118 +f 94 72 118 +f 98 85 118 +f 72 99 118 +f 112 98 118 +f 99 112 118 +f 97 83 119 +f 96 97 119 +f 110 71 119 +f 83 110 119 +f 112 96 119 +f 71 112 119 +f 89 78 120 +f 78 93 120 +f 67 116 120 +f 116 89 120 +f 66 67 121 +f 93 79 121 +f 108 66 121 +f 79 108 121 +f 67 120 121 +f 120 93 121 +f 87 78 122 +f 69 92 122 +f 105 69 122 +f 78 105 122 +f 74 90 123 +f 90 83 123 +f 97 74 123 +f 83 97 123 +f 72 88 124 +f 78 89 124 +f 89 72 124 +f 105 78 124 +f 88 105 124 +f 68 75 125 +f 85 68 125 +f 94 85 125 +f 75 107 125 +f 107 94 125 +f 73 87 126 +f 104 73 126 +f 92 104 126 +f 87 122 126 +f 122 92 126 +f 75 68 127 +f 68 100 127 +f 100 84 127 +f 114 75 127 +f 84 114 127 +f 86 70 128 +f 80 86 128 +f 70 91 128 +o convex_2 +v -0.881191 1.372585 0.314915 +v -0.752297 1.157735 -0.093320 +v -0.741541 1.157735 -0.093320 +v -0.741541 1.157735 0.357909 +v -0.945660 1.243704 0.078656 +v -0.741541 1.340341 0.046399 +v -0.956394 1.361845 0.100130 +v -0.891925 1.275925 0.347218 +v -0.741541 1.361845 0.185979 +v -0.870435 1.168500 0.035662 +v -0.945660 1.404830 0.228973 +v -0.741541 1.211460 0.379476 +v -0.806010 1.179240 0.357909 +v -0.752297 1.232964 -0.082537 +v -0.945660 1.254444 0.035662 +v -0.967172 1.351105 0.228973 +v -0.838256 1.157735 -0.039589 +v -0.902681 1.404830 0.207499 +v -0.902681 1.351105 0.336481 +v -0.827500 1.329601 0.336481 +v -0.848990 1.222224 0.368692 +v -0.741541 1.318861 0.003405 +v -0.924170 1.394066 0.153815 +v -0.827500 1.179240 -0.061063 +v -0.967172 1.318861 0.078656 +v -0.848990 1.297381 0.368692 +v -0.773787 1.157735 0.336481 +v -0.945660 1.297381 0.046399 +v -0.741541 1.243704 0.357909 +v -0.967172 1.383326 0.228973 +v -0.956394 1.265185 0.100130 +v -0.881191 1.394066 0.261230 +v -0.741541 1.361845 0.132341 +f 146 137 161 +f 130 131 132 +f 132 131 134 +f 132 134 137 +f 132 137 140 +f 133 138 141 +f 132 140 141 +f 131 130 142 +f 138 133 143 +f 130 132 145 +f 138 143 145 +f 129 139 147 +f 144 136 147 +f 137 129 148 +f 133 141 149 +f 141 140 149 +f 134 131 150 +f 131 142 150 +f 142 135 150 +f 135 139 151 +f 146 134 151 +f 139 146 151 +f 134 150 151 +f 150 135 151 +f 142 130 152 +f 130 145 152 +f 145 143 152 +f 129 147 154 +f 147 136 154 +f 148 129 154 +f 136 149 154 +f 149 140 154 +f 132 141 155 +f 141 138 155 +f 145 132 155 +f 138 145 155 +f 135 142 156 +f 142 152 156 +f 152 143 156 +f 153 135 156 +f 143 153 156 +f 140 137 157 +f 137 148 157 +f 148 154 157 +f 154 140 157 +f 139 135 158 +f 147 139 158 +f 144 147 158 +f 135 153 158 +f 153 144 158 +f 143 133 159 +f 136 144 159 +f 133 149 159 +f 149 136 159 +f 153 143 159 +f 144 153 159 +f 129 137 160 +f 139 129 160 +f 137 146 160 +f 146 139 160 +f 137 134 161 +f 134 146 161 +o convex_3 +v 0.343788 0.867587 0.153823 +v 0.623140 0.878339 0.057098 +v 0.623140 0.867587 0.057098 +v 0.429760 0.878339 -0.168537 +v 0.483464 0.953541 0.046370 +v 0.515649 0.878339 0.261277 +v 0.547888 0.867587 -0.147038 +v 0.408267 0.921300 -0.093312 +v 0.408267 0.921300 0.164552 +v 0.558634 0.910564 -0.093312 +v 0.558634 0.921300 0.164552 +v 0.354535 0.867587 -0.103998 +v 0.569381 0.867587 0.229007 +v 0.580127 0.921300 0.024913 +v 0.494156 0.932044 -0.093312 +v 0.376028 0.910564 0.024913 +v 0.429760 0.867587 0.250506 +v 0.472745 0.932044 0.175280 +v 0.537142 0.942788 0.024913 +v 0.408267 0.932044 0.024913 +v 0.429760 0.889084 0.239778 +v 0.515649 0.878339 -0.168537 +v 0.590874 0.878339 -0.082541 +v 0.343788 0.878339 0.153823 +v 0.354535 0.878339 -0.103998 +f 185 177 186 +f 164 162 168 +f 168 162 173 +f 165 168 173 +f 162 164 174 +f 164 163 174 +f 163 172 174 +f 172 167 174 +f 172 163 175 +f 169 166 176 +f 165 169 176 +f 162 174 178 +f 174 167 178 +f 166 170 179 +f 167 172 179 +f 172 166 179 +f 166 172 180 +f 175 171 180 +f 172 175 180 +f 176 166 180 +f 171 176 180 +f 166 169 181 +f 170 166 181 +f 169 177 181 +f 177 170 181 +f 178 167 182 +f 179 170 182 +f 167 179 182 +f 168 165 183 +f 171 168 183 +f 165 176 183 +f 176 171 183 +f 163 164 184 +f 164 168 184 +f 168 171 184 +f 175 163 184 +f 171 175 184 +f 173 162 185 +f 170 177 185 +f 162 178 185 +f 182 170 185 +f 178 182 185 +f 169 165 186 +f 165 173 186 +f 177 169 186 +f 173 185 186 +o convex_4 +v -0.924164 1.082508 0.121620 +v -0.741519 1.136206 0.357985 +v -0.752291 1.136206 0.357985 +v -0.741519 1.157711 -0.082575 +v -0.741519 1.061012 0.089363 +v -0.881191 1.157711 0.089363 +v -0.902678 1.061012 0.250519 +v -0.902678 1.125462 0.261257 +v -0.816750 1.114719 -0.050317 +v -0.752291 1.157711 0.336466 +v -0.741519 1.071765 0.271952 +v -0.902678 1.146949 0.057149 +v -0.773777 1.082508 -0.018060 +v -0.902678 1.061012 0.143009 +v -0.795264 1.093251 0.336466 +v -0.827503 1.146949 0.325728 +v -0.924164 1.136206 0.164529 +v -0.806017 1.157711 -0.071793 +v -0.741519 1.093251 -0.039580 +v -0.870438 1.103995 0.304252 +v -0.934917 1.103995 0.153747 +v -0.741519 1.061012 0.207480 +f 197 193 208 +f 190 188 191 +f 189 188 196 +f 188 190 196 +f 190 192 196 +f 191 188 197 +f 195 187 198 +f 195 199 200 +f 193 187 200 +f 191 193 200 +f 187 195 200 +f 199 191 200 +f 188 189 201 +f 197 188 201 +f 193 197 201 +f 189 196 202 +f 196 192 202 +f 192 198 203 +f 202 192 203 +f 194 202 203 +f 192 190 204 +f 190 195 204 +f 198 192 204 +f 195 198 204 +f 190 191 205 +f 195 190 205 +f 191 199 205 +f 199 195 205 +f 194 193 206 +f 201 189 206 +f 193 201 206 +f 202 194 206 +f 189 202 206 +f 187 193 207 +f 193 194 207 +f 198 187 207 +f 194 203 207 +f 203 198 207 +f 193 191 208 +f 191 197 208 diff --git a/data/duck_vhacd.urdf b/data/duck_vhacd.urdf new file mode 100644 index 000000000..8d9c4be50 --- /dev/null +++ b/data/duck_vhacd.urdf @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/kiva_shelf/model.sdf b/data/kiva_shelf/model.sdf index 138f88d6d..501a6107c 100644 --- a/data/kiva_shelf/model.sdf +++ b/data/kiva_shelf/model.sdf @@ -31,25 +31,175 @@ - 0 0 0 1.5707 0 0 + 0 0 1.21515 1.5707 0 0 - - meshes/pod_lowres.stl - + + 0.875 1.754 0.03 + + + + + + -0.42 0 1.18 1.5707 0 1.5707 + + + 0.875 1.754 0.03 + + + + + + 0.42 0.42 1.17 1.5707 0 0 + + + 0.03 2.3 0.03 + + + + + -0.42 0.42 1.17 1.5707 0 0 + + + 0.03 2.3 0.03 + + + + + -0.42 -0.42 1.17 1.5707 0 0 + + + 0.03 2.3 0.03 + + + + + 0.42 -0.42 1.17 1.5707 0 0 + + + 0.03 2.3 0.03 + + + + + 0.15 0 1.49 1.5707 0 1.5707 + + + 0.875 1.32 0.01 + + + + + -0.15 0 1.49 1.5707 0 1.5707 + + + 0.875 1.32 0.01 + + + + + 0 0 .57 1.5707 0 1.5707 + + + 0.875 0.45 0.01 + + + + + 0.42 0 1.18 1.5707 0 1.5707 + + + 0.875 1.754 0.03 + + + + + 0 0 1.06 0 0 0 + + + 0.905 0.856 0.028 + + + + + 0 0 1.06 0 0 0 + + + 0.905 0.856 0.028 + + + + + 0 0 0.80 0 0 0 + + + 0.905 0.856 0.028 + + + + + 0 0.43 0.81 0 0 0 + + + 0.856 0.018 0.028 + + + + + 0 -0.43 0.81 0 0 0 + + + 0.856 0.018 0.028 + + + + + 0 0.43 1.08 0 0 0 + + + 0.856 0.018 0.028 + + + + + 0 -0.43 1.08 0 0 0 + + + 0.856 0.018 0.028 + + + + + 0 0 0.37 0 0 0 + + + 0.905 0.856 0.028 + + + + + 0 0 1.29 0 0 0 + + + 0.905 0.856 0.028 + + + + + 0 0 1.53 0 0 0 + + + 0.905 0.856 0.028 + + + + + 0 0 1.78 0 0 0 + + + 0.905 0.856 0.028 + - - - - 0.8 - 0.8 - 0.0 0.0 0.0 - 1.0 - 1.0 - - - - diff --git a/data/lego/lego.obj b/data/lego/lego.obj new file mode 100644 index 000000000..7dd1a2c34 --- /dev/null +++ b/data/lego/lego.obj @@ -0,0 +1,3751 @@ +# Blender v2.68 (sub 0) OBJ File: '' +# www.blender.org +mtllib lego.mtl +o Duplo_Stein_2x2_V3 +v 0.000000 0.000000 -0.317500 +v 0.014976 0.000000 -0.302500 +v 0.000000 0.000000 0.000000 +v 0.317000 0.000000 -0.317500 +v 0.302024 0.000000 -0.302500 +v 0.302024 0.000000 -0.015000 +v 0.014976 0.000000 -0.015000 +v 0.317000 0.000000 0.000000 +v 0.317000 0.192500 0.000000 +v 0.000000 0.192500 0.000000 +v 0.000000 0.192500 -0.317500 +v 0.317000 0.192500 -0.317500 +v 0.023969 0.054000 -0.061626 +v 0.019677 -0.000000 -0.059351 +v 0.023969 -0.000000 -0.061626 +v 0.019677 0.054000 -0.059351 +v 0.032082 -0.000000 -0.083415 +v 0.032667 0.054000 -0.078500 +v 0.032667 -0.000000 -0.078500 +v 0.032082 0.054000 -0.083415 +v 0.030359 -0.000000 -0.088041 +v 0.030359 0.054000 -0.088041 +v 0.027601 -0.000000 -0.092107 +v 0.027601 0.054000 -0.092107 +v 0.023969 0.054000 -0.095374 +v 0.023969 -0.000000 -0.095374 +v 0.019677 0.054000 -0.097649 +v 0.019677 -0.000000 -0.097649 +v 0.030359 -0.000000 -0.068959 +v 0.027601 0.054000 -0.064893 +v 0.027601 -0.000000 -0.064893 +v 0.030359 0.054000 -0.068959 +v 0.032082 -0.000000 -0.073585 +v 0.032082 0.054000 -0.073585 +v 0.014976 -0.000000 -0.058200 +v 0.014976 0.054000 -0.058200 +v 0.014976 0.054000 -0.098800 +v 0.014976 -0.000000 -0.098800 +v 0.023969 0.054000 -0.221626 +v 0.019677 -0.000000 -0.219351 +v 0.023969 -0.000000 -0.221626 +v 0.019677 0.054000 -0.219351 +v 0.032082 -0.000000 -0.243415 +v 0.032667 0.054000 -0.238500 +v 0.032667 -0.000000 -0.238500 +v 0.032082 0.054000 -0.243415 +v 0.030359 -0.000000 -0.248041 +v 0.030359 0.054000 -0.248041 +v 0.027601 -0.000000 -0.252107 +v 0.027601 0.054000 -0.252107 +v 0.023969 0.054000 -0.255374 +v 0.023969 -0.000000 -0.255374 +v 0.019677 0.054000 -0.257649 +v 0.019677 -0.000000 -0.257649 +v 0.030359 -0.000000 -0.228959 +v 0.027601 0.054000 -0.224893 +v 0.027601 -0.000000 -0.224893 +v 0.030359 0.054000 -0.228959 +v 0.032082 -0.000000 -0.233585 +v 0.032082 0.054000 -0.233585 +v 0.014976 -0.000000 -0.218200 +v 0.014976 0.054000 -0.218200 +v 0.014976 0.054000 -0.258800 +v 0.014976 -0.000000 -0.258800 +v 0.204383 0.230000 -0.204205 +v 0.207773 0.230000 -0.230221 +v 0.206685 0.230000 -0.238500 +v 0.214412 0.230000 -0.196498 +v 0.210964 0.230000 -0.222506 +v 0.216040 0.230000 -0.215880 +v 0.226091 0.230000 -0.191653 +v 0.222654 0.230000 -0.210797 +v 0.230357 0.230000 -0.207601 +v 0.238624 0.230000 -0.190000 +v 0.238624 0.230000 -0.206511 +v 0.246890 0.230000 -0.207601 +v 0.251157 0.230000 -0.191653 +v 0.254593 0.230000 -0.210797 +v 0.262835 0.230000 -0.196498 +v 0.261208 0.230000 -0.215880 +v 0.266283 0.230000 -0.222506 +v 0.272864 0.230000 -0.204205 +v 0.269474 0.230000 -0.230221 +v 0.270562 0.230000 -0.238500 +v 0.191850 0.230000 -0.225947 +v 0.191850 0.230000 -0.251053 +v 0.190200 0.230000 -0.238500 +v 0.196688 0.230000 -0.214250 +v 0.196688 0.230000 -0.262750 +v 0.204383 0.230000 -0.272795 +v 0.207773 0.230000 -0.246779 +v 0.214412 0.230000 -0.280502 +v 0.210964 0.230000 -0.254494 +v 0.216040 0.230000 -0.261120 +v 0.226091 0.230000 -0.285347 +v 0.222654 0.230000 -0.266203 +v 0.230357 0.230000 -0.269399 +v 0.238624 0.230000 -0.287000 +v 0.238624 0.230000 -0.270489 +v 0.246890 0.230000 -0.269399 +v 0.251157 0.230000 -0.285347 +v 0.254593 0.230000 -0.266203 +v 0.262835 0.230000 -0.280502 +v 0.261208 0.230000 -0.261120 +v 0.266283 0.230000 -0.254494 +v 0.272864 0.230000 -0.272795 +v 0.269474 0.230000 -0.246779 +v 0.280560 0.230000 -0.214250 +v 0.280560 0.230000 -0.262750 +v 0.285397 0.230000 -0.225947 +v 0.285397 0.230000 -0.251053 +v 0.287047 0.230000 -0.238500 +v 0.287047 0.192500 -0.238500 +v 0.285397 0.192500 -0.225947 +v 0.238624 0.192500 -0.287000 +v 0.226091 0.192500 -0.285347 +v 0.251157 0.192500 -0.285347 +v 0.230357 0.192500 -0.269399 +v 0.238624 0.192500 -0.270489 +v 0.222654 0.192500 -0.266203 +v 0.216040 0.192500 -0.261120 +v 0.210964 0.192500 -0.254494 +v 0.207773 0.192500 -0.246779 +v 0.206685 0.192500 -0.238500 +v 0.238624 0.192500 -0.190000 +v 0.251157 0.192500 -0.191653 +v 0.207773 0.192500 -0.230221 +v 0.210964 0.192500 -0.222506 +v 0.216040 0.192500 -0.215880 +v 0.222654 0.192500 -0.210797 +v 0.230357 0.192500 -0.207601 +v 0.272864 0.192500 -0.272795 +v 0.280560 0.192500 -0.262750 +v 0.214412 0.192500 -0.196498 +v 0.226091 0.192500 -0.191653 +v 0.238624 0.192500 -0.206511 +v 0.262835 0.192500 -0.280502 +v 0.204383 0.192500 -0.204205 +v 0.191850 0.192500 -0.251053 +v 0.191850 0.192500 -0.225947 +v 0.190200 0.192500 -0.238500 +v 0.196688 0.192500 -0.262750 +v 0.196688 0.192500 -0.214250 +v 0.204383 0.192500 -0.272795 +v 0.214412 0.192500 -0.280502 +v 0.246890 0.192500 -0.269399 +v 0.254593 0.192500 -0.266203 +v 0.261208 0.192500 -0.261120 +v 0.266283 0.192500 -0.254494 +v 0.269474 0.192500 -0.246779 +v 0.270562 0.192500 -0.238500 +v 0.246890 0.192500 -0.207601 +v 0.254593 0.192500 -0.210797 +v 0.262835 0.192500 -0.196498 +v 0.261208 0.192500 -0.215880 +v 0.266283 0.192500 -0.222506 +v 0.272864 0.192500 -0.204205 +v 0.269474 0.192500 -0.230221 +v 0.280560 0.192500 -0.214250 +v 0.285397 0.192500 -0.251053 +v 0.044635 0.230000 -0.204205 +v 0.048025 0.230000 -0.230221 +v 0.046937 0.230000 -0.238500 +v 0.054664 0.230000 -0.196498 +v 0.051216 0.230000 -0.222506 +v 0.056292 0.230000 -0.215880 +v 0.066343 0.230000 -0.191653 +v 0.062906 0.230000 -0.210797 +v 0.070609 0.230000 -0.207601 +v 0.078876 0.230000 -0.190000 +v 0.078876 0.230000 -0.206511 +v 0.087142 0.230000 -0.207601 +v 0.091409 0.230000 -0.191653 +v 0.094845 0.230000 -0.210797 +v 0.103087 0.230000 -0.196498 +v 0.101459 0.230000 -0.215880 +v 0.106535 0.230000 -0.222506 +v 0.113116 0.230000 -0.204205 +v 0.109726 0.230000 -0.230221 +v 0.110814 0.230000 -0.238500 +v 0.032102 0.230000 -0.225947 +v 0.032102 0.230000 -0.251053 +v 0.030452 0.230000 -0.238500 +v 0.036940 0.230000 -0.214250 +v 0.036940 0.230000 -0.262750 +v 0.044635 0.230000 -0.272795 +v 0.048025 0.230000 -0.246779 +v 0.054664 0.230000 -0.280502 +v 0.051216 0.230000 -0.254494 +v 0.056292 0.230000 -0.261120 +v 0.066343 0.230000 -0.285347 +v 0.062906 0.230000 -0.266203 +v 0.070609 0.230000 -0.269399 +v 0.078876 0.230000 -0.287000 +v 0.078876 0.230000 -0.270489 +v 0.087142 0.230000 -0.269399 +v 0.091409 0.230000 -0.285347 +v 0.094845 0.230000 -0.266203 +v 0.103087 0.230000 -0.280502 +v 0.101459 0.230000 -0.261120 +v 0.106535 0.230000 -0.254494 +v 0.113116 0.230000 -0.272795 +v 0.109726 0.230000 -0.246779 +v 0.120812 0.230000 -0.214250 +v 0.120812 0.230000 -0.262750 +v 0.125649 0.230000 -0.225947 +v 0.125649 0.230000 -0.251053 +v 0.127299 0.230000 -0.238500 +v 0.127299 0.192500 -0.238500 +v 0.125649 0.192500 -0.225947 +v 0.078876 0.192500 -0.287000 +v 0.066343 0.192500 -0.285347 +v 0.091409 0.192500 -0.285347 +v 0.070609 0.192500 -0.269399 +v 0.078876 0.192500 -0.270489 +v 0.062906 0.192500 -0.266203 +v 0.056292 0.192500 -0.261120 +v 0.051216 0.192500 -0.254494 +v 0.048025 0.192500 -0.246779 +v 0.046937 0.192500 -0.238500 +v 0.078876 0.192500 -0.190000 +v 0.091409 0.192500 -0.191653 +v 0.048025 0.192500 -0.230221 +v 0.051216 0.192500 -0.222506 +v 0.056292 0.192500 -0.215880 +v 0.062906 0.192500 -0.210797 +v 0.070609 0.192500 -0.207601 +v 0.113116 0.192500 -0.272795 +v 0.120812 0.192500 -0.262750 +v 0.054664 0.192500 -0.196498 +v 0.066343 0.192500 -0.191653 +v 0.078876 0.192500 -0.206511 +v 0.103087 0.192500 -0.280502 +v 0.044635 0.192500 -0.204205 +v 0.032102 0.192500 -0.251053 +v 0.032102 0.192500 -0.225947 +v 0.030452 0.192500 -0.238500 +v 0.036940 0.192500 -0.262750 +v 0.036940 0.192500 -0.214250 +v 0.044635 0.192500 -0.272795 +v 0.054664 0.192500 -0.280502 +v 0.087142 0.192500 -0.269399 +v 0.094845 0.192500 -0.266203 +v 0.101459 0.192500 -0.261120 +v 0.106535 0.192500 -0.254494 +v 0.109726 0.192500 -0.246779 +v 0.110814 0.192500 -0.238500 +v 0.087142 0.192500 -0.207601 +v 0.094845 0.192500 -0.210797 +v 0.103087 0.192500 -0.196498 +v 0.101459 0.192500 -0.215880 +v 0.106535 0.192500 -0.222506 +v 0.113116 0.192500 -0.204205 +v 0.109726 0.192500 -0.230221 +v 0.120812 0.192500 -0.214250 +v 0.125649 0.192500 -0.251053 +v 0.204383 0.230000 -0.045205 +v 0.207773 0.230000 -0.071221 +v 0.206685 0.230000 -0.079500 +v 0.214412 0.230000 -0.037498 +v 0.210964 0.230000 -0.063506 +v 0.216040 0.230000 -0.056880 +v 0.226091 0.230000 -0.032653 +v 0.222654 0.230000 -0.051797 +v 0.230357 0.230000 -0.048601 +v 0.238624 0.230000 -0.031000 +v 0.238624 0.230000 -0.047511 +v 0.246890 0.230000 -0.048601 +v 0.251157 0.230000 -0.032653 +v 0.254593 0.230000 -0.051797 +v 0.262835 0.230000 -0.037498 +v 0.261208 0.230000 -0.056880 +v 0.266283 0.230000 -0.063506 +v 0.272864 0.230000 -0.045205 +v 0.269474 0.230000 -0.071221 +v 0.270562 0.230000 -0.079500 +v 0.191850 0.230000 -0.066947 +v 0.191850 0.230000 -0.092053 +v 0.190200 0.230000 -0.079500 +v 0.196688 0.230000 -0.055250 +v 0.196688 0.230000 -0.103750 +v 0.204383 0.230000 -0.113795 +v 0.207773 0.230000 -0.087779 +v 0.214412 0.230000 -0.121502 +v 0.210964 0.230000 -0.095494 +v 0.216040 0.230000 -0.102120 +v 0.226091 0.230000 -0.126347 +v 0.222654 0.230000 -0.107203 +v 0.230357 0.230000 -0.110399 +v 0.238624 0.230000 -0.128000 +v 0.238624 0.230000 -0.111489 +v 0.246890 0.230000 -0.110399 +v 0.251157 0.230000 -0.126347 +v 0.254593 0.230000 -0.107203 +v 0.262835 0.230000 -0.121502 +v 0.261208 0.230000 -0.102120 +v 0.266283 0.230000 -0.095494 +v 0.272864 0.230000 -0.113795 +v 0.269474 0.230000 -0.087779 +v 0.280560 0.230000 -0.055250 +v 0.280560 0.230000 -0.103750 +v 0.285397 0.230000 -0.066947 +v 0.285397 0.230000 -0.092053 +v 0.287047 0.230000 -0.079500 +v 0.287047 0.192500 -0.079500 +v 0.285397 0.192500 -0.066947 +v 0.238624 0.192500 -0.128000 +v 0.226091 0.192500 -0.126347 +v 0.251157 0.192500 -0.126347 +v 0.230357 0.192500 -0.110399 +v 0.238624 0.192500 -0.111489 +v 0.222654 0.192500 -0.107203 +v 0.216040 0.192500 -0.102120 +v 0.210964 0.192500 -0.095494 +v 0.207773 0.192500 -0.087779 +v 0.206685 0.192500 -0.079500 +v 0.238624 0.192500 -0.031000 +v 0.251157 0.192500 -0.032653 +v 0.207773 0.192500 -0.071221 +v 0.210964 0.192500 -0.063506 +v 0.216040 0.192500 -0.056880 +v 0.222654 0.192500 -0.051797 +v 0.230357 0.192500 -0.048601 +v 0.272864 0.192500 -0.113795 +v 0.280560 0.192500 -0.103750 +v 0.214412 0.192500 -0.037498 +v 0.226091 0.192500 -0.032653 +v 0.238624 0.192500 -0.047511 +v 0.262835 0.192500 -0.121502 +v 0.204383 0.192500 -0.045205 +v 0.191850 0.192500 -0.092053 +v 0.191850 0.192500 -0.066947 +v 0.190200 0.192500 -0.079500 +v 0.196688 0.192500 -0.103750 +v 0.196688 0.192500 -0.055250 +v 0.204383 0.192500 -0.113795 +v 0.214412 0.192500 -0.121502 +v 0.246890 0.192500 -0.110399 +v 0.254593 0.192500 -0.107203 +v 0.261208 0.192500 -0.102120 +v 0.266283 0.192500 -0.095494 +v 0.269474 0.192500 -0.087779 +v 0.270562 0.192500 -0.079500 +v 0.246890 0.192500 -0.048601 +v 0.254593 0.192500 -0.051797 +v 0.262835 0.192500 -0.037498 +v 0.261208 0.192500 -0.056880 +v 0.266283 0.192500 -0.063506 +v 0.272864 0.192500 -0.045205 +v 0.269474 0.192500 -0.071221 +v 0.280560 0.192500 -0.055250 +v 0.285397 0.192500 -0.092053 +v 0.044635 0.230000 -0.045205 +v 0.048025 0.230000 -0.071221 +v 0.046937 0.230000 -0.079500 +v 0.054664 0.230000 -0.037498 +v 0.051216 0.230000 -0.063506 +v 0.056292 0.230000 -0.056880 +v 0.066343 0.230000 -0.032653 +v 0.062906 0.230000 -0.051797 +v 0.070609 0.230000 -0.048601 +v 0.078876 0.230000 -0.031000 +v 0.078876 0.230000 -0.047511 +v 0.087142 0.230000 -0.048601 +v 0.091409 0.230000 -0.032653 +v 0.094845 0.230000 -0.051797 +v 0.103087 0.230000 -0.037498 +v 0.101459 0.230000 -0.056880 +v 0.106535 0.230000 -0.063506 +v 0.113116 0.230000 -0.045205 +v 0.109726 0.230000 -0.071221 +v 0.110814 0.230000 -0.079500 +v 0.032102 0.230000 -0.066947 +v 0.032102 0.230000 -0.092053 +v 0.030452 0.230000 -0.079500 +v 0.036940 0.230000 -0.055250 +v 0.036940 0.230000 -0.103750 +v 0.044635 0.230000 -0.113795 +v 0.048025 0.230000 -0.087779 +v 0.054664 0.230000 -0.121502 +v 0.051216 0.230000 -0.095494 +v 0.056292 0.230000 -0.102120 +v 0.066343 0.230000 -0.126347 +v 0.062906 0.230000 -0.107203 +v 0.070609 0.230000 -0.110399 +v 0.078876 0.230000 -0.128000 +v 0.078876 0.230000 -0.111489 +v 0.087142 0.230000 -0.110399 +v 0.091409 0.230000 -0.126347 +v 0.094845 0.230000 -0.107203 +v 0.103087 0.230000 -0.121502 +v 0.101459 0.230000 -0.102120 +v 0.106535 0.230000 -0.095494 +v 0.113116 0.230000 -0.113795 +v 0.109726 0.230000 -0.087779 +v 0.120812 0.230000 -0.055250 +v 0.120812 0.230000 -0.103750 +v 0.125649 0.230000 -0.066947 +v 0.125649 0.230000 -0.092053 +v 0.127299 0.230000 -0.079500 +v 0.127299 0.192500 -0.079500 +v 0.125649 0.192500 -0.066947 +v 0.078876 0.192500 -0.128000 +v 0.066343 0.192500 -0.126347 +v 0.091409 0.192500 -0.126347 +v 0.070609 0.192500 -0.110399 +v 0.078876 0.192500 -0.111489 +v 0.062906 0.192500 -0.107203 +v 0.056292 0.192500 -0.102120 +v 0.051216 0.192500 -0.095494 +v 0.048025 0.192500 -0.087779 +v 0.046937 0.192500 -0.079500 +v 0.078876 0.192500 -0.031000 +v 0.091409 0.192500 -0.032653 +v 0.048025 0.192500 -0.071221 +v 0.051216 0.192500 -0.063506 +v 0.056292 0.192500 -0.056880 +v 0.062906 0.192500 -0.051797 +v 0.070609 0.192500 -0.048601 +v 0.113116 0.192500 -0.113795 +v 0.120812 0.192500 -0.103750 +v 0.054664 0.192500 -0.037498 +v 0.066343 0.192500 -0.032653 +v 0.078876 0.192500 -0.047511 +v 0.103087 0.192500 -0.121502 +v 0.044635 0.192500 -0.045205 +v 0.032102 0.192500 -0.092053 +v 0.032102 0.192500 -0.066947 +v 0.030452 0.192500 -0.079500 +v 0.036940 0.192500 -0.103750 +v 0.036940 0.192500 -0.055250 +v 0.044635 0.192500 -0.113795 +v 0.054664 0.192500 -0.121502 +v 0.087142 0.192500 -0.110399 +v 0.094845 0.192500 -0.107203 +v 0.101459 0.192500 -0.102120 +v 0.106535 0.192500 -0.095494 +v 0.109726 0.192500 -0.087779 +v 0.110814 0.192500 -0.079500 +v 0.087142 0.192500 -0.048601 +v 0.094845 0.192500 -0.051797 +v 0.103087 0.192500 -0.037498 +v 0.101459 0.192500 -0.056880 +v 0.106535 0.192500 -0.063506 +v 0.113116 0.192500 -0.045205 +v 0.109726 0.192500 -0.071221 +v 0.120812 0.192500 -0.055250 +v 0.125649 0.192500 -0.092053 +v 0.209182 0.000000 -0.172168 +v 0.222082 0.000000 -0.169980 +v 0.209684 0.000000 -0.168346 +v 0.221365 0.000000 -0.175438 +v 0.203914 0.000000 -0.184905 +v 0.214837 0.000000 -0.191222 +v 0.195535 0.000000 -0.195843 +v 0.204453 0.000000 -0.204776 +v 0.184614 0.000000 -0.204236 +v 0.171897 0.000000 -0.209512 +v 0.190921 0.000000 -0.215176 +v 0.166890 0.000000 -0.210172 +v 0.168739 0.000000 -0.222561 +v 0.175162 0.000000 -0.221714 +v 0.210321 0.072000 -0.163500 +v 0.222934 0.072000 -0.153500 +v 0.210321 0.072000 -0.153500 +v 0.222934 0.072000 -0.163500 +v 0.307016 0.087500 -0.293358 +v 0.307016 0.177500 -0.307500 +v 0.307016 0.177500 -0.293358 +v 0.307016 0.087500 -0.307500 +v 0.292896 0.177500 -0.307500 +v 0.292896 0.087500 -0.307500 +v 0.306517 0.087500 -0.293858 +v 0.247919 0.164278 -0.255382 +v 0.260160 0.164278 -0.253499 +v 0.254979 0.164278 -0.248311 +v 0.253100 0.164278 -0.260570 +v 0.293346 0.095877 -0.300880 +v 0.296132 0.107500 -0.289528 +v 0.289072 0.107500 -0.296600 +v 0.300406 0.095877 -0.293809 +v 0.306517 0.177500 -0.293858 +v 0.293346 0.177500 -0.300880 +v 0.300406 0.177500 -0.293809 +v 0.205207 0.177500 -0.212602 +v 0.205171 0.064876 -0.212566 +v 0.196464 0.177500 -0.210916 +v 0.196464 0.056941 -0.210916 +v 0.204453 0.058366 -0.204776 +v 0.212231 0.064876 -0.205495 +v 0.210584 0.056941 -0.196774 +v 0.178763 -0.000000 -0.226654 +v 0.176796 0.037500 -0.224399 +v 0.178763 0.037500 -0.226654 +v 0.176796 -0.000000 -0.224399 +v 0.189269 0.037500 -0.228100 +v 0.187510 -0.000000 -0.229249 +v 0.187510 0.037500 -0.229249 +v 0.189269 -0.000000 -0.228100 +v 0.190630 0.037500 -0.226303 +v 0.190630 -0.000000 -0.226303 +v 0.191512 0.037500 -0.223964 +v 0.191512 -0.000000 -0.223964 +v 0.191862 -0.000000 -0.221220 +v 0.191862 0.037500 -0.221220 +v 0.191661 -0.000000 -0.218232 +v 0.191661 0.037500 -0.218232 +v 0.183223 0.037500 -0.229376 +v 0.180948 -0.000000 -0.228346 +v 0.180948 0.037500 -0.228346 +v 0.183223 -0.000000 -0.229376 +v 0.185454 0.037500 -0.229683 +v 0.185454 -0.000000 -0.229683 +v 0.175162 0.037500 -0.221714 +v 0.175162 -0.000000 -0.221714 +v 0.190921 -0.000000 -0.215176 +v 0.190921 0.037500 -0.215176 +v 0.221618 0.000000 -0.192474 +v 0.218279 0.037500 -0.192126 +v 0.221618 0.037500 -0.192474 +v 0.218279 0.000000 -0.192126 +v 0.230741 0.037500 -0.186335 +v 0.230356 0.000000 -0.188414 +v 0.230356 0.037500 -0.188414 +v 0.230741 0.000000 -0.186335 +v 0.230291 0.037500 -0.184042 +v 0.230291 0.000000 -0.184042 +v 0.229034 0.037500 -0.181668 +v 0.229034 0.000000 -0.181668 +v 0.227044 0.000000 -0.179355 +v 0.227044 0.037500 -0.179355 +v 0.224436 0.000000 -0.177236 +v 0.224436 0.037500 -0.177236 +v 0.227223 0.037500 -0.191460 +v 0.224659 0.000000 -0.192248 +v 0.224659 0.037500 -0.192248 +v 0.227223 0.000000 -0.191460 +v 0.229160 0.037500 -0.190157 +v 0.229160 0.000000 -0.190157 +v 0.214837 0.037500 -0.191222 +v 0.214837 0.000000 -0.191222 +v 0.221365 0.000000 -0.175438 +v 0.221365 0.037500 -0.175438 +v 0.222082 0.072000 -0.169980 +v 0.209684 0.072000 -0.168346 +v 0.163243 0.072000 -0.210653 +v 0.168739 0.072000 -0.222561 +v 0.166890 0.072000 -0.210172 +v 0.163243 0.072000 -0.223286 +v 0.172549 0.083750 -0.214469 +v 0.163894 0.177500 -0.215610 +v 0.163894 0.083750 -0.215610 +v 0.172549 0.177500 -0.214469 +v 0.186525 0.083750 -0.208855 +v 0.173808 0.177500 -0.214131 +v 0.173808 0.083750 -0.214131 +v 0.186525 0.177500 -0.208855 +v 0.198574 0.083750 -0.199810 +v 0.187653 0.177500 -0.208202 +v 0.187653 0.083750 -0.208202 +v 0.198574 0.177500 -0.199810 +v 0.199495 0.083750 -0.198887 +v 0.207875 0.177500 -0.187949 +v 0.199495 0.177500 -0.198887 +v 0.207875 0.083750 -0.187949 +v 0.208526 0.083750 -0.186819 +v 0.213794 0.177500 -0.174082 +v 0.208526 0.177500 -0.186819 +v 0.213794 0.083750 -0.174082 +v 0.214131 0.083750 -0.172821 +v 0.215270 0.177500 -0.164153 +v 0.214131 0.177500 -0.172821 +v 0.215270 0.083750 -0.164153 +v 0.215313 0.083750 -0.163500 +v 0.215313 0.177500 -0.153500 +v 0.215313 0.177500 -0.163500 +v 0.215313 0.083750 -0.153500 +v 0.210321 0.083750 -0.163500 +v 0.210321 0.083750 -0.153500 +v 0.209182 0.083750 -0.172168 +v 0.203914 0.083750 -0.184905 +v 0.195535 0.083750 -0.195843 +v 0.184614 0.083750 -0.204236 +v 0.171897 0.083750 -0.209512 +v 0.163243 0.083750 -0.210653 +v 0.163243 0.083750 -0.215610 +v 0.163243 0.177500 -0.215610 +v 0.213044 0.099358 -0.220451 +v 0.220104 0.099358 -0.213380 +v 0.225335 0.130500 -0.232761 +v 0.232395 0.130500 -0.225690 +v 0.236627 0.153845 -0.244072 +v 0.243687 0.153845 -0.237000 +v 0.281137 0.147843 -0.274509 +v 0.274077 0.147843 -0.281580 +v 0.271834 0.159131 -0.265192 +v 0.264774 0.159131 -0.272263 +v 0.222934 0.177500 -0.153500 +v 0.163243 0.177500 -0.223286 +v 0.175162 0.177500 -0.221714 +v 0.190921 0.177500 -0.215176 +v 0.221365 0.177500 -0.175438 +v 0.222934 0.177500 -0.163500 +v 0.214837 0.177500 -0.191222 +v 0.210809 0.177500 -0.196479 +v 0.210584 0.177500 -0.196774 +v 0.212231 0.177500 -0.205495 +v 0.144603 0.000000 -0.209512 +v 0.146788 0.000000 -0.222433 +v 0.148420 0.000000 -0.210015 +v 0.141339 0.000000 -0.221714 +v 0.131886 0.000000 -0.204236 +v 0.125580 0.000000 -0.215176 +v 0.120966 0.000000 -0.195843 +v 0.112047 0.000000 -0.204776 +v 0.112587 0.000000 -0.184905 +v 0.107319 0.000000 -0.172168 +v 0.101663 0.000000 -0.191222 +v 0.106660 0.000000 -0.167154 +v 0.094290 0.000000 -0.169005 +v 0.095136 0.000000 -0.175438 +v 0.153258 0.072000 -0.210653 +v 0.163243 0.072000 -0.223286 +v 0.163243 0.072000 -0.210653 +v 0.153258 0.072000 -0.223286 +v 0.023605 0.087500 -0.307500 +v 0.009485 0.177500 -0.307500 +v 0.023605 0.177500 -0.307500 +v 0.009485 0.087500 -0.307500 +v 0.009485 0.177500 -0.293358 +v 0.009485 0.087500 -0.293358 +v 0.023106 0.087500 -0.307000 +v 0.061521 0.164278 -0.248311 +v 0.063401 0.164278 -0.260570 +v 0.068581 0.164278 -0.255382 +v 0.056341 0.164278 -0.253499 +v 0.016095 0.095877 -0.293809 +v 0.027428 0.107500 -0.296600 +v 0.020368 0.107500 -0.289528 +v 0.023155 0.095877 -0.300880 +v 0.023106 0.177500 -0.307000 +v 0.016095 0.177500 -0.293809 +v 0.023155 0.177500 -0.300880 +v 0.104234 0.177500 -0.205531 +v 0.104269 0.064876 -0.205495 +v 0.105917 0.177500 -0.196774 +v 0.105917 0.056941 -0.196774 +v 0.112047 0.058366 -0.204776 +v 0.111329 0.064876 -0.212566 +v 0.120037 0.056941 -0.210916 +v 0.090203 -0.000000 -0.179045 +v 0.092455 0.037500 -0.177075 +v 0.090203 0.037500 -0.179045 +v 0.092455 -0.000000 -0.177075 +v 0.088760 0.037500 -0.189568 +v 0.087613 -0.000000 -0.187805 +v 0.087613 0.037500 -0.187805 +v 0.088760 -0.000000 -0.189568 +v 0.090554 0.037500 -0.190931 +v 0.090554 -0.000000 -0.190931 +v 0.092889 0.037500 -0.191814 +v 0.092889 -0.000000 -0.191814 +v 0.095629 -0.000000 -0.192165 +v 0.095629 0.037500 -0.192165 +v 0.098612 -0.000000 -0.191964 +v 0.098612 0.037500 -0.191964 +v 0.087486 0.037500 -0.183512 +v 0.088514 -0.000000 -0.181234 +v 0.088514 0.037500 -0.181234 +v 0.087486 -0.000000 -0.183512 +v 0.087179 0.037500 -0.185746 +v 0.087179 -0.000000 -0.185746 +v 0.095136 0.037500 -0.175438 +v 0.095136 -0.000000 -0.175438 +v 0.101663 -0.000000 -0.191222 +v 0.101663 0.037500 -0.191222 +v 0.124329 0.000000 -0.221967 +v 0.124678 0.037500 -0.218623 +v 0.124329 0.037500 -0.221967 +v 0.124678 0.000000 -0.218623 +v 0.130459 0.037500 -0.231105 +v 0.128383 0.000000 -0.230720 +v 0.128383 0.037500 -0.230720 +v 0.130459 0.000000 -0.231105 +v 0.132749 0.037500 -0.230655 +v 0.132749 0.000000 -0.230655 +v 0.135118 0.037500 -0.229396 +v 0.135118 0.000000 -0.229396 +v 0.137429 0.000000 -0.227402 +v 0.137429 0.037500 -0.227402 +v 0.139543 0.000000 -0.224790 +v 0.139543 0.037500 -0.224790 +v 0.125342 0.037500 -0.227582 +v 0.124555 0.000000 -0.225013 +v 0.124555 0.037500 -0.225013 +v 0.125342 0.000000 -0.227582 +v 0.126643 0.037500 -0.229522 +v 0.126643 0.000000 -0.229522 +v 0.125580 0.037500 -0.215176 +v 0.125580 0.000000 -0.215176 +v 0.141339 0.000000 -0.221714 +v 0.141339 0.037500 -0.221714 +v 0.146788 0.072000 -0.222433 +v 0.148420 0.072000 -0.210015 +v 0.106180 0.072000 -0.163500 +v 0.094290 0.072000 -0.169005 +v 0.106660 0.072000 -0.167154 +v 0.093567 0.072000 -0.163500 +v 0.102370 0.083750 -0.172821 +v 0.101230 0.177500 -0.164153 +v 0.101230 0.083750 -0.164153 +v 0.102370 0.177500 -0.172821 +v 0.107975 0.083750 -0.186819 +v 0.102707 0.177500 -0.174082 +v 0.102707 0.083750 -0.174082 +v 0.107975 0.177500 -0.186819 +v 0.117006 0.083750 -0.198887 +v 0.108626 0.177500 -0.187949 +v 0.108626 0.083750 -0.187949 +v 0.117006 0.177500 -0.198887 +v 0.117927 0.083750 -0.199810 +v 0.128847 0.177500 -0.208202 +v 0.117927 0.177500 -0.199810 +v 0.128847 0.083750 -0.208202 +v 0.129976 0.083750 -0.208855 +v 0.142693 0.177500 -0.214131 +v 0.129976 0.177500 -0.208855 +v 0.142693 0.083750 -0.214131 +v 0.143952 0.083750 -0.214469 +v 0.152607 0.177500 -0.215610 +v 0.143952 0.177500 -0.214469 +v 0.152607 0.083750 -0.215610 +v 0.153258 0.083750 -0.215653 +v 0.163243 0.177500 -0.215653 +v 0.153258 0.177500 -0.215653 +v 0.163243 0.083750 -0.215653 +v 0.153258 0.083750 -0.210653 +v 0.163243 0.083750 -0.210653 +v 0.144603 0.083750 -0.209512 +v 0.131886 0.083750 -0.204236 +v 0.120966 0.083750 -0.195843 +v 0.112587 0.083750 -0.184905 +v 0.107319 0.083750 -0.172168 +v 0.106180 0.083750 -0.163500 +v 0.101230 0.083750 -0.163500 +v 0.101230 0.177500 -0.163500 +v 0.096397 0.099358 -0.213380 +v 0.103457 0.099358 -0.220451 +v 0.084106 0.130500 -0.225690 +v 0.091166 0.130500 -0.232761 +v 0.072814 0.153845 -0.237000 +v 0.079874 0.153845 -0.244072 +v 0.042424 0.147843 -0.281580 +v 0.035364 0.147843 -0.274509 +v 0.051726 0.159131 -0.272263 +v 0.044667 0.159131 -0.265192 +v 0.163243 0.177500 -0.223286 +v 0.093567 0.177500 -0.163500 +v 0.095136 0.177500 -0.175438 +v 0.101663 0.177500 -0.191222 +v 0.141339 0.177500 -0.221714 +v 0.153258 0.177500 -0.223286 +v 0.125580 0.177500 -0.215176 +v 0.120331 0.177500 -0.211142 +v 0.120037 0.177500 -0.210916 +v 0.111329 0.177500 -0.212566 +v 0.107319 0.000000 -0.144832 +v 0.094419 0.000000 -0.147020 +v 0.106817 0.000000 -0.148654 +v 0.095136 0.000000 -0.141562 +v 0.112587 0.000000 -0.132094 +v 0.101663 0.000000 -0.125778 +v 0.120966 0.000000 -0.121157 +v 0.112047 0.000000 -0.112224 +v 0.131886 0.000000 -0.112764 +v 0.144603 0.000000 -0.107488 +v 0.125580 0.000000 -0.101824 +v 0.149610 0.000000 -0.106828 +v 0.147762 0.000000 -0.094439 +v 0.141339 0.000000 -0.095286 +v 0.106180 0.072000 -0.153500 +v 0.093567 0.072000 -0.163500 +v 0.106180 0.072000 -0.163500 +v 0.093567 0.072000 -0.153500 +v 0.009485 0.087500 -0.023642 +v 0.009485 0.177500 -0.009500 +v 0.009485 0.177500 -0.023642 +v 0.009485 0.087500 -0.009500 +v 0.023605 0.177500 -0.009500 +v 0.023605 0.087500 -0.009500 +v 0.009984 0.087500 -0.023142 +v 0.068581 0.164278 -0.061618 +v 0.056341 0.164278 -0.063501 +v 0.061521 0.164278 -0.068689 +v 0.063401 0.164278 -0.056430 +v 0.023155 0.095877 -0.016120 +v 0.020368 0.107500 -0.027472 +v 0.027428 0.107500 -0.020400 +v 0.016095 0.095877 -0.023191 +v 0.009984 0.177500 -0.023142 +v 0.023155 0.177500 -0.016120 +v 0.016095 0.177500 -0.023191 +v 0.111293 0.177500 -0.104398 +v 0.111329 0.064876 -0.104434 +v 0.120037 0.177500 -0.106084 +v 0.120037 0.056941 -0.106084 +v 0.112047 0.058366 -0.112224 +v 0.104269 0.064876 -0.111505 +v 0.105917 0.056941 -0.120226 +v 0.137738 -0.000000 -0.090346 +v 0.139705 0.037500 -0.092601 +v 0.137738 0.037500 -0.090346 +v 0.139705 -0.000000 -0.092601 +v 0.127231 0.037500 -0.088900 +v 0.128991 -0.000000 -0.087751 +v 0.128991 0.037500 -0.087751 +v 0.127231 -0.000000 -0.088900 +v 0.125871 0.037500 -0.090697 +v 0.125871 -0.000000 -0.090697 +v 0.124989 0.037500 -0.093036 +v 0.124989 -0.000000 -0.093036 +v 0.124639 -0.000000 -0.095780 +v 0.124639 0.037500 -0.095780 +v 0.124839 -0.000000 -0.098768 +v 0.124839 0.037500 -0.098768 +v 0.133278 0.037500 -0.087624 +v 0.135552 -0.000000 -0.088654 +v 0.135552 0.037500 -0.088654 +v 0.133278 -0.000000 -0.087624 +v 0.131047 0.037500 -0.087317 +v 0.131047 -0.000000 -0.087317 +v 0.141339 0.037500 -0.095286 +v 0.141339 -0.000000 -0.095286 +v 0.125580 -0.000000 -0.101824 +v 0.125580 0.037500 -0.101824 +v 0.094883 0.000000 -0.124526 +v 0.098222 0.037500 -0.124874 +v 0.094883 0.037500 -0.124526 +v 0.098222 0.000000 -0.124874 +v 0.085760 0.037500 -0.130665 +v 0.086145 0.000000 -0.128586 +v 0.086145 0.037500 -0.128586 +v 0.085760 0.000000 -0.130665 +v 0.086209 0.037500 -0.132958 +v 0.086209 0.000000 -0.132958 +v 0.087466 0.037500 -0.135332 +v 0.087466 0.000000 -0.135332 +v 0.089457 0.000000 -0.137645 +v 0.089457 0.037500 -0.137645 +v 0.092065 0.000000 -0.139764 +v 0.092065 0.037500 -0.139764 +v 0.089277 0.037500 -0.125540 +v 0.091842 0.000000 -0.124752 +v 0.091842 0.037500 -0.124752 +v 0.089277 0.000000 -0.125540 +v 0.087340 0.037500 -0.126843 +v 0.087340 0.000000 -0.126843 +v 0.101663 0.037500 -0.125778 +v 0.101663 0.000000 -0.125778 +v 0.095136 0.000000 -0.141562 +v 0.095136 0.037500 -0.141562 +v 0.094419 0.072000 -0.147020 +v 0.106817 0.072000 -0.148654 +v 0.153258 0.072000 -0.106347 +v 0.147762 0.072000 -0.094439 +v 0.149610 0.072000 -0.106828 +v 0.153258 0.072000 -0.093714 +v 0.143952 0.083750 -0.102531 +v 0.152607 0.177500 -0.101390 +v 0.152607 0.083750 -0.101390 +v 0.143952 0.177500 -0.102531 +v 0.129976 0.083750 -0.108145 +v 0.142693 0.177500 -0.102869 +v 0.142693 0.083750 -0.102869 +v 0.129976 0.177500 -0.108145 +v 0.117927 0.083750 -0.117190 +v 0.128847 0.177500 -0.108798 +v 0.128847 0.083750 -0.108798 +v 0.117927 0.177500 -0.117190 +v 0.117006 0.083750 -0.118113 +v 0.108626 0.177500 -0.129051 +v 0.117006 0.177500 -0.118113 +v 0.108626 0.083750 -0.129051 +v 0.107975 0.083750 -0.130181 +v 0.102707 0.177500 -0.142918 +v 0.107975 0.177500 -0.130181 +v 0.102707 0.083750 -0.142918 +v 0.102370 0.083750 -0.144179 +v 0.101230 0.177500 -0.152847 +v 0.102370 0.177500 -0.144179 +v 0.101230 0.083750 -0.152847 +v 0.101188 0.083750 -0.153500 +v 0.101188 0.177500 -0.163500 +v 0.101188 0.177500 -0.153500 +v 0.101188 0.083750 -0.163500 +v 0.106180 0.083750 -0.153500 +v 0.106180 0.083750 -0.163500 +v 0.107319 0.083750 -0.144832 +v 0.112587 0.083750 -0.132094 +v 0.120966 0.083750 -0.121157 +v 0.131886 0.083750 -0.112764 +v 0.144603 0.083750 -0.107488 +v 0.153258 0.083750 -0.106347 +v 0.153258 0.083750 -0.101390 +v 0.153258 0.177500 -0.101390 +v 0.103457 0.099358 -0.096549 +v 0.096397 0.099358 -0.103620 +v 0.091166 0.130500 -0.084238 +v 0.084106 0.130500 -0.091310 +v 0.079874 0.153845 -0.072928 +v 0.072814 0.153845 -0.080000 +v 0.035364 0.147843 -0.042491 +v 0.042424 0.147843 -0.035420 +v 0.044667 0.159131 -0.051808 +v 0.051726 0.159131 -0.044737 +v 0.093567 0.177500 -0.163500 +v 0.153258 0.177500 -0.093714 +v 0.141339 0.177500 -0.095286 +v 0.125580 0.177500 -0.101824 +v 0.095136 0.177500 -0.141562 +v 0.093567 0.177500 -0.153500 +v 0.101663 0.177500 -0.125778 +v 0.105691 0.177500 -0.120521 +v 0.105917 0.177500 -0.120226 +v 0.104269 0.177500 -0.111505 +v 0.171897 0.000000 -0.107488 +v 0.169713 0.000000 -0.094567 +v 0.168081 0.000000 -0.106985 +v 0.175162 0.000000 -0.095286 +v 0.184614 0.000000 -0.112764 +v 0.190921 0.000000 -0.101824 +v 0.195535 0.000000 -0.121157 +v 0.204453 0.000000 -0.112224 +v 0.203914 0.000000 -0.132094 +v 0.209182 0.000000 -0.144832 +v 0.214837 0.000000 -0.125778 +v 0.209841 0.000000 -0.149846 +v 0.222210 0.000000 -0.147995 +v 0.221365 0.000000 -0.141562 +v 0.163243 0.072000 -0.106347 +v 0.153258 0.072000 -0.093714 +v 0.153258 0.072000 -0.106347 +v 0.163243 0.072000 -0.093714 +v 0.292896 0.087500 -0.009500 +v 0.307016 0.177500 -0.009500 +v 0.292896 0.177500 -0.009500 +v 0.307016 0.087500 -0.009500 +v 0.307016 0.177500 -0.023642 +v 0.307016 0.087500 -0.023642 +v 0.293395 0.087500 -0.010000 +v 0.254979 0.164278 -0.068689 +v 0.253100 0.164278 -0.056430 +v 0.247919 0.164278 -0.061618 +v 0.260160 0.164278 -0.063501 +v 0.300406 0.095877 -0.023191 +v 0.289072 0.107500 -0.020400 +v 0.296132 0.107500 -0.027472 +v 0.293346 0.095877 -0.016120 +v 0.293395 0.177500 -0.010000 +v 0.300406 0.177500 -0.023191 +v 0.293346 0.177500 -0.016120 +v 0.212267 0.177500 -0.111469 +v 0.212231 0.064876 -0.111505 +v 0.210584 0.177500 -0.120226 +v 0.210584 0.056941 -0.120226 +v 0.204453 0.058366 -0.112224 +v 0.205171 0.064876 -0.104434 +v 0.196464 0.056941 -0.106084 +v 0.226297 -0.000000 -0.137955 +v 0.224046 0.037500 -0.139925 +v 0.226297 0.037500 -0.137955 +v 0.224046 -0.000000 -0.139925 +v 0.227741 0.037500 -0.127432 +v 0.228888 -0.000000 -0.129195 +v 0.228888 0.037500 -0.129195 +v 0.227741 -0.000000 -0.127432 +v 0.225947 0.037500 -0.126069 +v 0.225947 -0.000000 -0.126069 +v 0.223611 0.037500 -0.125186 +v 0.223611 -0.000000 -0.125186 +v 0.220872 -0.000000 -0.124835 +v 0.220872 0.037500 -0.124835 +v 0.217889 -0.000000 -0.125036 +v 0.217889 0.037500 -0.125036 +v 0.229015 0.037500 -0.133488 +v 0.227987 -0.000000 -0.135766 +v 0.227987 0.037500 -0.135766 +v 0.229015 -0.000000 -0.133488 +v 0.229321 0.037500 -0.131254 +v 0.229321 -0.000000 -0.131254 +v 0.221365 0.037500 -0.141562 +v 0.221365 -0.000000 -0.141562 +v 0.214837 -0.000000 -0.125778 +v 0.214837 0.037500 -0.125778 +v 0.192171 0.000000 -0.095033 +v 0.191823 0.037500 -0.098377 +v 0.192171 0.037500 -0.095033 +v 0.191823 0.000000 -0.098377 +v 0.186042 0.037500 -0.085895 +v 0.188118 0.000000 -0.086280 +v 0.188118 0.037500 -0.086280 +v 0.186042 0.000000 -0.085895 +v 0.183752 0.037500 -0.086345 +v 0.183752 0.000000 -0.086345 +v 0.181382 0.037500 -0.087604 +v 0.181382 0.000000 -0.087604 +v 0.179072 0.000000 -0.089598 +v 0.179072 0.037500 -0.089598 +v 0.176957 0.000000 -0.092210 +v 0.176957 0.037500 -0.092210 +v 0.191159 0.037500 -0.089418 +v 0.191945 0.000000 -0.091987 +v 0.191945 0.037500 -0.091987 +v 0.191159 0.000000 -0.089418 +v 0.189857 0.037500 -0.087478 +v 0.189857 0.000000 -0.087478 +v 0.190921 0.037500 -0.101824 +v 0.190921 0.000000 -0.101824 +v 0.175162 0.000000 -0.095286 +v 0.175162 0.037500 -0.095286 +v 0.169713 0.072000 -0.094567 +v 0.168081 0.072000 -0.106985 +v 0.210321 0.072000 -0.153500 +v 0.222210 0.072000 -0.147995 +v 0.209841 0.072000 -0.149846 +v 0.222934 0.072000 -0.153500 +v 0.214131 0.083750 -0.144179 +v 0.215270 0.177500 -0.152847 +v 0.215270 0.083750 -0.152847 +v 0.214131 0.177500 -0.144179 +v 0.208526 0.083750 -0.130181 +v 0.213794 0.177500 -0.142918 +v 0.213794 0.083750 -0.142918 +v 0.208526 0.177500 -0.130181 +v 0.199495 0.083750 -0.118113 +v 0.207875 0.177500 -0.129051 +v 0.207875 0.083750 -0.129051 +v 0.199495 0.177500 -0.118113 +v 0.198574 0.083750 -0.117190 +v 0.187653 0.177500 -0.108798 +v 0.198574 0.177500 -0.117190 +v 0.187653 0.083750 -0.108798 +v 0.186525 0.083750 -0.108145 +v 0.173808 0.177500 -0.102869 +v 0.186525 0.177500 -0.108145 +v 0.173808 0.083750 -0.102869 +v 0.172549 0.083750 -0.102531 +v 0.163894 0.177500 -0.101390 +v 0.172549 0.177500 -0.102531 +v 0.163894 0.083750 -0.101390 +v 0.163243 0.083750 -0.101347 +v 0.153258 0.177500 -0.101347 +v 0.163243 0.177500 -0.101347 +v 0.153258 0.083750 -0.101347 +v 0.163243 0.083750 -0.106347 +v 0.153258 0.083750 -0.106347 +v 0.171897 0.083750 -0.107488 +v 0.184614 0.083750 -0.112764 +v 0.195535 0.083750 -0.121157 +v 0.203914 0.083750 -0.132094 +v 0.209182 0.083750 -0.144832 +v 0.210321 0.083750 -0.153500 +v 0.215270 0.083750 -0.153500 +v 0.215270 0.177500 -0.153500 +v 0.220104 0.099358 -0.103620 +v 0.213044 0.099358 -0.096549 +v 0.232395 0.130500 -0.091310 +v 0.225335 0.130500 -0.084238 +v 0.243687 0.153845 -0.080000 +v 0.236627 0.153845 -0.072928 +v 0.274077 0.147843 -0.035420 +v 0.281137 0.147843 -0.042491 +v 0.264774 0.159131 -0.044737 +v 0.271834 0.159131 -0.051808 +v 0.153258 0.177500 -0.093714 +v 0.222934 0.177500 -0.153500 +v 0.221365 0.177500 -0.141562 +v 0.214837 0.177500 -0.125778 +v 0.175162 0.177500 -0.095286 +v 0.163243 0.177500 -0.093714 +v 0.190921 0.177500 -0.101824 +v 0.196170 0.177500 -0.105858 +v 0.196464 0.177500 -0.106084 +v 0.205171 0.177500 -0.104434 +v 0.095723 0.054000 -0.024007 +v 0.097995 -0.000000 -0.019708 +v 0.095723 -0.000000 -0.024007 +v 0.097995 0.054000 -0.019708 +v 0.073968 -0.000000 -0.032132 +v 0.078876 0.054000 -0.032719 +v 0.078876 -0.000000 -0.032719 +v 0.073968 0.054000 -0.032132 +v 0.069349 -0.000000 -0.030407 +v 0.069349 0.054000 -0.030407 +v 0.065290 -0.000000 -0.027644 +v 0.065290 0.054000 -0.027644 +v 0.062029 0.054000 -0.024007 +v 0.062029 -0.000000 -0.024007 +v 0.059757 0.054000 -0.019708 +v 0.059757 -0.000000 -0.019708 +v 0.088402 -0.000000 -0.030407 +v 0.092461 0.054000 -0.027644 +v 0.092461 -0.000000 -0.027644 +v 0.088402 0.054000 -0.030407 +v 0.083783 -0.000000 -0.032132 +v 0.083783 0.054000 -0.032132 +v 0.099144 -0.000000 -0.015000 +v 0.099144 0.054000 -0.015000 +v 0.058608 0.054000 -0.015000 +v 0.058608 -0.000000 -0.015000 +v 0.062029 0.054000 -0.293493 +v 0.059757 -0.000000 -0.297792 +v 0.062029 -0.000000 -0.293493 +v 0.059757 0.054000 -0.297792 +v 0.083783 -0.000000 -0.285368 +v 0.078876 0.054000 -0.284781 +v 0.078876 -0.000000 -0.284781 +v 0.083783 0.054000 -0.285368 +v 0.088402 -0.000000 -0.287093 +v 0.088402 0.054000 -0.287093 +v 0.092461 -0.000000 -0.289856 +v 0.092461 0.054000 -0.289856 +v 0.095723 0.054000 -0.293493 +v 0.095723 -0.000000 -0.293493 +v 0.097995 0.054000 -0.297792 +v 0.097995 -0.000000 -0.297792 +v 0.069349 -0.000000 -0.287093 +v 0.065290 0.054000 -0.289856 +v 0.065290 -0.000000 -0.289856 +v 0.069349 0.054000 -0.287093 +v 0.073968 -0.000000 -0.285368 +v 0.073968 0.054000 -0.285368 +v 0.058608 -0.000000 -0.302500 +v 0.058608 0.054000 -0.302500 +v 0.099144 0.054000 -0.302500 +v 0.099144 -0.000000 -0.302500 +v 0.255471 0.054000 -0.024007 +v 0.257743 -0.000000 -0.019708 +v 0.255471 -0.000000 -0.024007 +v 0.257743 0.054000 -0.019708 +v 0.233716 -0.000000 -0.032132 +v 0.238624 0.054000 -0.032719 +v 0.238624 -0.000000 -0.032719 +v 0.233716 0.054000 -0.032132 +v 0.229097 -0.000000 -0.030407 +v 0.229097 0.054000 -0.030407 +v 0.225038 -0.000000 -0.027644 +v 0.225038 0.054000 -0.027644 +v 0.221777 0.054000 -0.024007 +v 0.221777 -0.000000 -0.024007 +v 0.219505 0.054000 -0.019708 +v 0.219505 -0.000000 -0.019708 +v 0.248150 -0.000000 -0.030407 +v 0.252209 0.054000 -0.027644 +v 0.252209 -0.000000 -0.027644 +v 0.248150 0.054000 -0.030407 +v 0.243531 -0.000000 -0.032132 +v 0.243531 0.054000 -0.032132 +v 0.258892 -0.000000 -0.015000 +v 0.258892 0.054000 -0.015000 +v 0.218356 0.054000 -0.015000 +v 0.218356 -0.000000 -0.015000 +v 0.221777 0.054000 -0.293493 +v 0.219505 -0.000000 -0.297792 +v 0.221777 -0.000000 -0.293493 +v 0.219505 0.054000 -0.297792 +v 0.243531 -0.000000 -0.285368 +v 0.238624 0.054000 -0.284781 +v 0.238624 -0.000000 -0.284781 +v 0.243531 0.054000 -0.285368 +v 0.248150 -0.000000 -0.287093 +v 0.248150 0.054000 -0.287093 +v 0.252209 -0.000000 -0.289856 +v 0.252209 0.054000 -0.289856 +v 0.255471 0.054000 -0.293493 +v 0.255471 -0.000000 -0.293493 +v 0.257743 0.054000 -0.297792 +v 0.257743 -0.000000 -0.297792 +v 0.229097 -0.000000 -0.287093 +v 0.225038 0.054000 -0.289856 +v 0.225038 -0.000000 -0.289856 +v 0.229097 0.054000 -0.287093 +v 0.233716 -0.000000 -0.285368 +v 0.233716 0.054000 -0.285368 +v 0.218356 -0.000000 -0.302500 +v 0.218356 0.054000 -0.302500 +v 0.258892 0.054000 -0.302500 +v 0.258892 -0.000000 -0.302500 +v 0.293031 0.054000 -0.095374 +v 0.297323 -0.000000 -0.097649 +v 0.293031 -0.000000 -0.095374 +v 0.297323 0.054000 -0.097649 +v 0.284918 -0.000000 -0.073585 +v 0.284333 0.054000 -0.078500 +v 0.284333 -0.000000 -0.078500 +v 0.284918 0.054000 -0.073585 +v 0.286641 -0.000000 -0.068959 +v 0.286641 0.054000 -0.068959 +v 0.289399 -0.000000 -0.064893 +v 0.289399 0.054000 -0.064893 +v 0.293031 0.054000 -0.061626 +v 0.293031 -0.000000 -0.061626 +v 0.297323 0.054000 -0.059351 +v 0.297323 -0.000000 -0.059351 +v 0.286641 -0.000000 -0.088041 +v 0.289399 0.054000 -0.092107 +v 0.289399 -0.000000 -0.092107 +v 0.286641 0.054000 -0.088041 +v 0.284918 -0.000000 -0.083415 +v 0.284918 0.054000 -0.083415 +v 0.302024 -0.000000 -0.098800 +v 0.302024 0.054000 -0.098800 +v 0.302024 0.054000 -0.058200 +v 0.302024 -0.000000 -0.058200 +v 0.293031 0.054000 -0.255374 +v 0.297323 -0.000000 -0.257649 +v 0.293031 -0.000000 -0.255374 +v 0.297323 0.054000 -0.257649 +v 0.284918 -0.000000 -0.233585 +v 0.284333 0.054000 -0.238500 +v 0.284333 -0.000000 -0.238500 +v 0.284918 0.054000 -0.233585 +v 0.286641 -0.000000 -0.228959 +v 0.286641 0.054000 -0.228959 +v 0.289399 -0.000000 -0.224893 +v 0.289399 0.054000 -0.224893 +v 0.293031 0.054000 -0.221626 +v 0.293031 -0.000000 -0.221626 +v 0.297323 0.054000 -0.219351 +v 0.297323 -0.000000 -0.219351 +v 0.286641 -0.000000 -0.248041 +v 0.289399 0.054000 -0.252107 +v 0.289399 -0.000000 -0.252107 +v 0.286641 0.054000 -0.248041 +v 0.284918 -0.000000 -0.243415 +v 0.284918 0.054000 -0.243415 +v 0.302024 -0.000000 -0.258800 +v 0.302024 0.054000 -0.258800 +v 0.302024 0.054000 -0.218200 +v 0.302024 -0.000000 -0.218200 +v 0.009984 0.177500 -0.010000 +v 0.009984 0.177500 -0.307500 +v 0.307016 0.177500 -0.010000 +v 0.307016 0.177500 -0.023642 +v 0.302024 0.057500 -0.302500 +v 0.302024 0.057500 -0.015000 +v 0.307016 0.057500 -0.010000 +v 0.307016 0.057500 -0.307500 +v 0.302024 0.057500 -0.010000 +v 0.302024 0.057500 -0.307500 +v 0.014976 0.057500 -0.015000 +v 0.009984 0.057500 -0.010000 +v 0.009984 0.057500 -0.015000 +v 0.014976 0.057500 -0.302500 +v 0.009984 0.057500 -0.307500 +v 0.014976 0.057500 -0.307500 +v 0.300406 0.177500 -0.023191 +v 0.307016 0.159664 -0.023642 +v 0.289044 0.177500 -0.023642 +v 0.307016 0.147500 -0.023642 +v 0.307016 0.177500 -0.023642 +v 0.307016 0.147500 -0.302500 +v 0.307016 0.177500 -0.302500 +v 0.289044 0.177500 -0.302500 +v 0.293065 0.177500 -0.289761 +v 0.293065 0.147500 -0.307761 +v 0.293065 0.177500 -0.307761 +v 0.014646 0.147500 -0.307761 +v 0.014646 0.177500 -0.307761 +v 0.014646 0.177500 -0.289761 +v 0.027956 0.177500 -0.293392 +v 0.009984 0.147500 -0.293392 +v 0.009984 0.177500 -0.293392 +v 0.009984 0.147500 -0.014534 +v 0.009984 0.177500 -0.014534 +v 0.027956 0.177500 -0.014534 +v 0.023571 0.177500 -0.028000 +v 0.023571 0.147500 -0.010000 +v 0.023571 0.177500 -0.010000 +v 0.301990 0.147500 -0.010000 +v 0.301990 0.177500 -0.010000 +v 0.301990 0.177500 -0.028000 +usemtl None +s off +f 1 2 3 +f 2 1 4 +f 2 4 5 +f 5 4 6 +f 3 7 8 +f 7 3 2 +f 8 7 6 +f 8 6 4 +f 9 3 8 +f 3 9 10 +f 11 4 1 +f 4 11 12 +f 11 3 10 +f 3 11 1 +f 4 9 8 +f 9 4 12 +f 13 14 15 +f 14 13 16 +f 17 18 19 +f 18 17 20 +f 21 20 17 +f 20 21 22 +f 23 22 21 +f 22 23 24 +f 25 23 26 +f 23 25 24 +f 27 26 28 +f 26 27 25 +f 29 30 31 +f 30 29 32 +f 30 15 31 +f 15 30 13 +f 33 32 29 +f 32 33 34 +f 19 34 33 +f 34 19 18 +f 16 35 14 +f 35 16 36 +f 37 28 38 +f 28 37 27 +f 28 35 38 +f 35 28 14 +f 14 28 26 +f 14 26 15 +f 15 26 23 +f 15 23 31 +f 31 23 29 +f 29 23 21 +f 29 21 17 +f 29 17 33 +f 33 17 19 +f 37 35 36 +f 35 37 38 +f 36 27 37 +f 27 36 16 +f 27 16 25 +f 25 16 13 +f 25 13 30 +f 25 30 24 +f 24 30 32 +f 24 32 22 +f 22 32 34 +f 22 34 20 +f 20 34 18 +f 39 40 41 +f 40 39 42 +f 43 44 45 +f 44 43 46 +f 47 46 43 +f 46 47 48 +f 49 48 47 +f 48 49 50 +f 51 49 52 +f 49 51 50 +f 53 52 54 +f 52 53 51 +f 55 56 57 +f 56 55 58 +f 56 41 57 +f 41 56 39 +f 59 58 55 +f 58 59 60 +f 45 60 59 +f 60 45 44 +f 42 61 40 +f 61 42 62 +f 63 54 64 +f 54 63 53 +f 54 61 64 +f 61 54 40 +f 40 54 52 +f 40 52 41 +f 41 52 49 +f 41 49 57 +f 57 49 55 +f 55 49 47 +f 55 47 43 +f 55 43 59 +f 59 43 45 +f 63 61 62 +f 61 63 64 +f 62 53 63 +f 53 62 42 +f 53 42 51 +f 51 42 39 +f 51 39 56 +f 51 56 50 +f 50 56 58 +f 50 58 48 +f 48 58 60 +f 48 60 46 +f 46 60 44 +f 65 66 67 +f 66 65 68 +f 66 68 69 +f 69 68 70 +f 70 68 71 +f 70 71 72 +f 72 71 73 +f 73 71 74 +f 73 74 75 +f 75 74 76 +f 76 74 77 +f 76 77 78 +f 78 77 79 +f 78 79 80 +f 80 79 81 +f 81 79 82 +f 81 82 83 +f 83 82 84 +f 85 86 87 +f 86 85 88 +f 86 88 89 +f 89 88 90 +f 90 88 65 +f 90 65 67 +f 90 67 91 +f 90 91 92 +f 92 91 93 +f 92 93 94 +f 92 94 95 +f 95 94 96 +f 95 96 97 +f 95 97 98 +f 98 97 99 +f 98 99 100 +f 98 100 101 +f 101 100 102 +f 101 102 103 +f 103 102 104 +f 103 104 105 +f 103 105 106 +f 106 105 107 +f 106 107 84 +f 106 84 82 +f 106 82 108 +f 106 108 109 +f 109 108 110 +f 109 110 111 +f 111 110 112 +f 113 110 114 +f 110 113 112 +f 95 115 116 +f 115 95 98 +f 98 117 115 +f 117 98 101 +f 99 118 119 +f 118 99 97 +f 97 120 118 +f 120 97 96 +f 96 121 120 +f 121 96 94 +f 121 93 122 +f 93 121 94 +f 122 91 123 +f 91 122 93 +f 123 67 124 +f 67 123 91 +f 77 125 126 +f 125 77 74 +f 124 66 127 +f 66 124 67 +f 127 69 128 +f 69 127 66 +f 128 70 129 +f 70 128 69 +f 70 130 129 +f 130 70 72 +f 72 131 130 +f 131 72 73 +f 132 109 133 +f 109 132 106 +f 71 134 135 +f 134 71 68 +f 73 136 131 +f 136 73 75 +f 101 137 117 +f 137 101 103 +f 68 138 134 +f 138 68 65 +f 139 140 141 +f 140 139 142 +f 140 142 143 +f 143 142 144 +f 143 144 138 +f 138 144 124 +f 124 144 123 +f 123 144 145 +f 123 145 122 +f 122 145 121 +f 121 145 116 +f 121 116 120 +f 120 116 118 +f 118 116 115 +f 118 115 119 +f 119 115 117 +f 119 117 146 +f 146 117 147 +f 147 117 137 +f 147 137 148 +f 148 137 149 +f 149 137 132 +f 149 132 150 +f 150 132 151 +f 138 127 134 +f 127 138 124 +f 134 127 128 +f 134 128 129 +f 134 129 135 +f 135 129 130 +f 135 130 131 +f 135 131 125 +f 125 131 136 +f 125 136 152 +f 125 152 126 +f 126 152 153 +f 126 153 154 +f 154 153 155 +f 154 155 156 +f 154 156 157 +f 157 156 158 +f 157 158 151 +f 157 151 132 +f 157 132 133 +f 157 133 159 +f 159 133 160 +f 159 160 114 +f 114 160 113 +f 114 108 159 +f 108 114 110 +f 159 82 157 +f 82 159 108 +f 74 135 125 +f 135 74 71 +f 85 143 88 +f 143 85 140 +f 90 142 89 +f 142 90 144 +f 90 145 144 +f 145 90 92 +f 92 116 145 +f 116 92 95 +f 103 132 137 +f 132 103 106 +f 133 111 160 +f 111 133 109 +f 82 154 157 +f 154 82 79 +f 160 112 113 +f 112 160 111 +f 79 126 154 +f 126 79 77 +f 88 138 65 +f 138 88 143 +f 87 140 85 +f 140 87 141 +f 86 141 87 +f 141 86 139 +f 89 139 86 +f 139 89 142 +f 75 152 136 +f 152 75 76 +f 84 158 83 +f 158 84 151 +f 105 150 107 +f 150 105 149 +f 83 156 81 +f 156 83 158 +f 104 149 105 +f 149 104 148 +f 81 155 80 +f 155 81 156 +f 100 119 146 +f 119 100 99 +f 78 155 153 +f 155 78 80 +f 107 151 84 +f 151 107 150 +f 102 146 147 +f 146 102 100 +f 104 147 148 +f 147 104 102 +f 76 153 152 +f 153 76 78 +f 161 162 163 +f 162 161 164 +f 162 164 165 +f 165 164 166 +f 166 164 167 +f 166 167 168 +f 168 167 169 +f 169 167 170 +f 169 170 171 +f 171 170 172 +f 172 170 173 +f 172 173 174 +f 174 173 175 +f 174 175 176 +f 176 175 177 +f 177 175 178 +f 177 178 179 +f 179 178 180 +f 181 182 183 +f 182 181 184 +f 182 184 185 +f 185 184 186 +f 186 184 161 +f 186 161 163 +f 186 163 187 +f 186 187 188 +f 188 187 189 +f 188 189 190 +f 188 190 191 +f 191 190 192 +f 191 192 193 +f 191 193 194 +f 194 193 195 +f 194 195 196 +f 194 196 197 +f 197 196 198 +f 197 198 199 +f 199 198 200 +f 199 200 201 +f 199 201 202 +f 202 201 203 +f 202 203 180 +f 202 180 178 +f 202 178 204 +f 202 204 205 +f 205 204 206 +f 205 206 207 +f 207 206 208 +f 209 206 210 +f 206 209 208 +f 191 211 212 +f 211 191 194 +f 194 213 211 +f 213 194 197 +f 195 214 215 +f 214 195 193 +f 193 216 214 +f 216 193 192 +f 192 217 216 +f 217 192 190 +f 217 189 218 +f 189 217 190 +f 218 187 219 +f 187 218 189 +f 219 163 220 +f 163 219 187 +f 173 221 222 +f 221 173 170 +f 220 162 223 +f 162 220 163 +f 223 165 224 +f 165 223 162 +f 224 166 225 +f 166 224 165 +f 166 226 225 +f 226 166 168 +f 168 227 226 +f 227 168 169 +f 228 205 229 +f 205 228 202 +f 167 230 231 +f 230 167 164 +f 169 232 227 +f 232 169 171 +f 197 233 213 +f 233 197 199 +f 164 234 230 +f 234 164 161 +f 235 236 237 +f 236 235 238 +f 236 238 239 +f 239 238 240 +f 239 240 234 +f 234 240 220 +f 220 240 219 +f 219 240 241 +f 219 241 218 +f 218 241 217 +f 217 241 212 +f 217 212 216 +f 216 212 214 +f 214 212 211 +f 214 211 215 +f 215 211 213 +f 215 213 242 +f 242 213 243 +f 243 213 233 +f 243 233 244 +f 244 233 245 +f 245 233 228 +f 245 228 246 +f 246 228 247 +f 234 223 230 +f 223 234 220 +f 230 223 224 +f 230 224 225 +f 230 225 231 +f 231 225 226 +f 231 226 227 +f 231 227 221 +f 221 227 232 +f 221 232 248 +f 221 248 222 +f 222 248 249 +f 222 249 250 +f 250 249 251 +f 250 251 252 +f 250 252 253 +f 253 252 254 +f 253 254 247 +f 253 247 228 +f 253 228 229 +f 253 229 255 +f 255 229 256 +f 255 256 210 +f 210 256 209 +f 210 204 255 +f 204 210 206 +f 255 178 253 +f 178 255 204 +f 170 231 221 +f 231 170 167 +f 181 239 184 +f 239 181 236 +f 186 238 185 +f 238 186 240 +f 186 241 240 +f 241 186 188 +f 188 212 241 +f 212 188 191 +f 199 228 233 +f 228 199 202 +f 229 207 256 +f 207 229 205 +f 178 250 253 +f 250 178 175 +f 256 208 209 +f 208 256 207 +f 175 222 250 +f 222 175 173 +f 184 234 161 +f 234 184 239 +f 183 236 181 +f 236 183 237 +f 182 237 183 +f 237 182 235 +f 185 235 182 +f 235 185 238 +f 171 248 232 +f 248 171 172 +f 180 254 179 +f 254 180 247 +f 201 246 203 +f 246 201 245 +f 179 252 177 +f 252 179 254 +f 200 245 201 +f 245 200 244 +f 177 251 176 +f 251 177 252 +f 196 215 242 +f 215 196 195 +f 174 251 249 +f 251 174 176 +f 203 247 180 +f 247 203 246 +f 198 242 243 +f 242 198 196 +f 200 243 244 +f 243 200 198 +f 172 249 248 +f 249 172 174 +f 257 258 259 +f 258 257 260 +f 258 260 261 +f 261 260 262 +f 262 260 263 +f 262 263 264 +f 264 263 265 +f 265 263 266 +f 265 266 267 +f 267 266 268 +f 268 266 269 +f 268 269 270 +f 270 269 271 +f 270 271 272 +f 272 271 273 +f 273 271 274 +f 273 274 275 +f 275 274 276 +f 277 278 279 +f 278 277 280 +f 278 280 281 +f 281 280 282 +f 282 280 257 +f 282 257 259 +f 282 259 283 +f 282 283 284 +f 284 283 285 +f 284 285 286 +f 284 286 287 +f 287 286 288 +f 287 288 289 +f 287 289 290 +f 290 289 291 +f 290 291 292 +f 290 292 293 +f 293 292 294 +f 293 294 295 +f 295 294 296 +f 295 296 297 +f 295 297 298 +f 298 297 299 +f 298 299 276 +f 298 276 274 +f 298 274 300 +f 298 300 301 +f 301 300 302 +f 301 302 303 +f 303 302 304 +f 305 302 306 +f 302 305 304 +f 287 307 308 +f 307 287 290 +f 290 309 307 +f 309 290 293 +f 291 310 311 +f 310 291 289 +f 289 312 310 +f 312 289 288 +f 288 313 312 +f 313 288 286 +f 313 285 314 +f 285 313 286 +f 314 283 315 +f 283 314 285 +f 315 259 316 +f 259 315 283 +f 269 317 318 +f 317 269 266 +f 316 258 319 +f 258 316 259 +f 319 261 320 +f 261 319 258 +f 320 262 321 +f 262 320 261 +f 262 322 321 +f 322 262 264 +f 264 323 322 +f 323 264 265 +f 324 301 325 +f 301 324 298 +f 263 326 327 +f 326 263 260 +f 265 328 323 +f 328 265 267 +f 293 329 309 +f 329 293 295 +f 260 330 326 +f 330 260 257 +f 331 332 333 +f 332 331 334 +f 332 334 335 +f 335 334 336 +f 335 336 330 +f 330 336 316 +f 316 336 315 +f 315 336 337 +f 315 337 314 +f 314 337 313 +f 313 337 308 +f 313 308 312 +f 312 308 310 +f 310 308 307 +f 310 307 311 +f 311 307 309 +f 311 309 338 +f 338 309 339 +f 339 309 329 +f 339 329 340 +f 340 329 341 +f 341 329 324 +f 341 324 342 +f 342 324 343 +f 330 319 326 +f 319 330 316 +f 326 319 320 +f 326 320 321 +f 326 321 327 +f 327 321 322 +f 327 322 323 +f 327 323 317 +f 317 323 328 +f 317 328 344 +f 317 344 318 +f 318 344 345 +f 318 345 346 +f 346 345 347 +f 346 347 348 +f 346 348 349 +f 349 348 350 +f 349 350 343 +f 349 343 324 +f 349 324 325 +f 349 325 351 +f 351 325 352 +f 351 352 306 +f 306 352 305 +f 306 300 351 +f 300 306 302 +f 351 274 349 +f 274 351 300 +f 266 327 317 +f 327 266 263 +f 277 335 280 +f 335 277 332 +f 282 334 281 +f 334 282 336 +f 282 337 336 +f 337 282 284 +f 284 308 337 +f 308 284 287 +f 295 324 329 +f 324 295 298 +f 325 303 352 +f 303 325 301 +f 274 346 349 +f 346 274 271 +f 352 304 305 +f 304 352 303 +f 271 318 346 +f 318 271 269 +f 280 330 257 +f 330 280 335 +f 279 332 277 +f 332 279 333 +f 278 333 279 +f 333 278 331 +f 281 331 278 +f 331 281 334 +f 267 344 328 +f 344 267 268 +f 276 350 275 +f 350 276 343 +f 297 342 299 +f 342 297 341 +f 275 348 273 +f 348 275 350 +f 296 341 297 +f 341 296 340 +f 273 347 272 +f 347 273 348 +f 292 311 338 +f 311 292 291 +f 270 347 345 +f 347 270 272 +f 299 343 276 +f 343 299 342 +f 294 338 339 +f 338 294 292 +f 296 339 340 +f 339 296 294 +f 268 345 344 +f 345 268 270 +f 353 354 355 +f 354 353 356 +f 354 356 357 +f 357 356 358 +f 358 356 359 +f 358 359 360 +f 360 359 361 +f 361 359 362 +f 361 362 363 +f 363 362 364 +f 364 362 365 +f 364 365 366 +f 366 365 367 +f 366 367 368 +f 368 367 369 +f 369 367 370 +f 369 370 371 +f 371 370 372 +f 373 374 375 +f 374 373 376 +f 374 376 377 +f 377 376 378 +f 378 376 353 +f 378 353 355 +f 378 355 379 +f 378 379 380 +f 380 379 381 +f 380 381 382 +f 380 382 383 +f 383 382 384 +f 383 384 385 +f 383 385 386 +f 386 385 387 +f 386 387 388 +f 386 388 389 +f 389 388 390 +f 389 390 391 +f 391 390 392 +f 391 392 393 +f 391 393 394 +f 394 393 395 +f 394 395 372 +f 394 372 370 +f 394 370 396 +f 394 396 397 +f 397 396 398 +f 397 398 399 +f 399 398 400 +f 401 398 402 +f 398 401 400 +f 383 403 404 +f 403 383 386 +f 386 405 403 +f 405 386 389 +f 387 406 407 +f 406 387 385 +f 385 408 406 +f 408 385 384 +f 384 409 408 +f 409 384 382 +f 409 381 410 +f 381 409 382 +f 410 379 411 +f 379 410 381 +f 411 355 412 +f 355 411 379 +f 365 413 414 +f 413 365 362 +f 412 354 415 +f 354 412 355 +f 415 357 416 +f 357 415 354 +f 416 358 417 +f 358 416 357 +f 358 418 417 +f 418 358 360 +f 360 419 418 +f 419 360 361 +f 420 397 421 +f 397 420 394 +f 359 422 423 +f 422 359 356 +f 361 424 419 +f 424 361 363 +f 389 425 405 +f 425 389 391 +f 356 426 422 +f 426 356 353 +f 427 428 429 +f 428 427 430 +f 428 430 431 +f 431 430 432 +f 431 432 426 +f 426 432 412 +f 412 432 411 +f 411 432 433 +f 411 433 410 +f 410 433 409 +f 409 433 404 +f 409 404 408 +f 408 404 406 +f 406 404 403 +f 406 403 407 +f 407 403 405 +f 407 405 434 +f 434 405 435 +f 435 405 425 +f 435 425 436 +f 436 425 437 +f 437 425 420 +f 437 420 438 +f 438 420 439 +f 426 415 422 +f 415 426 412 +f 422 415 416 +f 422 416 417 +f 422 417 423 +f 423 417 418 +f 423 418 419 +f 423 419 413 +f 413 419 424 +f 413 424 440 +f 413 440 414 +f 414 440 441 +f 414 441 442 +f 442 441 443 +f 442 443 444 +f 442 444 445 +f 445 444 446 +f 445 446 439 +f 445 439 420 +f 445 420 421 +f 445 421 447 +f 447 421 448 +f 447 448 402 +f 402 448 401 +f 402 396 447 +f 396 402 398 +f 447 370 445 +f 370 447 396 +f 362 423 413 +f 423 362 359 +f 373 431 376 +f 431 373 428 +f 378 430 377 +f 430 378 432 +f 378 433 432 +f 433 378 380 +f 380 404 433 +f 404 380 383 +f 391 420 425 +f 420 391 394 +f 421 399 448 +f 399 421 397 +f 370 442 445 +f 442 370 367 +f 448 400 401 +f 400 448 399 +f 367 414 442 +f 414 367 365 +f 376 426 353 +f 426 376 431 +f 375 428 373 +f 428 375 429 +f 374 429 375 +f 429 374 427 +f 377 427 374 +f 427 377 430 +f 363 440 424 +f 440 363 364 +f 372 446 371 +f 446 372 439 +f 393 438 395 +f 438 393 437 +f 371 444 369 +f 444 371 446 +f 392 437 393 +f 437 392 436 +f 369 443 368 +f 443 369 444 +f 388 407 434 +f 407 388 387 +f 366 443 441 +f 443 366 368 +f 395 439 372 +f 439 395 438 +f 390 434 435 +f 434 390 388 +f 392 435 436 +f 435 392 390 +f 364 441 440 +f 441 364 366 +f 449 450 451 +f 450 449 452 +f 452 449 453 +f 452 453 454 +f 454 453 455 +f 454 455 456 +f 456 455 457 +f 456 457 458 +f 456 458 459 +f 459 458 460 +f 459 460 461 +f 459 461 462 +f 463 464 465 +f 464 463 466 +f 467 468 469 +f 468 467 470 +f 471 470 472 +f 470 471 468 +f 473 470 467 +f 470 473 472 +f 474 475 476 +f 475 474 477 +f 478 479 480 +f 479 478 481 +f 473 469 482 +f 469 473 467 +f 472 483 471 +f 483 472 478 +f 473 478 472 +f 478 473 481 +f 473 484 481 +f 484 473 482 +f 485 486 487 +f 486 488 487 +f 489 490 491 +f 490 489 488 +f 490 488 486 +f 492 493 494 +f 493 492 495 +f 496 497 498 +f 497 496 499 +f 500 499 496 +f 499 500 501 +f 502 501 500 +f 501 502 503 +f 504 502 505 +f 502 504 503 +f 506 505 507 +f 505 506 504 +f 508 509 510 +f 509 508 511 +f 509 494 510 +f 494 509 492 +f 512 511 508 +f 511 512 513 +f 498 513 512 +f 513 498 497 +f 495 514 493 +f 514 495 515 +f 516 507 517 +f 507 516 506 +f 507 514 517 +f 514 507 493 +f 493 507 505 +f 493 505 494 +f 494 505 502 +f 494 502 510 +f 510 502 508 +f 508 502 500 +f 508 500 496 +f 508 496 512 +f 512 496 498 +f 516 514 515 +f 514 516 517 +f 515 506 516 +f 506 515 495 +f 506 495 504 +f 504 495 492 +f 504 492 509 +f 504 509 503 +f 503 509 511 +f 503 511 501 +f 501 511 513 +f 501 513 499 +f 499 513 497 +f 518 519 520 +f 519 518 521 +f 522 523 524 +f 523 522 525 +f 526 525 522 +f 525 526 527 +f 528 527 526 +f 527 528 529 +f 530 528 531 +f 528 530 529 +f 532 531 533 +f 531 532 530 +f 534 535 536 +f 535 534 537 +f 535 520 536 +f 520 535 518 +f 538 537 534 +f 537 538 539 +f 524 539 538 +f 539 524 523 +f 521 540 519 +f 540 521 541 +f 542 533 543 +f 533 542 532 +f 533 540 543 +f 540 533 519 +f 519 533 531 +f 519 531 520 +f 520 531 528 +f 520 528 536 +f 536 528 534 +f 534 528 526 +f 534 526 522 +f 534 522 538 +f 538 522 524 +f 542 540 541 +f 540 542 543 +f 541 532 542 +f 532 541 521 +f 532 521 530 +f 530 521 518 +f 530 518 535 +f 530 535 529 +f 529 535 537 +f 529 537 527 +f 527 537 539 +f 527 539 525 +f 525 539 523 +f 463 544 466 +f 544 463 545 +f 450 545 451 +f 545 450 544 +f 546 547 548 +f 547 546 549 +f 461 548 547 +f 548 461 460 +f 550 551 552 +f 551 550 553 +f 554 555 556 +f 555 554 557 +f 558 559 560 +f 559 558 561 +f 562 563 564 +f 563 562 565 +f 566 567 568 +f 567 566 569 +f 570 571 572 +f 571 570 573 +f 574 575 576 +f 575 574 577 +f 577 578 579 +f 578 577 574 +f 578 574 580 +f 580 574 573 +f 580 573 570 +f 580 570 581 +f 581 570 569 +f 581 569 566 +f 581 566 582 +f 582 566 565 +f 582 565 562 +f 582 562 583 +f 583 562 558 +f 583 558 560 +f 583 560 584 +f 584 560 554 +f 584 554 556 +f 584 556 585 +f 585 556 586 +f 586 556 550 +f 586 550 552 +f 552 587 586 +f 587 552 551 +f 556 553 550 +f 553 556 555 +f 560 557 554 +f 557 560 559 +f 562 561 558 +f 561 562 564 +f 565 568 563 +f 568 565 566 +f 569 572 567 +f 572 569 570 +f 573 576 571 +f 576 573 574 +f 490 588 589 +f 588 490 486 +f 590 589 588 +f 589 590 591 +f 591 592 593 +f 592 591 590 +f 592 476 593 +f 476 592 474 +f 480 594 595 +f 594 480 479 +f 477 596 475 +f 596 477 597 +f 597 594 596 +f 594 597 595 +f 455 583 457 +f 583 455 582 +f 458 548 460 +f 548 585 546 +f 585 548 584 +f 584 548 458 +f 457 584 458 +f 584 457 583 +f 580 545 578 +f 545 580 449 +f 545 449 451 +f 463 578 545 +f 453 580 581 +f 580 453 449 +f 455 581 582 +f 581 455 453 +f 464 579 465 +f 579 464 577 +f 577 464 575 +f 575 464 598 +f 463 579 578 +f 579 463 465 +f 549 587 599 +f 587 549 586 +f 586 549 585 +f 585 549 546 +f 600 549 599 +f 549 600 547 +f 547 600 461 +f 461 600 462 +f 487 459 601 +f 459 487 488 +f 459 488 456 +f 456 488 489 +f 601 462 600 +f 462 601 459 +f 485 588 486 +f 588 485 590 +f 590 485 592 +f 592 485 474 +f 474 485 483 +f 474 483 477 +f 477 483 597 +f 597 483 595 +f 595 483 480 +f 480 483 478 +f 466 602 603 +f 602 466 544 +f 602 544 452 +f 452 544 450 +f 452 604 602 +f 604 452 454 +f 454 605 604 +f 605 454 606 +f 606 454 491 +f 491 456 489 +f 456 491 454 +f 491 607 606 +f 607 491 490 +f 607 476 484 +f 476 607 593 +f 593 607 591 +f 591 607 589 +f 589 607 490 +f 484 476 475 +f 484 475 596 +f 484 596 594 +f 484 594 479 +f 484 479 481 +f 464 603 598 +f 603 464 466 +f 468 482 469 +f 482 468 483 +f 483 468 471 +f 603 575 598 +f 575 603 576 +f 576 603 602 +f 576 602 571 +f 571 602 572 +f 572 602 567 +f 567 602 568 +f 568 602 604 +f 568 604 563 +f 563 604 564 +f 564 604 605 +f 564 605 606 +f 564 606 607 +f 564 607 561 +f 561 607 559 +f 559 607 485 +f 485 607 484 +f 559 485 557 +f 557 485 487 +f 485 484 483 +f 483 484 482 +f 487 555 557 +f 555 487 601 +f 555 601 553 +f 553 601 551 +f 551 601 600 +f 551 600 587 +f 587 600 599 +f 608 609 610 +f 609 608 611 +f 611 608 612 +f 611 612 613 +f 613 612 614 +f 613 614 615 +f 615 614 616 +f 615 616 617 +f 615 617 618 +f 618 617 619 +f 618 619 620 +f 618 620 621 +f 622 623 624 +f 623 622 625 +f 626 627 628 +f 627 626 629 +f 630 629 631 +f 629 630 627 +f 632 629 626 +f 629 632 631 +f 633 634 635 +f 634 633 636 +f 637 638 639 +f 638 637 640 +f 632 628 641 +f 628 632 626 +f 631 642 630 +f 642 631 637 +f 632 637 631 +f 637 632 640 +f 632 643 640 +f 643 632 641 +f 644 645 646 +f 645 647 646 +f 648 649 650 +f 649 648 647 +f 649 647 645 +f 651 652 653 +f 652 651 654 +f 655 656 657 +f 656 655 658 +f 659 658 655 +f 658 659 660 +f 661 660 659 +f 660 661 662 +f 663 661 664 +f 661 663 662 +f 665 664 666 +f 664 665 663 +f 667 668 669 +f 668 667 670 +f 668 653 669 +f 653 668 651 +f 671 670 667 +f 670 671 672 +f 657 672 671 +f 672 657 656 +f 654 673 652 +f 673 654 674 +f 675 666 676 +f 666 675 665 +f 666 673 676 +f 673 666 652 +f 652 666 664 +f 652 664 653 +f 653 664 661 +f 653 661 669 +f 669 661 667 +f 667 661 659 +f 667 659 655 +f 667 655 671 +f 671 655 657 +f 675 673 674 +f 673 675 676 +f 674 665 675 +f 665 674 654 +f 665 654 663 +f 663 654 651 +f 663 651 668 +f 663 668 662 +f 662 668 670 +f 662 670 660 +f 660 670 672 +f 660 672 658 +f 658 672 656 +f 677 678 679 +f 678 677 680 +f 681 682 683 +f 682 681 684 +f 685 684 681 +f 684 685 686 +f 687 686 685 +f 686 687 688 +f 689 687 690 +f 687 689 688 +f 691 690 692 +f 690 691 689 +f 693 694 695 +f 694 693 696 +f 694 679 695 +f 679 694 677 +f 697 696 693 +f 696 697 698 +f 683 698 697 +f 698 683 682 +f 680 699 678 +f 699 680 700 +f 701 692 702 +f 692 701 691 +f 692 699 702 +f 699 692 678 +f 678 692 690 +f 678 690 679 +f 679 690 687 +f 679 687 695 +f 695 687 693 +f 693 687 685 +f 693 685 681 +f 693 681 697 +f 697 681 683 +f 701 699 700 +f 699 701 702 +f 700 691 701 +f 691 700 680 +f 691 680 689 +f 689 680 677 +f 689 677 694 +f 689 694 688 +f 688 694 696 +f 688 696 686 +f 686 696 698 +f 686 698 684 +f 684 698 682 +f 622 703 625 +f 703 622 704 +f 609 704 610 +f 704 609 703 +f 705 706 707 +f 706 705 708 +f 620 707 706 +f 707 620 619 +f 709 710 711 +f 710 709 712 +f 713 714 715 +f 714 713 716 +f 717 718 719 +f 718 717 720 +f 721 722 723 +f 722 721 724 +f 725 726 727 +f 726 725 728 +f 729 730 731 +f 730 729 732 +f 733 734 735 +f 734 733 736 +f 736 737 738 +f 737 736 733 +f 737 733 739 +f 739 733 732 +f 739 732 729 +f 739 729 740 +f 740 729 728 +f 740 728 725 +f 740 725 741 +f 741 725 724 +f 741 724 721 +f 741 721 742 +f 742 721 717 +f 742 717 719 +f 742 719 743 +f 743 719 713 +f 743 713 715 +f 743 715 744 +f 744 715 745 +f 745 715 709 +f 745 709 711 +f 711 746 745 +f 746 711 710 +f 715 712 709 +f 712 715 714 +f 719 716 713 +f 716 719 718 +f 721 720 717 +f 720 721 723 +f 724 727 722 +f 727 724 725 +f 728 731 726 +f 731 728 729 +f 732 735 730 +f 735 732 733 +f 649 747 748 +f 747 649 645 +f 749 748 747 +f 748 749 750 +f 750 751 752 +f 751 750 749 +f 751 635 752 +f 635 751 633 +f 639 753 754 +f 753 639 638 +f 636 755 634 +f 755 636 756 +f 756 753 755 +f 753 756 754 +f 614 742 616 +f 742 614 741 +f 617 707 619 +f 707 744 705 +f 744 707 743 +f 743 707 617 +f 616 743 617 +f 743 616 742 +f 739 704 737 +f 704 739 608 +f 704 608 610 +f 622 737 704 +f 612 739 740 +f 739 612 608 +f 614 740 741 +f 740 614 612 +f 623 738 624 +f 738 623 736 +f 736 623 734 +f 734 623 757 +f 622 738 737 +f 738 622 624 +f 708 746 758 +f 746 708 745 +f 745 708 744 +f 744 708 705 +f 759 708 758 +f 708 759 706 +f 706 759 620 +f 620 759 621 +f 646 618 760 +f 618 646 647 +f 618 647 615 +f 615 647 648 +f 760 621 759 +f 621 760 618 +f 644 747 645 +f 747 644 749 +f 749 644 751 +f 751 644 633 +f 633 644 642 +f 633 642 636 +f 636 642 756 +f 756 642 754 +f 754 642 639 +f 639 642 637 +f 625 761 762 +f 761 625 703 +f 761 703 611 +f 611 703 609 +f 611 763 761 +f 763 611 613 +f 613 764 763 +f 764 613 765 +f 765 613 650 +f 650 615 648 +f 615 650 613 +f 650 766 765 +f 766 650 649 +f 766 635 643 +f 635 766 752 +f 752 766 750 +f 750 766 748 +f 748 766 649 +f 643 635 634 +f 643 634 755 +f 643 755 753 +f 643 753 638 +f 643 638 640 +f 623 762 757 +f 762 623 625 +f 627 641 628 +f 641 627 642 +f 642 627 630 +f 762 734 757 +f 734 762 735 +f 735 762 761 +f 735 761 730 +f 730 761 731 +f 731 761 726 +f 726 761 727 +f 727 761 763 +f 727 763 722 +f 722 763 723 +f 723 763 764 +f 723 764 765 +f 723 765 766 +f 723 766 720 +f 720 766 718 +f 718 766 644 +f 644 766 643 +f 718 644 716 +f 716 644 646 +f 644 643 642 +f 642 643 641 +f 646 714 716 +f 714 646 760 +f 714 760 712 +f 712 760 710 +f 710 760 759 +f 710 759 746 +f 746 759 758 +f 767 768 769 +f 768 767 770 +f 770 767 771 +f 770 771 772 +f 772 771 773 +f 772 773 774 +f 774 773 775 +f 774 775 776 +f 774 776 777 +f 777 776 778 +f 777 778 779 +f 777 779 780 +f 781 782 783 +f 782 781 784 +f 785 786 787 +f 786 785 788 +f 789 788 790 +f 788 789 786 +f 791 788 785 +f 788 791 790 +f 792 793 794 +f 793 792 795 +f 796 797 798 +f 797 796 799 +f 791 787 800 +f 787 791 785 +f 790 801 789 +f 801 790 796 +f 791 796 790 +f 796 791 799 +f 791 802 799 +f 802 791 800 +f 803 804 805 +f 804 806 805 +f 807 808 809 +f 808 807 806 +f 808 806 804 +f 810 811 812 +f 811 810 813 +f 814 815 816 +f 815 814 817 +f 818 817 814 +f 817 818 819 +f 820 819 818 +f 819 820 821 +f 822 820 823 +f 820 822 821 +f 824 823 825 +f 823 824 822 +f 826 827 828 +f 827 826 829 +f 827 812 828 +f 812 827 810 +f 830 829 826 +f 829 830 831 +f 816 831 830 +f 831 816 815 +f 813 832 811 +f 832 813 833 +f 834 825 835 +f 825 834 824 +f 825 832 835 +f 832 825 811 +f 811 825 823 +f 811 823 812 +f 812 823 820 +f 812 820 828 +f 828 820 826 +f 826 820 818 +f 826 818 814 +f 826 814 830 +f 830 814 816 +f 834 832 833 +f 832 834 835 +f 833 824 834 +f 824 833 813 +f 824 813 822 +f 822 813 810 +f 822 810 827 +f 822 827 821 +f 821 827 829 +f 821 829 819 +f 819 829 831 +f 819 831 817 +f 817 831 815 +f 836 837 838 +f 837 836 839 +f 840 841 842 +f 841 840 843 +f 844 843 840 +f 843 844 845 +f 846 845 844 +f 845 846 847 +f 848 846 849 +f 846 848 847 +f 850 849 851 +f 849 850 848 +f 852 853 854 +f 853 852 855 +f 853 838 854 +f 838 853 836 +f 856 855 852 +f 855 856 857 +f 842 857 856 +f 857 842 841 +f 839 858 837 +f 858 839 859 +f 860 851 861 +f 851 860 850 +f 851 858 861 +f 858 851 837 +f 837 851 849 +f 837 849 838 +f 838 849 846 +f 838 846 854 +f 854 846 852 +f 852 846 844 +f 852 844 840 +f 852 840 856 +f 856 840 842 +f 860 858 859 +f 858 860 861 +f 859 850 860 +f 850 859 839 +f 850 839 848 +f 848 839 836 +f 848 836 853 +f 848 853 847 +f 847 853 855 +f 847 855 845 +f 845 855 857 +f 845 857 843 +f 843 857 841 +f 781 862 784 +f 862 781 863 +f 768 863 769 +f 863 768 862 +f 864 865 866 +f 865 864 867 +f 779 866 865 +f 866 779 778 +f 868 869 870 +f 869 868 871 +f 872 873 874 +f 873 872 875 +f 876 877 878 +f 877 876 879 +f 880 881 882 +f 881 880 883 +f 884 885 886 +f 885 884 887 +f 888 889 890 +f 889 888 891 +f 892 893 894 +f 893 892 895 +f 895 896 897 +f 896 895 892 +f 896 892 898 +f 898 892 891 +f 898 891 888 +f 898 888 899 +f 899 888 887 +f 899 887 884 +f 899 884 900 +f 900 884 883 +f 900 883 880 +f 900 880 901 +f 901 880 876 +f 901 876 878 +f 901 878 902 +f 902 878 872 +f 902 872 874 +f 902 874 903 +f 903 874 904 +f 904 874 868 +f 904 868 870 +f 870 905 904 +f 905 870 869 +f 874 871 868 +f 871 874 873 +f 878 875 872 +f 875 878 877 +f 880 879 876 +f 879 880 882 +f 883 886 881 +f 886 883 884 +f 887 890 885 +f 890 887 888 +f 891 894 889 +f 894 891 892 +f 808 906 907 +f 906 808 804 +f 908 907 906 +f 907 908 909 +f 909 910 911 +f 910 909 908 +f 910 794 911 +f 794 910 792 +f 798 912 913 +f 912 798 797 +f 795 914 793 +f 914 795 915 +f 915 912 914 +f 912 915 913 +f 773 901 775 +f 901 773 900 +f 776 866 778 +f 866 903 864 +f 903 866 902 +f 902 866 776 +f 775 902 776 +f 902 775 901 +f 898 863 896 +f 863 898 767 +f 863 767 769 +f 781 896 863 +f 771 898 899 +f 898 771 767 +f 773 899 900 +f 899 773 771 +f 782 897 783 +f 897 782 895 +f 895 782 893 +f 893 782 916 +f 781 897 896 +f 897 781 783 +f 867 905 917 +f 905 867 904 +f 904 867 903 +f 903 867 864 +f 918 867 917 +f 867 918 865 +f 865 918 779 +f 779 918 780 +f 805 777 919 +f 777 805 806 +f 777 806 774 +f 774 806 807 +f 919 780 918 +f 780 919 777 +f 803 906 804 +f 906 803 908 +f 908 803 910 +f 910 803 792 +f 792 803 801 +f 792 801 795 +f 795 801 915 +f 915 801 913 +f 913 801 798 +f 798 801 796 +f 784 920 921 +f 920 784 862 +f 920 862 770 +f 770 862 768 +f 770 922 920 +f 922 770 772 +f 772 923 922 +f 923 772 924 +f 924 772 809 +f 809 774 807 +f 774 809 772 +f 809 925 924 +f 925 809 808 +f 925 794 802 +f 794 925 911 +f 911 925 909 +f 909 925 907 +f 907 925 808 +f 802 794 793 +f 802 793 914 +f 802 914 912 +f 802 912 797 +f 802 797 799 +f 782 921 916 +f 921 782 784 +f 786 800 787 +f 800 786 801 +f 801 786 789 +f 921 893 916 +f 893 921 894 +f 894 921 920 +f 894 920 889 +f 889 920 890 +f 890 920 885 +f 885 920 886 +f 886 920 922 +f 886 922 881 +f 881 922 882 +f 882 922 923 +f 882 923 924 +f 882 924 925 +f 882 925 879 +f 879 925 877 +f 877 925 803 +f 803 925 802 +f 877 803 875 +f 875 803 805 +f 803 802 801 +f 801 802 800 +f 805 873 875 +f 873 805 919 +f 873 919 871 +f 871 919 869 +f 869 919 918 +f 869 918 905 +f 905 918 917 +f 926 927 928 +f 927 926 929 +f 929 926 930 +f 929 930 931 +f 931 930 932 +f 931 932 933 +f 933 932 934 +f 933 934 935 +f 933 935 936 +f 936 935 937 +f 936 937 938 +f 936 938 939 +f 940 941 942 +f 941 940 943 +f 944 945 946 +f 945 944 947 +f 948 947 949 +f 947 948 945 +f 950 947 944 +f 947 950 949 +f 951 952 953 +f 952 951 954 +f 955 956 957 +f 956 955 958 +f 950 946 959 +f 946 950 944 +f 949 960 948 +f 960 949 955 +f 950 955 949 +f 955 950 958 +f 950 961 958 +f 961 950 959 +f 962 963 964 +f 963 965 964 +f 966 967 968 +f 967 966 965 +f 967 965 963 +f 969 970 971 +f 970 969 972 +f 973 974 975 +f 974 973 976 +f 977 976 973 +f 976 977 978 +f 979 978 977 +f 978 979 980 +f 981 979 982 +f 979 981 980 +f 983 982 984 +f 982 983 981 +f 985 986 987 +f 986 985 988 +f 986 971 987 +f 971 986 969 +f 989 988 985 +f 988 989 990 +f 975 990 989 +f 990 975 974 +f 972 991 970 +f 991 972 992 +f 993 984 994 +f 984 993 983 +f 984 991 994 +f 991 984 970 +f 970 984 982 +f 970 982 971 +f 971 982 979 +f 971 979 987 +f 987 979 985 +f 985 979 977 +f 985 977 973 +f 985 973 989 +f 989 973 975 +f 993 991 992 +f 991 993 994 +f 992 983 993 +f 983 992 972 +f 983 972 981 +f 981 972 969 +f 981 969 986 +f 981 986 980 +f 980 986 988 +f 980 988 978 +f 978 988 990 +f 978 990 976 +f 976 990 974 +f 995 996 997 +f 996 995 998 +f 999 1000 1001 +f 1000 999 1002 +f 1003 1002 999 +f 1002 1003 1004 +f 1005 1004 1003 +f 1004 1005 1006 +f 1007 1005 1008 +f 1005 1007 1006 +f 1009 1008 1010 +f 1008 1009 1007 +f 1011 1012 1013 +f 1012 1011 1014 +f 1012 997 1013 +f 997 1012 995 +f 1015 1014 1011 +f 1014 1015 1016 +f 1001 1016 1015 +f 1016 1001 1000 +f 998 1017 996 +f 1017 998 1018 +f 1019 1010 1020 +f 1010 1019 1009 +f 1010 1017 1020 +f 1017 1010 996 +f 996 1010 1008 +f 996 1008 997 +f 997 1008 1005 +f 997 1005 1013 +f 1013 1005 1011 +f 1011 1005 1003 +f 1011 1003 999 +f 1011 999 1015 +f 1015 999 1001 +f 1019 1017 1018 +f 1017 1019 1020 +f 1018 1009 1019 +f 1009 1018 998 +f 1009 998 1007 +f 1007 998 995 +f 1007 995 1012 +f 1007 1012 1006 +f 1006 1012 1014 +f 1006 1014 1004 +f 1004 1014 1016 +f 1004 1016 1002 +f 1002 1016 1000 +f 940 1021 943 +f 1021 940 1022 +f 927 1022 928 +f 1022 927 1021 +f 1023 1024 1025 +f 1024 1023 1026 +f 938 1025 1024 +f 1025 938 937 +f 1027 1028 1029 +f 1028 1027 1030 +f 1031 1032 1033 +f 1032 1031 1034 +f 1035 1036 1037 +f 1036 1035 1038 +f 1039 1040 1041 +f 1040 1039 1042 +f 1043 1044 1045 +f 1044 1043 1046 +f 1047 1048 1049 +f 1048 1047 1050 +f 1051 1052 1053 +f 1052 1051 1054 +f 1054 1055 1056 +f 1055 1054 1051 +f 1055 1051 1057 +f 1057 1051 1050 +f 1057 1050 1047 +f 1057 1047 1058 +f 1058 1047 1046 +f 1058 1046 1043 +f 1058 1043 1059 +f 1059 1043 1042 +f 1059 1042 1039 +f 1059 1039 1060 +f 1060 1039 1035 +f 1060 1035 1037 +f 1060 1037 1061 +f 1061 1037 1031 +f 1061 1031 1033 +f 1061 1033 1062 +f 1062 1033 1063 +f 1063 1033 1027 +f 1063 1027 1029 +f 1029 1064 1063 +f 1064 1029 1028 +f 1033 1030 1027 +f 1030 1033 1032 +f 1037 1034 1031 +f 1034 1037 1036 +f 1039 1038 1035 +f 1038 1039 1041 +f 1042 1045 1040 +f 1045 1042 1043 +f 1046 1049 1044 +f 1049 1046 1047 +f 1050 1053 1048 +f 1053 1050 1051 +f 967 1065 1066 +f 1065 967 963 +f 1067 1066 1065 +f 1066 1067 1068 +f 1068 1069 1070 +f 1069 1068 1067 +f 1069 953 1070 +f 953 1069 951 +f 957 1071 1072 +f 1071 957 956 +f 954 1073 952 +f 1073 954 1074 +f 1074 1071 1073 +f 1071 1074 1072 +f 932 1060 934 +f 1060 932 1059 +f 935 1025 937 +f 1025 1062 1023 +f 1062 1025 1061 +f 1061 1025 935 +f 934 1061 935 +f 1061 934 1060 +f 1057 1022 1055 +f 1022 1057 926 +f 1022 926 928 +f 940 1055 1022 +f 930 1057 1058 +f 1057 930 926 +f 932 1058 1059 +f 1058 932 930 +f 941 1056 942 +f 1056 941 1054 +f 1054 941 1052 +f 1052 941 1075 +f 940 1056 1055 +f 1056 940 942 +f 1026 1064 1076 +f 1064 1026 1063 +f 1063 1026 1062 +f 1062 1026 1023 +f 1077 1026 1076 +f 1026 1077 1024 +f 1024 1077 938 +f 938 1077 939 +f 964 936 1078 +f 936 964 965 +f 936 965 933 +f 933 965 966 +f 1078 939 1077 +f 939 1078 936 +f 962 1065 963 +f 1065 962 1067 +f 1067 962 1069 +f 1069 962 951 +f 951 962 960 +f 951 960 954 +f 954 960 1074 +f 1074 960 1072 +f 1072 960 957 +f 957 960 955 +f 943 1079 1080 +f 1079 943 1021 +f 1079 1021 929 +f 929 1021 927 +f 929 1081 1079 +f 1081 929 931 +f 931 1082 1081 +f 1082 931 1083 +f 1083 931 968 +f 968 933 966 +f 933 968 931 +f 968 1084 1083 +f 1084 968 967 +f 1084 953 961 +f 953 1084 1070 +f 1070 1084 1068 +f 1068 1084 1066 +f 1066 1084 967 +f 961 953 952 +f 961 952 1073 +f 961 1073 1071 +f 961 1071 956 +f 961 956 958 +f 941 1080 1075 +f 1080 941 943 +f 945 959 946 +f 959 945 960 +f 960 945 948 +f 1080 1052 1075 +f 1052 1080 1053 +f 1053 1080 1079 +f 1053 1079 1048 +f 1048 1079 1049 +f 1049 1079 1044 +f 1044 1079 1045 +f 1045 1079 1081 +f 1045 1081 1040 +f 1040 1081 1041 +f 1041 1081 1082 +f 1041 1082 1083 +f 1041 1083 1084 +f 1041 1084 1038 +f 1038 1084 1036 +f 1036 1084 962 +f 962 1084 961 +f 1036 962 1034 +f 1034 962 964 +f 962 961 960 +f 960 961 959 +f 964 1032 1034 +f 1032 964 1078 +f 1032 1078 1030 +f 1030 1078 1028 +f 1028 1078 1077 +f 1028 1077 1064 +f 1064 1077 1076 +f 1085 1086 1087 +f 1086 1085 1088 +f 1089 1090 1091 +f 1090 1089 1092 +f 1093 1092 1089 +f 1092 1093 1094 +f 1095 1094 1093 +f 1094 1095 1096 +f 1097 1095 1098 +f 1095 1097 1096 +f 1099 1098 1100 +f 1098 1099 1097 +f 1101 1102 1103 +f 1102 1101 1104 +f 1102 1087 1103 +f 1087 1102 1085 +f 1105 1104 1101 +f 1104 1105 1106 +f 1091 1106 1105 +f 1106 1091 1090 +f 1088 1107 1086 +f 1107 1088 1108 +f 1109 1100 1110 +f 1100 1109 1099 +f 1100 1107 1110 +f 1107 1100 1086 +f 1086 1100 1098 +f 1086 1098 1087 +f 1087 1098 1095 +f 1087 1095 1103 +f 1103 1095 1101 +f 1101 1095 1093 +f 1101 1093 1089 +f 1101 1089 1105 +f 1105 1089 1091 +f 1109 1107 1108 +f 1107 1109 1110 +f 1108 1099 1109 +f 1099 1108 1088 +f 1099 1088 1097 +f 1097 1088 1085 +f 1097 1085 1102 +f 1097 1102 1096 +f 1096 1102 1104 +f 1096 1104 1094 +f 1094 1104 1106 +f 1094 1106 1092 +f 1092 1106 1090 +f 1111 1112 1113 +f 1112 1111 1114 +f 1115 1116 1117 +f 1116 1115 1118 +f 1119 1118 1115 +f 1118 1119 1120 +f 1121 1120 1119 +f 1120 1121 1122 +f 1123 1121 1124 +f 1121 1123 1122 +f 1125 1124 1126 +f 1124 1125 1123 +f 1127 1128 1129 +f 1128 1127 1130 +f 1128 1113 1129 +f 1113 1128 1111 +f 1131 1130 1127 +f 1130 1131 1132 +f 1117 1132 1131 +f 1132 1117 1116 +f 1114 1133 1112 +f 1133 1114 1134 +f 1135 1126 1136 +f 1126 1135 1125 +f 1126 1133 1136 +f 1133 1126 1112 +f 1112 1126 1124 +f 1112 1124 1113 +f 1113 1124 1121 +f 1113 1121 1129 +f 1129 1121 1127 +f 1127 1121 1119 +f 1127 1119 1115 +f 1127 1115 1131 +f 1131 1115 1117 +f 1135 1133 1134 +f 1133 1135 1136 +f 1134 1125 1135 +f 1125 1134 1114 +f 1125 1114 1123 +f 1123 1114 1111 +f 1123 1111 1128 +f 1123 1128 1122 +f 1122 1128 1130 +f 1122 1130 1120 +f 1120 1130 1132 +f 1120 1132 1118 +f 1118 1132 1116 +f 1137 1138 1139 +f 1138 1137 1140 +f 1141 1142 1143 +f 1142 1141 1144 +f 1145 1144 1141 +f 1144 1145 1146 +f 1147 1146 1145 +f 1146 1147 1148 +f 1149 1147 1150 +f 1147 1149 1148 +f 1151 1150 1152 +f 1150 1151 1149 +f 1153 1154 1155 +f 1154 1153 1156 +f 1154 1139 1155 +f 1139 1154 1137 +f 1157 1156 1153 +f 1156 1157 1158 +f 1143 1158 1157 +f 1158 1143 1142 +f 1140 1159 1138 +f 1159 1140 1160 +f 1161 1152 1162 +f 1152 1161 1151 +f 1152 1159 1162 +f 1159 1152 1138 +f 1138 1152 1150 +f 1138 1150 1139 +f 1139 1150 1147 +f 1139 1147 1155 +f 1155 1147 1153 +f 1153 1147 1145 +f 1153 1145 1141 +f 1153 1141 1157 +f 1157 1141 1143 +f 1161 1159 1160 +f 1159 1161 1162 +f 1160 1151 1161 +f 1151 1160 1140 +f 1151 1140 1149 +f 1149 1140 1137 +f 1149 1137 1154 +f 1149 1154 1148 +f 1148 1154 1156 +f 1148 1156 1146 +f 1146 1156 1158 +f 1146 1158 1144 +f 1144 1158 1142 +f 1163 1164 1165 +f 1164 1163 1166 +f 1167 1168 1169 +f 1168 1167 1170 +f 1171 1170 1167 +f 1170 1171 1172 +f 1173 1172 1171 +f 1172 1173 1174 +f 1175 1173 1176 +f 1173 1175 1174 +f 1177 1176 1178 +f 1176 1177 1175 +f 1179 1180 1181 +f 1180 1179 1182 +f 1180 1165 1181 +f 1165 1180 1163 +f 1183 1182 1179 +f 1182 1183 1184 +f 1169 1184 1183 +f 1184 1169 1168 +f 1166 1185 1164 +f 1185 1166 1186 +f 1187 1178 1188 +f 1178 1187 1177 +f 1178 1185 1188 +f 1185 1178 1164 +f 1164 1178 1176 +f 1164 1176 1165 +f 1165 1176 1173 +f 1165 1173 1181 +f 1181 1173 1179 +f 1179 1173 1171 +f 1179 1171 1167 +f 1179 1167 1183 +f 1183 1167 1169 +f 1187 1185 1186 +f 1185 1187 1188 +f 1186 1177 1187 +f 1177 1186 1166 +f 1177 1166 1175 +f 1175 1166 1163 +f 1175 1163 1180 +f 1175 1180 1174 +f 1174 1180 1182 +f 1174 1182 1172 +f 1172 1182 1184 +f 1172 1184 1170 +f 1170 1184 1168 +f 1189 1190 1191 +f 1190 1189 1192 +f 1193 1194 1195 +f 1194 1193 1196 +f 1197 1196 1193 +f 1196 1197 1198 +f 1199 1198 1197 +f 1198 1199 1200 +f 1201 1199 1202 +f 1199 1201 1200 +f 1203 1202 1204 +f 1202 1203 1201 +f 1205 1206 1207 +f 1206 1205 1208 +f 1206 1191 1207 +f 1191 1206 1189 +f 1209 1208 1205 +f 1208 1209 1210 +f 1195 1210 1209 +f 1210 1195 1194 +f 1192 1211 1190 +f 1211 1192 1212 +f 1213 1204 1214 +f 1204 1213 1203 +f 1204 1211 1214 +f 1211 1204 1190 +f 1190 1204 1202 +f 1190 1202 1191 +f 1191 1202 1199 +f 1191 1199 1207 +f 1207 1199 1205 +f 1205 1199 1197 +f 1205 1197 1193 +f 1205 1193 1209 +f 1209 1193 1195 +f 1213 1211 1212 +f 1211 1213 1214 +f 1212 1203 1213 +f 1203 1212 1192 +f 1203 1192 1201 +f 1201 1192 1189 +f 1201 1189 1206 +f 1201 1206 1200 +f 1200 1206 1208 +f 1200 1208 1198 +f 1198 1208 1210 +f 1198 1210 1196 +f 1196 1210 1194 +f 1215 1216 1217 +f 1216 1215 1218 +f 1219 1220 1221 +f 1220 1219 1222 +f 1223 1222 1219 +f 1222 1223 1224 +f 1225 1224 1223 +f 1224 1225 1226 +f 1227 1225 1228 +f 1225 1227 1226 +f 1229 1228 1230 +f 1228 1229 1227 +f 1231 1232 1233 +f 1232 1231 1234 +f 1232 1217 1233 +f 1217 1232 1215 +f 1235 1234 1231 +f 1234 1235 1236 +f 1221 1236 1235 +f 1236 1221 1220 +f 1218 1237 1216 +f 1237 1218 1238 +f 1239 1230 1240 +f 1230 1239 1229 +f 1230 1237 1240 +f 1237 1230 1216 +f 1216 1230 1228 +f 1216 1228 1217 +f 1217 1228 1225 +f 1217 1225 1233 +f 1233 1225 1231 +f 1231 1225 1223 +f 1231 1223 1219 +f 1231 1219 1235 +f 1235 1219 1221 +f 1239 1237 1238 +f 1237 1239 1240 +f 1238 1229 1239 +f 1229 1238 1218 +f 1229 1218 1227 +f 1227 1218 1215 +f 1227 1215 1232 +f 1227 1232 1226 +f 1226 1232 1234 +f 1226 1234 1224 +f 1224 1234 1236 +f 1224 1236 1222 +f 1222 1236 1220 +f 468 1241 1242 +f 1241 468 1243 +f 1243 468 1244 +f 1245 6 1246 +f 6 1245 5 +f 9 11 10 +f 11 9 12 +f 1244 1247 1243 +f 1247 1244 1248 +f 1248 1244 468 +f 1247 1246 1249 +f 1246 1247 1245 +f 1245 1247 1250 +f 1250 1247 1248 +f 1251 6 7 +f 6 1251 1246 +f 1241 1249 1252 +f 1249 1241 1247 +f 1247 1241 1243 +f 1249 1253 1252 +f 1253 1249 1251 +f 1251 1249 1246 +f 2 1251 7 +f 1251 2 1254 +f 1253 1241 1252 +f 1241 1253 1242 +f 1242 1253 1255 +f 1251 1255 1253 +f 1255 1251 1256 +f 1256 1251 1254 +f 1245 2 5 +f 2 1245 1254 +f 468 1250 1248 +f 1250 468 1256 +f 1256 468 1255 +f 1255 468 1242 +f 1245 1256 1254 +f 1256 1245 1250 +f 1257 1258 1244 +f 1259 1260 1261 +f 1262 1261 1260 +f 1261 1262 1263 +f 1264 1260 1259 +f 1260 1264 1262 +f 1263 1262 1264 +f 1261 1264 1259 +f 1264 1261 1263 +f 1265 1266 1267 +f 1268 1267 1266 +f 1267 1268 1269 +f 1270 1266 1265 +f 1266 1270 1268 +f 1269 1268 1270 +f 1267 1270 1265 +f 1270 1267 1269 +f 1271 1272 1273 +f 1274 1273 1272 +f 1273 1274 1275 +f 1276 1272 1271 +f 1272 1276 1274 +f 1275 1274 1276 +f 1273 1276 1271 +f 1276 1273 1275 +f 1277 1278 1279 +f 1280 1279 1278 +f 1279 1280 1281 +f 1282 1278 1277 +f 1278 1282 1280 +f 1281 1280 1282 +f 1279 1282 1277 +f 1282 1279 1281 diff --git a/data/lego/lego.urdf b/data/lego/lego.urdf new file mode 100644 index 000000000..634a5e64c --- /dev/null +++ b/data/lego/lego.urdf @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/lego/lego_vhacd.obj b/data/lego/lego_vhacd.obj new file mode 100644 index 000000000..b136bb5f6 --- /dev/null +++ b/data/lego/lego_vhacd.obj @@ -0,0 +1,3072 @@ +o convex_0 +v -0.001024 0.146464 -0.318524 +v 0.054282 0.193568 -0.160829 +v -0.001024 0.193568 -0.160829 +v 0.054282 0.193568 -0.318524 +v 0.011270 0.146464 -0.160829 +v 0.054282 0.146464 -0.318524 +v -0.001024 0.193568 -0.318524 +v 0.054282 0.177181 -0.160829 +v -0.001024 0.146464 -0.160829 +v 0.054282 0.146464 -0.306205 +f 6 8 10 +f 3 2 4 +f 2 3 5 +f 4 2 6 +f 1 4 6 +f 5 1 6 +f 1 3 7 +f 3 4 7 +f 4 1 7 +f 2 5 8 +f 6 2 8 +f 3 1 9 +f 1 5 9 +f 5 3 9 +f 5 6 10 +f 8 5 10 +o convex_1 +v 0.304179 0.148508 -0.160783 +v 0.318523 0.193568 0.001024 +v 0.261169 0.193568 0.001024 +v 0.318523 0.148508 0.001024 +v 0.318523 0.193568 -0.160783 +v 0.261169 0.177182 -0.160783 +v 0.261169 0.148508 0.001024 +v 0.261169 0.193568 -0.160783 +v 0.318523 0.148508 -0.160783 +v 0.261169 0.148508 -0.013341 +f 17 16 20 +f 12 13 14 +f 13 12 15 +f 12 14 15 +f 15 11 16 +f 14 13 17 +f 11 14 17 +f 13 16 17 +f 13 15 18 +f 16 13 18 +f 15 16 18 +f 14 11 19 +f 11 15 19 +f 15 14 19 +f 16 11 20 +f 11 17 20 +o convex_2 +v 0.084994 0.230440 -0.029703 +v 0.031750 0.193576 -0.068621 +v 0.031750 0.193576 -0.062472 +v 0.125962 0.193576 -0.068621 +v 0.031750 0.230440 -0.068621 +v 0.125962 0.230440 -0.068621 +v 0.095232 0.193576 -0.031753 +v 0.052253 0.193576 -0.035852 +v 0.042006 0.230440 -0.044046 +v 0.115725 0.230440 -0.044042 +v 0.121867 0.193576 -0.052232 +v 0.062490 0.230440 -0.031753 +v 0.072728 0.193576 -0.029703 +v 0.105478 0.193576 -0.035852 +v 0.105478 0.230440 -0.035852 +v 0.042006 0.193576 -0.044046 +v 0.033807 0.230440 -0.056328 +v 0.123915 0.230440 -0.056328 +f 31 24 38 +f 23 22 24 +f 22 23 25 +f 24 22 25 +f 25 21 26 +f 24 25 26 +f 23 24 27 +f 23 27 28 +f 21 25 29 +f 26 21 30 +f 27 24 31 +f 21 29 32 +f 29 28 32 +f 27 21 33 +f 28 27 33 +f 21 32 33 +f 32 28 33 +f 27 31 34 +f 31 30 34 +f 21 27 35 +f 30 21 35 +f 27 34 35 +f 34 30 35 +f 23 28 36 +f 28 29 36 +f 36 29 37 +f 25 23 37 +f 29 25 37 +f 23 36 37 +f 24 26 38 +f 26 30 38 +f 30 31 38 +o convex_3 +v 0.244764 0.230440 -0.029703 +v 0.191551 0.193576 -0.068621 +v 0.191551 0.193576 -0.062472 +v 0.285742 0.193576 -0.068621 +v 0.191551 0.230440 -0.068621 +v 0.285742 0.230440 -0.068621 +v 0.255009 0.193576 -0.031753 +v 0.212021 0.193576 -0.035852 +v 0.201777 0.230440 -0.044046 +v 0.275497 0.230440 -0.044042 +v 0.281648 0.193576 -0.052232 +v 0.222266 0.230440 -0.031753 +v 0.232501 0.193576 -0.029703 +v 0.265244 0.193576 -0.035852 +v 0.265244 0.230440 -0.035852 +v 0.195636 0.193576 -0.052232 +v 0.193589 0.230440 -0.056328 +v 0.283695 0.230440 -0.056328 +f 49 42 56 +f 41 40 42 +f 40 41 43 +f 42 40 43 +f 43 39 44 +f 42 43 44 +f 41 42 45 +f 41 45 46 +f 39 43 47 +f 44 39 48 +f 45 42 49 +f 39 47 50 +f 47 46 50 +f 45 39 51 +f 46 45 51 +f 39 50 51 +f 50 46 51 +f 45 49 52 +f 49 48 52 +f 39 45 53 +f 48 39 53 +f 45 52 53 +f 52 48 53 +f 41 46 54 +f 46 47 54 +f 54 47 55 +f 43 41 55 +f 47 43 55 +f 41 54 55 +f 42 44 56 +f 44 48 56 +f 48 49 56 +o convex_4 +v 0.041996 0.193576 -0.273455 +v 0.074766 0.230440 -0.269356 +v 0.050191 0.230440 -0.228401 +v 0.035855 0.230440 -0.265257 +v 0.050191 0.193576 -0.228401 +v 0.074766 0.193576 -0.287793 +v 0.029702 0.193576 -0.228401 +v 0.060425 0.230440 -0.285740 +v 0.029702 0.230440 -0.228401 +v 0.074766 0.193576 -0.269356 +v 0.029702 0.193576 -0.248884 +v 0.074766 0.230440 -0.287793 +v 0.060425 0.193576 -0.285740 +v 0.029702 0.230440 -0.248884 +v 0.044042 0.230440 -0.275501 +v 0.031753 0.193576 -0.257076 +f 67 70 72 +f 59 58 60 +f 58 59 61 +f 61 57 62 +f 57 61 63 +f 61 59 63 +f 60 58 64 +f 59 60 65 +f 63 59 65 +f 58 61 66 +f 62 58 66 +f 61 62 66 +f 57 63 67 +f 63 65 67 +f 58 62 68 +f 64 58 68 +f 62 64 68 +f 62 57 69 +f 64 62 69 +f 65 60 70 +f 67 65 70 +f 57 60 71 +f 60 64 71 +f 69 57 71 +f 64 69 71 +f 60 57 72 +f 57 67 72 +f 70 60 72 +o convex_5 +v 0.113677 0.193576 -0.275501 +v 0.128024 0.230440 -0.228401 +v 0.128024 0.193576 -0.228401 +v 0.074766 0.230440 -0.269356 +v 0.117780 0.230440 -0.271402 +v 0.074766 0.193576 -0.287793 +v 0.107531 0.193576 -0.228401 +v 0.089113 0.230440 -0.287793 +v 0.107531 0.230440 -0.228401 +v 0.074766 0.193576 -0.269356 +v 0.125970 0.193576 -0.257076 +v 0.074766 0.230440 -0.287793 +v 0.128024 0.230440 -0.248884 +v 0.097303 0.193576 -0.285740 +v 0.105488 0.230440 -0.281647 +f 73 86 87 +f 76 74 77 +f 73 75 78 +f 75 74 79 +f 78 75 79 +f 76 77 80 +f 74 76 81 +f 79 74 81 +f 76 79 81 +f 76 78 82 +f 79 76 82 +f 78 79 82 +f 75 73 83 +f 73 77 83 +f 78 76 84 +f 76 80 84 +f 80 78 84 +f 74 75 85 +f 77 74 85 +f 75 83 85 +f 83 77 85 +f 73 78 86 +f 78 80 86 +f 86 80 87 +f 77 73 87 +f 80 77 87 +o convex_6 +v 0.056328 0.230440 -0.193578 +v 0.031750 0.193576 -0.228391 +v 0.031750 0.193576 -0.220198 +v 0.072718 0.193576 -0.189476 +v 0.050189 0.230440 -0.228391 +v 0.072718 0.193576 -0.207916 +v 0.072718 0.230440 -0.207916 +v 0.031750 0.230440 -0.220198 +v 0.046090 0.193576 -0.199723 +v 0.050189 0.193576 -0.228391 +v 0.072718 0.230440 -0.189476 +v 0.039947 0.230440 -0.205867 +v 0.031750 0.230440 -0.228391 +v 0.066570 0.193576 -0.189476 +f 91 98 101 +f 90 89 91 +f 91 89 93 +f 92 88 94 +f 93 92 94 +f 91 93 94 +f 89 90 95 +f 88 92 95 +f 90 91 96 +f 89 92 97 +f 93 89 97 +f 92 93 97 +f 94 88 98 +f 91 94 98 +f 88 95 99 +f 95 90 99 +f 90 96 99 +f 96 88 99 +f 92 89 100 +f 89 95 100 +f 95 92 100 +f 96 91 101 +f 88 96 101 +f 98 88 101 +o convex_7 +v 0.115732 0.230440 -0.203818 +v 0.107537 0.193576 -0.228391 +v 0.072718 0.193576 -0.207916 +v 0.105483 0.193576 -0.195628 +v 0.072718 0.230440 -0.207916 +v 0.072718 0.230440 -0.189476 +v 0.125976 0.230440 -0.228391 +v 0.125976 0.193576 -0.220198 +v 0.072718 0.193576 -0.189476 +v 0.091157 0.230440 -0.189476 +v 0.107537 0.230440 -0.228391 +v 0.125976 0.193576 -0.228391 +v 0.091157 0.193576 -0.189476 +v 0.117775 0.193576 -0.205867 +v 0.125976 0.230440 -0.220198 +f 102 115 116 +f 104 103 105 +f 103 104 106 +f 106 104 107 +f 102 106 107 +f 106 102 108 +f 105 103 109 +f 104 105 110 +f 107 104 110 +f 107 110 111 +f 105 102 111 +f 102 107 111 +f 103 106 112 +f 108 103 112 +f 106 108 112 +f 103 108 113 +f 109 103 113 +f 108 109 113 +f 110 105 114 +f 111 110 114 +f 105 111 114 +f 102 105 115 +f 105 109 115 +f 115 109 116 +f 108 102 116 +f 109 108 116 +o convex_8 +v 0.199724 0.193576 -0.271402 +v 0.232492 0.230440 -0.269356 +v 0.209963 0.230440 -0.228401 +v 0.195627 0.230440 -0.265257 +v 0.209963 0.193576 -0.228401 +v 0.232492 0.193576 -0.287793 +v 0.189484 0.193576 -0.228401 +v 0.220198 0.230440 -0.285740 +v 0.189484 0.230440 -0.228401 +v 0.232492 0.193576 -0.269356 +v 0.189484 0.193576 -0.248884 +v 0.232492 0.230440 -0.287793 +v 0.212005 0.193576 -0.281647 +v 0.189484 0.230440 -0.248884 +v 0.203820 0.230440 -0.275501 +f 124 129 131 +f 119 118 120 +f 118 119 121 +f 121 117 122 +f 117 121 123 +f 121 119 123 +f 120 118 124 +f 119 120 125 +f 123 119 125 +f 118 121 126 +f 122 118 126 +f 121 122 126 +f 120 117 127 +f 117 123 127 +f 123 125 127 +f 118 122 128 +f 124 118 128 +f 122 124 128 +f 122 117 129 +f 124 122 129 +f 125 120 130 +f 120 127 130 +f 127 125 130 +f 117 120 131 +f 120 124 131 +f 129 117 131 +o convex_9 +v 0.216100 0.230440 -0.193578 +v 0.191532 0.193576 -0.228391 +v 0.191532 0.193576 -0.220198 +v 0.232492 0.193576 -0.207916 +v 0.209965 0.230440 -0.228391 +v 0.191532 0.230440 -0.220198 +v 0.226341 0.193576 -0.189476 +v 0.232492 0.230440 -0.189476 +v 0.232492 0.230440 -0.207916 +v 0.205867 0.193576 -0.199723 +v 0.209965 0.193576 -0.228391 +v 0.199724 0.230440 -0.205867 +v 0.191532 0.230440 -0.228391 +v 0.232492 0.193576 -0.189476 +f 139 138 145 +f 134 133 135 +f 133 134 137 +f 132 136 137 +f 134 135 138 +f 136 132 139 +f 132 138 139 +f 136 139 140 +f 135 136 140 +f 139 135 140 +f 138 132 141 +f 134 138 141 +f 135 133 142 +f 136 135 142 +f 133 136 142 +f 132 137 143 +f 137 134 143 +f 141 132 143 +f 134 141 143 +f 136 133 144 +f 133 137 144 +f 137 136 144 +f 138 135 145 +f 135 139 145 +o convex_10 +v 0.275502 0.230440 -0.203818 +v 0.267308 0.193576 -0.228391 +v 0.232492 0.193576 -0.207916 +v 0.265259 0.193576 -0.195628 +v 0.232492 0.230440 -0.207916 +v 0.232492 0.230440 -0.189476 +v 0.285740 0.230440 -0.228391 +v 0.285740 0.193576 -0.220198 +v 0.232492 0.193576 -0.189476 +v 0.250934 0.230440 -0.189476 +v 0.267308 0.230440 -0.228391 +v 0.285740 0.193576 -0.228391 +v 0.250934 0.193576 -0.189476 +v 0.277550 0.193576 -0.205867 +v 0.285740 0.230440 -0.220198 +f 146 159 160 +f 148 147 149 +f 147 148 150 +f 150 148 151 +f 146 150 151 +f 150 146 152 +f 149 147 153 +f 148 149 154 +f 151 148 154 +f 151 154 155 +f 149 146 155 +f 146 151 155 +f 147 150 156 +f 152 147 156 +f 150 152 156 +f 147 152 157 +f 153 147 157 +f 152 153 157 +f 154 149 158 +f 155 154 158 +f 149 155 158 +f 146 149 159 +f 149 153 159 +f 159 153 160 +f 152 146 160 +f 153 152 160 +o convex_11 +v 0.025611 0.146460 -0.300092 +v -0.001024 -0.001010 -0.318524 +v 0.054282 -0.001010 -0.318524 +v -0.001024 -0.001010 -0.300092 +v -0.001024 0.146460 -0.318524 +v 0.054282 0.146460 -0.318524 +v 0.054282 -0.001010 -0.302140 +v -0.001024 0.146460 -0.300092 +v 0.054282 0.146460 -0.306236 +v 0.054282 0.058397 -0.302140 +v 0.015363 -0.001010 -0.300092 +f 167 161 171 +f 162 163 164 +f 163 162 165 +f 162 164 165 +f 165 161 166 +f 163 165 166 +f 164 163 167 +f 163 166 167 +f 164 161 168 +f 161 165 168 +f 165 164 168 +f 166 161 169 +f 167 166 169 +f 167 169 170 +f 161 167 170 +f 169 161 170 +f 161 164 171 +f 164 167 171 +o convex_12 +v 0.015361 -0.001010 -0.271390 +v 0.011265 0.146460 -0.160809 +v -0.001024 0.146460 -0.160809 +v 0.015361 -0.001010 -0.160809 +v -0.001024 -0.001010 -0.160809 +v -0.001024 0.146460 -0.271390 +v -0.001024 -0.001010 -0.271390 +v 0.011265 0.146460 -0.271390 +v 0.017411 0.054282 -0.259103 +v 0.015361 0.058382 -0.160809 +v 0.017411 -0.001010 -0.218151 +v 0.017411 0.054282 -0.218151 +v 0.017411 -0.001010 -0.259103 +v 0.015361 0.058382 -0.271390 +f 179 180 185 +f 173 174 175 +f 172 175 176 +f 175 174 176 +f 176 174 177 +f 174 173 177 +f 177 172 178 +f 172 176 178 +f 176 177 178 +f 177 173 179 +f 172 177 179 +f 179 173 180 +f 173 175 181 +f 175 172 182 +f 181 175 182 +f 180 173 183 +f 173 181 183 +f 182 180 183 +f 181 182 183 +f 172 180 184 +f 180 182 184 +f 182 172 184 +f 172 179 185 +f 180 172 185 +o convex_13 +v 0.031747 -0.001024 -0.250921 +v 0.021509 0.054282 -0.218157 +v 0.017413 0.054282 -0.218157 +v 0.017413 0.054282 -0.259121 +v 0.017413 -0.001024 -0.218157 +v 0.033797 0.054282 -0.232496 +v 0.017413 -0.001024 -0.259121 +v 0.031747 -0.001024 -0.226353 +v 0.031747 0.054282 -0.250921 +v 0.033797 -0.001024 -0.244778 +v 0.025604 -0.001024 -0.257068 +v 0.031747 0.054282 -0.226353 +v 0.025604 -0.001024 -0.220206 +v 0.021509 0.054282 -0.259121 +f 194 196 199 +f 188 187 189 +f 187 188 190 +f 188 189 190 +f 189 187 191 +f 186 190 192 +f 190 189 192 +f 190 186 193 +f 189 191 194 +f 194 191 195 +f 193 186 195 +f 191 193 195 +f 186 194 195 +f 186 192 196 +f 194 186 196 +f 191 187 197 +f 193 191 197 +f 193 197 198 +f 187 190 198 +f 190 193 198 +f 197 187 198 +f 192 189 199 +f 189 194 199 +f 196 192 199 +o convex_14 +v 0.054282 0.128024 -0.318521 +v 0.273452 0.193560 -0.281657 +v 0.273452 0.177174 -0.281657 +v 0.273452 0.193560 -0.318521 +v 0.054282 0.193560 -0.281657 +v 0.273452 0.128024 -0.318521 +v 0.054282 0.193560 -0.318521 +v 0.054282 0.128024 -0.306231 +v 0.273452 0.128024 -0.306231 +v 0.054282 0.177174 -0.281657 +f 207 202 209 +f 201 202 203 +f 202 201 204 +f 201 203 204 +f 203 202 205 +f 200 203 205 +f 203 200 206 +f 200 204 206 +f 204 203 206 +f 204 200 207 +f 200 205 207 +f 207 205 208 +f 205 202 208 +f 202 207 208 +f 202 204 209 +f 204 207 209 +o convex_15 +v 0.281659 0.130084 -0.281657 +v 0.273464 0.128024 -0.318521 +v 0.318524 0.128024 -0.318521 +v 0.273464 0.193560 -0.318521 +v 0.318524 0.193560 -0.281657 +v 0.273464 0.193560 -0.281657 +v 0.318524 0.193560 -0.318521 +v 0.318524 0.128024 -0.281657 +v 0.273464 0.142370 -0.281657 +v 0.279613 0.128024 -0.285753 +f 210 218 219 +f 212 211 213 +f 213 211 215 +f 210 214 215 +f 214 213 215 +f 212 213 216 +f 213 214 216 +f 214 212 216 +f 211 212 217 +f 214 210 217 +f 212 214 217 +f 215 211 218 +f 210 215 218 +f 211 217 219 +f 217 210 219 +f 218 211 219 +o convex_16 +v 0.265263 0.230440 -0.281649 +v 0.232492 0.193576 -0.287795 +v 0.232492 0.193576 -0.269366 +v 0.232492 0.230440 -0.287795 +v 0.263213 0.230440 -0.255024 +v 0.265263 0.193576 -0.255024 +v 0.265263 0.193576 -0.281649 +v 0.232492 0.230440 -0.269366 +v 0.248877 0.193576 -0.287795 +v 0.248877 0.230440 -0.287795 +f 220 228 229 +f 221 222 223 +f 220 223 224 +f 224 222 225 +f 222 221 225 +f 220 224 225 +f 220 225 226 +f 225 221 226 +f 223 222 227 +f 224 223 227 +f 222 224 227 +f 221 223 228 +f 220 226 228 +f 226 221 228 +f 223 220 229 +f 228 223 229 +o convex_17 +v 0.285747 0.230440 -0.257065 +v 0.265266 0.193576 -0.279595 +v 0.265266 0.230440 -0.279595 +v 0.267317 0.230440 -0.228395 +v 0.287796 0.193576 -0.228395 +v 0.267317 0.193576 -0.228395 +v 0.281650 0.193576 -0.265260 +v 0.287796 0.230440 -0.228395 +v 0.287796 0.193576 -0.248885 +v 0.275510 0.230440 -0.273455 +v 0.265266 0.193576 -0.250935 +f 235 233 240 +f 230 232 233 +f 231 234 235 +f 234 233 235 +f 234 231 236 +f 230 233 237 +f 233 234 237 +f 237 234 238 +f 236 230 238 +f 234 236 238 +f 230 237 238 +f 232 230 239 +f 231 232 239 +f 230 236 239 +f 236 231 239 +f 232 231 240 +f 233 232 240 +f 231 235 240 +o convex_18 +v 0.044040 0.146473 -0.035847 +v -0.001024 0.119838 -0.035847 +v 0.011270 0.119838 -0.035847 +v 0.044040 0.119838 0.001024 +v -0.001024 0.193573 0.001024 +v 0.044040 0.193573 -0.035847 +v -0.001024 0.119838 0.001024 +v -0.001024 0.193573 -0.035847 +v 0.044040 0.193573 0.001024 +v 0.033798 0.119838 -0.027650 +v 0.044040 0.119838 -0.011269 +v 0.029695 0.123931 -0.035847 +f 250 243 252 +f 243 242 241 +f 242 243 244 +f 241 242 246 +f 244 241 246 +f 242 244 247 +f 244 245 247 +f 245 242 247 +f 242 245 248 +f 246 242 248 +f 245 246 248 +f 245 244 249 +f 246 245 249 +f 244 246 249 +f 244 243 250 +f 244 250 251 +f 241 244 251 +f 250 241 251 +f 243 241 252 +f 241 250 252 +o convex_19 +v 0.044053 0.177173 -0.035847 +v 0.261169 0.193573 0.001024 +v 0.261169 0.119838 0.001024 +v 0.261169 0.193573 -0.035847 +v 0.044053 0.193573 0.001024 +v 0.044053 0.119838 -0.011269 +v 0.261169 0.119838 -0.011269 +v 0.044053 0.119838 0.001024 +v 0.044053 0.193573 -0.035847 +v 0.261169 0.177173 -0.035847 +f 259 253 262 +f 254 255 256 +f 255 254 257 +f 254 256 257 +f 257 253 258 +f 256 255 259 +f 255 258 259 +f 258 253 259 +f 255 257 260 +f 258 255 260 +f 257 258 260 +f 256 253 261 +f 253 257 261 +f 257 256 261 +f 253 256 262 +f 256 259 262 +o convex_20 +v 0.287802 0.054274 -0.062482 +v 0.300092 -0.001024 -0.160798 +v 0.318524 -0.001024 -0.160798 +v 0.318524 -0.001024 -0.046089 +v 0.318524 0.060421 -0.160798 +v 0.318524 0.060421 -0.046089 +v 0.283705 -0.001024 -0.070679 +v 0.300092 0.058376 -0.160798 +v 0.283705 0.054274 -0.087061 +v 0.300092 -0.001024 -0.046089 +v 0.306230 0.060421 -0.046089 +v 0.283705 -0.001024 -0.087061 +v 0.300092 0.058376 -0.046089 +v 0.283705 0.054274 -0.070679 +v 0.306230 0.060421 -0.160798 +f 271 273 277 +f 264 265 266 +f 265 264 267 +f 266 265 267 +f 266 267 268 +f 264 266 269 +f 267 264 270 +f 270 264 271 +f 266 268 272 +f 263 269 272 +f 269 266 272 +f 268 267 273 +f 272 268 273 +f 264 269 274 +f 271 264 274 +f 269 271 274 +f 263 272 275 +f 272 273 275 +f 275 273 276 +f 269 263 276 +f 271 269 276 +f 273 271 276 +f 263 275 276 +f 267 270 277 +f 270 271 277 +f 273 267 277 +o convex_21 +v 0.318523 0.148508 -0.046089 +v 0.306234 0.060436 -0.160798 +v 0.318523 0.060436 -0.160798 +v 0.306234 0.060436 -0.046089 +v 0.306234 0.148508 -0.160798 +v 0.318523 0.148508 -0.160798 +v 0.318523 0.060436 -0.046089 +v 0.306234 0.148508 -0.046089 +f 282 281 285 +f 279 280 281 +f 280 279 282 +f 279 281 282 +f 278 280 283 +f 282 278 283 +f 280 282 283 +f 280 278 284 +f 281 280 284 +f 278 281 284 +f 281 278 285 +f 278 282 285 +o convex_22 +v 0.318519 0.148508 -0.017411 +v 0.261169 -0.001024 -0.015359 +v 0.261169 -0.001024 0.001024 +v 0.318519 -0.001024 0.001024 +v 0.261169 0.148508 0.001024 +v 0.318519 -0.001024 -0.017411 +v 0.318519 0.148508 0.001024 +v 0.261169 0.148508 -0.011263 +v 0.291887 0.148508 -0.017411 +v 0.261169 0.058402 -0.015359 +v 0.300083 -0.001024 -0.017411 +f 294 291 296 +f 288 287 289 +f 287 288 290 +f 288 289 290 +f 289 287 291 +f 286 289 291 +f 289 286 292 +f 286 290 292 +f 290 289 292 +f 290 286 293 +f 287 290 293 +f 286 291 294 +f 293 286 294 +f 293 294 295 +f 287 293 295 +f 294 287 295 +f 291 287 296 +f 287 294 296 +o convex_23 +v 0.037897 0.193576 -0.109586 +v 0.080906 0.230440 -0.109586 +v 0.050188 0.230440 -0.068621 +v 0.080906 0.193576 -0.109586 +v 0.054289 0.230440 -0.123919 +v 0.029702 0.193576 -0.068621 +v 0.029702 0.230440 -0.091159 +v 0.080906 0.193576 -0.128024 +v 0.050188 0.193576 -0.068621 +v 0.080906 0.230440 -0.128024 +v 0.029702 0.230440 -0.068621 +v 0.064515 0.193576 -0.128024 +v 0.029702 0.193576 -0.091159 +v 0.037897 0.230440 -0.109586 +v 0.048138 0.193576 -0.119825 +v 0.064515 0.230440 -0.128024 +f 306 308 312 +f 298 299 300 +f 299 298 301 +f 297 300 302 +f 299 301 303 +f 298 300 304 +f 300 297 304 +f 300 299 305 +f 299 302 305 +f 302 300 305 +f 301 298 306 +f 298 304 306 +f 302 299 307 +f 299 303 307 +f 303 302 307 +f 304 297 308 +f 306 304 308 +f 297 302 309 +f 303 297 309 +f 302 303 309 +f 297 303 310 +f 303 301 310 +f 308 297 311 +f 301 308 311 +f 297 310 311 +f 310 301 311 +f 301 306 312 +f 308 301 312 +o convex_24 +v 0.103446 0.193576 -0.123919 +v 0.128020 0.230440 -0.068621 +v 0.128020 0.193576 -0.068621 +v 0.080911 0.230440 -0.109586 +v 0.119825 0.230440 -0.109586 +v 0.107537 0.193576 -0.068621 +v 0.080911 0.193576 -0.128024 +v 0.093209 0.230440 -0.128024 +v 0.107537 0.230440 -0.068621 +v 0.125968 0.193576 -0.097305 +v 0.080911 0.193576 -0.109586 +v 0.128020 0.230440 -0.091159 +v 0.080911 0.230440 -0.128024 +v 0.119825 0.193576 -0.109586 +v 0.109584 0.230440 -0.119825 +f 317 326 327 +f 316 314 317 +f 313 315 318 +f 315 314 318 +f 313 318 319 +f 316 317 320 +f 313 319 320 +f 314 316 321 +f 318 314 321 +f 316 318 321 +f 315 313 322 +f 318 316 323 +f 316 319 323 +f 319 318 323 +f 314 315 324 +f 317 314 324 +f 315 322 324 +f 322 317 324 +f 319 316 325 +f 316 320 325 +f 320 319 325 +f 322 313 326 +f 317 322 326 +f 313 320 327 +f 320 317 327 +f 326 313 327 +o convex_25 +v 0.117777 0.058382 -0.103447 +v 0.089105 0.111629 -0.099351 +v 0.089105 0.111629 -0.097303 +v 0.119828 0.117782 -0.117777 +v 0.103447 0.058382 -0.119828 +v 0.099351 0.117782 -0.089105 +v 0.103447 0.117782 -0.119828 +v 0.119828 0.056331 -0.119828 +v 0.119828 0.117782 -0.105489 +v 0.107537 0.064529 -0.103447 +v 0.093207 0.117782 -0.089105 +f 330 337 338 +f 330 329 332 +f 333 331 334 +f 332 329 334 +f 328 332 335 +f 334 331 335 +f 332 334 335 +f 333 328 336 +f 331 333 336 +f 328 335 336 +f 335 331 336 +f 332 328 337 +f 330 332 337 +f 329 330 338 +f 328 333 338 +f 333 334 338 +f 334 329 338 +f 337 328 338 +o convex_26 +v 0.197669 0.175135 -0.199720 +v 0.144413 0.156703 -0.224298 +v 0.144413 0.175135 -0.224298 +v 0.123927 0.175135 -0.203817 +v 0.195612 0.156703 -0.199720 +v 0.185377 0.156703 -0.220199 +v 0.123927 0.156703 -0.203817 +v 0.193569 0.175135 -0.216102 +v 0.123927 0.156703 -0.216102 +v 0.173084 0.175135 -0.224298 +v 0.125985 0.175135 -0.218151 +v 0.197669 0.156703 -0.212009 +f 344 346 350 +f 339 341 342 +f 339 342 343 +f 343 340 344 +f 343 342 345 +f 340 343 345 +f 341 339 346 +f 340 345 347 +f 345 342 347 +f 340 341 348 +f 344 340 348 +f 341 346 348 +f 346 344 348 +f 341 340 349 +f 342 341 349 +f 340 347 349 +f 347 342 349 +f 339 343 350 +f 343 344 350 +f 346 339 350 +o convex_27 +v 0.197676 0.193576 -0.109586 +v 0.240680 0.230440 -0.109586 +v 0.209966 0.230440 -0.068621 +v 0.240680 0.193576 -0.109586 +v 0.214061 0.230440 -0.123919 +v 0.189481 0.193576 -0.068621 +v 0.189481 0.230440 -0.091159 +v 0.240680 0.193576 -0.128024 +v 0.209966 0.193576 -0.068621 +v 0.240680 0.230440 -0.128024 +v 0.189481 0.230440 -0.068621 +v 0.224291 0.193576 -0.128024 +v 0.189481 0.193576 -0.091159 +v 0.197676 0.230440 -0.109586 +v 0.207916 0.193576 -0.119825 +v 0.224291 0.230440 -0.128024 +f 360 362 366 +f 352 353 354 +f 353 352 355 +f 351 354 356 +f 353 355 357 +f 352 354 358 +f 354 351 358 +f 354 353 359 +f 353 356 359 +f 356 354 359 +f 355 352 360 +f 352 358 360 +f 356 353 361 +f 353 357 361 +f 357 356 361 +f 358 351 362 +f 360 358 362 +f 351 356 363 +f 357 351 363 +f 356 357 363 +f 351 357 364 +f 357 355 364 +f 362 351 365 +f 355 362 365 +f 351 364 365 +f 364 355 365 +f 355 360 366 +f 362 355 366 +o convex_28 +v 0.263221 0.193576 -0.123919 +v 0.287789 0.230440 -0.068621 +v 0.287789 0.193576 -0.068621 +v 0.240685 0.230440 -0.109586 +v 0.279600 0.230440 -0.109586 +v 0.267311 0.193576 -0.068621 +v 0.240685 0.193576 -0.128024 +v 0.252984 0.230440 -0.128024 +v 0.267311 0.230440 -0.068621 +v 0.285742 0.193576 -0.097305 +v 0.240685 0.193576 -0.109586 +v 0.287789 0.230440 -0.091159 +v 0.240685 0.230440 -0.128024 +v 0.279600 0.193576 -0.109586 +v 0.269358 0.230440 -0.119825 +f 371 380 381 +f 370 368 371 +f 367 369 372 +f 369 368 372 +f 367 372 373 +f 370 371 374 +f 367 373 374 +f 368 370 375 +f 372 368 375 +f 370 372 375 +f 369 367 376 +f 372 370 377 +f 370 373 377 +f 373 372 377 +f 368 369 378 +f 371 368 378 +f 369 376 378 +f 376 371 378 +f 373 370 379 +f 370 374 379 +f 374 373 379 +f 376 367 380 +f 371 376 380 +f 367 374 381 +f 374 371 381 +f 380 367 381 +o convex_29 +v 0.191518 0.117782 -0.099351 +v 0.156702 0.070673 -0.107539 +v 0.193568 0.054282 -0.121876 +v 0.175142 0.054282 -0.093202 +v 0.156702 0.117782 -0.101398 +v 0.193568 0.117782 -0.115729 +v 0.195617 0.054282 -0.103447 +v 0.156702 0.117782 -0.093202 +v 0.156702 0.070673 -0.093202 +v 0.193568 0.085019 -0.121876 +v 0.166949 0.054282 -0.107539 +v 0.175142 0.117782 -0.093202 +v 0.195617 0.117782 -0.115729 +v 0.156702 0.085019 -0.107539 +v 0.195617 0.117782 -0.103447 +v 0.195617 0.054282 -0.121876 +v 0.191518 0.054282 -0.099351 +f 388 382 398 +f 386 382 387 +f 385 384 388 +f 382 386 389 +f 386 383 389 +f 389 383 390 +f 385 389 390 +f 384 383 391 +f 386 387 391 +f 383 384 392 +f 384 385 392 +f 390 383 392 +f 385 390 392 +f 385 382 393 +f 389 385 393 +f 382 389 393 +f 387 382 394 +f 391 387 394 +f 383 386 395 +f 391 383 395 +f 386 391 395 +f 382 388 396 +f 394 382 396 +f 388 394 396 +f 388 384 397 +f 384 391 397 +f 394 388 397 +f 391 394 397 +f 382 385 398 +f 385 388 398 +o convex_30 +v 0.156690 0.117778 -0.093202 +v 0.113689 0.117778 -0.115731 +v 0.119831 0.056331 -0.119828 +v 0.121886 0.056331 -0.101401 +v 0.156690 0.070680 -0.107537 +v 0.123931 0.154646 -0.115731 +v 0.113689 0.154646 -0.103447 +v 0.154649 0.154646 -0.101399 +v 0.140307 0.056331 -0.093202 +v 0.140307 0.154646 -0.093202 +v 0.156690 0.070680 -0.093202 +v 0.125981 0.085019 -0.119828 +v 0.150548 0.056331 -0.107537 +v 0.113689 0.154646 -0.115731 +v 0.113689 0.117778 -0.103447 +v 0.156690 0.085019 -0.107537 +v 0.154649 0.154646 -0.093202 +v 0.125981 0.056331 -0.119828 +v 0.152599 0.154646 -0.103447 +f 414 404 417 +f 404 405 406 +f 402 401 407 +f 405 407 408 +f 406 405 408 +f 407 399 408 +f 403 399 409 +f 399 407 409 +f 407 401 411 +f 403 409 411 +f 409 407 411 +f 401 400 412 +f 405 404 412 +f 400 405 412 +f 410 401 412 +f 404 410 412 +f 400 401 413 +f 401 402 413 +f 405 400 413 +f 402 407 413 +f 407 405 413 +f 399 403 414 +f 406 399 414 +f 403 410 414 +f 410 404 414 +f 399 406 415 +f 408 399 415 +f 406 408 415 +f 401 410 416 +f 410 403 416 +f 411 401 416 +f 403 411 416 +f 404 406 417 +f 406 414 417 +o convex_31 +v 0.015361 -0.001010 -0.300086 +v 0.011265 0.146460 -0.271411 +v 0.017411 0.146460 -0.291893 +v -0.001024 0.146460 -0.300086 +v -0.001024 -0.001010 -0.271411 +v 0.015361 -0.001010 -0.271411 +v -0.001024 -0.001010 -0.300086 +v -0.001024 0.146460 -0.271411 +v 0.017411 0.146460 -0.300086 +v 0.015361 0.058397 -0.271411 +v 0.017411 0.087039 -0.300086 +f 426 420 428 +f 419 420 421 +f 419 422 423 +f 422 418 423 +f 421 418 424 +f 418 422 424 +f 422 421 424 +f 419 421 425 +f 421 422 425 +f 422 419 425 +f 421 420 426 +f 418 421 426 +f 420 419 427 +f 419 423 427 +f 423 420 427 +f 423 418 428 +f 420 423 428 +f 418 426 428 +o convex_32 +v 0.025606 0.097306 -0.300089 +v 0.035842 0.146460 -0.273460 +v 0.033797 0.146460 -0.273460 +v 0.017411 0.146460 -0.300089 +v 0.017411 0.099355 -0.289842 +v 0.041992 0.146460 -0.285755 +v 0.017411 0.146460 -0.289842 +v 0.027654 0.146460 -0.300089 +v 0.017411 0.089110 -0.300089 +v 0.041992 0.142356 -0.279609 +f 429 434 438 +f 431 430 432 +f 430 431 433 +f 432 430 434 +f 431 432 435 +f 432 433 435 +f 433 431 435 +f 429 432 436 +f 434 429 436 +f 432 434 436 +f 432 429 437 +f 429 433 437 +f 433 432 437 +f 433 429 438 +f 430 433 438 +f 434 430 438 +o convex_33 +v 0.054282 -0.001024 -0.318524 +v 0.205863 0.128024 -0.306237 +v 0.205863 0.058384 -0.302139 +v 0.054282 0.128024 -0.306237 +v 0.205863 0.128024 -0.318524 +v 0.205863 -0.001024 -0.318524 +v 0.054282 -0.001024 -0.302139 +v 0.054282 0.128024 -0.318524 +v 0.205863 -0.001024 -0.302139 +v 0.054282 0.058384 -0.302139 +f 442 445 448 +f 441 440 442 +f 440 441 443 +f 442 440 443 +f 439 443 444 +f 443 441 444 +f 442 439 445 +f 439 444 445 +f 439 442 446 +f 443 439 446 +f 442 443 446 +f 444 441 447 +f 441 445 447 +f 445 444 447 +f 441 442 448 +f 445 441 448 +o convex_34 +v 0.085004 0.054282 -0.283705 +v 0.058387 -0.001024 -0.302135 +v 0.058387 -0.001024 -0.295990 +v 0.099347 -0.001024 -0.302135 +v 0.058387 0.054282 -0.302135 +v 0.099347 0.054282 -0.302135 +v 0.091147 -0.001024 -0.285753 +v 0.066579 0.054282 -0.285753 +v 0.072722 -0.001024 -0.283705 +v 0.097294 0.054282 -0.291897 +v 0.099347 -0.001024 -0.295990 +v 0.060432 0.054282 -0.291897 +v 0.066579 -0.001024 -0.285753 +f 460 451 461 +f 451 450 452 +f 450 451 453 +f 452 450 453 +f 453 449 454 +f 452 453 454 +f 451 452 455 +f 449 453 456 +f 455 449 457 +f 451 455 457 +f 449 456 457 +f 454 449 458 +f 449 455 458 +f 458 455 459 +f 452 454 459 +f 455 452 459 +f 454 458 459 +f 453 451 460 +f 456 453 460 +f 456 460 461 +f 451 457 461 +f 457 456 461 +o convex_35 +v 0.300092 0.058376 -0.160810 +v 0.300092 -0.001024 -0.281653 +v 0.318524 -0.001024 -0.281653 +v 0.318524 0.060421 -0.281653 +v 0.318524 -0.001024 -0.160810 +v 0.283705 -0.001024 -0.246817 +v 0.283705 0.054274 -0.246817 +v 0.318524 0.060421 -0.160810 +v 0.300092 -0.001024 -0.160810 +v 0.300092 0.058376 -0.281653 +v 0.283705 -0.001024 -0.230435 +v 0.283705 0.054274 -0.230435 +v 0.306230 0.060421 -0.160810 +v 0.306230 0.060421 -0.281653 +f 468 474 475 +f 464 463 465 +f 463 464 466 +f 464 465 466 +f 463 466 467 +f 463 467 468 +f 462 466 469 +f 466 465 469 +f 466 462 470 +f 467 466 470 +f 465 463 471 +f 463 468 471 +f 468 467 472 +f 470 462 472 +f 467 470 472 +f 472 462 473 +f 468 472 473 +f 462 469 474 +f 469 465 474 +f 473 462 474 +f 468 473 474 +f 465 471 475 +f 471 468 475 +f 474 465 475 +o convex_36 +v 0.318523 0.125976 -0.160810 +v 0.306234 0.060434 -0.281653 +v 0.318523 0.060434 -0.281653 +v 0.306234 0.060434 -0.160810 +v 0.306234 0.125976 -0.281653 +v 0.318523 0.125976 -0.281653 +v 0.318523 0.060434 -0.160810 +v 0.306234 0.125976 -0.160810 +f 480 479 483 +f 477 478 479 +f 478 477 480 +f 477 479 480 +f 476 478 481 +f 480 476 481 +f 478 480 481 +f 478 476 482 +f 479 478 482 +f 476 479 482 +f 479 476 483 +f 476 480 483 +o convex_37 +v 0.289847 0.193573 -0.160810 +v 0.306230 0.125982 -0.281653 +v 0.318521 0.125982 -0.281653 +v 0.289847 0.193573 -0.281653 +v 0.318521 0.193573 -0.281653 +v 0.318521 0.125982 -0.160810 +v 0.318521 0.193573 -0.160810 +v 0.289847 0.171031 -0.160810 +v 0.306230 0.125982 -0.160810 +v 0.289847 0.171031 -0.281653 +f 491 487 493 +f 486 485 487 +f 487 484 488 +f 486 487 488 +f 486 488 489 +f 485 486 489 +f 484 489 490 +f 488 484 490 +f 489 488 490 +f 484 487 491 +f 489 484 491 +f 489 491 492 +f 485 489 492 +f 491 485 492 +f 487 485 493 +f 485 491 493 +o convex_38 +v 0.015361 0.058382 -0.035847 +v 0.015361 -0.001024 -0.099338 +v 0.017411 -0.001024 -0.099338 +v -0.001024 0.121879 -0.160798 +v -0.001024 -0.001012 -0.035847 +v -0.001024 -0.001012 -0.160798 +v -0.001024 0.121879 -0.035847 +v 0.011265 0.121879 -0.160798 +v 0.015361 -0.001012 -0.160798 +v 0.011265 0.121879 -0.035847 +v 0.015361 -0.001012 -0.035847 +v 0.017411 0.054280 -0.099338 +v 0.017411 0.054280 -0.056334 +v 0.015361 0.058382 -0.160798 +v 0.017411 -0.001024 -0.056334 +f 496 506 508 +f 495 498 499 +f 496 495 499 +f 498 497 499 +f 498 494 500 +f 497 498 500 +f 499 497 501 +f 497 500 501 +f 496 499 502 +f 499 501 502 +f 500 494 503 +f 501 500 503 +f 494 498 504 +f 496 502 505 +f 501 503 505 +f 505 503 506 +f 503 494 506 +f 494 504 506 +f 496 505 506 +f 502 501 507 +f 505 502 507 +f 501 505 507 +f 495 496 508 +f 498 495 508 +f 504 498 508 +f 506 504 508 +o convex_39 +v 0.029699 -0.001024 -0.093196 +v 0.023557 0.054282 -0.058379 +v 0.017413 0.054282 -0.058379 +v 0.017413 0.054282 -0.099339 +v 0.017413 -0.001024 -0.058379 +v 0.033797 0.054282 -0.070673 +v 0.017413 -0.001024 -0.099339 +v 0.033797 -0.001024 -0.070673 +v 0.033797 0.054282 -0.085004 +v 0.025604 0.054282 -0.097294 +v 0.023557 -0.001024 -0.058379 +v 0.033797 -0.001024 -0.085004 +f 509 517 520 +f 511 510 512 +f 510 511 513 +f 511 512 513 +f 512 510 514 +f 509 513 515 +f 513 512 515 +f 513 509 516 +f 514 510 516 +f 514 516 517 +f 512 514 517 +f 512 517 518 +f 509 515 518 +f 515 512 518 +f 517 509 518 +f 510 513 519 +f 513 516 519 +f 516 510 519 +f 516 509 520 +f 517 516 520 +o convex_40 +v 0.027653 0.193566 -0.035847 +v -0.001021 0.121886 -0.160798 +v 0.011270 0.121886 -0.160798 +v -0.001021 0.121886 -0.035847 +v -0.001021 0.193566 -0.160798 +v -0.001021 0.193566 -0.035847 +v 0.027653 0.193566 -0.160798 +v 0.027653 0.173083 -0.035847 +v 0.011270 0.121886 -0.035847 +v 0.027653 0.173083 -0.160798 +f 527 528 530 +f 522 523 524 +f 523 522 525 +f 522 524 525 +f 524 521 526 +f 521 525 526 +f 525 524 526 +f 525 521 527 +f 523 525 527 +f 521 524 528 +f 527 521 528 +f 524 523 529 +f 523 528 529 +f 528 524 529 +f 523 527 530 +f 528 523 530 +o convex_41 +v 0.035847 0.119831 -0.011269 +v -0.001024 -0.001024 -0.035847 +v 0.015365 -0.001024 -0.035847 +v 0.035847 -0.001024 0.001024 +v -0.001024 0.119831 0.001024 +v -0.001024 0.119831 -0.035847 +v -0.001024 -0.001024 0.001024 +v 0.033797 0.119831 -0.025604 +v 0.035847 0.119831 0.001024 +v 0.035847 -0.001024 -0.015365 +v 0.025604 0.119831 -0.033797 +v 0.011269 0.119831 -0.035847 +f 536 541 542 +f 532 533 534 +f 532 535 536 +f 533 532 536 +f 535 531 536 +f 532 534 537 +f 534 535 537 +f 535 532 537 +f 536 531 538 +f 534 531 539 +f 531 535 539 +f 535 534 539 +f 534 533 540 +f 531 534 540 +f 533 538 540 +f 538 531 540 +f 538 533 541 +f 536 538 541 +f 533 536 542 +f 541 533 542 +o convex_42 +v 0.093195 -0.001024 -0.029701 +v 0.035860 -0.001024 -0.015365 +v 0.066579 -0.001024 -0.031746 +v 0.103444 0.119831 0.001024 +v 0.103444 -0.001024 0.001024 +v 0.035860 0.119831 0.001024 +v 0.072712 0.054305 -0.033798 +v 0.035860 -0.001024 0.001024 +v 0.035860 0.119831 -0.011267 +v 0.103444 0.119831 -0.011267 +v 0.091144 0.054305 -0.031746 +v 0.103444 -0.001024 -0.015365 +v 0.035860 0.058386 -0.015365 +v 0.084998 -0.001024 -0.033798 +v 0.097291 0.054305 -0.025604 +v 0.066579 0.054305 -0.031746 +v 0.084998 0.054305 -0.033798 +f 553 556 559 +f 543 544 545 +f 544 543 547 +f 547 546 548 +f 544 547 550 +f 548 544 550 +f 547 548 550 +f 544 548 551 +f 548 546 551 +f 549 551 552 +f 546 547 552 +f 551 546 552 +f 547 543 554 +f 552 547 554 +f 545 544 555 +f 544 551 555 +f 543 545 556 +f 545 549 556 +f 553 543 556 +f 543 553 557 +f 553 552 557 +f 552 554 557 +f 554 543 557 +f 549 545 558 +f 551 549 558 +f 545 555 558 +f 555 551 558 +f 549 552 559 +f 552 553 559 +f 556 549 559 +o convex_43 +v 0.218153 0.119831 -0.011265 +v 0.103444 -0.001024 -0.015363 +v 0.103444 -0.001024 0.001024 +v 0.218153 -0.001024 0.001024 +v 0.103444 0.119831 0.001024 +v 0.218153 -0.001024 -0.015363 +v 0.103444 0.119831 -0.011265 +v 0.218153 0.119831 0.001024 +v 0.218153 0.058386 -0.015363 +v 0.103444 0.058386 -0.015363 +f 568 561 569 +f 562 561 563 +f 561 562 564 +f 562 563 564 +f 563 561 565 +f 560 563 565 +f 564 560 566 +f 561 564 566 +f 563 560 567 +f 560 564 567 +f 564 563 567 +f 565 561 568 +f 560 565 568 +f 566 560 568 +f 566 568 569 +f 561 566 569 +o convex_44 +v 0.244779 -0.001024 -0.033798 +v 0.218157 -0.001024 -0.021507 +v 0.218157 -0.001024 0.001024 +v 0.261165 0.119831 0.001024 +v 0.218157 0.119831 -0.011267 +v 0.261165 -0.001024 0.001024 +v 0.250922 0.054305 -0.031746 +v 0.218157 0.119831 0.001024 +v 0.226351 0.054305 -0.031746 +v 0.261165 0.119831 -0.011267 +v 0.259115 -0.001024 -0.021507 +v 0.232498 0.054305 -0.033798 +v 0.226351 -0.001024 -0.031746 +v 0.220208 0.054305 -0.025604 +v 0.257069 0.054305 -0.025604 +v 0.261165 -0.001024 -0.015365 +v 0.250922 -0.001024 -0.031746 +v 0.244779 0.054305 -0.033798 +f 581 579 587 +f 572 571 570 +f 571 572 574 +f 572 570 575 +f 573 572 575 +f 572 573 577 +f 573 574 577 +f 574 572 577 +f 574 573 579 +f 573 575 579 +f 575 570 580 +f 578 574 581 +f 574 579 581 +f 570 571 582 +f 581 570 582 +f 578 581 582 +f 571 574 583 +f 574 578 583 +f 582 571 583 +f 578 582 583 +f 576 579 584 +f 579 580 584 +f 579 575 585 +f 580 579 585 +f 575 580 585 +f 570 576 586 +f 580 570 586 +f 576 584 586 +f 584 580 586 +f 576 570 587 +f 579 576 587 +f 570 581 587 +o convex_45 +v 0.300092 -0.001024 -0.046086 +v 0.318522 0.085008 -0.017411 +v 0.318522 0.085008 -0.046086 +v 0.318522 -0.001024 -0.017411 +v 0.300092 0.058371 -0.017411 +v 0.318522 -0.001024 -0.046086 +v 0.306236 0.085008 -0.046086 +v 0.300092 -0.001024 -0.017411 +v 0.300092 0.058371 -0.046086 +v 0.306236 0.085008 -0.017411 +f 594 592 597 +f 590 589 591 +f 591 589 592 +f 588 590 593 +f 590 591 593 +f 591 588 593 +f 590 588 594 +f 589 590 594 +f 588 591 595 +f 592 588 595 +f 591 592 595 +f 588 592 596 +f 594 588 596 +f 592 594 596 +f 592 589 597 +f 589 594 597 +o convex_46 +v 0.318524 0.148502 -0.017411 +v 0.273464 0.142361 -0.035844 +v 0.273464 0.142361 -0.033795 +v 0.318524 0.085008 -0.046086 +v 0.298036 0.087059 -0.017411 +v 0.318524 0.148502 -0.046086 +v 0.318524 0.085008 -0.017411 +v 0.289850 0.148502 -0.017411 +v 0.306227 0.085008 -0.046086 +v 0.277562 0.148502 -0.044037 +v 0.287803 0.103444 -0.019463 +f 606 602 608 +f 598 601 603 +f 601 598 604 +f 598 602 604 +f 602 598 605 +f 598 603 605 +f 603 601 606 +f 601 604 606 +f 604 602 606 +f 599 600 607 +f 600 605 607 +f 605 603 607 +f 606 599 607 +f 603 606 607 +f 600 599 608 +f 605 600 608 +f 602 605 608 +f 599 606 608 +o convex_47 +v 0.259116 0.054291 -0.300092 +v 0.205863 -0.001024 -0.318524 +v 0.277549 -0.001024 -0.318524 +v 0.205863 0.128024 -0.318524 +v 0.205863 -0.001024 -0.302140 +v 0.277549 0.128024 -0.318524 +v 0.205863 0.128024 -0.306236 +v 0.277549 -0.001024 -0.302140 +v 0.277549 0.128024 -0.306236 +v 0.218164 0.054291 -0.300092 +v 0.259116 -0.001024 -0.300092 +v 0.218164 -0.001024 -0.300092 +v 0.277549 0.058384 -0.302140 +v 0.205863 0.058384 -0.302140 +f 618 615 622 +f 611 610 612 +f 610 611 613 +f 612 610 613 +f 611 612 614 +f 614 612 615 +f 612 613 615 +f 613 611 616 +f 611 614 616 +f 616 614 617 +f 614 615 617 +f 615 609 617 +f 609 615 618 +f 616 609 619 +f 613 616 619 +f 609 618 619 +f 619 618 620 +f 618 613 620 +f 613 619 620 +f 609 616 621 +f 616 617 621 +f 617 609 621 +f 615 613 622 +f 613 618 622 +o convex_48 +v 0.244778 0.054282 -0.283703 +v 0.218157 -0.001024 -0.300089 +v 0.218157 -0.001024 -0.295991 +v 0.259121 -0.001024 -0.300089 +v 0.218157 0.054282 -0.300089 +v 0.259121 0.054282 -0.300089 +v 0.232496 -0.001024 -0.283703 +v 0.226353 0.054282 -0.285753 +v 0.250921 -0.001024 -0.285753 +v 0.257068 0.054282 -0.291896 +v 0.226353 -0.001024 -0.285753 +v 0.220206 0.054282 -0.291896 +v 0.259121 -0.001024 -0.295991 +f 632 631 635 +f 625 624 626 +f 624 625 627 +f 626 624 627 +f 627 623 628 +f 626 627 628 +f 625 626 629 +f 623 627 630 +f 629 623 630 +f 629 626 631 +f 623 629 631 +f 628 623 632 +f 623 631 632 +f 625 629 633 +f 629 630 633 +f 633 630 634 +f 627 625 634 +f 630 627 634 +f 625 633 634 +f 626 628 635 +f 631 626 635 +f 628 632 635 +o convex_49 +v 0.291899 0.128024 -0.300092 +v 0.277564 -0.001024 -0.318524 +v 0.318524 -0.001024 -0.318524 +v 0.277564 0.128024 -0.318524 +v 0.277564 -0.001024 -0.302140 +v 0.318524 0.128024 -0.300092 +v 0.318524 -0.001024 -0.300092 +v 0.318524 0.128024 -0.318524 +v 0.277564 0.128024 -0.306236 +v 0.300083 -0.001024 -0.300092 +v 0.277564 0.058384 -0.302140 +f 644 640 646 +f 638 637 639 +f 637 638 640 +f 639 637 640 +f 639 636 641 +f 640 638 642 +f 638 641 642 +f 641 636 642 +f 638 639 643 +f 641 638 643 +f 639 641 643 +f 636 639 644 +f 639 640 644 +f 636 640 645 +f 642 636 645 +f 640 642 645 +f 640 636 646 +f 636 644 646 +o convex_50 +v 0.259101 0.177189 -0.160810 +v 0.054282 0.177189 -0.281653 +v 0.054282 0.175137 -0.271398 +v 0.054282 0.193569 -0.160810 +v 0.259101 0.193569 -0.281653 +v 0.259101 0.175137 -0.267293 +v 0.054282 0.193569 -0.281653 +v 0.259101 0.193569 -0.160810 +v 0.093221 0.175137 -0.160810 +v 0.054282 0.177189 -0.160810 +v 0.259101 0.177189 -0.281653 +v 0.224273 0.175137 -0.160810 +f 655 652 658 +f 648 649 650 +f 651 647 652 +f 648 650 653 +f 650 651 653 +f 651 648 653 +f 650 647 654 +f 647 651 654 +f 651 650 654 +f 647 650 655 +f 649 652 655 +f 650 649 656 +f 649 655 656 +f 655 650 656 +f 649 648 657 +f 648 651 657 +f 651 652 657 +f 652 649 657 +f 652 647 658 +f 647 655 658 +o convex_51 +v 0.285745 0.128029 -0.281653 +v 0.261175 0.173089 -0.250933 +v 0.259124 0.173089 -0.250933 +v 0.289847 0.173089 -0.281653 +v 0.271415 0.173089 -0.281653 +v 0.259124 0.158745 -0.269359 +v 0.289847 0.128029 -0.279599 +v 0.259124 0.162843 -0.250933 +v 0.271415 0.148512 -0.281653 +v 0.259124 0.173089 -0.269359 +v 0.289847 0.173089 -0.279599 +f 665 662 669 +f 661 660 662 +f 662 659 663 +f 661 662 663 +f 659 662 665 +f 660 661 666 +f 661 664 666 +f 664 659 666 +f 659 665 666 +f 665 660 666 +f 663 659 667 +f 664 663 667 +f 659 664 667 +f 661 663 668 +f 664 661 668 +f 663 664 668 +f 662 660 669 +f 660 665 669 +o convex_52 +v 0.259124 0.173089 -0.269351 +v 0.289847 0.193571 -0.160810 +v 0.259124 0.193571 -0.160810 +v 0.289847 0.193571 -0.281653 +v 0.289847 0.175140 -0.160810 +v 0.259124 0.193571 -0.281653 +v 0.289847 0.173089 -0.281653 +v 0.259124 0.177189 -0.160810 +v 0.259124 0.177189 -0.281653 +v 0.259124 0.173089 -0.250910 +f 674 677 679 +f 672 671 673 +f 671 672 674 +f 673 671 674 +f 670 672 675 +f 672 673 675 +f 675 673 676 +f 673 674 676 +f 672 670 677 +f 674 672 677 +f 670 675 678 +f 675 676 678 +f 676 670 678 +f 670 676 679 +f 676 674 679 +f 677 670 679 +o convex_53 +v 0.060421 0.173089 -0.050189 +v 0.027653 0.128029 -0.037900 +v 0.029706 0.128029 -0.037900 +v 0.060421 0.162843 -0.068621 +v 0.027653 0.173089 -0.037900 +v 0.046085 0.148512 -0.035850 +v 0.058374 0.173089 -0.068621 +v 0.046085 0.173089 -0.035850 +v 0.060421 0.160792 -0.050189 +v 0.027653 0.128029 -0.035850 +f 685 687 689 +f 682 681 683 +f 683 681 686 +f 680 683 686 +f 684 680 686 +f 681 684 686 +f 680 684 687 +f 685 680 687 +f 682 683 688 +f 683 680 688 +f 685 682 688 +f 680 685 688 +f 681 682 689 +f 684 681 689 +f 682 685 689 +f 687 684 689 +o convex_54 +v 0.058374 0.173089 -0.068626 +v 0.060421 0.193571 -0.035847 +v 0.027653 0.193571 -0.035847 +v 0.060421 0.193571 -0.160798 +v 0.027653 0.175140 -0.160798 +v 0.027653 0.173089 -0.035847 +v 0.027653 0.193571 -0.160798 +v 0.060421 0.177189 -0.160798 +v 0.060421 0.177189 -0.035847 +v 0.060421 0.173089 -0.050194 +f 698 695 699 +f 692 691 693 +f 692 694 695 +f 691 692 695 +f 694 690 695 +f 692 693 696 +f 693 694 696 +f 694 692 696 +f 693 691 697 +f 690 694 697 +f 694 693 697 +f 691 695 698 +f 697 691 698 +f 697 698 699 +f 695 690 699 +f 690 697 699 +o convex_55 +v 0.291893 0.095257 -0.300087 +v 0.291893 0.128024 -0.281655 +v 0.285752 0.128024 -0.281655 +v 0.289845 0.128024 -0.300087 +v 0.298037 0.128024 -0.300087 +v 0.298037 0.097302 -0.291894 +v 0.279607 0.125971 -0.287801 +v 0.298037 0.128024 -0.287801 +f 705 704 707 +f 702 701 703 +f 700 703 704 +f 703 701 704 +f 701 702 705 +f 700 704 705 +f 705 702 706 +f 702 703 706 +f 703 700 706 +f 700 705 706 +f 704 701 707 +f 701 705 707 +o convex_56 +v 0.318522 0.128024 -0.281655 +v 0.298042 0.087046 -0.300087 +v 0.300091 -0.001024 -0.300087 +v 0.318522 0.128024 -0.300087 +v 0.318522 -0.001024 -0.281655 +v 0.300091 -0.001024 -0.281655 +v 0.298042 0.128024 -0.289848 +v 0.318522 -0.001024 -0.300087 +v 0.306236 0.128024 -0.281655 +v 0.298042 0.128024 -0.300087 +v 0.300091 0.058384 -0.281655 +f 716 714 718 +f 710 709 711 +f 711 708 712 +f 710 712 713 +f 709 710 713 +f 712 708 713 +f 708 711 714 +f 709 713 714 +f 710 711 715 +f 711 712 715 +f 712 710 715 +f 713 708 716 +f 708 714 716 +f 711 709 717 +f 709 714 717 +f 714 711 717 +f 714 713 718 +f 713 716 718 +o convex_57 +v 0.111631 -0.001024 -0.207910 +v 0.107535 0.056331 -0.166944 +v 0.093206 0.056331 -0.166944 +v 0.119827 0.056331 -0.212008 +v 0.107535 -0.001024 -0.166944 +v 0.087060 -0.001024 -0.191518 +v 0.119827 -0.001024 -0.191518 +v 0.087060 0.037894 -0.191518 +v 0.093206 -0.001024 -0.166944 +v 0.119827 0.056331 -0.191518 +v 0.119827 -0.001024 -0.212008 +v 0.087060 -0.001024 -0.179242 +v 0.099349 0.056331 -0.191518 +v 0.117778 0.056331 -0.212008 +v 0.087060 0.037894 -0.179242 +f 726 730 733 +f 721 720 722 +f 720 721 723 +f 719 723 724 +f 723 719 725 +f 720 723 725 +f 719 724 726 +f 723 721 727 +f 724 723 727 +f 722 720 728 +f 720 725 728 +f 725 722 728 +f 722 725 729 +f 725 719 729 +f 726 724 730 +f 727 721 730 +f 724 727 730 +f 721 722 731 +f 726 721 731 +f 726 731 732 +f 719 726 732 +f 729 719 732 +f 722 729 732 +f 731 722 732 +f 721 726 733 +f 730 721 733 +o convex_58 +v 0.123931 -0.001024 -0.230439 +v 0.121882 0.039944 -0.193576 +v 0.148502 0.039944 -0.207914 +v 0.121882 -0.001024 -0.193576 +v 0.148502 -0.001024 -0.207914 +v 0.134168 0.037894 -0.232492 +v 0.148502 -0.001024 -0.224294 +v 0.119831 0.039944 -0.214051 +v 0.148502 0.039944 -0.224294 +v 0.123931 0.037894 -0.230439 +v 0.119831 -0.001024 -0.193576 +v 0.134168 -0.001024 -0.232492 +v 0.119831 -0.001024 -0.214051 +f 744 741 746 +f 736 735 737 +f 737 734 738 +f 736 737 738 +f 738 734 740 +f 736 738 740 +f 735 736 741 +f 740 739 742 +f 736 740 742 +f 741 736 742 +f 739 734 743 +f 734 741 743 +f 742 739 743 +f 741 742 743 +f 737 735 744 +f 734 737 744 +f 735 741 744 +f 734 739 745 +f 740 734 745 +f 739 740 745 +f 741 734 746 +f 734 744 746 +o convex_59 +v 0.158750 0.111626 -0.224295 +v 0.117782 0.111626 -0.212007 +v 0.117782 0.111626 -0.203819 +v 0.119835 0.039951 -0.193573 +v 0.136225 0.039951 -0.222241 +v 0.158750 0.070680 -0.209953 +v 0.125979 0.156686 -0.203819 +v 0.136217 0.156686 -0.222247 +v 0.119835 0.039951 -0.214049 +v 0.148500 0.039951 -0.224295 +v 0.121885 0.085010 -0.193573 +v 0.158750 0.111626 -0.214049 +v 0.117782 0.156686 -0.212007 +v 0.148500 0.039951 -0.207918 +v 0.140311 0.156686 -0.212007 +v 0.117782 0.156686 -0.203819 +v 0.158750 0.070680 -0.224295 +v 0.125979 0.156686 -0.218148 +v 0.158750 0.085010 -0.209953 +v 0.144406 0.111626 -0.224295 +v 0.121885 0.039951 -0.193573 +f 760 752 767 +f 749 748 750 +f 750 748 755 +f 751 750 755 +f 750 751 756 +f 752 747 758 +f 748 749 759 +f 753 754 759 +f 755 748 759 +f 750 756 760 +f 756 752 760 +f 747 754 761 +f 754 753 761 +f 753 758 761 +f 758 747 761 +f 749 750 762 +f 750 757 762 +f 757 753 762 +f 759 749 762 +f 753 759 762 +f 747 752 763 +f 756 747 763 +f 752 756 763 +f 754 751 764 +f 751 755 764 +f 759 754 764 +f 755 759 764 +f 757 752 765 +f 753 757 765 +f 758 753 765 +f 752 758 765 +f 754 747 766 +f 751 754 766 +f 756 751 766 +f 747 756 766 +f 757 750 767 +f 752 757 767 +f 750 760 767 +o convex_60 +v 0.212008 0.056331 -0.197673 +v 0.164900 -0.001024 -0.212011 +v 0.166952 -0.001024 -0.224295 +v 0.212008 -0.001024 -0.195624 +v 0.189473 0.037894 -0.230440 +v 0.166952 0.056331 -0.207915 +v 0.189473 -0.001024 -0.230440 +v 0.166952 0.056331 -0.224295 +v 0.191520 -0.001024 -0.195624 +v 0.191520 0.056331 -0.195624 +v 0.193568 0.056331 -0.216101 +v 0.179241 -0.001024 -0.230440 +v 0.209951 -0.001024 -0.201770 +v 0.166952 -0.001024 -0.207915 +v 0.179241 0.037894 -0.230440 +v 0.212008 0.056331 -0.195624 +f 777 771 783 +f 769 770 771 +f 771 770 774 +f 770 769 775 +f 769 773 775 +f 773 768 775 +f 769 771 776 +f 773 776 777 +f 768 773 777 +f 776 771 777 +f 768 772 778 +f 775 768 778 +f 772 775 778 +f 774 770 779 +f 772 774 779 +f 770 775 779 +f 768 771 780 +f 772 768 780 +f 771 774 780 +f 774 772 780 +f 773 769 781 +f 769 776 781 +f 776 773 781 +f 775 772 782 +f 772 779 782 +f 779 775 782 +f 771 768 783 +f 768 777 783 +o convex_61 +v 0.230439 0.037894 -0.179237 +v 0.195625 -0.001024 -0.195621 +v 0.214058 -0.001024 -0.195621 +v 0.207916 -0.001024 -0.166949 +v 0.195625 0.039944 -0.195621 +v 0.207916 0.039944 -0.166949 +v 0.232492 -0.001024 -0.189471 +v 0.228389 0.037894 -0.193569 +v 0.222247 -0.001024 -0.168995 +v 0.222247 0.039944 -0.168995 +v 0.195625 0.039944 -0.191520 +v 0.214058 0.039944 -0.195621 +v 0.232492 -0.001024 -0.183327 +v 0.232492 0.037894 -0.189471 +f 796 790 797 +f 785 786 787 +f 786 785 788 +f 787 786 790 +f 790 786 791 +f 789 787 792 +f 787 790 792 +f 788 789 793 +f 792 784 793 +f 789 792 793 +f 785 787 794 +f 788 785 794 +f 787 789 794 +f 789 788 794 +f 786 788 795 +f 791 786 795 +f 788 793 795 +f 784 792 796 +f 792 790 796 +f 790 791 797 +f 793 784 797 +f 791 795 797 +f 795 793 797 +f 784 796 797 +o convex_62 +v 0.212004 0.111632 -0.203810 +v 0.224296 0.111632 -0.160802 +v 0.224296 0.156689 -0.160810 +v 0.195624 0.085008 -0.191520 +v 0.197673 0.175135 -0.201764 +v 0.222244 0.039964 -0.179235 +v 0.209961 0.070690 -0.160802 +v 0.195624 0.039964 -0.195617 +v 0.214051 0.175135 -0.160810 +v 0.218146 0.175135 -0.189470 +v 0.214051 0.039964 -0.195617 +v 0.216100 0.039964 -0.166949 +v 0.224293 0.175135 -0.169004 +v 0.211999 0.175135 -0.201764 +v 0.224296 0.070690 -0.160802 +v 0.199722 0.111632 -0.203810 +v 0.207915 0.039964 -0.166949 +v 0.197673 0.175135 -0.197663 +v 0.195624 0.039964 -0.191520 +v 0.212004 0.156689 -0.203810 +v 0.222244 0.175135 -0.179247 +f 807 810 818 +f 801 802 805 +f 799 800 806 +f 801 804 806 +f 804 799 806 +f 803 798 807 +f 802 806 807 +f 798 803 808 +f 805 798 808 +f 803 805 808 +f 805 803 809 +f 806 800 810 +f 807 806 810 +f 802 807 811 +f 800 799 812 +f 799 804 812 +f 809 803 812 +f 804 809 812 +f 810 800 812 +f 803 810 812 +f 798 805 813 +f 805 802 813 +f 809 804 814 +f 805 809 814 +f 802 801 815 +f 801 806 815 +f 806 802 815 +f 804 801 816 +f 801 805 816 +f 814 804 816 +f 805 814 816 +f 807 798 817 +f 802 811 817 +f 811 807 817 +f 798 813 817 +f 813 802 817 +f 803 807 818 +f 810 803 818 +o convex_63 +v 0.103451 0.111643 -0.203814 +v 0.101403 0.175135 -0.160803 +v 0.123927 0.175135 -0.201760 +v 0.101403 0.111643 -0.160803 +v 0.093205 0.175135 -0.177189 +v 0.123927 0.111643 -0.201760 +v 0.103451 0.175135 -0.203814 +v 0.093205 0.111643 -0.160803 +v 0.093205 0.111643 -0.177189 +v 0.093205 0.175135 -0.160803 +v 0.123927 0.111643 -0.203814 +v 0.123927 0.175135 -0.203814 +f 821 829 830 +f 821 820 822 +f 820 821 823 +f 821 822 824 +f 822 819 824 +f 819 823 825 +f 823 821 825 +f 822 820 826 +f 819 822 826 +f 826 823 827 +f 823 819 827 +f 819 826 827 +f 820 823 828 +f 826 820 828 +f 823 826 828 +f 824 819 829 +f 821 824 829 +f 819 825 829 +f 829 825 830 +f 825 821 830 +o convex_64 +v 0.144419 0.111637 -0.224296 +v 0.199718 0.156697 -0.197672 +v 0.199718 0.156697 -0.212004 +v 0.140320 0.156697 -0.212004 +v 0.199718 0.111637 -0.197672 +v 0.185374 0.111637 -0.220199 +v 0.173083 0.156697 -0.224296 +v 0.140320 0.111637 -0.212004 +v 0.140320 0.156697 -0.222247 +v 0.199718 0.111637 -0.212004 +v 0.173083 0.111637 -0.224296 +v 0.197660 0.111637 -0.197672 +v 0.144419 0.156697 -0.224296 +f 831 839 843 +f 832 833 834 +f 833 832 835 +f 835 831 836 +f 834 833 837 +f 833 836 837 +f 831 835 838 +f 834 837 839 +f 831 838 839 +f 838 834 839 +f 833 835 840 +f 836 833 840 +f 835 836 840 +f 836 831 841 +f 831 837 841 +f 837 836 841 +f 832 834 842 +f 835 832 842 +f 834 838 842 +f 838 835 842 +f 837 831 843 +f 839 837 843 +o convex_65 +v 0.123924 0.068621 -0.121885 +v 0.085008 -0.001024 -0.136215 +v 0.085008 -0.001024 -0.125981 +v 0.123924 -0.001024 -0.119834 +v 0.107536 0.068621 -0.150556 +v 0.107536 -0.001024 -0.150556 +v 0.103449 0.068621 -0.119834 +v 0.093206 0.068621 -0.148502 +v 0.085008 0.037889 -0.125981 +v 0.093206 -0.001024 -0.148502 +v 0.103449 -0.001024 -0.119834 +v 0.085008 0.037889 -0.136215 +v 0.123924 0.068621 -0.119834 +v 0.087061 -0.001024 -0.123933 +v 0.093206 0.068621 -0.140310 +v 0.123924 -0.001024 -0.121885 +f 849 844 859 +f 846 845 847 +f 847 845 849 +f 848 844 849 +f 844 848 850 +f 850 848 851 +f 848 849 851 +f 845 846 852 +f 849 845 853 +f 845 851 853 +f 851 849 853 +f 846 847 854 +f 847 850 854 +f 851 845 855 +f 845 852 855 +f 852 851 855 +f 847 844 856 +f 850 847 856 +f 844 850 856 +f 852 846 857 +f 850 852 857 +f 846 854 857 +f 854 850 857 +f 850 851 858 +f 852 850 858 +f 851 852 858 +f 844 847 859 +f 847 849 859 +o convex_66 +v 0.105497 0.068631 -0.150548 +v 0.119828 0.175135 -0.113690 +v 0.103452 0.175135 -0.113690 +v 0.101403 0.175135 -0.160798 +v 0.123927 0.068631 -0.121884 +v 0.093205 0.068631 -0.140311 +v 0.103449 0.068631 -0.119837 +v 0.093205 0.175135 -0.160798 +v 0.107545 0.070684 -0.160794 +v 0.093205 0.070684 -0.160794 +v 0.093205 0.175135 -0.140306 +v 0.119828 0.175135 -0.117794 +v 0.107545 0.085019 -0.160794 +v 0.119828 0.117796 -0.115737 +v 0.103452 0.154649 -0.113690 +v 0.123927 0.085019 -0.121884 +v 0.123927 0.068631 -0.119837 +f 864 875 876 +f 862 861 863 +f 860 864 865 +f 865 864 866 +f 862 863 867 +f 864 860 868 +f 867 863 868 +f 860 865 869 +f 865 867 869 +f 867 868 869 +f 868 860 869 +f 862 867 870 +f 867 865 870 +f 863 861 871 +f 864 868 872 +f 868 863 872 +f 863 871 872 +f 866 873 874 +f 861 862 874 +f 865 866 874 +f 862 870 874 +f 870 865 874 +f 873 861 874 +f 871 861 875 +f 864 872 875 +f 872 871 875 +f 866 864 876 +f 861 873 876 +f 873 866 876 +f 875 861 876 +o convex_67 +v 0.121879 0.056331 -0.101399 +v 0.105492 -0.001024 -0.119827 +v 0.125982 -0.001024 -0.119827 +v 0.125982 -0.001024 -0.087060 +v 0.150556 0.056331 -0.107535 +v 0.105492 0.056331 -0.119827 +v 0.150556 -0.001024 -0.107535 +v 0.148505 0.056331 -0.093206 +v 0.125982 0.037894 -0.087060 +v 0.148505 -0.001024 -0.093206 +v 0.125982 0.056331 -0.119827 +v 0.107543 0.056331 -0.113681 +v 0.136211 -0.001024 -0.087060 +v 0.107543 -0.001024 -0.113681 +v 0.136211 0.037894 -0.087060 +f 889 884 891 +f 878 879 880 +f 879 878 882 +f 877 881 882 +f 880 879 883 +f 879 881 883 +f 883 881 884 +f 881 877 884 +f 884 877 885 +f 880 883 886 +f 883 884 886 +f 881 879 887 +f 879 882 887 +f 882 881 887 +f 882 878 888 +f 877 882 888 +f 880 885 888 +f 885 877 888 +f 885 880 889 +f 880 886 889 +f 886 884 889 +f 878 880 890 +f 888 878 890 +f 880 888 890 +f 884 885 891 +f 885 889 891 +o convex_68 +v 0.220198 0.056331 -0.132124 +v 0.195624 -0.001024 -0.125980 +v 0.207915 -0.001024 -0.150551 +v 0.230440 -0.001024 -0.128029 +v 0.195624 0.056331 -0.121879 +v 0.220198 0.056331 -0.150551 +v 0.207915 0.056331 -0.150551 +v 0.226343 0.037894 -0.123931 +v 0.222247 -0.001024 -0.148505 +v 0.214053 -0.001024 -0.121879 +v 0.230440 0.037894 -0.136216 +v 0.214053 0.056331 -0.121879 +v 0.195624 -0.001024 -0.121879 +v 0.195624 0.056331 -0.125980 +v 0.230440 -0.001024 -0.136216 +f 900 902 906 +f 893 894 895 +f 896 892 897 +f 896 897 898 +f 894 893 898 +f 897 894 898 +f 895 894 900 +f 894 897 900 +f 893 895 901 +f 895 899 901 +f 897 892 902 +f 899 895 902 +f 892 899 902 +f 900 897 902 +f 892 896 903 +f 899 892 903 +f 896 901 903 +f 901 899 903 +f 896 893 904 +f 893 901 904 +f 901 896 904 +f 893 896 905 +f 896 898 905 +f 898 893 905 +f 895 900 906 +f 902 895 906 +o convex_69 +v 0.220195 0.056342 -0.150551 +v 0.212007 0.154633 -0.115734 +v 0.203823 0.154633 -0.115734 +v 0.214051 0.175135 -0.160798 +v 0.195624 0.056342 -0.125985 +v 0.224296 0.175135 -0.146449 +v 0.216102 0.056342 -0.123934 +v 0.197676 0.175135 -0.119841 +v 0.209961 0.070681 -0.160798 +v 0.224298 0.070681 -0.160798 +v 0.224296 0.175135 -0.160798 +v 0.222244 0.056342 -0.138266 +v 0.220198 0.175135 -0.132130 +v 0.195624 0.056342 -0.121883 +v 0.195624 0.085020 -0.125985 +v 0.212007 0.117780 -0.115734 +v 0.212002 0.175135 -0.117798 +v 0.207915 0.056342 -0.150551 +v 0.224298 0.070681 -0.146453 +v 0.214053 0.056342 -0.121883 +v 0.197676 0.175135 -0.117798 +v 0.203823 0.117780 -0.115734 +f 927 920 928 +f 911 907 913 +f 912 910 914 +f 907 915 916 +f 915 910 916 +f 910 912 917 +f 916 910 917 +f 912 916 917 +f 913 907 918 +f 907 916 918 +f 912 914 919 +f 918 912 919 +f 913 918 919 +f 911 913 920 +f 914 910 921 +f 910 915 921 +f 915 911 921 +f 911 920 921 +f 908 909 922 +f 919 908 922 +f 913 919 922 +f 909 908 923 +f 908 919 923 +f 919 914 923 +f 907 911 924 +f 915 907 924 +f 911 915 924 +f 916 912 925 +f 912 918 925 +f 918 916 925 +f 920 913 926 +f 913 922 926 +f 922 920 926 +f 914 921 927 +f 921 920 927 +f 909 923 927 +f 923 914 927 +f 922 909 928 +f 920 922 928 +f 909 927 928 +o convex_70 +v 0.191518 0.054282 -0.099349 +v 0.166944 -0.001024 -0.107538 +v 0.189476 -0.001024 -0.119826 +v 0.189476 -0.001024 -0.085008 +v 0.212008 0.054282 -0.121875 +v 0.166944 0.054282 -0.107538 +v 0.212008 -0.001024 -0.121875 +v 0.168995 0.054282 -0.093207 +v 0.189476 0.037895 -0.085008 +v 0.193569 0.054282 -0.121875 +v 0.168995 -0.001024 -0.093207 +v 0.212008 0.054282 -0.117776 +v 0.181289 0.037895 -0.085008 +v 0.212008 -0.001024 -0.117776 +v 0.193569 0.037895 -0.089111 +v 0.181289 -0.001024 -0.085008 +f 941 939 944 +f 930 931 932 +f 931 930 934 +f 929 933 934 +f 932 931 935 +f 934 930 936 +f 929 934 936 +f 929 936 937 +f 931 934 938 +f 934 933 938 +f 935 931 938 +f 933 935 938 +f 930 932 939 +f 936 930 939 +f 933 929 940 +f 935 933 940 +f 929 937 940 +f 932 937 941 +f 937 936 941 +f 936 939 941 +f 932 935 942 +f 935 940 942 +f 942 940 943 +f 937 932 943 +f 940 937 943 +f 932 942 943 +f 939 932 944 +f 932 941 944 +o convex_71 +v 0.203815 0.117782 -0.115734 +v 0.244774 0.154646 -0.076822 +v 0.242729 0.154646 -0.074771 +v 0.203815 0.154646 -0.115734 +v 0.220206 0.117782 -0.087060 +v 0.228390 0.117782 -0.099344 +v 0.203815 0.154646 -0.103437 +v 0.212014 0.154646 -0.115734 +v 0.236582 0.154646 -0.070674 +v 0.203815 0.117782 -0.103437 +v 0.244774 0.154646 -0.082966 +v 0.212014 0.117782 -0.115734 +v 0.232488 0.123932 -0.091157 +f 950 955 957 +f 947 946 948 +f 949 945 950 +f 947 948 951 +f 948 945 951 +f 948 946 952 +f 945 948 952 +f 949 947 953 +f 947 951 953 +f 953 951 954 +f 945 949 954 +f 951 945 954 +f 949 953 954 +f 952 946 955 +f 952 955 956 +f 950 945 956 +f 945 952 956 +f 955 950 956 +f 946 947 957 +f 947 949 957 +f 949 950 957 +f 955 946 957 +o convex_72 +v 0.154663 0.117782 -0.093205 +v 0.203814 0.154646 -0.103448 +v 0.203814 0.154646 -0.123927 +v 0.203814 0.117782 -0.103448 +v 0.201760 0.117782 -0.123927 +v 0.154663 0.154646 -0.101400 +v 0.175141 0.154646 -0.093205 +v 0.154663 0.117782 -0.101400 +v 0.175141 0.117782 -0.093205 +v 0.154663 0.154646 -0.093205 +f 964 963 967 +f 960 959 961 +f 961 958 962 +f 960 961 962 +f 960 962 963 +f 959 960 963 +f 961 959 964 +f 959 963 964 +f 962 958 965 +f 963 962 965 +f 958 963 965 +f 958 961 966 +f 964 958 966 +f 961 964 966 +f 963 958 967 +f 958 964 967 +o convex_73 +v 0.261155 0.175137 -0.048139 +v 0.060447 0.177190 -0.160798 +v 0.060447 0.193567 -0.160798 +v 0.060447 0.193567 -0.035847 +v 0.261155 0.193567 -0.160798 +v 0.261155 0.193567 -0.035847 +v 0.060447 0.177190 -0.035847 +v 0.261155 0.177190 -0.160798 +v 0.093220 0.175137 -0.160798 +v 0.060447 0.175137 -0.052236 +v 0.261155 0.177190 -0.035847 +v 0.224295 0.175137 -0.160798 +f 976 975 979 +f 970 969 971 +f 969 970 972 +f 970 971 972 +f 968 972 973 +f 972 971 973 +f 971 969 974 +f 973 971 974 +f 972 968 975 +f 969 972 975 +f 969 975 976 +f 974 969 977 +f 968 974 977 +f 969 976 977 +f 976 968 977 +f 968 973 978 +f 974 968 978 +f 973 974 978 +f 975 968 979 +f 968 976 979 +o convex_74 +v 0.259112 0.175135 -0.048144 +v 0.195634 0.154655 -0.117775 +v 0.195634 0.175135 -0.117775 +v 0.187427 0.154655 -0.099337 +v 0.246821 0.154655 -0.080918 +v 0.212018 0.175135 -0.117775 +v 0.187427 0.175135 -0.099337 +v 0.259112 0.158753 -0.048144 +v 0.261155 0.175135 -0.066583 +v 0.212018 0.154655 -0.117775 +v 0.187427 0.154655 -0.111627 +v 0.261155 0.162849 -0.066583 +v 0.238629 0.154655 -0.068628 +f 984 987 992 +f 983 981 984 +f 982 980 985 +f 981 982 985 +f 980 982 986 +f 983 980 986 +f 980 983 987 +f 985 980 988 +f 980 987 988 +f 984 981 989 +f 981 985 989 +f 985 988 989 +f 982 981 990 +f 981 983 990 +f 986 982 990 +f 983 986 990 +f 987 984 991 +f 988 987 991 +f 984 989 991 +f 989 988 991 +f 983 984 992 +f 987 983 992 +o convex_75 +v 0.107534 0.056331 -0.166948 +v 0.115727 0.056331 -0.212005 +v 0.109583 0.062481 -0.214051 +v 0.119822 0.111637 -0.197672 +v 0.091153 0.111637 -0.220191 +v 0.093202 0.111637 -0.160802 +v 0.093202 0.056331 -0.177194 +v 0.101398 0.111637 -0.226344 +v 0.107534 0.085002 -0.160802 +v 0.119828 0.056331 -0.191526 +v 0.119822 0.111637 -0.212005 +v 0.093202 0.070672 -0.160802 +v 0.101401 0.111637 -0.160802 +v 0.119822 0.056331 -0.212005 +v 0.103447 0.062481 -0.207912 +v 0.091153 0.107539 -0.220191 +v 0.093202 0.056331 -0.166948 +v 0.107534 0.070672 -0.160802 +f 1001 1004 1010 +f 996 997 998 +f 994 993 999 +f 997 996 1000 +f 993 994 1002 +f 996 1001 1002 +f 1000 996 1003 +f 996 1002 1003 +f 998 997 1004 +f 1001 998 1004 +f 996 998 1005 +f 1001 996 1005 +f 998 1001 1005 +f 994 995 1006 +f 995 1000 1006 +f 1002 994 1006 +f 1000 1003 1006 +f 1003 1002 1006 +f 995 994 1007 +f 994 999 1007 +f 1007 999 1008 +f 1000 995 1008 +f 997 1000 1008 +f 1004 997 1008 +f 995 1007 1008 +f 1004 1008 1009 +f 999 993 1009 +f 993 1004 1009 +f 1008 999 1009 +f 993 1002 1010 +f 1002 1001 1010 +f 1004 993 1010 +o convex_76 +v 0.181274 0.175135 -0.095253 +v 0.119831 0.154655 -0.115732 +v 0.119831 0.154655 -0.103446 +v 0.187414 0.154655 -0.109585 +v 0.119831 0.175135 -0.115732 +v 0.175128 0.154655 -0.093202 +v 0.140321 0.175135 -0.093202 +v 0.187414 0.175135 -0.109585 +v 0.140321 0.154655 -0.093202 +v 0.121888 0.175135 -0.101397 +v 0.187414 0.154655 -0.099350 +f 1014 1018 1021 +f 1013 1012 1014 +f 1012 1013 1015 +f 1014 1012 1015 +f 1013 1014 1016 +f 1011 1015 1017 +f 1016 1011 1017 +f 1015 1011 1018 +f 1014 1015 1018 +f 1013 1016 1019 +f 1016 1017 1019 +f 1019 1017 1020 +f 1015 1013 1020 +f 1017 1015 1020 +f 1013 1019 1020 +f 1011 1016 1021 +f 1016 1014 1021 +f 1018 1011 1021 +o convex_77 +v 0.164904 0.056331 -0.212002 +v 0.226347 0.111637 -0.216098 +v 0.226347 0.109585 -0.216098 +v 0.212008 0.111637 -0.195621 +v 0.158750 0.111637 -0.224293 +v 0.212008 0.056331 -0.195621 +v 0.173095 0.056331 -0.224293 +v 0.220193 0.109585 -0.226344 +v 0.158750 0.085002 -0.209960 +v 0.191523 0.056331 -0.195624 +v 0.207912 0.064527 -0.214050 +v 0.199721 0.111637 -0.195621 +v 0.158750 0.070672 -0.224293 +v 0.158750 0.111637 -0.214053 +v 0.214053 0.064527 -0.207918 +v 0.158750 0.070672 -0.209960 +v 0.191523 0.085002 -0.195624 +v 0.199721 0.056331 -0.212002 +f 1032 1036 1039 +f 1024 1023 1025 +f 1025 1023 1026 +f 1024 1025 1027 +f 1027 1022 1028 +f 1023 1024 1029 +f 1026 1023 1029 +f 1022 1027 1031 +f 1028 1029 1032 +f 1025 1026 1033 +f 1027 1025 1033 +f 1031 1027 1033 +f 1028 1022 1034 +f 1029 1028 1034 +f 1026 1029 1034 +f 1030 1026 1034 +f 1026 1030 1035 +f 1033 1026 1035 +f 1030 1033 1035 +f 1024 1027 1036 +f 1029 1024 1036 +f 1032 1029 1036 +f 1022 1031 1037 +f 1031 1030 1037 +f 1034 1022 1037 +f 1030 1034 1037 +f 1030 1031 1038 +f 1033 1030 1038 +f 1031 1033 1038 +f 1027 1028 1039 +f 1028 1032 1039 +f 1036 1027 1039 +o convex_78 +v 0.087065 0.117788 -0.097287 +v 0.119813 0.175135 -0.105485 +v 0.119813 0.154655 -0.105485 +v 0.062491 0.175135 -0.052246 +v 0.103431 0.175135 -0.113683 +v 0.113680 0.117788 -0.103434 +v 0.060427 0.175135 -0.070674 +v 0.103436 0.117788 -0.115734 +v 0.097291 0.117788 -0.087057 +v 0.060427 0.162846 -0.052246 +v 0.119813 0.154655 -0.113683 +v 0.060427 0.162846 -0.070674 +v 0.113680 0.117788 -0.115734 +v 0.103436 0.154644 -0.115734 +v 0.119813 0.175135 -0.113683 +v 0.062491 0.162846 -0.052246 +f 1049 1048 1055 +f 1042 1041 1043 +f 1043 1041 1044 +f 1043 1044 1046 +f 1045 1040 1047 +f 1045 1042 1048 +f 1040 1045 1048 +f 1043 1046 1049 +f 1040 1048 1049 +f 1041 1042 1050 +f 1042 1045 1050 +f 1046 1047 1051 +f 1047 1040 1051 +f 1040 1049 1051 +f 1049 1046 1051 +f 1045 1047 1052 +f 1050 1045 1052 +f 1046 1044 1053 +f 1047 1046 1053 +f 1052 1047 1053 +f 1044 1041 1054 +f 1041 1050 1054 +f 1050 1052 1054 +f 1052 1053 1054 +f 1053 1044 1054 +f 1042 1043 1055 +f 1048 1042 1055 +f 1043 1049 1055 +o convex_79 +v 0.089109 0.111637 -0.220205 +v 0.117777 0.156697 -0.203819 +v 0.117777 0.156697 -0.212014 +v 0.068621 0.156697 -0.238633 +v 0.078870 0.154646 -0.248879 +v 0.117777 0.111637 -0.212014 +v 0.103439 0.111637 -0.203819 +v 0.103439 0.156697 -0.203819 +v 0.099339 0.113693 -0.228391 +v 0.117777 0.111637 -0.203819 +v 0.074770 0.154646 -0.248879 +f 1060 1064 1066 +f 1057 1058 1059 +f 1059 1058 1060 +f 1060 1058 1061 +f 1058 1057 1061 +f 1056 1061 1062 +f 1059 1056 1062 +f 1059 1062 1063 +f 1057 1059 1063 +f 1062 1057 1063 +f 1060 1061 1064 +f 1061 1056 1064 +f 1061 1057 1065 +f 1062 1061 1065 +f 1057 1062 1065 +f 1056 1059 1066 +f 1059 1060 1066 +f 1064 1056 1066 +o convex_80 +v 0.207916 0.054282 -0.113680 +v 0.222246 0.117782 -0.089108 +v 0.218149 0.117782 -0.089108 +v 0.212010 0.117782 -0.121876 +v 0.195624 0.117782 -0.103447 +v 0.195624 0.054282 -0.103447 +v 0.195624 0.054282 -0.121876 +v 0.212010 0.054282 -0.121876 +v 0.228392 0.113680 -0.099351 +v 0.195624 0.117782 -0.117776 +v 0.214053 0.066577 -0.107537 +v 0.199724 0.117782 -0.121876 +v 0.222246 0.113680 -0.089108 +f 1077 1075 1079 +f 1069 1068 1070 +f 1069 1070 1071 +f 1069 1071 1072 +f 1072 1071 1073 +f 1067 1072 1073 +f 1067 1073 1074 +f 1073 1070 1074 +f 1070 1068 1075 +f 1074 1070 1075 +f 1071 1070 1076 +f 1073 1071 1076 +f 1072 1067 1077 +f 1067 1074 1077 +f 1074 1075 1077 +f 1070 1073 1078 +f 1076 1070 1078 +f 1073 1076 1078 +f 1068 1069 1079 +f 1069 1072 1079 +f 1075 1068 1079 +f 1072 1077 1079 +o convex_81 +v 0.212020 0.175135 -0.201766 +v 0.216116 0.111643 -0.226351 +v 0.218161 0.113694 -0.228397 +v 0.197669 0.175135 -0.212018 +v 0.259115 0.175135 -0.267308 +v 0.214065 0.111643 -0.203819 +v 0.259115 0.162847 -0.248870 +v 0.199726 0.111643 -0.203819 +v 0.257058 0.160796 -0.267308 +v 0.228392 0.113694 -0.218164 +v 0.199726 0.111643 -0.212018 +v 0.259115 0.175135 -0.248870 +v 0.197669 0.175135 -0.201766 +v 0.259115 0.160796 -0.267308 +v 0.226347 0.111643 -0.222258 +v 0.197669 0.156700 -0.212018 +f 1087 1092 1095 +f 1083 1080 1084 +f 1080 1085 1086 +f 1081 1085 1087 +f 1085 1080 1087 +f 1083 1084 1088 +f 1086 1085 1089 +f 1082 1081 1090 +f 1081 1087 1090 +f 1088 1082 1090 +f 1084 1080 1091 +f 1080 1086 1091 +f 1086 1084 1091 +f 1080 1083 1092 +f 1087 1080 1092 +f 1084 1086 1093 +f 1082 1088 1093 +f 1088 1084 1093 +f 1086 1089 1093 +f 1093 1089 1094 +f 1081 1082 1094 +f 1085 1081 1094 +f 1089 1085 1094 +f 1082 1093 1094 +f 1083 1088 1095 +f 1090 1087 1095 +f 1088 1090 1095 +f 1092 1083 1095 +o convex_82 +v 0.103442 0.175135 -0.203821 +v 0.054289 0.158753 -0.271405 +v 0.054289 0.175135 -0.271405 +v 0.123927 0.156703 -0.214062 +v 0.066581 0.156703 -0.240682 +v 0.123927 0.175135 -0.214062 +v 0.103442 0.156703 -0.203821 +v 0.054289 0.175135 -0.252967 +v 0.123927 0.156703 -0.203821 +v 0.056334 0.158753 -0.271405 +v 0.123927 0.175135 -0.203821 +v 0.054289 0.162849 -0.252967 +f 1103 1097 1107 +f 1098 1096 1101 +f 1100 1099 1102 +f 1096 1098 1103 +f 1098 1097 1103 +f 1102 1096 1103 +f 1099 1101 1104 +f 1096 1102 1104 +f 1102 1099 1104 +f 1097 1098 1105 +f 1099 1100 1105 +f 1100 1097 1105 +f 1098 1101 1105 +f 1101 1099 1105 +f 1101 1096 1106 +f 1096 1104 1106 +f 1104 1101 1106 +f 1097 1100 1107 +f 1100 1102 1107 +f 1102 1103 1107 diff --git a/examples/Importers/ImportURDFDemo/URDF2Bullet.cpp b/examples/Importers/ImportURDFDemo/URDF2Bullet.cpp index 1350076e6..d9e58ef5a 100644 --- a/examples/Importers/ImportURDFDemo/URDF2Bullet.cpp +++ b/examples/Importers/ImportURDFDemo/URDF2Bullet.cpp @@ -225,7 +225,8 @@ void ConvertURDF2BulletInternal( { - btVector3 color = selectColor2(); + btVector4 color = selectColor2(); + u2b.getLinkColor(urdfLinkIndex,color); /* if (visual->material.get()) { @@ -255,7 +256,7 @@ void ConvertURDF2BulletInternal( btRigidBody* body = creation.allocateRigidBody(urdfLinkIndex, mass, localInertiaDiagonal, inertialFrameInWorldSpace, compoundShape); linkRigidBody = body; - world1->addRigidBody(body, bodyCollisionFilterGroup, bodyCollisionFilterMask); + world1->addRigidBody(body); compoundShape->setUserIndex(graphicsIndex); diff --git a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp index fb0247dca..9015bdb81 100644 --- a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp +++ b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp @@ -1,6 +1,5 @@ #include "PhysicsServerCommandProcessor.h" - #include "../Importers/ImportURDFDemo/BulletUrdfImporter.h" #include "../Importers/ImportURDFDemo/MyMultiBodyCreator.h" #include "../Importers/ImportURDFDemo/URDF2Bullet.h" @@ -563,7 +562,7 @@ PhysicsServerCommandProcessor::PhysicsServerCommandProcessor() m_data = new PhysicsServerCommandProcessorInternalData(); createEmptyDynamicsWorld(); - m_data->m_dynamicsWorld->getSolverInfo().m_linearSlop = 0.0001; + m_data->m_dynamicsWorld->getSolverInfo().m_linearSlop = 0.00001; m_data->m_dynamicsWorld->getSolverInfo().m_numIterations = 100; } @@ -604,7 +603,7 @@ void PhysicsServerCommandProcessor::createEmptyDynamicsWorld() m_data->m_dynamicsWorld->setGravity(btVector3(0, 0, 0)); - m_data->m_dynamicsWorld->getSolverInfo().m_erp2 = 0.05; + m_data->m_dynamicsWorld->getSolverInfo().m_erp2 = 0.08; } @@ -1887,6 +1886,9 @@ bool PhysicsServerCommandProcessor::processCommand(const struct SharedMemoryComm applyJointDamping(i); } + + + btScalar deltaTimeScaled = m_data->m_physicsDeltaTime*simTimeScalingFactor; if (m_data->m_numSimulationSubSteps > 0) @@ -2820,6 +2822,7 @@ void PhysicsServerCommandProcessor::removePickingConstraint() m_data->m_dynamicsWorld->removeConstraint(m_data->m_pickedConstraint); delete m_data->m_pickedConstraint; m_data->m_pickedConstraint = 0; + m_data->m_pickedBody->forceActivationState(ACTIVE_TAG); m_data->m_pickedBody = 0; } if (m_data->m_pickingMultiBodyPoint2Point) @@ -2889,11 +2892,12 @@ void PhysicsServerCommandProcessor::stepSimulationRealTime(double dtInSec) btVector3 shiftPos = spawnDir*spawnDistance; btVector3 spawnPos = gVRGripperPos + shiftPos; loadUrdf("sphere_small.urdf", spawnPos, gVRGripperOrn, true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + //loadUrdf("lego/lego.urdf", spawnPos, gVRGripperOrn, true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); m_data->m_sphereId = bodyId; InteralBodyData* parentBody = m_data->getHandle(bodyId); if (parentBody->m_multiBody) { - parentBody->m_multiBody->setBaseVel(spawnDir * 3); + parentBody->m_multiBody->setBaseVel(spawnDir * 5); } } @@ -2942,9 +2946,9 @@ void PhysicsServerCommandProcessor::stepSimulationRealTime(double dtInSec) loadUrdf("cube.urdf", btVector3(3, -2, 0.5+i), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); } - loadUrdf("sphere2.urdf", btVector3(-5, 0, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("sphere2.urdf", btVector3(-5, 0, 2), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("sphere2.urdf", btVector3(-5, 0, 3), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + loadUrdf("lego/lego.urdf", btVector3(-3, 0, .1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + loadUrdf("lego/lego.urdf", btVector3(-3, 0, .2), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + loadUrdf("lego/lego.urdf", btVector3(-3, 0, .3), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); loadUrdf("r2d2.urdf", btVector3(2, -2, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); @@ -2973,15 +2977,27 @@ void PhysicsServerCommandProcessor::stepSimulationRealTime(double dtInSec) } } #endif - - //loadUrdf("nao/nao.urdf", btVector3(2,5, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + + + + for (int i = 0; i < 6; i++) + { + loadUrdf("jenga/jenga.urdf", btVector3(-1-0.1*i,-0.5, .07), btQuaternion(btVector3(0,1,0),SIMD_HALF_PI), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + } + + //loadUrdf("nao/nao.urdf", btVector3(2,5, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); loadUrdf("husky/husky.urdf", btVector3(5, 2, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); m_data->m_huskyId = bodyId; } loadSdf("kiva_shelf/model.sdf", &gBufferServerToClient[0], gBufferServerToClient.size(), true); loadUrdf("teddy_vhacd.urdf", btVector3(-0.1, 0.6, 0.85), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("sphere_small.urdf", btVector3(-0.1, 0.6, 1.25), gVRGripperOrn, true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + loadUrdf("duck_vhacd.urdf", btVector3(-0.3, 0.6, 0.6), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + //loadUrdf("cup/cup_small.urdf", btVector3(-0.3, 0.6, 0.85), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + //loadUrdf("cup/pitcher_small.urdf", btVector3(-0.3, 0.6, 1.15), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + + //loadUrdf("sphere_small.urdf", btVector3(-0.1, 0.6, 1.25), gVRGripperOrn, true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + m_data->m_dynamicsWorld->setGravity(btVector3(0, 0, -10)); diff --git a/examples/SharedMemory/PhysicsServerExample.cpp b/examples/SharedMemory/PhysicsServerExample.cpp index 540901af9..388a0dba7 100644 --- a/examples/SharedMemory/PhysicsServerExample.cpp +++ b/examples/SharedMemory/PhysicsServerExample.cpp @@ -1181,14 +1181,20 @@ void PhysicsServerExample::vrControllerButtonCallback(int controllerId, int butt } else { gDebugRenderToggle = 0; - simTimeScalingFactor *= 0.5; + if (simTimeScalingFactor==0) { simTimeScalingFactor = 1; - } - if (simTimeScalingFactor<0.01) + } else { - simTimeScalingFactor = 0; + if (simTimeScalingFactor==1) + { + simTimeScalingFactor = 0.25; + } + else + { + simTimeScalingFactor = 0; + } } } } else diff --git a/examples/pybullet/pybullet.c b/examples/pybullet/pybullet.c index de455ba49..cfa9ba684 100644 --- a/examples/pybullet/pybullet.c +++ b/examples/pybullet/pybullet.c @@ -909,6 +909,11 @@ static PyObject* pybullet_getJointState(PyObject* self, PyObject* args) { if (size == 2) // get body index and joint index { if (PyArg_ParseTuple(args, "ii", &bodyIndex, &jointIndex)) { + int status_type = 0; + b3SharedMemoryCommandHandle cmd_handle; + b3SharedMemoryStatusHandle status_handle; + + if (bodyIndex < 0) { PyErr_SetString(SpamError, "getJointState failed; invalid bodyIndex"); return NULL; @@ -918,10 +923,10 @@ static PyObject* pybullet_getJointState(PyObject* self, PyObject* args) { return NULL; } - int status_type = 0; - b3SharedMemoryCommandHandle cmd_handle = + + cmd_handle = b3RequestActualStateCommandInit(sm, bodyIndex); - b3SharedMemoryStatusHandle status_handle = + status_handle = b3SubmitClientCommandAndWaitStatus(sm, cmd_handle); status_type = b3GetStatusType(status_handle); @@ -984,6 +989,10 @@ static PyObject* pybullet_getLinkState(PyObject* self, PyObject* args) { if (PySequence_Size(args) == 2) // body index and link index { if (PyArg_ParseTuple(args, "ii", &bodyIndex, &linkIndex)) { + int status_type = 0; + b3SharedMemoryCommandHandle cmd_handle; + b3SharedMemoryStatusHandle status_handle; + if (bodyIndex < 0) { PyErr_SetString(SpamError, "getLinkState failed; invalid bodyIndex"); return NULL; @@ -993,10 +1002,10 @@ static PyObject* pybullet_getLinkState(PyObject* self, PyObject* args) { return NULL; } - int status_type = 0; - b3SharedMemoryCommandHandle cmd_handle = + + cmd_handle = b3RequestActualStateCommandInit(sm, bodyIndex); - b3SharedMemoryStatusHandle status_handle = + status_handle = b3SubmitClientCommandAndWaitStatus(sm, cmd_handle); status_type = b3GetStatusType(status_handle); From 6563c9c821ef3640ba6234b879d59719e1273f62 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Sun, 25 Sep 2016 23:13:23 -0700 Subject: [PATCH 2/9] add btVector3::safeNorm to avoid/workaround certain clang/g++ issue or returning -INF when taking sqrtf(0.0000000000000000000000000000000000000108333558) --- src/BulletDynamics/Featherstone/btMultiBody.cpp | 8 ++++---- .../Featherstone/btMultiBodySliderConstraint.cpp | 2 +- src/LinearMath/btVector3.h | 12 +++++++++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/BulletDynamics/Featherstone/btMultiBody.cpp b/src/BulletDynamics/Featherstone/btMultiBody.cpp index 29b851611..8a8a5da77 100644 --- a/src/BulletDynamics/Featherstone/btMultiBody.cpp +++ b/src/BulletDynamics/Featherstone/btMultiBody.cpp @@ -769,8 +769,8 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar //adding damping terms (only) btScalar linDampMult = 1., angDampMult = 1.; - zeroAccSpatFrc[0].addVector(angDampMult * m_baseInertia * spatVel[0].getAngular() * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR * spatVel[0].getAngular().norm()), - linDampMult * m_baseMass * spatVel[0].getLinear() * (DAMPING_K1_LINEAR + DAMPING_K2_LINEAR * spatVel[0].getLinear().norm())); + zeroAccSpatFrc[0].addVector(angDampMult * m_baseInertia * spatVel[0].getAngular() * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR * spatVel[0].getAngular().safeNorm()), + linDampMult * m_baseMass * spatVel[0].getLinear() * (DAMPING_K1_LINEAR + DAMPING_K2_LINEAR * spatVel[0].getLinear().safeNorm())); // //p += vhat x Ihat vhat - done in a simpler way @@ -856,8 +856,8 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar // //adding damping terms (only) btScalar linDampMult = 1., angDampMult = 1.; - zeroAccSpatFrc[i+1].addVector(angDampMult * m_links[i].m_inertiaLocal * spatVel[i+1].getAngular() * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR * spatVel[i+1].getAngular().norm()), - linDampMult * m_links[i].m_mass * spatVel[i+1].getLinear() * (DAMPING_K1_LINEAR + DAMPING_K2_LINEAR * spatVel[i+1].getLinear().norm())); + zeroAccSpatFrc[i+1].addVector(angDampMult * m_links[i].m_inertiaLocal * spatVel[i+1].getAngular() * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR * spatVel[i+1].getAngular().safeNorm()), + linDampMult * m_links[i].m_mass * spatVel[i+1].getLinear() * (DAMPING_K1_LINEAR + DAMPING_K2_LINEAR * spatVel[i+1].getLinear().safeNorm())); // calculate Ihat_i^A //init the spatial AB inertia (it has the simple form thanks to choosing local body frames origins at their COMs) diff --git a/src/BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp b/src/BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp index e29ae0691..67b106f28 100644 --- a/src/BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp +++ b/src/BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp @@ -133,7 +133,7 @@ void btMultiBodySliderConstraint::createConstraintRows(btMultiBodyConstraintArra for (int i = 0; i < 3; ++i) { constraintAxis[0] = frameAworld.getColumn(i).cross(jointAxis); - if (constraintAxis[0].norm() > EPSILON) + if (constraintAxis[0].safeNorm() > EPSILON) { constraintAxis[0] = constraintAxis[0].normalized(); constraintAxis[1] = jointAxis.cross(constraintAxis[0]); diff --git a/src/LinearMath/btVector3.h b/src/LinearMath/btVector3.h index 839b19c14..487670009 100644 --- a/src/LinearMath/btVector3.h +++ b/src/LinearMath/btVector3.h @@ -267,10 +267,20 @@ public: /**@brief Return the norm (length) of the vector */ SIMD_FORCE_INLINE btScalar norm() const - { + { return length(); } + /**@brief Return the norm (length) of the vector */ + SIMD_FORCE_INLINE btScalar safeNorm() const + { + btScalar d = length2(); + //workaround for some clang/gcc issue of sqrtf(tiny number) = -INF + if (d>SIMD_EPSILON) + return btSqrt(d); + return btScalar(0); + } + /**@brief Return the distance squared between the ends of this and another vector * This is symantically treating the vector like a point */ SIMD_FORCE_INLINE btScalar distance2(const btVector3& v) const; From ffee956ddec909bb11433ec3e902ca9b7d750963 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Mon, 26 Sep 2016 07:16:18 -0700 Subject: [PATCH 3/9] add VHACD, with the addition of Wavefront .obj export --- Extras/VHACD/inc/btAlignedAllocator.h | 104 + Extras/VHACD/inc/btAlignedObjectArray.h | 448 ++++ Extras/VHACD/inc/btConvexHullComputer.h | 97 + Extras/VHACD/inc/btMinMax.h | 65 + Extras/VHACD/inc/btScalar.h | 532 +++++ Extras/VHACD/inc/btVector3.h | 715 ++++++ Extras/VHACD/inc/vhacdCircularList.h | 79 + Extras/VHACD/inc/vhacdCircularList.inl | 161 ++ Extras/VHACD/inc/vhacdICHull.h | 98 + Extras/VHACD/inc/vhacdManifoldMesh.h | 142 ++ Extras/VHACD/inc/vhacdMesh.h | 129 ++ Extras/VHACD/inc/vhacdMutex.h | 146 ++ Extras/VHACD/inc/vhacdSArray.h | 158 ++ Extras/VHACD/inc/vhacdTimer.h | 121 + Extras/VHACD/inc/vhacdVHACD.h | 350 +++ Extras/VHACD/inc/vhacdVector.h | 103 + Extras/VHACD/inc/vhacdVector.inl | 362 +++ Extras/VHACD/inc/vhacdVolume.h | 419 ++++ Extras/VHACD/premake4.lua | 4 + Extras/VHACD/public/VHACD.h | 121 + Extras/VHACD/src/VHACD.cpp | 1433 ++++++++++++ Extras/VHACD/src/btAlignedAllocator.cpp | 176 ++ Extras/VHACD/src/btConvexHullComputer.cpp | 2475 +++++++++++++++++++++ Extras/VHACD/src/premake4.lua | 10 + Extras/VHACD/src/vhacdICHull.cpp | 725 ++++++ Extras/VHACD/src/vhacdManifoldMesh.cpp | 202 ++ Extras/VHACD/src/vhacdMesh.cpp | 323 +++ Extras/VHACD/src/vhacdVolume.cpp | 1617 ++++++++++++++ Extras/VHACD/test/inc/oclHelper.h | 50 + Extras/VHACD/test/src/main.cpp | 648 ++++++ Extras/VHACD/test/src/oclHelper.cpp | 329 +++ Extras/VHACD/test/src/premake4.lua | 25 + Extras/premake4.lua | 1 + 33 files changed, 12368 insertions(+) create mode 100644 Extras/VHACD/inc/btAlignedAllocator.h create mode 100644 Extras/VHACD/inc/btAlignedObjectArray.h create mode 100644 Extras/VHACD/inc/btConvexHullComputer.h create mode 100644 Extras/VHACD/inc/btMinMax.h create mode 100644 Extras/VHACD/inc/btScalar.h create mode 100644 Extras/VHACD/inc/btVector3.h create mode 100644 Extras/VHACD/inc/vhacdCircularList.h create mode 100644 Extras/VHACD/inc/vhacdCircularList.inl create mode 100644 Extras/VHACD/inc/vhacdICHull.h create mode 100644 Extras/VHACD/inc/vhacdManifoldMesh.h create mode 100644 Extras/VHACD/inc/vhacdMesh.h create mode 100644 Extras/VHACD/inc/vhacdMutex.h create mode 100644 Extras/VHACD/inc/vhacdSArray.h create mode 100644 Extras/VHACD/inc/vhacdTimer.h create mode 100644 Extras/VHACD/inc/vhacdVHACD.h create mode 100644 Extras/VHACD/inc/vhacdVector.h create mode 100644 Extras/VHACD/inc/vhacdVector.inl create mode 100644 Extras/VHACD/inc/vhacdVolume.h create mode 100644 Extras/VHACD/premake4.lua create mode 100644 Extras/VHACD/public/VHACD.h create mode 100644 Extras/VHACD/src/VHACD.cpp create mode 100644 Extras/VHACD/src/btAlignedAllocator.cpp create mode 100644 Extras/VHACD/src/btConvexHullComputer.cpp create mode 100644 Extras/VHACD/src/premake4.lua create mode 100644 Extras/VHACD/src/vhacdICHull.cpp create mode 100644 Extras/VHACD/src/vhacdManifoldMesh.cpp create mode 100644 Extras/VHACD/src/vhacdMesh.cpp create mode 100644 Extras/VHACD/src/vhacdVolume.cpp create mode 100644 Extras/VHACD/test/inc/oclHelper.h create mode 100644 Extras/VHACD/test/src/main.cpp create mode 100644 Extras/VHACD/test/src/oclHelper.cpp create mode 100644 Extras/VHACD/test/src/premake4.lua diff --git a/Extras/VHACD/inc/btAlignedAllocator.h b/Extras/VHACD/inc/btAlignedAllocator.h new file mode 100644 index 000000000..4af0472af --- /dev/null +++ b/Extras/VHACD/inc/btAlignedAllocator.h @@ -0,0 +1,104 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_ALIGNED_ALLOCATOR +#define BT_ALIGNED_ALLOCATOR + +///we probably replace this with our own aligned memory allocator +///so we replace _aligned_malloc and _aligned_free with our own +///that is better portable and more predictable + +#include "btScalar.h" +//#define BT_DEBUG_MEMORY_ALLOCATIONS 1 +#ifdef BT_DEBUG_MEMORY_ALLOCATIONS + +#define btAlignedAlloc(a, b) \ + btAlignedAllocInternal(a, b, __LINE__, __FILE__) + +#define btAlignedFree(ptr) \ + btAlignedFreeInternal(ptr, __LINE__, __FILE__) + +void* btAlignedAllocInternal(size_t size, int alignment, int line, char* filename); + +void btAlignedFreeInternal(void* ptr, int line, char* filename); + +#else +void* btAlignedAllocInternal(size_t size, int alignment); +void btAlignedFreeInternal(void* ptr); + +#define btAlignedAlloc(size, alignment) btAlignedAllocInternal(size, alignment) +#define btAlignedFree(ptr) btAlignedFreeInternal(ptr) + +#endif +typedef int size_type; + +typedef void*(btAlignedAllocFunc)(size_t size, int alignment); +typedef void(btAlignedFreeFunc)(void* memblock); +typedef void*(btAllocFunc)(size_t size); +typedef void(btFreeFunc)(void* memblock); + +///The developer can let all Bullet memory allocations go through a custom memory allocator, using btAlignedAllocSetCustom +void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc); +///If the developer has already an custom aligned allocator, then btAlignedAllocSetCustomAligned can be used. The default aligned allocator pre-allocates extra memory using the non-aligned allocator, and instruments it. +void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc); + +///The btAlignedAllocator is a portable class for aligned memory allocations. +///Default implementations for unaligned and aligned allocations can be overridden by a custom allocator using btAlignedAllocSetCustom and btAlignedAllocSetCustomAligned. +template +class btAlignedAllocator { + + typedef btAlignedAllocator self_type; + +public: + //just going down a list: + btAlignedAllocator() {} + /* + btAlignedAllocator( const self_type & ) {} + */ + + template + btAlignedAllocator(const btAlignedAllocator&) {} + + typedef const T* const_pointer; + typedef const T& const_reference; + typedef T* pointer; + typedef T& reference; + typedef T value_type; + + pointer address(reference ref) const { return &ref; } + const_pointer address(const_reference ref) const { return &ref; } + pointer allocate(size_type n, const_pointer* hint = 0) + { + (void)hint; + return reinterpret_cast(btAlignedAlloc(sizeof(value_type) * n, Alignment)); + } + void construct(pointer ptr, const value_type& value) { new (ptr) value_type(value); } + void deallocate(pointer ptr) + { + btAlignedFree(reinterpret_cast(ptr)); + } + void destroy(pointer ptr) { ptr->~value_type(); } + + template + struct rebind { + typedef btAlignedAllocator other; + }; + template + self_type& operator=(const btAlignedAllocator&) { return *this; } + + friend bool operator==(const self_type&, const self_type&) { return true; } +}; + +#endif //BT_ALIGNED_ALLOCATOR diff --git a/Extras/VHACD/inc/btAlignedObjectArray.h b/Extras/VHACD/inc/btAlignedObjectArray.h new file mode 100644 index 000000000..49413c856 --- /dev/null +++ b/Extras/VHACD/inc/btAlignedObjectArray.h @@ -0,0 +1,448 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_OBJECT_ARRAY__ +#define BT_OBJECT_ARRAY__ + +#include "btAlignedAllocator.h" +#include "btScalar.h" // has definitions like SIMD_FORCE_INLINE + +///If the platform doesn't support placement new, you can disable BT_USE_PLACEMENT_NEW +///then the btAlignedObjectArray doesn't support objects with virtual methods, and non-trivial constructors/destructors +///You can enable BT_USE_MEMCPY, then swapping elements in the array will use memcpy instead of operator= +///see discussion here: http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1231 and +///http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1240 + +#define BT_USE_PLACEMENT_NEW 1 +//#define BT_USE_MEMCPY 1 //disable, because it is cumbersome to find out for each platform where memcpy is defined. It can be in or or otherwise... +#define BT_ALLOW_ARRAY_COPY_OPERATOR // enabling this can accidently perform deep copies of data if you are not careful + +#ifdef BT_USE_MEMCPY +#include +#include +#endif //BT_USE_MEMCPY + +#ifdef BT_USE_PLACEMENT_NEW +#include //for placement new +#endif //BT_USE_PLACEMENT_NEW + +///The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods +///It is developed to replace stl::vector to avoid portability issues, including STL alignment issues to add SIMD/SSE data +template +//template +class btAlignedObjectArray { + btAlignedAllocator m_allocator; + + int m_size; + int m_capacity; + T* m_data; + //PCK: added this line + bool m_ownsMemory; + +#ifdef BT_ALLOW_ARRAY_COPY_OPERATOR +public: + SIMD_FORCE_INLINE btAlignedObjectArray& operator=(const btAlignedObjectArray& other) + { + copyFromArray(other); + return *this; + } +#else //BT_ALLOW_ARRAY_COPY_OPERATOR +private: + SIMD_FORCE_INLINE btAlignedObjectArray& operator=(const btAlignedObjectArray& other); +#endif //BT_ALLOW_ARRAY_COPY_OPERATOR + +protected: + SIMD_FORCE_INLINE int allocSize(int size) + { + return (size ? size * 2 : 1); + } + SIMD_FORCE_INLINE void copy(int start, int end, T* dest) const + { + int i; + for (i = start; i < end; ++i) +#ifdef BT_USE_PLACEMENT_NEW + new (&dest[i]) T(m_data[i]); +#else + dest[i] = m_data[i]; +#endif //BT_USE_PLACEMENT_NEW + } + + SIMD_FORCE_INLINE void init() + { + //PCK: added this line + m_ownsMemory = true; + m_data = 0; + m_size = 0; + m_capacity = 0; + } + SIMD_FORCE_INLINE void destroy(int first, int last) + { + int i; + for (i = first; i < last; i++) { + m_data[i].~T(); + } + } + + SIMD_FORCE_INLINE void* allocate(int size) + { + if (size) + return m_allocator.allocate(size); + return 0; + } + + SIMD_FORCE_INLINE void deallocate() + { + if (m_data) { + //PCK: enclosed the deallocation in this block + if (m_ownsMemory) { + m_allocator.deallocate(m_data); + } + m_data = 0; + } + } + +public: + btAlignedObjectArray() + { + init(); + } + + ~btAlignedObjectArray() + { + clear(); + } + + ///Generally it is best to avoid using the copy constructor of an btAlignedObjectArray, and use a (const) reference to the array instead. + btAlignedObjectArray(const btAlignedObjectArray& otherArray) + { + init(); + + int otherSize = otherArray.size(); + resize(otherSize); + otherArray.copy(0, otherSize, m_data); + } + + /// return the number of elements in the array + SIMD_FORCE_INLINE int size() const + { + return m_size; + } + + SIMD_FORCE_INLINE const T& at(int n) const + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE T& at(int n) + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE const T& operator[](int n) const + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE T& operator[](int n) + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + ///clear the array, deallocated memory. Generally it is better to use array.resize(0), to reduce performance overhead of run-time memory (de)allocations. + SIMD_FORCE_INLINE void clear() + { + destroy(0, size()); + + deallocate(); + + init(); + } + + SIMD_FORCE_INLINE void pop_back() + { + btAssert(m_size > 0); + m_size--; + m_data[m_size].~T(); + } + + ///resize changes the number of elements in the array. If the new size is larger, the new elements will be constructed using the optional second argument. + ///when the new number of elements is smaller, the destructor will be called, but memory will not be freed, to reduce performance overhead of run-time memory (de)allocations. + SIMD_FORCE_INLINE void resize(int newsize, const T& fillData = T()) + { + int curSize = size(); + + if (newsize < curSize) { + for (int i = newsize; i < curSize; i++) { + m_data[i].~T(); + } + } + else { + if (newsize > size()) { + reserve(newsize); + } +#ifdef BT_USE_PLACEMENT_NEW + for (int i = curSize; i < newsize; i++) { + new (&m_data[i]) T(fillData); + } +#endif //BT_USE_PLACEMENT_NEW + } + + m_size = newsize; + } + + SIMD_FORCE_INLINE T& expandNonInitializing() + { + int sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + m_size++; + + return m_data[sz]; + } + + SIMD_FORCE_INLINE T& expand(const T& fillValue = T()) + { + int sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + m_size++; +#ifdef BT_USE_PLACEMENT_NEW + new (&m_data[sz]) T(fillValue); //use the in-place new (not really allocating heap memory) +#endif + + return m_data[sz]; + } + + SIMD_FORCE_INLINE void push_back(const T& _Val) + { + int sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + +#ifdef BT_USE_PLACEMENT_NEW + new (&m_data[m_size]) T(_Val); +#else + m_data[size()] = _Val; +#endif //BT_USE_PLACEMENT_NEW + + m_size++; + } + + /// return the pre-allocated (reserved) elements, this is at least as large as the total number of elements,see size() and reserve() + SIMD_FORCE_INLINE int capacity() const + { + return m_capacity; + } + + SIMD_FORCE_INLINE void reserve(int _Count) + { // determine new minimum length of allocated storage + if (capacity() < _Count) { // not enough room, reallocate + T* s = (T*)allocate(_Count); + + copy(0, size(), s); + + destroy(0, size()); + + deallocate(); + + //PCK: added this line + m_ownsMemory = true; + + m_data = s; + + m_capacity = _Count; + } + } + + class less { + public: + bool operator()(const T& a, const T& b) + { + return (a < b); + } + }; + + template + void quickSortInternal(const L& CompareFunc, int lo, int hi) + { + // lo is the lower index, hi is the upper index + // of the region of array a that is to be sorted + int i = lo, j = hi; + T x = m_data[(lo + hi) / 2]; + + // partition + do { + while (CompareFunc(m_data[i], x)) + i++; + while (CompareFunc(x, m_data[j])) + j--; + if (i <= j) { + swap(i, j); + i++; + j--; + } + } while (i <= j); + + // recursion + if (lo < j) + quickSortInternal(CompareFunc, lo, j); + if (i < hi) + quickSortInternal(CompareFunc, i, hi); + } + + template + void quickSort(const L& CompareFunc) + { + //don't sort 0 or 1 elements + if (size() > 1) { + quickSortInternal(CompareFunc, 0, size() - 1); + } + } + + ///heap sort from http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/ + template + void downHeap(T* pArr, int k, int n, const L& CompareFunc) + { + /* PRE: a[k+1..N] is a heap */ + /* POST: a[k..N] is a heap */ + + T temp = pArr[k - 1]; + /* k has child(s) */ + while (k <= n / 2) { + int child = 2 * k; + + if ((child < n) && CompareFunc(pArr[child - 1], pArr[child])) { + child++; + } + /* pick larger child */ + if (CompareFunc(temp, pArr[child - 1])) { + /* move child up */ + pArr[k - 1] = pArr[child - 1]; + k = child; + } + else { + break; + } + } + pArr[k - 1] = temp; + } /*downHeap*/ + + void swap(int index0, int index1) + { +#ifdef BT_USE_MEMCPY + char temp[sizeof(T)]; + memcpy(temp, &m_data[index0], sizeof(T)); + memcpy(&m_data[index0], &m_data[index1], sizeof(T)); + memcpy(&m_data[index1], temp, sizeof(T)); +#else + T temp = m_data[index0]; + m_data[index0] = m_data[index1]; + m_data[index1] = temp; +#endif //BT_USE_PLACEMENT_NEW + } + + template + void heapSort(const L& CompareFunc) + { + /* sort a[0..N-1], N.B. 0 to N-1 */ + int k; + int n = m_size; + for (k = n / 2; k > 0; k--) { + downHeap(m_data, k, n, CompareFunc); + } + + /* a[1..N] is now a heap */ + while (n >= 1) { + swap(0, n - 1); /* largest of a[0..n-1] */ + + n = n - 1; + /* restore a[1..i-1] heap */ + downHeap(m_data, 1, n, CompareFunc); + } + } + + ///non-recursive binary search, assumes sorted array + int findBinarySearch(const T& key) const + { + int first = 0; + int last = size() - 1; + + //assume sorted array + while (first <= last) { + int mid = (first + last) / 2; // compute mid point. + if (key > m_data[mid]) + first = mid + 1; // repeat search in top half. + else if (key < m_data[mid]) + last = mid - 1; // repeat search in bottom half. + else + return mid; // found it. return position ///// + } + return size(); // failed to find key + } + + int findLinearSearch(const T& key) const + { + int index = size(); + int i; + + for (i = 0; i < size(); i++) { + if (m_data[i] == key) { + index = i; + break; + } + } + return index; + } + + void remove(const T& key) + { + + int findIndex = findLinearSearch(key); + if (findIndex < size()) { + swap(findIndex, size() - 1); + pop_back(); + } + } + + //PCK: whole function + void initializeFromBuffer(void* buffer, int size, int capacity) + { + clear(); + m_ownsMemory = false; + m_data = (T*)buffer; + m_size = size; + m_capacity = capacity; + } + + void copyFromArray(const btAlignedObjectArray& otherArray) + { + int otherSize = otherArray.size(); + resize(otherSize); + otherArray.copy(0, otherSize, m_data); + } +}; + +#endif //BT_OBJECT_ARRAY__ diff --git a/Extras/VHACD/inc/btConvexHullComputer.h b/Extras/VHACD/inc/btConvexHullComputer.h new file mode 100644 index 000000000..d59b81e5d --- /dev/null +++ b/Extras/VHACD/inc/btConvexHullComputer.h @@ -0,0 +1,97 @@ +/* +Copyright (c) 2011 Ole Kniemeyer, MAXON, www.maxon.net + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_CONVEX_HULL_COMPUTER_H +#define BT_CONVEX_HULL_COMPUTER_H + +#include "btAlignedObjectArray.h" +#include "btVector3.h" + +/// Convex hull implementation based on Preparata and Hong +/// See http://code.google.com/p/bullet/issues/detail?id=275 +/// Ole Kniemeyer, MAXON Computer GmbH +class btConvexHullComputer { +private: + btScalar compute(const void* coords, bool doubleCoords, int stride, int count, btScalar shrink, btScalar shrinkClamp); + +public: + class Edge { + private: + int next; + int reverse; + int targetVertex; + + friend class btConvexHullComputer; + + public: + int getSourceVertex() const + { + return (this + reverse)->targetVertex; + } + + int getTargetVertex() const + { + return targetVertex; + } + + const Edge* getNextEdgeOfVertex() const // clockwise list of all edges of a vertex + { + return this + next; + } + + const Edge* getNextEdgeOfFace() const // counter-clockwise list of all edges of a face + { + return (this + reverse)->getNextEdgeOfVertex(); + } + + const Edge* getReverseEdge() const + { + return this + reverse; + } + }; + + // Vertices of the output hull + btAlignedObjectArray vertices; + + // Edges of the output hull + btAlignedObjectArray edges; + + // Faces of the convex hull. Each entry is an index into the "edges" array pointing to an edge of the face. Faces are planar n-gons + btAlignedObjectArray faces; + + /* + Compute convex hull of "count" vertices stored in "coords". "stride" is the difference in bytes + between the addresses of consecutive vertices. If "shrink" is positive, the convex hull is shrunken + by that amount (each face is moved by "shrink" length units towards the center along its normal). + If "shrinkClamp" is positive, "shrink" is clamped to not exceed "shrinkClamp * innerRadius", where "innerRadius" + is the minimum distance of a face to the center of the convex hull. + + The returned value is the amount by which the hull has been shrunken. If it is negative, the amount was so large + that the resulting convex hull is empty. + + The output convex hull can be found in the member variables "vertices", "edges", "faces". + */ + btScalar compute(const float* coords, int stride, int count, btScalar shrink, btScalar shrinkClamp) + { + return compute(coords, false, stride, count, shrink, shrinkClamp); + } + + // same as above, but double precision + btScalar compute(const double* coords, int stride, int count, btScalar shrink, btScalar shrinkClamp) + { + return compute(coords, true, stride, count, shrink, shrinkClamp); + } +}; + +#endif //BT_CONVEX_HULL_COMPUTER_H diff --git a/Extras/VHACD/inc/btMinMax.h b/Extras/VHACD/inc/btMinMax.h new file mode 100644 index 000000000..40b0ceb6e --- /dev/null +++ b/Extras/VHACD/inc/btMinMax.h @@ -0,0 +1,65 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_GEN_MINMAX_H +#define BT_GEN_MINMAX_H + +#include "btScalar.h" + +template +SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b) +{ + return a < b ? a : b; +} + +template +SIMD_FORCE_INLINE const T& btMax(const T& a, const T& b) +{ + return a > b ? a : b; +} + +template +SIMD_FORCE_INLINE const T& btClamped(const T& a, const T& lb, const T& ub) +{ + return a < lb ? lb : (ub < a ? ub : a); +} + +template +SIMD_FORCE_INLINE void btSetMin(T& a, const T& b) +{ + if (b < a) { + a = b; + } +} + +template +SIMD_FORCE_INLINE void btSetMax(T& a, const T& b) +{ + if (a < b) { + a = b; + } +} + +template +SIMD_FORCE_INLINE void btClamp(T& a, const T& lb, const T& ub) +{ + if (a < lb) { + a = lb; + } + else if (ub < a) { + a = ub; + } +} + +#endif //BT_GEN_MINMAX_H diff --git a/Extras/VHACD/inc/btScalar.h b/Extras/VHACD/inc/btScalar.h new file mode 100644 index 000000000..ad3032497 --- /dev/null +++ b/Extras/VHACD/inc/btScalar.h @@ -0,0 +1,532 @@ +/* +Copyright (c) 2003-2009 Erwin Coumans http://bullet.googlecode.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_SCALAR_H +#define BT_SCALAR_H + +#ifdef BT_MANAGED_CODE +//Aligned data types not supported in managed code +#pragma unmanaged +#endif + +#include +#include +#include //size_t for MSVC 6.0 + +/* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ +#define BT_BULLET_VERSION 279 + +inline int btGetVersion() +{ + return BT_BULLET_VERSION; +} + +#if defined(DEBUG) || defined(_DEBUG) +#define BT_DEBUG +#endif + +#ifdef _WIN32 + +#if defined(__MINGW32__) || defined(__CYGWIN__) || (defined(_MSC_VER) && _MSC_VER < 1300) + +#define SIMD_FORCE_INLINE inline +#define ATTRIBUTE_ALIGNED16(a) a +#define ATTRIBUTE_ALIGNED64(a) a +#define ATTRIBUTE_ALIGNED128(a) a +#else +//#define BT_HAS_ALIGNED_ALLOCATOR +#pragma warning(disable : 4324) // disable padding warning +// #pragma warning(disable:4530) // Disable the exception disable but used in MSCV Stl warning. +// #pragma warning(disable:4996) //Turn off warnings about deprecated C routines +// #pragma warning(disable:4786) // Disable the "debug name too long" warning + +#define SIMD_FORCE_INLINE __forceinline +#define ATTRIBUTE_ALIGNED16(a) __declspec(align(16)) a +#define ATTRIBUTE_ALIGNED64(a) __declspec(align(64)) a +#define ATTRIBUTE_ALIGNED128(a) __declspec(align(128)) a +#ifdef _XBOX +#define BT_USE_VMX128 + +#include +#define BT_HAVE_NATIVE_FSEL +#define btFsel(a, b, c) __fsel((a), (b), (c)) +#else + +#if (defined(_WIN32) && (_MSC_VER) && _MSC_VER >= 1400) && (!defined(BT_USE_DOUBLE_PRECISION)) +#define BT_USE_SSE +#include +#endif + +#endif //_XBOX + +#endif //__MINGW32__ + +#include +#ifdef BT_DEBUG +#define btAssert assert +#else +#define btAssert(x) +#endif +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) + +#define btLikely(_c) _c +#define btUnlikely(_c) _c + +#else + +#if defined(__CELLOS_LV2__) +#define SIMD_FORCE_INLINE inline __attribute__((always_inline)) +#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) +#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) +#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) +#ifndef assert +#include +#endif +#ifdef BT_DEBUG +#ifdef __SPU__ +#include +#define printf spu_printf +#define btAssert(x) \ + { \ + if (!(x)) { \ + printf("Assert " __FILE__ ":%u (" #x ")\n", __LINE__); \ + spu_hcmpeq(0, 0); \ + } \ + } +#else +#define btAssert assert +#endif + +#else +#define btAssert(x) +#endif +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) + +#define btLikely(_c) _c +#define btUnlikely(_c) _c + +#else + +#ifdef USE_LIBSPE2 + +#define SIMD_FORCE_INLINE __inline +#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) +#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) +#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) +#ifndef assert +#include +#endif +#ifdef BT_DEBUG +#define btAssert assert +#else +#define btAssert(x) +#endif +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) + +#define btLikely(_c) __builtin_expect((_c), 1) +#define btUnlikely(_c) __builtin_expect((_c), 0) + +#else +//non-windows systems + +#if (defined(__APPLE__) && defined(__i386__) && (!defined(BT_USE_DOUBLE_PRECISION))) +#define BT_USE_SSE +#include + +#define SIMD_FORCE_INLINE inline +///@todo: check out alignment methods for other platforms/compilers +#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) +#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) +#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) +#ifndef assert +#include +#endif + +#if defined(DEBUG) || defined(_DEBUG) +#define btAssert assert +#else +#define btAssert(x) +#endif + +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) +#define btLikely(_c) _c +#define btUnlikely(_c) _c + +#else + +#define SIMD_FORCE_INLINE inline +///@todo: check out alignment methods for other platforms/compilers +///#define ATTRIBUTE_ALIGNED16(a) a __attribute__ ((aligned (16))) +///#define ATTRIBUTE_ALIGNED64(a) a __attribute__ ((aligned (64))) +///#define ATTRIBUTE_ALIGNED128(a) a __attribute__ ((aligned (128))) +#define ATTRIBUTE_ALIGNED16(a) a +#define ATTRIBUTE_ALIGNED64(a) a +#define ATTRIBUTE_ALIGNED128(a) a +#ifndef assert +#include +#endif + +#if defined(DEBUG) || defined(_DEBUG) +#define btAssert assert +#else +#define btAssert(x) +#endif + +//btFullAssert is optional, slows down a lot +#define btFullAssert(x) +#define btLikely(_c) _c +#define btUnlikely(_c) _c +#endif //__APPLE__ + +#endif // LIBSPE2 + +#endif //__CELLOS_LV2__ +#endif + +///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision. +#if defined(BT_USE_DOUBLE_PRECISION) +typedef double btScalar; +//this number could be bigger in double precision +#define BT_LARGE_FLOAT 1e30 +#else +typedef float btScalar; +//keep BT_LARGE_FLOAT*BT_LARGE_FLOAT < FLT_MAX +#define BT_LARGE_FLOAT 1e18f +#endif + +#define BT_DECLARE_ALIGNED_ALLOCATOR() \ + SIMD_FORCE_INLINE void* operator new(size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ + SIMD_FORCE_INLINE void operator delete(void* ptr) { btAlignedFree(ptr); } \ + SIMD_FORCE_INLINE void* operator new(size_t, void* ptr) { return ptr; } \ + SIMD_FORCE_INLINE void operator delete(void*, void*) {} \ + SIMD_FORCE_INLINE void* operator new[](size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ + SIMD_FORCE_INLINE void operator delete[](void* ptr) { btAlignedFree(ptr); } \ + SIMD_FORCE_INLINE void* operator new[](size_t, void* ptr) { return ptr; } \ + SIMD_FORCE_INLINE void operator delete[](void*, void*) {} + +#if defined(BT_USE_DOUBLE_PRECISION) || defined(BT_FORCE_DOUBLE_FUNCTIONS) + +SIMD_FORCE_INLINE btScalar btSqrt(btScalar x) +{ + return sqrt(x); +} +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabs(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cos(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sin(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tan(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return acos(x); +} +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return asin(x); +} +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atan(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return exp(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return log(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return pow(x, y); } +SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmod(x, y); } + +#else + +SIMD_FORCE_INLINE btScalar btSqrt(btScalar y) +{ +#ifdef USE_APPROXIMATION + double x, z, tempf; + unsigned long* tfptr = ((unsigned long*)&tempf) + 1; + + tempf = y; + *tfptr = (0xbfcdd90a - *tfptr) >> 1; /* estimate of 1/sqrt(y) */ + x = tempf; + z = y * btScalar(0.5); + x = (btScalar(1.5) * x) - (x * x) * (x * z); /* iteration formula */ + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + return x * y; +#else + return sqrtf(y); +#endif +} +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabsf(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cosf(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sinf(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tanf(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return acosf(x); +} +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return asinf(x); +} +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atanf(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2f(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return expf(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return logf(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return powf(x, y); } +SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmodf(x, y); } + +#endif + +#define SIMD_2_PI btScalar(6.283185307179586232) +#define SIMD_PI (SIMD_2_PI * btScalar(0.5)) +#define SIMD_HALF_PI (SIMD_2_PI * btScalar(0.25)) +#define SIMD_RADS_PER_DEG (SIMD_2_PI / btScalar(360.0)) +#define SIMD_DEGS_PER_RAD (btScalar(360.0) / SIMD_2_PI) +#define SIMDSQRT12 btScalar(0.7071067811865475244008443621048490) + +#define btRecipSqrt(x) ((btScalar)(btScalar(1.0) / btSqrt(btScalar(x)))) /* reciprocal square root */ + +#ifdef BT_USE_DOUBLE_PRECISION +#define SIMD_EPSILON DBL_EPSILON +#define SIMD_INFINITY DBL_MAX +#else +#define SIMD_EPSILON FLT_EPSILON +#define SIMD_INFINITY FLT_MAX +#endif + +SIMD_FORCE_INLINE btScalar btAtan2Fast(btScalar y, btScalar x) +{ + btScalar coeff_1 = SIMD_PI / 4.0f; + btScalar coeff_2 = 3.0f * coeff_1; + btScalar abs_y = btFabs(y); + btScalar angle; + if (x >= 0.0f) { + btScalar r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } + else { + btScalar r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + return (y < 0.0f) ? -angle : angle; +} + +SIMD_FORCE_INLINE bool btFuzzyZero(btScalar x) { return btFabs(x) < SIMD_EPSILON; } + +SIMD_FORCE_INLINE bool btEqual(btScalar a, btScalar eps) +{ + return (((a) <= eps) && !((a) < -eps)); +} +SIMD_FORCE_INLINE bool btGreaterEqual(btScalar a, btScalar eps) +{ + return (!((a) <= eps)); +} + +SIMD_FORCE_INLINE int btIsNegative(btScalar x) +{ + return x < btScalar(0.0) ? 1 : 0; +} + +SIMD_FORCE_INLINE btScalar btRadians(btScalar x) { return x * SIMD_RADS_PER_DEG; } +SIMD_FORCE_INLINE btScalar btDegrees(btScalar x) { return x * SIMD_DEGS_PER_RAD; } + +#define BT_DECLARE_HANDLE(name) \ + typedef struct name##__ { \ + int unused; \ + } * name + +#ifndef btFsel +SIMD_FORCE_INLINE btScalar btFsel(btScalar a, btScalar b, btScalar c) +{ + return a >= 0 ? b : c; +} +#endif +#define btFsels(a, b, c) (btScalar) btFsel(a, b, c) + +SIMD_FORCE_INLINE bool btMachineIsLittleEndian() +{ + long int i = 1; + const char* p = (const char*)&i; + if (p[0] == 1) // Lowest address contains the least significant byte + return true; + else + return false; +} + +///btSelect avoids branches, which makes performance much better for consoles like Playstation 3 and XBox 360 +///Thanks Phil Knight. See also http://www.cellperformance.com/articles/2006/04/more_techniques_for_eliminatin_1.html +SIMD_FORCE_INLINE unsigned btSelect(unsigned condition, unsigned valueIfConditionNonZero, unsigned valueIfConditionZero) +{ + // Set testNz to 0xFFFFFFFF if condition is nonzero, 0x00000000 if condition is zero + // Rely on positive value or'ed with its negative having sign bit on + // and zero value or'ed with its negative (which is still zero) having sign bit off + // Use arithmetic shift right, shifting the sign bit through all 32 bits + unsigned testNz = (unsigned)(((int)condition | -(int)condition) >> 31); + unsigned testEqz = ~testNz; + return ((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE int btSelect(unsigned condition, int valueIfConditionNonZero, int valueIfConditionZero) +{ + unsigned testNz = (unsigned)(((int)condition | -(int)condition) >> 31); + unsigned testEqz = ~testNz; + return static_cast((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE float btSelect(unsigned condition, float valueIfConditionNonZero, float valueIfConditionZero) +{ +#ifdef BT_HAVE_NATIVE_FSEL + return (float)btFsel((btScalar)condition - btScalar(1.0f), valueIfConditionNonZero, valueIfConditionZero); +#else + return (condition != 0) ? valueIfConditionNonZero : valueIfConditionZero; +#endif +} + +template +SIMD_FORCE_INLINE void btSwap(T& a, T& b) +{ + T tmp = a; + a = b; + b = tmp; +} + +//PCK: endian swapping functions +SIMD_FORCE_INLINE unsigned btSwapEndian(unsigned val) +{ + return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); +} + +SIMD_FORCE_INLINE unsigned short btSwapEndian(unsigned short val) +{ + return static_cast(((val & 0xff00) >> 8) | ((val & 0x00ff) << 8)); +} + +SIMD_FORCE_INLINE unsigned btSwapEndian(int val) +{ + return btSwapEndian((unsigned)val); +} + +SIMD_FORCE_INLINE unsigned short btSwapEndian(short val) +{ + return btSwapEndian((unsigned short)val); +} + +///btSwapFloat uses using char pointers to swap the endianness +////btSwapFloat/btSwapDouble will NOT return a float, because the machine might 'correct' invalid floating point values +///Not all values of sign/exponent/mantissa are valid floating point numbers according to IEEE 754. +///When a floating point unit is faced with an invalid value, it may actually change the value, or worse, throw an exception. +///In most systems, running user mode code, you wouldn't get an exception, but instead the hardware/os/runtime will 'fix' the number for you. +///so instead of returning a float/double, we return integer/long long integer +SIMD_FORCE_INLINE unsigned int btSwapEndianFloat(float d) +{ + unsigned int a = 0; + unsigned char* dst = (unsigned char*)&a; + unsigned char* src = (unsigned char*)&d; + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + return a; +} + +// unswap using char pointers +SIMD_FORCE_INLINE float btUnswapEndianFloat(unsigned int a) +{ + float d = 0.0f; + unsigned char* src = (unsigned char*)&a; + unsigned char* dst = (unsigned char*)&d; + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + + return d; +} + +// swap using char pointers +SIMD_FORCE_INLINE void btSwapEndianDouble(double d, unsigned char* dst) +{ + unsigned char* src = (unsigned char*)&d; + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; +} + +// unswap using char pointers +SIMD_FORCE_INLINE double btUnswapEndianDouble(const unsigned char* src) +{ + double d = 0.0; + unsigned char* dst = (unsigned char*)&d; + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; + + return d; +} + +// returns normalized value in range [-SIMD_PI, SIMD_PI] +SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians) +{ + angleInRadians = btFmod(angleInRadians, SIMD_2_PI); + if (angleInRadians < -SIMD_PI) { + return angleInRadians + SIMD_2_PI; + } + else if (angleInRadians > SIMD_PI) { + return angleInRadians - SIMD_2_PI; + } + else { + return angleInRadians; + } +} + +///rudimentary class to provide type info +struct btTypedObject { + btTypedObject(int objectType) + : m_objectType(objectType) + { + } + int m_objectType; + inline int getObjectType() const + { + return m_objectType; + } +}; +#endif //BT_SCALAR_H diff --git a/Extras/VHACD/inc/btVector3.h b/Extras/VHACD/inc/btVector3.h new file mode 100644 index 000000000..159d67bdd --- /dev/null +++ b/Extras/VHACD/inc/btVector3.h @@ -0,0 +1,715 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_VECTOR3_H +#define BT_VECTOR3_H + +#include "btMinMax.h" +#include "btScalar.h" + +#ifdef BT_USE_DOUBLE_PRECISION +#define btVector3Data btVector3DoubleData +#define btVector3DataName "btVector3DoubleData" +#else +#define btVector3Data btVector3FloatData +#define btVector3DataName "btVector3FloatData" +#endif //BT_USE_DOUBLE_PRECISION + +/**@brief btVector3 can be used to represent 3D points and vectors. + * It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user + * Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers + */ +ATTRIBUTE_ALIGNED16(class) +btVector3 +{ +public: +#if defined(__SPU__) && defined(__CELLOS_LV2__) + btScalar m_floats[4]; + +public: + SIMD_FORCE_INLINE const vec_float4& get128() const + { + return *((const vec_float4*)&m_floats[0]); + } + +public: +#else //__CELLOS_LV2__ __SPU__ +#ifdef BT_USE_SSE // _WIN32 + union { + __m128 mVec128; + btScalar m_floats[4]; + }; + SIMD_FORCE_INLINE __m128 get128() const + { + return mVec128; + } + SIMD_FORCE_INLINE void set128(__m128 v128) + { + mVec128 = v128; + } +#else + btScalar m_floats[4]; +#endif +#endif //__CELLOS_LV2__ __SPU__ + +public: + /**@brief No initialization constructor */ + SIMD_FORCE_INLINE btVector3() {} + + /**@brief Constructor from scalars + * @param x X value + * @param y Y value + * @param z Z value + */ + SIMD_FORCE_INLINE btVector3(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = btScalar(0.); + } + + /**@brief Add a vector to this one + * @param The vector to add to this one */ + SIMD_FORCE_INLINE btVector3& operator+=(const btVector3& v) + { + + m_floats[0] += v.m_floats[0]; + m_floats[1] += v.m_floats[1]; + m_floats[2] += v.m_floats[2]; + return *this; + } + + /**@brief Subtract a vector from this one + * @param The vector to subtract */ + SIMD_FORCE_INLINE btVector3& operator-=(const btVector3& v) + { + m_floats[0] -= v.m_floats[0]; + m_floats[1] -= v.m_floats[1]; + m_floats[2] -= v.m_floats[2]; + return *this; + } + /**@brief Scale the vector + * @param s Scale factor */ + SIMD_FORCE_INLINE btVector3& operator*=(const btScalar& s) + { + m_floats[0] *= s; + m_floats[1] *= s; + m_floats[2] *= s; + return *this; + } + + /**@brief Inversely scale the vector + * @param s Scale factor to divide by */ + SIMD_FORCE_INLINE btVector3& operator/=(const btScalar& s) + { + btFullAssert(s != btScalar(0.0)); + return * this *= btScalar(1.0) / s; + } + + /**@brief Return the dot product + * @param v The other vector in the dot product */ + SIMD_FORCE_INLINE btScalar dot(const btVector3& v) const + { + return m_floats[0] * v.m_floats[0] + m_floats[1] * v.m_floats[1] + m_floats[2] * v.m_floats[2]; + } + + /**@brief Return the length of the vector squared */ + SIMD_FORCE_INLINE btScalar length2() const + { + return dot(*this); + } + + /**@brief Return the length of the vector */ + SIMD_FORCE_INLINE btScalar length() const + { + return btSqrt(length2()); + } + + /**@brief Return the distance squared between the ends of this and another vector + * This is symantically treating the vector like a point */ + SIMD_FORCE_INLINE btScalar distance2(const btVector3& v) const; + + /**@brief Return the distance between the ends of this and another vector + * This is symantically treating the vector like a point */ + SIMD_FORCE_INLINE btScalar distance(const btVector3& v) const; + + SIMD_FORCE_INLINE btVector3& safeNormalize() + { + btVector3 absVec = this->absolute(); + int maxIndex = absVec.maxAxis(); + if (absVec[maxIndex] > 0) { + *this /= absVec[maxIndex]; + return * this /= length(); + } + setValue(1, 0, 0); + return *this; + } + + /**@brief Normalize this vector + * x^2 + y^2 + z^2 = 1 */ + SIMD_FORCE_INLINE btVector3& normalize() + { + return * this /= length(); + } + + /**@brief Return a normalized version of this vector */ + SIMD_FORCE_INLINE btVector3 normalized() const; + + /**@brief Return a rotated version of this vector + * @param wAxis The axis to rotate about + * @param angle The angle to rotate by */ + SIMD_FORCE_INLINE btVector3 rotate(const btVector3& wAxis, const btScalar angle) const; + + /**@brief Return the angle between this and another vector + * @param v The other vector */ + SIMD_FORCE_INLINE btScalar angle(const btVector3& v) const + { + btScalar s = btSqrt(length2() * v.length2()); + btFullAssert(s != btScalar(0.0)); + return btAcos(dot(v) / s); + } + /**@brief Return a vector will the absolute values of each element */ + SIMD_FORCE_INLINE btVector3 absolute() const + { + return btVector3( + btFabs(m_floats[0]), + btFabs(m_floats[1]), + btFabs(m_floats[2])); + } + /**@brief Return the cross product between this and another vector + * @param v The other vector */ + SIMD_FORCE_INLINE btVector3 cross(const btVector3& v) const + { + return btVector3( + m_floats[1] * v.m_floats[2] - m_floats[2] * v.m_floats[1], + m_floats[2] * v.m_floats[0] - m_floats[0] * v.m_floats[2], + m_floats[0] * v.m_floats[1] - m_floats[1] * v.m_floats[0]); + } + + SIMD_FORCE_INLINE btScalar triple(const btVector3& v1, const btVector3& v2) const + { + return m_floats[0] * (v1.m_floats[1] * v2.m_floats[2] - v1.m_floats[2] * v2.m_floats[1]) + m_floats[1] * (v1.m_floats[2] * v2.m_floats[0] - v1.m_floats[0] * v2.m_floats[2]) + m_floats[2] * (v1.m_floats[0] * v2.m_floats[1] - v1.m_floats[1] * v2.m_floats[0]); + } + + /**@brief Return the axis with the smallest value + * Note return values are 0,1,2 for x, y, or z */ + SIMD_FORCE_INLINE int minAxis() const + { + return m_floats[0] < m_floats[1] ? (m_floats[0] < m_floats[2] ? 0 : 2) : (m_floats[1] < m_floats[2] ? 1 : 2); + } + + /**@brief Return the axis with the largest value + * Note return values are 0,1,2 for x, y, or z */ + SIMD_FORCE_INLINE int maxAxis() const + { + return m_floats[0] < m_floats[1] ? (m_floats[1] < m_floats[2] ? 2 : 1) : (m_floats[0] < m_floats[2] ? 2 : 0); + } + + SIMD_FORCE_INLINE int furthestAxis() const + { + return absolute().minAxis(); + } + + SIMD_FORCE_INLINE int closestAxis() const + { + return absolute().maxAxis(); + } + + SIMD_FORCE_INLINE void setInterpolate3(const btVector3& v0, const btVector3& v1, btScalar rt) + { + btScalar s = btScalar(1.0) - rt; + m_floats[0] = s * v0.m_floats[0] + rt * v1.m_floats[0]; + m_floats[1] = s * v0.m_floats[1] + rt * v1.m_floats[1]; + m_floats[2] = s * v0.m_floats[2] + rt * v1.m_floats[2]; + //don't do the unused w component + // m_co[3] = s * v0[3] + rt * v1[3]; + } + + /**@brief Return the linear interpolation between this and another vector + * @param v The other vector + * @param t The ration of this to v (t = 0 => return this, t=1 => return other) */ + SIMD_FORCE_INLINE btVector3 lerp(const btVector3& v, const btScalar& t) const + { + return btVector3(m_floats[0] + (v.m_floats[0] - m_floats[0]) * t, + m_floats[1] + (v.m_floats[1] - m_floats[1]) * t, + m_floats[2] + (v.m_floats[2] - m_floats[2]) * t); + } + + /**@brief Elementwise multiply this vector by the other + * @param v The other vector */ + SIMD_FORCE_INLINE btVector3& operator*=(const btVector3& v) + { + m_floats[0] *= v.m_floats[0]; + m_floats[1] *= v.m_floats[1]; + m_floats[2] *= v.m_floats[2]; + return *this; + } + + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& getX() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& getY() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& getZ() const { return m_floats[2]; } + /**@brief Set the x value */ + SIMD_FORCE_INLINE void setX(btScalar x) { m_floats[0] = x; }; + /**@brief Set the y value */ + SIMD_FORCE_INLINE void setY(btScalar y) { m_floats[1] = y; }; + /**@brief Set the z value */ + SIMD_FORCE_INLINE void setZ(btScalar z) { m_floats[2] = z; }; + /**@brief Set the w value */ + SIMD_FORCE_INLINE void setW(btScalar w) { m_floats[3] = w; }; + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& x() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& y() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& z() const { return m_floats[2]; } + /**@brief Return the w value */ + SIMD_FORCE_INLINE const btScalar& w() const { return m_floats[3]; } + + //SIMD_FORCE_INLINE btScalar& operator[](int i) { return (&m_floats[0])[i]; } + //SIMD_FORCE_INLINE const btScalar& operator[](int i) const { return (&m_floats[0])[i]; } + ///operator btScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons. + SIMD_FORCE_INLINE operator btScalar*() { return &m_floats[0]; } + SIMD_FORCE_INLINE operator const btScalar*() const { return &m_floats[0]; } + + SIMD_FORCE_INLINE bool operator==(const btVector3& other) const + { + return ((m_floats[3] == other.m_floats[3]) && (m_floats[2] == other.m_floats[2]) && (m_floats[1] == other.m_floats[1]) && (m_floats[0] == other.m_floats[0])); + } + + SIMD_FORCE_INLINE bool operator!=(const btVector3& other) const + { + return !(*this == other); + } + + /**@brief Set each element to the max of the current values and the values of another btVector3 + * @param other The other btVector3 to compare with + */ + SIMD_FORCE_INLINE void setMax(const btVector3& other) + { + btSetMax(m_floats[0], other.m_floats[0]); + btSetMax(m_floats[1], other.m_floats[1]); + btSetMax(m_floats[2], other.m_floats[2]); + btSetMax(m_floats[3], other.w()); + } + /**@brief Set each element to the min of the current values and the values of another btVector3 + * @param other The other btVector3 to compare with + */ + SIMD_FORCE_INLINE void setMin(const btVector3& other) + { + btSetMin(m_floats[0], other.m_floats[0]); + btSetMin(m_floats[1], other.m_floats[1]); + btSetMin(m_floats[2], other.m_floats[2]); + btSetMin(m_floats[3], other.w()); + } + + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = btScalar(0.); + } + + void getSkewSymmetricMatrix(btVector3 * v0, btVector3 * v1, btVector3 * v2) const + { + v0->setValue(0., -z(), y()); + v1->setValue(z(), 0., -x()); + v2->setValue(-y(), x(), 0.); + } + + void setZero() + { + setValue(btScalar(0.), btScalar(0.), btScalar(0.)); + } + + SIMD_FORCE_INLINE bool isZero() const + { + return m_floats[0] == btScalar(0) && m_floats[1] == btScalar(0) && m_floats[2] == btScalar(0); + } + + SIMD_FORCE_INLINE bool fuzzyZero() const + { + return length2() < SIMD_EPSILON; + } + + SIMD_FORCE_INLINE void serialize(struct btVector3Data & dataOut) const; + + SIMD_FORCE_INLINE void deSerialize(const struct btVector3Data& dataIn); + + SIMD_FORCE_INLINE void serializeFloat(struct btVector3FloatData & dataOut) const; + + SIMD_FORCE_INLINE void deSerializeFloat(const struct btVector3FloatData& dataIn); + + SIMD_FORCE_INLINE void serializeDouble(struct btVector3DoubleData & dataOut) const; + + SIMD_FORCE_INLINE void deSerializeDouble(const struct btVector3DoubleData& dataIn); +}; + +/**@brief Return the sum of two vectors (Point symantics)*/ +SIMD_FORCE_INLINE btVector3 +operator+(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] + v2.m_floats[0], v1.m_floats[1] + v2.m_floats[1], v1.m_floats[2] + v2.m_floats[2]); +} + +/**@brief Return the elementwise product of two vectors */ +SIMD_FORCE_INLINE btVector3 +operator*(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] * v2.m_floats[0], v1.m_floats[1] * v2.m_floats[1], v1.m_floats[2] * v2.m_floats[2]); +} + +/**@brief Return the difference between two vectors */ +SIMD_FORCE_INLINE btVector3 +operator-(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] - v2.m_floats[0], v1.m_floats[1] - v2.m_floats[1], v1.m_floats[2] - v2.m_floats[2]); +} +/**@brief Return the negative of the vector */ +SIMD_FORCE_INLINE btVector3 +operator-(const btVector3& v) +{ + return btVector3(-v.m_floats[0], -v.m_floats[1], -v.m_floats[2]); +} + +/**@brief Return the vector scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator*(const btVector3& v, const btScalar& s) +{ + return btVector3(v.m_floats[0] * s, v.m_floats[1] * s, v.m_floats[2] * s); +} + +/**@brief Return the vector scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator*(const btScalar& s, const btVector3& v) +{ + return v * s; +} + +/**@brief Return the vector inversely scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator/(const btVector3& v, const btScalar& s) +{ + btFullAssert(s != btScalar(0.0)); + return v * (btScalar(1.0) / s); +} + +/**@brief Return the vector inversely scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator/(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] / v2.m_floats[0], v1.m_floats[1] / v2.m_floats[1], v1.m_floats[2] / v2.m_floats[2]); +} + +/**@brief Return the dot product between two vectors */ +SIMD_FORCE_INLINE btScalar +btDot(const btVector3& v1, const btVector3& v2) +{ + return v1.dot(v2); +} + +/**@brief Return the distance squared between two vectors */ +SIMD_FORCE_INLINE btScalar +btDistance2(const btVector3& v1, const btVector3& v2) +{ + return v1.distance2(v2); +} + +/**@brief Return the distance between two vectors */ +SIMD_FORCE_INLINE btScalar +btDistance(const btVector3& v1, const btVector3& v2) +{ + return v1.distance(v2); +} + +/**@brief Return the angle between two vectors */ +SIMD_FORCE_INLINE btScalar +btAngle(const btVector3& v1, const btVector3& v2) +{ + return v1.angle(v2); +} + +/**@brief Return the cross product of two vectors */ +SIMD_FORCE_INLINE btVector3 +btCross(const btVector3& v1, const btVector3& v2) +{ + return v1.cross(v2); +} + +SIMD_FORCE_INLINE btScalar +btTriple(const btVector3& v1, const btVector3& v2, const btVector3& v3) +{ + return v1.triple(v2, v3); +} + +/**@brief Return the linear interpolation between two vectors + * @param v1 One vector + * @param v2 The other vector + * @param t The ration of this to v (t = 0 => return v1, t=1 => return v2) */ +SIMD_FORCE_INLINE btVector3 +lerp(const btVector3& v1, const btVector3& v2, const btScalar& t) +{ + return v1.lerp(v2, t); +} + +SIMD_FORCE_INLINE btScalar btVector3::distance2(const btVector3& v) const +{ + return (v - *this).length2(); +} + +SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3& v) const +{ + return (v - *this).length(); +} + +SIMD_FORCE_INLINE btVector3 btVector3::normalized() const +{ + return *this / length(); +} + +SIMD_FORCE_INLINE btVector3 btVector3::rotate(const btVector3& wAxis, const btScalar angle) const +{ + // wAxis must be a unit lenght vector + + btVector3 o = wAxis * wAxis.dot(*this); + btVector3 x = *this - o; + btVector3 y; + + y = wAxis.cross(*this); + + return (o + x * btCos(angle) + y * btSin(angle)); +} + +class btVector4 : public btVector3 { +public: + SIMD_FORCE_INLINE btVector4() {} + + SIMD_FORCE_INLINE btVector4(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) + : btVector3(x, y, z) + { + m_floats[3] = w; + } + + SIMD_FORCE_INLINE btVector4 absolute4() const + { + return btVector4( + btFabs(m_floats[0]), + btFabs(m_floats[1]), + btFabs(m_floats[2]), + btFabs(m_floats[3])); + } + + btScalar getW() const { return m_floats[3]; } + + SIMD_FORCE_INLINE int maxAxis4() const + { + int maxIndex = -1; + btScalar maxVal = btScalar(-BT_LARGE_FLOAT); + if (m_floats[0] > maxVal) { + maxIndex = 0; + maxVal = m_floats[0]; + } + if (m_floats[1] > maxVal) { + maxIndex = 1; + maxVal = m_floats[1]; + } + if (m_floats[2] > maxVal) { + maxIndex = 2; + maxVal = m_floats[2]; + } + if (m_floats[3] > maxVal) { + maxIndex = 3; + } + return maxIndex; + } + + SIMD_FORCE_INLINE int minAxis4() const + { + int minIndex = -1; + btScalar minVal = btScalar(BT_LARGE_FLOAT); + if (m_floats[0] < minVal) { + minIndex = 0; + minVal = m_floats[0]; + } + if (m_floats[1] < minVal) { + minIndex = 1; + minVal = m_floats[1]; + } + if (m_floats[2] < minVal) { + minIndex = 2; + minVal = m_floats[2]; + } + if (m_floats[3] < minVal) { + minIndex = 3; + } + + return minIndex; + } + + SIMD_FORCE_INLINE int closestAxis4() const + { + return absolute4().maxAxis4(); + } + + /**@brief Set x,y,z and zero w + * @param x Value of x + * @param y Value of y + * @param z Value of z + */ + + /* void getValue(btScalar *m) const + { + m[0] = m_floats[0]; + m[1] = m_floats[1]; + m[2] =m_floats[2]; + } +*/ + /**@brief Set the values + * @param x Value of x + * @param y Value of y + * @param z Value of z + * @param w Value of w + */ + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = w; + } +}; + +///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar& sourceVal, btScalar& destVal) +{ +#ifdef BT_USE_DOUBLE_PRECISION + unsigned char* dest = (unsigned char*)&destVal; + unsigned char* src = (unsigned char*)&sourceVal; + dest[0] = src[7]; + dest[1] = src[6]; + dest[2] = src[5]; + dest[3] = src[4]; + dest[4] = src[3]; + dest[5] = src[2]; + dest[6] = src[1]; + dest[7] = src[0]; +#else + unsigned char* dest = (unsigned char*)&destVal; + unsigned char* src = (unsigned char*)&sourceVal; + dest[0] = src[3]; + dest[1] = src[2]; + dest[2] = src[1]; + dest[3] = src[0]; +#endif //BT_USE_DOUBLE_PRECISION +} +///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btSwapVector3Endian(const btVector3& sourceVec, btVector3& destVec) +{ + for (int i = 0; i < 4; i++) { + btSwapScalarEndian(sourceVec[i], destVec[i]); + } +} + +///btUnSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btUnSwapVector3Endian(btVector3& vector) +{ + + btVector3 swappedVec; + for (int i = 0; i < 4; i++) { + btSwapScalarEndian(vector[i], swappedVec[i]); + } + vector = swappedVec; +} + +template +SIMD_FORCE_INLINE void btPlaneSpace1(const T& n, T& p, T& q) +{ + if (btFabs(n[2]) > SIMDSQRT12) { + // choose p in y-z plane + btScalar a = n[1] * n[1] + n[2] * n[2]; + btScalar k = btRecipSqrt(a); + p[0] = 0; + p[1] = -n[2] * k; + p[2] = n[1] * k; + // set q = n x p + q[0] = a * k; + q[1] = -n[0] * p[2]; + q[2] = n[0] * p[1]; + } + else { + // choose p in x-y plane + btScalar a = n[0] * n[0] + n[1] * n[1]; + btScalar k = btRecipSqrt(a); + p[0] = -n[1] * k; + p[1] = n[0] * k; + p[2] = 0; + // set q = n x p + q[0] = -n[2] * p[1]; + q[1] = n[2] * p[0]; + q[2] = a * k; + } +} + +struct btVector3FloatData { + float m_floats[4]; +}; + +struct btVector3DoubleData { + double m_floats[4]; +}; + +SIMD_FORCE_INLINE void btVector3::serializeFloat(struct btVector3FloatData& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int i = 0; i < 4; i++) + dataOut.m_floats[i] = float(m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::deSerializeFloat(const struct btVector3FloatData& dataIn) +{ + for (int i = 0; i < 4; i++) + m_floats[i] = btScalar(dataIn.m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::serializeDouble(struct btVector3DoubleData& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int i = 0; i < 4; i++) + dataOut.m_floats[i] = double(m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::deSerializeDouble(const struct btVector3DoubleData& dataIn) +{ + for (int i = 0; i < 4; i++) + m_floats[i] = btScalar(dataIn.m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::serialize(struct btVector3Data& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int i = 0; i < 4; i++) + dataOut.m_floats[i] = m_floats[i]; +} + +SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn) +{ + for (int i = 0; i < 4; i++) + m_floats[i] = dataIn.m_floats[i]; +} + +#endif //BT_VECTOR3_H diff --git a/Extras/VHACD/inc/vhacdCircularList.h b/Extras/VHACD/inc/vhacdCircularList.h new file mode 100644 index 000000000..0f5ddf9ec --- /dev/null +++ b/Extras/VHACD/inc/vhacdCircularList.h @@ -0,0 +1,79 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_CIRCULAR_LIST_H +#define VHACD_CIRCULAR_LIST_H +#include +namespace VHACD { +//! CircularListElement class. +template +class CircularListElement { +public: + T& GetData() { return m_data; } + const T& GetData() const { return m_data; } + CircularListElement*& GetNext() { return m_next; } + CircularListElement*& GetPrev() { return m_prev; } + const CircularListElement*& GetNext() const { return m_next; } + const CircularListElement*& GetPrev() const { return m_prev; } + //! Constructor + CircularListElement(const T& data) { m_data = data; } + CircularListElement(void) {} + //! Destructor + ~CircularListElement(void) {} +private: + T m_data; + CircularListElement* m_next; + CircularListElement* m_prev; + + CircularListElement(const CircularListElement& rhs); +}; +//! CircularList class. +template +class CircularList { +public: + CircularListElement*& GetHead() { return m_head; } + const CircularListElement* GetHead() const { return m_head; } + bool IsEmpty() const { return (m_size == 0); } + size_t GetSize() const { return m_size; } + const T& GetData() const { return m_head->GetData(); } + T& GetData() { return m_head->GetData(); } + bool Delete(); + bool Delete(CircularListElement* element); + CircularListElement* Add(const T* data = 0); + CircularListElement* Add(const T& data); + bool Next(); + bool Prev(); + void Clear() + { + while (Delete()) + ; + }; + const CircularList& operator=(const CircularList& rhs); + //! Constructor + CircularList() + { + m_head = 0; + m_size = 0; + } + CircularList(const CircularList& rhs); + //! Destructor + ~CircularList(void) { Clear(); }; +private: + CircularListElement* m_head; //!< a pointer to the head of the circular list + size_t m_size; //!< number of element in the circular list +}; +} +#include "vhacdCircularList.inl" +#endif // VHACD_CIRCULAR_LIST_H \ No newline at end of file diff --git a/Extras/VHACD/inc/vhacdCircularList.inl b/Extras/VHACD/inc/vhacdCircularList.inl new file mode 100644 index 000000000..2be518052 --- /dev/null +++ b/Extras/VHACD/inc/vhacdCircularList.inl @@ -0,0 +1,161 @@ +#pragma once +#ifndef HACD_CIRCULAR_LIST_INL +#define HACD_CIRCULAR_LIST_INL +namespace VHACD +{ + template < typename T > + inline bool CircularList::Delete(CircularListElement * element) + { + if (!element) + { + return false; + } + if (m_size > 1) + { + CircularListElement * next = element->GetNext(); + CircularListElement * prev = element->GetPrev(); + delete element; + m_size--; + if (element == m_head) + { + m_head = next; + } + next->GetPrev() = prev; + prev->GetNext() = next; + return true; + } + else if (m_size == 1) + { + delete m_head; + m_size--; + m_head = 0; + return true; + } + else + { + return false; + } + } + + template < typename T > + inline bool CircularList::Delete() + { + if (m_size > 1) + { + CircularListElement * next = m_head->GetNext(); + CircularListElement * prev = m_head->GetPrev(); + delete m_head; + m_size--; + m_head = next; + next->GetPrev() = prev; + prev->GetNext() = next; + return true; + } + else if (m_size == 1) + { + delete m_head; + m_size--; + m_head = 0; + return true; + } + else + { + return false; + } + } + template < typename T > + inline CircularListElement * CircularList::Add(const T * data) + { + if (m_size == 0) + { + if (data) + { + m_head = new CircularListElement(*data); + } + else + { + m_head = new CircularListElement(); + } + m_head->GetNext() = m_head->GetPrev() = m_head; + } + else + { + CircularListElement * next = m_head->GetNext(); + CircularListElement * element = m_head; + if (data) + { + m_head = new CircularListElement(*data); + } + else + { + m_head = new CircularListElement(); + } + m_head->GetNext() = next; + m_head->GetPrev() = element; + element->GetNext() = m_head; + next->GetPrev() = m_head; + } + m_size++; + return m_head; + } + template < typename T > + inline CircularListElement * CircularList::Add(const T & data) + { + const T * pData = &data; + return Add(pData); + } + template < typename T > + inline bool CircularList::Next() + { + if (m_size == 0) + { + return false; + } + m_head = m_head->GetNext(); + return true; + } + template < typename T > + inline bool CircularList::Prev() + { + if (m_size == 0) + { + return false; + } + m_head = m_head->GetPrev(); + return true; + } + template < typename T > + inline CircularList::CircularList(const CircularList& rhs) + { + if (rhs.m_size > 0) + { + CircularListElement * current = rhs.m_head; + do + { + current = current->GetNext(); + Add(current->GetData()); + } + while ( current != rhs.m_head ); + } + } + template < typename T > + inline const CircularList& CircularList::operator=(const CircularList& rhs) + { + if (&rhs != this) + { + Clear(); + if (rhs.m_size > 0) + { + CircularListElement * current = rhs.m_head; + do + { + current = current->GetNext(); + Add(current->GetData()); + } + while ( current != rhs.m_head ); + } + } + return (*this); + } +} +#endif \ No newline at end of file diff --git a/Extras/VHACD/inc/vhacdICHull.h b/Extras/VHACD/inc/vhacdICHull.h new file mode 100644 index 000000000..a3d941651 --- /dev/null +++ b/Extras/VHACD/inc/vhacdICHull.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_ICHULL_H +#define VHACD_ICHULL_H +#include "vhacdManifoldMesh.h" +#include "vhacdVector.h" + +namespace VHACD { +//! Incremental Convex Hull algorithm (cf. http://cs.smith.edu/~orourke/books/ftp.html ). +enum ICHullError { + ICHullErrorOK = 0, + ICHullErrorCoplanarPoints, + ICHullErrorNoVolume, + ICHullErrorInconsistent, + ICHullErrorNotEnoughPoints +}; +class ICHull { +public: + static const double sc_eps; + //! + bool IsFlat() { return m_isFlat; } + //! Returns the computed mesh + TMMesh& GetMesh() { return m_mesh; } + //! Add one point to the convex-hull + bool AddPoint(const Vec3& point) { return AddPoints(&point, 1); } + //! Add one point to the convex-hull + bool AddPoint(const Vec3& point, int id); + //! Add points to the convex-hull + bool AddPoints(const Vec3* points, size_t nPoints); + //! + ICHullError Process(); + //! + ICHullError Process(const unsigned int nPointsCH, const double minVolume = 0.0); + //! + bool IsInside(const Vec3& pt0, const double eps = 0.0); + //! + const ICHull& operator=(ICHull& rhs); + + //! Constructor + ICHull(); + //! Destructor + ~ICHull(void){}; + +private: + //! DoubleTriangle builds the initial double triangle. It first finds 3 noncollinear points and makes two faces out of them, in opposite order. It then finds a fourth point that is not coplanar with that face. The vertices are stored in the face structure in counterclockwise order so that the volume between the face and the point is negative. Lastly, the 3 newfaces to the fourth point are constructed and the data structures are cleaned up. + ICHullError DoubleTriangle(); + //! MakeFace creates a new face structure from three vertices (in ccw order). It returns a pointer to the face. + CircularListElement* MakeFace(CircularListElement* v0, + CircularListElement* v1, + CircularListElement* v2, + CircularListElement* fold); + //! + CircularListElement* MakeConeFace(CircularListElement* e, CircularListElement* v); + //! + bool ProcessPoint(); + //! + bool ComputePointVolume(double& totalVolume, bool markVisibleFaces); + //! + bool FindMaxVolumePoint(const double minVolume = 0.0); + //! + bool CleanEdges(); + //! + bool CleanVertices(unsigned int& addedPoints); + //! + bool CleanTriangles(); + //! + bool CleanUp(unsigned int& addedPoints); + //! + bool MakeCCW(CircularListElement* f, + CircularListElement* e, + CircularListElement* v); + void Clear(); + +private: + static const int sc_dummyIndex; + TMMesh m_mesh; + SArray*> m_edgesToDelete; + SArray*> m_edgesToUpdate; + SArray*> m_trianglesToDelete; + Vec3 m_normal; + bool m_isFlat; + ICHull(const ICHull& rhs); +}; +} +#endif // VHACD_ICHULL_H diff --git a/Extras/VHACD/inc/vhacdManifoldMesh.h b/Extras/VHACD/inc/vhacdManifoldMesh.h new file mode 100644 index 000000000..f178ad3d5 --- /dev/null +++ b/Extras/VHACD/inc/vhacdManifoldMesh.h @@ -0,0 +1,142 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) +All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once +#ifndef VHACD_MANIFOLD_MESH_H +#define VHACD_MANIFOLD_MESH_H +#include "vhacdCircularList.h" +#include "vhacdSArray.h" +#include "vhacdVector.h" +namespace VHACD { +class TMMTriangle; +class TMMEdge; +class TMMesh; +class ICHull; + +//! Vertex data structure used in a triangular manifold mesh (TMM). +class TMMVertex { +public: + void Initialize(); + TMMVertex(void); + ~TMMVertex(void); + +private: + Vec3 m_pos; + int m_name; + size_t m_id; + CircularListElement* m_duplicate; // pointer to incident cone edge (or NULL) + bool m_onHull; + bool m_tag; + TMMVertex(const TMMVertex& rhs); + friend class ICHull; + friend class TMMesh; + friend class TMMTriangle; + friend class TMMEdge; +}; + +//! Edge data structure used in a triangular manifold mesh (TMM). +class TMMEdge { +public: + void Initialize(); + TMMEdge(void); + ~TMMEdge(void); + +private: + size_t m_id; + CircularListElement* m_triangles[2]; + CircularListElement* m_vertices[2]; + CircularListElement* m_newFace; + TMMEdge(const TMMEdge& rhs); + friend class ICHull; + friend class TMMTriangle; + friend class TMMVertex; + friend class TMMesh; +}; + +//! Triangle data structure used in a triangular manifold mesh (TMM). +class TMMTriangle { +public: + void Initialize(); + TMMTriangle(void); + ~TMMTriangle(void); + +private: + size_t m_id; + CircularListElement* m_edges[3]; + CircularListElement* m_vertices[3]; + bool m_visible; + + TMMTriangle(const TMMTriangle& rhs); + friend class ICHull; + friend class TMMesh; + friend class TMMVertex; + friend class TMMEdge; +}; +//! triangular manifold mesh data structure. +class TMMesh { +public: + //! Returns the number of vertices> + inline size_t GetNVertices() const { return m_vertices.GetSize(); } + //! Returns the number of edges + inline size_t GetNEdges() const { return m_edges.GetSize(); } + //! Returns the number of triangles + inline size_t GetNTriangles() const { return m_triangles.GetSize(); } + //! Returns the vertices circular list + inline const CircularList& GetVertices() const { return m_vertices; } + //! Returns the edges circular list + inline const CircularList& GetEdges() const { return m_edges; } + //! Returns the triangles circular list + inline const CircularList& GetTriangles() const { return m_triangles; } + //! Returns the vertices circular list + inline CircularList& GetVertices() { return m_vertices; } + //! Returns the edges circular list + inline CircularList& GetEdges() { return m_edges; } + //! Returns the triangles circular list + inline CircularList& GetTriangles() { return m_triangles; } + //! Add vertex to the mesh + CircularListElement* AddVertex() { return m_vertices.Add(); } + //! Add vertex to the mesh + CircularListElement* AddEdge() { return m_edges.Add(); } + //! Add vertex to the mesh + CircularListElement* AddTriangle() { return m_triangles.Add(); } + //! Print mesh information + void Print(); + //! + void GetIFS(Vec3* const points, Vec3* const triangles); + //! + void Clear(); + //! + void Copy(TMMesh& mesh); + //! + bool CheckConsistancy(); + //! + bool Normalize(); + //! + bool Denormalize(); + //! Constructor + TMMesh(); + //! Destructor + virtual ~TMMesh(void); + +private: + CircularList m_vertices; + CircularList m_edges; + CircularList m_triangles; + + // not defined + TMMesh(const TMMesh& rhs); + friend class ICHull; +}; +} +#endif // VHACD_MANIFOLD_MESH_H \ No newline at end of file diff --git a/Extras/VHACD/inc/vhacdMesh.h b/Extras/VHACD/inc/vhacdMesh.h new file mode 100644 index 000000000..0975d8e1f --- /dev/null +++ b/Extras/VHACD/inc/vhacdMesh.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_MESH_H +#define VHACD_MESH_H +#include "vhacdSArray.h" +#include "vhacdVector.h" + +#define VHACD_DEBUG_MESH + +namespace VHACD { +enum AXIS { + AXIS_X = 0, + AXIS_Y = 1, + AXIS_Z = 2 +}; +struct Plane { + double m_a; + double m_b; + double m_c; + double m_d; + AXIS m_axis; + short m_index; +}; +#ifdef VHACD_DEBUG_MESH +struct Material { + + Vec3 m_diffuseColor; + double m_ambientIntensity; + Vec3 m_specularColor; + Vec3 m_emissiveColor; + double m_shininess; + double m_transparency; + Material(void) + { + m_diffuseColor.X() = 0.5; + m_diffuseColor.Y() = 0.5; + m_diffuseColor.Z() = 0.5; + m_specularColor.X() = 0.5; + m_specularColor.Y() = 0.5; + m_specularColor.Z() = 0.5; + m_ambientIntensity = 0.4; + m_emissiveColor.X() = 0.0; + m_emissiveColor.Y() = 0.0; + m_emissiveColor.Z() = 0.0; + m_shininess = 0.4; + m_transparency = 0.0; + }; +}; +#endif // VHACD_DEBUG_MESH + +//! Triangular mesh data structure +class Mesh { +public: + void AddPoint(const Vec3& pt) { m_points.PushBack(pt); }; + void SetPoint(size_t index, const Vec3& pt) { m_points[index] = pt; }; + const Vec3& GetPoint(size_t index) const { return m_points[index]; }; + Vec3& GetPoint(size_t index) { return m_points[index]; }; + size_t GetNPoints() const { return m_points.Size(); }; + double* GetPoints() { return (double*)m_points.Data(); } // ugly + const double* const GetPoints() const { return (double*)m_points.Data(); } // ugly + const Vec3* const GetPointsBuffer() const { return m_points.Data(); } // + Vec3* const GetPointsBuffer() { return m_points.Data(); } // + void AddTriangle(const Vec3& tri) { m_triangles.PushBack(tri); }; + void SetTriangle(size_t index, const Vec3& tri) { m_triangles[index] = tri; }; + const Vec3& GetTriangle(size_t index) const { return m_triangles[index]; }; + Vec3& GetTriangle(size_t index) { return m_triangles[index]; }; + size_t GetNTriangles() const { return m_triangles.Size(); }; + int* GetTriangles() { return (int*)m_triangles.Data(); } // ugly + const int* const GetTriangles() const { return (int*)m_triangles.Data(); } // ugly + const Vec3* const GetTrianglesBuffer() const { return m_triangles.Data(); } + Vec3* const GetTrianglesBuffer() { return m_triangles.Data(); } + const Vec3& GetCenter() const { return m_center; } + const Vec3& GetMinBB() const { return m_minBB; } + const Vec3& GetMaxBB() const { return m_maxBB; } + void ClearPoints() { m_points.Clear(); } + void ClearTriangles() { m_triangles.Clear(); } + void Clear() + { + ClearPoints(); + ClearTriangles(); + } + void ResizePoints(size_t nPts) { m_points.Resize(nPts); } + void ResizeTriangles(size_t nTri) { m_triangles.Resize(nTri); } + void CopyPoints(SArray >& points) const { points = m_points; } + double GetDiagBB() const { return m_diag; } + double ComputeVolume() const; + void ComputeConvexHull(const double* const pts, + const size_t nPts); + void Clip(const Plane& plane, + SArray >& positivePart, + SArray >& negativePart) const; + bool IsInside(const Vec3& pt) const; + double ComputeDiagBB(); + +#ifdef VHACD_DEBUG_MESH + bool LoadOFF(const std::string& fileName, bool invert); + bool SaveVRML2(const std::string& fileName) const; + bool SaveVRML2(std::ofstream& fout, const Material& material) const; + bool SaveOFF(const std::string& fileName) const; +#endif // VHACD_DEBUG_MESH + + //! Constructor. + Mesh(); + //! Destructor. + ~Mesh(void); + +private: + SArray > m_points; + SArray > m_triangles; + Vec3 m_minBB; + Vec3 m_maxBB; + Vec3 m_center; + double m_diag; +}; +} +#endif \ No newline at end of file diff --git a/Extras/VHACD/inc/vhacdMutex.h b/Extras/VHACD/inc/vhacdMutex.h new file mode 100644 index 000000000..a28f6be61 --- /dev/null +++ b/Extras/VHACD/inc/vhacdMutex.h @@ -0,0 +1,146 @@ +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#pragma once +#ifndef VHACD_MUTEX_H +#define VHACD_MUTEX_H + +#if defined(WIN32) + +//#define _WIN32_WINNT 0x400 +#include +#pragma comment(lib, "winmm.lib") +#endif + +#if defined(__linux__) +//#include +#include +#include +#include +#define __stdcall +#endif + +#if defined(__APPLE__) || defined(__linux__) +#include +#endif + +#if defined(__APPLE__) +#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE +#endif + +#define VHACD_DEBUG + +//#define VHACD_NDEBUG +#ifdef VHACD_NDEBUG +#define VHACD_VERIFY(x) (x) +#else +#define VHACD_VERIFY(x) assert((x)) +#endif + +namespace VHACD { +class Mutex { +public: + Mutex(void) + { +#if defined(WIN32) || defined(_XBOX) + InitializeCriticalSection(&m_mutex); +#elif defined(__APPLE__) || defined(__linux__) + pthread_mutexattr_t mutexAttr; // Mutex Attribute + VHACD_VERIFY(pthread_mutexattr_init(&mutexAttr) == 0); + VHACD_VERIFY(pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0); + VHACD_VERIFY(pthread_mutex_init(&m_mutex, &mutexAttr) == 0); + VHACD_VERIFY(pthread_mutexattr_destroy(&mutexAttr) == 0); +#endif + } + ~Mutex(void) + { +#if defined(WIN32) || defined(_XBOX) + DeleteCriticalSection(&m_mutex); +#elif defined(__APPLE__) || defined(__linux__) + VHACD_VERIFY(pthread_mutex_destroy(&m_mutex) == 0); +#endif + } + void Lock(void) + { +#if defined(WIN32) || defined(_XBOX) + EnterCriticalSection(&m_mutex); +#elif defined(__APPLE__) || defined(__linux__) + VHACD_VERIFY(pthread_mutex_lock(&m_mutex) == 0); +#endif + } + bool TryLock(void) + { +#if defined(WIN32) || defined(_XBOX) + bool bRet = false; + //assert(("TryEnterCriticalSection seems to not work on XP???", 0)); + bRet = TryEnterCriticalSection(&m_mutex) ? true : false; + return bRet; +#elif defined(__APPLE__) || defined(__linux__) + int result = pthread_mutex_trylock(&m_mutex); + return (result == 0); +#endif + } + + void Unlock(void) + { +#if defined(WIN32) || defined(_XBOX) + LeaveCriticalSection(&m_mutex); +#elif defined(__APPLE__) || defined(__linux__) + VHACD_VERIFY(pthread_mutex_unlock(&m_mutex) == 0); +#endif + } + +private: +#if defined(WIN32) || defined(_XBOX) + CRITICAL_SECTION m_mutex; +#elif defined(__APPLE__) || defined(__linux__) + pthread_mutex_t m_mutex; +#endif +}; +} +#endif // VHACD_MUTEX_H diff --git a/Extras/VHACD/inc/vhacdSArray.h b/Extras/VHACD/inc/vhacdSArray.h new file mode 100644 index 000000000..4d84d1ae2 --- /dev/null +++ b/Extras/VHACD/inc/vhacdSArray.h @@ -0,0 +1,158 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_SARRAY_H +#define VHACD_SARRAY_H +#include +#include +#include + +#define SARRAY_DEFAULT_MIN_SIZE 16 + +namespace VHACD { +//! SArray. +template +class SArray { +public: + T& operator[](size_t i) + { + T* const data = Data(); + return data[i]; + } + const T& operator[](size_t i) const + { + const T* const data = Data(); + return data[i]; + } + size_t Size() const + { + return m_size; + } + T* const Data() + { + return (m_maxSize == N) ? m_data0 : m_data; + } + const T* const Data() const + { + return (m_maxSize == N) ? m_data0 : m_data; + } + void Clear() + { + m_size = 0; + delete[] m_data; + m_data = 0; + m_maxSize = N; + } + void PopBack() + { + --m_size; + } + void Allocate(size_t size) + { + if (size > m_maxSize) { + T* temp = new T[size]; + memcpy(temp, Data(), m_size * sizeof(T)); + delete[] m_data; + m_data = temp; + m_maxSize = size; + } + } + void Resize(size_t size) + { + Allocate(size); + m_size = size; + } + + void PushBack(const T& value) + { + if (m_size == m_maxSize) { + size_t maxSize = (m_maxSize << 1); + T* temp = new T[maxSize]; + memcpy(temp, Data(), m_maxSize * sizeof(T)); + delete[] m_data; + m_data = temp; + m_maxSize = maxSize; + } + T* const data = Data(); + data[m_size++] = value; + } + bool Find(const T& value, size_t& pos) + { + T* const data = Data(); + for (pos = 0; pos < m_size; ++pos) + if (value == data[pos]) + return true; + return false; + } + bool Insert(const T& value) + { + size_t pos; + if (Find(value, pos)) + return false; + PushBack(value); + return true; + } + bool Erase(const T& value) + { + size_t pos; + T* const data = Data(); + if (Find(value, pos)) { + for (size_t j = pos + 1; j < m_size; ++j) + data[j - 1] = data[j]; + --m_size; + return true; + } + return false; + } + void operator=(const SArray& rhs) + { + if (m_maxSize < rhs.m_size) { + delete[] m_data; + m_maxSize = rhs.m_maxSize; + m_data = new T[m_maxSize]; + } + m_size = rhs.m_size; + memcpy(Data(), rhs.Data(), m_size * sizeof(T)); + } + void Initialize() + { + m_data = 0; + m_size = 0; + m_maxSize = N; + } + SArray(const SArray& rhs) + { + m_data = 0; + m_size = 0; + m_maxSize = N; + *this = rhs; + } + SArray() + { + Initialize(); + } + ~SArray() + { + delete[] m_data; + } + +private: + T m_data0[N]; + T* m_data; + size_t m_size; + size_t m_maxSize; +}; +} +#endif \ No newline at end of file diff --git a/Extras/VHACD/inc/vhacdTimer.h b/Extras/VHACD/inc/vhacdTimer.h new file mode 100644 index 000000000..ba0e2c336 --- /dev/null +++ b/Extras/VHACD/inc/vhacdTimer.h @@ -0,0 +1,121 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_TIMER_H +#define VHACD_TIMER_H + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#endif +#include +#elif __MACH__ +#include +#include +#else +#include +#include +#endif + +namespace VHACD { +#ifdef _WIN32 +class Timer { +public: + Timer(void) + { + m_start.QuadPart = 0; + m_stop.QuadPart = 0; + QueryPerformanceFrequency(&m_freq); + }; + ~Timer(void){}; + void Tic() + { + QueryPerformanceCounter(&m_start); + } + void Toc() + { + QueryPerformanceCounter(&m_stop); + } + double GetElapsedTime() // in ms + { + LARGE_INTEGER delta; + delta.QuadPart = m_stop.QuadPart - m_start.QuadPart; + return (1000.0 * delta.QuadPart) / (double)m_freq.QuadPart; + } + +private: + LARGE_INTEGER m_start; + LARGE_INTEGER m_stop; + LARGE_INTEGER m_freq; +}; + +#elif __MACH__ +class Timer { +public: + Timer(void) + { + memset(this, 0, sizeof(Timer)); + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &m_cclock); + }; + ~Timer(void) + { + mach_port_deallocate(mach_task_self(), m_cclock); + }; + void Tic() + { + clock_get_time(m_cclock, &m_start); + } + void Toc() + { + clock_get_time(m_cclock, &m_stop); + } + double GetElapsedTime() // in ms + { + return 1000.0 * (m_stop.tv_sec - m_start.tv_sec + (1.0E-9) * (m_stop.tv_nsec - m_start.tv_nsec)); + } + +private: + clock_serv_t m_cclock; + mach_timespec_t m_start; + mach_timespec_t m_stop; +}; +#else +class Timer { +public: + Timer(void) + { + memset(this, 0, sizeof(Timer)); + }; + ~Timer(void){}; + void Tic() + { + clock_gettime(CLOCK_REALTIME, &m_start); + } + void Toc() + { + clock_gettime(CLOCK_REALTIME, &m_stop); + } + double GetElapsedTime() // in ms + { + return 1000.0 * (m_stop.tv_sec - m_start.tv_sec + (1.0E-9) * (m_stop.tv_nsec - m_start.tv_nsec)); + } + +private: + struct timespec m_start; + struct timespec m_stop; +}; +#endif +} +#endif // VHACD_TIMER_H diff --git a/Extras/VHACD/inc/vhacdVHACD.h b/Extras/VHACD/inc/vhacdVHACD.h new file mode 100644 index 000000000..8c6c7fea1 --- /dev/null +++ b/Extras/VHACD/inc/vhacdVHACD.h @@ -0,0 +1,350 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) +All rights reserved. + + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once +#ifndef VHACD_VHACD_H +#define VHACD_VHACD_H + +#ifdef OPENCL_FOUND +#ifdef __MACH__ +#include +#else +#include +#endif +#endif //OPENCL_FOUND + +#include "vhacdMutex.h" +#include "vhacdVolume.h" + +#define USE_THREAD 1 +#define OCL_MIN_NUM_PRIMITIVES 4096 +#define CH_APP_MIN_NUM_PRIMITIVES 64000 +namespace VHACD { +class VHACD : public IVHACD { +public: + //! Constructor. + VHACD() + { +#if USE_THREAD == 1 && _OPENMP + m_ompNumProcessors = 2 * omp_get_num_procs(); + omp_set_num_threads(m_ompNumProcessors); +#else //USE_THREAD == 1 && _OPENMP + m_ompNumProcessors = 1; +#endif //USE_THREAD == 1 && _OPENMP +#ifdef CL_VERSION_1_1 + m_oclWorkGroupSize = 0; + m_oclDevice = 0; + m_oclQueue = 0; + m_oclKernelComputePartialVolumes = 0; + m_oclKernelComputeSum = 0; +#endif //CL_VERSION_1_1 + Init(); + } + //! Destructor. + ~VHACD(void) {} + unsigned int GetNConvexHulls() const + { + return (unsigned int)m_convexHulls.Size(); + } + void Cancel() + { + SetCancel(true); + } + void GetConvexHull(const unsigned int index, ConvexHull& ch) const + { + Mesh* mesh = m_convexHulls[index]; + ch.m_nPoints = (unsigned int)mesh->GetNPoints(); + ch.m_nTriangles = (unsigned int)mesh->GetNTriangles(); + ch.m_points = mesh->GetPoints(); + ch.m_triangles = mesh->GetTriangles(); + } + void Clean(void) + { + delete m_volume; + delete m_pset; + size_t nCH = m_convexHulls.Size(); + for (size_t p = 0; p < nCH; ++p) { + delete m_convexHulls[p]; + } + m_convexHulls.Clear(); + Init(); + } + void Release(void) + { + delete this; + } + bool Compute(const float* const points, + const unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int nTriangles, + const Parameters& params); + bool Compute(const double* const points, + const unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int nTriangles, + const Parameters& params); + bool OCLInit(void* const oclDevice, + IUserLogger* const logger = 0); + bool OCLRelease(IUserLogger* const logger = 0); + +private: + void SetCancel(bool cancel) + { + m_cancelMutex.Lock(); + m_cancel = cancel; + m_cancelMutex.Unlock(); + } + bool GetCancel() + { + + m_cancelMutex.Lock(); + bool cancel = m_cancel; + m_cancelMutex.Unlock(); + return cancel; + } + void Update(const double stageProgress, + const double operationProgress, + const Parameters& params) + { + m_stageProgress = stageProgress; + m_operationProgress = operationProgress; + if (params.m_callback) { + params.m_callback->Update(m_overallProgress, + m_stageProgress, + m_operationProgress, + m_stage.c_str(), + m_operation.c_str()); + } + } + void Init() + { + memset(m_rot, 0, sizeof(double) * 9); + m_dim = 64; + m_volume = 0; + m_volumeCH0 = 0.0; + m_pset = 0; + m_overallProgress = 0.0; + m_stageProgress = 0.0; + m_operationProgress = 0.0; + m_stage = ""; + m_operation = ""; + m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; + m_rot[0][0] = m_rot[1][1] = m_rot[2][2] = 1.0; + SetCancel(false); + } + void ComputePrimitiveSet(const Parameters& params); + void ComputeACD(const Parameters& params); + void MergeConvexHulls(const Parameters& params); + void SimplifyConvexHulls(const Parameters& params); + void ComputeBestClippingPlane(const PrimitiveSet* inputPSet, + const double volume, + const SArray& planes, + const Vec3& preferredCuttingDirection, + const double w, + const double alpha, + const double beta, + const int convexhullDownsampling, + const double progress0, + const double progress1, + Plane& bestPlane, + double& minConcavity, + const Parameters& params); + template + void AlignMesh(const T* const points, + const unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int nTriangles, + const Parameters& params) + { + if (GetCancel() || !params.m_pca) { + return; + } + m_timer.Tic(); + + m_stage = "Align mesh"; + m_operation = "Voxelization"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + if (GetCancel()) { + return; + } + m_dim = (size_t)(pow((double)params.m_resolution, 1.0 / 3.0) + 0.5); + Volume volume; + volume.Voxelize(points, stridePoints, nPoints, + triangles, strideTriangles, nTriangles, + m_dim, m_barycenter, m_rot); + size_t n = volume.GetNPrimitivesOnSurf() + volume.GetNPrimitivesInsideSurf(); + Update(50.0, 100.0, params); + + if (params.m_logger) { + msg.str(""); + msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + if (GetCancel()) { + return; + } + m_operation = "PCA"; + Update(50.0, 0.0, params); + volume.AlignToPrincipalAxes(m_rot); + m_overallProgress = 1.0; + Update(100.0, 100.0, params); + + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + } + template + void VoxelizeMesh(const T* const points, + const unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int nTriangles, + const Parameters& params) + { + if (GetCancel()) { + return; + } + + m_timer.Tic(); + m_stage = "Voxelization"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + delete m_volume; + m_volume = 0; + int iteration = 0; + const int maxIteration = 5; + double progress = 0.0; + while (iteration++ < maxIteration && !m_cancel) { + msg.str(""); + msg << "Iteration " << iteration; + m_operation = msg.str(); + + progress = iteration * 100.0 / maxIteration; + Update(progress, 0.0, params); + + m_volume = new Volume; + m_volume->Voxelize(points, stridePoints, nPoints, + triangles, strideTriangles, nTriangles, + m_dim, m_barycenter, m_rot); + + Update(progress, 100.0, params); + + size_t n = m_volume->GetNPrimitivesOnSurf() + m_volume->GetNPrimitivesInsideSurf(); + if (params.m_logger) { + msg.str(""); + msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + double a = pow((double)(params.m_resolution) / n, 0.33); + size_t dim_next = (size_t)(m_dim * a + 0.5); + if (n < params.m_resolution && iteration < maxIteration && m_volume->GetNPrimitivesOnSurf() < params.m_resolution / 8 && m_dim != dim_next) { + delete m_volume; + m_volume = 0; + m_dim = dim_next; + } + else { + break; + } + } + m_overallProgress = 10.0; + Update(100.0, 100.0, params); + + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + } + template + bool ComputeACD(const T* const points, + const unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int nTriangles, + const Parameters& params) + { + Init(); + if (params.m_oclAcceleration) { + // build kernals + } + AlignMesh(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params); + VoxelizeMesh(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params); + ComputePrimitiveSet(params); + ComputeACD(params); + MergeConvexHulls(params); + SimplifyConvexHulls(params); + if (params.m_oclAcceleration) { + // Release kernals + } + if (GetCancel()) { + Clean(); + return false; + } + return true; + } + +private: + SArray m_convexHulls; + std::string m_stage; + std::string m_operation; + double m_overallProgress; + double m_stageProgress; + double m_operationProgress; + double m_rot[3][3]; + double m_volumeCH0; + Vec3 m_barycenter; + Timer m_timer; + size_t m_dim; + Volume* m_volume; + PrimitiveSet* m_pset; + Mutex m_cancelMutex; + bool m_cancel; + int m_ompNumProcessors; +#ifdef CL_VERSION_1_1 + cl_device_id* m_oclDevice; + cl_context m_oclContext; + cl_program m_oclProgram; + cl_command_queue* m_oclQueue; + cl_kernel* m_oclKernelComputePartialVolumes; + cl_kernel* m_oclKernelComputeSum; + size_t m_oclWorkGroupSize; +#endif //CL_VERSION_1_1 +}; +} +#endif // VHACD_VHACD_H diff --git a/Extras/VHACD/inc/vhacdVector.h b/Extras/VHACD/inc/vhacdVector.h new file mode 100644 index 000000000..c9edfb1ae --- /dev/null +++ b/Extras/VHACD/inc/vhacdVector.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_VECTOR_H +#define VHACD_VECTOR_H +#include +#include + +namespace VHACD { +//! Vector dim 3. +template +class Vec3 { +public: + T& operator[](size_t i) { return m_data[i]; } + const T& operator[](size_t i) const { return m_data[i]; } + T& X(); + T& Y(); + T& Z(); + const T& X() const; + const T& Y() const; + const T& Z() const; + void Normalize(); + T GetNorm() const; + void operator=(const Vec3& rhs); + void operator+=(const Vec3& rhs); + void operator-=(const Vec3& rhs); + void operator-=(T a); + void operator+=(T a); + void operator/=(T a); + void operator*=(T a); + Vec3 operator^(const Vec3& rhs) const; + T operator*(const Vec3& rhs) const; + Vec3 operator+(const Vec3& rhs) const; + Vec3 operator-(const Vec3& rhs) const; + Vec3 operator-() const; + Vec3 operator*(T rhs) const; + Vec3 operator/(T rhs) const; + bool operator<(const Vec3& rhs) const; + bool operator>(const Vec3& rhs) const; + Vec3(); + Vec3(T a); + Vec3(T x, T y, T z); + Vec3(const Vec3& rhs); + /*virtual*/ ~Vec3(void); + +private: + T m_data[3]; +}; +//! Vector dim 2. +template +class Vec2 { +public: + T& operator[](size_t i) { return m_data[i]; } + const T& operator[](size_t i) const { return m_data[i]; } + T& X(); + T& Y(); + const T& X() const; + const T& Y() const; + void Normalize(); + T GetNorm() const; + void operator=(const Vec2& rhs); + void operator+=(const Vec2& rhs); + void operator-=(const Vec2& rhs); + void operator-=(T a); + void operator+=(T a); + void operator/=(T a); + void operator*=(T a); + T operator^(const Vec2& rhs) const; + T operator*(const Vec2& rhs) const; + Vec2 operator+(const Vec2& rhs) const; + Vec2 operator-(const Vec2& rhs) const; + Vec2 operator-() const; + Vec2 operator*(T rhs) const; + Vec2 operator/(T rhs) const; + Vec2(); + Vec2(T a); + Vec2(T x, T y); + Vec2(const Vec2& rhs); + /*virtual*/ ~Vec2(void); + +private: + T m_data[2]; +}; + +template +const bool Colinear(const Vec3& a, const Vec3& b, const Vec3& c); +template +const T ComputeVolume4(const Vec3& a, const Vec3& b, const Vec3& c, const Vec3& d); +} +#include "vhacdVector.inl" // template implementation +#endif \ No newline at end of file diff --git a/Extras/VHACD/inc/vhacdVector.inl b/Extras/VHACD/inc/vhacdVector.inl new file mode 100644 index 000000000..223c2ef17 --- /dev/null +++ b/Extras/VHACD/inc/vhacdVector.inl @@ -0,0 +1,362 @@ +#pragma once +#ifndef VHACD_VECTOR_INL +#define VHACD_VECTOR_INL +namespace VHACD +{ + template + inline Vec3 operator*(T lhs, const Vec3 & rhs) + { + return Vec3(lhs * rhs.X(), lhs * rhs.Y(), lhs * rhs.Z()); + } + template + inline T & Vec3::X() + { + return m_data[0]; + } + template + inline T & Vec3::Y() + { + return m_data[1]; + } + template + inline T & Vec3::Z() + { + return m_data[2]; + } + template + inline const T & Vec3::X() const + { + return m_data[0]; + } + template + inline const T & Vec3::Y() const + { + return m_data[1]; + } + template + inline const T & Vec3::Z() const + { + return m_data[2]; + } + template + inline void Vec3::Normalize() + { + T n = sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]); + if (n != 0.0) (*this) /= n; + } + template + inline T Vec3::GetNorm() const + { + return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]); + } + template + inline void Vec3::operator= (const Vec3 & rhs) + { + this->m_data[0] = rhs.m_data[0]; + this->m_data[1] = rhs.m_data[1]; + this->m_data[2] = rhs.m_data[2]; + } + template + inline void Vec3::operator+=(const Vec3 & rhs) + { + this->m_data[0] += rhs.m_data[0]; + this->m_data[1] += rhs.m_data[1]; + this->m_data[2] += rhs.m_data[2]; + } + template + inline void Vec3::operator-=(const Vec3 & rhs) + { + this->m_data[0] -= rhs.m_data[0]; + this->m_data[1] -= rhs.m_data[1]; + this->m_data[2] -= rhs.m_data[2]; + } + template + inline void Vec3::operator-=(T a) + { + this->m_data[0] -= a; + this->m_data[1] -= a; + this->m_data[2] -= a; + } + template + inline void Vec3::operator+=(T a) + { + this->m_data[0] += a; + this->m_data[1] += a; + this->m_data[2] += a; + } + template + inline void Vec3::operator/=(T a) + { + this->m_data[0] /= a; + this->m_data[1] /= a; + this->m_data[2] /= a; + } + template + inline void Vec3::operator*=(T a) + { + this->m_data[0] *= a; + this->m_data[1] *= a; + this->m_data[2] *= a; + } + template + inline Vec3 Vec3::operator^ (const Vec3 & rhs) const + { + return Vec3(m_data[1] * rhs.m_data[2] - m_data[2] * rhs.m_data[1], + m_data[2] * rhs.m_data[0] - m_data[0] * rhs.m_data[2], + m_data[0] * rhs.m_data[1] - m_data[1] * rhs.m_data[0]); + } + template + inline T Vec3::operator*(const Vec3 & rhs) const + { + return (m_data[0] * rhs.m_data[0] + m_data[1] * rhs.m_data[1] + m_data[2] * rhs.m_data[2]); + } + template + inline Vec3 Vec3::operator+(const Vec3 & rhs) const + { + return Vec3(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1],m_data[2] + rhs.m_data[2]); + } + template + inline Vec3 Vec3::operator-(const Vec3 & rhs) const + { + return Vec3(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1],m_data[2] - rhs.m_data[2]) ; + } + template + inline Vec3 Vec3::operator-() const + { + return Vec3(-m_data[0],-m_data[1],-m_data[2]) ; + } + + template + inline Vec3 Vec3::operator*(T rhs) const + { + return Vec3(rhs * this->m_data[0], rhs * this->m_data[1], rhs * this->m_data[2]); + } + template + inline Vec3 Vec3::operator/ (T rhs) const + { + return Vec3(m_data[0] / rhs, m_data[1] / rhs, m_data[2] / rhs); + } + template + inline Vec3::Vec3(T a) + { + m_data[0] = m_data[1] = m_data[2] = a; + } + template + inline Vec3::Vec3(T x, T y, T z) + { + m_data[0] = x; + m_data[1] = y; + m_data[2] = z; + } + template + inline Vec3::Vec3(const Vec3 & rhs) + { + m_data[0] = rhs.m_data[0]; + m_data[1] = rhs.m_data[1]; + m_data[2] = rhs.m_data[2]; + } + template + inline Vec3::~Vec3(void){}; + + template + inline Vec3::Vec3() {} + + template + inline const bool Colinear(const Vec3 & a, const Vec3 & b, const Vec3 & c) + { + return ((c.Z() - a.Z()) * (b.Y() - a.Y()) - (b.Z() - a.Z()) * (c.Y() - a.Y()) == 0.0 /*EPS*/) && + ((b.Z() - a.Z()) * (c.X() - a.X()) - (b.X() - a.X()) * (c.Z() - a.Z()) == 0.0 /*EPS*/) && + ((b.X() - a.X()) * (c.Y() - a.Y()) - (b.Y() - a.Y()) * (c.X() - a.X()) == 0.0 /*EPS*/); + } + + template + inline const T ComputeVolume4(const Vec3 & a, const Vec3 & b, const Vec3 & c, const Vec3 & d) + { + return (a-d) * ((b-d) ^ (c-d)); + } + + template + inline bool Vec3::operator<(const Vec3 & rhs) const + { + if (X() == rhs[0]) + { + if (Y() == rhs[1]) + { + return (Z() + inline bool Vec3::operator>(const Vec3 & rhs) const + { + if (X() == rhs[0]) + { + if (Y() == rhs[1]) + { + return (Z()>rhs[2]); + } + return (Y()>rhs[1]); + } + return (X()>rhs[0]); + } + template + inline Vec2 operator*(T lhs, const Vec2 & rhs) + { + return Vec2(lhs * rhs.X(), lhs * rhs.Y()); + } + template + inline T & Vec2::X() + { + return m_data[0]; + } + template + inline T & Vec2::Y() + { + return m_data[1]; + } + template + inline const T & Vec2::X() const + { + return m_data[0]; + } + template + inline const T & Vec2::Y() const + { + return m_data[1]; + } + template + inline void Vec2::Normalize() + { + T n = sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]); + if (n != 0.0) (*this) /= n; + } + template + inline T Vec2::GetNorm() const + { + return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]); + } + template + inline void Vec2::operator= (const Vec2 & rhs) + { + this->m_data[0] = rhs.m_data[0]; + this->m_data[1] = rhs.m_data[1]; + } + template + inline void Vec2::operator+=(const Vec2 & rhs) + { + this->m_data[0] += rhs.m_data[0]; + this->m_data[1] += rhs.m_data[1]; + } + template + inline void Vec2::operator-=(const Vec2 & rhs) + { + this->m_data[0] -= rhs.m_data[0]; + this->m_data[1] -= rhs.m_data[1]; + } + template + inline void Vec2::operator-=(T a) + { + this->m_data[0] -= a; + this->m_data[1] -= a; + } + template + inline void Vec2::operator+=(T a) + { + this->m_data[0] += a; + this->m_data[1] += a; + } + template + inline void Vec2::operator/=(T a) + { + this->m_data[0] /= a; + this->m_data[1] /= a; + } + template + inline void Vec2::operator*=(T a) + { + this->m_data[0] *= a; + this->m_data[1] *= a; + } + template + inline T Vec2::operator^ (const Vec2 & rhs) const + { + return m_data[0] * rhs.m_data[1] - m_data[1] * rhs.m_data[0]; + } + template + inline T Vec2::operator*(const Vec2 & rhs) const + { + return (m_data[0] * rhs.m_data[0] + m_data[1] * rhs.m_data[1]); + } + template + inline Vec2 Vec2::operator+(const Vec2 & rhs) const + { + return Vec2(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1]); + } + template + inline Vec2 Vec2::operator-(const Vec2 & rhs) const + { + return Vec2(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1]); + } + template + inline Vec2 Vec2::operator-() const + { + return Vec2(-m_data[0],-m_data[1]) ; + } + + template + inline Vec2 Vec2::operator*(T rhs) const + { + return Vec2(rhs * this->m_data[0], rhs * this->m_data[1]); + } + template + inline Vec2 Vec2::operator/ (T rhs) const + { + return Vec2(m_data[0] / rhs, m_data[1] / rhs); + } + template + inline Vec2::Vec2(T a) + { + m_data[0] = m_data[1] = a; + } + template + inline Vec2::Vec2(T x, T y) + { + m_data[0] = x; + m_data[1] = y; + } + template + inline Vec2::Vec2(const Vec2 & rhs) + { + m_data[0] = rhs.m_data[0]; + m_data[1] = rhs.m_data[1]; + } + template + inline Vec2::~Vec2(void){}; + + template + inline Vec2::Vec2() {} + + /* + InsideTriangle decides if a point P is Inside of the triangle + defined by A, B, C. + */ + template + inline const bool InsideTriangle(const Vec2 & a, const Vec2 & b, const Vec2 & c, const Vec2 & p) + { + T ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; + T cCROSSap, bCROSScp, aCROSSbp; + ax = c.X() - b.X(); ay = c.Y() - b.Y(); + bx = a.X() - c.X(); by = a.Y() - c.Y(); + cx = b.X() - a.X(); cy = b.Y() - a.Y(); + apx= p.X() - a.X(); apy= p.Y() - a.Y(); + bpx= p.X() - b.X(); bpy= p.Y() - b.Y(); + cpx= p.X() - c.X(); cpy= p.Y() - c.Y(); + aCROSSbp = ax*bpy - ay*bpx; + cCROSSap = cx*apy - cy*apx; + bCROSScp = bx*cpy - by*cpx; + return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)); + } +} +#endif //VHACD_VECTOR_INL \ No newline at end of file diff --git a/Extras/VHACD/inc/vhacdVolume.h b/Extras/VHACD/inc/vhacdVolume.h new file mode 100644 index 000000000..31377aa6c --- /dev/null +++ b/Extras/VHACD/inc/vhacdVolume.h @@ -0,0 +1,419 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_VOLUME_H +#define VHACD_VOLUME_H +#include "vhacdMesh.h" +#include "vhacdVector.h" +#include + +namespace VHACD { + +enum VOXEL_VALUE { + PRIMITIVE_UNDEFINED = 0, + PRIMITIVE_OUTSIDE_SURFACE = 1, + PRIMITIVE_INSIDE_SURFACE = 2, + PRIMITIVE_ON_SURFACE = 3 +}; + +struct Voxel { +public: + short m_coord[3]; + short m_data; +}; + +class PrimitiveSet { +public: + virtual ~PrimitiveSet(){}; + virtual PrimitiveSet* Create() const = 0; + virtual const size_t GetNPrimitives() const = 0; + virtual const size_t GetNPrimitivesOnSurf() const = 0; + virtual const size_t GetNPrimitivesInsideSurf() const = 0; + virtual const double GetEigenValue(AXIS axis) const = 0; + virtual const double ComputeMaxVolumeError() const = 0; + virtual const double ComputeVolume() const = 0; + virtual void Clip(const Plane& plane, PrimitiveSet* const positivePart, + PrimitiveSet* const negativePart) const = 0; + virtual void Intersect(const Plane& plane, SArray >* const positivePts, + SArray >* const negativePts, const size_t sampling) const = 0; + virtual void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray >* const exteriorPts) const = 0; + virtual void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, + double& negativeVolume) const = 0; + virtual void SelectOnSurface(PrimitiveSet* const onSurfP) const = 0; + virtual void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const = 0; + virtual void ComputeBB() = 0; + virtual void ComputePrincipalAxes() = 0; + virtual void AlignToPrincipalAxes() = 0; + virtual void RevertAlignToPrincipalAxes() = 0; + virtual void Convert(Mesh& mesh, const VOXEL_VALUE value) const = 0; + const Mesh& GetConvexHull() const { return m_convexHull; }; + Mesh& GetConvexHull() { return m_convexHull; }; +private: + Mesh m_convexHull; +}; + +//! +class VoxelSet : public PrimitiveSet { + friend class Volume; + +public: + //! Destructor. + ~VoxelSet(void); + //! Constructor. + VoxelSet(); + + const size_t GetNPrimitives() const { return m_voxels.Size(); } + const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; } + const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } + const double ComputeVolume() const { return m_unitVolume * m_voxels.Size(); } + const double ComputeMaxVolumeError() const { return m_unitVolume * m_numVoxelsOnSurface; } + const Vec3& GetMinBBVoxels() const { return m_minBBVoxels; } + const Vec3& GetMaxBBVoxels() const { return m_maxBBVoxels; } + const Vec3& GetMinBB() const { return m_minBB; } + const double& GetScale() const { return m_scale; } + const double& GetUnitVolume() const { return m_unitVolume; } + Vec3 GetPoint(Vec3 voxel) const + { + return Vec3(voxel[0] * m_scale + m_minBB[0], + voxel[1] * m_scale + m_minBB[1], + voxel[2] * m_scale + m_minBB[2]); + } + Vec3 GetPoint(const Voxel& voxel) const + { + return Vec3(voxel.m_coord[0] * m_scale + m_minBB[0], + voxel.m_coord[1] * m_scale + m_minBB[1], + voxel.m_coord[2] * m_scale + m_minBB[2]); + } + Vec3 GetPoint(Vec3 voxel) const + { + return Vec3(voxel[0] * m_scale + m_minBB[0], + voxel[1] * m_scale + m_minBB[1], + voxel[2] * m_scale + m_minBB[2]); + } + void GetPoints(const Voxel& voxel, Vec3* const pts) const; + void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; + void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const; + void Intersect(const Plane& plane, SArray >* const positivePts, + SArray >* const negativePts, const size_t sampling) const; + void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray >* const exteriorPts) const; + void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const; + void SelectOnSurface(PrimitiveSet* const onSurfP) const; + void ComputeBB(); + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + void ComputePrincipalAxes(); + PrimitiveSet* Create() const + { + return new VoxelSet(); + } + void AlignToPrincipalAxes(){}; + void RevertAlignToPrincipalAxes(){}; + Voxel* const GetVoxels() { return m_voxels.Data(); } + const Voxel* const GetVoxels() const { return m_voxels.Data(); } + +private: + size_t m_numVoxelsOnSurface; + size_t m_numVoxelsInsideSurface; + Vec3 m_minBB; + double m_scale; + SArray m_voxels; + double m_unitVolume; + Vec3 m_minBBPts; + Vec3 m_maxBBPts; + Vec3 m_minBBVoxels; + Vec3 m_maxBBVoxels; + Vec3 m_barycenter; + double m_Q[3][3]; + double m_D[3][3]; + Vec3 m_barycenterPCA; +}; + +struct Tetrahedron { +public: + Vec3 m_pts[4]; + unsigned char m_data; +}; + +//! +class TetrahedronSet : public PrimitiveSet { + friend class Volume; + +public: + //! Destructor. + ~TetrahedronSet(void); + //! Constructor. + TetrahedronSet(); + + const size_t GetNPrimitives() const { return m_tetrahedra.Size(); } + const size_t GetNPrimitivesOnSurf() const { return m_numTetrahedraOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numTetrahedraInsideSurface; } + const Vec3& GetMinBB() const { return m_minBB; } + const Vec3& GetMaxBB() const { return m_maxBB; } + const Vec3& GetBarycenter() const { return m_barycenter; } + const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } + const double GetSacle() const { return m_scale; } + const double ComputeVolume() const; + const double ComputeMaxVolumeError() const; + void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; + void ComputePrincipalAxes(); + void AlignToPrincipalAxes(); + void RevertAlignToPrincipalAxes(); + void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const; + void Intersect(const Plane& plane, SArray >* const positivePts, + SArray >* const negativePts, const size_t sampling) const; + void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray >* const exteriorPts) const; + void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const; + void SelectOnSurface(PrimitiveSet* const onSurfP) const; + void ComputeBB(); + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + inline bool Add(Tetrahedron& tetrahedron); + PrimitiveSet* Create() const + { + return new TetrahedronSet(); + } + static const double EPS; + +private: + void AddClippedTetrahedra(const Vec3 (&pts)[10], const int nPts); + + size_t m_numTetrahedraOnSurface; + size_t m_numTetrahedraInsideSurface; + double m_scale; + Vec3 m_minBB; + Vec3 m_maxBB; + Vec3 m_barycenter; + SArray m_tetrahedra; + double m_Q[3][3]; + double m_D[3][3]; +}; + +//! +class Volume { +public: + //! Destructor. + ~Volume(void); + + //! Constructor. + Volume(); + + //! Voxelize + template + void Voxelize(const T* const points, const unsigned int stridePoints, const unsigned int nPoints, + const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles, + const size_t dim, const Vec3& barycenter, const double (&rot)[3][3]); + unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) + { + assert(i < m_dim[0] || i >= 0); + assert(j < m_dim[0] || j >= 0); + assert(k < m_dim[0] || k >= 0); + return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; + } + const unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) const + { + assert(i < m_dim[0] || i >= 0); + assert(j < m_dim[0] || j >= 0); + assert(k < m_dim[0] || k >= 0); + return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; + } + const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; } + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + void Convert(VoxelSet& vset) const; + void Convert(TetrahedronSet& tset) const; + void AlignToPrincipalAxes(double (&rot)[3][3]) const; + +private: + void FillOutsideSurface(const size_t i0, const size_t j0, const size_t k0, const size_t i1, + const size_t j1, const size_t k1); + void FillInsideSurface(); + template + void ComputeBB(const T* const points, const unsigned int stridePoints, const unsigned int nPoints, + const Vec3& barycenter, const double (&rot)[3][3]); + void Allocate(); + void Free(); + + Vec3 m_minBB; + Vec3 m_maxBB; + double m_scale; + size_t m_dim[3]; //>! dim + size_t m_numVoxelsOnSurface; + size_t m_numVoxelsInsideSurface; + size_t m_numVoxelsOutsideSurface; + unsigned char* m_data; +}; +int TriBoxOverlap(const Vec3& boxcenter, const Vec3& boxhalfsize, const Vec3& triver0, + const Vec3& triver1, const Vec3& triver2); +template +inline void ComputeAlignedPoint(const T* const points, const unsigned int idx, const Vec3& barycenter, + const double (&rot)[3][3], Vec3& pt){}; +template <> +inline void ComputeAlignedPoint(const float* const points, const unsigned int idx, const Vec3& barycenter, const double (&rot)[3][3], Vec3& pt) +{ + double x = points[idx + 0] - barycenter[0]; + double y = points[idx + 1] - barycenter[1]; + double z = points[idx + 2] - barycenter[2]; + pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; + pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; + pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; +} +template <> +inline void ComputeAlignedPoint(const double* const points, const unsigned int idx, const Vec3& barycenter, const double (&rot)[3][3], Vec3& pt) +{ + double x = points[idx + 0] - barycenter[0]; + double y = points[idx + 1] - barycenter[1]; + double z = points[idx + 2] - barycenter[2]; + pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; + pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; + pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; +} +template +void Volume::ComputeBB(const T* const points, const unsigned int stridePoints, const unsigned int nPoints, + const Vec3& barycenter, const double (&rot)[3][3]) +{ + Vec3 pt; + ComputeAlignedPoint(points, 0, barycenter, rot, pt); + m_maxBB = pt; + m_minBB = pt; + for (unsigned int v = 1; v < nPoints; ++v) { + ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt); + for (int i = 0; i < 3; ++i) { + if (pt[i] < m_minBB[i]) + m_minBB[i] = pt[i]; + else if (pt[i] > m_maxBB[i]) + m_maxBB[i] = pt[i]; + } + } +} +template +void Volume::Voxelize(const T* const points, const unsigned int stridePoints, const unsigned int nPoints, + const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles, + const size_t dim, const Vec3& barycenter, const double (&rot)[3][3]) +{ + if (nPoints == 0) { + return; + } + ComputeBB(points, stridePoints, nPoints, barycenter, rot); + + double d[3] = { m_maxBB[0] - m_minBB[0], m_maxBB[1] - m_minBB[1], m_maxBB[2] - m_minBB[2] }; + double r; + if (d[0] > d[1] && d[0] > d[2]) { + r = d[0]; + m_dim[0] = dim; + m_dim[1] = 2 + static_cast(dim * d[1] / d[0]); + m_dim[2] = 2 + static_cast(dim * d[2] / d[0]); + } + else if (d[1] > d[0] && d[1] > d[2]) { + r = d[1]; + m_dim[1] = dim; + m_dim[0] = 2 + static_cast(dim * d[0] / d[1]); + m_dim[2] = 2 + static_cast(dim * d[2] / d[1]); + } + else { + r = d[2]; + m_dim[2] = dim; + m_dim[0] = 2 + static_cast(dim * d[0] / d[2]); + m_dim[1] = 2 + static_cast(dim * d[1] / d[2]); + } + + m_scale = r / (dim - 1); + double invScale = (dim - 1) / r; + + Allocate(); + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + m_numVoxelsOutsideSurface = 0; + + Vec3 p[3]; + size_t i, j, k; + size_t i0, j0, k0; + size_t i1, j1, k1; + Vec3 boxcenter; + Vec3 pt; + const Vec3 boxhalfsize(0.5, 0.5, 0.5); + for (size_t t = 0, ti = 0; t < nTriangles; ++t, ti += strideTriangles) { + Vec3 tri(triangles[ti + 0], + triangles[ti + 1], + triangles[ti + 2]); + for (int c = 0; c < 3; ++c) { + ComputeAlignedPoint(points, tri[c] * stridePoints, barycenter, rot, pt); + p[c][0] = (pt[0] - m_minBB[0]) * invScale; + p[c][1] = (pt[1] - m_minBB[1]) * invScale; + p[c][2] = (pt[2] - m_minBB[2]) * invScale; + i = static_cast(p[c][0] + 0.5); + j = static_cast(p[c][1] + 0.5); + k = static_cast(p[c][2] + 0.5); + assert(i < m_dim[0] && i >= 0 && j < m_dim[1] && j >= 0 && k < m_dim[2] && k >= 0); + + if (c == 0) { + i0 = i1 = i; + j0 = j1 = j; + k0 = k1 = k; + } + else { + if (i < i0) + i0 = i; + if (j < j0) + j0 = j; + if (k < k0) + k0 = k; + if (i > i1) + i1 = i; + if (j > j1) + j1 = j; + if (k > k1) + k1 = k; + } + } + if (i0 > 0) + --i0; + if (j0 > 0) + --j0; + if (k0 > 0) + --k0; + if (i1 < m_dim[0]) + ++i1; + if (j1 < m_dim[1]) + ++j1; + if (k1 < m_dim[2]) + ++k1; + for (size_t i = i0; i < i1; ++i) { + boxcenter[0] = (double)i; + for (size_t j = j0; j < j1; ++j) { + boxcenter[1] = (double)j; + for (size_t k = k0; k < k1; ++k) { + boxcenter[2] = (double)k; + int res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]); + unsigned char& value = GetVoxel(i, j, k); + if (res == 1 && value == PRIMITIVE_UNDEFINED) { + value = PRIMITIVE_ON_SURFACE; + ++m_numVoxelsOnSurface; + } + } + } + } + } + FillOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1); + FillOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]); + FillOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]); + FillOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]); + FillInsideSurface(); +} +} +#endif // VHACD_VOLUME_H diff --git a/Extras/VHACD/premake4.lua b/Extras/VHACD/premake4.lua new file mode 100644 index 000000000..f66f160e2 --- /dev/null +++ b/Extras/VHACD/premake4.lua @@ -0,0 +1,4 @@ + +include "src" +include "test/src" + diff --git a/Extras/VHACD/public/VHACD.h b/Extras/VHACD/public/VHACD.h new file mode 100644 index 000000000..20f6aacd9 --- /dev/null +++ b/Extras/VHACD/public/VHACD.h @@ -0,0 +1,121 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_H +#define VHACD_H + +#define VHACD_VERSION_MAJOR 2 +#define VHACD_VERSION_MINOR 2 + +namespace VHACD { +class IVHACD { +public: + class IUserCallback { + public: + virtual ~IUserCallback(){}; + virtual void Update(const double overallProgress, + const double stageProgress, + const double operationProgress, + const char* const stage, + const char* const operation) + = 0; + }; + + class IUserLogger { + public: + virtual ~IUserLogger(){}; + virtual void Log(const char* const msg) = 0; + }; + + class ConvexHull { + public: + double* m_points; + int* m_triangles; + unsigned int m_nPoints; + unsigned int m_nTriangles; + }; + + class Parameters { + public: + Parameters(void) { Init(); } + void Init(void) + { + m_resolution = 1000000; + m_depth = 20; + m_concavity = 0.001; + m_planeDownsampling = 4; + m_convexhullDownsampling = 4; + m_alpha = 0.05; + m_beta = 0.05; + m_gamma = 0.0005; + m_pca = 0; + m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based + m_maxNumVerticesPerCH = 64; + m_minVolumePerCH = 0.0001; + m_callback = 0; + m_logger = 0; + m_convexhullApproximation = true; + m_oclAcceleration = true; + } + double m_concavity; + double m_alpha; + double m_beta; + double m_gamma; + double m_minVolumePerCH; + IUserCallback* m_callback; + IUserLogger* m_logger; + unsigned int m_resolution; + unsigned int m_maxNumVerticesPerCH; + int m_depth; + int m_planeDownsampling; + int m_convexhullDownsampling; + int m_pca; + int m_mode; + int m_convexhullApproximation; + int m_oclAcceleration; + }; + + virtual void Cancel() = 0; + virtual bool Compute(const float* const points, + const unsigned int stridePoints, + const unsigned int countPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int countTriangles, + const Parameters& params) + = 0; + virtual bool Compute(const double* const points, + const unsigned int stridePoints, + const unsigned int countPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int countTriangles, + const Parameters& params) + = 0; + virtual unsigned int GetNConvexHulls() const = 0; + virtual void GetConvexHull(const unsigned int index, ConvexHull& ch) const = 0; + virtual void Clean(void) = 0; // release internally allocated memory + virtual void Release(void) = 0; // release IVHACD + virtual bool OCLInit(void* const oclDevice, + IUserLogger* const logger = 0) + = 0; + virtual bool OCLRelease(IUserLogger* const logger = 0) = 0; + +protected: + virtual ~IVHACD(void) {} +}; +IVHACD* CreateVHACD(void); +} +#endif // VHACD_H \ No newline at end of file diff --git a/Extras/VHACD/src/VHACD.cpp b/Extras/VHACD/src/VHACD.cpp new file mode 100644 index 000000000..8f1d30903 --- /dev/null +++ b/Extras/VHACD/src/VHACD.cpp @@ -0,0 +1,1433 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include +#if _OPENMP +#include +#endif // _OPENMP + +#include "../public/VHACD.h" +#include "btConvexHullComputer.h" +#include "vhacdICHull.h" +#include "vhacdMesh.h" +#include "vhacdSArray.h" +#include "vhacdTimer.h" +#include "vhacdVHACD.h" +#include "vhacdVector.h" +#include "vhacdVolume.h" + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define ABS(a) (((a) < 0) ? -(a) : (a)) +#define ZSGN(a) (((a) < 0) ? -1 : (a) > 0 ? 1 : 0) +#define MAX_DOUBLE (1.79769e+308) + +#ifdef USE_SSE +#include + +const int SIMD_WIDTH = 4; +inline int FindMinimumElement(const float* const d, float* const m, const int n) +{ + // Min within vectors + __m128 min_i = _mm_set1_ps(-1.0f); + __m128 min_v = _mm_set1_ps(std::numeric_limits::max()); + for (int i = 0; i <= n - SIMD_WIDTH; i += SIMD_WIDTH) { + const __m128 data = _mm_load_ps(&d[i]); + const __m128 pred = _mm_cmplt_ps(data, min_v); + + min_i = _mm_blendv_ps(min_i, _mm_set1_ps(i), pred); + min_v = _mm_min_ps(data, min_v); + } + + /* Min within vector */ + const __m128 min1 = _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 min2 = _mm_min_ps(min_v, min1); + const __m128 min3 = _mm_shuffle_ps(min2, min2, _MM_SHUFFLE(0, 1, 0, 1)); + const __m128 min4 = _mm_min_ps(min2, min3); + float min_d = _mm_cvtss_f32(min4); + + // Min index + const int min_idx = __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4))); + int ret = min_i[min_idx] + min_idx; + + // Trailing elements + for (int i = (n & ~(SIMD_WIDTH - 1)); i < n; ++i) { + if (d[i] < min_d) { + min_d = d[i]; + ret = i; + } + } + + *m = min_d; + return ret; +} + +inline int FindMinimumElement(const float* const d, float* const m, const int begin, const int end) +{ + // Leading elements + int min_i = -1; + float min_d = std::numeric_limits::max(); + const int aligned = (begin & ~(SIMD_WIDTH - 1)) + ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0); + for (int i = begin; i < std::min(end, aligned); ++i) { + if (d[i] < min_d) { + min_d = d[i]; + min_i = i; + } + } + + // Middle and trailing elements + float r_m = std::numeric_limits::max(); + const int n = end - aligned; + const int r_i = (n > 0) ? FindMinimumElement(&d[aligned], &r_m, n) : 0; + + // Pick the lowest + if (r_m < min_d) { + *m = r_m; + return r_i + aligned; + } + else { + *m = min_d; + return min_i; + } +} +#else +inline int FindMinimumElement(const float* const d, float* const m, const int begin, const int end) +{ + int idx = -1; + float min = (std::numeric_limits::max)(); + for (int i = begin; i < end; ++i) { + if (d[i] < min) { + idx = i; + min = d[i]; + } + } + + *m = min; + return idx; +} +#endif + +//#define OCL_SOURCE_FROM_FILE +#ifndef OCL_SOURCE_FROM_FILE +const char* oclProgramSource = "\ +__kernel void ComputePartialVolumes(__global short4 * voxels, \ + const int numVoxels, \ + const float4 plane, \ + const float4 minBB, \ + const float4 scale, \ + __local uint4 * localPartialVolumes, \ + __global uint4 * partialVolumes) \ +{ \ + int localId = get_local_id(0); \ + int groupSize = get_local_size(0); \ + int i0 = get_global_id(0) << 2; \ + float4 voxel; \ + uint4 v; \ + voxel = convert_float4(voxels[i0]); \ + v.s0 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 < numVoxels);\ + voxel = convert_float4(voxels[i0 + 1]); \ + v.s1 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 1 < numVoxels);\ + voxel = convert_float4(voxels[i0 + 2]); \ + v.s2 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 2 < numVoxels);\ + voxel = convert_float4(voxels[i0 + 3]); \ + v.s3 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 3 < numVoxels);\ + localPartialVolumes[localId] = v; \ + barrier(CLK_LOCAL_MEM_FENCE); \ + for (int i = groupSize >> 1; i > 0; i >>= 1) \ + { \ + if (localId < i) \ + { \ + localPartialVolumes[localId] += localPartialVolumes[localId + i]; \ + } \ + barrier(CLK_LOCAL_MEM_FENCE); \ + } \ + if (localId == 0) \ + { \ + partialVolumes[get_group_id(0)] = localPartialVolumes[0]; \ + } \ +} \ +__kernel void ComputePartialSums(__global uint4 * data, \ + const int dataSize, \ + __local uint4 * partialSums) \ +{ \ + int globalId = get_global_id(0); \ + int localId = get_local_id(0); \ + int groupSize = get_local_size(0); \ + int i; \ + if (globalId < dataSize) \ + { \ + partialSums[localId] = data[globalId]; \ + } \ + else \ + { \ + partialSums[localId] = (0, 0, 0, 0); \ + } \ + barrier(CLK_LOCAL_MEM_FENCE); \ + for (i = groupSize >> 1; i > 0; i >>= 1) \ + { \ + if (localId < i) \ + { \ + partialSums[localId] += partialSums[localId + i]; \ + } \ + barrier(CLK_LOCAL_MEM_FENCE); \ + } \ + if (localId == 0) \ + { \ + data[get_group_id(0)] = partialSums[0]; \ + } \ +}"; +#endif //OCL_SOURCE_FROM_FILE + +namespace VHACD { +IVHACD* CreateVHACD(void) +{ + return new VHACD(); +} +bool VHACD::OCLInit(void* const oclDevice, IUserLogger* const logger) +{ +#ifdef CL_VERSION_1_1 + m_oclDevice = (cl_device_id*)oclDevice; + cl_int error; + m_oclContext = clCreateContext(NULL, 1, m_oclDevice, NULL, NULL, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create context\n"); + } + return false; + } + +#ifdef OCL_SOURCE_FROM_FILE + std::string cl_files = OPENCL_CL_FILES; +// read kernal from file +#ifdef _WIN32 + std::replace(cl_files.begin(), cl_files.end(), '/', '\\'); +#endif // _WIN32 + + FILE* program_handle = fopen(cl_files.c_str(), "rb"); + fseek(program_handle, 0, SEEK_END); + size_t program_size = ftell(program_handle); + rewind(program_handle); + char* program_buffer = new char[program_size + 1]; + program_buffer[program_size] = '\0'; + fread(program_buffer, sizeof(char), program_size, program_handle); + fclose(program_handle); + // create program + m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&program_buffer, &program_size, &error); + delete[] program_buffer; +#else + size_t program_size = strlen(oclProgramSource); + m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&oclProgramSource, &program_size, &error); +#endif + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create program\n"); + } + return false; + } + + /* Build program */ + error = clBuildProgram(m_oclProgram, 1, m_oclDevice, "-cl-denorms-are-zero", NULL, NULL); + if (error != CL_SUCCESS) { + size_t log_size; + /* Find Size of log and print to std output */ + clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size); + char* program_log = new char[log_size + 2]; + program_log[log_size] = '\n'; + program_log[log_size + 1] = '\0'; + clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, log_size + 1, program_log, NULL); + if (logger) { + logger->Log("Couldn't build program\n"); + logger->Log(program_log); + } + delete[] program_log; + return false; + } + + delete[] m_oclQueue; + delete[] m_oclKernelComputePartialVolumes; + delete[] m_oclKernelComputeSum; + m_oclQueue = new cl_command_queue[m_ompNumProcessors]; + m_oclKernelComputePartialVolumes = new cl_kernel[m_ompNumProcessors]; + m_oclKernelComputeSum = new cl_kernel[m_ompNumProcessors]; + + const char nameKernelComputePartialVolumes[] = "ComputePartialVolumes"; + const char nameKernelComputeSum[] = "ComputePartialSums"; + for (int k = 0; k < m_ompNumProcessors; ++k) { + m_oclKernelComputePartialVolumes[k] = clCreateKernel(m_oclProgram, nameKernelComputePartialVolumes, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create kernel\n"); + } + return false; + } + m_oclKernelComputeSum[k] = clCreateKernel(m_oclProgram, nameKernelComputeSum, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create kernel\n"); + } + return false; + } + } + + error = clGetKernelWorkGroupInfo(m_oclKernelComputePartialVolumes[0], + *m_oclDevice, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), + &m_oclWorkGroupSize, + NULL); + size_t workGroupSize = 0; + error = clGetKernelWorkGroupInfo(m_oclKernelComputeSum[0], + *m_oclDevice, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), + &workGroupSize, + NULL); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't query work group info\n"); + } + return false; + } + + if (workGroupSize < m_oclWorkGroupSize) { + m_oclWorkGroupSize = workGroupSize; + } + + for (int k = 0; k < m_ompNumProcessors; ++k) { + m_oclQueue[k] = clCreateCommandQueue(m_oclContext, *m_oclDevice, 0 /*CL_QUEUE_PROFILING_ENABLE*/, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create queue\n"); + } + return false; + } + } + return true; +#else //CL_VERSION_1_1 + return false; +#endif //CL_VERSION_1_1 +} +bool VHACD::OCLRelease(IUserLogger* const logger) +{ +#ifdef CL_VERSION_1_1 + cl_int error; + if (m_oclKernelComputePartialVolumes) { + for (int k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseKernel(m_oclKernelComputePartialVolumes[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release kernal\n"); + } + return false; + } + } + delete[] m_oclKernelComputePartialVolumes; + } + if (m_oclKernelComputeSum) { + for (int k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseKernel(m_oclKernelComputeSum[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release kernal\n"); + } + return false; + } + } + delete[] m_oclKernelComputeSum; + } + if (m_oclQueue) { + for (int k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseCommandQueue(m_oclQueue[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release queue\n"); + } + return false; + } + } + delete[] m_oclQueue; + } + error = clReleaseProgram(m_oclProgram); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release program\n"); + } + return false; + } + error = clReleaseContext(m_oclContext); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release context\n"); + } + return false; + } + + return true; +#else //CL_VERSION_1_1 + return false; +#endif //CL_VERSION_1_1 +} +void VHACD::ComputePrimitiveSet(const Parameters& params) +{ + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Compute primitive set"; + m_operation = "Convert volume to pset"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + if (params.m_mode == 0) { + VoxelSet* vset = new VoxelSet; + m_volume->Convert(*vset); + m_pset = vset; + } + else { + TetrahedronSet* tset = new TetrahedronSet; + m_volume->Convert(*tset); + m_pset = tset; + } + + delete m_volume; + m_volume = 0; + + if (params.m_logger) { + msg.str(""); + msg << "\t # primitives " << m_pset->GetNPrimitives() << std::endl; + msg << "\t # inside surface " << m_pset->GetNPrimitivesInsideSurf() << std::endl; + msg << "\t # on surface " << m_pset->GetNPrimitivesOnSurf() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + m_overallProgress = 15.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} +bool VHACD::Compute(const double* const points, const unsigned int stridePoints, const unsigned int nPoints, + const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles, const Parameters& params) +{ + return ComputeACD(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params); +} +bool VHACD::Compute(const float* const points, const unsigned int stridePoints, const unsigned int nPoints, + const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles, const Parameters& params) +{ + return ComputeACD(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params); +} +double ComputePreferredCuttingDirection(const PrimitiveSet* const tset, Vec3& dir) +{ + double ex = tset->GetEigenValue(AXIS_X); + double ey = tset->GetEigenValue(AXIS_Y); + double ez = tset->GetEigenValue(AXIS_Z); + double vx = (ey - ez) * (ey - ez); + double vy = (ex - ez) * (ex - ez); + double vz = (ex - ey) * (ex - ey); + if (vx < vy && vx < vz) { + double e = ey * ey + ez * ez; + dir[0] = 1.0; + dir[1] = 0.0; + dir[2] = 0.0; + return (e == 0.0) ? 0.0 : 1.0 - vx / e; + } + else if (vy < vx && vy < vz) { + double e = ex * ex + ez * ez; + dir[0] = 0.0; + dir[1] = 1.0; + dir[2] = 0.0; + return (e == 0.0) ? 0.0 : 1.0 - vy / e; + } + else { + double e = ex * ex + ey * ey; + dir[0] = 0.0; + dir[1] = 0.0; + dir[2] = 1.0; + return (e == 0.0) ? 0.0 : 1.0 - vz / e; + } +} +void ComputeAxesAlignedClippingPlanes(const VoxelSet& vset, const short downsampling, SArray& planes) +{ + const Vec3 minV = vset.GetMinBBVoxels(); + const Vec3 maxV = vset.GetMaxBBVoxels(); + Vec3 pt; + Plane plane; + const short i0 = minV[0]; + const short i1 = maxV[0]; + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; i += downsampling) { + pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); + plane.m_d = -pt[0]; + plane.m_index = i; + planes.PushBack(plane); + } + const short j0 = minV[1]; + const short j1 = maxV[1]; + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; j += downsampling) { + pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); + plane.m_d = -pt[1]; + plane.m_index = j; + planes.PushBack(plane); + } + const short k0 = minV[2]; + const short k1 = maxV[2]; + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; k += downsampling) { + pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); + plane.m_d = -pt[2]; + plane.m_index = k; + planes.PushBack(plane); + } +} +void ComputeAxesAlignedClippingPlanes(const TetrahedronSet& tset, const short downsampling, SArray& planes) +{ + const Vec3 minV = tset.GetMinBB(); + const Vec3 maxV = tset.GetMaxBB(); + const double scale = tset.GetSacle(); + const short i0 = 0; + const short j0 = 0; + const short k0 = 0; + const short i1 = static_cast((maxV[0] - minV[0]) / scale + 0.5); + const short j1 = static_cast((maxV[1] - minV[1]) / scale + 0.5); + const short k1 = static_cast((maxV[2] - minV[2]) / scale + 0.5); + + Plane plane; + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; i += downsampling) { + double x = minV[0] + scale * i; + plane.m_d = -x; + plane.m_index = i; + planes.PushBack(plane); + } + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; j += downsampling) { + double y = minV[1] + scale * j; + plane.m_d = -y; + plane.m_index = j; + planes.PushBack(plane); + } + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; k += downsampling) { + double z = minV[2] + scale * k; + plane.m_d = -z; + plane.m_index = k; + planes.PushBack(plane); + } +} +void RefineAxesAlignedClippingPlanes(const VoxelSet& vset, const Plane& bestPlane, const short downsampling, + SArray& planes) +{ + const Vec3 minV = vset.GetMinBBVoxels(); + const Vec3 maxV = vset.GetMaxBBVoxels(); + Vec3 pt; + Plane plane; + + if (bestPlane.m_axis == AXIS_X) { + const short i0 = MAX(minV[0], bestPlane.m_index - downsampling); + const short i1 = MIN(maxV[0], bestPlane.m_index + downsampling); + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; ++i) { + pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); + plane.m_d = -pt[0]; + plane.m_index = i; + planes.PushBack(plane); + } + } + else if (bestPlane.m_axis == AXIS_Y) { + const short j0 = MAX(minV[1], bestPlane.m_index - downsampling); + const short j1 = MIN(maxV[1], bestPlane.m_index + downsampling); + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; ++j) { + pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); + plane.m_d = -pt[1]; + plane.m_index = j; + planes.PushBack(plane); + } + } + else { + const short k0 = MAX(minV[2], bestPlane.m_index - downsampling); + const short k1 = MIN(maxV[2], bestPlane.m_index + downsampling); + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; ++k) { + pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); + plane.m_d = -pt[2]; + plane.m_index = k; + planes.PushBack(plane); + } + } +} +void RefineAxesAlignedClippingPlanes(const TetrahedronSet& tset, const Plane& bestPlane, const short downsampling, + SArray& planes) +{ + const Vec3 minV = tset.GetMinBB(); + const Vec3 maxV = tset.GetMaxBB(); + const double scale = tset.GetSacle(); + Plane plane; + + if (bestPlane.m_axis == AXIS_X) { + const short i0 = MAX(0, bestPlane.m_index - downsampling); + const short i1 = static_cast(MIN((maxV[0] - minV[0]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; ++i) { + double x = minV[0] + scale * i; + plane.m_d = -x; + plane.m_index = i; + planes.PushBack(plane); + } + } + else if (bestPlane.m_axis == AXIS_Y) { + const short j0 = MAX(0, bestPlane.m_index - downsampling); + const short j1 = static_cast(MIN((maxV[1] - minV[1]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; ++j) { + double y = minV[1] + scale * j; + plane.m_d = -y; + plane.m_index = j; + planes.PushBack(plane); + } + } + else { + const short k0 = MAX(0, bestPlane.m_index - downsampling); + const short k1 = static_cast(MIN((maxV[2] - minV[2]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; ++k) { + double z = minV[2] + scale * k; + plane.m_d = -z; + plane.m_index = k; + planes.PushBack(plane); + } + } +} +inline double ComputeLocalConcavity(const double volume, const double volumeCH) +{ + return fabs(volumeCH - volume) / volumeCH; +} +inline double ComputeConcavity(const double volume, const double volumeCH, const double volume0) +{ + return fabs(volumeCH - volume) / volume0; +} + +//#define DEBUG_TEMP +void VHACD::ComputeBestClippingPlane(const PrimitiveSet* inputPSet, const double volume, const SArray& planes, + const Vec3& preferredCuttingDirection, const double w, const double alpha, const double beta, + const int convexhullDownsampling, const double progress0, const double progress1, Plane& bestPlane, + double& minConcavity, const Parameters& params) +{ + if (GetCancel()) { + return; + } + char msg[256]; + size_t nPrimitives = inputPSet->GetNPrimitives(); + bool oclAcceleration = (nPrimitives > OCL_MIN_NUM_PRIMITIVES && params.m_oclAcceleration && params.m_mode == 0) ? true : false; + int iBest = -1; + int nPlanes = static_cast(planes.Size()); + bool cancel = false; + int done = 0; + double minTotal = MAX_DOUBLE; + double minBalance = MAX_DOUBLE; + double minSymmetry = MAX_DOUBLE; + minConcavity = MAX_DOUBLE; + + SArray >* chPts = new SArray >[2 * m_ompNumProcessors]; + Mesh* chs = new Mesh[2 * m_ompNumProcessors]; + PrimitiveSet* onSurfacePSet = inputPSet->Create(); + inputPSet->SelectOnSurface(onSurfacePSet); + + PrimitiveSet** psets = 0; + if (!params.m_convexhullApproximation) { + psets = new PrimitiveSet*[2 * m_ompNumProcessors]; + for (int i = 0; i < 2 * m_ompNumProcessors; ++i) { + psets[i] = inputPSet->Create(); + } + } + +#ifdef CL_VERSION_1_1 + // allocate OpenCL data structures + cl_mem voxels; + cl_mem* partialVolumes = 0; + size_t globalSize = 0; + size_t nWorkGroups = 0; + double unitVolume = 0.0; + if (oclAcceleration) { + VoxelSet* vset = (VoxelSet*)inputPSet; + const Vec3 minBB = vset->GetMinBB(); + const float fMinBB[4] = { (float)minBB[0], (float)minBB[1], (float)minBB[2], 1.0f }; + const float fSclae[4] = { (float)vset->GetScale(), (float)vset->GetScale(), (float)vset->GetScale(), 0.0f }; + const int nVoxels = (int)nPrimitives; + unitVolume = vset->GetUnitVolume(); + nWorkGroups = (nPrimitives + 4 * m_oclWorkGroupSize - 1) / (4 * m_oclWorkGroupSize); + globalSize = nWorkGroups * m_oclWorkGroupSize; + cl_int error; + voxels = clCreateBuffer(m_oclContext, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(Voxel) * nPrimitives, + vset->GetVoxels(), + &error); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't create buffer\n"); + } + SetCancel(true); + } + + partialVolumes = new cl_mem[m_ompNumProcessors]; + for (int i = 0; i < m_ompNumProcessors; ++i) { + partialVolumes[i] = clCreateBuffer(m_oclContext, + CL_MEM_WRITE_ONLY, + sizeof(unsigned int) * 4 * nWorkGroups, + NULL, + &error); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't create buffer\n"); + } + SetCancel(true); + break; + } + error = clSetKernelArg(m_oclKernelComputePartialVolumes[i], 0, sizeof(cl_mem), &voxels); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 1, sizeof(unsigned int), &nVoxels); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 3, sizeof(float) * 4, fMinBB); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 4, sizeof(float) * 4, &fSclae); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 5, sizeof(unsigned int) * 4 * m_oclWorkGroupSize, NULL); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 6, sizeof(cl_mem), &(partialVolumes[i])); + error |= clSetKernelArg(m_oclKernelComputeSum[i], 0, sizeof(cl_mem), &(partialVolumes[i])); + error |= clSetKernelArg(m_oclKernelComputeSum[i], 2, sizeof(unsigned int) * 4 * m_oclWorkGroupSize, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); + } + SetCancel(true); + } + } + } +#else // CL_VERSION_1_1 + oclAcceleration = false; +#endif // CL_VERSION_1_1 + +#ifdef DEBUG_TEMP + Timer timerComputeCost; + timerComputeCost.Tic(); +#endif // DEBUG_TEMP + +#if USE_THREAD == 1 && _OPENMP +#pragma omp parallel for +#endif + for (int x = 0; x < nPlanes; ++x) { + int threadID = 0; +#if USE_THREAD == 1 && _OPENMP + threadID = omp_get_thread_num(); +#pragma omp flush(cancel) +#endif + if (!cancel) { + //Update progress + if (GetCancel()) { + cancel = true; +#if USE_THREAD == 1 && _OPENMP +#pragma omp flush(cancel) +#endif + } + Plane plane = planes[x]; + + if (oclAcceleration) { +#ifdef CL_VERSION_1_1 + const float fPlane[4] = { (float)plane.m_a, (float)plane.m_b, (float)plane.m_c, (float)plane.m_d }; + cl_int error = clSetKernelArg(m_oclKernelComputePartialVolumes[threadID], 2, sizeof(float) * 4, fPlane); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); + } + SetCancel(true); + } + + error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputePartialVolumes[threadID], + 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't run kernel \n"); + } + SetCancel(true); + } + int nValues = (int)nWorkGroups; + while (nValues > 1) { + error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, sizeof(int), &nValues); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); + } + SetCancel(true); + } + size_t nWorkGroups = (nValues + m_oclWorkGroupSize - 1) / m_oclWorkGroupSize; + size_t globalSize = nWorkGroups * m_oclWorkGroupSize; + error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputeSum[threadID], + 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't run kernel \n"); + } + SetCancel(true); + } + nValues = (int)nWorkGroups; + } +#endif // CL_VERSION_1_1 + } + + Mesh& leftCH = chs[threadID]; + Mesh& rightCH = chs[threadID + m_ompNumProcessors]; + rightCH.ResizePoints(0); + leftCH.ResizePoints(0); + rightCH.ResizeTriangles(0); + leftCH.ResizeTriangles(0); + +// compute convex-hulls +#ifdef TEST_APPROX_CH + double volumeLeftCH1; + double volumeRightCH1; +#endif //TEST_APPROX_CH + if (params.m_convexhullApproximation) { + SArray >& leftCHPts = chPts[threadID]; + SArray >& rightCHPts = chPts[threadID + m_ompNumProcessors]; + rightCHPts.Resize(0); + leftCHPts.Resize(0); + onSurfacePSet->Intersect(plane, &rightCHPts, &leftCHPts, convexhullDownsampling * 32); + inputPSet->GetConvexHull().Clip(plane, rightCHPts, leftCHPts); + rightCH.ComputeConvexHull((double*)rightCHPts.Data(), rightCHPts.Size()); + leftCH.ComputeConvexHull((double*)leftCHPts.Data(), leftCHPts.Size()); +#ifdef TEST_APPROX_CH + Mesh leftCH1; + Mesh rightCH1; + VoxelSet right; + VoxelSet left; + onSurfacePSet->Clip(plane, &right, &left); + right.ComputeConvexHull(rightCH1, convexhullDownsampling); + left.ComputeConvexHull(leftCH1, convexhullDownsampling); + + volumeLeftCH1 = leftCH1.ComputeVolume(); + volumeRightCH1 = rightCH1.ComputeVolume(); +#endif //TEST_APPROX_CH + } + else { + PrimitiveSet* const right = psets[threadID]; + PrimitiveSet* const left = psets[threadID + m_ompNumProcessors]; + onSurfacePSet->Clip(plane, right, left); + right->ComputeConvexHull(rightCH, convexhullDownsampling); + left->ComputeConvexHull(leftCH, convexhullDownsampling); + } + double volumeLeftCH = leftCH.ComputeVolume(); + double volumeRightCH = rightCH.ComputeVolume(); + + // compute clipped volumes + double volumeLeft = 0.0; + double volumeRight = 0.0; + if (oclAcceleration) { +#ifdef CL_VERSION_1_1 + unsigned int volumes[4]; + cl_int error = clEnqueueReadBuffer(m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE, + 0, sizeof(unsigned int) * 4, volumes, 0, NULL, NULL); + size_t nPrimitivesRight = volumes[0] + volumes[1] + volumes[2] + volumes[3]; + size_t nPrimitivesLeft = nPrimitives - nPrimitivesRight; + volumeRight = nPrimitivesRight * unitVolume; + volumeLeft = nPrimitivesLeft * unitVolume; + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't read buffer \n"); + } + SetCancel(true); + } +#endif // CL_VERSION_1_1 + } + else { + inputPSet->ComputeClippedVolumes(plane, volumeRight, volumeLeft); + } + double concavityLeft = ComputeConcavity(volumeLeft, volumeLeftCH, m_volumeCH0); + double concavityRight = ComputeConcavity(volumeRight, volumeRightCH, m_volumeCH0); + double concavity = (concavityLeft + concavityRight); + + // compute cost + double balance = alpha * fabs(volumeLeft - volumeRight) / m_volumeCH0; + double d = w * (preferredCuttingDirection[0] * plane.m_a + preferredCuttingDirection[1] * plane.m_b + preferredCuttingDirection[2] * plane.m_c); + double symmetry = beta * d; + double total = concavity + balance + symmetry; + +#if USE_THREAD == 1 && _OPENMP +#pragma omp critical +#endif + { + if (total < minTotal || (total == minTotal && x < iBest)) { + minConcavity = concavity; + minBalance = balance; + minSymmetry = symmetry; + bestPlane = plane; + minTotal = total; + iBest = x; + } + ++done; + if (!(done & 127)) // reduce update frequency + { + double progress = done * (progress1 - progress0) / nPlanes + progress0; + Update(m_stageProgress, progress, params); + } + } + } + } + +#ifdef DEBUG_TEMP + timerComputeCost.Toc(); + printf_s("Cost[%i] = %f\n", nPlanes, timerComputeCost.GetElapsedTime()); +#endif // DEBUG_TEMP + +#ifdef CL_VERSION_1_1 + if (oclAcceleration) { + clReleaseMemObject(voxels); + for (int i = 0; i < m_ompNumProcessors; ++i) { + clReleaseMemObject(partialVolumes[i]); + } + delete[] partialVolumes; + } +#endif // CL_VERSION_1_1 + + if (psets) { + for (int i = 0; i < 2 * m_ompNumProcessors; ++i) { + delete psets[i]; + } + delete[] psets; + } + delete onSurfacePSet; + delete[] chPts; + delete[] chs; + if (params.m_logger) { + sprintf(msg, "\n\t\t\t Best %04i T=%2.6f C=%2.6f B=%2.6f S=%2.6f (%1.1f, %1.1f, %1.1f, %3.3f)\n\n", iBest, minTotal, minConcavity, minBalance, minSymmetry, bestPlane.m_a, bestPlane.m_b, bestPlane.m_c, bestPlane.m_d); + params.m_logger->Log(msg); + } +} +void VHACD::ComputeACD(const Parameters& params) +{ + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Approximate Convex Decomposition"; + m_stageProgress = 0.0; + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + SArray parts; + SArray inputParts; + SArray temp; + inputParts.PushBack(m_pset); + m_pset = 0; + SArray planes; + SArray planesRef; + int sub = 0; + bool firstIteration = true; + m_volumeCH0 = 1.0; + while (sub++ < params.m_depth && inputParts.Size() > 0 && !m_cancel) { + msg.str(""); + msg << "Subdivision level " << sub; + m_operation = msg.str(); + + if (params.m_logger) { + msg.str(""); + msg << "\t Subdivision level " << sub << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + double maxConcavity = 0.0; + const size_t nInputParts = inputParts.Size(); + Update(m_stageProgress, 0.0, params); + for (size_t p = 0; p < nInputParts && !m_cancel; ++p) { + const double progress0 = p * 100.0 / nInputParts; + const double progress1 = (p + 0.75) * 100.0 / nInputParts; + const double progress2 = (p + 1.00) * 100.0 / nInputParts; + + Update(m_stageProgress, progress0, params); + + PrimitiveSet* pset = inputParts[p]; + inputParts[p] = 0; + double volume = pset->ComputeVolume(); + pset->ComputeBB(); + pset->ComputePrincipalAxes(); + if (params.m_pca) { + pset->AlignToPrincipalAxes(); + } + + pset->ComputeConvexHull(pset->GetConvexHull()); + double volumeCH = fabs(pset->GetConvexHull().ComputeVolume()); + if (firstIteration) { + m_volumeCH0 = volumeCH; + } + + double concavity = ComputeConcavity(volume, volumeCH, m_volumeCH0); + double error = 1.01 * pset->ComputeMaxVolumeError() / m_volumeCH0; + + if (firstIteration) { + firstIteration = false; + } + + if (params.m_logger) { + msg.str(""); + msg << "\t -> Part[" << p + << "] C = " << concavity + << ", E = " << error + << ", VS = " << pset->GetNPrimitivesOnSurf() + << ", VI = " << pset->GetNPrimitivesInsideSurf() + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + if (concavity > params.m_concavity && concavity > error) { + Vec3 preferredCuttingDirection; + double w = ComputePreferredCuttingDirection(pset, preferredCuttingDirection); + planes.Resize(0); + if (params.m_mode == 0) { + VoxelSet* vset = (VoxelSet*)pset; + ComputeAxesAlignedClippingPlanes(*vset, params.m_planeDownsampling, planes); + } + else { + TetrahedronSet* tset = (TetrahedronSet*)pset; + ComputeAxesAlignedClippingPlanes(*tset, params.m_planeDownsampling, planes); + } + + if (params.m_logger) { + msg.str(""); + msg << "\t\t [Regular sampling] Number of clipping planes " << planes.Size() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Plane bestPlane; + double minConcavity = MAX_DOUBLE; + ComputeBestClippingPlane(pset, + volume, + planes, + preferredCuttingDirection, + w, + concavity * params.m_alpha, + concavity * params.m_beta, + params.m_convexhullDownsampling, + progress0, + progress1, + bestPlane, + minConcavity, + params); + if (!m_cancel && (params.m_planeDownsampling > 1 || params.m_convexhullDownsampling > 1)) { + planesRef.Resize(0); + + if (params.m_mode == 0) { + VoxelSet* vset = (VoxelSet*)pset; + RefineAxesAlignedClippingPlanes(*vset, bestPlane, params.m_planeDownsampling, planesRef); + } + else { + TetrahedronSet* tset = (TetrahedronSet*)pset; + RefineAxesAlignedClippingPlanes(*tset, bestPlane, params.m_planeDownsampling, planesRef); + } + + if (params.m_logger) { + msg.str(""); + msg << "\t\t [Refining] Number of clipping planes " << planesRef.Size() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + ComputeBestClippingPlane(pset, + volume, + planesRef, + preferredCuttingDirection, + w, + concavity * params.m_alpha, + concavity * params.m_beta, + 1, // convexhullDownsampling = 1 + progress1, + progress2, + bestPlane, + minConcavity, + params); + } + if (GetCancel()) { + delete pset; // clean up + break; + } + else { + if (maxConcavity < minConcavity) { + maxConcavity = minConcavity; + } + PrimitiveSet* bestLeft = pset->Create(); + PrimitiveSet* bestRight = pset->Create(); + temp.PushBack(bestLeft); + temp.PushBack(bestRight); + pset->Clip(bestPlane, bestRight, bestLeft); + if (params.m_pca) { + bestRight->RevertAlignToPrincipalAxes(); + bestLeft->RevertAlignToPrincipalAxes(); + } + delete pset; + } + } + else { + if (params.m_pca) { + pset->RevertAlignToPrincipalAxes(); + } + parts.PushBack(pset); + } + } + + Update(95.0 * (1.0 - maxConcavity) / (1.0 - params.m_concavity), 100.0, params); + if (GetCancel()) { + const size_t nTempParts = temp.Size(); + for (size_t p = 0; p < nTempParts; ++p) { + delete temp[p]; + } + temp.Resize(0); + } + else { + inputParts = temp; + temp.Resize(0); + } + } + const size_t nInputParts = inputParts.Size(); + for (size_t p = 0; p < nInputParts; ++p) { + parts.PushBack(inputParts[p]); + } + + if (GetCancel()) { + const size_t nParts = parts.Size(); + for (size_t p = 0; p < nParts; ++p) { + delete parts[p]; + } + return; + } + + m_overallProgress = 90.0; + Update(m_stageProgress, 100.0, params); + + msg.str(""); + msg << "Generate convex-hulls"; + m_operation = msg.str(); + size_t nConvexHulls = parts.Size(); + if (params.m_logger) { + msg.str(""); + msg << "+ Generate " << nConvexHulls << " convex-hulls " << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(m_stageProgress, 0.0, params); + m_convexHulls.Resize(0); + for (size_t p = 0; p < nConvexHulls && !m_cancel; ++p) { + Update(m_stageProgress, p * 100.0 / nConvexHulls, params); + m_convexHulls.PushBack(new Mesh); + parts[p]->ComputeConvexHull(*m_convexHulls[p]); + size_t nv = m_convexHulls[p]->GetNPoints(); + double x, y, z; + for (size_t i = 0; i < nv; ++i) { + Vec3& pt = m_convexHulls[p]->GetPoint(i); + x = pt[0]; + y = pt[1]; + z = pt[2]; + pt[0] = m_rot[0][0] * x + m_rot[0][1] * y + m_rot[0][2] * z + m_barycenter[0]; + pt[1] = m_rot[1][0] * x + m_rot[1][1] * y + m_rot[1][2] * z + m_barycenter[1]; + pt[2] = m_rot[2][0] * x + m_rot[2][1] * y + m_rot[2][2] * z + m_barycenter[2]; + } + } + + const size_t nParts = parts.Size(); + for (size_t p = 0; p < nParts; ++p) { + delete parts[p]; + parts[p] = 0; + } + parts.Resize(0); + + if (GetCancel()) { + const size_t nConvexHulls = m_convexHulls.Size(); + for (size_t p = 0; p < nConvexHulls; ++p) { + delete m_convexHulls[p]; + } + m_convexHulls.Clear(); + return; + } + + m_overallProgress = 95.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} +void AddPoints(const Mesh* const mesh, SArray >& pts) +{ + const int n = (int)mesh->GetNPoints(); + for (int i = 0; i < n; ++i) { + pts.PushBack(mesh->GetPoint(i)); + } +} +void ComputeConvexHull(const Mesh* const ch1, const Mesh* const ch2, SArray >& pts, Mesh* const combinedCH) +{ + pts.Resize(0); + AddPoints(ch1, pts); + AddPoints(ch2, pts); + + btConvexHullComputer ch; + ch.compute((double*)pts.Data(), 3 * sizeof(double), (int)pts.Size(), -1.0, -1.0); + combinedCH->ResizePoints(0); + combinedCH->ResizeTriangles(0); + for (int v = 0; v < ch.vertices.size(); v++) { + combinedCH->AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int nt = ch.faces.size(); + for (int t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int a = sourceEdge->getSourceVertex(); + int b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int c = edge->getTargetVertex(); + while (c != a) { + combinedCH->AddTriangle(Vec3(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } +} +void VHACD::MergeConvexHulls(const Parameters& params) +{ + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Merge Convex Hulls"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + size_t nConvexHulls = m_convexHulls.Size(); + int iteration = 0; + if (nConvexHulls > 1 && !m_cancel) { + const double threshold = params.m_gamma; + SArray > pts; + Mesh combinedCH; + + // Populate the cost matrix + size_t idx = 0; + SArray costMatrix; + costMatrix.Resize(((nConvexHulls * nConvexHulls) - nConvexHulls) >> 1); + for (size_t p1 = 1; p1 < nConvexHulls; ++p1) { + const float volume1 = m_convexHulls[p1]->ComputeVolume(); + for (size_t p2 = 0; p2 < p1; ++p2) { + ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, &combinedCH); + costMatrix[idx++] = ComputeConcavity(volume1 + m_convexHulls[p2]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + } + } + + // Until we cant merge below the maximum cost + size_t costSize = m_convexHulls.Size(); + while (!m_cancel) { + msg.str(""); + msg << "Iteration " << iteration++; + m_operation = msg.str(); + + // Search for lowest cost + float bestCost = (std::numeric_limits::max)(); + const size_t addr = FindMinimumElement(costMatrix.Data(), &bestCost, 0, costMatrix.Size()); + + // Check if we should merge these hulls + if (bestCost >= threshold) { + break; + } + double nr = 1 + (8 * addr); + const size_t addrI = (static_cast(sqrt(nr)) - 1) >> 1; + const size_t p1 = addrI + 1; + const size_t p2 = addr - ((addrI * (addrI + 1)) >> 1); + assert(p1 >= 0); + assert(p2 >= 0); + assert(p1 < costSize); + assert(p2 < costSize); + + if (params.m_logger) { + msg.str(""); + msg << "\t\t Merging (" << p1 << ", " << p2 << ") " << bestCost << std::endl + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + // Make the lowest cost row and column into a new hull + Mesh* cch = new Mesh; + ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, cch); + delete m_convexHulls[p2]; + m_convexHulls[p2] = cch; + + delete m_convexHulls[p1]; + std::swap(m_convexHulls[p1], m_convexHulls[m_convexHulls.Size() - 1]); + m_convexHulls.PopBack(); + + costSize = costSize - 1; + + // Calculate costs versus the new hull + size_t rowIdx = ((p2 - 1) * p2) >> 1; + const float volume1 = m_convexHulls[p2]->ComputeVolume(); + for (size_t i = 0; (i < p2) && (!m_cancel); ++i) { + ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH); + costMatrix[rowIdx++] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + } + + rowIdx += p2; + for (size_t i = p2 + 1; (i < costSize) && (!m_cancel); ++i) { + ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH); + costMatrix[rowIdx] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + rowIdx += i; + assert(rowIdx >= 0); + } + + // Move the top column in to replace its space + const size_t erase_idx = ((costSize - 1) * costSize) >> 1; + if (p1 < costSize) { + rowIdx = (addrI * p1) >> 1; + size_t top_row = erase_idx; + for (size_t i = 0; i < p1; ++i) { + if (i != p2) { + costMatrix[rowIdx] = costMatrix[top_row]; + } + ++rowIdx; + ++top_row; + } + + ++top_row; + rowIdx += p1; + for (size_t i = p1 + 1; i < (costSize + 1); ++i) { + costMatrix[rowIdx] = costMatrix[top_row++]; + rowIdx += i; + assert(rowIdx >= 0); + } + } + costMatrix.Resize(erase_idx); + } + } + m_overallProgress = 99.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} +void SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume) +{ + if (nvertices <= 4) { + return; + } + ICHull icHull; + icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints()); + icHull.Process((unsigned int)nvertices, minVolume); + TMMesh& mesh = icHull.GetMesh(); + const size_t nT = mesh.GetNTriangles(); + const size_t nV = mesh.GetNVertices(); + ch->ResizePoints(nV); + ch->ResizeTriangles(nT); + mesh.GetIFS(ch->GetPointsBuffer(), ch->GetTrianglesBuffer()); +} +void VHACD::SimplifyConvexHulls(const Parameters& params) +{ + if (m_cancel || params.m_maxNumVerticesPerCH < 4) { + return; + } + m_timer.Tic(); + + m_stage = "Simplify convex-hulls"; + m_operation = "Simplify convex-hulls"; + + std::ostringstream msg; + const size_t nConvexHulls = m_convexHulls.Size(); + if (params.m_logger) { + msg << "+ Simplify " << nConvexHulls << " convex-hulls " << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + for (size_t i = 0; i < nConvexHulls && !m_cancel; ++i) { + if (params.m_logger) { + msg.str(""); + msg << "\t\t Simplify CH[" << std::setfill('0') << std::setw(5) << i << "] " << m_convexHulls[i]->GetNPoints() << " V, " << m_convexHulls[i]->GetNTriangles() << " T" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + SimplifyConvexHull(m_convexHulls[i], params.m_maxNumVerticesPerCH, m_volumeCH0 * params.m_minVolumePerCH); + } + + m_overallProgress = 100.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} +} diff --git a/Extras/VHACD/src/btAlignedAllocator.cpp b/Extras/VHACD/src/btAlignedAllocator.cpp new file mode 100644 index 000000000..e69442f67 --- /dev/null +++ b/Extras/VHACD/src/btAlignedAllocator.cpp @@ -0,0 +1,176 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "btAlignedAllocator.h" + +int gNumAlignedAllocs = 0; +int gNumAlignedFree = 0; +int gTotalBytesAlignedAllocs = 0; //detect memory leaks + +static void* btAllocDefault(size_t size) +{ + return malloc(size); +} + +static void btFreeDefault(void* ptr) +{ + free(ptr); +} + +static btAllocFunc* sAllocFunc = btAllocDefault; +static btFreeFunc* sFreeFunc = btFreeDefault; + +#if defined(BT_HAS_ALIGNED_ALLOCATOR) +#include +static void* btAlignedAllocDefault(size_t size, int alignment) +{ + return _aligned_malloc(size, (size_t)alignment); +} + +static void btAlignedFreeDefault(void* ptr) +{ + _aligned_free(ptr); +} +#elif defined(__CELLOS_LV2__) +#include + +static inline void* btAlignedAllocDefault(size_t size, int alignment) +{ + return memalign(alignment, size); +} + +static inline void btAlignedFreeDefault(void* ptr) +{ + free(ptr); +} +#else +static inline void* btAlignedAllocDefault(size_t size, int alignment) +{ + void* ret; + char* real; + unsigned long offset; + + real = (char*)sAllocFunc(size + sizeof(void*) + (alignment - 1)); + if (real) { + offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1); + ret = (void*)((real + sizeof(void*)) + offset); + *((void**)(ret)-1) = (void*)(real); + } + else { + ret = (void*)(real); + } + return (ret); +} + +static inline void btAlignedFreeDefault(void* ptr) +{ + void* real; + + if (ptr) { + real = *((void**)(ptr)-1); + sFreeFunc(real); + } +} +#endif + +static btAlignedAllocFunc* sAlignedAllocFunc = btAlignedAllocDefault; +static btAlignedFreeFunc* sAlignedFreeFunc = btAlignedFreeDefault; + +void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc) +{ + sAlignedAllocFunc = allocFunc ? allocFunc : btAlignedAllocDefault; + sAlignedFreeFunc = freeFunc ? freeFunc : btAlignedFreeDefault; +} + +void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc) +{ + sAllocFunc = allocFunc ? allocFunc : btAllocDefault; + sFreeFunc = freeFunc ? freeFunc : btFreeDefault; +} + +#ifdef BT_DEBUG_MEMORY_ALLOCATIONS +//this generic allocator provides the total allocated number of bytes +#include + +void* btAlignedAllocInternal(size_t size, int alignment, int line, char* filename) +{ + void* ret; + char* real; + unsigned long offset; + + gTotalBytesAlignedAllocs += size; + gNumAlignedAllocs++; + + real = (char*)sAllocFunc(size + 2 * sizeof(void*) + (alignment - 1)); + if (real) { + offset = (alignment - (unsigned long)(real + 2 * sizeof(void*))) & (alignment - 1); + ret = (void*)((real + 2 * sizeof(void*)) + offset); + *((void**)(ret)-1) = (void*)(real); + *((int*)(ret)-2) = size; + } + else { + ret = (void*)(real); //?? + } + + printf("allocation#%d at address %x, from %s,line %d, size %d\n", gNumAlignedAllocs, real, filename, line, size); + + int* ptr = (int*)ret; + *ptr = 12; + return (ret); +} + +void btAlignedFreeInternal(void* ptr, int line, char* filename) +{ + + void* real; + gNumAlignedFree++; + + if (ptr) { + real = *((void**)(ptr)-1); + int size = *((int*)(ptr)-2); + gTotalBytesAlignedAllocs -= size; + + printf("free #%d at address %x, from %s,line %d, size %d\n", gNumAlignedFree, real, filename, line, size); + + sFreeFunc(real); + } + else { + printf("NULL ptr\n"); + } +} + +#else //BT_DEBUG_MEMORY_ALLOCATIONS + +void* btAlignedAllocInternal(size_t size, int alignment) +{ + gNumAlignedAllocs++; + void* ptr; + ptr = sAlignedAllocFunc(size, alignment); + // printf("btAlignedAllocInternal %d, %x\n",size,ptr); + return ptr; +} + +void btAlignedFreeInternal(void* ptr) +{ + if (!ptr) { + return; + } + + gNumAlignedFree++; + // printf("btAlignedFreeInternal %x\n",ptr); + sAlignedFreeFunc(ptr); +} + +#endif //BT_DEBUG_MEMORY_ALLOCATIONS diff --git a/Extras/VHACD/src/btConvexHullComputer.cpp b/Extras/VHACD/src/btConvexHullComputer.cpp new file mode 100644 index 000000000..a84f7c02a --- /dev/null +++ b/Extras/VHACD/src/btConvexHullComputer.cpp @@ -0,0 +1,2475 @@ +/* +Copyright (c) 2011 Ole Kniemeyer, MAXON, www.maxon.net + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include + +#include "btAlignedObjectArray.h" +#include "btConvexHullComputer.h" +#include "btMinMax.h" +#include "btVector3.h" + +#ifdef __GNUC__ +#include +#elif defined(_MSC_VER) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +typedef int int32_t; +typedef long long int int64_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif + +//The definition of USE_X86_64_ASM is moved into the build system. You can enable it manually by commenting out the following lines +//#if (defined(__GNUC__) && defined(__x86_64__) && !defined(__ICL)) // || (defined(__ICL) && defined(_M_X64)) bug in Intel compiler, disable inline assembly +// #define USE_X86_64_ASM +//#endif + +//#define DEBUG_CONVEX_HULL +//#define SHOW_ITERATIONS + +#if defined(DEBUG_CONVEX_HULL) || defined(SHOW_ITERATIONS) +#include +#endif + +// Convex hull implementation based on Preparata and Hong +// Ole Kniemeyer, MAXON Computer GmbH +class btConvexHullInternal { +public: + class Point64 { + public: + int64_t x; + int64_t y; + int64_t z; + + Point64(int64_t x, int64_t y, int64_t z) + : x(x) + , y(y) + , z(z) + { + } + + bool isZero() + { + return (x == 0) && (y == 0) && (z == 0); + } + + int64_t dot(const Point64& b) const + { + return x * b.x + y * b.y + z * b.z; + } + }; + + class Point32 { + public: + int32_t x; + int32_t y; + int32_t z; + int index; + + Point32() + { + } + + Point32(int32_t x, int32_t y, int32_t z) + : x(x) + , y(y) + , z(z) + , index(-1) + { + } + + bool operator==(const Point32& b) const + { + return (x == b.x) && (y == b.y) && (z == b.z); + } + + bool operator!=(const Point32& b) const + { + return (x != b.x) || (y != b.y) || (z != b.z); + } + + bool isZero() + { + return (x == 0) && (y == 0) && (z == 0); + } + + Point64 cross(const Point32& b) const + { + return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); + } + + Point64 cross(const Point64& b) const + { + return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); + } + + int64_t dot(const Point32& b) const + { + return x * b.x + y * b.y + z * b.z; + } + + int64_t dot(const Point64& b) const + { + return x * b.x + y * b.y + z * b.z; + } + + Point32 operator+(const Point32& b) const + { + return Point32(x + b.x, y + b.y, z + b.z); + } + + Point32 operator-(const Point32& b) const + { + return Point32(x - b.x, y - b.y, z - b.z); + } + }; + + class Int128 { + public: + uint64_t low; + uint64_t high; + + Int128() + { + } + + Int128(uint64_t low, uint64_t high) + : low(low) + , high(high) + { + } + + Int128(uint64_t low) + : low(low) + , high(0) + { + } + + Int128(int64_t value) + : low(value) + , high((value >= 0) ? 0 : (uint64_t)-1LL) + { + } + + static Int128 mul(int64_t a, int64_t b); + + static Int128 mul(uint64_t a, uint64_t b); + + Int128 operator-() const + { + return Int128((uint64_t) - (int64_t)low, ~high + (low == 0)); + } + + Int128 operator+(const Int128& b) const + { +#ifdef USE_X86_64_ASM + Int128 result; + __asm__("addq %[bl], %[rl]\n\t" + "adcq %[bh], %[rh]\n\t" + : [rl] "=r"(result.low), [rh] "=r"(result.high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); + return result; +#else + uint64_t lo = low + b.low; + return Int128(lo, high + b.high + (lo < low)); +#endif + } + + Int128 operator-(const Int128& b) const + { +#ifdef USE_X86_64_ASM + Int128 result; + __asm__("subq %[bl], %[rl]\n\t" + "sbbq %[bh], %[rh]\n\t" + : [rl] "=r"(result.low), [rh] "=r"(result.high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); + return result; +#else + return *this + -b; +#endif + } + + Int128& operator+=(const Int128& b) + { +#ifdef USE_X86_64_ASM + __asm__("addq %[bl], %[rl]\n\t" + "adcq %[bh], %[rh]\n\t" + : [rl] "=r"(low), [rh] "=r"(high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); +#else + uint64_t lo = low + b.low; + if (lo < low) { + ++high; + } + low = lo; + high += b.high; +#endif + return *this; + } + + Int128& operator++() + { + if (++low == 0) { + ++high; + } + return *this; + } + + Int128 operator*(int64_t b) const; + + btScalar toScalar() const + { + return ((int64_t)high >= 0) ? btScalar(high) * (btScalar(0x100000000LL) * btScalar(0x100000000LL)) + btScalar(low) + : -(-*this).toScalar(); + } + + int getSign() const + { + return ((int64_t)high < 0) ? -1 : (high || low) ? 1 : 0; + } + + bool operator<(const Int128& b) const + { + return (high < b.high) || ((high == b.high) && (low < b.low)); + } + + int ucmp(const Int128& b) const + { + if (high < b.high) { + return -1; + } + if (high > b.high) { + return 1; + } + if (low < b.low) { + return -1; + } + if (low > b.low) { + return 1; + } + return 0; + } + }; + + class Rational64 { + private: + uint64_t m_numerator; + uint64_t m_denominator; + int sign; + + public: + Rational64(int64_t numerator, int64_t denominator) + { + if (numerator > 0) { + sign = 1; + m_numerator = (uint64_t)numerator; + } + else if (numerator < 0) { + sign = -1; + m_numerator = (uint64_t)-numerator; + } + else { + sign = 0; + m_numerator = 0; + } + if (denominator > 0) { + m_denominator = (uint64_t)denominator; + } + else if (denominator < 0) { + sign = -sign; + m_denominator = (uint64_t)-denominator; + } + else { + m_denominator = 0; + } + } + + bool isNegativeInfinity() const + { + return (sign < 0) && (m_denominator == 0); + } + + bool isNaN() const + { + return (sign == 0) && (m_denominator == 0); + } + + int compare(const Rational64& b) const; + + btScalar toScalar() const + { + return sign * ((m_denominator == 0) ? SIMD_INFINITY : (btScalar)m_numerator / m_denominator); + } + }; + + class Rational128 { + private: + Int128 numerator; + Int128 denominator; + int sign; + bool isInt64; + + public: + Rational128(int64_t value) + { + if (value > 0) { + sign = 1; + this->numerator = value; + } + else if (value < 0) { + sign = -1; + this->numerator = -value; + } + else { + sign = 0; + this->numerator = (uint64_t)0; + } + this->denominator = (uint64_t)1; + isInt64 = true; + } + + Rational128(const Int128& numerator, const Int128& denominator) + { + sign = numerator.getSign(); + if (sign >= 0) { + this->numerator = numerator; + } + else { + this->numerator = -numerator; + } + int dsign = denominator.getSign(); + if (dsign >= 0) { + this->denominator = denominator; + } + else { + sign = -sign; + this->denominator = -denominator; + } + isInt64 = false; + } + + int compare(const Rational128& b) const; + + int compare(int64_t b) const; + + btScalar toScalar() const + { + return sign * ((denominator.getSign() == 0) ? SIMD_INFINITY : numerator.toScalar() / denominator.toScalar()); + } + }; + + class PointR128 { + public: + Int128 x; + Int128 y; + Int128 z; + Int128 denominator; + + PointR128() + { + } + + PointR128(Int128 x, Int128 y, Int128 z, Int128 denominator) + : x(x) + , y(y) + , z(z) + , denominator(denominator) + { + } + + btScalar xvalue() const + { + return x.toScalar() / denominator.toScalar(); + } + + btScalar yvalue() const + { + return y.toScalar() / denominator.toScalar(); + } + + btScalar zvalue() const + { + return z.toScalar() / denominator.toScalar(); + } + }; + + class Edge; + class Face; + + class Vertex { + public: + Vertex* next; + Vertex* prev; + Edge* edges; + Face* firstNearbyFace; + Face* lastNearbyFace; + PointR128 point128; + Point32 point; + int copy; + + Vertex() + : next(NULL) + , prev(NULL) + , edges(NULL) + , firstNearbyFace(NULL) + , lastNearbyFace(NULL) + , copy(-1) + { + } + +#ifdef DEBUG_CONVEX_HULL + void print() + { + printf("V%d (%d, %d, %d)", point.index, point.x, point.y, point.z); + } + + void printGraph(); +#endif + + Point32 operator-(const Vertex& b) const + { + return point - b.point; + } + + Rational128 dot(const Point64& b) const + { + return (point.index >= 0) ? Rational128(point.dot(b)) + : Rational128(point128.x * b.x + point128.y * b.y + point128.z * b.z, point128.denominator); + } + + btScalar xvalue() const + { + return (point.index >= 0) ? btScalar(point.x) : point128.xvalue(); + } + + btScalar yvalue() const + { + return (point.index >= 0) ? btScalar(point.y) : point128.yvalue(); + } + + btScalar zvalue() const + { + return (point.index >= 0) ? btScalar(point.z) : point128.zvalue(); + } + + void receiveNearbyFaces(Vertex* src) + { + if (lastNearbyFace) { + lastNearbyFace->nextWithSameNearbyVertex = src->firstNearbyFace; + } + else { + firstNearbyFace = src->firstNearbyFace; + } + if (src->lastNearbyFace) { + lastNearbyFace = src->lastNearbyFace; + } + for (Face* f = src->firstNearbyFace; f; f = f->nextWithSameNearbyVertex) { + btAssert(f->nearbyVertex == src); + f->nearbyVertex = this; + } + src->firstNearbyFace = NULL; + src->lastNearbyFace = NULL; + } + }; + + class Edge { + public: + Edge* next; + Edge* prev; + Edge* reverse; + Vertex* target; + Face* face; + int copy; + + ~Edge() + { + next = NULL; + prev = NULL; + reverse = NULL; + target = NULL; + face = NULL; + } + + void link(Edge* n) + { + btAssert(reverse->target == n->reverse->target); + next = n; + n->prev = this; + } + +#ifdef DEBUG_CONVEX_HULL + void print() + { + printf("E%p : %d -> %d, n=%p p=%p (0 %d\t%d\t%d) -> (%d %d %d)", this, reverse->target->point.index, target->point.index, next, prev, + reverse->target->point.x, reverse->target->point.y, reverse->target->point.z, target->point.x, target->point.y, target->point.z); + } +#endif + }; + + class Face { + public: + Face* next; + Vertex* nearbyVertex; + Face* nextWithSameNearbyVertex; + Point32 origin; + Point32 dir0; + Point32 dir1; + + Face() + : next(NULL) + , nearbyVertex(NULL) + , nextWithSameNearbyVertex(NULL) + { + } + + void init(Vertex* a, Vertex* b, Vertex* c) + { + nearbyVertex = a; + origin = a->point; + dir0 = *b - *a; + dir1 = *c - *a; + if (a->lastNearbyFace) { + a->lastNearbyFace->nextWithSameNearbyVertex = this; + } + else { + a->firstNearbyFace = this; + } + a->lastNearbyFace = this; + } + + Point64 getNormal() + { + return dir0.cross(dir1); + } + }; + + template + class DMul { + private: + static uint32_t high(uint64_t value) + { + return (uint32_t)(value >> 32); + } + + static uint32_t low(uint64_t value) + { + return (uint32_t)value; + } + + static uint64_t mul(uint32_t a, uint32_t b) + { + return (uint64_t)a * (uint64_t)b; + } + + static void shlHalf(uint64_t& value) + { + value <<= 32; + } + + static uint64_t high(Int128 value) + { + return value.high; + } + + static uint64_t low(Int128 value) + { + return value.low; + } + + static Int128 mul(uint64_t a, uint64_t b) + { + return Int128::mul(a, b); + } + + static void shlHalf(Int128& value) + { + value.high = value.low; + value.low = 0; + } + + public: + static void mul(UWord a, UWord b, UWord& resLow, UWord& resHigh) + { + UWord p00 = mul(low(a), low(b)); + UWord p01 = mul(low(a), high(b)); + UWord p10 = mul(high(a), low(b)); + UWord p11 = mul(high(a), high(b)); + UWord p0110 = UWord(low(p01)) + UWord(low(p10)); + p11 += high(p01); + p11 += high(p10); + p11 += high(p0110); + shlHalf(p0110); + p00 += p0110; + if (p00 < p0110) { + ++p11; + } + resLow = p00; + resHigh = p11; + } + }; + +private: + class IntermediateHull { + public: + Vertex* minXy; + Vertex* maxXy; + Vertex* minYx; + Vertex* maxYx; + + IntermediateHull() + : minXy(NULL) + , maxXy(NULL) + , minYx(NULL) + , maxYx(NULL) + { + } + + void print(); + }; + + enum Orientation { NONE, + CLOCKWISE, + COUNTER_CLOCKWISE }; + + template + class PoolArray { + private: + T* array; + int size; + + public: + PoolArray* next; + + PoolArray(int size) + : size(size) + , next(NULL) + { + array = (T*)btAlignedAlloc(sizeof(T) * size, 16); + } + + ~PoolArray() + { + btAlignedFree(array); + } + + T* init() + { + T* o = array; + for (int i = 0; i < size; i++, o++) { + o->next = (i + 1 < size) ? o + 1 : NULL; + } + return array; + } + }; + + template + class Pool { + private: + PoolArray* arrays; + PoolArray* nextArray; + T* freeObjects; + int arraySize; + + public: + Pool() + : arrays(NULL) + , nextArray(NULL) + , freeObjects(NULL) + , arraySize(256) + { + } + + ~Pool() + { + while (arrays) { + PoolArray* p = arrays; + arrays = p->next; + p->~PoolArray(); + btAlignedFree(p); + } + } + + void reset() + { + nextArray = arrays; + freeObjects = NULL; + } + + void setArraySize(int arraySize) + { + this->arraySize = arraySize; + } + + T* newObject() + { + T* o = freeObjects; + if (!o) { + PoolArray* p = nextArray; + if (p) { + nextArray = p->next; + } + else { + p = new (btAlignedAlloc(sizeof(PoolArray), 16)) PoolArray(arraySize); + p->next = arrays; + arrays = p; + } + o = p->init(); + } + freeObjects = o->next; + return new (o) T(); + }; + + void freeObject(T* object) + { + object->~T(); + object->next = freeObjects; + freeObjects = object; + } + }; + + btVector3 scaling; + btVector3 center; + Pool vertexPool; + Pool edgePool; + Pool facePool; + btAlignedObjectArray originalVertices; + int mergeStamp; + int minAxis; + int medAxis; + int maxAxis; + int usedEdgePairs; + int maxUsedEdgePairs; + + static Orientation getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t); + Edge* findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot); + void findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1); + + Edge* newEdgePair(Vertex* from, Vertex* to); + + void removeEdgePair(Edge* edge) + { + Edge* n = edge->next; + Edge* r = edge->reverse; + + btAssert(edge->target && r->target); + + if (n != edge) { + n->prev = edge->prev; + edge->prev->next = n; + r->target->edges = n; + } + else { + r->target->edges = NULL; + } + + n = r->next; + + if (n != r) { + n->prev = r->prev; + r->prev->next = n; + edge->target->edges = n; + } + else { + edge->target->edges = NULL; + } + + edgePool.freeObject(edge); + edgePool.freeObject(r); + usedEdgePairs--; + } + + void computeInternal(int start, int end, IntermediateHull& result); + + bool mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1); + + void merge(IntermediateHull& h0, IntermediateHull& h1); + + btVector3 toBtVector(const Point32& v); + + btVector3 getBtNormal(Face* face); + + bool shiftFace(Face* face, btScalar amount, btAlignedObjectArray stack); + +public: + Vertex* vertexList; + + void compute(const void* coords, bool doubleCoords, int stride, int count); + + btVector3 getCoordinates(const Vertex* v); + + btScalar shrink(btScalar amount, btScalar clampAmount); +}; + +btConvexHullInternal::Int128 btConvexHullInternal::Int128::operator*(int64_t b) const +{ + bool negative = (int64_t)high < 0; + Int128 a = negative ? -*this : *this; + if (b < 0) { + negative = !negative; + b = -b; + } + Int128 result = mul(a.low, (uint64_t)b); + result.high += a.high * (uint64_t)b; + return negative ? -result : result; +} + +btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(int64_t a, int64_t b) +{ + Int128 result; + +#ifdef USE_X86_64_ASM + __asm__("imulq %[b]" + : "=a"(result.low), "=d"(result.high) + : "0"(a), [b] "r"(b) + : "cc"); + return result; + +#else + bool negative = a < 0; + if (negative) { + a = -a; + } + if (b < 0) { + negative = !negative; + b = -b; + } + DMul::mul((uint64_t)a, (uint64_t)b, result.low, result.high); + return negative ? -result : result; +#endif +} + +btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(uint64_t a, uint64_t b) +{ + Int128 result; + +#ifdef USE_X86_64_ASM + __asm__("mulq %[b]" + : "=a"(result.low), "=d"(result.high) + : "0"(a), [b] "r"(b) + : "cc"); + +#else + DMul::mul(a, b, result.low, result.high); +#endif + + return result; +} + +int btConvexHullInternal::Rational64::compare(const Rational64& b) const +{ + if (sign != b.sign) { + return sign - b.sign; + } + else if (sign == 0) { + return 0; + } + +// return (numerator * b.denominator > b.numerator * denominator) ? sign : (numerator * b.denominator < b.numerator * denominator) ? -sign : 0; + +#ifdef USE_X86_64_ASM + + int result; + int64_t tmp; + int64_t dummy; + __asm__("mulq %[bn]\n\t" + "movq %%rax, %[tmp]\n\t" + "movq %%rdx, %%rbx\n\t" + "movq %[tn], %%rax\n\t" + "mulq %[bd]\n\t" + "subq %[tmp], %%rax\n\t" + "sbbq %%rbx, %%rdx\n\t" // rdx:rax contains 128-bit-difference "numerator*b.denominator - b.numerator*denominator" + "setnsb %%bh\n\t" // bh=1 if difference is non-negative, bh=0 otherwise + "orq %%rdx, %%rax\n\t" + "setnzb %%bl\n\t" // bl=1 if difference if non-zero, bl=0 if it is zero + "decb %%bh\n\t" // now bx=0x0000 if difference is zero, 0xff01 if it is negative, 0x0001 if it is positive (i.e., same sign as difference) + "shll $16, %%ebx\n\t" // ebx has same sign as difference + : "=&b"(result), [tmp] "=&r"(tmp), "=a"(dummy) + : "a"(denominator), [bn] "g"(b.numerator), [tn] "g"(numerator), [bd] "g"(b.denominator) + : "%rdx", "cc"); + return result ? result ^ sign // if sign is +1, only bit 0 of result is inverted, which does not change the sign of result (and cannot result in zero) + // if sign is -1, all bits of result are inverted, which changes the sign of result (and again cannot result in zero) + : 0; + +#else + + return sign * Int128::mul(m_numerator, b.m_denominator).ucmp(Int128::mul(m_denominator, b.m_numerator)); + +#endif +} + +int btConvexHullInternal::Rational128::compare(const Rational128& b) const +{ + if (sign != b.sign) { + return sign - b.sign; + } + else if (sign == 0) { + return 0; + } + if (isInt64) { + return -b.compare(sign * (int64_t)numerator.low); + } + + Int128 nbdLow, nbdHigh, dbnLow, dbnHigh; + DMul::mul(numerator, b.denominator, nbdLow, nbdHigh); + DMul::mul(denominator, b.numerator, dbnLow, dbnHigh); + + int cmp = nbdHigh.ucmp(dbnHigh); + if (cmp) { + return cmp * sign; + } + return nbdLow.ucmp(dbnLow) * sign; +} + +int btConvexHullInternal::Rational128::compare(int64_t b) const +{ + if (isInt64) { + int64_t a = sign * (int64_t)numerator.low; + return (a > b) ? 1 : (a < b) ? -1 : 0; + } + if (b > 0) { + if (sign <= 0) { + return -1; + } + } + else if (b < 0) { + if (sign >= 0) { + return 1; + } + b = -b; + } + else { + return sign; + } + + return numerator.ucmp(denominator * b) * sign; +} + +btConvexHullInternal::Edge* btConvexHullInternal::newEdgePair(Vertex* from, Vertex* to) +{ + btAssert(from && to); + Edge* e = edgePool.newObject(); + Edge* r = edgePool.newObject(); + e->reverse = r; + r->reverse = e; + e->copy = mergeStamp; + r->copy = mergeStamp; + e->target = to; + r->target = from; + e->face = NULL; + r->face = NULL; + usedEdgePairs++; + if (usedEdgePairs > maxUsedEdgePairs) { + maxUsedEdgePairs = usedEdgePairs; + } + return e; +} + +bool btConvexHullInternal::mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1) +{ + Vertex* v0 = h0.maxYx; + Vertex* v1 = h1.minYx; + if ((v0->point.x == v1->point.x) && (v0->point.y == v1->point.y)) { + btAssert(v0->point.z < v1->point.z); + Vertex* v1p = v1->prev; + if (v1p == v1) { + c0 = v0; + if (v1->edges) { + btAssert(v1->edges->next == v1->edges); + v1 = v1->edges->target; + btAssert(v1->edges->next == v1->edges); + } + c1 = v1; + return false; + } + Vertex* v1n = v1->next; + v1p->next = v1n; + v1n->prev = v1p; + if (v1 == h1.minXy) { + if ((v1n->point.x < v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y < v1p->point.y))) { + h1.minXy = v1n; + } + else { + h1.minXy = v1p; + } + } + if (v1 == h1.maxXy) { + if ((v1n->point.x > v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y > v1p->point.y))) { + h1.maxXy = v1n; + } + else { + h1.maxXy = v1p; + } + } + } + + v0 = h0.maxXy; + v1 = h1.maxXy; + Vertex* v00 = NULL; + Vertex* v10 = NULL; + int32_t sign = 1; + + for (int side = 0; side <= 1; side++) { + int32_t dx = (v1->point.x - v0->point.x) * sign; + if (dx > 0) { + while (true) { + int32_t dy = v1->point.y - v0->point.y; + + Vertex* w0 = side ? v0->next : v0->prev; + if (w0 != v0) { + int32_t dx0 = (w0->point.x - v0->point.x) * sign; + int32_t dy0 = w0->point.y - v0->point.y; + if ((dy0 <= 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx <= dy * dx0)))) { + v0 = w0; + dx = (v1->point.x - v0->point.x) * sign; + continue; + } + } + + Vertex* w1 = side ? v1->next : v1->prev; + if (w1 != v1) { + int32_t dx1 = (w1->point.x - v1->point.x) * sign; + int32_t dy1 = w1->point.y - v1->point.y; + int32_t dxn = (w1->point.x - v0->point.x) * sign; + if ((dxn > 0) && (dy1 < 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx < dy * dx1)))) { + v1 = w1; + dx = dxn; + continue; + } + } + + break; + } + } + else if (dx < 0) { + while (true) { + int32_t dy = v1->point.y - v0->point.y; + + Vertex* w1 = side ? v1->prev : v1->next; + if (w1 != v1) { + int32_t dx1 = (w1->point.x - v1->point.x) * sign; + int32_t dy1 = w1->point.y - v1->point.y; + if ((dy1 >= 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx <= dy * dx1)))) { + v1 = w1; + dx = (v1->point.x - v0->point.x) * sign; + continue; + } + } + + Vertex* w0 = side ? v0->prev : v0->next; + if (w0 != v0) { + int32_t dx0 = (w0->point.x - v0->point.x) * sign; + int32_t dy0 = w0->point.y - v0->point.y; + int32_t dxn = (v1->point.x - w0->point.x) * sign; + if ((dxn < 0) && (dy0 > 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx < dy * dx0)))) { + v0 = w0; + dx = dxn; + continue; + } + } + + break; + } + } + else { + int32_t x = v0->point.x; + int32_t y0 = v0->point.y; + Vertex* w0 = v0; + Vertex* t; + while (((t = side ? w0->next : w0->prev) != v0) && (t->point.x == x) && (t->point.y <= y0)) { + w0 = t; + y0 = t->point.y; + } + v0 = w0; + + int32_t y1 = v1->point.y; + Vertex* w1 = v1; + while (((t = side ? w1->prev : w1->next) != v1) && (t->point.x == x) && (t->point.y >= y1)) { + w1 = t; + y1 = t->point.y; + } + v1 = w1; + } + + if (side == 0) { + v00 = v0; + v10 = v1; + + v0 = h0.minXy; + v1 = h1.minXy; + sign = -1; + } + } + + v0->prev = v1; + v1->next = v0; + + v00->next = v10; + v10->prev = v00; + + if (h1.minXy->point.x < h0.minXy->point.x) { + h0.minXy = h1.minXy; + } + if (h1.maxXy->point.x >= h0.maxXy->point.x) { + h0.maxXy = h1.maxXy; + } + + h0.maxYx = h1.maxYx; + + c0 = v00; + c1 = v10; + + return true; +} + +void btConvexHullInternal::computeInternal(int start, int end, IntermediateHull& result) +{ + int n = end - start; + switch (n) { + case 0: + result.minXy = NULL; + result.maxXy = NULL; + result.minYx = NULL; + result.maxYx = NULL; + return; + case 2: { + Vertex* v = originalVertices[start]; + Vertex* w = v + 1; + if (v->point != w->point) { + int32_t dx = v->point.x - w->point.x; + int32_t dy = v->point.y - w->point.y; + + if ((dx == 0) && (dy == 0)) { + if (v->point.z > w->point.z) { + Vertex* t = w; + w = v; + v = t; + } + btAssert(v->point.z < w->point.z); + v->next = v; + v->prev = v; + result.minXy = v; + result.maxXy = v; + result.minYx = v; + result.maxYx = v; + } + else { + v->next = w; + v->prev = w; + w->next = v; + w->prev = v; + + if ((dx < 0) || ((dx == 0) && (dy < 0))) { + result.minXy = v; + result.maxXy = w; + } + else { + result.minXy = w; + result.maxXy = v; + } + + if ((dy < 0) || ((dy == 0) && (dx < 0))) { + result.minYx = v; + result.maxYx = w; + } + else { + result.minYx = w; + result.maxYx = v; + } + } + + Edge* e = newEdgePair(v, w); + e->link(e); + v->edges = e; + + e = e->reverse; + e->link(e); + w->edges = e; + + return; + } + } + // lint -fallthrough + case 1: { + Vertex* v = originalVertices[start]; + v->edges = NULL; + v->next = v; + v->prev = v; + + result.minXy = v; + result.maxXy = v; + result.minYx = v; + result.maxYx = v; + + return; + } + } + + int split0 = start + n / 2; + Point32 p = originalVertices[split0 - 1]->point; + int split1 = split0; + while ((split1 < end) && (originalVertices[split1]->point == p)) { + split1++; + } + computeInternal(start, split0, result); + IntermediateHull hull1; + computeInternal(split1, end, hull1); +#ifdef DEBUG_CONVEX_HULL + printf("\n\nMerge\n"); + result.print(); + hull1.print(); +#endif + merge(result, hull1); +#ifdef DEBUG_CONVEX_HULL + printf("\n Result\n"); + result.print(); +#endif +} + +#ifdef DEBUG_CONVEX_HULL +void btConvexHullInternal::IntermediateHull::print() +{ + printf(" Hull\n"); + for (Vertex* v = minXy; v;) { + printf(" "); + v->print(); + if (v == maxXy) { + printf(" maxXy"); + } + if (v == minYx) { + printf(" minYx"); + } + if (v == maxYx) { + printf(" maxYx"); + } + if (v->next->prev != v) { + printf(" Inconsistency"); + } + printf("\n"); + v = v->next; + if (v == minXy) { + break; + } + } + if (minXy) { + minXy->copy = (minXy->copy == -1) ? -2 : -1; + minXy->printGraph(); + } +} + +void btConvexHullInternal::Vertex::printGraph() +{ + print(); + printf("\nEdges\n"); + Edge* e = edges; + if (e) { + do { + e->print(); + printf("\n"); + e = e->next; + } while (e != edges); + do { + Vertex* v = e->target; + if (v->copy != copy) { + v->copy = copy; + v->printGraph(); + } + e = e->next; + } while (e != edges); + } +} +#endif + +btConvexHullInternal::Orientation btConvexHullInternal::getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t) +{ + btAssert(prev->reverse->target == next->reverse->target); + if (prev->next == next) { + if (prev->prev == next) { + Point64 n = t.cross(s); + Point64 m = (*prev->target - *next->reverse->target).cross(*next->target - *next->reverse->target); + btAssert(!m.isZero()); + int64_t dot = n.dot(m); + btAssert(dot != 0); + return (dot > 0) ? COUNTER_CLOCKWISE : CLOCKWISE; + } + return COUNTER_CLOCKWISE; + } + else if (prev->prev == next) { + return CLOCKWISE; + } + else { + return NONE; + } +} + +btConvexHullInternal::Edge* btConvexHullInternal::findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot) +{ + Edge* minEdge = NULL; + +#ifdef DEBUG_CONVEX_HULL + printf("find max edge for %d\n", start->point.index); +#endif + Edge* e = start->edges; + if (e) { + do { + if (e->copy > mergeStamp) { + Point32 t = *e->target - *start; + Rational64 cot(t.dot(sxrxs), t.dot(rxs)); +#ifdef DEBUG_CONVEX_HULL + printf(" Angle is %f (%d) for ", (float)btAtan(cot.toScalar()), (int)cot.isNaN()); + e->print(); +#endif + if (cot.isNaN()) { + btAssert(ccw ? (t.dot(s) < 0) : (t.dot(s) > 0)); + } + else { + int cmp; + if (minEdge == NULL) { + minCot = cot; + minEdge = e; + } + else if ((cmp = cot.compare(minCot)) < 0) { + minCot = cot; + minEdge = e; + } + else if ((cmp == 0) && (ccw == (getOrientation(minEdge, e, s, t) == COUNTER_CLOCKWISE))) { + minEdge = e; + } + } +#ifdef DEBUG_CONVEX_HULL + printf("\n"); +#endif + } + e = e->next; + } while (e != start->edges); + } + return minEdge; +} + +void btConvexHullInternal::findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1) +{ + Edge* start0 = e0; + Edge* start1 = e1; + Point32 et0 = start0 ? start0->target->point : c0->point; + Point32 et1 = start1 ? start1->target->point : c1->point; + Point32 s = c1->point - c0->point; + Point64 normal = ((start0 ? start0 : start1)->target->point - c0->point).cross(s); + int64_t dist = c0->point.dot(normal); + btAssert(!start1 || (start1->target->point.dot(normal) == dist)); + Point64 perp = s.cross(normal); + btAssert(!perp.isZero()); + +#ifdef DEBUG_CONVEX_HULL + printf(" Advancing %d %d (%p %p, %d %d)\n", c0->point.index, c1->point.index, start0, start1, start0 ? start0->target->point.index : -1, start1 ? start1->target->point.index : -1); +#endif + + int64_t maxDot0 = et0.dot(perp); + if (e0) { + while (e0->target != stop0) { + Edge* e = e0->reverse->prev; + if (e->target->point.dot(normal) < dist) { + break; + } + btAssert(e->target->point.dot(normal) == dist); + if (e->copy == mergeStamp) { + break; + } + int64_t dot = e->target->point.dot(perp); + if (dot <= maxDot0) { + break; + } + maxDot0 = dot; + e0 = e; + et0 = e->target->point; + } + } + + int64_t maxDot1 = et1.dot(perp); + if (e1) { + while (e1->target != stop1) { + Edge* e = e1->reverse->next; + if (e->target->point.dot(normal) < dist) { + break; + } + btAssert(e->target->point.dot(normal) == dist); + if (e->copy == mergeStamp) { + break; + } + int64_t dot = e->target->point.dot(perp); + if (dot <= maxDot1) { + break; + } + maxDot1 = dot; + e1 = e; + et1 = e->target->point; + } + } + +#ifdef DEBUG_CONVEX_HULL + printf(" Starting at %d %d\n", et0.index, et1.index); +#endif + + int64_t dx = maxDot1 - maxDot0; + if (dx > 0) { + while (true) { + int64_t dy = (et1 - et0).dot(s); + + if (e0 && (e0->target != stop0)) { + Edge* f0 = e0->next->reverse; + if (f0->copy > mergeStamp) { + int64_t dx0 = (f0->target->point - et0).dot(perp); + int64_t dy0 = (f0->target->point - et0).dot(s); + if ((dx0 == 0) ? (dy0 < 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) >= 0))) { + et0 = f0->target->point; + dx = (et1 - et0).dot(perp); + e0 = (e0 == start0) ? NULL : f0; + continue; + } + } + } + + if (e1 && (e1->target != stop1)) { + Edge* f1 = e1->reverse->next; + if (f1->copy > mergeStamp) { + Point32 d1 = f1->target->point - et1; + if (d1.dot(normal) == 0) { + int64_t dx1 = d1.dot(perp); + int64_t dy1 = d1.dot(s); + int64_t dxn = (f1->target->point - et0).dot(perp); + if ((dxn > 0) && ((dx1 == 0) ? (dy1 < 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) > 0)))) { + e1 = f1; + et1 = e1->target->point; + dx = dxn; + continue; + } + } + else { + btAssert((e1 == start1) && (d1.dot(normal) < 0)); + } + } + } + + break; + } + } + else if (dx < 0) { + while (true) { + int64_t dy = (et1 - et0).dot(s); + + if (e1 && (e1->target != stop1)) { + Edge* f1 = e1->prev->reverse; + if (f1->copy > mergeStamp) { + int64_t dx1 = (f1->target->point - et1).dot(perp); + int64_t dy1 = (f1->target->point - et1).dot(s); + if ((dx1 == 0) ? (dy1 > 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) <= 0))) { + et1 = f1->target->point; + dx = (et1 - et0).dot(perp); + e1 = (e1 == start1) ? NULL : f1; + continue; + } + } + } + + if (e0 && (e0->target != stop0)) { + Edge* f0 = e0->reverse->prev; + if (f0->copy > mergeStamp) { + Point32 d0 = f0->target->point - et0; + if (d0.dot(normal) == 0) { + int64_t dx0 = d0.dot(perp); + int64_t dy0 = d0.dot(s); + int64_t dxn = (et1 - f0->target->point).dot(perp); + if ((dxn < 0) && ((dx0 == 0) ? (dy0 > 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) < 0)))) { + e0 = f0; + et0 = e0->target->point; + dx = dxn; + continue; + } + } + else { + btAssert((e0 == start0) && (d0.dot(normal) < 0)); + } + } + } + + break; + } + } +#ifdef DEBUG_CONVEX_HULL + printf(" Advanced edges to %d %d\n", et0.index, et1.index); +#endif +} + +void btConvexHullInternal::merge(IntermediateHull& h0, IntermediateHull& h1) +{ + if (!h1.maxXy) { + return; + } + if (!h0.maxXy) { + h0 = h1; + return; + } + + mergeStamp--; + + Vertex* c0 = NULL; + Edge* toPrev0 = NULL; + Edge* firstNew0 = NULL; + Edge* pendingHead0 = NULL; + Edge* pendingTail0 = NULL; + Vertex* c1 = NULL; + Edge* toPrev1 = NULL; + Edge* firstNew1 = NULL; + Edge* pendingHead1 = NULL; + Edge* pendingTail1 = NULL; + Point32 prevPoint; + + if (mergeProjection(h0, h1, c0, c1)) { + Point32 s = *c1 - *c0; + Point64 normal = Point32(0, 0, -1).cross(s); + Point64 t = s.cross(normal); + btAssert(!t.isZero()); + + Edge* e = c0->edges; + Edge* start0 = NULL; + if (e) { + do { + int64_t dot = (*e->target - *c0).dot(normal); + btAssert(dot <= 0); + if ((dot == 0) && ((*e->target - *c0).dot(t) > 0)) { + if (!start0 || (getOrientation(start0, e, s, Point32(0, 0, -1)) == CLOCKWISE)) { + start0 = e; + } + } + e = e->next; + } while (e != c0->edges); + } + + e = c1->edges; + Edge* start1 = NULL; + if (e) { + do { + int64_t dot = (*e->target - *c1).dot(normal); + btAssert(dot <= 0); + if ((dot == 0) && ((*e->target - *c1).dot(t) > 0)) { + if (!start1 || (getOrientation(start1, e, s, Point32(0, 0, -1)) == COUNTER_CLOCKWISE)) { + start1 = e; + } + } + e = e->next; + } while (e != c1->edges); + } + + if (start0 || start1) { + findEdgeForCoplanarFaces(c0, c1, start0, start1, NULL, NULL); + if (start0) { + c0 = start0->target; + } + if (start1) { + c1 = start1->target; + } + } + + prevPoint = c1->point; + prevPoint.z++; + } + else { + prevPoint = c1->point; + prevPoint.x++; + } + + Vertex* first0 = c0; + Vertex* first1 = c1; + bool firstRun = true; + + while (true) { + Point32 s = *c1 - *c0; + Point32 r = prevPoint - c0->point; + Point64 rxs = r.cross(s); + Point64 sxrxs = s.cross(rxs); + +#ifdef DEBUG_CONVEX_HULL + printf("\n Checking %d %d\n", c0->point.index, c1->point.index); +#endif + Rational64 minCot0(0, 0); + Edge* min0 = findMaxAngle(false, c0, s, rxs, sxrxs, minCot0); + Rational64 minCot1(0, 0); + Edge* min1 = findMaxAngle(true, c1, s, rxs, sxrxs, minCot1); + if (!min0 && !min1) { + Edge* e = newEdgePair(c0, c1); + e->link(e); + c0->edges = e; + + e = e->reverse; + e->link(e); + c1->edges = e; + return; + } + else { + int cmp = !min0 ? 1 : !min1 ? -1 : minCot0.compare(minCot1); +#ifdef DEBUG_CONVEX_HULL + printf(" -> Result %d\n", cmp); +#endif + if (firstRun || ((cmp >= 0) ? !minCot1.isNegativeInfinity() : !minCot0.isNegativeInfinity())) { + Edge* e = newEdgePair(c0, c1); + if (pendingTail0) { + pendingTail0->prev = e; + } + else { + pendingHead0 = e; + } + e->next = pendingTail0; + pendingTail0 = e; + + e = e->reverse; + if (pendingTail1) { + pendingTail1->next = e; + } + else { + pendingHead1 = e; + } + e->prev = pendingTail1; + pendingTail1 = e; + } + + Edge* e0 = min0; + Edge* e1 = min1; + +#ifdef DEBUG_CONVEX_HULL + printf(" Found min edges to %d %d\n", e0 ? e0->target->point.index : -1, e1 ? e1->target->point.index : -1); +#endif + + if (cmp == 0) { + findEdgeForCoplanarFaces(c0, c1, e0, e1, NULL, NULL); + } + + if ((cmp >= 0) && e1) { + if (toPrev1) { + for (Edge *e = toPrev1->next, *n = NULL; e != min1; e = n) { + n = e->next; + removeEdgePair(e); + } + } + + if (pendingTail1) { + if (toPrev1) { + toPrev1->link(pendingHead1); + } + else { + min1->prev->link(pendingHead1); + firstNew1 = pendingHead1; + } + pendingTail1->link(min1); + pendingHead1 = NULL; + pendingTail1 = NULL; + } + else if (!toPrev1) { + firstNew1 = min1; + } + + prevPoint = c1->point; + c1 = e1->target; + toPrev1 = e1->reverse; + } + + if ((cmp <= 0) && e0) { + if (toPrev0) { + for (Edge *e = toPrev0->prev, *n = NULL; e != min0; e = n) { + n = e->prev; + removeEdgePair(e); + } + } + + if (pendingTail0) { + if (toPrev0) { + pendingHead0->link(toPrev0); + } + else { + pendingHead0->link(min0->next); + firstNew0 = pendingHead0; + } + min0->link(pendingTail0); + pendingHead0 = NULL; + pendingTail0 = NULL; + } + else if (!toPrev0) { + firstNew0 = min0; + } + + prevPoint = c0->point; + c0 = e0->target; + toPrev0 = e0->reverse; + } + } + + if ((c0 == first0) && (c1 == first1)) { + if (toPrev0 == NULL) { + pendingHead0->link(pendingTail0); + c0->edges = pendingTail0; + } + else { + for (Edge *e = toPrev0->prev, *n = NULL; e != firstNew0; e = n) { + n = e->prev; + removeEdgePair(e); + } + if (pendingTail0) { + pendingHead0->link(toPrev0); + firstNew0->link(pendingTail0); + } + } + + if (toPrev1 == NULL) { + pendingTail1->link(pendingHead1); + c1->edges = pendingTail1; + } + else { + for (Edge *e = toPrev1->next, *n = NULL; e != firstNew1; e = n) { + n = e->next; + removeEdgePair(e); + } + if (pendingTail1) { + toPrev1->link(pendingHead1); + pendingTail1->link(firstNew1); + } + } + + return; + } + + firstRun = false; + } +} + +static bool pointCmp(const btConvexHullInternal::Point32& p, const btConvexHullInternal::Point32& q) +{ + return (p.y < q.y) || ((p.y == q.y) && ((p.x < q.x) || ((p.x == q.x) && (p.z < q.z)))); +} + +void btConvexHullInternal::compute(const void* coords, bool doubleCoords, int stride, int count) +{ + btVector3 min(btScalar(1e30), btScalar(1e30), btScalar(1e30)), max(btScalar(-1e30), btScalar(-1e30), btScalar(-1e30)); + const char* ptr = (const char*)coords; + if (doubleCoords) { + for (int i = 0; i < count; i++) { + const double* v = (const double*)ptr; + btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); + ptr += stride; + min.setMin(p); + max.setMax(p); + } + } + else { + for (int i = 0; i < count; i++) { + const float* v = (const float*)ptr; + btVector3 p(v[0], v[1], v[2]); + ptr += stride; + min.setMin(p); + max.setMax(p); + } + } + + btVector3 s = max - min; + maxAxis = s.maxAxis(); + minAxis = s.minAxis(); + if (minAxis == maxAxis) { + minAxis = (maxAxis + 1) % 3; + } + medAxis = 3 - maxAxis - minAxis; + + s /= btScalar(10216); + if (((medAxis + 1) % 3) != maxAxis) { + s *= -1; + } + scaling = s; + + if (s[0] != 0) { + s[0] = btScalar(1) / s[0]; + } + if (s[1] != 0) { + s[1] = btScalar(1) / s[1]; + } + if (s[2] != 0) { + s[2] = btScalar(1) / s[2]; + } + + center = (min + max) * btScalar(0.5); + + btAlignedObjectArray points; + points.resize(count); + ptr = (const char*)coords; + if (doubleCoords) { + for (int i = 0; i < count; i++) { + const double* v = (const double*)ptr; + btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); + ptr += stride; + p = (p - center) * s; + points[i].x = (int32_t)p[medAxis]; + points[i].y = (int32_t)p[maxAxis]; + points[i].z = (int32_t)p[minAxis]; + points[i].index = i; + } + } + else { + for (int i = 0; i < count; i++) { + const float* v = (const float*)ptr; + btVector3 p(v[0], v[1], v[2]); + ptr += stride; + p = (p - center) * s; + points[i].x = (int32_t)p[medAxis]; + points[i].y = (int32_t)p[maxAxis]; + points[i].z = (int32_t)p[minAxis]; + points[i].index = i; + } + } + points.quickSort(pointCmp); + + vertexPool.reset(); + vertexPool.setArraySize(count); + originalVertices.resize(count); + for (int i = 0; i < count; i++) { + Vertex* v = vertexPool.newObject(); + v->edges = NULL; + v->point = points[i]; + v->copy = -1; + originalVertices[i] = v; + } + + points.clear(); + + edgePool.reset(); + edgePool.setArraySize(6 * count); + + usedEdgePairs = 0; + maxUsedEdgePairs = 0; + + mergeStamp = -3; + + IntermediateHull hull; + computeInternal(0, count, hull); + vertexList = hull.minXy; +#ifdef DEBUG_CONVEX_HULL + printf("max. edges %d (3v = %d)", maxUsedEdgePairs, 3 * count); +#endif +} + +btVector3 btConvexHullInternal::toBtVector(const Point32& v) +{ + btVector3 p; + p[medAxis] = btScalar(v.x); + p[maxAxis] = btScalar(v.y); + p[minAxis] = btScalar(v.z); + return p * scaling; +} + +btVector3 btConvexHullInternal::getBtNormal(Face* face) +{ + return toBtVector(face->dir0).cross(toBtVector(face->dir1)).normalized(); +} + +btVector3 btConvexHullInternal::getCoordinates(const Vertex* v) +{ + btVector3 p; + p[medAxis] = v->xvalue(); + p[maxAxis] = v->yvalue(); + p[minAxis] = v->zvalue(); + return p * scaling + center; +} + +btScalar btConvexHullInternal::shrink(btScalar amount, btScalar clampAmount) +{ + if (!vertexList) { + return 0; + } + int stamp = --mergeStamp; + btAlignedObjectArray stack; + vertexList->copy = stamp; + stack.push_back(vertexList); + btAlignedObjectArray faces; + + Point32 ref = vertexList->point; + Int128 hullCenterX(0, 0); + Int128 hullCenterY(0, 0); + Int128 hullCenterZ(0, 0); + Int128 volume(0, 0); + + while (stack.size() > 0) { + Vertex* v = stack[stack.size() - 1]; + stack.pop_back(); + Edge* e = v->edges; + if (e) { + do { + if (e->target->copy != stamp) { + e->target->copy = stamp; + stack.push_back(e->target); + } + if (e->copy != stamp) { + Face* face = facePool.newObject(); + face->init(e->target, e->reverse->prev->target, v); + faces.push_back(face); + Edge* f = e; + + Vertex* a = NULL; + Vertex* b = NULL; + do { + if (a && b) { + int64_t vol = (v->point - ref).dot((a->point - ref).cross(b->point - ref)); + btAssert(vol >= 0); + Point32 c = v->point + a->point + b->point + ref; + hullCenterX += vol * c.x; + hullCenterY += vol * c.y; + hullCenterZ += vol * c.z; + volume += vol; + } + + btAssert(f->copy != stamp); + f->copy = stamp; + f->face = face; + + a = b; + b = f->target; + + f = f->reverse->prev; + } while (f != e); + } + e = e->next; + } while (e != v->edges); + } + } + + if (volume.getSign() <= 0) { + return 0; + } + + btVector3 hullCenter; + hullCenter[medAxis] = hullCenterX.toScalar(); + hullCenter[maxAxis] = hullCenterY.toScalar(); + hullCenter[minAxis] = hullCenterZ.toScalar(); + hullCenter /= 4 * volume.toScalar(); + hullCenter *= scaling; + + int faceCount = faces.size(); + + if (clampAmount > 0) { + btScalar minDist = SIMD_INFINITY; + for (int i = 0; i < faceCount; i++) { + btVector3 normal = getBtNormal(faces[i]); + btScalar dist = normal.dot(toBtVector(faces[i]->origin) - hullCenter); + if (dist < minDist) { + minDist = dist; + } + } + + if (minDist <= 0) { + return 0; + } + + amount = btMin(amount, minDist * clampAmount); + } + + unsigned int seed = 243703; + for (int i = 0; i < faceCount; i++, seed = 1664525 * seed + 1013904223) { + btSwap(faces[i], faces[seed % faceCount]); + } + + for (int i = 0; i < faceCount; i++) { + if (!shiftFace(faces[i], amount, stack)) { + return -amount; + } + } + + return amount; +} + +bool btConvexHullInternal::shiftFace(Face* face, btScalar amount, btAlignedObjectArray stack) +{ + btVector3 origShift = getBtNormal(face) * -amount; + if (scaling[0] != 0) { + origShift[0] /= scaling[0]; + } + if (scaling[1] != 0) { + origShift[1] /= scaling[1]; + } + if (scaling[2] != 0) { + origShift[2] /= scaling[2]; + } + Point32 shift((int32_t)origShift[medAxis], (int32_t)origShift[maxAxis], (int32_t)origShift[minAxis]); + if (shift.isZero()) { + return true; + } + Point64 normal = face->getNormal(); +#ifdef DEBUG_CONVEX_HULL + printf("\nShrinking face (%d %d %d) (%d %d %d) (%d %d %d) by (%d %d %d)\n", + face->origin.x, face->origin.y, face->origin.z, face->dir0.x, face->dir0.y, face->dir0.z, face->dir1.x, face->dir1.y, face->dir1.z, shift.x, shift.y, shift.z); +#endif + int64_t origDot = face->origin.dot(normal); + Point32 shiftedOrigin = face->origin + shift; + int64_t shiftedDot = shiftedOrigin.dot(normal); + btAssert(shiftedDot <= origDot); + if (shiftedDot >= origDot) { + return false; + } + + Edge* intersection = NULL; + + Edge* startEdge = face->nearbyVertex->edges; +#ifdef DEBUG_CONVEX_HULL + printf("Start edge is "); + startEdge->print(); + printf(", normal is (%lld %lld %lld), shifted dot is %lld\n", normal.x, normal.y, normal.z, shiftedDot); +#endif + Rational128 optDot = face->nearbyVertex->dot(normal); + int cmp = optDot.compare(shiftedDot); +#ifdef SHOW_ITERATIONS + int n = 0; +#endif + if (cmp >= 0) { + Edge* e = startEdge; + do { +#ifdef SHOW_ITERATIONS + n++; +#endif + Rational128 dot = e->target->dot(normal); + btAssert(dot.compare(origDot) <= 0); +#ifdef DEBUG_CONVEX_HULL + printf("Moving downwards, edge is "); + e->print(); + printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); +#endif + if (dot.compare(optDot) < 0) { + int c = dot.compare(shiftedDot); + optDot = dot; + e = e->reverse; + startEdge = e; + if (c < 0) { + intersection = e; + break; + } + cmp = c; + } + e = e->prev; + } while (e != startEdge); + + if (!intersection) { + return false; + } + } + else { + Edge* e = startEdge; + do { +#ifdef SHOW_ITERATIONS + n++; +#endif + Rational128 dot = e->target->dot(normal); + btAssert(dot.compare(origDot) <= 0); +#ifdef DEBUG_CONVEX_HULL + printf("Moving upwards, edge is "); + e->print(); + printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); +#endif + if (dot.compare(optDot) > 0) { + cmp = dot.compare(shiftedDot); + if (cmp >= 0) { + intersection = e; + break; + } + optDot = dot; + e = e->reverse; + startEdge = e; + } + e = e->prev; + } while (e != startEdge); + + if (!intersection) { + return true; + } + } + +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to find initial intersection\n", n); +#endif + + if (cmp == 0) { + Edge* e = intersection->reverse->next; +#ifdef SHOW_ITERATIONS + n = 0; +#endif + while (e->target->dot(normal).compare(shiftedDot) <= 0) { +#ifdef SHOW_ITERATIONS + n++; +#endif + e = e->next; + if (e == intersection->reverse) { + return true; + } +#ifdef DEBUG_CONVEX_HULL + printf("Checking for outwards edge, current edge is "); + e->print(); + printf("\n"); +#endif + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to check for complete containment\n", n); +#endif + } + + Edge* firstIntersection = NULL; + Edge* faceEdge = NULL; + Edge* firstFaceEdge = NULL; + +#ifdef SHOW_ITERATIONS + int m = 0; +#endif + while (true) { +#ifdef SHOW_ITERATIONS + m++; +#endif +#ifdef DEBUG_CONVEX_HULL + printf("Intersecting edge is "); + intersection->print(); + printf("\n"); +#endif + if (cmp == 0) { + Edge* e = intersection->reverse->next; + startEdge = e; +#ifdef SHOW_ITERATIONS + n = 0; +#endif + while (true) { +#ifdef SHOW_ITERATIONS + n++; +#endif + if (e->target->dot(normal).compare(shiftedDot) >= 0) { + break; + } + intersection = e->reverse; + e = e->next; + if (e == startEdge) { + return true; + } + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to advance intersection\n", n); +#endif + } + +#ifdef DEBUG_CONVEX_HULL + printf("Advanced intersecting edge to "); + intersection->print(); + printf(", cmp = %d\n", cmp); +#endif + + if (!firstIntersection) { + firstIntersection = intersection; + } + else if (intersection == firstIntersection) { + break; + } + + int prevCmp = cmp; + Edge* prevIntersection = intersection; + Edge* prevFaceEdge = faceEdge; + + Edge* e = intersection->reverse; +#ifdef SHOW_ITERATIONS + n = 0; +#endif + while (true) { +#ifdef SHOW_ITERATIONS + n++; +#endif + e = e->reverse->prev; + btAssert(e != intersection->reverse); + cmp = e->target->dot(normal).compare(shiftedDot); +#ifdef DEBUG_CONVEX_HULL + printf("Testing edge "); + e->print(); + printf(" -> cmp = %d\n", cmp); +#endif + if (cmp >= 0) { + intersection = e; + break; + } + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to find other intersection of face\n", n); +#endif + + if (cmp > 0) { + Vertex* removed = intersection->target; + e = intersection->reverse; + if (e->prev == e) { + removed->edges = NULL; + } + else { + removed->edges = e->prev; + e->prev->link(e->next); + e->link(e); + } +#ifdef DEBUG_CONVEX_HULL + printf("1: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); +#endif + + Point64 n0 = intersection->face->getNormal(); + Point64 n1 = intersection->reverse->face->getNormal(); + int64_t m00 = face->dir0.dot(n0); + int64_t m01 = face->dir1.dot(n0); + int64_t m10 = face->dir0.dot(n1); + int64_t m11 = face->dir1.dot(n1); + int64_t r0 = (intersection->face->origin - shiftedOrigin).dot(n0); + int64_t r1 = (intersection->reverse->face->origin - shiftedOrigin).dot(n1); + Int128 det = Int128::mul(m00, m11) - Int128::mul(m01, m10); + btAssert(det.getSign() != 0); + Vertex* v = vertexPool.newObject(); + v->point.index = -1; + v->copy = -1; + v->point128 = PointR128(Int128::mul(face->dir0.x * r0, m11) - Int128::mul(face->dir0.x * r1, m01) + + Int128::mul(face->dir1.x * r1, m00) - Int128::mul(face->dir1.x * r0, m10) + det * shiftedOrigin.x, + Int128::mul(face->dir0.y * r0, m11) - Int128::mul(face->dir0.y * r1, m01) + + Int128::mul(face->dir1.y * r1, m00) - Int128::mul(face->dir1.y * r0, m10) + det * shiftedOrigin.y, + Int128::mul(face->dir0.z * r0, m11) - Int128::mul(face->dir0.z * r1, m01) + + Int128::mul(face->dir1.z * r1, m00) - Int128::mul(face->dir1.z * r0, m10) + det * shiftedOrigin.z, + det); + v->point.x = (int32_t)v->point128.xvalue(); + v->point.y = (int32_t)v->point128.yvalue(); + v->point.z = (int32_t)v->point128.zvalue(); + intersection->target = v; + v->edges = e; + + stack.push_back(v); + stack.push_back(removed); + stack.push_back(NULL); + } + + if (cmp || prevCmp || (prevIntersection->reverse->next->target != intersection->target)) { + faceEdge = newEdgePair(prevIntersection->target, intersection->target); + if (prevCmp == 0) { + faceEdge->link(prevIntersection->reverse->next); + } + if ((prevCmp == 0) || prevFaceEdge) { + prevIntersection->reverse->link(faceEdge); + } + if (cmp == 0) { + intersection->reverse->prev->link(faceEdge->reverse); + } + faceEdge->reverse->link(intersection->reverse); + } + else { + faceEdge = prevIntersection->reverse->next; + } + + if (prevFaceEdge) { + if (prevCmp > 0) { + faceEdge->link(prevFaceEdge->reverse); + } + else if (faceEdge != prevFaceEdge->reverse) { + stack.push_back(prevFaceEdge->target); + while (faceEdge->next != prevFaceEdge->reverse) { + Vertex* removed = faceEdge->next->target; + removeEdgePair(faceEdge->next); + stack.push_back(removed); +#ifdef DEBUG_CONVEX_HULL + printf("2: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); +#endif + } + stack.push_back(NULL); + } + } + faceEdge->face = face; + faceEdge->reverse->face = intersection->face; + + if (!firstFaceEdge) { + firstFaceEdge = faceEdge; + } + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to process all intersections\n", m); +#endif + + if (cmp > 0) { + firstFaceEdge->reverse->target = faceEdge->target; + firstIntersection->reverse->link(firstFaceEdge); + firstFaceEdge->link(faceEdge->reverse); + } + else if (firstFaceEdge != faceEdge->reverse) { + stack.push_back(faceEdge->target); + while (firstFaceEdge->next != faceEdge->reverse) { + Vertex* removed = firstFaceEdge->next->target; + removeEdgePair(firstFaceEdge->next); + stack.push_back(removed); +#ifdef DEBUG_CONVEX_HULL + printf("3: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); +#endif + } + stack.push_back(NULL); + } + + btAssert(stack.size() > 0); + vertexList = stack[0]; + +#ifdef DEBUG_CONVEX_HULL + printf("Removing part\n"); +#endif +#ifdef SHOW_ITERATIONS + n = 0; +#endif + int pos = 0; + while (pos < stack.size()) { + int end = stack.size(); + while (pos < end) { + Vertex* kept = stack[pos++]; +#ifdef DEBUG_CONVEX_HULL + kept->print(); +#endif + bool deeper = false; + Vertex* removed; + while ((removed = stack[pos++]) != NULL) { +#ifdef SHOW_ITERATIONS + n++; +#endif + kept->receiveNearbyFaces(removed); + while (removed->edges) { + if (!deeper) { + deeper = true; + stack.push_back(kept); + } + stack.push_back(removed->edges->target); + removeEdgePair(removed->edges); + } + } + if (deeper) { + stack.push_back(NULL); + } + } + } +#ifdef SHOW_ITERATIONS + printf("Needed %d iterations to remove part\n", n); +#endif + + stack.resize(0); + face->origin = shiftedOrigin; + + return true; +} + +static int getVertexCopy(btConvexHullInternal::Vertex* vertex, btAlignedObjectArray& vertices) +{ + int index = vertex->copy; + if (index < 0) { + index = vertices.size(); + vertex->copy = index; + vertices.push_back(vertex); +#ifdef DEBUG_CONVEX_HULL + printf("Vertex %d gets index *%d\n", vertex->point.index, index); +#endif + } + return index; +} + +btScalar btConvexHullComputer::compute(const void* coords, bool doubleCoords, int stride, int count, btScalar shrink, btScalar shrinkClamp) +{ + if (count <= 0) { + vertices.clear(); + edges.clear(); + faces.clear(); + return 0; + } + + btConvexHullInternal hull; + hull.compute(coords, doubleCoords, stride, count); + + btScalar shift = 0; + if ((shrink > 0) && ((shift = hull.shrink(shrink, shrinkClamp)) < 0)) { + vertices.clear(); + edges.clear(); + faces.clear(); + return shift; + } + + vertices.resize(0); + edges.resize(0); + faces.resize(0); + + btAlignedObjectArray oldVertices; + getVertexCopy(hull.vertexList, oldVertices); + int copied = 0; + while (copied < oldVertices.size()) { + btConvexHullInternal::Vertex* v = oldVertices[copied]; + vertices.push_back(hull.getCoordinates(v)); + btConvexHullInternal::Edge* firstEdge = v->edges; + if (firstEdge) { + int firstCopy = -1; + int prevCopy = -1; + btConvexHullInternal::Edge* e = firstEdge; + do { + if (e->copy < 0) { + int s = edges.size(); + edges.push_back(Edge()); + edges.push_back(Edge()); + Edge* c = &edges[s]; + Edge* r = &edges[s + 1]; + e->copy = s; + e->reverse->copy = s + 1; + c->reverse = 1; + r->reverse = -1; + c->targetVertex = getVertexCopy(e->target, oldVertices); + r->targetVertex = copied; +#ifdef DEBUG_CONVEX_HULL + printf(" CREATE: Vertex *%d has edge to *%d\n", copied, c->getTargetVertex()); +#endif + } + if (prevCopy >= 0) { + edges[e->copy].next = prevCopy - e->copy; + } + else { + firstCopy = e->copy; + } + prevCopy = e->copy; + e = e->next; + } while (e != firstEdge); + edges[firstCopy].next = prevCopy - firstCopy; + } + copied++; + } + + for (int i = 0; i < copied; i++) { + btConvexHullInternal::Vertex* v = oldVertices[i]; + btConvexHullInternal::Edge* firstEdge = v->edges; + if (firstEdge) { + btConvexHullInternal::Edge* e = firstEdge; + do { + if (e->copy >= 0) { +#ifdef DEBUG_CONVEX_HULL + printf("Vertex *%d has edge to *%d\n", i, edges[e->copy].getTargetVertex()); +#endif + faces.push_back(e->copy); + btConvexHullInternal::Edge* f = e; + do { +#ifdef DEBUG_CONVEX_HULL + printf(" Face *%d\n", edges[f->copy].getTargetVertex()); +#endif + f->copy = -1; + f = f->reverse->prev; + } while (f != e); + } + e = e->next; + } while (e != firstEdge); + } + } + + return shift; +} diff --git a/Extras/VHACD/src/premake4.lua b/Extras/VHACD/src/premake4.lua new file mode 100644 index 000000000..3566943be --- /dev/null +++ b/Extras/VHACD/src/premake4.lua @@ -0,0 +1,10 @@ + project "vhacd" + + kind "StaticLib" + includedirs { + "../inc","../public", + } + files { + "*.cpp", + "*.h" + } diff --git a/Extras/VHACD/src/vhacdICHull.cpp b/Extras/VHACD/src/vhacdICHull.cpp new file mode 100644 index 000000000..4216f0599 --- /dev/null +++ b/Extras/VHACD/src/vhacdICHull.cpp @@ -0,0 +1,725 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "vhacdICHull.h" +#include +namespace VHACD { +const double ICHull::sc_eps = 1.0e-15; +const int ICHull::sc_dummyIndex = std::numeric_limits::max(); +ICHull::ICHull() +{ + m_isFlat = false; +} +bool ICHull::AddPoints(const Vec3* points, size_t nPoints) +{ + if (!points) { + return false; + } + CircularListElement* vertex = NULL; + for (size_t i = 0; i < nPoints; i++) { + vertex = m_mesh.AddVertex(); + vertex->GetData().m_pos.X() = points[i].X(); + vertex->GetData().m_pos.Y() = points[i].Y(); + vertex->GetData().m_pos.Z() = points[i].Z(); + vertex->GetData().m_name = static_cast(i); + } + return true; +} +bool ICHull::AddPoint(const Vec3& point, int id) +{ + if (AddPoints(&point, 1)) { + m_mesh.m_vertices.GetData().m_name = id; + return true; + } + return false; +} + +ICHullError ICHull::Process() +{ + unsigned int addedPoints = 0; + if (m_mesh.GetNVertices() < 3) { + return ICHullErrorNotEnoughPoints; + } + if (m_mesh.GetNVertices() == 3) { + m_isFlat = true; + CircularListElement* t1 = m_mesh.AddTriangle(); + CircularListElement* t2 = m_mesh.AddTriangle(); + CircularListElement* v0 = m_mesh.m_vertices.GetHead(); + CircularListElement* v1 = v0->GetNext(); + CircularListElement* v2 = v1->GetNext(); + // Compute the normal to the plane + Vec3 p0 = v0->GetData().m_pos; + Vec3 p1 = v1->GetData().m_pos; + Vec3 p2 = v2->GetData().m_pos; + m_normal = (p1 - p0) ^ (p2 - p0); + m_normal.Normalize(); + t1->GetData().m_vertices[0] = v0; + t1->GetData().m_vertices[1] = v1; + t1->GetData().m_vertices[2] = v2; + t2->GetData().m_vertices[0] = v1; + t2->GetData().m_vertices[1] = v2; + t2->GetData().m_vertices[2] = v2; + return ICHullErrorOK; + } + if (m_isFlat) { + m_mesh.m_edges.Clear(); + m_mesh.m_triangles.Clear(); + m_isFlat = false; + } + if (m_mesh.GetNTriangles() == 0) // we have to create the first polyhedron + { + ICHullError res = DoubleTriangle(); + if (res != ICHullErrorOK) { + return res; + } + else { + addedPoints += 3; + } + } + CircularList& vertices = m_mesh.GetVertices(); + // go to the first added and not processed vertex + while (!(vertices.GetHead()->GetPrev()->GetData().m_tag)) { + vertices.Prev(); + } + while (!vertices.GetData().m_tag) // not processed + { + vertices.GetData().m_tag = true; + if (ProcessPoint()) { + addedPoints++; + CleanUp(addedPoints); + vertices.Next(); + if (!GetMesh().CheckConsistancy()) { + size_t nV = m_mesh.GetNVertices(); + CircularList& vertices = m_mesh.GetVertices(); + for (size_t v = 0; v < nV; ++v) { + if (vertices.GetData().m_name == sc_dummyIndex) { + vertices.Delete(); + break; + } + vertices.Next(); + } + return ICHullErrorInconsistent; + } + } + } + if (m_isFlat) { + SArray*> trianglesToDuplicate; + size_t nT = m_mesh.GetNTriangles(); + for (size_t f = 0; f < nT; f++) { + TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData(); + if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex) { + m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead()); + for (int k = 0; k < 3; k++) { + for (int h = 0; h < 2; h++) { + if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead()) { + currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0; + break; + } + } + } + } + else { + trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead()); + } + m_mesh.m_triangles.Next(); + } + size_t nE = m_mesh.GetNEdges(); + for (size_t e = 0; e < nE; e++) { + TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData(); + if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0) { + m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead()); + } + m_mesh.m_edges.Next(); + } + size_t nV = m_mesh.GetNVertices(); + CircularList& vertices = m_mesh.GetVertices(); + for (size_t v = 0; v < nV; ++v) { + if (vertices.GetData().m_name == sc_dummyIndex) { + vertices.Delete(); + } + else { + vertices.GetData().m_tag = false; + vertices.Next(); + } + } + CleanEdges(); + CleanTriangles(); + CircularListElement* newTriangle; + for (size_t t = 0; t < trianglesToDuplicate.Size(); t++) { + newTriangle = m_mesh.AddTriangle(); + newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1]; + newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0]; + newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2]; + } + } + return ICHullErrorOK; +} +ICHullError ICHull::Process(const unsigned int nPointsCH, + const double minVolume) +{ + unsigned int addedPoints = 0; + if (nPointsCH < 3 || m_mesh.GetNVertices() < 3) { + return ICHullErrorNotEnoughPoints; + } + if (m_mesh.GetNVertices() == 3) { + m_isFlat = true; + CircularListElement* t1 = m_mesh.AddTriangle(); + CircularListElement* t2 = m_mesh.AddTriangle(); + CircularListElement* v0 = m_mesh.m_vertices.GetHead(); + CircularListElement* v1 = v0->GetNext(); + CircularListElement* v2 = v1->GetNext(); + // Compute the normal to the plane + Vec3 p0 = v0->GetData().m_pos; + Vec3 p1 = v1->GetData().m_pos; + Vec3 p2 = v2->GetData().m_pos; + m_normal = (p1 - p0) ^ (p2 - p0); + m_normal.Normalize(); + t1->GetData().m_vertices[0] = v0; + t1->GetData().m_vertices[1] = v1; + t1->GetData().m_vertices[2] = v2; + t2->GetData().m_vertices[0] = v1; + t2->GetData().m_vertices[1] = v0; + t2->GetData().m_vertices[2] = v2; + return ICHullErrorOK; + } + + if (m_isFlat) { + m_mesh.m_triangles.Clear(); + m_mesh.m_edges.Clear(); + m_isFlat = false; + } + + if (m_mesh.GetNTriangles() == 0) // we have to create the first polyhedron + { + ICHullError res = DoubleTriangle(); + if (res != ICHullErrorOK) { + return res; + } + else { + addedPoints += 3; + } + } + CircularList& vertices = m_mesh.GetVertices(); + while (!vertices.GetData().m_tag && addedPoints < nPointsCH) // not processed + { + if (!FindMaxVolumePoint((addedPoints > 4) ? minVolume : 0.0)) { + break; + } + vertices.GetData().m_tag = true; + if (ProcessPoint()) { + addedPoints++; + CleanUp(addedPoints); + if (!GetMesh().CheckConsistancy()) { + size_t nV = m_mesh.GetNVertices(); + CircularList& vertices = m_mesh.GetVertices(); + for (size_t v = 0; v < nV; ++v) { + if (vertices.GetData().m_name == sc_dummyIndex) { + vertices.Delete(); + break; + } + vertices.Next(); + } + return ICHullErrorInconsistent; + } + vertices.Next(); + } + } + // delete remaining points + while (!vertices.GetData().m_tag) { + vertices.Delete(); + } + if (m_isFlat) { + SArray*> trianglesToDuplicate; + size_t nT = m_mesh.GetNTriangles(); + for (size_t f = 0; f < nT; f++) { + TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData(); + if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex) { + m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead()); + for (int k = 0; k < 3; k++) { + for (int h = 0; h < 2; h++) { + if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead()) { + currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0; + break; + } + } + } + } + else { + trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead()); + } + m_mesh.m_triangles.Next(); + } + size_t nE = m_mesh.GetNEdges(); + for (size_t e = 0; e < nE; e++) { + TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData(); + if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0) { + m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead()); + } + m_mesh.m_edges.Next(); + } + size_t nV = m_mesh.GetNVertices(); + CircularList& vertices = m_mesh.GetVertices(); + for (size_t v = 0; v < nV; ++v) { + if (vertices.GetData().m_name == sc_dummyIndex) { + vertices.Delete(); + } + else { + vertices.GetData().m_tag = false; + vertices.Next(); + } + } + CleanEdges(); + CleanTriangles(); + CircularListElement* newTriangle; + for (size_t t = 0; t < trianglesToDuplicate.Size(); t++) { + newTriangle = m_mesh.AddTriangle(); + newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1]; + newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0]; + newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2]; + } + } + return ICHullErrorOK; +} +bool ICHull::FindMaxVolumePoint(const double minVolume) +{ + CircularList& vertices = m_mesh.GetVertices(); + CircularListElement* vMaxVolume = 0; + CircularListElement* vHeadPrev = vertices.GetHead()->GetPrev(); + + double maxVolume = minVolume; + double volume = 0.0; + while (!vertices.GetData().m_tag) // not processed + { + if (ComputePointVolume(volume, false)) { + if (maxVolume < volume) { + maxVolume = volume; + vMaxVolume = vertices.GetHead(); + } + vertices.Next(); + } + } + CircularListElement* vHead = vHeadPrev->GetNext(); + vertices.GetHead() = vHead; + if (!vMaxVolume) { + return false; + } + if (vMaxVolume != vHead) { + Vec3 pos = vHead->GetData().m_pos; + int id = vHead->GetData().m_name; + vHead->GetData().m_pos = vMaxVolume->GetData().m_pos; + vHead->GetData().m_name = vMaxVolume->GetData().m_name; + vMaxVolume->GetData().m_pos = pos; + vHead->GetData().m_name = id; + } + return true; +} +ICHullError ICHull::DoubleTriangle() +{ + // find three non colinear points + m_isFlat = false; + CircularList& vertices = m_mesh.GetVertices(); + CircularListElement* v0 = vertices.GetHead(); + while (Colinear(v0->GetData().m_pos, + v0->GetNext()->GetData().m_pos, + v0->GetNext()->GetNext()->GetData().m_pos)) { + if ((v0 = v0->GetNext()) == vertices.GetHead()) { + return ICHullErrorCoplanarPoints; + } + } + CircularListElement* v1 = v0->GetNext(); + CircularListElement* v2 = v1->GetNext(); + // mark points as processed + v0->GetData().m_tag = v1->GetData().m_tag = v2->GetData().m_tag = true; + + // create two triangles + CircularListElement* f0 = MakeFace(v0, v1, v2, 0); + MakeFace(v2, v1, v0, f0); + + // find a fourth non-coplanar point to form tetrahedron + CircularListElement* v3 = v2->GetNext(); + vertices.GetHead() = v3; + + double vol = ComputeVolume4(v0->GetData().m_pos, v1->GetData().m_pos, v2->GetData().m_pos, v3->GetData().m_pos); + while (fabs(vol) < sc_eps && !v3->GetNext()->GetData().m_tag) { + v3 = v3->GetNext(); + vol = ComputeVolume4(v0->GetData().m_pos, v1->GetData().m_pos, v2->GetData().m_pos, v3->GetData().m_pos); + } + if (fabs(vol) < sc_eps) { + // compute the barycenter + Vec3 bary(0.0, 0.0, 0.0); + CircularListElement* vBary = v0; + do { + bary += vBary->GetData().m_pos; + } while ((vBary = vBary->GetNext()) != v0); + bary /= static_cast(vertices.GetSize()); + + // Compute the normal to the plane + Vec3 p0 = v0->GetData().m_pos; + Vec3 p1 = v1->GetData().m_pos; + Vec3 p2 = v2->GetData().m_pos; + m_normal = (p1 - p0) ^ (p2 - p0); + m_normal.Normalize(); + // add dummy vertex placed at (bary + normal) + vertices.GetHead() = v2; + Vec3 newPt = bary + m_normal; + AddPoint(newPt, sc_dummyIndex); + m_isFlat = true; + return ICHullErrorOK; + } + else if (v3 != vertices.GetHead()) { + TMMVertex temp; + temp.m_name = v3->GetData().m_name; + temp.m_pos = v3->GetData().m_pos; + v3->GetData().m_name = vertices.GetHead()->GetData().m_name; + v3->GetData().m_pos = vertices.GetHead()->GetData().m_pos; + vertices.GetHead()->GetData().m_name = temp.m_name; + vertices.GetHead()->GetData().m_pos = temp.m_pos; + } + return ICHullErrorOK; +} +CircularListElement* ICHull::MakeFace(CircularListElement* v0, + CircularListElement* v1, + CircularListElement* v2, + CircularListElement* fold) +{ + CircularListElement* e0; + CircularListElement* e1; + CircularListElement* e2; + int index = 0; + if (!fold) // if first face to be created + { + e0 = m_mesh.AddEdge(); // create the three edges + e1 = m_mesh.AddEdge(); + e2 = m_mesh.AddEdge(); + } + else // otherwise re-use existing edges (in reverse order) + { + e0 = fold->GetData().m_edges[2]; + e1 = fold->GetData().m_edges[1]; + e2 = fold->GetData().m_edges[0]; + index = 1; + } + e0->GetData().m_vertices[0] = v0; + e0->GetData().m_vertices[1] = v1; + e1->GetData().m_vertices[0] = v1; + e1->GetData().m_vertices[1] = v2; + e2->GetData().m_vertices[0] = v2; + e2->GetData().m_vertices[1] = v0; + // create the new face + CircularListElement* f = m_mesh.AddTriangle(); + f->GetData().m_edges[0] = e0; + f->GetData().m_edges[1] = e1; + f->GetData().m_edges[2] = e2; + f->GetData().m_vertices[0] = v0; + f->GetData().m_vertices[1] = v1; + f->GetData().m_vertices[2] = v2; + // link edges to face f + e0->GetData().m_triangles[index] = e1->GetData().m_triangles[index] = e2->GetData().m_triangles[index] = f; + return f; +} +CircularListElement* ICHull::MakeConeFace(CircularListElement* e, CircularListElement* p) +{ + // create two new edges if they don't already exist + CircularListElement* newEdges[2]; + for (int i = 0; i < 2; ++i) { + if (!(newEdges[i] = e->GetData().m_vertices[i]->GetData().m_duplicate)) { // if the edge doesn't exits add it and mark the vertex as duplicated + newEdges[i] = m_mesh.AddEdge(); + newEdges[i]->GetData().m_vertices[0] = e->GetData().m_vertices[i]; + newEdges[i]->GetData().m_vertices[1] = p; + e->GetData().m_vertices[i]->GetData().m_duplicate = newEdges[i]; + } + } + // make the new face + CircularListElement* newFace = m_mesh.AddTriangle(); + newFace->GetData().m_edges[0] = e; + newFace->GetData().m_edges[1] = newEdges[0]; + newFace->GetData().m_edges[2] = newEdges[1]; + MakeCCW(newFace, e, p); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + if (!newEdges[i]->GetData().m_triangles[j]) { + newEdges[i]->GetData().m_triangles[j] = newFace; + break; + } + } + } + return newFace; +} +bool ICHull::ComputePointVolume(double& totalVolume, bool markVisibleFaces) +{ + // mark visible faces + CircularListElement* fHead = m_mesh.GetTriangles().GetHead(); + CircularListElement* f = fHead; + CircularList& vertices = m_mesh.GetVertices(); + CircularListElement* vertex0 = vertices.GetHead(); + bool visible = false; + Vec3 pos0 = Vec3(vertex0->GetData().m_pos.X(), + vertex0->GetData().m_pos.Y(), + vertex0->GetData().m_pos.Z()); + double vol = 0.0; + totalVolume = 0.0; + Vec3 ver0, ver1, ver2; + do { + ver0.X() = f->GetData().m_vertices[0]->GetData().m_pos.X(); + ver0.Y() = f->GetData().m_vertices[0]->GetData().m_pos.Y(); + ver0.Z() = f->GetData().m_vertices[0]->GetData().m_pos.Z(); + ver1.X() = f->GetData().m_vertices[1]->GetData().m_pos.X(); + ver1.Y() = f->GetData().m_vertices[1]->GetData().m_pos.Y(); + ver1.Z() = f->GetData().m_vertices[1]->GetData().m_pos.Z(); + ver2.X() = f->GetData().m_vertices[2]->GetData().m_pos.X(); + ver2.Y() = f->GetData().m_vertices[2]->GetData().m_pos.Y(); + ver2.Z() = f->GetData().m_vertices[2]->GetData().m_pos.Z(); + vol = ComputeVolume4(ver0, ver1, ver2, pos0); + if (vol < -sc_eps) { + vol = fabs(vol); + totalVolume += vol; + if (markVisibleFaces) { + f->GetData().m_visible = true; + m_trianglesToDelete.PushBack(f); + } + visible = true; + } + f = f->GetNext(); + } while (f != fHead); + + if (m_trianglesToDelete.Size() == m_mesh.m_triangles.GetSize()) { + for (size_t i = 0; i < m_trianglesToDelete.Size(); i++) { + m_trianglesToDelete[i]->GetData().m_visible = false; + } + visible = false; + } + // if no faces visible from p then p is inside the hull + if (!visible && markVisibleFaces) { + vertices.Delete(); + m_trianglesToDelete.Resize(0); + return false; + } + return true; +} +bool ICHull::ProcessPoint() +{ + double totalVolume = 0.0; + if (!ComputePointVolume(totalVolume, true)) { + return false; + } + // Mark edges in interior of visible region for deletion. + // Create a new face based on each border edge + CircularListElement* v0 = m_mesh.GetVertices().GetHead(); + CircularListElement* eHead = m_mesh.GetEdges().GetHead(); + CircularListElement* e = eHead; + CircularListElement* tmp = 0; + int nvisible = 0; + m_edgesToDelete.Resize(0); + m_edgesToUpdate.Resize(0); + do { + tmp = e->GetNext(); + nvisible = 0; + for (int k = 0; k < 2; k++) { + if (e->GetData().m_triangles[k]->GetData().m_visible) { + nvisible++; + } + } + if (nvisible == 2) { + m_edgesToDelete.PushBack(e); + } + else if (nvisible == 1) { + e->GetData().m_newFace = MakeConeFace(e, v0); + m_edgesToUpdate.PushBack(e); + } + e = tmp; + } while (e != eHead); + return true; +} +bool ICHull::MakeCCW(CircularListElement* f, + CircularListElement* e, + CircularListElement* v) +{ + // the visible face adjacent to e + CircularListElement* fv; + if (e->GetData().m_triangles[0]->GetData().m_visible) { + fv = e->GetData().m_triangles[0]; + } + else { + fv = e->GetData().m_triangles[1]; + } + + // set vertex[0] and vertex[1] to have the same orientation as the corresponding vertices of fv. + int i; // index of e->m_vertices[0] in fv + CircularListElement* v0 = e->GetData().m_vertices[0]; + CircularListElement* v1 = e->GetData().m_vertices[1]; + for (i = 0; fv->GetData().m_vertices[i] != v0; i++) + ; + + if (fv->GetData().m_vertices[(i + 1) % 3] != e->GetData().m_vertices[1]) { + f->GetData().m_vertices[0] = v1; + f->GetData().m_vertices[1] = v0; + } + else { + f->GetData().m_vertices[0] = v0; + f->GetData().m_vertices[1] = v1; + // swap edges + CircularListElement* tmp = f->GetData().m_edges[0]; + f->GetData().m_edges[0] = f->GetData().m_edges[1]; + f->GetData().m_edges[1] = tmp; + } + f->GetData().m_vertices[2] = v; + return true; +} +bool ICHull::CleanUp(unsigned int& addedPoints) +{ + bool r0 = CleanEdges(); + bool r1 = CleanTriangles(); + bool r2 = CleanVertices(addedPoints); + return r0 && r1 && r2; +} +bool ICHull::CleanEdges() +{ + // integrate the new faces into the data structure + CircularListElement* e; + const size_t ne_update = m_edgesToUpdate.Size(); + for (size_t i = 0; i < ne_update; ++i) { + e = m_edgesToUpdate[i]; + if (e->GetData().m_newFace) { + if (e->GetData().m_triangles[0]->GetData().m_visible) { + e->GetData().m_triangles[0] = e->GetData().m_newFace; + } + else { + e->GetData().m_triangles[1] = e->GetData().m_newFace; + } + e->GetData().m_newFace = 0; + } + } + // delete edges maked for deletion + CircularList& edges = m_mesh.GetEdges(); + const size_t ne_delete = m_edgesToDelete.Size(); + for (size_t i = 0; i < ne_delete; ++i) { + edges.Delete(m_edgesToDelete[i]); + } + m_edgesToDelete.Resize(0); + m_edgesToUpdate.Resize(0); + return true; +} +bool ICHull::CleanTriangles() +{ + CircularList& triangles = m_mesh.GetTriangles(); + const size_t nt_delete = m_trianglesToDelete.Size(); + for (size_t i = 0; i < nt_delete; ++i) { + triangles.Delete(m_trianglesToDelete[i]); + } + m_trianglesToDelete.Resize(0); + return true; +} +bool ICHull::CleanVertices(unsigned int& addedPoints) +{ + // mark all vertices incident to some undeleted edge as on the hull + CircularList& edges = m_mesh.GetEdges(); + CircularListElement* e = edges.GetHead(); + size_t nE = edges.GetSize(); + for (size_t i = 0; i < nE; i++) { + e->GetData().m_vertices[0]->GetData().m_onHull = true; + e->GetData().m_vertices[1]->GetData().m_onHull = true; + e = e->GetNext(); + } + // delete all the vertices that have been processed but are not on the hull + CircularList& vertices = m_mesh.GetVertices(); + CircularListElement* vHead = vertices.GetHead(); + CircularListElement* v = vHead; + v = v->GetPrev(); + do { + if (v->GetData().m_tag && !v->GetData().m_onHull) { + CircularListElement* tmp = v->GetPrev(); + vertices.Delete(v); + v = tmp; + addedPoints--; + } + else { + v->GetData().m_duplicate = 0; + v->GetData().m_onHull = false; + v = v->GetPrev(); + } + } while (v->GetData().m_tag && v != vHead); + return true; +} +void ICHull::Clear() +{ + m_mesh.Clear(); + m_edgesToDelete.Resize(0); + m_edgesToUpdate.Resize(0); + m_trianglesToDelete.Resize(0); + m_isFlat = false; +} +const ICHull& ICHull::operator=(ICHull& rhs) +{ + if (&rhs != this) { + m_mesh.Copy(rhs.m_mesh); + m_edgesToDelete = rhs.m_edgesToDelete; + m_edgesToUpdate = rhs.m_edgesToUpdate; + m_trianglesToDelete = rhs.m_trianglesToDelete; + m_isFlat = rhs.m_isFlat; + } + return (*this); +} +bool ICHull::IsInside(const Vec3& pt0, const double eps) +{ + const Vec3 pt(pt0.X(), pt0.Y(), pt0.Z()); + if (m_isFlat) { + size_t nT = m_mesh.m_triangles.GetSize(); + Vec3 ver0, ver1, ver2, a, b, c; + double u, v; + for (size_t t = 0; t < nT; t++) { + ver0.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.X(); + ver0.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Y(); + ver0.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Z(); + ver1.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.X(); + ver1.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Y(); + ver1.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Z(); + ver2.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.X(); + ver2.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Y(); + ver2.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Z(); + a = ver1 - ver0; + b = ver2 - ver0; + c = pt - ver0; + u = c * a; + v = c * b; + if (u >= 0.0 && u <= 1.0 && v >= 0.0 && u + v <= 1.0) { + return true; + } + m_mesh.m_triangles.Next(); + } + return false; + } + else { + size_t nT = m_mesh.m_triangles.GetSize(); + Vec3 ver0, ver1, ver2; + double vol; + for (size_t t = 0; t < nT; t++) { + ver0.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.X(); + ver0.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Y(); + ver0.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Z(); + ver1.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.X(); + ver1.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Y(); + ver1.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Z(); + ver2.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.X(); + ver2.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Y(); + ver2.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Z(); + vol = ComputeVolume4(ver0, ver1, ver2, pt); + if (vol < eps) { + return false; + } + m_mesh.m_triangles.Next(); + } + return true; + } +} +} diff --git a/Extras/VHACD/src/vhacdManifoldMesh.cpp b/Extras/VHACD/src/vhacdManifoldMesh.cpp new file mode 100644 index 000000000..dffa0244e --- /dev/null +++ b/Extras/VHACD/src/vhacdManifoldMesh.cpp @@ -0,0 +1,202 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "vhacdManifoldMesh.h" +namespace VHACD { +TMMVertex::TMMVertex(void) +{ + Initialize(); +} +void TMMVertex::Initialize() +{ + m_name = 0; + m_id = 0; + m_duplicate = 0; + m_onHull = false; + m_tag = false; +} + +TMMVertex::~TMMVertex(void) +{ +} +TMMEdge::TMMEdge(void) +{ + Initialize(); +} +void TMMEdge::Initialize() +{ + m_id = 0; + m_triangles[0] = m_triangles[1] = m_newFace = 0; + m_vertices[0] = m_vertices[1] = 0; +} +TMMEdge::~TMMEdge(void) +{ +} +void TMMTriangle::Initialize() +{ + m_id = 0; + for (int i = 0; i < 3; i++) { + m_edges[i] = 0; + m_vertices[0] = 0; + } + m_visible = false; +} +TMMTriangle::TMMTriangle(void) +{ + Initialize(); +} +TMMTriangle::~TMMTriangle(void) +{ +} +TMMesh::TMMesh() +{ +} +TMMesh::~TMMesh(void) +{ +} +void TMMesh::GetIFS(Vec3* const points, Vec3* const triangles) +{ + size_t nV = m_vertices.GetSize(); + size_t nT = m_triangles.GetSize(); + + for (size_t v = 0; v < nV; v++) { + points[v] = m_vertices.GetData().m_pos; + m_vertices.GetData().m_id = v; + m_vertices.Next(); + } + for (size_t f = 0; f < nT; f++) { + TMMTriangle& currentTriangle = m_triangles.GetData(); + triangles[f].X() = static_cast(currentTriangle.m_vertices[0]->GetData().m_id); + triangles[f].Y() = static_cast(currentTriangle.m_vertices[1]->GetData().m_id); + triangles[f].Z() = static_cast(currentTriangle.m_vertices[2]->GetData().m_id); + m_triangles.Next(); + } +} +void TMMesh::Clear() +{ + m_vertices.Clear(); + m_edges.Clear(); + m_triangles.Clear(); +} +void TMMesh::Copy(TMMesh& mesh) +{ + Clear(); + // updating the id's + size_t nV = mesh.m_vertices.GetSize(); + size_t nE = mesh.m_edges.GetSize(); + size_t nT = mesh.m_triangles.GetSize(); + for (size_t v = 0; v < nV; v++) { + mesh.m_vertices.GetData().m_id = v; + mesh.m_vertices.Next(); + } + for (size_t e = 0; e < nE; e++) { + mesh.m_edges.GetData().m_id = e; + mesh.m_edges.Next(); + } + for (size_t f = 0; f < nT; f++) { + mesh.m_triangles.GetData().m_id = f; + mesh.m_triangles.Next(); + } + // copying data + m_vertices = mesh.m_vertices; + m_edges = mesh.m_edges; + m_triangles = mesh.m_triangles; + + // generate mapping + CircularListElement** vertexMap = new CircularListElement*[nV]; + CircularListElement** edgeMap = new CircularListElement*[nE]; + CircularListElement** triangleMap = new CircularListElement*[nT]; + for (size_t v = 0; v < nV; v++) { + vertexMap[v] = m_vertices.GetHead(); + m_vertices.Next(); + } + for (size_t e = 0; e < nE; e++) { + edgeMap[e] = m_edges.GetHead(); + m_edges.Next(); + } + for (size_t f = 0; f < nT; f++) { + triangleMap[f] = m_triangles.GetHead(); + m_triangles.Next(); + } + + // updating pointers + for (size_t v = 0; v < nV; v++) { + if (vertexMap[v]->GetData().m_duplicate) { + vertexMap[v]->GetData().m_duplicate = edgeMap[vertexMap[v]->GetData().m_duplicate->GetData().m_id]; + } + } + for (size_t e = 0; e < nE; e++) { + if (edgeMap[e]->GetData().m_newFace) { + edgeMap[e]->GetData().m_newFace = triangleMap[edgeMap[e]->GetData().m_newFace->GetData().m_id]; + } + if (nT > 0) { + for (int f = 0; f < 2; f++) { + if (edgeMap[e]->GetData().m_triangles[f]) { + edgeMap[e]->GetData().m_triangles[f] = triangleMap[edgeMap[e]->GetData().m_triangles[f]->GetData().m_id]; + } + } + } + for (int v = 0; v < 2; v++) { + if (edgeMap[e]->GetData().m_vertices[v]) { + edgeMap[e]->GetData().m_vertices[v] = vertexMap[edgeMap[e]->GetData().m_vertices[v]->GetData().m_id]; + } + } + } + for (size_t f = 0; f < nT; f++) { + if (nE > 0) { + for (int e = 0; e < 3; e++) { + if (triangleMap[f]->GetData().m_edges[e]) { + triangleMap[f]->GetData().m_edges[e] = edgeMap[triangleMap[f]->GetData().m_edges[e]->GetData().m_id]; + } + } + } + for (int v = 0; v < 3; v++) { + if (triangleMap[f]->GetData().m_vertices[v]) { + triangleMap[f]->GetData().m_vertices[v] = vertexMap[triangleMap[f]->GetData().m_vertices[v]->GetData().m_id]; + } + } + } + delete[] vertexMap; + delete[] edgeMap; + delete[] triangleMap; +} +bool TMMesh::CheckConsistancy() +{ + size_t nE = m_edges.GetSize(); + size_t nT = m_triangles.GetSize(); + for (size_t e = 0; e < nE; e++) { + for (int f = 0; f < 2; f++) { + if (!m_edges.GetHead()->GetData().m_triangles[f]) { + return false; + } + } + m_edges.Next(); + } + for (size_t f = 0; f < nT; f++) { + for (int e = 0; e < 3; e++) { + int found = 0; + for (int k = 0; k < 2; k++) { + if (m_triangles.GetHead()->GetData().m_edges[e]->GetData().m_triangles[k] == m_triangles.GetHead()) { + found++; + } + } + if (found != 1) { + return false; + } + } + m_triangles.Next(); + } + return true; +} +} \ No newline at end of file diff --git a/Extras/VHACD/src/vhacdMesh.cpp b/Extras/VHACD/src/vhacdMesh.cpp new file mode 100644 index 000000000..542ce430b --- /dev/null +++ b/Extras/VHACD/src/vhacdMesh.cpp @@ -0,0 +1,323 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define _CRT_SECURE_NO_WARNINGS + +#include "btConvexHullComputer.h" +#include "vhacdMesh.h" +#include +#include +#include +#include +#include +#include + +namespace VHACD { +Mesh::Mesh() +{ + m_diag = 1.0; +} +Mesh::~Mesh() +{ +} +double Mesh::ComputeVolume() const +{ + const size_t nV = GetNPoints(); + const size_t nT = GetNTriangles(); + if (nV == 0 || nT == 0) { + return 0.0; + } + + Vec3 bary(0.0, 0.0, 0.0); + for (size_t v = 0; v < nV; v++) { + bary += GetPoint(v); + } + bary /= static_cast(nV); + + Vec3 ver0, ver1, ver2; + double totalVolume = 0.0; + for (int t = 0; t < nT; t++) { + const Vec3& tri = GetTriangle(t); + ver0 = GetPoint(tri[0]); + ver1 = GetPoint(tri[1]); + ver2 = GetPoint(tri[2]); + totalVolume += ComputeVolume4(ver0, ver1, ver2, bary); + } + return totalVolume / 6.0; +} + +void Mesh::ComputeConvexHull(const double* const pts, + const size_t nPts) +{ + ResizePoints(0); + ResizeTriangles(0); + btConvexHullComputer ch; + ch.compute(pts, 3 * sizeof(double), (int)nPts, -1.0, -1.0); + for (int v = 0; v < ch.vertices.size(); v++) { + AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int nt = ch.faces.size(); + for (int t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int a = sourceEdge->getSourceVertex(); + int b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int c = edge->getTargetVertex(); + while (c != a) { + AddTriangle(Vec3(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } +} +void Mesh::Clip(const Plane& plane, + SArray >& positivePart, + SArray >& negativePart) const +{ + const size_t nV = GetNPoints(); + if (nV == 0) { + return; + } + double d; + for (size_t v = 0; v < nV; v++) { + const Vec3& pt = GetPoint(v); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + if (d > 0.0) { + positivePart.PushBack(pt); + } + else if (d < 0.0) { + negativePart.PushBack(pt); + } + else { + positivePart.PushBack(pt); + negativePart.PushBack(pt); + } + } +} +bool Mesh::IsInside(const Vec3& pt) const +{ + const size_t nV = GetNPoints(); + const size_t nT = GetNTriangles(); + if (nV == 0 || nT == 0) { + return false; + } + Vec3 ver0, ver1, ver2; + double volume; + for (int t = 0; t < nT; t++) { + const Vec3& tri = GetTriangle(t); + ver0 = GetPoint(tri[0]); + ver1 = GetPoint(tri[1]); + ver2 = GetPoint(tri[2]); + volume = ComputeVolume4(ver0, ver1, ver2, pt); + if (volume < 0.0) { + return false; + } + } + return true; +} +double Mesh::ComputeDiagBB() +{ + const size_t nPoints = GetNPoints(); + if (nPoints == 0) + return 0.0; + Vec3 minBB = m_points[0]; + Vec3 maxBB = m_points[0]; + double x, y, z; + for (size_t v = 1; v < nPoints; v++) { + x = m_points[v][0]; + y = m_points[v][1]; + z = m_points[v][2]; + if (x < minBB[0]) + minBB[0] = x; + else if (x > maxBB[0]) + maxBB[0] = x; + if (y < minBB[1]) + minBB[1] = y; + else if (y > maxBB[1]) + maxBB[1] = y; + if (z < minBB[2]) + minBB[2] = z; + else if (z > maxBB[2]) + maxBB[2] = z; + } + return (m_diag = (maxBB - minBB).GetNorm()); +} + +#ifdef VHACD_DEBUG_MESH +bool Mesh::SaveVRML2(const std::string& fileName) const +{ + std::ofstream fout(fileName.c_str()); + if (fout.is_open()) { + const Material material; + + if (SaveVRML2(fout, material)) { + fout.close(); + return true; + } + return false; + } + return false; +} +bool Mesh::SaveVRML2(std::ofstream& fout, const Material& material) const +{ + if (fout.is_open()) { + fout.setf(std::ios::fixed, std::ios::floatfield); + fout.setf(std::ios::showpoint); + fout.precision(6); + size_t nV = m_points.Size(); + size_t nT = m_triangles.Size(); + fout << "#VRML V2.0 utf8" << std::endl; + fout << "" << std::endl; + fout << "# Vertices: " << nV << std::endl; + fout << "# Triangles: " << nT << std::endl; + fout << "" << std::endl; + fout << "Group {" << std::endl; + fout << " children [" << std::endl; + fout << " Shape {" << std::endl; + fout << " appearance Appearance {" << std::endl; + fout << " material Material {" << std::endl; + fout << " diffuseColor " << material.m_diffuseColor[0] << " " + << material.m_diffuseColor[1] << " " + << material.m_diffuseColor[2] << std::endl; + fout << " ambientIntensity " << material.m_ambientIntensity << std::endl; + fout << " specularColor " << material.m_specularColor[0] << " " + << material.m_specularColor[1] << " " + << material.m_specularColor[2] << std::endl; + fout << " emissiveColor " << material.m_emissiveColor[0] << " " + << material.m_emissiveColor[1] << " " + << material.m_emissiveColor[2] << std::endl; + fout << " shininess " << material.m_shininess << std::endl; + fout << " transparency " << material.m_transparency << std::endl; + fout << " }" << std::endl; + fout << " }" << std::endl; + fout << " geometry IndexedFaceSet {" << std::endl; + fout << " ccw TRUE" << std::endl; + fout << " solid TRUE" << std::endl; + fout << " convex TRUE" << std::endl; + if (nV > 0) { + fout << " coord DEF co Coordinate {" << std::endl; + fout << " point [" << std::endl; + for (size_t v = 0; v < nV; v++) { + fout << " " << m_points[v][0] << " " + << m_points[v][1] << " " + << m_points[v][2] << "," << std::endl; + } + fout << " ]" << std::endl; + fout << " }" << std::endl; + } + if (nT > 0) { + fout << " coordIndex [ " << std::endl; + for (size_t f = 0; f < nT; f++) { + fout << " " << m_triangles[f][0] << ", " + << m_triangles[f][1] << ", " + << m_triangles[f][2] << ", -1," << std::endl; + } + fout << " ]" << std::endl; + } + fout << " }" << std::endl; + fout << " }" << std::endl; + fout << " ]" << std::endl; + fout << "}" << std::endl; + return true; + } + return false; +} +bool Mesh::SaveOFF(const std::string& fileName) const +{ + std::ofstream fout(fileName.c_str()); + if (fout.is_open()) { + size_t nV = m_points.Size(); + size_t nT = m_triangles.Size(); + fout << "OFF" << std::endl; + fout << nV << " " << nT << " " << 0 << std::endl; + for (size_t v = 0; v < nV; v++) { + fout << m_points[v][0] << " " + << m_points[v][1] << " " + << m_points[v][2] << std::endl; + } + for (size_t f = 0; f < nT; f++) { + fout << "3 " << m_triangles[f][0] << " " + << m_triangles[f][1] << " " + << m_triangles[f][2] << std::endl; + } + fout.close(); + return true; + } + return false; +} + +bool Mesh::LoadOFF(const std::string& fileName, bool invert) +{ + FILE* fid = fopen(fileName.c_str(), "r"); + if (fid) { + const std::string strOFF("OFF"); + char temp[1024]; + fscanf(fid, "%s", temp); + if (std::string(temp) != strOFF) { + fclose(fid); + return false; + } + else { + int nv = 0; + int nf = 0; + int ne = 0; + fscanf(fid, "%i", &nv); + fscanf(fid, "%i", &nf); + fscanf(fid, "%i", &ne); + m_points.Resize(nv); + m_triangles.Resize(nf); + Vec3 coord; + float x, y, z; + for (int p = 0; p < nv; p++) { + fscanf(fid, "%f", &x); + fscanf(fid, "%f", &y); + fscanf(fid, "%f", &z); + m_points[p][0] = x; + m_points[p][1] = y; + m_points[p][2] = z; + } + int i, j, k, s; + for (int t = 0; t < nf; ++t) { + fscanf(fid, "%i", &s); + if (s == 3) { + fscanf(fid, "%i", &i); + fscanf(fid, "%i", &j); + fscanf(fid, "%i", &k); + m_triangles[t][0] = i; + if (invert) { + m_triangles[t][1] = k; + m_triangles[t][2] = j; + } + else { + m_triangles[t][1] = j; + m_triangles[t][2] = k; + } + } + else // Fix me: support only triangular meshes + { + for (int h = 0; h < s; ++h) + fscanf(fid, "%i", &s); + } + } + fclose(fid); + } + } + else { + return false; + } + return true; +} +#endif // VHACD_DEBUG_MESH +} diff --git a/Extras/VHACD/src/vhacdVolume.cpp b/Extras/VHACD/src/vhacdVolume.cpp new file mode 100644 index 000000000..a3cf60360 --- /dev/null +++ b/Extras/VHACD/src/vhacdVolume.cpp @@ -0,0 +1,1617 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#define _CRT_SECURE_NO_WARNINGS +#include "btConvexHullComputer.h" +#include "vhacdVolume.h" +#include +#include +#include +#include +#include + +namespace VHACD { +/********************************************************/ +/* AABB-triangle overlap test code */ +/* by Tomas Akenine-Möller */ +/* Function: int triBoxOverlap(float boxcenter[3], */ +/* float boxhalfsize[3],float triverts[3][3]); */ +/* History: */ +/* 2001-03-05: released the code in its first version */ +/* 2001-06-18: changed the order of the tests, faster */ +/* */ +/* Acknowledgement: Many thanks to Pierre Terdiman for */ +/* suggestions and discussions on how to optimize code. */ +/* Thanks to David Hunt for finding a ">="-bug! */ +/********************************************************/ + +#define X 0 +#define Y 1 +#define Z 2 +#define FINDMINMAX(x0, x1, x2, min, max) \ + min = max = x0; \ + if (x1 < min) \ + min = x1; \ + if (x1 > max) \ + max = x1; \ + if (x2 < min) \ + min = x2; \ + if (x2 > max) \ + max = x2; + +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a * v0[Y] - b * v0[Z]; \ + p2 = a * v2[Y] - b * v2[Z]; \ + if (p0 < p2) { \ + min = p0; \ + max = p2; \ + } \ + else { \ + min = p2; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a * v0[Y] - b * v0[Z]; \ + p1 = a * v1[Y] - b * v1[Z]; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } \ + else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a * v0[X] + b * v0[Z]; \ + p2 = -a * v2[X] + b * v2[Z]; \ + if (p0 < p2) { \ + min = p0; \ + max = p2; \ + } \ + else { \ + min = p2; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a * v0[X] + b * v0[Z]; \ + p1 = -a * v1[X] + b * v1[Z]; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } \ + else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a * v1[X] - b * v1[Y]; \ + p2 = a * v2[X] - b * v2[Y]; \ + if (p2 < p1) { \ + min = p2; \ + max = p1; \ + } \ + else { \ + min = p1; \ + max = p2; \ + } \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ + if (min > rad || max < -rad) \ + return 0; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a * v0[X] - b * v0[Y]; \ + p1 = a * v1[X] - b * v1[Y]; \ + if (p0 < p1) { \ + min = p0; \ + max = p1; \ + } \ + else { \ + min = p1; \ + max = p0; \ + } \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ + if (min > rad || max < -rad) \ + return 0; + +int PlaneBoxOverlap(const Vec3& normal, + const Vec3& vert, + const Vec3& maxbox) +{ + int q; + Vec3 vmin, vmax; + double v; + for (q = X; q <= Z; q++) { + v = vert[q]; + if (normal[q] > 0.0) { + vmin[q] = -maxbox[q] - v; + vmax[q] = maxbox[q] - v; + } + else { + vmin[q] = maxbox[q] - v; + vmax[q] = -maxbox[q] - v; + } + } + if (normal * vmin > 0.0) + return 0; + if (normal * vmax >= 0.0) + return 1; + return 0; +} + +int TriBoxOverlap(const Vec3& boxcenter, + const Vec3& boxhalfsize, + const Vec3& triver0, + const Vec3& triver1, + const Vec3& triver2) +{ + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + + Vec3 v0, v1, v2; + double min, max, p0, p1, p2, rad, fex, fey, fez; // -NJMP- "d" local variable removed + Vec3 normal, e0, e1, e2; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + + v0 = triver0 - boxcenter; + v1 = triver1 - boxcenter; + v2 = triver2 - boxcenter; + + /* compute triangle edges */ + e0 = v1 - v0; /* tri edge 0 */ + e1 = v2 - v1; /* tri edge 1 */ + e2 = v0 - v2; /* tri edge 2 */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = fabs(e0[X]); + fey = fabs(e0[Y]); + fez = fabs(e0[Z]); + + AXISTEST_X01(e0[Z], e0[Y], fez, fey); + AXISTEST_Y02(e0[Z], e0[X], fez, fex); + AXISTEST_Z12(e0[Y], e0[X], fey, fex); + + fex = fabs(e1[X]); + fey = fabs(e1[Y]); + fez = fabs(e1[Z]); + + AXISTEST_X01(e1[Z], e1[Y], fez, fey); + AXISTEST_Y02(e1[Z], e1[X], fez, fex); + AXISTEST_Z0(e1[Y], e1[X], fey, fex); + + fex = fabs(e2[X]); + fey = fabs(e2[Y]); + fez = fabs(e2[Z]); + + AXISTEST_X2(e2[Z], e2[Y], fez, fey); + AXISTEST_Y1(e2[Z], e2[X], fez, fex); + AXISTEST_Z12(e2[Y], e2[X], fey, fex); + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in X-direction */ + FINDMINMAX(v0[X], v1[X], v2[X], min, max); + if (min > boxhalfsize[X] || max < -boxhalfsize[X]) + return 0; + + /* test in Y-direction */ + FINDMINMAX(v0[Y], v1[Y], v2[Y], min, max); + if (min > boxhalfsize[Y] || max < -boxhalfsize[Y]) + return 0; + + /* test in Z-direction */ + FINDMINMAX(v0[Z], v1[Z], v2[Z], min, max); + if (min > boxhalfsize[Z] || max < -boxhalfsize[Z]) + return 0; + + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + normal = e0 ^ e1; + + if (!PlaneBoxOverlap(normal, v0, boxhalfsize)) + return 0; + return 1; /* box and triangle overlaps */ +} + +// Slightly modified version of Stan Melax's code for 3x3 matrix diagonalization (Thanks Stan!) +// source: http://www.melax.com/diag.html?attredirects=0 +void Diagonalize(const double (&A)[3][3], double (&Q)[3][3], double (&D)[3][3]) +{ + // A must be a symmetric matrix. + // returns Q and D such that + // Diagonal matrix D = QT * A * Q; and A = Q*D*QT + const int maxsteps = 24; // certainly wont need that many. + int k0, k1, k2; + double o[3], m[3]; + double q[4] = { 0.0, 0.0, 0.0, 1.0 }; + double jr[4]; + double sqw, sqx, sqy, sqz; + double tmp1, tmp2, mq; + double AQ[3][3]; + double thet, sgn, t, c; + for (int i = 0; i < maxsteps; ++i) { + // quat to matrix + sqx = q[0] * q[0]; + sqy = q[1] * q[1]; + sqz = q[2] * q[2]; + sqw = q[3] * q[3]; + Q[0][0] = (sqx - sqy - sqz + sqw); + Q[1][1] = (-sqx + sqy - sqz + sqw); + Q[2][2] = (-sqx - sqy + sqz + sqw); + tmp1 = q[0] * q[1]; + tmp2 = q[2] * q[3]; + Q[1][0] = 2.0 * (tmp1 + tmp2); + Q[0][1] = 2.0 * (tmp1 - tmp2); + tmp1 = q[0] * q[2]; + tmp2 = q[1] * q[3]; + Q[2][0] = 2.0 * (tmp1 - tmp2); + Q[0][2] = 2.0 * (tmp1 + tmp2); + tmp1 = q[1] * q[2]; + tmp2 = q[0] * q[3]; + Q[2][1] = 2.0 * (tmp1 + tmp2); + Q[1][2] = 2.0 * (tmp1 - tmp2); + + // AQ = A * Q + AQ[0][0] = Q[0][0] * A[0][0] + Q[1][0] * A[0][1] + Q[2][0] * A[0][2]; + AQ[0][1] = Q[0][1] * A[0][0] + Q[1][1] * A[0][1] + Q[2][1] * A[0][2]; + AQ[0][2] = Q[0][2] * A[0][0] + Q[1][2] * A[0][1] + Q[2][2] * A[0][2]; + AQ[1][0] = Q[0][0] * A[0][1] + Q[1][0] * A[1][1] + Q[2][0] * A[1][2]; + AQ[1][1] = Q[0][1] * A[0][1] + Q[1][1] * A[1][1] + Q[2][1] * A[1][2]; + AQ[1][2] = Q[0][2] * A[0][1] + Q[1][2] * A[1][1] + Q[2][2] * A[1][2]; + AQ[2][0] = Q[0][0] * A[0][2] + Q[1][0] * A[1][2] + Q[2][0] * A[2][2]; + AQ[2][1] = Q[0][1] * A[0][2] + Q[1][1] * A[1][2] + Q[2][1] * A[2][2]; + AQ[2][2] = Q[0][2] * A[0][2] + Q[1][2] * A[1][2] + Q[2][2] * A[2][2]; + // D = Qt * AQ + D[0][0] = AQ[0][0] * Q[0][0] + AQ[1][0] * Q[1][0] + AQ[2][0] * Q[2][0]; + D[0][1] = AQ[0][0] * Q[0][1] + AQ[1][0] * Q[1][1] + AQ[2][0] * Q[2][1]; + D[0][2] = AQ[0][0] * Q[0][2] + AQ[1][0] * Q[1][2] + AQ[2][0] * Q[2][2]; + D[1][0] = AQ[0][1] * Q[0][0] + AQ[1][1] * Q[1][0] + AQ[2][1] * Q[2][0]; + D[1][1] = AQ[0][1] * Q[0][1] + AQ[1][1] * Q[1][1] + AQ[2][1] * Q[2][1]; + D[1][2] = AQ[0][1] * Q[0][2] + AQ[1][1] * Q[1][2] + AQ[2][1] * Q[2][2]; + D[2][0] = AQ[0][2] * Q[0][0] + AQ[1][2] * Q[1][0] + AQ[2][2] * Q[2][0]; + D[2][1] = AQ[0][2] * Q[0][1] + AQ[1][2] * Q[1][1] + AQ[2][2] * Q[2][1]; + D[2][2] = AQ[0][2] * Q[0][2] + AQ[1][2] * Q[1][2] + AQ[2][2] * Q[2][2]; + o[0] = D[1][2]; + o[1] = D[0][2]; + o[2] = D[0][1]; + m[0] = fabs(o[0]); + m[1] = fabs(o[1]); + m[2] = fabs(o[2]); + + k0 = (m[0] > m[1] && m[0] > m[2]) ? 0 : (m[1] > m[2]) ? 1 : 2; // index of largest element of offdiag + k1 = (k0 + 1) % 3; + k2 = (k0 + 2) % 3; + if (o[k0] == 0.0) { + break; // diagonal already + } + thet = (D[k2][k2] - D[k1][k1]) / (2.0 * o[k0]); + sgn = (thet > 0.0) ? 1.0 : -1.0; + thet *= sgn; // make it positive + t = sgn / (thet + ((thet < 1.E6) ? sqrt(thet * thet + 1.0) : thet)); // sign(T)/(|T|+sqrt(T^2+1)) + c = 1.0 / sqrt(t * t + 1.0); // c= 1/(t^2+1) , t=s/c + if (c == 1.0) { + break; // no room for improvement - reached machine precision. + } + jr[0] = jr[1] = jr[2] = jr[3] = 0.0; + jr[k0] = sgn * sqrt((1.0 - c) / 2.0); // using 1/2 angle identity sin(a/2) = sqrt((1-cos(a))/2) + jr[k0] *= -1.0; // since our quat-to-matrix convention was for v*M instead of M*v + jr[3] = sqrt(1.0 - jr[k0] * jr[k0]); + if (jr[3] == 1.0) { + break; // reached limits of floating point precision + } + q[0] = (q[3] * jr[0] + q[0] * jr[3] + q[1] * jr[2] - q[2] * jr[1]); + q[1] = (q[3] * jr[1] - q[0] * jr[2] + q[1] * jr[3] + q[2] * jr[0]); + q[2] = (q[3] * jr[2] + q[0] * jr[1] - q[1] * jr[0] + q[2] * jr[3]); + q[3] = (q[3] * jr[3] - q[0] * jr[0] - q[1] * jr[1] - q[2] * jr[2]); + mq = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); + q[0] /= mq; + q[1] /= mq; + q[2] /= mq; + q[3] /= mq; + } +} +const double TetrahedronSet::EPS = 0.0000000000001; +VoxelSet::VoxelSet() +{ + m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; + m_minBBVoxels[0] = m_minBBVoxels[1] = m_minBBVoxels[2] = 0; + m_maxBBVoxels[0] = m_maxBBVoxels[1] = m_maxBBVoxels[2] = 1; + m_minBBPts[0] = m_minBBPts[1] = m_minBBPts[2] = 0; + m_maxBBPts[0] = m_maxBBPts[1] = m_maxBBPts[2] = 1; + m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0; + m_barycenterPCA[0] = m_barycenterPCA[1] = m_barycenterPCA[2] = 0.0; + m_scale = 1.0; + m_unitVolume = 1.0; + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + memset(m_Q, 0, sizeof(double) * 9); + memset(m_D, 0, sizeof(double) * 9); +} +VoxelSet::~VoxelSet(void) +{ +} +void VoxelSet::ComputeBB() +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + for (int h = 0; h < 3; ++h) { + m_minBBVoxels[h] = m_voxels[0].m_coord[h]; + m_maxBBVoxels[h] = m_voxels[0].m_coord[h]; + } + Vec3 bary(0.0); + for (size_t p = 0; p < nVoxels; ++p) { + for (int h = 0; h < 3; ++h) { + bary[h] += m_voxels[p].m_coord[h]; + if (m_minBBVoxels[h] > m_voxels[p].m_coord[h]) + m_minBBVoxels[h] = m_voxels[p].m_coord[h]; + if (m_maxBBVoxels[h] < m_voxels[p].m_coord[h]) + m_maxBBVoxels[h] = m_voxels[p].m_coord[h]; + } + } + bary /= (double)nVoxels; + for (int h = 0; h < 3; ++h) { + m_minBBPts[h] = m_minBBVoxels[h] * m_scale + m_minBB[h]; + m_maxBBPts[h] = m_maxBBVoxels[h] * m_scale + m_minBB[h]; + m_barycenter[h] = (short)(bary[h] + 0.5); + } +} +void VoxelSet::ComputeConvexHull(Mesh& meshCH, const size_t sampling) const +{ + const size_t CLUSTER_SIZE = 65536; + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + + SArray > cpoints; + + Vec3* points = new Vec3[CLUSTER_SIZE]; + size_t p = 0; + size_t s = 0; + short i, j, k; + while (p < nVoxels) { + size_t q = 0; + while (q < CLUSTER_SIZE && p < nVoxels) { + if (m_voxels[p].m_data == PRIMITIVE_ON_SURFACE) { + ++s; + if (s == sampling) { + s = 0; + i = m_voxels[p].m_coord[0]; + j = m_voxels[p].m_coord[1]; + k = m_voxels[p].m_coord[2]; + Vec3 p0((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3 p1((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3 p2((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3 p3((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3 p4((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3 p5((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3 p6((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3 p7((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); + points[q++] = p0 + m_minBB; + points[q++] = p1 + m_minBB; + points[q++] = p2 + m_minBB; + points[q++] = p3 + m_minBB; + points[q++] = p4 + m_minBB; + points[q++] = p5 + m_minBB; + points[q++] = p6 + m_minBB; + points[q++] = p7 + m_minBB; + } + } + ++p; + } + btConvexHullComputer ch; + ch.compute((double*)points, 3 * sizeof(double), (int)q, -1.0, -1.0); + for (int v = 0; v < ch.vertices.size(); v++) { + cpoints.PushBack(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + } + delete[] points; + + points = cpoints.Data(); + btConvexHullComputer ch; + ch.compute((double*)points, 3 * sizeof(double), (int)cpoints.Size(), -1.0, -1.0); + meshCH.ResizePoints(0); + meshCH.ResizeTriangles(0); + for (int v = 0; v < ch.vertices.size(); v++) { + meshCH.AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int nt = ch.faces.size(); + for (int t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int a = sourceEdge->getSourceVertex(); + int b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int c = edge->getTargetVertex(); + while (c != a) { + meshCH.AddTriangle(Vec3(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } +} +void VoxelSet::GetPoints(const Voxel& voxel, + Vec3* const pts) const +{ + short i = voxel.m_coord[0]; + short j = voxel.m_coord[1]; + short k = voxel.m_coord[2]; + pts[0][0] = (i - 0.5) * m_scale + m_minBB[0]; + pts[1][0] = (i + 0.5) * m_scale + m_minBB[0]; + pts[2][0] = (i + 0.5) * m_scale + m_minBB[0]; + pts[3][0] = (i - 0.5) * m_scale + m_minBB[0]; + pts[4][0] = (i - 0.5) * m_scale + m_minBB[0]; + pts[5][0] = (i + 0.5) * m_scale + m_minBB[0]; + pts[6][0] = (i + 0.5) * m_scale + m_minBB[0]; + pts[7][0] = (i - 0.5) * m_scale + m_minBB[0]; + pts[0][1] = (j - 0.5) * m_scale + m_minBB[1]; + pts[1][1] = (j - 0.5) * m_scale + m_minBB[1]; + pts[2][1] = (j + 0.5) * m_scale + m_minBB[1]; + pts[3][1] = (j + 0.5) * m_scale + m_minBB[1]; + pts[4][1] = (j - 0.5) * m_scale + m_minBB[1]; + pts[5][1] = (j - 0.5) * m_scale + m_minBB[1]; + pts[6][1] = (j + 0.5) * m_scale + m_minBB[1]; + pts[7][1] = (j + 0.5) * m_scale + m_minBB[1]; + pts[0][2] = (k - 0.5) * m_scale + m_minBB[2]; + pts[1][2] = (k - 0.5) * m_scale + m_minBB[2]; + pts[2][2] = (k - 0.5) * m_scale + m_minBB[2]; + pts[3][2] = (k - 0.5) * m_scale + m_minBB[2]; + pts[4][2] = (k + 0.5) * m_scale + m_minBB[2]; + pts[5][2] = (k + 0.5) * m_scale + m_minBB[2]; + pts[6][2] = (k + 0.5) * m_scale + m_minBB[2]; + pts[7][2] = (k + 0.5) * m_scale + m_minBB[2]; +} +void VoxelSet::Intersect(const Plane& plane, + SArray >* const positivePts, + SArray >* const negativePts, + const size_t sampling) const +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + const double d0 = m_scale; + double d; + Vec3 pts[8]; + Vec3 pt; + Voxel voxel; + size_t sp = 0; + size_t sn = 0; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + pt = GetPoint(voxel); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + // if (d >= 0.0 && d <= d0) positivePts->PushBack(pt); + // else if (d < 0.0 && -d <= d0) negativePts->PushBack(pt); + if (d >= 0.0) { + if (d <= d0) { + GetPoints(voxel, pts); + for (int k = 0; k < 8; ++k) { + positivePts->PushBack(pts[k]); + } + } + else { + if (++sp == sampling) { + // positivePts->PushBack(pt); + GetPoints(voxel, pts); + for (int k = 0; k < 8; ++k) { + positivePts->PushBack(pts[k]); + } + sp = 0; + } + } + } + else { + if (-d <= d0) { + GetPoints(voxel, pts); + for (int k = 0; k < 8; ++k) { + negativePts->PushBack(pts[k]); + } + } + else { + if (++sn == sampling) { + // negativePts->PushBack(pt); + GetPoints(voxel, pts); + for (int k = 0; k < 8; ++k) { + negativePts->PushBack(pts[k]); + } + sn = 0; + } + } + } + } +} +void VoxelSet::ComputeExteriorPoints(const Plane& plane, + const Mesh& mesh, + SArray >* const exteriorPts) const +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + double d; + Vec3 pt; + Vec3 pts[8]; + Voxel voxel; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + pt = GetPoint(voxel); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + if (d >= 0.0) { + if (!mesh.IsInside(pt)) { + GetPoints(voxel, pts); + for (int k = 0; k < 8; ++k) { + exteriorPts->PushBack(pts[k]); + } + } + } + } +} +void VoxelSet::ComputeClippedVolumes(const Plane& plane, + double& positiveVolume, + double& negativeVolume) const +{ + negativeVolume = 0.0; + positiveVolume = 0.0; + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + double d; + Vec3 pt; + size_t nPositiveVoxels = 0; + for (size_t v = 0; v < nVoxels; ++v) { + pt = GetPoint(m_voxels[v]); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + nPositiveVoxels += (d >= 0.0); + } + size_t nNegativeVoxels = nVoxels - nPositiveVoxels; + positiveVolume = m_unitVolume * nPositiveVoxels; + negativeVolume = m_unitVolume * nNegativeVoxels; +} +void VoxelSet::SelectOnSurface(PrimitiveSet* const onSurfP) const +{ + VoxelSet* const onSurf = (VoxelSet*)onSurfP; + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + + for (int h = 0; h < 3; ++h) { + onSurf->m_minBB[h] = m_minBB[h]; + } + onSurf->m_voxels.Resize(0); + onSurf->m_scale = m_scale; + onSurf->m_unitVolume = m_unitVolume; + onSurf->m_numVoxelsOnSurface = 0; + onSurf->m_numVoxelsInsideSurface = 0; + Voxel voxel; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + if (voxel.m_data == PRIMITIVE_ON_SURFACE) { + onSurf->m_voxels.PushBack(voxel); + ++onSurf->m_numVoxelsOnSurface; + } + } +} +void VoxelSet::Clip(const Plane& plane, + PrimitiveSet* const positivePartP, + PrimitiveSet* const negativePartP) const +{ + VoxelSet* const positivePart = (VoxelSet*)positivePartP; + VoxelSet* const negativePart = (VoxelSet*)negativePartP; + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + + for (int h = 0; h < 3; ++h) { + negativePart->m_minBB[h] = positivePart->m_minBB[h] = m_minBB[h]; + } + positivePart->m_voxels.Resize(0); + negativePart->m_voxels.Resize(0); + positivePart->m_voxels.Allocate(nVoxels); + negativePart->m_voxels.Allocate(nVoxels); + negativePart->m_scale = positivePart->m_scale = m_scale; + negativePart->m_unitVolume = positivePart->m_unitVolume = m_unitVolume; + negativePart->m_numVoxelsOnSurface = positivePart->m_numVoxelsOnSurface = 0; + negativePart->m_numVoxelsInsideSurface = positivePart->m_numVoxelsInsideSurface = 0; + + double d; + Vec3 pt; + Voxel voxel; + const double d0 = m_scale; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + pt = GetPoint(voxel); + d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; + if (d >= 0.0) { + if (voxel.m_data == PRIMITIVE_ON_SURFACE || d <= d0) { + voxel.m_data = PRIMITIVE_ON_SURFACE; + positivePart->m_voxels.PushBack(voxel); + ++positivePart->m_numVoxelsOnSurface; + } + else { + positivePart->m_voxels.PushBack(voxel); + ++positivePart->m_numVoxelsInsideSurface; + } + } + else { + if (voxel.m_data == PRIMITIVE_ON_SURFACE || -d <= d0) { + voxel.m_data = PRIMITIVE_ON_SURFACE; + negativePart->m_voxels.PushBack(voxel); + ++negativePart->m_numVoxelsOnSurface; + } + else { + negativePart->m_voxels.PushBack(voxel); + ++negativePart->m_numVoxelsInsideSurface; + } + } + } +} +void VoxelSet::Convert(Mesh& mesh, const VOXEL_VALUE value) const +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + Voxel voxel; + Vec3 pts[8]; + for (size_t v = 0; v < nVoxels; ++v) { + voxel = m_voxels[v]; + if (voxel.m_data == value) { + GetPoints(voxel, pts); + int s = (int)mesh.GetNPoints(); + for (int k = 0; k < 8; ++k) { + mesh.AddPoint(pts[k]); + } + mesh.AddTriangle(Vec3(s + 0, s + 2, s + 1)); + mesh.AddTriangle(Vec3(s + 0, s + 3, s + 2)); + mesh.AddTriangle(Vec3(s + 4, s + 5, s + 6)); + mesh.AddTriangle(Vec3(s + 4, s + 6, s + 7)); + mesh.AddTriangle(Vec3(s + 7, s + 6, s + 2)); + mesh.AddTriangle(Vec3(s + 7, s + 2, s + 3)); + mesh.AddTriangle(Vec3(s + 4, s + 1, s + 5)); + mesh.AddTriangle(Vec3(s + 4, s + 0, s + 1)); + mesh.AddTriangle(Vec3(s + 6, s + 5, s + 1)); + mesh.AddTriangle(Vec3(s + 6, s + 1, s + 2)); + mesh.AddTriangle(Vec3(s + 7, s + 0, s + 4)); + mesh.AddTriangle(Vec3(s + 7, s + 3, s + 0)); + } + } +} +void VoxelSet::ComputePrincipalAxes() +{ + const size_t nVoxels = m_voxels.Size(); + if (nVoxels == 0) + return; + m_barycenterPCA[0] = m_barycenterPCA[1] = m_barycenterPCA[2] = 0.0; + for (size_t v = 0; v < nVoxels; ++v) { + Voxel& voxel = m_voxels[v]; + m_barycenterPCA[0] += voxel.m_coord[0]; + m_barycenterPCA[1] += voxel.m_coord[1]; + m_barycenterPCA[2] += voxel.m_coord[2]; + } + m_barycenterPCA /= (double)nVoxels; + + double covMat[3][3] = { { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }; + double x, y, z; + for (size_t v = 0; v < nVoxels; ++v) { + Voxel& voxel = m_voxels[v]; + x = voxel.m_coord[0] - m_barycenter[0]; + y = voxel.m_coord[1] - m_barycenter[1]; + z = voxel.m_coord[2] - m_barycenter[2]; + covMat[0][0] += x * x; + covMat[1][1] += y * y; + covMat[2][2] += z * z; + covMat[0][1] += x * y; + covMat[0][2] += x * z; + covMat[1][2] += y * z; + } + covMat[0][0] /= nVoxels; + covMat[1][1] /= nVoxels; + covMat[2][2] /= nVoxels; + covMat[0][1] /= nVoxels; + covMat[0][2] /= nVoxels; + covMat[1][2] /= nVoxels; + covMat[1][0] = covMat[0][1]; + covMat[2][0] = covMat[0][2]; + covMat[2][1] = covMat[1][2]; + Diagonalize(covMat, m_Q, m_D); +} +Volume::Volume() +{ + m_dim[0] = m_dim[1] = m_dim[2] = 0; + m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; + m_maxBB[0] = m_maxBB[1] = m_maxBB[2] = 1.0; + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + m_numVoxelsOutsideSurface = 0; + m_scale = 1.0; + m_data = 0; +} +Volume::~Volume(void) +{ + delete[] m_data; +} +void Volume::Allocate() +{ + delete[] m_data; + size_t size = m_dim[0] * m_dim[1] * m_dim[2]; + m_data = new unsigned char[size]; + memset(m_data, PRIMITIVE_UNDEFINED, sizeof(unsigned char) * size); +} +void Volume::Free() +{ + delete[] m_data; + m_data = 0; +} +void Volume::FillOutsideSurface(const size_t i0, + const size_t j0, + const size_t k0, + const size_t i1, + const size_t j1, + const size_t k1) +{ + const short neighbours[6][3] = { { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + { -1, 0, 0 }, + { 0, -1, 0 }, + { 0, 0, -1 } }; + std::queue > fifo; + Vec3 current; + short a, b, c; + for (size_t i = i0; i < i1; ++i) { + for (size_t j = j0; j < j1; ++j) { + for (size_t k = k0; k < k1; ++k) { + + if (GetVoxel(i, j, k) == PRIMITIVE_UNDEFINED) { + current[0] = (short)i; + current[1] = (short)j; + current[2] = (short)k; + fifo.push(current); + GetVoxel(current[0], current[1], current[2]) = PRIMITIVE_OUTSIDE_SURFACE; + ++m_numVoxelsOutsideSurface; + while (fifo.size() > 0) { + current = fifo.front(); + fifo.pop(); + for (int h = 0; h < 6; ++h) { + a = current[0] + neighbours[h][0]; + b = current[1] + neighbours[h][1]; + c = current[2] + neighbours[h][2]; + if (a < 0 || a >= (int)m_dim[0] || b < 0 || b >= (int)m_dim[1] || c < 0 || c >= (int)m_dim[2]) { + continue; + } + unsigned char& v = GetVoxel(a, b, c); + if (v == PRIMITIVE_UNDEFINED) { + v = PRIMITIVE_OUTSIDE_SURFACE; + ++m_numVoxelsOutsideSurface; + fifo.push(Vec3(a, b, c)); + } + } + } + } + } + } + } +} +void Volume::FillInsideSurface() +{ + const size_t i0 = m_dim[0]; + const size_t j0 = m_dim[1]; + const size_t k0 = m_dim[2]; + for (size_t i = 0; i < i0; ++i) { + for (size_t j = 0; j < j0; ++j) { + for (size_t k = 0; k < k0; ++k) { + unsigned char& v = GetVoxel(i, j, k); + if (v == PRIMITIVE_UNDEFINED) { + v = PRIMITIVE_INSIDE_SURFACE; + ++m_numVoxelsInsideSurface; + } + } + } + } +} +void Volume::Convert(Mesh& mesh, const VOXEL_VALUE value) const +{ + const size_t i0 = m_dim[0]; + const size_t j0 = m_dim[1]; + const size_t k0 = m_dim[2]; + for (size_t i = 0; i < i0; ++i) { + for (size_t j = 0; j < j0; ++j) { + for (size_t k = 0; k < k0; ++k) { + const unsigned char& voxel = GetVoxel(i, j, k); + if (voxel == value) { + Vec3 p0((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3 p1((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3 p2((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3 p3((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); + Vec3 p4((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3 p5((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3 p6((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); + Vec3 p7((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); + int s = (int)mesh.GetNPoints(); + mesh.AddPoint(p0 + m_minBB); + mesh.AddPoint(p1 + m_minBB); + mesh.AddPoint(p2 + m_minBB); + mesh.AddPoint(p3 + m_minBB); + mesh.AddPoint(p4 + m_minBB); + mesh.AddPoint(p5 + m_minBB); + mesh.AddPoint(p6 + m_minBB); + mesh.AddPoint(p7 + m_minBB); + mesh.AddTriangle(Vec3(s + 0, s + 2, s + 1)); + mesh.AddTriangle(Vec3(s + 0, s + 3, s + 2)); + mesh.AddTriangle(Vec3(s + 4, s + 5, s + 6)); + mesh.AddTriangle(Vec3(s + 4, s + 6, s + 7)); + mesh.AddTriangle(Vec3(s + 7, s + 6, s + 2)); + mesh.AddTriangle(Vec3(s + 7, s + 2, s + 3)); + mesh.AddTriangle(Vec3(s + 4, s + 1, s + 5)); + mesh.AddTriangle(Vec3(s + 4, s + 0, s + 1)); + mesh.AddTriangle(Vec3(s + 6, s + 5, s + 1)); + mesh.AddTriangle(Vec3(s + 6, s + 1, s + 2)); + mesh.AddTriangle(Vec3(s + 7, s + 0, s + 4)); + mesh.AddTriangle(Vec3(s + 7, s + 3, s + 0)); + } + } + } + } +} +void Volume::Convert(VoxelSet& vset) const +{ + for (int h = 0; h < 3; ++h) { + vset.m_minBB[h] = m_minBB[h]; + } + vset.m_voxels.Allocate(m_numVoxelsInsideSurface + m_numVoxelsOnSurface); + vset.m_scale = m_scale; + vset.m_unitVolume = m_scale * m_scale * m_scale; + const short i0 = (short)m_dim[0]; + const short j0 = (short)m_dim[1]; + const short k0 = (short)m_dim[2]; + Voxel voxel; + vset.m_numVoxelsOnSurface = 0; + vset.m_numVoxelsInsideSurface = 0; + for (short i = 0; i < i0; ++i) { + for (short j = 0; j < j0; ++j) { + for (short k = 0; k < k0; ++k) { + const unsigned char& value = GetVoxel(i, j, k); + if (value == PRIMITIVE_INSIDE_SURFACE) { + voxel.m_coord[0] = i; + voxel.m_coord[1] = j; + voxel.m_coord[2] = k; + voxel.m_data = PRIMITIVE_INSIDE_SURFACE; + vset.m_voxels.PushBack(voxel); + ++vset.m_numVoxelsInsideSurface; + } + else if (value == PRIMITIVE_ON_SURFACE) { + voxel.m_coord[0] = i; + voxel.m_coord[1] = j; + voxel.m_coord[2] = k; + voxel.m_data = PRIMITIVE_ON_SURFACE; + vset.m_voxels.PushBack(voxel); + ++vset.m_numVoxelsOnSurface; + } + } + } + } +} + +void Volume::Convert(TetrahedronSet& tset) const +{ + tset.m_tetrahedra.Allocate(5 * (m_numVoxelsInsideSurface + m_numVoxelsOnSurface)); + tset.m_scale = m_scale; + const short i0 = (short)m_dim[0]; + const short j0 = (short)m_dim[1]; + const short k0 = (short)m_dim[2]; + tset.m_numTetrahedraOnSurface = 0; + tset.m_numTetrahedraInsideSurface = 0; + Tetrahedron tetrahedron; + for (short i = 0; i < i0; ++i) { + for (short j = 0; j < j0; ++j) { + for (short k = 0; k < k0; ++k) { + const unsigned char& value = GetVoxel(i, j, k); + if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { + tetrahedron.m_data = value; + Vec3 p1((i - 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); + Vec3 p2((i + 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); + Vec3 p3((i + 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); + Vec3 p4((i - 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); + Vec3 p5((i - 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); + Vec3 p6((i + 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); + Vec3 p7((i + 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); + Vec3 p8((i - 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); + + tetrahedron.m_pts[0] = p2; + tetrahedron.m_pts[1] = p4; + tetrahedron.m_pts[2] = p7; + tetrahedron.m_pts[3] = p5; + tset.m_tetrahedra.PushBack(tetrahedron); + + tetrahedron.m_pts[0] = p6; + tetrahedron.m_pts[1] = p2; + tetrahedron.m_pts[2] = p7; + tetrahedron.m_pts[3] = p5; + tset.m_tetrahedra.PushBack(tetrahedron); + + tetrahedron.m_pts[0] = p3; + tetrahedron.m_pts[1] = p4; + tetrahedron.m_pts[2] = p7; + tetrahedron.m_pts[3] = p2; + tset.m_tetrahedra.PushBack(tetrahedron); + + tetrahedron.m_pts[0] = p1; + tetrahedron.m_pts[1] = p4; + tetrahedron.m_pts[2] = p2; + tetrahedron.m_pts[3] = p5; + tset.m_tetrahedra.PushBack(tetrahedron); + + tetrahedron.m_pts[0] = p8; + tetrahedron.m_pts[1] = p5; + tetrahedron.m_pts[2] = p7; + tetrahedron.m_pts[3] = p4; + tset.m_tetrahedra.PushBack(tetrahedron); + if (value == PRIMITIVE_INSIDE_SURFACE) { + tset.m_numTetrahedraInsideSurface += 5; + } + else { + tset.m_numTetrahedraOnSurface += 5; + } + } + } + } + } +} + +void Volume::AlignToPrincipalAxes(double (&rot)[3][3]) const +{ + const short i0 = (short)m_dim[0]; + const short j0 = (short)m_dim[1]; + const short k0 = (short)m_dim[2]; + Vec3 barycenter(0.0); + size_t nVoxels = 0; + for (short i = 0; i < i0; ++i) { + for (short j = 0; j < j0; ++j) { + for (short k = 0; k < k0; ++k) { + const unsigned char& value = GetVoxel(i, j, k); + if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { + barycenter[0] += i; + barycenter[1] += j; + barycenter[2] += k; + ++nVoxels; + } + } + } + } + barycenter /= (double)nVoxels; + + double covMat[3][3] = { { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }; + double x, y, z; + for (short i = 0; i < i0; ++i) { + for (short j = 0; j < j0; ++j) { + for (short k = 0; k < k0; ++k) { + const unsigned char& value = GetVoxel(i, j, k); + if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { + x = i - barycenter[0]; + y = j - barycenter[1]; + z = k - barycenter[2]; + covMat[0][0] += x * x; + covMat[1][1] += y * y; + covMat[2][2] += z * z; + covMat[0][1] += x * y; + covMat[0][2] += x * z; + covMat[1][2] += y * z; + } + } + } + } + covMat[1][0] = covMat[0][1]; + covMat[2][0] = covMat[0][2]; + covMat[2][1] = covMat[1][2]; + double D[3][3]; + Diagonalize(covMat, rot, D); +} +TetrahedronSet::TetrahedronSet() +{ + m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; + m_maxBB[0] = m_maxBB[1] = m_maxBB[2] = 1.0; + m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; + m_scale = 1.0; + m_numTetrahedraOnSurface = 0; + m_numTetrahedraInsideSurface = 0; + memset(m_Q, 0, sizeof(double) * 9); + memset(m_D, 0, sizeof(double) * 9); +} +TetrahedronSet::~TetrahedronSet(void) +{ +} +void TetrahedronSet::ComputeBB() +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + + for (int h = 0; h < 3; ++h) { + m_minBB[h] = m_maxBB[h] = m_tetrahedra[0].m_pts[0][h]; + m_barycenter[h] = 0.0; + } + for (size_t p = 0; p < nTetrahedra; ++p) { + for (int i = 0; i < 4; ++i) { + for (int h = 0; h < 3; ++h) { + if (m_minBB[h] > m_tetrahedra[p].m_pts[i][h]) + m_minBB[h] = m_tetrahedra[p].m_pts[i][h]; + if (m_maxBB[h] < m_tetrahedra[p].m_pts[i][h]) + m_maxBB[h] = m_tetrahedra[p].m_pts[i][h]; + m_barycenter[h] += m_tetrahedra[p].m_pts[i][h]; + } + } + } + m_barycenter /= (double)(4 * nTetrahedra); +} +void TetrahedronSet::ComputeConvexHull(Mesh& meshCH, const size_t sampling) const +{ + const size_t CLUSTER_SIZE = 65536; + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + + SArray > cpoints; + + Vec3* points = new Vec3[CLUSTER_SIZE]; + size_t p = 0; + while (p < nTetrahedra) { + size_t q = 0; + size_t s = 0; + while (q < CLUSTER_SIZE && p < nTetrahedra) { + if (m_tetrahedra[p].m_data == PRIMITIVE_ON_SURFACE) { + ++s; + if (s == sampling) { + s = 0; + for (int a = 0; a < 4; ++a) { + points[q++] = m_tetrahedra[p].m_pts[a]; + for (int xx = 0; xx < 3; ++xx) { + assert(m_tetrahedra[p].m_pts[a][xx] + EPS >= m_minBB[xx]); + assert(m_tetrahedra[p].m_pts[a][xx] <= m_maxBB[xx] + EPS); + } + } + } + } + ++p; + } + btConvexHullComputer ch; + ch.compute((double*)points, 3 * sizeof(double), (int)q, -1.0, -1.0); + for (int v = 0; v < ch.vertices.size(); v++) { + cpoints.PushBack(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + } + delete[] points; + + points = cpoints.Data(); + btConvexHullComputer ch; + ch.compute((double*)points, 3 * sizeof(double), (int)cpoints.Size(), -1.0, -1.0); + meshCH.ResizePoints(0); + meshCH.ResizeTriangles(0); + for (int v = 0; v < ch.vertices.size(); v++) { + meshCH.AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int nt = ch.faces.size(); + for (int t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int a = sourceEdge->getSourceVertex(); + int b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int c = edge->getTargetVertex(); + while (c != a) { + meshCH.AddTriangle(Vec3(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } +} +inline bool TetrahedronSet::Add(Tetrahedron& tetrahedron) +{ + double v = ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3]); + + const double EPS = 0.0000000001; + if (fabs(v) < EPS) { + return false; + } + else if (v < 0.0) { + Vec3 tmp = tetrahedron.m_pts[0]; + tetrahedron.m_pts[0] = tetrahedron.m_pts[1]; + tetrahedron.m_pts[1] = tmp; + } + + for (int a = 0; a < 4; ++a) { + for (int xx = 0; xx < 3; ++xx) { + assert(tetrahedron.m_pts[a][xx] + EPS >= m_minBB[xx]); + assert(tetrahedron.m_pts[a][xx] <= m_maxBB[xx] + EPS); + } + } + m_tetrahedra.PushBack(tetrahedron); + return true; +} + +void TetrahedronSet::AddClippedTetrahedra(const Vec3 (&pts)[10], const int nPts) +{ + const int tetF[4][3] = { { 0, 1, 2 }, { 2, 1, 3 }, { 3, 1, 0 }, { 3, 0, 2 } }; + if (nPts < 4) { + return; + } + else if (nPts == 4) { + Tetrahedron tetrahedron; + tetrahedron.m_data = PRIMITIVE_ON_SURFACE; + tetrahedron.m_pts[0] = pts[0]; + tetrahedron.m_pts[1] = pts[1]; + tetrahedron.m_pts[2] = pts[2]; + tetrahedron.m_pts[3] = pts[3]; + if (Add(tetrahedron)) { + ++m_numTetrahedraOnSurface; + } + } + else if (nPts == 5) { + const int tet[15][4] = { + { 0, 1, 2, 3 }, { 1, 2, 3, 4 }, { 0, 2, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 2, 4 }, + }; + const int rem[5] = { 4, 0, 1, 2, 3 }; + double maxVol = 0.0; + int h0 = -1; + Tetrahedron tetrahedron0; + tetrahedron0.m_data = PRIMITIVE_ON_SURFACE; + for (int h = 0; h < 5; ++h) { + double v = ComputeVolume4(pts[tet[h][0]], pts[tet[h][1]], pts[tet[h][2]], pts[tet[h][3]]); + if (v > maxVol) { + h0 = h; + tetrahedron0.m_pts[0] = pts[tet[h][0]]; + tetrahedron0.m_pts[1] = pts[tet[h][1]]; + tetrahedron0.m_pts[2] = pts[tet[h][2]]; + tetrahedron0.m_pts[3] = pts[tet[h][3]]; + maxVol = v; + } + else if (-v > maxVol) { + h0 = h; + tetrahedron0.m_pts[0] = pts[tet[h][1]]; + tetrahedron0.m_pts[1] = pts[tet[h][0]]; + tetrahedron0.m_pts[2] = pts[tet[h][2]]; + tetrahedron0.m_pts[3] = pts[tet[h][3]]; + maxVol = -v; + } + } + if (h0 == -1) + return; + if (Add(tetrahedron0)) { + ++m_numTetrahedraOnSurface; + } + else { + return; + } + int a = rem[h0]; + maxVol = 0.0; + int h1 = -1; + Tetrahedron tetrahedron1; + tetrahedron1.m_data = PRIMITIVE_ON_SURFACE; + for (int h = 0; h < 4; ++h) { + double v = ComputeVolume4(pts[a], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); + if (v > maxVol) { + h1 = h; + tetrahedron1.m_pts[0] = pts[a]; + tetrahedron1.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; + tetrahedron1.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; + tetrahedron1.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; + maxVol = v; + } + } + if (h1 == -1 && Add(tetrahedron1)) { + ++m_numTetrahedraOnSurface; + } + } + else if (nPts == 6) { + + const int tet[15][4] = { { 2, 3, 4, 5 }, { 1, 3, 4, 5 }, { 1, 2, 4, 5 }, { 1, 2, 3, 5 }, { 1, 2, 3, 4 }, + { 0, 3, 4, 5 }, { 0, 2, 4, 5 }, { 0, 2, 3, 5 }, { 0, 2, 3, 4 }, { 0, 1, 4, 5 }, + { 0, 1, 3, 5 }, { 0, 1, 3, 4 }, { 0, 1, 2, 5 }, { 0, 1, 2, 4 }, { 0, 1, 2, 3 } }; + const int rem[15][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 0, 5 }, + { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 }, { 2, 3 }, + { 2, 4 }, { 2, 5 }, { 3, 4 }, { 3, 5 }, { 4, 5 } }; + double maxVol = 0.0; + int h0 = -1; + Tetrahedron tetrahedron0; + tetrahedron0.m_data = PRIMITIVE_ON_SURFACE; + for (int h = 0; h < 15; ++h) { + double v = ComputeVolume4(pts[tet[h][0]], pts[tet[h][1]], pts[tet[h][2]], pts[tet[h][3]]); + if (v > maxVol) { + h0 = h; + tetrahedron0.m_pts[0] = pts[tet[h][0]]; + tetrahedron0.m_pts[1] = pts[tet[h][1]]; + tetrahedron0.m_pts[2] = pts[tet[h][2]]; + tetrahedron0.m_pts[3] = pts[tet[h][3]]; + maxVol = v; + } + else if (-v > maxVol) { + h0 = h; + tetrahedron0.m_pts[0] = pts[tet[h][1]]; + tetrahedron0.m_pts[1] = pts[tet[h][0]]; + tetrahedron0.m_pts[2] = pts[tet[h][2]]; + tetrahedron0.m_pts[3] = pts[tet[h][3]]; + maxVol = -v; + } + } + if (h0 == -1) + return; + if (Add(tetrahedron0)) { + ++m_numTetrahedraOnSurface; + } + else { + return; + } + + int a0 = rem[h0][0]; + int a1 = rem[h0][1]; + int h1 = -1; + Tetrahedron tetrahedron1; + tetrahedron1.m_data = PRIMITIVE_ON_SURFACE; + maxVol = 0.0; + for (int h = 0; h < 4; ++h) { + double v = ComputeVolume4(pts[a0], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); + if (v > maxVol) { + h1 = h; + tetrahedron1.m_pts[0] = pts[a0]; + tetrahedron1.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; + tetrahedron1.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; + tetrahedron1.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; + maxVol = v; + } + } + if (h1 != -1 && Add(tetrahedron1)) { + ++m_numTetrahedraOnSurface; + } + else { + h1 = -1; + } + maxVol = 0.0; + int h2 = -1; + Tetrahedron tetrahedron2; + tetrahedron2.m_data = PRIMITIVE_ON_SURFACE; + for (int h = 0; h < 4; ++h) { + double v = ComputeVolume4(pts[a0], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); + if (h == h1) + continue; + if (v > maxVol) { + h2 = h; + tetrahedron2.m_pts[0] = pts[a1]; + tetrahedron2.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; + tetrahedron2.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; + tetrahedron2.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; + maxVol = v; + } + } + if (h1 != -1) { + for (int h = 0; h < 4; ++h) { + double v = ComputeVolume4(pts[a1], tetrahedron1.m_pts[tetF[h][0]], tetrahedron1.m_pts[tetF[h][1]], tetrahedron1.m_pts[tetF[h][2]]); + if (h == 1) + continue; + if (v > maxVol) { + h2 = h; + tetrahedron2.m_pts[0] = pts[a1]; + tetrahedron2.m_pts[1] = tetrahedron1.m_pts[tetF[h][0]]; + tetrahedron2.m_pts[2] = tetrahedron1.m_pts[tetF[h][1]]; + tetrahedron2.m_pts[3] = tetrahedron1.m_pts[tetF[h][2]]; + maxVol = v; + } + } + } + if (h2 != -1 && Add(tetrahedron2)) { + ++m_numTetrahedraOnSurface; + } + } + else { + assert(0); + } +} + +void TetrahedronSet::Intersect(const Plane& plane, + SArray >* const positivePts, + SArray >* const negativePts, + const size_t sampling) const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; +} +void TetrahedronSet::ComputeExteriorPoints(const Plane& plane, + const Mesh& mesh, + SArray >* const exteriorPts) const +{ +} +void TetrahedronSet::ComputeClippedVolumes(const Plane& plane, + double& positiveVolume, + double& negativeVolume) const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; +} + +void TetrahedronSet::SelectOnSurface(PrimitiveSet* const onSurfP) const +{ + TetrahedronSet* const onSurf = (TetrahedronSet*)onSurfP; + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + onSurf->m_tetrahedra.Resize(0); + onSurf->m_scale = m_scale; + onSurf->m_numTetrahedraOnSurface = 0; + onSurf->m_numTetrahedraInsideSurface = 0; + onSurf->m_barycenter = m_barycenter; + onSurf->m_minBB = m_minBB; + onSurf->m_maxBB = m_maxBB; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + onSurf->m_Q[i][j] = m_Q[i][j]; + onSurf->m_D[i][j] = m_D[i][j]; + } + } + Tetrahedron tetrahedron; + for (size_t v = 0; v < nTetrahedra; ++v) { + tetrahedron = m_tetrahedra[v]; + if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { + onSurf->m_tetrahedra.PushBack(tetrahedron); + ++onSurf->m_numTetrahedraOnSurface; + } + } +} +void TetrahedronSet::Clip(const Plane& plane, + PrimitiveSet* const positivePartP, + PrimitiveSet* const negativePartP) const +{ + TetrahedronSet* const positivePart = (TetrahedronSet*)positivePartP; + TetrahedronSet* const negativePart = (TetrahedronSet*)negativePartP; + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + positivePart->m_tetrahedra.Resize(0); + negativePart->m_tetrahedra.Resize(0); + positivePart->m_tetrahedra.Allocate(nTetrahedra); + negativePart->m_tetrahedra.Allocate(nTetrahedra); + negativePart->m_scale = positivePart->m_scale = m_scale; + negativePart->m_numTetrahedraOnSurface = positivePart->m_numTetrahedraOnSurface = 0; + negativePart->m_numTetrahedraInsideSurface = positivePart->m_numTetrahedraInsideSurface = 0; + negativePart->m_barycenter = m_barycenter; + positivePart->m_barycenter = m_barycenter; + negativePart->m_minBB = m_minBB; + positivePart->m_minBB = m_minBB; + negativePart->m_maxBB = m_maxBB; + positivePart->m_maxBB = m_maxBB; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + negativePart->m_Q[i][j] = positivePart->m_Q[i][j] = m_Q[i][j]; + negativePart->m_D[i][j] = positivePart->m_D[i][j] = m_D[i][j]; + } + } + + Tetrahedron tetrahedron; + double delta, alpha; + int sign[4]; + int npos, nneg; + Vec3 posPts[10]; + Vec3 negPts[10]; + Vec3 P0, P1, M; + const Vec3 n(plane.m_a, plane.m_b, plane.m_c); + const int edges[6][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 } }; + double dist; + for (size_t v = 0; v < nTetrahedra; ++v) { + tetrahedron = m_tetrahedra[v]; + npos = nneg = 0; + for (int i = 0; i < 4; ++i) { + dist = plane.m_a * tetrahedron.m_pts[i][0] + plane.m_b * tetrahedron.m_pts[i][1] + plane.m_c * tetrahedron.m_pts[i][2] + plane.m_d; + if (dist > 0.0) { + sign[i] = 1; + posPts[npos] = tetrahedron.m_pts[i]; + ++npos; + } + else { + sign[i] = -1; + negPts[nneg] = tetrahedron.m_pts[i]; + ++nneg; + } + } + + if (npos == 4) { + positivePart->Add(tetrahedron); + if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { + ++positivePart->m_numTetrahedraOnSurface; + } + else { + ++positivePart->m_numTetrahedraInsideSurface; + } + } + else if (nneg == 4) { + negativePart->Add(tetrahedron); + if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { + ++negativePart->m_numTetrahedraOnSurface; + } + else { + ++negativePart->m_numTetrahedraInsideSurface; + } + } + else { + int nnew = 0; + for (int j = 0; j < 6; ++j) { + if (sign[edges[j][0]] * sign[edges[j][1]] == -1) { + P0 = tetrahedron.m_pts[edges[j][0]]; + P1 = tetrahedron.m_pts[edges[j][1]]; + delta = (P0 - P1) * n; + alpha = -(plane.m_d + (n * P1)) / delta; + assert(alpha >= 0.0 && alpha <= 1.0); + M = alpha * P0 + (1 - alpha) * P1; + for (int xx = 0; xx < 3; ++xx) { + assert(M[xx] + EPS >= m_minBB[xx]); + assert(M[xx] <= m_maxBB[xx] + EPS); + } + posPts[npos++] = M; + negPts[nneg++] = M; + ++nnew; + } + } + negativePart->AddClippedTetrahedra(negPts, nneg); + positivePart->AddClippedTetrahedra(posPts, npos); + } + } +} +void TetrahedronSet::Convert(Mesh& mesh, const VOXEL_VALUE value) const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + for (size_t v = 0; v < nTetrahedra; ++v) { + const Tetrahedron& tetrahedron = m_tetrahedra[v]; + if (tetrahedron.m_data == value) { + int s = (int)mesh.GetNPoints(); + mesh.AddPoint(tetrahedron.m_pts[0]); + mesh.AddPoint(tetrahedron.m_pts[1]); + mesh.AddPoint(tetrahedron.m_pts[2]); + mesh.AddPoint(tetrahedron.m_pts[3]); + mesh.AddTriangle(Vec3(s + 0, s + 1, s + 2)); + mesh.AddTriangle(Vec3(s + 2, s + 1, s + 3)); + mesh.AddTriangle(Vec3(s + 3, s + 1, s + 0)); + mesh.AddTriangle(Vec3(s + 3, s + 0, s + 2)); + } + } +} +const double TetrahedronSet::ComputeVolume() const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return 0.0; + double volume = 0.0; + for (size_t v = 0; v < nTetrahedra; ++v) { + const Tetrahedron& tetrahedron = m_tetrahedra[v]; + volume += fabs(ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3])); + } + return volume / 6.0; +} +const double TetrahedronSet::ComputeMaxVolumeError() const +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return 0.0; + double volume = 0.0; + for (size_t v = 0; v < nTetrahedra; ++v) { + const Tetrahedron& tetrahedron = m_tetrahedra[v]; + if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { + volume += fabs(ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3])); + } + } + return volume / 6.0; +} +void TetrahedronSet::RevertAlignToPrincipalAxes() +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + double x, y, z; + for (size_t v = 0; v < nTetrahedra; ++v) { + Tetrahedron& tetrahedron = m_tetrahedra[v]; + for (int i = 0; i < 4; ++i) { + x = tetrahedron.m_pts[i][0] - m_barycenter[0]; + y = tetrahedron.m_pts[i][1] - m_barycenter[1]; + z = tetrahedron.m_pts[i][2] - m_barycenter[2]; + tetrahedron.m_pts[i][0] = m_Q[0][0] * x + m_Q[0][1] * y + m_Q[0][2] * z + m_barycenter[0]; + tetrahedron.m_pts[i][1] = m_Q[1][0] * x + m_Q[1][1] * y + m_Q[1][2] * z + m_barycenter[1]; + tetrahedron.m_pts[i][2] = m_Q[2][0] * x + m_Q[2][1] * y + m_Q[2][2] * z + m_barycenter[2]; + } + } + ComputeBB(); +} +void TetrahedronSet::ComputePrincipalAxes() +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + double covMat[3][3] = { { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }; + double x, y, z; + for (size_t v = 0; v < nTetrahedra; ++v) { + Tetrahedron& tetrahedron = m_tetrahedra[v]; + for (int i = 0; i < 4; ++i) { + x = tetrahedron.m_pts[i][0] - m_barycenter[0]; + y = tetrahedron.m_pts[i][1] - m_barycenter[1]; + z = tetrahedron.m_pts[i][2] - m_barycenter[2]; + covMat[0][0] += x * x; + covMat[1][1] += y * y; + covMat[2][2] += z * z; + covMat[0][1] += x * y; + covMat[0][2] += x * z; + covMat[1][2] += y * z; + } + } + double n = nTetrahedra * 4.0; + covMat[0][0] /= n; + covMat[1][1] /= n; + covMat[2][2] /= n; + covMat[0][1] /= n; + covMat[0][2] /= n; + covMat[1][2] /= n; + covMat[1][0] = covMat[0][1]; + covMat[2][0] = covMat[0][2]; + covMat[2][1] = covMat[1][2]; + Diagonalize(covMat, m_Q, m_D); +} +void TetrahedronSet::AlignToPrincipalAxes() +{ + const size_t nTetrahedra = m_tetrahedra.Size(); + if (nTetrahedra == 0) + return; + double x, y, z; + for (size_t v = 0; v < nTetrahedra; ++v) { + Tetrahedron& tetrahedron = m_tetrahedra[v]; + for (int i = 0; i < 4; ++i) { + x = tetrahedron.m_pts[i][0] - m_barycenter[0]; + y = tetrahedron.m_pts[i][1] - m_barycenter[1]; + z = tetrahedron.m_pts[i][2] - m_barycenter[2]; + tetrahedron.m_pts[i][0] = m_Q[0][0] * x + m_Q[1][0] * y + m_Q[2][0] * z + m_barycenter[0]; + tetrahedron.m_pts[i][1] = m_Q[0][1] * x + m_Q[1][1] * y + m_Q[2][1] * z + m_barycenter[1]; + tetrahedron.m_pts[i][2] = m_Q[0][2] * x + m_Q[1][2] * y + m_Q[2][2] * z + m_barycenter[2]; + } + } + ComputeBB(); +} +} diff --git a/Extras/VHACD/test/inc/oclHelper.h b/Extras/VHACD/test/inc/oclHelper.h new file mode 100644 index 000000000..ff63c3006 --- /dev/null +++ b/Extras/VHACD/test/inc/oclHelper.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifdef OPENCL_FOUND + +#pragma once +#ifndef OCL_HELPER_H +#define OCL_HELPER_H + +#include +#include + +#ifdef __MACH__ +#include +#else +#include +#endif + +class OCLHelper { +public: + OCLHelper(void){}; + ~OCLHelper(void){}; + bool InitPlatform(const unsigned int platformIndex = 0); + bool InitDevice(const unsigned int deviceIndex); + bool GetPlatformsInfo(std::vector& info, const std::string& indentation); + bool GetDevicesInfo(std::vector& info, const std::string& indentation); + cl_platform_id* GetPlatform() { return &m_platform; } + const cl_platform_id* GetPlatform() const { return &m_platform; } + cl_device_id* GetDevice() { return &m_device; } + const cl_device_id* GetDevice() const { return &m_device; } +private: + cl_platform_id m_platform; + cl_device_id m_device; + cl_int m_lastError; +}; + +#endif // OCL_HELPER_H + +#endif //OPENCL_FOUND diff --git a/Extras/VHACD/test/src/main.cpp b/Extras/VHACD/test/src/main.cpp new file mode 100644 index 000000000..6b1204726 --- /dev/null +++ b/Extras/VHACD/test/src/main.cpp @@ -0,0 +1,648 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define _CRTDBG_MAP_ALLOC + +#ifdef _CRTDBG_MAP_ALLOC +#include +#include +#endif // _CRTDBG_MAP_ALLOC + +#include "VHACD.h" + + +using namespace VHACD; +using namespace std; + +class MyCallback : public IVHACD::IUserCallback { +public: + MyCallback(void) {} + ~MyCallback(){}; + void Update(const double overallProgress, const double stageProgress, const double operationProgress, + const char* const stage, const char* const operation) + { + cout << setfill(' ') << setw(3) << (int)(overallProgress + 0.5) << "% " + << "[ " << stage << " " << setfill(' ') << setw(3) << (int)(stageProgress + 0.5) << "% ] " + << operation << " " << setfill(' ') << setw(3) << (int)(operationProgress + 0.5) << "%" << endl; + }; +}; +class MyLogger : public IVHACD::IUserLogger { +public: + MyLogger(void) {} + MyLogger(const string& fileName) { OpenFile(fileName); } + ~MyLogger(){}; + void Log(const char* const msg) + { + if (m_file.is_open()) { + m_file << msg; + m_file.flush(); + } + } + void OpenFile(const string& fileName) + { + m_file.open(fileName.c_str()); + } + +private: + ofstream m_file; +}; +struct Material { + + float m_diffuseColor[3]; + float m_ambientIntensity; + float m_specularColor[3]; + float m_emissiveColor[3]; + float m_shininess; + float m_transparency; + Material(void) + { + m_diffuseColor[0] = 0.5f; + m_diffuseColor[1] = 0.5f; + m_diffuseColor[2] = 0.5f; + m_specularColor[0] = 0.5f; + m_specularColor[1] = 0.5f; + m_specularColor[2] = 0.5f; + m_ambientIntensity = 0.4f; + m_emissiveColor[0] = 0.0f; + m_emissiveColor[1] = 0.0f; + m_emissiveColor[2] = 0.0f; + m_shininess = 0.4f; + m_transparency = 0.5f; + }; +}; +struct Parameters { + unsigned int m_oclPlatformID; + unsigned int m_oclDeviceID; + string m_fileNameIn; + string m_fileNameOut; + string m_fileNameLog; + bool m_run; + IVHACD::Parameters m_paramsVHACD; + Parameters(void) + { + m_run = true; + m_oclPlatformID = 0; + m_oclDeviceID = 0; + m_fileNameIn = ""; + m_fileNameOut = "output.obj"; + m_fileNameLog = "log.txt"; + } +}; +bool LoadOFF(const string& fileName, vector& points, vector& triangles, IVHACD::IUserLogger& logger); +bool LoadOBJ(const string& fileName, vector& points, vector& triangles, IVHACD::IUserLogger& logger); +bool SaveOFF(const string& fileName, const float* const& points, const int* const& triangles, const unsigned int& nPoints, + const unsigned int& nTriangles, IVHACD::IUserLogger& logger); +bool SaveOBJ(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints, + const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger, int convexPart, int vertexOffset); + +bool SaveVRML2(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints, + const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger); +void GetFileExtension(const string& fileName, string& fileExtension); +void ComputeRandomColor(Material& mat); +void Usage(const Parameters& params); +void ParseParameters(int argc, char* argv[], Parameters& params); + + +int main(int argc, char* argv[]) +{ + // --input camel.off --output camel_acd.obj --log log.txt --resolution 1000000 --depth 20 --concavity 0.0025 --planeDownsampling 4 --convexhullDownsampling 4 --alpha 0.05 --beta 0.05 --gamma 0.00125 --pca 0 --mode 0 --maxNumVerticesPerCH 256 --minVolumePerCH 0.0001 --convexhullApproximation 1 --oclDeviceID 2 + { + // set parameters + Parameters params; + ParseParameters(argc, argv, params); + MyCallback myCallback; + MyLogger myLogger(params.m_fileNameLog); + params.m_paramsVHACD.m_logger = &myLogger; + params.m_paramsVHACD.m_callback = &myCallback; + Usage(params); + if (!params.m_run) { + return 0; + } + + std::ostringstream msg; + + msg << "+ OpenCL (OFF)" << std::endl; + + msg << "+ Parameters" << std::endl; + msg << "\t input " << params.m_fileNameIn << endl; + msg << "\t resolution " << params.m_paramsVHACD.m_resolution << endl; + msg << "\t max. depth " << params.m_paramsVHACD.m_depth << endl; + msg << "\t max. concavity " << params.m_paramsVHACD.m_concavity << endl; + msg << "\t plane down-sampling " << params.m_paramsVHACD.m_planeDownsampling << endl; + msg << "\t convex-hull down-sampling " << params.m_paramsVHACD.m_convexhullDownsampling << endl; + msg << "\t alpha " << params.m_paramsVHACD.m_alpha << endl; + msg << "\t beta " << params.m_paramsVHACD.m_beta << endl; + msg << "\t gamma " << params.m_paramsVHACD.m_gamma << endl; + msg << "\t pca " << params.m_paramsVHACD.m_pca << endl; + msg << "\t mode " << params.m_paramsVHACD.m_mode << endl; + msg << "\t max. vertices per convex-hull " << params.m_paramsVHACD.m_maxNumVerticesPerCH << endl; + msg << "\t min. volume to add vertices to convex-hulls " << params.m_paramsVHACD.m_minVolumePerCH << endl; + msg << "\t convex-hull approximation " << params.m_paramsVHACD.m_convexhullApproximation << endl; + msg << "\t OpenCL acceleration " << params.m_paramsVHACD.m_oclAcceleration << endl; + msg << "\t OpenCL platform ID " << params.m_oclPlatformID << endl; + msg << "\t OpenCL device ID " << params.m_oclDeviceID << endl; + msg << "\t output " << params.m_fileNameOut << endl; + msg << "\t log " << params.m_fileNameLog << endl; + msg << "+ Load mesh" << std::endl; + myLogger.Log(msg.str().c_str()); + + cout << msg.str().c_str(); + + // load mesh + vector points; + vector triangles; + string fileExtension; + GetFileExtension(params.m_fileNameIn, fileExtension); + if (fileExtension == ".OFF") { + if (!LoadOFF(params.m_fileNameIn, points, triangles, myLogger)) { + return -1; + } + } + else if (fileExtension == ".OBJ") { + if (!LoadOBJ(params.m_fileNameIn, points, triangles, myLogger)) { + return -1; + } + } + else { + myLogger.Log("Format not supported!\n"); + return -1; + } + + // run V-HACD + IVHACD* interfaceVHACD = CreateVHACD(); + +#ifdef CL_VERSION_1_1 + if (params.m_paramsVHACD.m_oclAcceleration) { + bool res = interfaceVHACD->OCLInit(oclHelper.GetDevice(), &myLogger); + if (!res) { + params.m_paramsVHACD.m_oclAcceleration = false; + } + } +#endif //CL_VERSION_1_1 + bool res = interfaceVHACD->Compute(&points[0], 3, (unsigned int)points.size() / 3, + &triangles[0], 3, (unsigned int)triangles.size() / 3, params.m_paramsVHACD); + if (res) { + // save output + unsigned int nConvexHulls = interfaceVHACD->GetNConvexHulls(); + msg.str(""); + msg << "+ Generate output: " << nConvexHulls << " convex-hulls " << endl; + myLogger.Log(msg.str().c_str()); + ofstream foutCH(params.m_fileNameOut.c_str()); + IVHACD::ConvexHull ch; + if (foutCH.is_open()) { + Material mat; + int vertexOffset = 1;//obj wavefront starts counting at 1... + for (unsigned int p = 0; p < nConvexHulls; ++p) { + interfaceVHACD->GetConvexHull(p, ch); + + + SaveOBJ(foutCH, ch.m_points, ch.m_triangles, ch.m_nPoints, ch.m_nTriangles, mat, myLogger, p, vertexOffset); + vertexOffset+=ch.m_nPoints; + msg.str(""); + msg << "\t CH[" << setfill('0') << setw(5) << p << "] " << ch.m_nPoints << " V, " << ch.m_nTriangles << " T" << endl; + myLogger.Log(msg.str().c_str()); + } + foutCH.close(); + } + } + else { + myLogger.Log("Decomposition cancelled by user!\n"); + } + +#ifdef CL_VERSION_1_1 + if (params.m_paramsVHACD.m_oclAcceleration) { + bool res = interfaceVHACD->OCLRelease(&myLogger); + if (!res) { + assert(-1); + } + } +#endif //CL_VERSION_1_1 + + interfaceVHACD->Clean(); + interfaceVHACD->Release(); + } +#ifdef _CRTDBG_MAP_ALLOC + _CrtDumpMemoryLeaks(); +#endif // _CRTDBG_MAP_ALLOC + return 0; +} + +void Usage(const Parameters& params) +{ + std::ostringstream msg; + msg << "V-HACD V" << VHACD_VERSION_MAJOR << "." << VHACD_VERSION_MINOR << endl; + msg << "Syntax: testVHACD [options] --input infile.obj --output outfile.obj --log logfile.txt" << endl + << endl; + msg << "Options:" << endl; + msg << " --input Wavefront .obj input file name" << endl; + msg << " --output VRML 2.0 output file name" << endl; + msg << " --log Log file name" << endl; + msg << " --resolution Maximum number of voxels generated during the voxelization stage (default=100,000, range=10,000-16,000,000)" << endl; + msg << " --depth Maximum number of clipping stages. During each split stage, parts with a concavity higher than the user defined threshold are clipped according the \"best\" clipping plane (default=20, range=1-32)" << endl; + msg << " --concavity Maximum allowed concavity (default=0.0025, range=0.0-1.0)" << endl; + msg << " --planeDownsampling Controls the granularity of the search for the \"best\" clipping plane (default=4, range=1-16)" << endl; + msg << " --convexhullDownsampling Controls the precision of the convex-hull generation process during the clipping plane selection stage (default=4, range=1-16)" << endl; + msg << " --alpha Controls the bias toward clipping along symmetry planes (default=0.05, range=0.0-1.0)" << endl; + msg << " --beta Controls the bias toward clipping along revolution axes (default=0.05, range=0.0-1.0)" << endl; + msg << " --gamma Controls the maximum allowed concavity during the merge stage (default=0.00125, range=0.0-1.0)" << endl; + msg << " --delta Controls the bias toward maximaxing local concavity (default=0.05, range=0.0-1.0)" << endl; + msg << " --pca Enable/disable normalizing the mesh before applying the convex decomposition (default=0, range={0,1})" << endl; + msg << " --mode 0: voxel-based approximate convex decomposition, 1: tetrahedron-based approximate convex decomposition (default=0, range={0,1})" << endl; + msg << " --maxNumVerticesPerCH Controls the maximum number of triangles per convex-hull (default=64, range=4-1024)" << endl; + msg << " --minVolumePerCH Controls the adaptive sampling of the generated convex-hulls (default=0.0001, range=0.0-0.01)" << endl; + msg << " --convexhullApproximation Enable/disable approximation when computing convex-hulls (default=1, range={0,1})" << endl; + msg << " --oclAcceleration Enable/disable OpenCL acceleration (default=0, range={0,1})" << endl; + msg << " --oclPlatformID OpenCL platform id (default=0, range=0-# OCL platforms)" << endl; + msg << " --oclDeviceID OpenCL device id (default=0, range=0-# OCL devices)" << endl; + msg << " --help Print usage" << endl + << endl; + msg << "Examples:" << endl; + msg << " testVHACD.exe --input bunny.obj --output bunny_acd.obj --log log.txt" << endl + << endl; + cout << msg.str(); + if (params.m_paramsVHACD.m_logger) { + params.m_paramsVHACD.m_logger->Log(msg.str().c_str()); + } +} +void ParseParameters(int argc, char* argv[], Parameters& params) +{ + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--input")) { + if (++i < argc) + params.m_fileNameIn = argv[i]; + } + else if (!strcmp(argv[i], "--output")) { + if (++i < argc) + params.m_fileNameOut = argv[i]; + } + else if (!strcmp(argv[i], "--log")) { + if (++i < argc) + params.m_fileNameLog = argv[i]; + } + else if (!strcmp(argv[i], "--resolution")) { + if (++i < argc) + params.m_paramsVHACD.m_resolution = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--depth")) { + if (++i < argc) + params.m_paramsVHACD.m_depth = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--concavity")) { + if (++i < argc) + params.m_paramsVHACD.m_concavity = atof(argv[i]); + } + else if (!strcmp(argv[i], "--planeDownsampling")) { + if (++i < argc) + params.m_paramsVHACD.m_planeDownsampling = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--convexhullDownsampling")) { + if (++i < argc) + params.m_paramsVHACD.m_convexhullDownsampling = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--alpha")) { + if (++i < argc) + params.m_paramsVHACD.m_alpha = atof(argv[i]); + } + else if (!strcmp(argv[i], "--beta")) { + if (++i < argc) + params.m_paramsVHACD.m_beta = atof(argv[i]); + } + else if (!strcmp(argv[i], "--gamma")) { + if (++i < argc) + params.m_paramsVHACD.m_gamma = atof(argv[i]); + } + else if (!strcmp(argv[i], "--pca")) { + if (++i < argc) + params.m_paramsVHACD.m_pca = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--mode")) { + if (++i < argc) + params.m_paramsVHACD.m_mode = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--maxNumVerticesPerCH")) { + if (++i < argc) + params.m_paramsVHACD.m_maxNumVerticesPerCH = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--minVolumePerCH")) { + if (++i < argc) + params.m_paramsVHACD.m_minVolumePerCH = atof(argv[i]); + } + else if (!strcmp(argv[i], "--convexhullApproximation")) { + if (++i < argc) + params.m_paramsVHACD.m_convexhullApproximation = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--oclAcceleration")) { + if (++i < argc) + params.m_paramsVHACD.m_oclAcceleration = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--oclPlatformID")) { + if (++i < argc) + params.m_oclPlatformID = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--oclDeviceID")) { + if (++i < argc) + params.m_oclDeviceID = atoi(argv[i]); + } + else if (!strcmp(argv[i], "--help")) { + params.m_run = false; + } + } + params.m_paramsVHACD.m_resolution = (params.m_paramsVHACD.m_resolution < 64) ? 0 : params.m_paramsVHACD.m_resolution; + params.m_paramsVHACD.m_planeDownsampling = (params.m_paramsVHACD.m_planeDownsampling < 1) ? 1 : params.m_paramsVHACD.m_planeDownsampling; + params.m_paramsVHACD.m_convexhullDownsampling = (params.m_paramsVHACD.m_convexhullDownsampling < 1) ? 1 : params.m_paramsVHACD.m_convexhullDownsampling; +} + +void GetFileExtension(const string& fileName, string& fileExtension) +{ + size_t lastDotPosition = fileName.find_last_of("."); + if (lastDotPosition == string::npos) { + fileExtension = ""; + } + else { + fileExtension = fileName.substr(lastDotPosition, fileName.size()); + transform(fileExtension.begin(), fileExtension.end(), fileExtension.begin(), ::toupper); + } +} +void ComputeRandomColor(Material& mat) +{ + mat.m_diffuseColor[0] = mat.m_diffuseColor[1] = mat.m_diffuseColor[2] = 0.0f; + while (mat.m_diffuseColor[0] == mat.m_diffuseColor[1] || mat.m_diffuseColor[2] == mat.m_diffuseColor[1] || mat.m_diffuseColor[2] == mat.m_diffuseColor[0]) { + mat.m_diffuseColor[0] = (rand() % 100) / 100.0f; + mat.m_diffuseColor[1] = (rand() % 100) / 100.0f; + mat.m_diffuseColor[2] = (rand() % 100) / 100.0f; + } +} +bool LoadOFF(const string& fileName, vector& points, vector& triangles, IVHACD::IUserLogger& logger) +{ + FILE* fid = fopen(fileName.c_str(), "r"); + if (fid) { + const string strOFF("OFF"); + char temp[1024]; + fscanf(fid, "%s", temp); + if (string(temp) != strOFF) { + logger.Log("Loading error: format not recognized \n"); + fclose(fid); + return false; + } + else { + int nv = 0; + int nf = 0; + int ne = 0; + fscanf(fid, "%i", &nv); + fscanf(fid, "%i", &nf); + fscanf(fid, "%i", &ne); + points.resize(nv * 3); + triangles.resize(nf * 3); + const int np = nv * 3; + for (int p = 0; p < np; p++) { + fscanf(fid, "%f", &(points[p])); + } + int s; + for (int t = 0, r = 0; t < nf; ++t) { + fscanf(fid, "%i", &s); + if (s == 3) { + fscanf(fid, "%i", &(triangles[r++])); + fscanf(fid, "%i", &(triangles[r++])); + fscanf(fid, "%i", &(triangles[r++])); + } + else // Fix me: support only triangular meshes + { + for (int h = 0; h < s; ++h) + fscanf(fid, "%i", &s); + } + } + fclose(fid); + } + } + else { + logger.Log("Loading error: file not found \n"); + return false; + } + return true; +} +bool LoadOBJ(const string& fileName, vector& points, vector& triangles, IVHACD::IUserLogger& logger) +{ + const unsigned int BufferSize = 1024; + FILE* fid = fopen(fileName.c_str(), "r"); + + if (fid) { + char buffer[BufferSize]; + int ip[4]; + float x[3]; + char* pch; + char* str; + while (!feof(fid)) { + if (!fgets(buffer, BufferSize, fid)) { + break; + } + else if (buffer[0] == 'v') { + if (buffer[1] == ' ') { + str = buffer + 2; + for (int k = 0; k < 3; ++k) { + pch = strtok(str, " "); + if (pch) + x[k] = (float)atof(pch); + else { + return false; + } + str = NULL; + } + points.push_back(x[0]); + points.push_back(x[1]); + points.push_back(x[2]); + } + } + else if (buffer[0] == 'f') { + + pch = str = buffer + 2; + int k = 0; + while (pch) { + pch = strtok(str, " "); + if (pch) { + ip[k++] = atoi(pch) - 1; + } + else { + break; + } + str = NULL; + } + if (k == 3) { + triangles.push_back(ip[0]); + triangles.push_back(ip[1]); + triangles.push_back(ip[2]); + } + else if (k == 4) { + triangles.push_back(ip[0]); + triangles.push_back(ip[1]); + triangles.push_back(ip[2]); + + triangles.push_back(ip[0]); + triangles.push_back(ip[2]); + triangles.push_back(ip[3]); + } + } + } + fclose(fid); + } + else { + logger.Log("File not found\n"); + return false; + } + return true; +} +bool SaveOFF(const string& fileName, const float* const& points, const int* const& triangles, const unsigned int& nPoints, + const unsigned int& nTriangles, IVHACD::IUserLogger& logger) +{ + ofstream fout(fileName.c_str()); + if (fout.is_open()) { + size_t nV = nPoints * 3; + size_t nT = nTriangles * 3; + fout << "OFF" << std::endl; + fout << nPoints << " " << nTriangles << " " << 0 << std::endl; + for (size_t v = 0; v < nV; v += 3) { + fout << points[v + 0] << " " + << points[v + 1] << " " + << points[v + 2] << std::endl; + } + for (size_t f = 0; f < nT; f += 3) { + fout << "3 " << triangles[f + 0] << " " + << triangles[f + 1] << " " + << triangles[f + 2] << std::endl; + } + fout.close(); + return true; + } + else { + logger.Log("Can't open file\n"); + return false; + } +} + +bool SaveOBJ(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints, + const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger, int convexPart, int vertexOffset) +{ + if (fout.is_open()) { + + fout.setf(std::ios::fixed, std::ios::floatfield); + fout.setf(std::ios::showpoint); + fout.precision(6); + size_t nV = nPoints * 3; + size_t nT = nTriangles * 3; + + fout << "o convex_" << convexPart << std::endl; + + if (nV > 0) { + for (size_t v = 0; v < nV; v += 3) { + fout << "v " << points[v + 0] << " " << points[v + 1] << " " << points[v + 2] << std::endl; + } + } + if (nT > 0) { + for (size_t f = 0; f < nT; f += 3) { + fout << "f " + << triangles[f + 0]+vertexOffset << " " + << triangles[f + 1]+vertexOffset << " " + << triangles[f + 2]+vertexOffset << " " << std::endl; + } + } + return true; + } + else { + logger.Log("Can't open file\n"); + return false; + } +} + + + +bool SaveVRML2(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints, + const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger) +{ + if (fout.is_open()) { + fout.setf(std::ios::fixed, std::ios::floatfield); + fout.setf(std::ios::showpoint); + fout.precision(6); + size_t nV = nPoints * 3; + size_t nT = nTriangles * 3; + fout << "#VRML V2.0 utf8" << std::endl; + fout << "" << std::endl; + fout << "# Vertices: " << nPoints << std::endl; + fout << "# Triangles: " << nTriangles << std::endl; + fout << "" << std::endl; + fout << "Group {" << std::endl; + fout << " children [" << std::endl; + fout << " Shape {" << std::endl; + fout << " appearance Appearance {" << std::endl; + fout << " material Material {" << std::endl; + fout << " diffuseColor " << material.m_diffuseColor[0] << " " + << material.m_diffuseColor[1] << " " + << material.m_diffuseColor[2] << std::endl; + fout << " ambientIntensity " << material.m_ambientIntensity << std::endl; + fout << " specularColor " << material.m_specularColor[0] << " " + << material.m_specularColor[1] << " " + << material.m_specularColor[2] << std::endl; + fout << " emissiveColor " << material.m_emissiveColor[0] << " " + << material.m_emissiveColor[1] << " " + << material.m_emissiveColor[2] << std::endl; + fout << " shininess " << material.m_shininess << std::endl; + fout << " transparency " << material.m_transparency << std::endl; + fout << " }" << std::endl; + fout << " }" << std::endl; + fout << " geometry IndexedFaceSet {" << std::endl; + fout << " ccw TRUE" << std::endl; + fout << " solid TRUE" << std::endl; + fout << " convex TRUE" << std::endl; + if (nV > 0) { + fout << " coord DEF co Coordinate {" << std::endl; + fout << " point [" << std::endl; + for (size_t v = 0; v < nV; v += 3) { + fout << " " << points[v + 0] << " " + << points[v + 1] << " " + << points[v + 2] << "," << std::endl; + } + fout << " ]" << std::endl; + fout << " }" << std::endl; + } + if (nT > 0) { + fout << " coordIndex [ " << std::endl; + for (size_t f = 0; f < nT; f += 3) { + fout << " " << triangles[f + 0] << ", " + << triangles[f + 1] << ", " + << triangles[f + 2] << ", -1," << std::endl; + } + fout << " ]" << std::endl; + } + fout << " }" << std::endl; + fout << " }" << std::endl; + fout << " ]" << std::endl; + fout << "}" << std::endl; + return true; + } + else { + logger.Log("Can't open file\n"); + return false; + } +} diff --git a/Extras/VHACD/test/src/oclHelper.cpp b/Extras/VHACD/test/src/oclHelper.cpp new file mode 100644 index 000000000..efaa9fa99 --- /dev/null +++ b/Extras/VHACD/test/src/oclHelper.cpp @@ -0,0 +1,329 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) +All rights reserved. + + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifdef OPENCL_FOUND + +#include "oclHelper.h" +#include +#include + +bool OCLHelper::InitPlatform(const unsigned int platformIndex) +{ + cl_uint numPlatforms; + m_lastError = clGetPlatformIDs(1, NULL, &numPlatforms); + if (m_lastError != CL_SUCCESS || platformIndex >= numPlatforms) + return false; + + cl_platform_id* platforms = new cl_platform_id[numPlatforms]; + m_lastError = clGetPlatformIDs(numPlatforms, platforms, NULL); + if (m_lastError != CL_SUCCESS) { + delete[] platforms; + return false; + } + + m_platform = platforms[platformIndex]; + delete[] platforms; + return true; +} +bool OCLHelper::GetPlatformsInfo(std::vector& info, const std::string& indentation) +{ + const char* platformInfoParameters[] = { "CL_PLATFORM_NAME", + "CL_PLATFORM_VENDOR", + "CL_PLATFORM_VERSION", + "CL_PLATFORM_PROFILE", + "CL_PLATFORM_EXTENSIONS" }; + + cl_uint numPlatforms; + m_lastError = clGetPlatformIDs(1, NULL, &numPlatforms); + if (m_lastError != CL_SUCCESS) + return false; + + cl_platform_id* platforms = new cl_platform_id[numPlatforms]; + m_lastError = clGetPlatformIDs(numPlatforms, platforms, NULL); + if (m_lastError != CL_SUCCESS) { + delete[] platforms; + return false; + } + + size_t bufferSize = 4096; + char* buffer = new char[bufferSize]; + size_t size; + info.resize(numPlatforms); + for (cl_uint i = 0; i < numPlatforms; ++i) { + for (int j = CL_PLATFORM_PROFILE; j <= CL_PLATFORM_EXTENSIONS; ++j) { + info[i] += indentation + platformInfoParameters[j - CL_PLATFORM_PROFILE] + std::string(": "); + m_lastError = clGetPlatformInfo(platforms[i], j, 0, NULL, &size); + if (m_lastError != CL_SUCCESS) { + delete[] buffer; + delete[] platforms; + return false; + } + if (bufferSize < size) { + delete[] buffer; + bufferSize = size; + buffer = new char[bufferSize]; + } + m_lastError = clGetPlatformInfo(platforms[i], j, size, buffer, NULL); + if (m_lastError != CL_SUCCESS) { + delete[] buffer; + delete[] platforms; + return false; + } + info[i] += buffer + std::string("\n"); + } + } + delete[] platforms; + delete[] buffer; + return true; +} +bool OCLHelper::InitDevice(const unsigned int deviceIndex) +{ + cl_uint numDevices; + m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices); + if (m_lastError != CL_SUCCESS || deviceIndex >= numDevices) + return false; + + cl_device_id* devices = new cl_device_id[numDevices]; + m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, numDevices, devices, NULL); + if (m_lastError != CL_SUCCESS) { + delete[] devices; + return false; + } + m_device = devices[deviceIndex]; + delete[] devices; + return true; +} +bool OCLHelper::GetDevicesInfo(std::vector& info, const std::string& indentation) +{ + enum { + DATA_TYPE_CL_UINT, + DATA_TYPE_CL_BOOL, + DATA_TYPE_STRING, + DATA_TYPE_CL_ULONG, + DATA_TYPE_CL_DEVICE_FP_CONFIG, + DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES, + DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE, + DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE, + DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP, + DATA_TYPE_CL_DEVICE_TYPE, + DATA_TYPE_SIZE_T, + DATA_TYPE_SIZE_T_3, + }; + typedef struct + { + cl_device_info id; + const char* name; + int type; + } DeviceInfoParam; + const int numDeviceInfoParameters = 49; + const DeviceInfoParam deviceInfoParameters[numDeviceInfoParameters] = { + { CL_DEVICE_NAME, "CL_DEVICE_NAME", DATA_TYPE_STRING }, + { CL_DEVICE_PROFILE, "CL_DEVICE_PROFILE", DATA_TYPE_STRING }, + { CL_DEVICE_VENDOR, "CL_DEVICE_VENDOR", DATA_TYPE_STRING }, + { CL_DEVICE_VERSION, "CL_DEVICE_VERSION", DATA_TYPE_STRING }, + { CL_DRIVER_VERSION, "CL_DRIVER_VERSION", DATA_TYPE_STRING }, + { CL_DEVICE_EXTENSIONS, "CL_DEVICE_EXTENSIONS", DATA_TYPE_STRING }, + { CL_DEVICE_VERSION, "CL_DEVICE_VERSION", DATA_TYPE_STRING }, + { CL_DEVICE_ADDRESS_BITS, "CL_DEVICE_ADDRESS_BITS", DATA_TYPE_CL_UINT }, + { CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, "CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MAX_CLOCK_FREQUENCY, "CL_DEVICE_MAX_CLOCK_FREQUENCY", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MAX_COMPUTE_UNITS, "CL_DEVICE_MAX_COMPUTE_UNITS", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MAX_CONSTANT_ARGS, "CL_DEVICE_MAX_CONSTANT_ARGS", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MAX_READ_IMAGE_ARGS, "CL_DEVICE_MAX_READ_IMAGE_ARGS", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MAX_SAMPLERS, "CL_DEVICE_MAX_SAMPLERS", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MAX_WRITE_IMAGE_ARGS, "CL_DEVICE_MAX_WRITE_IMAGE_ARGS", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MEM_BASE_ADDR_ALIGN, "CL_DEVICE_MEM_BASE_ADDR_ALIGN", DATA_TYPE_CL_UINT }, + { CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, "CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE", DATA_TYPE_CL_UINT }, + { CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR", DATA_TYPE_CL_UINT }, + { CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT", DATA_TYPE_CL_UINT }, + { CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT", DATA_TYPE_CL_UINT }, + { CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG", DATA_TYPE_CL_UINT }, + { CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT", DATA_TYPE_CL_UINT }, + { CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE", DATA_TYPE_CL_UINT }, + { CL_DEVICE_VENDOR_ID, "CL_DEVICE_VENDOR_ID", DATA_TYPE_CL_UINT }, + { CL_DEVICE_AVAILABLE, "CL_DEVICE_AVAILABLE", DATA_TYPE_CL_BOOL }, + { CL_DEVICE_COMPILER_AVAILABLE, "CL_DEVICE_COMPILER_AVAILABLE", DATA_TYPE_CL_BOOL }, + { CL_DEVICE_ENDIAN_LITTLE, "CL_DEVICE_ENDIAN_LITTLE", DATA_TYPE_CL_BOOL }, + { CL_DEVICE_ERROR_CORRECTION_SUPPORT, "CL_DEVICE_ERROR_CORRECTION_SUPPORT", DATA_TYPE_CL_BOOL }, + { CL_DEVICE_IMAGE_SUPPORT, "CL_DEVICE_IMAGE_SUPPORT", DATA_TYPE_CL_BOOL }, + { CL_DEVICE_EXECUTION_CAPABILITIES, "CL_DEVICE_EXECUTION_CAPABILITIES", DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES }, + { CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, "CL_DEVICE_GLOBAL_MEM_CACHE_SIZE", DATA_TYPE_CL_ULONG }, + { CL_DEVICE_GLOBAL_MEM_SIZE, "CL_DEVICE_GLOBAL_MEM_SIZE", DATA_TYPE_CL_ULONG }, + { CL_DEVICE_LOCAL_MEM_SIZE, "CL_DEVICE_LOCAL_MEM_SIZE", DATA_TYPE_CL_ULONG }, + { CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, "CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE", DATA_TYPE_CL_ULONG }, + { CL_DEVICE_MAX_MEM_ALLOC_SIZE, "CL_DEVICE_MAX_MEM_ALLOC_SIZE", DATA_TYPE_CL_ULONG }, + { CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, "CL_DEVICE_GLOBAL_MEM_CACHE_TYPE", DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE }, + { CL_DEVICE_IMAGE2D_MAX_HEIGHT, "CL_DEVICE_IMAGE2D_MAX_HEIGHT", DATA_TYPE_SIZE_T }, + { CL_DEVICE_IMAGE2D_MAX_WIDTH, "CL_DEVICE_IMAGE2D_MAX_WIDTH", DATA_TYPE_SIZE_T }, + { CL_DEVICE_IMAGE3D_MAX_DEPTH, "CL_DEVICE_IMAGE3D_MAX_DEPTH", DATA_TYPE_SIZE_T }, + { CL_DEVICE_IMAGE3D_MAX_HEIGHT, "CL_DEVICE_IMAGE3D_MAX_HEIGHT", DATA_TYPE_SIZE_T }, + { CL_DEVICE_IMAGE3D_MAX_WIDTH, "CL_DEVICE_IMAGE3D_MAX_WIDTH", DATA_TYPE_SIZE_T }, + { CL_DEVICE_MAX_PARAMETER_SIZE, "CL_DEVICE_MAX_PARAMETER_SIZE", DATA_TYPE_SIZE_T }, + { CL_DEVICE_MAX_WORK_GROUP_SIZE, "CL_DEVICE_MAX_WORK_GROUP_SIZE", DATA_TYPE_SIZE_T }, + { CL_DEVICE_PROFILING_TIMER_RESOLUTION, "CL_DEVICE_PROFILING_TIMER_RESOLUTION", DATA_TYPE_SIZE_T }, + { CL_DEVICE_QUEUE_PROPERTIES, "CL_DEVICE_QUEUE_PROPERTIES", DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP }, + { CL_DEVICE_TYPE, "CL_DEVICE_TYPE", DATA_TYPE_CL_DEVICE_TYPE }, + { CL_DEVICE_LOCAL_MEM_TYPE, "CL_DEVICE_LOCAL_MEM_TYPE", DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE }, + { CL_DEVICE_MAX_WORK_ITEM_SIZES, "CL_DEVICE_MAX_WORK_ITEM_SIZES", DATA_TYPE_SIZE_T_3 } + // { CL_DEVICE_DOUBLE_FP_CONFIG, "CL_DEVICE_DOUBLE_FP_CONFIG", DATA_TYPE_CL_DEVICE_FP_CONFIG }, + // + }; + cl_uint numDevices; + m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices); + if (m_lastError != CL_SUCCESS) + return false; + + cl_device_id* devices = new cl_device_id[numDevices]; + m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, numDevices, devices, NULL); + if (m_lastError != CL_SUCCESS) { + delete[] devices; + return false; + } + size_t bufferSize = 4096; + char* buffer = new char[bufferSize]; + size_t size; + info.resize(numDevices); + + for (cl_uint i = 0; i < numDevices; ++i) { + for (int j = 0; j < numDeviceInfoParameters; ++j) { + const DeviceInfoParam& infoParam = deviceInfoParameters[j]; + info[i] += indentation + infoParam.name + std::string(": "); + + if (infoParam.type == DATA_TYPE_STRING) { + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, 0, NULL, &size); + if (m_lastError == CL_SUCCESS) { + if (bufferSize < size) { + delete[] buffer; + bufferSize = size; + buffer = new char[bufferSize]; + } + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, size, buffer, NULL); + if (m_lastError != CL_SUCCESS) { + delete[] devices; + delete[] buffer; + return false; + } + info[i] += buffer + std::string("\n"); + } + } + else if (infoParam.type == DATA_TYPE_CL_UINT) { + cl_uint value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_uint), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_CL_BOOL) { + cl_bool value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_bool), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_CL_ULONG) { + cl_ulong value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_ulong), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_CL_DEVICE_FP_CONFIG) { + cl_device_fp_config value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_fp_config), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES) { + cl_device_exec_capabilities value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_exec_capabilities), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE) { + cl_device_mem_cache_type value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_mem_cache_type), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE) { + cl_device_local_mem_type value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_local_mem_type), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP) { + cl_command_queue_properties value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_command_queue_properties), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_CL_DEVICE_TYPE) { + cl_device_type value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_type), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_SIZE_T) { + size_t value; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(size_t), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << value; + info[i] += svalue.str() + "\n"; + } + } + else if (infoParam.type == DATA_TYPE_SIZE_T_3) { + size_t value[3]; + m_lastError = clGetDeviceInfo(devices[i], infoParam.id, 3 * sizeof(size_t), &value, &size); + if (m_lastError == CL_SUCCESS) { + std::ostringstream svalue; + svalue << "(" << value[0] << ", " << value[1] << ", " << value[2] << ")"; + info[i] += svalue.str() + "\n"; + } + } + else { + assert(0); + } + } + } + delete[] devices; + delete[] buffer; + return true; +} +#endif // OPENCL_FOUND diff --git a/Extras/VHACD/test/src/premake4.lua b/Extras/VHACD/test/src/premake4.lua new file mode 100644 index 000000000..a329f5439 --- /dev/null +++ b/Extras/VHACD/test/src/premake4.lua @@ -0,0 +1,25 @@ + +project "test_vhacd" + +if _OPTIONS["ios"] then + kind "WindowedApp" +else + kind "ConsoleApp" +end + +includedirs {"../../public"} + +links { + "vhacd" +} + +language "C++" + +files { + "main.cpp", +} + + +if os.is("Linux") then + links {"pthread"} +end \ No newline at end of file diff --git a/Extras/premake4.lua b/Extras/premake4.lua index dd4853f6e..c3660783a 100644 --- a/Extras/premake4.lua +++ b/Extras/premake4.lua @@ -1,5 +1,6 @@ include "HACD" +include "VHACD" include "ConvexDecomposition" include "InverseDynamics" include "Serialize/BulletFileLoader" From 96c1ee42565d951347e515e40f41f71d0963d2d0 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Mon, 26 Sep 2016 07:55:30 -0700 Subject: [PATCH 4/9] revert default contact 'erp' (error-reduction-parameter) from 0.8 to 0.2 as it used to be. --- data/multibody.bullet | Bin 14488 -> 14660 bytes .../ConstraintSolver/btContactSolverInfo.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/data/multibody.bullet b/data/multibody.bullet index 19dacc389d0aa171f6386162662e30c365b555b6..70f15a27e117b1ec111c66b925cf5508c751ad28 100644 GIT binary patch delta 1049 zcmaiy&ubG=5Xa|jc9Tt-#*In(yEW0MiKWtt#dv73X@pRMrbcOn3OUIh(;h@naP`!K zhpyuxqDT*(JSn|+uu-W8kyd&Udh9U={{V0LousKT2+s0(Z)RtBGjHBhmp`w>vNsBa z%hzv@-8q+;%q)L4XWP3njeM5Od<+1yRih)n3h*uj5n4e??1;nbL0 z8{l<+A1~=!$UpDydik3kV8?iDWEwHu-d;I?enB|pO@_rNLMx+DO&51yS z9dFN(KM?`^4@d;R$FX1aP=~(@{x0#Gl0}j{@kKALKwkzE0Ii!I!9II6JJ*N11)&Qm z(EduzT9&m47kpVhqg!sxoOM1Rs!jf9lhX`7jB^N=T1ZQxRsoF{f!$d^^Qvt|UjJ*f+f z2EZT~0>eOhe+WyQkDTO?t?E=& vt7@N2wWn<5XlvR>eUI)co2tlU<7O$WW$=aUpsL`=eQj0kQf1@&=vMGI-p$WL delta 1354 zcmZ`(O-NKx6h8O)W}Lqu@h?N{NrM>LkforMj`I^}O@C^XFj7-9H3K^TN}%9GLM@CM z?#3iY$VEg@U<)_;MhpoV!9^fMEuu|}7UnLD>YQ<&-7N3p%=_MV&iTH3@4NHf#3y1! z&Wj$;$(|cmhK@Ejxi5L#!F1_xg&zR)7~L2QgN6=6<}tY?y-_2rOT3Cz8&lKKxq0MM zyn|Us59m4;zg~zg(0wPa^*+gH{C}{EasuxnjKaB*Hvz113Hm#60~zOp@S zp3LFA$Zl9PQiTbq=~zj%oO`JZx}5Ibbv#`Ld_Ih!9kfFJ&0y!x)w;pS<@e#ds&k-Em9gg#j*k{s<9?yi?Nd&v*mZIp-+@PS%3B7DJo2kuMaUNe`RS){=Jm0|87qNRRq3 zP@+t#f&!GsYhqy`^MQfRt!r!p%8-H69AzbVb66 zWZV}DC+n3^QVIGJ%8)M}iVP-{&_FaE843&p!pVe9=?R9A3SaLZ^ar+cekBnl6{_ve zRr->lNLcZO{mQk#wmrQo(ca9fO{e>ar5*W&gI51Df2U6btwiI$3q0*4@s!u<=@Ua* z8c#XKi*!}|uA#-WavZ>Y*3n?F&DhM(6`cjFVBjC?Dn@%?40pc~uM%iS1sTtf3<^}j z31|inUi%QywHNblSY^N_;4;iY5WcXf0z Date: Tue, 27 Sep 2016 08:33:53 -0700 Subject: [PATCH 5/9] pybullet premake, keep extension --- examples/pybullet/premake4.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/pybullet/premake4.lua b/examples/pybullet/premake4.lua index 86b4eedc5..6f684a331 100644 --- a/examples/pybullet/premake4.lua +++ b/examples/pybullet/premake4.lua @@ -3,8 +3,7 @@ project ("pybullet") language "C++" kind "SharedLib" - targetsuffix ("") - targetprefix ("") + includedirs {"../../src", "../../examples", "../../examples/ThirdPartyLibs"} defines {"PHYSICS_IN_PROCESS_EXAMPLE_BROWSER"} From 0936ae66000b8e2f8f8ce70ae936775327c3d244 Mon Sep 17 00:00:00 2001 From: erwin coumans Date: Tue, 27 Sep 2016 12:13:45 -0700 Subject: [PATCH 6/9] expose getNumBodies, getBodyUniqueId, getBodyInfo (char* baseName) to shared memory API and pybullet., making it easier to serialize the state of the world. --- examples/SharedMemory/PhysicsClient.h | 10 +- examples/SharedMemory/PhysicsClientC_API.cpp | 23 +++++ examples/SharedMemory/PhysicsClientC_API.h | 9 ++ .../SharedMemory/PhysicsClientExample.cpp | 2 +- .../PhysicsClientSharedMemory.cpp | 45 +++++++-- .../SharedMemory/PhysicsClientSharedMemory.h | 10 +- examples/SharedMemory/PhysicsDirect.cpp | 40 +++++++- examples/SharedMemory/PhysicsDirect.h | 6 ++ examples/SharedMemory/PhysicsLoopBack.cpp | 15 +++ examples/SharedMemory/PhysicsLoopBack.h | 6 ++ .../PhysicsServerCommandProcessor.cpp | 4 + examples/SharedMemory/SharedMemoryPublic.h | 7 ++ examples/pybullet/pybullet.c | 96 ++++++++++++++++++- 13 files changed, 250 insertions(+), 23 deletions(-) diff --git a/examples/SharedMemory/PhysicsClient.h b/examples/SharedMemory/PhysicsClient.h index b76e7e18c..3fc0fa16a 100644 --- a/examples/SharedMemory/PhysicsClient.h +++ b/examples/SharedMemory/PhysicsClient.h @@ -24,9 +24,15 @@ public: virtual bool submitClientCommand(const struct SharedMemoryCommand& command) = 0; - virtual int getNumJoints(int bodyIndex) const = 0; + virtual int getNumBodies() const = 0; - virtual bool getJointInfo(int bodyIndex, int jointIndex, struct b3JointInfo& info) const = 0; + virtual int getBodyUniqueId(int serialIndex) const = 0; + + virtual bool getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const = 0; + + virtual int getNumJoints(int bodyUniqueId) const = 0; + + virtual bool getJointInfo(int bodyUniqueId, int jointIndex, struct b3JointInfo& info) const = 0; virtual void setSharedMemoryKey(int key) = 0; diff --git a/examples/SharedMemory/PhysicsClientC_API.cpp b/examples/SharedMemory/PhysicsClientC_API.cpp index 61a192fa8..a9f7cc56f 100644 --- a/examples/SharedMemory/PhysicsClientC_API.cpp +++ b/examples/SharedMemory/PhysicsClientC_API.cpp @@ -739,6 +739,29 @@ b3SharedMemoryStatusHandle b3SubmitClientCommandAndWaitStatus(b3PhysicsClientHan } +///return the total number of bodies in the simulation +int b3GetNumBodies(b3PhysicsClientHandle physClient) +{ + PhysicsClient* cl = (PhysicsClient* ) physClient; + return cl->getNumBodies(); +} + +/// return the body unique id, given the index in range [0 , b3GetNumBodies() ) +int b3GetBodyUniqueId(b3PhysicsClientHandle physClient, int serialIndex) +{ + PhysicsClient* cl = (PhysicsClient* ) physClient; + return cl->getBodyUniqueId(serialIndex); +} + +///given a body unique id, return the body information. See b3BodyInfo in SharedMemoryPublic.h +int b3GetBodyInfo(b3PhysicsClientHandle physClient, int bodyUniqueId, struct b3BodyInfo* info) +{ + PhysicsClient* cl = (PhysicsClient* ) physClient; + return cl->getBodyInfo(bodyUniqueId,*info); +} + + + int b3GetNumJoints(b3PhysicsClientHandle physClient, int bodyId) { PhysicsClient* cl = (PhysicsClient* ) physClient; diff --git a/examples/SharedMemory/PhysicsClientC_API.h b/examples/SharedMemory/PhysicsClientC_API.h index a57851fd8..d35f472fb 100644 --- a/examples/SharedMemory/PhysicsClientC_API.h +++ b/examples/SharedMemory/PhysicsClientC_API.h @@ -53,6 +53,15 @@ int b3GetStatusActualState(b3SharedMemoryStatusHandle statusHandle, const double* actualStateQdot[], const double* jointReactionForces[]); +///return the total number of bodies in the simulation +int b3GetNumBodies(b3PhysicsClientHandle physClient); + +/// return the body unique id, given the index in range [0 , b3GetNumBodies() ) +int b3GetBodyUniqueId(b3PhysicsClientHandle physClient, int serialIndex); + +///given a body unique id, return the body information. See b3BodyInfo in SharedMemoryPublic.h +int b3GetBodyInfo(b3PhysicsClientHandle physClient, int bodyUniqueId, struct b3BodyInfo* info); + ///give a unique body index (after loading the body) return the number of joints. int b3GetNumJoints(b3PhysicsClientHandle physClient, int bodyIndex); diff --git a/examples/SharedMemory/PhysicsClientExample.cpp b/examples/SharedMemory/PhysicsClientExample.cpp index 07ba317a4..8ba6cfca9 100644 --- a/examples/SharedMemory/PhysicsClientExample.cpp +++ b/examples/SharedMemory/PhysicsClientExample.cpp @@ -624,7 +624,7 @@ void PhysicsClientExample::initPhysics() { MyCallback(CMD_LOAD_URDF, true, this); MyCallback(CMD_STEP_FORWARD_SIMULATION,true,this); - MyCallback(CMD_STEP_FORWARD_SIMULATION,true,this); + MyCallback(CMD_RESET_SIMULATION,true,this); } diff --git a/examples/SharedMemory/PhysicsClientSharedMemory.cpp b/examples/SharedMemory/PhysicsClientSharedMemory.cpp index e622cf716..1ed323289 100644 --- a/examples/SharedMemory/PhysicsClientSharedMemory.cpp +++ b/examples/SharedMemory/PhysicsClientSharedMemory.cpp @@ -18,6 +18,7 @@ struct BodyJointInfoCache { + std::string m_baseName; btAlignedObjectArray m_jointInfo; }; @@ -74,10 +75,37 @@ struct PhysicsClientSharedMemoryInternalData { - -int PhysicsClientSharedMemory::getNumJoints(int bodyIndex) const +int PhysicsClientSharedMemory::getNumBodies() const { - BodyJointInfoCache** bodyJointsPtr = m_data->m_bodyJointMap[bodyIndex]; + return m_data->m_bodyJointMap.size(); +} + +int PhysicsClientSharedMemory::getBodyUniqueId(int serialIndex) const +{ + if ((serialIndex >= 0) && (serialIndex < getNumBodies())) + { + return m_data->m_bodyJointMap.getKeyAtIndex(serialIndex).getUid1(); + } + return -1; +} + +bool PhysicsClientSharedMemory::getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const +{ + BodyJointInfoCache** bodyJointsPtr = m_data->m_bodyJointMap[bodyUniqueId]; + if (bodyJointsPtr && *bodyJointsPtr) + { + BodyJointInfoCache* bodyJoints = *bodyJointsPtr; + info.m_baseName = bodyJoints->m_baseName.c_str(); + return true; + } + return false; +} + + + +int PhysicsClientSharedMemory::getNumJoints(int bodyUniqueId) const +{ + BodyJointInfoCache** bodyJointsPtr = m_data->m_bodyJointMap[bodyUniqueId]; if (bodyJointsPtr && *bodyJointsPtr) { BodyJointInfoCache* bodyJoints = *bodyJointsPtr; @@ -88,9 +116,9 @@ int PhysicsClientSharedMemory::getNumJoints(int bodyIndex) const } -bool PhysicsClientSharedMemory::getJointInfo(int bodyIndex, int jointIndex, b3JointInfo& info) const +bool PhysicsClientSharedMemory::getJointInfo(int bodyUniqueId, int jointIndex, b3JointInfo& info) const { - BodyJointInfoCache** bodyJointsPtr = m_data->m_bodyJointMap[bodyIndex]; + BodyJointInfoCache** bodyJointsPtr = m_data->m_bodyJointMap[bodyUniqueId]; if (bodyJointsPtr && *bodyJointsPtr) { BodyJointInfoCache* bodyJoints = *bodyJointsPtr; @@ -196,12 +224,13 @@ void PhysicsClientSharedMemory::processBodyJointInfo(int bodyUniqueId, const Sha Bullet::btMultiBodyDoubleData* mb = (Bullet::btMultiBodyDoubleData*)bf.m_multiBodies[i]; + bodyJoints->m_baseName = mb->m_baseName; addJointInfoFromMultiBodyData(mb,bodyJoints, m_data->m_verboseOutput); } else { Bullet::btMultiBodyFloatData* mb = (Bullet::btMultiBodyFloatData*)bf.m_multiBodies[i]; - + bodyJoints->m_baseName = mb->m_baseName; addJointInfoFromMultiBodyData(mb,bodyJoints, m_data->m_verboseOutput); } } @@ -272,10 +301,10 @@ const SharedMemoryStatus* PhysicsClientSharedMemory::processServerStatus() { serverCmd.m_dataStreamArguments.m_streamChunkLength); bf.setFileDNAisMemoryDNA(); bf.parse(false); - int bodyIndex = serverCmd.m_dataStreamArguments.m_bodyUniqueId; + int bodyUniqueId = serverCmd.m_dataStreamArguments.m_bodyUniqueId; BodyJointInfoCache* bodyJoints = new BodyJointInfoCache; - m_data->m_bodyJointMap.insert(bodyIndex,bodyJoints); + m_data->m_bodyJointMap.insert(bodyUniqueId,bodyJoints); for (int i = 0; i < bf.m_multiBodies.size(); i++) { diff --git a/examples/SharedMemory/PhysicsClientSharedMemory.h b/examples/SharedMemory/PhysicsClientSharedMemory.h index 69e5ed64d..1d0047bff 100644 --- a/examples/SharedMemory/PhysicsClientSharedMemory.h +++ b/examples/SharedMemory/PhysicsClientSharedMemory.h @@ -34,9 +34,15 @@ public: virtual bool submitClientCommand(const struct SharedMemoryCommand& command); - virtual int getNumJoints(int bodyIndex) const; + virtual int getNumBodies() const; - virtual bool getJointInfo(int bodyIndex, int jointIndex, struct b3JointInfo& info) const; + virtual int getBodyUniqueId(int serialIndex) const; + + virtual bool getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const; + + virtual int getNumJoints(int bodyUniqueId) const; + + virtual bool getJointInfo(int bodyUniqueId, int jointIndex, struct b3JointInfo& info) const; virtual void setSharedMemoryKey(int key); diff --git a/examples/SharedMemory/PhysicsDirect.cpp b/examples/SharedMemory/PhysicsDirect.cpp index 6ac5d6212..3367b8323 100644 --- a/examples/SharedMemory/PhysicsDirect.cpp +++ b/examples/SharedMemory/PhysicsDirect.cpp @@ -9,10 +9,11 @@ #include "../../Extras/Serialize/BulletFileLoader/btBulletFile.h" #include "../../Extras/Serialize/BulletFileLoader/autogenerated/bullet.h" #include "BodyJointInfoUtility.h" - +#include struct BodyJointInfoCache2 { + std::string m_baseName; btAlignedObjectArray m_jointInfo; }; @@ -329,8 +330,7 @@ void PhysicsDirect::processBodyJointInfo(int bodyUniqueId, const SharedMemorySta serverCmd.m_dataStreamArguments.m_streamChunkLength); bf.setFileDNAisMemoryDNA(); bf.parse(false); - - + BodyJointInfoCache2* bodyJoints = new BodyJointInfoCache2; m_data->m_bodyJointMap.insert(bodyUniqueId,bodyJoints); @@ -341,13 +341,15 @@ void PhysicsDirect::processBodyJointInfo(int bodyUniqueId, const SharedMemorySta { Bullet::btMultiBodyDoubleData* mb = (Bullet::btMultiBodyDoubleData*)bf.m_multiBodies[i]; - + bodyJoints->m_baseName = mb->m_baseName; + addJointInfoFromMultiBodyData(mb,bodyJoints, m_data->m_verboseOutput); } else { Bullet::btMultiBodyFloatData* mb = (Bullet::btMultiBodyFloatData*)bf.m_multiBodies[i]; - + + bodyJoints->m_baseName = mb->m_baseName; addJointInfoFromMultiBodyData(mb,bodyJoints, m_data->m_verboseOutput); } } @@ -458,6 +460,34 @@ bool PhysicsDirect::submitClientCommand(const struct SharedMemoryCommand& comman return hasStatus; } +int PhysicsDirect::getNumBodies() const +{ + return m_data->m_bodyJointMap.size(); +} + + +int PhysicsDirect::getBodyUniqueId(int serialIndex) const +{ + if ((serialIndex >= 0) && (serialIndex < getNumBodies())) + { + return m_data->m_bodyJointMap.getKeyAtIndex(serialIndex).getUid1(); + } + return -1; +} + +bool PhysicsDirect::getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const +{ + BodyJointInfoCache2** bodyJointsPtr = m_data->m_bodyJointMap[bodyUniqueId]; + if (bodyJointsPtr && *bodyJointsPtr) + { + BodyJointInfoCache2* bodyJoints = *bodyJointsPtr; + info.m_baseName = bodyJoints->m_baseName.c_str(); + return true; + } + + return false; +} + int PhysicsDirect::getNumJoints(int bodyIndex) const { BodyJointInfoCache2** bodyJointsPtr = m_data->m_bodyJointMap[bodyIndex]; diff --git a/examples/SharedMemory/PhysicsDirect.h b/examples/SharedMemory/PhysicsDirect.h index eae128998..e312823e2 100644 --- a/examples/SharedMemory/PhysicsDirect.h +++ b/examples/SharedMemory/PhysicsDirect.h @@ -49,6 +49,12 @@ public: virtual bool submitClientCommand(const struct SharedMemoryCommand& command); + virtual int getNumBodies() const; + + virtual int getBodyUniqueId(int serialIndex) const; + + virtual bool getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const; + virtual int getNumJoints(int bodyIndex) const; virtual bool getJointInfo(int bodyIndex, int jointIndex, struct b3JointInfo& info) const; diff --git a/examples/SharedMemory/PhysicsLoopBack.cpp b/examples/SharedMemory/PhysicsLoopBack.cpp index 91ac0d2cc..eeb641023 100644 --- a/examples/SharedMemory/PhysicsLoopBack.cpp +++ b/examples/SharedMemory/PhysicsLoopBack.cpp @@ -74,6 +74,21 @@ bool PhysicsLoopBack::submitClientCommand(const struct SharedMemoryCommand& comm return m_data->m_physicsClient->submitClientCommand(command); } +int PhysicsLoopBack::getNumBodies() const +{ + return m_data->m_physicsClient->getNumBodies(); +} + +int PhysicsLoopBack::getBodyUniqueId(int serialIndex) const +{ + return m_data->m_physicsClient->getBodyUniqueId(serialIndex); +} + +bool PhysicsLoopBack::getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const +{ + return m_data->m_physicsClient->getBodyInfo(bodyUniqueId, info); +} + int PhysicsLoopBack::getNumJoints(int bodyIndex) const { return m_data->m_physicsClient->getNumJoints(bodyIndex); diff --git a/examples/SharedMemory/PhysicsLoopBack.h b/examples/SharedMemory/PhysicsLoopBack.h index 326e21128..baa7939fc 100644 --- a/examples/SharedMemory/PhysicsLoopBack.h +++ b/examples/SharedMemory/PhysicsLoopBack.h @@ -38,6 +38,12 @@ public: virtual bool submitClientCommand(const struct SharedMemoryCommand& command); + virtual int getNumBodies() const; + + virtual int getBodyUniqueId(int serialIndex) const; + + virtual bool getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const; + virtual int getNumJoints(int bodyIndex) const; virtual bool getJointInfo(int bodyIndex, int jointIndex, struct b3JointInfo& info) const; diff --git a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp index 9015bdb81..deaa5bda8 100644 --- a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp +++ b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp @@ -1064,6 +1064,10 @@ int PhysicsServerCommandProcessor::createBodyInfoStream(int bodyUniqueId, char* util->m_memSerializer = new btDefaultSerializer(bufferSizeInBytes ,(unsigned char*)bufferServerToClient); //disable serialization of the collision objects (they are too big, and the client likely doesn't need them); util->m_memSerializer->m_skipPointers.insert(mb->getBaseCollider(),0); + if (mb->getBaseName()) + { + util->m_memSerializer->registerNameForPointer(mb->getBaseName(),mb->getBaseName()); + } bodyHandle->m_linkLocalInertialFrames.reserve(mb->getNumLinks()); for (int i=0;igetNumLinks();i++) diff --git a/examples/SharedMemory/SharedMemoryPublic.h b/examples/SharedMemory/SharedMemoryPublic.h index dbb4dcd19..9b1068470 100644 --- a/examples/SharedMemory/SharedMemoryPublic.h +++ b/examples/SharedMemory/SharedMemoryPublic.h @@ -117,6 +117,13 @@ struct b3JointInfo double m_jointAxis[3]; // joint axis in parent local frame }; +struct b3BodyInfo +{ + const char* m_baseName; +}; + + + struct b3JointSensorState { double m_jointPosition; diff --git a/examples/pybullet/pybullet.c b/examples/pybullet/pybullet.c index cfa9ba684..6e010141d 100644 --- a/examples/pybullet/pybullet.c +++ b/examples/pybullet/pybullet.c @@ -567,7 +567,7 @@ static int pybullet_internalGetBasePositionAndOrientation( const int status_type = b3GetStatusType(status_handle); const double* actualStateQ; // const double* jointReactionForces[]; - int i; + if (status_type != CMD_ACTUAL_STATE_UPDATE_COMPLETED) { PyErr_SetString(SpamError, "getBasePositionAndOrientation failed."); @@ -661,10 +661,88 @@ static PyObject* pybullet_getBasePositionAndOrientation(PyObject* self, } } +static PyObject* pybullet_getNumBodies(PyObject* self, PyObject* args) +{ + if (0 == sm) { + PyErr_SetString(SpamError, "Not connected to physics server."); + return NULL; + } + + { + int numBodies = b3GetNumBodies(sm); + +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong(numBodies); +#else + return PyInt_FromLong(numBodies); +#endif + } +} + +static PyObject* pybullet_getBodyUniqueId(PyObject* self, PyObject* args) +{ + if (0 == sm) { + PyErr_SetString(SpamError, "Not connected to physics server."); + return NULL; + } + + { + int serialIndex = -1; + int bodyUniqueId = -1; + if (!PyArg_ParseTuple(args, "i", &serialIndex)) { + PyErr_SetString(SpamError, "Expected a serialIndex in range [0..number of bodies)."); + return NULL; + } + bodyUniqueId = b3GetBodyUniqueId(sm, serialIndex); + +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong(bodyUniqueId); +#else + return PyInt_FromLong(bodyUniqueId); +#endif + } +} + +static PyObject* pybullet_getBodyInfo(PyObject* self, PyObject* args) +{ + if (0 == sm) { + PyErr_SetString(SpamError, "Not connected to physics server."); + return NULL; + } + + { + int bodyUniqueId= -1; + int numJoints = 0; + if (!PyArg_ParseTuple(args, "i", &bodyUniqueId)) + { + PyErr_SetString(SpamError, "Expected a body unique id (integer)."); + return NULL; + } + { + struct b3BodyInfo info; + if (b3GetBodyInfo(sm,bodyUniqueId,&info)) + { + PyObject* pyListJointInfo = PyTuple_New(1); + PyTuple_SetItem(pyListJointInfo, 0, PyString_FromString(info.m_baseName)); + return pyListJointInfo; + } else + { + PyErr_SetString(SpamError, "Couldn't get body info"); + return NULL; + } + } + } + + PyErr_SetString(SpamError, "error in getBodyInfo."); + return NULL; +} + + // Return the number of joints in an object based on // body index; body index is based on order of sequence // the object is loaded into simulation -static PyObject* pybullet_getNumJoints(PyObject* self, PyObject* args) { +static PyObject* pybullet_getNumJoints(PyObject* self, PyObject* args) +{ if (0 == sm) { PyErr_SetString(SpamError, "Not connected to physics server."); return NULL; @@ -888,16 +966,15 @@ static PyObject* pybullet_getJointInfo(PyObject* self, PyObject* args) { static PyObject* pybullet_getJointState(PyObject* self, PyObject* args) { PyObject* pyListJointForceTorque; PyObject* pyListJointState; - PyObject* item; - struct b3JointInfo info; + struct b3JointSensorState sensorState; int bodyIndex = -1; int jointIndex = -1; int sensorStateSize = 4; // size of struct b3JointSensorState int forceTorqueSize = 6; // size of force torque list from b3JointSensorState - int i, j; + int j; int size = PySequence_Size(args); @@ -2005,6 +2082,15 @@ static PyMethodDef SpamMethods[] = { {"loadSDF", pybullet_loadSDF, METH_VARARGS, "Load multibodies from an SDF file."}, + {"getNumBodies", pybullet_getNumBodies, METH_VARARGS, + "Get the number of bodies in the simulation."}, + + {"getBodyUniqueId", pybullet_getBodyUniqueId, METH_VARARGS, + "Get the unique id of the body, given a integer serial index in range [0.. number of bodies)."}, + + {"getBodyInfo", pybullet_getBodyInfo, METH_VARARGS, + "Get the body info, given a body unique id."}, + {"getBasePositionAndOrientation", pybullet_getBasePositionAndOrientation, METH_VARARGS, "Get the world position and orientation of the base of the object. " From 3910003e0a57fdb7852b851a55366cbef04dd28d Mon Sep 17 00:00:00 2001 From: YunfeiBai Date: Tue, 27 Sep 2016 16:37:49 -0700 Subject: [PATCH 7/9] Add IK damping coefficient vector. --- ... => wsg50_one_motor_gripper_free_base.sdf} | 0 data/table.urdf | 61 ------------- data/tray.urdf | 91 ------------------- examples/SharedMemory/IKTrajectoryHelper.cpp | 9 +- examples/SharedMemory/IKTrajectoryHelper.h | 13 ++- .../PhysicsServerCommandProcessor.cpp | 19 +--- 6 files changed, 15 insertions(+), 178 deletions(-) rename data/gripper/{wsg50_one_motor_gripper_new.sdf => wsg50_one_motor_gripper_free_base.sdf} (100%) delete mode 100644 data/table.urdf delete mode 100644 data/tray.urdf diff --git a/data/gripper/wsg50_one_motor_gripper_new.sdf b/data/gripper/wsg50_one_motor_gripper_free_base.sdf similarity index 100% rename from data/gripper/wsg50_one_motor_gripper_new.sdf rename to data/gripper/wsg50_one_motor_gripper_free_base.sdf diff --git a/data/table.urdf b/data/table.urdf deleted file mode 100644 index 07d819315..000000000 --- a/data/table.urdf +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/tray.urdf b/data/tray.urdf deleted file mode 100644 index 32325a815..000000000 --- a/data/tray.urdf +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/SharedMemory/IKTrajectoryHelper.cpp b/examples/SharedMemory/IKTrajectoryHelper.cpp index ff93d7bbb..c4ee51b0e 100644 --- a/examples/SharedMemory/IKTrajectoryHelper.cpp +++ b/examples/SharedMemory/IKTrajectoryHelper.cpp @@ -43,7 +43,7 @@ bool IKTrajectoryHelper::computeIK(const double endEffectorTargetPosition[3], const double endEffectorWorldPosition[3], const double endEffectorWorldOrientation[4], const double* q_current, int numQ,int endEffectorIndex, - double* q_new, int ikMethod, const double* linear_jacobian, const double* angular_jacobian, int jacobian_size, double dampIk) + double* q_new, int ikMethod, const double* linear_jacobian, const double* angular_jacobian, int jacobian_size, const double dampIk[6]) { bool useAngularPart = (ikMethod==IK2_VEL_DLS_WITH_ORIENTATION) ? true : false; @@ -69,7 +69,7 @@ bool IKTrajectoryHelper::computeIK(const double endEffectorTargetPosition[3], VectorRn deltaS(3); for (int i = 0; i < 3; ++i) { - deltaS.Set(i,dampIk*(endEffectorTargetPosition[i]-endEffectorWorldPosition[i])); + deltaS.Set(i,dampIk[i]*(endEffectorTargetPosition[i]-endEffectorWorldPosition[i])); } // Set one end effector world orientation from Bullet @@ -79,13 +79,12 @@ bool IKTrajectoryHelper::computeIK(const double endEffectorTargetPosition[3], btQuaternion deltaQ = endQ*startQ.inverse(); float angle = deltaQ.getAngle(); btVector3 axis = deltaQ.getAxis(); - float angleDot = angle*dampIk; + float angleDot = angle; btVector3 angularVel = angleDot*axis.normalize(); for (int i = 0; i < 3; ++i) { - deltaR.Set(i,angularVel[i]); + deltaR.Set(i,dampIk[i+3]*angularVel[i]); } - deltaR[2] = 0.0; { diff --git a/examples/SharedMemory/IKTrajectoryHelper.h b/examples/SharedMemory/IKTrajectoryHelper.h index b4783b41d..c8bb761c2 100644 --- a/examples/SharedMemory/IKTrajectoryHelper.h +++ b/examples/SharedMemory/IKTrajectoryHelper.h @@ -21,13 +21,12 @@ public: IKTrajectoryHelper(); virtual ~IKTrajectoryHelper(); - - bool computeIK(const double endEffectorTargetPosition[3], - const double endEffectorTargetOrientation[4], - const double endEffectorWorldPosition[3], - const double endEffectorWorldOrientation[4], - const double* q_old, int numQ,int endEffectorIndex, - double* q_new, int ikMethod, const double* linear_jacobian, const double* angular_jacobian, int jacobian_size, double dampIk=1.); + bool computeIK(const double endEffectorTargetPosition[3], + const double endEffectorTargetOrientation[4], + const double endEffectorWorldPosition[3], + const double endEffectorWorldOrientation[4], + const double* q_old, int numQ, int endEffectorIndex, + double* q_new, int ikMethod, const double* linear_jacobian, const double* angular_jacobian, int jacobian_size, const double dampIk[6]); }; #endif //IK_TRAJECTORY_HELPER_H diff --git a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp index 071b0f338..7e3a057c7 100644 --- a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp +++ b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp @@ -2650,12 +2650,12 @@ bool PhysicsServerCommandProcessor::processCommand(const struct SharedMemoryComm endEffectorPosWorld.serializeDouble(endEffectorWorldPosition); endEffectorOri.serializeDouble(endEffectorWorldOrientation); - + double dampIK[6] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; ikHelperPtr->computeIK(clientCmd.m_calculateInverseKinematicsArguments.m_targetPosition, clientCmd.m_calculateInverseKinematicsArguments.m_targetOrientation, endEffectorWorldPosition.m_floats, endEffectorWorldOrientation.m_floats, &q_current[0], numDofs, clientCmd.m_calculateInverseKinematicsArguments.m_endEffectorLinkIndex, - &q_new[0], ikMethod, &jacobian_linear[0], &jacobian_angular[0], jacSize*2); + &q_new[0], ikMethod, &jacobian_linear[0], &jacobian_angular[0], jacSize*2, dampIK); serverCmd.m_inverseKinematicsResultArgs.m_bodyUniqueId =clientCmd.m_calculateInverseDynamicsArguments.m_bodyUniqueId; for (int i=0;im_KukaId = bodyId; // Load one motor gripper for kuka - loadSdf("gripper/wsg50_one_motor_gripper_new.sdf", &gBufferServerToClient[0], gBufferServerToClient.size(), true); + loadSdf("gripper/wsg50_one_motor_gripper_free_base.sdf", &gBufferServerToClient[0], gBufferServerToClient.size(), true); m_data->m_gripperId = bodyId + 1; InteralBodyData* kukaBody = m_data->getHandle(m_data->m_KukaId); InteralBodyData* gripperBody = m_data->getHandle(m_data->m_gripperId); @@ -3013,15 +3013,6 @@ void PhysicsServerCommandProcessor::stepSimulationRealTime(double dtInSec) loadUrdf("sphere_small.urdf", btVector3(-0.1, 0.6, 1.25), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); loadUrdf("cube_small.urdf", btVector3(0.3, 0.6, 0.85), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - // Table area - loadUrdf("table.urdf", btVector3(0, -1.9, 0.0), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("tray.urdf", btVector3(0, -1.7, 0.64), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("cup_small.urdf", btVector3(0.5, -2.0, 0.85), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("pitcher_small.urdf", btVector3(0.4, -1.8, 0.85), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("teddy_vhacd.urdf", btVector3(-0.1, -1.9, 0.7), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("cube_small.urdf", btVector3(0.1, -1.8, 0.7), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("sphere_small.urdf", btVector3(-0.2, -1.7, 0.7), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("husky/husky.urdf", btVector3(5, 2, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); m_data->m_huskyId = bodyId; @@ -3105,7 +3096,7 @@ void PhysicsServerCommandProcessor::stepSimulationRealTime(double dtInSec) if (closeToKuka) { - int dampIk = 1; + double dampIk[6] = {1.0, 1.0, 1.0, 1.0, 1.0, 0.0}; IKTrajectoryHelper** ikHelperPtrPtr = m_data->m_inverseKinematicsHelpers.find(bodyHandle->m_multiBody); IKTrajectoryHelper* ikHelperPtr = 0; From 7101cf013ee5c859053017082153ce0ea117ae5f Mon Sep 17 00:00:00 2001 From: YunfeiBai Date: Tue, 27 Sep 2016 16:41:02 -0700 Subject: [PATCH 8/9] Remove unused model. --- data/gripper/wsg50_one_motor_gripper_free.sdf | 364 ------------------ data/pr2_gripper_short.urdf | 142 ------- 2 files changed, 506 deletions(-) delete mode 100644 data/gripper/wsg50_one_motor_gripper_free.sdf delete mode 100644 data/pr2_gripper_short.urdf diff --git a/data/gripper/wsg50_one_motor_gripper_free.sdf b/data/gripper/wsg50_one_motor_gripper_free.sdf deleted file mode 100644 index c37e1d8b7..000000000 --- a/data/gripper/wsg50_one_motor_gripper_free.sdf +++ /dev/null @@ -1,364 +0,0 @@ - - - - - - - 0 0 0 0 0 0 - - 0 0 0 0 0 0 - 1.2 - - 1 - 0 - 0 - 1 - 0 - 1 - - - - - 0 0 0 0 -0 0 - - - 1 1 1 - meshes/WSG50_110.stl - - - - - - - - - - - 0 0 0.03 0 0 0 - - 0 0 0 0 0 0 - 0.1 - - 0.1 - 0 - 0 - 0.1 - 0 - 0.1 - - - - 0 0 0.01 0 0 0 - - - 0.02 0.02 0.02 - - - - - - - motor - base_link - - 0 0 1 - - -0.047 - 0.001 - 10.0 - 10.0 - - - 0 - 0 - 0 - 0 - - - - - - 0 0 0.04 0 0 0 - - 0 0 0.035 0 0 0 - 0.1 - - 0.1 - 0 - 0 - 0.1 - 0 - 0.1 - - - - -0.03 0 0.01 0 -1.2 0 - - - 0.02 0.02 0.07 - - - - - - - left_hinge - motor - - 0 1 0 - - -20.0 - 20.0 - 10 - 10 - - - 0 - 0 - 0 - 0 - - 0 - - - - - 0 0 0.04 0 0 0 - - 0 0 0.035 0 0 0 - 0.1 - - 0.1 - 0 - 0 - 0.1 - 0 - 0.1 - - - - 0.03 0 0.01 0 1.2 0 - - - 0.02 0.02 0.07 - - - - - - - right_hinge - motor - - 0 1 0 - - -20.0 - 20.0 - 10 - 10 - - - 0 - 0 - 0 - 0 - - 0 - - - - - -0.055 0 0.06 0 -0 0 - - 0 0 0.0115 0 -0 0 - 0.2 - - 0.1 - 0 - 0 - 0.1 - 0 - 0.1 - - - - - 0 0 -0.06 0 0 0 - - - 0.001 0.001 0.001 - meshes/GUIDE_WSG50_110.stl - - - - - 0 0 -0.037 0 0 0 - - - 0.001 0.001 0.001 - meshes/WSG-FMF.stl - - - - - - - - gripper_left - left_hinge - - 0 1 0 - - -1.0 - 1.0 - 10 - 10 - - - 0.01 - 0 - 0 - 0 - - 0 - - - - - 0.055 0 0.06 0 0 3.14159 - - 0 0 0.0115 0 -0 0 - 0.2 - - 0.1 - 0 - 0 - 0.1 - 0 - 0.1 - - - - - 0 0 -0.06 0 0 0 - - - 0.001 0.001 0.001 - meshes/GUIDE_WSG50_110.stl - - - - - 0 0 -0.037 0 0 0 - - - 0.001 0.001 0.001 - meshes/WSG-FMF.stl - - - - - - - gripper_right - right_hinge - - 0 1 0 - - -1.0 - 1.0 - 10 - 10 - - - 0.01 - 0 - 0 - 0 - - 0 - - - - - 0.062 0 0.145 0 0 1.5708 - - 0.2 - - 0.1 - 0 - 0 - 0.1 - 0 - 0.1 - - - - - 0 0 0.042 0 0 0 - - - 0.02 0.02 0.15 - - - - - - - 0 0 0 0 0 0 - - - 1 1 1 - meshes/l_gripper_tip_scaled.stl - - - - - - - gripper_right - finger_right - - - - -0.062 0 0.145 0 0 4.71239 - - 0.2 - - 0.1 - 0 - 0 - 0.1 - 0 - 0.1 - - - - - 0 0 0.042 0 0 0 - - - 0.02 0.02 0.15 - - - - - - - 0 0 0 0 0 0 - - - 1 1 1 - meshes/l_gripper_tip_scaled.stl - - - - - - - gripper_left - finger_left - - - - \ No newline at end of file diff --git a/data/pr2_gripper_short.urdf b/data/pr2_gripper_short.urdf deleted file mode 100644 index e41abf926..000000000 --- a/data/pr2_gripper_short.urdf +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a62d7957b6f01e56b1b46b3c1e81cd5e359e077d Mon Sep 17 00:00:00 2001 From: YunfeiBai Date: Tue, 27 Sep 2016 16:55:10 -0700 Subject: [PATCH 9/9] Keep one r2d2. --- examples/SharedMemory/PhysicsServerCommandProcessor.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp index 450e936b2..8d14e5ce4 100644 --- a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp +++ b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp @@ -2951,9 +2951,9 @@ void PhysicsServerCommandProcessor::stepSimulationRealTime(double dtInSec) loadUrdf("kuka_iiwa/model.urdf", btVector3(0, -3.0, 0.0), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); m_data->m_KukaId = bodyId; - loadUrdf("lego/lego.urdf", btVector3(0, -2.8, .1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("lego/lego.urdf", btVector3(0, -2.8, .2), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("lego/lego.urdf", btVector3(0, -2.8, .3), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + loadUrdf("lego/lego.urdf", btVector3(0, -2.5, .1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + loadUrdf("lego/lego.urdf", btVector3(0, -2.5, .2), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); + loadUrdf("lego/lego.urdf", btVector3(0, -2.5, .3), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); loadUrdf("r2d2.urdf", btVector3(2, -2, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); // Load one motor gripper for kuka @@ -3024,7 +3024,6 @@ void PhysicsServerCommandProcessor::stepSimulationRealTime(double dtInSec) loadUrdf("sphere2.urdf", btVector3(-5, 0, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); loadUrdf("sphere2.urdf", btVector3(-5, 0, 2), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); loadUrdf("sphere2.urdf", btVector3(-5, 0, 3), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); - loadUrdf("r2d2.urdf", btVector3(2, -2, 1), btQuaternion(0, 0, 0, 1), true, false, &bodyId, &gBufferServerToClient[0], gBufferServerToClient.size()); // Shelf area loadSdf("kiva_shelf/model.sdf", &gBufferServerToClient[0], gBufferServerToClient.size(), true);