openscad-cpp-evaluator 0.26.1__tar.gz → 0.27.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/evaluator.hpp +12 -0
  3. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/pyproject.toml +1 -1
  4. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/csg_generate.cpp +59 -1
  5. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/csg_resolve.cpp +1 -0
  6. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_manifold_cache.cpp +120 -7
  7. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/.github/workflows/ci.yml +0 -0
  8. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/.github/workflows/wheels.yml +0 -0
  9. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/.gitignore +0 -0
  10. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/.gitmodules +0 -0
  11. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/CLAUDE.md +0 -0
  12. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/CMakeLists.txt +0 -0
  13. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/LICENSE +0 -0
  14. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/README.md +0 -0
  15. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/bindings/CMakeLists.txt +0 -0
  16. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/bindings/ast_to_py.cpp +0 -0
  17. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/bindings/module.cpp +0 -0
  18. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/cmake/embed_font.cmake +0 -0
  19. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/examples/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/examples/eval_perf_benchmark.cpp +0 -0
  21. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/examples/manifold_cache_reuse.cpp +0 -0
  22. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/examples/minimal_debugger.cpp +0 -0
  23. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  24. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/.gitignore +0 -0
  25. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  26. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  27. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/README.md +0 -0
  28. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  29. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  30. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  31. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  32. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  33. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  34. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  35. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  36. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  37. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  39. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  40. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  41. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  42. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  43. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  44. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
  45. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  46. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  47. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  48. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  49. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  50. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  51. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  52. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  53. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  54. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  55. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  56. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  57. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  58. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  59. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  60. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  61. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
  62. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  63. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  64. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  65. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  66. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  67. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  68. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  69. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  70. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  71. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  72. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  73. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  74. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  75. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  76. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  77. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  78. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  79. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  80. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  81. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  82. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  83. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  84. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  85. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  86. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  87. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  88. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  89. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  90. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  91. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  92. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  93. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  94. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  95. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  96. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  97. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  98. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  99. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  100. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  101. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  102. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  103. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  104. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
  105. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  106. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  107. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  108. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  109. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  110. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  111. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  112. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  113. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  114. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  115. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  116. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  117. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  118. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
  119. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  120. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
  121. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/resources/fonts/LICENSE +0 -0
  122. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  123. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/CMakeLists.txt +0 -0
  124. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/booleans.cpp +0 -0
  125. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/builtins.hpp +0 -0
  126. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/call_args.cpp +0 -0
  127. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/color.cpp +0 -0
  128. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/control.cpp +0 -0
  129. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/extrude.cpp +0 -0
  130. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/function_builtins.cpp +0 -0
  131. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/import.cpp +0 -0
  132. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/modifiers.cpp +0 -0
  133. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/primitives_2d.cpp +0 -0
  134. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/primitives_3d.cpp +0 -0
  135. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/registry.cpp +0 -0
  136. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/roof.cpp +0 -0
  137. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/segments.cpp +0 -0
  138. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/surface.cpp +0 -0
  139. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/text.cpp +0 -0
  140. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/topology.cpp +0 -0
  141. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/builtins/transforms.cpp +0 -0
  142. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/bytecode_compiler.cpp +0 -0
  143. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/bytecode_vm.cpp +0 -0
  144. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/colored_body.cpp +0 -0
  145. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/css_colors.cpp +0 -0
  146. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/debug/debug_repl.cpp +0 -0
  147. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/debug_profile.cpp +0 -0
  148. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/eval_context.cpp +0 -0
  149. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/eval_error.cpp +0 -0
  150. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/eval_use.cpp +0 -0
  151. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/export.cpp +0 -0
  152. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/expr_eval.cpp +0 -0
  153. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/import/dxf_import.cpp +0 -0
  154. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/import/mesh_import.cpp +0 -0
  155. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/import/surface_load.cpp +0 -0
  156. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/import/svg_import.cpp +0 -0
  157. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/manifold_cache.cpp +0 -0
  158. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/mesh_check.cpp +0 -0
  159. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/stmt_eval.cpp +0 -0
  160. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/text/stb_font_provider.cpp +0 -0
  161. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/text/text_metrics.cpp +0 -0
  162. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/user_calls.cpp +0 -0
  163. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/value.cpp +0 -0
  164. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/src/zip_stored.cpp +0 -0
  165. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/CMakeLists.txt +0 -0
  166. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_booleans.cpp +0 -0
  167. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_bytecode_compiler.cpp +0 -0
  168. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_cli.cpp +0 -0
  169. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_control_flow.cpp +0 -0
  170. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_csg_tree.cpp +0 -0
  171. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_debug_hooks.cpp +0 -0
  172. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_dxf_svg_import.cpp +0 -0
  173. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_eval_context.cpp +0 -0
  174. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_export.cpp +0 -0
  175. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_expr_eval.cpp +0 -0
  176. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_extrude_roof.cpp +0 -0
  177. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_function_builtins.cpp +0 -0
  178. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_helpers.hpp +0 -0
  179. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_import_export.cpp +0 -0
  180. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_mesh_check.cpp +0 -0
  181. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_multi_color_merge.cpp +0 -0
  182. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_primitives.cpp +0 -0
  183. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_profiling.cpp +0 -0
  184. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_python_bindings.py +0 -0
  185. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_scope_trail.cpp +0 -0
  186. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_scoping.cpp +0 -0
  187. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_surface.cpp +0 -0
  188. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_tail_calls.cpp +0 -0
  189. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_text.cpp +0 -0
  190. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_transforms.cpp +0 -0
  191. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_use.cpp +0 -0
  192. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_value.cpp +0 -0
  193. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_viewport_params.cpp +0 -0
  194. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tests/test_zip_stored.cpp +0 -0
  195. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tools/cli/CMakeLists.txt +0 -0
  196. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tools/cli/cli_lib.cpp +0 -0
  197. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tools/cli/cli_lib.hpp +0 -0
  198. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/tools/cli/main.cpp +0 -0
  199. {openscad_cpp_evaluator-0.26.1 → openscad_cpp_evaluator-0.27.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.26.1
3
+ Version: 0.27.1
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -223,6 +223,11 @@ public:
223
223
  // evaluate()/resolveTree()+generateTree() complete. Mirrors the
224
224
  // reference's Evaluator.id_to_node/id_to_color (also plain public
225
225
  // attributes there).
226
+ // Give cached bodies fresh originalIDs so a second call site reusing
227
+ // them is not confused with the first. See its definition.
228
+ void restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::ASTNode& node,
229
+ const oscad::ASTNode* producer);
230
+
226
231
  std::unordered_map<uint32_t, const oscad::ASTNode*> idToNode;
