openscad-cpp-evaluator 1.41.0__tar.gz → 1.43.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 (245) hide show
  1. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/CLAUDE.md +39 -18
  2. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/PKG-INFO +3 -2
  3. openscad_cpp_evaluator-1.43.0/external/openscad_cpp_parser/LICENSE +21 -0
  4. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/bytecode.hpp +14 -46
  5. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/colored_body.hpp +3 -3
  6. openscad_cpp_evaluator-1.43.0/include/openscad_cpp_evaluator/dxf_svg_import.hpp +67 -0
  7. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/evaluator.hpp +31 -0
  8. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/export.hpp +5 -8
  9. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/import_builtin.hpp +3 -1
  10. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/segments.hpp +9 -18
  11. openscad_cpp_evaluator-1.43.0/include/openscad_cpp_evaluator/surface_load.hpp +20 -0
  12. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/value.hpp +6 -7
  13. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/pyproject.toml +3 -3
  14. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/CMakeLists.txt +1 -1
  15. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/booleans.cpp +2 -2
  16. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/builtins.hpp +15 -2
  17. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/control.cpp +3 -4
  18. openscad_cpp_evaluator-1.43.0/src/builtins/dxf_dim.cpp +220 -0
  19. openscad_cpp_evaluator-1.43.0/src/builtins/extrude.cpp +563 -0
  20. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/function_builtins.cpp +332 -378
  21. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/import.cpp +150 -80
  22. openscad_cpp_evaluator-1.43.0/src/builtins/primitives_2d.cpp +266 -0
  23. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/primitives_3d.cpp +122 -173
  24. openscad_cpp_evaluator-1.43.0/src/builtins/roof.cpp +485 -0
  25. openscad_cpp_evaluator-1.43.0/src/builtins/segments.cpp +253 -0
  26. openscad_cpp_evaluator-1.43.0/src/builtins/surface.cpp +114 -0
  27. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/transforms.cpp +206 -190
  28. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/bytecode_compiler.cpp +40 -82
  29. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/bytecode_vm.cpp +10 -29
  30. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/colored_body.cpp +1 -1
  31. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/eval_context.cpp +2 -1
  32. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/export.cpp +91 -45
  33. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/expr_eval.cpp +50 -25
  34. openscad_cpp_evaluator-1.43.0/src/import/dxf_import.cpp +370 -0
  35. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/import/mesh_import.cpp +56 -18
  36. openscad_cpp_evaluator-1.43.0/src/import/surface_load.cpp +124 -0
  37. openscad_cpp_evaluator-1.43.0/src/import/svg_import.cpp +1017 -0
  38. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/stmt_eval.cpp +36 -15
  39. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/text/freetype_font_provider.cpp +18 -3
  40. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/user_calls.cpp +38 -30
  41. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/value.cpp +229 -158
  42. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_bytecode_compiler.cpp +10 -7
  43. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_control_flow.cpp +10 -3
  44. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_dxf_svg_import.cpp +314 -51
  45. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_eval_context.cpp +2 -1
  46. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_export.cpp +18 -5
  47. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_expr_eval.cpp +3 -6
  48. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_extrude_roof.cpp +2 -2
  49. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_helpers.hpp +24 -0
  50. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_import_export.cpp +56 -54
  51. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_openscad_parity.cpp +113 -0
  52. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_python_bindings.py +14 -2
  53. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_transforms.cpp +1 -0
  54. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/uv.lock +1 -1
  55. openscad_cpp_evaluator-1.41.0/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -46
  56. openscad_cpp_evaluator-1.41.0/include/openscad_cpp_evaluator/surface_load.hpp +0 -23
  57. openscad_cpp_evaluator-1.41.0/src/builtins/dxf_dim.cpp +0 -244
  58. openscad_cpp_evaluator-1.41.0/src/builtins/extrude.cpp +0 -549
  59. openscad_cpp_evaluator-1.41.0/src/builtins/primitives_2d.cpp +0 -242
  60. openscad_cpp_evaluator-1.41.0/src/builtins/roof.cpp +0 -489
  61. openscad_cpp_evaluator-1.41.0/src/builtins/segments.cpp +0 -284
  62. openscad_cpp_evaluator-1.41.0/src/builtins/surface.cpp +0 -118
  63. openscad_cpp_evaluator-1.41.0/src/import/dxf_import.cpp +0 -115
  64. openscad_cpp_evaluator-1.41.0/src/import/surface_load.cpp +0 -132
  65. openscad_cpp_evaluator-1.41.0/src/import/svg_import.cpp +0 -628
  66. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/.github/workflows/ci.yml +0 -0
  67. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/.github/workflows/wheels.yml +0 -0
  68. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/.gitignore +0 -0
  69. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/.gitmodules +0 -0
  70. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/CMakeLists.txt +0 -0
  71. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/LICENSE +0 -0
  72. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/README.md +0 -0
  73. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/bindings/CMakeLists.txt +0 -0
  74. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/bindings/ast_to_py.cpp +0 -0
  75. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/bindings/module.cpp +0 -0
  76. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/cmake/embed_font.cmake +0 -0
  77. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/cmake/patch_manifold.cmake +0 -0
  78. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/examples/CMakeLists.txt +0 -0
  79. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/examples/eval_perf_benchmark.cpp +0 -0
  80. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/examples/manifold_cache_reuse.cpp +0 -0
  81. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/examples/minimal_debugger.cpp +0 -0
  82. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  83. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/.gitignore +0 -0
  84. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  85. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  86. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/README.md +0 -0
  87. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  88. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  89. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  90. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  91. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  92. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  93. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  94. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  95. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  96. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  97. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  98. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  99. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  100. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  101. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  102. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  103. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/.DS_Store +0 -0
  104. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  105. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  106. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  107. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  108. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  109. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  110. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  111. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  112. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  113. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  114. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  115. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  116. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  117. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  118. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  119. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  120. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  121. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  122. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  123. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  124. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  125. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  126. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  127. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  128. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  129. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  130. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  131. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  132. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
  133. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  134. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  135. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  136. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  137. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  138. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  139. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  140. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  141. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  142. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  143. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
  144. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  145. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  146. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  147. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  148. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  149. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  150. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  151. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  152. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  153. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  154. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  155. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  156. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  157. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  158. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  159. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  160. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  161. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  162. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  163. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  164. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  165. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  166. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  167. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  168. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  169. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  170. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  171. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  172. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  173. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  174. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  175. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  176. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  177. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  178. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  179. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  180. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  181. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  182. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/resources/fonts/LICENSE +0 -0
  183. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  184. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  185. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  186. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  187. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/call_args.cpp +0 -0
  188. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/color.cpp +0 -0
  189. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/modifiers.cpp +0 -0
  190. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/profile_time.cpp +0 -0
  191. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/registry.cpp +0 -0
  192. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/text.cpp +0 -0
  193. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/builtins/topology.cpp +0 -0
  194. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/coverage.cpp +0 -0
  195. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/csg_generate.cpp +0 -0
  196. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/csg_resolve.cpp +0 -0
  197. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/css_colors.cpp +0 -0
  198. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/debug/debug_repl.cpp +0 -0
  199. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/debug_profile.cpp +0 -0
  200. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/eval_error.cpp +0 -0
  201. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/eval_use.cpp +0 -0
  202. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/format_closure.cpp +0 -0
  203. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/import/scene_import.cpp +0 -0
  204. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/manifold_cache.cpp +0 -0
  205. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/measure_geometry.cpp +0 -0
  206. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/mesh_check.cpp +0 -0
  207. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/text/font_match.cpp +0 -0
  208. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/text/text_metrics.cpp +0 -0
  209. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/utf8.cpp +0 -0
  210. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/xkcd_colors.cpp +0 -0
  211. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/xkcd_colors.hpp +0 -0
  212. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/src/zip_stored.cpp +0 -0
  213. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/CMakeLists.txt +0 -0
  214. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_booleans.cpp +0 -0
  215. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_children_separate.cpp +0 -0
  216. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_cli.cpp +0 -0
  217. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_coverage.cpp +0 -0
  218. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_csg_tree.cpp +0 -0
  219. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_debug_hooks.cpp +0 -0
  220. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_discretizer.cpp +0 -0
  221. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_feature_detection.cpp +0 -0
  222. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_function_builtins.cpp +0 -0
  223. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_include_cache.cpp +0 -0
  224. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_manifold_cache.cpp +0 -0
  225. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_mesh_check.cpp +0 -0
  226. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_multi_color_merge.cpp +0 -0
  227. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_primitives.cpp +0 -0
  228. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_profile_time.cpp +0 -0
  229. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_profiling.cpp +0 -0
  230. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_render_expr.cpp +0 -0
  231. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_scope_trail.cpp +0 -0
  232. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_scoping.cpp +0 -0
  233. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_surface.cpp +0 -0
  234. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_tail_calls.cpp +0 -0
  235. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_text.cpp +0 -0
  236. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_undef_args.cpp +0 -0
  237. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_unexpected_args.cpp +0 -0
  238. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_use.cpp +0 -0
  239. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_value.cpp +0 -0
  240. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_viewport_params.cpp +0 -0
  241. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tests/test_zip_stored.cpp +0 -0
  242. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tools/cli/CMakeLists.txt +0 -0
  243. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tools/cli/cli_lib.cpp +0 -0
  244. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tools/cli/cli_lib.hpp +0 -0
  245. {openscad_cpp_evaluator-1.41.0 → openscad_cpp_evaluator-1.43.0}/tools/cli/main.cpp +0 -0
