openscad-cpp-evaluator 1.52.0__tar.gz → 1.52.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/CLAUDE.md +37 -4
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/offset3d.cpp +282 -41
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_offset3d.cpp +48 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/README.md +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/cmake/patch_manifold.cmake +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/LICENSE +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/.DS_Store +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/offset3d.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/reassign.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/alpha_wrap.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/export_as.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/import/scene_import.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/xkcd_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/xkcd_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_alpha_wrap.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_discretizer.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_export_as.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_openscad_parity.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_undef_args.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/third_party/predicates/predicates.c +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/third_party/predicates/predicates.h +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/uv.lock +0 -0
|
@@ -2066,6 +2066,9 @@ point verified against the hull's planes). Mitered pieces are trimmed by the shi
|
|
|
2066
2066
|
of the faces at their end vertices -- the ends of the whole straight edge (`chainEnds`), not of the
|
|
2067
2067
|
triangle edge, or `refine()`'s midpoints left a 0.6 spike -- and by far faces only within their
|
|
2068
2068
|
own convex patch (faces joined through convex or flat edges), which keeps separate parts apart.
|
|
2069
|
+
A straight convex edge made of several triangle edges gets **one** wedge: refine()'s halves each
|
|
2070
|
+
had their own, which met face to face at the midpoint and left a zero-width crack (genus 1 for 0),
|
|
2071
|
+
and Manifold later collapsed it with 0.27 of the volume.
|
|
2069
2072
|
|
|
2070
2073
|
**Robustness rules, each from a failure:**
|
|
2071
2074
|
- Pieces start `e = min(d, max(1e-5·d, 1e-9·scale))` behind the surface, so none shares a plane
|
|
@@ -2097,9 +2100,39 @@ own convex patch (faces joined through convex or flat edges), which keeps separa
|
|
|
2097
2100
|
out.
|
|
2098
2101
|
- `|d|` below `1e-7` of the part's size returns the input: double precision cannot place it.
|
|
2099
2102
|
|
|
2100
|
-
**
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2103
|
+
**Exact contact.** Where parts meet at precisely the offset distance, every contact joins (the
|
|
2104
|
+
growth by `1e-9·scale` above), so the result is the one just past contact. Do not check that
|
|
2105
|
+
against Manifold's own `MinkowskiSum`/`MinkowskiDifference` at the same distance: there it joins
|
|
2106
|
+
only some of the contacts (two meeting in two places came out genus 0, not 1) or leaves a
|
|
2107
|
+
zero-volume shell. Compare topology against the reference at `d + 1e-7` instead.
|
|
2108
|
+
|
|
2109
|
+
**Speed.**
|
|
2110
|
+
- Pieces are planned serially, since the wedge bookkeeping feeds the caps, and built as jobs on
|
|
2111
|
+
every core (`parallelFor`, plain `std::thread`s; Manifold's TBB nests inside them).
|
|
2112
|
+
- A convex flat face gets one slab, not one per triangle.
|
|
2113
|
+
- The lost check runs its colours in parallel.
|
|
2114
|
+
- **Outward `delta=` of a convex solid skips the union.** Its offset is convex, so it is the hull
|
|
2115
|
+
of the pieces. Only the corners go into the hull: vertices on three or more shifted planes
|
|
2116
|
+
whose normals span 3D. Manifold's hull of every piece vertex (11k-24k points) came out with two
|
|
2117
|
+
shells. The hull is used only if it has one shell, lies inside every shifted plane, and contains
|
|
2118
|
+
every piece vertex; otherwise the union runs.
|
|
2119
|
+
- Not for `chamfer=`: a convex solid's chamfered offset is not always convex (the hull was up to
|
|
2120
|
+
2% larger).
|
|
2121
|
+
- Union chunking (parallel sub-unions) was tried and gained nothing: `BatchBoolean` already runs
|
|
2122
|
+
in parallel.
|
|
2123
|
+
|
|
2124
|
+
Against 1.52.0, same machine and load (load average 7-10):
|
|
2125
|
+
|
|
2126
|
+
| Case | 1.52.0 | Now |
|
|
2127
|
+
|---|---|---|
|
|
2128
|
+
| sphere(10,32) `delta=1` | 2.2 s | 0.42 s |
|
|
2129
|
+
| cone256 `delta=1` | 5.1 s | 0.28 s |
|
|
2130
|
+
| sphere(5,48) `delta=1` | 4.9 s | 1.25 s |
|
|
2131
|
+
| sphere(10,32) `r=-1` | 1.57 s | 0.90 s |
|
|
2132
|
+
| sphere(10,32) `chamfer=1` | 0.74 s | 0.59 s |
|
|
2133
|
+
| sphere(10,32) `delta=-1` | 0.38 s | 0.36 s |
|
|
2134
|
+
|
|
2135
|
+
The union of a non-convex part's pieces is now the main cost. It is a tool for parts, not for
|
|
2136
|
+
dense scans. `OFFSET3D_TIMING=1` prints each phase's
|
|
2104
2137
|
wall time to stderr. `_offset3d(verts, tris, d, mode, segs)` in the Python facade is the test hook
|
|
2105
2138
|
the prototype's suites run against; not public API. Tests: `tests/test_offset3d.cpp`.
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
#include <cstdlib>
|
|
29
29
|
#include <string>
|
|
30
30
|
#include <array>
|
|
31
|
+
#include <atomic>
|
|
31
32
|
#include <cmath>
|
|
32
33
|
#include <functional>
|
|
33
34
|
#include <limits>
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
#include <numeric>
|
|
36
37
|
#include <optional>
|
|
37
38
|
#include <set>
|
|
39
|
+
#include <thread>
|
|
38
40
|
#include <unordered_map>
|
|
39
41
|
#include <vector>
|
|
40
42
|
|
|
@@ -352,7 +354,26 @@ struct Edge {
|
|
|
352
354
|
std::vector<int> ts;
|
|
353
355
|
};
|
|
354
356
|
|
|
357
|
+
// fn(0..n-1) on every core. ponytail: one thread per core over an atomic
|
|
358
|
+
// counter; Manifold's own TBB work nests inside, which TBB handles.
|
|
359
|
+
void parallelFor(size_t n, const std::function<void(size_t)>& fn) {
|
|
360
|
+
std::atomic<size_t> next{0};
|
|
361
|
+
auto worker = [&] {
|
|
362
|
+
for (size_t i; (i = next++) < n;) fn(i);
|
|
363
|
+
};
|
|
364
|
+
const unsigned k = unsigned(std::min<size_t>(std::max(1u, std::thread::hardware_concurrency()), n));
|
|
365
|
+
std::vector<std::thread> pool;
|
|
366
|
+
for (unsigned t = 1; t < k; ++t) pool.emplace_back(worker);
|
|
367
|
+
worker();
|
|
368
|
+
for (std::thread& t : pool) t.join();
|
|
369
|
+
}
|
|
370
|
+
|
|
355
371
|
class Builder {
|
|
372
|
+
// Each piece is a job, run in parallel by build(): the planning that
|
|
373
|
+
// decides what to build (and fills the wedge bookkeeping the caps read)
|
|
374
|
+
// stays serial, the hulls and trims run on every core.
|
|
375
|
+
using Jobs = std::vector<std::function<Manifold()>>;
|
|
376
|
+
|
|
356
377
|
public:
|
|
357
378
|
Builder(const std::vector<vec3>& V, std::vector<std::array<int, 3>> T, double d, Offset3DMode mode, int segs, bool flip)
|
|
358
379
|
: V_(V), T_(std::move(T)), d_(d), mode_(mode), ball_(sphereDirs(segs)) {
|
|
@@ -379,7 +400,21 @@ public:
|
|
|
379
400
|
}
|
|
380
401
|
|
|
381
402
|
std::vector<Manifold> build() {
|
|
382
|
-
|
|
403
|
+
Jobs out;
|
|
404
|
+
plan(out);
|
|
405
|
+
std::vector<Manifold> made(out.size());
|
|
406
|
+
parallelFor(out.size(), [&](size_t i) {
|
|
407
|
+
const Manifold m = out[i]();
|
|
408
|
+
if (!m.IsEmpty()) made[i] = bare(m);
|
|
409
|
+
});
|
|
410
|
+
std::vector<Manifold> res;
|
|
411
|
+
for (Manifold& m : made)
|
|
412
|
+
if (!m.IsEmpty()) res.push_back(std::move(m));
|
|
413
|
+
return res;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
private:
|
|
417
|
+
void plan(Jobs& out) {
|
|
383
418
|
if (mode_ == Offset3DMode::Round) {
|
|
384
419
|
// Each triangle swept by the one faceted ball: exactly the
|
|
385
420
|
// Minkowski sum with it.
|
|
@@ -388,19 +423,17 @@ public:
|
|
|
388
423
|
for (int k = 0; k < 3; ++k) pts.push_back(V_[T_[t][k]] - e_ * N_[t]);
|
|
389
424
|
for (int k = 0; k < 3; ++k)
|
|
390
425
|
for (const vec3& b : ball_) pts.push_back(V_[T_[t][k]] + d_ * b);
|
|
391
|
-
push(out, hullChecked(pts));
|
|
426
|
+
push(out, [pts] { return hullChecked(pts); });
|
|
392
427
|
}
|
|
393
|
-
return
|
|
428
|
+
return;
|
|
394
429
|
}
|
|
395
430
|
indexEdges();
|
|
396
|
-
slabs(out);
|
|
397
431
|
classify();
|
|
432
|
+
slabs(out);
|
|
398
433
|
wedges(out);
|
|
399
434
|
caps(out);
|
|
400
|
-
return out;
|
|
401
435
|
}
|
|
402
436
|
|
|
403
|
-
private:
|
|
404
437
|
const std::vector<vec3>& V_;
|
|
405
438
|
std::vector<std::array<int, 3>> T_;
|
|
406
439
|
double d_;
|
|
@@ -424,9 +457,7 @@ private:
|
|
|
424
457
|
std::map<int, std::vector<int>> members_;
|
|
425
458
|
std::map<int, bool> gone_;
|
|
426
459
|
|
|
427
|
-
static void push(std::
|
|
428
|
-
if (!m.IsEmpty()) out.push_back(bare(m));
|
|
429
|
-
}
|
|
460
|
+
static void push(Jobs& out, std::function<Manifold()> job) { out.push_back(std::move(job)); }
|
|
430
461
|
|
|
431
462
|
uint64_t key(int a, int b) const {
|
|
432
463
|
return (uint64_t(std::min(a, b)) << 32) | uint64_t(std::max(a, b));
|
|
@@ -470,8 +501,82 @@ private:
|
|
|
470
501
|
// Past a concave edge the sliver lands inside A outward, outside A
|
|
471
502
|
// inward, and past a flat one inside the neighbour's slab: within d of the
|
|
472
503
|
// surface every time. Convex edges have their wedges' strips.
|
|
473
|
-
|
|
504
|
+
// The boundary of a convex flat face, or nothing if the face is not convex
|
|
505
|
+
// (or has a hole): every face vertex on the inner side of every boundary
|
|
506
|
+
// edge. q gets the face's vertices, plus each non-convex boundary edge's
|
|
507
|
+
// ends pushed e outward, as the triangle slabs below widen them.
|
|
508
|
+
bool convexFace(int g, const std::vector<int>& ts, std::vector<vec3>& q) const {
|
|
509
|
+
const vec3 n = N_[ts[0]];
|
|
510
|
+
std::set<int> vs;
|
|
511
|
+
for (int t : ts)
|
|
512
|
+
for (int v : T_[t]) vs.insert(v);
|
|
513
|
+
std::vector<vec3> wide;
|
|
514
|
+
for (int t : ts)
|
|
515
|
+
for (int i = 0; i < 3; ++i) {
|
|
516
|
+
const int a = T_[t][i], b = T_[t][(i + 1) % 3];
|
|
517
|
+
const int u = across(t, a, b);
|
|
518
|
+
if (flat_[u] == g) continue;
|
|
519
|
+
const vec3 in = la::cross(n, V_[b] - V_[a]); // left of a->b: the face's side
|
|
520
|
+
const double tol = 1e-9 * scale_ * la::length(in);
|
|
521
|
+
for (int v : vs)
|
|
522
|
+
if (la::dot(in, V_[v] - V_[a]) < -tol) return false;
|
|
523
|
+
if (isConvex(t, u, a, b)) continue;
|
|
524
|
+
const vec3 k = -la::normalize(in);
|
|
525
|
+
wide.push_back(V_[a] + e_ * k);
|
|
526
|
+
wide.push_back(V_[b] + e_ * k);
|
|
527
|
+
}
|
|
528
|
+
std::vector<vec3> all;
|
|
529
|
+
for (int v : vs) all.push_back(V_[v]);
|
|
530
|
+
all.insert(all.end(), wide.begin(), wide.end());
|
|
531
|
+
q = corners2D(all, n);
|
|
532
|
+
return q.size() >= 3;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// The strict corners of coplanar points (normal n): a 2D hull in the
|
|
536
|
+
// plane, collinear points dropped. refine() leaves midpoints along every
|
|
537
|
+
// edge; Manifold's hull kept them as zero-area slivers (a 422-triangle
|
|
538
|
+
// slab), and the union with them lost 0.27 of t41 seed 18 once moved back.
|
|
539
|
+
std::vector<vec3> corners2D(const std::vector<vec3>& P, const vec3& n) const {
|
|
540
|
+
const vec3 u = la::normalize(std::fabs(n.x) < 0.9 ? la::cross(n, vec3(1, 0, 0)) : la::cross(n, vec3(0, 1, 0)));
|
|
541
|
+
const vec3 w = la::cross(n, u);
|
|
542
|
+
std::vector<std::pair<std::array<double, 2>, size_t>> pts;
|
|
543
|
+
for (size_t i = 0; i < P.size(); ++i) pts.push_back({{la::dot(P[i], u), la::dot(P[i], w)}, i});
|
|
544
|
+
std::sort(pts.begin(), pts.end());
|
|
545
|
+
const double tol = 1e-12 * scale_ * scale_;
|
|
546
|
+
auto cross = [](const std::array<double, 2>& o, const std::array<double, 2>& a, const std::array<double, 2>& b) {
|
|
547
|
+
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
|
|
548
|
+
};
|
|
549
|
+
std::vector<size_t> h(2 * pts.size());
|
|
550
|
+
size_t k = 0;
|
|
551
|
+
for (size_t i = 0; i < pts.size(); ++i) { // Andrew's monotone chain
|
|
552
|
+
while (k >= 2 && cross(pts[h[k - 2]].first, pts[h[k - 1]].first, pts[i].first) <= tol) --k;
|
|
553
|
+
h[k++] = i;
|
|
554
|
+
}
|
|
555
|
+
for (size_t i = pts.size() - 1, t = k + 1; i-- > 0;) {
|
|
556
|
+
while (k >= t && cross(pts[h[k - 2]].first, pts[h[k - 1]].first, pts[i].first) <= tol) --k;
|
|
557
|
+
h[k++] = i;
|
|
558
|
+
}
|
|
559
|
+
std::vector<vec3> out;
|
|
560
|
+
for (size_t i = 0; i + 1 < k; ++i) out.push_back(P[pts[h[i]].second]);
|
|
561
|
+
return out;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
void slabs(Jobs& out) {
|
|
565
|
+
// One slab per convex flat face: a fan of 254 slabs meeting at one
|
|
566
|
+
// vertex (a cylinder's end) made the union several times slower.
|
|
567
|
+
std::vector<char> done(T_.size(), 0);
|
|
568
|
+
for (const auto& [g, ts] : members_) {
|
|
569
|
+
std::vector<vec3> q;
|
|
570
|
+
if (ts.size() < 2 || !convexFace(g, ts, q)) continue;
|
|
571
|
+
for (int t : ts) done[t] = 1;
|
|
572
|
+
const vec3 n = N_[ts[0]];
|
|
573
|
+
std::vector<vec3> pts;
|
|
574
|
+
for (const vec3& p : q) pts.push_back(p - e_ * n);
|
|
575
|
+
for (const vec3& p : q) pts.push_back(p + d_ * n);
|
|
576
|
+
push(out, [pts] { return hullChecked(pts); });
|
|
577
|
+
}
|
|
474
578
|
for (size_t t = 0; t < T_.size(); ++t) {
|
|
579
|
+
if (done[t]) continue;
|
|
475
580
|
const vec3 n = N_[t];
|
|
476
581
|
std::vector<vec3> q;
|
|
477
582
|
for (int k = 0; k < 3; ++k) q.push_back(V_[T_[t][k]]);
|
|
@@ -487,7 +592,7 @@ private:
|
|
|
487
592
|
std::vector<vec3> pts;
|
|
488
593
|
for (const vec3& p : q) pts.push_back(p - e_ * n);
|
|
489
594
|
for (const vec3& p : q) pts.push_back(p + d_ * n);
|
|
490
|
-
push(out, hullChecked(pts));
|
|
595
|
+
push(out, [pts] { return hullChecked(pts); });
|
|
491
596
|
}
|
|
492
597
|
}
|
|
493
598
|
|
|
@@ -690,7 +795,12 @@ private:
|
|
|
690
795
|
return {walk(e.b, e.a), walk(e.a, e.b)};
|
|
691
796
|
}
|
|
692
797
|
|
|
693
|
-
|
|
798
|
+
// One wedge per straight convex edge, however many triangle edges it is
|
|
799
|
+
// made of: refine() split an edge in two, and the halves' wedges met face
|
|
800
|
+
// to face at the midpoint -- a zero-width crack (genus 1 for 0), which
|
|
801
|
+
// Manifold later collapsed and took 0.27 of t41 seed 18's volume with it.
|
|
802
|
+
void wedges(Jobs& out) {
|
|
803
|
+
std::map<std::pair<int, int>, std::vector<size_t>> chains;
|
|
694
804
|
for (size_t ei = 0; ei < edges_.size(); ++ei) {
|
|
695
805
|
const Edge& e = edges_[ei];
|
|
696
806
|
const int a = e.a, b = e.b, t1 = e.ts[0], t2 = e.ts[1];
|
|
@@ -701,38 +811,52 @@ private:
|
|
|
701
811
|
convEdgeOther_[a].push_back(b);
|
|
702
812
|
convEdgeNormals_[b].push_back({n1, n2});
|
|
703
813
|
convEdgeOther_[b].push_back(a);
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
814
|
+
const auto [ea, eb] = chainEnds(ei);
|
|
815
|
+
chains[{std::min(ea, eb), std::max(ea, eb)}].push_back(ei);
|
|
816
|
+
}
|
|
817
|
+
for (const auto& [ends, members] : chains) {
|
|
818
|
+
const auto [ea, eb] = ends;
|
|
819
|
+
const Edge& e0 = edges_[members[0]];
|
|
820
|
+
const vec3 n1 = N_[e0.ts[0]], n2 = N_[e0.ts[1]];
|
|
821
|
+
const vec3 pa = V_[ea], pb = V_[eb];
|
|
708
822
|
std::vector<vec3> pts{pa - e_ * (n1 + n2), pb - e_ * (n1 + n2), pa + d_ * n1, pb + d_ * n1,
|
|
709
823
|
pa + d_ * n2, pb + d_ * n2};
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
824
|
+
std::vector<int> tris;
|
|
825
|
+
// A sliver of each adjacent slab, so the wedge overlaps both:
|
|
826
|
+
// walls that touch exactly leave rounding-thin tunnels.
|
|
827
|
+
for (size_t ei : members) {
|
|
828
|
+
const Edge& e = edges_[ei];
|
|
829
|
+
for (int t : e.ts) {
|
|
830
|
+
tris.push_back(t);
|
|
831
|
+
const vec3 c = V_[otherVertex(t, e.a, e.b)], n = N_[t];
|
|
832
|
+
for (const vec3& p : {V_[e.a], V_[e.b]})
|
|
833
|
+
for (const vec3& h : {-e_ * n, d_ * n}) pts.push_back(toward(p, c, kLam * d_) + h);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
713
836
|
if (mode_ != Offset3DMode::Delta) {
|
|
714
|
-
push(out, hullChecked(pts));
|
|
837
|
+
push(out, [pts] { return hullChecked(pts); });
|
|
715
838
|
continue;
|
|
716
839
|
}
|
|
717
840
|
const vec3 m = miter(n1, n2);
|
|
718
841
|
pts.push_back(pa + d_ * m);
|
|
719
842
|
pts.push_back(pb + d_ * m);
|
|
720
|
-
Manifold w = hullChecked(pts);
|
|
721
843
|
// The miter must not run past any plane meeting at either end (a
|
|
722
844
|
// sharp edge's miter line would otherwise overshoot a third face
|
|
723
845
|
// that tilts over it).
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
846
|
+
push(out, [this, pts, ea, eb, tris] {
|
|
847
|
+
Manifold w = hullChecked(pts);
|
|
848
|
+
for (const int v : {ea, eb})
|
|
849
|
+
if (!concaveAt_[v]) {
|
|
850
|
+
std::vector<vec3> ns;
|
|
851
|
+
for (int t : vertFaces_[v]) ns.push_back(N_[t]);
|
|
852
|
+
w = clipPlanes(w, ns, V_[v], d_);
|
|
853
|
+
}
|
|
854
|
+
return clipFar(w, {V_[ea], V_[eb]}, pts, reach(tris));
|
|
855
|
+
});
|
|
732
856
|
}
|
|
733
857
|
}
|
|
734
858
|
|
|
735
|
-
void caps(
|
|
859
|
+
void caps(Jobs& out) {
|
|
736
860
|
for (size_t v = 0; v < V_.size(); ++v) {
|
|
737
861
|
const std::vector<int>& faces = vertFaces_[v];
|
|
738
862
|
if (faces.empty()) continue;
|
|
@@ -753,7 +877,7 @@ private:
|
|
|
753
877
|
for (int t : faces)
|
|
754
878
|
for (int c : T_[t])
|
|
755
879
|
if (c != int(v)) pts.push_back(toward(pv, V_[c], kLam * d_) + d_ * N_[t]);
|
|
756
|
-
push(out, hullChecked(pts));
|
|
880
|
+
push(out, [pts] { return hullChecked(pts); });
|
|
757
881
|
}
|
|
758
882
|
continue;
|
|
759
883
|
}
|
|
@@ -763,7 +887,7 @@ private:
|
|
|
763
887
|
// d of v is in any true mitered offset, so the ball is safe.
|
|
764
888
|
std::vector<vec3> pts;
|
|
765
889
|
for (const vec3& b : ball_) pts.push_back(pv + d_ * b);
|
|
766
|
-
push(out, hullChecked(pts));
|
|
890
|
+
push(out, [pts] { return hullChecked(pts); });
|
|
767
891
|
}
|
|
768
892
|
if (ns.size() < 3) continue; // the edge wedges already meet here
|
|
769
893
|
std::vector<vec3> pts{pv - e_ * sum};
|
|
@@ -777,7 +901,7 @@ private:
|
|
|
777
901
|
if (mode_ == Offset3DMode::Delta) pts.push_back(q + d_ * miter(n1, n2));
|
|
778
902
|
}
|
|
779
903
|
if (mode_ != Offset3DMode::Delta || concaveAt_[v]) {
|
|
780
|
-
push(out, hullChecked(pts));
|
|
904
|
+
push(out, [pts] { return hullChecked(pts); });
|
|
781
905
|
continue;
|
|
782
906
|
}
|
|
783
907
|
for (const vec3& x : cornerPoints(pv, ns, d_)) pts.push_back(x);
|
|
@@ -792,6 +916,7 @@ private:
|
|
|
792
916
|
} else {
|
|
793
917
|
for (const auto& nn : convEdgeNormals_[v]) pts.push_back(pv + d_ * miter(nn[0], nn[1]));
|
|
794
918
|
}
|
|
919
|
+
push(out, [this, pts, pv, fn, faces] {
|
|
795
920
|
Manifold cap = hullChecked(pts);
|
|
796
921
|
// The cap is the hull of a few points of the mitered corner, which
|
|
797
922
|
// need not hold the ball of radius d round v -- though the corner
|
|
@@ -812,7 +937,8 @@ private:
|
|
|
812
937
|
}
|
|
813
938
|
}
|
|
814
939
|
cap = clipPlanes(cap, fn, pv, d_);
|
|
815
|
-
|
|
940
|
+
return clipFar(cap, {pv}, pts, reach(faces));
|
|
941
|
+
});
|
|
816
942
|
}
|
|
817
943
|
}
|
|
818
944
|
};
|
|
@@ -914,23 +1040,27 @@ std::vector<size_t> lostPieces(const std::vector<Manifold>& ps, const Manifold&
|
|
|
914
1040
|
ncol = std::max(ncol, c + 1);
|
|
915
1041
|
grid[k].push_back(i);
|
|
916
1042
|
}
|
|
917
|
-
|
|
918
|
-
|
|
1043
|
+
// Each colour is its own check: all of them at once.
|
|
1044
|
+
std::vector<std::vector<size_t>> lostBy(ncol);
|
|
1045
|
+
parallelFor(size_t(ncol), [&](size_t c) {
|
|
919
1046
|
std::vector<Manifold> group;
|
|
920
1047
|
std::vector<size_t> who;
|
|
921
1048
|
for (size_t i = 0; i < n; ++i)
|
|
922
|
-
if (colour[i] == c) { group.push_back(ps[i]); who.push_back(i); }
|
|
1049
|
+
if (colour[i] == int(c)) { group.push_back(ps[i]); who.push_back(i); }
|
|
923
1050
|
const Manifold G = Manifold::Compose(group);
|
|
924
1051
|
const Manifold R = wantInside ? G - u : G ^ u;
|
|
925
|
-
if (R.IsEmpty())
|
|
1052
|
+
if (R.IsEmpty()) return;
|
|
926
1053
|
for (const Manifold& comp : R.Decompose()) {
|
|
927
1054
|
const double v = std::fabs(comp.Volume()), a = comp.SurfaceArea();
|
|
928
1055
|
if (!(a > 0) || 2 * v / a <= 10 * e) continue;
|
|
929
1056
|
const manifold::Box cb = comp.BoundingBox();
|
|
930
1057
|
for (size_t i : who)
|
|
931
|
-
if (la::all(la::lequal(box[i].min, cb.max)) && la::all(la::lequal(cb.min, box[i].max)))
|
|
1058
|
+
if (la::all(la::lequal(box[i].min, cb.max)) && la::all(la::lequal(cb.min, box[i].max)))
|
|
1059
|
+
lostBy[c].push_back(i);
|
|
932
1060
|
}
|
|
933
|
-
}
|
|
1061
|
+
});
|
|
1062
|
+
std::set<size_t> lost;
|
|
1063
|
+
for (const auto& l : lostBy) lost.insert(l.begin(), l.end());
|
|
934
1064
|
return {lost.begin(), lost.end()};
|
|
935
1065
|
}
|
|
936
1066
|
|
|
@@ -1030,6 +1160,39 @@ Manifold cleanShells(const Manifold& out, const Manifold& A, const std::function
|
|
|
1030
1160
|
return holes.empty() ? res : res - unionPieces(holes);
|
|
1031
1161
|
}
|
|
1032
1162
|
|
|
1163
|
+
// One closed, connected surface with no concave edge: a convex solid (a
|
|
1164
|
+
// locally convex closed surface bounds a convex body).
|
|
1165
|
+
bool convexSolid(const Mesh& mesh) {
|
|
1166
|
+
const size_t nt = mesh.T.size();
|
|
1167
|
+
if (nt < 4) return false;
|
|
1168
|
+
std::vector<vec3> N(nt);
|
|
1169
|
+
for (size_t t = 0; t < nt; ++t) {
|
|
1170
|
+
const auto& f = mesh.T[t];
|
|
1171
|
+
N[t] = la::normalize(la::cross(mesh.V[f[1]] - mesh.V[f[0]], mesh.V[f[2]] - mesh.V[f[0]]));
|
|
1172
|
+
}
|
|
1173
|
+
std::unordered_map<uint64_t, std::vector<int>> edges;
|
|
1174
|
+
for (size_t t = 0; t < nt; ++t)
|
|
1175
|
+
for (int i = 0; i < 3; ++i) {
|
|
1176
|
+
const int a = mesh.T[t][i], b = mesh.T[t][(i + 1) % 3];
|
|
1177
|
+
edges[(uint64_t(std::min(a, b)) << 32) | uint64_t(std::max(a, b))].push_back(int(t));
|
|
1178
|
+
}
|
|
1179
|
+
std::vector<int> root(nt);
|
|
1180
|
+
std::iota(root.begin(), root.end(), 0);
|
|
1181
|
+
std::function<int(int)> find = [&](int i) { return root[i] == i ? i : root[i] = find(root[i]); };
|
|
1182
|
+
const double tol = 1e-9 * std::max(1.0, maxAbs(mesh.V));
|
|
1183
|
+
for (const auto& [k, ts] : edges) {
|
|
1184
|
+
if (ts.size() != 2) return false;
|
|
1185
|
+
const int a = int(k >> 32), t1 = ts[0], t2 = ts[1];
|
|
1186
|
+
root[find(t1)] = find(t2);
|
|
1187
|
+
if (la::dot(N[t1], N[t2]) > kFlat) continue;
|
|
1188
|
+
for (int v : mesh.T[t2])
|
|
1189
|
+
if (la::dot(N[t1], mesh.V[v] - mesh.V[a]) > tol) return false; // concave
|
|
1190
|
+
}
|
|
1191
|
+
for (size_t t = 0; t < nt; ++t)
|
|
1192
|
+
if (find(int(t)) != find(0)) return false;
|
|
1193
|
+
return true;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1033
1196
|
Manifold offsetCentered(const Manifold& m, double d, Offset3DMode mode, int segs) {
|
|
1034
1197
|
const manifold::Box bb = m.BoundingBox();
|
|
1035
1198
|
const vec3 size = bb.max - bb.min;
|
|
@@ -1061,7 +1224,85 @@ Manifold offsetCentered(const Manifold& m, double d, Offset3DMode mode, int segs
|
|
|
1061
1224
|
const double mu = 1e-9 * std::max(1.0, maxAbs(mesh.V));
|
|
1062
1225
|
ps = Builder(mesh.V, mesh.T, std::fabs(d) + mu, mode, segs, d < 0).build();
|
|
1063
1226
|
timer.lap("pieces");
|
|
1064
|
-
|
|
1227
|
+
if (d > 0 && mode == Offset3DMode::Delta && convexSolid(mesh)) {
|
|
1228
|
+
// The mitered offset of a convex solid is convex, so it is the
|
|
1229
|
+
// hull of the pieces that fill it: one hull, not a union of
|
|
1230
|
+
// hundreds of near-coplanar wedges (a 256-sided cone: 4.4 s).
|
|
1231
|
+
// Not chamfer=: its offset of a convex solid need not be convex.
|
|
1232
|
+
const double tol = 1e-9 * std::max(1.0, maxAbs(mesh.V)), D = d + mu;
|
|
1233
|
+
std::vector<vec3> fn;
|
|
1234
|
+
std::vector<double> fc;
|
|
1235
|
+
for (const auto& f : mesh.T) {
|
|
1236
|
+
const vec3 n = la::normalize(la::cross(mesh.V[f[1]] - mesh.V[f[0]], mesh.V[f[2]] - mesh.V[f[0]]));
|
|
1237
|
+
fn.push_back(n);
|
|
1238
|
+
fc.push_back(la::dot(n, mesh.V[f[0]]) + D);
|
|
1239
|
+
}
|
|
1240
|
+
// The pieces' vertices, near-duplicates (shared corners, ~1e-9 apart)
|
|
1241
|
+
// merged to one exact point each rather than snapped to a grid.
|
|
1242
|
+
std::map<std::array<long long, 3>, vec3> near;
|
|
1243
|
+
const double q = 10 * tol;
|
|
1244
|
+
for (const Manifold& p : ps)
|
|
1245
|
+
for (const vec3& v : meshOf(p).V)
|
|
1246
|
+
near.emplace(std::array<long long, 3>{std::llround(v.x / q), std::llround(v.y / q), std::llround(v.z / q)}, v);
|
|
1247
|
+
// Only the offset's corners go in: a vertex of an intersection of
|
|
1248
|
+
// half-spaces lies on three or more planes whose normals span 3D.
|
|
1249
|
+
// Manifold's hull of every piece vertex (11k-24k of them, most on a
|
|
1250
|
+
// face's plane) came out with two shells, or points 3e-4 outside.
|
|
1251
|
+
std::vector<vec3> all;
|
|
1252
|
+
for (const auto& kv : near) all.push_back(kv.second);
|
|
1253
|
+
std::vector<char> corner(all.size(), 0);
|
|
1254
|
+
parallelFor(all.size(), [&](size_t a) {
|
|
1255
|
+
const vec3 v = all[a];
|
|
1256
|
+
std::vector<vec3> on;
|
|
1257
|
+
bool inside = true;
|
|
1258
|
+
for (size_t j = 0; j < fn.size() && inside; ++j) {
|
|
1259
|
+
const double w = la::dot(fn[j], v) - fc[j];
|
|
1260
|
+
if (w > 10 * tol) inside = false;
|
|
1261
|
+
else if (w > -10 * tol) on.push_back(fn[j]);
|
|
1262
|
+
}
|
|
1263
|
+
if (!inside || on.size() < 3) return;
|
|
1264
|
+
bool span = false;
|
|
1265
|
+
for (size_t i = 1; i < on.size() && !span; ++i) {
|
|
1266
|
+
const vec3 c = la::cross(on[0], on[i]);
|
|
1267
|
+
if (la::length(c) < 1e-9) continue;
|
|
1268
|
+
for (size_t j = i + 1; j < on.size() && !span; ++j)
|
|
1269
|
+
span = std::fabs(la::dot(c, on[j])) > 1e-9;
|
|
1270
|
+
}
|
|
1271
|
+
corner[a] = span;
|
|
1272
|
+
});
|
|
1273
|
+
std::vector<vec3> pts;
|
|
1274
|
+
for (size_t a = 0; a < all.size(); ++a)
|
|
1275
|
+
if (corner[a]) pts.push_back(all[a]);
|
|
1276
|
+
// Trusted only if provably the offset: one shell, inside every
|
|
1277
|
+
// shifted plane (so within the offset), and holding every piece
|
|
1278
|
+
// vertex (so every piece, and their union). Otherwise the union
|
|
1279
|
+
// runs as for any other input.
|
|
1280
|
+
Manifold h = pts.size() >= 4 ? whitenedHull(pts) : Manifold();
|
|
1281
|
+
bool good = !h.IsEmpty() && h.Status() == Manifold::Error::NoError && h.Genus() == 0 &&
|
|
1282
|
+
h.Decompose().size() == 1;
|
|
1283
|
+
if (good) {
|
|
1284
|
+
const Planes pl = planesOf(h);
|
|
1285
|
+
std::atomic<bool> ok{true};
|
|
1286
|
+
parallelFor(all.size(), [&](size_t a) {
|
|
1287
|
+
for (size_t j = 0; ok && j < pl.n.size(); ++j)
|
|
1288
|
+
if (la::dot(all[a], pl.n[j]) > pl.c[j] + 10 * tol) ok = false;
|
|
1289
|
+
});
|
|
1290
|
+
const std::vector<vec3> hv = meshOf(h).V;
|
|
1291
|
+
parallelFor(hv.size(), [&](size_t a) {
|
|
1292
|
+
for (size_t j = 0; ok && j < fn.size(); ++j)
|
|
1293
|
+
if (la::dot(fn[j], hv[a]) > fc[j] + 10 * tol) ok = false;
|
|
1294
|
+
});
|
|
1295
|
+
good = ok;
|
|
1296
|
+
}
|
|
1297
|
+
if (good) {
|
|
1298
|
+
ps.clear(); // nothing for lostPieces() to check: no union ran
|
|
1299
|
+
tools = h;
|
|
1300
|
+
} else {
|
|
1301
|
+
tools = unionPieces(ps);
|
|
1302
|
+
}
|
|
1303
|
+
} else {
|
|
1304
|
+
tools = unionPieces(ps);
|
|
1305
|
+
}
|
|
1065
1306
|
timer.lap("union");
|
|
1066
1307
|
out = d > 0 ? m + tools : m - tools;
|
|
1067
1308
|
out.NumTri();
|
|
@@ -63,6 +63,13 @@ TEST(Offset3D, CubeEveryModeBothWays) {
|
|
|
63
63
|
EXPECT_GT(r, exact * 0.99);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
TEST(Offset3D, ConvexMiteredOffsetIsExact) {
|
|
67
|
+
// A convex solid's delta= offset is built as one hull: a 64-sided prism
|
|
68
|
+
// grows to the 64-gon whose apothem is one more, extruded 2 taller.
|
|
69
|
+
const double n = 64, a = 5 * std::cos(M_PI / n) + 1;
|
|
70
|
+
EXPECT_VOL("offset(delta=1) cylinder(h=10, r=5, $fn=64);", 12 * n * a * a * std::tan(M_PI / n));
|
|
71
|
+
}
|
|
72
|
+
|
|
66
73
|
TEST(Offset3D, ThinPlateVanishesInward) {
|
|
67
74
|
EXPECT_NEAR(vol("offset(delta=-1) cube([10, 10, 1.5]);"), 0, 1e-9);
|
|
68
75
|
EXPECT_NEAR(vol("offset(r=-1) cube([10, 10, 1.5]);"), 0, 1e-9);
|
|
@@ -107,6 +114,47 @@ TEST(Offset3D, TopologyChangesAreKept) {
|
|
|
107
114
|
EXPECT_EQ(solid("offset(delta=-1) " + U).Decompose().size(), 1u);
|
|
108
115
|
}
|
|
109
116
|
|
|
117
|
+
TEST(Offset3D, TouchingAtExactlyTheDistanceJoinsEveryContact) {
|
|
118
|
+
// Grown by 0.5 with chamfers, two parts of this block meet in two places at
|
|
119
|
+
// once: genus -1 (two shells) just short of 0.5, genus 1 just past it. At
|
|
120
|
+
// exactly 0.5 touching counts as joined, at both contacts -- Manifold's own
|
|
121
|
+
// Minkowski sum joins only one of them there (genus 0).
|
|
122
|
+
const std::string P =
|
|
123
|
+
"difference() { difference() { difference() { union() { union() { union() { cube(8);"
|
|
124
|
+
" translate([2, -2, -2]) cube([5, 1, 4]); } translate([3, 3, -2]) cube([5, 4, 6]); }"
|
|
125
|
+
" translate([6, 6, -1]) cube([2, 6, 4]); } translate([2, 7, -1]) cube([5, 6, 6]); }"
|
|
126
|
+
" translate([4, 2, 5]) cube([3, 1, 3]); } translate([2, -1, -2]) cube([2, 6, 6]); }";
|
|
127
|
+
const Manifold o = solid("offset(delta=0.5, chamfer=true) " + P);
|
|
128
|
+
EXPECT_EQ(o.Decompose().size(), 1u);
|
|
129
|
+
EXPECT_EQ(o.Genus(), 1);
|
|
130
|
+
EXPECT_NEAR(o.Volume(), 818.75, 1e-6 * 818.75);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
TEST(Offset3D, SplitEdgesGiveTheSameOffset) {
|
|
134
|
+
// A 10-cube whose faces are each a 2x2 grid: every edge is two triangle
|
|
135
|
+
// edges and every face eight triangles. Its offset is the plain cube's.
|
|
136
|
+
// (The halves of a split edge once got a wedge each, meeting face to face;
|
|
137
|
+
// on t41 seed 18's refine(2) that left a zero-width crack, genus 1, that
|
|
138
|
+
// cost 0.27 of volume. This cube came out right even then: it guards the
|
|
139
|
+
// one-wedge-per-straight-edge path, it does not reproduce that crack.)
|
|
140
|
+
const std::string grid =
|
|
141
|
+
"polyhedron([[0,0,0],[0,0,5],[0,0,10],[0,5,0],[0,5,5],[0,5,10],[0,10,0],[0,10,5],[0,10,10],[5,0,0"
|
|
142
|
+
"],[5,0,5],[5,0,10],[5,5,0],[5,5,10],[5,10,0],[5,10,5],[5,10,10],[10,0,0],[10,0,5],[10,0,10],[10,"
|
|
143
|
+
"5,0],[10,5,5],[10,5,10],[10,10,0],[10,10,5],[10,10,10]], [[0,3,4],[0,4,1],[1,4,5],[1,5,2],[3,6,7"
|
|
144
|
+
"],[3,7,4],[4,7,8],[4,8,5],[18,21,20],[18,20,17],[19,22,21],[19,21,18],[21,24,23],[21,23,20],[22,"
|
|
145
|
+
"25,24],[22,24,21],[1,10,9],[1,9,0],[2,11,10],[2,10,1],[10,18,17],[10,17,9],[11,19,18],[11,18,10]"
|
|
146
|
+
",[6,14,15],[6,15,7],[7,15,16],[7,16,8],[14,23,24],[14,24,15],[15,24,25],[15,25,16],[0,9,12],[0,1"
|
|
147
|
+
"2,3],[3,12,14],[3,14,6],[9,17,20],[9,20,12],[12,20,23],[12,23,14],[5,13,11],[5,11,2],[8,16,13],["
|
|
148
|
+
"8,13,5],[13,22,19],[13,19,11],[16,25,22],[16,22,13]]);";
|
|
149
|
+
ASSERT_NEAR(vol(grid), 1000, 1e-9);
|
|
150
|
+
for (const std::string& mode : {"delta=1", "delta=1, chamfer=true", "delta=-1", "delta=-1, chamfer=true"}) {
|
|
151
|
+
const Manifold a = solid("offset(" + mode + ") cube(10);"), b = solid("offset(" + mode + ") " + grid);
|
|
152
|
+
EXPECT_NEAR(b.Volume(), a.Volume(), 1e-6 * a.Volume()) << mode;
|
|
153
|
+
EXPECT_EQ(b.Genus(), 0) << mode;
|
|
154
|
+
EXPECT_EQ(b.Decompose().size(), 1u) << mode;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
110
158
|
TEST(Offset3D, FarFromTheOriginIsTheSame) {
|
|
111
159
|
// The offset runs at the origin: Manifold's union lost 0.35 of this
|
|
112
160
|
// volume at 1e5 from it.
|
|
File without changes
|
{openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/.github/workflows/wheels.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/examples/eval_perf_benchmark.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/examples/manifold_cache_reuse.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-1.52.0 → openscad_cpp_evaluator-1.52.1}/examples/minimal_debugger.cpp
RENAMED
|
File without changes
|