openscad-cpp-evaluator 1.12.0__tar.gz → 1.13.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.
Files changed (215) hide show
  1. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/CLAUDE.md +18 -0
  2. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/PKG-INFO +1 -1
  3. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/bindings/module.cpp +7 -0
  4. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/evaluator.hpp +7 -0
  5. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/pyproject.toml +1 -1
  6. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/booleans.cpp +7 -1
  7. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/color.cpp +31 -1
  8. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/topology.cpp +8 -1
  9. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/csg_generate.cpp +9 -0
  10. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_booleans.cpp +79 -0
  11. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/.github/workflows/ci.yml +0 -0
  12. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/.github/workflows/wheels.yml +0 -0
  13. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/.gitignore +0 -0
  14. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/.gitmodules +0 -0
  15. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/CMakeLists.txt +0 -0
  16. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/LICENSE +0 -0
  17. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/README.md +0 -0
  18. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/bindings/CMakeLists.txt +0 -0
  19. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/bindings/ast_to_py.cpp +0 -0
  20. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/cmake/embed_font.cmake +0 -0
  21. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/examples/CMakeLists.txt +0 -0
  22. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/examples/eval_perf_benchmark.cpp +0 -0
  23. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/examples/manifold_cache_reuse.cpp +0 -0
  24. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/examples/minimal_debugger.cpp +0 -0
  25. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  26. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/.gitignore +0 -0
  27. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  28. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  29. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/README.md +0 -0
  30. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  31. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  32. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  33. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  34. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  35. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  36. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  37. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  38. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  39. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  40. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  41. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  42. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  43. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  44. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  45. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  46. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  47. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  48. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  49. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  50. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  51. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  52. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  53. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  54. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  55. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  56. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  57. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  58. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  59. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  60. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  61. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  62. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  63. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  64. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  65. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  66. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  67. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  68. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  69. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  70. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  71. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  72. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  73. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  74. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  75. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  76. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  77. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  78. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  79. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  80. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  81. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  82. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  83. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  84. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  85. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  86. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  87. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  88. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  89. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  90. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  91. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  92. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  93. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  94. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  95. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  96. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  97. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  98. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  99. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  100. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  101. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  102. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  103. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  104. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  105. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  106. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  107. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  108. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  109. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  110. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  111. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  112. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  113. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  114. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  115. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  116. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  117. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  118. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  119. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  120. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  121. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  122. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  123. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  124. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  125. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  126. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  127. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  128. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  129. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/resources/fonts/LICENSE +0 -0
  130. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  131. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/CMakeLists.txt +0 -0
  132. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/builtins.hpp +0 -0
  133. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/call_args.cpp +0 -0
  134. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/control.cpp +0 -0
  135. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/dxf_dim.cpp +0 -0
  136. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/extrude.cpp +0 -0
  137. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/function_builtins.cpp +0 -0
  138. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/import.cpp +0 -0
  139. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/modifiers.cpp +0 -0
  140. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/primitives_2d.cpp +0 -0
  141. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/primitives_3d.cpp +0 -0
  142. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/registry.cpp +0 -0
  143. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/roof.cpp +0 -0
  144. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/segments.cpp +0 -0
  145. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/surface.cpp +0 -0
  146. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/text.cpp +0 -0
  147. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/builtins/transforms.cpp +0 -0
  148. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/bytecode_compiler.cpp +0 -0
  149. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/bytecode_vm.cpp +0 -0
  150. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/colored_body.cpp +0 -0
  151. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/csg_resolve.cpp +0 -0
  152. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/css_colors.cpp +0 -0
  153. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/debug/debug_repl.cpp +0 -0
  154. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/debug_profile.cpp +0 -0
  155. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/eval_context.cpp +0 -0
  156. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/eval_error.cpp +0 -0
  157. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/eval_use.cpp +0 -0
  158. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/export.cpp +0 -0
  159. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/expr_eval.cpp +0 -0
  160. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/format_closure.cpp +0 -0
  161. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/import/dxf_import.cpp +0 -0
  162. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/import/mesh_import.cpp +0 -0
  163. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/import/surface_load.cpp +0 -0
  164. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/import/svg_import.cpp +0 -0
  165. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/manifold_cache.cpp +0 -0
  166. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/measure_geometry.cpp +0 -0
  167. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/mesh_check.cpp +0 -0
  168. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/stmt_eval.cpp +0 -0
  169. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/text/font_match.cpp +0 -0
  170. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/text/freetype_font_provider.cpp +0 -0
  171. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/text/text_metrics.cpp +0 -0
  172. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/user_calls.cpp +0 -0
  173. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/utf8.cpp +0 -0
  174. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/value.cpp +0 -0
  175. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/src/zip_stored.cpp +0 -0
  176. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/CMakeLists.txt +0 -0
  177. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_bytecode_compiler.cpp +0 -0
  178. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_children_separate.cpp +0 -0
  179. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_cli.cpp +0 -0
  180. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_control_flow.cpp +0 -0
  181. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_csg_tree.cpp +0 -0
  182. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_debug_hooks.cpp +0 -0
  183. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_dxf_svg_import.cpp +0 -0
  184. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_eval_context.cpp +0 -0
  185. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_export.cpp +0 -0
  186. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_expr_eval.cpp +0 -0
  187. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_extrude_roof.cpp +0 -0
  188. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_feature_detection.cpp +0 -0
  189. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_function_builtins.cpp +0 -0
  190. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_helpers.hpp +0 -0
  191. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_import_export.cpp +0 -0
  192. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_include_cache.cpp +0 -0
  193. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_manifold_cache.cpp +0 -0
  194. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_mesh_check.cpp +0 -0
  195. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_multi_color_merge.cpp +0 -0
  196. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_primitives.cpp +0 -0
  197. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_profiling.cpp +0 -0
  198. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_python_bindings.py +0 -0
  199. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_render_expr.cpp +0 -0
  200. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_scope_trail.cpp +0 -0
  201. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_scoping.cpp +0 -0
  202. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_surface.cpp +0 -0
  203. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_tail_calls.cpp +0 -0
  204. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_text.cpp +0 -0
  205. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_transforms.cpp +0 -0
  206. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_unexpected_args.cpp +0 -0
  207. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_use.cpp +0 -0
  208. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_value.cpp +0 -0
  209. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_viewport_params.cpp +0 -0
  210. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tests/test_zip_stored.cpp +0 -0
  211. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tools/cli/CMakeLists.txt +0 -0
  212. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tools/cli/cli_lib.cpp +0 -0
  213. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tools/cli/cli_lib.hpp +0 -0
  214. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/tools/cli/main.cpp +0 -0
  215. {openscad_cpp_evaluator-1.12.0 → openscad_cpp_evaluator-1.13.0}/uv.lock +0 -0
