openscad-cpp-evaluator 0.13.9__tar.gz → 0.14.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 (195) hide show
  1. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/pyproject.toml +1 -1
  3. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/booleans.cpp +17 -1
  4. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/extrude.cpp +12 -1
  5. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/function_builtins.cpp +42 -6
  6. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/primitives_3d.cpp +26 -22
  7. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/transforms.cpp +44 -6
  8. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_booleans.cpp +19 -0
  9. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_extrude_roof.cpp +21 -5
  10. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_function_builtins.cpp +50 -0
  11. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_primitives.cpp +40 -3
  12. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_transforms.cpp +50 -0
  13. openscad_cpp_evaluator-0.14.0/uv.lock +65 -0
  14. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/.github/workflows/ci.yml +0 -0
  15. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/.github/workflows/wheels.yml +0 -0
  16. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/.gitignore +0 -0
  17. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/.gitmodules +0 -0
  18. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/CLAUDE.md +0 -0
  19. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/LICENSE +0 -0
  21. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/README.md +0 -0
  22. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/bindings/CMakeLists.txt +0 -0
  23. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/bindings/module.cpp +0 -0
  24. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/cmake/embed_font.cmake +0 -0
  25. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/examples/CMakeLists.txt +0 -0
  26. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/examples/eval_perf_benchmark.cpp +0 -0
  27. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/examples/manifold_cache_reuse.cpp +0 -0
  28. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/examples/minimal_debugger.cpp +0 -0
  29. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  30. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/.gitignore +0 -0
  31. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  32. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  33. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/README.md +0 -0
  34. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  35. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  36. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  37. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  38. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  39. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  40. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  41. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  42. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  43. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  44. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  45. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  46. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  47. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  48. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  49. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  50. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  51. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  52. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  53. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  54. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  55. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  56. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  57. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  58. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  59. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  60. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  61. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  62. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  63. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  64. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  65. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  66. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  67. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  68. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  69. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  70. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  71. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  72. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  73. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  74. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  75. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  76. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  77. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  78. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  79. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  80. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  81. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  82. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  83. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  84. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  85. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  86. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  87. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  88. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  89. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  90. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  91. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  92. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  93. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  94. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  95. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  96. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  97. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  98. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  99. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  100. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  101. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  102. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  103. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  104. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  105. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  106. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  107. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  108. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  109. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  110. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  111. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  112. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  113. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  114. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  115. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  116. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  117. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  118. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  119. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  120. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  121. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
  122. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  123. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  124. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  125. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  126. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  127. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/resources/fonts/LICENSE +0 -0
  128. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  129. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/CMakeLists.txt +0 -0
  130. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/builtins.hpp +0 -0
  131. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/call_args.cpp +0 -0
  132. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/color.cpp +0 -0
  133. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/control.cpp +0 -0
  134. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/import.cpp +0 -0
  135. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/modifiers.cpp +0 -0
  136. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/primitives_2d.cpp +0 -0
  137. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/registry.cpp +0 -0
  138. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/roof.cpp +0 -0
  139. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/segments.cpp +0 -0
  140. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/surface.cpp +0 -0
  141. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/text.cpp +0 -0
  142. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/builtins/topology.cpp +0 -0
  143. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/bytecode_compiler.cpp +0 -0
  144. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/bytecode_vm.cpp +0 -0
  145. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/colored_body.cpp +0 -0
  146. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/csg_generate.cpp +0 -0
  147. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/csg_resolve.cpp +0 -0
  148. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/css_colors.cpp +0 -0
  149. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/debug/debug_repl.cpp +0 -0
  150. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/debug_profile.cpp +0 -0
  151. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/eval_context.cpp +0 -0
  152. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/eval_error.cpp +0 -0
  153. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/eval_use.cpp +0 -0
  154. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/export.cpp +0 -0
  155. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/expr_eval.cpp +0 -0
  156. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/import/dxf_import.cpp +0 -0
  157. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/import/mesh_import.cpp +0 -0
  158. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/import/surface_load.cpp +0 -0
  159. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/import/svg_import.cpp +0 -0
  160. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/manifold_cache.cpp +0 -0
  161. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/stmt_eval.cpp +0 -0
  162. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/text/stb_font_provider.cpp +0 -0
  163. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/text/text_metrics.cpp +0 -0
  164. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/user_calls.cpp +0 -0
  165. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/value.cpp +0 -0
  166. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/src/zip_stored.cpp +0 -0
  167. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/CMakeLists.txt +0 -0
  168. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_bytecode_compiler.cpp +0 -0
  169. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_cli.cpp +0 -0
  170. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_control_flow.cpp +0 -0
  171. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_csg_tree.cpp +0 -0
  172. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_debug_hooks.cpp +0 -0
  173. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_dxf_svg_import.cpp +0 -0
  174. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_eval_context.cpp +0 -0
  175. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_export.cpp +0 -0
  176. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_expr_eval.cpp +0 -0
  177. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_helpers.hpp +0 -0
  178. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_import_export.cpp +0 -0
  179. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_manifold_cache.cpp +0 -0
  180. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_multi_color_merge.cpp +0 -0
  181. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_profiling.cpp +0 -0
  182. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_python_bindings.py +0 -0
  183. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_scope_trail.cpp +0 -0
  184. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_scoping.cpp +0 -0
  185. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_surface.cpp +0 -0
  186. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_tail_calls.cpp +0 -0
  187. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_text.cpp +0 -0
  188. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_use.cpp +0 -0
  189. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_value.cpp +0 -0
  190. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_viewport_params.cpp +0 -0
  191. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tests/test_zip_stored.cpp +0 -0
  192. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tools/cli/CMakeLists.txt +0 -0
  193. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tools/cli/cli_lib.cpp +0 -0
  194. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tools/cli/cli_lib.hpp +0 -0
  195. {openscad_cpp_evaluator-0.13.9 → openscad_cpp_evaluator-0.14.0}/tools/cli/main.cpp +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 0.13.9
3
+ Version: 0.14.0
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 = "0.13.9"
7
+ version = "0.14.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -176,7 +176,23 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
176
176
 
177
177
  std::vector<ColoredBody> bodies3d, sections2d;
178
178
  for (const ColoredBody& c : split.foreground) {
179
- if (c.body) bodies3d.push_back(c);
179
+ // A body whose own Manifold::Status() isn't NoError (e.g.
180
+ // NonFiniteVertex, from a degenerate accumulated transform deep
181
+ // in an unrelated ancestor's positioning math -- found via a
182
+ // real user script, snappy-reprap's z_tower_assembly chain,
183
+ // where one already-invalid sub-part silently zeroed out an
184
+ // entire 65-operand union of otherwise-valid geometry) must
185
+ // never reach Manifold's own `+`/`-`/`^` operators: unlike a
186
+ // genuinely empty operand (0 triangles, NoError), Manifold
187
+ // propagates a non-NoError status through boolean ops onto the
188
+ // WHOLE result instead of treating it as a no-op contributor,
189
+ // so a single bad part silently discards every valid sibling.
190
+ // Real OpenSCAD's CGAL backend doesn't hit this at all here
191
+ // (more robust to the same input) -- dropping the one invalid
192
+ // operand and unioning everything else is the closest match to
193
+ // its own behavior available without new numerical-robustness
194
+ // work on Manifold's own boolean ops.
195
+ if (c.body && c.body->Status() == manifold::Manifold::Error::NoError) bodies3d.push_back(c);
180
196
  }
