openscad-cpp-evaluator 0.55.3__tar.gz → 0.55.4__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 (208) hide show
  1. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/bytecode_vm.hpp +3 -0
  3. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/pyproject.toml +1 -1
  4. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/python/openscad_cpp_evaluator/__init__.py +2 -1
  5. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/booleans.cpp +47 -0
  6. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/bytecode_compiler.cpp +15 -1
  7. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/bytecode_vm.cpp +6 -1
  8. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_booleans.cpp +78 -8
  9. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_control_flow.cpp +6 -6
  10. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/.github/workflows/ci.yml +0 -0
  11. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/.github/workflows/wheels.yml +0 -0
  12. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/.gitignore +0 -0
  13. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/.gitmodules +0 -0
  14. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/CLAUDE.md +0 -0
  15. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/CMakeLists.txt +0 -0
  16. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/LICENSE +0 -0
  17. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/README.md +0 -0
  18. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/bindings/CMakeLists.txt +0 -0
  19. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/bindings/ast_to_py.cpp +0 -0
  20. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/bindings/module.cpp +0 -0
  21. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/cmake/embed_font.cmake +0 -0
  22. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/examples/CMakeLists.txt +0 -0
  23. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/examples/eval_perf_benchmark.cpp +0 -0
  24. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/examples/manifold_cache_reuse.cpp +0 -0
  25. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/examples/minimal_debugger.cpp +0 -0
  26. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  27. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/.gitignore +0 -0
  28. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  29. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  30. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/README.md +0 -0
  31. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  32. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  33. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  34. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  35. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  36. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  37. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  38. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  39. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  40. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  41. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  42. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  43. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  44. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  45. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  46. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  47. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/api.cpp +0 -0
  48. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  49. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  50. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  51. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  52. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  53. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  54. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  55. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  56. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  57. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  58. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  59. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  60. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  61. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  62. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  63. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  64. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/position.cpp +0 -0
  65. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  66. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  67. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  68. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  69. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  70. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  71. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  72. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  73. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  74. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  75. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  76. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  77. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  78. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  79. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  80. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  81. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  82. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  83. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  84. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  85. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  86. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  87. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  88. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  89. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  90. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  91. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  92. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  93. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  94. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  95. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  96. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  97. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  98. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  99. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  100. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  101. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  102. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  103. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  104. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  105. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  106. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  107. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  108. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/export.hpp +0 -0
  109. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  110. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  111. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  112. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  113. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  114. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  115. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  116. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  117. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  118. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  119. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  120. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  121. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  122. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  123. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/value.hpp +0 -0
  124. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  125. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/resources/fonts/LICENSE +0 -0
  126. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  127. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/CMakeLists.txt +0 -0
  128. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/builtins.hpp +0 -0
  129. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/call_args.cpp +0 -0
  130. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/color.cpp +0 -0
  131. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/control.cpp +0 -0
  132. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/dxf_dim.cpp +0 -0
  133. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/extrude.cpp +0 -0
  134. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/function_builtins.cpp +0 -0
  135. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/import.cpp +0 -0
  136. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/modifiers.cpp +0 -0
  137. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/primitives_2d.cpp +0 -0
  138. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/primitives_3d.cpp +0 -0
  139. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/registry.cpp +0 -0
  140. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/roof.cpp +0 -0
  141. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/segments.cpp +0 -0
  142. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/surface.cpp +0 -0
  143. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/text.cpp +0 -0
  144. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/topology.cpp +0 -0
  145. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/builtins/transforms.cpp +0 -0
  146. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/colored_body.cpp +0 -0
  147. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/csg_generate.cpp +0 -0
  148. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/csg_resolve.cpp +0 -0
  149. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/css_colors.cpp +0 -0
  150. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/debug/debug_repl.cpp +0 -0
  151. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/debug_profile.cpp +0 -0
  152. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/eval_context.cpp +0 -0
  153. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/eval_error.cpp +0 -0
  154. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/eval_use.cpp +0 -0
  155. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/export.cpp +0 -0
  156. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/expr_eval.cpp +0 -0
  157. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/import/dxf_import.cpp +0 -0
  158. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/import/mesh_import.cpp +0 -0
  159. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/import/surface_load.cpp +0 -0
  160. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/import/svg_import.cpp +0 -0
  161. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/manifold_cache.cpp +0 -0
  162. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/measure_geometry.cpp +0 -0
  163. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/mesh_check.cpp +0 -0
  164. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/stmt_eval.cpp +0 -0
  165. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/text/font_match.cpp +0 -0
  166. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/text/freetype_font_provider.cpp +0 -0
  167. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/text/text_metrics.cpp +0 -0
  168. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/user_calls.cpp +0 -0
  169. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/value.cpp +0 -0
  170. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/src/zip_stored.cpp +0 -0
  171. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/CMakeLists.txt +0 -0
  172. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_bytecode_compiler.cpp +0 -0
  173. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_children_separate.cpp +0 -0
  174. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_cli.cpp +0 -0
  175. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_csg_tree.cpp +0 -0
  176. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_debug_hooks.cpp +0 -0
  177. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_dxf_svg_import.cpp +0 -0
  178. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_eval_context.cpp +0 -0
  179. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_export.cpp +0 -0
  180. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_expr_eval.cpp +0 -0
  181. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_extrude_roof.cpp +0 -0
  182. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_feature_detection.cpp +0 -0
  183. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_function_builtins.cpp +0 -0
  184. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_helpers.hpp +0 -0
  185. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_import_export.cpp +0 -0
  186. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_manifold_cache.cpp +0 -0
  187. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_mesh_check.cpp +0 -0
  188. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_multi_color_merge.cpp +0 -0
  189. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_primitives.cpp +0 -0
  190. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_profiling.cpp +0 -0
  191. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_python_bindings.py +0 -0
  192. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_render_expr.cpp +0 -0
  193. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_scope_trail.cpp +0 -0
  194. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_scoping.cpp +0 -0
  195. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_surface.cpp +0 -0
  196. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_tail_calls.cpp +0 -0
  197. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_text.cpp +0 -0
  198. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_transforms.cpp +0 -0
  199. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_unexpected_args.cpp +0 -0
  200. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_use.cpp +0 -0
  201. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_value.cpp +0 -0
  202. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_viewport_params.cpp +0 -0
  203. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tests/test_zip_stored.cpp +0 -0
  204. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tools/cli/CMakeLists.txt +0 -0
  205. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tools/cli/cli_lib.cpp +0 -0
  206. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tools/cli/cli_lib.hpp +0 -0
  207. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/tools/cli/main.cpp +0 -0
  208. {openscad_cpp_evaluator-0.55.3 → openscad_cpp_evaluator-0.55.4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.55.3
3
+ Version: 0.55.4
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -68,6 +68,9 @@ struct PendingCsgWrap {
68
68
  std::uint64_t randsBefore = 0;
69
69
  int siteIdx = -1;
70
70
  std::vector<Value> groupSizes;
71
+ // Parallel to groupSizes: whether a group that came out EMPTY is still
72
+ // an operand. See Op::CsgGroupEnd's `a`.
73
+ std::vector<Value> emptyIsAGroup;
71
74
  size_t groupStartSize = 0;
72
75
  };
73
76
 
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.55.3"
7
+ version = "0.55.4"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -213,7 +213,8 @@ def _summarize_param(value, max_items: int = 6, max_len: int = 40) -> str:
213
213
  # Params keys never shown in format_csg_tree's per-node summary, regardless
214
214
  # of kind -- pure internal bookkeeping or already represented structurally
215
215
  # elsewhere in the tree. Mirrors the reference's _DUMP_HIDDEN_PARAM_KEYS.
216
- _DUMP_HIDDEN_PARAM_KEYS = frozenset({"color", "op", "name", "group_sizes"})
216
+ _DUMP_HIDDEN_PARAM_KEYS = frozenset(
217
+ {"color", "op", "name", "group_sizes", "empty_is_a_group"})
217
218
 
218
219
  # Auto-generated tessellation data (not user-authored) for every kind except
219
220
  # polyhedron, where the equivalent data *is* the user's own points/faces.
@@ -108,16 +108,43 @@ CSGParams resolveCsg(Evaluator& ev, const oscad::ModularCall& node, EvalContext&
108
108
  if (!assignNodes.empty()) ev.evalChildren(assignNodes, blockCtx);
109
109
 
110
110
  std::vector<Value> groupSizes;
111
+ std::vector<Value> emptyIsAGroup;
111
112
  groupSizes.reserve(geoNodes.size());
113
+ emptyIsAGroup.reserve(geoNodes.size());
112
114
  for (const oscad::ASTNode* geoNode : geoNodes) {
113
115
  const size_t before = ev.currentTreeFrameSize();
114
116
  ev.evalChildren(std::vector<const oscad::ASTNode*>{geoNode}, blockCtx);
115
117
  groupSizes.push_back(Value{static_cast<double>(ev.currentTreeFrameSize() - before)});
118
+ // Whether a statement that produced NOTHING still counts as an
119
+ // operand.
120
+ //
121
+ // The reference builds a node for a module instantiation and for a
122
+ // loop whatever they contain, so an empty one is an empty operand
123
+ // and annihilates an intersection. An `if` that takes no branch, a
124
+ // bare block, and a `*`-disabled statement build no node at all, so
125
+ // they are not operands and must be skipped.
126
+ //
127
+ // Every line of this was checked against OpenSCAD rather than
128
+ // reasoned about -- `intersection(){ cube(10); X; }` keeps the cube
129
+ // for `if(false) sphere(6)`, `{ }` and `*cube(1)`, and comes out
130
+ // empty for `for(i=[1:0]) sphere(6)`, `union(){}`, `group(){}` and
131
+ // a call to an empty user module.
132
+ const oscad::NodeKind k = geoNode->kind();
133
+ emptyIsAGroup.push_back(Value{
134
+ k == oscad::NodeKind::ModularCall ||
135
+ k == oscad::NodeKind::ModularFor ||
136
+ k == oscad::NodeKind::ModularIntersectionFor ||
137
+ k == oscad::NodeKind::ModularLet ||
138
+ k == oscad::NodeKind::ModularModifierShowOnly ||
139
+ k == oscad::NodeKind::ModularModifierHighlight ||
140
+ k == oscad::NodeKind::ModularModifierBackground});
116
141
  }
117
142
 
118
143
  CSGParams params;
119
144
  params["op"] = Value{op};
120
145
  params["group_sizes"] = Value{std::make_shared<const ValueList>(ValueList{std::move(groupSizes)})};
146
+ params["empty_is_a_group"] =
147
+ Value{std::make_shared<const ValueList>(ValueList{std::move(emptyIsAGroup)})};
121
148
  return params;
122
149
  }
123
150
 
@@ -180,16 +207,36 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
180
207
  const oscad::ASTNode&) {
181
208
  const std::string& op = std::get<std::string>(params.at("op"));
182
209
  const auto& groupSizes = std::get<ListPtr>(params.at("group_sizes"))->items;
210
+ const auto emptyIsAGroupIt = params.find("empty_is_a_group");
211
+ const std::vector<Value>* emptyIsAGroup =
212
+ emptyIsAGroupIt == params.end()
213
+ ? nullptr
214
+ : &std::get<ListPtr>(emptyIsAGroupIt->second)->items;
183
215
 
184
216
  std::vector<ColoredBody> allBg, allHi, allSo, allDo;
185
217
  std::optional<ColoredBody> csgResult;
186
218
  size_t idx = 0;
187
219
 
220
+ size_t stmtIndex = 0;
188
221
  for (const Value& sizeVal : groupSizes) {
189
222
  const size_t size = static_cast<size_t>(std::get<double>(sizeVal));
223
+ const size_t thisStmt = stmtIndex++;
190
224
  std::vector<ColoredBody> stmtBodies = flattenCsgTree(children, idx, size);
191
225
  idx += size;
192
226
 
227
+ // A statement that built no CSG node at all is not an operand --
228
+ // an `if` whose branch was not taken, or an empty block. Only a
229
+ // for-loop still counts as one when it produced nothing, matching
230
+ // the group node the reference builds for it regardless. Without
231
+ // this, `intersection(){ X; if (crop) Y; }` -- BOSL2's hirth()
232
+ // among others -- came out empty whenever the condition was false.
233
+ if (size == 0) {
234
+ const bool isGroup = emptyIsAGroup && thisStmt < emptyIsAGroup->size() &&
235
+ std::holds_alternative<bool>((*emptyIsAGroup)[thisStmt]) &&
236
+ std::get<bool>((*emptyIsAGroup)[thisStmt]);
237
+ if (!isGroup) continue;
238
+ }
239
+
193
240
  RoleSplit split = splitByRole(stmtBodies);
194
241
  allBg.insert(allBg.end(), split.background.begin(), split.background.end());
195
242
  allHi.insert(allHi.end(), split.highlight.begin(), split.highlight.end());
@@ -1348,7 +1348,21 @@ public:
1348
1348
  }
1349
1349
  out.push_back({Op::CsgGroupStart, 0, 0, nullptr});
1350
1350
  compileStatementList(std::vector<const oscad::ASTNode*>{geoNode}, out);
1351
- out.push_back({Op::CsgGroupEnd, 0, 0, nullptr});
1351
+ // `a` = whether a group that turns out EMPTY is still an
1352
+ // operand: a module instantiation or a loop builds a node
1353
+ // whatever it contains, an `if`/block/`*` does not. Mirrors
1354
+ // resolveCsg's own "empty_is_a_group" (booleans.cpp), where the
1355
+ // full reasoning and the OpenSCAD checks live.
1356
+ const oscad::NodeKind k = geoNode->kind();
1357
+ const int emptyIsAGroup = (
1358
+ k == oscad::NodeKind::ModularCall ||
1359
+ k == oscad::NodeKind::ModularFor ||
1360
+ k == oscad::NodeKind::ModularIntersectionFor ||
1361
+ k == oscad::NodeKind::ModularLet ||
1362
+ k == oscad::NodeKind::ModularModifierShowOnly ||
1363
+ k == oscad::NodeKind::ModularModifierHighlight ||
1364
+ k == oscad::NodeKind::ModularModifierBackground) ? 1 : 0;
1365
+ out.push_back({Op::CsgGroupEnd, emptyIsAGroup, 0, nullptr});
1352
1366
  }
