openscad-cpp-evaluator 1.20.3__tar.gz → 1.21.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.20.3 → openscad_cpp_evaluator-1.21.0}/.gitignore +2 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/CLAUDE.md +39 -1
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/bindings/module.cpp +31 -3
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/csg_node.hpp +7 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/evaluator.hpp +83 -1
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/python/openscad_cpp_evaluator/__init__.py +41 -4
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/bytecode_vm.cpp +2 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/csg_generate.cpp +14 -2
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/csg_resolve.cpp +3 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/export.cpp +33 -18
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/mesh_check.cpp +81 -26
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_export.cpp +64 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_mesh_check.cpp +20 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_python_bindings.py +116 -0
- openscad_cpp_evaluator-1.20.3/src/.DS_Store +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.20.3 → openscad_cpp_evaluator-1.21.0}/uv.lock +0 -0
|
@@ -1609,8 +1609,46 @@ implementation of "CSG subtree → `object()`"; both engines call it — the
|
|
|
1609
1609
|
interpreter from `evalRenderExpr`, the VM from `Op::PopBuiltinWrap`'s
|
|
1610
1610
|
`Kind::Measure` branch.
|
|
1611
1611
|
|
|
1612
|
+
**`idToCallChain` is what a picker should actually walk.** `idToNode` names the
|
|
1613
|
+
node that *produced* the geometry, and for anything a library builds that is a
|
|
1614
|
+
node inside the library — a plain `cube(10)` maps into BOSL2's `builtins.scad`
|
|
1615
|
+
the moment BOSL2 is included, because BOSL2 overrides the primitives with its
|
|
1616
|
+
own modules. Pointing an editor at that span means pointing it at another file,
|
|
1617
|
+
or (if the consumer forgets to check `origin`) splicing a library's byte offsets
|
|
1618
|
+
into the user's buffer, which is how BelfrySCAD #450 appended a `translate()` to
|
|
1619
|
+
the end of a 59-character script.
|
|
1620
|
+
|
|
1621
|
+
`idToCallChain` maps each `originalID` to its **whole call chain**, innermost
|
|
1622
|
+
frame first. Deliberately unfiltered: a single `cuboid()` is 24 frames, most of
|
|
1623
|
+
them BOSL2's own `attachable`/`_attach_transform` layers, and someone debugging
|
|
1624
|
+
the library wants to step into exactly those. Which frames are reachable depends
|
|
1625
|
+
on what the front end has open, which the evaluator has no business guessing —
|
|
1626
|
+
so it records all of them and a consumer picks its level (BelfrySCAD defaults to
|
|
1627
|
+
the last frame in the running script).
|
|
1628
|
+
|
|
1629
|
+
It is stored as a **cactus stack**, not a list per node. Chains nest, so the
|
|
1630
|
+
distinct chains over a run form a tree: `callChains_` holds `{site, parent,
|
|
1631
|
+
isModule}` and a `CSGNode` holds one `uint32` into it. Memory tracks distinct
|
|
1632
|
+
call *paths* — tens on a real model; Dalek's 139 bodies share 13 innermost sites
|
|
1633
|
+
— rather than CSG nodes, and nothing allocates per node. That is what makes this
|
|
1634
|
+
affordable where "the full frame list a TRACE would need" (`csg_node.hpp`) was
|
|
1635
|
+
not.
|
|
1636
|
+
|
|
1637
|
+
`isModule` separates a module frame, which has geometry behind it, from a
|
|
1638
|
+
function frame like `_find_anchor`, which does not: both are worth showing, only
|
|
1639
|
+
the former is worth dragging.
|
|
1640
|
+
|
|
1641
|
+
A cache hit restamps to the chain reaching the geometry *now*, not the one that
|
|
1642
|
+
first produced it: a module called twice genuinely is two call chains.
|
|
1643
|
+
|
|
1644
|
+
No stored script path is needed to tell the user's file from a library:
|
|
1645
|
+
`callStack_.front()` is by construction the call made from top level.
|
|
1646
|
+
|
|
1647
|
+
Exposed to Python as `node.call_sites` (innermost-first `_CallFrame`s, each a
|
|
1648
|
+
`_Position` plus `is_module`), with `node.call_site` as the innermost.
|
|
1649
|
+
|
|
1612
1650
|
`Evaluator::measuring_` is set for the whole generate. It suppresses the four
|
|
1613
|
-
writes that exist solely to describe *drawn* geometry — `idToNode`/`idToColor` in
|
|
1651
|
+
writes that exist solely to describe *drawn* geometry — `idToNode`/`idToColor`/`idToCallChain` in
|
|
1614
1652
|
`tagGenerated` and `tagDisplayOnly`, the `restampCachedIds` call on a cache hit,
|
|
1615
1653
|
and `cacheProducer_` — because those tables are cleared once per pass, so a leak
|
|
1616
1654
|
is permanent and surfaces later as wrong click-to-source. It also suppresses
|
|
@@ -196,13 +196,37 @@ struct IdSpan {
|
|
|
196
196
|
uint32_t id;
|
|
197
197
|
int start, end, line, column;
|
|
198
198
|
std::string origin;
|
|
199
|
+
// The whole call chain behind this body, innermost frame first. Empty
|
|
200
|
+
// for geometry written at top level, where the span above is already
|
|
201
|
+
// the user's own source. Library frames included on purpose -- a BOSL2
|
|
202
|
+
// author stepping into `attachable` wants them; a front end picks the
|
|
203
|
+
// level it can actually show. See Evaluator::idToCallChain.
|
|
204
|
+
struct Frame {
|
|
205
|
+
int start, end, line, column;
|
|
206
|
+
std::string origin;
|
|
207
|
+
bool is_module;
|
|
208
|
+
};
|
|
209
|
+
std::vector<Frame> chain;
|
|
199
210
|
};
|
|
200
211
|
|
|
201
212
|
void collectIdSpans(const oscadeval::Evaluator& ev, std::vector<IdSpan>& out) {
|
|
202
213
|
out.reserve(ev.idToNode.size());
|
|
203
214
|
for (const auto& [id, node] : ev.idToNode) {
|
|
204
215
|
const oscad::Position& p = node->position();
|
|
205
|
-
|
|
216
|
+
IdSpan s{id, p.start_offset, p.end_offset, p.line, p.column, p.origin};
|
|
217
|
+
auto found = ev.idToCallChain.find(id);
|
|
218
|
+
uint32_t idx = found == ev.idToCallChain.end()
|
|
219
|
+
? oscadeval::Evaluator::kNoCallChain : found->second;
|
|
220
|
+
while (idx != oscadeval::Evaluator::kNoCallChain && idx < ev.callChains_.size()) {
|
|
221
|
+
const auto& e = ev.callChains_[idx];
|
|
222
|
+
if (e.site) {
|
|
223
|
+
s.chain.push_back({e.site->start_offset, e.site->end_offset,
|
|
224
|
+
e.site->line, e.site->column, e.site->origin,
|
|
225
|
+
e.isModule});
|
|
226
|
+
}
|
|
227
|
+
idx = e.parent;
|
|
228
|
+
}
|
|
229
|
+
out.push_back(std::move(s));
|
|
206
230
|
}
|
|
207
231
|
}
|
|
208
232
|
|
|
@@ -219,8 +243,12 @@ nb::list bodiesToList(std::vector<oscadeval::ColoredBody>& bodies) {
|
|
|
219
243
|
|
|
220
244
|
nb::dict idSpansToDict(const std::vector<IdSpan>& idSpans) {
|
|
221
245
|
nb::dict d;
|
|
222
|
-
for (const IdSpan& s : idSpans)
|
|
223
|
-
|
|
246
|
+
for (const IdSpan& s : idSpans) {
|
|
247
|
+
nb::list chain;
|
|
248
|
+
for (const IdSpan::Frame& f : s.chain)
|
|
249
|
+
chain.append(nb::make_tuple(f.start, f.end, f.line, f.column, f.origin, f.is_module));
|
|
250
|
+
d[nb::cast(s.id)] = nb::make_tuple(s.start, s.end, s.line, s.column, s.origin, chain);
|
|
251
|
+
}
|
|
224
252
|
return d;
|
|
225
253
|
}
|
|
226
254
|
|
|
@@ -51,6 +51,13 @@ struct CSGNode {
|
|
|
51
51
|
// call sites still share a cache entry.
|
|
52
52
|
const oscad::Position* warnEntry = nullptr;
|
|
53
53
|
|
|
54
|
+
// This node's interned call chain (Evaluator::callChains_), captured
|
|
55
|
+
// at RESOLVE time for the same reason warnEntry is: a picker runs long
|
|
56
|
+
// after the stack has unwound. kNoCallChain at top level. One uint32,
|
|
57
|
+
// not a frame list -- see Evaluator's call-chain section for why that
|
|
58
|
+
// is affordable where a per-node list was not.
|
|
59
|
+
uint32_t callChain = UINT32_MAX;
|
|
60
|
+
|
|
54
61
|
// 1 + the deepest child's own treeDepth (1 for a leaf) -- set once,
|
|
55
62
|
// at construction, by whichever csg_resolve.cpp site finalizes this
|
|
56
63
|
// node's own `children` (buildTreeNode, evalModularCall's non-splice
|
|
@@ -284,9 +284,21 @@ public:
|
|
|
284
284
|
// Give cached bodies fresh originalIDs so a second call site reusing
|
|
285
285
|
// them is not confused with the first. See its definition.
|
|
286
286
|
void restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::ASTNode& node,
|
|
287
|
-
const oscad::ASTNode* producer);
|
|
287
|
+
const oscad::ASTNode* producer, uint32_t callChain);
|
|
288
288
|
|
|
289
289
|
std::unordered_map<uint32_t, const oscad::ASTNode*> idToNode;
|
|
290
|
+
// originalID -> its interned call chain (innermost frame; walk
|
|
291
|
+
// callChains_[i].parent outwards), or kNoCallChain for geometry
|
|
292
|
+
// written at top level, where idToNode already is the user's node.
|
|
293
|
+
//
|
|
294
|
+
// idToNode names the node that PRODUCED the geometry, which for
|
|
295
|
+
// anything a library builds is a node inside that library -- including
|
|
296
|
+
// a plain `cube(10)` once BOSL2 is included, since BOSL2 overrides the
|
|
297
|
+
// primitives with its own modules. A consumer that wants to point an
|
|
298
|
+
// editor at a line the user can actually see walks this instead, and
|
|
299
|
+
// picks the level it wants: the last frame in the script for an
|
|
300
|
+
// ordinary pick, deeper for someone debugging the library itself.
|
|
301
|
+
std::unordered_map<uint32_t, uint32_t> idToCallChain;
|
|
290
302
|
// Nodes whose bodies were actually generated this run (not served from
|
|
291
303
|
// the ManifoldCache). Reset per run; a diagnostic, and the only honest
|
|
292
304
|
// way for a test to tell a cache hit from a miss now that an inner
|
|
@@ -327,6 +339,57 @@ public:
|
|
|
327
339
|
return callStack_.empty() ? nullptr : callStack_.front().callPosition;
|
|
328
340
|
}
|
|
329
341
|
|
|
342
|
+
// -- Call chains -----------------------------------------------------
|
|
343
|
+
//
|
|
344
|
+
// The whole call chain behind a body, so a picker can step through it.
|
|
345
|
+
// Deliberately UNFILTERED: which frames are reachable depends on what
|
|
346
|
+
// the front end has open, which the evaluator has no business guessing.
|
|
347
|
+
// A BOSL2 author stepping into `attachable` -> `_attach_transform` is
|
|
348
|
+
// the case that settles it; a single cuboid() call is seven frames, six
|
|
349
|
+
// of them inside the library.
|
|
350
|
+
//
|
|
351
|
+
// Stored as a cactus stack rather than a list per node. Call chains
|
|
352
|
+
// nest, so the distinct chains over a run form a TREE: each entry is
|
|
353
|
+
// one frame plus its parent's index, and a CSGNode holds a single
|
|
354
|
+
// uint32 into the pool. Memory tracks distinct call PATHS (tens on a
|
|
355
|
+
// real model -- Dalek's 139 bodies share 13 innermost sites) rather
|
|
356
|
+
// than CSG nodes, and nothing allocates per node. That is what makes
|
|
357
|
+
// this affordable where "the full frame list a TRACE would need"
|
|
358
|
+
// (csg_node.hpp) was not.
|
|
359
|
+
static constexpr uint32_t kNoCallChain = UINT32_MAX;
|
|
360
|
+
|
|
361
|
+
struct CallChainEntry {
|
|
362
|
+
const oscad::Position* site = nullptr; // non-owning, AST-lifetime-bound
|
|
363
|
+
uint32_t parent = kNoCallChain; // next frame OUT, or kNoCallChain
|
|
364
|
+
bool isModule = false; // a function frame has no geometry to drag
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// Intern callStack_ as it stands and return its index, or kNoCallChain
|
|
368
|
+
// at top level. Outermost-first walk, so a chain sharing a prefix with
|
|
369
|
+
// one already interned costs only its own tail.
|
|
370
|
+
uint32_t internCurrentCallChain() {
|
|
371
|
+
uint32_t parent = kNoCallChain;
|
|
372
|
+
for (const CallStackFrame& f : callStack_) {
|
|
373
|
+
if (!f.callPosition) continue;
|
|
374
|
+
const ChainKey key{parent, f.callPosition};
|
|
375
|
+
auto found = chainIndex_.find(key);
|
|
376
|
+
if (found != chainIndex_.end()) {
|
|
377
|
+
parent = found->second;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
const uint32_t idx = static_cast<uint32_t>(callChains_.size());
|
|
381
|
+
callChains_.push_back({f.callPosition, parent,
|
|
382
|
+
f.kind == CallStackFrame::Kind::Module});
|
|
383
|
+
chainIndex_.emplace(key, idx);
|
|
384
|
+
parent = idx;
|
|
385
|
+
}
|
|
386
|
+
return parent; // the innermost frame, or kNoCallChain
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// The interned pool. Public for the same reason idToNode is: a binding
|
|
390
|
+
// walks it after evaluate() to flatten each id's chain.
|
|
391
|
+
std::vector<CallChainEntry> callChains_;
|
|
392
|
+
|
|
330
393
|
// Set by generateTreeImpl() to the CSGNode currently being generated,
|
|
331
394
|
// so warn() can name the user's own call site during a phase where
|
|
332
395
|
// callStack_ is necessarily empty. Public for the same reason
|
|
@@ -335,6 +398,9 @@ public:
|
|
|
335
398
|
// than anything reentrancy-aware, since generateTreeImpl recurses
|
|
336
399
|
// depth-first on one thread.
|
|
337
400
|
const oscad::Position* generateWarnEntry = nullptr;
|
|
401
|
+
// The same republish for CSGNode::callChain, read by tagGenerated to
|
|
402
|
+
// fill idToCallChain.
|
|
403
|
+
uint32_t generateCallChain = kNoCallChain;
|
|
338
404
|
|
|
339
405
|
// Set while generating anything beneath a hull(). An open mesh there is
|
|
340
406
|
// not a mistake to report: a convex hull needs only points, so BOSL2's
|
|
@@ -1790,6 +1856,22 @@ private:
|
|
|
1790
1856
|
// eval_error.hpp).
|
|
1791
1857
|
std::vector<CallStackFrame> callStack_;
|
|
1792
1858
|
|
|
1859
|
+
// (parent chain index, frame position) -> chain index, the memo that
|
|
1860
|
+
// makes internCurrentCallChain() share prefixes. Cleared with the pool
|
|
1861
|
+
// wherever idToNode is.
|
|
1862
|
+
struct ChainKey {
|
|
1863
|
+
uint32_t parent;
|
|
1864
|
+
const oscad::Position* site;
|
|
1865
|
+
bool operator==(const ChainKey& o) const { return parent == o.parent && site == o.site; }
|
|
1866
|
+
};
|
|
1867
|
+
struct ChainKeyHash {
|
|
1868
|
+
std::size_t operator()(const ChainKey& k) const {
|
|
1869
|
+
return std::hash<uint32_t>{}(k.parent) ^
|
|
1870
|
+
(std::hash<const void*>{}(k.site) << 1);
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
std::unordered_map<ChainKey, uint32_t, ChainKeyHash> chainIndex_;
|
|
1874
|
+
|
|
1793
1875
|
// Count of callStack_ entries that ACTUALLY cost native C++ stack --
|
|
1794
1876
|
// i.e. pushed with skipDepthGuard=false (enterUserCall's own
|
|
1795
1877
|
// interpreted-call sites: evalUserFunctionCore, evalUserModule).
|
|
@@ -47,11 +47,43 @@ class _Position:
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class _DeclNode:
|
|
50
|
-
__slots__ = ("name", "position")
|
|
50
|
+
__slots__ = ("name", "position", "call_sites")
|
|
51
51
|
|
|
52
|
-
def __init__(self, name, position):
|
|
52
|
+
def __init__(self, name, position, call_sites=()):
|
|
53
53
|
self.name = name
|
|
54
54
|
self.position = position
|
|
55
|
+
#: For a geometry node: the whole call chain behind it, INNERMOST
|
|
56
|
+
#: frame first, each a `_CallFrame`. Empty for geometry written at
|
|
57
|
+
#: top level, where `position` is already the user's own source.
|
|
58
|
+
#:
|
|
59
|
+
#: `position` names the node that PRODUCED the geometry, which for
|
|
60
|
+
#: anything a library builds is inside that library -- a plain
|
|
61
|
+
#: `cube(10)` lands in BOSL2's builtins.scad once BOSL2 is included.
|
|
62
|
+
#: A caller that wants a line to show the user walks this and picks
|
|
63
|
+
#: the level it can display: the last frame in the running script
|
|
64
|
+
#: for an ordinary pick, a deeper one for someone reading the
|
|
65
|
+
#: library itself. Library frames are included deliberately.
|
|
66
|
+
self.call_sites = tuple(call_sites)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def call_site(self):
|
|
70
|
+
"""The innermost frame, or None. Convenience for a caller that does
|
|
71
|
+
not care about the chain."""
|
|
72
|
+
return self.call_sites[0] if self.call_sites else None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class _CallFrame(_Position):
|
|
76
|
+
"""One call in a chain: a position, plus whether it is a MODULE call.
|
|
77
|
+
|
|
78
|
+
A function frame (`_find_anchor`, an `assert`) has no geometry of its
|
|
79
|
+
own to drag, so a gizmo should decline it even though it is worth
|
|
80
|
+
showing.
|
|
81
|
+
"""
|
|
82
|
+
__slots__ = ("is_module",)
|
|
83
|
+
|
|
84
|
+
def __init__(self, line, column, origin, start_offset, end_offset, is_module):
|
|
85
|
+
super().__init__(line, column, origin, start_offset, end_offset)
|
|
86
|
+
self.is_module = is_module
|
|
55
87
|
|
|
56
88
|
|
|
57
89
|
class OscObject:
|
|
@@ -635,8 +667,13 @@ class Evaluator:
|
|
|
635
667
|
# originalID -> a node with `.position` (start/end offsets) for WYSIWYG
|
|
636
668
|
# picking and gizmo write-back.
|
|
637
669
|
id_to_node = {
|
|
638
|
-
oid: _DeclNode(
|
|
639
|
-
|
|
670
|
+
oid: _DeclNode(
|
|
671
|
+
None,
|
|
672
|
+
_Position(line, column, origin, start, end),
|
|
673
|
+
[_CallFrame(cl, cc, co, cs, ce, mod)
|
|
674
|
+
for (cs, ce, cl, cc, co, mod) in chain],
|
|
675
|
+
)
|
|
676
|
+
for oid, (start, end, line, column, origin, chain) in id_spans.items()
|
|
640
677
|
}
|
|
641
678
|
return bodies, id_to_node
|
|
642
679
|
|
|
@@ -1400,6 +1400,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
|
|
|
1400
1400
|
treeNode->node = site.node;
|
|
1401
1401
|
treeNode->isBuiltin = true;
|
|
1402
1402
|
treeNode->warnEntry = ev.currentWarnEntry();
|
|
1403
|
+
treeNode->callChain = ev.internCurrentCallChain();
|
|
1403
1404
|
treeNode->children = std::move(children);
|
|
1404
1405
|
treeNode->params = std::move(pending.params);
|
|
1405
1406
|
treeNode->uncacheable = uncacheable;
|
|
@@ -1503,6 +1504,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
|
|
|
1503
1504
|
treeNode->node = site.node;
|
|
1504
1505
|
treeNode->isBuiltin = true;
|
|
1505
1506
|
treeNode->warnEntry = ev.currentWarnEntry();
|
|
1507
|
+
treeNode->callChain = ev.internCurrentCallChain();
|
|
1506
1508
|
treeNode->children = std::move(children);
|
|
1507
1509
|
treeNode->params = std::move(params);
|
|
1508
1510
|
treeNode->uncacheable = uncacheable;
|
|
@@ -68,7 +68,8 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
68
68
|
if (node.node && !measuring_) {
|
|
69
69
|
auto producer = cacheProducer_.find(*key);
|
|
70
70
|
restampCachedIds(node.bodies, *node.node,
|
|
71
|
-
producer == cacheProducer_.end() ? nullptr : producer->second
|
|
71
|
+
producer == cacheProducer_.end() ? nullptr : producer->second,
|
|
72
|
+
node.callChain);
|
|
72
73
|
}
|
|
73
74
|
} else {
|
|
74
75
|
// Recurse into children first (bottom-up) -- populates each
|
|
@@ -95,9 +96,12 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
95
96
|
// can name that line. Restored after (rather than left set)
|
|
96
97
|
// so a sibling generated at top level doesn't inherit it.
|
|
97
98
|
const oscad::Position* savedWarnEntry = generateWarnEntry;
|
|
99
|
+
const uint32_t savedCallChain = generateCallChain;
|
|
98
100
|
generateWarnEntry = node.warnEntry;
|
|
101
|
+
generateCallChain = node.callChain;
|
|
99
102
|
node.bodies = it->second(*this, node.params, node.children, *node.node);
|
|
100
103
|
generateWarnEntry = savedWarnEntry;
|
|
104
|
+
generateCallChain = savedCallChain;
|
|
101
105
|
} else {
|
|
102
106
|
// No registered GenerateFn for this kind (a builtin with
|
|
103
107
|
// display-only semantics like render(), or a non-builtin
|
|
@@ -331,7 +335,7 @@ std::shared_ptr<const std::vector<ColoredBody>> remapParts(const std::vector<Col
|
|
|
331
335
|
} // namespace
|
|
332
336
|
|
|
333
337
|
void Evaluator::restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::ASTNode& node,
|
|
334
|
-
const oscad::ASTNode* producer) {
|
|
338
|
+
const oscad::ASTNode* producer, uint32_t callChain) {
|
|
335
339
|
// A cache hit hands back the geometry AND the originalIDs of whichever
|
|
336
340
|
// call site first produced it. Those IDs are provenance -- "which node
|
|
337
341
|
// made this" -- not content, so two identical shapes at two call sites
|
|
@@ -374,6 +378,12 @@ void Evaluator::restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::
|
|
|
374
378
|
auto old = idToNode.find(id);
|
|
375
379
|
idToNode[fresh] =
|
|
376
380
|
(old != idToNode.end() && old->second != producer) ? old->second : &node;
|
|
381
|
+
// Unlike idToNode, this is the call site that reached the
|
|
382
|
+
// geometry NOW: whichever node the cached copy is being
|
|
383
|
+
// reused at is the line the user would be shown, whatever
|
|
384
|
+
// produced the original. A module called twice really is
|
|
385
|
+
// two different call sites.
|
|
386
|
+
idToCallChain[fresh] = callChain;
|
|
377
387
|
// The colour has to be re-recorded too, or a later merge
|
|
378
388
|
// cannot tell this body's runs apart from uncoloured ones
|
|
379
389
|
// (attachTriColors looks runs up here). Across renders
|
|
@@ -453,6 +463,7 @@ ColoredBody Evaluator::tagGenerated(manifold::Manifold body, const oscad::ASTNod
|
|
|
453
463
|
for (uint32_t originalId : mesh.runOriginalID) {
|
|
454
464
|
idToNode[originalId] = &node;
|
|
455
465
|
idToColor[originalId] = color;
|
|
466
|
+
idToCallChain[originalId] = generateCallChain;
|
|
456
467
|
}
|
|
457
468
|
}
|
|
458
469
|
ColoredBody cb;
|
|
@@ -474,6 +485,7 @@ ColoredBody Evaluator::tagDisplayOnly(manifold::MeshGL mesh, const oscad::ASTNod
|
|
|
474
485
|
if (!measuring_) {
|
|
475
486
|
idToNode[originalId] = &node;
|
|
476
487
|
idToColor[originalId] = color;
|
|
488
|
+
idToCallChain[originalId] = generateCallChain;
|
|
477
489
|
}
|
|
478
490
|
|
|
479
491
|
ColoredBody cb;
|
|
@@ -68,6 +68,7 @@ void Evaluator::buildTreeNode(const std::string& kind, const oscad::ASTNode& nod
|
|
|
68
68
|
treeNode->node = &node;
|
|
69
69
|
treeNode->isBuiltin = true;
|
|
70
70
|
treeNode->warnEntry = currentWarnEntry();
|
|
71
|
+
treeNode->callChain = internCurrentCallChain();
|
|
71
72
|
treeNode->children = std::move(children);
|
|
72
73
|
treeNode->params = std::move(params);
|
|
73
74
|
treeNode->uncacheable = uncacheable;
|
|
@@ -139,6 +140,7 @@ void Evaluator::evalModularCall(const oscad::ModularCall& node, EvalContext& ctx
|
|
|
139
140
|
treeNode->node = &node;
|
|
140
141
|
treeNode->isBuiltin = true;
|
|
141
142
|
treeNode->warnEntry = currentWarnEntry();
|
|
143
|
+
treeNode->callChain = internCurrentCallChain();
|
|
142
144
|
treeNode->uncacheable = uncacheable;
|
|
143
145
|
treeNode->children = std::move(children);
|
|
144
146
|
treeNode->params = std::move(params);
|
|
@@ -164,6 +166,7 @@ void Evaluator::spliceModuleChildren(std::vector<std::unique_ptr<CSGNode>> child
|
|
|
164
166
|
unionNode->node = &callNode;
|
|
165
167
|
unionNode->isBuiltin = false;
|
|
166
168
|
unionNode->warnEntry = currentWarnEntry();
|
|
169
|
+
unionNode->callChain = internCurrentCallChain();
|
|
167
170
|
unionNode->uncacheable = std::any_of(children.begin(), children.end(), [](const auto& c) { return c->uncacheable; });
|
|
168
171
|
unionNode->children = std::move(children);
|
|
169
172
|
setTreeDepthOrThrow(*unionNode, callNode);
|
|
@@ -362,9 +362,7 @@ void meshToArrays(const manifold::MeshGL& mesh, std::vector<float>& verts, std::
|
|
|
362
362
|
tris = mesh.triVerts;
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
-
bool boxesOverlap(const manifold::
|
|
366
|
-
const manifold::Box ba = a.BoundingBox();
|
|
367
|
-
const manifold::Box bb = b.BoundingBox();
|
|
365
|
+
bool boxesOverlap(const manifold::Box& ba, const manifold::Box& bb) {
|
|
368
366
|
return !(ba.max.x < bb.min.x || bb.max.x < ba.min.x || ba.max.y < bb.min.y || bb.max.y < ba.min.y ||
|
|
369
367
|
ba.max.z < bb.min.z || bb.max.z < ba.min.z);
|
|
370
368
|
}
|
|
@@ -512,25 +510,42 @@ std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& b
|
|
|
512
510
|
c.key = keys[0];
|
|
513
511
|
claimedGroups.push_back(std::move(c));
|
|
514
512
|
} else {
|
|
515
|
-
// Reverse order
|
|
516
|
-
//
|
|
517
|
-
//
|
|
518
|
-
|
|
513
|
+
// Reverse order is what makes the LATER body win: by the time
|
|
514
|
+
// an earlier one is reached, everything after it has already
|
|
515
|
+
// taken its volume.
|
|
516
|
+
//
|
|
517
|
+
// Each body is cut by the later ones that actually REACH it, not
|
|
518
|
+
// by a running union of all of them. The running union was
|
|
519
|
+
// quadratic -- it grows to span the whole model, so every one of
|
|
520
|
+
// n subtractions faced O(n) geometry however little the parts
|
|
521
|
+
// really touched, and a 100-body 3MF spent 6.6s on it (#177).
|
|
522
|
+
//
|
|
523
|
+
// The result is identical: a later body whose bounding box is
|
|
524
|
+
// disjoint from this one cannot intersect it, and subtracting a
|
|
525
|
+
// disjoint solid removes no volume. Bounding boxes settle that in
|
|
526
|
+
// nanoseconds, so what is left is proportional to how much the
|
|
527
|
+
// model overlaps rather than to how many parts it has.
|
|
528
|
+
//
|
|
529
|
+
// Skipping the subtraction entirely when nothing overlaps is not
|
|
530
|
+
// just a shortcut: `A - disjoint B` returns A's volume but
|
|
531
|
+
// REORDERS its triangle list, which throws away any per-triangle
|
|
532
|
+
// colours A carried. Most models are mostly disjoint parts, so
|
|
533
|
+
// without this a two-tone body lost its colours the moment any
|
|
534
|
+
// other differently-coloured body existed.
|
|
535
|
+
std::vector<manifold::Box> boxes;
|
|
536
|
+
boxes.reserve(solids.size());
|
|
537
|
+
for (const Solid& s : solids) boxes.push_back(s.man.BoundingBox());
|
|
538
|
+
|
|
519
539
|
std::vector<Claimed> owned;
|
|
540
|
+
std::vector<manifold::Manifold> blockers;
|
|
520
541
|
for (size_t n = solids.size(); n-- > 0;) {
|
|
521
542
|
Solid& s = solids[n];
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
// cannot overlap is not just a shortcut: `A - disjoint
|
|
526
|
-
// B` returns A's volume but REORDERS its triangle list,
|
|
527
|
-
// which throws away any per-triangle colours A carried.
|
|
528
|
-
// Most models are mostly disjoint parts, so without this
|
|
529
|
-
// a two-tone body lost its colours the moment any other
|
|
530
|
-
// differently-coloured body existed.
|
|
531
|
-
if (boxesOverlap(s.man, *claimed)) piece = s.man - *claimed;
|
|
543
|
+
blockers.clear();
|
|
544
|
+
for (size_t m = n + 1; m < solids.size(); ++m) {
|
|
545
|
+
if (boxesOverlap(boxes[n], boxes[m])) blockers.push_back(solids[m].man);
|
|
532
546
|
}
|
|
533
|
-
|
|
547
|
+
manifold::Manifold piece = s.man;
|
|
548
|
+
if (!blockers.empty()) piece = s.man - addAll(blockers);
|
|
534
549
|
if (piece.IsEmpty()) continue;
|
|
535
550
|
Claimed c;
|
|
536
551
|
c.man = std::move(piece);
|
|
@@ -79,20 +79,56 @@ size_t countUnwelded(const M& m) {
|
|
|
79
79
|
// The faces around `v`, as (prev, next) pairs on the opposite edge. The link
|
|
80
80
|
// is one cycle when following those pairs from any starting corner visits
|
|
81
81
|
// all of them; more than one walk means the surface pinches at v.
|
|
82
|
+
//
|
|
83
|
+
// `vertTris` is CSR: the triangles around vertex v are
|
|
84
|
+
// vertTriList[vertTriStart[v] .. vertTriStart[v+1]).
|
|
85
|
+
//
|
|
86
|
+
// Every buffer here is hoisted out of the loop and cleared per vertex. The
|
|
87
|
+
// straightforward spelling -- an unordered_map of vectors for the link and a
|
|
88
|
+
// std::set for the walk, both built fresh per vertex -- allocated on the
|
|
89
|
+
// order of a dozen blocks for each of a mesh's vertices, millions of them on
|
|
90
|
+
// a 200K-triangle model, to answer a question about roughly six neighbours.
|
|
91
|
+
// Same reason the edge and face counts below collect flat and sort; see
|
|
92
|
+
// their comment.
|
|
82
93
|
template <typename M>
|
|
83
94
|
size_t countPinched(const M& m,
|
|
84
|
-
const std::vector<
|
|
95
|
+
const std::vector<size_t>& vertTriStart,
|
|
96
|
+
const std::vector<size_t>& vertTriList) {
|
|
85
97
|
size_t pinched = 0;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
const size_t nVerts = vertTriStart.empty() ? 0 : vertTriStart.size() - 1;
|
|
99
|
+
|
|
100
|
+
std::vector<Vert> nodes; // the distinct neighbours of v
|
|
101
|
+
std::vector<size_t> parent; // union-find over indices into `nodes`
|
|
102
|
+
std::vector<std::pair<uint32_t, uint32_t>> edges;
|
|
103
|
+
|
|
104
|
+
// Linear search, not a map: a vertex has a handful of neighbours, and at
|
|
105
|
+
// that size a scan beats hashing outright -- no allocation, no hashing,
|
|
106
|
+
// and the whole thing stays in cache.
|
|
107
|
+
const auto indexOf = [&](Vert w) -> uint32_t {
|
|
108
|
+
for (size_t i = 0; i < nodes.size(); ++i) {
|
|
109
|
+
if (nodes[i] == w) return static_cast<uint32_t>(i);
|
|
110
|
+
}
|
|
111
|
+
nodes.push_back(w);
|
|
112
|
+
parent.push_back(parent.size());
|
|
113
|
+
return static_cast<uint32_t>(nodes.size() - 1);
|
|
114
|
+
};
|
|
115
|
+
const auto find = [&](size_t i) {
|
|
116
|
+
while (parent[i] != i) {
|
|
117
|
+
parent[i] = parent[parent[i]]; // path halving
|
|
118
|
+
i = parent[i];
|
|
119
|
+
}
|
|
120
|
+
return i;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
for (Vert v = 0; v < nVerts; ++v) {
|
|
124
|
+
const size_t begin = vertTriStart[v], end = vertTriStart[v + 1];
|
|
125
|
+
if (end - begin < 2) continue;
|
|
126
|
+
nodes.clear();
|
|
127
|
+
parent.clear();
|
|
128
|
+
edges.clear();
|
|
129
|
+
for (size_t k = begin; k < end; ++k) {
|
|
94
130
|
Vert w[3];
|
|
95
|
-
triVerts(m,
|
|
131
|
+
triVerts(m, vertTriList[k], w);
|
|
96
132
|
Vert a = 0, b = 0;
|
|
97
133
|
int found = 0;
|
|
98
134
|
for (int i = 0; i < 3; ++i) {
|
|
@@ -100,21 +136,20 @@ size_t countPinched(const M& m,
|
|
|
100
136
|
(found++ == 0 ? a : b) = w[i];
|
|
101
137
|
}
|
|
102
138
|
if (found < 2) continue; // degenerate; counted elsewhere
|
|
103
|
-
|
|
104
|
-
link[b].push_back(a);
|
|
139
|
+
edges.emplace_back(indexOf(a), indexOf(b));
|
|
105
140
|
}
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
141
|
+
if (nodes.empty()) continue;
|
|
142
|
+
// Undirected, so a reversed neighbour still links up -- winding is
|
|
143
|
+
// checked separately and should not show up as a pinch too.
|
|
144
|
+
for (const auto& e : edges) {
|
|
145
|
+
const size_t ra = find(e.first), rb = find(e.second);
|
|
146
|
+
if (ra != rb) parent[ra] = rb;
|
|
147
|
+
}
|
|
148
|
+
size_t components = 0;
|
|
149
|
+
for (size_t i = 0; i < parent.size(); ++i) {
|
|
150
|
+
if (find(i) == i) ++components;
|
|
116
151
|
}
|
|
117
|
-
if (
|
|
152
|
+
if (components > 1) ++pinched;
|
|
118
153
|
}
|
|
119
154
|
return pinched;
|
|
120
155
|
}
|
|
@@ -158,10 +193,16 @@ MeshDiagnosis checkMesh(const M& mesh) {
|
|
|
158
193
|
// index below; see its comment for the measurement.
|
|
159
194
|
std::vector<std::pair<uint64_t, bool>> uses; // {undirected edge, traversed a->b}
|
|
160
195
|
uses.reserve(tris * 3);
|
|
161
|
-
std::vector<std::vector<size_t>> vertTris(nVerts);
|
|
162
196
|
std::vector<std::array<Vert, 3>> seenFaces; // sorted-corner triples, deduplicated below
|
|
163
197
|
seenFaces.reserve(tris);
|
|
164
198
|
|
|
199
|
+
// The triangles around each vertex, CSR rather than a vector per vertex:
|
|
200
|
+
// one inner vector per vertex is an allocation per vertex, and they are
|
|
201
|
+
// only ever read back as a flat run. Counted here, filled after the scan.
|
|
202
|
+
std::vector<size_t> vertTriStart(nVerts + 1, 0);
|
|
203
|
+
std::vector<size_t> vertTriList;
|
|
204
|
+
vertTriList.reserve(tris * 3);
|
|
205
|
+
|
|
165
206
|
for (size_t t = 0; t < tris; ++t) {
|
|
166
207
|
if (degenerate(mesh, t)) ++d.degenerateFaces;
|
|
167
208
|
// Counted, but NOT removed from the topology. A zero-area triangle
|
|
@@ -181,12 +222,26 @@ MeshDiagnosis checkMesh(const M& mesh) {
|
|
|
181
222
|
seenFaces.push_back(sorted);
|
|
182
223
|
|
|
183
224
|
for (int i = 0; i < 3; ++i) {
|
|
184
|
-
if (v[i] < nVerts)
|
|
225
|
+
if (v[i] < nVerts) ++vertTriStart[v[i] + 1];
|
|
185
226
|
const Vert a = v[i], b = v[(i + 1) % 3];
|
|
186
227
|
uses.emplace_back(edgeKey(undirected(a, b)), a < b);
|
|
187
228
|
}
|
|
188
229
|
}
|
|
189
230
|
|
|
231
|
+
for (size_t i = 0; i < nVerts; ++i) vertTriStart[i + 1] += vertTriStart[i];
|
|
232
|
+
vertTriList.resize(vertTriStart[nVerts]);
|
|
233
|
+
{
|
|
234
|
+
std::vector<size_t> fill(vertTriStart.begin(), vertTriStart.end() - 1);
|
|
235
|
+
for (size_t t = 0; t < tris; ++t) {
|
|
236
|
+
if (repeatsAVertex(mesh, t)) continue; // skipped above too
|
|
237
|
+
Vert v[3];
|
|
238
|
+
triVerts(mesh, t, v);
|
|
239
|
+
for (int i = 0; i < 3; ++i) {
|
|
240
|
+
if (v[i] < nVerts) vertTriList[fill[v[i]]++] = t;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
190
245
|
// A face seen N times is N-1 duplicates, which is what insert().second
|
|
191
246
|
// counted one at a time.
|
|
192
247
|
std::sort(seenFaces.begin(), seenFaces.end());
|
|
@@ -214,7 +269,7 @@ MeshDiagnosis checkMesh(const M& mesh) {
|
|
|
214
269
|
i = j;
|
|
215
270
|
}
|
|
216
271
|
|
|
217
|
-
d.pinchedVertices = countPinched(mesh,
|
|
272
|
+
d.pinchedVertices = countPinched(mesh, vertTriStart, vertTriList);
|
|
218
273
|
d.unweldedVertices = countUnwelded(mesh);
|
|
219
274
|
return d;
|
|
220
275
|
}
|