227
232
  std::unordered_map<uint32_t, std::optional<std::array<float, 4>>> idToColor;
228
233
 
@@ -1697,6 +1702,13 @@ private:
1697
1702
  EchoFn echoFn_;
1698
1703
  std::shared_ptr<FontProvider> fontProvider_; // null until first fontProvider() call if not injected
1699
1704
  std::shared_ptr<ManifoldCache> manifoldCache_; // opt-in, see the constructor's own doc comment
1705
+
1706
+ // Cache key -> the AST node whose generation filled that entry, for
1707
+ // this render only (cleared alongside idToNode, whose pointers share
1708
+ // the same lifetime). restampCachedIds() uses it to tell a cached ID
1709
+ // that stands for the reused node itself from one that stands for a
1710
+ // node deeper inside it; see there.
1711
+ std::unordered_map<std::string, const oscad::ASTNode*> cacheProducer_;
1700
1712
  std::uint64_t randsCallCount_ = 0; // see noteRandsCall()
1701
1713
 
1702
1714
  // -- Debugging (Phase 9) --------------------------------------------
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.26.1"
7
+ version = "0.27.1"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -48,6 +48,11 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
48
48
  std::optional<std::vector<ColoredBody>> cached = key ? manifoldCache_->get(*key) : std::nullopt;
49
49
  if (cached) {
50
50
  node.bodies = std::move(*cached);
51
+ if (node.node) {
52
+ auto producer = cacheProducer_.find(*key);
53
+ restampCachedIds(node.bodies, *node.node,
54
+ producer == cacheProducer_.end() ? nullptr : producer->second);
55
+ }
51
56
  } else {
52
57
  // Recurse into children first (bottom-up) -- populates each
53
58
  // child's own .bodies in place so flattenCsgTree/a GenerateFn
@@ -75,7 +80,10 @@ std::vector<ColoredBody> Evaluator::generateTreeImpl(const std::vector<CSGNode*>
75
80
  // Mirrors generate_tree()'s own fallback.
76
81
  node.bodies = flattenCsgTree(node.children);
77
82
  }
78
- if (key) manifoldCache_->put(*key, node.bodies);
83
+ if (key) {
84
+ manifoldCache_->put(*key, node.bodies);
85
+ cacheProducer_[*key] = node.node;
86
+ }
79
87
  }
80
88
  for (const ColoredBody& b : node.bodies) topLevelBodies.push_back(b);