1353
1367
  out.push_back({Op::CloseExprScope, 0, 0, nullptr});
1354
1368
  out.push_back({Op::PopCsgWrap, idx, 0, &call.position()});
@@ -1311,7 +1311,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
1311
1311
  // ITERATION child ctxs are pushed separately, by the
1312
1312
  // compiled loop's own Op::ForIterNext.
1313
1313
  ev.treeStack_.emplace_back();
1314
- f.csgWrapStack.push_back({site.op, randsBefore, ins.a, {}, 0});
1314
+ f.csgWrapStack.push_back({site.op, randsBefore, ins.a, {}, {}, 0});
1315
1315
  ++f.pc;
1316
1316
  break;
1317
1317
  }
@@ -1336,6 +1336,8 @@ Value driveVm(Evaluator& ev, size_t floor) {
1336
1336
  ev.evalChildren(std::vector<const oscad::ASTNode*>{stmt}, ctx);
1337
1337
  pending.groupSizes.push_back(
1338
1338
  Value{static_cast<double>(ev.treeStack_.back().size() - before)});
1339
+ // A spliced child is a plain statement, never a loop.
1340
+ pending.emptyIsAGroup.push_back(Value{false});
1339
1341
  }
1340
1342
  ++f.pc;
1341
1343
  break;
