openscad-cpp-evaluator 1.49.3__tar.gz → 1.49.5__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 (235) hide show
  1. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/mesh_check.hpp +14 -0
  3. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/pyproject.toml +1 -1
  4. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/extrude.cpp +5 -1
  5. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/primitives_3d.cpp +10 -15
  6. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/mesh_check.cpp +96 -43
  7. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/user_calls.cpp +13 -2
  8. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_extrude_roof.cpp +15 -0
  9. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_primitives.cpp +17 -0
  10. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_scoping.cpp +15 -0
  11. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/uv.lock +1 -1
  12. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/.github/workflows/ci.yml +0 -0
  13. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/.github/workflows/wheels.yml +0 -0
  14. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/.gitignore +0 -0
  15. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/.gitmodules +0 -0
  16. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/CLAUDE.md +0 -0
  17. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/CMakeLists.txt +0 -0
  18. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/LICENSE +0 -0
  19. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/README.md +0 -0
  20. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/bindings/CMakeLists.txt +0 -0
  21. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/bindings/ast_to_py.cpp +0 -0
  22. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/bindings/module.cpp +0 -0
  23. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/cmake/embed_font.cmake +0 -0
  24. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/cmake/patch_manifold.cmake +0 -0
  25. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/examples/CMakeLists.txt +0 -0
  26. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/examples/eval_perf_benchmark.cpp +0 -0
  27. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/examples/manifold_cache_reuse.cpp +0 -0
  28. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/examples/minimal_debugger.cpp +0 -0
  29. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  30. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/.gitignore +0 -0
  31. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  32. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  33. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/LICENSE +0 -0
  34. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/README.md +0 -0
  35. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  36. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  37. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  38. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  39. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  40. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  41. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  42. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  43. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  44. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  45. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  46. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  47. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  48. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  49. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  50. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  51. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/.DS_Store +0 -0
  52. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  53. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/api.cpp +0 -0
  54. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  55. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  56. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  57. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  58. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  59. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  60. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  61. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  62. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  63. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  64. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  65. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  66. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  67. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  68. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  69. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  70. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/position.cpp +0 -0
  71. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  72. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  73. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  74. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  75. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  76. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  77. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  78. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  79. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  80. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
  81. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  82. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  83. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  84. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  85. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  86. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  87. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  88. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  89. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  90. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  91. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
  92. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  93. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  94. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  95. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  96. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  97. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  98. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  99. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  100. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  101. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  102. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  103. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  104. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  105. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  106. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  107. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  108. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  109. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  110. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  111. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  112. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  113. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  114. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  115. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  116. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  117. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  118. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  119. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/export.hpp +0 -0
  120. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  121. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  122. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  123. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  124. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  125. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  126. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  127. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  128. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  129. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  130. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/reassign.hpp +0 -0
  131. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  132. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  133. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  134. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  135. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  136. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/value.hpp +0 -0
  137. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  138. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/python/openscad_cpp_evaluator/__init__.py +0 -0
  139. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/resources/fonts/LICENSE +0 -0
  140. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  141. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  142. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  143. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  144. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/CMakeLists.txt +0 -0
  145. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/booleans.cpp +0 -0
  146. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/builtins.hpp +0 -0
  147. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/call_args.cpp +0 -0
  148. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/color.cpp +0 -0
  149. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/control.cpp +0 -0
  150. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/dxf_dim.cpp +0 -0
  151. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/function_builtins.cpp +0 -0
  152. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/import.cpp +0 -0
  153. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/modifiers.cpp +0 -0
  154. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/primitives_2d.cpp +0 -0
  155. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/profile_time.cpp +0 -0
  156. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/registry.cpp +0 -0
  157. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/roof.cpp +0 -0
  158. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/segments.cpp +0 -0
  159. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/surface.cpp +0 -0
  160. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/text.cpp +0 -0
  161. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/topology.cpp +0 -0
  162. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/builtins/transforms.cpp +0 -0
  163. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/bytecode_compiler.cpp +0 -0
  164. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/bytecode_vm.cpp +0 -0
  165. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/colored_body.cpp +0 -0
  166. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/coverage.cpp +0 -0
  167. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/csg_generate.cpp +0 -0
  168. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/csg_resolve.cpp +0 -0
  169. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/css_colors.cpp +0 -0
  170. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/debug/debug_repl.cpp +0 -0
  171. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/debug_profile.cpp +0 -0
  172. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/eval_context.cpp +0 -0
  173. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/eval_error.cpp +0 -0
  174. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/eval_use.cpp +0 -0
  175. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/export.cpp +0 -0
  176. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/expr_eval.cpp +0 -0
  177. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/format_closure.cpp +0 -0
  178. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/import/dxf_import.cpp +0 -0
  179. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/import/mesh_import.cpp +0 -0
  180. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/import/scene_import.cpp +0 -0
  181. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/import/surface_load.cpp +0 -0
  182. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/import/svg_import.cpp +0 -0
  183. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/manifold_cache.cpp +0 -0
  184. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/measure_geometry.cpp +0 -0
  185. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/stmt_eval.cpp +0 -0
  186. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/text/font_match.cpp +0 -0
  187. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/text/freetype_font_provider.cpp +0 -0
  188. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/text/text_metrics.cpp +0 -0
  189. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/utf8.cpp +0 -0
  190. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/value.cpp +0 -0
  191. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/xkcd_colors.cpp +0 -0
  192. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/xkcd_colors.hpp +0 -0
  193. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/src/zip_stored.cpp +0 -0
  194. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/CMakeLists.txt +0 -0
  195. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_booleans.cpp +0 -0
  196. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_bytecode_compiler.cpp +0 -0
  197. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_children_separate.cpp +0 -0
  198. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_cli.cpp +0 -0
  199. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_control_flow.cpp +0 -0
  200. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_coverage.cpp +0 -0
  201. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_csg_tree.cpp +0 -0
  202. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_debug_hooks.cpp +0 -0
  203. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_discretizer.cpp +0 -0
  204. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_dxf_svg_import.cpp +0 -0
  205. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_eval_context.cpp +0 -0
  206. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_export.cpp +0 -0
  207. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_expr_eval.cpp +0 -0
  208. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_feature_detection.cpp +0 -0
  209. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_function_builtins.cpp +0 -0
  210. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_helpers.hpp +0 -0
  211. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_import_export.cpp +0 -0
  212. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_include_cache.cpp +0 -0
  213. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_manifold_cache.cpp +0 -0
  214. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_mesh_check.cpp +0 -0
  215. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_multi_color_merge.cpp +0 -0
  216. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_openscad_parity.cpp +0 -0
  217. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_profile_time.cpp +0 -0
  218. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_profiling.cpp +0 -0
  219. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_python_bindings.py +0 -0
  220. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_render_expr.cpp +0 -0
  221. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_scope_trail.cpp +0 -0
  222. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_surface.cpp +0 -0
  223. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_tail_calls.cpp +0 -0
  224. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_text.cpp +0 -0
  225. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_transforms.cpp +0 -0
  226. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_undef_args.cpp +0 -0
  227. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_unexpected_args.cpp +0 -0
  228. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_use.cpp +0 -0
  229. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_value.cpp +0 -0
  230. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_viewport_params.cpp +0 -0
  231. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tests/test_zip_stored.cpp +0 -0
  232. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tools/cli/CMakeLists.txt +0 -0
  233. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tools/cli/cli_lib.cpp +0 -0
  234. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/tools/cli/cli_lib.hpp +0 -0
  235. {openscad_cpp_evaluator-1.49.3 → openscad_cpp_evaluator-1.49.5}/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.49.3