@@ -63,7 +63,7 @@ plus gating the silent `is_undef` probe on the call actually resolving to the bu
63
63
  CSG-tree shape like union/difference/intersection, role-split via the shared `splitByRole`),
64
64
  `linear_extrude()`/`rotate_extrude()`/`projection(cut=)`, and `roof()`; and (Phase 7) `offset()`,
65
65
  `surface()` (`.dat` text + PNG/JPEG/BMP/GIF via `stb_image`), hand-rolled DXF (`LWPOLYLINE`/2D
66
- `POLYLINE`) and SVG (`path`/`polygon`/`polyline`/`rect`/`circle`/`ellipse`, nested
66
+ `POLYLINE`) and SVG (a port of upstream's libsvg: shapes, strokes, `<use>`, layers, nested
67
67
  `transform=`, all path commands including elliptical arcs) 2D contour import in both module
68
68
  (Region geometry) and expression (Region value) contexts, and the `FontProvider` abstraction
69
69
  (`font_provider.hpp`) + built-in FreeType/HarfBuzz backend + `text()`/`textmetrics()`/
@@ -1248,9 +1248,13 @@ grep for `ponytail:`.
1248
1248
  the module *and* expression forms, enclosing transforms still apply to a selected element (it
1249
1249
  has to land where it does in the drawing), and **a filter that matches nothing imports nothing
1250
1250
  and warns** rather than falling back to the whole file — a cut layer that quietly became every
1251
- layer is the worst answer available. `layer=` stays DXF-only on purpose: upstream reuses the
1252
- name for SVG but reads only Inkscape's `inkscape:label`, a vendor convention rather than
1253
- anything SVG defines, so it cannot match a layer from any other tool. `id` had been in
1251
+ layer is the worst answer available. `layer=` (and deprecated `layername=`) also selects in
1252
+ SVG, as upstream does: an Inkscape layer (`inkscape:groupmode="layer"` with an
1253
+ `inkscape:label`). Alone it takes that layer's own group; with `id=`/`class=` it restricts them
1254
+ to elements inside the named layer at any depth. The selection rules were read off the binary:
1255
+ the nearest selected or `display:none` ancestor-or-self decides whether a shape is drawn, so an
1256
+ id selects an element even under a hidden layer, while `layer=` alone does not reach into a
1257
+ hidden sublayer. The miss warning names `id`, `layer`, `class` in that order. `id` had been in
1254
1258
  `registry.cpp`'s declared parameter list since long before anything read it, so a script