@@ -1344,6 +1346,7 @@ Value driveVm(Evaluator& ev, size_t floor) {
1344
1346
  PendingCsgWrap& pending = f.csgWrapStack.back();
1345
1347
  const size_t after = ev.treeStack_.back().size();
1346
1348
  pending.groupSizes.push_back(Value{static_cast<double>(after - pending.groupStartSize)});
1349
+ pending.emptyIsAGroup.push_back(Value{ins.a != 0});
1347
1350
  ++f.pc;
1348
1351
  break;
1349
1352
  }
@@ -1367,6 +1370,8 @@ Value driveVm(Evaluator& ev, size_t floor) {
1367
1370
  if (site.includeOpParam) params["op"] = Value{pending.op};
1368
1371
  params["group_sizes"] =
1369
1372
  Value{std::make_shared<const ValueList>(ValueList{std::move(pending.groupSizes)})};
1373
+ params["empty_is_a_group"] = Value{std::make_shared<const ValueList>(
1374
+ ValueList{std::move(pending.emptyIsAGroup)})};
1370
1375
  // Mirrors Evaluator::buildTreeNode's own post-
1371
1376
  // resolveBody() half exactly (csg_resolve.cpp).
1372
1377
  const bool uncacheable =
@@ -40,11 +40,76 @@ TEST(Intersection, KeepsOnlyOverlappingVolume) {
40
40
  EXPECT_NEAR(e.bodies[0].body->Volume(), 1.0, 1e-6);
41
41
  }
42
42
 
43
- TEST(Intersection, DisabledStatementIsTrulyEmptyAndDiscardsResult) {
43
+ TEST(Intersection, ADisabledStatementIsNotAnOperandAtAll) {
44
+ // `*cube(1)` produces no CSGNode, so it is not an operand -- the
45
+ // intersection has one child and returns it. This used to assert an
46
+ // EMPTY result on the reasoning that a statement contributing no bodies
47
+ // is "intersection with nothing"; checking against OpenSCAD showed that
48
+ // is wrong (it gives volume 8, the cube(2)), and the same mistake is
49
+ // what made BOSL2's hirth() render nothing at all: its geometry is
50
+ // `intersection(){ ...; if (crop) tube(...); }` and crop defaults false.
44
51
  Evaluated e = evalSrc("intersection() { cube(2); *cube(1); }");
45
- // *cube(1) produces no CSGNode at all -> its "statement" contributes a
46
- // group of size 0 -- flattening an empty group yields no bodies, which
47
- // intersection() treats as "intersection with nothing" -> empty result.
52
+ ASSERT_EQ(e.bodies.size(), 1u);
53
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 8.0, 1e-6);
54
+ }
55
+
56
+ TEST(Intersection, AnUntakenIfIsNotAnOperandEither) {
57
+ // The reported case, reduced. Verified against OpenSCAD: volume 1000.
58
+ Evaluated e = evalSrc("intersection() { cube(10); if (false) sphere(6); }");
59
+ ASSERT_EQ(e.bodies.size(), 1u);
60
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 1000.0, 1e-6);
61
+ }
62
+
63
+ TEST(Intersection, EmptyStatementsFollowTheReferenceExactly) {
64
+ // Every row checked against OpenSCAD, not reasoned about. The rule that
65
+ // falls out: a module instantiation or a loop builds a node whatever it
66
+ // contains, so an empty one is an empty operand and annihilates; an
67
+ // `if` with no branch taken, a bare block and a `*`-disabled statement
68
+ // build no node at all and are not operands.
69
+ struct Case { const char* stmt; bool keepsTheCube; };
70
+ const Case cases[] = {
71
+ {"if (false) sphere(6);", true},
72
+ {"{ }", true},
73
+ {"*cube(1);", true},
74
+ {"for (i = [1:0]) sphere(6);", false},
75
+ {"union() { }", false},
76
+ {"group() { }", false},
77
+ {"let(x = 1) { }", false},
78
+ {"#union() { }", false},
79
+ };
80
+ for (const Case& c : cases) {
81
+ const std::string src =
82
+ std::string("intersection() { cube(10, center=true); ") + c.stmt + " }";
83
+ Evaluated e = evalSrc(src);
84
+ if (c.keepsTheCube) {
85
+ ASSERT_EQ(e.bodies.size(), 1u) << src;
86
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 1000.0, 1e-6) << src;
87
+ } else {
88
+ const bool anyVolume = std::any_of(
89
+ e.bodies.begin(), e.bodies.end(),
90
+ [](const ColoredBody& b) { return b.body && !b.body->IsEmpty(); });
91
+ EXPECT_FALSE(anyVolume) << src;
92
+ }
93
+ }
94
+ }
95
+
96
+ TEST(Intersection, AnEmptyUserModuleIsStillAnOperand) {
97
+ // The reference builds a node for a module instantiation whatever the
98
+ // module's body does, so calling one that draws nothing empties the
99
+ // intersection. Verified against OpenSCAD (writes no STL).
100
+ Evaluated e = evalSrc("module m() { } intersection() { cube(10); m(); }");
101
+ const bool anyVolume = std::any_of(
102
+ e.bodies.begin(), e.bodies.end(),
103
+ [](const ColoredBody& b) { return b.body && !b.body->IsEmpty(); });
104
+ EXPECT_FALSE(anyVolume);
105
+ }
106
+
107
+ TEST(Intersection, AnEmptyForLoopIsStillAnOperand) {
108
+ // The one empty statement that DOES annihilate: the reference builds a
109
+ // group node for a for-loop however many times it iterates, so an empty
110
+ // one is an empty operand. Verified against OpenSCAD, which writes no
111
+ // STL at all for this.
112
+ Evaluated e = evalSrc("intersection() { cube(10); for (i = [1:0]) sphere(6); }");
48
113
  EXPECT_TRUE(e.bodies.empty());
49
114
  }