181
197
  for (const ColoredBody& c : split.foreground) {
182
198
  if (c.section) sections2d.push_back(c);
@@ -181,8 +181,19 @@ std::vector<ColoredBody> generateProjection(Evaluator&, const CSGParams& params,
181
181
  BuiltinWrapParams computeOffsetParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
182
182
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
183
183
 
184
- const Value rArg = getArg(args, std::nullopt, "r", Value{});
184
+ // `r` owns position 0 (offset(2) == offset(r=2)); delta/chamfer are
185
+ // named-only, matching the reference. `r` also wins outright when both
186
+ // are given -- the reference never even looks at delta in that case.
187
+ Value rArg = getArg(args, 0, "r", Value{});
185
188
  const Value deltaArg = getArg(args, std::nullopt, "delta", Value{});
189
+ // A bare offset() is offset(r=1), not a pass-through: the reference
190
+ // initialises delta to 1 with a round join and only ever overwrites it
191
+ // from an argument, so "neither given" lands on the same state "r=1"
192
+ // does. Verified against OpenSCAD 2022.08.22 -- offset() and offset(r=1)
193
+ // produce a byte-identical mesh for the same input.
194
+ if (std::holds_alternative<std::monostate>(rArg) && std::holds_alternative<std::monostate>(deltaArg)) {
195
+ rArg = Value{1.0};
196
+ }
186
197
  const bool chamfer = truthy(getArg(args, std::nullopt, "chamfer", Value{false}));
187
198
 
188
199
  CSGParams params;
@@ -279,7 +279,13 @@ Value builtinSearch(const CallArgs& args) {
279
279
  }
280
280
 
281
281
  Value builtinLookup(const CallArgs& args) {
282
- const double key = toDoubleLenient(getArg(args, 0, "key", Value{}));
282
+ // Guarded here rather than via scalarNumericArity() -- the second
283
+ // argument is a table, so the all-arguments-are-numbers rule can't
284
+ // apply. Without this, toDoubleLenient() turns a non-numeric key into
285
+ // 0.0 and the lookup silently returns the table's first value.
286
+ const Value keyArg = getArg(args, 0, "key", Value{});
287
+ if (!std::holds_alternative<double>(keyArg)) return Value{};
288
+ const double key = std::get<double>(keyArg);
283
289
  const Value tableArg = getArg(args, 1, "table", Value{});
284
290
  const ListPtr* tablePtr = std::get_if<ListPtr>(&tableArg);
285
291
  if (!tablePtr || !*tablePtr) return Value{};
@@ -359,11 +365,35 @@ Value builtinFontmetrics(Evaluator& ev, const CallArgs& args) {
359
365
  });
360
366
  }
361
367
 
362
- const std::unordered_set<std::string>& numericOnlyNames() {
363
- static const std::unordered_set<std::string> names = {
364
- "abs", "sign", "ceil", "floor", "round", "sqrt", "ln", "log", "exp", "sin", "cos", "tan",
365
- "asin", "acos", "atan", "atan2", "pow", "max", "min", "norm", "cross",
368
+ // Take only scalar numbers, mapped to how many leading positional
369
+ // arguments must each actually BE a number. Anything else there (undef, a
370
+ // string, a list, a bool, an object) is a type error yielding undef, and
371
+ // so is supplying fewer arguments than the arity -- matching the
372
+ // reference, which rejects the call outright rather than coercing.
373
+ //
374
+ // This has to be checked up front rather than left to each case below:
375
+ // toDoubleLenient() silently turns undef/string/list into 0.0, which
376
+ // would make `cos(misspelled_var)` quietly 1 instead of undef, and a
377
+ // typo'd variable then produces plausible-looking geometry instead of an
378
+ // obvious failure. Arity is deliberately a floor, not an exact match --
379
+ // the reference also rejects EXTRA arguments, but tightening that too
380
+ // risks breaking working library code for no correctness gain here.
381
+ const std::unordered_map<std::string, size_t>& scalarNumericArity() {
382
+ static const std::unordered_map<std::string, size_t> arity = {
383
+ {"abs", 1}, {"sign", 1}, {"ceil", 1}, {"floor", 1}, {"round", 1}, {"sqrt", 1},
384
+ {"ln", 1}, {"log", 1}, {"exp", 1}, {"sin", 1}, {"cos", 1}, {"tan", 1},
385
+ {"asin", 1}, {"acos", 1}, {"atan", 1}, {"atan2", 2}, {"pow", 2}, {"rands", 3},
366
386
  };
387
+ return arity;
388
+ }
389
+
390
+ // Numeric functions that legitimately take a LIST argument, so the
391
+ // all-arguments-must-be-numbers rule above can't apply -- each validates
392
+ // its own operands, and only needs the bool case caught here (Value's
393
+ // bool and double are distinct alternatives, but the reference rejects
394
+ // `max(true, 1)` as a type error rather than treating true as 1).
395
+ const std::unordered_set<std::string>& numericOnlyNames() {
396
+ static const std::unordered_set<std::string> names = {"max", "min", "norm", "cross"};
367
397
  return names;
368
398
  }
369
399
 
@@ -479,7 +509,13 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
479
509
  // elsewhere before reaching here) -- mirrors the old chain's fallthrough.
480
510
  if (idIt == ids.end()) return Value{};
481
511
 
482
- if (numericOnlyNames().count(name)) {
512
+ if (const auto arityIt = scalarNumericArity().find(name); arityIt != scalarNumericArity().end()) {
513
+ const std::vector<Value> positional = allPositional(args);
514
+ if (positional.size() < arityIt->second) return Value{};
515
+ for (size_t i = 0; i < arityIt->second; ++i) {
516
+ if (!std::holds_alternative<double>(positional[i])) return Value{};
517
+ }
518
+ } else if (numericOnlyNames().count(name)) {
483
519
  for (const Value& v : allPositional(args)) {
484
520
  if (isBoolOrListContainsBool(v)) return Value{};
485
521
  }
@@ -144,34 +144,38 @@ std::vector<ColoredBody> generateSphere(Evaluator& ev, const CSGParams& params,
144
144
  CSGParams resolveCylinder(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
145
145
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
146
146
  const double h = toDoubleLenient(getArg(args, 0, "h", Value{1.0}));
147
- Value rV = getArg(args, 1, "r", Value{});
148
- Value r1V = getArg(args, std::nullopt, "r1", Value{});
149
- Value r2V = getArg(args, std::nullopt, "r2", Value{});
147
+ // Positional order is (h, r1, r2, center); r/d/d1/d2 are named-only.
148
+ // NOTE r1/r2 -- not r -- own positions 1 and 2, so `cylinder(10, 5, 2)`
149
+ // is a cone, and `cylinder(10, 5)` is a cone tapering to the r2 default
150
+ // of 1 rather than a straight r=5 cylinder. Both match the reference.
151
+ Value rV = getArg(args, std::nullopt, "r", Value{});
150
152
  Value dV = getArg(args, std::nullopt, "d", Value{});
153
+ Value r1V = getArg(args, 1, "r1", Value{});
154
+ Value r2V = getArg(args, 2, "r2", Value{});
151
155
  Value d1V = getArg(args, std::nullopt, "d1", Value{});
152
156
  Value d2V = getArg(args, std::nullopt, "d2", Value{});
153
- const bool center = truthy(getArg(args, std::nullopt, "center", Value{false}));
154
-
155
- std::optional<double> r = isUndef(rV) ? std::nullopt : std::optional<double>(toDoubleLenient(rV));
156
- std::optional<double> r1 = isUndef(r1V) ? std::nullopt : std::optional<double>(toDoubleLenient(r1V));
157
- std::optional<double> r2 = isUndef(r2V) ? std::nullopt : std::optional<double>(toDoubleLenient(r2V));
158
-
159
- if (!isUndef(dV) && !r) r = toDoubleLenient(dV) / 2.0;
160
- if (!isUndef(d1V) && !r1) r1 = toDoubleLenient(d1V) / 2.0;
161
- if (!isUndef(d2V) && !r2) r2 = toDoubleLenient(d2V) / 2.0;
162
- if (r) {
163
- r1 = *r;
164
- r2 = *r;
165
- }
166
- if (!r1) r1 = 1.0;
167
- if (!r2) r2 = *r1;
168
-
169
- const int segs = fnSegmentsFromCtx(effCtx, std::max(*r1, *r2));
157
+ const bool center = truthy(getArg(args, 3, "center", Value{false}));
158
+
159
+ // Application order is significant and mirrors the reference's own
160
+ // sequence of independent `if (x.isNumber()) ...` assignments: r, then
161
+ // d (which overrides r outright rather than deferring to it), then the
162
+ // per-end r1/r2, then the per-end d1/d2. So `cylinder(h=10, d=8, r1=5)`
163
+ // is r1=5/r2=4, and `cylinder(h=10, r=5, r2=2)` is r1=5/r2=2. Both ends
164
+ // default to 1 independently -- r2 does NOT fall back to r1.
165
+ double r1 = 1.0, r2 = 1.0;
166
+ if (!isUndef(rV)) r1 = r2 = toDoubleLenient(rV);
167
+ if (!isUndef(dV)) r1 = r2 = toDoubleLenient(dV) / 2.0;
168
+ if (!isUndef(r1V)) r1 = toDoubleLenient(r1V);
169
+ if (!isUndef(r2V)) r2 = toDoubleLenient(r2V);
170
+ if (!isUndef(d1V)) r1 = toDoubleLenient(d1V) / 2.0;
171
+ if (!isUndef(d2V)) r2 = toDoubleLenient(d2V) / 2.0;
172
+
173
+ const int segs = fnSegmentsFromCtx(effCtx, std::max(r1, r2));
170
174
 
171
175
  CSGParams params;
172
176
  params["h"] = Value{h};
173
- params["r1"] = Value{*r1};
174
- params["r2"] = Value{*r2};
177
+ params["r1"] = Value{r1};
178
+ params["r2"] = Value{r2};
175
179
  params["center"] = Value{center};
176
180
  params["segs"] = Value{static_cast<double>(segs)};
177
181
  params["color"] = colorToValue(effCtx.color);
@@ -3,6 +3,7 @@
3
3
  #include "openscad_cpp_evaluator/call_args.hpp"
4
4
  #include "openscad_cpp_evaluator/evaluator.hpp"
5
5
 
6
+ #include <array>
6
7
  #include <cmath>
7
8
  #include <numbers>
8
9
 
@@ -32,6 +33,21 @@ manifold::vec2 toVec2(const Value& v, double defaultX = 0.0, double defaultY = 0
32
33
  return manifold::vec2(defaultX, defaultY);
33
34
  }
34
35
 
36
+ // resize()'s `auto` argument: a bare bool applies to all three axes, a
37
+ // list applies element-wise (missing elements are false), anything else
38
+ // (including undef) disables auto entirely. Mirrors the reference's
39
+ // Value::toBool()/vector handling.
40
+ std::array<bool, 3> toAutoAxes(const Value& v) {
41
+ if (const bool* b = std::get_if<bool>(&v)) return {*b, *b, *b};
42
+ if (const ListPtr* l = std::get_if<ListPtr>(&v); l && *l) {
43
+ const auto& items = (*l)->items;
44
+ std::array<bool, 3> out{false, false, false};
45
+ for (size_t i = 0; i < 3 && i < items.size(); ++i) out[i] = truthy(items[i]);
46
+ return out;
47
+ }
48
+ return {false, false, false};
49
+ }
50
+
35
51
  // Rodrigues' rotation formula as a 3x4 transform (rotate(angle, axis) --
36
52
  // the axis form, as opposed to rotate([x,y,z]) Euler angles, which uses
37
53
  // Manifold's own Rotate() directly and never needs this). Mirrors
@@ -117,13 +133,35 @@ manifold::Manifold applyTransform3d(const std::string& name, const CallArgs& arg
117
133
  return body.Mirror(toVec3(getArg(args, 0, "v", Value{}), 1.0, 0.0, 0.0));
118
134
  }
119
135
  if (name == "resize") {
120
- const manifold::vec3 newSize = toVec3(getArg(args, 0, "newsize", Value{}));
136
+ const manifold::vec3 newSizeV = toVec3(getArg(args, 0, "newsize", Value{}));
137
+ std::array<bool, 3> autoAxes = toAutoAxes(getArg(args, 1, "auto", Value{}));
121
138
  const manifold::Box bbox = body.BoundingBox();
122
- const manifold::vec3 span = bbox.max - bbox.min;
123
- const double sx = (newSize.x != 0.0 && span.x != 0.0) ? newSize.x / span.x : 1.0;
124
- const double sy = (newSize.y != 0.0 && span.y != 0.0) ? newSize.y / span.y : 1.0;
125
- const double sz = (newSize.z != 0.0 && span.z != 0.0) ? newSize.z / span.z : 1.0;
126
- return body.Scale(manifold::vec3(sx, sy, sz));
139
+ const manifold::vec3 spanV = bbox.max - bbox.min;
140
+ const double ns[3] = {newSizeV.x, newSizeV.y, newSizeV.z};
141
+ const double span[3] = {spanV.x, spanV.y, spanV.z};
142
+
143
+ // An `auto` axis takes the scale factor of whichever axis asked for
144
+ // the LARGEST new size -- not its own (it has none) and not the
145
+ // first specified one. Mirrors the reference's newsizemax_index:
146
+ // ties keep the earlier axis, and an all-zero newsize leaves the
147
+ // factor at 1 so `resize([0,0,0], true)` is a no-op.
148
+ int maxIdx = 0;
149
+ for (int i = 1; i < 3; ++i) {
150
+ if (ns[i] > ns[maxIdx]) maxIdx = i;
151
+ }
152
+ const double autoScale = (ns[maxIdx] != 0.0 && span[maxIdx] != 0.0) ? ns[maxIdx] / span[maxIdx] : 1.0;
153
+
154
+ double scale[3];
155
+ for (int i = 0; i < 3; ++i) {
156
+ if (ns[i] != 0.0 && span[i] != 0.0) {
157
+ scale[i] = ns[i] / span[i];
158
+ } else if (ns[i] == 0.0 && autoAxes[static_cast<size_t>(i)]) {
159
+ scale[i] = autoScale;
160
+ } else {
161
+ scale[i] = 1.0;
162
+ }
163
+ }
164
+ return body.Scale(manifold::vec3(scale[0], scale[1], scale[2]));
127
165
  }
128
166
  if (name == "multmatrix") {
129
167
  Value m = getArg(args, 0, "m", Value{});
@@ -87,6 +87,25 @@ TEST(Difference, EmptySubtractorLeavesBaseUnchanged) {
87
87
  EXPECT_NEAR(e.bodies[0].body->Volume(), 64.0, 1e-6);
88
88
  }
89
89
 
90
+ TEST(Union, OneInvalidOperandDoesNotDiscardValidSiblings) {
91
+ // Regression: Manifold's own `+` propagates a non-NoError Status()
92
+ // (e.g. NonFiniteVertex, from a degenerate NaN-vertex polyhedron here --
93
+ // in the wild, from an accumulated transform deep in an unrelated
94
+ // ancestor's own positioning math) onto the WHOLE combined result
95
+ // instead of treating the bad operand as a no-op contributor. Before
96
+ // filtering invalid operands out in generateCsg, a single bad part
97
+ // anywhere in a many-operand union() silently discarded every valid
98
+ // sibling too -- found via a real user project (snappy-reprap) where
99
+ // this zeroed out an entire ~65-part extruder assembly with no warning.
100
+ Evaluated e = evalSrc("union() {"
101
+ " polyhedron(points=[[0,0,0],[2,0,0],[0,2,0],[0/0,0,2]],"
102
+ " faces=[[0,1,2],[0,3,1],[0,2,3],[1,3,2]]);"
103
+ " translate([10,0,0]) cube(2);"
104
+ "}");
105
+ ASSERT_EQ(e.bodies.size(), 1u);
106
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 8.0, 1e-6); // just the valid cube -- the NaN polyhedron is dropped, not fatal
107
+ }
108
+
90
109
  // -- 2D-native boolean CSG (union/difference/intersection applied directly
91
110
  // to 2D children, not mediated through linear_extrude()) -----------------
92
111
 
@@ -151,11 +151,27 @@ TEST(Offset, NegativeDeltaShrinksTheSquare) {
151
151
  EXPECT_NEAR(e.bodies[0].section->Area(), 2.0 * 2.0, 1e-6);
152
152
  }
153
153
 
154
- TEST(Offset, NoRadiusOrDeltaPassesThroughFirstChild) {
155
- Evaluated e = evalSrc("offset() square(4, center=true);");
156
- ASSERT_EQ(e.bodies.size(), 1u);
157
- ASSERT_TRUE(e.bodies[0].section.has_value());
158
- EXPECT_NEAR(e.bodies[0].section->Area(), 16.0, 1e-6);
154
+ // `r` owns position 0, so offset(2) == offset(r=2). It used to be
155
+ // named-only, making a bare offset(2) a silent no-op.
156
+ TEST(Offset, FirstPositionalArgumentIsRadius) {
157
+ Evaluated positional = evalSrc("offset(1, $fn=64) square(4, center=true);");
158
+ Evaluated named = evalSrc("offset(r=1, $fn=64) square(4, center=true);");
159
+ ASSERT_TRUE(positional.bodies[0].section.has_value());
160
+ EXPECT_NEAR(positional.bodies[0].section->Area(), named.bodies[0].section->Area(), 1e-9);
161
+ EXPECT_GT(positional.bodies[0].section->Area(), 16.0);
162
+ }
163
+
164
+ // A bare offset() is offset(r=1), NOT a pass-through -- the reference
165
+ // initialises its delta to 1 with a round join and only overwrites it from
166
+ // an argument. Verified against OpenSCAD 2022.08.22, where offset() and
167
+ // offset(r=1) produce identical geometry.
168
+ TEST(Offset, NoRadiusOrDeltaDefaultsToRadiusOne) {
169
+ Evaluated bare = evalSrc("offset() square(4, center=true);");
170
+ Evaluated explicitR = evalSrc("offset(r=1) square(4, center=true);");
171
+ ASSERT_EQ(bare.bodies.size(), 1u);
172
+ ASSERT_TRUE(bare.bodies[0].section.has_value());
173
+ EXPECT_NEAR(bare.bodies[0].section->Area(), explicitR.bodies[0].section->Area(), 1e-9);
174
+ EXPECT_GT(bare.bodies[0].section->Area(), 16.0); // grew, rather than passing through
159
175
  }
160
176
 
161
177
  // -- roof -----------------------------------------------------------------
@@ -96,6 +96,56 @@ TEST(MathBuiltins, BoolArgumentIsUndefForNumericOnlyFns) {
96
96
  EXPECT_TRUE(isUndef(evalSrc("norm([true, 0])", ev)));
97
97
  }
98
98
 
99
+ // Every scalar-numeric builtin rejects a non-number argument outright
100
+ // rather than coercing it through toDoubleLenient()'s 0.0. Each expected
101
+ // value below was confirmed against real OpenSCAD 2022.08.22, which warns
102
+ // ("parameter could not be converted") and yields undef. Before this,
103
+ // `cos(undef)` was 1, `ln(undef)` was -inf and `acos([1,2])` was 90 --
104
+ // a misspelled variable produced plausible geometry instead of failing.
105
+ TEST(MathBuiltins, NonNumericArgumentIsUndefForScalarNumericFns) {
106
+ Evaluator ev;
107
+ for (const char* fn : {"abs", "sign", "ceil", "floor", "round", "sqrt", "ln", "log", "exp",
108
+ "sin", "cos", "tan", "asin", "acos", "atan"}) {
109
+ const std::string f(fn);
110
+ EXPECT_TRUE(isUndef(evalSrc(f + "(undef)", ev))) << f << "(undef)";
111
+ EXPECT_TRUE(isUndef(evalSrc(f + "(\"hi\")", ev))) << f << "(\"hi\")";
112
+ EXPECT_TRUE(isUndef(evalSrc(f + "([1,2])", ev))) << f << "([1,2])";
113
+ EXPECT_TRUE(isUndef(evalSrc(f + "(true)", ev))) << f << "(true)";
114
+ }
115
+ EXPECT_TRUE(isUndef(evalSrc("atan2(1, undef)", ev)));
116
+ EXPECT_TRUE(isUndef(evalSrc("atan2(undef, 1)", ev)));
117
+ EXPECT_TRUE(isUndef(evalSrc("pow(2, undef)", ev)));
118
+ EXPECT_TRUE(isUndef(evalSrc("pow(undef, 2)", ev)));
119
+ EXPECT_TRUE(isUndef(evalSrc("rands(undef, 1, 3, 42)", ev)));
120
+ EXPECT_TRUE(isUndef(evalSrc("rands(0, 1, undef, 42)", ev)));
121
+ // lookup() can't use the same rule (its second argument is a table),
122
+ // so it guards its key separately -- without that, a non-numeric key
123
+ // became 0.0 and silently returned the table's first value.
124
+ EXPECT_TRUE(isUndef(evalSrc("lookup(undef, [[0,1],[10,5]])", ev)));
125
+ EXPECT_TRUE(isUndef(evalSrc("lookup(5, undef)", ev)));
126
+ EXPECT_TRUE(isUndef(evalSrc("lookup(5, \"hi\")", ev)));
127
+ }
128
+
129
+ // Too few arguments is a type error too, not a call against an implicit 0.
130
+ TEST(MathBuiltins, MissingArgumentIsUndefForScalarNumericFns) {
131
+ Evaluator ev;
132
+ EXPECT_TRUE(isUndef(evalSrc("abs()", ev)));
133
+ EXPECT_TRUE(isUndef(evalSrc("pow(2)", ev)));
134
+ EXPECT_TRUE(isUndef(evalSrc("atan2(1)", ev)));
135
+ EXPECT_TRUE(isUndef(evalSrc("rands(0, 1)", ev)));
136
+ }
137
+
138
+ // The guard must not disturb the functions that legitimately take lists.
139
+ TEST(MathBuiltins, ListAcceptingNumericFnsStillWork) {
140
+ Evaluator ev;
141
+ EXPECT_DOUBLE_EQ(asNum(evalSrc("max([4,9,2])", ev)), 9.0);
142
+ EXPECT_DOUBLE_EQ(asNum(evalSrc("min([4,9,2])", ev)), 2.0);
143
+ EXPECT_DOUBLE_EQ(asNum(evalSrc("norm([3,4])", ev)), 5.0);
144
+ EXPECT_DOUBLE_EQ(asNum(evalSrc("lookup(5, [[0,5],[10,8],[20,2]])", ev)), 6.5);
145
+ EXPECT_TRUE(isUndef(evalSrc("max(undef, 1)", ev)));
146
+ EXPECT_TRUE(isUndef(evalSrc("norm(undef)", ev)));
147
+ }
148
+
99
149
  TEST(MathBuiltins, Rands) {
100
150
  Evaluator ev;
101
151
  Value v = evalSrc("rands(1, 2, 5, 42)", ev);
@@ -78,10 +78,47 @@ TEST(Cylinder, NoRadiusArgumentAtAllDefaultsToRadiusOne) {
78
78
  EXPECT_NEAR(withNoArgs.bodies[0].body->Volume(), withExplicitR.bodies[0].body->Volume(), 1e-9);
79
79
  }
80
80
 
81
- TEST(Cylinder, R1OnlyDefaultsR2ToR1) {
81
+ // r2 does NOT fall back to r1 -- each end defaults to 1 on its own, so
82
+ // `cylinder(h, r1=3)` is a cone tapering 3 -> 1, not a straight r=3
83
+ // cylinder. Verified against OpenSCAD 2022.08.22 (rbot 3, rtop 1).
84
+ TEST(Cylinder, R1OnlyLeavesR2AtItsOwnDefaultOfOne) {
82
85
  Evaluated withR1Only = evalSrc("cylinder(h=5, r1=3, $fn=16);");
83
- Evaluated withBoth = evalSrc("cylinder(h=5, r1=3, r2=3, $fn=16);");
84
- EXPECT_NEAR(withR1Only.bodies[0].body->Volume(), withBoth.bodies[0].body->Volume(), 1e-9);
86
+ Evaluated asCone = evalSrc("cylinder(h=5, r1=3, r2=1, $fn=16);");
87
+ Evaluated asStraight = evalSrc("cylinder(h=5, r1=3, r2=3, $fn=16);");
88
+ EXPECT_NEAR(withR1Only.bodies[0].body->Volume(), asCone.bodies[0].body->Volume(), 1e-9);
89
+ EXPECT_GT(std::abs(withR1Only.bodies[0].body->Volume() - asStraight.bodies[0].body->Volume()), 1.0);
90
+ }
91
+
92
+ // Positional order is (h, r1, r2, center) -- position 1 is r1, not r, so a
93
+ // third positional argument really does make a cone and a fourth really
94
+ // does centre it. This is the arg-order bug that made `cylinder(10, 5, 2)`
95
+ // silently render as a straight r=5 cylinder.
96
+ TEST(Cylinder, PositionalArgsAreHR1R2Center) {
97
+ Evaluated positional = evalSrc("cylinder(10, 5, 2, true, $fn=16);");
98
+ Evaluated named = evalSrc("cylinder(h=10, r1=5, r2=2, center=true, $fn=16);");
99
+ EXPECT_NEAR(positional.bodies[0].body->Volume(), named.bodies[0].body->Volume(), 1e-9);
100
+ const manifold::Box bb = positional.bodies[0].body->BoundingBox();
101
+ EXPECT_NEAR(bb.min.z, -5.0, 1e-9);
102
+ EXPECT_NEAR(bb.max.z, 5.0, 1e-9);
103
+ }
104
+
105
+ // Application order: r, then d (which overrides r outright), then r1/r2,
106
+ // then d1/d2. Each verified against OpenSCAD 2022.08.22.
107
+ TEST(Cylinder, DiameterAndRadiusOverrideOrderMatchesReference) {
108
+ // d=8 sets both ends to 4; the explicit r1=5 then overrides the bottom.
109
+ Evaluated dThenR1 = evalSrc("cylinder(h=10, d=8, r1=5, $fn=16);");
110
+ Evaluated expected1 = evalSrc("cylinder(h=10, r1=5, r2=4, $fn=16);");
111
+ EXPECT_NEAR(dThenR1.bodies[0].body->Volume(), expected1.bodies[0].body->Volume(), 1e-9);
112
+
113
+ // r=5 sets both ends; the explicit r2=2 then overrides the top.
114
+ Evaluated rThenR2 = evalSrc("cylinder(h=10, r=5, r2=2, $fn=16);");
115
+ Evaluated expected2 = evalSrc("cylinder(h=10, r1=5, r2=2, $fn=16);");
116
+ EXPECT_NEAR(rThenR2.bodies[0].body->Volume(), expected2.bodies[0].body->Volume(), 1e-9);
117
+
118
+ // d wins over r outright rather than deferring to it.
119
+ Evaluated dWinsOverR = evalSrc("cylinder(h=10, r=5, d=8, $fn=16);");
120
+ Evaluated expected3 = evalSrc("cylinder(h=10, r=4, $fn=16);");
121
+ EXPECT_NEAR(dWinsOverR.bodies[0].body->Volume(), expected3.bodies[0].body->Volume(), 1e-9);
85
122
  }
86
123
 
87
124
  // -- polyhedron -----------------------------------------------------------
@@ -150,6 +150,56 @@ TEST(Resize, ZeroComponentLeavesThatAxisUnscaled) {
150
150
  EXPECT_NEAR(bbox.max.z, 1.0, 1e-9); // unscaled
151
151
  }
152
152
 
153
+ // `auto` scales the zero-valued axes by the factor of whichever axis asked
154
+ // for the LARGEST new size. Every expectation below was confirmed against
155
+ // real OpenSCAD 2022.08.22; before this, `auto` was accepted and ignored.
156
+ TEST(Resize, AutoScalesZeroAxesByTheLargestRequestedAxisFactor) {
157
+ // Bare `true` covers all three axes: 20/5 = 4x on X, so Y and Z too.
158
+ Evaluated allAxes = evalSrc("resize([20,0,0], true) cube(5);");
159
+ manifold::Box bbox = allAxes.bodies[0].body->BoundingBox();
160
+ EXPECT_NEAR(bbox.max.x, 20.0, 1e-9);
161
+ EXPECT_NEAR(bbox.max.y, 20.0, 1e-9);
162
+ EXPECT_NEAR(bbox.max.z, 20.0, 1e-9);
163
+
164
+ // Named form is identical to the positional one.
165
+ Evaluated named = evalSrc("resize([20,0,0], auto=true) cube(5);");
166
+ manifold::Box nbox = named.bodies[0].body->BoundingBox();
167
+ EXPECT_NEAR(nbox.max.y, 20.0, 1e-9);
168
+
169
+ // A per-axis list only auto-scales the axes it names.
170
+ Evaluated perAxis = evalSrc("resize([20,0,0], [false,true,false]) cube(5);");
171
+ manifold::Box pbox = perAxis.bodies[0].body->BoundingBox();
172
+ EXPECT_NEAR(pbox.max.x, 20.0, 1e-9);
173
+ EXPECT_NEAR(pbox.max.y, 20.0, 1e-9);
174
+ EXPECT_NEAR(pbox.max.z, 5.0, 1e-9); // not auto -- left alone
175
+ }
176
+
177
+ TEST(Resize, AutoFactorComesFromTheLargestNewsizeNotTheFirst) {
178
+ // span (5,10,20); newsize (20,30,0). The largest requested size is 30
179
+ // on Y, so the auto Z axis scales by 30/10 = 3 -> 60, NOT by X's 20/5.
180
+ Evaluated e = evalSrc("resize([20,30,0], true) cube([5,10,20]);");
181
+ manifold::Box bbox = e.bodies[0].body->BoundingBox();
182
+ EXPECT_NEAR(bbox.max.x, 20.0, 1e-9);
183
+ EXPECT_NEAR(bbox.max.y, 30.0, 1e-9);
184
+ EXPECT_NEAR(bbox.max.z, 60.0, 1e-9);
185
+ }
186
+
187
+ TEST(Resize, AutoWithNoRequestedSizeIsANoOp) {
188
+ Evaluated e = evalSrc("resize([0,0,0], true) cube(5);");
189
+ manifold::Box bbox = e.bodies[0].body->BoundingBox();
190
+ EXPECT_NEAR(bbox.max.x, 5.0, 1e-9);
191
+ EXPECT_NEAR(bbox.max.y, 5.0, 1e-9);
192
+ EXPECT_NEAR(bbox.max.z, 5.0, 1e-9);
193
+ }
194
+
195
+ TEST(Resize, AutoFalseLeavesZeroAxesUnscaled) {
196
+ Evaluated e = evalSrc("resize([20,0,0], false) cube(5);");
197
+ manifold::Box bbox = e.bodies[0].body->BoundingBox();
198
+ EXPECT_NEAR(bbox.max.x, 20.0, 1e-9);
199
+ EXPECT_NEAR(bbox.max.y, 5.0, 1e-9);
200
+ EXPECT_NEAR(bbox.max.z, 5.0, 1e-9);
201
+ }
202
+
153
203
  // -- 2D transform dispatch (a transform's child is a CrossSection, not a
154
204
  // Manifold -- resolved per-body at generate time) ---------------------------
155
205
 
@@ -0,0 +1,65 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = ">=3.12"
4
+
5
+ [[package]]
6
+ name = "numpy"
7
+ version = "2.5.1"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/22/fd/89965aa4ac08c74998539fcbf24fa3540f3e15237fbeb6bcf9c908f4aade/numpy-2.5.1.tar.gz", hash = "sha256:a48a113e6afea91f5608793bafa7ef2ad481fefbda87ec5069f483de61cb9fa3", size = 20755553, upload-time = "2026-07-04T17:08:00.933Z" }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/62/7b/14687aa674250e5e546f616f486b0d56d3631cd5b2415739141ce40bdcea/numpy-2.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c889b56fe48b1018f764b0eec8df59ab654e9148aa91faa12596043500de277", size = 16801574, upload-time = "2026-07-04T17:06:12.423Z" },
12
+ { url = "https://files.pythonhosted.org/packages/e1/19/cc5bb2a3f2913d27d6dbb2c78d25921fabaedc6741d4a5a615a11f3c5bf3/numpy-2.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab451b59c5643c570974c43aef780703ef1d3b4965d2be07afd530615a9358d1", size = 11772250, upload-time = "2026-07-04T17:06:15.726Z" },
13
+ { url = "https://files.pythonhosted.org/packages/42/77/fdf34a71dd30f54979b18603bee915e0aaf825b07afe79acd60b04b691e2/numpy-2.5.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:78798bd5b9ad744056af8efa90e3b9ddaa53272a0848a483084a1cc0a13b2dc0", size = 5331516, upload-time = "2026-07-04T17:06:17.913Z" },
14
+ { url = "https://files.pythonhosted.org/packages/ce/e2/eb7efa015b4cce41e2517bf182a7fce0d7d5b9d9ed76a29bfa0f4fe4505c/numpy-2.5.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:2ae0ca40bcb22d6ba59c1dfd5446f49940b0f2d821fde133f10dda11f816b84e", size = 6664863, upload-time = "2026-07-04T17:06:20.02Z" },
15
+ { url = "https://files.pythonhosted.org/packages/a9/4b/a2b32dd94ee9ffbeecb28152240042a3949db33b1c834d44090b80e1b3b8/numpy-2.5.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61ac47e772e6b8ea489e1d2f441a34c5c3ac17327e7ce294cbdf535795ad4e75", size = 15167977, upload-time = "2026-07-04T17:06:21.621Z" },
16
+ { url = "https://files.pythonhosted.org/packages/b8/a9/6e73d68500f80773f65f0654ea932019d6694329a0eb0ed0533de38df376/numpy-2.5.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:59fda5e192b570217ec2580c96f00e9a7e12ef6866a900eb089b62c1a32545ca", size = 16672469, upload-time = "2026-07-04T17:06:24.064Z" },
17
+ { url = "https://files.pythonhosted.org/packages/24/7d/ad3e59015135f5261c95fd4cafeff159c955febd83a99a1d9250c4233815/numpy-2.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f7119ebff1a9829e9f431a4f9d28e703023bb6b9fe7c8f724467dbfc27c94ab3", size = 16527531, upload-time = "2026-07-04T17:06:26.69Z" },
18
+ { url = "https://files.pythonhosted.org/packages/83/d0/a39b2fbcde9cb17a1dac678f254b33a6336298af9df338824c685425d5e8/numpy-2.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e824c2acf8862052246be5a44c15da1777940c60d010dd2aab897824d9c430f9", size = 18431940, upload-time = "2026-07-04T17:06:29.521Z" },
19
+ { url = "https://files.pythonhosted.org/packages/04/12/cff070947791c1ed425ff76413189adbdc2fbe215eba7ce7fa454a03c7f8/numpy-2.5.1-cp312-cp312-win32.whl", hash = "sha256:08d60c810432eb83360958dea0999ac4cfb94531ea8efcbf0b7f277c2068aeb2", size = 6066764, upload-time = "2026-07-04T17:06:32.571Z" },
20
+ { url = "https://files.pythonhosted.org/packages/65/66/53f31807a48a750f9d748da273bc3fcedd12b27ff1f3e373bfec55ef2dc0/numpy-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:f7d60026c0bdb1380e83bfa7a0419c4577ee4b9a08880afcb6dadeb74c649fa2", size = 12430966, upload-time = "2026-07-04T17:06:34.926Z" },
21
+ { url = "https://files.pythonhosted.org/packages/2b/2a/d1a88066b1c14186f5d3c0d18c94f17b064511982bab0578d49ee9d43c29/numpy-2.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:17a25e09640602e10bc8de0e6fa2b3fd68eedd84ba6d7842dc8f32f9ab87bd0b", size = 10350488, upload-time = "2026-07-04T17:06:37.785Z" },
22
+ { url = "https://files.pythonhosted.org/packages/eb/07/ec2a3f0c91761581d4b7104a740791800025983f9a4dc4e73f91a99aeac4/numpy-2.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0bfebd8695f9863592fe744be833a258120b14a9f39da255e8aa8fade2c0ddd1", size = 16796419, upload-time = "2026-07-04T17:06:40.37Z" },
23
+ { url = "https://files.pythonhosted.org/packages/ab/ab/ddb499fc4f8780354395face5b65c7fd107bcd6e1d667a5f07d046956f6f/numpy-2.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30b44a6b53a7ae63c54c089a8726e5563ed302716c5b7ccc85afade40b0e7ff6", size = 11765832, upload-time = "2026-07-04T17:06:42.768Z" },
24
+ { url = "https://files.pythonhosted.org/packages/88/b3/3c28c558a09fc72100c646dac6d2fce8e834c471b0edca01a29996706117/numpy-2.5.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6165343f81b56ef8f514f396989e529b61d9dc709b99421b07e9f3e698e2287d", size = 5325143, upload-time = "2026-07-04T17:06:45.466Z" },
25
+ { url = "https://files.pythonhosted.org/packages/5e/0e/ce19b985bb15c596f4f05954e76cccc77c845083b3b8f938a6c68e523128/numpy-2.5.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4939237038ada79308dda3204ac6462df056b5672b2e25db1149cf873668b3e1", size = 6659749, upload-time = "2026-07-04T17:06:47.288Z" },
26
+ { url = "https://files.pythonhosted.org/packages/2e/20/1ee6614d64332a1bba6411f38e68cb79eec1b2459e20a623777c5c5492a2/numpy-2.5.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c6759f538fb912fc46de0a6b1758ccf7b57bc7c7ebebc23974fdac3de8db0cd", size = 15164716, upload-time = "2026-07-04T17:06:49.494Z" },
27
+ { url = "https://files.pythonhosted.org/packages/ed/a7/2bcd3fdbb87804755c35b729bf8709d62025c5f4cfd7d5b2415997097515/numpy-2.5.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9726558e8db4a5bf7929a70ae50f63abda4daf0efe810e3bfbab95976f75fc1a", size = 16661440, upload-time = "2026-07-04T17:06:52.061Z" },
28
+ { url = "https://files.pythonhosted.org/packages/fc/d7/a41e3310c886fe457d36e670bbf24fae411aca8a7b6ad92a32afd924077c/numpy-2.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3935f3b419b244a02732676fa5317a9193cc596a4c0646db07e5b421229ac9f7", size = 16526305, upload-time = "2026-07-04T17:06:54.605Z" },
29
+ { url = "https://files.pythonhosted.org/packages/53/75/4333a9a707c1edd3a4e1a0c58eca52c0f31e55089fa80db02b5565b24df7/numpy-2.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc932a65ded7ce9013d120845a2514dcccb1a67bfc8deb8d37633762951904a6", size = 18423008, upload-time = "2026-07-04T17:06:57.54Z" },
30
+ { url = "https://files.pythonhosted.org/packages/ee/90/e314a32b1c11a2ffe818ddad3a57b50b4b6e1b6c487192eb50cdef0415d0/numpy-2.5.1-cp313-cp313-win32.whl", hash = "sha256:4b4ff1608417eb7a59da7b967bbb798cacfe071d2caf526a24281cd562072ed9", size = 6063885, upload-time = "2026-07-04T17:07:00.14Z" },
31
+ { url = "https://files.pythonhosted.org/packages/10/70/800b3fca480af32df9e8ea9f3d4a0c8feb4b32d7f195d174eabbda4829ad/numpy-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:6c3fe51bc6a16453d452997053454f309e8e0ed7b42d6b361ce4ac8c32913d74", size = 12425674, upload-time = "2026-07-04T17:07:02.387Z" },
32
+ { url = "https://files.pythonhosted.org/packages/8b/0b/196350c122f50f6ca56846f2d71efd5e0d24b7b2e07355e019b2e2c7a11e/numpy-2.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f7feb014281029e628ba2d5a007407443b06e418b6fe451d1e2adcbc8eba0107", size = 10350256, upload-time = "2026-07-04T17:07:04.878Z" },
33
+ { url = "https://files.pythonhosted.org/packages/db/f4/731b6085a83faf6ca843394cbd5e217280c214399f7e8b21b9f552af0ae2/numpy-2.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7c786fe9a5bbe360022e584c5a34cf6b54265c71bd7ec8ac3d8fec38968071f8", size = 16795063, upload-time = "2026-07-04T17:07:07.374Z" },
34
+ { url = "https://files.pythonhosted.org/packages/bf/64/0e215f2048dd11a55bb989ed41b3585ef57452404e638d703a211a3e4157/numpy-2.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32985c896d897419ef8da6917872d80b78ad0ea26d85b23245c7366ffde76d75", size = 11776652, upload-time = "2026-07-04T17:07:09.907Z" },
35
+ { url = "https://files.pythonhosted.org/packages/b5/59/2b844c7a6e9deff69b404a66221e1542937734f65d5e6e39411876053862/numpy-2.5.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:efd736408cc97c79b9e6917338dfc8f06013b2274f992e96b1d9a81a71e2a2c2", size = 5335944, upload-time = "2026-07-04T17:07:12.227Z" },
36
+ { url = "https://files.pythonhosted.org/packages/86/51/9bf7cb2cabcebc9e017e4ec7e6322b378317a542c08b4cb68479c1efc716/numpy-2.5.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:ab84dc6b074fa881cae55bea94cc4f68e285181ba7f32497bf7dee6b1496165b", size = 6656266, upload-time = "2026-07-04T17:07:14.368Z" },
37
+ { url = "https://files.pythonhosted.org/packages/83/3e/fb7615b211b82a32f44d5180a6d421b61f84d4fadd578b48ba4ac34e189f/numpy-2.5.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caf3e317d33d60c37986b452613f4ab51246d0691350c03d0cb4a898627f4a95", size = 15179720, upload-time = "2026-07-04T17:07:16.272Z" },
38
+ { url = "https://files.pythonhosted.org/packages/41/5f/0f992cb24560673496c5d68de61913b57166ce530ffda07c1f280e0cc464/numpy-2.5.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54ad769f17bc2d833b620851989f62054fb9ab93c969d9e1dc3c8e3d56beea21", size = 16664835, upload-time = "2026-07-04T17:07:19.021Z" },
39
+ { url = "https://files.pythonhosted.org/packages/a2/2f/97d6475ee91afe2587797d09446f9d3e475ad4cb681662d824809327b75a/numpy-2.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c12afb53450fa976d4c681c50a7423729a4c51c0465ed9f32b8a9cabbc472373", size = 16539135, upload-time = "2026-07-04T17:07:22.015Z" },
40
+ { url = "https://files.pythonhosted.org/packages/c4/5b/4db81e4ba0be7e2776b1de68c82aa862c7f8ec27e1b4927d4ae075e20678/numpy-2.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e8c11c405efc5ff6816d5983c96cdfa215bab3428961243af3ff59b228490438", size = 18426684, upload-time = "2026-07-04T17:07:24.941Z" },
41
+ { url = "https://files.pythonhosted.org/packages/1f/64/c0ba2d90724d450279a7df8f32057241070250a26a7e2b5337d77347f481/numpy-2.5.1-cp314-cp314-win32.whl", hash = "sha256:f2479a47f8d5932d1718168a681ad6e536a9df484c83cfcf9de365e164537ace", size = 6116103, upload-time = "2026-07-04T17:07:27.622Z" },
42
+ { url = "https://files.pythonhosted.org/packages/c1/1a/837f9ed7405adcd7a40538792eb169eddd8fa5630c16a1ef49dae71a30f4/numpy-2.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:24d0eb82c0541d3415a33425db64ae439dffccd7b4dbcb30e7c35120205c506a", size = 12562177, upload-time = "2026-07-04T17:07:29.887Z" },
43
+ { url = "https://files.pythonhosted.org/packages/22/ed/49707938b6dd0a78a9178dd93227dc89e4c11af47f5c798d70366e8d0483/numpy-2.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:5a4c988b38d261deeeaad9954e3deb091ad905c94e8bb6708654ef1d97f286b0", size = 10627739, upload-time = "2026-07-04T17:07:32.568Z" },
44
+ { url = "https://files.pythonhosted.org/packages/a6/c7/bb4b882cfe7f299cbc8b66e42e7dd78cf9d14e40f9469fc5e3db7e15b3bd/numpy-2.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a33276be12fa045805f477f22482088b66bb758ffbe89a9d21457de863a32e22", size = 11894709, upload-time = "2026-07-04T17:07:34.941Z" },
45
+ { url = "https://files.pythonhosted.org/packages/40/3f/5af7f4a7f6224aef48017aa82bb6174c7a659d724be0c75017b7e64a55b4/numpy-2.5.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f089d7b00756190aacf1f5d34bdf38c3c430ac82b4f868f8cede73380460fce7", size = 5453810, upload-time = "2026-07-04T17:07:37.495Z" },
46
+ { url = "https://files.pythonhosted.org/packages/20/c9/3474309bc94d634d3f9c3eddf03250ecb8c22cd948ef16fef69a77cc5d7b/numpy-2.5.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:09e9bfd8d2cf479c7d174804fb3811c53a8e9f20a37444008606b57d6b7a826d", size = 6761189, upload-time = "2026-07-04T17:07:39.563Z" },
47
+ { url = "https://files.pythonhosted.org/packages/90/8a/558ae39fdd55d7e7f7fef9a84a6e964ac6b23edbd2a07e52bb084500507d/numpy-2.5.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e68d8dd1e7eba712948f2053a29ec86917bc70ba1358df869d9f06649ef9cf09", size = 15225039, upload-time = "2026-07-04T17:07:41.682Z" },
48
+ { url = "https://files.pythonhosted.org/packages/63/27/ca7392b2d030277bdf0273e7d23255b3ee57d57a7c170a6f4fb3981e1e5d/numpy-2.5.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99d5095fa265a0c4152e7bb12759e14381ef5496152f1ce58f44bdf55c44beb4", size = 16701306, upload-time = "2026-07-04T17:07:44.611Z" },
49
+ { url = "https://files.pythonhosted.org/packages/02/42/03d53ae7996c44d4374a8262e9dc41671fd56cbb98f7d47ef85cf5da4c6b/numpy-2.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ab87a91b3cc3382b8956095bd8f95e00cf679bb81554339be1a2ba404a1473c1", size = 16589955, upload-time = "2026-07-04T17:07:47.694Z" },
50
+ { url = "https://files.pythonhosted.org/packages/7b/15/6c1784ae469640e65db111e9a34b3d0f14d91e8a38b9ce34810ced370dbb/numpy-2.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:224ca51130ef7da85bea2191625181cb4f337f9cb64b471f10c1a12aa8b60077", size = 18464252, upload-time = "2026-07-04T17:07:50.684Z" },
51
+ { url = "https://files.pythonhosted.org/packages/94/a8/f98e50356cf167df656c526c2dfeec2d7dde182f2a3da4b458a5938e2776/numpy-2.5.1-cp314-cp314t-win32.whl", hash = "sha256:6eab239876581b2b3c5a242281b6007bbdbcd1c7085d7709bb57c5929b11e6bf", size = 6263298, upload-time = "2026-07-04T17:07:53.445Z" },
52
+ { url = "https://files.pythonhosted.org/packages/72/ac/96ae880cdecad0b3275d9359fcec72667b49a4863c9f12942e43679dda02/numpy-2.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:83ce9c80d5b521b0d77ddcbe5447c218d247929b6cc056ca5351342accfff0af", size = 12748623, upload-time = "2026-07-04T17:07:55.384Z" },
53
+ { url = "https://files.pythonhosted.org/packages/a1/5a/4d2b1601df3602dba7a14f3348ba9bfe94a18adb428e693df6154c293831/numpy-2.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:5a6db61f9aaa57e369905c67d852045d3c4f7126405b29d09b19dec118e9c9cb", size = 10697674, upload-time = "2026-07-04T17:07:58.506Z" },
54
+ ]
55
+
56
+ [[package]]
57
+ name = "openscad-cpp-evaluator"
58
+ version = "0.13.0"
59
+ source = { editable = "." }
60
+ dependencies = [
61
+ { name = "numpy" },
62
+ ]
63
+
64
+ [package.metadata]
65
+ requires-dist = [{ name = "numpy" }]