openscad-cpp-evaluator 0.33.0__tar.gz → 0.35.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 (203) hide show
  1. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/CLAUDE.md +54 -2
  2. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/PKG-INFO +1 -1
  3. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/evaluator.hpp +6 -0
  4. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/pyproject.toml +1 -1
  5. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/CMakeLists.txt +1 -0
  6. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/builtins.hpp +14 -0
  7. openscad_cpp_evaluator-0.35.0/src/builtins/dxf_dim.cpp +221 -0
  8. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/function_builtins.cpp +7 -2
  9. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/registry.cpp +6 -0
  10. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/topology.cpp +132 -0
  11. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/csg_generate.cpp +107 -1
  12. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/eval_context.cpp +6 -0
  13. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_booleans.cpp +259 -0
  14. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_dxf_svg_import.cpp +131 -0
  15. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_scoping.cpp +12 -0
  16. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/uv.lock +1 -1
  17. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/.github/workflows/ci.yml +0 -0
  18. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/.github/workflows/wheels.yml +0 -0
  19. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/.gitignore +0 -0
  20. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/.gitmodules +0 -0
  21. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/CMakeLists.txt +0 -0
  22. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/LICENSE +0 -0
  23. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/README.md +0 -0
  24. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/bindings/CMakeLists.txt +0 -0
  25. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/bindings/ast_to_py.cpp +0 -0
  26. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/bindings/module.cpp +0 -0
  27. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/cmake/embed_font.cmake +0 -0
  28. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/examples/CMakeLists.txt +0 -0
  29. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/examples/eval_perf_benchmark.cpp +0 -0
  30. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/examples/manifold_cache_reuse.cpp +0 -0
  31. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/examples/minimal_debugger.cpp +0 -0
  32. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  33. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/.gitignore +0 -0
  34. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  35. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  36. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/README.md +0 -0
  37. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  38. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  39. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  40. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  41. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  42. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  43. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  44. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  45. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  46. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  47. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  48. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  49. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  50. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  51. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  52. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  53. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  54. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  55. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  56. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  57. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  58. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  59. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  60. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  61. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  62. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  63. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  64. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  65. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  66. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  67. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  68. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  69. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  70. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  71. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  72. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  73. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  74. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  75. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  76. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  77. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  78. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  79. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  80. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  81. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  82. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  83. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  84. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  85. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  86. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  87. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  88. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  89. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  90. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  91. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  92. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  93. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  94. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  95. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  96. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  97. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  98. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  99. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  100. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  101. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  102. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  103. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  104. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  105. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  106. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  107. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  108. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  109. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  110. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  111. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  112. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  113. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  114. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  115. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  116. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  117. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  118. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  119. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  120. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  121. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  122. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  123. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  124. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  125. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  126. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  127. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  128. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  129. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  130. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  131. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/resources/fonts/LICENSE +0 -0
  132. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  133. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/booleans.cpp +0 -0
  134. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/call_args.cpp +0 -0
  135. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/color.cpp +0 -0
  136. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/control.cpp +0 -0
  137. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/extrude.cpp +0 -0
  138. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/import.cpp +0 -0
  139. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/modifiers.cpp +0 -0
  140. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/primitives_2d.cpp +0 -0
  141. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/primitives_3d.cpp +0 -0
  142. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/roof.cpp +0 -0
  143. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/segments.cpp +0 -0
  144. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/surface.cpp +0 -0
  145. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/text.cpp +0 -0
  146. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/builtins/transforms.cpp +0 -0
  147. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/bytecode_compiler.cpp +0 -0
  148. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/bytecode_vm.cpp +0 -0
  149. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/colored_body.cpp +0 -0
  150. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/csg_resolve.cpp +0 -0
  151. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/css_colors.cpp +0 -0
  152. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/debug/debug_repl.cpp +0 -0
  153. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/debug_profile.cpp +0 -0
  154. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/eval_error.cpp +0 -0
  155. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/eval_use.cpp +0 -0
  156. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/export.cpp +0 -0
  157. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/expr_eval.cpp +0 -0
  158. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/import/dxf_import.cpp +0 -0
  159. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/import/mesh_import.cpp +0 -0
  160. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/import/surface_load.cpp +0 -0
  161. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/import/svg_import.cpp +0 -0
  162. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/manifold_cache.cpp +0 -0
  163. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/mesh_check.cpp +0 -0
  164. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/stmt_eval.cpp +0 -0
  165. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/text/font_match.cpp +0 -0
  166. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/text/freetype_font_provider.cpp +0 -0
  167. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/text/text_metrics.cpp +0 -0
  168. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/user_calls.cpp +0 -0
  169. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/value.cpp +0 -0
  170. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/src/zip_stored.cpp +0 -0
  171. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/CMakeLists.txt +0 -0
  172. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_bytecode_compiler.cpp +0 -0
  173. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_cli.cpp +0 -0
  174. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_control_flow.cpp +0 -0
  175. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_csg_tree.cpp +0 -0
  176. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_debug_hooks.cpp +0 -0
  177. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_eval_context.cpp +0 -0
  178. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_export.cpp +0 -0
  179. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_expr_eval.cpp +0 -0
  180. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_extrude_roof.cpp +0 -0
  181. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_function_builtins.cpp +0 -0
  182. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_helpers.hpp +0 -0
  183. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_import_export.cpp +0 -0
  184. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_manifold_cache.cpp +0 -0
  185. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_mesh_check.cpp +0 -0
  186. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_multi_color_merge.cpp +0 -0
  187. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_primitives.cpp +0 -0
  188. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_profiling.cpp +0 -0
  189. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_python_bindings.py +0 -0
  190. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_scope_trail.cpp +0 -0
  191. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_surface.cpp +0 -0
  192. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_tail_calls.cpp +0 -0
  193. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_text.cpp +0 -0
  194. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_transforms.cpp +0 -0
  195. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_unexpected_args.cpp +0 -0
  196. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_use.cpp +0 -0
  197. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_value.cpp +0 -0
  198. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_viewport_params.cpp +0 -0
  199. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tests/test_zip_stored.cpp +0 -0
  200. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tools/cli/CMakeLists.txt +0 -0
  201. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tools/cli/cli_lib.cpp +0 -0
  202. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tools/cli/cli_lib.hpp +0 -0
  203. {openscad_cpp_evaluator-0.33.0 → openscad_cpp_evaluator-0.35.0}/tools/cli/main.cpp +0 -0
