├── .clang-format
├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── tests.yml
│ └── wheels.yml
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── MANIFEST.in
├── README.md
├── assets
├── 2D_polygon_text.png
├── api-doc.svg
├── pl_3d.png
├── pl_logo.png
├── pl_logo_poly.png
└── polylidar_3D_architecture.jpg
├── bench
├── CMakeLists.txt
├── bench_mesh.cpp
├── bench_mesh_paper.cpp
├── records
│ ├── 04_03_2019.txt
│ ├── 04_03_2019_2.txt
│ ├── 04_04_2019.txt
│ ├── 04_06_2019.txt
│ ├── 06_07_2020.txt
│ └── bench_mesh_paper.json
└── run-bench.cpp
├── cmake
├── Extras.cmake
├── FindSphinx.cmake
└── GperftoolsConfig.cmake
├── dev-requirements.txt
├── docs
├── .nojekyll
├── _images
│ ├── opc.png
│ ├── opc_mesh.png
│ ├── opc_polygons.png
│ ├── pl_logo.png
│ ├── tutorial_Python_basicdemo_14_0.png
│ ├── tutorial_Python_basicdemo_17_0.png
│ ├── tutorial_Python_basicdemo_21_1.png
│ ├── tutorial_Python_basicdemo_6_1.png
│ ├── tutorial_Python_organizeddemo_10_0.png
│ ├── tutorial_Python_organizeddemo_12_0.png
│ ├── tutorial_Python_organizeddemo_22_0.png
│ ├── tutorial_Python_organizeddemo_27_0.png
│ ├── tutorial_Python_organizeddemo_37_0.png
│ ├── tutorial_Python_organizeddemo_5_0.jpg
│ ├── tutorial_Python_organizeddemo_6_1.png
│ └── tutorial_Python_organizeddemo_7_0.png
├── _static
│ ├── basic.css
│ ├── collapsible-lists
│ │ ├── LICENSE.md
│ │ ├── css
│ │ │ ├── button-closed.png
│ │ │ ├── button-open.png
│ │ │ ├── button.png
│ │ │ ├── list-item-contents.png
│ │ │ ├── list-item-last-open.png
│ │ │ ├── list-item-last.png
│ │ │ ├── list-item-open.png
│ │ │ ├── list-item-root.png
│ │ │ ├── list-item.png
│ │ │ └── tree_view.css
│ │ └── js
│ │ │ ├── CollapsibleLists.compressed.js
│ │ │ └── apply-collapsible-lists.js
│ ├── cpp_tutorial
│ │ ├── opc.png
│ │ ├── opc_mesh.png
│ │ └── opc_polygons.png
│ ├── css
│ │ ├── badge_only.css
│ │ └── theme.css
│ ├── doctools.js
│ ├── documentation_options.js
│ ├── file.png
│ ├── fonts
│ │ ├── Inconsolata-Bold.ttf
│ │ ├── Inconsolata-Regular.ttf
│ │ ├── Inconsolata.ttf
│ │ ├── Lato-Bold.ttf
│ │ ├── Lato-Regular.ttf
│ │ ├── Lato
│ │ │ ├── lato-bold.eot
│ │ │ ├── lato-bold.ttf
│ │ │ ├── lato-bold.woff
│ │ │ ├── lato-bold.woff2
│ │ │ ├── lato-bolditalic.eot
│ │ │ ├── lato-bolditalic.ttf
│ │ │ ├── lato-bolditalic.woff
│ │ │ ├── lato-bolditalic.woff2
│ │ │ ├── lato-italic.eot
│ │ │ ├── lato-italic.ttf
│ │ │ ├── lato-italic.woff
│ │ │ ├── lato-italic.woff2
│ │ │ ├── lato-regular.eot
│ │ │ ├── lato-regular.ttf
│ │ │ ├── lato-regular.woff
│ │ │ └── lato-regular.woff2
│ │ ├── RobotoSlab-Bold.ttf
│ │ ├── RobotoSlab-Regular.ttf
│ │ ├── RobotoSlab
│ │ │ ├── roboto-slab-v7-bold.eot
│ │ │ ├── roboto-slab-v7-bold.ttf
│ │ │ ├── roboto-slab-v7-bold.woff
│ │ │ ├── roboto-slab-v7-bold.woff2
│ │ │ ├── roboto-slab-v7-regular.eot
│ │ │ ├── roboto-slab-v7-regular.ttf
│ │ │ ├── roboto-slab-v7-regular.woff
│ │ │ └── roboto-slab-v7-regular.woff2
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
│ ├── jquery-3.5.1.js
│ ├── jquery.js
│ ├── js
│ │ ├── modernizr.min.js
│ │ └── theme.js
│ ├── language_data.js
│ ├── minus.png
│ ├── organized
│ │ ├── l515_mesh_smooth.png
│ │ ├── l515_opc.png
│ │ ├── l515_polygons.png
│ │ ├── organized_mesh.png
│ │ ├── organized_pc_raw.png
│ │ └── organized_polygons.png
│ ├── pl_logo.png
│ ├── plus.png
│ ├── pygments.css
│ ├── searchtools.js
│ ├── theme_overrides.css
│ ├── underscore-1.13.1.js
│ └── underscore.js
├── builddocs.html
├── cpp_api
│ ├── class_polylidar_1_1_delaunator_1_1_delaunator.html
│ ├── class_polylidar_1_1_matrix.html
│ ├── class_polylidar_1_1_mesh_helper_1_1_half_edge_triangulation.html
│ ├── class_polylidar_1_1_polylidar3_d.html
│ ├── class_polylidar_1_1_utility_1_1_timer.html
│ ├── cpp_library_root.html
│ ├── define__delaunator_8hpp_1aab75489dd91b143a909025f8368e4009.html
│ ├── define__types_8hpp_1a1d095d37fed141ea28ec04974442c0ef.html
│ ├── define__types_8hpp_1a23804f18a0fbd9b4e157ae31b457bd10.html
│ ├── define__types_8hpp_1a2d9cb1057a4e14234548ae19b0f3443d.html
│ ├── define__types_8hpp_1a3f22be63c8fea45a7fa5cad21f12695d.html
│ ├── define__types_8hpp_1a42420c51823910aab1a5fdc9dfe3c5d5.html
│ ├── define__types_8hpp_1a525335710b53cb064ca56b936120431e.html
│ ├── define__types_8hpp_1a70a969a6491e2de5132ae9aba81a4ec9.html
│ ├── define__types_8hpp_1a7dea8871416165edb4f7173bcb2eda11.html
│ ├── define__types_8hpp_1a8cee264b5d4e3db3070756c9746c2151.html
│ ├── define__types_8hpp_1ab2eac6ae5b6808421541f54b4f036372.html
│ ├── define__types_8hpp_1ab43aa29c4419785679216d072ec4cdfb.html
│ ├── define__types_8hpp_1ad8ab559c0f1938709b6e578f862a8ce7.html
│ ├── define__types_8hpp_1ae71449b1cc6e6250b91f539153a7a0d3.html
│ ├── define__types_8hpp_1af2ca5f5b76596d9bbd6a67a4764ae2be.html
│ ├── dir_include.html
│ ├── dir_include_Polylidar.html
│ ├── dir_include_Polylidar_Delaunator.html
│ ├── dir_include_Polylidar_Mesh.html
│ ├── file_include_Polylidar_Core.hpp.html
│ ├── file_include_Polylidar_Delaunator_Delaunator.hpp.html
│ ├── file_include_Polylidar_Mesh_MeshHelper.hpp.html
│ ├── file_include_Polylidar_Polylidar.hpp.html
│ ├── file_include_Polylidar_Types.hpp.html
│ ├── file_include_Polylidar_Utility.hpp.html
│ ├── file_include_Polylidar_UtilityMath.hpp.html
│ ├── function__core_8hpp_1a1f316f22f93e14c1a249b69beebba10b.html
│ ├── function__core_8hpp_1a2009c70a2125ad04dd6e40e10bcbdb6a.html
│ ├── function__core_8hpp_1a3263a29f4127df8843e459510f6d97e5.html
│ ├── function__core_8hpp_1a4b9651a843a1229e8ca0dbd3156febcc.html
│ ├── function__core_8hpp_1a70489c955319359ace18fef3176f2de1.html
│ ├── function__core_8hpp_1a85802683bd9f7f8dba26d1729ad120cf.html
│ ├── function__core_8hpp_1ab98cac6c050263d49ddef3bcf0d14036.html
│ ├── function__core_8hpp_1ac88b3986070c1372fc522341a6514f15.html
│ ├── function__core_8hpp_1add4a1efaafa2041846ba2c94d20c26b7.html
│ ├── function__core_8hpp_1adde24b475c79c5e0be903f2b028b33d9.html
│ ├── function__core_8hpp_1af677514aacc51035b4e2d05bba23ca05.html
│ ├── function__mesh_helper_8hpp_1a0795f32234b0753964afbba2e25b11f1.html
│ ├── function__mesh_helper_8hpp_1a0a5f2849ec799733e0f81afd6bed52b8.html
│ ├── function__mesh_helper_8hpp_1a0fcc773725c30395ec9e9ffa9039b828.html
│ ├── function__mesh_helper_8hpp_1a2d4bb214cc0b745369fa53d794809aa5.html
│ ├── function__mesh_helper_8hpp_1a394d710b518258deec8d884b9737d5bc.html
│ ├── function__mesh_helper_8hpp_1a3ee540b953482106943613bfc88bb702.html
│ ├── function__mesh_helper_8hpp_1a3f6f46f413ce11ac7ff641e5be6228a8.html
│ ├── function__mesh_helper_8hpp_1a4b714130e02f1532a43d0fbf5f2e1564.html
│ ├── function__mesh_helper_8hpp_1a6dcf4d32a3ee2497c6a6531242b1ab96.html
│ ├── function__mesh_helper_8hpp_1a8253ecf63b14865cd30a37d80f7ebb7b.html
│ ├── function__mesh_helper_8hpp_1ac0df864c0d84953a2168aae444463f45.html
│ ├── function__mesh_helper_8hpp_1ad5eec264e9112ea3274fd0e482c0b860.html
│ ├── function__mesh_helper_8hpp_1af2f5a484e368296155be589dc1582d17.html
│ ├── function__utility_8hpp_1a0564ff05b3cece45c33d3da2be2a3d18.html
│ ├── function__utility_8hpp_1a1f9f63580a80812a401deff72b328fd2.html
│ ├── function__utility_8hpp_1a32ed1d575166842388914c19dbf4ee37.html
│ ├── function__utility_8hpp_1a3492984e36d695d627dbee908655bda9.html
│ ├── function__utility_8hpp_1a6279e829eac7208343304e98af7edd2c.html
│ ├── function__utility_8hpp_1a80a81b1bc1d0622a664461278f812c6b.html
│ ├── function__utility_8hpp_1a897603d7c94440bdc16cd802109e7322.html
│ ├── function__utility_8hpp_1abd490d4eba9cfea63b99fa22b66e095b.html
│ ├── function__utility_8hpp_1accdcc0ee5e84acf219e509dd656bae4a.html
│ ├── function__utility_8hpp_1ad3899922c5306e5c2fe940b0b5116302.html
│ ├── function__utility_8hpp_1adf0acd4d7f0129216d005c5048828863.html
│ ├── function__utility_8hpp_1af2b8f0dfdf23b0c7867c60290dd20209.html
│ ├── function__utility_8hpp_1af486b42b4386373131e33d10effa2089.html
│ ├── function__utility_math_8hpp_1a26a15551cd5a1a9ab3505d972ff59357.html
│ ├── function__utility_math_8hpp_1a2eb226872f1b37bf51e22ce02cab642f.html
│ ├── function__utility_math_8hpp_1a466c1d74ae5b142b06bbce541aa90053.html
│ ├── function__utility_math_8hpp_1a468a3acbfb944236a42464f59fb3fd2c.html
│ ├── function__utility_math_8hpp_1a5d203b5b0dbaad40858e729adf651b49.html
│ ├── function__utility_math_8hpp_1a63c41a05f6be15da2be8b5c1aeb76e3b.html
│ ├── function__utility_math_8hpp_1a72028d52fab531c7e7d3402e2459214b.html
│ ├── function__utility_math_8hpp_1a8244427fefea52816f58db3d959c1f51.html
│ ├── function__utility_math_8hpp_1aae7fdef8c6c54ef9852b116ac45be081.html
│ ├── function__utility_math_8hpp_1ad4cf028794ad41b5b96aa3410f5ee676.html
│ ├── function__utility_math_8hpp_1ad83063c4e395cce3d244fe54abdaee46.html
│ ├── function__utility_math_8hpp_1ae03792bc59ac31647991255e5943c95d.html
│ ├── function__utility_math_8hpp_1af05c745bac954b5612ce0770794c7ee7.html
│ ├── function__utility_math_8hpp_1af16ec1926a2335a668880e0951205474.html
│ ├── namespace_Polylidar.html
│ ├── namespace_Polylidar__Core.html
│ ├── namespace_Polylidar__Delaunator.html
│ ├── namespace_Polylidar__MeshHelper.html
│ ├── namespace_Polylidar__Utility.html
│ ├── namespace_Polylidar__Utility__Math.html
│ ├── program_listing_file_include_Polylidar_Core.hpp.html
│ ├── program_listing_file_include_Polylidar_Delaunator_Delaunator.hpp.html
│ ├── program_listing_file_include_Polylidar_Mesh_MeshHelper.hpp.html
│ ├── program_listing_file_include_Polylidar_Polylidar.hpp.html
│ ├── program_listing_file_include_Polylidar_Types.hpp.html
│ ├── program_listing_file_include_Polylidar_Utility.hpp.html
│ ├── program_listing_file_include_Polylidar_UtilityMath.hpp.html
│ ├── struct_polylidar_1_1_extreme_point.html
│ ├── struct_polylidar_1_1_plane_data.html
│ ├── struct_polylidar_1_1_polygon.html
│ ├── typedef__mesh_helper_8hpp_1a5a3fec4bd6f8157d80d99b69780b7c0c.html
│ ├── typedef__mesh_helper_8hpp_1a6cc699607d9890f4ac89d51249ce23cf.html
│ ├── typedef__types_8hpp_1a047cfd2555d7032f95197f210777b4fa.html
│ ├── typedef__types_8hpp_1a151f5359a0516169fbafa0e6c9951175.html
│ ├── typedef__types_8hpp_1a1e2a29b8f7fee3fa66e78ab6aa1f7767.html
│ ├── typedef__types_8hpp_1a6783a447a165ea396cf8858152752049.html
│ ├── typedef__types_8hpp_1a694d2b6ca322939ac8b1716b80bb919a.html
│ ├── typedef__types_8hpp_1a6fd40a1a3c08ebb65a2f11e3b21bfbfa.html
│ ├── typedef__types_8hpp_1a979e2b7eb8f15adb3f986eccc3fdc0a7.html
│ ├── typedef__types_8hpp_1a9d00629f79c841e79a258bb36637cb32.html
│ ├── typedef__types_8hpp_1ac93001b58b8f7837585ecb11570954e2.html
│ ├── unabridged_orphan.html
│ ├── variable__types_8hpp_1a0e9042f4830755cf4427a031dff73e32.html
│ ├── variable__types_8hpp_1a1f5d04597ffd4b5cca6fb1ee2e9b780c.html
│ ├── variable__types_8hpp_1a4079d21632ff8d22636e133700c44fed.html
│ ├── variable__types_8hpp_1a60b9643ef6a7874391123c78d41e03ef.html
│ ├── variable__types_8hpp_1a6b4cab8513b8c1a6705de5e08744b07c.html
│ ├── variable__types_8hpp_1ade7920342e2ab6fee9a3e9b21d70f567.html
│ ├── variable__types_8hpp_1affa84bf61aeceb782f8b1bd3bac39fc9.html
│ └── variable__utility_8hpp_1afc5adb5092e2cf0c283ed7c37b1772dd.html
├── genindex.html
├── index.html
├── install_instructions.html
├── introduction.html
├── objects.inv
├── python_api
│ ├── polylidar.Delaunator.html
│ ├── polylidar.HalfEdgeTriangulation.html
│ ├── polylidar.MatrixDouble.html
│ ├── polylidar.MatrixFloat.html
│ ├── polylidar.MatrixInt.html
│ ├── polylidar.MatrixUInt8.html
│ ├── polylidar.MatrixULongInt.html
│ ├── polylidar.Polygon.html
│ ├── polylidar.Polylidar3D.html
│ ├── polylidar.VectorDouble.html
│ ├── polylidar.VectorInt.html
│ ├── polylidar.VectorUInt8.html
│ ├── polylidar.VectorULongInt.html
│ ├── polylidar.bilateral_filter_normals.html
│ ├── polylidar.create_tri_mesh_copy.html
│ ├── polylidar.extract_point_cloud_from_float_depth.html
│ ├── polylidar.extract_tri_mesh_from_float_depth.html
│ ├── polylidar.extract_tri_mesh_from_organized_point_cloud.html
│ ├── polylidar.get_polylidar_version.html
│ ├── polylidar.html
│ ├── polylidar.laplacian_filter_vertices.html
│ ├── polylidar.polylidarutil.plane_filtering.JOIN_STYLE.html
│ ├── polylidar.polylidarutil.plane_filtering.Polygon.html
│ ├── polylidar.polylidarutil.plane_filtering.R.html
│ ├── polylidar.polylidarutil.plane_filtering.add_column.html
│ ├── polylidar.polylidarutil.plane_filtering.create_kd_tree.html
│ ├── polylidar.polylidarutil.plane_filtering.filter_planes.html
│ ├── polylidar.polylidarutil.plane_filtering.filter_planes_and_holes.html
│ ├── polylidar.polylidarutil.plane_filtering.get_points.html
│ ├── polylidar.polylidarutil.plane_filtering.html
│ ├── polylidar.polylidarutil.plane_filtering.plot_indices_text.html
│ ├── polylidar.polylidarutil.plane_filtering.plot_poly.html
│ ├── polylidar.polylidarutil.plane_filtering.recover_3d.html
│ └── polylidar.robust_predicates_activated.html
├── search.html
├── searchindex.js
└── tutorial
│ ├── C++
│ ├── basic.html
│ └── index.html
│ └── Python
│ ├── basicdemo.html
│ ├── basicdemo.ipynb
│ ├── index.html
│ ├── organizeddemo.html
│ └── organizeddemo.ipynb
├── examples
├── CMakeLists.txt
├── cpp
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── debug.cpp
│ ├── polylidar-full-example.cpp
│ ├── simple.cpp
│ └── vis_util.hpp
└── python
│ ├── basic25d.py
│ ├── basic25d_algorithm.py
│ ├── basic2d.py
│ ├── basic2d_algorithm.py
│ ├── basic_mesh.py
│ ├── bilateral.py
│ ├── extras
│ ├── open3d_mesh.py
│ ├── test_delaunay.py
│ ├── test_matrix.py
│ └── test_o3d.py
│ ├── for_landing
│ ├── PolylidarParams.yaml
│ ├── PolylidarParamsSimple.yaml
│ ├── basic_rooftop_classification.py
│ ├── basic_rooftop_classification_algorithm.py
│ ├── o3d_slow_down.py
│ └── test_classified.py
│ ├── for_paper
│ ├── basic25d_algorithm_explained.py
│ ├── bench_mesh_report.py
│ ├── california_points.py
│ ├── generate_logo.py
│ ├── manifold_meshes.py
│ ├── mesh_examples.py
│ ├── plane_segmentation.py
│ ├── polygon_example.py
│ ├── polygon_example_research_statement.py
│ └── polygon_extraction_from_mesh.py
│ ├── notebooks
│ ├── BasicDemo.ipynb
│ └── OrganizedDemo.ipynb
│ ├── realsense_mesh.py
│ ├── robust.py
│ └── util
│ ├── helper_mesh.py
│ ├── helper_polylidar.py
│ ├── mesh_util.py
│ └── realsense_util.py
├── fixtures
└── .gitignore
├── include
└── Polylidar
│ ├── Core.hpp
│ ├── Delaunator
│ └── Delaunator.hpp
│ ├── Mesh
│ └── MeshHelper.hpp
│ ├── Polylidar.hpp
│ ├── Types.hpp
│ ├── Utility.hpp
│ └── UtilityMath.hpp
├── pyproject.toml
├── pytest.ini
├── requirements.txt
├── scripts
├── make_release.sh
└── manage_versions.py
├── setup.py
├── src
├── CMakeLists.txt
├── Polylidar
│ ├── CMakeLists.txt
│ ├── Core.cpp
│ ├── Delaunator.cpp
│ ├── MeshFilter.cpp
│ ├── MeshHelper.cpp
│ ├── PLConfig.cpp
│ ├── PLConfig.hpp.in
│ └── Polylidar.cpp
├── Python
│ ├── CMakeLists.txt
│ ├── LICENSE
│ ├── MANIFEST.in
│ ├── README.md
│ ├── make_install_pip_package.cmake
│ ├── make_python_package.cmake
│ ├── polylidar
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ └── polylidarutil
│ │ │ ├── __init__.py
│ │ │ ├── descartes_patch.py
│ │ │ ├── line_mesh.py
│ │ │ ├── open3d_util.py
│ │ │ └── plane_filtering.py
│ ├── polylidar_pybind
│ │ ├── docstring
│ │ │ ├── docstring.cpp
│ │ │ └── docstring.hpp
│ │ ├── polylidar_pybind.cpp
│ │ └── polylidar_pybind.hpp
│ ├── requirements.txt
│ └── setup.py
└── version.txt
├── src_docs
├── .nojekyll
├── Doxyfile
├── _static
│ ├── cpp_tutorial
│ │ ├── opc.png
│ │ ├── opc_mesh.png
│ │ └── opc_polygons.png
│ ├── organized
│ │ ├── l515_mesh_smooth.png
│ │ ├── l515_opc.png
│ │ ├── l515_polygons.png
│ │ ├── organized_mesh.png
│ │ ├── organized_pc_raw.png
│ │ └── organized_polygons.png
│ ├── pl_logo.png
│ └── theme_overrides.css
├── builddocs.rst
├── conf.py
├── index.rst
├── install_instructions.rst
├── introduction.rst
├── make.bat
├── make_docs.py
└── tutorial
│ ├── C++
│ ├── basic.rst
│ └── index.rst
│ └── Python
│ ├── basicdemo.nblink
│ ├── index.rst
│ └── organizeddemo.nblink
├── tests
├── CMakeLists.txt
├── __init__.py
├── cpp
│ ├── CMakeLists.txt
│ └── run-tests.cpp
└── python
│ ├── __init__.py
│ ├── benchmark_test.py
│ ├── conftest.py
│ ├── generated_test.py
│ ├── helpers
│ ├── __init__.py
│ └── utils.py
│ └── shape_test.py
└── thirdparty
├── CMakeLists.txt
├── FastExp
├── LICENSE
├── fastexp.h
├── ieee.h
└── product.h
├── npy
└── npy
│ └── npy.h
└── predicates
├── CMakeLists.txt
├── README.md
├── constants.c
├── constants.h
├── predicates.c
├── predicates.h
├── predicates.hpp
├── predicatesDLLExport.h
├── printing.c
└── random.c
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | # Mapbox.Variant C/C+ style
3 | Language: Cpp
4 | AccessModifierOffset: -2
5 | AlignAfterOpenBracket: Align
6 | AlignConsecutiveAssignments: false
7 | AlignConsecutiveDeclarations: false
8 | AlignEscapedNewlinesLeft: false
9 | AlignOperands: true
10 | AlignTrailingComments: true
11 | AllowAllParametersOfDeclarationOnNextLine: true
12 | AllowShortBlocksOnASingleLine: false
13 | AllowShortCaseLabelsOnASingleLine: false
14 | AllowShortFunctionsOnASingleLine: All
15 | AllowShortIfStatementsOnASingleLine: true
16 | AllowShortLoopsOnASingleLine: true
17 | AlwaysBreakAfterDefinitionReturnType: None
18 | AlwaysBreakAfterReturnType: None
19 | AlwaysBreakBeforeMultilineStrings: false
20 | AlwaysBreakTemplateDeclarations: true
21 | BinPackArguments: true
22 | BinPackParameters: true
23 | BraceWrapping:
24 | AfterClass: true
25 | AfterControlStatement: true
26 | AfterEnum: true
27 | AfterFunction: true
28 | AfterNamespace: false
29 | AfterObjCDeclaration: true
30 | AfterStruct: true
31 | AfterUnion: true
32 | BeforeCatch: true
33 | BeforeElse: true
34 | IndentBraces: false
35 | BreakBeforeBinaryOperators: None
36 | BreakBeforeBraces: Custom
37 | BreakBeforeTernaryOperators: true
38 | BreakConstructorInitializersBeforeComma: false
39 | ColumnLimit: 120
40 | CommentPragmas: '^ IWYU pragma:'
41 | ConstructorInitializerAllOnOneLineOrOnePerLine: true
42 | ConstructorInitializerIndentWidth: 4
43 | ContinuationIndentWidth: 4
44 | Cpp11BracedListStyle: true
45 | DerivePointerAlignment: false
46 | DisableFormat: false
47 | ExperimentalAutoDetectBinPacking: false
48 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
49 | IncludeCategories:
50 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
51 | Priority: 2
52 | - Regex: '^(<|"(gtest|isl|json)/)'
53 | Priority: 3
54 | - Regex: '.*'
55 | Priority: 1
56 | IndentCaseLabels: false
57 | IndentWidth: 4
58 | IndentWrappedFunctionNames: false
59 | KeepEmptyLinesAtTheStartOfBlocks: true
60 | MacroBlockBegin: ''
61 | MacroBlockEnd: ''
62 | MaxEmptyLinesToKeep: 1
63 | NamespaceIndentation: None
64 | ObjCBlockIndentWidth: 2
65 | ObjCSpaceAfterProperty: false
66 | ObjCSpaceBeforeProtocolList: true
67 | PenaltyBreakBeforeFirstCallParameter: 19
68 | PenaltyBreakComment: 300
69 | PenaltyBreakFirstLessLess: 120
70 | PenaltyBreakString: 1000
71 | PenaltyExcessCharacter: 1000000
72 | PenaltyReturnTypeOnItsOwnLine: 60
73 | PointerAlignment: Left
74 | ReflowComments: true
75 | SortIncludes: false
76 | SpaceAfterCStyleCast: false
77 | SpaceBeforeAssignmentOperators: true
78 | SpaceBeforeParens: ControlStatements
79 | SpaceInEmptyParentheses: false
80 | SpacesBeforeTrailingComments: 1
81 | SpacesInAngles: false
82 | SpacesInContainerLiterals: true
83 | SpacesInCStyleCastParentheses: false
84 | SpacesInParentheses: false
85 | SpacesInSquareBrackets: false
86 | Standard: Cpp11
87 | TabWidth: 4
88 | UseTab: Never
89 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is. Using Polylidar as Python or pure C++.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 |
16 | **Expected behavior**
17 | A clear and concise description of what you expected to happen.
18 |
19 | **Environment (please complete the following information):**
20 | - OS: [e.g. WIndows, Linux, MacOS]
21 | - Architecture (x64, Arm)
22 | - Version [e.g. 0.0.6]
23 |
24 | **Additional context**
25 | Add any other context about the problem here.
26 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Run Tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - dev
8 | env:
9 | NPROC: 2
10 |
11 | jobs:
12 |
13 | # Create Source Distribution and upload artifacts
14 | python_test:
15 | runs-on: ${{ matrix.os }}
16 | name: Python Tests
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | os: ['ubuntu-latest', 'windows-latest', 'macos-latest']
21 | python-version: ['3.7', '3.8', '3.9', '3.10' ]
22 | exclude:
23 | # excludes 3.10 on macOS
24 | - os: macos-latest
25 | python-version: '3.10'
26 | steps:
27 | - name: Checkout code
28 | uses: actions/checkout@v2
29 |
30 | - name: Setup Python
31 | uses: actions/setup-python@v2
32 | with:
33 | python-version: ${{ matrix.python-version }}
34 | architecture: x64
35 |
36 | # - name: Install OpenMP for MacOS
37 | # if: ${{ matrix.os == 'macos-latest' }}
38 | # run: brew install libomp
39 |
40 | - name: Setup CMake
41 | uses: jwlawson/actions-setup-cmake@v1.9
42 | with:
43 | cmake-version: '3.19.x'
44 |
45 | - name: Install requirements
46 | run: python -m pip install --upgrade pip setuptools wheel pytest
47 |
48 | - name: Build sdist
49 | run: |
50 | python setup.py sdist -d wheelhouse
51 | - name: Show files
52 | shell: bash
53 | run: ls -lh wheelhouse
54 |
55 | - name: Install from sdist
56 | shell: bash
57 | run: |
58 | pip install wheelhouse/*.tar.gz
59 |
60 | - name: Run Tests
61 | run: |
62 | pytest
--------------------------------------------------------------------------------
/.github/workflows/wheels.yml:
--------------------------------------------------------------------------------
1 | name: Wheels
2 |
3 | on:
4 | push:
5 | branches:
6 | - dev
7 | tags:
8 | - v*
9 | env:
10 | NPROC: 2
11 |
12 | jobs:
13 |
14 | # Create Source Distribution and upload artifacts
15 | build_sdist:
16 | name: Source Distribution
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: Checkout code
20 | uses: actions/checkout@v2
21 |
22 | - name: Setup Python
23 | uses: actions/setup-python@v2
24 | with:
25 | python-version: '3.8'
26 |
27 | - name: Setup CMake
28 | uses: jwlawson/actions-setup-cmake@v1.9
29 | with:
30 | cmake-version: '3.19.x'
31 |
32 | - name: Install requirements
33 | run: pip install --user twine
34 |
35 | - name: Build sdist
36 | run: |
37 | python setup.py sdist -d wheelhouse
38 |
39 | - name: Install from sdist
40 | run: |
41 | pip install --user wheelhouse/*.tar.gz
42 |
43 | - name: Check sdist
44 | run: |
45 | python -m twine check wheelhouse/*
46 |
47 | - name: Upload sdist
48 | uses: actions/upload-artifact@v2
49 | with:
50 | name: wheels
51 | path: wheelhouse/*.tar.gz
52 |
53 | # Create Binary Distribution (wheels) and upload artifacts
54 | build_wheels:
55 | runs-on: ${{ matrix.os }}
56 | name: Binary Wheels
57 | strategy:
58 | fail-fast: false
59 | matrix:
60 | include:
61 | - os: ubuntu-latest
62 | cibw-arch: manylinux_x86_64
63 | - os: windows-latest
64 | cibw-arch: win_amd64
65 | - os: macos-latest
66 | cibw-arch: macosx_x86_64
67 | - os: macos-latest
68 | cibw-arch: macosx_arm64
69 | - os: macos-latest
70 | cibw-arch: macosx_universal2
71 | # - os: ubuntu-latest
72 | # cibw-arch: manylinux_i686
73 | # - os: windows-latest
74 | # cibw-arch: win32
75 | # python-arch: x86
76 | # cmake-arch: -A Win32
77 | env:
78 | CIBW_SKIP: "cp27-* cp35-* cp36-* cp311-* pp*"
79 | steps:
80 | - name: Checkout source code
81 | uses: actions/checkout@v2
82 |
83 | - name: Set up Python version
84 | uses: actions/setup-python@v2
85 | with:
86 | python-version: 3.8
87 |
88 | # - name: Install OpenMP for MacOS
89 | # if: ${{ matrix.os == 'macos-latest' }}
90 | # run: brew install libomp
91 |
92 | - name: Setup CMake
93 | uses: jwlawson/actions-setup-cmake@v1.9
94 | with:
95 | cmake-version: '3.19.x'
96 |
97 | - name: Install requirements
98 | run: |
99 | python -m pip install cibuildwheel twine
100 |
101 | - name: Configure cibuildwheel
102 | shell: bash
103 | run: |
104 | CMAKE_OSX_ARCHITECTURES=${{ matrix.cibw-arch == 'macosx_x86_64' && 'x86_64' || matrix.cibw-arch == 'macosx_arm64' && 'arm64' || matrix.cibw-arch == 'macosx_universal2' && '"arm64;x86_64"' || '' }}
105 | echo "CIBW_ARCHS_MACOS=x86_64 arm64 universal2" >> $GITHUB_ENV
106 | echo "CIBW_BUILD=*-${{ matrix.cibw-arch }}" >> $GITHUB_ENV
107 |
108 | - name: Build Wheels
109 | run: |
110 | python -m cibuildwheel --output-dir wheelhouse
111 |
112 | - name: Show files
113 | shell: bash
114 | run: ls -lh wheelhouse
115 |
116 | - name: Upload wheels to artifact
117 | uses: actions/upload-artifact@v2
118 | with:
119 | path: wheelhouse/*.whl
120 | name: wheels
121 |
122 | upload_wheels:
123 | name: Upload Wheels to PyPI
124 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
125 | runs-on: ubuntu-latest
126 | needs: [build_sdist, build_wheels]
127 |
128 | steps:
129 | - name: Collect sdist and wheels
130 | uses: actions/download-artifact@v2
131 | with:
132 | name: wheels
133 | path: wheelhouse
134 |
135 | # # Upload to Testing PyPI
136 | # - name: Publish package to TestPyPI
137 | # uses: pypa/gh-action-pypi-publish@master
138 | # with:
139 | # user: __token__
140 | # password: ${{ secrets.test_pypi_password }}
141 | # packages_dir: wheelhouse/
142 | # repository_url: https://test.pypi.org/legacy/
143 | # skip_existing: true
144 |
145 | # Publish to official PyPI channel
146 | - name: Publish package to PyPI
147 | uses: pypa/gh-action-pypi-publish@master
148 | with:
149 | user: __token__
150 | password: ${{ secrets.pypi_password }}
151 | packages_dir: wheelhouse/
152 | skip_existing: true
153 |
154 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | fixtures/meshes
2 | fixtures/temp
3 | fixtures/realsense
4 | fixtures/pointsets
5 | scratch
6 | cmake-build
7 | assets/scratch
8 | tests/fixtures/meshes
9 | o3d_slow_down
10 | # Documentation Sphinx
11 | src_docs/_build
12 | src_docs/_out
13 | src_docs/cpp_api
14 | src_docs/python_api
15 | docs/.doctrees
16 | docs/.buildinfo
17 | docs/_sources
18 | docds/objects.inv
19 | # docs
20 |
21 | # examples/cpp/bin
22 | .vscode
23 | .nox
24 | archive
25 | scratch
26 | solns
27 | *.prof
28 | *.o
29 | *.swp
30 | *.swo
31 | *.so
32 | *.pyc
33 | *.dll
34 | *.exp
35 | *.lib
36 | *.obj
37 | .vscode/ipch
38 |
39 | # Byte-compiled / optimized / DLL files
40 | __pycache__/
41 | *.py[cod]
42 | *$py.class
43 |
44 | # C extensions
45 | *.so
46 |
47 | # Distribution / packaging
48 | .Python
49 | build/
50 | develop-eggs/
51 | dist/
52 | downloads/
53 | eggs/
54 | .eggs/
55 | lib/
56 | lib64/
57 | parts/
58 | sdist/
59 | var/
60 | wheels/
61 | *.egg-info/
62 | .installed.cfg
63 | *.egg
64 | MANIFEST
65 |
66 | # PyInstaller
67 | # Usually these files are written by a python script from a template
68 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
69 | *.manifest
70 | *.spec
71 |
72 | # Installer logs
73 | pip-log.txt
74 | pip-delete-this-directory.txt
75 |
76 | # Unit test / coverage reports
77 | htmlcov/
78 | .tox/
79 | .coverage
80 | .coverage.*
81 | .cache
82 | nosetests.xml
83 | coverage.xml
84 | *.cover
85 | .hypothesis/
86 | .pytest_cache/
87 |
88 | # Translations
89 | *.mo
90 | *.pot
91 |
92 | # Django stuff:
93 | .static_storage/
94 | .media/
95 | local_settings.py
96 |
97 | # Flask stuff:
98 | instance/
99 | .webassets-cache
100 |
101 | # Scrapy stuff:
102 | .scrapy
103 |
104 | # Sphinx documentation
105 | docs/_build/
106 |
107 | # PyBuilder
108 | target/
109 |
110 | # Jupyter Notebook
111 | .ipynb_checkpoints
112 |
113 | # pyenv
114 | .python-version
115 |
116 | # celery beat schedule file
117 | celerybeat-schedule
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 |
144 | !build/.gitignore
145 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.14)
2 | # Project Details
3 | set(PROJECT_NAME "Polylidar")
4 | set(PROJECT_VERSION 1.0.0)
5 | set(PROJECT_EMAIL "")
6 | set(PROJECT_HOME "")
7 | set(PROJECT_DOCS "")
8 | set(PROJECT_CODE "")
9 | set(PROJECT_ISSUES "")
10 | set(PYPI_PACKAGE_NAME "polylidar")
11 | # Set Project Properties
12 | project(${PROJECT_NAME} VERSION ${PROJECT_VERSION}
13 | DESCRIPTION "Fast Polygon Extraction from Point Clouds and Meshes"
14 | LANGUAGES CXX)
15 | # Set Global Properties
16 | set(CMAKE_CXX_STANDARD 14)
17 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
18 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
19 | set(CMAKE_CXX_EXTENSIONS OFF)
20 |
21 | if(NOT CMAKE_BUILD_TYPE)
22 | set(CMAKE_BUILD_TYPE Release)
23 | endif()
24 |
25 | # Include cmake folder
26 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
27 | include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Extras.cmake)
28 |
29 | set(ORIG_CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
30 |
31 | # Output Folders
32 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
33 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
34 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
35 |
36 | # Options for PL
37 | option(PL_BUILD_PYMODULE "PL - Build Python Module" ON)
38 | option(PL_BUILD_TESTS "PL - Build Tests" ON)
39 | option(PL_BUILD_BENCHMARKS "PL - Build Benchmarks" OFF)
40 | option(PL_BUILD_EXAMPLES "PL - Build Examples" ON)
41 | option(PL_WITH_OPENMP "PL - Build with OpenMP Support" ON)
42 | option(PL_BUILD_FASTGA "PL - Build FastGA with Example" OFF)
43 | option(PL_USE_ROBUST_PREDICATES "PL - Build with Fast Geometric Predicates" OFF)
44 | option(PL_BUILD_WERROR "PL - Add Werror flag to build (turns warnings into errors)" OFF)
45 |
46 | # Add any dependencies needed by our library
47 | add_subdirectory("thirdparty")
48 |
49 | # Build our library
50 | add_subdirectory("src")
51 |
52 | # Build examples if configured
53 | if(PL_BUILD_EXAMPLES)
54 | add_subdirectory("examples")
55 | endif()
56 |
57 | # Build tests if configured
58 | if(PL_BUILD_TESTS)
59 | add_subdirectory("tests")
60 | endif()
61 |
62 | # Build benchmarks if configured
63 | if(PL_BUILD_BENCHMARKS)
64 | add_subdirectory("bench")
65 | endif()
66 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Jeremy Castagno
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.txt
2 | include LICENSE
3 | recursive-include bench *.cpp
4 | recursive-include bench *.txt
5 | recursive-include cmake *.cmake
6 | recursive-include examples *.cpp
7 | recursive-include examples *.py
8 | recursive-include examples *.txt
9 | recursive-include include *.hpp
10 | recursive-include scripts *.py
11 | recursive-include scripts *.sh
12 | recursive-include src *.cmake
13 | recursive-include src *.cpp
14 | recursive-include src *.hpp
15 | recursive-include src *.in
16 | recursive-include src *.md
17 | recursive-include src *.py
18 | recursive-include src *.txt
19 | recursive-include tests *.cpp
20 | recursive-include tests *.py
21 | recursive-include tests *.txt
22 | recursive-include thirdparty *.cmake
23 | recursive-include thirdparty *.cpp
24 | recursive-include thirdparty *.hpp
25 | recursive-include thirdparty *.h
26 | recursive-include thirdparty *LICENSE
27 | recursive-include thirdparty *.in
28 | recursive-include thirdparty *.md
29 | recursive-include thirdparty *.py
30 | recursive-include thirdparty *.txt
--------------------------------------------------------------------------------
/assets/2D_polygon_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/assets/2D_polygon_text.png
--------------------------------------------------------------------------------
/assets/api-doc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/pl_3d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/assets/pl_3d.png
--------------------------------------------------------------------------------
/assets/pl_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/assets/pl_logo.png
--------------------------------------------------------------------------------
/assets/pl_logo_poly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/assets/pl_logo_poly.png
--------------------------------------------------------------------------------
/assets/polylidar_3D_architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/assets/polylidar_3D_architecture.jpg
--------------------------------------------------------------------------------
/bench/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | include(FetchContent)
2 |
3 | FetchContent_Declare(
4 | extern_googlebenchmark
5 | GIT_REPOSITORY https://github.com/google/benchmark.git
6 | GIT_TAG v1.5.0
7 | )
8 |
9 | # This module depend on having googletest and googlebenchmark installed
10 | FetchContent_GetProperties(extern_googlebenchmark)
11 | if(NOT extern_googlebenchmark_POPULATED)
12 | FetchContent_Populate(extern_googlebenchmark)
13 | SET(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Enable testing of the benchmark library.")
14 | add_subdirectory(${extern_googlebenchmark_SOURCE_DIR} ${extern_googlebenchmark_BINARY_DIR})
15 | endif()
16 |
17 | # Google Benchmark needs threads!
18 | find_package(Threads REQUIRED)
19 | find_package(Open3D REQUIRED)
20 |
21 |
22 | add_executable(run-bench ${CMAKE_CURRENT_SOURCE_DIR}/run-bench.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bench_mesh.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bench_mesh_paper.cpp
23 | )
24 | # ${CMAKE_CURRENT_SOURCE_DIR}/parallelism_test.cpp
25 | target_link_libraries(run-bench PRIVATE Polylidar::PL benchmark Threads::Threads ${Open3D_LIBRARIES})
26 | target_link_directories(run-bench PRIVATE ${Open3D_LIBRARY_DIRS})
27 | target_include_directories(run-bench PRIVATE ${Open3D_INCLUDE_DIRS})
--------------------------------------------------------------------------------
/bench/bench_mesh_paper.cpp:
--------------------------------------------------------------------------------
1 | // These benchmarks explicitely time plane and polygon extraction
2 | // It also measures how CPU scores scale with the problem
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | const std::string SPARSE_MESH = "./fixtures/meshes/basement_chair_5cm.ply";
17 | const std::string DENSE_MESH = "./fixtures/meshes/dense_first_floor_map_smoothed.ply";
18 |
19 | using namespace Polylidar;
20 | namespace o3d = open3d;
21 | using TriMesh = MeshHelper::HalfEdgeTriangulation;
22 |
23 | static constexpr int max_threads = 8;
24 |
25 | inline void CustomArguments_Paper(benchmark::internal::Benchmark* b)
26 | {
27 | for (int i = 1; i <= 4; i += 1)
28 | for (int j = 1; j <= max_threads; j += 1) b->Args({i, j});
29 | }
30 |
31 | class SparseMeshPaper : public benchmark::Fixture
32 | {
33 | public:
34 | std::shared_ptr sparse_mesh_o3d;
35 | TriMesh sparse_mesh;
36 | // Eigen::Matrix3d mesh_rotation;
37 | void SetUp(const ::benchmark::State& state)
38 | {
39 |
40 | // Sparse Mesh In O3D Format
41 | sparse_mesh_o3d = o3d::io::CreateMeshFromFile(SPARSE_MESH);
42 | // Sparse Mesh In HalfEdgeTriangulation
43 | auto triangles_ptr = reinterpret_cast(sparse_mesh_o3d->triangles_.data());
44 | auto num_triangles = sparse_mesh_o3d->triangles_.size();
45 | auto vertices_ptr = reinterpret_cast(sparse_mesh_o3d->vertices_.data());
46 | auto num_vertices = sparse_mesh_o3d->vertices_.size();
47 | Matrix triangles(triangles_ptr, num_triangles, 3);
48 | Matrix vertices(vertices_ptr, num_vertices, 3);
49 |
50 | sparse_mesh = MeshHelper::CreateTriMeshCopy(vertices, triangles, true);
51 | }
52 | };
53 |
54 | class DenseMeshPaper : public benchmark::Fixture
55 | {
56 | public:
57 | std::shared_ptr dense_mesh_o3d;
58 | TriMesh dense_mesh;
59 | // Eigen::Matrix3d mesh_rotation;
60 | void SetUp(const ::benchmark::State& state)
61 | {
62 |
63 | // Sparse Mesh In O3D Format
64 | dense_mesh_o3d = o3d::io::CreateMeshFromFile(DENSE_MESH);
65 | auto triangles_ptr = reinterpret_cast(dense_mesh_o3d->triangles_.data());
66 | auto num_triangles = dense_mesh_o3d->triangles_.size();
67 | auto vertices_ptr = reinterpret_cast(dense_mesh_o3d->vertices_.data());
68 | auto num_vertices = dense_mesh_o3d->vertices_.size();
69 | Matrix triangles(triangles_ptr, num_triangles, 3);
70 | Matrix vertices(vertices_ptr, num_vertices, 3);
71 |
72 | dense_mesh = MeshHelper::CreateTriMeshCopy(vertices, triangles, true);
73 | }
74 | };
75 |
76 | BENCHMARK_DEFINE_F(SparseMeshPaper, BM_Paper)
77 | (benchmark::State& st)
78 | {
79 | std::vector> normals = {{-0.0296, 0.0003, 0.9996},
80 | {-0.9534, 0.3016, 0.0077},
81 | {0.9365, -0.3371, 0.0963},
82 | {0.3054, 0.9522, -0.012},
83 | {0.0111 - 0.0113 - 0.9999}};
84 |
85 | int num_normals = st.range(0);
86 | int num_threads = st.range(1);
87 | omp_set_num_threads(num_threads);
88 | Polylidar3D pl3d(0.0, 0.1, 1000, 6, 0.03, 0.95, 0.92, num_threads);
89 | const Matrix normals_mat((double*)(normals.data()), num_normals, 3);
90 | for (auto _ : st)
91 | {
92 | auto planes_and_polygons = pl3d.ExtractPlanesAndPolygonsOptimized(sparse_mesh, normals_mat);
93 | benchmark::DoNotOptimize(planes_and_polygons);
94 | }
95 | }
96 |
97 | BENCHMARK_DEFINE_F(DenseMeshPaper, BM_Paper)
98 | (benchmark::State& st)
99 | {
100 | std::vector> normals = {
101 | {0.0123, 0.0021, 0.9999}, {0.2241, -0.9744, 0.0171}, {-0.2207, 0.9752, -0.0144}, {0.9612, 0.2756, -0.0129}};
102 |
103 | int num_normals = st.range(0);
104 | int num_threads = st.range(1);
105 | omp_set_num_threads(num_threads);
106 | Polylidar3D pl3d(0.0, 0.1, 1000, 6, 0.03, 0.95, 0.92, num_threads);
107 | const Matrix normals_mat((double*)(normals.data()), num_normals, 3);
108 | for (auto _ : st)
109 | {
110 | auto planes_and_polygons = pl3d.ExtractPlanesAndPolygonsOptimized(dense_mesh, normals_mat);
111 | benchmark::DoNotOptimize(planes_and_polygons);
112 | }
113 | }
114 |
115 | BENCHMARK_REGISTER_F(SparseMeshPaper, BM_Paper)
116 | ->Apply(CustomArguments_Paper)
117 | ->UseRealTime()
118 | ->Unit(benchmark::kMicrosecond);
119 | BENCHMARK_REGISTER_F(DenseMeshPaper, BM_Paper)
120 | ->Apply(CustomArguments_Paper)
121 | ->UseRealTime()
122 | ->Unit(benchmark::kMillisecond);
123 |
--------------------------------------------------------------------------------
/bench/run-bench.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | // Run the benchmark
4 | BENCHMARK_MAIN();
--------------------------------------------------------------------------------
/cmake/Extras.cmake:
--------------------------------------------------------------------------------
1 | function(target_link_libraries_system target)
2 | set(libs ${ARGN})
3 | foreach(lib ${libs})
4 | get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
5 | target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs})
6 | target_link_libraries(${target} PRIVATE ${lib})
7 | endforeach(lib)
8 | endfunction(target_link_libraries_system)
9 |
10 | function(target_link_libraries_system_public target)
11 | set(libs ${ARGN})
12 | foreach(lib ${libs})
13 | get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
14 | target_include_directories(${target} SYSTEM PUBLIC ${lib_include_dirs})
15 | target_link_libraries(${target} PUBLIC ${lib})
16 | endforeach(lib)
17 | endfunction(target_link_libraries_system_public)
18 |
19 | macro(add_subdirectory_if_not_exists LIBRARY SUB_DIR)
20 | if(NOT TARGET ${LIBRARY})
21 | message("-- INFO - Trying to add ${LIBRARY} from thirdparty submodule")
22 | set(LOCAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}")
23 | if (EXISTS $LOCAL_DIR)
24 | add_subdirectory("${LOCAL_DIR}")
25 | else()
26 | add_subdirectory("${PROJECT_SOURCE_DIR}/${SUB_DIR}")
27 | endif()
28 | else()
29 | message("-- INFO - Skipping add_subdirectory(${LIBRARY}) because it has already been added")
30 | endif()
31 | endmacro(add_subdirectory_if_not_exists)
--------------------------------------------------------------------------------
/cmake/FindSphinx.cmake:
--------------------------------------------------------------------------------
1 | #Look for an executable called sphinx-build
2 | find_program(SPHINX_EXECUTABLE
3 | NAMES sphinx-build
4 | DOC "Path to sphinx-build executable")
5 |
6 | include(FindPackageHandleStandardArgs)
7 | #Handle standard arguments to find_package like REQUIRED and QUIET
8 | find_package_handle_standard_args(Sphinx
9 | "Failed to find sphinx-build executable"
10 | SPHINX_EXECUTABLE)
--------------------------------------------------------------------------------
/cmake/GperftoolsConfig.cmake:
--------------------------------------------------------------------------------
1 | # Tries to find Gperftools.
2 | #
3 | # Usage of this module as follows:
4 | #
5 | # find_package(Gperftools)
6 | #
7 | # Variables used by this module, they can change the default behaviour and need
8 | # to be set before calling find_package:
9 | #
10 | # Gperftools_ROOT_DIR Set this variable to the root installation of Gperftools
11 | # if the module has problems finding the proper installation path.
12 | #
13 | # Variables defined by this module:
14 | #
15 | # GPERFTOOLS_FOUND System has Gperftools libs/headers
16 | # GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler)
17 | # GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers
18 |
19 | find_library(
20 | GPERFTOOLS_TCMALLOC
21 | NAMES tcmalloc
22 | HINTS ${Gperftools_ROOT_DIR}/lib)
23 |
24 | find_library(
25 | GPERFTOOLS_PROFILER
26 | NAMES profiler
27 | HINTS ${Gperftools_ROOT_DIR}/lib)
28 |
29 | find_library(
30 | GPERFTOOLS_TCMALLOC_AND_PROFILER
31 | NAMES tcmalloc_and_profiler
32 | HINTS ${Gperftools_ROOT_DIR}/lib)
33 |
34 | find_path(
35 | GPERFTOOLS_INCLUDE_DIR
36 | NAMES gperftools/heap-profiler.h
37 | HINTS ${Gperftools_ROOT_DIR}/include)
38 |
39 | set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_TCMALLOC_AND_PROFILER})
40 |
41 | include(FindPackageHandleStandardArgs)
42 | find_package_handle_standard_args(Gperftools DEFAULT_MSG GPERFTOOLS_LIBRARIES
43 | GPERFTOOLS_INCLUDE_DIR)
44 |
45 | mark_as_advanced(
46 | Gperftools_ROOT_DIR GPERFTOOLS_TCMALLOC GPERFTOOLS_PROFILER
47 | GPERFTOOLS_TCMALLOC_AND_PROFILER GPERFTOOLS_LIBRARIES GPERFTOOLS_INCLUDE_DIR)
48 |
49 | # create IMPORTED targets
50 | if (Gperftools_FOUND AND NOT TARGET gperftools::tcmalloc)
51 | add_library(gperftools::tcmalloc UNKNOWN IMPORTED)
52 | set_target_properties(
53 | gperftools::tcmalloc
54 | PROPERTIES
55 | IMPORTED_LOCATION ${GPERFTOOLS_TCMALLOC} INTERFACE_INCLUDE_DIRECTORIES
56 | "${GPERFTOOLS_INCLUDE_DIR}")
57 | add_library(gperftools::profiler UNKNOWN IMPORTED)
58 | set_target_properties(
59 | gperftools::profiler
60 | PROPERTIES
61 | IMPORTED_LOCATION ${GPERFTOOLS_PROFILER} INTERFACE_INCLUDE_DIRECTORIES
62 | "${GPERFTOOLS_INCLUDE_DIR}")
63 | endif ()
64 |
--------------------------------------------------------------------------------
/dev-requirements.txt:
--------------------------------------------------------------------------------
1 | gitpython # for version bump script
2 | pytest # for testing
3 |
4 | numpy
5 | matplotlib
6 | scipy
7 | open3d
8 |
9 | # Documentation Generation Packages
10 | notebook==5.7.10
11 | docutils>=0.17
12 | Sphinx>=4.0,<5
13 | sphinx_rtd_theme
14 | exhale==0.3.6
15 | breathe>=4.32.0
16 | m2r2==0.3.2
17 | nbsphinx==0.7.1
18 | nbsphinx-link==1.3.0
19 | Jinja2==2.11.3
20 | MarkupSafe==2.0.1
21 |
22 | # Also need to use conda to install these through conda or get binaries another way
23 | # conda install -c conda-forge pandoc doxygen
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/_images/opc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/opc.png
--------------------------------------------------------------------------------
/docs/_images/opc_mesh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/opc_mesh.png
--------------------------------------------------------------------------------
/docs/_images/opc_polygons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/opc_polygons.png
--------------------------------------------------------------------------------
/docs/_images/pl_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/pl_logo.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_basicdemo_14_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_basicdemo_14_0.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_basicdemo_17_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_basicdemo_17_0.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_basicdemo_21_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_basicdemo_21_1.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_basicdemo_6_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_basicdemo_6_1.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_organizeddemo_10_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_organizeddemo_10_0.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_organizeddemo_12_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_organizeddemo_12_0.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_organizeddemo_22_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_organizeddemo_22_0.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_organizeddemo_27_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_organizeddemo_27_0.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_organizeddemo_37_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_organizeddemo_37_0.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_organizeddemo_5_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_organizeddemo_5_0.jpg
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_organizeddemo_6_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_organizeddemo_6_1.png
--------------------------------------------------------------------------------
/docs/_images/tutorial_Python_organizeddemo_7_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_images/tutorial_Python_organizeddemo_7_0.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/LICENSE.md:
--------------------------------------------------------------------------------
1 | This code is the fruit of Kate Morley's labor, taken from here:
2 |
3 | - http://code.iamkate.com/javascript/collapsible-lists/
4 |
5 | She includes a generous CC0 1.0 license for all materials on her site:
6 |
7 | - http://code.iamkate.com/
8 |
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/button-closed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/button-closed.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/button-open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/button-open.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/button.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/list-item-contents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/list-item-contents.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/list-item-last-open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/list-item-last-open.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/list-item-last.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/list-item-last.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/list-item-open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/list-item-open.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/list-item-root.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/list-item-root.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/list-item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/collapsible-lists/css/list-item.png
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/css/tree_view.css:
--------------------------------------------------------------------------------
1 | /* Source taken directly from:
2 | * view-source:http://code.iamkate.com/javascript/collapsible-lists/
3 | *
4 | * Kate Morley's license for this code is CC0:
5 | * Created by [Kate Morley](http://iamkate.com/). Except where explicitly
6 | * stated otherwise, all content is released under the terms of the
7 | * [CC0 1.0 Universal legal code](http://creativecommons.org/publicdomain/zero/1.0/legalcode).
8 | */
9 | .treeView{
10 | -moz-user-select:none;
11 | position:relative;
12 | }
13 |
14 | .treeView ul{
15 | margin:0 0 0 -1.5em ! important;
16 | padding:0 0 0 1.5em ! important;
17 | }
18 |
19 | .treeView ul ul{
20 | background:url('list-item-contents.png') repeat-y left ! important;
21 | }
22 |
23 | .treeView li.lastChild > ul{
24 | background-image:none ! important;
25 | }
26 |
27 | .treeView li{
28 | margin:0 ! important;
29 | padding:0 ! important;
30 | background:url('list-item-root.png') no-repeat top left ! important;
31 | list-style-position:inside ! important;
32 | list-style-image:url('button.png') ! important;
33 | cursor:auto;
34 | }
35 |
36 | .treeView li.collapsibleListOpen{
37 | list-style-image:url('button-open.png') ! important;
38 | cursor:pointer;
39 | }
40 |
41 | .treeView li.collapsibleListClosed{
42 | list-style-image:url('button-closed.png') ! important;
43 | cursor:pointer;
44 | }
45 |
46 | .treeView li li{
47 | background-image:url('list-item.png') ! important;
48 | padding-left:1.5em ! important;
49 | }
50 |
51 | .treeView li.lastChild{
52 | background-image:url('list-item-last.png') ! important;
53 | }
54 |
55 | .treeView li.collapsibleListOpen{
56 | background-image:url('list-item-open.png') ! important;
57 | }
58 |
59 | .treeView li.collapsibleListOpen.lastChild{
60 | background-image:url('list-item-last-open.png') ! important;
61 | }
62 |
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/js/CollapsibleLists.compressed.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | CollapsibleLists.js
4 |
5 | An object allowing lists to dynamically expand and collapse
6 |
7 | Created by Kate Morley - http://code.iamkate.com/ - and released under
8 | the terms of the CC0 1.0 Universal legal code:
9 |
10 | http://creativecommons.org/publicdomain/zero/1.0/legalcode
11 |
12 | */
13 |
14 | var CollapsibleLists=new function(){
15 | this.apply=function(_1){
16 | var _2=document.getElementsByTagName("ul");
17 | for(var _3=0;_3<_2.length;_3++){
18 | if(_2[_3].className.match(/(^| )collapsibleList( |$)/)){
19 | this.applyTo(_2[_3],true);
20 | if(!_1){
21 | var _4=_2[_3].getElementsByTagName("ul");
22 | for(var _5=0;_5<_4.length;_5++){
23 | _4[_5].className+=" collapsibleList";
24 | }
25 | }
26 | }
27 | }
28 | };
29 | this.applyTo=function(_6,_7){
30 | var _8=_6.getElementsByTagName("li");
31 | for(var _9=0;_9<_8.length;_9++){
32 | if(!_7||_6==_8[_9].parentNode){
33 | if(_8[_9].addEventListener){
34 | _8[_9].addEventListener("mousedown",function(e){
35 | e.preventDefault();
36 | },false);
37 | }else{
38 | _8[_9].attachEvent("onselectstart",function(){
39 | event.returnValue=false;
40 | });
41 | }
42 | if(_8[_9].addEventListener){
43 | _8[_9].addEventListener("click",_a(_8[_9]),false);
44 | }else{
45 | _8[_9].attachEvent("onclick",_a(_8[_9]));
46 | }
47 | _b(_8[_9]);
48 | }
49 | }
50 | };
51 | function _a(_c){
52 | return function(e){
53 | if(!e){
54 | e=window.event;
55 | }
56 | var _d=(e.target?e.target:e.srcElement);
57 | while(_d.nodeName!="LI"){
58 | _d=_d.parentNode;
59 | }
60 | if(_d==_c){
61 | _b(_c);
62 | }
63 | };
64 | };
65 | function _b(_e){
66 | var _f=_e.className.match(/(^| )collapsibleListClosed( |$)/);
67 | var uls=_e.getElementsByTagName("ul");
68 | for(var _10=0;_100){
79 | _e.className+=" collapsibleList"+(_f?"Open":"Closed");
80 | }
81 | };
82 | }();
83 |
84 |
--------------------------------------------------------------------------------
/docs/_static/collapsible-lists/js/apply-collapsible-lists.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | CollapsibleLists.apply();
3 | });
4 |
--------------------------------------------------------------------------------
/docs/_static/cpp_tutorial/opc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/cpp_tutorial/opc.png
--------------------------------------------------------------------------------
/docs/_static/cpp_tutorial/opc_mesh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/cpp_tutorial/opc_mesh.png
--------------------------------------------------------------------------------
/docs/_static/cpp_tutorial/opc_polygons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/cpp_tutorial/opc_polygons.png
--------------------------------------------------------------------------------
/docs/_static/css/badge_only.css:
--------------------------------------------------------------------------------
1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../fonts/fontawesome-webfont.eot");src:url("../fonts/fontawesome-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff") format("woff"),url("../fonts/fontawesome-webfont.ttf") format("truetype"),url("../fonts/fontawesome-webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
2 |
--------------------------------------------------------------------------------
/docs/_static/documentation_options.js:
--------------------------------------------------------------------------------
1 | var DOCUMENTATION_OPTIONS = {
2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
3 | VERSION: '1.0.9',
4 | LANGUAGE: 'None',
5 | COLLAPSE_INDEX: false,
6 | BUILDER: 'html',
7 | FILE_SUFFIX: '.html',
8 | LINK_SUFFIX: '.html',
9 | HAS_SOURCE: true,
10 | SOURCELINK_SUFFIX: '.txt',
11 | NAVIGATION_WITH_KEYS: false,
12 | SHOW_SEARCH_SUMMARY: true,
13 | ENABLE_SEARCH_SHORTCUTS: true,
14 | };
--------------------------------------------------------------------------------
/docs/_static/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/file.png
--------------------------------------------------------------------------------
/docs/_static/fonts/Inconsolata-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Inconsolata-Bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Inconsolata-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Inconsolata-Regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Inconsolata.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Inconsolata.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato-Bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato-Regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-bold.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-bold.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bolditalic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-bolditalic.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bolditalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-bolditalic.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bolditalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-bolditalic.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-bolditalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-bolditalic.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-italic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-italic.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-italic.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-italic.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-italic.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-regular.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-regular.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/Lato/lato-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/Lato/lato-regular.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab-Bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab-Regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2
--------------------------------------------------------------------------------
/docs/_static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/_static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/_static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/_static/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/docs/_static/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/_static/js/theme.js:
--------------------------------------------------------------------------------
1 | /* sphinx_rtd_theme version 0.4.3 | MIT license */
2 | /* Built 20190212 16:02 */
3 | require=function r(s,a,l){function c(e,n){if(!a[e]){if(!s[e]){var i="function"==typeof require&&require;if(!n&&i)return i(e,!0);if(u)return u(e,!0);var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}var o=a[e]={exports:{}};s[e][0].call(o.exports,function(n){return c(s[e][1][n]||n)},o,o.exports,r,s,a,l)}return a[e].exports}for(var u="function"==typeof require&&require,n=0;n"),i("table.docutils.footnote").wrap(""),i("table.docutils.citation").wrap(""),i(".wy-menu-vertical ul").not(".simple").siblings("a").each(function(){var e=i(this);expand=i(''),expand.on("click",function(n){return t.toggleCurrent(e),n.stopPropagation(),!1}),e.prepend(expand)})},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),i=e.find('[href="'+n+'"]');if(0===i.length){var t=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(i=e.find('[href="#'+t.attr("id")+'"]')).length&&(i=e.find('[href="#"]'))}0this.docHeight||(this.navBar.scrollTop(i),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",function(){this.linkScroll=!1})},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:e.exports.ThemeNav,StickyNav:e.exports.ThemeNav}),function(){for(var r=0,n=["ms","moz","webkit","o"],e=0;e
--------------------------------------------------------------------------------
/examples/cpp/debug.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "Polylidar/Polylidar.hpp"
3 | #include "Polylidar/Delaunator/Delaunator.hpp"
4 | #include "npy/npy.h"
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | using namespace Polylidar;
11 | namespace o3d = open3d;
12 | using TriMesh = MeshHelper::HalfEdgeTriangulation;
13 |
14 | void PrintPoint(Polylidar::Delaunator::Delaunator &mesh, size_t idx)
15 | {
16 |
17 | std:: cout << "[" << mesh.triangles(idx, 0) << ", " << mesh.triangles(idx, 1) << ", " << mesh.triangles(idx, 2) << "]" << std::endl;
18 | }
19 |
20 | int main(int argc, char const *argv[])
21 | {
22 | const std::string SPARSE_MESH = "./fixtures/meshes/sparse_basement.ply";
23 | Eigen::Matrix3d mesh_rotation;
24 | std::shared_ptr sparse_mesh_o3d;
25 |
26 | // Rotation for mesh, camera axis to global frame
27 | mesh_rotation << 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0;
28 | // Sparse Mesh In O3D Format
29 | sparse_mesh_o3d = o3d::io::CreateMeshFromFile(SPARSE_MESH);
30 | sparse_mesh_o3d->Rotate(mesh_rotation);
31 | // sparse_mesh_o3d->Rotate();
32 | // Sparse Mesh In HalfEdgeTriangulation
33 | auto triangles_ptr = reinterpret_cast(sparse_mesh_o3d->triangles_.data());
34 | auto num_triangles = sparse_mesh_o3d->triangles_.size();
35 | auto vertices_ptr = reinterpret_cast(sparse_mesh_o3d->vertices_.data());
36 | auto num_vertices = sparse_mesh_o3d->vertices_.size();
37 | Matrix triangles(triangles_ptr, num_triangles, 3);
38 | Matrix vertices(vertices_ptr, num_vertices, 3);
39 |
40 | TriMesh sparse_mesh = MeshHelper::CreateTriMeshCopy(vertices, triangles, true);
41 |
42 | // MeshHelper::BilateralFilterNormals(sparse_mesh, 1, 0.1, 0.1);
43 | // alpha, lmax, min_tri, min_hole_vert, z_thresh, norm__thresh, norm_thresh_min, _task_threads
44 | Polylidar3D pl3d(0.0, 0.1, 1000, 6, 0.03, 0.95, 0.90);
45 | auto planes_and_polygons = pl3d.ExtractPlanesAndPolygonsOptimized(sparse_mesh, {{0.0, 0.0, 1.0}});
46 | std::cout << "Stop Here";
47 |
48 |
49 | return 0;
50 | }
51 |
--------------------------------------------------------------------------------
/examples/cpp/simple.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include "Polylidar/Polylidar.hpp"
5 |
6 | template
7 | std::ostream& operator<<(std::ostream& os, const std::vector& vec)
8 | {
9 | auto iter_begin = vec.begin();
10 | auto iter_end = vec.end();
11 | os << "[";
12 | for (auto iter = iter_begin; iter != iter_end; ++iter)
13 | {
14 | std::cout << ((iter != iter_begin) ? "," : "") << *iter;
15 | }
16 | os << "]";
17 | return os;
18 | }
19 | int main(int argc, char const* argv[])
20 | {
21 | std::cout << "Very Simple C++ Example of Polylidar3D on 2D Point Set" << std::endl;
22 | std::vector points_data = {
23 | 0.0, 0.0, // Point index 0
24 | 0.0, 1.0, // Point index 1
25 | 1.0, 1.0, // Point index 2
26 | 1.0, 0.0, // Point index 3
27 | 5.0, 0.1, // Point index 4, outlier and should not be included in polygon
28 | };
29 |
30 | // 5 X 2 matrix as one contigious array
31 | std::vector shape = {points_data.size() / 2, 2};
32 | Polylidar::Matrix points(points_data.data(), shape[0], shape[1]);
33 | // Set configuration parameters
34 |
35 | // alpha, lmax, min triangles, min hole vertices,
36 | Polylidar::Polylidar3D pl(0.0, 2.0, 1, 3);
37 |
38 | Polylidar::MeshHelper::HalfEdgeTriangulation mesh;
39 | Polylidar::Planes planes;
40 | Polylidar::Polygons polygons;
41 | auto before = std::chrono::high_resolution_clock::now();
42 | std:tie(mesh, planes, polygons) = pl.ExtractPlanesAndPolygons(points);
43 | auto after = std::chrono::high_resolution_clock::now();
44 |
45 | auto elapsed = std::chrono::duration_cast(after - before);
46 | std::cout << "Polylidar took " << elapsed.count() << " milliseconds processing a " << shape[0] << " point cloud"
47 | << std::endl;
48 | std::cout << "Point indices of Polygon Shell: ";
49 | // Extract polygon
50 | for (auto const& polygon : polygons)
51 | {
52 | std::cout << polygon.shell << std::endl;
53 | }
54 | // Point indices of Polygon Shell: [3,0,1,2]
55 |
56 | std::cout << std::endl;
57 |
58 | return 0;
59 | }
60 |
--------------------------------------------------------------------------------
/examples/cpp/vis_util.hpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include "FastGA/FastGA.hpp"
4 | #include "open3d/Open3D.h"
5 | namespace VisUtility {
6 |
7 | FastGA::MatX3d colors_palette{{0.12156862745098039, 0.4666666666666667, 0.7058823529411765},
8 | {1.0, 0.4980392156862745, 0.054901960784313725},
9 | {0.17254901960784313, 0.6274509803921569, 0.17254901960784313},
10 | {0.8392156862745098, 0.15294117647058825, 0.1568627450980392},
11 | {0.5803921568627451, 0.403921568627451, 0.7411764705882353},
12 | {0.5490196078431373, 0.33725490196078434, 0.29411764705882354},
13 | {0.8901960784313725, 0.4666666666666667, 0.7607843137254902},
14 | {0.4980392156862745, 0.4980392156862745, 0.4980392156862745},
15 | {0.7372549019607844, 0.7411764705882353, 0.13333333333333333},
16 | {0.09019607843137255, 0.7450980392156863, 0.8117647058823529}};
17 |
18 | int color_counter = 0;
19 |
20 | std::array NextColor()
21 | {
22 | auto color = colors_palette[color_counter];
23 | color_counter++;
24 | if (color_counter >= colors_palette.size())
25 | {
26 | color_counter = 0;
27 | }
28 | return color;
29 | }
30 |
31 | template
32 | std::vector MapEigen(Polylidar::Matrix& array, const bool set_nan_to_zero = true)
33 | {
34 | std::vector eigen_vectors;
35 | using TO_T = eigen_vector::Scalar;
36 | for (auto i = 0; i < array.rows; ++i)
37 | {
38 | // Open3D Point cloud can't handle Nans
39 | if (set_nan_to_zero && std::isnan(static_cast(array(i, 0))))
40 | {
41 | eigen_vectors.emplace_back(static_cast(0), static_cast(0), static_cast(0));
42 | }
43 | else
44 | {
45 | eigen_vectors.emplace_back(static_cast(array(i, 0)), static_cast(array(i, 1)),
46 | static_cast(array(i, 2)));
47 | }
48 | }
49 | return eigen_vectors;
50 | }
51 |
52 | std::shared_ptr CreateOpen3DMesh(Polylidar::MeshHelper::HalfEdgeTriangulation& tri_mesh)
53 | {
54 | std::shared_ptr triangle_ptr(new open3d::geometry::TriangleMesh());
55 | triangle_ptr->vertices_ = MapEigen(tri_mesh.vertices);
56 | triangle_ptr->triangles_ = MapEigen(tri_mesh.triangles, false);
57 | triangle_ptr->triangle_normals_ = MapEigen(tri_mesh.triangle_normals);
58 | triangle_ptr->ComputeVertexNormals();
59 | triangle_ptr->PaintUniformColor({0.5, 0.5, 0.5});
60 | return triangle_ptr;
61 | }
62 |
63 | void PaintPlane(std::shared_ptr o3d_mesh, std::vector& segment,
64 | std::array color)
65 | {
66 | auto& triangles = o3d_mesh->triangles_;
67 | Eigen::Vector3d color_e(color[0], color[1], color[2]);
68 | for (auto& t : segment)
69 | {
70 | auto vertices_idx = triangles[t];
71 | o3d_mesh->vertex_colors_[vertices_idx[0]] = color_e;
72 | o3d_mesh->vertex_colors_[vertices_idx[1]] = color_e;
73 | o3d_mesh->vertex_colors_[vertices_idx[2]] = color_e;
74 | }
75 | }
76 |
77 | std::shared_ptr CreateLineSetFromPolygon(Polylidar::Polygon& polygon,
78 | Polylidar::Matrix points,
79 | Eigen::Vector3d shell_color = {0.0, 1.0, 0.0},
80 | Eigen::Vector3d hole_color = {1.0, 0.0, 0.0})
81 | {
82 | std::vector line_points;
83 | std::vector lines;
84 | std::vector colors;
85 |
86 | std::shared_ptr ls_ptr(new open3d::geometry::LineSet());
87 | for (int i = 0; i < polygon.shell.size(); ++i)
88 | {
89 | auto idx = polygon.shell[i];
90 | Eigen::Vector3d line_point(points(idx, 0), points(idx, 1), points(idx, 2));
91 | line_points.push_back(line_point);
92 | int cur_point = static_cast(line_points.size() - 1);
93 | colors.push_back(shell_color);
94 | if (i != polygon.shell.size() - 1)
95 | {
96 | lines.push_back({cur_point, cur_point + 1});
97 | colors.push_back(shell_color);
98 | }
99 | }
100 | for (auto& hole : polygon.holes)
101 | {
102 | for (int i = 0; i < hole.size(); ++i)
103 | {
104 | auto idx = hole[i];
105 | Eigen::Vector3d line_point(points(idx, 0), points(idx, 1), points(idx, 2));
106 | line_points.push_back(line_point);
107 | int cur_point = static_cast(line_points.size() - 1);
108 | colors.push_back(hole_color);
109 | if (i != hole.size() - 1)
110 | {
111 | lines.push_back({cur_point, cur_point + 1});
112 | colors.push_back(hole_color);
113 | }
114 | }
115 | }
116 | ls_ptr->points_ = line_points;
117 | ls_ptr->lines_ = lines;
118 | ls_ptr->colors_ = colors;
119 | return ls_ptr;
120 | }
121 |
122 | } // namespace VisUtility
--------------------------------------------------------------------------------
/examples/python/basic25d.py:
--------------------------------------------------------------------------------
1 | """Example MultiPolygon Extraction with an **Unorganized* 3D Point Cloud
2 | Note this method is only suitable if you know the dominant surface normal of the plane you desire to extract.
3 | The 3D point cloud must be rotated such that this surface normal is [0,0,1], i.e., the plane is aligned with the XY Plane
4 | The method relies upon 3D->2D Projection and performs 2.5D Delaunay Triangulation.
5 | """
6 | import time
7 | import math
8 |
9 | import numpy as np
10 | import matplotlib.pyplot as plt
11 | from mpl_toolkits.mplot3d import Axes3D
12 |
13 |
14 | from polylidar import MatrixDouble, Polylidar3D
15 | from polylidar.polylidarutil import (plot_polygons_3d, generate_3d_plane, set_axes_equal, plot_planes_3d,
16 | scale_points, rotation_matrix, apply_rotation)
17 |
18 |
19 | def main():
20 | np.random.seed(1)
21 | # generate random plane with hole
22 | plane = generate_3d_plane(bounds_x=[0, 10, 0.5], bounds_y=[0, 10, 0.5], holes=[
23 | [[3, 5], [3, 5]]], height_noise=0.02, planar_noise=0.02)
24 | # Generate top of box (causing the hole that we see)
25 | box_top = generate_3d_plane(bounds_x=[3, 5, 0.2], bounds_y=[3, 5, 0.2], holes=[
26 | ], height_noise=0.02, height=2, planar_noise=0.02)
27 | # Generate side of box (causing the hole that we see)
28 | box_side = generate_3d_plane(bounds_x=[0, 2, 0.2], bounds_y=[
29 | 0, 2, 0.2], holes=[], height_noise=0.02, planar_noise=0.02)
30 | rm = rotation_matrix([0, 1, 0], -math.pi / 2.0)
31 | box_side = apply_rotation(rm, box_side) + [5, 3, 0]
32 | # All points joined together
33 | points = np.concatenate((plane, box_side, box_top))
34 |
35 | points_mat = MatrixDouble(points)
36 | polylidar_kwargs = dict(alpha=0.0, lmax=1.0, min_triangles=20, z_thresh=0.1, norm_thresh_min=0.94)
37 | polylidar = Polylidar3D(**polylidar_kwargs)
38 |
39 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
40 | subplot_kw=dict(projection='3d'))
41 | # plot points
42 | ax.scatter(*scale_points(points), s=2.5, c=points[:, 2], cmap=plt.cm.plasma)
43 | set_axes_equal(ax)
44 | ax.view_init(elev=15., azim=-35)
45 | plt.show()
46 |
47 | # Extracts planes and polygons, time
48 | t1 = time.time()
49 | mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
50 | t2 = time.time()
51 | print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))
52 | print("Should see two planes extracted, please rotate.")
53 |
54 | triangles = np.asarray(mesh.triangles)
55 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
56 | subplot_kw=dict(projection='3d'))
57 | # plot all triangles
58 | plot_planes_3d(points, triangles, planes, ax)
59 | plot_polygons_3d(points, polygons, ax)
60 | # plot points
61 | ax.scatter(*scale_points(points), c='k', s=0.1)
62 | set_axes_equal(ax)
63 | ax.view_init(elev=15., azim=-35)
64 | plt.show()
65 | print("")
66 |
67 |
68 | if __name__ == "__main__":
69 | main()
70 |
--------------------------------------------------------------------------------
/examples/python/basic25d_algorithm.py:
--------------------------------------------------------------------------------
1 | """Example MultiPolygon Extraction with an **Unorganized* 3D Point Cloud. Step through algorithm.
2 | Note this method is only suitable if you know the dominant surface normal of the plane you desire to extract.
3 | The 3D point cloud must be rotated such that this surface normal is [0,0,1], i.e., the plane is aligned with the XY Plane
4 | The method relies upon 3D->2D Projection and performs 2.5D Delaunay Triangulation.
5 | """
6 | import time
7 | import math
8 |
9 | import numpy as np
10 | import matplotlib.pyplot as plt
11 | from mpl_toolkits.mplot3d import Axes3D
12 |
13 |
14 | from polylidar import MatrixDouble, Polylidar3D
15 | from polylidar.polylidarutil import (plot_polygons_3d, generate_3d_plane, set_axes_equal, plot_planes_3d,
16 | scale_points, rotation_matrix, apply_rotation)
17 |
18 |
19 | def main():
20 | np.random.seed(1)
21 | # generate random plane with hole
22 | plane = generate_3d_plane(bounds_x=[0, 10, 0.5], bounds_y=[0, 10, 0.5], holes=[
23 | [[3, 5], [3, 5]]], height_noise=0.02, planar_noise=0.02)
24 | # Generate top of box (causing the hole that we see)
25 | box_top = generate_3d_plane(bounds_x=[3, 5, 0.2], bounds_y=[3, 5, 0.2], holes=[
26 | ], height_noise=0.02, height=2, planar_noise=0.02)
27 | # Generate side of box (causing the hole that we see)
28 | box_side = generate_3d_plane(bounds_x=[0, 2, 0.2], bounds_y=[
29 | 0, 2, 0.2], holes=[], height_noise=0.02, planar_noise=0.02)
30 | rm = rotation_matrix([0, 1, 0], -math.pi / 2.0)
31 | box_side = apply_rotation(rm, box_side) + [5, 3, 0]
32 | # All points joined together
33 | points = np.concatenate((plane, box_side, box_top))
34 |
35 | points_mat = MatrixDouble(points)
36 | polylidar_kwargs = dict(alpha=0.0, lmax=1.0, min_triangles=20, z_thresh=0.1, norm_thresh_min=0.94)
37 | polylidar = Polylidar3D(**polylidar_kwargs)
38 |
39 | elev = 15.0
40 | azim = -35
41 |
42 | # Show Point Cloud
43 | print("Should see point raw point cloud")
44 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
45 | subplot_kw=dict(projection='3d'))
46 | # plot points
47 | ax.scatter(*scale_points(points), s=20.0, c=points[:, 2], cmap=plt.cm.plasma)
48 | set_axes_equal(ax)
49 | ax.view_init(elev=elev, azim=azim)
50 | fig.savefig("assets/scratch/Basic25DAlgorithm_pointcloud.pdf", bbox_inches='tight')
51 | fig.savefig("assets/scratch/Basic25DAlgorithm_pointcloud.png", bbox_inches='tight', pad_inches=-0.8)
52 | plt.show()
53 |
54 | # Extracts planes and polygons, time
55 | t1 = time.time()
56 | mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
57 | t2 = time.time()
58 | print("Polylidar Took {:.2f} milliseconds".format((t2 - t1) * 1000))
59 |
60 | triangles = np.asarray(mesh.triangles)
61 | all_planes = [np.arange(triangles.shape[0])]
62 |
63 | # Show Triangulation
64 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
65 | subplot_kw=dict(projection='3d'))
66 |
67 | plot_planes_3d(points, triangles, all_planes, ax, alpha=0.0, z_value=-4.0)
68 | plot_planes_3d(points, triangles, all_planes, ax, alpha=0.5)
69 | # plot points
70 | ax.scatter(*scale_points(points), s=0.1, c='k')
71 | set_axes_equal(ax, ignore_z=True)
72 | ax.set_zlim3d([-4, 6])
73 | ax.view_init(elev=elev, azim=azim)
74 | print("Should see triangulation point cloud")
75 | fig.savefig("assets/scratch/Basic25DAlgorithm_mesh.pdf", bbox_inches='tight')
76 | fig.savefig("assets/scratch/Basic25DAlgorithm_mesh.png", bbox_inches='tight', pad_inches=-0.8)
77 | plt.show()
78 |
79 | print("")
80 | print("Should see two planes extracted, please rotate.")
81 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
82 | subplot_kw=dict(projection='3d'))
83 | # plot all triangles
84 | plot_polygons_3d(points, polygons, ax)
85 | plot_planes_3d(points, triangles, planes, ax)
86 | # plot points
87 | ax.scatter(*scale_points(points), c='k', s=0.1)
88 | set_axes_equal(ax)
89 | ax.view_init(elev=elev, azim=azim)
90 | fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.pdf", bbox_inches='tight')
91 | fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.png", bbox_inches='tight', pad_inches=-0.8)
92 | plt.show()
93 | print("")
94 |
95 |
96 | if __name__ == "__main__":
97 | main()
98 |
--------------------------------------------------------------------------------
/examples/python/basic2d.py:
--------------------------------------------------------------------------------
1 | """Example of extracting MultiPolygons with holes of a 2D point set
2 | """
3 | import time
4 |
5 | import numpy as np
6 | from polylidar import MatrixDouble, Polylidar3D
7 | from polylidar.polylidarutil import (generate_test_points, plot_triangles, get_estimated_lmax,
8 | plot_triangle_meshes, get_triangles_from_list, get_colored_planar_segments, plot_polygons)
9 | import matplotlib.pyplot as plt
10 |
11 | np.random.seed(1)
12 |
13 |
14 | def main():
15 | kwargs = dict(num_groups=2, group_size=1000, dist=100.0, seed=1)
16 | # generate 2 random normally distributed clusters of points, 200 X 2 numpy array.
17 | points = generate_test_points(**kwargs)
18 | lmax = get_estimated_lmax(**kwargs)
19 | polylidar_kwargs = dict(alpha=0.0, lmax=lmax, min_triangles=5)
20 |
21 | # Convert points to matrix format (no copy) and make Polylidar3D Object
22 | points_mat = MatrixDouble(points, copy=False)
23 | polylidar = Polylidar3D(**polylidar_kwargs)
24 |
25 | # Extract the mesh, planes, polygons, and time
26 | t1 = time.perf_counter()
27 | mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
28 | t2 = time.perf_counter()
29 |
30 | print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))
31 |
32 | # Convert to numpy format, no copy with np.asarray()
33 | triangles = np.asarray(mesh.triangles)
34 |
35 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
36 | # plot points
37 | ax.scatter(points[:, 0], points[:, 1], c='k')
38 | # plot all triangles
39 | # plt.triplot(points[:,0], points[:,1], triangles) # better alternative
40 | plot_triangles(get_triangles_from_list(triangles, points), ax)
41 | # plot seperated planar triangular segments
42 | triangle_meshes = get_colored_planar_segments(planes, triangles, points)
43 | plot_triangle_meshes(triangle_meshes, ax)
44 | # plot polygons
45 | plot_polygons(polygons, points, ax)
46 | plt.axis('equal')
47 | plt.show()
48 |
49 |
50 | if __name__ == "__main__":
51 | main()
52 |
--------------------------------------------------------------------------------
/examples/python/basic2d_algorithm.py:
--------------------------------------------------------------------------------
1 | """Example of extracting MultiPolygons with holes of a 2D point set. Step through algorithm.
2 | """
3 | import time
4 | import numpy as np
5 | from polylidar import Delaunator, MatrixDouble, Polylidar3D
6 | from polylidar.polylidarutil import (generate_test_points, plot_points, plot_triangles, get_estimated_lmax,
7 | plot_triangle_meshes, get_triangles_from_list, get_colored_planar_segments, plot_polygons)
8 | import matplotlib.pyplot as plt
9 |
10 | np.random.seed(1)
11 |
12 | def get_np_buffer_ptr(a):
13 | pointer, read_only_flag = a.__array_interface__['data']
14 | return pointer
15 |
16 | def main():
17 |
18 | kwargs = dict(num_groups=2, group_size=1000, dist=100.0, seed=1)
19 | # generate random normally distributed clusters of points, 200 X 2 numpy array.
20 | points = generate_test_points(**kwargs)
21 | lmax = get_estimated_lmax(**kwargs)
22 | polylidar_kwargs = dict(alpha=0.0, lmax=lmax, min_triangles=5)
23 | # print(polylidar_kwargs)
24 | # Convert Points and make Polylidar
25 | points_mat = MatrixDouble(points)
26 | polylidar = Polylidar3D(**polylidar_kwargs)
27 | # Extracts planes and polygons, time
28 | t1 = time.perf_counter()
29 | mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
30 | t2 = time.perf_counter()
31 | triangles = np.asarray(mesh.triangles)
32 | # print(triangles, triangles.shape)
33 | triangles = triangles.flatten()
34 | # print(triangles, triangles.shape)
35 | planes_np = np.asarray(planes)
36 | # print(planes_np)
37 | print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))
38 |
39 | # Plot Data
40 | if points.shape[0] < 100000:
41 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
42 | # plot points
43 | print("Plot Points")
44 | ax.scatter(points[:, 0], points[:, 1], c='k', s=20.0)
45 | fig.savefig("assets/scratch/Basic2DAlgorithm_pointcloud.png", bbox_inches='tight', pad_inches=-0.6)
46 | plt.show()
47 |
48 | print("Plot Mesh")
49 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
50 | # plot points
51 | ax.scatter(points[:, 0], points[:, 1], c='k', s=0.1)
52 | # plot all triangles
53 | plot_triangles(get_triangles_from_list(triangles, points), ax)
54 | fig.savefig("assets/scratch/Basic2DAlgorithm_mesh.png", bbox_inches='tight', pad_inches=-0.6)
55 | plt.show()
56 |
57 | print("Planes and Polygons")
58 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
59 | # plot points
60 | ax.scatter(points[:, 0], points[:, 1], c='k', s=0.1)
61 | # plot all triangles
62 | plot_triangles(get_triangles_from_list(triangles, points), ax)
63 | # plot mesh triangles
64 | triangle_meshes = get_colored_planar_segments(planes_np, triangles, points)
65 | plot_triangle_meshes(triangle_meshes, ax)
66 | # plot polygons
67 | plot_polygons(polygons, points, ax)
68 |
69 | plt.axis('equal')
70 |
71 | plt.show()
72 |
73 | if __name__ == "__main__":
74 | main()
--------------------------------------------------------------------------------
/examples/python/bilateral.py:
--------------------------------------------------------------------------------
1 | """
2 | Simple example demonstrating a bilateral filter implented in C++.
3 | Note that this is NOT the accelerated bilateral filter discussed in the Paper. This is just something fun I tried out that works for generalized meshes.
4 | The accelerated bilateral filter for **organized** point clouds is found in OrganizedPointFilters repo (not this repo).
5 | Requires: Fixtures data
6 | """
7 | import time
8 | import logging
9 | import warnings
10 |
11 | import numpy as np
12 | from scipy.spatial.transform import Rotation as R
13 | import open3d as o3d
14 |
15 |
16 | from examples.python.util.mesh_util import get_mesh_data_iterator
17 | from polylidar import bilateral_filter_normals
18 | from polylidar.polylidarutil.open3d_util import open_3d_mesh_to_trimesh
19 |
20 | def main():
21 | for i, mesh in enumerate(get_mesh_data_iterator()):
22 | if i < 0:
23 | continue
24 | mesh.compute_vertex_normals()
25 | mesh.compute_triangle_normals()
26 | print("Before")
27 | o3d.visualization.draw_geometries([mesh])
28 |
29 | tri_mesh = open_3d_mesh_to_trimesh(mesh)
30 | t1 = time.perf_counter()
31 | bilateral_filter_normals(tri_mesh, iterations=20, sigma_length=0.1, sigma_angle=0.1)
32 | t2 = time.perf_counter()
33 | print(t2-t1)
34 | normals_smooth = np.asarray(tri_mesh.triangle_normals)
35 | mesh.triangle_normals = o3d.utility.Vector3dVector(normals_smooth)
36 |
37 | print("After")
38 | o3d.visualization.draw_geometries([mesh])
39 |
40 |
41 |
42 | if __name__ == "__main__":
43 | main()
44 |
--------------------------------------------------------------------------------
/examples/python/extras/test_delaunay.py:
--------------------------------------------------------------------------------
1 | """Used to just test Delaunay triangulation
2 | """
3 | import numpy as np
4 | from polylidar import Delaunator, MatrixDouble
5 | import matplotlib.pyplot as plt
6 |
7 | def main():
8 | points = np.random.randn(1000, 2)
9 |
10 | # Use Delaunay Triangulation
11 | points_mat = MatrixDouble(points)
12 | delaunay = Delaunator(points_mat)
13 | delaunay.triangulate()
14 | triangles = np.copy(np.asarray(delaunay.triangles))
15 |
16 | plt.triplot(points[:,0], points[:,1], triangles)
17 |
18 | plt.scatter(points[:, 0], points[: ,1])
19 | plt.show()
20 |
21 | if __name__ == "__main__":
22 | main()
--------------------------------------------------------------------------------
/examples/python/extras/test_matrix.py:
--------------------------------------------------------------------------------
1 | """Used to test Polyidars Matrix Class
2 | This test ensures that a no copy operation actually works
3 | """
4 | import numpy as np
5 | from polylidar import Polylidar3D, MatrixDouble
6 |
7 | def get_np_buffer_ptr(a):
8 | pointer, read_only_flag = a.__array_interface__['data']
9 | return pointer
10 |
11 | def main():
12 | a = np.arange(4, dtype=np.float64).reshape((2,2))
13 | print("Numpy Array and Buffer Ptr")
14 | print(a)
15 | print(get_np_buffer_ptr(a))
16 | b = MatrixDouble(a, False)
17 | c = np.asarray(b)
18 | print("")
19 | print("Matrix Array and Buffer Ptr")
20 | print(c)
21 | print(get_np_buffer_ptr(c))
22 | try:
23 |
24 | d = a.reshape((4,))
25 | print("")
26 | print("Reshape to 1 Dimension")
27 | print(d)
28 | print("Should throw runtime error because not 2 Dim")
29 | e = MatrixDouble(d, False)
30 | except Exception:
31 | print("Successfully threw an exception")
32 |
33 |
34 |
35 | if __name__ == "__main__":
36 | main()
--------------------------------------------------------------------------------
/examples/python/extras/test_o3d.py:
--------------------------------------------------------------------------------
1 | """Demo of Open3D 0.10.0 Slowdown
2 | Please modify DIRECTORY to point to the folder of meshes attached to this issue reply
3 | """
4 | import os
5 | import open3d as o3d
6 | import copy
7 | DIRECTORY = 'fixtures/o3d_slow_down'
8 | # o3d 0.10.0 - 9 Seconds to load meshes (time to being user interation), 1 FPS (with draw edges enabled 'w')
9 | # o3d 0.11.0+f1d478c4 - 0.5 seconds, 45-60 FPS (with draw edges)
10 |
11 | duplicate = 50
12 | def main():
13 | all_meshes = []
14 | all_files = sorted(list(os.listdir(DIRECTORY)))
15 | for filename in all_files:
16 | print(filename)
17 | mesh = o3d.io.read_triangle_mesh(os.path.join(DIRECTORY, filename))
18 |
19 | all_meshes.extend([ copy.deepcopy(mesh) for i in range(duplicate)])
20 | print(len(all_meshes))
21 | o3d.visualization.draw_geometries(all_meshes)
22 |
23 | if __name__ == "__main__":
24 | main()
--------------------------------------------------------------------------------
/examples/python/for_landing/PolylidarParams.yaml:
--------------------------------------------------------------------------------
1 | mesh:
2 | use_cuda: true # use GPU CUDA acceleration for mesh smoothing
3 | stride: 1 # skip rows/columns
4 | filter:
5 | loops_laplacian: 1 # how many iterations
6 | _lambda: 0.65 # weight factor for laplacian update
7 | kernel_size: 3 # only changes for laplacian
8 | loops_bilateral: 4 # how many iterations
9 | sigma_length: 0.3 # std of distance between triangles centroids
10 | sigma_angle: 0.2 # std of distance between triangles normals
11 | polylidar: # Parameters we send to polylidar. Determine plane and polygon extraction from point clouds.
12 | alpha: 0.0 # must be set to 0.0 if using lmax
13 | lmax: 1.5 # maximum distance between points in plane for spatial connection
14 | z_thresh: 0.2 # enforce point to plane distance constraints during region growing.
15 | norm_thresh: 0.90 # Not used, set to the same as norm_thresh_min. Will deprecate later.
16 | norm_thresh_min: 0.96 # triangles must have a minimum amount of planarity.
17 | min_hole_vertices : 4 # minimum number of vertices in a hole to return
18 | min_triangles: 500 # minimum number of triangles needed to make a plane
19 | fastga: # Parameters used for dominant plane normal estimation
20 | level: 5 # refinement level of the gaussian accumulator
21 | down_sample_fraction: 0.50 # only use X% of triangle normals from mesh for integration, lower the faster
22 | find_peaks_kwargs: # peak detection arguments
23 | threshold_abs: 50 # [0-255], minimum value of normalized histogram of S2 to be a peak
24 | min_distance: 1 # 1 = 3X3 kernel for peak detector. I recommend to not change
25 | exclude_border: true
26 | indices: false # must return mask
27 | cluster_kwargs: # Agglomerative hierarchal clustering
28 | t: 0.28 # min distance in 3D space of peaks (surface normals on sphere) before merging
29 | criterion: 'distance'
30 | average_filter: # A merge group must have at least x% of value in all of histogram, this doesn't have much meaning and will probably be deprecated
31 | min_total_weight: 0.1
32 | polygon:
33 | postprocess: # post processing of polygons returned from polylidar for ground/obstacle identification
34 | filter: # obstacles must have these characteristics
35 | hole_area:
36 | min: 0.10 # m^2
37 | max: 80.0 # m^2
38 | hole_vertices:
39 | min: 4
40 | plane_area:
41 | min: 4 # m^2
42 | max: 400
43 | # These parameters correspond to Shapely polygon geometry operations
44 | positive_buffer: 0.1 # m, Positively expand polygon. Fills in small holes
45 | negative_buffer: 0.25 # m, Negative buffer to polygon. Expands holes and constricts outer hull of polygon
46 | simplify: 0.25 # m, simplify edges of polygon
47 | polylabel:
48 | precision: 0.1
--------------------------------------------------------------------------------
/examples/python/for_landing/PolylidarParamsSimple.yaml:
--------------------------------------------------------------------------------
1 | mesh:
2 | use_cuda: true # use GPU CUDA acceleration for mesh smoothing
3 | stride: 1 # skip rows/columns
4 | filter:
5 | loops_laplacian: 1 # how many iterations
6 | _lambda: 0.65 # weight factor for laplacian update
7 | kernel_size: 3 # only changes for laplacian
8 | loops_bilateral: 4 # how many iterations
9 | sigma_length: 0.3 # std of distance between triangles centroids
10 | sigma_angle: 0.2 # std of distance between triangles normals
11 | polylidar: # Parameters we send to polylidar. Determine plane and polygon extraction from point clouds.
12 | alpha: 0.0 # must be set to 0.0 if using lmax
13 | lmax: 2.5 # maximum distance between points in plane for spatial connection
14 | z_thresh: 0.2 # enforce point to plane distance constraints during region growing.
15 | norm_thresh: 0.96 # Not used, set to the same as norm_thresh_min. Will deprecate later.
16 | norm_thresh_min: 0.96 # triangles must have a minimum amount of planarity.
17 | min_hole_vertices: 4 # minimum number of vertices in a hole to return
18 | min_triangles: 100 # minimum number of triangles needed to make a plane
19 | fastga: # Parameters used for dominant plane normal estimation
20 | level: 5 # refinement level of the gaussian accumulator
21 | down_sample_fraction: 0.50 # only use X% of triangle normals from mesh for integration, lower the faster
22 | find_peaks_kwargs: # peak detection arguments
23 | threshold_abs: 50 # [0-255], minimum value of normalized histogram of S2 to be a peak
24 | min_distance: 1 # 1 = 3X3 kernel for peak detector. I recommend to not change
25 | exclude_border: true
26 | indices: false # must return mask
27 | cluster_kwargs: # Agglomerative hierarchal clustering
28 | t: 0.28 # min distance in 3D space of peaks (surface normals on sphere) before merging
29 | criterion: "distance"
30 | average_filter: # A merge group must have at least x% of value in all of histogram, this doesn't have much meaning and will probably be deprecated
31 | min_total_weight: 0.1
32 | polygon:
33 | postprocess: # post processing of polygons returned from polylidar for ground/obstacle identification
34 | filter: # obstacles must have these characteristics
35 | hole_area:
36 | min: 0.10 # m^2
37 | max: 10000.0 # m^2
38 | hole_vertices:
39 | min: 4
40 | plane_area:
41 | min: 4 # m^2
42 | max: 400
43 | # These parameters correspond to Shapely polygon geometry operations
44 | positive_buffer: 0.1 # m, Positively expand polygon. Fills in small holes
45 | negative_buffer: 0.2 # m, Negative buffer to polygon. Expands holes and constricts outer hull of polygon
46 | simplify: 0.3 # m, simplify edges of polygon
47 |
--------------------------------------------------------------------------------
/examples/python/for_landing/basic_rooftop_classification.py:
--------------------------------------------------------------------------------
1 | import time
2 | import logging
3 | import sys
4 | import functools
5 | import operator
6 | from pathlib import Path
7 |
8 | import numpy as np
9 | import open3d as o3d
10 | import matplotlib.cm as cm
11 | import yaml
12 |
13 |
14 | from polylidar import (Polylidar3D, MatrixDouble, MatrixFloat, extract_tri_mesh_from_float_depth,
15 | extract_point_cloud_from_float_depth, MatrixUInt8)
16 |
17 | from polylidar.polylidarutil.open3d_util import construct_grid, create_lines, flatten, create_open_3d_mesh_from_tri_mesh
18 | from polylidar.polylidarutil.plane_filtering import filter_planes_and_holes
19 | from polylidar.polylidarutil.line_mesh import LineMesh
20 |
21 | from organizedpointfilters.utility.helper import create_meshes_cuda
22 | from fastga import GaussianAccumulatorS2Beta, IcoCharts
23 |
24 | from examples.python.util.helper_polylidar import extract_all_dominant_plane_normals, extract_planes_and_polygons_from_mesh, extract_planes_and_polygons_from_classified_mesh
25 | from examples.python.util.helper_mesh import create_open_3d_mesh_from_tri_mesh
26 |
27 |
28 |
29 | def convert_to_square_image(pc, invert=False, row=None):
30 | if invert:
31 | row = int(pc.shape[0])
32 | col = int(pc.shape[1])
33 | new_pc = pc.reshape((row * col, 3))
34 | else:
35 | if row is None:
36 | row = int(np.sqrt(pc.shape[0]))
37 | col = row
38 | else:
39 | col = int(pc.shape[0] / row)
40 | shape = (row, col, 3) if pc.ndim > 1 else (row, col)
41 | new_pc = pc.reshape(shape)
42 | return new_pc
43 |
44 |
45 | def create_color(classes):
46 | classes_ = (classes)
47 | colors = cm.tab20(classes_)
48 | colors = colors[:, :, :3]
49 | colors = np.copy(convert_to_square_image(colors, True))
50 | return colors
51 |
52 | def extract_polygons(opc, classes, config):
53 | pl = Polylidar3D(**config['polylidar'])
54 | ga = GaussianAccumulatorS2Beta(level=config['fastga']['level'])
55 | ico = IcoCharts(level=config['fastga']['level'])
56 | # 1. Create mesh
57 | tri_mesh = create_meshes_cuda(
58 | opc, **config['mesh']['filter'])
59 |
60 | mesh_o3d = create_open_3d_mesh_from_tri_mesh(tri_mesh)
61 | classes = classes.reshape((np.asarray(mesh_o3d.vertices).shape[0], 1))
62 | classes_mat = MatrixUInt8(classes)
63 | tri_mesh.set_vertex_classes(classes_mat, True)
64 | # alg_timings.update(timings)
65 | # 2. Get dominant plane normals
66 | avg_peaks, _, _, _, alg_timings = extract_all_dominant_plane_normals(
67 | tri_mesh, ga_=ga, ico_chart_=ico, **config['fastga'])
68 |
69 | # alg_timings.update(timings)
70 | # 3. Extract Planes and Polygons
71 | planes, obstacles, timings = extract_planes_and_polygons_from_classified_mesh(tri_mesh, avg_peaks, pl_=pl,
72 | filter_polygons=True, optimized=True,
73 | postprocess=config['polygon']['postprocess'])
74 |
75 | return mesh_o3d, planes, obstacles, timings
76 |
77 | def handle_shapes(planes, obstacles, line_radius=0.15):
78 | all_polys = []
79 |
80 | for plane, _ in planes:
81 | points = np.array(plane.exterior)
82 | line_mesh = LineMesh(points, colors=[0,1,0], radius=line_radius)
83 | all_polys.append(line_mesh)
84 |
85 | for plane, _ in obstacles:
86 | points = np.array(plane.exterior)
87 | line_mesh = LineMesh(points, colors=[1, 0, 0], radius=line_radius)
88 | all_polys.append(line_mesh)
89 |
90 | all_meshes = [poly.cylinder_segments for poly in all_polys]
91 | all_meshes = functools.reduce(operator.iconcat, all_meshes, [])
92 |
93 | return all_polys, all_meshes
94 |
95 | def main():
96 | # Load yaml file
97 | config = None
98 | with open('./examples/python/for_landing/PolylidarParams.yaml') as file:
99 | try:
100 | config = yaml.safe_load(file)
101 | except yaml.YAMLError as exc:
102 | print("Error parsing yaml")
103 |
104 | example = Path("./fixtures/unreal/example1")
105 | pc_lidar = np.load(example / "140-0-0.npy")
106 |
107 | pc_image = convert_to_square_image(pc_lidar[:, :3], row=64)
108 | classes = convert_to_square_image(pc_lidar[:, 3], row=64).astype(np.uint8)
109 | colors = create_color(classes)
110 | mask = classes == 4
111 | classes[~mask] = 0
112 | classes[mask] = 1
113 |
114 |
115 | pcd = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(pc_lidar[:, :3]))
116 | pcd.colors = o3d.utility.Vector3dVector(colors)
117 | o3d.visualization.draw_geometries([pcd])
118 |
119 |
120 | mesh_o3d, planes, obstacles, time = extract_polygons(pc_image, classes, config)
121 |
122 |
123 | all_polys, all_meshes = handle_shapes(planes, obstacles)
124 | all_meshes = [mesh_o3d, *all_meshes]
125 | o3d.visualization.draw_geometries([mesh_o3d, pcd, *all_meshes])
126 |
127 |
128 |
129 |
130 | if __name__ == "__main__":
131 | main()
132 |
--------------------------------------------------------------------------------
/examples/python/for_landing/basic_rooftop_classification_algorithm.py:
--------------------------------------------------------------------------------
1 | """Example Multipolygon Extraction
2 | """
3 | import time
4 | import math
5 |
6 | import numpy as np
7 | import matplotlib.pyplot as plt
8 | from mpl_toolkits.mplot3d import Axes3D
9 |
10 |
11 | from polylidar import MatrixDouble, Polylidar3D
12 | from polylidar.polylidarutil import (plot_polygons_3d, generate_3d_plane, set_axes_equal, plot_planes_3d,
13 | scale_points, rotation_matrix, apply_rotation)
14 |
15 |
16 | def extract_polygons(opc, classes, config):
17 | pl = Polylidar3D(**config['polylidar'])
18 | ga = GaussianAccumulatorS2Beta(level=config['fastga']['level'])
19 | ico = IcoCharts(level=config['fastga']['level'])
20 | # 1. Create mesh
21 | tri_mesh = create_meshes_cuda(
22 | opc, **config['mesh']['filter'])
23 |
24 | mesh_o3d = create_open_3d_mesh_from_tri_mesh(tri_mesh)
25 | classes = classes.reshape((np.asarray(mesh_o3d.vertices).shape[0], 1))
26 | classes_mat = MatrixUInt8(classes)
27 | tri_mesh.set_vertex_classes(classes_mat, True)
28 | # alg_timings.update(timings)
29 | # 2. Get dominant plane normals
30 | avg_peaks, _, _, _, alg_timings = extract_all_dominant_plane_normals(
31 | tri_mesh, ga_=ga, ico_chart_=ico, **config['fastga'])
32 |
33 | # alg_timings.update(timings)
34 | # 3. Extract Planes and Polygons
35 | planes, obstacles, timings = extract_planes_and_polygons_from_classified_mesh(tri_mesh, avg_peaks, pl_=pl,
36 | filter_polygons=True, optimized=True,
37 | postprocess=config['polygon']['postprocess'])
38 |
39 | return mesh_o3d, planes, obstacles, timings
40 |
41 | def main():
42 | np.random.seed(1)
43 | # generate random plane with hole
44 | plane = generate_3d_plane(bounds_x=[0, 10, 0.5], bounds_y=[0, 10, 0.5], holes=[
45 | [[3, 5], [3, 5]]], height_noise=0.02, planar_noise=0.02)
46 | # Generate top of box (causing the hole that we see)
47 | box_top = generate_3d_plane(bounds_x=[3, 5, 0.2], bounds_y=[3, 5, 0.2], holes=[
48 | ], height_noise=0.02, height=2, planar_noise=0.02)
49 | # Generate side of box (causing the hole that we see)
50 | box_side = generate_3d_plane(bounds_x=[0, 2, 0.2], bounds_y=[
51 | 0, 2, 0.2], holes=[], height_noise=0.02, planar_noise=0.02)
52 | rm = rotation_matrix([0, 1, 0], -math.pi / 2.0)
53 | box_side = apply_rotation(rm, box_side) + [5, 3, 0]
54 | # All points joined together
55 | points = np.concatenate((plane, box_side, box_top))
56 |
57 | points_mat = MatrixDouble(points)
58 | polylidar_kwargs = dict(alpha=0.0, lmax=1.0, min_triangles=20, z_thresh=0.1, norm_thresh_min=0.94)
59 | polylidar = Polylidar3D(**polylidar_kwargs)
60 |
61 | elev = 15.0
62 | azim = -35
63 |
64 | # Show Point Cloud
65 | print("Should see point raw point cloud")
66 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
67 | subplot_kw=dict(projection='3d'))
68 | # plot points
69 | ax.scatter(*scale_points(points), s=20.0, c=points[:, 2], cmap=plt.cm.plasma)
70 | set_axes_equal(ax)
71 | ax.view_init(elev=elev, azim=azim)
72 | fig.savefig("assets/scratch/Basic25DAlgorithm_pointcloud.pdf", bbox_inches='tight')
73 | fig.savefig("assets/scratch/Basic25DAlgorithm_pointcloud.png", bbox_inches='tight', pad_inches=-0.8)
74 | plt.show()
75 |
76 | # Extracts planes and polygons, time
77 | t1 = time.time()
78 | mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
79 | t2 = time.time()
80 | print("Polylidar Took {:.2f} milliseconds".format((t2 - t1) * 1000))
81 |
82 | triangles = np.asarray(mesh.triangles)
83 | all_planes = [np.arange(triangles.shape[0])]
84 |
85 | # Show Triangulation
86 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
87 | subplot_kw=dict(projection='3d'))
88 |
89 | plot_planes_3d(points, triangles, all_planes, ax, alpha=0.0, z_value=-4.0)
90 | plot_planes_3d(points, triangles, all_planes, ax, alpha=0.5)
91 | # plot points
92 | ax.scatter(*scale_points(points), s=0.1, c='k')
93 | set_axes_equal(ax, ignore_z=True)
94 | ax.set_zlim3d([-4, 6])
95 | ax.view_init(elev=elev, azim=azim)
96 | print("Should see triangulation point cloud")
97 | fig.savefig("assets/scratch/Basic25DAlgorithm_mesh.pdf", bbox_inches='tight')
98 | fig.savefig("assets/scratch/Basic25DAlgorithm_mesh.png", bbox_inches='tight', pad_inches=-0.8)
99 | plt.show()
100 |
101 | print("")
102 | print("Should see two planes extracted, please rotate.")
103 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
104 | subplot_kw=dict(projection='3d'))
105 | # plot all triangles
106 | plot_polygons_3d(points, polygons, ax)
107 | plot_planes_3d(points, triangles, planes, ax)
108 | # plot points
109 | ax.scatter(*scale_points(points), c='k', s=0.1)
110 | set_axes_equal(ax)
111 | ax.view_init(elev=elev, azim=azim)
112 | fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.pdf", bbox_inches='tight')
113 | fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.png", bbox_inches='tight', pad_inches=-0.8)
114 | plt.show()
115 | print("")
116 |
117 |
118 | if __name__ == "__main__":
119 | main()
120 |
--------------------------------------------------------------------------------
/examples/python/for_landing/o3d_slow_down.py:
--------------------------------------------------------------------------------
1 | """Demo of Open3D 0.10.0 Slowdown
2 | Please modify DIRECTORY to point to the folder of meshes attached to this issue reply
3 | """
4 | import os
5 | import open3d as o3d
6 | DIRECTORY = 'o3d_slow_down'
7 | def main():
8 | all_meshes = []
9 | all_files = sorted(list(os.listdir(DIRECTORY)))
10 | for filename in all_files:
11 | print(filename)
12 | mesh = o3d.io.read_triangle_mesh(os.path.join(DIRECTORY, filename))
13 | all_meshes.append(mesh)
14 | o3d.visualization.draw_geometries(all_meshes)
15 |
16 | if __name__ == "__main__":
17 | main()
--------------------------------------------------------------------------------
/examples/python/for_paper/basic25d_algorithm_explained.py:
--------------------------------------------------------------------------------
1 | import time
2 | import math
3 |
4 | import numpy as np
5 | import matplotlib.pyplot as plt
6 | from mpl_toolkits.mplot3d import Axes3D
7 | import matplotlib
8 |
9 | matplotlib.rc('xtick', labelsize=14)
10 | matplotlib.rc('ytick', labelsize=14)
11 | # matplotlib.rc('ztick', labelsize=20)
12 | matplotlib.rc('axes', labelsize=18)
13 |
14 |
15 | from polylidar import Delaunator, MatrixDouble, Polylidar3D
16 | from polylidar.polylidarutil import (plot_polygons_3d, generate_3d_plane, set_axes_equal, plot_planes_3d,
17 | scale_points, rotation_matrix, apply_rotation)
18 |
19 |
20 |
21 | def set_labels(ax):
22 | ax.set_xlabel('X')
23 | ax.set_ylabel('Y')
24 | ax.set_zlabel('Z')
25 |
26 |
27 | def main():
28 | np.random.seed(1)
29 | # generate random plane with hole
30 | plane = generate_3d_plane(bounds_x=[0, 10, 0.5], bounds_y=[0, 10, 0.5], holes=[
31 | [[3, 6], [3, 6]]], height_noise=0.04, planar_noise=0.04)
32 |
33 | large_wall = generate_3d_plane(bounds_x=[0.5, 4, 1.0], bounds_y=[0, 9.5, 0.5], holes=[], height_noise=0.04, planar_noise=0.1)
34 | # Generate top of box (causing the hole that we see)
35 | box_top = generate_3d_plane(bounds_x=[3, 6, 0.5], bounds_y=[3, 6, 0.5], holes=[
36 | ], height_noise=0.02, height=2, planar_noise=0.03)
37 | # Generate side of box (causing the hole that we see)
38 | box_side = generate_3d_plane(bounds_x=[.25, 2, 0.5], bounds_y=[
39 | 0, 3, 0.5], holes=[], height_noise=0.02, planar_noise=0.02)
40 | rm = rotation_matrix([0,1,0], -math.pi/2.0)
41 | box_side = apply_rotation(rm, box_side) + [6, 3, 0]
42 |
43 | large_wall = apply_rotation(rm , large_wall) + [10, 0, -3.5]
44 | # box_side = r.apply(box_side) + [5, 3, 0]
45 | # All points joined together
46 | points = np.concatenate((plane, box_side, box_top, large_wall))
47 |
48 | points_mat = MatrixDouble(points)
49 | polylidar_kwargs = dict(alpha=0.0, lmax=1.0, min_triangles=10, z_thresh=0.15, norm_thresh_min=0.95)
50 | polylidar = Polylidar3D(**polylidar_kwargs)
51 |
52 | elev = 15.0
53 | azim = -35
54 |
55 | # Show Point Cloud
56 | print("Should see point raw point cloud")
57 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
58 | subplot_kw=dict(projection='3d'))
59 | # plot points
60 | ax.scatter(*scale_points(points), s=20.0, c=points[:, 2], cmap=plt.cm.plasma)
61 | set_axes_equal(ax, ignore_z=True)
62 | ax.set_zlim3d([-4, 6])
63 | ax.view_init(elev=elev, azim=azim)
64 | set_labels(ax)
65 | fig.savefig("assets/scratch/Basic25DAlgorithm_pointcloud.pdf", bbox_inches='tight')
66 | fig.savefig("assets/scratch/Basic25DAlgorithm_pointcloud.png", bbox_inches='tight', pad_inches=-0.8)
67 | plt.show()
68 |
69 |
70 | # Extracts planes and polygons, time
71 | t1 = time.time()
72 | mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
73 | t2 = time.time()
74 | print("Polylidar Took {:.2f} milliseconds".format((t2 - t1) * 1000))
75 |
76 | triangles = np.asarray(mesh.triangles)
77 | all_planes = [np.arange(triangles.shape[0])]
78 |
79 | # Show Triangulation
80 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
81 | subplot_kw=dict(projection='3d'))
82 |
83 | plot_planes_3d(points, triangles, all_planes, ax, alpha=0.0, z_value=-4.0)
84 | plot_planes_3d(points, triangles, all_planes, ax, alpha=0.5)
85 | # plot points
86 | ax.scatter(*scale_points(points), s=0.1, c='k')
87 | set_axes_equal(ax, ignore_z=True)
88 | ax.set_zlim3d([-4, 6])
89 | ax.view_init(elev=elev, azim=azim)
90 | set_labels(ax)
91 | print("Should see triangulation point cloud")
92 | fig.savefig("assets/scratch/Basic25DAlgorithm_mesh.pdf", bbox_inches='tight')
93 | fig.savefig("assets/scratch/Basic25DAlgorithm_mesh.png", bbox_inches='tight', pad_inches=-0.8)
94 | plt.show()
95 |
96 |
97 |
98 | print("")
99 | print("Should see two planes extracted, please rotate.")
100 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
101 | subplot_kw=dict(projection='3d'))
102 | # plot all triangles
103 | plot_polygons_3d(points, polygons, ax)
104 | plot_planes_3d(points, triangles, planes, ax)
105 | # plot points
106 | ax.scatter(*scale_points(points), c='k', s=0.1)
107 | set_axes_equal(ax, ignore_z=True)
108 | ax.set_zlim3d([-4, 6])
109 | ax.view_init(elev=elev, azim=azim)
110 | set_labels(ax)
111 | fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.pdf", bbox_inches='tight')
112 | fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.png", bbox_inches='tight', pad_inches=-0.8)
113 | plt.show()
114 | print("")
115 |
116 | if __name__ == "__main__":
117 | main()
118 |
--------------------------------------------------------------------------------
/examples/python/for_paper/bench_mesh_report.py:
--------------------------------------------------------------------------------
1 | import json
2 | import pandas as pd
3 | import seaborn as sns
4 | import matplotlib.pyplot as plt
5 |
6 | sns.set(font_scale=1.15)
7 | sns.set_style('white')
8 | sns.set_style("ticks")
9 |
10 |
11 |
12 | def plot_df(df, name='basement'):
13 | fig = plt.figure(figsize=(6,3.5))
14 | ax = sns.lineplot(x="threads", y="Speedup", hue="normals", palette='deep', data=df, legend=False)
15 | ax2 = ax.twinx()
16 | sns.lineplot(x="threads", y="Execution Time (ms)", ax=ax2, hue='normals',palette='muted', data=df, legend=False)
17 | for line in ax2.lines:
18 | line.set_linestyle('--')
19 |
20 | current_palette = sns.color_palette()
21 | from matplotlib.lines import Line2D
22 | custom_lines = [Line2D([0], [0], color=current_palette[i], lw=1) for i in range(4)]
23 | custom_lines_2 = [Line2D([0], [0], color='k', lw=1),
24 | Line2D([0], [0], color='k', lw=1, dashes=[2,6])]
25 |
26 | ax.legend(custom_lines, ['1 DP', '2 DP', '3 DP', '4 DP'], loc='upper left', bbox_to_anchor=(0.1, 1,))
27 | ax2.legend(custom_lines_2, ['Speedup', 'Execution Time'], loc='upper left', bbox_to_anchor=(0.35, 1,))
28 | # ax.axis('equal')
29 | # ax.legend(custom_lines, ['1 DP', '2 DP', '3 DP', '4 DP'], loc='upper left', bbox_to_anchor=(0.4, 0.4,))
30 | # ax2.legend(custom_lines_2, ['Speedup', 'Execution Time'], loc='upper left', bbox_to_anchor=(0.6, 0.4,))
31 | fig.savefig(f'assets/scratch/{name}_speedup.pdf', bbox_inches='tight')
32 | plt.tight_layout()
33 | plt.show()
34 |
35 |
36 | def main():
37 | with open('bench/records/bench_mesh_paper.json') as json_file:
38 | data = json.load(json_file)
39 | benchmarks = data['benchmarks']
40 |
41 | benchmarks_filtered = []
42 |
43 | for i, bench in enumerate(benchmarks):
44 | name = bench['name']
45 | run_name = bench['run_name']
46 | aggregate_name = bench['aggregate_name']
47 | if i % 3 == 0:
48 | names = name.split('/')
49 | bench['mesh'] = names[0]
50 | bench['normals'] = int(names[2])
51 | bench['threads'] = int(names[3])
52 | bench['time_mean'] = bench['real_time'] if bench['time_unit'] == 'ms' else bench['real_time'] / 1000
53 | bench_median = benchmarks[i+1]
54 | bench['time_median'] = bench_median['real_time'] if bench_median['time_unit'] == 'ms' else bench_median['real_time'] / 1000
55 | bench_std = benchmarks[i+2]
56 | bench['time_std'] = bench_std['real_time'] if bench_std['time_unit'] == 'ms' else bench_std['real_time'] / 1000
57 | benchmarks_filtered.append(bench)
58 | # calculate speedup
59 | current_speed = None
60 | for i, bench in enumerate(benchmarks_filtered):
61 | # reset current speed every 8 iterations (8 threads)
62 | if i % 8 == 0:
63 | current_speed = bench['time_mean']
64 | bench['speedup'] = current_speed / bench['time_mean']
65 |
66 | df = pd.DataFrame.from_records(benchmarks_filtered)
67 | df_reduced = df[['mesh', 'normals', 'threads', 'speedup', 'time_mean', 'time_median', 'time_std']]
68 | df_reduced = df_reduced.rename(columns={'time_mean': "Execution Time (ms)", "speedup": "Speedup"})
69 |
70 | df_sparse = df_reduced[df_reduced['mesh'] == 'SparseMeshPaper']
71 | df_dense = df_reduced[df_reduced['mesh'] == 'DenseMeshPaper']
72 |
73 | plot_df(df_sparse, 'basement')
74 | plot_df(df_dense, 'mainfloor')
75 |
76 | if __name__ == "__main__":
77 | main()
--------------------------------------------------------------------------------
/examples/python/for_paper/california_points.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | from tests.python.helpers.utils import load_csv, load_npy
3 |
4 |
5 | def plot_points(points):
6 |
7 | fig, ax = plt.subplots(nrows=1, ncols=1)
8 | ax.scatter(points[:,0], points[:, 1], s=1)
9 | plt.axis('off')
10 | ax.axis('equal')
11 |
12 | plt.show()
13 |
14 |
15 | def main():
16 | points_1 = load_csv('caholes_8000.csv', delimeter=' ')
17 | plot_points(points_1)
18 |
19 | points_1 = load_csv('ca_8000.csv', delimeter=' ')
20 | plot_points(points_1)
21 |
22 |
23 | if __name__ == "__main__":
24 | main()
--------------------------------------------------------------------------------
/examples/python/for_paper/generate_logo.py:
--------------------------------------------------------------------------------
1 | import time
2 | import pickle
3 | import numpy as np
4 | from polylidar import Delaunator, MatrixDouble, Polylidar3D
5 | from polylidar.polylidarutil import (generate_test_points, plot_points, plot_triangles, get_estimated_lmax,
6 | plot_triangle_meshes, get_triangles_from_list, get_colored_planar_segments, plot_polygons)
7 | import matplotlib.pyplot as plt
8 |
9 | np.random.seed(1)
10 |
11 |
12 | def get_np_buffer_ptr(a):
13 | pointer, read_only_flag = a.__array_interface__['data']
14 | return pointer
15 |
16 |
17 | def stitch_letters(letters_map, letters=['P', 'L']):
18 | # letters = ['P', 'O', 'L', 'Y', 'L', 'I', 'D', 'A', 'R']
19 | x_offset = np.array([0.0, 0.0])
20 | x_spacing = np.array([-30.0, 0.0])
21 | all_points = []
22 | for letter in letters:
23 | letter_points = np.copy(letters_map[letter])
24 | letter_points = letter_points + x_offset + x_spacing
25 | x_offset[0] = letter_points.max(axis=0)[0]
26 | all_points.append(letter_points)
27 |
28 | all_points = np.concatenate(all_points, axis=0)
29 |
30 | return all_points
31 |
32 | def read_alphabet(file_name='fixtures/pointsets/alphabet_2000.pkl'):
33 | with open(file_name, 'rb') as file:
34 | data = pickle.load(file)
35 | letters = ['P', 'O', 'L', 'Y', 'I', 'D', 'A', 'R']
36 | letter_map = dict()
37 | for letter in letters:
38 | letter_map[letter] = [item for item in data if item['poly_param']['name'] == letter][0]['points']
39 |
40 | return letter_map
41 |
42 |
43 | def main():
44 |
45 | letter_map = read_alphabet()
46 | # letters = ['P', 'O', 'L', 'Y', 'L', 'I', 'D', 'A', 'R']
47 | points = stitch_letters(letter_map)
48 |
49 | # plt.scatter(points[:, 0], points[:, 1], s=0.2)
50 | # plt.show()
51 | print(int(points.shape[0] / 2))
52 | idx = np.random.randint(0, points.shape[0], size=int(points.shape[0] / 2))
53 | points = np.ascontiguousarray(points[idx, :])
54 | lmax = 10.0
55 | polylidar_kwargs = dict(alpha=0.0, lmax=lmax, min_triangles=5)
56 | # print(polylidar_kwargs)
57 | # Convert Points and make Polylidar
58 | points_mat = MatrixDouble(points)
59 | polylidar = Polylidar3D(**polylidar_kwargs)
60 | # Extracts planes and polygons, time
61 | t1 = time.perf_counter()
62 | mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
63 | t2 = time.perf_counter()
64 | triangles = np.asarray(mesh.triangles)
65 | # print(triangles, triangles.shape)
66 | triangles = triangles.flatten()
67 | # print(triangles, triangles.shape)
68 | planes_np = planes
69 | # print(planes_np)
70 | print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))
71 |
72 | # Plot Data
73 | if points.shape[0] < 100000:
74 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
75 | ax.set_xticks([])
76 | ax.set_yticks([])
77 | plt.axis('equal')
78 | plt.axis('off')
79 | # plot points
80 | plot_points(points, ax)
81 | fig.savefig('assets/scratch/pl_logo_points.png', bbox_inches='tight', transparent=True)
82 | # plot polygons
83 | plot_polygons(polygons, points, ax, linewidth=6.0)
84 | fig.savefig('assets/scratch/pl_logo_poly.png', bbox_inches='tight', transparent=True)
85 | # plot all triangles
86 | plot_triangles(get_triangles_from_list(triangles, points), ax)
87 | # plot mesh triangles
88 | triangle_meshes = get_colored_planar_segments(planes_np, triangles, points)
89 | plot_triangle_meshes(triangle_meshes, ax)
90 | # plot polygons
91 | plot_polygons(polygons, points, ax, linewidth=4.0)
92 |
93 | plt.subplots_adjust(wspace=0.185, hspace=0.185,left=0.110,top=0.535,right=0.750,bottom=0.110)
94 | fig.savefig('assets/scratch/pl_logo.png', bbox_inches='tight', transparent=True)
95 | plt.show()
96 |
97 |
98 | if __name__ == "__main__":
99 | main()
100 |
--------------------------------------------------------------------------------
/examples/python/for_paper/manifold_meshes.py:
--------------------------------------------------------------------------------
1 | import time
2 | import math
3 |
4 | import numpy as np
5 | import matplotlib.pyplot as plt
6 | from mpl_toolkits.mplot3d import Axes3D
7 | import matplotlib
8 |
9 | matplotlib.rc('xtick', labelsize=14)
10 | matplotlib.rc('ytick', labelsize=14)
11 | # matplotlib.rc('ztick', labelsize=20)
12 | matplotlib.rc('axes', labelsize=18)
13 |
14 |
15 | from polylidar import Delaunator, MatrixDouble, Polylidar3D
16 | from polylidar.polylidarutil import (plot_polygons_3d, generate_3d_plane, set_axes_equal, plot_planes_3d,
17 | scale_points, rotation_matrix, apply_rotation)
18 |
19 |
20 |
21 | def set_labels(ax):
22 | ax.set_xlabel('X')
23 | ax.set_ylabel('Y')
24 | ax.set_zlabel('Z')
25 |
26 | def triangulate_and_combine(points, points_bad, triangles_bad):
27 | points_mat = MatrixDouble(points)
28 | mesh = Delaunator(points_mat)
29 | mesh.triangulate()
30 |
31 | triangles = np.asarray(mesh.triangles)
32 | points_new = np.concatenate([points, points_bad], axis=0)
33 | triangles_new = np.concatenate([triangles, triangles_bad], axis=0)
34 | all_planes = [np.arange(triangles_new.shape[0])]
35 |
36 | return mesh, points_new, triangles_new, all_planes
37 |
38 | def main():
39 | np.random.seed(1)
40 | # generate random plane with hole
41 |
42 | z_lim = [-4, 6]
43 | elev = 10.0
44 | azim = -35
45 |
46 | points = generate_3d_plane(bounds_x=[2.0, 6.0, 1.0], bounds_y=[2.0, 6.0, 0.5], holes=[], planar_noise=0.0, height_noise=0.01)
47 |
48 | points_bad = np.array([
49 | [3.0, 3.5, 0.0],
50 | [3.5, 3.5, 1.0],
51 | [4.0, 3.5, 0.0]
52 | ])
53 | end_point_idx = points.shape[0]
54 | triangles_bad = np.array([
55 | [end_point_idx, end_point_idx + 1, end_point_idx + 2]
56 | ])
57 |
58 | mesh, points_new, triangles_new, all_planes = triangulate_and_combine(points, points_bad, triangles_bad)
59 |
60 | # Show Triangulation
61 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
62 | subplot_kw=dict(projection='3d'))
63 |
64 | plot_planes_3d(points_new, triangles_new, all_planes, ax, alpha=0.5)
65 | set_axes_equal(ax, ignore_z=True)
66 | ax.set_zlim3d(z_lim)
67 | ax.view_init(elev=elev, azim=azim)
68 | plt.show()
69 |
70 |
71 | points = generate_3d_plane(bounds_x=[2.0, 6.0, 1.0], bounds_y=[2.0, 6.0, 0.5], holes=[], planar_noise=0.0, height_noise=0.01)
72 | points[18, 2] = points[18, 2] + 0.30
73 | points_bad = np.array([
74 | [3.0, 3.5, 0.0],
75 | [3.5, 4.0, 0.20],
76 | [4.0, 3.5, 0.0]
77 | ])
78 | end_point_idx = points.shape[0]
79 | triangles_bad = np.array([
80 | [end_point_idx, end_point_idx + 1, end_point_idx + 2]
81 | ])
82 |
83 | mesh, points_new, triangles_new, all_planes = triangulate_and_combine(points, points_bad, triangles_bad)
84 | # all_planes.append([13, 14])
85 | # Show Triangulation
86 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
87 | subplot_kw=dict(projection='3d'))
88 |
89 | plot_planes_3d(points_new, triangles_new, all_planes, ax, alpha=0.5)
90 | # plot_planes_3d(points_new, triangles_new, [[triangles_new.shape[0]-1]], ax, alpha=1.0, color=(1,0,0))
91 |
92 | ax.plot_trisurf(*scale_points(points_new, z_value=None, z_scale=1.0),triangles=triangles_new[[triangles_new.shape[0]-1]], color=(1,0,0,1.0), edgecolor=(0,0,0,0.3), linewidth=0.5, antialiased=True, zorder=1.5)
93 | ax.plot_trisurf(*scale_points(points_new, z_value=None, z_scale=1.0),triangles=triangles_new[[13,14]], color=(0,1,0,0.5), edgecolor=(0,0,0,0.3), linewidth=0.5, antialiased=False, zorder=0.5)
94 | # plot_planes_3d(points_new, triangles_new, [[13,14]], ax, alpha=0.9, color=(0,1,0))
95 | set_axes_equal(ax, ignore_z=True)
96 | ax.set_zlim3d(z_lim)
97 | ax.view_init(elev=elev, azim=azim)
98 | plt.show()
99 |
100 |
101 |
102 |
103 |
104 |
105 | if __name__ == "__main__":
106 | main()
107 |
--------------------------------------------------------------------------------
/examples/python/robust.py:
--------------------------------------------------------------------------------
1 | """Demonstrates Polylidar3D using Robust Geometric Predicates with Delaunator
2 | You need to have built Polylidar3D with this enabled to see correct polygon generation
3 | """
4 | import time
5 | import numpy as np
6 | import matplotlib.pyplot as plt
7 | from tests.python.helpers.utils import load_csv
8 | from polylidar import Polylidar3D, MatrixDouble
9 | from polylidar.polylidarutil import (plot_points, plot_triangles,
10 | plot_triangle_meshes, get_triangles_from_list, get_colored_planar_segments, plot_polygons)
11 |
12 | # Load point set that for which delaunator generates invalid convex hulls when using non-robust predicates
13 | # Convex hull should be malformed if polylidar not built with robust predicates
14 | def main():
15 | points = load_csv('robust_1.csv')
16 | polylidar_kwargs = dict(alpha=0.0, lmax=1000.0, min_triangles=1)
17 |
18 | points_mat = MatrixDouble(points)
19 | polylidar = Polylidar3D(**polylidar_kwargs)
20 |
21 | # Extracts planes and polygons, time
22 | t1 = time.time()
23 | # pick large lmax to get the convex hull, no triangles filtered
24 | mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
25 | t2 = time.time()
26 | print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))
27 | print("If Robust Geometric Predicates is NOT activated, you should see a malformed convex hull")
28 | print("See README.md to activate if desired. ~20% performance penalty when active.")
29 | print("")
30 | # Convert to numpy format, no copy with np.asarray()
31 | triangles = np.asarray(mesh.triangles)
32 |
33 | fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
34 | # plot points
35 | ax.scatter(points[:, 0], points[:, 1], c='k')
36 | # plot all triangles
37 | plot_triangles(get_triangles_from_list(triangles, points), ax)
38 | # plot seperated planar triangular segments
39 | triangle_meshes = get_colored_planar_segments(planes, triangles, points)
40 | plot_triangle_meshes(triangle_meshes, ax)
41 | # plot polygons
42 | plot_polygons(polygons, points, ax)
43 | plt.axis('equal')
44 | plt.show()
45 |
46 |
47 | if __name__ == "__main__":
48 | main()
49 |
--------------------------------------------------------------------------------
/examples/python/util/mesh_util.py:
--------------------------------------------------------------------------------
1 | import time
2 | import math
3 | import sys
4 | from os import path, listdir
5 | from os.path import exists, isfile, join, splitext
6 | import re
7 | import logging
8 |
9 | import numpy as np
10 | from polylidar.polylidarutil import COLOR_PALETTE
11 | from polylidar.polylidarutil.line_mesh import o3d_major_version
12 | import open3d as o3d
13 | from scipy.spatial.transform import Rotation as R
14 |
15 | DIR_NAME = path.dirname(__file__)
16 | FIXTURES_DIR = path.join(DIR_NAME, '../../..', 'fixtures')
17 | MESHES_DIR = path.join(FIXTURES_DIR, 'meshes')
18 |
19 | DENSE_MESH = path.join(MESHES_DIR, 'dense_first_floor_map_smoothed.ply')
20 | SPARSE_MESH = path.join(MESHES_DIR, 'sparse_basement.ply')
21 | BASEMENT_CHAIR = path.join(MESHES_DIR, 'basement_chair_5cm.ply')
22 |
23 | ALL_MESHES = [DENSE_MESH, SPARSE_MESH, BASEMENT_CHAIR]
24 | # ALL_MESHES_ROTATIONS = [R.from_rotvec(-np.pi / 2 * np.array([1, 0, 0])),
25 | # R.from_rotvec(-np.pi / 2 * np.array([1, 0, 0]))]
26 |
27 | ALL_MESHES_ROTATIONS = [None, None, None]
28 |
29 |
30 | def get_mesh_data_iterator():
31 | for i, (mesh_fpath, r) in enumerate(zip(ALL_MESHES, ALL_MESHES_ROTATIONS)):
32 | example_mesh = o3d.io.read_triangle_mesh(str(mesh_fpath))
33 | if r is not None:
34 | center = [0, 0, 0] if o3d_major_version > 9 else True
35 | example_mesh = example_mesh.rotate(r.as_matrix(), center=center)
36 | example_mesh_filtered = example_mesh
37 | example_mesh_filtered.compute_triangle_normals()
38 | yield example_mesh_filtered
39 |
40 |
41 | def main():
42 | for i, mesh in enumerate(get_mesh_data_iterator()):
43 | if i < 1:
44 | continue
45 | colors = np.asarray(mesh.vertex_colors)
46 | colors2 = np.column_stack((colors[:, 2], colors[:, 1], colors[:, 0]))
47 | mesh.vertex_colors = o3d.utility.Vector3dVector(colors2)
48 | o3d.io.write_triangle_mesh('test.ply', mesh)
49 |
50 | if __name__ == "__main__":
51 | main()
52 |
--------------------------------------------------------------------------------
/fixtures/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything
2 | *
3 |
4 | # But not these files...
5 | !.gitignore
--------------------------------------------------------------------------------
/include/Polylidar/Core.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYLIDAR_CORE
2 | #define POLYLIDAR_CORE
3 | #include
4 |
5 | #include "Polylidar/Types.hpp"
6 | #include "Polylidar/Utility.hpp"
7 | #include "Polylidar/Mesh/MeshHelper.hpp"
8 |
9 | namespace Polylidar {
10 |
11 | namespace Core {
12 |
13 |
14 |
15 | void ExtractMeshSet(MeshHelper::HalfEdgeTriangulation& mesh, std::vector& tri_set, size_t seed_idx,
16 | std::vector& candidates, const PlaneData &plane_data, const double &z_thresh);
17 | void ConstructPointHash(VUI& plane, MeshHelper::HalfEdgeTriangulation& mesh, PointHash& point_hash, EdgeSet& edge_hash,
18 | ExtremePoint& xPoint, PlaneData& plane_data);
19 |
20 | Polygons ExtractConcaveHulls(Planes &planes, MeshHelper::HalfEdgeTriangulation &mesh, PlaneData &plane_data, size_t min_hole_vertices_);
21 | Polygon ExtractConcaveHull(VUI &plane, MeshHelper::HalfEdgeTriangulation &mesh, PlaneData &plane_data, size_t min_hole_vertices_);
22 |
23 |
24 | // Inline Function
25 |
26 |
27 |
28 | inline bool PassPlaneConstraints(std::vector& plane_set, size_t min_triangles)
29 | {
30 | return plane_set.size() >= min_triangles;
31 | }
32 |
33 | inline size_t fast_mod(const size_t i, const size_t c) { return i >= c ? i % c : i; }
34 |
35 | inline size_t nextHalfedge(size_t e) { return fast_mod(e, 3) == 2 ? e - 2 : e + 1; }
36 |
37 | inline std::array GetVector(size_t edge, MeshHelper::HalfEdgeTriangulation& mesh, std::array& rm,
38 | bool& need_rotation, bool flip = false)
39 | {
40 | auto& coords = mesh.vertices;
41 | auto& triangles = mesh.triangles.data;
42 | auto pi = triangles[edge];
43 | auto piNext = triangles[nextHalfedge(edge)];
44 |
45 | // Points projected on 2D plane, assumes normal is [0,0,1]
46 | std::array p0 = {coords(pi, 0), coords(pi, 1)};
47 | std::array p1 = {coords(piNext, 0), coords(piNext, 1)};
48 | std::array result;
49 |
50 | // Check if points need to be projected onto a different plane
51 | if (need_rotation)
52 | {
53 | // std::cout<< "rotating points for edge: " << edge << std::endl;
54 | // print_matrix(rm);
55 | auto rotated_point = Utility::Math::RotateVector(&coords(pi, 0), rm);
56 | // std::cout<< "p0 before: " << PL_PRINT_ARRAY2(p0) << std::endl;
57 | p0[0] = rotated_point[0];
58 | p0[1] = rotated_point[1];
59 | // std::cout<< "p0 after: " << PL_PRINT_ARRAY2(p0) << std::endl;
60 | auto rotated_point_2 = Utility::Math::RotateVector(&coords(piNext, 0), rm);
61 | // std::cout<< "p1 before: " << PL_PRINT_ARRAY2(p1) << std::endl;
62 | p1[0] = rotated_point_2[0];
63 | p1[1] = rotated_point_2[1];
64 | // std::cout<< "p1 after: " << PL_PRINT_ARRAY2(p1) << std::endl;
65 | }
66 |
67 | if (flip)
68 | {
69 | result[0] = p0[0] - p1[0];
70 | result[1] = p0[1] - p1[1];
71 | }
72 | else
73 | {
74 | result[0] = p1[0] - p0[0];
75 | result[1] = p1[1] - p0[1];
76 | }
77 | return result; // RVO
78 | }
79 |
80 |
81 | inline size_t GetHullEdge(const std::array& v1, const std::vector& outgoingEdges,
82 | MeshHelper::HalfEdgeTriangulation& mesh, std::array& rm, bool& need_rotation,
83 | const bool is_ccw = false)
84 | {
85 | std::vector> otherVectors;
86 | std::transform(outgoingEdges.begin(), outgoingEdges.end(), std::back_inserter(otherVectors),
87 | [&mesh, &rm, &need_rotation](size_t edge) -> std::array {
88 | return GetVector(edge, mesh, rm, need_rotation, false);
89 | });
90 |
91 | std::vector angleDist;
92 | std::transform(otherVectors.begin(), otherVectors.end(), std::back_inserter(angleDist),
93 | [&v1](std::array& outVector) -> double { return Utility::Math::Get360Angle(v1, outVector); });
94 |
95 | if (is_ccw)
96 | {
97 | auto min_pos = std::distance(angleDist.begin(), std::min_element(angleDist.begin(), angleDist.end()));
98 | return outgoingEdges[min_pos];
99 | }
100 | else
101 | {
102 | auto max_pos = std::distance(angleDist.begin(), std::max_element(angleDist.begin(), angleDist.end()));
103 | return outgoingEdges[max_pos];
104 | }
105 | }
106 |
107 | std::vector ConcaveSection(PointHash& pointHash, EdgeSet& edgeHash, MeshHelper::HalfEdgeTriangulation& mesh,
108 | size_t startEdge, size_t stopPoint, PlaneData& plane_data);
109 |
110 | std::vector> ExtractInteriorHoles(PointHash& pointHash, EdgeSet& edgeHash,
111 | MeshHelper::HalfEdgeTriangulation& mesh, PlaneData& plane_data);
112 |
113 | } // namespace Core
114 |
115 | } // namespace Polylidar
116 |
117 | #endif
--------------------------------------------------------------------------------
/include/Polylidar/Delaunator/Delaunator.hpp:
--------------------------------------------------------------------------------
1 | // MIT License
2 |
3 | // Copyright (c) 2018 Volodymyr Bilonenko and Jeremy Castagno
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | #pragma once
24 |
25 | #ifndef DELAUNATOR
26 | #define DELAUNATOR
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | #ifdef PL_USE_ROBUST_PREDICATES
38 | #include "predicates.h"
39 | #endif
40 |
41 | #include "Polylidar/Mesh/MeshHelper.hpp"
42 |
43 | namespace Polylidar {
44 |
45 | namespace Delaunator {
46 |
47 | class Delaunator : public Polylidar::MeshHelper::HalfEdgeTriangulation
48 | {
49 |
50 | public:
51 | std::vector &triangles_ref;
52 | std::vector &halfedges_ref;
53 | std::vector hull_prev;
54 | std::vector hull_next;
55 | std::vector hull_tri;
56 | std::size_t hull_start;
57 |
58 | Delaunator(Matrix &&in_vertices);
59 | Delaunator(const Matrix &in_vertices);
60 | void triangulate();
61 |
62 | double get_hull_area();
63 |
64 | private:
65 | std::vector m_hash;
66 | double m_center_x;
67 | double m_center_y;
68 | std::size_t m_hash_size;
69 | std::vector m_edge_stack;
70 |
71 | std::size_t legalize(std::size_t a);
72 | std::size_t hash_key(double x, double y) const;
73 | std::size_t add_triangle(std::size_t i0, std::size_t i1, std::size_t i2, std::size_t a, std::size_t b,
74 | std::size_t c);
75 | void link(std::size_t a, std::size_t b);
76 | };
77 |
78 | } // namespace delaunator
79 | } // namespace Polylidar
80 | #endif
--------------------------------------------------------------------------------
/include/Polylidar/UtilityMath.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POLYLIDAR_UTILITY_MATH
2 | #define POLYLIDAR_UTILITY_MATH
3 |
4 | #include "Polylidar/Types.hpp"
5 |
6 | namespace Polylidar {
7 |
8 | namespace Utility {
9 |
10 | namespace Math
11 |
12 | {
13 |
14 | inline double Determinant(const std::array& v1, const std::array& v2)
15 | {
16 | return v1[0] * v2[1] - v1[1] * v2[0];
17 | }
18 |
19 | inline double DotProduct2(const std::array& v1, const std::array& v2)
20 | {
21 | return v1[0] * v2[0] + v1[1] * v2[1];
22 | }
23 |
24 | inline void CrossProduct3(const std::array& u, const std::array& v, double* normal)
25 | {
26 | // cross product
27 | normal[0] = u[1] * v[2] - u[2] * v[1];
28 | normal[1] = u[2] * v[0] - u[0] * v[2];
29 | normal[2] = u[0] * v[1] - u[1] * v[0];
30 | }
31 |
32 | inline void Subtract(const std::array& u, const std::array& v, std::array& out)
33 | {
34 | out[0] = u[0] - v[0];
35 | out[1] = u[1] - v[1];
36 | out[2] = u[2] - v[2];
37 | }
38 |
39 | inline void Subtract(const double* u, const double* v, std::array& out)
40 | {
41 | out[0] = u[0] - v[0];
42 | out[1] = u[1] - v[1];
43 | out[2] = u[2] - v[2];
44 | }
45 |
46 | inline void Normalize3(double* normal)
47 | {
48 | auto norm = std::sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]);
49 | normal[0] /= norm;
50 | normal[1] /= norm;
51 | normal[2] /= norm;
52 | }
53 |
54 | inline double L2Norm(double dx, double dy) { return std::sqrt(dx * dx + dy * dy); }
55 |
56 | inline double L2Norm3D(double dx, double dy, double dz) { return std::sqrt(dx * dx + dy * dy + dz * dz); }
57 |
58 | inline double DotProduct3(const std::array& v1, const std::array& v2)
59 | {
60 | return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
61 | }
62 |
63 | inline double DotProduct3(const double* v1, const std::array& v2)
64 | {
65 | return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
66 | }
67 | inline std::array AxisAngleToRotationMatrix(const std::array& axis, const double angle)
68 | {
69 | std::array rm{{1, 0, 0, 0, 1, 0, 0, 0, 1}};
70 | // TODO Research what is the proper way to handle this
71 | if (std::abs(angle) < PL_EPS_RADIAN) return rm;
72 | auto c = std::cos(angle);
73 | auto s = std::sin(angle);
74 | auto t = 1 - c;
75 | auto& x = axis[0];
76 | auto& y = axis[1];
77 | auto& z = axis[2];
78 | // Creat matrix
79 | rm[0] = t * x * x + c;
80 | rm[1] = t * x * y - z * s;
81 | rm[2] = t * x * z + y * s;
82 |
83 | rm[3] = t * x * y + z * s;
84 | rm[4] = t * y * y + c;
85 | rm[5] = t * y * z - x * s;
86 |
87 | rm[6] = t * x * z - y * s;
88 | rm[7] = t * y * z + x * s;
89 | rm[8] = t * z * z + c;
90 |
91 | return rm;
92 | }
93 | inline std::tuple, double> AxisAngleFromVectors(const std::array& v1,
94 | const std::array& v2)
95 | {
96 | std::array axis;
97 | CrossProduct3(v1, v2, axis.data());
98 | Normalize3(axis.data());
99 | double angle = std::acos(DotProduct3(v1, v2));
100 |
101 | return std::make_tuple(axis, angle);
102 | }
103 | // Rotate Vector
104 | inline std::array RotateVector(const double* v1, const std::array& rm)
105 | {
106 | std::array rv1;
107 | rv1[0] = rm[0] * v1[0] + rm[1] * v1[1] + rm[2] * v1[2];
108 | rv1[1] = rm[3] * v1[0] + rm[4] * v1[1] + rm[5] * v1[2];
109 | rv1[2] = rm[6] * v1[0] + rm[7] * v1[1] + rm[8] * v1[2];
110 |
111 | return rv1;
112 | }
113 |
114 | inline double Get360Angle(const std::array& v1, const std::array& v2)
115 | {
116 | auto dot = DotProduct2(v1, v2);
117 | auto det = Determinant(v1, v2);
118 | auto ang = std::atan2(det, dot);
119 | if (ang < 0)
120 | {
121 | ang += M_PI * 2;
122 | }
123 | return ang;
124 | }
125 |
126 | } // namespace Math
127 | } // namespace Utility
128 | } // namespace Polylidar
129 | #endif
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 |
2 | [build-system]
3 | requires = ["setuptools", "wheel", "cmake>=3.14.0"]
4 |
5 | [tool.pytest.ini_options]
6 | minversion = "6.0"
7 | addopts = "-ra -v"
8 | testpaths = [
9 | "tests/python/generated_test.py"
10 | ]
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | # pytest.ini
2 | [pytest]
3 | minversion = 6.0
4 | addopts = -ra -q
5 | testpaths =
6 | tests/python/generated_test.py
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy>=1.15.0
2 | shapely
3 | matplotlib
4 |
--------------------------------------------------------------------------------
/scripts/make_release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # exit when any command fails
3 | set -e
4 |
5 | # keep track of the last executed command
6 | trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
7 | # echo an error message before exiting
8 | trap 'echo "\"${last_command}\" command filed with exit code $?."' EXIT
9 |
10 | python scripts/manage_versions.py --bump patch
11 | pip install .
12 | cd src_docs && python make_docs.py && cd ..
13 | git status
14 |
15 | read -p "Are you sure you want to add all these files? " -n 1 -r
16 | echo # (optional) move to a new line
17 | if [[ $REPLY =~ ^[Yy]$ ]]
18 | then
19 | read -p "Enter commit message: " commmit_message
20 | git add .
21 | git commit -m "$commmit_message"
22 | python scripts/manage_versions.py --tag
23 | echo -e "Dont forget to push:\n \t git push origin dev --tags"
24 | fi
--------------------------------------------------------------------------------
/scripts/manage_versions.py:
--------------------------------------------------------------------------------
1 | """ Manage Versions in a python project
2 | This will bump the file holding the current version (src/version.txt) as well as tag releases
3 | """
4 | import argparse
5 | from pathlib import Path
6 | from git import Repo
7 | import sys
8 |
9 | def parse_args():
10 | parser = argparse.ArgumentParser("Manage the project version including git tags.")
11 | parser.add_argument('--bump',
12 | default='none',
13 | const='none',
14 | nargs='?',
15 | choices=['major', 'minor', 'patch', 'none'],
16 | help='Bump version of project')
17 | parser.add_argument('--version_file',
18 | default='src/version.txt',
19 | help='Path to version file')
20 | parser.add_argument("--tag",
21 | dest="tag",
22 | action="store_true",
23 | default=False,
24 | help="Create git tag")
25 | args = parser.parse_args()
26 |
27 | assert Path(args.version_file).exists()
28 |
29 | return args
30 |
31 | def bump(bump_type:str='patch', version_file='src/version.txt'):
32 | with open('src/version.txt', 'r') as f:
33 | lines = f.readlines()
34 | version = ([int(line.split(' ')[1].strip()) for line in lines if line])
35 |
36 | # Increment the version, resetting minor/patches
37 | if bump_type == 'major':
38 | version[0] += 1
39 | version[1] = 0
40 | version[2] = 0
41 | if bump_type == 'minor':
42 | version[1] += 1
43 | version[2] = 0
44 | if bump_type == 'patch':
45 | version[2] += 1
46 |
47 | if bump_type != 'none':
48 | with open('src/version.txt', 'w') as f:
49 | f.write("MAJOR {}\nMINOR {}\nPATCH {}".format(*version))
50 | else:
51 | pass
52 | # print("Not incrementing version, bump was 'none'.\n")
53 |
54 | string_ints = [str(int_version) for int_version in version]
55 | version_str = ".".join(string_ints)
56 |
57 | return version_str
58 |
59 | def create_tag(version_str, message="Tagged for release"):
60 | version_str = 'v{}'.format(version_str)
61 | repo = Repo('.')
62 | if repo.is_dirty():
63 | resp:str = input("Repo is dirty do you want to continue? [y/n]: ")
64 | if resp.lower() != 'y':
65 | print("Quiting...")
66 | sys.exit()
67 | hexsha = repo.head.commit.hexsha
68 | print("Creating new tag: {} ; commit: {}".format(version_str, hexsha))
69 | new_tag = repo.create_tag(version_str, message=message)
70 | return version_str
71 |
72 | def main():
73 | args = parse_args()
74 | if args.tag:
75 | version_str = bump(bump_type='none', version_file=args.version_file)
76 | new_tag = create_tag(version_str)
77 | print("Don't forget to push the tags with:")
78 | print("\t git push origin {} OR".format(new_tag))
79 | print("\t git push origin --tags")
80 | else:
81 | version_str = bump(bump_type=args.bump, version_file=args.version_file)
82 | print('Incremented version to {}'.format(version_str))
83 |
84 |
85 |
86 |
87 | if __name__ == "__main__":
88 | main()
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | ############ Start Version Parsing ##################
2 |
3 | file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/version.txt" POLYLIDAR_VERSION_READ)
4 | foreach(ver ${POLYLIDAR_VERSION_READ})
5 | if (ver MATCHES "(MAJOR|MINOR|PATCH) +([^ ]+)$")
6 | set(POLYLIDAR_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "")
7 | endif()
8 | endforeach()
9 | string(CONCAT POLYLIDAR_VERSION
10 | "${POLYLIDAR_VERSION_MAJOR}"
11 | ".${POLYLIDAR_VERSION_MINOR}"
12 | ".${POLYLIDAR_VERSION_PATCH}"
13 | )
14 |
15 | set(PROJECT_VERSION "${POLYLIDAR_VERSION}")
16 |
17 | message("Version is ${PROJECT_VERSION}")
18 |
19 | ############# End Version Parsing ##################
20 |
21 |
22 | add_subdirectory(Polylidar)
23 |
24 | if (PL_BUILD_PYMODULE)
25 | add_subdirectory(Python)
26 | endif ()
27 |
--------------------------------------------------------------------------------
/src/Polylidar/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | configure_file("PLConfig.hpp.in" "PLConfig.hpp" @ONLY)
3 |
4 | set(POLYLIDAR_SRC "${CMAKE_CURRENT_SOURCE_DIR}")
5 | set(INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include")
6 | set(POLYLIDAR_INCLUDE_DIR "${INCLUDE_DIR}/Polylidar")
7 | set(POLYLIDAR_SOURCE_FILES ${POLYLIDAR_SRC}/Polylidar.cpp ${POLYLIDAR_SRC}/MeshHelper.cpp ${POLYLIDAR_SRC}/MeshFilter.cpp
8 | ${POLYLIDAR_SRC}/Delaunator.cpp ${POLYLIDAR_SRC}/Core.cpp ${POLYLIDAR_SRC}/PLConfig.cpp)
9 | set(POLYLIDAR_HEADER_FILES ${POLYLIDAR_INCLUDE_DIR}/Polylidar.hpp ${POLYLIDAR_INCLUDE_DIR}/Types.hpp
10 | ${POLYLIDAR_INCLUDE_DIR}/Utility.hpp ${POLYLIDAR_INCLUDE_DIR}/Mesh/MeshHelper.hpp
11 | ${POLYLIDAR_INCLUDE_DIR}/Delaunator/Delaunator.hpp ${POLYLIDAR_INCLUDE_DIR}/Core.hpp)
12 |
13 |
14 | set(LIB_TYPE STATIC)
15 | add_library(${PROJECT_NAME} ${LIB_TYPE} ${POLYLIDAR_SOURCE_FILES})
16 | add_library(Polylidar::PL ALIAS ${PROJECT_NAME})
17 |
18 | ############# Start Build Options ###################
19 |
20 | # Configure optimization
21 | if (CMAKE_BUILD_TYPE STREQUAL "Debug")
22 | set(OPTIMIZATION_FLAGS "-O0 -DDEBUG")
23 | message("-- Configuring debug build")
24 | else()
25 | set(OPTIMIZATION_FLAGS "-O3 -DNDEBUG")
26 | message("-- Configuring release build")
27 | endif()
28 |
29 | # Enable warnings, will set to compile with src/CMakeLists.txt
30 | set(DESIRED_WARNINGS "-Wall -Wextra -Wconversion -Wunreachable-code -Wuninitialized -pedantic-errors -Wold-style-cast -Wno-error=unused-variable -Wshadow -Wfloat-equal -Weffc++")
31 | set(MINIMAL_DESIRED_WARNINGS "-Wall -Wextra -pedantic-errors")
32 | if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
33 | set(DESIRED_WARNINGS "${DESIRED_WARNINGS} -Wmost")
34 | endif()
35 | separate_arguments(DESIRED_WARNINGS)
36 | separate_arguments(MINIMAL_DESIRED_WARNINGS)
37 |
38 | if (PL_BUILD_WERROR)
39 | set(DESIRED_WARNINGS "${DESIRED_WARNINGS} -Werror")
40 | set(MINIMAL_DESIRED_WARNINGS "${MINIMAL_DESIRED_WARNINGS} -Werror")
41 | endif()
42 |
43 | # Set GLOBAL CMAKE_CXX_FLAGS
44 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPTIMIZATION_FLAGS}")
45 |
46 | ############# End Build Options #####################
47 |
48 |
49 | # Set Compile Options for ONLY this target.
50 | target_compile_options(${PROJECT_NAME} PRIVATE
51 | $<$,$,$>:
52 | ${DESIRED_WARNINGS}>
53 | $<$:
54 | /W4>)
55 |
56 | # To include the PLConfig.hpp file
57 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
58 |
59 | # Link with Eigen
60 | # Method to hide warnings from eigen using SYSTEM
61 | get_target_property(eigen_include_dirs eigen INTERFACE_INCLUDE_DIRECTORIES)
62 | target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE ${eigen_include_dirs})
63 | # -pedantic-errors marl has issues with this
64 | target_link_libraries_system_public(${PROJECT_NAME} marl)
65 |
66 | # Fast Exponential
67 | set(FASTEXP_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/thirdparty")
68 | target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${FASTEXP_INCLUDE_DIR})
69 |
70 | # Fast PHMAP
71 | get_target_property(phmap_include_dirs phmap INTERFACE_INCLUDE_DIRECTORIES)
72 | target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${phmap_include_dirs})
73 |
74 | # OPTION Build with OpenMP Support
75 | if (PL_WITH_OPENMP AND NOT APPLE)
76 | find_package(OpenMP)
77 | target_link_libraries(${PROJECT_NAME} PRIVATE OpenMP::OpenMP_CXX)
78 | endif()
79 |
80 | # OPTION Build with Robust Predicates
81 | if (PL_USE_ROBUST_PREDICATES)
82 | MESSAGE(STATUS "Building with Robust Geometric Predicates")
83 | target_link_libraries(${PROJECT_NAME} PRIVATE PL_Predicates)
84 | target_compile_definitions(${PROJECT_NAME} PRIVATE PL_USE_ROBUST_PREDICATES)
85 | endif()
86 |
87 | # Build with FastGA Support
88 | # add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/GaussianAccumulator")
89 | # target_link_libraries(polylidar PRIVATE FastGA::GA)
90 |
91 |
92 | # Set Properties
93 | set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${POLYLIDAR_HEADER_FILES}")
94 | set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION} POSITION_INDEPENDENT_CODE ON)
95 | # Set Public and Private Include Directories
96 | target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIR})
97 | # TODO - This is something that I still do not fully understand, Build vs Install?
98 | target_include_directories(${PROJECT_NAME} PUBLIC
99 | $
100 | $
101 | PRIVATE src)
102 |
103 |
--------------------------------------------------------------------------------
/src/Polylidar/PLConfig.cpp:
--------------------------------------------------------------------------------
1 | #include "Polylidar/Utility.hpp"
2 | #include "PLConfig.hpp"
3 |
4 | namespace Polylidar {
5 |
6 | std::string GetPolylidarVersion() { return std::string("Polylidar ") + POLYLIDAR_VERSION; }
7 |
8 | bool RobustPredicatesActivated() { return PL_USE_ROBUST_PREDICATES; }
9 |
10 | } // namespace Polylidar
--------------------------------------------------------------------------------
/src/Polylidar/PLConfig.hpp.in:
--------------------------------------------------------------------------------
1 | #define POLYLIDAR_VERSION_MAJOR @POLYLIDAR_VERSION_MAJOR@
2 | #define POLYLIDAR_VERSION_MINOR @POLYLIDAR_VERSION_MINOR@
3 | #define POLYLIDAR_VERSION_PATCH @POLYLIDAR_VERSION_PATCH@
4 | #define POLYLIDAR_VERSION "@POLYLIDAR_VERSION@"
5 |
6 | #cmakedefine01 PL_USE_ROBUST_PREDICATES
7 |
--------------------------------------------------------------------------------
/src/Python/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020 Jeremy Castagno
2 |
3 | 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:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | 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.
8 |
--------------------------------------------------------------------------------
/src/Python/MANIFEST.in:
--------------------------------------------------------------------------------
1 | # Misc
2 | include README.md
3 | include LICENSE
4 | include requirements.txt
5 |
6 | # Compiled module
7 | include polylidar/polylidar*.pyd
8 | include polylidar/polylidar*.so
9 | include polylidar_pybind*.so
10 |
11 | # Exclude
12 | global-exclude *.py[co]
--------------------------------------------------------------------------------
/src/Python/make_install_pip_package.cmake:
--------------------------------------------------------------------------------
1 | # License MIT - Open3D
2 | # We need this file for cross-platform support on Windows
3 | # For Ubuntu/Mac, we can simply do `pip install ${PYTHON_PACKAGE_DST_DIR}/pip_package/*.whl -U`
4 |
5 | # Note: Since `make python-package` clears PYTHON_COMPILED_MODULE_DIR every time,
6 | # it is guaranteed that there is only one wheel in ${PYTHON_PACKAGE_DST_DIR}/pip_package/*.whl
7 | file(GLOB WHEEL_FILE "${PYTHON_PACKAGE_DST_DIR}/pip_package/*.whl")
8 | execute_process(COMMAND pip install ${WHEEL_FILE} -U)
--------------------------------------------------------------------------------
/src/Python/make_python_package.cmake:
--------------------------------------------------------------------------------
1 | # Clean up directory
2 | file(REMOVE_RECURSE ${PYTHON_PACKAGE_DST_DIR})
3 | file(MAKE_DIRECTORY ${PYTHON_PACKAGE_DST_DIR}/polylidar)
4 |
5 | # Create python pacakge. It contains:
6 | # 1) Pure-python code and misc files, copied from src/Python/package
7 | # 2) The compiled python-C++ module, i.e. xxx.so (or the equivalents)
8 | # 3) Configured files and supporting files
9 |
10 | # 1) Pure-python code and misc files, copied from src/Python/package
11 | file(COPY ${PYTHON_PACKAGE_SRC_DIR}/
12 | DESTINATION ${PYTHON_PACKAGE_DST_DIR}
13 | )
14 |
15 | # 2) The compiled python-C++ module, i.e. xxx.so (or the equivalents)
16 | get_filename_component(PYTHON_COMPILED_MODULE_NAME ${PYTHON_COMPILED_MODULE_PATH} NAME)
17 | file(COPY ${PYTHON_COMPILED_MODULE_PATH}
18 | DESTINATION ${PYTHON_PACKAGE_DST_DIR}/polylidar)
19 |
20 | MESSAGE("COPYING THIS FILE ${PYTHON_COMPILED_MODULE_PATH} to ${PYTHON_PACKAGE_DST_DIR}/polylidar ")
21 | if (PL_USE_ROBUST_PREDICATES)
22 | MESSAGE(STATUS "Attempting to copy PL_Predicates.dll to python package destination folder")
23 | file(COPY ${ORIG_CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Release/PL_Predicates.dll
24 | DESTINATION ${PYTHON_PACKAGE_DST_DIR}/polylidar)
25 | endif()
26 |
27 |
28 |
29 |
30 | # 2.5) Copy the compiled module to the parent directory as well, only if this variable is set
31 | if (ORIG_CMAKE_LIBRARY_OUTPUT_DIRECTORY)
32 | file(COPY ${PYTHON_COMPILED_MODULE_PATH}
33 | DESTINATION ${ORIG_CMAKE_LIBRARY_OUTPUT_DIRECTORY})
34 | endif()
35 |
36 |
37 | # 3) Configured files and supporting files
38 | configure_file("${PYTHON_PACKAGE_SRC_DIR}/setup.py"
39 | "${PYTHON_PACKAGE_DST_DIR}/setup.py")
40 | configure_file("${PYTHON_PACKAGE_SRC_DIR}/polylidar/__init__.py"
41 | "${PYTHON_PACKAGE_DST_DIR}/polylidar/__init__.py")
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/Python/polylidar/__init__.py:
--------------------------------------------------------------------------------
1 | try:
2 | from .polylidar_pybind import * # py2 py3 compatible
3 | except Exception:
4 | # this was installed with as a python wheel
5 | from polylidar_pybind import *
6 |
7 | try:
8 | import pkg_resources # part of setuptools
9 | __version__ = pkg_resources.require("polylidar")[0].version
10 | except Exception:
11 | __version__ = '@PROJECT_VERSION@'
12 |
--------------------------------------------------------------------------------
/src/Python/polylidar/__main__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src/Python/polylidar/__main__.py
--------------------------------------------------------------------------------
/src/Python/polylidar/polylidarutil/descartes_patch.py:
--------------------------------------------------------------------------------
1 | """Paths and patches"""
2 |
3 | from matplotlib.patches import PathPatch
4 | from matplotlib.path import Path
5 | from numpy import asarray, concatenate, ones
6 |
7 |
8 | class Polygon(object):
9 | # Adapt Shapely or GeoJSON/geo_interface polygons to a common interface
10 | def __init__(self, context):
11 | if isinstance(context, dict):
12 | self.context = context['coordinates']
13 | else:
14 | self.context = context
15 |
16 | @property
17 | def exterior(self):
18 | return (getattr(self.context, 'exterior', None)
19 | or self.context[0])
20 |
21 | @property
22 | def interiors(self):
23 | value = getattr(self.context, 'interiors', None)
24 | if value is None:
25 | value = self.context[1:]
26 | return value
27 |
28 |
29 | def PolygonPath(polygon):
30 | """Constructs a compound matplotlib path from a Shapely or GeoJSON-like
31 | geometric object"""
32 |
33 | def coding(ob):
34 | # The codes will be all "LINETO" commands, except for "MOVETO"s at the
35 | # beginning of each subpath
36 | n = len(getattr(ob, 'coords', None) or ob)
37 | vals = ones(n, dtype=Path.code_type) * Path.LINETO
38 | vals[0] = Path.MOVETO
39 | return vals
40 |
41 | if hasattr(polygon, 'geom_type'): # Shapely
42 | ptype = polygon.geom_type
43 | if ptype == 'Polygon':
44 | polygon = [Polygon(polygon)]
45 | elif ptype == 'MultiPolygon':
46 | polygon = [Polygon(p) for p in polygon]
47 | else:
48 | raise ValueError(
49 | "A polygon or multi-polygon representation is required")
50 |
51 | else: # GeoJSON
52 | polygon = getattr(polygon, '__geo_interface__', polygon)
53 | ptype = polygon["type"]
54 | if ptype == 'Polygon':
55 | polygon = [Polygon(polygon)]
56 | elif ptype == 'MultiPolygon':
57 | polygon = [Polygon(p) for p in polygon['coordinates']]
58 | else:
59 | raise ValueError(
60 | "A polygon or multi-polygon representation is required")
61 |
62 | vertices = concatenate([
63 | concatenate([asarray(t.exterior.coords)[:, :2]] +
64 | [asarray(r)[:, :2] for r in t.interiors])
65 | for t in polygon])
66 | codes = concatenate([
67 | concatenate([coding(t.exterior.coords)] +
68 | [coding(r) for r in t.interiors]) for t in polygon])
69 |
70 | return Path(vertices, codes)
71 |
72 |
73 | def PolygonPatch(polygon, **kwargs):
74 | """Constructs a matplotlib patch from a geometric object
75 |
76 | The `polygon` may be a Shapely or GeoJSON-like object with or without holes.
77 | The `kwargs` are those supported by the matplotlib.patches.Polygon class
78 | constructor. Returns an instance of matplotlib.patches.PathPatch.
79 |
80 | Example (using Shapely Point and a matplotlib axes):
81 |
82 | >>> b = Point(0, 0).buffer(1.0)
83 | >>> patch = PolygonPatch(b, fc='blue', ec='blue', alpha=0.5)
84 | >>> axis.add_patch(patch)
85 |
86 | """
87 | return PathPatch(PolygonPath(polygon), **kwargs)
--------------------------------------------------------------------------------
/src/Python/polylidar_pybind/polylidar_pybind.hpp:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 | #include
6 | #include // Include first to suppress compiler warnings
7 | #include
8 | #include
9 |
10 | #include "Polylidar/Polylidar.hpp"
11 | #include "polylidar_pybind/docstring/docstring.hpp"
12 |
13 | namespace py = pybind11;
14 | using namespace py::literals;
15 |
16 | PYBIND11_MAKE_OPAQUE(std::vector);
17 | PYBIND11_MAKE_OPAQUE(std::vector);
18 | PYBIND11_MAKE_OPAQUE(std::vector);
19 | PYBIND11_MAKE_OPAQUE(std::vector);
20 | PYBIND11_MAKE_OPAQUE(std::vector>);
21 | PYBIND11_MAKE_OPAQUE(std::vector>);
22 | PYBIND11_MAKE_OPAQUE(std::vector>);
23 | PYBIND11_MAKE_OPAQUE(std::vector>);
24 |
25 | // some helper functions
26 | namespace pybind11 {
27 | namespace detail {
28 |
29 | template
30 | void bind_default_constructor(Class_& cl)
31 | {
32 | cl.def(py::init([]() { return new T(); }), "Default constructor");
33 | }
34 |
35 | template
36 | void bind_copy_functions(Class_& cl)
37 | {
38 | cl.def(py::init([](const T& cp) { return new T(cp); }), "Copy constructor");
39 | cl.def("__copy__", [](T& v) { return T(v); });
40 | cl.def("__deepcopy__", [](T& v, py::dict& memo) { return T(v); });
41 | }
42 |
43 | } // namespace detail
44 | } // namespace pybind11
--------------------------------------------------------------------------------
/src/Python/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy>=1.13.0
2 | shapely
3 | matplotlib
4 |
--------------------------------------------------------------------------------
/src/Python/setup.py:
--------------------------------------------------------------------------------
1 |
2 | from setuptools import setup, find_packages
3 | import glob
4 |
5 | # Force platform specific wheel
6 | try:
7 | from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
8 | # https://stackoverflow.com/a/45150383/1255535
9 |
10 | class bdist_wheel(_bdist_wheel):
11 | def finalize_options(self):
12 | _bdist_wheel.finalize_options(self)
13 | self.root_is_pure = False
14 | print("bdist wheel is working and installed")
15 | except ImportError:
16 | print('Warning: cannot import "wheel" package to build platform-specific wheel')
17 | print('Install the "wheel" package to fix this warning')
18 | bdist_wheel = None
19 |
20 | cmdclass = {'bdist_wheel': bdist_wheel} if bdist_wheel is not None else dict()
21 |
22 |
23 | # Read requirements.txt
24 | with open('requirements.txt', 'r') as f:
25 | lines = f.readlines()
26 | install_requires = [line.strip() for line in lines if line]
27 |
28 |
29 | setup(
30 | author='Jeremy Castagno',
31 | author_email='',
32 | classifiers=[
33 | ],
34 | description=[
35 | "Polylidar ...."
36 | ],
37 | cmdclass=cmdclass,
38 | install_requires=install_requires,
39 | include_package_data=True,
40 | keywords="",
41 | license="MIT",
42 | long_description=open('README.md').read(),
43 | long_description_content_type='text/markdown',
44 | # Name of the package on PyPI
45 | name="@PYPI_PACKAGE_NAME@",
46 | packages=[
47 | 'polylidar',
48 | ],
49 | url="@PROJECT_HOME@",
50 | project_urls={
51 | 'Documentation': '@PROJECT_DOCS@',
52 | 'Source code': '@PROJECT_CODE@',
53 | 'Issues': '@PROJECT_ISSUES@',
54 | },
55 | version='@PROJECT_VERSION@',
56 | zip_safe=False,
57 | )
58 |
--------------------------------------------------------------------------------
/src/version.txt:
--------------------------------------------------------------------------------
1 | MAJOR 1
2 | MINOR 0
3 | PATCH 9
--------------------------------------------------------------------------------
/src_docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/.nojekyll
--------------------------------------------------------------------------------
/src_docs/_static/cpp_tutorial/opc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/cpp_tutorial/opc.png
--------------------------------------------------------------------------------
/src_docs/_static/cpp_tutorial/opc_mesh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/cpp_tutorial/opc_mesh.png
--------------------------------------------------------------------------------
/src_docs/_static/cpp_tutorial/opc_polygons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/cpp_tutorial/opc_polygons.png
--------------------------------------------------------------------------------
/src_docs/_static/organized/l515_mesh_smooth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/organized/l515_mesh_smooth.png
--------------------------------------------------------------------------------
/src_docs/_static/organized/l515_opc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/organized/l515_opc.png
--------------------------------------------------------------------------------
/src_docs/_static/organized/l515_polygons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/organized/l515_polygons.png
--------------------------------------------------------------------------------
/src_docs/_static/organized/organized_mesh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/organized/organized_mesh.png
--------------------------------------------------------------------------------
/src_docs/_static/organized/organized_pc_raw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/organized/organized_pc_raw.png
--------------------------------------------------------------------------------
/src_docs/_static/organized/organized_polygons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/organized/organized_polygons.png
--------------------------------------------------------------------------------
/src_docs/_static/pl_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/src_docs/_static/pl_logo.png
--------------------------------------------------------------------------------
/src_docs/_static/theme_overrides.css:
--------------------------------------------------------------------------------
1 | /* Force table wrap: https://rackerlabs.github.io/docs-rackspace/tools/rtd-tables.html */
2 | /* override table width restrictions */
3 | @media screen and (min-width: 767px) {
4 |
5 | .wy-table-responsive table td {
6 | /* !important prevents the common CSS stylesheets from overriding
7 | this as on RTD they are loaded after this stylesheet */
8 | white-space: normal !important;
9 | }
10 |
11 | .wy-table-responsive {
12 | overflow: visible !important;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src_docs/builddocs.rst:
--------------------------------------------------------------------------------
1 | .. _builddocs:
2 |
3 | Building Documentation
4 | ======================
5 |
6 | The main documentation and Python documentation is written in
7 | `reStructuredText `_ and
8 | generated by `sphinx `_. The C++ API documentation
9 | is generated as xml with `Doxygen `_ and parsed by
10 | `breathe `_ and `exhale `_.
11 |
12 |
13 | Prerequisites
14 | -------------
15 |
16 | 1. Build Polylidar3D from source
17 | ``````````````````````````````````````
18 |
19 | Clone and build Polylidar from source: :ref:`install_instructions`.
20 |
21 | 2. Install Sphinx
22 | `````````````````
23 |
24 | .. code-block:: bash
25 |
26 | # For pip
27 | pip install sphinx sphinx-autobuild
28 |
29 | # Or, for Conda
30 | conda install sphinx sphinx-autobuild
31 |
32 |
33 | 3. Install Doxygen
34 | ``````````````````
35 |
36 | **Ubuntu**
37 |
38 | .. code-block:: bash
39 |
40 | sudo apt-get -y install doxygen
41 |
42 | **macOS**
43 |
44 | .. code-block:: bash
45 |
46 | brew install doxygen
47 |
48 | **Windows**
49 |
50 | Visit `Doxygen downloads `_ page to
51 | checkout the source or
52 | `binaries `_.
53 |
54 |
55 | Build
56 | -----
57 |
58 | .. code-block:: bash
59 |
60 | cd docs
61 |
62 | # You may optionally select the --sphinx flag
63 | python make_docs.py --sphinx
64 |
65 | The docs html will be saved in ``docs/_out`` folder.
66 |
--------------------------------------------------------------------------------
/src_docs/index.rst:
--------------------------------------------------------------------------------
1 | .. Polylidar3D documentation master file, created by
2 | sphinx-quickstart on Mon Apr 3 14:18:28 2017.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | .. image:: _static/pl_logo.png
7 | :alt: Polylidar3D Logo
8 | :width: 320px
9 | :align: center
10 |
11 | -----------
12 |
13 | Polylidar3D: Fast Polygon Extraction from 2D & 3D Data
14 | ========================================================
15 |
16 | .. only: not latex
17 |
18 | Contents:
19 |
20 | .. _gettting_started_index:
21 |
22 | .. toctree::
23 | :maxdepth: 1
24 | :caption: Getting Started
25 |
26 | introduction
27 | install_instructions
28 | builddocs
29 |
30 |
31 | .. _tutorial_index:
32 |
33 | .. toctree::
34 | :maxdepth: 1
35 | :caption: Tutorial
36 |
37 | tutorial/Python/index
38 | tutorial/C++/index
39 |
40 | .. _python_api_index:
41 |
42 | .. toctree::
43 | :maxdepth: 1
44 | :caption: Python API
45 |
46 | python_api/polylidar
47 | python_api/polylidar.polylidarutil.plane_filtering
48 |
49 | ..
50 | Please put the module and meta data you want here!
51 | MAKE_DOCS/python_api/polylidar
52 | MAKE_DOCS/python_api/polylidar.polylidarutil.plane_filtering python_only
53 |
54 | .. _cpp_api_index:
55 |
56 | .. toctree::
57 | :maxdepth: 1
58 | :caption: C++ API
59 |
60 | cpp_api/cpp_library_root
61 |
--------------------------------------------------------------------------------
/src_docs/introduction.rst:
--------------------------------------------------------------------------------
1 | .. _introduction:
2 |
3 | Introducing Polylidar3D
4 | ############################
5 |
6 |
7 | .. mdinclude:: ../README.md
8 |
9 |
--------------------------------------------------------------------------------
/src_docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 |
11 | %SPHINXBUILD% >NUL 2>NUL
12 | if errorlevel 9009 (
13 | echo.
14 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
15 | echo.installed, then set the SPHINXBUILD environment variable to point
16 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
17 | echo.may add the Sphinx directory to PATH.
18 | echo.
19 | echo.If you don't have Sphinx installed, grab it from
20 | echo.http://sphinx-doc.org/
21 | exit /b 1
22 | )
23 |
24 | python make.py %1
25 | goto end
26 |
27 | :end
28 | popd
29 |
--------------------------------------------------------------------------------
/src_docs/tutorial/C++/basic.rst:
--------------------------------------------------------------------------------
1 | .. _cplusplus_basic_tutorial:
2 |
3 | C++ interface
4 | ------------------
5 |
6 | Take a look at `examples/cpp/polylidar-full-example.cpp`.
7 |
8 | First we load a numpy file of an organized point cloud that has been previosly captured.
9 |
10 | .. literalinclude:: ../../../examples/cpp/polylidar-full-example.cpp
11 | :language: cpp
12 | :lines: 42-64
13 |
14 | Then we convert the vector to a matrix wrapper (no copy) that Polylidar uses.
15 |
16 | .. literalinclude:: ../../../examples/cpp/polylidar-full-example.cpp
17 | :language: cpp
18 | :lines: 66-71
19 |
20 | Then we visualize the point cloud:
21 |
22 | .. literalinclude:: ../../../examples/cpp/polylidar-full-example.cpp
23 | :language: cpp
24 | :lines: 73-77
25 |
26 | .. image:: /_static/cpp_tutorial/opc.png
27 |
28 |
29 | Then we create and visualize the mesh:
30 |
31 | .. literalinclude:: ../../../examples/cpp/polylidar-full-example.cpp
32 | :language: cpp
33 | :lines: 79-92
34 |
35 | .. image:: /_static/cpp_tutorial/opc_mesh.png
36 |
37 |
38 | Then we find the dominant plane normals using FastGA:
39 |
40 | .. literalinclude:: ../../../examples/cpp/polylidar-full-example.cpp
41 | :language: cpp
42 | :lines: 94-99
43 |
44 |
45 | Then we extract the Planes and Polygons:
46 |
47 | .. literalinclude:: ../../../examples/cpp/polylidar-full-example.cpp
48 | :language: cpp
49 | :lines: 101-107
50 |
51 |
52 | Then we colorize the Open3D mesh by plane segments and create the corresponding polygons:
53 |
54 | .. literalinclude:: ../../../examples/cpp/polylidar-full-example.cpp
55 | :language: cpp
56 | :lines: 109-133
57 |
58 | .. image:: /_static/cpp_tutorial/opc_polygons.png
59 |
60 |
61 | Full output:
62 |
63 | .. code-block:: bash
64 |
65 | $ ./cmake-build/bin/Release/polylidar-full.exe
66 | A more complete example of using Polylidar3D with 3D Data. Needs Open3D and FastGA.
67 | Loading previously captured Organized Point Cloud from an L515 Camera.
68 | Attempting to load OPC file from ./fixtures/realsense/opc_example_one/L515_OPC.npy
69 | Shape of Point Cloud is: [180, 320, 3]
70 | Visualizing Point Cloud. Rotate View. Close Open3D window when satisfied...
71 | Creating Mesh and smoothing. NOT using optimized filters, see OrganizedPointFilters
72 | Visualizing mesh. Rotate View.
73 | Detected Peaks with FastGaussianAccumulator. Here are the detected peaks: [0.0641286, 0.525564, -0.848333], [0.00935028, -0.849775, -0.527063],
74 |
75 | Extracting Polygons from all detected peaks:
76 | Visualing Planes and Polygon. Each plane segment is color coded. Polgyons are shown as thin lines (OpenGL has no thickness):
--------------------------------------------------------------------------------
/src_docs/tutorial/C++/index.rst:
--------------------------------------------------------------------------------
1 | C++
2 | ===================================================================
3 |
4 | C++ Interface Tutorials
5 |
6 | .. toctree::
7 |
8 | basic
--------------------------------------------------------------------------------
/src_docs/tutorial/Python/basicdemo.nblink:
--------------------------------------------------------------------------------
1 | {
2 | "path": "../../../examples/python/notebooks/BasicDemo.ipynb"
3 | }
--------------------------------------------------------------------------------
/src_docs/tutorial/Python/index.rst:
--------------------------------------------------------------------------------
1 | Python
2 | ===================================================================
3 |
4 | Python Tutorials
5 |
6 | .. toctree::
7 | :maxdepth: 0
8 |
9 | basicdemo
10 | organizeddemo
11 |
12 |
13 | .. warning::
14 | Polylidar3D uses Open3D to visualize 3D geometries (pointclouds, polygons, meshes, etc.). A recent version of Open3D has a serious performance regression which causes severe slowdown during visualization.
15 | This regression is on versions 0.10 and 0.11 of Open3D. Regression details: `Link `_ , `Issue1 `_ , `Issue2 `_.
16 | I recommend that you stick with 0.9.0, build from master, or wait for 0.12.
--------------------------------------------------------------------------------
/src_docs/tutorial/Python/organizeddemo.nblink:
--------------------------------------------------------------------------------
1 | {
2 | "path": "../../../examples/python/notebooks/OrganizedDemo.ipynb"
3 | }
--------------------------------------------------------------------------------
/tests/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(cpp)
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/tests/__init__.py
--------------------------------------------------------------------------------
/tests/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | include(FetchContent)
2 | FetchContent_Declare(
3 | extern_doctest
4 | GIT_REPOSITORY https://github.com/onqtam/doctest.git
5 | GIT_TAG 2.3.7
6 | )
7 |
8 | # This module depend on having doctest installed
9 | FetchContent_MakeAvailable(extern_doctest)
10 |
11 | add_executable(run-tests ${CMAKE_CURRENT_SOURCE_DIR}/run-tests.cpp)
12 | target_link_libraries(run-tests PRIVATE doctest::doctest Polylidar::PL)
13 |
--------------------------------------------------------------------------------
/tests/cpp/run-tests.cpp:
--------------------------------------------------------------------------------
1 | // This file configured doctest to run, please leave empty
2 | // Write more tests as separate files and add in CMakeLists.txt
3 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
4 | #include "doctest/doctest.h"
5 |
6 |
--------------------------------------------------------------------------------
/tests/python/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/tests/python/__init__.py
--------------------------------------------------------------------------------
/tests/python/benchmark_test.py:
--------------------------------------------------------------------------------
1 | from os import path
2 | import pytest
3 | import numpy as np
4 |
5 |
6 | from tests.python.helpers.utils import load_csv, verify_points, basic_polylidar_verification, verify_all_polygons_are_valid, load_npy
7 | try:
8 | from polylidar import extractPlanesAndPolygons, extractPolygons, extractPolygonsAndTimings
9 | Polylidar3D = None
10 | except Exception:
11 | print("Using new Polylidar3D")
12 | from polylidar import Polylidar3D, MatrixDouble
13 |
14 |
15 | # def test_building1(benchmark, building1, basic_params):
16 | # benchmark(extractPlanesAndPolygons, building1, **basic_params)
17 |
18 |
19 | # def test_building2(benchmark, building2, basic_params):
20 | # benchmark(extractPlanesAndPolygons, building2, **basic_params)
21 |
22 |
23 | def test_100k_array_lmax(benchmark, np_100K_array, params_lmax):
24 | if Polylidar3D is not None:
25 | points_mat = MatrixDouble(np_100K_array)
26 | pl = Polylidar3D(**params_lmax)
27 | _, _ , _ = benchmark(pl.extract_planes_and_polygons, points_mat)
28 | else:
29 | benchmark(extractPolygons, np_100K_array, **params_lmax)
30 |
31 | def test_100k_array_3d_lmax(benchmark, np_100K_array_3d, params_lmax):
32 | if Polylidar3D is not None:
33 | points_mat = MatrixDouble(np_100K_array_3d)
34 | pl = Polylidar3D(**params_lmax)
35 | _, _ , _ = benchmark(pl.extract_planes_and_polygons, points_mat)
36 | else:
37 | benchmark(extractPolygons, np_100K_array_3d, **params_lmax)
38 |
39 | def test_clusters(benchmark, cluster_groups):
40 | """
41 | Will benchmark clusters AND check that python overhead is less than 3ms (ussually <1 ms)
42 | """
43 | points = cluster_groups['points']
44 | polylidar_kwargs = cluster_groups['polylidar_kwargs']
45 | if Polylidar3D is not None:
46 | points_mat = MatrixDouble(points)
47 | pl = Polylidar3D(**polylidar_kwargs)
48 | _, _ , _ = benchmark(pl.extract_planes_and_polygons, points_mat)
49 | else:
50 | _, _ , _ = benchmark(extractPlanesAndPolygons, points, **polylidar_kwargs)
51 |
52 |
53 | # def test_clusters(benchmark, cluster_groups):
54 | # """
55 | # Will benchmark clusters AND check that python overhead is less than 3ms (ussually <1 ms)
56 | # """
57 | # points = cluster_groups['points']
58 | # polylidar_kwargs = cluster_groups['polylidar_kwargs']
59 | # _, timings = benchmark(extractPolygonsAndTimings, points, **polylidar_kwargs)
60 | # cpp_timing = np.sum(timings) / 1000.0 # Actuall c++ timing
61 | # median_benchmark = benchmark.stats.stats.median # python benchmark timing
62 | # assert cpp_timing == pytest.approx(median_benchmark, abs=0.003)
63 |
64 |
--------------------------------------------------------------------------------
/tests/python/conftest.py:
--------------------------------------------------------------------------------
1 | from os import path
2 | import pytest
3 | import numpy as np
4 |
5 |
6 | from tests.python.helpers.utils import load_csv, load_npy
7 | from polylidar.polylidarutil import generate_test_points, get_estimated_lmax
8 |
9 | np.random.seed(1)
10 |
11 |
12 | @pytest.fixture
13 | def building1():
14 | return load_csv('building1.csv')
15 |
16 |
17 | @pytest.fixture
18 | def building2():
19 | return load_csv("building2.csv")
20 |
21 |
22 | @pytest.fixture
23 | def hardcase1():
24 | return load_csv("hardcase1.csv")
25 |
26 |
27 | # @pytest.fixture
28 | # def bad_convex_hull():
29 | # return load_npy("possible_error_free.npy")
30 |
31 |
32 | @pytest.fixture()
33 | def basic_params():
34 | return dict(alpha=0.5)
35 |
36 |
37 | @pytest.fixture()
38 | def params_lmax():
39 | return dict(alpha=0.0, lmax=100.0)
40 |
41 |
42 | @pytest.fixture()
43 | def hardcase1_params():
44 | return dict(alpha=0.0, lmax=20.0)
45 |
46 |
47 | @pytest.fixture
48 | def np_100K_array():
49 | return load_csv("100K_array_2d.csv")
50 |
51 |
52 | @pytest.fixture
53 | def np_100K_array_3d():
54 | return load_csv("100K_array_3d.csv")
55 |
56 |
57 | @pytest.fixture()
58 | def bad_convex_hull_params():
59 | return dict(alpha=0.0, lmax=1300.0)
60 |
61 | @pytest.fixture(params=range(2, 11))
62 | def num_groups(request):
63 | return request.param
64 |
65 | @pytest.fixture(params=[1000, 10000])
66 | def group_size(request):
67 | return request.param
68 |
69 | @pytest.fixture
70 | def cluster_groups(num_groups, group_size):
71 | cluster_params = dict(num_groups=num_groups, group_size=group_size)
72 | points = generate_test_points(**cluster_params)
73 | lmax = get_estimated_lmax(**cluster_params)
74 | polylidar_kwargs = dict(alpha=0.0, lmax=lmax)
75 | return dict(points=points, polylidar_kwargs=polylidar_kwargs, cluster_params=cluster_params)
76 |
77 | # This creates 100 numpy arrays frangin from (1000,2) -> (100000,2)
78 | ts = range(1000, 100000, 1000)
79 | @pytest.fixture(params=ts)
80 | def random_points(request):
81 | points = np.random.randn(request.param, 2) * 100 + 700000
82 | return points
83 |
--------------------------------------------------------------------------------
/tests/python/generated_test.py:
--------------------------------------------------------------------------------
1 | from os import path
2 | import pytest
3 | import numpy as np
4 |
5 |
6 | from tests.python.helpers.utils import verify_points, basic_polylidar_verification, verify_all_polygons_are_valid, verify_all
7 |
8 |
9 | np.random.seed(1)
10 |
11 | def test_random_points(random_points, params_lmax):
12 | verify_all(random_points, params_lmax)
13 |
14 | def test_clusters(cluster_groups):
15 | points = cluster_groups['points']
16 | polylidar_kwargs = cluster_groups['polylidar_kwargs']
17 | verify_all(points, polylidar_kwargs)
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/python/helpers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JeremyBYU/polylidar/e64fd96274786f9126eb8597afe9a8fd40a823d2/tests/python/helpers/__init__.py
--------------------------------------------------------------------------------
/tests/python/helpers/utils.py:
--------------------------------------------------------------------------------
1 | from os import path
2 | import pytest
3 | import numpy as np
4 |
5 | from shapely.geometry import Polygon
6 | from polylidar import Polylidar3D, MatrixDouble, HalfEdgeTriangulation
7 |
8 | DIR_NAME = path.dirname(path.dirname(path.dirname(path.dirname(__file__))))
9 | FIXTURES_DIR = path.join(DIR_NAME, 'fixtures', 'pointsets')
10 |
11 | def get_poly_coords(outline, points):
12 | return [get_point(pi, points) for pi in outline]
13 |
14 | def get_point(pi, points):
15 | return [points[pi, 0], points[pi, 1]]
16 |
17 | def load_csv(file, delimeter=','):
18 | return np.loadtxt(path.join(FIXTURES_DIR, file), delimiter=delimeter, dtype=np.float64, skiprows=1)
19 |
20 | def load_npy(file):
21 | return np.load(path.join(FIXTURES_DIR, file))
22 |
23 | def verify_points(data, n_cols=2):
24 | assert isinstance(data, np.ndarray)
25 | assert data.shape[1] == n_cols
26 |
27 | def basic_polylidar_verification(points, mesh:HalfEdgeTriangulation, planes, polygons):
28 | # make sure delaunay has the correct number of points
29 | assert points.shape[0] == np.array(mesh.vertices).shape[0]
30 | # make sure that some planes were extracted
31 | assert len(planes) > 0
32 | # make sure that some polygons were extracted
33 | assert len(polygons) > 0
34 |
35 | def verify_all_polygons_are_valid(polygons, points):
36 | for poly in polygons:
37 | verify_valid_polygon(poly, points)
38 |
39 | def verify_valid_polygon(poly, points):
40 | shell_coords = get_poly_coords(poly.shell, points)
41 | hole_coords = [get_poly_coords(hole, points) for hole in poly.holes]
42 | # Verify polygon outer shell
43 | shapelyPoly = Polygon(shell=shell_coords)
44 | assert shapelyPoly.is_valid
45 | # Verify polygon outer shell and holes
46 | shapelyPoly = Polygon(shell=shell_coords, holes=hole_coords)
47 | # if not shapelyPoly.is_valid:
48 | # np.save("scratch/error_{}.npy".format(points.shape[0]), points)
49 | assert shapelyPoly.is_valid
50 |
51 | def verify_all(points, polylidar_kwargs):
52 | pl = Polylidar3D(**polylidar_kwargs)
53 | points_mat = MatrixDouble(points)
54 | mesh, planes, polygons = pl.extract_planes_and_polygons(points_mat)
55 | # Basic test to ensure no obvious errors occurred
56 | basic_polylidar_verification(points, mesh, planes, polygons)
57 | # Ensure that the polygons returned are valid
58 | verify_all_polygons_are_valid(polygons, points)
59 |
60 |
61 |
--------------------------------------------------------------------------------
/tests/python/shape_test.py:
--------------------------------------------------------------------------------
1 | from os import path
2 | import pytest
3 | import numpy as np
4 |
5 |
6 | from tests.python.helpers.utils import verify_points, basic_polylidar_verification, verify_all_polygons_are_valid, verify_all
7 |
8 |
9 | np.random.seed(1)
10 |
11 | def test_verify_building1(building1):
12 | verify_points(building1, 4)
13 |
14 | def test_verify_building2(building2):
15 | verify_points(building2, 4)
16 |
17 | def test_verify_hardcase1(hardcase1):
18 | verify_points(hardcase1, 2)
19 |
20 | def test_building1(building1, basic_params):
21 | verify_all(building1, basic_params)
22 |
23 |
24 | def test_building2(building2, basic_params):
25 | verify_all(building2, basic_params)
26 |
27 | def test_hardcase1(hardcase1, hardcase1_params):
28 | verify_all(hardcase1, hardcase1_params)
29 |
30 | # def test_bad_convex_hull(bad_convex_hull, bad_convex_hull_params):
31 | # verify_all(bad_convex_hull, bad_convex_hull_params)
32 |
33 | def test_random_points(random_points, params_lmax):
34 | verify_all(random_points, params_lmax)
35 |
36 | def test_clusters(cluster_groups):
37 | points = cluster_groups['points']
38 | polylidar_kwargs = cluster_groups['polylidar_kwargs']
39 | verify_all(points, polylidar_kwargs)
40 |
41 |
42 |
--------------------------------------------------------------------------------
/thirdparty/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | include(FetchContent)
2 | # Put any LIBRARY dependencies in here
3 | FetchContent_Declare(
4 | extern_pybind11
5 | GIT_REPOSITORY https://github.com/pybind/pybind11.git
6 | GIT_TAG v2.9.1
7 | GIT_PROGRESS TRUE
8 | )
9 |
10 | FetchContent_Declare(
11 | extern_parallel_hashmap
12 | GIT_REPOSITORY https://github.com/greg7mdp/parallel-hashmap.git
13 | GIT_TAG 1.31
14 | GIT_PROGRESS TRUE
15 | )
16 |
17 | FetchContent_Declare(
18 | extern_eigen
19 | GIT_REPOSITORY https://github.com/eigenteam/eigen-git-mirror.git
20 | GIT_TAG 3.3.7
21 | GIT_PROGRESS TRUE
22 | )
23 |
24 | FetchContent_Declare(
25 | extern_marl
26 | GIT_REPOSITORY https://github.com/google/marl.git
27 | GIT_TAG 32af8bb50807f719818168eb165761e60e0090d5
28 | GIT_PROGRESS TRUE
29 | )
30 |
31 | FetchContent_Declare(
32 | extern_fastga
33 | GIT_REPOSITORY https://github.com/JeremyBYU/FastGaussianAccumulator
34 | GIT_TAG origin/master
35 | GIT_PROGRESS TRUE
36 | )
37 |
38 |
39 | # Build Robust Predicates if requested
40 | if (PL_USE_ROBUST_PREDICATES)
41 | MESSAGE(STATUS "Building Robust Geometric Predicates")
42 | add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/predicates")
43 | endif()
44 |
45 | # After the following call, the library will be installed
46 | FetchContent_MakeAvailable(extern_parallel_hashmap extern_pybind11 extern_eigen extern_marl)
47 |
48 | # Build Robust Predicates if requested
49 | if (PL_BUILD_FASTGA AND PL_BUILD_EXAMPLES)
50 | MESSAGE(STATUS "Building Examples with FastGA")
51 | # Add fastga but dont build examples and unit tests
52 | FetchContent_GetProperties(extern_fastga)
53 | if(NOT extern_fastga_POPULATED)
54 | FetchContent_Populate(extern_fastga)
55 | set(GA_BUILD_PYMODULE OFF CACHE BOOL "GA -Build Python Module" FORCE)
56 | set(GA_BUILD_TESTS OFF CACHE BOOL "GA - Build Tests" FORCE)
57 | set(GA_BUILD_BENCHMARKS OFF CACHE BOOL "GA - Build Benchmarks" FORCE)
58 | set(GA_BUILD_EXAMPLES OFF CACHE BOOL "GA - Build Examples" FORCE)
59 | add_subdirectory(${extern_fastga_SOURCE_DIR} ${extern_fastga_BINARY_DIR})
60 | endif()
61 | endif()
62 |
--------------------------------------------------------------------------------
/thirdparty/FastExp/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 simonpf
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/thirdparty/FastExp/fastexp.h:
--------------------------------------------------------------------------------
1 | #ifndef FASTEXP_H
2 | #define FASTEXP_H
3 |
4 | #include "math.h"
5 | #include
6 | #include
7 | #include
8 | #include "FastExp/product.h"
9 | #include "FastExp/ieee.h"
10 |
11 | namespace fastexp
12 | {
13 |
14 | enum class Approximation {IEEE, PRODUCT};
15 |
16 | /** \brief Fast approximate exponential.
17 | *
18 | * This function implements a fast, vectorizable approximation
19 | * of the exponential function based on the following two articles:
20 | *
21 | * - Malossi, A. Cristiano I. & Ineichen, Yves & Bekas, Costas & Curioni,
22 | * Alessandro. "Fast Exponential Computation on SIMD Architectures." (2015)
23 | * 10.13140/2.1.4362.3207.
24 | * - IEEE, Nicol N. "A fast, compact approximation of the exponential
25 | * function." Neural Computation 11.4 (1999): 853-862.
26 | *
27 | * The approximation interpolates linearly between points on the curve of
28 | * the exponential function that can be expressed as 2^i where i is an
29 | * a signed integer. So yes, that is very approximate ...
30 | *
31 | * \tparam Real The floating point type of the arguments.
32 | * \param x The argument of the exponential function.
33 | * \return The approximated value of the exponential function.
34 | */
35 | #ifndef _WIN32
36 | #pragma omp declare simd notinbranch
37 | #endif
38 | template
39 | <
40 | typename Real,
41 | template class Approximation = IEEE,
42 | size_t degree = 2
43 | >
44 | inline Real exp(const Real &x)
45 | {
46 | return Approximation::evaluate(x);
47 | }
48 |
49 | /** \brief Fast approximate array exponential.
50 | *
51 | * Applies the fast exponential to an array of given length making
52 | * use of SIMD instructions if available. To enable vectorization
53 | * the code needs to be compiled with OpenMP support.
54 | *
55 | * \tparam Real The floating point type of the arguments.
56 | * \param x The array to which apply the exponential function.
57 | * \return n The number of elements in the array.
58 | */
59 | template
60 | <
61 | typename Real,
62 | template class Approximation = IEEE,
63 | size_t degree = 2
64 | >
65 | inline void exp(Real *x, size_t n) {
66 | // Vectorized part.
67 | #pragma omp simd
68 | for (size_t i = 0; i < n; ++i) {
69 | Real e = fastexp::exp(x[i]);
70 | x[i] = e;
71 | }
72 | }
73 |
74 | template
75 | <
76 | typename Real,
77 | template class Approximation = IEEE,
78 | size_t degree = 2
79 | >
80 | inline void exp(std::vector x) {
81 | // Vectorized part.
82 | size_t n = x.size();
83 | Real * x_ptr = &x[0];
84 |
85 | #pragma omp simd
86 | for (size_t i = 0; i < n; ++i) {
87 | Real e = fastexp::exp(x_ptr[i]);
88 | x_ptr[i] = e;
89 | }
90 | }
91 |
92 | } // fastexp
93 | #endif // FASTEXP_H
94 |
--------------------------------------------------------------------------------
/thirdparty/FastExp/product.h:
--------------------------------------------------------------------------------
1 | #ifndef FASTEXP_PRODUCT_H
2 | #define FASTEXP_PRODUCT_H
3 |
4 | #include "math.h"
5 | #include
6 |
7 | namespace fastexp {
8 |
9 | template
10 | struct Recursion {
11 | static Real evaluate(Real x) {
12 | constexpr Real c = 1.0 / static_cast(1u << degree);
13 | x = Recursion::evaluate(x);
14 | return x * x;
15 | }
16 | };
17 |
18 | template
19 | struct Recursion {
20 | static Real evaluate(Real x) {
21 | constexpr Real c = 1.0 / static_cast(1u << degree);
22 | x = 1.0 + c * x;
23 | return x;
24 | }
25 | };
26 |
27 | template
28 | struct Product {
29 | static Real evaluate(Real x) {
30 | return Recursion::evaluate(x);
31 | }
32 | };
33 |
34 |
35 | } // fastexp
36 | #endif // FASTEXP_PRODUCT_H
37 |
--------------------------------------------------------------------------------
/thirdparty/predicates/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10.0)
2 | project(PL_Predicates)
3 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -std=c++14")
4 |
5 | set(LIB_TYPE SHARED)
6 | set (PL_predicates_SRCS
7 | ${CMAKE_CURRENT_SOURCE_DIR}/constants.c
8 | ${CMAKE_CURRENT_SOURCE_DIR}/predicates.c
9 | ${CMAKE_CURRENT_SOURCE_DIR}/random.c
10 | ${CMAKE_CURRENT_SOURCE_DIR}/printing.c
11 | )
12 |
13 | set(PL_predicates_HDRS
14 | ${CMAKE_CURRENT_SOURCE_DIR}/constants.h
15 | ${CMAKE_CURRENT_SOURCE_DIR}/predicates.h
16 | ${CMAKE_CURRENT_SOURCE_DIR}/predicates.hpp
17 | ${CMAKE_CURRENT_SOURCE_DIR}/predicatesDLLExport.h
18 | )
19 |
20 | set(PL_predicates_header_dir ${CMAKE_CURRENT_SOURCE_DIR})
21 | add_library (PL_Predicates ${LIB_TYPE} ${PL_predicates_SRCS} ${PL_predicates_HDRS})
22 |
23 | target_include_directories(PL_Predicates PUBLIC
24 | $
25 | $
26 | PRIVATE src)
27 | # set_property(TARGET predicates PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
28 |
--------------------------------------------------------------------------------
/thirdparty/predicates/README.md:
--------------------------------------------------------------------------------
1 | All source code in this directory is from the github repository: https://github.com/danshapero/predicates
2 |
3 | Here is an excerpt of the repository README.md on licensing:
4 |
5 | This code has not been released under a well-known free software license such as the BSD License or the GNU Public License. Instead, Shewchuk released the original predicates code stating only that it was "Placed in the public domain". Lawrence Rosen, general counsel for the Open Source Initiative, has argued that public domain is not a license. I am not a lawyer, but if you're thinking of using this code for proprietary software you should consult a lawyer first. I do not own the copyright on this code and cannot grant a license to use it.
--------------------------------------------------------------------------------
/thirdparty/predicates/constants.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | /**
5 | * Different sources define the "machine epsilon" in one of two ways.
6 | * The C standard library header `float.h` defines DBL_EPSILON, such that
7 | * 1.0 + DBL_EPSILON > 1.0
8 | * in double-precision arithmetic, but such that
9 | * 1.0 + z = 1.0
10 | * for any `z < DBL_EPSILON`. Many other sources, included Shewchuk's original
11 | * code and the supporting paper, define the machine epsilon instead as the
12 | * largest floating-point number such that
13 | * 1.0 + epsilon = 1.0
14 | * The two are related by
15 | * epsilon = DBL_EPSILON / 2.
16 | */
17 | #define EPS (DBL_EPSILON / 2)
18 |
19 | const double epsilon = EPS;
20 | const double splitter = ((DBL_MANT_DIG + 1) >> 1) + 1.0;
21 |
22 | const double resulterrbound = (3.0 + 8.0 * EPS) * EPS;
23 |
24 | const double ccwerrboundA = (3.0 + 16.0 * EPS) * EPS;
25 | const double ccwerrboundB = (2.0 + 12.0 * EPS) * EPS;
26 | const double ccwerrboundC = (9.0 + 64.0 * EPS) * EPS * EPS;
27 | const double o3derrboundA = (7.0 + 56.0 * EPS) * EPS;
28 | const double o3derrboundB = (3.0 + 28.0 * EPS) * EPS;
29 | const double o3derrboundC = (26.0 + 288.0 * EPS) * EPS * EPS;
30 | const double iccerrboundA = (10.0 + 96.0 * EPS) * EPS;
31 | const double iccerrboundB = (4.0 + 48.0 * EPS) * EPS;
32 | const double iccerrboundC = (44.0 + 576.0 * EPS) * EPS * EPS;
33 | const double isperrboundA = (16.0 + 224.0 * EPS) * EPS;
34 | const double isperrboundB = (5.0 + 72.0 * EPS) * EPS;
35 | const double isperrboundC = (71.0 + 1408.0 * EPS) * EPS * EPS;
36 |
--------------------------------------------------------------------------------
/thirdparty/predicates/constants.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef CONSTANTS_H
3 | #define CONSTANTS_H
4 |
5 | extern const double epsilon;
6 | extern const double splitter;
7 |
8 | extern const double resulterrbound;
9 |
10 | extern const double ccwerrboundA;
11 | extern const double ccwerrboundB;
12 | extern const double ccwerrboundC;
13 |
14 | extern const double o3derrboundA;
15 | extern const double o3derrboundB;
16 | extern const double o3derrboundC;
17 |
18 | extern const double iccerrboundA;
19 | extern const double iccerrboundB;
20 | extern const double iccerrboundC;
21 |
22 | extern const double isperrboundA;
23 | extern const double isperrboundB;
24 | extern const double isperrboundC;
25 |
26 | #endif
27 |
--------------------------------------------------------------------------------
/thirdparty/predicates/predicates.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef PREDICATES_H
3 | #define PREDICATES_H
4 |
5 | #include
6 |
7 | #include "predicatesDLLExport.h"
8 |
9 | #ifdef __cplusplus
10 | extern "C"
11 | {
12 | namespace predicates {
13 | #endif
14 |
15 | /**
16 | * Robust, adaptive-precision geometric predicates
17 | */
18 |
19 | PREDICATES_EXPORT
20 | double orient2d(const double * pa, const double * pb, const double * pc);
21 |
22 | PREDICATES_EXPORT
23 | double orient3d(const double * pa,
24 | const double * pb,
25 | const double * pc,
26 | const double * pd);
27 |
28 | PREDICATES_EXPORT
29 | double incircle(const double * pa,
30 | const double * pb,
31 | const double * pc,
32 | const double * pd);
33 |
34 | PREDICATES_EXPORT
35 | double insphere(const double * pa,
36 | const double * pb,
37 | const double * pc,
38 | const double * pd,
39 | const double * pe);
40 |
41 |
42 | /**
43 | * Nonrobust, fast geometric predicates.
44 | */
45 |
46 | PREDICATES_EXPORT
47 | double orient2dfast(const double * pa, const double * pb, const double * pc);
48 |
49 | PREDICATES_EXPORT
50 | double orient3dfast(const double * pa,
51 | const double * pb,
52 | const double * pc,
53 | const double * pd);
54 |
55 | PREDICATES_EXPORT
56 | double incirclefast(const double * pa,
57 | const double * pb,
58 | const double * pc,
59 | const double * pd);
60 |
61 | PREDICATES_EXPORT
62 | double inspherefast(const double * pa,
63 | const double * pb,
64 | const double * pc,
65 | const double * pd,
66 | const double * pe);
67 |
68 | #ifdef __cplusplus
69 | } // end of namespace predicates
70 |
71 | } // end of extern "C"
72 | #endif
73 |
74 | #endif
75 |
--------------------------------------------------------------------------------
/thirdparty/predicates/predicates.hpp:
--------------------------------------------------------------------------------
1 |
2 | #ifndef PREDICATES_HPP
3 | #define PREDICATES_HPP
4 |
5 |
6 | #include
7 |
8 | #include "predicates.h"
9 |
10 | namespace predicates {
11 |
12 | template
13 | void perturb2d(
14 | const Predicate& predicate,
15 | const double * x0,
16 | const size_t nx,
17 | const size_t ny,
18 | Continuation& continuation
19 | )
20 | {
21 | double x[] = {x0[0], x0[1]};
22 |
23 | for (size_t i = 0; i < ny; ++i) {
24 | x[0] = x0[0];
25 |
26 | for (size_t j = 0; j < nx; ++j) {
27 | const double p = predicate(x);
28 | continuation(p, x, i, j);
29 |
30 | x[0] = nextafter(x[0], x[0] + 1);
31 | }
32 | x[1] = nextafter(x[1], x[1] + 1);
33 | }
34 | }
35 |
36 |
37 | } // end of namespace predicates
38 |
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/thirdparty/predicates/predicatesDLLExport.h:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ifndef _predicatesdllexport_h_
4 | #define _predicatesdllexport_h_
5 |
6 |
7 | #if defined (_MSC_VER)
8 | //#pragma warning(disable: 4267)
9 | //#pragma warning(disable: 4800) /* warning C4800: 'double' : forcing value to bool 'true' or 'false' */
10 | #endif
11 |
12 |
13 | /* Cmake will define predicates_EXPORTS on Windows when it
14 | configures to build a shared library. If you are going to use
15 | another build system on windows or create the visual studio
16 | projects by hand you need to define predicates_EXPORTS when
17 | building the predicates DLL on windows.
18 | */
19 |
20 |
21 | #if defined (predicates_EXPORTS) /* Compiling the predicates DLL/Dylib */
22 | #if defined (_MSC_VER) /* MSVC Compiler Case */
23 | #define PREDICATES_EXPORT __declspec(dllexport)
24 | #define EXPIMP_TEMPLATE
25 | #elif (__GNUC__ >= 4) /* GCC 4.x has support for visibility options */
26 | #define PREDICATES_EXPORT __attribute__ ((visibility("default")))
27 | #endif
28 | #else /* Importing the DLL into another project */
29 | #if defined (_MSC_VER) /* MSVC Compiler Case */
30 | #define PREDICATES_EXPORT __declspec(dllimport)
31 | #define EXPIMP_TEMPLATE extern
32 | #elif (__GNUC__ >= 4) /* GCC 4.x has support for visibility options */
33 | #define PREDICATES_EXPORT __attribute__ ((visibility("default")))
34 | #endif
35 | #endif
36 |
37 |
38 | /* If PREDICATES_EXPORT was never defined, define it here */
39 | #ifndef PREDICATES_EXPORT
40 | #define PREDICATES_EXPORT
41 | #define EXPIMP_TEMPLATE
42 | #endif
43 |
44 | #endif /* */
45 |
46 |
--------------------------------------------------------------------------------
/thirdparty/predicates/printing.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 |
5 | /**
6 | * Print the bit representation of a double.
7 | */
8 | void print_double(double number)
9 | {
10 | unsigned long long no;
11 | unsigned long long sign, expo;
12 | int exponent;
13 | int bottomi;
14 |
15 | no = *(unsigned long long *) &number;
16 | sign = no & 0x8000000000000000ll;
17 | expo = (no >> 52) & 0x7ffll;
18 | exponent = (int) expo;
19 | exponent = exponent - 1023;
20 |
21 | if (sign)
22 | printf("-");
23 | else
24 | printf(" ");
25 |
26 | if (exponent == -1023) {
27 | printf("0.0000000000000000000000000000000000000000000000000000_ ( )");
28 | } else {
29 | printf("1.");
30 | bottomi = -1;
31 | for (int i = 0; i < 52; i++) {
32 | if (no & 0x0008000000000000ll) {
33 | printf("1");
34 | bottomi = i;
35 | } else {
36 | printf("0");
37 | }
38 | no <<= 1;
39 | }
40 | printf("_%d (%d)", exponent, exponent - 1 - bottomi);
41 | }
42 | }
43 |
44 |
45 | /**
46 | * Print the bit representation of a float.
47 | */
48 | void print_float(float number)
49 | {
50 | unsigned no;
51 | unsigned sign, expo;
52 | int exponent;
53 | int bottomi;
54 |
55 | no = *(unsigned *) &number;
56 | sign = no & 0x80000000;
57 | expo = (no >> 23) & 0xff;
58 | exponent = (int) expo;
59 | exponent = exponent - 127;
60 | if (sign) {
61 | printf("-");
62 | } else {
63 | printf(" ");
64 | }
65 | if (exponent == -127) {
66 | printf("0.00000000000000000000000_ ( )");
67 | } else {
68 | printf("1.");
69 | bottomi = -1;
70 | for (int i = 0; i < 23; i++) {
71 | if (no & 0x00400000) {
72 | printf("1");
73 | bottomi = i;
74 | } else {
75 | printf("0");
76 | }
77 | no <<= 1;
78 | }
79 | printf("_%3d (%3d)", exponent, exponent - 1 - bottomi);
80 | }
81 | }
82 |
83 |
84 | /**
85 | * Print the bit representation of an expansion.
86 | */
87 | void print_expansion(size_t elen, double * e)
88 | {
89 | for (int i = elen - 1; i >= 0; i--) {
90 | print_double(e[i]);
91 | if (i > 0)
92 | printf(" +\n");
93 | else
94 | printf("\n");
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/thirdparty/predicates/random.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | /*****************************************************************************/
5 | /* */
6 | /* doublerand() Generate a double with random 53-bit significand and a */
7 | /* random exponent in [0, 511]. */
8 | /* */
9 | /*****************************************************************************/
10 |
11 | double doublerand()
12 | {
13 | int a = rand();
14 | int b = rand();
15 | int c = rand();
16 | double expo = 2;
17 | double result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
18 | for (int i = 512; i <= 131072; i *= 2, expo = expo * expo) {
19 | if (c & i) {
20 | result *= expo;
21 | }
22 | }
23 | return result;
24 | }
25 |
26 | /*****************************************************************************/
27 | /* */
28 | /* narrowdoublerand() Generate a double with random 53-bit significand */
29 | /* and a random exponent in [0, 7]. */
30 | /* */
31 | /*****************************************************************************/
32 |
33 | double narrowdoublerand()
34 | {
35 | int a = rand();
36 | int b = rand();
37 | int c = rand();
38 | double expo = 2;
39 | double result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
40 | for (int i = 512; i <= 2048; i *= 2, expo = expo * expo) {
41 | if (c & i) {
42 | result *= expo;
43 | }
44 | }
45 | return result;
46 | }
47 |
48 | /*****************************************************************************/
49 | /* */
50 | /* uniformdoublerand() Generate a double with random 53-bit significand. */
51 | /* */
52 | /*****************************************************************************/
53 |
54 | double uniformdoublerand()
55 | {
56 | int a = rand();
57 | int b = rand();
58 | double result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
59 | return result;
60 | }
61 |
62 | /*****************************************************************************/
63 | /* */
64 | /* floatrand() Generate a float with random 24-bit significand and a */
65 | /* random exponent in [0, 63]. */
66 | /* */
67 | /*****************************************************************************/
68 |
69 | float floatrand()
70 | {
71 | int a = rand();
72 | int c = rand();
73 | float expo = 2;
74 | float result = (float) ((a - 1073741824) >> 6);
75 | for (int i = 512; i <= 16384; i *= 2, expo = expo * expo) {
76 | if (c & i) {
77 | result *= expo;
78 | }
79 | }
80 | return result;
81 | }
82 |
83 | /*****************************************************************************/
84 | /* */
85 | /* narrowfloatrand() Generate a float with random 24-bit significand and */
86 | /* a random exponent in [0, 7]. */
87 | /* */
88 | /*****************************************************************************/
89 |
90 | float narrowfloatrand()
91 | {
92 | int a = rand();
93 | int c = rand();
94 | float expo = 2;
95 | float result = (float) ((a - 1073741824) >> 6);
96 | for (int i = 512; i <= 2048; i *= 2, expo = expo * expo) {
97 | if (c & i) {
98 | result *= expo;
99 | }
100 | }
101 | return result;
102 | }
103 |
104 | /*****************************************************************************/
105 | /* */
106 | /* uniformfloatrand() Generate a float with random 24-bit significand. */
107 | /* */
108 | /*****************************************************************************/
109 |
110 | float uniformfloatrand()
111 | {
112 | int a = rand();
113 | float result = (float) ((a - 1073741824) >> 6);
114 | return result;
115 | }
116 |
--------------------------------------------------------------------------------