openscad-cpp-evaluator 1.19.2__tar.gz → 1.20.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.19.2 → openscad_cpp_evaluator-1.20.0}/CLAUDE.md +27 -2
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/bindings/module.cpp +3 -2
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/colored_body.hpp +23 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/evaluator.hpp +16 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/python/openscad_cpp_evaluator/__init__.py +8 -2
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/booleans.cpp +106 -8
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/color.cpp +16 -24
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/transforms.cpp +17 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/csg_generate.cpp +91 -9
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_manifold_cache.cpp +41 -0
- openscad_cpp_evaluator-1.20.0/tests/test_multi_color_merge.cpp +255 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_python_bindings.py +11 -0
- openscad_cpp_evaluator-1.19.2/tests/test_multi_color_merge.cpp +0 -111
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/.DS_Store +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/uv.lock +0 -0
|
@@ -866,8 +866,33 @@ grep for `ponytail:`.
|
|
|
866
866
|
And the invariant behind both: **`triColors` must index the body's CURRENT mesh**. The renderer
|
|
867
867
|
masks its vertex arrays with it, so a stale length is an `IndexError` and a blank viewport rather
|
|
868
868
|
than a wrong colour. Anything that re-meshes clears it — `simplify()`, `minkowski_difference()`,
|
|
869
|
-
and
|
|
870
|
-
|
|
869
|
+
and `bodyToDict` drops a mismatched array as a backstop for whichever one gets missed next. The
|
|
870
|
+
ID-retag rebuild on a cache hit (`restampCachedIds`) used to clear it too when the triangle count
|
|
871
|
+
moved; it now **rebuilds it from the runs**, which survive the rebuild, and **re-records every
|
|
872
|
+
run's colour against the fresh IDs** from the cached body itself (its `color`, or per run what its
|
|
873
|
+
`triColors` carry) — this render's `idToColor` has never heard of an ID an earlier render minted,
|
|
874
|
+
and without it every cached operand looked uncoloured to the next merge: an edited three-colour
|
|
875
|
+
`difference()` lost the unchanged subtrahend's colour on the first re-render and went flat on the
|
|
876
|
+
next (BelfrySCAD #412).
|
|
877
|
+
**An uncoloured subtrahend paints the faces it exposes the cut green** (`kCutFaceColor`,
|
|
878
|
+
`colored_body.hpp`: #9DCB51, the reference's CGAL back-face green, which its preview shows and its
|
|
879
|
+
colour-preserving render keeps — measured in a 3MF export from 2026.02.01), recorded against the
|
|
880
|
+
tool's runs before the merge, and the merge is forced to look even when every operand's own colour
|
|
881
|
+
agrees. It used to fall to the default geometry colour, so a cut through an uncoloured part could
|
|
882
|
+
not be seen as a cut. A coloured tool still paints its cut with its own colour, as the reference
|
|
883
|
+
does.
|
|
884
|
+
**`Evaluator::keepMinuendColor`** (bindings: `Evaluator(keep_minuend_color=True)`) is the viewer
|
|
885
|
+
option the reference cannot offer (openscad/openscad#4798 — OpenCSG preview is pixels in a frame
|
|
886
|
+
buffer): `difference()` paints its cut faces with the **minuend's** colour instead. Since
|
|
887
|
+
(A ∪ B) − S = (A − S) ∪ (B − S), each minuend part is differenced on its own and its cut-face runs
|
|
888
|
+
are re-minted under fresh IDs carrying that part's colour (`finishKeepMinuend`, `booleans.cpp`; the
|
|
889
|
+
source node stays the tool's, so a click on a cut face still finds it), then the parts are unioned.
|
|
890
|
+
A `union()` built in this mode remembers what it merged (`ColoredBody::mergedFrom`, unmerged and
|
|
891
|
+
carried through `generateTransform`/`generateColor`/`restampCachedIds`), so `difference() {
|
|
892
|
+
union() { red; blue; } tool }` cuts red on the red side and blue on the blue side; any op that builds
|
|
893
|
+
a new body drops the record and that body is one part. Off by default — a viewer option, not a
|
|
894
|
+
language feature, so it is honest for every existing script — and cache keys are prefixed per
|
|
895
|
+
mode so the two never serve each other's bodies. Costs one boolean per minuend part.
|
|
871
896
|
**2D cannot use any of that, and keeps colour geometrically instead** (`Part2d`, same file): a
|
|
872
897
|
`CrossSection` is contours, not a mesh, so nothing in it remembers which child an edge came from
|
|
873
898
|
and there is no provenance for `attachTriColors` to read back. So the 2D accumulator holds **one
|
|
@@ -477,7 +477,7 @@ nb::object coverageResultToPy(const std::optional<oscadeval::CoverageResult>& cr
|
|
|
477
477
|
|
|
478
478
|
nb::object evaluate(const std::string& path, nb::dict viewportParams,
|
|
479
479
|
std::shared_ptr<oscadeval::ManifoldCache> manifoldCache, bool profile,
|
|
480
|
-
bool generate, bool strictCommas, bool coverage) {
|
|
480
|
+
bool generate, bool strictCommas, bool coverage, bool keepMinuendColor) {
|
|
481
481
|
std::unordered_map<std::string, oscadeval::Value> vp = toViewportParams(viewportParams);
|
|
482
482
|
|
|
483
483
|
std::vector<oscadeval::ColoredBody> bodies;
|
|
@@ -501,6 +501,7 @@ nb::object evaluate(const std::string& path, nb::dict viewportParams,
|
|
|
501
501
|
oscad::ParsedProgram program = oscad::getProgramFromFile(path);
|
|
502
502
|
oscadeval::ResolvedUseScopes used = oscadeval::resolveUseScopes(program.nodes, path, logFn);
|
|
503
503
|
oscadeval::Evaluator ev(logFn, nullptr, manifoldCache, oscadeval::DebugHooks{}, profile, coverage);
|
|
504
|
+
ev.keepMinuendColor = keepMinuendColor;
|
|
504
505
|
ev.setUsedFileGlobals(used.usedFileGlobals);
|
|
505
506
|
oscadeval::EvalContext ctx = oscadeval::EvalContext::makeRoot(used.rootScope.get());
|
|
506
507
|
bodies = oscadeval::toRenderableBodies(ev.evaluate(used.processedNodes, ctx, vp, generate));
|
|
@@ -955,7 +956,7 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
|
|
|
955
956
|
|
|
956
957
|
m.def("evaluate", &evaluate, nb::arg("path"), nb::arg("viewport_params"), nb::arg("manifold_cache") = nullptr,
|
|
957
958
|
nb::arg("profile") = false, nb::arg("generate") = true, nb::arg("strict_commas") = false,
|
|
958
|
-
nb::arg("coverage") = false,
|
|
959
|
+
nb::arg("coverage") = false, nb::arg("keep_minuend_color") = false,
|
|
959
960
|
"Evaluate a .scad file; return (bodies, echoes, id_to_node, csg_tree, profile_result, dyn, dyn_explicit, "
|
|
960
961
|
"geometry, coverage_result).\n"
|
|
961
962
|
"coverage=True records which statements, branch arms and bodies ran: coverage_result is a dict "
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include <manifold/manifold.h>
|
|
7
7
|
|
|
8
8
|
#include <array>
|
|
9
|
+
#include <memory>
|
|
9
10
|
#include <optional>
|
|
10
11
|
#include <vector>
|
|
11
12
|
|
|
@@ -23,6 +24,18 @@ enum class BodyRole { Normal, Highlight, Background, ShowOnly };
|
|
|
23
24
|
// `role`/`triColors` are later-phase fields (modifier tagging -- Phase 3;
|
|
24
25
|
// multi-color CSG merges -- Phase 3/8) included now so the struct shape
|
|
25
26
|
// doesn't need reworking when those land.
|
|
27
|
+
// The colour drawn for geometry with no explicit color() wherever a live
|
|
28
|
+
// theme cannot be consulted -- per-triangle colour arrays, which are baked.
|
|
29
|
+
// Matches the reference's SceneRenderer._default_color.
|
|
30
|
+
inline constexpr std::array<float, 4> kDefaultGeometryColor{0.9f, 0.85f, 0.1f, 1.0f};
|
|
31
|
+
|
|
32
|
+
// The colour of a face a difference() exposed when the subtrahend that cut
|
|
33
|
+
// it carried no colour of its own: the reference's CGAL "back face" green,
|
|
34
|
+
// the same one its preview paints and its colour-preserving render (and
|
|
35
|
+
// 3MF export, measured on 2026.02.01) keeps. Distinct from the default
|
|
36
|
+
// geometry colour so a cut reads as a cut.
|
|
37
|
+
inline constexpr std::array<float, 4> kCutFaceColor{157.0f / 255.0f, 203.0f / 255.0f, 81.0f / 255.0f, 1.0f};
|
|
38
|
+
|
|
26
39
|
struct ColoredBody {
|
|
27
40
|
std::optional<manifold::Manifold> body;
|
|
28
41
|
std::optional<std::array<float, 4>> color; // RGBA; nullopt = "no explicit color() -- follow the live theme"
|
|
@@ -53,6 +66,16 @@ struct ColoredBody {
|
|
|
53
66
|
BodyRole role = BodyRole::Normal;
|
|
54
67
|
std::optional<std::vector<std::array<float, 4>>> triColors; // per-triangle RGBA, multi-color CSG merges only
|
|
55
68
|
|
|
69
|
+
// The operands a union() merged into this body, kept unmerged, each as
|
|
70
|
+
// it was when merged (colour and transforms carried along since --
|
|
71
|
+
// generateTransform and generateColor map over them). Set only under
|
|
72
|
+
// Evaluator::keepMinuendColor, where a difference() cuts each part on
|
|
73
|
+
// its own so its cut faces take THAT part's colour; nothing else reads
|
|
74
|
+
// it, and any other op that builds a new body from this one drops it.
|
|
75
|
+
// Shared, never mutated in place: an op that changes it makes a new
|
|
76
|
+
// vector.
|
|
77
|
+
std::shared_ptr<const std::vector<ColoredBody>> mergedFrom;
|
|
78
|
+
|
|
56
79
|
// Whether `body` is empty, once anything has asked (isEmptyBody,
|
|
57
80
|
// csg_generate.cpp). Asking Manifold is not free: every accessor goes
|
|
58
81
|
// through GetImpl(), which MATERIALIZES a lazy transform (a full copy of
|
|
@@ -294,6 +294,22 @@ public:
|
|
|
294
294
|
std::size_t generatedNodeCount = 0;
|
|
295
295
|
std::unordered_map<uint32_t, std::optional<std::array<float, 4>>> idToColor;
|
|
296
296
|
|
|
297
|
+
// Record `rgba` against every run ID of `b`, so a later merge can still
|
|
298
|
+
// tell the body's triangles apart once the body itself is gone
|
|
299
|
+
// (attachTriColors looks runs up in idToColor). Cheap for a body that is
|
|
300
|
+
// still one original; a merged body pays a GetMeshGL().
|
|
301
|
+
void recordRunColors(ColoredBody& b, const std::optional<std::array<float, 4>>& rgba);
|
|
302
|
+
|
|
303
|
+
// difference() keeps the minuend's colour on the faces a subtrahend
|
|
304
|
+
// exposes, instead of the subtrahend's colour (or the cut green). Off
|
|
305
|
+
// by default: OpenSCAD paints cut faces with the cutter's colour, and
|
|
306
|
+
// that is what a script author sees there. A viewer option, not a
|
|
307
|
+
// language feature -- it is honest for every existing script. Costs one
|
|
308
|
+
// boolean per coloured minuend part instead of one per difference,
|
|
309
|
+
// since (A ∪ B) − S = (A − S) ∪ (B − S) is how each part's cut faces get
|
|
310
|
+
// that part's colour. Cached results are keyed apart per mode.
|
|
311
|
+
bool keepMinuendColor = false;
|
|
312
|
+
|
|
297
313
|
// Called by primitive-construction generate functions (cube, sphere,
|
|
298
314
|
// cylinder, polyhedron) right after building a brand-new Manifold:
|
|
299
315
|
// reads back its mesh's runOriginalID run(s) and records each against
|
|
@@ -532,7 +532,8 @@ class Evaluator:
|
|
|
532
532
|
"""
|
|
533
533
|
|
|
534
534
|
def __init__(self, echo_fn=None, debug_hook=None, error_break_fn=None, return_hook=None,
|
|
535
|
-
manifold_cache=None, profile=False, fast_continue_signal=None, coverage=False
|
|
535
|
+
manifold_cache=None, profile=False, fast_continue_signal=None, coverage=False,
|
|
536
|
+
keep_minuend_color=False):
|
|
536
537
|
self._echo_fn = echo_fn
|
|
537
538
|
self._debug_hook = debug_hook
|
|
538
539
|
self._error_break_fn = error_break_fn
|
|
@@ -541,6 +542,11 @@ class Evaluator:
|
|
|
541
542
|
self._profile = profile
|
|
542
543
|
self._fast_continue_signal = fast_continue_signal
|
|
543
544
|
self._coverage = coverage
|
|
545
|
+
# keep_minuend_color=True: difference() paints the faces a
|
|
546
|
+
# subtrahend exposes with the MINUEND's colour rather than the
|
|
547
|
+
# subtrahend's (or the cut green). A viewer option; see
|
|
548
|
+
# Evaluator::keepMinuendColor in evaluator.hpp.
|
|
549
|
+
self._keep_minuend_color = keep_minuend_color
|
|
544
550
|
self.csg_tree = []
|
|
545
551
|
self.profile_result = None
|
|
546
552
|
# coverage=True: after evaluate(), {"spans": [...], "files": [...],
|
|
@@ -594,7 +600,7 @@ class Evaluator:
|
|
|
594
600
|
(body_dicts, echoes, id_spans, csg_tree, profile_result, dyn,
|
|
595
601
|
dyn_explicit, geometry, coverage_result) = _ext.evaluate(
|
|
596
602
|
source_path, vp, self._manifold_cache, self._profile, generate,
|
|
597
|
-
strict_commas, self._coverage)
|
|
603
|
+
strict_commas, self._coverage, self._keep_minuend_color)
|
|
598
604
|
# The evaluated bodies, still on the C++ side. Stashed like
|
|
599
605
|
# csg_tree/profile_result rather than returned, so
|
|
600
606
|
# evaluate()'s own 2-tuple result is unchanged -- callers
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <array>
|
|
8
8
|
#include <cstdint>
|
|
9
9
|
#include <optional>
|
|
10
|
+
#include <unordered_map>
|
|
10
11
|
|
|
11
12
|
namespace oscadeval {
|
|
12
13
|
|
|
@@ -150,14 +151,6 @@ CSGParams resolveCsg(Evaluator& ev, const oscad::ModularCall& node, EvalContext&
|
|
|
150
151
|
|
|
151
152
|
namespace {
|
|
152
153
|
|
|
153
|
-
// Matches the reference's own SceneRenderer._default_color -- shown for
|
|
154
|
-
// geometry with no explicit color() override. ColoredBody::color normally
|
|
155
|
-
// stays nullopt for uncolored geometry so a live renderer can resolve it
|
|
156
|
-
// against its own color theme, but a per-triangle triColors override has
|
|
157
|
-
// no such live-resolution mechanism, so a concrete fallback is needed here
|
|
158
|
-
// specifically.
|
|
159
|
-
constexpr std::array<float, 4> kDefaultGeometryColor{0.9f, 0.85f, 0.1f, 1.0f};
|
|
160
|
-
|
|
161
154
|
// After a real boolean merge, `cb.color` is just one arbitrary child's
|
|
162
155
|
// color (the first contributing operand above) -- every other child's own
|
|
163
156
|
// color is otherwise lost, e.g. union()-ing an opaque cube with a
|
|
@@ -207,6 +200,61 @@ void attachTriColors(Evaluator& ev, ColoredBody& cb) {
|
|
|
207
200
|
cb.triColors = std::move(triColors);
|
|
208
201
|
}
|
|
209
202
|
|
|
203
|
+
// keepMinuendColor: the leaf parts of a minuend operand -- the operand
|
|
204
|
+
// itself, or what a union() merged it from, recursively.
|
|
205
|
+
void collectKeepParts(const ColoredBody& b, std::vector<ColoredBody>& out) {
|
|
206
|
+
if (b.mergedFrom) {
|
|
207
|
+
for (const ColoredBody& part : *b.mergedFrom) collectKeepParts(part, out);
|
|
208
|
+
} else if (b.body) {
|
|
209
|
+
out.push_back(b);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// keepMinuendColor: one minuend part, differenced on its own.
|
|
214
|
+
struct KeepPart {
|
|
215
|
+
ColoredBody body;
|
|
216
|
+
std::vector<uint32_t> ownIds; // run IDs the part was born with; anything else on it afterwards is a cut face
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
std::vector<uint32_t> runIdsOf(const ColoredBody& b) {
|
|
220
|
+
const int original = b.body->OriginalID();
|
|
221
|
+
if (original >= 0) return {static_cast<uint32_t>(original)};
|
|
222
|
+
return b.body->GetMeshGL().runOriginalID;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Union the per-part differences back into one body, first re-minting each
|
|
226
|
+
// part's cut-face runs (the subtrahend's IDs, shared by every part's result)
|
|
227
|
+
// under fresh IDs carrying THAT part's colour, so attachTriColors can tell
|
|
228
|
+
// one part's cut faces from another's. The source node stays the
|
|
229
|
+
// subtrahend's: clicking a cut face still finds the tool that made it.
|
|
230
|
+
manifold::Manifold finishKeepMinuend(Evaluator& ev, std::vector<KeepPart>& parts) {
|
|
231
|
+
std::optional<manifold::Manifold> out;
|
|
232
|
+
for (KeepPart& part : parts) {
|
|
233
|
+
if (!part.body.body) continue;
|
|
234
|
+
manifold::MeshGL mesh = part.body.body->GetMeshGL();
|
|
235
|
+
if (mesh.triVerts.empty()) continue;
|
|
236
|
+
std::unordered_map<uint32_t, uint32_t> remap;
|
|
237
|
+
for (uint32_t& id : mesh.runOriginalID) {
|
|
238
|
+
if (std::find(part.ownIds.begin(), part.ownIds.end(), id) != part.ownIds.end()) continue;
|
|
239
|
+
auto found = remap.find(id);
|
|
240
|
+
if (found == remap.end()) {
|
|
241
|
+
const uint32_t fresh = manifold::Manifold::ReserveIDs(1);
|
|
242
|
+
auto node = ev.idToNode.find(id);
|
|
243
|
+
if (node != ev.idToNode.end()) ev.idToNode[fresh] = node->second;
|
|
244
|
+
// ponytail: a part that is itself a multi-colour merge
|
|
245
|
+
// gives its cut faces its first child's colour rather than
|
|
246
|
+
// the colour of whichever child the cut passed through.
|
|
247
|
+
ev.idToColor[fresh] = part.body.color;
|
|
248
|
+
found = remap.emplace(id, fresh).first;
|
|
249
|
+
}
|
|
250
|
+
id = found->second;
|
|
251
|
+
}
|
|
252
|
+
manifold::Manifold rebuilt(mesh);
|
|
253
|
+
out = out ? *out + rebuilt : rebuilt;
|
|
254
|
+
}
|
|
255
|
+
return out.value_or(manifold::Manifold());
|
|
256
|
+
}
|
|
257
|
+
|
|
210
258
|
// One colour's worth of 2D result.
|
|
211
259
|
//
|
|
212
260
|
// 3D recovers per-child colour AFTER the merge, from Manifold's own
|
|
@@ -294,6 +342,15 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
294
342
|
// only produce a uniformly coloured result.
|
|
295
343
|
std::optional<std::optional<std::array<float, 4>>> firstColor;
|
|
296
344
|
bool mixedColors = false;
|
|
345
|
+
// keepMinuendColor: the minuend's parts, each differenced on its own
|
|
346
|
+
// (see Evaluator::keepMinuendColor). Not while measuring: a render()
|
|
347
|
+
// expression only wants the volume, which is the same either way.
|
|
348
|
+
const bool keeping = op == "difference" && ev.keepMinuendColor && !ev.measuring();
|
|
349
|
+
std::vector<KeepPart> keepParts;
|
|
350
|
+
// ... and a union() built under that mode remembers what it merged, so
|
|
351
|
+
// a difference() it is the minuend of can cut each part on its own.
|
|
352
|
+
const bool rememberParts = op == "union" && ev.keepMinuendColor && !ev.measuring();
|
|
353
|
+
std::vector<ColoredBody> unionParts;
|
|
297
354
|
|
|
298
355
|
size_t stmtIndex = 0;
|
|
299
356
|
for (const Value& sizeVal : groupSizes) {
|
|
@@ -346,6 +403,20 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
346
403
|
if (!firstColor) firstColor = c.color;
|
|
347
404
|
else if (*firstColor != c.color) mixedColors = true;
|
|
348
405
|
}
|
|
406
|
+
// A subtrahend with no colour of its own paints the faces it
|
|
407
|
+
// exposes the cut green, as the reference does, rather than the
|
|
408
|
+
// default geometry colour -- otherwise a cut through an uncoloured
|
|
409
|
+
// part is invisible as a cut. Recorded against its runs, which is
|
|
410
|
+
// all attachTriColors will have left after the merge; and the
|
|
411
|
+
// merge must then look, even when every operand's own colour
|
|
412
|
+
// agrees.
|
|
413
|
+
if (op == "difference" && res3d && !ev.measuring()) {
|
|
414
|
+
for (ColoredBody& c : bodies3d) {
|
|
415
|
+
if (c.color || c.triColors) continue;
|
|
416
|
+
ev.recordRunColors(c, kCutFaceColor);
|
|
417
|
+
mixedColors = true;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
349
420
|
for (const ColoredBody& c : split.foreground) {
|
|
350
421
|
if (c.section) sections2d.push_back(c);
|
|
351
422
|
}
|
|
@@ -385,8 +456,27 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
385
456
|
cb.knownStatus = manifold::Manifold::Error::NoError; // every operand was
|
|
386
457
|
cb.knownEmpty = grpEmpty;
|
|
387
458
|
res3d = std::move(cb);
|
|
459
|
+
if (keeping) {
|
|
460
|
+
std::vector<ColoredBody> leaves;
|
|
461
|
+
for (const ColoredBody& c : bodies3d) collectKeepParts(c, leaves);
|
|
462
|
+
for (ColoredBody& leaf : leaves) {
|
|
463
|
+
std::vector<uint32_t> ids = runIdsOf(leaf);
|
|
464
|
+
keepParts.push_back({std::move(leaf), std::move(ids)});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
if (rememberParts) {
|
|
468
|
+
for (const ColoredBody& c : bodies3d) collectKeepParts(c, unionParts);
|
|
469
|
+
}
|
|
470
|
+
} else if (keeping) {
|
|
471
|
+
// The whole-minuend result above is never evaluated (Manifold
|
|
472
|
+
// is lazy); finishKeepMinuend replaces it after the loop.
|
|
473
|
+
for (KeepPart& part : keepParts) part.body.body = *part.body.body - grp;
|
|
474
|
+
res3d->knownEmpty.reset();
|
|
388
475
|
} else if (op == "union") {
|
|
389
476
|
res3d->body = *res3d->body + grp;
|
|
477
|
+
if (rememberParts) {
|
|
478
|
+
for (const ColoredBody& c : bodies3d) collectKeepParts(c, unionParts);
|
|
479
|
+
}
|
|
390
480
|
if (known(res3d->knownEmpty, false) || known(grpEmpty, false)) res3d->knownEmpty = false;
|
|
391
481
|
else if (known(res3d->knownEmpty, true) && known(grpEmpty, true)) res3d->knownEmpty = true;
|
|
392
482
|
else res3d->knownEmpty.reset();
|
|
@@ -423,6 +513,14 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
423
513
|
}
|
|
424
514
|
}
|
|
425
515
|
|
|
516
|
+
if (res3d && res3d->body && rememberParts && unionParts.size() > 1) {
|
|
517
|
+
res3d->mergedFrom = std::make_shared<const std::vector<ColoredBody>>(std::move(unionParts));
|
|
518
|
+
}
|
|
519
|
+
if (res3d && res3d->body && keeping && !keepParts.empty()) {
|
|
520
|
+
res3d->body = finishKeepMinuend(ev, keepParts);
|
|
521
|
+
res3d->knownEmpty.reset();
|
|
522
|
+
mixedColors = true; // attachTriColors still no-ops when every run agrees
|
|
523
|
+
}
|
|
426
524
|
if (res3d && res3d->body && mixedColors) attachTriColors(ev, *res3d);
|
|
427
525
|
|
|
428
526
|
std::vector<ColoredBody> result;
|
|
@@ -52,31 +52,19 @@ CSGParams resolveColor(Evaluator& ev, const oscad::ModularCall& node, EvalContex
|
|
|
52
52
|
|
|
53
53
|
namespace {
|
|
54
54
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// `union() { color("red") stroke(...); text(); }` had no red recorded for
|
|
66
|
-
// the stroke's runs at all, so every run looked identical, the merge kept
|
|
67
|
-
// the first child's colour, and the whole thing came out red.
|
|
68
|
-
void recordRunColors(Evaluator& ev, ColoredBody& b, const std::optional<std::array<float, 4>>& rgba) {
|
|
69
|
-
if (!b.body || bodyIsEmpty(b)) return;
|
|
70
|
-
// A body that is still one original knows its own ID without building
|
|
71
|
-
// a mesh -- the common case, and the cheap one.
|
|
72
|
-
const int original = b.body->OriginalID();
|
|
73
|
-
if (original >= 0) {
|
|
74
|
-
ev.idToColor[static_cast<uint32_t>(original)] = rgba;
|
|
75
|
-
return;
|
|
55
|
+
} // namespace
|
|
56
|
+
|
|
57
|
+
namespace {
|
|
58
|
+
std::shared_ptr<const std::vector<ColoredBody>> recolorParts(const std::vector<ColoredBody>& parts,
|
|
59
|
+
const std::optional<std::array<float, 4>>& rgba) {
|
|
60
|
+
std::vector<ColoredBody> out;
|
|
61
|
+
for (ColoredBody part : parts) {
|
|
62
|
+
part.color = rgba;
|
|
63
|
+
if (part.mergedFrom) part.mergedFrom = recolorParts(*part.mergedFrom, rgba);
|
|
64
|
+
out.push_back(std::move(part));
|
|
76
65
|
}
|
|
77
|
-
|
|
66
|
+
return std::make_shared<const std::vector<ColoredBody>>(std::move(out));
|
|
78
67
|
}
|
|
79
|
-
|
|
80
68
|
} // namespace
|
|
81
69
|
|
|
82
70
|
std::vector<ColoredBody> generateColor(Evaluator& ev, const CSGParams& params,
|
|
@@ -85,7 +73,11 @@ std::vector<ColoredBody> generateColor(Evaluator& ev, const CSGParams& params,
|
|
|
85
73
|
std::vector<ColoredBody> result;
|
|
86
74
|
for (ColoredBody b : flattenCsgTree(children)) {
|
|
87
75
|
b.color = rgba;
|
|
88
|
-
if (!ev.measuring()) recordRunColors(
|
|
76
|
+
if (!ev.measuring()) ev.recordRunColors(b, rgba);
|
|
77
|
+
// color() over a union() colours every part it was merged from
|
|
78
|
+
// (see ColoredBody::mergedFrom) -- the parts' runs are the body's
|
|
79
|
+
// runs, so recording once above already covers them.
|
|
80
|
+
if (b.mergedFrom) b.mergedFrom = recolorParts(*b.mergedFrom, rgba);
|
|
89
81
|
result.push_back(std::move(b));
|
|
90
82
|
}
|
|
91
83
|
return result;
|
|
@@ -349,6 +349,19 @@ CSGParams resolveTransform(Evaluator& ev, const oscad::ModularCall& node, EvalCo
|
|
|
349
349
|
return std::move(result.params);
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
namespace {
|
|
353
|
+
std::shared_ptr<const std::vector<ColoredBody>> transformParts(const std::vector<ColoredBody>& parts,
|
|
354
|
+
const std::string& name, const CallArgs& args) {
|
|
355
|
+
std::vector<ColoredBody> out;
|
|
356
|
+
for (ColoredBody part : parts) {
|
|
357
|
+
if (part.body) part.body = applyTransform3d(name, args, std::move(*part.body));
|
|
358
|
+
if (part.mergedFrom) part.mergedFrom = transformParts(*part.mergedFrom, name, args);
|
|
359
|
+
out.push_back(std::move(part));
|
|
360
|
+
}
|
|
361
|
+
return std::make_shared<const std::vector<ColoredBody>>(std::move(out));
|
|
362
|
+
}
|
|
363
|
+
} // namespace
|
|
364
|
+
|
|
352
365
|
std::vector<ColoredBody> generateTransform(Evaluator&, const CSGParams& params,
|
|
353
366
|
const std::vector<std::unique_ptr<CSGNode>>& children, const oscad::ASTNode&) {
|
|
354
367
|
const std::string& name = std::get<std::string>(params.at("name"));
|
|
@@ -391,6 +404,10 @@ std::vector<ColoredBody> generateTransform(Evaluator&, const CSGParams& params,
|
|
|
391
404
|
if (std::optional<manifold::mat3x4> m = transformMatrix3d(name, args))
|
|
392
405
|
transformMeshInPlace(*b.rawMesh, *m);
|
|
393
406
|
}
|
|
407
|
+
// The unmerged parts of a union move with it (see
|
|
408
|
+
// ColoredBody::mergedFrom). Recursion through this same
|
|
409
|
+
// function keeps their own parts moving too.
|
|
410
|
+
if (b.mergedFrom) b.mergedFrom = transformParts(*b.mergedFrom, name, args);
|
|
394
411
|
}
|
|
395
412
|
result.push_back(std::move(b));
|
|
396
413
|
}
|
|
@@ -46,6 +46,11 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
|
|
|
46
46
|
std::optional<std::string> key;
|
|
47
47
|
if (manifoldCache_ && !node.uncacheable) {
|
|
48
48
|
key = cacheKey(node);
|
|
49
|
+
// A difference() built with keepMinuendColor colours its cut
|
|
50
|
+
// faces differently, so the two modes must never serve each
|
|
51
|
+
// other's bodies. Prefixing every key (not only differences)
|
|
52
|
+
// keeps the rule simple; the cache holds both.
|
|
53
|
+
if (keepMinuendColor) key->insert(0, "keepMinuendColor;");
|
|
49
54
|
// A key that serialized a function literal is not a pure
|
|
50
55
|
// function of content -- it embeds a raw AST address that the
|
|
51
56
|
// next parse can hand out again. Dropping the key here forces
|
|
@@ -305,6 +310,26 @@ std::vector<ColoredBody> Evaluator::generatePartialTree() {
|
|
|
305
310
|
return generateTreeImpl(flat);
|
|
306
311
|
}
|
|
307
312
|
|
|
313
|
+
namespace {
|
|
314
|
+
std::shared_ptr<const std::vector<ColoredBody>> remapParts(const std::vector<ColoredBody>& parts,
|
|
315
|
+
const std::unordered_map<uint32_t, uint32_t>& remap) {
|
|
316
|
+
std::vector<ColoredBody> out;
|
|
317
|
+
for (ColoredBody part : parts) {
|
|
318
|
+
if (part.mergedFrom) part.mergedFrom = remapParts(*part.mergedFrom, remap);
|
|
319
|
+
if (part.body && !part.body->IsEmpty()) {
|
|
320
|
+
manifold::MeshGL mesh = part.body->GetMeshGL();
|
|
321
|
+
for (uint32_t& id : mesh.runOriginalID) {
|
|
322
|
+
auto found = remap.find(id);
|
|
323
|
+
if (found != remap.end()) id = found->second;
|
|
324
|
+
}
|
|
325
|
+
part.body = manifold::Manifold(mesh);
|
|
326
|
+
}
|
|
327
|
+
out.push_back(std::move(part));
|
|
328
|
+
}
|
|
329
|
+
return std::make_shared<const std::vector<ColoredBody>>(std::move(out));
|
|
330
|
+
}
|
|
331
|
+
} // namespace
|
|
332
|
+
|
|
308
333
|
void Evaluator::restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::ASTNode& node,
|
|
309
334
|
const oscad::ASTNode* producer) {
|
|
310
335
|
// A cache hit hands back the geometry AND the originalIDs of whichever
|
|
@@ -338,32 +363,89 @@ void Evaluator::restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::
|
|
|
338
363
|
manifold::MeshGL mesh = cb.body->GetMeshGL();
|
|
339
364
|
if (mesh.runOriginalID.empty()) continue;
|
|
340
365
|
std::unordered_map<uint32_t, uint32_t> remap;
|
|
341
|
-
|
|
366
|
+
// fresh ID -> the colour its triangles carried, multi-colour
|
|
367
|
+
// bodies only; what triColors is rebuilt from below.
|
|
368
|
+
std::unordered_map<uint32_t, std::array<float, 4>> runColor;
|
|
369
|
+
for (size_t run = 0; run < mesh.runOriginalID.size(); ++run) {
|
|
370
|
+
uint32_t& id = mesh.runOriginalID[run];
|
|
342
371
|
auto found = remap.find(id);
|
|
343
372
|
if (found == remap.end()) {
|
|
344
373
|
const uint32_t fresh = manifold::Manifold::ReserveIDs(1);
|
|
345
374
|
auto old = idToNode.find(id);
|
|
346
375
|
idToNode[fresh] =
|
|
347
376
|
(old != idToNode.end() && old->second != producer) ? old->second : &node;
|
|
377
|
+
// The colour has to be re-recorded too, or a later merge
|
|
378
|
+
// cannot tell this body's runs apart from uncoloured ones
|
|
379
|
+
// (attachTriColors looks runs up here). Across renders
|
|
380
|
+
// the old ID was minted by an earlier Evaluator and this
|
|
381
|
+
// one's idToColor has never heard of it, so the cached
|
|
382
|
+
// body itself is the record: its own colour, or for a
|
|
383
|
+
// multi-colour merge the colour its triangles carry. This
|
|
384
|
+
// is what made `difference() { color("orange") cube();
|
|
385
|
+
// color("cyan") cylinder(); ... }` lose the cyan on the
|
|
386
|
+
// first re-render after an edit and go flat orange on the
|
|
387
|
+
// next (BelfrySCAD #412) -- every cached operand looked
|
|
388
|
+
// uncoloured, so the merge saw nothing to distinguish.
|
|
348
389
|
auto color = idToColor.find(id);
|
|
349
|
-
if (color != idToColor.end())
|
|
390
|
+
if (color != idToColor.end()) {
|
|
391
|
+
idToColor[fresh] = color->second;
|
|
392
|
+
} else if (cb.triColors && run + 1 < mesh.runIndex.size() &&
|
|
393
|
+
mesh.runIndex[run] / 3 < cb.triColors->size()) {
|
|
394
|
+
// ponytail: a run whose triangles carry the default
|
|
395
|
+
// fallback colour reads back as that explicit colour
|
|
396
|
+
// rather than "follow the theme"; the tint is the same.
|
|
397
|
+
idToColor[fresh] = (*cb.triColors)[mesh.runIndex[run] / 3];
|
|
398
|
+
} else {
|
|
399
|
+
idToColor[fresh] = cb.color;
|
|
400
|
+
}
|
|
401
|
+
if (cb.triColors && run + 1 < mesh.runIndex.size() &&
|
|
402
|
+
mesh.runIndex[run] / 3 < cb.triColors->size()) {
|
|
403
|
+
runColor[fresh] = (*cb.triColors)[mesh.runIndex[run] / 3];
|
|
404
|
+
}
|
|
350
405
|
found = remap.emplace(id, fresh).first;
|
|
351
406
|
}
|
|
352
407
|
id = found->second;
|
|
353
408
|
}
|
|
354
|
-
const size_t trisBefore = mesh.triVerts.size() / 3;
|
|
355
409
|
cb.body = manifold::Manifold(mesh);
|
|
410
|
+
// The unmerged parts a union() remembered (ColoredBody::mergedFrom)
|
|
411
|
+
// share the merged body's IDs, so the same remap applies to them;
|
|
412
|
+
// a part left on the old IDs would look uncoloured next to it.
|
|
413
|
+
if (cb.mergedFrom) cb.mergedFrom = remapParts(*cb.mergedFrom, remap);
|
|
356
414
|
// Rebuilding a Manifold from a mesh can clean it up (degenerate
|
|
357
|
-
// triangles merged away), and triColors is indexed by triangle
|
|
358
|
-
// array
|
|
359
|
-
//
|
|
360
|
-
// than
|
|
361
|
-
|
|
362
|
-
|
|
415
|
+
// triangles merged away), and triColors is indexed by triangle, so
|
|
416
|
+
// the old array no longer lines up. The RUNS survive the rebuild,
|
|
417
|
+
// and each run's colour was just recorded, so the array is
|
|
418
|
+
// rebuilt from them rather than dropped -- dropping it turned a
|
|
419
|
+
// fully cached multi-colour difference() flat (BelfrySCAD #412).
|
|
420
|
+
if (cb.triColors && cb.body) {
|
|
421
|
+
const manifold::MeshGL rebuilt = cb.body->GetMeshGL();
|
|
422
|
+
const size_t numTris = rebuilt.triVerts.size() / 3;
|
|
423
|
+
std::vector<std::array<float, 4>> triColors(numTris, cb.color.value_or(kDefaultGeometryColor));
|
|
424
|
+
for (size_t i = 0; i + 1 < rebuilt.runIndex.size() && i < rebuilt.runOriginalID.size(); ++i) {
|
|
425
|
+
auto it = runColor.find(rebuilt.runOriginalID[i]);
|
|
426
|
+
if (it == runColor.end()) continue;
|
|
427
|
+
const size_t start = rebuilt.runIndex[i] / 3;
|
|
428
|
+
const size_t end = std::min<size_t>(rebuilt.runIndex[i + 1] / 3, numTris);
|
|
429
|
+
for (size_t t = start; t < end; ++t) triColors[t] = it->second;
|
|
430
|
+
}
|
|
431
|
+
cb.triColors = std::move(triColors);
|
|
363
432
|
}
|
|
364
433
|
}
|
|
365
434
|
}
|
|
366
435
|
|
|
436
|
+
|
|
437
|
+
void Evaluator::recordRunColors(ColoredBody& b, const std::optional<std::array<float, 4>>& rgba) {
|
|
438
|
+
if (!b.body || bodyIsEmpty(b)) return;
|
|
439
|
+
// A body that is still one original knows its own ID without building
|
|
440
|
+
// a mesh -- the common case, and the cheap one.
|
|
441
|
+
const int original = b.body->OriginalID();
|
|
442
|
+
if (original >= 0) {
|
|
443
|
+
idToColor[static_cast<uint32_t>(original)] = rgba;
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
for (uint32_t id : b.body->GetMeshGL().runOriginalID) idToColor[id] = rgba;
|
|
447
|
+
}
|
|
448
|
+
|
|
367
449
|
ColoredBody Evaluator::tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue) {
|
|
368
450
|
manifold::MeshGL mesh = body.GetMeshGL();
|
|
369
451
|
std::optional<std::array<float, 4>> color = valueToColor(colorValue);
|
{openscad_cpp_evaluator-1.19.2 → openscad_cpp_evaluator-1.20.0}/tests/test_manifold_cache.cpp
RENAMED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
#include <gtest/gtest.h>
|
|
8
8
|
#include <algorithm>
|
|
9
|
+
#include <array>
|
|
9
10
|
#include <iterator>
|
|
10
11
|
#include <numbers>
|
|
11
12
|
#include <set>
|
|
@@ -327,6 +328,46 @@ TEST(ManifoldCache, ACachedMultiPartSubtreeKeepsOneIdPerPart) {
|
|
|
327
328
|
EXPECT_EQ(all.size(), 4u) << "parts of the reused subtree share IDs";
|
|
328
329
|
}
|
|
329
330
|
|
|
331
|
+
namespace {
|
|
332
|
+
// Distinct RGB triples a body's triangles carry (its flat colour if it has
|
|
333
|
+
// no per-triangle colours).
|
|
334
|
+
std::set<std::array<float, 3>> distinctColors(const ColoredBody& cb) {
|
|
335
|
+
std::set<std::array<float, 3>> out;
|
|
336
|
+
if (cb.triColors) {
|
|
337
|
+
for (const auto& c : *cb.triColors) out.insert({c[0], c[1], c[2]});
|
|
338
|
+
} else if (cb.color) {
|
|
339
|
+
out.insert({(*cb.color)[0], (*cb.color)[1], (*cb.color)[2]});
|
|
340
|
+
}
|
|
341
|
+
return out;
|
|
342
|
+
}
|
|
343
|
+
} // namespace
|
|
344
|
+
|
|
345
|
+
TEST(ManifoldCache, CachedOperandsKeepTheirColoursInALaterMerge) {
|
|
346
|
+
// BelfrySCAD #412: edit one subtrahend's colour and re-render with the
|
|
347
|
+
// cache shared, as the GUI does. The unchanged operands are cache hits
|
|
348
|
+
// and their run colours were never re-recorded, so the merge saw them
|
|
349
|
+
// as uncoloured: first re-render lost the cyan, the next (every
|
|
350
|
+
// operand cached) collapsed to flat orange.
|
|
351
|
+
const std::string a =
|
|
352
|
+
"difference() { color(\"orange\") cube(20, center=true);"
|
|
353
|
+
" color(\"cyan\") cylinder(30, r=8, center=true, $fn=24);"
|
|
354
|
+
" color(\"magenta\") rotate([0,90,0]) cylinder(30, r=8, center=true, $fn=24); }";
|
|
355
|
+
const std::string b = [&] { std::string s = a; s.replace(s.find("magenta"), 7, "green"); return s; }();
|
|
356
|
+
|
|
357
|
+
const std::set<std::array<float, 3>> wantB = distinctColors(evalSrc(b).bodies[0]);
|
|
358
|
+
ASSERT_EQ(wantB.size(), 3u);
|
|
359
|
+
|
|
360
|
+
auto cache = std::make_shared<ManifoldCache>();
|
|
361
|
+
evalSrcWithCache(a, cache);
|
|
362
|
+
EXPECT_EQ(distinctColors(evalSrcWithCache(b, cache).bodies[0]), wantB) << "first re-render";
|
|
363
|
+
EXPECT_EQ(distinctColors(evalSrcWithCache(b, cache).bodies[0]), wantB) << "fully cached";
|
|
364
|
+
// And a cached multi-colour body merged again keeps every colour.
|
|
365
|
+
Evaluated wrapped = evalSrcWithCache("union() { " + b + " translate([30,0,0]) color(\"red\") cube(5); }", cache);
|
|
366
|
+
std::set<std::array<float, 3>> wantWrapped = wantB;
|
|
367
|
+
wantWrapped.insert({1.0f, 0.0f, 0.0f});
|
|
368
|
+
EXPECT_EQ(distinctColors(wrapped.bodies[0]), wantWrapped);
|
|
369
|
+
}
|
|
370
|
+
|
|
330
371
|
TEST(ManifoldCache, OutputIsIdenticalCacheOnVsCacheOffAcrossBuiltinCategories) {
|
|
331
372
|
const std::vector<std::string> scripts = {
|
|
332
373
|
"cube(3);",
|