@@ -512,6 +512,9 @@ grep for `ponytail:`.
512
512
  `Evaluator::evaluate()` also takes an optional `viewportParams` map (added after a test-suite
513
513
  parity review found it missing entirely): seeds arbitrary `$`-prefixed entries into `ctx.dyn`
514
514
  (e.g. `$vpt`/`$vpr`/`$vpd`/`$vpf` from a GUI host's current camera, or `$t` for animation time)
515
+ — note `EvalContext::makeRoot` also seeds `$preview` to **`false`**, always: there is no preview
516
+ mode here, and leaving it undef made the common `$preview ? cheap : real` idiom take the cheap
517
+ branch by accident while warning about an unknown variable
515
518
  before evaluation starts, via a direct `ctx.dyn` write that deliberately never touches
516
519
  `ctx.dynExplicit` — lets a caller tell "the script itself assigned this `$`-var"
517
520
  (`dynExplicit`) apart from "merely present because the caller seeded it" after `evaluate()`
@@ -558,13 +561,62 @@ grep for `ponytail:`.
558
561
  color instead of preserving each part's own, matching real OpenSCAD's actual behavior once fixed
559
562
  (cross-checked triangle-color-count-and-values against the Python reference directly, not just
560
563
  “does it run”).
561
- - `src/builtins/topology.cpp` — `hull()`/`minkowski()`. Both splice their children transparently in
564
+ - `src/builtins/dxf_dim.cpp` — `dxf_dim()`/`dxf_cross()`, which read a measurement out of a DXF file
565
+ rather than out of the model. Ported from the reference's `io/dxfdim.cc` plus the DIMENSION/LINE
566
+ half of `io/DxfData.cc`: `dxf_dim()` returns the first matching DIMENSION's value by its type
567
+ (`70 & 7` — 0 rotated, 1 aligned, 2 angular, 3/4 diameter/radius, 6 ordinate; type 5 is
568
+ unsupported there too), `dxf_cross()` intersects the first two LINE entities. Note the reference's
569
+ own coordinate quirk, reproduced here: groups 11/12/16 (and 21/22/26) are scaled but NOT
570
+ origin-shifted, being extents rather than positions.
571
+
572
+ Deliberately its own group-code walk rather than sharing `src/import/dxf_import.cpp`'s: that one
573
+ produces closed contours, and these need the DIMENSION entities' seven coordinate slots and the
574
+ raw LINE endpoints, neither of which a contour list keeps. One divergence: `dxf_cross()` takes the
575
+ first two LINEs, where the reference walks every 2-point path — for a real cross (two strokes
576
+ meeting in the middle, not at their endpoints) that is the same set.
577
+
578
+ - `src/csg_generate.cpp` — `Evaluator::applyDimensionRules`, run centrally just before every node's
579
+ `GenerateFn` (and once more over the top-level list, which is an implicit union). Mirrors the
580
+ reference's `isValidDim` + `collectChildren2D`/`collectChildren3D`: a node's dimension is fixed by
581
+ its first non-background, non-empty child, and every child of the other dimension is dropped with
582
+ `Mixing 2D and 3D objects is not supported` followed by `Ignoring {N}D child object for {M}D
583
+ operation`. `linear_extrude`/`rotate_extrude`/`offset` are always-2D and `projection` always-3D, so
584
+ they skip the "mixing" line and emit only the second; `roof()` emits neither, matching the
585
+ reference. Background (`%`) children are exempt.
586
+
587
+ Doing this centrally is also what keeps `generateCsg` safe: it builds ONE result and switches on
588
+ whether it holds a `body` or a `section`, so a group that changed dimension part-way dereferenced
589
+ the empty optional -- `union() { cube(1); square(4); }` took the process down with "mutex lock
590
+ failed: Invalid argument". Filtering before dispatch means no `GenerateFn` ever sees a mixed group.
591
+ All 25 module/shape combinations were diffed against OpenSCAD 2026.02.01 warning for warning, with
592
+ the surviving geometry compared by STL volume.
593
+
594
+ - `src/builtins/topology.cpp` — `hull()`/`minkowski()`/`minkowski_difference()`/`fill()`. All four splice their children transparently in
562
595
  the CSG tree exactly like union/difference/intersection (resolve just evaluates them for the
563
596
  side effect); `hull()` picks an all-3D (`Manifold::Hull`) or all-2D (`CrossSection::Hull`) hull of
564
597
  the *foreground* (role-split) children, falling back to sections only when no child has a body;
565
598
  `minkowski()` only ever operates on 3D bodies (a 2D sibling among the foreground children is
566
599
  silently ignored, matching the reference exactly — no 2D `minkowski_sum` fallback the way hull
567
- has one). Both pass background/highlight/show_only bodies through untouched.
600
+ has one). All pass background/highlight/show_only bodies through untouched. `fill()` is 2D-only
601
+ (a 3D foreground child draws the reference's own `fill() not yet implemented for 3D` warning and
602
+ contributes nothing): it unions the foreground sections, keeps only the outlines with positive
603
+ signed area — outer boundaries, since Manifold winds a hole clockwise — and re-unions those,
604
+ which matters because two outers can overlap once the holes between them are gone. Ported from
605
+ the reference's `applyFill2D`, and checked shape-for-shape against OpenSCAD 2026.02.01 by
606
+ extruding and comparing STL volumes.
607
+
608
+ `minkowski_difference()` is erosion, and a deliberate BelfrySCAD extension: OpenSCAD has no such
609
+ module and no way to spell the operation in the language, since `minkowski()` only ever sums.
610
+ Reads the same way — first child is the body, every child after it is a tool eroded away in turn
611
+ (`Manifold::MinkowskiDifference`). 3D only, matching `minkowski()`'s own 3D-wins dispatch, since
612
+ Manifold has no CrossSection erosion and 2D already has one in `offset(r=-N)`.
613
+
614
+ Worth knowing why this is a module rather than an extension of `offset()`: a *positive* 3D offset
615
+ is already expressible as `minkowski() { X; sphere(r); }`, so `offset(r=N)` on a solid would have
616
+ been pure sugar. Erosion is the half that is genuinely unavailable, and naming it after the
617
+ operation beats borrowing a 2D name for it. Eroding a cube by a cube is exact (no curved tool to
618
+ tessellate), which is what the tests assert on; a sphere tool lands a little large, and that gap
619
+ is the sphere's tessellation rather than an error in the erosion.
568
620
  - `src/builtins/extrude.cpp` — `linear_extrude()`/`rotate_extrude()`/`projection(cut=)`. All three