1255
1259
  filtering by id was accepted in full and silently handed the whole drawing.
1256
1260
  `resolveFilePath()` (also declared here, defined in import.cpp) — resolves a file argument
@@ -1281,20 +1285,37 @@ grep for `ponytail:`.
1281
1285
  triangle-count parity.
1282
1286
  - `include/openscad_cpp_evaluator/dxf_svg_import.hpp`, `src/import/dxf_import.cpp`,
1283
1287
  `src/import/svg_import.cpp` — hand-rolled, dependency-free 2D-contour loaders (no XML/DXF library
1284
- pulled in, matching the plan's §9 dependency table). `loadDxfContours()` reads DXF's group-code
1285
- text format directly (closed `LWPOLYLINE`/2D `POLYLINE` entities only, optional layer filter) --
1286
- covers the same narrow entity set the reference's own `ezdxf`-based loader does, nothing more.
1288
+ pulled in, matching the plan's §9 dependency table). `loadDxf()` reads DXF's group-code
1289
+ text format directly and matches OpenSCAD 2026.02.01's import, established black-box (its DXF
1290
+ source is GPL; do not read or port it): `LINE`/`CIRCLE`/`ARC`/`ELLIPSE`/`LWPOLYLINE`/`INSERT`
1291
+ become segments (arcs by the circle fragment rules), points snap to a 1/1024 grid, segments chain
1292
+ into paths that are ALL closed (an open chain gets a straight closing edge), filled even-odd;
1293
+ `origin=`/`scale=`/`layer=` and its warnings ("Illegal value", "Not enough input values",
1294
+ "Unsupported DXF Entity") as OpenSCAD has them. The notes atop `dxf_import.cpp` list the rules,
1295
+ the two ellipse bugs deliberately not copied, and the `POLYLINE` extension.
1287
1296
  `loadSvgContours()` pairs a minimal recursive-descent XML tree parser (open/close/self-closing
1288
- tags, quoted attributes, comments/CDATA/prolog skipping, entity decoding — no namespace URI
1289
- resolution, a tag's `prefix:` is stripped textually) with a close port of the reference's own
1290
- path-command tokenizer/flattener (`M`/`L`/`H`/`V`/`C`/`S`/`Q`/`T`/`A`, both absolute and relative,
1291
- cubic/quadratic Bezier flattening, full elliptical-arc endpoint-to-center parameterization) plus
1292
- `rect`/`circle`/`ellipse`/`polygon`/`polyline` and nested `transform="matrix()/translate()/
1293
- scale()/rotate()"` composition (including through `<g>` groups), in SVG user units at the
1294
- point-transform step, `applyMat()` (named to avoid colliding with `std::apply` in an ADL lookup
1295
- that broke the build once — see its own comment). **Placement is `pageMap()`, a port of the
1296
- reference's `import_svg.cc`**: the page's `width`/`height` to mm (a unitless length at `dpi=`,
1297
- 72 by default; `px` at 96), the `viewBox` scaled onto the page under `preserveAspectRatio`
1297
+ tags, quoted attributes, comments/CDATA/prolog skipping, entity and character-reference
1298
+ decoding, attribute whitespace normalisation; a tag keeps its `prefix:`, as libxml2 reports it,
1299
+ so `<svg:rect>` is not a rect -- in OpenSCAD either) with **a port of upstream's MIT-licensed
1300
+ `src/libsvg`** (its copyright notice is in the file): its `d` tokenizer with every quirk (a `-`
1301
+ is a token, `1.5.5` is two numbers, a number with trailing junk is 0), Beziers in `max($fn, 20)`
1302
+ segments, arcs and circles/ellipses through `Discretizer::circular` (circles at least 40),
1303
+ rounded `rect`s rebuilt as path data printed at 6 digits, `transform` with `skewX`/`skewY` and
1304
+ libsvg's argument-count checks, `<use>` of a shape in an earlier `<defs>` only, unknown
1305
+ elements (`<a>`, `<symbol>`, `<switch>`) see-through with their transform ignored, and
1306
+ **strokes**: an open subpath, `<line>` and `<polyline>` become their stroke's outline via
1307
+ Clipper2's `ClipperOffset` at 2^27 per user unit (measured), with `stroke-width` (below 0.01 is
1308
+ 1), `stroke-linecap` and `stroke-linejoin` (`bevel` maps to Clipper's Square, as libsvg has it).
1309
+ Each element then fills even-odd on its own and the elements union (`fill()`), which is what
1310
+ the reference does: separate overlapping rects union, one path's overlapping subpaths cancel.
1311
+ Two departures: an arc with a zero radius or coincident endpoints is a line or nothing, as the
1312
+ SVG spec says, where libsvg divides by zero and draws NaN vertices at the origin; and
1313
+ OpenSCAD's 2D pipeline drops vertices within ~1.1e-8 mm of collinear, which ours (any 2D
1314
+ shape, not just SVG) does not -- spec `paths-cubic02` keeps two such vertices at an S-curve's
1315
+ inflection. **Placement is `pageMap()`, matching the reference's `import_svg.cc` by its
1316
+ output**: the page's `width`/`height` to mm (a unitless length at `dpi=`,
1317
+ 72 by default; `px` at 96; a `dpi` below 0.001 warns and is 72), the `viewBox` scaled onto the
1318
+ page under `preserveAspectRatio`
1298
1319
  (default `xMidYMid meet`), and Y flipped about the page HEIGHT -- or about the drawing's centre
1299
1320
  with `center=true`. Negating Y alone, as this did before, put a unitless 100-unit drawing in 2.8x
1300
1321
  too large and below the X axis. Bounds match OpenSCAD 2026.02.01 (`SvgPlacement` tests).