3
+ Version: 1.49.5
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT AND OFL-1.1
@@ -1,5 +1,6 @@
1
1
  #pragma once
2
2
 
3
+ #include <cstddef>
3
4
  #include <cstdint>
4
5
  #include <string>
5
6
  #include <vector>
@@ -82,6 +83,19 @@ struct MeshRepairReport {
82
83
  std::string summary() const;
83
84
  };
84
85
 
86
+ // Weld points on a grid of `tolerance`: returns, for each point, the index
87
+ // of the lowest-numbered point it welds to (itself if none). Points in one
88
+ // grid cell weld, as rounding to the grid always did -- and so do points
89
+ // within tolerance/32 of each other across a cell boundary, which rounding
90
+ // alone splits however close they are: two copies of a vertex at x*1e6 ==
91
+ // 10454062.5, a few ULPs apart, round to different cells (BelfrySCAD #832).
92
+ // `data` holds `n` points, each starting `stride` values after the last.
93
+ // A tolerance of 0 or less welds exact duplicates only.
94
+ template <typename T>
95
+ std::vector<size_t> weldPoints(const T* data, size_t n, size_t stride, double tolerance);
96
+ extern template std::vector<size_t> weldPoints<float>(const float*, size_t, size_t, double);
97
+ extern template std::vector<size_t> weldPoints<double>(const double*, size_t, size_t, double);
98
+
85
99
  //: Default welding tolerance: OpenSCAD's own GRID_FINE, 2^-20. Theirs is
