openscad-cpp-evaluator 1.38.0__tar.gz → 1.38.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/pyproject.toml +1 -1
  3. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/extrude.cpp +100 -12
  4. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_extrude_roof.cpp +129 -0
  5. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/uv.lock +1 -1
  6. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/.github/workflows/ci.yml +0 -0
  7. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/.github/workflows/wheels.yml +0 -0
  8. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/.gitignore +0 -0
  9. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/.gitmodules +0 -0
  10. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/CLAUDE.md +0 -0
  11. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/CMakeLists.txt +0 -0
  12. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/LICENSE +0 -0
  13. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/README.md +0 -0
  14. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/bindings/CMakeLists.txt +0 -0
  15. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/bindings/ast_to_py.cpp +0 -0
  16. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/bindings/module.cpp +0 -0
  17. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/cmake/embed_font.cmake +0 -0
  18. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/cmake/patch_manifold.cmake +0 -0
  19. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/examples/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/examples/eval_perf_benchmark.cpp +0 -0
  21. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/examples/manifold_cache_reuse.cpp +0 -0
  22. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/examples/minimal_debugger.cpp +0 -0
  23. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  24. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/.gitignore +0 -0
  25. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  26. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  27. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/README.md +0 -0
  28. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  29. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  30. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  31. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  32. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  33. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  34. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  35. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  36. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  37. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  39. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  40. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  41. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  42. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  43. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  44. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/.DS_Store +0 -0
  45. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  46. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
  47. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  48. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  49. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  50. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  51. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  52. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  53. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  54. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  55. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  56. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  57. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  58. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  59. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  60. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  61. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  62. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  63. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
  64. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  65. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  66. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  67. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  68. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  69. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  70. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  71. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  72. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  73. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
  74. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  75. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  76. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  77. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  78. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  79. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  80. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  81. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  82. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  83. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  84. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
  85. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  86. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  87. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  88. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  89. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  90. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  91. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  92. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  93. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  94. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  95. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  96. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  97. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  98. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  99. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  100. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  101. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  102. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  103. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  104. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  105. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  106. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  107. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  108. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  109. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  110. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  111. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  112. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
  113. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  114. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  115. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  116. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  117. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  118. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  119. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  120. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  121. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  122. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  123. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  124. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  125. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  126. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  127. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  128. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  129. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
  130. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  131. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
  132. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LICENSE +0 -0
  133. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  134. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  135. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  136. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  137. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/CMakeLists.txt +0 -0
  138. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/booleans.cpp +0 -0
  139. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/builtins.hpp +0 -0
  140. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/call_args.cpp +0 -0
  141. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/color.cpp +0 -0
  142. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/control.cpp +0 -0
  143. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/dxf_dim.cpp +0 -0
  144. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/function_builtins.cpp +0 -0
  145. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/import.cpp +0 -0
  146. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/modifiers.cpp +0 -0
  147. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/primitives_2d.cpp +0 -0
  148. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/primitives_3d.cpp +0 -0
  149. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/profile_time.cpp +0 -0
  150. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/registry.cpp +0 -0
  151. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/roof.cpp +0 -0
  152. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/segments.cpp +0 -0
  153. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/surface.cpp +0 -0
  154. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/text.cpp +0 -0
  155. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/topology.cpp +0 -0
  156. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/builtins/transforms.cpp +0 -0
  157. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/bytecode_compiler.cpp +0 -0
  158. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/bytecode_vm.cpp +0 -0
  159. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/colored_body.cpp +0 -0
  160. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/coverage.cpp +0 -0
  161. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/csg_generate.cpp +0 -0
  162. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/csg_resolve.cpp +0 -0
  163. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/css_colors.cpp +0 -0
  164. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/debug/debug_repl.cpp +0 -0
  165. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/debug_profile.cpp +0 -0
  166. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/eval_context.cpp +0 -0
  167. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/eval_error.cpp +0 -0
  168. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/eval_use.cpp +0 -0
  169. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/export.cpp +0 -0
  170. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/expr_eval.cpp +0 -0
  171. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/format_closure.cpp +0 -0
  172. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/import/dxf_import.cpp +0 -0
  173. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/import/mesh_import.cpp +0 -0
  174. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/import/scene_import.cpp +0 -0
  175. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/import/surface_load.cpp +0 -0
  176. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/import/svg_import.cpp +0 -0
  177. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/manifold_cache.cpp +0 -0
  178. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/measure_geometry.cpp +0 -0
  179. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/mesh_check.cpp +0 -0
  180. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/stmt_eval.cpp +0 -0
  181. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/text/font_match.cpp +0 -0
  182. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/text/freetype_font_provider.cpp +0 -0
  183. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/text/text_metrics.cpp +0 -0
  184. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/user_calls.cpp +0 -0
  185. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/utf8.cpp +0 -0
  186. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/value.cpp +0 -0
  187. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/xkcd_colors.cpp +0 -0
  188. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/xkcd_colors.hpp +0 -0
  189. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/src/zip_stored.cpp +0 -0
  190. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/CMakeLists.txt +0 -0
  191. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_booleans.cpp +0 -0
  192. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_bytecode_compiler.cpp +0 -0
  193. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_children_separate.cpp +0 -0
  194. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_cli.cpp +0 -0
  195. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_control_flow.cpp +0 -0
  196. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_coverage.cpp +0 -0
  197. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_csg_tree.cpp +0 -0
  198. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_debug_hooks.cpp +0 -0
  199. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_discretizer.cpp +0 -0
  200. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_dxf_svg_import.cpp +0 -0
  201. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_eval_context.cpp +0 -0
  202. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_export.cpp +0 -0
  203. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_expr_eval.cpp +0 -0
  204. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_feature_detection.cpp +0 -0
  205. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_function_builtins.cpp +0 -0
  206. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_helpers.hpp +0 -0
  207. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_import_export.cpp +0 -0
  208. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_include_cache.cpp +0 -0
  209. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_manifold_cache.cpp +0 -0
  210. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_mesh_check.cpp +0 -0
  211. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_multi_color_merge.cpp +0 -0
  212. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_primitives.cpp +0 -0
  213. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_profile_time.cpp +0 -0
  214. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_profiling.cpp +0 -0
  215. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_python_bindings.py +0 -0
  216. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_render_expr.cpp +0 -0
  217. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_scope_trail.cpp +0 -0
  218. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_scoping.cpp +0 -0
  219. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_surface.cpp +0 -0
  220. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_tail_calls.cpp +0 -0
  221. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_text.cpp +0 -0
  222. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_transforms.cpp +0 -0
  223. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_undef_args.cpp +0 -0
  224. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_unexpected_args.cpp +0 -0
  225. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_use.cpp +0 -0
  226. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_value.cpp +0 -0
  227. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_viewport_params.cpp +0 -0
  228. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tests/test_zip_stored.cpp +0 -0
  229. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tools/cli/CMakeLists.txt +0 -0
  230. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tools/cli/cli_lib.cpp +0 -0
  231. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tools/cli/cli_lib.hpp +0 -0
  232. {openscad_cpp_evaluator-1.38.0 → openscad_cpp_evaluator-1.38.1}/tools/cli/main.cpp +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.38.0
