snappy 3.3__cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snappy/CyOpenGL.cpython-310-aarch64-linux-gnu.so +0 -0
- snappy/SnapPy.cpython-310-aarch64-linux-gnu.so +0 -0
- snappy/SnapPy.ico +0 -0
- snappy/SnapPy.png +0 -0
- snappy/SnapPyHP.cpython-310-aarch64-linux-gnu.so +0 -0
- snappy/__init__.py +534 -0
- snappy/app.py +604 -0
- snappy/app_menus.py +372 -0
- snappy/browser.py +998 -0
- snappy/cache.py +25 -0
- snappy/canonical.py +249 -0
- snappy/cusps/__init__.py +280 -0
- snappy/cusps/cusp_area_matrix.py +98 -0
- snappy/cusps/cusp_areas_from_matrix.py +96 -0
- snappy/cusps/maximal_cusp_area_matrix.py +136 -0
- snappy/cusps/short_slopes_for_cusp.py +217 -0
- snappy/cusps/test.py +22 -0
- snappy/cusps/trig_cusp_area_matrix.py +63 -0
- snappy/database.py +454 -0
- snappy/db_utilities.py +79 -0
- snappy/decorated_isosig.py +717 -0
- snappy/dev/__init__.py +0 -0
- snappy/dev/extended_ptolemy/__init__.py +8 -0
- snappy/dev/extended_ptolemy/closed.py +106 -0
- snappy/dev/extended_ptolemy/complexVolumesClosed.py +149 -0
- snappy/dev/extended_ptolemy/direct.py +42 -0
- snappy/dev/extended_ptolemy/extended.py +406 -0
- snappy/dev/extended_ptolemy/giac_helper.py +43 -0
- snappy/dev/extended_ptolemy/giac_rur.py +129 -0
- snappy/dev/extended_ptolemy/gluing.py +46 -0
- snappy/dev/extended_ptolemy/phc_wrapper.py +220 -0
- snappy/dev/extended_ptolemy/printMatrices.py +70 -0
- snappy/dev/vericlosed/__init__.py +1 -0
- snappy/dev/vericlosed/computeApproxHyperbolicStructureNew.py +159 -0
- snappy/dev/vericlosed/computeApproxHyperbolicStructureOrb.py +90 -0
- snappy/dev/vericlosed/computeVerifiedHyperbolicStructure.py +111 -0
- snappy/dev/vericlosed/gimbalLoopFinder.py +130 -0
- snappy/dev/vericlosed/hyperbolicStructure.py +313 -0
- snappy/dev/vericlosed/krawczykCertifiedEdgeLengthsEngine.py +165 -0
- snappy/dev/vericlosed/oneVertexTruncatedComplex.py +122 -0
- snappy/dev/vericlosed/orb/__init__.py +1 -0
- snappy/dev/vericlosed/orb/orb_solution_for_snappea_finite_triangulation_mac +0 -0
- snappy/dev/vericlosed/parseVertexGramMatrixFile.py +47 -0
- snappy/dev/vericlosed/polishApproxHyperbolicStructure.py +61 -0
- snappy/dev/vericlosed/test.py +54 -0
- snappy/dev/vericlosed/truncatedComplex.py +176 -0
- snappy/dev/vericlosed/verificationError.py +58 -0
- snappy/dev/vericlosed/verifyHyperbolicStructureEngine.py +177 -0
- snappy/doc/_images/SnapPy-196.png +0 -0
- snappy/doc/_images/m004_paper_plane_on_systole.jpg +0 -0
- snappy/doc/_images/m125_paper_plane.jpg +0 -0
- snappy/doc/_images/mac.png +0 -0
- snappy/doc/_images/o9_00000_systole_paper_plane.jpg +0 -0
- snappy/doc/_images/o9_00000_systole_paper_plane_closer.jpg +0 -0
- snappy/doc/_images/plink-action.png +0 -0
- snappy/doc/_images/ubuntu.png +0 -0
- snappy/doc/_images/win7.png +0 -0
- snappy/doc/_sources/additional_classes.rst.txt +40 -0
- snappy/doc/_sources/bugs.rst.txt +14 -0
- snappy/doc/_sources/censuses.rst.txt +52 -0
- snappy/doc/_sources/credits.rst.txt +81 -0
- snappy/doc/_sources/development.rst.txt +261 -0
- snappy/doc/_sources/index.rst.txt +215 -0
- snappy/doc/_sources/installing.rst.txt +249 -0
- snappy/doc/_sources/manifold.rst.txt +6 -0
- snappy/doc/_sources/manifoldhp.rst.txt +46 -0
- snappy/doc/_sources/news.rst.txt +425 -0
- snappy/doc/_sources/other.rst.txt +25 -0
- snappy/doc/_sources/platonic_census.rst.txt +20 -0
- snappy/doc/_sources/plink.rst.txt +102 -0
- snappy/doc/_sources/ptolemy.rst.txt +66 -0
- snappy/doc/_sources/ptolemy_classes.rst.txt +42 -0
- snappy/doc/_sources/ptolemy_examples1.rst.txt +298 -0
- snappy/doc/_sources/ptolemy_examples2.rst.txt +363 -0
- snappy/doc/_sources/ptolemy_examples3.rst.txt +301 -0
- snappy/doc/_sources/ptolemy_examples4.rst.txt +61 -0
- snappy/doc/_sources/ptolemy_prelim.rst.txt +105 -0
- snappy/doc/_sources/screenshots.rst.txt +21 -0
- snappy/doc/_sources/snap.rst.txt +87 -0
- snappy/doc/_sources/snappy.rst.txt +28 -0
- snappy/doc/_sources/spherogram.rst.txt +103 -0
- snappy/doc/_sources/todo.rst.txt +47 -0
- snappy/doc/_sources/triangulation.rst.txt +11 -0
- snappy/doc/_sources/tutorial.rst.txt +49 -0
- snappy/doc/_sources/verify.rst.txt +210 -0
- snappy/doc/_sources/verify_internals.rst.txt +79 -0
- snappy/doc/_static/SnapPy-horizontal-128.png +0 -0
- snappy/doc/_static/SnapPy.ico +0 -0
- snappy/doc/_static/_sphinx_javascript_frameworks_compat.js +123 -0
- snappy/doc/_static/basic.css +906 -0
- snappy/doc/_static/css/badge_only.css +1 -0
- snappy/doc/_static/css/fonts/Roboto-Slab-Bold.woff +0 -0
- snappy/doc/_static/css/fonts/Roboto-Slab-Bold.woff2 +0 -0
- snappy/doc/_static/css/fonts/Roboto-Slab-Regular.woff +0 -0
- snappy/doc/_static/css/fonts/Roboto-Slab-Regular.woff2 +0 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.eot +0 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.svg +2671 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.ttf +0 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.woff +0 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.woff2 +0 -0
- snappy/doc/_static/css/fonts/lato-bold-italic.woff +0 -0
- snappy/doc/_static/css/fonts/lato-bold-italic.woff2 +0 -0
- snappy/doc/_static/css/fonts/lato-bold.woff +0 -0
- snappy/doc/_static/css/fonts/lato-bold.woff2 +0 -0
- snappy/doc/_static/css/fonts/lato-normal-italic.woff +0 -0
- snappy/doc/_static/css/fonts/lato-normal-italic.woff2 +0 -0
- snappy/doc/_static/css/fonts/lato-normal.woff +0 -0
- snappy/doc/_static/css/fonts/lato-normal.woff2 +0 -0
- snappy/doc/_static/css/theme.css +4 -0
- snappy/doc/_static/doctools.js +149 -0
- snappy/doc/_static/documentation_options.js +13 -0
- snappy/doc/_static/file.png +0 -0
- snappy/doc/_static/fonts/Lato/lato-bold.eot +0 -0
- snappy/doc/_static/fonts/Lato/lato-bold.ttf +0 -0
- snappy/doc/_static/fonts/Lato/lato-bold.woff +0 -0
- snappy/doc/_static/fonts/Lato/lato-bold.woff2 +0 -0
- snappy/doc/_static/fonts/Lato/lato-bolditalic.eot +0 -0
- snappy/doc/_static/fonts/Lato/lato-bolditalic.ttf +0 -0
- snappy/doc/_static/fonts/Lato/lato-bolditalic.woff +0 -0
- snappy/doc/_static/fonts/Lato/lato-bolditalic.woff2 +0 -0
- snappy/doc/_static/fonts/Lato/lato-italic.eot +0 -0
- snappy/doc/_static/fonts/Lato/lato-italic.ttf +0 -0
- snappy/doc/_static/fonts/Lato/lato-italic.woff +0 -0
- snappy/doc/_static/fonts/Lato/lato-italic.woff2 +0 -0
- snappy/doc/_static/fonts/Lato/lato-regular.eot +0 -0
- snappy/doc/_static/fonts/Lato/lato-regular.ttf +0 -0
- snappy/doc/_static/fonts/Lato/lato-regular.woff +0 -0
- snappy/doc/_static/fonts/Lato/lato-regular.woff2 +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 +0 -0
- snappy/doc/_static/jquery.js +2 -0
- snappy/doc/_static/js/badge_only.js +1 -0
- snappy/doc/_static/js/theme.js +1 -0
- snappy/doc/_static/js/versions.js +228 -0
- snappy/doc/_static/language_data.js +192 -0
- snappy/doc/_static/minus.png +0 -0
- snappy/doc/_static/plus.png +0 -0
- snappy/doc/_static/pygments.css +75 -0
- snappy/doc/_static/searchtools.js +635 -0
- snappy/doc/_static/snappy_furo.css +33 -0
- snappy/doc/_static/snappy_sphinx_rtd_theme.css +42 -0
- snappy/doc/_static/sphinx_highlight.js +154 -0
- snappy/doc/additional_classes.html +1500 -0
- snappy/doc/bugs.html +132 -0
- snappy/doc/censuses.html +453 -0
- snappy/doc/credits.html +184 -0
- snappy/doc/development.html +385 -0
- snappy/doc/doc-latest/additional_classes.html +1500 -0
- snappy/doc/doc-latest/bugs.html +132 -0
- snappy/doc/doc-latest/censuses.html +453 -0
- snappy/doc/doc-latest/credits.html +184 -0
- snappy/doc/doc-latest/development.html +385 -0
- snappy/doc/doc-latest/genindex.html +1349 -0
- snappy/doc/doc-latest/index.html +287 -0
- snappy/doc/doc-latest/installing.html +346 -0
- snappy/doc/doc-latest/manifold.html +3632 -0
- snappy/doc/doc-latest/manifoldhp.html +180 -0
- snappy/doc/doc-latest/news.html +438 -0
- snappy/doc/doc-latest/objects.inv +0 -0
- snappy/doc/doc-latest/other.html +160 -0
- snappy/doc/doc-latest/platonic_census.html +376 -0
- snappy/doc/doc-latest/plink.html +210 -0
- snappy/doc/doc-latest/ptolemy.html +253 -0
- snappy/doc/doc-latest/ptolemy_classes.html +1144 -0
- snappy/doc/doc-latest/ptolemy_examples1.html +409 -0
- snappy/doc/doc-latest/ptolemy_examples2.html +471 -0
- snappy/doc/doc-latest/ptolemy_examples3.html +414 -0
- snappy/doc/doc-latest/ptolemy_examples4.html +195 -0
- snappy/doc/doc-latest/ptolemy_prelim.html +248 -0
- snappy/doc/doc-latest/py-modindex.html +165 -0
- snappy/doc/doc-latest/screenshots.html +141 -0
- snappy/doc/doc-latest/search.html +135 -0
- snappy/doc/doc-latest/searchindex.js +1 -0
- snappy/doc/doc-latest/snap.html +202 -0
- snappy/doc/doc-latest/snappy.html +181 -0
- snappy/doc/doc-latest/spherogram.html +1346 -0
- snappy/doc/doc-latest/todo.html +166 -0
- snappy/doc/doc-latest/triangulation.html +1676 -0
- snappy/doc/doc-latest/tutorial.html +159 -0
- snappy/doc/doc-latest/verify.html +330 -0
- snappy/doc/doc-latest/verify_internals.html +1235 -0
- snappy/doc/genindex.html +1349 -0
- snappy/doc/index.html +287 -0
- snappy/doc/installing.html +346 -0
- snappy/doc/manifold.html +3632 -0
- snappy/doc/manifoldhp.html +180 -0
- snappy/doc/news.html +438 -0
- snappy/doc/objects.inv +0 -0
- snappy/doc/other.html +160 -0
- snappy/doc/platonic_census.html +376 -0
- snappy/doc/plink.html +210 -0
- snappy/doc/ptolemy.html +253 -0
- snappy/doc/ptolemy_classes.html +1144 -0
- snappy/doc/ptolemy_examples1.html +409 -0
- snappy/doc/ptolemy_examples2.html +471 -0
- snappy/doc/ptolemy_examples3.html +414 -0
- snappy/doc/ptolemy_examples4.html +195 -0
- snappy/doc/ptolemy_prelim.html +248 -0
- snappy/doc/py-modindex.html +165 -0
- snappy/doc/screenshots.html +141 -0
- snappy/doc/search.html +135 -0
- snappy/doc/searchindex.js +1 -0
- snappy/doc/snap.html +202 -0
- snappy/doc/snappy.html +181 -0
- snappy/doc/spherogram.html +1346 -0
- snappy/doc/todo.html +166 -0
- snappy/doc/triangulation.html +1676 -0
- snappy/doc/tutorial.html +159 -0
- snappy/doc/verify.html +330 -0
- snappy/doc/verify_internals.html +1235 -0
- snappy/drilling/__init__.py +456 -0
- snappy/drilling/barycentric.py +103 -0
- snappy/drilling/constants.py +5 -0
- snappy/drilling/crush.py +270 -0
- snappy/drilling/cusps.py +125 -0
- snappy/drilling/debug.py +242 -0
- snappy/drilling/epsilons.py +6 -0
- snappy/drilling/exceptions.py +55 -0
- snappy/drilling/moves.py +620 -0
- snappy/drilling/peripheral_curves.py +210 -0
- snappy/drilling/perturb.py +188 -0
- snappy/drilling/shorten.py +36 -0
- snappy/drilling/subdivide.py +274 -0
- snappy/drilling/test.py +23 -0
- snappy/drilling/test_cases.py +132 -0
- snappy/drilling/tracing.py +351 -0
- snappy/exceptions.py +26 -0
- snappy/export_stl.py +120 -0
- snappy/exterior_to_link/__init__.py +2 -0
- snappy/exterior_to_link/barycentric_geometry.py +463 -0
- snappy/exterior_to_link/exceptions.py +6 -0
- snappy/exterior_to_link/geodesic_map.json +14408 -0
- snappy/exterior_to_link/hyp_utils.py +112 -0
- snappy/exterior_to_link/link_projection.py +323 -0
- snappy/exterior_to_link/main.py +198 -0
- snappy/exterior_to_link/mcomplex_with_expansion.py +261 -0
- snappy/exterior_to_link/mcomplex_with_link.py +687 -0
- snappy/exterior_to_link/mcomplex_with_memory.py +162 -0
- snappy/exterior_to_link/pl_utils.py +491 -0
- snappy/exterior_to_link/put_in_S3.py +156 -0
- snappy/exterior_to_link/rational_linear_algebra.py +130 -0
- snappy/exterior_to_link/rational_linear_algebra_wrapped.py +135 -0
- snappy/exterior_to_link/simplify_to_base_tri.py +114 -0
- snappy/exterior_to_link/stored_moves.py +475 -0
- snappy/exterior_to_link/test.py +31 -0
- snappy/filedialog.py +28 -0
- snappy/geometric_structure/__init__.py +212 -0
- snappy/geometric_structure/cusp_neighborhood/__init__.py +3 -0
- snappy/geometric_structure/cusp_neighborhood/complex_cusp_cross_section.py +691 -0
- snappy/geometric_structure/cusp_neighborhood/cusp_cross_section_base.py +480 -0
- snappy/geometric_structure/cusp_neighborhood/exceptions.py +41 -0
- snappy/geometric_structure/cusp_neighborhood/real_cusp_cross_section.py +294 -0
- snappy/geometric_structure/cusp_neighborhood/tiles_for_cusp_neighborhood.py +156 -0
- snappy/geometric_structure/cusp_neighborhood/vertices.py +35 -0
- snappy/geometric_structure/geodesic/__init__.py +0 -0
- snappy/geometric_structure/geodesic/add_core_curves.py +152 -0
- snappy/geometric_structure/geodesic/avoid_core_curves.py +369 -0
- snappy/geometric_structure/geodesic/canonical_representatives.py +52 -0
- snappy/geometric_structure/geodesic/check_away_from_core_curve.py +60 -0
- snappy/geometric_structure/geodesic/constants.py +6 -0
- snappy/geometric_structure/geodesic/exceptions.py +22 -0
- snappy/geometric_structure/geodesic/fixed_points.py +106 -0
- snappy/geometric_structure/geodesic/geodesic_start_point_info.py +435 -0
- snappy/geometric_structure/geodesic/graph_trace_helper.py +67 -0
- snappy/geometric_structure/geodesic/line.py +30 -0
- snappy/geometric_structure/geodesic/multiplicity.py +127 -0
- snappy/geometric_structure/geodesic/tiles_for_geodesic.py +128 -0
- snappy/geometric_structure/test.py +22 -0
- snappy/gui.py +121 -0
- snappy/horoviewer.py +443 -0
- snappy/hyperboloid/__init__.py +212 -0
- snappy/hyperboloid/distances.py +259 -0
- snappy/hyperboloid/horoball.py +19 -0
- snappy/hyperboloid/line.py +35 -0
- snappy/hyperboloid/point.py +9 -0
- snappy/hyperboloid/triangle.py +29 -0
- snappy/info_icon.gif +0 -0
- snappy/infowindow.py +65 -0
- snappy/isometry_signature.py +389 -0
- snappy/len_spec/__init__.py +609 -0
- snappy/len_spec/geodesic_info.py +129 -0
- snappy/len_spec/geodesic_key_info_dict.py +116 -0
- snappy/len_spec/geodesic_piece.py +146 -0
- snappy/len_spec/geometric_structure.py +182 -0
- snappy/len_spec/geometry.py +136 -0
- snappy/len_spec/length_spectrum_geodesic_info.py +185 -0
- snappy/len_spec/spine.py +128 -0
- snappy/len_spec/test.py +24 -0
- snappy/len_spec/test_cases.py +69 -0
- snappy/len_spec/tile.py +276 -0
- snappy/len_spec/word.py +86 -0
- snappy/manifolds/HTWKnots/alternating.gz +0 -0
- snappy/manifolds/HTWKnots/nonalternating.gz +0 -0
- snappy/manifolds/__init__.py +3 -0
- snappy/margulis/__init__.py +332 -0
- snappy/margulis/cusp_neighborhood_neighborhood.py +66 -0
- snappy/margulis/geodesic_neighborhood.py +152 -0
- snappy/margulis/margulis_info.py +21 -0
- snappy/margulis/mu_from_neighborhood_pair.py +175 -0
- snappy/margulis/neighborhood.py +29 -0
- snappy/margulis/test.py +22 -0
- snappy/math_basics.py +187 -0
- snappy/matrix.py +525 -0
- snappy/number.py +657 -0
- snappy/numeric_output_checker.py +345 -0
- snappy/pari.py +41 -0
- snappy/phone_home.py +57 -0
- snappy/polyviewer.py +259 -0
- snappy/ptolemy/__init__.py +17 -0
- snappy/ptolemy/component.py +103 -0
- snappy/ptolemy/coordinates.py +2290 -0
- snappy/ptolemy/fieldExtensions.py +153 -0
- snappy/ptolemy/findLoops.py +473 -0
- snappy/ptolemy/geometricRep.py +59 -0
- snappy/ptolemy/homology.py +165 -0
- snappy/ptolemy/magma/default.magma_template +229 -0
- snappy/ptolemy/magma/radicalsOfPrimaryDecomposition.magma_template +79 -0
- snappy/ptolemy/manifoldMethods.py +395 -0
- snappy/ptolemy/matrix.py +350 -0
- snappy/ptolemy/numericalSolutionsToGroebnerBasis.py +113 -0
- snappy/ptolemy/polynomial.py +856 -0
- snappy/ptolemy/processComponents.py +173 -0
- snappy/ptolemy/processFileBase.py +247 -0
- snappy/ptolemy/processFileDispatch.py +46 -0
- snappy/ptolemy/processMagmaFile.py +392 -0
- snappy/ptolemy/processRurFile.py +150 -0
- snappy/ptolemy/ptolemyGeneralizedObstructionClass.py +102 -0
- snappy/ptolemy/ptolemyObstructionClass.py +64 -0
- snappy/ptolemy/ptolemyVariety.py +995 -0
- snappy/ptolemy/ptolemyVarietyPrimeIdealGroebnerBasis.py +140 -0
- snappy/ptolemy/reginaWrapper.py +698 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c2.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c3.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c4.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c5.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c6.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c7.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m003__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m003__sl3_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m015__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m015__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m015__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m015__sl3_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/rur.py +545 -0
- snappy/ptolemy/solutionsToPrimeIdealGroebnerBasis.py +277 -0
- snappy/ptolemy/test.py +1126 -0
- snappy/ptolemy/testing_files/3_1__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/3_1__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl4_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl4_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/5_2__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/5_2__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c2.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c3.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c4.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c5.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c6.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c7.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/data/pgl2/OrientableCuspedCensus/03_tetrahedra/m019__sl2_c0.magma_out +95 -0
- snappy/ptolemy/testing_files/data/pgl2/OrientableCuspedCensus/03_tetrahedra/m019__sl2_c1.magma_out +95 -0
- snappy/ptolemy/testing_files/m015__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c2.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c3.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c4.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c5.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c6.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c7.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/s000__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/s000__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/t00000__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/t00000__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/v0000__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/v0000__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/v0000__sl2_c2.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/v0000__sl2_c3.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m003__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m003__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m003__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m003__sl3_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m004__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m004__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m015__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m015__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_rur/m052__sl3_c0.rur.bz2 +0 -0
- snappy/ptolemy/utilities.py +236 -0
- snappy/raytracing/__init__.py +64 -0
- snappy/raytracing/additional_horospheres.py +64 -0
- snappy/raytracing/additional_len_spec_choices.py +63 -0
- snappy/raytracing/cohomology_fractal.py +197 -0
- snappy/raytracing/eyeball.py +124 -0
- snappy/raytracing/finite_raytracing_data.py +237 -0
- snappy/raytracing/finite_viewer.py +590 -0
- snappy/raytracing/geodesic_tube_info.py +174 -0
- snappy/raytracing/geodesics.py +246 -0
- snappy/raytracing/geodesics_window.py +258 -0
- snappy/raytracing/gui_utilities.py +293 -0
- snappy/raytracing/hyperboloid_navigation.py +556 -0
- snappy/raytracing/hyperboloid_utilities.py +234 -0
- snappy/raytracing/ideal_raytracing_data.py +592 -0
- snappy/raytracing/inside_viewer.py +974 -0
- snappy/raytracing/pack.py +22 -0
- snappy/raytracing/raytracing_data.py +126 -0
- snappy/raytracing/raytracing_view.py +454 -0
- snappy/raytracing/shaders/Eye.png +0 -0
- snappy/raytracing/shaders/NonGeometric.png +0 -0
- snappy/raytracing/shaders/__init__.py +101 -0
- snappy/raytracing/shaders/fragment.glsl +1744 -0
- snappy/raytracing/test.py +29 -0
- snappy/raytracing/tooltip.py +146 -0
- snappy/raytracing/upper_halfspace_utilities.py +98 -0
- snappy/raytracing/view_scale_controller.py +98 -0
- snappy/raytracing/zoom_slider/__init__.py +263 -0
- snappy/raytracing/zoom_slider/inward.png +0 -0
- snappy/raytracing/zoom_slider/inward18.png +0 -0
- snappy/raytracing/zoom_slider/outward.png +0 -0
- snappy/raytracing/zoom_slider/outward18.png +0 -0
- snappy/raytracing/zoom_slider/test.py +20 -0
- snappy/sage_helper.py +119 -0
- snappy/settings.py +407 -0
- snappy/shell.py +53 -0
- snappy/snap/__init__.py +117 -0
- snappy/snap/character_varieties.py +375 -0
- snappy/snap/find_field.py +372 -0
- snappy/snap/fox_milnor.py +271 -0
- snappy/snap/fundamental_polyhedron.py +569 -0
- snappy/snap/generators.py +39 -0
- snappy/snap/interval_reps.py +81 -0
- snappy/snap/kernel_structures.py +128 -0
- snappy/snap/mcomplex_base.py +18 -0
- snappy/snap/nsagetools.py +716 -0
- snappy/snap/peripheral/__init__.py +1 -0
- snappy/snap/peripheral/dual_cellulation.py +219 -0
- snappy/snap/peripheral/link.py +127 -0
- snappy/snap/peripheral/peripheral.py +159 -0
- snappy/snap/peripheral/surface.py +522 -0
- snappy/snap/peripheral/test.py +35 -0
- snappy/snap/polished_reps.py +335 -0
- snappy/snap/shapes.py +152 -0
- snappy/snap/slice_obs_HKL/__init__.py +194 -0
- snappy/snap/slice_obs_HKL/basics.py +236 -0
- snappy/snap/slice_obs_HKL/direct.py +217 -0
- snappy/snap/slice_obs_HKL/poly_norm.py +212 -0
- snappy/snap/slice_obs_HKL/rep_theory.py +424 -0
- snappy/snap/t3mlite/__init__.py +2 -0
- snappy/snap/t3mlite/arrow.py +243 -0
- snappy/snap/t3mlite/corner.py +22 -0
- snappy/snap/t3mlite/edge.py +172 -0
- snappy/snap/t3mlite/face.py +37 -0
- snappy/snap/t3mlite/files.py +211 -0
- snappy/snap/t3mlite/homology.py +53 -0
- snappy/snap/t3mlite/linalg.py +419 -0
- snappy/snap/t3mlite/mcomplex.py +1499 -0
- snappy/snap/t3mlite/perm4.py +320 -0
- snappy/snap/t3mlite/setup.py +12 -0
- snappy/snap/t3mlite/simplex.py +199 -0
- snappy/snap/t3mlite/spun.py +297 -0
- snappy/snap/t3mlite/surface.py +519 -0
- snappy/snap/t3mlite/test.py +20 -0
- snappy/snap/t3mlite/test_vs_regina.py +86 -0
- snappy/snap/t3mlite/tetrahedron.py +109 -0
- snappy/snap/t3mlite/vertex.py +42 -0
- snappy/snap/test.py +139 -0
- snappy/snap/utilities.py +288 -0
- snappy/test.py +213 -0
- snappy/test_cases.py +263 -0
- snappy/testing.py +131 -0
- snappy/tiling/__init__.py +2 -0
- snappy/tiling/dict_based_set.py +79 -0
- snappy/tiling/floor.py +49 -0
- snappy/tiling/hyperboloid_dict.py +54 -0
- snappy/tiling/iter_utils.py +78 -0
- snappy/tiling/lifted_tetrahedron.py +22 -0
- snappy/tiling/lifted_tetrahedron_set.py +54 -0
- snappy/tiling/quotient_dict.py +70 -0
- snappy/tiling/real_hash_dict.py +164 -0
- snappy/tiling/test.py +23 -0
- snappy/tiling/tile.py +224 -0
- snappy/tiling/triangle.py +33 -0
- snappy/tkterminal.py +920 -0
- snappy/twister/__init__.py +20 -0
- snappy/twister/main.py +646 -0
- snappy/twister/surfaces/S_0_1 +3 -0
- snappy/twister/surfaces/S_0_2 +3 -0
- snappy/twister/surfaces/S_0_4 +7 -0
- snappy/twister/surfaces/S_0_4_Lantern +8 -0
- snappy/twister/surfaces/S_1 +3 -0
- snappy/twister/surfaces/S_1_1 +4 -0
- snappy/twister/surfaces/S_1_2 +5 -0
- snappy/twister/surfaces/S_1_2_5 +6 -0
- snappy/twister/surfaces/S_2 +6 -0
- snappy/twister/surfaces/S_2_1 +8 -0
- snappy/twister/surfaces/S_2_heeg +10 -0
- snappy/twister/surfaces/S_3 +8 -0
- snappy/twister/surfaces/S_3_1 +10 -0
- snappy/twister/surfaces/S_4_1 +12 -0
- snappy/twister/surfaces/S_5_1 +14 -0
- snappy/twister/surfaces/heeg_fig8 +9 -0
- snappy/twister/twister_core.cpython-310-aarch64-linux-gnu.so +0 -0
- snappy/upper_halfspace/__init__.py +146 -0
- snappy/upper_halfspace/ideal_point.py +29 -0
- snappy/verify/__init__.py +13 -0
- snappy/verify/canonical.py +542 -0
- snappy/verify/complex_volume/__init__.py +18 -0
- snappy/verify/complex_volume/adjust_torsion.py +86 -0
- snappy/verify/complex_volume/closed.py +168 -0
- snappy/verify/complex_volume/compute_ptolemys.py +90 -0
- snappy/verify/complex_volume/cusped.py +56 -0
- snappy/verify/complex_volume/extended_bloch.py +201 -0
- snappy/verify/cusp_translations.py +85 -0
- snappy/verify/edge_equations.py +80 -0
- snappy/verify/exceptions.py +254 -0
- snappy/verify/hyperbolicity.py +224 -0
- snappy/verify/interval_newton_shapes_engine.py +523 -0
- snappy/verify/interval_tree.py +400 -0
- snappy/verify/krawczyk_shapes_engine.py +518 -0
- snappy/verify/real_algebra.py +286 -0
- snappy/verify/shapes.py +25 -0
- snappy/verify/square_extensions.py +1005 -0
- snappy/verify/test.py +72 -0
- snappy/verify/volume.py +128 -0
- snappy/version.py +2 -0
- snappy-3.3.dist-info/METADATA +58 -0
- snappy-3.3.dist-info/RECORD +541 -0
- snappy-3.3.dist-info/WHEEL +6 -0
- snappy-3.3.dist-info/entry_points.txt +2 -0
- snappy-3.3.dist-info/top_level.txt +28 -0
snappy/drilling/moves.py
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
from .cusps import CuspPostDrillInfo
|
|
2
|
+
from .tracing import compute_plane_intersection_param, Endpoint, GeodesicPiece
|
|
3
|
+
from .epsilons import compute_epsilon
|
|
4
|
+
from . import constants
|
|
5
|
+
from . import exceptions
|
|
6
|
+
|
|
7
|
+
from ..geometric_structure import add_r13_planes_to_tetrahedron
|
|
8
|
+
from ..snap.t3mlite import simplex, Perm4, Tetrahedron, Corner # type: ignore
|
|
9
|
+
from ..matrix import make_identity_matrix # type: ignore
|
|
10
|
+
from ..exceptions import InsufficientPrecisionError # type: ignore
|
|
11
|
+
|
|
12
|
+
from typing import Sequence, Optional, List
|
|
13
|
+
|
|
14
|
+
__all__ = ['one_four_move', 'two_three_move']
|
|
15
|
+
|
|
16
|
+
def one_four_move(given_pieces : Sequence[GeodesicPiece],
|
|
17
|
+
verified : bool) -> Sequence[GeodesicPiece]:
|
|
18
|
+
"""
|
|
19
|
+
Performs a 1-4 move.
|
|
20
|
+
|
|
21
|
+
Given pieces are supposed to be either two pieces
|
|
22
|
+
F-T-F (see subdivide.py for notation) or just one piece F-F.
|
|
23
|
+
|
|
24
|
+
For F-T-F, the new point for the 1-4 move will be the one given
|
|
25
|
+
for T. For F-F, the 1-4 move will pick a point on the line segment
|
|
26
|
+
(not picking exactly the middle to avoid coincidences).
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
tet : Tetrahedron = given_pieces[0].tet
|
|
30
|
+
RF = tet.O13_matrices[simplex.F0].base_ring()
|
|
31
|
+
|
|
32
|
+
if not given_pieces[0].endpoints[0].subsimplex in simplex.TwoSubsimplices:
|
|
33
|
+
raise Exception("Expected given geodesic piece to start "
|
|
34
|
+
"on a face for one-four move.")
|
|
35
|
+
if not given_pieces[-1].endpoints[1].subsimplex in simplex.TwoSubsimplices:
|
|
36
|
+
raise Exception("Expected given geodesic piece to end "
|
|
37
|
+
"on a face for one-four move.")
|
|
38
|
+
|
|
39
|
+
n = len(given_pieces)
|
|
40
|
+
|
|
41
|
+
if n == 1:
|
|
42
|
+
bias = RF(constants.piece_midpoint_bias)
|
|
43
|
+
new_point = (
|
|
44
|
+
given_pieces[0].endpoints[0].r13_point +
|
|
45
|
+
bias * given_pieces[0].endpoints[1].r13_point)
|
|
46
|
+
elif n == 2:
|
|
47
|
+
if not given_pieces[0].endpoints[1].subsimplex == simplex.T:
|
|
48
|
+
raise Exception("Expected middle point to be in the tetrahedron "
|
|
49
|
+
"when given two pieces to one-four move.")
|
|
50
|
+
if not given_pieces[1].endpoints[0].subsimplex == simplex.T:
|
|
51
|
+
raise Exception("Expected middle point to be in the tetrahedron "
|
|
52
|
+
"when given two pieces to one-four move.")
|
|
53
|
+
|
|
54
|
+
if not given_pieces[0].tet is given_pieces[1].tet:
|
|
55
|
+
raise Exception("Expected pieces to be in the same tetrahedron "
|
|
56
|
+
"when given two pieces to one-four move.")
|
|
57
|
+
|
|
58
|
+
new_point = given_pieces[0].endpoints[1].r13_point
|
|
59
|
+
else:
|
|
60
|
+
raise Exception("Bad 1-4 move")
|
|
61
|
+
|
|
62
|
+
# The new tetrahedra.
|
|
63
|
+
# new_tets[f] shares face f with the old tetrahedron.
|
|
64
|
+
# That is vertex v of the new tetrahedron corresponds to
|
|
65
|
+
# vertex v of the old tetrahedron if v is on the face f.
|
|
66
|
+
# Otherwise, the vertex v of the new tetrahedron corresponds
|
|
67
|
+
# to the new vertex in the center.
|
|
68
|
+
|
|
69
|
+
new_tets : dict[int, Tetrahedron] = {
|
|
70
|
+
f : Tetrahedron() for f in simplex.TwoSubsimplices }
|
|
71
|
+
|
|
72
|
+
neighbors : dict[int, Tetrahedron] = dict(tet.Neighbor.items())
|
|
73
|
+
gluings : dict[int, Perm4] = dict(tet.Gluing.items())
|
|
74
|
+
|
|
75
|
+
id_matrix = make_identity_matrix(ring=RF, n=4)
|
|
76
|
+
|
|
77
|
+
for f0, new_tet0 in new_tets.items():
|
|
78
|
+
new_tet0.geodesic_pieces = []
|
|
79
|
+
v0 = simplex.comp(f0)
|
|
80
|
+
new_tet0.R13_vertices = { v0 : new_point }
|
|
81
|
+
new_tet0.post_drill_infos = {
|
|
82
|
+
v0 : CuspPostDrillInfo(index=given_pieces[0].index) }
|
|
83
|
+
new_tet0.O13_matrices = {}
|
|
84
|
+
new_tet0.PeripheralCurves = [
|
|
85
|
+
[ { v : { face : 0 for face in simplex.TwoSubsimplices }
|
|
86
|
+
for v in simplex.ZeroSubsimplices }
|
|
87
|
+
for sheet in range(2) ]
|
|
88
|
+
for ml in range(2) ]
|
|
89
|
+
|
|
90
|
+
for f1, new_tet1 in new_tets.items():
|
|
91
|
+
if f0 != f1:
|
|
92
|
+
new_tet0.attach(f1, new_tet1, _swap_perms[f0, f1])
|
|
93
|
+
v1 = simplex.comp(f1)
|
|
94
|
+
new_tet0.R13_vertices[v1] = tet.R13_vertices[v1]
|
|
95
|
+
new_tet0.post_drill_infos[v1] = tet.post_drill_infos[v1]
|
|
96
|
+
new_tet0.O13_matrices[f1] = id_matrix
|
|
97
|
+
|
|
98
|
+
neighbor : Tetrahedron = neighbors[f0]
|
|
99
|
+
gluing : Perm4 = gluings[f0]
|
|
100
|
+
if neighbor is tet:
|
|
101
|
+
other_tet = new_tets[gluing.image(f0)]
|
|
102
|
+
else:
|
|
103
|
+
other_tet = neighbor
|
|
104
|
+
new_tet0.attach(f0, other_tet, gluing.tuple())
|
|
105
|
+
|
|
106
|
+
new_tet0.O13_matrices[f0] = tet.O13_matrices[f0]
|
|
107
|
+
|
|
108
|
+
add_r13_planes_to_tetrahedron(new_tet0)
|
|
109
|
+
|
|
110
|
+
for ml in range(2):
|
|
111
|
+
for sheet in range(2):
|
|
112
|
+
for v, faces in simplex.FacesAroundVertexCounterclockwise.items():
|
|
113
|
+
for face in faces:
|
|
114
|
+
new_tets[face].PeripheralCurves[ml][sheet][v][face] = (
|
|
115
|
+
tet.PeripheralCurves[ml][sheet][v][face])
|
|
116
|
+
f0, f1, f2 = faces
|
|
117
|
+
new_tets[f0].PeripheralCurves[ml][sheet][v][f1] = (
|
|
118
|
+
-tet.PeripheralCurves[ml][sheet][v][f0])
|
|
119
|
+
new_tets[f1].PeripheralCurves[ml][sheet][v][f0] = (
|
|
120
|
+
tet.PeripheralCurves[ml][sheet][v][f0])
|
|
121
|
+
new_tets[f1].PeripheralCurves[ml][sheet][v][f2] = (
|
|
122
|
+
tet.PeripheralCurves[ml][sheet][v][f2])
|
|
123
|
+
new_tets[f2].PeripheralCurves[ml][sheet][v][f1] = (
|
|
124
|
+
-tet.PeripheralCurves[ml][sheet][v][f2])
|
|
125
|
+
|
|
126
|
+
# Transfer or re-trace the other pieces of the geodesic going through
|
|
127
|
+
# the old tetrahedron in the new tetrahedra.
|
|
128
|
+
for old_piece in tet.geodesic_pieces:
|
|
129
|
+
if old_piece in given_pieces:
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
start_subsimplex : int = old_piece.endpoints[0].subsimplex
|
|
133
|
+
end_subsimplex : int = old_piece.endpoints[1].subsimplex
|
|
134
|
+
|
|
135
|
+
dimension_start_subsimplex = simplex.dimension(start_subsimplex)
|
|
136
|
+
dimension_end_subsimplex = simplex.dimension(end_subsimplex)
|
|
137
|
+
|
|
138
|
+
if dimension_start_subsimplex == 0 and dimension_end_subsimplex == 0:
|
|
139
|
+
# Both endpoints are vertices, no re-tracing necessary.
|
|
140
|
+
total_subsimplex = start_subsimplex | end_subsimplex
|
|
141
|
+
for new_face, new_tet in new_tets.items():
|
|
142
|
+
if simplex.is_subset(total_subsimplex, new_face):
|
|
143
|
+
GeodesicPiece.replace_by(
|
|
144
|
+
old_piece, old_piece,
|
|
145
|
+
[ GeodesicPiece.create_and_attach(
|
|
146
|
+
old_piece.index,
|
|
147
|
+
new_tet,
|
|
148
|
+
old_piece.endpoints) ])
|
|
149
|
+
break
|
|
150
|
+
else:
|
|
151
|
+
raise Exception("Unhandled edge case")
|
|
152
|
+
else:
|
|
153
|
+
# Re-trace.
|
|
154
|
+
r13_endpoints = [ e.r13_point for e in old_piece.endpoints ]
|
|
155
|
+
|
|
156
|
+
GeodesicPiece.replace_by(
|
|
157
|
+
old_piece, old_piece,
|
|
158
|
+
_retrace_geodesic_piece(
|
|
159
|
+
index=old_piece.index,
|
|
160
|
+
r13_points=r13_endpoints,
|
|
161
|
+
start_corners=[ Corner(new_tets[f], start_subsimplex)
|
|
162
|
+
for f in simplex.TwoSubsimplices
|
|
163
|
+
if simplex.is_subset(start_subsimplex, f) ],
|
|
164
|
+
end_corners=[ Corner(new_tets[f], end_subsimplex)
|
|
165
|
+
for f in simplex.TwoSubsimplices
|
|
166
|
+
if simplex.is_subset(end_subsimplex, f) ],
|
|
167
|
+
verified=verified))
|
|
168
|
+
|
|
169
|
+
# Turn given pieces into F-V-F
|
|
170
|
+
start_point : Endpoint = given_pieces[0].endpoints[0]
|
|
171
|
+
end_point : Endpoint = given_pieces[-1].endpoints[1]
|
|
172
|
+
new_pieces : Sequence[GeodesicPiece] = [
|
|
173
|
+
GeodesicPiece.create_face_to_vertex_and_attach(
|
|
174
|
+
given_pieces[0].index,
|
|
175
|
+
new_tets[start_point.subsimplex],
|
|
176
|
+
start_point,
|
|
177
|
+
direction=+1),
|
|
178
|
+
GeodesicPiece.create_face_to_vertex_and_attach(
|
|
179
|
+
given_pieces[0].index,
|
|
180
|
+
new_tets[end_point.subsimplex],
|
|
181
|
+
end_point,
|
|
182
|
+
direction=-1) ]
|
|
183
|
+
|
|
184
|
+
GeodesicPiece.replace_by(
|
|
185
|
+
given_pieces[0], given_pieces[-1], new_pieces)
|
|
186
|
+
|
|
187
|
+
return new_pieces
|
|
188
|
+
|
|
189
|
+
def two_three_move(given_pieces : Sequence[GeodesicPiece],
|
|
190
|
+
verified : bool) -> Sequence[GeodesicPiece]:
|
|
191
|
+
"""
|
|
192
|
+
Expects two given pieces V-F-V which form one straight line
|
|
193
|
+
segment from V to V.
|
|
194
|
+
|
|
195
|
+
The 2-3 move is performed on the two tetrahedra adjacent to
|
|
196
|
+
that face.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
# We imagine the two old tetrahedra such that the shared face
|
|
200
|
+
# is horizontal. We list them in the order where the top one
|
|
201
|
+
# is first and has index 0.
|
|
202
|
+
|
|
203
|
+
old_tets = [ old_piece.tet for old_piece in given_pieces ]
|
|
204
|
+
old_tips = [ given_pieces[i].endpoints[i].subsimplex
|
|
205
|
+
for i in range(2) ]
|
|
206
|
+
# For each old tetrahedron, its face that it shared with the other
|
|
207
|
+
# tetrahedron.
|
|
208
|
+
old_shared_faces = [ simplex.comp(old_tip)
|
|
209
|
+
for old_tip in old_tips ]
|
|
210
|
+
|
|
211
|
+
RF = old_tets[0].O13_matrices[simplex.F0].base_ring()
|
|
212
|
+
id_matrix = make_identity_matrix(ring=RF, n=4)
|
|
213
|
+
|
|
214
|
+
# Embeddings to map both tetrahedra into the same coordinate
|
|
215
|
+
# system
|
|
216
|
+
O13_embeddings = [ old_tets[0].O13_matrices[old_shared_faces[0]],
|
|
217
|
+
id_matrix ]
|
|
218
|
+
O13_inverse_embeddings = [ old_tets[1].O13_matrices[old_shared_faces[1]],
|
|
219
|
+
id_matrix ]
|
|
220
|
+
|
|
221
|
+
tip_points = [
|
|
222
|
+
O13_embeddings[i] * old_tets[i].R13_vertices[old_tips[i]]
|
|
223
|
+
for i in range(2) ]
|
|
224
|
+
|
|
225
|
+
# Find permutation such that we can label the top vertex of
|
|
226
|
+
# the top tetrahedron by 0.
|
|
227
|
+
for perm in Perm4.A4():
|
|
228
|
+
if perm.image(simplex.V0) == old_tips[0]:
|
|
229
|
+
break
|
|
230
|
+
gluing = old_tets[0].Gluing[old_shared_faces[0]]
|
|
231
|
+
|
|
232
|
+
new_to_old_tets = [ [ p, gluing * p * Perm4((1,0,2,3)) ]
|
|
233
|
+
for p in [ perm,
|
|
234
|
+
perm * Perm4((0,2,3,1)),
|
|
235
|
+
perm * Perm4((0,3,1,2)) ] ]
|
|
236
|
+
|
|
237
|
+
# The new tetrahedra have vertices as follows:
|
|
238
|
+
# Vertex 0 always corresponds to the top vertex of the
|
|
239
|
+
# top tetrahedron.
|
|
240
|
+
# Vertex 1 always corresponds to the top vertex of the
|
|
241
|
+
# bottom tetrahedron
|
|
242
|
+
# Vertex 3 is glued to vertex 2 of the next tetrahedron.
|
|
243
|
+
#
|
|
244
|
+
# Note that face 0 of a new tetrahedron is shared with
|
|
245
|
+
# some face of the old top tetrahedron (index=1).
|
|
246
|
+
# Similarly for face 1 and the bottom tetrahedron (index=0).
|
|
247
|
+
#
|
|
248
|
+
new_tets = [ Tetrahedron() for i in range(3) ]
|
|
249
|
+
|
|
250
|
+
for i, new_tet in enumerate(new_tets):
|
|
251
|
+
new_tet.geodesic_pieces = []
|
|
252
|
+
new_tet.O13_matrices = {
|
|
253
|
+
simplex.F2 : id_matrix,
|
|
254
|
+
simplex.F3 : id_matrix
|
|
255
|
+
}
|
|
256
|
+
new_tet.PeripheralCurves = [
|
|
257
|
+
[ { v : { face : 0 for face in simplex.TwoSubsimplices }
|
|
258
|
+
for v in simplex.ZeroSubsimplices }
|
|
259
|
+
for sheet in range(2) ]
|
|
260
|
+
for ml in range(2) ]
|
|
261
|
+
|
|
262
|
+
for j, old_tet in enumerate(old_tets):
|
|
263
|
+
new_face = simplex.TwoSubsimplices[1-j]
|
|
264
|
+
old_face = new_to_old_tets[i][j].image(new_face)
|
|
265
|
+
neighbor = old_tet.Neighbor[old_face]
|
|
266
|
+
new_tet.attach(
|
|
267
|
+
new_face,
|
|
268
|
+
neighbor,
|
|
269
|
+
old_tet.Gluing[old_face] * new_to_old_tets[i][j])
|
|
270
|
+
new_tet.O13_matrices[new_face] = (
|
|
271
|
+
old_tet.O13_matrices[old_face] * O13_inverse_embeddings[j])
|
|
272
|
+
|
|
273
|
+
neighbor_face = new_tet.Gluing[new_face].image(new_face)
|
|
274
|
+
neighbor.O13_matrices[neighbor_face] = (
|
|
275
|
+
O13_embeddings[j] * neighbor.O13_matrices[neighbor_face])
|
|
276
|
+
|
|
277
|
+
for ml in range(2):
|
|
278
|
+
for sheet in range(2):
|
|
279
|
+
for v in [ simplex.V2, simplex.V3 ]:
|
|
280
|
+
old_v = new_to_old_tets[i][j].image(v)
|
|
281
|
+
new_tet.PeripheralCurves[ml][sheet][v][new_face] = (
|
|
282
|
+
old_tet.PeripheralCurves[ml][sheet][old_v][old_face])
|
|
283
|
+
|
|
284
|
+
for ml in range(2):
|
|
285
|
+
for sheet in range(2):
|
|
286
|
+
for v, f in [ (simplex.V2, simplex.F3), (simplex.V3, simplex.F2) ]:
|
|
287
|
+
p = new_tet.PeripheralCurves[ml][sheet][v]
|
|
288
|
+
p[f] = - (p[simplex.F0] + p[simplex.F1])
|
|
289
|
+
|
|
290
|
+
new_tet.attach(
|
|
291
|
+
simplex.F2,
|
|
292
|
+
new_tets[(i+1) % 3],
|
|
293
|
+
(0,1,3,2))
|
|
294
|
+
|
|
295
|
+
new_tet.R13_vertices = {
|
|
296
|
+
simplex.V0 : tip_points[0],
|
|
297
|
+
simplex.V1 : tip_points[1],
|
|
298
|
+
simplex.V2 : old_tets[1].R13_vertices[new_to_old_tets[i][1].image(simplex.V2)],
|
|
299
|
+
simplex.V3 : old_tets[1].R13_vertices[new_to_old_tets[i][1].image(simplex.V3)],
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
new_tet.post_drill_infos = {
|
|
303
|
+
simplex.V0 : old_tets[0].post_drill_infos[old_tips[0]],
|
|
304
|
+
simplex.V1 : old_tets[1].post_drill_infos[old_tips[1]],
|
|
305
|
+
simplex.V2 : old_tets[1].post_drill_infos[new_to_old_tets[i][1].image(simplex.V2)],
|
|
306
|
+
simplex.V3 : old_tets[1].post_drill_infos[new_to_old_tets[i][1].image(simplex.V3)],
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
add_r13_planes_to_tetrahedron(new_tet)
|
|
310
|
+
|
|
311
|
+
old_to_new_tets = [ [ ~new_to_old_tets[j][i] for j in range(3) ]
|
|
312
|
+
for i in range(2) ]
|
|
313
|
+
|
|
314
|
+
# Transfer or re-trace the other pieces of the geodesic going through
|
|
315
|
+
# the old tetrahedron in the new tetrahedra.
|
|
316
|
+
# Consider one old tetrahedron at a time.
|
|
317
|
+
for j, old_tet in enumerate(old_tets):
|
|
318
|
+
for old_piece in old_tet.geodesic_pieces:
|
|
319
|
+
|
|
320
|
+
if old_piece in given_pieces:
|
|
321
|
+
continue
|
|
322
|
+
|
|
323
|
+
start_point = old_piece.endpoints[0]
|
|
324
|
+
start_subsimplex = start_point.subsimplex
|
|
325
|
+
# Index of old tet where piece is starting
|
|
326
|
+
start_j = j
|
|
327
|
+
|
|
328
|
+
# A geodesic piece crossing the face shared between the bottom and
|
|
329
|
+
# top tetrahedron was originally two pieces. Of these two pieces,
|
|
330
|
+
# discard the one after crossing the shared face.
|
|
331
|
+
if start_subsimplex == old_shared_faces[j]:
|
|
332
|
+
continue
|
|
333
|
+
|
|
334
|
+
end_point = old_piece.endpoints[1]
|
|
335
|
+
end_subsimplex = end_point.subsimplex
|
|
336
|
+
# Index of old tet where piece is ending
|
|
337
|
+
end_j = j
|
|
338
|
+
|
|
339
|
+
old_pieces = [ old_piece ]
|
|
340
|
+
# If a geodesic piece ends at the shared face, merge it
|
|
341
|
+
# with the next piece.
|
|
342
|
+
if end_subsimplex == old_shared_faces[j]:
|
|
343
|
+
old_pieces.append(old_piece.next_)
|
|
344
|
+
end_point = old_piece.next_.endpoints[1]
|
|
345
|
+
end_subsimplex = end_point.subsimplex
|
|
346
|
+
end_j = 1 - j
|
|
347
|
+
|
|
348
|
+
dimension_start_subsimplex = simplex.dimension(start_subsimplex)
|
|
349
|
+
dimension_end_subsimplex = simplex.dimension(end_subsimplex)
|
|
350
|
+
|
|
351
|
+
# Face of new tetrahedron that is shared with old tetrahedron.
|
|
352
|
+
new_start_face = simplex.TwoSubsimplices[1 - start_j]
|
|
353
|
+
new_end_face = simplex.TwoSubsimplices[1 - end_j]
|
|
354
|
+
|
|
355
|
+
if dimension_start_subsimplex == 0 and dimension_end_subsimplex == 0:
|
|
356
|
+
# Vertex to vertex piece. We can just transfer.
|
|
357
|
+
for i, new_tet in enumerate(new_tets):
|
|
358
|
+
# We need to find the right new tetrahedron such that the
|
|
359
|
+
# geodesic piece is an edge of the face shared with the old
|
|
360
|
+
# tetrahedron.
|
|
361
|
+
new_start_subsimplex = old_to_new_tets[j][i].image(start_subsimplex)
|
|
362
|
+
new_end_subsimplex = old_to_new_tets[j][i].image(end_subsimplex)
|
|
363
|
+
new_subsimplex = new_start_subsimplex | new_end_subsimplex
|
|
364
|
+
|
|
365
|
+
if simplex.is_subset(new_subsimplex, new_start_face):
|
|
366
|
+
GeodesicPiece.replace_by(
|
|
367
|
+
old_pieces[0], old_pieces[-1],
|
|
368
|
+
[
|
|
369
|
+
GeodesicPiece.create_and_attach(
|
|
370
|
+
old_piece.index,
|
|
371
|
+
new_tet,
|
|
372
|
+
[ Endpoint(new_tet.R13_vertices[v], v)
|
|
373
|
+
for v in [ new_start_subsimplex, new_end_subsimplex ] ])])
|
|
374
|
+
break
|
|
375
|
+
else:
|
|
376
|
+
raise Exception("Unhandled edge case.")
|
|
377
|
+
else:
|
|
378
|
+
# We need to re-trace.
|
|
379
|
+
r13_endpoints = [
|
|
380
|
+
O13_embeddings[start_j] * start_point.r13_point,
|
|
381
|
+
O13_embeddings[end_j] * end_point.r13_point
|
|
382
|
+
]
|
|
383
|
+
|
|
384
|
+
new_start_subsimplices = [
|
|
385
|
+
old_to_new_tets[start_j][i].image(start_subsimplex)
|
|
386
|
+
for i in range(3) ]
|
|
387
|
+
start_corners = [
|
|
388
|
+
Corner(new_tets[i], new_start_subsimplices[i])
|
|
389
|
+
for i in range(3)
|
|
390
|
+
if simplex.is_subset(new_start_subsimplices[i], new_start_face) ]
|
|
391
|
+
new_end_subsimplices = [
|
|
392
|
+
old_to_new_tets[end_j][i].image(end_subsimplex)
|
|
393
|
+
for i in range(3) ]
|
|
394
|
+
end_corners = [
|
|
395
|
+
Corner(new_tets[i], new_end_subsimplices[i])
|
|
396
|
+
for i in range(3)
|
|
397
|
+
if simplex.is_subset(new_end_subsimplices[i], new_end_face) ]
|
|
398
|
+
|
|
399
|
+
GeodesicPiece.replace_by(
|
|
400
|
+
old_pieces[0], old_pieces[-1],
|
|
401
|
+
_retrace_geodesic_piece(
|
|
402
|
+
index=old_piece.index,
|
|
403
|
+
r13_points=r13_endpoints,
|
|
404
|
+
start_corners=start_corners,
|
|
405
|
+
end_corners=end_corners,
|
|
406
|
+
verified=verified))
|
|
407
|
+
|
|
408
|
+
# Given pieces are converted to one V-V piece.
|
|
409
|
+
new_piece = GeodesicPiece.create_and_attach(
|
|
410
|
+
given_pieces[0].index,
|
|
411
|
+
new_tets[0],
|
|
412
|
+
[ Endpoint(tip_points[0], simplex.V0),
|
|
413
|
+
Endpoint(tip_points[1], simplex.V1) ])
|
|
414
|
+
GeodesicPiece.replace_by(
|
|
415
|
+
given_pieces[0], given_pieces[1],
|
|
416
|
+
[new_piece])
|
|
417
|
+
|
|
418
|
+
return new_piece
|
|
419
|
+
|
|
420
|
+
def _swap_perm(i, j):
|
|
421
|
+
result = [0, 1, 2, 3]
|
|
422
|
+
result[i] = j
|
|
423
|
+
result[j] = i
|
|
424
|
+
return result
|
|
425
|
+
|
|
426
|
+
_swap_perms = { (f0, f1) : _swap_perm(i, j)
|
|
427
|
+
for i, f0 in enumerate(simplex.TwoSubsimplices)
|
|
428
|
+
for j, f1 in enumerate(simplex.TwoSubsimplices) }
|
|
429
|
+
|
|
430
|
+
# Re-trace a line segment between two points in R^{1,3} in the new
|
|
431
|
+
# tetrahedra (which are assumed to be all in the same coordinate system).
|
|
432
|
+
#
|
|
433
|
+
# We are given the combinatorial information about the start and end points
|
|
434
|
+
# of the line segment through Corner's which are pairs of a tetrahedron and
|
|
435
|
+
# subsimplex.
|
|
436
|
+
#
|
|
437
|
+
# If such a start or end point is on a face, we give retrace exactly one
|
|
438
|
+
# corner encoding the face of the respective tetrahedron.
|
|
439
|
+
#
|
|
440
|
+
# If such a point is inside one of the new tetrahedra (we do not know yet
|
|
441
|
+
# which), we given an empty list of Corner's.
|
|
442
|
+
#
|
|
443
|
+
# If such a point is a vertex, we give all pairs of a new tetrahedron
|
|
444
|
+
# and one of its vertices that correspond to this vertex.
|
|
445
|
+
#
|
|
446
|
+
# index identifies to which of the geodesics we want to drill the retraced
|
|
447
|
+
# pieces belong. It is the index of the cusp that the geodesic will become
|
|
448
|
+
# in the drilled manifold.
|
|
449
|
+
#
|
|
450
|
+
def _retrace_geodesic_piece(
|
|
451
|
+
index : int,
|
|
452
|
+
r13_points,
|
|
453
|
+
start_corners : Sequence[Corner],
|
|
454
|
+
end_corners : Sequence[Corner],
|
|
455
|
+
verified : bool) -> Sequence[GeodesicPiece]:
|
|
456
|
+
|
|
457
|
+
if len(start_corners) == 1:
|
|
458
|
+
# If we have a unique start corner (which is supposed
|
|
459
|
+
# to be a face of a tetrahedron), we can just start
|
|
460
|
+
# tracing from that face.
|
|
461
|
+
trace_direction = +1
|
|
462
|
+
else:
|
|
463
|
+
# Otherwise, we need to trace the other way.
|
|
464
|
+
# The end corner was supposed to be a face of a tetrahedron
|
|
465
|
+
# in this case.
|
|
466
|
+
trace_direction = -1
|
|
467
|
+
start_corners, end_corners = end_corners, start_corners
|
|
468
|
+
r13_points = r13_points[::-1]
|
|
469
|
+
|
|
470
|
+
if len(start_corners) != 1:
|
|
471
|
+
raise Exception("No unique start corner")
|
|
472
|
+
|
|
473
|
+
# Tet and face to start re-tracing
|
|
474
|
+
tet = start_corners[0].Tetrahedron
|
|
475
|
+
face = start_corners[0].Subsimplex
|
|
476
|
+
|
|
477
|
+
if face not in simplex.TwoSubsimplices:
|
|
478
|
+
raise Exception("Tracing not starting on a face")
|
|
479
|
+
|
|
480
|
+
# Get dimension of subsimplex where line segment
|
|
481
|
+
# we are re-tracing ends.
|
|
482
|
+
dimension_end_subsimplex = 3
|
|
483
|
+
if len(end_corners) > 0:
|
|
484
|
+
dimension_end_subsimplex = simplex.dimension(
|
|
485
|
+
end_corners[0].Subsimplex)
|
|
486
|
+
|
|
487
|
+
start_point, end_point = r13_points
|
|
488
|
+
|
|
489
|
+
RF = start_point[0].parent()
|
|
490
|
+
if verified:
|
|
491
|
+
epsilon = 0
|
|
492
|
+
else:
|
|
493
|
+
epsilon = compute_epsilon(RF)
|
|
494
|
+
|
|
495
|
+
# Result
|
|
496
|
+
pieces : List[GeodesicPiece] = []
|
|
497
|
+
|
|
498
|
+
# Parametrizes ray. That is, we are start_point + param * direction.
|
|
499
|
+
param = RF(0)
|
|
500
|
+
direction = end_point - start_point
|
|
501
|
+
|
|
502
|
+
# 1-4 and 2-3 move never breaks up one line segment into more
|
|
503
|
+
# than 4 pieces.
|
|
504
|
+
for i in range(4):
|
|
505
|
+
hit_end : bool = False
|
|
506
|
+
# Record the face and param through which the ray is leaving
|
|
507
|
+
# the tet - that is which face the ray is hitting next.
|
|
508
|
+
hit_subsimplex : Optional[int] = None
|
|
509
|
+
hit_param = None
|
|
510
|
+
|
|
511
|
+
if dimension_end_subsimplex == 0:
|
|
512
|
+
# Check if we just entered a tetrahedron adjacent to the
|
|
513
|
+
# vertex where the line segments stops.
|
|
514
|
+
for end_corner in end_corners:
|
|
515
|
+
if tet == end_corner.Tetrahedron:
|
|
516
|
+
# If that is true, we have finished re-tracing
|
|
517
|
+
# and just need to emit the final F-V piece below.
|
|
518
|
+
# Do some sanity checks first though.
|
|
519
|
+
hit_subsimplex = simplex.comp(face)
|
|
520
|
+
if hit_subsimplex != end_corner.Subsimplex:
|
|
521
|
+
raise Exception("Implementation error: "
|
|
522
|
+
"ray entered tetrahedron through "
|
|
523
|
+
"unexpected face.")
|
|
524
|
+
hit_end = True
|
|
525
|
+
|
|
526
|
+
if not hit_end:
|
|
527
|
+
# Above condition not met, do actual ray-tracing.
|
|
528
|
+
if dimension_end_subsimplex == 3:
|
|
529
|
+
# The line-segment to be re-traced ends in the interior
|
|
530
|
+
# of a simplex. We set hit_param to 1 so that any face
|
|
531
|
+
# the ray hits after reaching the end of the line
|
|
532
|
+
# segment are ignored.
|
|
533
|
+
hit_subsimplex = simplex.T
|
|
534
|
+
hit_param = RF(1)
|
|
535
|
+
|
|
536
|
+
# Now intersect the ray with each face we did not enter through.
|
|
537
|
+
for candidate_face, plane in tet.R13_unnormalised_planes.items():
|
|
538
|
+
# Skip the face through which the ray just entered the tet
|
|
539
|
+
if candidate_face == face:
|
|
540
|
+
continue
|
|
541
|
+
# Compute the param at which the ray intersects this face
|
|
542
|
+
candidate_param = compute_plane_intersection_param(
|
|
543
|
+
plane, start_point, direction, verified)
|
|
544
|
+
|
|
545
|
+
# If the ray crossed this face before it crossed the
|
|
546
|
+
# entry face, ignore. Can happen when a dihedral angle is obtuse.
|
|
547
|
+
if candidate_param < param - epsilon:
|
|
548
|
+
continue
|
|
549
|
+
if not candidate_param > param + epsilon:
|
|
550
|
+
raise InsufficientPrecisionError(
|
|
551
|
+
"When re-tracing the geodesic, the intersection with the "
|
|
552
|
+
"next tetrahedron face was too close to the previous "
|
|
553
|
+
"to tell them apart. Increasing the precision will "
|
|
554
|
+
"probably avoid this problem.")
|
|
555
|
+
|
|
556
|
+
# This face is the (potential) exit face if the ray crossed
|
|
557
|
+
# it before it crossed the other faces (encountered so far).
|
|
558
|
+
if hit_param is None:
|
|
559
|
+
# No other face encountered so far
|
|
560
|
+
hit_param = candidate_param
|
|
561
|
+
hit_subsimplex = candidate_face
|
|
562
|
+
else:
|
|
563
|
+
# Check this face crossed before other faces
|
|
564
|
+
if candidate_param + epsilon < hit_param:
|
|
565
|
+
hit_param = candidate_param
|
|
566
|
+
hit_subsimplex = candidate_face
|
|
567
|
+
elif not candidate_param > hit_param + epsilon:
|
|
568
|
+
# If there is any ambiguity whether this face was
|
|
569
|
+
# crossed before the other face, fail!
|
|
570
|
+
# Most likely, this is because the ray is close to
|
|
571
|
+
# or crossing an edge of the triangulation.
|
|
572
|
+
raise exceptions.RetracingRayHittingOneSkeletonError()
|
|
573
|
+
|
|
574
|
+
if hit_param is None or hit_subsimplex is None:
|
|
575
|
+
raise InsufficientPrecisionError(
|
|
576
|
+
"Could not find the next intersection of the geodesic with a "
|
|
577
|
+
"tetrahedron face. Increasing the precision should solve this "
|
|
578
|
+
"problem.")
|
|
579
|
+
|
|
580
|
+
if dimension_end_subsimplex == 3:
|
|
581
|
+
# If we hit not face before the line segment ended, we are done
|
|
582
|
+
# Emit final F-T segment below.
|
|
583
|
+
if hit_subsimplex == simplex.T:
|
|
584
|
+
hit_end = True
|
|
585
|
+
else:
|
|
586
|
+
if hit_subsimplex == simplex.T:
|
|
587
|
+
raise Exception("Implementation error. Got interior of "
|
|
588
|
+
"simplex when expected face.")
|
|
589
|
+
# Did we hit the face where the line segment is ending at?
|
|
590
|
+
hit_end = (
|
|
591
|
+
tet == end_corners[0].Tetrahedron and
|
|
592
|
+
hit_subsimplex == end_corners[0].Subsimplex)
|
|
593
|
+
|
|
594
|
+
if hit_end:
|
|
595
|
+
# Use end point of line segment if we hit the end.
|
|
596
|
+
point = end_point
|
|
597
|
+
else:
|
|
598
|
+
# Advance ray.
|
|
599
|
+
point = start_point + hit_param * direction
|
|
600
|
+
|
|
601
|
+
endpoints = [ Endpoint(start_point + param * direction, face),
|
|
602
|
+
Endpoint(point, hit_subsimplex) ][::trace_direction]
|
|
603
|
+
|
|
604
|
+
pieces.append(
|
|
605
|
+
GeodesicPiece.create_and_attach(index, tet, endpoints))
|
|
606
|
+
|
|
607
|
+
if hit_end:
|
|
608
|
+
break
|
|
609
|
+
|
|
610
|
+
# Teleport to the next tetrahedron.
|
|
611
|
+
face = tet.Gluing[hit_subsimplex].image(hit_subsimplex)
|
|
612
|
+
tet = tet.Neighbor[hit_subsimplex]
|
|
613
|
+
param = hit_param
|
|
614
|
+
else:
|
|
615
|
+
raise Exception(
|
|
616
|
+
"Too many steps when re-tracing a geodesic piece. "
|
|
617
|
+
"This is either due to a lack of precision or an "
|
|
618
|
+
"implementation bug.")
|
|
619
|
+
|
|
620
|
+
return pieces[::trace_direction]
|