openscad-cpp-evaluator 1.49.4__tar.gz → 1.49.6__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.4 → openscad_cpp_evaluator-1.49.6}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/colored_body.hpp +6 -0
  3. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/mesh_check.hpp +14 -0
  4. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/pyproject.toml +1 -1
  5. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/booleans.cpp +5 -2
  6. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/extrude.cpp +8 -2
  7. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/primitives_3d.cpp +10 -15
  8. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/mesh_check.cpp +96 -43
  9. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_extrude_roof.cpp +40 -0
  10. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_primitives.cpp +17 -0
  11. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/.github/workflows/ci.yml +0 -0
  12. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/.github/workflows/wheels.yml +0 -0
  13. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/.gitignore +0 -0
  14. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/.gitmodules +0 -0
  15. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/CLAUDE.md +0 -0
  16. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/CMakeLists.txt +0 -0
  17. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/LICENSE +0 -0
  18. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/README.md +0 -0
  19. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/bindings/CMakeLists.txt +0 -0
  20. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/bindings/ast_to_py.cpp +0 -0
  21. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/bindings/module.cpp +0 -0
  22. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/cmake/embed_font.cmake +0 -0
  23. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/cmake/patch_manifold.cmake +0 -0
  24. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/examples/CMakeLists.txt +0 -0
  25. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/examples/eval_perf_benchmark.cpp +0 -0
  26. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/examples/manifold_cache_reuse.cpp +0 -0
  27. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/examples/minimal_debugger.cpp +0 -0
  28. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  29. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/.gitignore +0 -0
  30. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  31. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  32. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/LICENSE +0 -0
  33. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/README.md +0 -0
  34. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  35. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  36. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  37. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  38. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  39. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  40. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  41. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  42. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  43. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  44. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  45. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  46. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  47. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  48. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  49. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  50. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/.DS_Store +0 -0
  51. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  52. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/api.cpp +0 -0
  53. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  54. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  55. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  56. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  57. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  58. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  59. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  60. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  61. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  62. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  63. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  64. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  65. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  66. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  67. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  68. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  69. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/position.cpp +0 -0
  70. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  71. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  72. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  73. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  74. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  75. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  76. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  77. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  78. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  79. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
  80. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  81. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  82. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  83. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  84. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  85. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  86. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  87. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  88. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  89. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  90. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
  91. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  92. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  93. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  94. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  95. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  96. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  97. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  98. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  99. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  100. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  101. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  102. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  103. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  104. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  105. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  106. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  107. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  108. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  109. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  110. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  111. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  112. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  113. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  114. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  115. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  116. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  117. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/export.hpp +0 -0
  118. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  119. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  120. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  121. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  122. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  123. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  124. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  125. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  126. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  127. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  128. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/reassign.hpp +0 -0
  129. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  130. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  131. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  132. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  133. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  134. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/value.hpp +0 -0
  135. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  136. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/python/openscad_cpp_evaluator/__init__.py +0 -0
  137. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/resources/fonts/LICENSE +0 -0
  138. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  139. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  140. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  141. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  142. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/CMakeLists.txt +0 -0
  143. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/builtins.hpp +0 -0
  144. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/call_args.cpp +0 -0
  145. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/color.cpp +0 -0
  146. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/control.cpp +0 -0
  147. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/dxf_dim.cpp +0 -0
  148. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/function_builtins.cpp +0 -0
  149. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/import.cpp +0 -0
  150. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/modifiers.cpp +0 -0
  151. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/primitives_2d.cpp +0 -0
  152. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/profile_time.cpp +0 -0
  153. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/registry.cpp +0 -0
  154. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/roof.cpp +0 -0
  155. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/segments.cpp +0 -0
  156. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/surface.cpp +0 -0
  157. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/text.cpp +0 -0
  158. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/topology.cpp +0 -0
  159. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/builtins/transforms.cpp +0 -0
  160. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/bytecode_compiler.cpp +0 -0
  161. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/bytecode_vm.cpp +0 -0
  162. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/colored_body.cpp +0 -0
  163. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/coverage.cpp +0 -0
  164. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/csg_generate.cpp +0 -0
  165. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/csg_resolve.cpp +0 -0
  166. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/css_colors.cpp +0 -0
  167. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/debug/debug_repl.cpp +0 -0
  168. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/debug_profile.cpp +0 -0
  169. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/eval_context.cpp +0 -0
  170. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/eval_error.cpp +0 -0
  171. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/eval_use.cpp +0 -0
  172. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/export.cpp +0 -0
  173. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/expr_eval.cpp +0 -0
  174. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/format_closure.cpp +0 -0
  175. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/import/dxf_import.cpp +0 -0
  176. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/import/mesh_import.cpp +0 -0
  177. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/import/scene_import.cpp +0 -0
  178. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/import/surface_load.cpp +0 -0
  179. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/import/svg_import.cpp +0 -0
  180. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/manifold_cache.cpp +0 -0
  181. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/measure_geometry.cpp +0 -0
  182. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/stmt_eval.cpp +0 -0
  183. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/text/font_match.cpp +0 -0
  184. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/text/freetype_font_provider.cpp +0 -0
  185. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/text/text_metrics.cpp +0 -0
  186. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/user_calls.cpp +0 -0
  187. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/utf8.cpp +0 -0
  188. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/value.cpp +0 -0
  189. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/xkcd_colors.cpp +0 -0
  190. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/xkcd_colors.hpp +0 -0
  191. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/src/zip_stored.cpp +0 -0
  192. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/CMakeLists.txt +0 -0
  193. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_booleans.cpp +0 -0
  194. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_bytecode_compiler.cpp +0 -0
  195. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_children_separate.cpp +0 -0
  196. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_cli.cpp +0 -0
  197. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_control_flow.cpp +0 -0
  198. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_coverage.cpp +0 -0
  199. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_csg_tree.cpp +0 -0
  200. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_debug_hooks.cpp +0 -0
  201. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_discretizer.cpp +0 -0
  202. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_dxf_svg_import.cpp +0 -0
  203. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_eval_context.cpp +0 -0
  204. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_export.cpp +0 -0
  205. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_expr_eval.cpp +0 -0
  206. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_feature_detection.cpp +0 -0
  207. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_function_builtins.cpp +0 -0
  208. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_helpers.hpp +0 -0
  209. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_import_export.cpp +0 -0
  210. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_include_cache.cpp +0 -0
  211. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_manifold_cache.cpp +0 -0
  212. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_mesh_check.cpp +0 -0
  213. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_multi_color_merge.cpp +0 -0
  214. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_openscad_parity.cpp +0 -0
  215. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_profile_time.cpp +0 -0
  216. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_profiling.cpp +0 -0
  217. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_python_bindings.py +0 -0
  218. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_render_expr.cpp +0 -0
  219. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_scope_trail.cpp +0 -0
  220. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_scoping.cpp +0 -0
  221. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_surface.cpp +0 -0
  222. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_tail_calls.cpp +0 -0
  223. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_text.cpp +0 -0
  224. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_transforms.cpp +0 -0
  225. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_undef_args.cpp +0 -0
  226. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_unexpected_args.cpp +0 -0
  227. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_use.cpp +0 -0
  228. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_value.cpp +0 -0
  229. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_viewport_params.cpp +0 -0
  230. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tests/test_zip_stored.cpp +0 -0
  231. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tools/cli/CMakeLists.txt +0 -0
  232. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tools/cli/cli_lib.cpp +0 -0
  233. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tools/cli/cli_lib.hpp +0 -0
  234. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/tools/cli/main.cpp +0 -0
  235. {openscad_cpp_evaluator-1.49.4 → openscad_cpp_evaluator-1.49.6}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.49.4