50
115
 
@@ -73,14 +138,19 @@ TEST(Union, MultipleBodiesInFirstStatementAllSurvive) {
73
138
  EXPECT_NEAR(e.bodies[0].body->Volume(), 20.0, 1e-6);
74
139
  }
75
140
 
76
- TEST(Intersection, EmptyFirstOperandGivesEmpty) {
141
+ TEST(Intersection, ADisabledFirstStatementLetsTheNextOneLead) {
142
+ // Verified against OpenSCAD: volume 8, not empty.
77
143
  Evaluated e = evalSrc("intersection() { *cube(10); cube(2); }");
78
- EXPECT_TRUE(e.bodies.empty());
144
+ ASSERT_EQ(e.bodies.size(), 1u);
145
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 8.0, 1e-6);
79
146
  }
80
147
 
81
- TEST(Difference, EmptyFirstOperandGivesEmpty) {
148
+ TEST(Difference, ADisabledFirstStatementLetsTheNextOneBecomeTheBase) {
149
+ // Verified against OpenSCAD: volume 8 -- cube(2) becomes the base
150
+ // rather than the difference collapsing to nothing.
82
151
  Evaluated e = evalSrc("difference() { *cube(10); cube(2); }");
83
- EXPECT_TRUE(e.bodies.empty());
152
+ ASSERT_EQ(e.bodies.size(), 1u);
153
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 8.0, 1e-6);
84
154
  }