@@ -793,6 +793,24 @@ grep for `ponytail:`.
793
793
  color instead of preserving each part's own, matching real OpenSCAD's actual behavior once fixed
794
794
  (cross-checked triangle-color-count-and-values against the Python reference directly, not just
795
795
  “does it run”).
796
+ Two things that pipeline depends on, both learned from BelfrySCAD #372 (`union() { color("red")
797
+ stroke(...); text(); }` came out entirely red):
798
+ **`color()` records its colour against the body's RUN IDS**, not only in `cb.color`
799
+ (`recordRunColors`, `color.cpp`). `tagGenerated()` covers a body coloured at birth — the colour
800
+ reaches a primitive through the context — but not one that already existed when colour was applied
801
+ to it, which is every module that wraps FORWARDED children: `module setcolor(clr) { color(clr)
802
+ children(); }` (BOSL2's `stroke()` has exactly that) generates the children in the caller's
803
+ colourless context, so nothing recorded them as red and after the merge every run looked
804
+ uncoloured. Uses `Manifold::OriginalID()` where the body is still one original, so the common case
805
+ costs no mesh build.
806
+ **An unknown run falls back to `nullopt`, never `cb.color`** — `cb.color` is the *first* child's
807
+ colour, so falling back to it made an uncoloured sibling look identically coloured, `allSame` came
808
+ out true, and nothing was tagged at all.
809
+ And the invariant behind both: **`triColors` must index the body's CURRENT mesh**. The renderer
810
+ masks its vertex arrays with it, so a stale length is an `IndexError` and a blank viewport rather
811
+ than a wrong colour. Anything that re-meshes clears it — `simplify()`, `minkowski_difference()`,
812
+ and the ID-retag rebuild when the triangle count moves — and `bodyToDict` drops a mismatched array
813
+ as a backstop for whichever one gets missed next.
796
814
  **2D cannot use any of that, and keeps colour geometrically instead** (`Part2d`, same file): a
797
815
  `CrossSection` is contours, not a mesh, so nothing in it remembers which child an edge came from
798
816
  and there is no provenance for `attachTriColors` to read back. So the 2D accumulator holds **one
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.12.0
3
+ Version: 1.13.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -168,6 +168,13 @@ nb::dict bodyToDict(oscadeval::ColoredBody& cb) {
168
168
  // (numTri, 4) float32 array or None). The renderer reads this to split
169
169
  // one merged body's triangles across separate opaque/translucent draw
170
170
  // buffers.
171
+ // Size-checked, not trusted: a per-triangle array that disagrees with
172
+ // the mesh it is meant to index makes the renderer raise IndexError and
173
+ // draw nothing. Dropping it costs the per-triangle colours of one body
174
+ // and keeps the model on screen. Everything that can invalidate it is
175
+ // supposed to clear it (simplify(), minkowski_difference(), the retag
176
+ // rebuild); this is the backstop for the one that gets missed.
177
+ if (cb.triColors && cb.triColors->size() != numTri) cb.triColors.reset();
171
178
  if (cb.triColors) {
172
179
  const std::vector<std::array<float, 4>>& src = *cb.triColors;
173
180
  std::vector<float> flat;
@@ -1237,6 +1237,13 @@ public:
1237
1237
  // it), which no RAII guard can span.
1238
1238
  bool measuring_ = false;
1239
1239
 
1240
+ // True while a render() expression is measuring geometry it will then
1241
+ // discard. The provenance tables (idToNode/idToColor) are not written
1242
+ // during that, so anything outside this class applying the same guard
1243
+ // -- color.cpp records run colours the way tagGenerated does -- reads
1244
+ // it here.
1245
+ bool measuring() const { return measuring_; }
1246
+
1240
1247
  // Test-only accessors for the invariants above -- a leaked measuring_ or
1241
1248
  // an unbalanced treeStack_ is otherwise silent until something much
1242
1249
  // later goes mysteriously wrong.
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.12.0"
7
+ version = "1.13.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -184,7 +184,13 @@ void attachTriColors(Evaluator& ev, ColoredBody& cb) {
184
184
  perRunColor.reserve(runIds.size());
185
185
  for (uint32_t rid : runIds) {
186
186
  auto it = ev.idToColor.find(rid);
187
- perRunColor.push_back(it != ev.idToColor.end() ? it->second : cb.color);
187
+ // nullopt, NOT cb.color, when a run has no recorded colour of its
188
+ // own. cb.color is the FIRST child's colour, so falling back to it
189
+ // made an uncoloured child look identically coloured, `allSame`
190
+ // came out true, and the whole merge kept one colour -- which is
191
+ // the "union() { color("red") a; b; } is all red" bug. nullopt is
192
+ // what an uncoloured body means everywhere else: follow the theme.
193
+ perRunColor.push_back(it != ev.idToColor.end() ? it->second : std::nullopt);
188
194
  }
189
195
  const bool allSame =
190
196
  std::all_of(perRunColor.begin(), perRunColor.end(), [&](const auto& c) { return c == perRunColor.front(); });
@@ -50,12 +50,42 @@ CSGParams resolveColor(Evaluator& ev, const oscad::ModularCall& node, EvalContex
50
50
  return std::move(result.params);
51
51
  }
52
52
 
53
- std::vector<ColoredBody> generateColor(Evaluator&, const CSGParams& params,
53
+ namespace {
54
+
55
+ // Stamping cb.color is not enough: the colour has to be recorded against
56
+ // the body's RUN IDS too.
57
+ //
58
+ // attachTriColors (booleans.cpp) reads `idToColor` back AFTER a merge has
59
+ // thrown the individual bodies away -- run IDs are all it has left to go
60
+ // on -- and a run it cannot find looks uncoloured. tagGenerated() records
61
+ // the colour a body was BORN with, which covers `color("red") cube(10)`
62
+ // because the colour reaches the primitive through the context. It does
63
+ // not cover a body that already existed when colour was applied to it,
64
+ // which is every module whose geometry comes out of its own CSG:
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, const ColoredBody& b, const std::optional<std::array<float, 4>>& rgba) {
69
+ if (!b.body || b.body->IsEmpty()) 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;
76
+ }
77
+ for (uint32_t id : b.body->GetMeshGL().runOriginalID) ev.idToColor[id] = rgba;
78
+ }
79
+
80
+ } // namespace
81
+
82
+ std::vector<ColoredBody> generateColor(Evaluator& ev, const CSGParams& params,
54
83
  const std::vector<std::unique_ptr<CSGNode>>& children, const oscad::ASTNode&) {
55
84
  const auto rgba = valueToColor(params.at("rgba"));
56
85
  std::vector<ColoredBody> result;
57
86
  for (ColoredBody b : flattenCsgTree(children)) {
58
87
  b.color = rgba;
88
+ if (!ev.measuring()) recordRunColors(ev, b, rgba);
59
89
  result.push_back(std::move(b));
60
90
  }
61
91
  return result;
@@ -481,7 +481,14 @@ std::vector<ColoredBody> generateSimplify(Evaluator& ev, const CSGParams& params
481
481
  const manifold::vec3 d = bb.Size();
482
482
  tol = kDefaultSimplifyFraction * std::sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
483
483
  }
484
- if (tol > 0.0) cb.body = b.body->Simplify(tol);
484
+ if (tol > 0.0) {
485
+ cb.body = b.body->Simplify(tol);
486
+ // triColors is indexed by triangle, and decimation changes
487
+ // how many there are -- carrying it across leaves the
488
+ // renderer masking a 550-triangle mesh with a 556-entry
489
+ // array, which is an IndexError, not a wrong colour.
490
+ cb.triColors.reset();
491
+ }
485
492
  } else if (b.section) {
486
493
  double tol = explicitValue;
487
494
  if (!explicitTol) {
@@ -331,7 +331,16 @@ void Evaluator::restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::
331
331
  }
332
332
  id = found->second;
333
333
  }
334
+ const size_t trisBefore = mesh.triVerts.size() / 3;
334
335
  cb.body = manifold::Manifold(mesh);
336
+ // Rebuilding a Manifold from a mesh can clean it up (degenerate
337
+ // triangles merged away), and triColors is indexed by triangle. An
338
+ // array that no longer matches is worse than none: the renderer
339
+ // masks its vertex arrays with it and raises IndexError rather
340
+ // than drawing anything at all.
341
+ if (cb.triColors && cb.body && cb.body->GetMeshGL().triVerts.size() / 3 != trisBefore) {
342
+ cb.triColors.reset();
343
+ }
335
344
  }
336
345
  }
337
346
 
@@ -6,6 +6,7 @@
6
6
 
7
7
  #include <algorithm>
8
8
  #include <gtest/gtest.h>
9
+ #include <set>
9
10
 
10
11
  using namespace oscadeval;
11
12
  using namespace oscadeval::test;
@@ -1554,3 +1555,81 @@ TEST(Union2dColor, ExtrudingAColouredUnionStillDropsColour) {
1554
1555
  EXPECT_FALSE(e.bodies[0].color.has_value());
1555
1556
  EXPECT_NEAR(e.bodies[0].body->Volume(), 1000.0, 1e-6);
1556
1557
  }
1558
+
1559
+
1560
+ // -- 3D colour survives a merge (BelfrySCAD #372) -------------------------
1561
+
1562
+ namespace {
1563
+
1564
+ std::set<std::array<float, 4>> distinctTriColors(const ColoredBody& b) {
1565
+ std::set<std::array<float, 4>> out;
1566
+ if (b.triColors) out.insert(b.triColors->begin(), b.triColors->end());
1567
+ return out;
1568
+ }
1569
+
1570
+ } // namespace
1571
+
1572
+ TEST(Union3dColor, ColourAppliedToForwardedChildrenSurvivesTheMerge) {
1573
+ // The reported case, reduced out of BOSL2. Its stroke() colours what
1574
+ // it draws through exactly this shape -- a module that wraps FORWARDED
1575
+ // children in color(). The children were generated in the caller's
1576
+ // context, where there is no colour, so tagGenerated() recorded them as
1577
+ // uncoloured; color() then stamped cb.color and nothing else. After the
1578
+ // merge, attachTriColors had only run IDs to go on, every one of them
1579
+ // looked uncoloured, they all "matched", and the union kept the first
1580
+ // child's colour -- the whole model came out red.
1581
+ Evaluated e = evalSrc(
1582
+ "module setcolor(clr) { color(clr) children(); }\n"
1583
+ "union() {"
1584
+ " setcolor(\"red\") cube(10);"
1585
+ " translate([12,0,0]) cube(10);"
1586
+ "}");
1587
+ ASSERT_EQ(e.bodies.size(), 1u);
1588
+ ASSERT_TRUE(e.bodies[0].triColors.has_value());
1589
+ const auto colours = distinctTriColors(e.bodies[0]);
1590
+ EXPECT_EQ(colours.size(), 2u);
1591
+ EXPECT_EQ(colours.count(std::array<float, 4>{1, 0, 0, 1}), 1u);
1592
+ }
1593
+
1594
+ TEST(Union3dColor, AnUncolouredChildDoesNotInheritTheColouredOne) {
1595
+ // perRunColor's fallback used to be cb.color -- the FIRST child's
1596
+ // colour -- so an uncoloured sibling looked identically coloured, the
1597
+ // "do the colours differ?" test said no, and nothing was tagged at all.
1598
+ Evaluated e = evalSrc(
1599
+ "union() { color(\"red\") cube(10); translate([12,0,0]) cube(10); }");
1600
+ ASSERT_EQ(e.bodies.size(), 1u);
1601
+ ASSERT_TRUE(e.bodies[0].triColors.has_value());
1602
+ const auto colours = distinctTriColors(e.bodies[0]);
1603
+ EXPECT_EQ(colours.size(), 2u);
1604
+ EXPECT_EQ(colours.count(std::array<float, 4>{1, 0, 0, 1}), 1u);
1605
+ // ... and the other half is the default, not red.
1606
+ for (const auto& c : colours) {
1607
+ if (c != std::array<float, 4>{1, 0, 0, 1}) EXPECT_NE(c[0], 1.0f);
1608
+ }
1609
+ }
1610
+
1611
+ TEST(Union3dColor, EveryTriangleIsAccountedFor) {
1612
+ // triColors must index the body's CURRENT mesh exactly: the renderer
1613
+ // masks its vertex arrays with it, so a stale length is an IndexError
1614
+ // and a blank viewport, not a wrong colour.
1615
+ Evaluated e = evalSrc(
1616
+ "union() { color(\"red\") cube(10); color(\"blue\") translate([12,0,0]) cube(10); }");
1617
+ ASSERT_EQ(e.bodies.size(), 1u);
1618
+ ASSERT_TRUE(e.bodies[0].triColors.has_value());
1619
+ EXPECT_EQ(e.bodies[0].triColors->size(), e.bodies[0].body->GetMeshGL().triVerts.size() / 3);
1620
+ }
1621
+
1622
+ TEST(Union3dColor, SimplifyDropsPerTriangleColoursRatherThanKeepingStaleOnes) {
1623
+ // Decimation changes the triangle count; an array indexed against the
1624
+ // old one cannot be salvaged, and handing it on crashes the renderer.
1625
+ Evaluated e = evalSrc(
1626
+ "simplify(0.5) union() {"
1627
+ " color(\"red\") sphere(10, $fn=32);"
1628
+ " color(\"blue\") translate([25,0,0]) sphere(10, $fn=32);"
1629
+ "}");
1630
+ ASSERT_FALSE(e.bodies.empty());
1631
+ for (const ColoredBody& b : e.bodies) {
1632
+ if (!b.body || !b.triColors) continue;
1633
+ EXPECT_EQ(b.triColors->size(), b.body->GetMeshGL().triVerts.size() / 3);
1634
+ }
1635
+ }