3
+ Version: 1.38.1
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.38.0"
7
+ version = "1.38.1"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -118,34 +118,86 @@ manifold::Manifold extrudeTwisted(const manifold::Polygons& polys, double height
118
118
  BuiltinWrapParams computeLinearExtrudeParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
119
119
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
120
120
 
121
- // 100 when not given, as upstream (the old 1 was a guess), and `h` as
122
- // upstream's alias -- BOSL2's linear_extrude override forwards it.
123
- const double height = toDoubleLenient(getArgOrAlias(ev, &node.position(), args, 0, "height", "h", Value{100.0}));
124
- const bool center = truthy(getArg(args, std::nullopt, "center", Value{false}));
125
- const double twist = toDoubleLenient(getArg(args, std::nullopt, "twist", Value{0.0}));
121
+ // Positional order is upstream's: height, v, scale, center, twist,
122
+ // slices, segments. Only height used to be read positionally.
123
+ //
124
+ // Upstream's LinearExtrudeNode: the extrusion runs along a vector, (0,0,1)
125
+ // unless `v` gives one. Its length is `height` (`h` its alias, BOSL2's
126
+ // override forwards it) when given -- `v` then only sets the direction --
127
+ // else |v| when `v` is given, else 100. A vector pointing down (z <= 0)
128
+ // extrudes nothing. Bad values warn and fall back as upstream's do.
129
+ // `v` was ignored here, so an oblique extrusion came out straight.
130
+ double hv[3] = {0.0, 0.0, 1.0};
131
+ double length = 100.0;
132
+ const Value vArg = getArg(args, 1, "v", Value{});
133
+ if (!std::holds_alternative<std::monostate>(vArg)) {
134
+ const ListPtr* l = std::get_if<ListPtr>(&vArg);
135
+ bool ok = l && *l && (*l)->items.size() == 3;
136
+ double t[3] = {0, 0, 0};
137
+ for (int i = 0; ok && i < 3; ++i) {
138
+ const double* d = std::get_if<double>(&(*l)->items[i]);
139
+ ok = d && std::isfinite(*d);
140
+ if (ok) t[i] = *d;
141
+ }
142
+ if (ok) std::copy(t, t + 3, hv);
143
+ else ev.warn("v when specified should be a 3d vector", &node.position());
144
+ length = 1.0;
145
+ }
146
+ const Value heightArg = getArgOrAlias(ev, &node.position(), args, 0, "height", "h", Value{});
147
+ if (!std::holds_alternative<std::monostate>(heightArg)) {
148
+ const double* d = std::get_if<double>(&heightArg);
149
+ if (d && std::isfinite(*d)) {
150
+ length = *d;
151
+ } else {
152
+ ev.warn("height when specified should be a number", &node.position());
153
+ length = 100.0;
154
+ }
155
+ const double norm = std::sqrt(hv[0] * hv[0] + hv[1] * hv[1] + hv[2] * hv[2]);
156
+ for (double& c : hv) c /= norm;
157
+ }
158
+ for (double& c : hv) c *= length;
159
+ if (hv[2] <= 0) hv[2] = 0;
160
+ const double height = hv[2];
161
+ // Only a real boolean counts, as upstream: center="yes" is not true.
162
+ const Value centerArg = getArg(args, 3, "center", Value{false});
163
+ const bool* centerBool = std::get_if<bool>(&centerArg);
164
+ const bool center = centerBool && *centerBool;
165
+ const double twist = toDoubleLenient(getArg(args, 4, "twist", Value{0.0}));
126
166
  // Upstream's validate_integral: any finite number counts as given, and is
127
167
  // truncated and clamped (slices >= 1, segments >= 0). Not given, the
128
168
  // discretizer decides.
129
- const auto integral = [&](const char* name, double lo) -> std::optional<double> {
130
- const Value v = getArg(args, std::nullopt, name, Value{});
169
+ const auto integral = [&](int pos, const char* name, double lo) -> std::optional<double> {
170
+ const Value v = getArg(args, pos, name, Value{});
131
171
  const double* d = std::get_if<double>(&v);
132
172
  if (!d || !std::isfinite(*d)) return std::nullopt;
133
173
  return *d < lo ? lo : std::trunc(*d);
134
174
  };
135
- const std::optional<double> slices = integral("slices", 1.0);
136
- const std::optional<double> segments = integral("segments", 0.0);
137
- const Value scaleArg = getArg(args, std::nullopt, "scale", Value{});
175
+ const std::optional<double> slices = integral(5, "slices", 1.0);
176
+ const std::optional<double> segments = integral(6, "segments", 0.0);
177
+ const Value scaleArg = getArg(args, 2, "scale", Value{});
138
178
 
139
179
  double scaleX = 1.0, scaleY = 1.0;
180
+ bool scaleOk = true;
140
181
  if (const double* s = std::get_if<double>(&scaleArg)) {
141
182
  scaleX = scaleY = *s;
183
+ scaleOk = std::isfinite(*s);
142
184
  } else if (const ListPtr* l = std::get_if<ListPtr>(&scaleArg); l && *l && (*l)->items.size() >= 2) {
143
185
  scaleX = toDoubleLenient((*l)->items[0]);
144
186
  scaleY = toDoubleLenient((*l)->items[1]);
187
+ scaleOk = std::isfinite(scaleX) && std::isfinite(scaleY);
188
+ } else if (!std::holds_alternative<std::monostate>(scaleArg)) {
189
+ scaleOk = false;
190
+ }
191
+ if (!scaleOk) {
192
+ ev.warn("linear_extrude(..., scale=" + fmtValue(scaleArg) + ") could not be converted", &node.position());
193
+ scaleX = scaleY = 1.0;
145
194
  }
146
195
 
147
196
  CSGParams params;
148
197
  params["height"] = Value{height};
198
+ // How far the top slides sideways per unit of height (0 unless `v`).
199
+ params["shear_x"] = Value{height > 0 ? hv[0] / height : 0.0};
200
+ params["shear_y"] = Value{height > 0 ? hv[1] / height : 0.0};
149
201
  params["center"] = Value{center};
150
202
  params["twist"] = Value{twist};
151
203
  params["slices"] = slices ? Value{*slices} : Value{};
@@ -227,6 +279,14 @@ std::vector<ColoredBody> generateLinearExtrude(Evaluator& ev, const CSGParams& p
227
279
  ? extrudeTwisted(polys, height, slices, twist, scaleX, scaleY)
228
280
  : manifold::Manifold::Extrude(polys, height, slices - 1, -twist, manifold::vec2(scaleX, scaleY));
229
281
  if (std::get<bool>(params.at("center"))) body = body.Translate(manifold::vec3(0, 0, -height / 2));
282
+ // `v`: slide each point sideways in proportion to its height -- upstream
283
+ // places slice k at bottom + v*k/n, and centring subtracts v/2, so the
284
+ // same shear is right either way.
285
+ const double shearX = std::get<double>(params.at("shear_x"));
286
+ const double shearY = std::get<double>(params.at("shear_y"));
287
+ if (shearX != 0.0 || shearY != 0.0) {
288
+ body = body.Transform(manifold::mat3x4({1, 0, 0}, {0, 1, 0}, {shearX, shearY, 1}, {0, 0, 0}));
289
+ }
230
290
  return {ev.tagGenerated(std::move(body), node, params.at("color"))};
231
291
  }
232
292
 
@@ -241,11 +301,36 @@ std::vector<ColoredBody> generateLinearExtrude(Evaluator& ev, const CSGParams& p
241
301
  BuiltinWrapParams computeRotateExtrudeParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
242
302
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
243
303
 
244
- const double angle = toDoubleLenient(getArgOrAlias(ev, &node.position(), args, 0, "angle", "a", Value{360.0}));
304
+ // Positional order is upstream's: angle, start.
305
+ //
306
+ // Upstream's RotateExtrudeNode: the sweep runs from `start` through
307
+ // `start + angle`. With an angle, start defaults to 0 (the +X axis) and
308
+ // an angle outside (-360, 360] becomes a full turn; with none, the turn
309
+ // is 360 and starts at 180 (-X), which upstream says will change, and
310
+ // says so for an odd $fn, where the start shows. An explicit start
311
+ // always wins. It was ignored here: a quarter turn from 90 drew 0..90.
312
+ const Value angleArg = getArgOrAlias(ev, &node.position(), args, 0, "angle", "a", Value{});
313
+ const Value startArg = getArg(args, 1, "start", Value{});
314
+ const bool hasAngle = !std::holds_alternative<std::monostate>(angleArg) && std::isfinite(toDoubleLenient(angleArg));
315
+ const bool hasStart = !std::holds_alternative<std::monostate>(startArg) && std::isfinite(toDoubleLenient(startArg));
316
+ double angle = 360.0;
317
+ double start = 180.0;
318
+ if (hasAngle) {
319
+ angle = toDoubleLenient(angleArg);
320
+ start = 0.0;
321
+ if (angle <= -360.0 || angle > 360.0) angle = 360.0;
322
+ }
323
+ if (hasStart) start = toDoubleLenient(startArg);
324
+ const Discretizer disc = Discretizer::fromCtx(effCtx);
325
+ if (!hasAngle && !hasStart && (static_cast<int>(disc.fn) & 1)) {
326
+ ev.emitWarning("DEPRECATED: In future releases, rotational extrusion without \"angle\" will start at zero, "
327
+ "the +X axis. Set start=180 to explicitly start on the -X axis.");
328
+ }
245
329
 
246
330
  CSGParams params;
247
331
  params["angle"] = Value{angle};
248
- Discretizer::fromCtx(effCtx).store(params);
332
+ params["start"] = Value{start};
333
+ disc.store(params);
249
334
  params["color"] = colorToValue(effCtx.color);
250
335
  return BuiltinWrapParams{std::move(params), std::move(effCtx)};
251
336
  }
@@ -299,6 +384,9 @@ std::vector<ColoredBody> generateRotateExtrude(Evaluator& ev, const CSGParams& p
299
384
  v = manifold::vec3(v.x * std::cos(a), v.x * std::sin(a), sign * v.y);
300
385
  });
301
386
  }