85
155
 
86
156
  TEST(Difference, EmptySubtractorLeavesBaseUnchanged) {
@@ -845,15 +845,15 @@ TEST(ExprEvalFunctionCall, CallingANonFunctionVariableWarnsAndIsUndef) {
845
845
  EXPECT_NE(warnings.back().find("Ignoring unknown function 'x'"), std::string::npos);
846
846
  }
847
847
 
848
- TEST(Intersection, EchoInDisabledFirstStatementStillFiresDespiteEmptyResult) {
849
- // A deliberate resolve/generate-split consequence: resolve can't yet
850
- // know the whole intersection() will end up geometrically empty (that
851
- // isn't known until generate time), so echo()'s side effect during
852
- // resolve still fires even though the final result has zero bodies.
848
+ TEST(Intersection, EchoInADisabledStatementStillFires) {
849
+ // echo() runs during resolve, so its side effect happens whatever the
850
+ // geometry does. The result is the cube(2): neither the disabled
851
+ // statement nor the echo statement is an operand.
853
852
  std::string captured;
854
853
  Evaluated e = evalSrc("intersection() { *cube(10); echo(\"fired\"); cube(2); }", [&](const std::string& msg) { captured = msg; });
855
854
  EXPECT_EQ(captured, "ECHO: \"fired\"");
856
- EXPECT_TRUE(e.bodies.empty());
855
+ ASSERT_EQ(e.bodies.size(), 1u);
856
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 8.0, 1e-6);
857
857
  }
858
858
 
859
859
  TEST(ListComprehension, IfClauseWithNestedListLiteralBody) {