@@ -1670,7 +1691,7 @@ the side-quad diagonal choice changes a twisted solid without changing its trian
1670
1691
  default below 3, so these are an extrude warped round Z. The warp inverts the solid for a positive
1671
1692
  angle, hence the mirror-in-Y dance.
1672
1693
  - Kept departure: `$fa`/`$fs` <= 0 fall back to 12/2 rather than upstream's clamp-to-0.01 with a warning.
1673
- - Not ported: `linear_extrude(v=)` (accepted, ignored); DXF/SVG import arcs don't read the discretizer.
1694
+ - Not ported: `linear_extrude(v=)` (accepted, ignored); DXF import arcs don't read the discretizer.
1674
1695
 
1675
1696
  ## Cancel and the module recursion limit (#554)
1676
1697
 
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.41.0
3
+ Version: 1.43.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
- License-Expression: MIT
6
+ License-Expression: MIT AND OFL-1.1
7
7
  License-File: LICENSE
8
+ License-File: resources/fonts/LICENSE
8
9
  Classifier: Development Status :: 3 - Alpha
9
10
  Classifier: Intended Audience :: Developers
10
11
  Classifier: Operating System :: OS Independent
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 BelfrySCAD Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -43,6 +43,7 @@ enum class Op {
43
43
  StoreLocal, // a = slot index (pops)
44
44
  LoadDyn, // a = name pool index ($-prefixed)
45
45
  StoreDyn, // a = name pool index (pops; also marks dynExplicit)
46
+ WarnDuplicateLet, // a = name pool index (pops; warns, binds nothing -- see Evaluator::warnDuplicateLet)
46
47
  LoadFree, // a = name pool index -- Evaluator::evalIdentifier fallback (warnIfUndef=true)
47
48
  // Same as LoadFree, but Evaluator::evalIdentifier's warnIfUndef=false --
48
49
  // only ever emitted for a PrimaryCall's own callee probe (bytecode_
@@ -240,20 +241,6 @@ enum class Op {
240
241
  // there's no branch to emit here).
241
242
  Echo,
242
243
 
243
- // Only ever emitted for a non-empty-argument AssertOp (see
244
- // bytecode_compiler.cpp's own AssertOp case -- a zero-argument
245
- // assert() is unconditionally true and compiles straight through with
246
- // no check at all). Reached only on the condition-false path (guarded
247
- // by a JumpIfTrue the compiler emits around it, mirroring LogicalOrOp's
248
- // own jump-then-patch shape) -- always throws via Evaluator::error(),
249
- // i.e. [[noreturn]] at runtime. a = 1 if a message Value was compiled
250
- // and pushed (pop it), else 0. b = constant-pool index of condText
251
- // (the condition expression's own source text, interned ONCE at
252
- // compile time via its toString() -- cheaper than the interpreter,
253
- // which recomputes this on every failing call). pos/node: same
254
- // Evaluator::error() TRACE-walk need CheckIterLimit already has.
255
- AssertFail,
256
-
257
244
  // -- Module-body compilation (Stage 2) --------------------------------
258
245
  // A call to a user module resolved to a specific ModuleDeclaration AT
259
246
  // COMPILE TIME (see CompiledChunk::ModuleCallSite) -- the module-side
@@ -717,16 +704,10 @@ enum class Op {
717
704
  StoreLetVar,
718
705
 
719
706
  // a = index into CompiledChunk::assertSites. Pops site.argCount values
720
- // (all of this statement's own arguments, already compiled+pushed in
721
- // source order via compileExpr -- EVERY one, not just condition/
722
- // message, mirrors evalAssertStatement's own eager resolveArgs() over
723
- // AssertOp's lazy-message contract) and replicates
724
- // Evaluator::evalAssertStatement exactly: named "condition"/"message"
725
- // win over positional 0/1 (resolved at COMPILE time into
726
- // AssertSite::conditionArgIndex/messageArgIndex -- the argument SHAPE
727
- // is static, only values vary per call), throws via Evaluator::error()
728
- // on failure, otherwise runs any chained node.children natively (rare;
729
- // not worth its own compiled path -- see AssertSite's own doc comment).
707
+ // (every argument of an assert() statement or expression, pushed in
708
+ // source order) and hands them to Evaluator::checkAssert; a statement
709
+ // then runs any chained children natively (rare; not worth its own
710
+ // compiled path).
730
711
  AssertStatement,
731
712
  // a = nativeStatements index of the AST node whose arm this is. Emitted
732
713
  // only when the Evaluator was built with coverage on (compile-time
@@ -763,6 +744,10 @@ struct Instruction {
763
744
  // applyDefaults()'s existing sibling-isolated defaultCtx; empty if that
764
745
  // parameter has no default. `bodyCode` is the function's own expression body.
765
746
  struct CompiledChunk {
747
+ // String literals whose text holds an escape OpenSCAD calls undefined,
748
+ // with how many: the Evaluator warns for them once, when the chunk is
749
+ // compiled (it has the warning channel; the compiler does not).
750
+ std::vector<std::pair<const oscad::StringLiteral*, int>> undefinedEscapeLiterals;
766
751
  struct Param {
767
752
  std::string name;
768
753
  int slot = 0; // unused when isDyn is true -- $-params are never slot-addressed
@@ -1036,30 +1021,13 @@ struct CompiledChunk {
1036
1021
  const oscad::ASTNode* node = nullptr;
1037
1022
  };
1038
1023
 
1039
- // One Op::AssertStatement site -- see that op's own doc comment for
1040
- // the full contract. `conditionArgIndex`/`messageArgIndex` are indices
1041
- // into the site's own argCount-sized popped-argument array (source
1042
- // order), resolved at COMPILE time via the same "named wins over
1043
- // positional 0/1" priority Evaluator::getArg applies at runtime --
1044
- // nullopt means "no argument supplies this logical parameter" (mirrors
1045
- // getArg's own defaultValue fallback: absent condition reads as
1046
- // `Value{true}`, absent message means no ": ..." suffix).
1047
- // `condTextConstIdx`: constants[] index of the condition argument's own
1048
- // source text (or "false" when no condition argument exists, matching
1049
- // evalAssertStatement's `node.arguments.empty() ? "false" : ...`),
1050
- // interned once at compile time. `node`: for both Evaluator::error()'s
1051
- // TRACE walk and the (rare) chained-children fallback below.
1052
- //
1053
- // ponytail: a script with TWO named args of the SAME name (e.g.
1054
- // `assert(condition=true, condition=false)`) would resolve to the
1055
- // LAST one here, not getArg's own first-match; not worth the extra
1056
- // bookkeeping to fix for input that's already nonsensical.
1024
+ // One Op::AssertStatement site. `statement` is null for the expression
1025
+ // form, which has no children.
1057
1026
  struct AssertSite {
1058
1027
  int argCount = 0;
1059
- std::optional<int> conditionArgIndex;
1060
- std::optional<int> messageArgIndex;
1061
- int condTextConstIdx = -1;
1062
- const oscad::ModularAssert* node = nullptr;
1028
+ const std::vector<std::unique_ptr<oscad::Argument>>* arguments = nullptr;
1029
+ const oscad::ASTNode* node = nullptr;
1030
+ const oscad::ModularAssert* statement = nullptr;
1063
1031
  };
1064
1032
 
1065
1033
  // Every distinct chunk this SPECIFIC CompiledChunk owns is either a
@@ -188,7 +188,7 @@ std::vector<ColoredBody> toRenderableBodies(const std::vector<ColoredBody>& bodi
188
188
  double flatHeight = kTopLevel2dHeight);
189
189
 
190
190
  // A 3D affine transform as it acts on the XY plane: rows x and y, columns
191
- // x, y and translation -- what OpenSCAD's GeometryEvaluator applies to 2D
191
+ // x, y and translation -- what OpenSCAD applies to 2D
192
192
  // geometry (z, and everything out of the plane, simply drops away).
193
193
  manifold::mat2x3 projectTo2d(const manifold::mat3x4& m);
194
194
 
@@ -203,10 +203,10 @@ bool projectSectionXform(ColoredBody& b);
203
203
 
204
204
  // Applies `m`, projected onto the plane, to a CrossSection. A singular
205
205
  // projection -- scale([0,1]), a quarter turn about X -- empties it, as
206
- // upstream's Polygon2d::transform does, and returns false.
206
+ // OpenSCAD does, and returns false.
207
207
  bool transformSection(manifold::CrossSection& cs, const manifold::mat3x4& m);
208
208
 
209
- // Upstream's warning for that case, word for word.
209
+ // OpenSCAD's warning for that case, word for word.
210
210
  inline constexpr const char* kZeroScale2dWarning = "Scaling a 2D object with 0 - removing object";
211
211
 
212
212
  } // namespace oscadeval
@@ -0,0 +1,67 @@
1
+ #pragma once
2
+
3
+ #include "openscad_cpp_evaluator/segments.hpp"
4
+
5
+ #include <array>
6
+ #include <optional>
7
+ #include <string>
8
+ #include <vector>
9
+
10
+ namespace oscadeval {
11
+
12
+ using Contour2d = std::vector<std::array<double, 2>>;
13
+
14
+ // import()'s DXF arguments. `disc` carries $fn/$fa/$fs/$fe for arcs.
15
+ struct DxfOptions {
16
+ std::optional<std::string> layer;
17
+ double xorigin = 0.0, yorigin = 0.0, scale = 1.0;
18
+ Discretizer disc;
19
+ };
20
+
21
+ // The outlines, to be filled even-odd, and OpenSCAD's warnings about the
22
+ // file, in the order it prints them.
23
+ struct DxfImport {
24
+ std::vector<Contour2d> contours;
25
+ std::vector<std::string> warnings;
26
+ };
27
+
28
+ // Hand-rolled DXF reader matching OpenSCAD 2026.02.01's import (see the
29
+ // notes atop dxf_import.cpp): LINE, CIRCLE, ARC, ELLIPSE, LWPOLYLINE,
30
+ // POLYLINE and INSERT, chained into closed outlines. Throws
31
+ // std::runtime_error on I/O failure.
32
+ DxfImport loadDxf(const std::string& path, const DxfOptions& opts = {});
33
+
34
+ // SVG reader, as OpenSCAD 2026.02.01 reads it (a port of its MIT-licensed
35
+ // libsvg): <path>, <rect>, <circle>, <ellipse>, <line>, <polygon>,
36
+ // <polyline> and <use>, with transforms, display:none, and strokes -- an
37
+ // open path, <line> or <polyline> is drawn as its stroke's outline. Curves
38
+ // split by `disc`. Y is flipped (SVG's is down, OpenSCAD's is up). Throws
39
+ // std::runtime_error on I/O/parse failure.
40
+ //: Which part of the drawing to import. All unset means the whole file.
41
+ //: `id` matches an element's `id` attribute; `cls` (not an OpenSCAD
42
+ //: parameter) one entry of its space-separated `class` list; either takes
43
+ //: everything under the element, so naming a <g> means "that group".
44
+ //: `layer` names an Inkscape layer (inkscape:groupmode="layer", by its
45
+ //: inkscape:label): alone it takes that layer, with id/cls it restricts
46
+ //: them to elements inside it.
47
+ struct SvgFilter {
48
+ std::optional<std::string> id;
49
+ std::optional<std::string> cls;
50
+ std::optional<std::string> layer;
51
+ };
52
+
53
+ // `matched`, when given, reports whether the filter found anything. A miss
54
+ // imports nothing rather than falling back to the whole drawing; the caller
55
+ // warns. Always true when no filter was set.
56
+ // Placed as OpenSCAD places them: page units to mm (a unitless length at
57
+ // `dpi`), the viewBox under preserveAspectRatio, Y flipped about the page
58
+ // height -- or about the drawing's centre with `center`. The result is
59
+ // filled: overlapping contours have already been combined.
60
+ std::vector<Contour2d> loadSvgContours(const std::string& path,
61
+ const SvgFilter& filter = {},
62
+ bool* matched = nullptr,
63
+ double dpi = 72.0,
64
+ bool center = false,
65
+ const Discretizer& disc = {});
66
+
67
+ } // namespace oscadeval
@@ -2,6 +2,8 @@
2
2
 
3
3
  #include "openscad_cpp_evaluator/bound_args.hpp"
4
4
  #include "openscad_cpp_evaluator/bytecode.hpp"
5
+
6
+ #include <unordered_set>
5
7
  #include "openscad_cpp_evaluator/bytecode_vm.hpp"
6
8
  #include "openscad_cpp_evaluator/call_args.hpp"
7
9
  #include "openscad_cpp_evaluator/csg_node.hpp"
@@ -557,10 +559,39 @@ public:
557
559
  // Public: builtins/import.cpp's not-manifold warning is emitted from a
558
560
  // free function, same reasoning as tagGenerated()/builtinChildren().
559
561
  void warn(const std::string& message, const oscad::Position* position);
562
+ // OpenSCAD's lexer warns "Undefined escape sequence" once per bad escape
563
+ // in the source. This warns for a literal the first time it is
564
+ // evaluated or compiled, and never again for that literal.
565
+ void warnUndefinedEscapes(const oscad::StringLiteral& lit, int count);
566
+ // let() keeps the FIRST binding of a repeated name: OpenSCAD evaluates
567
+ // the later right-hand side, warns with its value, and discards it.
568
+ // OpenSCAD's Assert::performAssert, given every argument's value in
569
+ // source order -- it evaluates them all, pass or fail, so a passing
570
+ // `assert(true, nosuch)` still warns. Warns about unexpected arguments;
571
+ // throws via error() when the condition is false or missing (`assert()`
572
+ // fails). The message prints as echo() would print it.
573
+ void checkAssert(const std::vector<std::unique_ptr<oscad::Argument>>& arguments, const std::vector<Value>& values,
574
+ const oscad::ASTNode& node);
575
+ void warnDuplicateLet(const std::string& name, const Value& v, const oscad::Position* position);
576
+ template <typename Assignments>
577
+ static bool repeatsEarlierLetName(const Assignments& assignments, size_t i) {
578
+ for (size_t j = 0; j < i; ++j)
579
+ if (assignments[j]->name->name == assignments[i]->name->name) return true;
580
+ return false;
581
+ }
582
+ std::unordered_set<const oscad::StringLiteral*> undefinedEscapesWarned_;
583
+ void warnUndefinedEscapes(const CompiledChunk& chunk) {
584
+ for (const auto& [lit, n] : chunk.undefinedEscapeLiterals) warnUndefinedEscapes(*lit, n);
585
+ }
560
586
  // Hands one already-formatted warning line to echoFn_, recording it in
561
587
  // warnCapture first when one is active. Every warning path goes through
562
588
  // here, including a ManifoldCache hit replaying a cached line.
563
589
  void emitWarning(const std::string& formatted);
590
+ // "ERROR: {message}{locSuffix(position)}" that does NOT stop the run:
591
+ // OpenSCAD logs several of its errors this way and carries on.
592
+ void nonFatalError(const std::string& message, const oscad::Position* position) {
593
+ emitWarning("ERROR: " + message + locSuffix(position));
594
+ }
564
595
 
565
596
  // Applies OpenSCAD's mixed-2D/3D rules to a node's children just
566
597
  // before its GenerateFn runs -- see csg_generate.cpp.
@@ -167,7 +167,7 @@ struct ExportSvgOptions {
167
167
  void writeSvg(const std::string& path, const std::vector<ColoredBody>& bodies,
168
168
  const ExportSvgOptions& opts = {});
169
169
 
170
- // DXF, the third 2D writer: OpenSCAD's own export_dxf.cc layout (an AC1006
170
+ // DXF, the third 2D writer: byte-compatible with OpenSCAD's layout (an AC1006
171
171
  // header with the model's extents, one CONTINUOUS linetype, layer 0) and
172
172
  // every contour of every body a closed LWPOLYLINE on layer 0 -- holes are
173
173
  // contours like any other, as in OpenSCAD. A 1-vertex contour is a POINT
@@ -259,13 +259,10 @@ struct ExportPovOptions {
259
259
  // POV-Ray scene description. Every object splitBodiesForExport produces is
260
260
  // one mesh2 -- a welded vertex list, a texture per colour, and a texture
261
261
  // index per triangle, so per-triangle colour needs no unwelding -- with
262
- // inside_vector, which makes it a SOLID to POV-Ray (refraction, CSG). That
263
- // is the difference from OpenSCAD's export_pov.cc, which writes every face
264
- // as its own flat polygon object. The rest of the scene is OpenSCAD's: its
265
- // header, MATERIAL finish and ior 1.32 interior, 27 lights around the
266
- // bounding box, its camera, and radiosity from rad_def.inc. Colour is rgbf
267
- // with filter = 1 - alpha, as OpenSCAD writes it. Numbers are the shortest
268
- // text that round-trips, in the classic locale.
262
+ // inside_vector, which makes it a SOLID to POV-Ray (refraction, CSG). The
263
+ // rest of the scene (material, lights, default camera) is spec section F2.
264
+ // Colour is rgbf with filter = 1 - alpha. Numbers are the shortest text that
265
+ // round-trips, in the classic locale.
269
266
  void writePov(const std::string& path, const std::vector<ExportObject>& objects,
270
267
  const ExportPovOptions& opts = {});
271
268
 
@@ -9,12 +9,14 @@ class ASTNode;
9
9
  namespace oscadeval {
10
10
 
11
11
  class Evaluator;
12
+ struct EvalContext;
12
13
 
13
14
  // import() used as an expression (not a geometry statement): returns a VNF
14
15
  // ([[verts],[faces]]) for a mesh file (.stl/.obj/.off/.3mf), or the JSON
15
16
  // file's content converted to native values (.json). DXF/SVG-as-Region
16
17
  // import lands alongside their module-context counterparts in a later
17
18
  // phase. Mirrors Evaluator._import_as_value (minus the dxf/svg branch).
18
- Value importAsValue(Evaluator& ev, const CallArgs& args, const oscad::ASTNode& node);
19
+ // `ctx` supplies $fn/$fa/$fs/$fe for an SVG's curves.
20
+ Value importAsValue(Evaluator& ev, const CallArgs& args, const oscad::ASTNode& node, const EvalContext& ctx);
19
21
 
20
22
  } // namespace oscadeval
@@ -11,34 +11,25 @@
11
11
 
12
12
  namespace oscadeval {
13
13
 
14
- // How curves become polygons: OpenSCAD's CurveDiscretizer (src/core/
15
- // CurveDiscretizer.cc, upstream master cc19000e0), ported. Every count here
16
- // matches the 2026.02.01 binary's.
17
- //
18
- // $fn > $fe > $fa/$fs. $fe is the most a curve's polygon may stray from the
19
- // true circle, measured along a radius to an edge's midpoint; when set (>=
20
- // 2^-20) it replaces $fa/$fs entirely. Upstream reads $fe only behind
21
- // --enable=discretization-by-error; this evaluator always honours it, and
22
- // advertises that as supported_feature("discretization-by-error").
23
- //
24
- // Departures from upstream, kept from before this port: $fa/$fs <= 0 fall
25
- // back to their defaults (12/2) rather than clamping to 0.01 with a warning.
14
+ // How curves become polygons: segment and slice counts from $fn/$fe/$fa/$fs
15
+ // (spec sections B7 and C6). $fe is always honoured here (OpenSCAD reads it
16
+ // only behind --enable=discretization-by-error), advertised as
17
+ // supported_feature("discretization-by-error").
26
18
  struct Discretizer {
27
19
  double fn = 0.0;
28
20
  double fe = 0.0;
29
21
  double fa = 12.0;
30
22
  double fs = 2.0;
31
23
 
32
- // `warn`, when given, receives upstream's clamping warnings.
24
+ // `warn`, when given, receives the clamping warnings (spec section B7).
33
25
  static Discretizer fromCtx(const EvalContext& ctx, const std::function<void(const std::string&)>& warn = {});
34
26
  // Stored in, and read back from, a node's params -- they are part of its
35
27
  // cache key, and generate has only the params to go on.
36
28
  static Discretizer fromParams(const CSGParams& params);
37
29
  void store(CSGParams& params) const;
38
30
 
39
- // Segments for `angle` degrees of arc at radius r. nullopt where
40
- // upstream has none (r below 2^-20, a non-finite $fn/$fe/angle): every
41
- // caller supplies its own fallback, as upstream's value_or() does.
31
+ // Segments for `angle` degrees of arc at radius r. nullopt where there is
32
+ // no count (spec section B7); every caller supplies its own fallback.
42
33
  std::optional<int> circular(double r, double angleDegrees = 360.0) const;
43
34
 
44
35
  // linear_extrude slice counts: twist without scale, twist with uniform
@@ -54,8 +45,8 @@ struct Discretizer {
54
45
  double scaleX, double scaleY, unsigned slices, unsigned segments) const;
55
46
  };
56
47
 
57
- // Full-circle segment count from ctx's $fn/$fe/$fa/$fs at radius r;
58
- // upstream's primitives fall back to 3.
48
+ // Full-circle segment count from ctx's $fn/$fe/$fa/$fs at radius r, or 3
49
+ // when there is none.
59
50
  int fnSegmentsFromCtx(const EvalContext& ctx, double r = 0.0,
60
51
  const std::function<void(const std::string&)>& warn = {});
61
52
 
@@ -0,0 +1,20 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+ #include <vector>
5
+
6
+ namespace oscadeval {
7
+
8
+ // Height data for surface() (spec section D3). Never throws: a file that
9
+ // cannot be read gives no heights and a warning, and the script carries on.
10
+ // `heights[row * cols + col]`; `minVal` is what the base sits 1 below.
11
+ struct SurfaceData {
12
+ std::vector<double> heights;
13
+ int rows = 0;
14
+ int cols = 0;
15
+ double minVal = 0;
16
+ std::vector<std::string> warnings;
17
+ };
18
+ SurfaceData loadSurface(const std::string& path, bool invert);
19
+
20
+ } // namespace oscadeval
@@ -268,12 +268,9 @@ Value vecSub(const Value& a, const Value& b);
268
268
  // behavior in the reference implementation -- revisit only if a real
269
269
  // script's `*` on a bool-containing vector is shown to need it.
270
270
  //
271
- // `error`, when non-null, receives the reference's own diagnostic text for
272
- // whichever undefined case was hit (multiply_visitor / multvecvec /
273
- // multmatvec / multvecmat in the reference's Value.cc) -- "vector*vector
274
- // requires matching lengths (2 != 3)", "Matrix must be rectangular. Problem
275
- // at row 1", and so on. applyBinaryOp warns with it; a caller that only
276
- // wants the value can leave it null. It is only written on an undef result.
271
+ // `error`, when non-null, receives the diagnostic for whichever undefined
272
+ // case was hit (spec section A6); applyBinaryOp warns with it. It is only
273
+ // written on an undef result.
277
274
  Value matmul(const Value& a, const Value& b, std::string* error = nullptr);
278
275
 
279
276
  // OpenSCAD's echo()/str() number formatting: at most 6 significant digits,
@@ -306,7 +303,9 @@ std::string formatNumber(double v);
306
303
  //
307
304
  // A trailing lone backslash is kept as itself. It can only occur in an
308
305
  // unterminated literal, which is a parse error long before this runs.
309
- std::string unescapeStringLiteral(const std::string& raw);
306
+ // `undefinedEscapes`, when given, counts the escapes OpenSCAD's lexer calls
307
+ // undefined (it warns once for each).
308
+ std::string unescapeStringLiteral(const std::string& raw, int* undefinedEscapes = nullptr);
310
309
 
311
310
  // echo()/str()/assert-message display format: "undef" | "true"/"false" |
312
311
  // "[start : step : end]" (range) | formatNumber() (number) | "[e1, e2, ...]"
@@ -4,12 +4,12 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.41.0"
7
+ version = "1.43.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
11
- license = "MIT"
12
- license-files = ["LICENSE"]
11
+ license = "MIT AND OFL-1.1"
12
+ license-files = ["LICENSE", "resources/fonts/LICENSE"]
13
13
  authors = [{ name = "NeuSCAD Contributors" }]
14
14
  classifiers = [
15
15
  "Development Status :: 3 - Alpha",
@@ -68,7 +68,7 @@ target_include_directories(openscad_cpp_evaluator PRIVATE ${stb_SOURCE_DIR} ${CM
68
68
  add_dependencies(openscad_cpp_evaluator generate_bundled_font)
69
69
 
70
70
  target_link_libraries(openscad_cpp_evaluator PUBLIC openscad_cpp_parser manifold Boost::polygon)
71
- target_link_libraries(openscad_cpp_evaluator PRIVATE linenoise freetype harfbuzz)
71
+ target_link_libraries(openscad_cpp_evaluator PRIVATE linenoise freetype harfbuzz Clipper2)
72
72
  target_compile_options(openscad_cpp_evaluator PRIVATE
73
73
  $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall;-Wextra>
74
74
  )
@@ -392,8 +392,8 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
392
392
  }
393
393
 
394
394
  RoleSplit split = splitByRole(stmtBodies);
395
- // A `%` statement is scenery, not an operand: upstream leaves it out
396
- // of the CSG entirely (collectChildren skips isBackground()), so the
395
+ // A `%` statement is scenery, not an operand: OpenSCAD leaves it out
396
+ // of the CSG entirely, so the
397
397
  // NEXT statement is the minuend of a difference(), and an
398
398
  // intersection() is not cancelled by it. It was taken as an empty
399
399
  // operand, which emptied both. Only the `%` statement itself: a
@@ -271,15 +271,28 @@ std::vector<ColoredBody> generateSurface(Evaluator& ev, const CSGParams& params,
271
271
  // operand (and so empties an intersection) -- see resolveCsg.
272
272
  bool emptyStatementIsAnOperand(const oscad::ASTNode& stmt);
273
273
 
274
- // OpenSCAD's sin_degrees/cos_degrees: exact at multiples of 30 and 45 degrees.
274
+ // OpenSCAD's sin_degrees/cos_degrees/tan_degrees/atan2_degrees: exact at
275
+ // multiples of 30 and 45 degrees.
275
276
  double sinDeg(double x);
276
277
  double cosDeg(double x);
278
+ double tanDeg(double x);
279
+ double atan2Deg(double y, double x);
277
280
 
278
- // OpenSCAD's lookup_radius() (primitives_2d.cpp): `d` beats `r`, numbers only.
281
+ // A radius/diameter argument pair: a numeric `d` beats `r`, numbers only.
279
282
  std::optional<double> lookupRadius(Evaluator& ev, const CallArgs& args, std::optional<int> rPos,
280
283
  std::optional<int> dPos, const std::string& rName, const std::string& dName,
281
284
  const oscad::Position* where);
282
285
 
286
+ // "ERROR: <text>" at `where`, for input a primitive repairs and carries on with.
287
+ void emitInputError(Evaluator& ev, const std::string& text, const oscad::Position* where);
288
+
289
+ // Reads polygon() paths or polyhedron() faces (`listName`): each entry a
290
+ // list of point indices. Entries that are not lists and indices that are not
291
+ // numbers are reported as ERRORs and skipped; out-of-range indices are
292
+ // WARNINGs and skipped. Every surviving entry is returned, however short.
293
+ std::vector<std::vector<size_t>> readIndexLists(Evaluator& ev, const ValueList& lists, const std::string& listName,
294
+ size_t pointCount, const oscad::Position* where);
295
+
283
296
  CSGParams resolveText(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
284
297
 
285
298
  // The font size text()/textmetrics()/fontmetrics() draw at: `size`
@@ -118,15 +118,14 @@ CSGParams resolveIntersectionFor(Evaluator& ev, const oscad::ModularIntersection
118
118
  // needs `i` visible in `j`'s own range expression).
119
119
  std::function<void(size_t, EvalContext&)> recurse = [&](size_t depth, EvalContext& parentCtx) {
120
120
  if (depth == node.assignments.size()) {
121
+ if (depth == 0) return; // for() with no loop variables iterates zero times in OpenSCAD, not once.
121
122
  // One body-entry marker per full cartesian-product iteration
122
123
  // and nothing per individual variable binding (unlike
123
124
  // evalFor) -- mirrors _resolve_intersection_for's single
124
125
  // `_check_debug(body_node[0], loop_ctx, expr_level=True)`.
125
126
  if (!bodyNodes.empty()) ev.checkDebug(*bodyNodes.front(), parentCtx, /*forced=*/false, /*exprLevel=*/true);
126
- // One operand per child STATEMENT of every iteration: upstream
127
- // instantiates the children straight into the intersection
128
- // node (control.cc), so they are intersected flat, not
129
- // unioned per iteration first.
127
+ // One operand per child STATEMENT of every iteration: OpenSCAD
128
+ // intersects them flat, not unioned per iteration first.
130
129
  std::vector<const oscad::ASTNode*> assigns, stmts;
131
130
  for (const oscad::ASTNode* stmt : bodyNodes) {
132
131
  if (stmt->kind() == oscad::NodeKind::Assignment) assigns.push_back(stmt);