569
621
  union every 2D child into one `CrossSection` via `toCrossSection` first. `rotate_extrude`'s
570
622
  segment count can't be resolved until generate time (it depends on the merged children's bounds,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.33.0
3
+ Version: 0.35.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -330,6 +330,12 @@ public:
330
330
  // free function, same reasoning as tagGenerated()/builtinChildren().
331
331
  void warn(const std::string& message, const oscad::Position* position);
332
332
 
333
+ // Applies OpenSCAD's mixed-2D/3D rules to a node's children just
334
+ // before its GenerateFn runs -- see csg_generate.cpp.
335
+ void applyDimensionRules(CSGNode& node);
336
+ // ruleValue is a DimRule; the enum is file-local to csg_generate.cpp.
337
+ void applyDimensionRulesTo(const std::vector<CSGNode*>& children, int ruleValue);
338
+
333
339
  // Apply `!`: replace the whole tree with the first show_only
334
340
  // subtree, discarding its ancestors and every sibling. See its
335
341
  // definition for why this cannot be done by tagging bodies.
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "0.33.0"
7
+ version = "0.35.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -29,6 +29,7 @@ add_library(openscad_cpp_evaluator STATIC
29
29
  builtins/call_args.cpp
30
30
  builtins/segments.cpp
31
31
  builtins/registry.cpp
32
+ builtins/dxf_dim.cpp
32
33
  builtins/primitives_2d.cpp
33
34
  builtins/primitives_3d.cpp
34
35
  builtins/transforms.cpp