387
+ // The arc above runs from +X; turn it to begin at `start`.
388
+ const double start = std::get<double>(params.at("start"));
389
+ if (start != 0.0) body = body.Rotate(0.0, 0.0, start);
302
390
  return {ev.tagGenerated(std::move(body), node, params.at("color"))};
303
391
  }
304
392
 
@@ -82,6 +82,85 @@ TEST(LinearExtrude, ScaleShrinksTheTopFace) {
82
82
  EXPECT_NEAR(e.bodies[0].body->Volume(), straight / 3.0, straight * 0.02);
83
83
  }
84
84
 
85
+ // linear_extrude's `v`, and its argument checks, as upstream's
86
+ // LinearExtrudeNode has them; each case below matched OpenSCAD 2026.02's
87
+ // bounding box and volume. `v` was ignored here before.
88
+ namespace {
89
+ struct Extruded {
90
+ manifold::Box box;
91
+ double volume = 0;
92
+ std::vector<std::string> msgs;
93
+ };
94
+ Extruded linearExtrude(const std::string& args) {
95
+ Extruded r;
96
+ Evaluated e = evalSrc("linear_extrude(" + args + ") translate([5,0]) square([4,2]);",
97
+ [&](const std::string& m) { r.msgs.push_back(m); });
98
+ if (!e.bodies.empty() && e.bodies[0].body) {
99
+ r.box = e.bodies[0].body->BoundingBox();
100
+ r.volume = e.bodies[0].body->Volume();
101
+ }
102
+ return r;
103
+ }
104
+ } // namespace
105
+
106
+ TEST(LinearExtrude, VAloneIsTheWholeExtrusion) {
107
+ const Extruded r = linearExtrude("v=[1,0,1]"); // length |v|, leaning +X
108
+ EXPECT_NEAR(r.box.max.x, 10.0, 1e-6);
109
+ EXPECT_NEAR(r.box.max.z, 1.0, 1e-6);
110
+ EXPECT_NEAR(r.volume, 8.0, 1e-6);
111
+ }
112
+
113
+ TEST(LinearExtrude, HeightWithVSetsOnlyTheDirection) {
114
+ const Extruded r = linearExtrude("v=[1,1,1], height=10");
115
+ const double side = 10.0 / std::sqrt(3.0);
116
+ EXPECT_NEAR(r.box.max.x, 9.0 + side, 1e-3);
117
+ EXPECT_NEAR(r.box.max.y, 2.0 + side, 1e-3);
118
+ EXPECT_NEAR(r.box.max.z, side, 1e-3);
119
+ }
120
+
121
+ TEST(LinearExtrude, CenteringShiftsByHalfOfV) {
122
+ const Extruded r = linearExtrude("v=[2,3,10], center=true");
123
+ EXPECT_NEAR(r.box.min.x, 4.0, 1e-6);
124
+ EXPECT_NEAR(r.box.max.x, 10.0, 1e-6);
125
+ EXPECT_NEAR(r.box.min.y, -1.5, 1e-6);
126
+ EXPECT_NEAR(r.box.min.z, -5.0, 1e-6);
127
+ }
128
+
129
+ TEST(LinearExtrude, DownwardVExtrudesNothing) {
130
+ EXPECT_EQ(linearExtrude("v=[0,0,-1]").volume, 0.0);
131
+ }
132
+
133
+ TEST(LinearExtrude, OnlyARealBooleanCenters) {
134
+ EXPECT_NEAR(linearExtrude("height=10, center=\"yes\"").box.min.z, 0.0, 1e-6);
135
+ EXPECT_NEAR(linearExtrude("height=10, center=true").box.min.z, -5.0, 1e-6);
136
+ }
137
+
138
+ TEST(LinearExtrude, BadArgumentsWarnAndFallBack) {
139
+ const Extruded h = linearExtrude("height=\"a\"");
140
+ EXPECT_NEAR(h.box.max.z, 100.0, 1e-6);
141
+ ASSERT_EQ(h.msgs.size(), 1u);
142
+ EXPECT_EQ(h.msgs[0].rfind("WARNING: height when specified should be a number", 0), 0u) << h.msgs[0];
143
+
144
+ const Extruded v = linearExtrude("v=[1,2]");
145
+ EXPECT_NEAR(v.box.max.z, 1.0, 1e-6);
146
+ ASSERT_EQ(v.msgs.size(), 1u);
147
+ EXPECT_EQ(v.msgs[0].rfind("WARNING: v when specified should be a 3d vector", 0), 0u) << v.msgs[0];
148
+
149
+ const Extruded s = linearExtrude("height=10, scale=\"x\"");
150
+ EXPECT_NEAR(s.volume, 80.0, 1e-6);
151
+ ASSERT_EQ(s.msgs.size(), 1u);
152
+ EXPECT_EQ(s.msgs[0].rfind("WARNING: linear_extrude(..., scale=\"x\") could not be converted", 0), 0u) << s.msgs[0];
153
+ }
154
+
155
+ TEST(LinearExtrude, ArgumentsArePositionalInUpstreamsOrder) {
156
+ // height, v, scale, center, twist, slices, segments
157
+ EXPECT_NEAR(linearExtrude("10, [1,0,1]").box.max.x, 9.0 + 10.0 / std::sqrt(2.0), 1e-3);
158
+ EXPECT_NEAR(linearExtrude("10, undef, 0.5").volume, 46.67, 0.01);
159
+ EXPECT_NEAR(linearExtrude("10, undef, 1, true").box.min.z, -5.0, 1e-6);
160
+ EXPECT_NEAR(linearExtrude("10, undef, 1, false, 90").box.min.y, -9.0, 1e-6);
161
+ EXPECT_NEAR(linearExtrude("10, undef, 1, false, 90, 2").volume, 81.62, 0.01);
162
+ }
163
+
85
164
  // -- rotate_extrude -----------------------------------------------------
