openscad-cpp-evaluator 1.39.0__tar.gz → 1.40.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 (232) hide show
  1. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/pyproject.toml +1 -1
  3. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/booleans.cpp +2 -1
  4. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/primitives_3d.cpp +116 -14
  5. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/colored_body.cpp +3 -2
  6. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/csg_generate.cpp +9 -2
  7. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_manifold_cache.cpp +23 -0
  8. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_primitives.cpp +40 -5
  9. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/uv.lock +1 -1
  10. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/.github/workflows/ci.yml +0 -0
  11. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/.github/workflows/wheels.yml +0 -0
  12. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/.gitignore +0 -0
  13. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/.gitmodules +0 -0
  14. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/CLAUDE.md +0 -0
  15. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/CMakeLists.txt +0 -0
  16. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/LICENSE +0 -0
  17. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/README.md +0 -0
  18. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/bindings/CMakeLists.txt +0 -0
  19. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/bindings/ast_to_py.cpp +0 -0
  20. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/bindings/module.cpp +0 -0
  21. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/cmake/embed_font.cmake +0 -0
  22. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/cmake/patch_manifold.cmake +0 -0
  23. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/examples/CMakeLists.txt +0 -0
  24. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/examples/eval_perf_benchmark.cpp +0 -0
  25. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/examples/manifold_cache_reuse.cpp +0 -0
  26. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/examples/minimal_debugger.cpp +0 -0
  27. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  28. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/.gitignore +0 -0
  29. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  30. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  31. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/README.md +0 -0
  32. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  33. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  34. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  35. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  36. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  37. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  38. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  39. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  40. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  41. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  42. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  43. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  44. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  45. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  46. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  47. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  48. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/.DS_Store +0 -0
  49. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  50. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  51. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  52. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  53. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  54. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  55. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  56. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  57. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  58. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  59. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  60. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  61. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  62. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  63. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  64. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  65. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  66. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  67. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  68. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  69. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  70. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  71. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  72. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  73. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  74. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  75. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  76. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  77. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
  78. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  79. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  80. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  81. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  82. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  83. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  84. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  85. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  86. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  87. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  88. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
  89. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  90. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  91. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  92. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  93. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  94. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  95. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  96. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  97. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  98. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  99. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  100. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  101. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  102. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  103. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  104. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  105. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  106. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  107. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  108. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  109. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  110. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  111. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  112. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  113. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  114. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  115. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  116. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  117. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  118. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  119. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  120. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  121. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  122. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  123. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  124. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  125. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  126. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  127. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  128. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  129. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  130. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  131. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  132. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  133. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  134. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  135. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  136. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/resources/fonts/LICENSE +0 -0
  137. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  138. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  139. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  140. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  141. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/CMakeLists.txt +0 -0
  142. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/builtins.hpp +0 -0
  143. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/call_args.cpp +0 -0
  144. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/color.cpp +0 -0
  145. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/control.cpp +0 -0
  146. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/dxf_dim.cpp +0 -0
  147. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/extrude.cpp +0 -0
  148. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/function_builtins.cpp +0 -0
  149. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/import.cpp +0 -0
  150. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/modifiers.cpp +0 -0
  151. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/primitives_2d.cpp +0 -0
  152. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/profile_time.cpp +0 -0
  153. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/registry.cpp +0 -0
  154. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/roof.cpp +0 -0
  155. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/segments.cpp +0 -0
  156. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/surface.cpp +0 -0
  157. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/text.cpp +0 -0
  158. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/topology.cpp +0 -0
  159. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/builtins/transforms.cpp +0 -0
  160. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/bytecode_compiler.cpp +0 -0
  161. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/bytecode_vm.cpp +0 -0
  162. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/coverage.cpp +0 -0
  163. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/csg_resolve.cpp +0 -0
  164. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/css_colors.cpp +0 -0
  165. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/debug/debug_repl.cpp +0 -0
  166. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/debug_profile.cpp +0 -0
  167. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/eval_context.cpp +0 -0
  168. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/eval_error.cpp +0 -0
  169. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/eval_use.cpp +0 -0
  170. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/export.cpp +0 -0
  171. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/expr_eval.cpp +0 -0
  172. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/format_closure.cpp +0 -0
  173. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/import/dxf_import.cpp +0 -0
  174. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/import/mesh_import.cpp +0 -0
  175. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/import/scene_import.cpp +0 -0
  176. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/import/surface_load.cpp +0 -0
  177. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/import/svg_import.cpp +0 -0
  178. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/manifold_cache.cpp +0 -0
  179. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/measure_geometry.cpp +0 -0
  180. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/mesh_check.cpp +0 -0
  181. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/stmt_eval.cpp +0 -0
  182. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/text/font_match.cpp +0 -0
  183. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/text/freetype_font_provider.cpp +0 -0
  184. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/text/text_metrics.cpp +0 -0
  185. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/user_calls.cpp +0 -0
  186. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/utf8.cpp +0 -0
  187. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/value.cpp +0 -0
  188. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/xkcd_colors.cpp +0 -0
  189. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/xkcd_colors.hpp +0 -0
  190. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/src/zip_stored.cpp +0 -0
  191. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/CMakeLists.txt +0 -0
  192. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_booleans.cpp +0 -0
  193. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_bytecode_compiler.cpp +0 -0
  194. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_children_separate.cpp +0 -0
  195. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_cli.cpp +0 -0
  196. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_control_flow.cpp +0 -0
  197. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_coverage.cpp +0 -0
  198. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_csg_tree.cpp +0 -0
  199. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_debug_hooks.cpp +0 -0
  200. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_discretizer.cpp +0 -0
  201. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_dxf_svg_import.cpp +0 -0
  202. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_eval_context.cpp +0 -0
  203. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_export.cpp +0 -0
  204. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_expr_eval.cpp +0 -0
  205. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_extrude_roof.cpp +0 -0
  206. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_feature_detection.cpp +0 -0
  207. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_function_builtins.cpp +0 -0
  208. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_helpers.hpp +0 -0
  209. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_import_export.cpp +0 -0
  210. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_include_cache.cpp +0 -0
  211. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_mesh_check.cpp +0 -0
  212. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_multi_color_merge.cpp +0 -0
  213. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_profile_time.cpp +0 -0
  214. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_profiling.cpp +0 -0
  215. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_python_bindings.py +0 -0
  216. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_render_expr.cpp +0 -0
  217. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_scope_trail.cpp +0 -0
  218. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_scoping.cpp +0 -0
  219. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_surface.cpp +0 -0
  220. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_tail_calls.cpp +0 -0
  221. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_text.cpp +0 -0
  222. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_transforms.cpp +0 -0
  223. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_undef_args.cpp +0 -0
  224. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_unexpected_args.cpp +0 -0
  225. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_use.cpp +0 -0
  226. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_value.cpp +0 -0
  227. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_viewport_params.cpp +0 -0
  228. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tests/test_zip_stored.cpp +0 -0
  229. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tools/cli/CMakeLists.txt +0 -0
  230. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tools/cli/cli_lib.cpp +0 -0
  231. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.0}/tools/cli/cli_lib.hpp +0 -0
  232. {openscad_cpp_evaluator-1.39.0 → openscad_cpp_evaluator-1.40.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: 1.39.0
3
+ Version: 1.40.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 = "1.39.0"
7
+ version = "1.40.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -231,7 +231,8 @@ manifold::Manifold finishKeepMinuend(Evaluator& ev, std::vector<KeepPart>& parts
231
231
  std::optional<manifold::Manifold> out;
232
232
  for (KeepPart& part : parts) {
233
233
  if (!part.body.body) continue;
234
- manifold::MeshGL mesh = part.body.body->GetMeshGL();
234
+ // MeshGL64: rebuilt below, and float32 would move every vertex.
235
+ manifold::MeshGL64 mesh = part.body.body->GetMeshGL64();
235
236
  if (mesh.triVerts.empty()) continue;
236
237
  std::unordered_map<uint32_t, uint32_t> remap;
237
238
  for (uint32_t& id : mesh.runOriginalID) {
@@ -275,6 +275,121 @@ SphereMesh buildAligned(double r, int hsides, int vsides, bool stagger) {
275
275
  return m;
276
276
  }
277
277
 
278
+ // "octa": an octahedron with each face cut into an n x n triangular grid,
279
+ // n = ceil(segments / 4), pushed out to the sphere -- 4n^2 + 2 vertices,
280
+ // one on each pole of all three axes. Vertex placement is upstream
281
+ // OpenSCAD's (primitives.cc, SphereNode::createGeometryOcta): an interior
282
+ // vertex is where the great-circle arcs through equally spaced edge points
283
+ // meet, the three pairwise intersections averaged.
284
+ //
285
+ // The mesh is EXACTLY symmetric under all 48 symmetries of the octahedron,
286
+ // not merely to rounding: every vertex is computed once, for its
287
+ // barycentric weights sorted largest first, and the other copies are that
288
+ // one point with its coordinates permuted and negated, which is exact.
289
+ // Within a rounding error is not good enough. Manifold::Sphere, which this
290
+ // used to be, is symmetric only to ~1e-15, so a copy rotated 90 degrees
291
+ // landed a hair off the original and difference() of the two left a mass of
292
+ // sliver shards where it should leave nothing.
293
+ //
294
+ // ponytail: the great-circle vertices are not bit-identical to circle()'s
295
+ // (Manifold's Circle reduces angles by quadrant, not octant, so it is not
296
+ // exactly symmetric itself); a sphere and a coaxial circle of the same $fn
297
+ // agree to rounding only.
298
+ namespace {
299
+ // (cos, sin) of (90 * m / n) degrees, always computed from the angle at or
300
+ // below 45 degrees, so the points at m and n - m are exact mirrors.
301
+ std::array<double, 2> quarterArc(int m, int n) {
302
+ if (2 * m == n) return {std::sqrt(0.5), std::sqrt(0.5)};
303
+ if (2 * m > n) {
304
+ const std::array<double, 2> p = quarterArc(n - m, n);
305
+ return {p[1], p[0]};
306
+ }
307
+ const double t = (std::numbers::pi / 2.0) * m / n;
308
+ return {std::cos(t), std::sin(t)};
309
+ }
310
+
311
+ using V3 = std::array<double, 3>;
312
+ V3 cross(const V3& a, const V3& b) {
313
+ return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]};
314
+ }
315
+ V3 normalized(const V3& v) {
316
+ const double l = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
317
+ return {v[0] / l, v[1] / l, v[2] / l};
318
+ }
319
+
320
+ // The unit-sphere point with barycentric weights a >= b >= c (a + b + c = n)
321
+ // toward three orthogonal axes, as coordinates along those axes.
322
+ V3 octaCanonical(int a, int b, int c, int n) {
323
+ if (b == 0) return {1.0, 0.0, 0.0};
324
+ if (c == 0) {
325
+ const std::array<double, 2> e = quarterArc(b, n);
326
+ return {e[0], e[1], 0.0};
327
+ }
328
+ // Upstream's construction, with its i, j, k = a, b, c.
329
+ const auto ac = [&](int m) { const auto e = quarterArc(m, n); return V3{e[0], 0.0, e[1]}; };
330
+ const auto bc = [&](int m) { const auto e = quarterArc(m, n); return V3{0.0, e[0], e[1]}; };
331
+ const auto ab = [&](int m) { const auto e = quarterArc(m, n); return V3{e[0], e[1], 0.0}; };
332
+ const auto meet = [](const V3& p, const V3& q, const V3& r, const V3& s) {
333
+ V3 x = normalized(cross(cross(p, q), cross(r, s)));
334
+ if (x[0] + x[1] + x[2] < 0) x = {-x[0], -x[1], -x[2]}; // the octant's side
335
+ return x;
336
+ };
337
+ const V3 xz_k = ac(c), yz_k = bc(c);
338
+ const V3 xy_j = ab(b), yz_j = bc(n - b);
339
+ const V3 xy_i = ab(n - a), xz_i = ac(n - a);
340
+ const V3 p1 = meet(xz_k, yz_k, xy_j, yz_j);
341
+ const V3 p2 = meet(xz_k, yz_k, xy_i, xz_i);
342
+ const V3 p3 = meet(xy_j, yz_j, xy_i, xz_i);
343
+ V3 p = normalized({p1[0] + p2[0] + p3[0], p1[1] + p2[1] + p3[1], p1[2] + p2[2] + p3[2]});
344
+ // Equal weights must give equal coordinates exactly, or the two
345
+ // permutations that swap them would disagree about this one vertex.
346
+ if (a == b) p[1] = p[0];
347
+ if (b == c) p[2] = p[1];
348
+ return p;
349
+ }
350
+ } // namespace
351
+
352
+ SphereMesh buildOcta(double r, int segments) {
353
+ const int n = std::max(1, (segments + 3) / 4);
354
+ SphereMesh m;
355
+ std::map<std::array<int, 3>, int> index; // signed barycentric weights -> vertex
356
+ const auto vertex = [&](int i, int j, int k, int sx, int sy, int sz) {
357
+ const std::array<int, 3> key{sx * i, sy * j, sz * k};
358
+ auto [it, fresh] = index.try_emplace(key, static_cast<int>(m.verts.size() / 3));
359
+ if (fresh) {
360
+ const std::array<int, 3> w{i, j, k};
361
+ std::array<int, 3> axis{0, 1, 2};
362
+ std::stable_sort(axis.begin(), axis.end(), [&](int p, int q) { return w[p] > w[q]; });
363
+ const V3 c = octaCanonical(w[axis[0]], w[axis[1]], w[axis[2]], n);
364
+ V3 p{};
365
+ for (int rank = 0; rank < 3; ++rank) p[axis[rank]] = c[rank];
366
+ pushVert(m, {r * sx * p[0], r * sy * p[1], r * sz * p[2]});
367
+ }
368
+ return it->second;
369
+ };
370
+ for (int sx : {1, -1})
371
+ for (int sy : {1, -1})
372
+ for (int sz : {1, -1}) {
373
+ const bool flip = sx * sy * sz < 0;
374
+ const auto tri = [&](int a, int b, int c) {
375
+ if (flip) std::swap(b, c);
376
+ m.tris.insert(m.tris.end(), {a, b, c});
377
+ };
378
+ // "Up" triangles toward +x, +y, +z, then the "down" ones
379
+ // between them; both wind outward in the (+,+,+) octant.
380
+ for (int i = 0; i < n; ++i)
381
+ for (int j = 0; i + j < n; ++j) {
382
+ const int k = n - 1 - i - j;
383
+ tri(vertex(i + 1, j, k, sx, sy, sz), vertex(i, j + 1, k, sx, sy, sz),
384
+ vertex(i, j, k + 1, sx, sy, sz));
385
+ if (k >= 1)
386
+ tri(vertex(i, j + 1, k, sx, sy, sz), vertex(i + 1, j, k, sx, sy, sz),
387
+ vertex(i + 1, j + 1, k - 1, sx, sy, sz));
388
+ }
389
+ }
390
+ return m;
391
+ }
392
+
278
393
  // "icosa": subdivide every icosahedral face into a triangular grid and push
279
394
  // each sample out to the sphere. BOSL2 subsamples one face and rotates
280
395
  // copies onto the rest; sampling each face against its own corners is the
@@ -398,18 +513,10 @@ CSGParams resolveSphere(Evaluator& ev, const oscad::ModularCall& node, EvalConte
398
513
  params["segs"] = Value{static_cast<double>(hsides)};
399
514
  params["color"] = colorToValue(effCtx.color);
400
515
 
401
- if (style == "octa") {
402
- // Manifold::Sphere IS a subdivided octahedron (Shape::Octahedron,
403
- // then Subdivide), so this needs no mesh of our own -- generate
404
- // calls it directly.
405
- params["verts"] = Value{makeList()};
406
- params["tris"] = Value{makeList()};
407
- return params;
408
- }
409
-
410
516
  const SphereMesh m = style == "aligned" ? buildAligned(r, hsides, vsides, false)
411
517
  : style == "stagger" ? buildAligned(r, hsides, vsides, true)
412
518
  : style == "icosa" ? buildIcosa(r, hsides)
519
+ : style == "octa" ? buildOcta(r, hsides)
413
520
  : buildOrig(r, hsides, vsides);
414
521
 
415
522
  std::vector<Value> vertsValues;
@@ -433,11 +540,6 @@ std::vector<ColoredBody> generateSphere(Evaluator& ev, const CSGParams& params,
433
540
  for (const Value& t : std::get<ListPtr>(params.at("tris"))->items) {
434
541
  mesh.triVerts.push_back(static_cast<uint64_t>(std::get<double>(t)));
435
542
  }
436
- if (std::get<std::string>(params.at("style")) == "octa") {
437
- manifold::Manifold octa = manifold::Manifold::Sphere(
438
- std::get<double>(params.at("r")), static_cast<int>(std::get<double>(params.at("segs"))));
439
- return {ev.tagGenerated(std::move(octa), node, params.at("color"))};
440
- }
441
543
  manifold::Manifold body(mesh);
442
544
  return {ev.tagGenerated(std::move(body), node, params.at("color"))};
443
545
  }
@@ -40,9 +40,10 @@ std::vector<ColoredBody> toRenderableBodies(const std::vector<ColoredBody>& bodi
40
40
  // slab finds the node that built the shape (see
41
41
  // ColoredBody::sectionId). Manifold keeps a MeshGL's
42
42
  // runOriginalID when it is given one.
43
- manifold::MeshGL mesh = flat.body->GetMeshGL();
43
+ // MeshGL64: rebuilt below, and float32 would move every vertex.
44
+ manifold::MeshGL64 mesh = flat.body->GetMeshGL64();
44
45
  mesh.runOriginalID = {*cb.sectionId};
45
- mesh.runIndex = {0, static_cast<uint32_t>(mesh.triVerts.size())};
46
+ mesh.runIndex = {0, mesh.triVerts.size()};
46
47
  mesh.runTransform.clear();
47
48
  mesh.faceID.clear();
48
49
  flat.body = manifold::Manifold(mesh);
@@ -364,7 +364,7 @@ std::shared_ptr<const std::vector<ColoredBody>> remapParts(const std::vector<Col
364
364
  for (ColoredBody part : parts) {
365
365
  if (part.mergedFrom) part.mergedFrom = remapParts(*part.mergedFrom, remap);
366
366
  if (part.body && !part.body->IsEmpty()) {
367
- manifold::MeshGL mesh = part.body->GetMeshGL();
367
+ manifold::MeshGL64 mesh = part.body->GetMeshGL64();
368
368
  for (uint32_t& id : mesh.runOriginalID) {
369
369
  auto found = remap.find(id);
370
370
  if (found != remap.end()) id = found->second;
@@ -417,7 +417,14 @@ void Evaluator::restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::
417
417
  cb.sectionId = fresh;
418
418
  }
419
419
  if (!cb.body || cb.body->IsEmpty()) continue;
420
- manifold::MeshGL mesh = cb.body->GetMeshGL();
420
+ // MeshGL64, not MeshGL: the body is rebuilt from this mesh below,
421
+ // and MeshGL is float32. Rounding every vertex made a cache hit a
422
+ // slightly different solid from the same shape built fresh, so
423
+ // difference() { sphere(50); sphere(50); } left a shell of slivers
424
+ // wherever one operand came from the cache -- only in the GUI,
425
+ // which has a cache, never in the CLI. A cube survived because its
426
+ // coordinates are exact in float32.
427
+ manifold::MeshGL64 mesh = cb.body->GetMeshGL64();
421
428
  if (mesh.runOriginalID.empty()) continue;
422
429
  std::unordered_map<uint32_t, uint32_t> remap;
423
430
  // fresh ID -> the colour its triangles carried, multi-colour
@@ -496,3 +496,26 @@ TEST(ManifoldCache, AHitOnAQuietSubtreeStaysQuiet) {
496
496
  EXPECT_EQ(countLines(), 0);
497
497
  EXPECT_EQ(countLines(), 0);
498
498
  }
499
+
500
+ // A cache hit used to come back rebuilt through float32 (MeshGL), so the
501
+ // second of two identical spheres was a slightly different solid from the
502
+ // first and difference() left a shell of slivers -- in the GUI only, since
503
+ // the CLI has no cache. A cube hid it: its coordinates are exact in float.
504
+ // Covers the hit within one render, across renders, and the keep-minuend
505
+ // path, which rebuilt its parts the same way.
506
+ TEST(ManifoldCache, AHitIsTheSameSolidNotAFloatRoundedCopy) {
507
+ const std::string srcs[] = {
508
+ "difference() { sphere(d=100, $fn=16); sphere(d=100, $fn=16); }",
509
+ "difference() { sphere(d=100, style=\"octa\", $fn=16); sphere(d=100, style=\"octa\", $fn=16); }",
510
+ "difference() { rotate([10, 20, 30]) cylinder(h=7.3, r=3.1); rotate([10, 20, 30]) cylinder(h=7.3, r=3.1); }",
511
+ };
512
+ for (const std::string& src : srcs) {
513
+ auto cache = std::make_shared<ManifoldCache>();
514
+ for (int render = 0; render < 2; ++render) {
515
+ Evaluated e = evalSrcWithCache(src, cache);
516
+ for (const ColoredBody& b : e.bodies) {
517
+ EXPECT_TRUE(!b.body || b.body->IsEmpty()) << src << " render " << render;
518
+ }
519
+ }
520
+ }
521
+ }
@@ -3,7 +3,11 @@
3
3
 
4
4
  #include "test_helpers.hpp"
5
5
 
6
+ #include <algorithm>
7
+ #include <array>
6
8
  #include <cmath>
9
+ #include <set>
10
+ #include <string>
7
11
  #include <gtest/gtest.h>
8
12
  #include <numbers>
9
13
 
@@ -398,14 +402,45 @@ TEST(SphereStyle, EveryStyleMatchesBosl2sCounts) {
398
402
  }
399
403
 
400
404
  TEST(SphereStyle, EveryStyleMatchesBosl2sVolume) {
401
- // aligned/stagger/icosa agree with BOSL2 to the last digit. "octa" comes
402
- // from Manifold::Sphere, whose octahedral subdivision distributes its
403
- // vertices a little differently than BOSL2's -- same 146/288 topology,
404
- // ~0.05% apart in volume.
405
+ // aligned/stagger/icosa agree with BOSL2 to the last digit. "octa" is
406
+ // upstream OpenSCAD's construction (sphere(style="octa"), 2026-09), not
407
+ // BOSL2's -- same 146/288 topology. 4026.4886 is the hull volume of an
408
+ // independent port of upstream's createGeometryOcta.
405
409
  EXPECT_NEAR(sphereStats("sphere(r=10, style=\"aligned\", $fn=24);").volume, 4070.5524, 1e-3);
406
410
  EXPECT_NEAR(sphereStats("sphere(r=10, style=\"stagger\", $fn=24);").volume, 4082.1246, 1e-3);
407
411
  EXPECT_NEAR(sphereStats("sphere(r=10, style=\"icosa\", $fn=24);").volume, 4097.2467, 1e-3);
408
- EXPECT_NEAR(sphereStats("sphere(r=10, style=\"octa\", $fn=24);").volume, 4024.3239, 1e-3);
412
+ EXPECT_NEAR(sphereStats("sphere(r=10, style=\"octa\", $fn=24);").volume, 4026.4886, 1e-3);
413
+ }
414
+
415
+ // "octa" must be EXACTLY symmetric under the octahedron's 48 symmetries, not
416
+ // to rounding: a copy rotated 90 degrees otherwise lands a hair off the
417
+ // original, and difference() of the two leaves a mass of sliver shards
418
+ // instead of nothing. It used to be Manifold::Sphere, symmetric to ~1e-15.
419
+ TEST(SphereStyle, OctaIsExactlyOctahedrallySymmetric) {
420
+ for (int fn : {3, 8, 16, 21, 37, 64}) {
421
+ const std::string sphere = "sphere(d=100, style=\"octa\", $fn=" + std::to_string(fn) + ")";
422
+ Evaluated e = evalSrc(sphere + ";");
423
+ const manifold::MeshGL64 mesh = e.bodies[0].body->GetMeshGL64();
424
+ const size_t n = static_cast<size_t>(std::max(1, (fn + 3) / 4));
425
+ EXPECT_EQ(mesh.vertProperties.size() / 3, 4 * n * n + 2) << fn;
426
+ std::set<std::array<double, 3>> verts;
427
+ for (size_t v = 0; v < mesh.vertProperties.size(); v += 3)
428
+ verts.insert({mesh.vertProperties[v] + 0.0, mesh.vertProperties[v + 1] + 0.0, mesh.vertProperties[v + 2] + 0.0});
429
+ std::array<int, 3> perm{0, 1, 2};
430
+ do {
431
+ for (int signs = 0; signs < 8; ++signs) {
432
+ for (const std::array<double, 3>& p : verts) {
433
+ std::array<double, 3> q{};
434
+ for (int a = 0; a < 3; ++a) q[a] = ((signs >> a) & 1 ? -p[perm[a]] : p[perm[a]]) + 0.0;
435
+ EXPECT_TRUE(verts.count(q)) << fn;
436
+ }
437
+ }
438
+ } while (std::next_permutation(perm.begin(), perm.end()));
439
+ for (const char* rot : {"[90,0,0]", "[0,90,0]", "[0,0,90]", "[180,0,0]"}) {
440
+ Evaluated d = evalSrc("difference() { " + sphere + "; rotate(" + rot + ") " + sphere + "; }");
441
+ for (const ColoredBody& b : d.bodies) EXPECT_TRUE(!b.body || b.body->IsEmpty()) << fn << " " << rot;
442
+ }
443
+ }
409
444
  }
410
445
 
411
446
  TEST(SphereStyle, EveryStyleIsAClosedSolidUnderTheTrueVolume) {
@@ -55,7 +55,7 @@ wheels = [
55
55
 
56
56
  [[package]]
57
57
  name = "openscad-cpp-evaluator"
58
- version = "1.39.0"
58
+ version = "1.40.0"
59
59
  source = { editable = "." }
60
60
  dependencies = [
61
61
  { name = "numpy" },