86
100
  //: the constant their Grid.h uses to decide two points are one, so a mesh
87
101
  //: that welds there welds here. Exact in binary, so the multiply-and-round
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.49.3"
7
+ version = "1.49.5"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -478,7 +478,11 @@ std::vector<ColoredBody> generateProjection(Evaluator&, const CSGParams& params,
478
478
  const ColoredBody combined = combineBodies(bodies3d);
479
479
  manifold::CrossSection cs;
480
480
  if (std::get<bool>(params.at("cut"))) {
481
- cs = manifold::CrossSection(combined.body->Slice(0.0), manifold::CrossSection::FillRule::EvenOdd);
481
+ // Positive, as OpenSCAD fills it. A self-overlapping polyhedron (a
482
+ // rope spool whose loops overlap, BelfrySCAD #831) slices to
483
+ // overlapping contours, and EvenOdd cut every overlap out as a hole.
484
+ // A clean solid slices the same either way.
485
+ cs = manifold::CrossSection(combined.body->Slice(0.0), manifold::CrossSection::FillRule::Positive);
482
486
  } else {
483
487
  const manifold::Polygons polys = combined.body->Project();
484
488
  cs = polys.empty() ? manifold::CrossSection() : manifold::CrossSection(polys, manifold::CrossSection::FillRule::Positive);
@@ -889,21 +889,16 @@ CSGParams resolvePolyhedron(Evaluator& ev, const oscad::ModularCall& node, EvalC
889
889
 
890
890
  std::vector<std::array<double, 3>> uniqueVerts;
891
891
  std::vector<size_t> remap(rawVerts.size());
892
- std::map<std::array<int64_t, 3>, size_t> seen;
893
- for (size_t i = 0; i < rawVerts.size(); ++i) {
894
- const std::array<int64_t, 3> key = {
895
- static_cast<int64_t>(std::llround(rawVerts[i][0] * 1e6)),
896
- static_cast<int64_t>(std::llround(rawVerts[i][1] * 1e6)),
897
- static_cast<int64_t>(std::llround(rawVerts[i][2] * 1e6)),
898
- };
899
- auto it = seen.find(key);
900
- if (it != seen.end()) {
901
- remap[i] = it->second;
902
- } else {
903
- const size_t newIdx = uniqueVerts.size();
904
- uniqueVerts.push_back(rawVerts[i]);
905
- seen[key] = newIdx;
906
- remap[i] = newIdx;
892
+ {
893
+ const std::vector<size_t> rep =
894
+ weldPoints(rawVerts.empty() ? nullptr : rawVerts.front().data(), rawVerts.size(), 3, 1e-6);
895
+ for (size_t i = 0; i < rawVerts.size(); ++i) {
896
+ if (rep[i] == i) {
897
+ remap[i] = uniqueVerts.size();
898
+ uniqueVerts.push_back(rawVerts[i]);
899
+ } else {
900
+ remap[i] = remap[rep[i]]; // rep[i] < i, so already numbered
901
+ }
907
902
  }
908
903
  }
909
904
 
@@ -1,6 +1,8 @@
1
1
  #include "openscad_cpp_evaluator/mesh_check.hpp"
2
2
 
3
3
  #include <algorithm>
4
+ #include <array>
5
+ #include <cstring>
4
6
  #include <cmath>
5
7
  #include <map>
6
8
  #include <set>
@@ -65,14 +67,9 @@ template <typename M>
65
67
  size_t countUnwelded(const M& m) {
66
68
  const size_t stride = m.numProp ? m.numProp : 3;
67
69
  const size_t n = stride ? m.vertProperties.size() / stride : 0;
68
- std::map<std::tuple<long long, long long, long long>, size_t> seen;
70
+ const std::vector<size_t> rep = weldPoints(m.vertProperties.data(), n, stride, 1e-6);
69
71
  size_t extra = 0;
70
- for (size_t v = 0; v < n; ++v) {
71
- double p[3];
72
- pos(m, static_cast<Vert>(v), p);
73
- auto key = std::make_tuple(llround(p[0] * 1e6), llround(p[1] * 1e6), llround(p[2] * 1e6));
74
- if (++seen[key] > 1) ++extra;
75
- }
72
+ for (size_t v = 0; v < n; ++v) extra += rep[v] != v;
76
73
  return extra;
77
74
  }
78
75
 
@@ -312,45 +309,12 @@ template <typename M>
312
309
  std::vector<Vert> weldMap(const M& m, size_t& welded, double tolerance) {
313
310
  const size_t stride = m.numProp ? m.numProp : 3;
314
311
  const size_t n = stride ? m.vertProperties.size() / stride : 0;
312
+ const std::vector<size_t> rep = weldPoints(m.vertProperties.data(), n, stride, tolerance);
315
313
  std::vector<Vert> remap(n);
316
314
  welded = 0;
317
-
318
- // Grid key plus the vertex it came from, sorted so that equal positions
319
- // land in a run. This was a std::map keyed by the tuple -- one tree node
320
- // per VERTEX, ~885,000 of them for a 295K-triangle import -- and sorting
321
- // gets the same answer with one allocation. Sorting by (key, vertex)
322
- // puts the LOWEST-numbered vertex first in its run, which is the one
323
- // emplace() kept as the representative, so the remap is unchanged.
324
- struct Keyed {
325
- long long x, y, z;
326
- Vert v;
327
- bool operator<(const Keyed& o) const {
328
- if (x != o.x) return x < o.x;
329
- if (y != o.y) return y < o.y;
330
- if (z != o.z) return z < o.z;
331
- return v < o.v;
332
- }
333
- bool samePos(const Keyed& o) const { return x == o.x && y == o.y && z == o.z; }
334
- };
335
- // Snap to a grid of `tolerance`. Guarded because a caller can pass 0 or
336
- // a negative to mean "weld only exact duplicates" -- dividing by that
337
- // would give inf/NaN keys and weld the whole mesh into one point.
338
- const double inv = tolerance > 0.0 ? 1.0 / tolerance : 0.0;
339
- std::vector<Keyed> keyed;
340
- keyed.reserve(n);
341
315
  for (size_t v = 0; v < n; ++v) {
342
- double p[3];
343
- pos(m, static_cast<Vert>(v), p);
344
- keyed.push_back({llround(p[0] * inv), llround(p[1] * inv), llround(p[2] * inv),
345
- static_cast<Vert>(v)});
346
- }
347
- std::sort(keyed.begin(), keyed.end());
348
- for (size_t i = 0; i < keyed.size();) {
349
- size_t j = i + 1;
350
- while (j < keyed.size() && keyed[j].samePos(keyed[i])) ++j;
351
- for (size_t k = i; k < j; ++k) remap[keyed[k].v] = keyed[i].v;
352
- welded += j - i - 1;
353
- i = j;
316
+ remap[v] = static_cast<Vert>(rep[v]);
317
+ welded += rep[v] != v;
354
318
  }
355
319
  return remap;
356
320
  }
@@ -849,6 +813,93 @@ M stripSlivers(const M& mesh, SliverStripReport& report) {
849
813
  }
850
814
 
851
815
 
816
+ template <typename T>
817
+ std::vector<size_t> weldPoints(const T* data, size_t n, size_t stride, double tolerance) {
818
+ std::vector<size_t> parent(n);
819
+ for (size_t i = 0; i < n; ++i) parent[i] = i;
820
+ auto find = [&](size_t v) {
821
+ while (parent[v] != v) v = parent[v] = parent[parent[v]];
822
+ return v;
823
+ };
824
+ // The lower index stays the root, so each point maps to the lowest it welds to.
825
+ auto unite = [&](size_t a, size_t b) {
826
+ a = find(a);
827
+ b = find(b);
828
+ if (a != b) parent[std::max(a, b)] = std::min(a, b);
829
+ };
830
+
831
+ // Grid key plus the point it came from, sorted so that one cell is a
832
+ // run. Sorting rather than a map: one allocation, not a tree or hash
833
+ // node per vertex (~885,000 of them for a 295K-triangle import).
834
+ struct Keyed {
835
+ int64_t k[3];
836
+ size_t v;
837
+ bool operator<(const Keyed& o) const {
838
+ for (int a = 0; a < 3; ++a)
839
+ if (k[a] != o.k[a]) return k[a] < o.k[a];
840
+ return v < o.v;
841
+ }
842
+ };
843
+ const bool exact = !(tolerance > 0.0);
844
+ const double inv = exact ? 0.0 : 1.0 / tolerance;
845
+ // How near a cell boundary a point must be to be compared with the
846
+ // next cell's points. Well above rounding noise, well below a grid
847
+ // step, and rare enough (~6% of points) to cost nothing.
848
+ constexpr double kNear = 1.0 / 64;
849
+ std::vector<Keyed> keyed(n);
850
+ std::vector<std::pair<size_t, std::array<int, 3>>> leaning; // (point, which way per axis)
851
+ for (size_t i = 0; i < n; ++i) {
852
+ const T* p = data + i * stride;
853
+ std::array<int, 3> lean{};
854
+ for (int a = 0; a < 3; ++a) {
855
+ if (exact) {
856
+ const double c = static_cast<double>(p[a]) + 0.0; // -0 and 0 are one position
857
+ std::memcpy(&keyed[i].k[a], &c, sizeof c);
858
+ continue;
859
+ }
860
+ const double s = p[a] * inv;
861
+ keyed[i].k[a] = std::llround(s);
862
+ const double f = s - static_cast<double>(keyed[i].k[a]); // in [-0.5, 0.5]
863
+ lean[a] = f > 0.5 - kNear ? 1 : f < -0.5 + kNear ? -1 : 0;
864
+ }
865
+ keyed[i].v = i;
866
+ if (lean[0] || lean[1] || lean[2]) leaning.push_back({i, lean});
867
+ }
868
+ std::vector<Keyed> sorted = keyed;
869
+ std::sort(sorted.begin(), sorted.end());
870
+ for (size_t i = 1; i < n; ++i)
871
+ if (std::equal(sorted[i].k, sorted[i].k + 3, sorted[i - 1].k)) unite(sorted[i - 1].v, sorted[i].v);
872
+
873
+ // Across a boundary: each cell the point leans toward, by each subset
874
+ // of the axes it leans on, compared point by point.
875
+ const double reach = 2 * kNear * tolerance;
876
+ for (const auto& [i, lean] : leaning) {
877
+ const T* p = data + i * stride;
878
+ for (int mask = 1; mask < 8; ++mask) {
879
+ Keyed probe{{keyed[i].k[0], keyed[i].k[1], keyed[i].k[2]}, 0};
880
+ bool any = true;
881
+ for (int a = 0; a < 3; ++a) {
882
+ if (!(mask & (1 << a))) continue;
883
+ if (!lean[a]) { any = false; break; }
884
+ probe.k[a] += lean[a];
885
+ }
886
+ if (!any) continue;
887
+ for (auto it = std::lower_bound(sorted.begin(), sorted.end(), probe);
888
+ it != sorted.end() && std::equal(it->k, it->k + 3, probe.k); ++it) {
889
+ const T* q = data + it->v * stride;
890
+ if (std::abs(p[0] - q[0]) <= reach && std::abs(p[1] - q[1]) <= reach
891
+ && std::abs(p[2] - q[2]) <= reach)
892
+ unite(i, it->v);
893
+ }
894
+ }
895
+ }
896
+
897
+ std::vector<size_t> rep(n);
898
+ for (size_t i = 0; i < n; ++i) rep[i] = find(i);
899
+ return rep;
900
+ }
901
+
902
+
852
903
  // Instantiated for both precisions: MeshGL for the paths that are float by
853
904
  // nature (STL, the renderer), MeshGL64 wherever the caller still has its
854
905
  // real coordinates. Keeping the definitions in this file, rather than the
@@ -859,5 +910,7 @@ template manifold::MeshGL repairMesh<manifold::MeshGL>(const manifold::MeshGL&,
859
910
  template manifold::MeshGL64 repairMesh<manifold::MeshGL64>(const manifold::MeshGL64&, MeshRepairReport&, double);
860
911
  template manifold::MeshGL stripSlivers<manifold::MeshGL>(const manifold::MeshGL&, SliverStripReport&);
861
912
  template manifold::MeshGL64 stripSlivers<manifold::MeshGL64>(const manifold::MeshGL64&, SliverStripReport&);
913
+ template std::vector<size_t> weldPoints<float>(const float*, size_t, size_t, double);
914
+ template std::vector<size_t> weldPoints<double>(const double*, size_t, size_t, double);
862
915
 
863
916
  } // namespace oscadeval
@@ -1043,9 +1043,12 @@ std::optional<Evaluator::ChildrenForward> Evaluator::prepareChildrenForward(cons
1043
1043
  // subsequent body-index lookup, so the Nth statement is evaluated
1044
1044
  // directly instead.
1045
1045
  std::vector<const oscad::ASTNode*> geoNodes;
1046
+ std::vector<const oscad::ASTNode*> assignments;
1046
1047
  for (const oscad::ASTNode* c : *ctx.childrenNodes) {
1047
- if (c->kind() != oscad::NodeKind::Assignment && c->kind() != oscad::NodeKind::ModuleDeclaration &&
1048
- c->kind() != oscad::NodeKind::FunctionDeclaration) {
1048
+ if (c->kind() == oscad::NodeKind::Assignment) {
1049
+ assignments.push_back(c);
1050
+ } else if (c->kind() != oscad::NodeKind::ModuleDeclaration &&
1051
+ c->kind() != oscad::NodeKind::FunctionDeclaration) {
1049
1052
  geoNodes.push_back(c);
1050
1053
  }
1051
1054
  }
@@ -1112,6 +1115,14 @@ std::optional<Evaluator::ChildrenForward> Evaluator::prepareChildrenForward(cons
1112
1115
  pickedIndices.push_back(static_cast<size_t>(idx));
1113
1116
  }
1114
1117
  if (picked.empty()) return std::nullopt;
1118
+ // The picked statement runs in its block's scope, so the block's
1119
+ // assignments must be in it: `m() { n = 2; for (i=[0:n]) ...; }` with
1120
+ // `children(0)` in m() read n as unknown (#815, BOSL2's attachable()).
1121
+ // evalChildren runs them into evalCtx's let_, which the picked
1122
+ // statements' scopes share (withScope).
1123
+ // ponytail: a children(i, separate=true) expansion prepares twice, so a
1124
+ // side effect in one of these assignments (echo, rands) runs twice there.
1125
+ if (!assignments.empty()) evalChildren(assignments, evalCtx);
1115
1126
  return ChildrenForward{std::move(evalCtx), std::move(picked), separate, std::move(pickedIndices)};
1116
1127
  }
1117
1128
 
@@ -245,6 +245,21 @@ TEST(Projection, CutSlicesAtZEqualsZero) {
245
245
  EXPECT_TRUE(area < 1e-6 || std::fabs(area - 6.0) < 1e-6);
246
246
  }
247
247
 
248
+ // One polyhedron holding two overlapping cube shells, as a self-overlapping
249
+ // mesh like a tightly wound rope spool is (BelfrySCAD #831). Its slice is
250
+ // the union of the two squares, as OpenSCAD fills it -- not the two squares
251
+ // with their overlap cut out.
252
+ TEST(Projection, CutFillsWhereAPolyhedronOverlapsItself) {
253
+ Evaluated e = evalSrc(
254
+ "function box(o) = [for (z=[-1,1], y=[0,2], x=[0,2]) [x+o, y, z]];"
255
+ "f = [[0,1,3,2],[4,6,7,5],[0,4,5,1],[2,3,7,6],[0,2,6,4],[1,5,7,3]];"
256
+ "projection(cut=true) polyhedron(concat(box(0), box(1)),"
257
+ " concat([for (q=f) [for (i=q) i]], [for (q=f) [for (i=q) i+8]]));");
258
+ ASSERT_EQ(e.bodies.size(), 1u);
259
+ ASSERT_TRUE(e.bodies[0].section.has_value());
260
+ EXPECT_NEAR(e.bodies[0].section->Area(), 6.0, 1e-6);
261
+ }
262
+
248
263
  // -- offset -----------------------------------------------------------------
249
264
 
250
265
  TEST(Offset, RoundGrowsAreaBySquarePerimeterPlusCircleCorners) {
@@ -153,6 +153,23 @@ TEST(Polyhedron, TetrahedronVolumeMatchesAnalyticFormula) {
153
153
  EXPECT_NEAR(e.bodies[0].body->Volume(), 4.0 / 3.0, 1e-6);
154
154
  }
155
155
 
156
+ // Two copies of a corner a few ULPs apart, either side of a weld-grid
157
+ // boundary (x*1e6 == 0.5): rounding to the grid put them in different cells,
158
+ // so the seam stayed open and a sound BOSL2 thread drew as an open surface
159
+ // (BelfrySCAD #832). They weld now, and the solid is closed.
160
+ TEST(Polyhedron, TwinVerticesStraddlingTheWeldGridStillWeld) {
161
+ std::vector<std::string> log;
162
+ Evaluated e = evalSrc("a = 5e-7 * (1 - 1e-12); b = 5e-7 * (1 + 1e-12);"
163
+ "polyhedron("
164
+ "points=[[a,0,0],[2,0,0],[0,2,0],[0,0,2],[b,0,0]], "
165
+ "faces=[[0,1,2],[0,3,1],[4,2,3],[1,3,2]]);",
166
+ [&](const std::string& m) { log.push_back(m); });
167
+ EXPECT_TRUE(log.empty()) << log[0];
168
+ ASSERT_TRUE(e.bodies[0].body.has_value());
169
+ EXPECT_EQ(e.bodies[0].body->Status(), manifold::Manifold::Error::NoError);
170
+ EXPECT_NEAR(e.bodies[0].body->Volume(), 4.0 / 3.0, 1e-5);
171
+ }
172
+
156
173
  TEST(Polyhedron, MissingFacesWarnsAndDrawsNothing) {
157
174
  // Upstream builtin_polyhedron warns and draws nothing; the rest of the
158
175
  // script carries on (this used to stop the render).
@@ -212,3 +212,18 @@ TEST(Scoping, ReassignedVariableTakesItsLastValueAtItsFirstPosition) {
212
212
  for (const auto& m : out) warnings += m.find("but was overwritten") != std::string::npos;
213
213
  EXPECT_EQ(warnings, 3u) << "one per reassignment, even though m() could run again";
214
214
  }
215
+
216
+ // #815: BOSL2's attachable() runs its children by index, and children(N)
217
+ // used to skip the child block's assignments, so `last` was unknown.
218
+ TEST(Scoping, IndexedChildrenSeeTheirBlocksAssignments) {
219
+ std::vector<std::string> out;
220
+ evaluateSrc("module one() children(0);\n"
221
+ "module both() { children(1); children([0:1]); }\n"
222
+ "one() { n = 2; for (i = [0:n]) echo(a = i); }\n"
223
+ "both() { k = 3; echo(b = k); echo(c = k + 1); }\n",
224
+ [&](const std::string& m) { out.push_back(m); });
225
+ EXPECT_TRUE(anyContains(out, "ECHO: a = 2"));
226
+ EXPECT_TRUE(anyContains(out, "ECHO: b = 3"));
227
+ EXPECT_TRUE(anyContains(out, "ECHO: c = 4"));
228
+ EXPECT_FALSE(anyContains(out, "unknown variable"));
229
+ }
@@ -55,7 +55,7 @@ wheels = [
55
55
 
56
56
  [[package]]
57
57
  name = "openscad-cpp-evaluator"
58
- version = "1.49.3"
58
+ version = "1.49.4"
59
59
  source = { editable = "." }
60
60
  dependencies = [
61
61
  { name = "numpy" },