86
165
 
87
166
  TEST(RotateExtrude, FullRevolveOfASquareMatchesPappusTheorem) {
@@ -96,6 +175,56 @@ TEST(RotateExtrude, FullRevolveOfASquareMatchesPappusTheorem) {
96
175
  EXPECT_NEAR(e.bodies[0].body->Volume(), analytic, analytic * 0.01);
97
176
  }
98
177
 
178
+ // The sweep runs from `start` through start + angle, as upstream's
179
+ // RotateExtrudeNode has it; start was ignored here until this was checked
180
+ // against OpenSCAD 2026.02 (every case below matched it bounding box for
181
+ // bounding box).
182
+ namespace {
183
+ manifold::Box rotateExtrudeBox(const std::string& args, std::vector<std::string>* msgs = nullptr) {
184
+ Evaluated e = evalSrc("rotate_extrude(" + args + ") translate([10,0]) square(2);",
185
+ [&](const std::string& m) { if (msgs) msgs->push_back(m); });
186
+ EXPECT_EQ(e.bodies.size(), 1u);
187
+ return e.bodies.empty() ? manifold::Box{} : e.bodies[0].body->BoundingBox();
188
+ }
189
+ } // namespace
190
+
191
+ TEST(RotateExtrude, StartTurnsWhereTheSweepBegins) {
192
+ const manifold::Box b = rotateExtrudeBox("angle=90, start=90"); // the +Y/-X quadrant
193
+ EXPECT_NEAR(b.min.x, -12.0, 1e-6);
194
+ EXPECT_NEAR(b.max.x, 0.0, 1e-6);
195
+ EXPECT_NEAR(b.min.y, 0.0, 1e-6);
196
+ const manifold::Box neg = rotateExtrudeBox("angle=-90, start=90"); // sweeps back to +X
197
+ EXPECT_NEAR(neg.min.x, 0.0, 1e-6);
198
+ EXPECT_NEAR(neg.min.y, 0.0, 1e-6);
199
+ EXPECT_NEAR(neg.max.y, 12.0, 1e-6);
200
+ }
201
+
202
+ TEST(RotateExtrude, AFullTurnWithNoAngleStartsOnMinusXAndSaysItWillChange) {
203
+ std::vector<std::string> msgs;
204
+ const manifold::Box b = rotateExtrudeBox("$fn=5", &msgs); // a pentagon: the start shows
205
+ EXPECT_NEAR(b.min.x, -12.0, 1e-6);
206
+ EXPECT_NEAR(b.max.x, 12.0 * std::cos(std::numbers::pi / 5), 1e-3);
207
+ ASSERT_EQ(msgs.size(), 1u);
208
+ EXPECT_EQ(msgs[0].rfind("DEPRECATED: In future releases, rotational extrusion without \"angle\"", 0), 0u) << msgs[0];
209
+ // Given an angle or a start, it starts where told and says nothing.
210
+ msgs.clear();
211
+ EXPECT_NEAR(rotateExtrudeBox("angle=360, $fn=5", &msgs).max.x, 12.0, 1e-6);
212
+ EXPECT_NEAR(rotateExtrudeBox("start=0, $fn=5", &msgs).max.x, 12.0, 1e-6);
213
+ EXPECT_TRUE(msgs.empty());
214
+ }
215
+
216
+ TEST(RotateExtrude, StartIsTheSecondPositionalArgument) {
217
+ EXPECT_NEAR(rotateExtrudeBox("90, 90").min.x, -12.0, 1e-6); // angle, start
218
+ }
219
+
220
+ TEST(RotateExtrude, AnAngleOutsideAFullTurnIsAFullTurn) {
221
+ for (const char* a : {"angle=400", "angle=-400", "angle=-360"}) {
222
+ const manifold::Box b = rotateExtrudeBox(a);
223
+ EXPECT_NEAR(b.min.x, -12.0, 1e-3) << a;
224
+ EXPECT_NEAR(b.max.x, 12.0, 1e-3) << a;
225
+ }
226
+ }
227
+
99
228
  // -- projection -----------------------------------------------------------
100
229
 
101
230
  TEST(Projection, NonCutProjectsFullSilhouette) {
@@ -55,7 +55,7 @@ wheels = [
55
55
 
56
56
  [[package]]
57
57
  name = "openscad-cpp-evaluator"
58
- version = "1.38.0"
58
+ version = "1.38.1"
59
59
  source = { editable = "." }
60
60
  dependencies = [
61
61
  { name = "numpy" },