openscad-cpp-evaluator 1.23.1__tar.gz → 1.25.0__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.23.1 → openscad_cpp_evaluator-1.25.0}/CLAUDE.md +34 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/evaluator.hpp +12 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +17 -3
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/mesh_check.hpp +13 -3
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/CMakeLists.txt +34 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/builtins.hpp +4 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/function_builtins.cpp +1 -32
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/import.cpp +23 -2
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/primitives_3d.cpp +57 -6
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/registry.cpp +4 -1
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/topology.cpp +112 -12
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/csg_generate.cpp +33 -3
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/expr_eval.cpp +7 -3
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/manifold_cache.cpp +3 -3
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/mesh_check.cpp +11 -6
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/value.cpp +0 -30
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/CMakeLists.txt +4 -1
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_booleans.cpp +96 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_csg_tree.cpp +70 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_manifold_cache.cpp +63 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/uv.lock +0 -0
|
@@ -946,6 +946,40 @@ grep for `ponytail:`.
|
|
|
946
946
|
shut. Note the reduction is NOT monotonic in the tolerance (1.0 gave *more* triangles than 0.5 on
|
|
947
947
|
the test model, since simplification can force re-triangulation), so don't read a jump as a bug.
|
|
948
948
|
|
|
949
|
+
- `mesh_repair()` (also `topology.cpp`) — makes a broken mesh watertight, so geometry that was
|
|
950
|
+
merely *almost* closed can take part in CSG. Another BelfrySCAD extension: nothing in the language
|
|
951
|
+
could repair geometry, and a mesh from a library generator or an import is routinely a few
|
|
952
|
+
boundary edges short of a solid. The work is `repairMesh()`'s, already used by
|
|
953
|
+
`import(..., repair=true)`; this exposes the same six steps to any geometry — weld, drop
|
|
954
|
+
degenerate/duplicate, orient, fill holes, flip outward, strip zero-area faces.
|
|
955
|
+
|
|
956
|
+
**It reads `ColoredBody::rawMesh`, not `body`.** An open `polyhedron()` cannot build a Manifold,
|
|
957
|
+
so it keeps the raw triangle soup there and leaves `body` set but EMPTY. Repairing the empty
|
|
958
|
+
Manifold finds nothing to do and reports success at having done nothing, which is how the first
|
|
959
|
+
version of this failed silently. On success the soup is cleared, or `splitByRole()` keeps pulling
|
|
960
|
+
the body aside as un-CSG-able — the very thing the repair just fixed.
|
|
961
|
+
|
|
962
|
+
**The default tolerance is `GRID_FINE` (2⁻²⁰ ≈ 9.54e-7)**, OpenSCAD's own constant for deciding
|
|
963
|
+
two points are one, so a mesh that welds there welds here; it is exact in binary, so the
|
|
964
|
+
multiply-and-round in `weldMap()` adds no error. Deliberately tight, unlike `simplify()`'s
|
|
965
|
+
scale-relative default, because welding DISCARDS vertices: a default that quietly merged real
|
|
966
|
+
detail would be worse than one that leaves a mesh open, and hole filling closes what welding
|
|
967
|
+
declines to. `import(..., repair=true)` takes the same `tolerance=` argument for the same reason.
|
|
968
|
+
|
|
969
|
+
**Watertight is not the same as clean**, and the tolerance chooses. A gap whose rim is two
|
|
970
|
+
vertices that should be one closes either by welding (the duplicate disappears, no new geometry)
|
|
971
|
+
or by filling (the pair survives and step 4 stitches a triangle across). The filled patch is a
|
|
972
|
+
*needle* — two corners a hair apart — and step 6 will not remove it, because it strips
|
|
973
|
+
genuinely zero-area faces and a needle across a 2e-5 gap with its third corner 0.25 away has an
|
|
974
|
+
area around 2.5e-6. Thin, not degenerate. So a repaired-but-ugly mesh means the tolerance was too
|
|
975
|
+
small; the `polyhedron()` warning names a boundary edge's endpoints, so measure the gap and pass
|
|
976
|
+
a little more. A boundary loop of fewer than three edges — a crack, not a hole — has no fan to
|
|
977
|
+
build at all and is reported as left open; only a coarser weld closes that.
|
|
978
|
+
|
|
979
|
+
Not a substitute for a generator that emits a closed mesh: repair costs a full pass over the
|
|
980
|
+
geometry every render. Prompted by BelfrySCAD #521, where BOSL2's `isosurface()` computed a shared
|
|
981
|
+
voxel corner once per voxel and let the results drift 2e-5 apart.
|
|
982
|
+
|
|
949
983
|
- `src/builtins/dxf_dim.cpp` — `dxf_dim()`/`dxf_cross()`, which read a measurement out of a DXF file
|
|
950
984
|
rather than out of the model. Ported from the reference's `io/dxfdim.cc` plus the DIMENSION/LINE
|
|
951
985
|
half of `io/DxfData.cc`: `dxf_dim()` returns the first matching DIMENSION's value by its type
|
|
@@ -398,6 +398,14 @@ public:
|
|
|
398
398
|
// than anything reentrancy-aware, since generateTreeImpl recurses
|
|
399
399
|
// depth-first on one thread.
|
|
400
400
|
const oscad::Position* generateWarnEntry = nullptr;
|
|
401
|
+
// Non-null while generateTreeImpl() is generating a subtree whose result
|
|
402
|
+
// it intends to cache: warn() appends each formatted line here as well as
|
|
403
|
+
// emitting it, so the entry can replay them on a later hit. Saved and
|
|
404
|
+
// restored around each node rather than left set, and the inner vector is
|
|
405
|
+
// merged into the outer one on the way out, so a parent's entry carries
|
|
406
|
+
// everything its whole subtree said -- which is what a parent-level hit
|
|
407
|
+
// has to reproduce, since it skips all of it. See ManifoldCache.
|
|
408
|
+
std::vector<std::string>* warnCapture = nullptr;
|
|
401
409
|
// The same republish for CSGNode::callChain, read by tagGenerated to
|
|
402
410
|
// fill idToCallChain.
|
|
403
411
|
uint32_t generateCallChain = kNoCallChain;
|
|
@@ -530,6 +538,10 @@ public:
|
|
|
530
538
|
// Public: builtins/import.cpp's not-manifold warning is emitted from a
|
|
531
539
|
// free function, same reasoning as tagGenerated()/builtinChildren().
|
|
532
540
|
void warn(const std::string& message, const oscad::Position* position);
|
|
541
|
+
// Hands one already-formatted warning line to echoFn_, recording it in
|
|
542
|
+
// warnCapture first when one is active. Every warning path goes through
|
|
543
|
+
// here, including a ManifoldCache hit replaying a cached line.
|
|
544
|
+
void emitWarning(const std::string& formatted);
|
|
533
545
|
|
|
534
546
|
// Applies OpenSCAD's mixed-2D/3D rules to a node's children just
|
|
535
547
|
// before its GenerateFn runs -- see csg_generate.cpp.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include "openscad_cpp_evaluator/csg_node.hpp"
|
|
4
4
|
|
|
5
5
|
#include <mutex>
|
|
6
|
+
#include <vector>
|
|
6
7
|
#include <optional>
|
|
7
8
|
#include <string>
|
|
8
9
|
#include <unordered_map>
|
|
@@ -22,15 +23,28 @@ namespace oscadeval {
|
|
|
22
23
|
// threading.Lock -- a host may run renders/evaluates on a background
|
|
23
24
|
// thread while another operation reads/clears the same cache). Mirrors
|
|
24
25
|
// the reference's ManifoldCache class.
|
|
26
|
+
// What one cache entry holds. The warnings matter as much as the bodies:
|
|
27
|
+
// a hit skips the whole subtree's GenerateFn, and every generate-time
|
|
28
|
+
// diagnostic is raised *inside* one of those (polyhedron's "mesh is not
|
|
29
|
+
// closed", import()'s failures, the dimension rules). Cached without them,
|
|
30
|
+
// a second render of unchanged source silently dropped every warning the
|
|
31
|
+
// first one raised, so a real defect looked intermittent and the author
|
|
32
|
+
// went hunting for a phantom. A cache has to be transparent; that includes
|
|
33
|
+
// what it says, not only what it draws. See BelfrySCAD issue #521.
|
|
34
|
+
struct CachedSubtree {
|
|
35
|
+
std::vector<ColoredBody> bodies;
|
|
36
|
+
std::vector<std::string> warnings; // already formatted, replayed verbatim
|
|
37
|
+
};
|
|
38
|
+
|
|
25
39
|
class ManifoldCache {
|
|
26
40
|
public:
|
|
27
|
-
std::optional<
|
|
28
|
-
void put(std::string key, std::vector<ColoredBody> bodies);
|
|
41
|
+
std::optional<CachedSubtree> get(const std::string& key) const;
|
|
42
|
+
void put(std::string key, std::vector<ColoredBody> bodies, std::vector<std::string> warnings);
|
|
29
43
|
void clear();
|
|
30
44
|
|
|
31
45
|
private:
|
|
32
46
|
mutable std::mutex mutex_;
|
|
33
|
-
std::unordered_map<std::string,
|
|
47
|
+
std::unordered_map<std::string, CachedSubtree> entries_;
|
|
34
48
|
};
|
|
35
49
|
|
|
36
50
|
// Structural content-hash key for `node`, used by generateTree()'s
|
|
@@ -82,6 +82,15 @@ struct MeshRepairReport {
|
|
|
82
82
|
std::string summary() const;
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
+
//: Default welding tolerance: OpenSCAD's own GRID_FINE, 2^-20. Theirs is
|
|
86
|
+
//: the constant their Grid.h uses to decide two points are one, so a mesh
|
|
87
|
+
//: that welds there welds here. Exact in binary, so the multiply-and-round
|
|
88
|
+
//: in weldMap() adds no error of its own. Deliberately tight: welding
|
|
89
|
+
//: DISCARDS vertices, and a default that quietly merged real detail would
|
|
90
|
+
//: be worse than one that leaves a mesh open -- hole filling closes what
|
|
91
|
+
//: welding declines to. See issue #190.
|
|
92
|
+
constexpr double kDefaultWeldTolerance = 0.00000095367431640625;
|
|
93
|
+
|
|
85
94
|
// Best-effort repair, in the only order that works: welding first (it
|
|
86
95
|
// changes which edges are shared, so every later test depends on it), then
|
|
87
96
|
// degenerate and duplicate removal, then orientation, then hole filling
|
|
@@ -91,9 +100,10 @@ struct MeshRepairReport {
|
|
|
91
100
|
// Returns the repaired mesh. A mesh it cannot close is still returned,
|
|
92
101
|
// improved as far as it got, with the remainder reported in `report`.
|
|
93
102
|
template <typename M>
|
|
94
|
-
M repairMesh(const M& mesh, MeshRepairReport& report
|
|
95
|
-
|
|
96
|
-
extern template manifold::
|
|
103
|
+
M repairMesh(const M& mesh, MeshRepairReport& report,
|
|
104
|
+
double tolerance = kDefaultWeldTolerance);
|
|
105
|
+
extern template manifold::MeshGL repairMesh<manifold::MeshGL>(const manifold::MeshGL&, MeshRepairReport&, double);
|
|
106
|
+
extern template manifold::MeshGL64 repairMesh<manifold::MeshGL64>(const manifold::MeshGL64&, MeshRepairReport&, double);
|
|
97
107
|
|
|
98
108
|
struct SliverStripReport {
|
|
99
109
|
size_t removed = 0; // zero-area faces taken out
|
|
@@ -69,3 +69,37 @@ target_link_libraries(openscad_cpp_evaluator PRIVATE linenoise freetype harfbuzz
|
|
|
69
69
|
target_compile_options(openscad_cpp_evaluator PRIVATE
|
|
70
70
|
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall;-Wextra>
|
|
71
71
|
)
|
|
72
|
+
|
|
73
|
+
# -Wall -Wextra above is about OUR code, but two TUs pull a vendored header
|
|
74
|
+
# in wholesale and inherit its warnings: 27 from Boost.Polygon's voronoi
|
|
75
|
+
# headers and 9 from stb_image_write's implementation, against 5 that were
|
|
76
|
+
# real. Silenced per source file, per warning.
|
|
77
|
+
#
|
|
78
|
+
# Deliberately NOT -isystem, which looks like the tidier fix and is a trap:
|
|
79
|
+
# clang searches -isystem AFTER /usr/local/include, so a Homebrew Boost
|
|
80
|
+
# 1.90 silently won over the pinned 1.86 (measured, not assumed). Keeping
|
|
81
|
+
# -I keeps the fetched copy winning.
|
|
82
|
+
include(CheckCXXCompilerFlag)
|
|
83
|
+
foreach(_w deprecated-enum-float-conversion missing-field-initializers deprecated-declarations)
|
|
84
|
+
check_cxx_compiler_flag("-Wno-${_w}" OSCAD_HAS_WNO_${_w})
|
|
85
|
+
endforeach()
|
|
86
|
+
|
|
87
|
+
if(OSCAD_HAS_WNO_deprecated-enum-float-conversion)
|
|
88
|
+
# <boost/polygon/voronoi.hpp> does floating-point arithmetic on an
|
|
89
|
+
# unnamed enum, deprecated in C++20.
|
|
90
|
+
set_source_files_properties(builtins/roof.cpp PROPERTIES
|
|
91
|
+
COMPILE_OPTIONS "-Wno-deprecated-enum-float-conversion")
|
|
92
|
+
endif()
|
|
93
|
+
|
|
94
|
+
# zip_stored.cpp defines STB_IMAGE_WRITE_IMPLEMENTATION, so stb's C compiles
|
|
95
|
+
# as ours: `= { 0 }` partial initializers, and one sprintf().
|
|
96
|
+
set(_stb_quiet "")
|
|
97
|
+
if(OSCAD_HAS_WNO_missing-field-initializers)
|
|
98
|
+
list(APPEND _stb_quiet "-Wno-missing-field-initializers")
|
|
99
|
+
endif()
|
|
100
|
+
if(OSCAD_HAS_WNO_deprecated-declarations)
|
|
101
|
+
list(APPEND _stb_quiet "-Wno-deprecated-declarations")
|
|
102
|
+
endif()
|
|
103
|
+
if(_stb_quiet)
|
|
104
|
+
set_source_files_properties(zip_stored.cpp PROPERTIES COMPILE_OPTIONS "${_stb_quiet}")
|
|
105
|
+
endif()
|
|
@@ -188,6 +188,10 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
188
188
|
const std::vector<std::unique_ptr<CSGNode>>& children,
|
|
189
189
|
const oscad::ASTNode& node);
|
|
190
190
|
|
|
191
|
+
CSGParams resolveMeshRepair(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
|
|
192
|
+
std::vector<ColoredBody> generateMeshRepair(Evaluator& ev, const CSGParams& params,
|
|
193
|
+
const std::vector<std::unique_ptr<CSGNode>>& children,
|
|
194
|
+
const oscad::ASTNode& node);
|
|
191
195
|
CSGParams resolveSimplify(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
|
|
192
196
|
std::vector<ColoredBody> generateSimplify(Evaluator& ev, const CSGParams& params,
|
|
193
197
|
const std::vector<std::unique_ptr<CSGNode>>& children,
|
{openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -837,38 +837,6 @@ Value builtinFontmetrics(Evaluator& ev, const CallArgs& args) {
|
|
|
837
837
|
});
|
|
838
838
|
}
|
|
839
839
|
|
|
840
|
-
// Take only scalar numbers, mapped to how many leading positional
|
|
841
|
-
// arguments must each actually BE a number. Anything else there (undef, a
|
|
842
|
-
// string, a list, a bool, an object) is a type error yielding undef, and
|
|
843
|
-
// so is supplying fewer arguments than the arity -- matching the
|
|
844
|
-
// reference, which rejects the call outright rather than coercing.
|
|
845
|
-
//
|
|
846
|
-
// This has to be checked up front rather than left to each case below:
|
|
847
|
-
// toDoubleLenient() silently turns undef/string/list into 0.0, which
|
|
848
|
-
// would make `cos(misspelled_var)` quietly 1 instead of undef, and a
|
|
849
|
-
// typo'd variable then produces plausible-looking geometry instead of an
|
|
850
|
-
// obvious failure. Arity is deliberately a floor, not an exact match --
|
|
851
|
-
// the reference also rejects EXTRA arguments, but tightening that too
|
|
852
|
-
// risks breaking working library code for no correctness gain here.
|
|
853
|
-
const std::unordered_map<std::string, size_t>& scalarNumericArity() {
|
|
854
|
-
static const std::unordered_map<std::string, size_t> arity = {
|
|
855
|
-
{"abs", 1}, {"sign", 1}, {"ceil", 1}, {"floor", 1}, {"round", 1}, {"sqrt", 1},
|
|
856
|
-
{"ln", 1}, {"log", 1}, {"exp", 1}, {"sin", 1}, {"cos", 1}, {"tan", 1},
|
|
857
|
-
{"asin", 1}, {"acos", 1}, {"atan", 1}, {"atan2", 2}, {"pow", 2}, {"rands", 3},
|
|
858
|
-
};
|
|
859
|
-
return arity;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
// Numeric functions that legitimately take a LIST argument, so the
|
|
863
|
-
// all-arguments-must-be-numbers rule above can't apply -- each validates
|
|
864
|
-
// its own operands, and only needs the bool case caught here (Value's
|
|
865
|
-
// bool and double are distinct alternatives, but the reference rejects
|
|
866
|
-
// `max(true, 1)` as a type error rather than treating true as 1).
|
|
867
|
-
const std::unordered_set<std::string>& numericOnlyNames() {
|
|
868
|
-
static const std::unordered_set<std::string> names = {"max", "min", "norm", "cross"};
|
|
869
|
-
return names;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
840
|
} // namespace
|
|
873
841
|
|
|
874
842
|
bool isBuiltinFunctionName(const std::string& name) {
|
|
@@ -1043,6 +1011,7 @@ const std::unordered_map<std::string, double>& featureLevels() {
|
|
|
1043
1011
|
{"sphere-styles", 1.0}, // sphere(style=)
|
|
1044
1012
|
{"export-name", 1.0}, // $export_name
|
|
1045
1013
|
{"simplify-op", 1.0}, // simplify()
|
|
1014
|
+
{"mesh-repair", 1.0}, // mesh_repair(), and import(tolerance=)
|
|
1046
1015
|
{"expr-import", 1.0}, // import() in expression position
|
|
1047
1016
|
{"object-function", 1.0}, // object(), unconditional here
|
|
1048
1017
|
{"roof-op", 1.0}, // roof(), method="voronoi" only
|
|
@@ -186,6 +186,10 @@ CSGParams resolveImport(Evaluator& ev, const oscad::ModularCall& node, EvalConte
|
|
|
186
186
|
// Not an OpenSCAD parameter. A script using it will not run upstream,
|
|
187
187
|
// which is why it has to be asked for rather than being the default.
|
|
188
188
|
const bool repair = truthy(getArg(args, std::nullopt, "repair", Value{false}));
|
|
189
|
+
// How far apart two vertices may be and still be welded into one.
|
|
190
|
+
// Named rather than hidden so import() and mesh_repair() answer the
|
|
191
|
+
// same question the same way (issue #190); undef keeps the default.
|
|
192
|
+
const Value toleranceArg = getArg(args, std::nullopt, "tolerance", Value{});
|
|
189
193
|
|
|
190
194
|
CSGParams params;
|
|
191
195
|
params["color"] = colorToValue(effCtx.color);
|
|
@@ -255,6 +259,7 @@ CSGParams resolveImport(Evaluator& ev, const oscad::ModularCall& node, EvalConte
|
|
|
255
259
|
}
|
|
256
260
|
params["kind"] = Value{std::string("mesh")};
|
|
257
261
|
params["repair"] = Value{repair};
|
|
262
|
+
params["tolerance"] = toleranceArg;
|
|
258
263
|
params["verts"] = Value{std::make_shared<const ValueList>(ValueList{std::move(vertsFlat)})};
|
|
259
264
|
params["tris"] = Value{std::make_shared<const ValueList>(ValueList{std::move(trisFlat)})};
|
|
260
265
|
return params;
|
|
@@ -315,9 +320,23 @@ std::vector<ColoredBody> generateImport(Evaluator& ev, const CSGParams& params,
|
|
|
315
320
|
const auto repairIt = params.find("repair");
|
|
316
321
|
const bool repair = repairIt != params.end() && truthy(repairIt->second);
|
|
317
322
|
if (repair) {
|
|
323
|
+
double tolerance = kDefaultWeldTolerance;
|
|
324
|
+
const auto tolIt = params.find("tolerance");
|
|
325
|
+
if (tolIt != params.end()) {
|
|
326
|
+
if (const double* t = std::get_if<double>(&tolIt->second)) {
|
|
327
|
+
if (*t < 0.0) {
|
|
328
|
+
ev.warn("import: tolerance must not be negative; using the default",
|
|
329
|
+
&node.position());
|
|
330
|
+
} else {
|
|
331
|
+
tolerance = *t;
|
|
332
|
+
}
|
|
333
|
+
} else if (!std::holds_alternative<std::monostate>(tolIt->second)) {
|
|
334
|
+
ev.warn("import: tolerance must be a number", &node.position());
|
|
335
|
+
}
|
|
336
|
+
}
|
|
318
337
|
const MeshDiagnosis before = checkMesh(mesh);
|
|
319
338
|
MeshRepairReport rep;
|
|
320
|
-
manifold::MeshGL64 fixed = repairMesh(mesh, rep);
|
|
339
|
+
manifold::MeshGL64 fixed = repairMesh(mesh, rep, tolerance);
|
|
321
340
|
const MeshDiagnosis after = checkMesh(fixed);
|
|
322
341
|
if (rep.didAnything()) {
|
|
323
342
|
ev.warn("import: repaired the mesh -- " + rep.summary(), &node.position());
|
|
@@ -344,7 +363,9 @@ std::vector<ColoredBody> generateImport(Evaluator& ev, const CSGParams& params,
|
|
|
344
363
|
if (why.empty()) why = manifoldErrorName(body.Status());
|
|
345
364
|
if (!ev.insideHull) {
|
|
346
365
|
ev.warn("import: mesh is not a closed solid (" + why +
|
|
347
|
-
|
|
366
|
+
// Same rewording as polyhedron's: see the comment there.
|
|
367
|
+
"); drawing the object as an open surface rather than a solid "
|
|
368
|
+
"-- nothing is patched. hull() can still use its points, but "
|
|
348
369
|
"it cannot take part in union/difference/intersection" +
|
|
349
370
|
std::string(repair ? "" : ". Try import(..., repair=true)"),
|
|
350
371
|
&node.position());
|
{openscad_cpp_evaluator-1.23.1 → openscad_cpp_evaluator-1.25.0}/src/builtins/primitives_3d.cpp
RENAMED
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
#include <cstdint>
|
|
12
12
|
#include <map>
|
|
13
13
|
#include <numbers>
|
|
14
|
+
#include <optional>
|
|
14
15
|
#include <set>
|
|
16
|
+
#include <string>
|
|
17
|
+
#include <utility>
|
|
15
18
|
|
|
16
19
|
namespace oscadeval {
|
|
17
20
|
|
|
@@ -42,8 +45,18 @@ bool isDrawableFailure(const manifold::Manifold& body, const M& mesh) {
|
|
|
42
45
|
[](float v) { return std::isfinite(v); });
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
struct BoundaryEdges {
|
|
49
|
+
size_t count = 0;
|
|
50
|
+
// Vertex indices of one representative open edge, or nullopt when the
|
|
51
|
+
// mesh is closed. A count alone is enough on a hand-written face list,
|
|
52
|
+
// where you can just read it; it is useless on generated geometry --
|
|
53
|
+
// three bad edges in a few thousand triangles cannot be found by eye,
|
|
54
|
+
// which is what happened in BelfrySCAD #521 / issue #187.
|
|
55
|
+
std::optional<std::pair<uint32_t, uint32_t>> first;
|
|
56
|
+
};
|
|
57
|
+
|
|
45
58
|
template <typename M>
|
|
46
|
-
|
|
59
|
+
BoundaryEdges findBoundaryEdges(const M& mesh) {
|
|
47
60
|
std::map<std::pair<uint32_t, uint32_t>, int> uses;
|
|
48
61
|
for (size_t i = 0; i + 2 < mesh.triVerts.size(); i += 3) {
|
|
49
62
|
const uint32_t v[3] = {static_cast<uint32_t>(mesh.triVerts[i]),
|
|
@@ -54,11 +67,34 @@ size_t countBoundaryEdges(const M& mesh) {
|
|
|
54
67
|
++uses[{std::min(a, b), std::max(a, b)}];
|
|
55
68
|
}
|
|
56
69
|
}
|
|
57
|
-
|
|
70
|
+
BoundaryEdges out;
|
|
71
|
+
// std::map iterates in vertex-index order, which is arbitrary from the
|
|
72
|
+
// author's point of view but DETERMINISTIC: the same mesh names the same
|
|
73
|
+
// edge on every run. That matters more than which edge gets picked --
|
|
74
|
+
// an identifier that moves between renders is worse than no identifier.
|
|
75
|
+
// A cluster of open edges is nearly always one hole, so the first is
|
|
76
|
+
// usually representative of the rest.
|
|
58
77
|
for (const auto& [edge, count] : uses) {
|
|
59
|
-
if (count % 2
|
|
78
|
+
if (count % 2 == 0) continue;
|
|
79
|
+
++out.count;
|
|
80
|
+
if (!out.first) out.first = edge;
|
|
60
81
|
}
|
|
61
|
-
return
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// "[1, 2.5, 0] - [1, 3, 0]" for one edge, using the same number formatting
|
|
86
|
+
// echo() uses, so a coordinate can be pasted straight back into a script.
|
|
87
|
+
template <typename M>
|
|
88
|
+
std::string describeEdge(const M& mesh, std::pair<uint32_t, uint32_t> edge) {
|
|
89
|
+
const size_t stride = mesh.numProp > 0 ? static_cast<size_t>(mesh.numProp) : 3;
|
|
90
|
+
auto point = [&](uint32_t v) {
|
|
91
|
+
const size_t base = static_cast<size_t>(v) * stride;
|
|
92
|
+
if (base + 2 >= mesh.vertProperties.size()) return std::string("?");
|
|
93
|
+
return "[" + formatNumber(static_cast<double>(mesh.vertProperties[base])) + ", " +
|
|
94
|
+
formatNumber(static_cast<double>(mesh.vertProperties[base + 1])) + ", " +
|
|
95
|
+
formatNumber(static_cast<double>(mesh.vertProperties[base + 2])) + "]";
|
|
96
|
+
};
|
|
97
|
+
return point(edge.first) + " - " + point(edge.second);
|
|
62
98
|
}
|
|
63
99
|
} // namespace
|
|
64
100
|
|
|
@@ -810,8 +846,23 @@ std::vector<ColoredBody> generatePolyhedron(Evaluator& ev, const CSGParams& para
|
|
|
810
846
|
// self-intersections builds perfectly well, and only an OPEN one
|
|
811
847
|
// reaches this branch. The count points straight at the problem.
|
|
812
848
|
if (!ev.insideHull) {
|
|
813
|
-
|
|
814
|
-
|
|
849
|
+
const BoundaryEdges open = findBoundaryEdges(mesh);
|
|
850
|
+
// Name one of them. Where the hole IS is the only part of this
|
|
851
|
+
// message an author can act on; the count alone just says to go
|
|
852
|
+
// looking, across the whole mesh.
|
|
853
|
+
const std::string where =
|
|
854
|
+
open.first ? ", first at " + describeEdge(mesh, *open.first) : std::string();
|
|
855
|
+
ev.warn("polyhedron: mesh is not closed -- " + std::to_string(open.count) +
|
|
856
|
+
" boundary edge(s)" + where +
|
|
857
|
+
// "drawing it as a surface" was read by a reporter as
|
|
858
|
+
// the HOLE being surfaced over -- i.e. as automatic
|
|
859
|
+
// repair hiding their defect. Nothing is repaired: the
|
|
860
|
+
// mesh is drawn as the open surface it is. Name the
|
|
861
|
+
// object as the thing being drawn, and say what it is
|
|
862
|
+
// NOT, since "surface" alone does not imply "not solid"
|
|
863
|
+
// to someone who did not know it could be either.
|
|
864
|
+
"; drawing the object as an open surface rather than a "
|
|
865
|
+
"solid -- nothing is patched. hull() can still "
|
|
815
866
|
"use its points, but it cannot take part in union/difference/intersection",
|
|
816
867
|
&node.position());
|
|
817
868
|
}
|
|
@@ -37,6 +37,7 @@ const std::unordered_map<std::string_view, ResolveFn>& resolveDispatch() {
|
|
|
37
37
|
{"fill", &resolveFill},
|
|
38
38
|
{"minkowski_difference", &resolveMinkowskiDifference},
|
|
39
39
|
{"simplify", &resolveSimplify},
|
|
40
|
+
{"mesh_repair", &resolveMeshRepair},
|
|
40
41
|
{"levelset", &resolveLevelSet},
|
|
41
42
|
{"offset", &resolveOffset},
|
|
42
43
|
{"surface", &resolveSurface},
|
|
@@ -79,6 +80,7 @@ const std::unordered_map<std::string_view, GenerateFn>& generateDispatch() {
|
|
|
79
80
|
{"fill", &generateFill},
|
|
80
81
|
{"minkowski_difference", &generateMinkowskiDifference},
|
|
81
82
|
{"simplify", &generateSimplify},
|
|
83
|
+
{"mesh_repair", &generateMeshRepair},
|
|
82
84
|
{"levelset", &generateLevelSet},
|
|
83
85
|
{"offset", &generateOffset},
|
|
84
86
|
{"surface", &generateSurface},
|
|
@@ -122,7 +124,7 @@ const std::vector<std::string>* builtinParamNames(const std::string& name) {
|
|
|
122
124
|
// full and quietly given the whole drawing.)
|
|
123
125
|
{"import",
|
|
124
126
|
{"file", "layer", "convexity", "origin", "scale", "width", "height", "filename", "layername", "center", "dpi",
|
|
125
|
-
"id", "class", "repair"}},
|
|
127
|
+
"id", "class", "repair", "tolerance"}},
|
|
126
128
|
{"linear_extrude", {"height", "v", "scale", "center", "twist", "slices", "segments", "convexity"}},
|
|
127
129
|
{"rotate_extrude", {"angle", "start", "convexity"}},
|
|
128
130
|
{"projection", {"cut", "convexity"}},
|
|
@@ -130,6 +132,7 @@ const std::vector<std::string>* builtinParamNames(const std::string& name) {
|
|
|
130
132
|
{"fill", {}},
|
|
131
133
|
{"minkowski_difference", {}},
|
|
132
134
|
{"simplify", {"tolerance"}},
|
|
135
|
+
{"mesh_repair", {"tolerance"}},
|
|
133
136
|
{"levelset", {"field", "bounds", "isovalue", "invert", "edge"}},
|
|
134
137
|
{"offset", {"r", "delta", "chamfer"}},
|
|
135
138
|
{"surface", {"file", "center", "convexity", "invert"}},
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include "openscad_cpp_evaluator/call_args.hpp"
|
|
4
4
|
#include "openscad_cpp_evaluator/evaluator.hpp"
|
|
5
|
+
#include "openscad_cpp_evaluator/mesh_check.hpp"
|
|
5
6
|
|
|
6
7
|
#include <manifold/polygon.h>
|
|
7
8
|
|
|
@@ -503,6 +504,117 @@ std::vector<ColoredBody> generateSimplify(Evaluator& ev, const CSGParams& params
|
|
|
503
504
|
return out;
|
|
504
505
|
}
|
|
505
506
|
|
|
507
|
+
// mesh_repair(tolerance) -- make a broken mesh watertight. A BelfrySCAD
|
|
508
|
+
// extension: nothing in the OpenSCAD language can repair geometry, and a
|
|
509
|
+
// mesh arriving from a library generator or an import is routinely almost
|
|
510
|
+
// closed rather than closed. See issue #190.
|
|
511
|
+
//
|
|
512
|
+
// The work is repairMesh()'s, already used by import(..., repair=true);
|
|
513
|
+
// this exposes the same six steps to any geometry, not only imported
|
|
514
|
+
// files: weld, drop degenerate/duplicate, orient, fill holes, flip
|
|
515
|
+
// outward, strip zero-area faces.
|
|
516
|
+
//
|
|
517
|
+
// NOT a substitute for a generator that emits a closed mesh. Repair costs
|
|
518
|
+
// a full pass over the geometry every render, and welding discards
|
|
519
|
+
// vertices -- which is why the default tolerance is tight enough to
|
|
520
|
+
// weld only what is unambiguously one point, leaving hole filling to
|
|
521
|
+
// close the rest.
|
|
522
|
+
|
|
523
|
+
CSGParams resolveMeshRepair(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
|
|
524
|
+
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
525
|
+
CSGParams params;
|
|
526
|
+
// Position 0, so mesh_repair(0.001) reads the way simplify(0.1) does.
|
|
527
|
+
params["tolerance"] = getArg(args, 0, "tolerance", Value{});
|
|
528
|
+
EvalContext blockCtx = ev.blockScope(effCtx);
|
|
529
|
+
ev.evalChildren(node.children, blockCtx);
|
|
530
|
+
return params;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
std::vector<ColoredBody> generateMeshRepair(Evaluator& ev, const CSGParams& params,
|
|
534
|
+
const std::vector<std::unique_ptr<CSGNode>>& children,
|
|
535
|
+
const oscad::ASTNode& node) {
|
|
536
|
+
const std::vector<ColoredBody> bodies = flattenCsgTree(children);
|
|
537
|
+
if (bodies.empty()) return {};
|
|
538
|
+
|
|
539
|
+
const Value tolArg = params.at("tolerance");
|
|
540
|
+
double tolerance = kDefaultWeldTolerance;
|
|
541
|
+
if (std::holds_alternative<double>(tolArg)) {
|
|
542
|
+
tolerance = std::get<double>(tolArg);
|
|
543
|
+
if (tolerance < 0.0) {
|
|
544
|
+
ev.warn("mesh_repair: tolerance must not be negative", &node.position());
|
|
545
|
+
return bodies;
|
|
546
|
+
}
|
|
547
|
+
} else if (!std::holds_alternative<std::monostate>(tolArg)) {
|
|
548
|
+
ev.warn("mesh_repair: tolerance must be a number", &node.position());
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
std::vector<ColoredBody> out;
|
|
552
|
+
out.reserve(bodies.size());
|
|
553
|
+
for (const ColoredBody& b : bodies) {
|
|
554
|
+
// Background/highlight geometry is a debugging aid, not part of the
|
|
555
|
+
// model; repairing it would spend the pass and say nothing useful.
|
|
556
|
+
// A 2D section has no mesh to repair either.
|
|
557
|
+
if (b.role != BodyRole::Normal || (!b.body && !b.rawMesh)) {
|
|
558
|
+
out.push_back(b);
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
// The case this module exists for: polyhedron() could not build a
|
|
562
|
+
// Manifold from an open mesh, so it kept the raw soup in rawMesh
|
|
563
|
+
// and left `body` set but EMPTY (see ColoredBody::rawMesh). Repair
|
|
564
|
+
// the soup -- repairing the empty Manifold would find nothing to
|
|
565
|
+
// do and report success at having done nothing.
|
|
566
|
+
MeshRepairReport report;
|
|
567
|
+
ColoredBody cb = b;
|
|
568
|
+
manifold::MeshGL64 fixed;
|
|
569
|
+
if (b.rawMesh) {
|
|
570
|
+
manifold::MeshGL64 soup;
|
|
571
|
+
soup.numProp = b.rawMesh->numProp;
|
|
572
|
+
soup.vertProperties.assign(b.rawMesh->vertProperties.begin(),
|
|
573
|
+
b.rawMesh->vertProperties.end());
|
|
574
|
+
soup.triVerts.assign(b.rawMesh->triVerts.begin(), b.rawMesh->triVerts.end());
|
|
575
|
+
fixed = repairMesh(soup, report, tolerance);
|
|
576
|
+
} else {
|
|
577
|
+
fixed = repairMesh(b.body->GetMeshGL64(), report, tolerance);
|
|
578
|
+
}
|
|
579
|
+
if (report.didAnything()) {
|
|
580
|
+
manifold::Manifold rebuilt(fixed);
|
|
581
|
+
if (rebuilt.Status() == manifold::Manifold::Error::NoError) {
|
|
582
|
+
cb.body = std::move(rebuilt);
|
|
583
|
+
// It is a real solid now, so stop carrying the display-only
|
|
584
|
+
// soup: leaving it set would keep splitByRole() pulling the
|
|
585
|
+
// body aside as un-CSG-able, which is the whole thing the
|
|
586
|
+
// repair just fixed.
|
|
587
|
+
cb.rawMesh.reset();
|
|
588
|
+
// triColors is indexed by triangle, and repair changes how
|
|
589
|
+
// many there are -- filling adds, sliver stripping removes.
|
|
590
|
+
// Carrying it across would mask an N-triangle mesh with an
|
|
591
|
+
// M-entry array, which is an IndexError rather than a wrong
|
|
592
|
+
// colour. Same reasoning as simplify()'s.
|
|
593
|
+
cb.triColors.reset();
|
|
594
|
+
ev.warn("mesh_repair: " + report.summary(), &node.position());
|
|
595
|
+
} else {
|
|
596
|
+
// Say what it could not do. Silently returning the original
|
|
597
|
+
// would leave the author believing a repair happened.
|
|
598
|
+
ev.warn("mesh_repair: could not rebuild the repaired mesh (" +
|
|
599
|
+
std::string(manifoldErrorName(rebuilt.Status())) +
|
|
600
|
+
"); geometry left unchanged",
|
|
601
|
+
&node.position());
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (report.unfilledHoles) {
|
|
605
|
+
// The sub-3-edge boundary loop: a crack between two nearly
|
|
606
|
+
// coincident vertices, with no fan to build. Only a coarser
|
|
607
|
+
// weld closes it, so name the knob rather than just the count.
|
|
608
|
+
ev.warn("mesh_repair: " + std::to_string(report.unfilledHoles) +
|
|
609
|
+
" hole(s) left open -- a crack too narrow to fill needs a"
|
|
610
|
+
" larger tolerance to weld shut instead",
|
|
611
|
+
&node.position());
|
|
612
|
+
}
|
|
613
|
+
out.push_back(std::move(cb));
|
|
614
|
+
}
|
|
615
|
+
return out;
|
|
616
|
+
}
|
|
617
|
+
|
|
506
618
|
// -- levelset(field, bounds, isovalue) -------------------------------------
|
|
507
619
|
//
|
|
508
620
|
// A solid from a grid of sampled values. Wraps Manifold::LevelSet with a C++
|
|
@@ -621,18 +733,6 @@ double bandDistance(double v, double lo, double hi, bool invert) {
|
|
|
621
733
|
return invert ? -d : d;
|
|
622
734
|
}
|
|
623
735
|
|
|
624
|
-
std::optional<std::array<double, 3>> readVec3(const Value& v) {
|
|
625
|
-
const ListPtr* l = std::get_if<ListPtr>(&v);
|
|
626
|
-
if (!l || !*l || (*l)->items.size() != 3) return std::nullopt;
|
|
627
|
-
std::array<double, 3> out{};
|
|
628
|
-
for (size_t i = 0; i < 3; ++i) {
|
|
629
|
-
const double* d = std::get_if<double>(&(*l)->items[i]);
|
|
630
|
-
if (!d || !std::isfinite(*d)) return std::nullopt;
|
|
631
|
-
out[i] = *d;
|
|
632
|
-
}
|
|
633
|
-
return out;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
736
|
} // namespace
|
|
637
737
|
|
|
638
738
|
// -- 2D: marching squares --------------------------------------------------
|
|
@@ -58,9 +58,16 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
58
58
|
// skipped. See CSGNode::keyHasClosure.
|
|
59
59
|
if (node.keyHasClosure) key.reset();
|
|
60
60
|
}
|
|
61
|
-
std::optional<
|
|
61
|
+
std::optional<CachedSubtree> cached = key ? manifoldCache_->get(*key) : std::nullopt;
|
|
62
62
|
if (cached) {
|
|
63
|
-
node.bodies = std::move(
|
|
63
|
+
node.bodies = std::move(cached->bodies);
|
|
64
|
+
// Replay what generating this subtree said the first time. A hit
|
|
65
|
+
// runs none of it again, so without this the diagnostics simply
|
|
66
|
+
// disappear on every render after the first -- which is how a
|
|
67
|
+
// permanently-open polyhedron came to look like an intermittent
|
|
68
|
+
// one (BelfrySCAD #521). Goes through emitWarning so an enclosing
|
|
69
|
+
// capture records them too, exactly as a fresh generate would.
|
|
70
|
+
for (const std::string& w : cached->warnings) emitWarning(w);
|
|
64
71
|
// Skipped entirely while measuring: restampCachedIds writes
|
|
65
72
|
// idToNode/idToColor, and geometry that is about to be discarded
|
|
66
73
|
// has no click-to-source identity worth recording. See
|
|
@@ -83,6 +90,23 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
83
90
|
// inside this hull.
|
|
84
91
|
const bool savedInsideHull = insideHull;
|
|
85
92
|
if (node.isBuiltin && node.kind == "hull") insideHull = true;
|
|
93
|
+
// Collect everything this subtree warns about, so put() below can
|
|
94
|
+
// store it with the bodies. Only worth doing when the result is
|
|
95
|
+
// actually cacheable -- an uncacheable node (rands() taint, a
|
|
96
|
+
// closure in the key) regenerates every time and warns every time
|
|
97
|
+
// by itself.
|
|
98
|
+
std::vector<std::string> subtreeWarnings;
|
|
99
|
+
// RAII, unlike the plain save/restore insideHull and
|
|
100
|
+
// generateWarnEntry use above: those restore a value, this one
|
|
101
|
+
// restores a POINTER to a stack vector. An EvalError thrown out
|
|
102
|
+
// of a GenerateFn would otherwise leave warnCapture dangling at
|
|
103
|
+
// a destroyed vector for any warning raised afterwards.
|
|
104
|
+
struct CaptureGuard {
|
|
105
|
+
Evaluator& ev;
|
|
106
|
+
std::vector<std::string>* saved;
|
|
107
|
+
~CaptureGuard() { ev.warnCapture = saved; }
|
|
108
|
+
} captureGuard{*this, warnCapture};
|
|
109
|
+
if (key) warnCapture = &subtreeWarnings;
|
|
86
110
|
generateTreeImpl(childPtrs);
|
|
87
111
|
insideHull = savedInsideHull;
|
|
88
112
|
|
|
@@ -111,12 +135,18 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
111
135
|
node.bodies = flattenCsgTree(node.children);
|
|
112
136
|
}
|
|
113
137
|
if (key) {
|
|
138
|
+
warnCapture = captureGuard.saved;
|
|
139
|
+
// Merge inward-out: an ancestor still capturing must end up
|
|
140
|
+
// holding this subtree's lines too, since a hit at ITS level
|
|
141
|
+
// skips everything down here.
|
|
142
|
+
if (warnCapture != nullptr)
|
|
143
|
+
warnCapture->insert(warnCapture->end(), subtreeWarnings.begin(), subtreeWarnings.end());
|
|
114
144
|
// The geometry itself is still cached while measuring --
|
|
115
145
|
// cacheKey is content-addressed, so the real render can
|
|
116
146
|
// legitimately reuse it. Only the PRODUCER attribution is
|
|
117
147
|
// suppressed: a node that gets discarded must never be
|
|
118
148
|
// recorded as the origin of geometry a later render draws.
|
|
119
|
-
manifoldCache_->put(*key, node.bodies);
|
|
149
|
+
manifoldCache_->put(*key, node.bodies, std::move(subtreeWarnings));
|
|
120
150
|
if (!measuring_) cacheProducer_[*key] = node.node;
|
|
121
151
|
}
|
|
122
152
|
// The children's bodies have been consumed (by the dispatch above,
|