@@ -160,6 +160,20 @@ CSGParams resolveHull(Evaluator& ev, const oscad::ModularCall& node, EvalContext
160
160
  std::vector<ColoredBody> generateHull(Evaluator& ev, const CSGParams& params,
161
161
  const std::vector<std::unique_ptr<CSGNode>>& children, const oscad::ASTNode& node);
162
162
 
163
+ // dxf_dim()/dxf_cross() -- read a dimension or a cross's centre out of a
164
+ // DXF file. Functions, not modules; dispatched from evalBuiltinFunction.
165
+ Value builtinDxfDim(Evaluator& ev, const CallArgs& args, const oscad::ASTNode& node);
166
+ Value builtinDxfCross(Evaluator& ev, const CallArgs& args, const oscad::ASTNode& node);
167
+
168
+ CSGParams resolveMinkowskiDifference(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
169
+ std::vector<ColoredBody> generateMinkowskiDifference(Evaluator& ev, const CSGParams& params,
170
+ const std::vector<std::unique_ptr<CSGNode>>& children,
171
+ const oscad::ASTNode& node);
172
+
173
+ CSGParams resolveFill(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
174
+ std::vector<ColoredBody> generateFill(Evaluator& ev, const CSGParams& params,
175
+ const std::vector<std::unique_ptr<CSGNode>>& children,
176
+ const oscad::ASTNode& node);
163
177
  CSGParams resolveMinkowski(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
164
178
  std::vector<ColoredBody> generateMinkowski(Evaluator& ev, const CSGParams& params,
165
179
  const std::vector<std::unique_ptr<CSGNode>>& children,
@@ -0,0 +1,221 @@
1
+ #include "builtins.hpp"
2
+
3
+ #include "openscad_cpp_evaluator/call_args.hpp"
4
+ #include "openscad_cpp_evaluator/evaluator.hpp"
5
+
6
+ #include <cmath>
7
+ #include <filesystem>
8
+ #include <fstream>
9
+ #include <sstream>
10
+
11
+ namespace oscadeval {
12
+
13
+ // dxf_dim() / dxf_cross() -- read a measurement straight out of a DXF file
14
+ // rather than out of the model. Ported from the reference's io/dxfdim.cc
15
+ // plus the DIMENSION/LINE half of io/DxfData.cc.
16
+ //
17
+ // These share the group-code walk with src/import/dxf_import.cpp but not its
18
+ // output: that one only cares about closed contours, while these two need
19
+ // the DIMENSION entities' seven coordinate slots and the raw LINE endpoints,
20
+ // neither of which a contour list keeps.
21
+
22
+ namespace {
23
+
24
+ struct Entity {
25
+ std::string type; // "DIMENSION", "LINE", ...
26
+ std::string layer;
27
+ std::string name; // group 1: DIMENSION's text override
28
+ int dimType = 0; // group 70
29
+ double angle = 0.0; // group 50
30
+ double coords[7][2] = {}; // groups 10-16 / 20-26
31
+ std::vector<double> xverts, yverts; // groups 10/11 and 20/21, in order
32
+ };
33
+
34
+ std::string trimmed(const std::string& s) {
35
+ const size_t b = s.find_first_not_of(" \t\r\n");
36
+ if (b == std::string::npos) return "";
37
+ return s.substr(b, s.find_last_not_of(" \t\r\n") - b + 1);
38
+ }
39
+
40
+ // One entity per group-0 marker. Coordinates carry the origin shift and
41
+ // scale the reference applies while parsing, with its own exception: groups
42
+ // 11, 12 and 16 (and their 21/22/26 partners) are scaled but NOT shifted,
43
+ // since they are extents rather than positions.
44
+ std::vector<Entity> readEntities(const std::string& path, double xorigin, double yorigin, double scale) {
45
+ std::ifstream in(path);
46
+ std::vector<Entity> out;
47
+ if (!in) return out;
48
+
49
+ std::string codeLine, dataLine;
50
+ Entity cur;
51
+ bool started = false;
52
+ while (std::getline(in, codeLine) && std::getline(in, dataLine)) {
53
+ const std::string codeStr = trimmed(codeLine);
54
+ const std::string data = trimmed(dataLine);
55
+ if (codeStr.empty()) continue;
56
+ int code = 0;
57
+ try {
58
+ code = std::stoi(codeStr);
59
+ } catch (...) {
60
+ break;
61
+ }
62
+ const auto num = [&]() -> double {
63
+ try {
64
+ return std::stod(data);
65
+ } catch (...) {
66
+ return 0.0;
67
+ }
68
+ };
69
+
70
+ if (code == 0) {
71
+ if (started) out.push_back(cur);
72
+ cur = Entity{};
73
+ cur.type = data;
74
+ started = true;
75
+ continue;
76
+ }
77
+ if (!started) continue;
78
+
79
+ if (code >= 10 && code <= 16) {
80
+ cur.coords[code - 10][0] =
81
+ (code == 11 || code == 12 || code == 16) ? num() * scale : (num() - xorigin) * scale;
82
+ } else if (code >= 20 && code <= 26) {
83
+ cur.coords[code - 20][1] =
84
+ (code == 21 || code == 22 || code == 26) ? num() * scale : (num() - yorigin) * scale;
85
+ }
86
+ switch (code) {
87
+ case 1: cur.name = data; break;
88
+ case 8: cur.layer = data; break;
89
+ case 10:
90
+ case 11: cur.xverts.push_back((num() - xorigin) * scale); break;
91
+ case 20:
92
+ case 21: cur.yverts.push_back((num() - yorigin) * scale); break;
93
+ case 50: cur.angle = num(); break;
94
+ case 70:
95
+ try {
96
+ cur.dimType = std::stoi(data);
97
+ } catch (...) {
98
+ }
99
+ break;
100
+ default: break;
101
+ }
102
+ }
103
+ if (started) out.push_back(cur);
104
+ return out;
105
+ }
106
+
107
+ double deg(double r) { return r * 180.0 / 3.14159265358979323846; }
108
+ double rad(double d) { return d * 3.14159265358979323846 / 180.0; }
109
+
110
+ struct CommonArgs {
111
+ std::string path;
112
+ std::string rawFile;
113
+ std::string layer;
114
+ double xorigin = 0.0, yorigin = 0.0, scale = 1.0;
115
+ };
116
+
117
+ CommonArgs commonArgs(const CallArgs& args, const oscad::ASTNode& node) {
118
+ CommonArgs c;
119
+ const Value fileArg = getArg(args, 0, "file", Value{});
120
+ c.rawFile = std::holds_alternative<std::string>(fileArg) ? std::get<std::string>(fileArg) : fmtValue(fileArg);
121
+ c.path = resolveFilePath(fileArg, node);
122
+ const Value layerArg = getArg(args, std::nullopt, "layer", Value{});
123
+ if (const std::string* s = std::get_if<std::string>(&layerArg)) c.layer = *s;
124
+ const Value originArg = getArg(args, std::nullopt, "origin", Value{});
125
+ if (const ListPtr* l = std::get_if<ListPtr>(&originArg); l && *l && (*l)->items.size() >= 2) {
126
+ c.xorigin = toDoubleLenient((*l)->items[0]);
127
+ c.yorigin = toDoubleLenient((*l)->items[1]);
128
+ }
129
+ const Value scaleArg = getArg(args, std::nullopt, "scale", Value{1.0});
130
+ if (std::holds_alternative<double>(scaleArg)) c.scale = std::get<double>(scaleArg);
131
+ return c;
132
+ }
133
+
134
+ } // namespace
135
+
136
+ Value builtinDxfDim(Evaluator& ev, const CallArgs& args, const oscad::ASTNode& node) {
137
+ const CommonArgs c = commonArgs(args, node);
138
+ const Value nameArg = getArg(args, std::nullopt, "name", Value{});
139
+ const std::string name = std::holds_alternative<std::string>(nameArg) ? std::get<std::string>(nameArg) : "";
140
+
141
+ if (!std::filesystem::exists(c.path)) {
142
+ ev.warn("Can't open DXF file '" + c.rawFile + "'!", &node.position());
143
+ return Value{};
144
+ }
145
+ for (const Entity& e : readEntities(c.path, c.xorigin, c.yorigin, c.scale)) {
146
+ if (e.type != "DIMENSION") continue;
147
+ if (!c.layer.empty() && c.layer != e.layer) continue;
148
+ if (!name.empty() && e.name != name) continue;
149
+
150
+ const int type = e.dimType & 7;
151
+ if (type == 0) { // rotated, horizontal or vertical
152
+ const double x = e.coords[4][0] - e.coords[3][0];
153
+ const double y = e.coords[4][1] - e.coords[3][1];
154
+ return Value{std::fabs(x * std::cos(rad(e.angle)) + y * std::sin(rad(e.angle)))};
155
+ }
156
+ if (type == 1) { // aligned
157
+ const double x = e.coords[4][0] - e.coords[3][0];
158
+ const double y = e.coords[4][1] - e.coords[3][1];
159
+ return Value{std::sqrt(x * x + y * y)};
160
+ }
161
+ if (type == 2) { // angular
162
+ const double a1 = deg(std::atan2(e.coords[0][0] - e.coords[5][0], e.coords[0][1] - e.coords[5][1]));
163
+ const double a2 = deg(std::atan2(e.coords[4][0] - e.coords[3][0], e.coords[4][1] - e.coords[3][1]));
164
+ return Value{std::fabs(a1 - a2)};
165
+ }
166
+ if (type == 3 || type == 4) { // diameter or radius
167
+ const double x = e.coords[5][0] - e.coords[0][0];
168
+ const double y = e.coords[5][1] - e.coords[0][1];
169
+ return Value{std::sqrt(x * x + y * y)};
170
+ }
171
+ if (type == 6) { // ordinate
172
+ return Value{(e.dimType & 64) ? e.coords[3][0] : e.coords[3][1]};
173
+ }
174
+ // type 5 (angular 3-point) falls through, as in the reference.
175
+ ev.warn("Dimension '" + name + "' in '" + c.rawFile + "', layer '" + c.layer + "' has unsupported type!",
176
+ &node.position());
177
+ return Value{};
178
+ }
179
+ ev.warn("Can't find dimension '" + name + "' in '" + c.rawFile + "', layer '" + c.layer + "'!",
180
+ &node.position());
181
+ return Value{};
182
+ }
183
+
184
+ Value builtinDxfCross(Evaluator& ev, const CallArgs& args, const oscad::ASTNode& node) {
185
+ const CommonArgs c = commonArgs(args, node);
186
+ if (!std::filesystem::exists(c.path)) {
187
+ ev.warn("Can't open DXF file '" + c.rawFile + "'!", &node.position());
188
+ return Value{};
189
+ }
190
+
191
+ // The first two LINEs on the layer are taken to be the cross. The
192
+ // reference walks every 2-point path instead, which for a real cross is
193
+ // the same set: its two strokes meet in the middle rather than at their
194
+ // endpoints, so neither gets joined into a longer path.
195
+ double p[4][2];
196
+ int found = 0;
197
+ for (const Entity& e : readEntities(c.path, c.xorigin, c.yorigin, c.scale)) {
198
+ if (e.type != "LINE") continue;
199
+ if (!c.layer.empty() && c.layer != e.layer) continue;
200
+ if (e.xverts.size() < 2 || e.yverts.size() < 2) continue;
201
+ p[found][0] = e.xverts[0];
202
+ p[found][1] = e.yverts[0];
203
+ ++found;
204
+ p[found][0] = e.xverts[1];
205
+ p[found][1] = e.yverts[1];
206
+ ++found;
207
+ if (found == 4) {
208
+ const double x1 = p[0][0], y1 = p[0][1], x2 = p[1][0], y2 = p[1][1];
209
+ const double x3 = p[2][0], y3 = p[2][1], x4 = p[3][0], y4 = p[3][1];
210
+ const double dem = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
211
+ if (dem == 0.0) break; // parallel: no cross, same as the reference
212
+ const double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / dem;
213
+ std::vector<Value> xy{Value{x1 + ua * (x2 - x1)}, Value{y1 + ua * (y2 - y1)}};
214
+ return Value{std::make_shared<const ValueList>(ValueList{std::move(xy)})};
215
+ }
216
+ }
217
+ ev.warn("Can't find cross in '" + c.rawFile + "', layer '" + c.layer + "'!", &node.position());
218
+ return Value{};
219
+ }
220
+
221
+ } // namespace oscadeval
@@ -1,5 +1,7 @@
1
1
  #include "openscad_cpp_evaluator/function_builtins.hpp"
2
2
 
3
+ #include "builtins.hpp" // builtinDxfDim/builtinDxfCross
4
+
3
5
  #include "openscad_cpp_evaluator/dispatch.hpp"
4
6
  #include "openscad_cpp_evaluator/evaluator.hpp"
5
7
  #include "openscad_cpp_evaluator/segments.hpp"
@@ -447,7 +449,7 @@ bool isBuiltinFunctionName(const std::string& name) {
447
449
  "acos", "atan", "atan2", "max", "min", "pow", "norm", "cross", "rands", "concat", "len", "str",
448
450
  "chr", "ord", "is_undef", "is_num", "is_bool", "is_string", "is_list", "is_function", "is_object",
449
451
  "search", "lookup", "has_key", "version", "version_num", "parent_module",
450
- "object", "textmetrics", "fontmetrics",
452
+ "object", "textmetrics", "fontmetrics", "dxf_dim", "dxf_cross",
451
453
  };
452
454
  return names.count(name) > 0;
453
455
  }
@@ -514,7 +516,7 @@ enum class BuiltinFnId {
514
516
  TextMetrics, FontMetrics, Abs, Sign, Ceil, Floor, Round, Sqrt, Ln, Log, Exp, Sin, Cos, Tan,
515
517
  Asin, Acos, Atan, Atan2, Max, Min, Pow, Norm, Cross, Rands, Concat, Len, Str, Chr, Ord,
516
518
  IsUndef, IsNum, IsBool, IsString, IsList, IsFunction, IsObject, Search, Lookup, HasKey,
517
- Version, VersionNum, ParentModule,
519
+ Version, VersionNum, ParentModule, DxfDim, DxfCross,
518
520
  };
519
521
 
520
522
  const std::unordered_map<std::string, BuiltinFnId>& builtinFnIds() {
@@ -536,6 +538,7 @@ const std::unordered_map<std::string, BuiltinFnId>& builtinFnIds() {
536
538
  {"lookup", BuiltinFnId::Lookup}, {"has_key", BuiltinFnId::HasKey},
537
539
  {"version", BuiltinFnId::Version}, {"version_num", BuiltinFnId::VersionNum},
538
540
  {"parent_module", BuiltinFnId::ParentModule},
541
+ {"dxf_dim", BuiltinFnId::DxfDim}, {"dxf_cross", BuiltinFnId::DxfCross},
539
542
  };
540
543
  return ids;
541
544
  }
@@ -949,6 +952,8 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
949
952
  if (!v || (v->size() != 2 && v->size() != 3)) return Value{};
950
953
  return Value{(*v)[0] * 10000.0 + (*v)[1] * 100.0 + (v->size() == 3 ? (*v)[2] : 0.0)};
951
954
  }
955
+ case BuiltinFnId::DxfDim: return builtinDxfDim(ev, args, node);
956
+ case BuiltinFnId::DxfCross: return builtinDxfCross(ev, args, node);
952
957
  case BuiltinFnId::ParentModule: {
953
958
  // Defaults to 1, not 0, when called with no argument at all --
954
959
  // and an index past the end of the stack is a warning, not a
@@ -34,6 +34,8 @@ const std::unordered_map<std::string_view, ResolveFn>& resolveDispatch() {
34
34
  {"rotate_extrude", &resolveRotateExtrude},
35
35
  {"projection", &resolveProjection},
36
36
  {"roof", &resolveRoof},
37
+ {"fill", &resolveFill},
38
+ {"minkowski_difference", &resolveMinkowskiDifference},
37
39
  {"offset", &resolveOffset},
38
40
  {"surface", &resolveSurface},
39
41
  {"text", &resolveText},
@@ -72,6 +74,8 @@ const std::unordered_map<std::string_view, GenerateFn>& generateDispatch() {
72
74
  {"rotate_extrude", &generateRotateExtrude},
73
75
  {"projection", &generateProjection},
74
76
  {"roof", &generateRoof},
77
+ {"fill", &generateFill},
78
+ {"minkowski_difference", &generateMinkowskiDifference},
75
79
  {"offset", &generateOffset},
76
80
  {"surface", &generateSurface},
77
81
  {"text", &generateText},
@@ -116,6 +120,8 @@ const std::vector<std::string>* builtinParamNames(const std::string& name) {
116
120
  {"rotate_extrude", {"angle", "start", "convexity"}},
117
121
  {"projection", {"cut", "convexity"}},
118
122
  {"roof", {"method", "convexity"}},
123
+ {"fill", {}},
124
+ {"minkowski_difference", {}},
119
125
  {"offset", {"r", "delta", "chamfer"}},
120
126
  {"surface", {"file", "center", "convexity", "invert"}},
121
127
  {"text", {"text", "size", "font", "direction", "language", "script", "halign", "valign", "spacing"}},
@@ -67,6 +67,76 @@ std::vector<ColoredBody> generateHull(Evaluator&, const CSGParams&, const std::v
67
67
  return result;
68
68
  }
69
69
 
70
+ // fill() -- 2D only: union the children, then discard every hole, keeping
71
+ // just the outer boundaries. Ported from the reference's applyFill2D: union
72
+ // the children, keep only the *positive* outlines, then re-union those (two
73
+ // outers can overlap once the holes between them are gone, and the result
74
+ // has to come back as one sanitized region rather than a pile of loops).
75
+ //
76
+ // A 3D child gets the reference's own "not yet implemented for 3D" warning
77
+ // and contributes nothing.
78
+
79
+ CSGParams resolveFill(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
80
+ auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
81
+ (void)args;
82
+ ev.evalChildren(node.children, effCtx);
83
+ return CSGParams{};
84
+ }
85
+
86
+ namespace {
87
+
88
+ // Shoelace. Manifold orients an outer contour counter-clockwise (positive
89
+ // area) and a hole clockwise, so the sign is the whole test.
90
+ double signedArea(const manifold::SimplePolygon& poly) {
91
+ double a = 0.0;
92
+ for (size_t i = 0, n = poly.size(); i < n; ++i) {
93
+ const auto& p = poly[i];
94
+ const auto& q = poly[(i + 1) % n];
95
+ a += static_cast<double>(p.x) * q.y - static_cast<double>(q.x) * p.y;
96
+ }
97
+ return a / 2.0;
98
+ }
99
+
100
+ } // namespace
101
+
102
+ std::vector<ColoredBody> generateFill(Evaluator& ev, const CSGParams&,
103
+ const std::vector<std::unique_ptr<CSGNode>>& children,
104
+ const oscad::ASTNode& node) {
105
+ const std::vector<ColoredBody> bodies = flattenCsgTree(children);
106
+ if (bodies.empty()) return {};
107
+ const RoleSplit split = splitByRole(bodies);
108
+
109
+ std::optional<ColoredBody> filled;
110
+ if (!split.foreground.empty()) {
111
+ for (const ColoredBody& c : split.foreground) {
112
+ if (c.body) {
113
+ ev.warn("fill() not yet implemented for 3D", &node.position());
114
+ break;
115
+ }
116
+ }
117
+ if (const std::optional<manifold::CrossSection> cs = toCrossSection(split.foreground)) {
118
+ manifold::Polygons outers;
119
+ for (const manifold::SimplePolygon& loop : cs->ToPolygons()) {
120
+ if (signedArea(loop) > 0.0) outers.push_back(loop);
121
+ }
122
+ if (!outers.empty()) {
123
+ ColoredBody cb;
124
+ cb.section = manifold::CrossSection(outers, manifold::CrossSection::FillRule::Positive);
125
+ cb.color = split.foreground.front().color;
126
+ filled = std::move(cb);
127
+ }
128
+ }
129
+ }
130
+
131
+ std::vector<ColoredBody> result;
132
+ if (filled) result.push_back(std::move(*filled));
133
+ result.insert(result.end(), split.background.begin(), split.background.end());
134
+ result.insert(result.end(), split.highlight.begin(), split.highlight.end());
135
+ result.insert(result.end(), split.showOnly.begin(), split.showOnly.end());
136
+ result.insert(result.end(), split.displayOnly.begin(), split.displayOnly.end());
137
+ return result;
138
+ }
139
+
70
140
  CSGParams resolveMinkowski(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
71
141
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
72
142
  (void)args;
@@ -243,4 +313,66 @@ std::vector<ColoredBody> generateMinkowski(Evaluator& ev, const CSGParams&, cons
243
313
  return out;
244
314
  }
245
315
 
316
+ // minkowski_difference() -- erosion, the operation `minkowski()` has no
317
+ // inverse for. A BelfrySCAD extension: OpenSCAD has no equivalent module and
318
+ // no way to express it in the language, since minkowski() only ever sums.
319
+ // Shrinking a part by a clearance, or hollowing one to a wall thickness,
320
+ // otherwise needs hand-built difference() scaffolding that is not the same
321
+ // operation.
322
+ //
323
+ // Reads like minkowski(): the first child is the body being eroded, and
324
+ // every child after it is a tool eroded away from it in turn. 3D only, same
325
+ // as minkowski()'s own 3D path -- Manifold has no CrossSection erosion, and
326
+ // 2D already has one in offset(r=-N).
327
+
328
+ CSGParams resolveMinkowskiDifference(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
329
+ auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
330
+ (void)args;
331
+ ev.evalChildren(node.children, effCtx);
332
+ return CSGParams{};
333
+ }
334
+
335
+ std::vector<ColoredBody> generateMinkowskiDifference(Evaluator& ev, const CSGParams&,
336
+ const std::vector<std::unique_ptr<CSGNode>>& children,
337
+ const oscad::ASTNode& node) {
338
+ const std::vector<ColoredBody> bodies = flattenCsgTree(children);
339
+ const RoleSplit split = splitByRole(bodies);
340
+
341
+ std::vector<const ColoredBody*> bodies3d;
342
+ for (const ColoredBody& c : split.foreground) {
343
+ if (c.body) bodies3d.push_back(&c);
344
+ }
345
+
346
+ std::vector<ColoredBody> passthrough;
347
+ passthrough.insert(passthrough.end(), split.background.begin(), split.background.end());
348
+ passthrough.insert(passthrough.end(), split.highlight.begin(), split.highlight.end());
349
+ passthrough.insert(passthrough.end(), split.showOnly.begin(), split.showOnly.end());
350
+ passthrough.insert(passthrough.end(), split.displayOnly.begin(), split.displayOnly.end());
351
+
352
+ if (bodies3d.empty()) return passthrough;
353
+ if (bodies3d.size() == 1) {
354
+ // Nothing to erode with, so nothing happens -- the same no-op
355
+ // minkowski() gives for a single child.
356
+ std::vector<ColoredBody> result = {*bodies3d.front()};
357
+ result.insert(result.end(), passthrough.begin(), passthrough.end());
358
+ return result;
359
+ }
360
+
361
+ manifold::Manifold result = *bodies3d.front()->body;
362
+ for (size_t i = 1; i < bodies3d.size(); ++i) result = result.MinkowskiDifference(*bodies3d[i]->body);
363
+ if (result.Status() != manifold::Manifold::Error::NoError) {
364
+ ev.warn("minkowski_difference: result is not manifold", &node.position());
365
+ }
366
+
367
+ ColoredBody cb;
368
+ cb.body = std::move(result);
369
+ cb.color = bodies3d.front()->color;
370
+ // Per-triangle colour cannot survive an erosion: the surface it was
371
+ // indexed against is gone.
372
+ cb.triColors.reset();
373
+ std::vector<ColoredBody> out = {std::move(cb)};
374
+ out.insert(out.end(), passthrough.begin(), passthrough.end());
375
+ return out;
376
+ }
377
+
246
378
  } // namespace oscadeval