3
+ Version: 1.49.6
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT AND OFL-1.1
@@ -132,6 +132,12 @@ struct ColoredBody {
132
132
  bool isDisplayOnly() const { return rawMesh.has_value(); }
133
133
  };
134
134
 
135
+ // A `%` body or a `#` operand's ghost copy: drawn, never part of the model,
136
+ // so an operation that consumes its children's geometry must skip it.
137
+ inline bool isModifierGhost(const ColoredBody& b) {
138
+ return b.role == BodyRole::Background || b.role == BodyRole::HighlightGhost;
139
+ }
140
+
135
141
  // The cached questions -- see ColoredBody::knownEmpty. Ask Manifold once per
136
142
  // body, never once per tree level.
137
143
  inline bool bodyIsEmpty(ColoredBody& b) {
@@ -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.4"
7
+ version = "1.49.6"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -64,11 +64,14 @@ RoleSplit splitByRole(const std::vector<ColoredBody>& bodies) {
64
64
 
65
65
  // Unions every 2D child into a single CrossSection; nullopt if there are no
66
66
  // 2D children at all. Mirrors _to_cross_section. Shared by
67
- // linear_extrude/rotate_extrude/roof (extrude.cpp, roof.cpp).
67
+ // linear_extrude/rotate_extrude/offset/roof (extrude.cpp, roof.cpp).
68
+ // A `%` body and a `#` operand's ghost copy are display aids, not model:
69
+ // unioning them in filled a differenced-away notch back in (BelfrySCAD
70
+ // #866). OpenSCAD shows neither under these operations, so they are dropped.
68
71
  std::optional<manifold::CrossSection> toCrossSection(const std::vector<ColoredBody>& bodies) {
69
72
  std::optional<manifold::CrossSection> cs;
70
73
  for (const ColoredBody& b : bodies) {
71
- if (!b.section) continue;
74
+ if (!b.section || isModifierGhost(b)) continue;
72
75
  cs = cs ? (*cs + *b.section) : *b.section;
73
76
  }
74
77
  return cs;
@@ -471,14 +471,20 @@ std::vector<ColoredBody> generateProjection(Evaluator&, const CSGParams& params,
471
471
  const std::vector<ColoredBody> bodies = flattenCsgTree(children);
472
472
  std::vector<ColoredBody> bodies3d;
473
473
  for (const ColoredBody& c : bodies) {
474
- if (c.body) bodies3d.push_back(c);
474
+ // Not a `%` body or a `#` ghost: projecting them drew the
475
+ // differenced-away part's outline (BelfrySCAD #866). See toCrossSection.
476
+ if (c.body && !isModifierGhost(c)) bodies3d.push_back(c);
475
477
  }
476
478
  if (bodies3d.empty()) return {};
477
479
 
478
480
  const ColoredBody combined = combineBodies(bodies3d);
479
481
  manifold::CrossSection cs;
480
482
  if (std::get<bool>(params.at("cut"))) {
481
- cs = manifold::CrossSection(combined.body->Slice(0.0), manifold::CrossSection::FillRule::EvenOdd);
483
+ // Positive, as OpenSCAD fills it. A self-overlapping polyhedron (a
484
+ // rope spool whose loops overlap, BelfrySCAD #831) slices to
485
+ // overlapping contours, and EvenOdd cut every overlap out as a hole.
486
+ // A clean solid slices the same either way.
487
+ cs = manifold::CrossSection(combined.body->Slice(0.0), manifold::CrossSection::FillRule::Positive);
482
488
  } else {
483
489
  const manifold::Polygons polys = combined.body->Project();
484
490
  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
@@ -245,6 +245,46 @@ 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
+
263
+ // A `#` operand is drawn again as a ghost and a `%` body is drawn but not
264
+ // modelled; neither may reach the operation. Both leaked into projection()
265
+ // and every 2D-consuming operation (BelfrySCAD #866): the ghost refilled
266
+ // the cut-away corner and the `%` cube added its square.
267
+ TEST(Projection, ModifierGhostsAreNotProjected) {
268
+ for (const char* src : {
269
+ "projection() difference() { cube([4,4,1]); #translate([-1,-1,-1]) cube(3); }",
270
+ "projection() union() { difference() { cube([4,4,1]); #translate([-1,-1,-1]) cube(3); }"
271
+ " %translate([10,0,0]) cube(1); }"}) {
272
+ Evaluated e = evalSrc(src);
273
+ double area = 0;
274
+ for (const ColoredBody& b : e.bodies)
275
+ if (b.section) area += b.section->Area();
276
+ EXPECT_NEAR(area, 12.0, 1e-6) << src;
277
+ }
278
+ }
279
+
280
+ TEST(LinearExtrude, ModifierGhostsAreNotExtruded) {
281
+ Evaluated e = evalSrc("linear_extrude(1) difference() { square(4); #translate([-1,-1]) square(3); }");
282
+ double volume = 0;
283
+ for (const ColoredBody& b : e.bodies)
284
+ if (b.body) volume += b.body->Volume();
285
+ EXPECT_NEAR(volume, 12.0, 1e-6);
286
+ }
287
+
248
288
  // -- offset -----------------------------------------------------------------
249
289
 
250
290
  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).