81
89
  }
@@ -104,6 +112,56 @@ std::vector<ColoredBody> Evaluator::generatePartialTree() {
104
112
  return generateTreeImpl(flat);
105
113
  }
106
114
 
115
+ void Evaluator::restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::ASTNode& node,
116
+ const oscad::ASTNode* producer) {
117
+ // A cache hit hands back the geometry AND the originalIDs of whichever
118
+ // call site first produced it. Those IDs are provenance -- "which node
119
+ // made this" -- not content, so two identical shapes at two call sites
120
+ // came back sharing one identity and selecting either picked both.
121
+ //
122
+ // Each distinct run gets a fresh ID, one per run rather than one per
123
+ // body, so a cached subtree made of several parts stays selectable
124
+ // part by part. Which node a fresh ID points at depends on what the old
125
+ // one stood for:
126
+ //
127
+ // - the reused node itself (`producer`): two separate pieces of
128
+ // source wrote the same shape, so this copy belongs to the node
129
+ // reusing it, not the one that happened to be generated first.
130
+ // - something deeper inside it -- the cube and sphere inside a module
131
+ // called twice: both calls really do come from that one line of the
132
+ // module body, so the old node is the honest answer and re-pointing
133
+ // it at the call site would collapse every part of the call onto a
134
+ // single line.
135
+ //
136
+ // Across renders the old IDs predate this render's idToNode, so nothing
137
+ // is known about their structure and everything falls to `node`.
138
+ //
139
+ // ponytail: two subtrees identical down to their transforms still
140
+ // share one attribution, since only the inner node is a cache hit and
141
+ // its producer check keeps the first copy's descendants. They occupy
142
+ // the same space, so there is nothing to select apart.
143
+ for (ColoredBody& cb : bodies) {
144
+ if (!cb.body || cb.body->IsEmpty()) continue;
145
+ manifold::MeshGL mesh = cb.body->GetMeshGL();
146
+ if (mesh.runOriginalID.empty()) continue;
147
+ std::unordered_map<uint32_t, uint32_t> remap;
148
+ for (uint32_t& id : mesh.runOriginalID) {
149
+ auto found = remap.find(id);
150
+ if (found == remap.end()) {
151
+ const uint32_t fresh = manifold::Manifold::ReserveIDs(1);
152
+ auto old = idToNode.find(id);
153
+ idToNode[fresh] =
154
+ (old != idToNode.end() && old->second != producer) ? old->second : &node;
155
+ auto color = idToColor.find(id);
156
+ if (color != idToColor.end()) idToColor[fresh] = color->second;
157
+ found = remap.emplace(id, fresh).first;
158
+ }
159
+ id = found->second;
160
+ }
161
+ cb.body = manifold::Manifold(mesh);
162
+ }
163
+ }
164
+
107
165
  ColoredBody Evaluator::tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue) {
108
166
  manifold::MeshGL mesh = body.GetMeshGL();
109
167
  std::optional<std::array<float, 4>> color = valueToColor(colorValue);
@@ -205,6 +205,7 @@ template <typename NodeList>
205
205
  std::vector<std::unique_ptr<CSGNode>> Evaluator::resolveTreeImpl(const NodeList& nodes, EvalContext& ctx) {
206
206
  idToNode.clear();
207
207
  idToColor.clear();
208
+ cacheProducer_.clear(); // holds AST pointers, same lifetime as idToNode's
208
209
  treeStack_.clear();
209
210
  treeStack_.emplace_back();
210
211
  rootCtx_ = &ctx;
@@ -5,7 +5,10 @@
5
5
  #include "test_helpers.hpp"
6
6
 
7
7
  #include <gtest/gtest.h>
8
+ #include <algorithm>
9
+ #include <iterator>
8
10
  #include <numbers>
11
+ #include <set>
9
12
 
10
13
  using namespace oscadeval;
11
14
  using namespace oscadeval::test;
@@ -121,20 +124,48 @@ TEST(ManifoldCacheUncacheable, RandsBeforeGeometryInModuleBodyTaintsSplicedSibli
121
124
  // -- cache reuse -----------------------------------------------------------
122
125
 
123
126
  TEST(ManifoldCache, CacheHitSkipsRegeneratingTheSubtree) {
127
+ // Read through the children: a hit serves the whole subtree at once, so
128
+ // the child is never generated and its own .bodies stays empty while
129
+ // the parent's is populated. (idToNode is no longer a proxy for this --
130
+ // restampCachedIds() populates it on a hit too, on purpose, or nothing
131
+ // served from cache would be selectable.)
132
+ const char* src = "translate([1,0,0]) cube(5, center=true);";
124
133
  auto cache = std::make_shared<ManifoldCache>();
125
- Evaluated first = evalSrcWithCache("cube(5, center=true);", cache);
126
- ASSERT_FALSE(first.ev.idToNode.empty()); // real generate work happened, tagGenerated() ran
127
134
 
128
- Evaluated second = evalSrcWithCache("cube(5, center=true);", cache);
129
- // A cache hit means generateCube() (and thus tagGenerated()) never ran
130
- // for the second Evaluator's own node -- its idToNode stays empty even
131
- // though its bodies are populated (served from the cache).
132
- EXPECT_TRUE(second.ev.idToNode.empty());
135
+ Evaluated first = evalSrcWithCache(src, cache);
136
+ ASSERT_EQ(first.tree.size(), 1u);
137
+ ASSERT_EQ(first.tree[0]->children.size(), 1u);
138
+ EXPECT_FALSE(first.tree[0]->children[0]->bodies.empty()) << "the cube really was generated";
139
+
140
+ Evaluated second = evalSrcWithCache(src, cache);
141
+ ASSERT_EQ(second.tree.size(), 1u);
142
+ ASSERT_EQ(second.tree[0]->children.size(), 1u);
143
+ EXPECT_TRUE(second.tree[0]->children[0]->bodies.empty())
144
+ << "a cache hit regenerated the subtree it was supposed to replace";
145
+
133
146
  ASSERT_EQ(second.bodies.size(), 1u);
134
147
  ASSERT_TRUE(second.bodies[0].body.has_value());
135
148
  EXPECT_NEAR(second.bodies[0].body->Volume(), 125.0, 1e-9);
136
149
  }
137
150
 
151
+ // Geometry served from the cache must still map back to source. Before
152
+ // this, a re-render of an unchanged file was all cache hits against a
153
+ // fresh Evaluator, so no ID carried a node and nothing in the viewport
154
+ // could be picked at all.
155
+ TEST(ManifoldCache, GeometryServedFromAnEarlierEvaluateStillMapsToItsSource) {
156
+ auto cache = std::make_shared<ManifoldCache>();
157
+ evalSrcWithCache("cube(5, center=true);", cache);
158
+
159
+ Evaluated second = evalSrcWithCache("cube(5, center=true);", cache);
160
+ ASSERT_EQ(second.bodies.size(), 1u);
161
+ const manifold::MeshGL mesh = second.bodies[0].body->GetMeshGL();
162
+ ASSERT_FALSE(mesh.runOriginalID.empty());
163
+ for (uint32_t id : mesh.runOriginalID) {
164
+ ASSERT_EQ(second.ev.idToNode.count(id), 1u) << "cached geometry has no source node";
165
+ EXPECT_NE(second.ev.idToNode.at(id), nullptr);
166
+ }
167
+ }
168
+
138
169
  TEST(ManifoldCache, DifferentParamsAreNotServedEachOthersCachedResult) {
139
170
  auto cache = std::make_shared<ManifoldCache>();
140
171
  Evaluated a = evalSrcWithCache("cube(2, center=true);", cache);
@@ -156,6 +187,88 @@ TEST(ManifoldCache, UncacheableNodeAlwaysRegeneratesNeverHitsOrPopulatesCache) {
156
187
  EXPECT_FALSE(second.ev.idToNode.empty());
157
188
  }
158
189
 
190
+ // A cache hit must not hand back the originalIDs of whichever call site
191
+ // first produced the geometry. Those IDs are provenance, and sharing them
192
+ // made two identical shapes one thing to a UI: selecting either picked
193
+ // both. Common in CAD, where arrays of identical parts are the norm.
194
+ TEST(ManifoldCache, IdenticalShapesAtDifferentCallSitesGetDistinctOriginalIds) {
195
+ auto cache = std::make_shared<ManifoldCache>();
196
+ Evaluated e = evalSrcWithCache(
197
+ "translate([0,0,0]) cylinder(h=20,r=4,$fn=32);\n"
198
+ "translate([25,0,0]) cylinder(h=20,r=4,$fn=32);\n", cache);
199
+ ASSERT_EQ(e.bodies.size(), 2u);
200
+
201
+ // Held in locals: GetMeshGL() returns by value, so taking begin() and
202
+ // end() from two separate calls walks between unrelated buffers.
203
+ const manifold::MeshGL meshA = e.bodies[0].body->GetMeshGL();
204
+ const manifold::MeshGL meshB = e.bodies[1].body->GetMeshGL();
205
+ const std::set<uint32_t> first(meshA.runOriginalID.begin(), meshA.runOriginalID.end());
206
+ const std::set<uint32_t> second(meshB.runOriginalID.begin(), meshB.runOriginalID.end());
207
+ ASSERT_FALSE(first.empty());
208
+ ASSERT_FALSE(second.empty());
209
+ std::vector<uint32_t> shared;
210
+ std::set_intersection(first.begin(), first.end(), second.begin(), second.end(),
211
+ std::back_inserter(shared));
212
+ EXPECT_TRUE(shared.empty()) << "the two cylinders share an originalID";
213
+
214
+ // Both still resolve to a node, so re-stamping does not lose the
215
+ // mapping selection needs.
216
+ for (uint32_t id : first) EXPECT_EQ(e.ev.idToNode.count(id), 1u);
217
+ for (uint32_t id : second) EXPECT_EQ(e.ev.idToNode.count(id), 1u);
218
+
219
+ // ...and to *different* nodes. Distinct IDs alone only separated them
220
+ // in the viewport: inheriting the first call site's node left the
221
+ // editor highlighting one line whichever cylinder was picked.
222
+ for (uint32_t a : first)
223
+ for (uint32_t b : second)
224
+ EXPECT_NE(e.ev.idToNode.at(a), e.ev.idToNode.at(b))
225
+ << "both cylinders point at one source node";
226
+ }
227
+
228
+ // The other half of that rule. A cached ID standing for something *inside*
229
+ // the reused node keeps its own node: both calls of a module really do
230
+ // come from the one body that spells the parts out. Re-pointing those at
231
+ // the call site would collapse every part of the call onto a single line
232
+ // -- the same defect as above, one level down.
233
+ TEST(ManifoldCache, PartsOfAReusedModuleKeepTheirOwnSourceNodes) {
234
+ auto cache = std::make_shared<ManifoldCache>();
235
+ Evaluated e = evalSrcWithCache(
236
+ "module pair() { cube(2); translate([5,0,0]) sphere(1,$fn=8); }\n"
237
+ "pair();\n"
238
+ "translate([0,20,0]) pair();\n", cache);
239
+ ASSERT_EQ(e.bodies.size(), 4u); // cube, sphere, cube, sphere
240
+
241
+ std::vector<const oscad::ASTNode*> nodes;
242
+ for (const ColoredBody& b : e.bodies) {
243
+ const manifold::MeshGL mesh = b.body->GetMeshGL();
244
+ ASSERT_EQ(mesh.runOriginalID.size(), 1u);
245
+ const uint32_t id = mesh.runOriginalID[0];
246
+ ASSERT_EQ(e.ev.idToNode.count(id), 1u);
247
+ nodes.push_back(e.ev.idToNode.at(id));
248
+ }
249
+ EXPECT_NE(nodes[0], nodes[1]) << "cube and sphere are not the same source";
250
+ EXPECT_EQ(nodes[0], nodes[2]) << "the second call's cube lost the cube() that wrote it";
251
+ EXPECT_EQ(nodes[1], nodes[3]) << "the second call's sphere lost the sphere() that wrote it";
252
+ }
253
+
254
+ // Re-stamping is per run, not per body, so a cached subtree made of
255
+ // several parts stays selectable part by part rather than collapsing into
256
+ // one -- which is what Manifold's own AsOriginal() would have done.
257
+ TEST(ManifoldCache, ACachedMultiPartSubtreeKeepsOneIdPerPart) {
258
+ auto cache = std::make_shared<ManifoldCache>();
259
+ Evaluated e = evalSrcWithCache(
260
+ "module pair() { cube(2); translate([5,0,0]) sphere(1,$fn=8); }\n"
261
+ "pair();\n"
262
+ "translate([0,20,0]) pair();\n", cache);
263
+ ASSERT_EQ(e.bodies.size(), 4u);
264
+ std::set<uint32_t> all;
265
+ for (const ColoredBody& b : e.bodies) {
266
+ const manifold::MeshGL mesh = b.body->GetMeshGL();
267
+ for (uint32_t id : mesh.runOriginalID) all.insert(id);
268
+ }
269
+ EXPECT_EQ(all.size(), 4u) << "parts of the reused subtree share IDs";
270
+ }
271
+
159
272
  TEST(ManifoldCache, OutputIsIdenticalCacheOnVsCacheOffAcrossBuiltinCategories) {
160
273
  const std::vector<std::string> scripts = {
161
274
  "cube(3);",