openscad-cpp-evaluator 1.20.2__tar.gz → 1.20.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 (222) hide show
  1. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/pyproject.toml +1 -1
  3. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/text.cpp +10 -0
  4. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/export.cpp +33 -18
  5. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/mesh_check.cpp +81 -26
  6. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_export.cpp +64 -0
  7. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_manifold_cache.cpp +22 -0
  8. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_mesh_check.cpp +20 -0
  9. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_text.cpp +27 -0
  10. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/.github/workflows/ci.yml +0 -0
  11. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/.github/workflows/wheels.yml +0 -0
  12. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/.gitignore +0 -0
  13. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/.gitmodules +0 -0
  14. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/CLAUDE.md +0 -0
  15. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/CMakeLists.txt +0 -0
  16. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/LICENSE +0 -0
  17. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/README.md +0 -0
  18. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/bindings/CMakeLists.txt +0 -0
  19. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/bindings/ast_to_py.cpp +0 -0
  20. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/bindings/module.cpp +0 -0
  21. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/cmake/embed_font.cmake +0 -0
  22. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/examples/CMakeLists.txt +0 -0
  23. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/examples/eval_perf_benchmark.cpp +0 -0
  24. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/examples/manifold_cache_reuse.cpp +0 -0
  25. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/examples/minimal_debugger.cpp +0 -0
  26. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  27. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/.gitignore +0 -0
  28. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  29. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  30. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/README.md +0 -0
  31. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  32. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  33. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  34. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  35. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  36. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  37. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  38. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  39. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  40. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  41. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  42. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  43. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  44. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  45. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  46. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  47. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  48. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/api.cpp +0 -0
  49. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  50. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  51. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  52. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  53. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  54. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  55. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  56. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  57. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  58. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  59. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  60. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  61. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  62. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  63. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  64. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  65. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/position.cpp +0 -0
  66. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  67. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  68. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  69. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  70. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  71. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  72. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  73. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  74. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  75. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  76. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  77. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  78. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  79. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  80. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  81. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  82. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  83. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  84. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  85. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  86. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  87. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  88. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  89. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  90. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  91. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  92. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  93. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  94. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  95. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  96. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  97. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  98. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  99. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  100. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  101. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  102. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  103. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  104. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  105. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  106. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  107. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  108. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  109. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  110. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  111. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  112. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/export.hpp +0 -0
  113. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  114. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  115. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  116. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  117. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  118. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  119. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  120. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  121. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  122. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  123. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  124. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  125. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  126. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  127. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  128. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  129. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/value.hpp +0 -0
  130. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  131. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/python/openscad_cpp_evaluator/__init__.py +0 -0
  132. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/resources/fonts/LICENSE +0 -0
  133. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  134. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  135. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  136. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  137. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/.DS_Store +0 -0
  138. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/CMakeLists.txt +0 -0
  139. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/booleans.cpp +0 -0
  140. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/builtins.hpp +0 -0
  141. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/call_args.cpp +0 -0
  142. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/color.cpp +0 -0
  143. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/control.cpp +0 -0
  144. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/dxf_dim.cpp +0 -0
  145. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/extrude.cpp +0 -0
  146. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/function_builtins.cpp +0 -0
  147. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/import.cpp +0 -0
  148. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/modifiers.cpp +0 -0
  149. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/primitives_2d.cpp +0 -0
  150. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/primitives_3d.cpp +0 -0
  151. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/registry.cpp +0 -0
  152. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/roof.cpp +0 -0
  153. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/segments.cpp +0 -0
  154. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/surface.cpp +0 -0
  155. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/topology.cpp +0 -0
  156. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/builtins/transforms.cpp +0 -0
  157. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/bytecode_compiler.cpp +0 -0
  158. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/bytecode_vm.cpp +0 -0
  159. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/colored_body.cpp +0 -0
  160. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/coverage.cpp +0 -0
  161. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/csg_generate.cpp +0 -0
  162. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/csg_resolve.cpp +0 -0
  163. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/css_colors.cpp +0 -0
  164. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/debug/debug_repl.cpp +0 -0
  165. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/debug_profile.cpp +0 -0
  166. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/eval_context.cpp +0 -0
  167. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/eval_error.cpp +0 -0
  168. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/eval_use.cpp +0 -0
  169. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/expr_eval.cpp +0 -0
  170. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/format_closure.cpp +0 -0
  171. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/import/dxf_import.cpp +0 -0
  172. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/import/mesh_import.cpp +0 -0
  173. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/import/surface_load.cpp +0 -0
  174. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/import/svg_import.cpp +0 -0
  175. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/manifold_cache.cpp +0 -0
  176. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/measure_geometry.cpp +0 -0
  177. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/stmt_eval.cpp +0 -0
  178. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/text/font_match.cpp +0 -0
  179. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/text/freetype_font_provider.cpp +0 -0
  180. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/text/text_metrics.cpp +0 -0
  181. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/user_calls.cpp +0 -0
  182. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/utf8.cpp +0 -0
  183. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/value.cpp +0 -0
  184. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/src/zip_stored.cpp +0 -0
  185. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/CMakeLists.txt +0 -0
  186. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_booleans.cpp +0 -0
  187. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_bytecode_compiler.cpp +0 -0
  188. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_children_separate.cpp +0 -0
  189. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_cli.cpp +0 -0
  190. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_control_flow.cpp +0 -0
  191. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_coverage.cpp +0 -0
  192. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_csg_tree.cpp +0 -0
  193. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_debug_hooks.cpp +0 -0
  194. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_dxf_svg_import.cpp +0 -0
  195. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_eval_context.cpp +0 -0
  196. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_expr_eval.cpp +0 -0
  197. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_extrude_roof.cpp +0 -0
  198. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_feature_detection.cpp +0 -0
  199. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_function_builtins.cpp +0 -0
  200. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_helpers.hpp +0 -0
  201. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_import_export.cpp +0 -0
  202. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_include_cache.cpp +0 -0
  203. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_multi_color_merge.cpp +0 -0
  204. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_primitives.cpp +0 -0
  205. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_profiling.cpp +0 -0
  206. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_python_bindings.py +0 -0
  207. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_render_expr.cpp +0 -0
  208. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_scope_trail.cpp +0 -0
  209. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_scoping.cpp +0 -0
  210. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_surface.cpp +0 -0
  211. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_tail_calls.cpp +0 -0
  212. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_transforms.cpp +0 -0
  213. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_unexpected_args.cpp +0 -0
  214. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_use.cpp +0 -0
  215. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_value.cpp +0 -0
  216. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_viewport_params.cpp +0 -0
  217. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tests/test_zip_stored.cpp +0 -0
  218. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tools/cli/CMakeLists.txt +0 -0
  219. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tools/cli/cli_lib.cpp +0 -0
  220. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tools/cli/cli_lib.hpp +0 -0
  221. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/tools/cli/main.cpp +0 -0
  222. {openscad_cpp_evaluator-1.20.2 → openscad_cpp_evaluator-1.20.5}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.20.2
3
+ Version: 1.20.5
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.20.2"
7
+ version = "1.20.5"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -70,6 +70,16 @@ CSGParams resolveText(Evaluator& ev, const oscad::ModularCall& node, EvalContext
70
70
  }
71
71
 
72
72
  CSGParams params;
73
+ // The handle is what generateText looks the face up by, but it is an
74
+ // index into THIS evaluator's FontProvider -- the first font resolved
75
+ // is handle 0 whatever it is. Two evaluators sharing a ManifoldCache
76
+ // (two tabs in a GUI, say) therefore agreed on "font_handle=0" for
77
+ // entirely different fonts, and the second render was served the
78
+ // first one's glyphs. The spec goes in the params too, so the cache
79
+ // key tells them apart: the same spec always resolves to the same
80
+ // face, and two specs that happen to resolve alike merely miss the
81
+ // cache rather than collide.
82
+ params["font_spec"] = Value{fontSpec};
73
83
  params["font_handle"] = Value{static_cast<double>(handle)};
74
84
  params["scale"] = Value{scale};
75
85
  params["segs"] = Value{static_cast<double>(segs)};
@@ -362,9 +362,7 @@ void meshToArrays(const manifold::MeshGL& mesh, std::vector<float>& verts, std::
362
362
  tris = mesh.triVerts;
363
363
  }
364
364
 
365
- bool boxesOverlap(const manifold::Manifold& a, const manifold::Manifold& b) {
366
- const manifold::Box ba = a.BoundingBox();
367
- const manifold::Box bb = b.BoundingBox();
365
+ bool boxesOverlap(const manifold::Box& ba, const manifold::Box& bb) {
368
366
  return !(ba.max.x < bb.min.x || bb.max.x < ba.min.x || ba.max.y < bb.min.y || bb.max.y < ba.min.y ||
369
367
  ba.max.z < bb.min.z || bb.max.z < ba.min.z);
370
368
  }
@@ -512,25 +510,42 @@ std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& b
512
510
  c.key = keys[0];
513
511
  claimedGroups.push_back(std::move(c));
514
512
  } else {
515
- // Reverse order + subtract-what-is-already-claimed is what makes
516
- // the LATER body win: by the time an earlier one is reached,
517
- // everything after it has already taken its volume.
518
- std::optional<manifold::Manifold> claimed;
513
+ // Reverse order is what makes the LATER body win: by the time
514
+ // an earlier one is reached, everything after it has already
515
+ // taken its volume.
516
+ //
517
+ // Each body is cut by the later ones that actually REACH it, not
518
+ // by a running union of all of them. The running union was
519
+ // quadratic -- it grows to span the whole model, so every one of
520
+ // n subtractions faced O(n) geometry however little the parts
521
+ // really touched, and a 100-body 3MF spent 6.6s on it (#177).
522
+ //
523
+ // The result is identical: a later body whose bounding box is
524
+ // disjoint from this one cannot intersect it, and subtracting a
525
+ // disjoint solid removes no volume. Bounding boxes settle that in
526
+ // nanoseconds, so what is left is proportional to how much the
527
+ // model overlaps rather than to how many parts it has.
528
+ //
529
+ // Skipping the subtraction entirely when nothing overlaps is not
530
+ // just a shortcut: `A - disjoint B` returns A's volume but
531
+ // REORDERS its triangle list, which throws away any per-triangle
532
+ // colours A carried. Most models are mostly disjoint parts, so
533
+ // without this a two-tone body lost its colours the moment any
534
+ // other differently-coloured body existed.
535
+ std::vector<manifold::Box> boxes;
536
+ boxes.reserve(solids.size());
537
+ for (const Solid& s : solids) boxes.push_back(s.man.BoundingBox());
538
+
519
539
  std::vector<Claimed> owned;
540
+ std::vector<manifold::Manifold> blockers;
520
541
  for (size_t n = solids.size(); n-- > 0;) {
521
542
  Solid& s = solids[n];
522
- manifold::Manifold piece = s.man;
523
- if (claimed) {
524
- // Skipping the subtraction when the bounding boxes
525
- // cannot overlap is not just a shortcut: `A - disjoint
526
- // B` returns A's volume but REORDERS its triangle list,
527
- // which throws away any per-triangle colours A carried.
528
- // Most models are mostly disjoint parts, so without this
529
- // a two-tone body lost its colours the moment any other
530
- // differently-coloured body existed.
531
- if (boxesOverlap(s.man, *claimed)) piece = s.man - *claimed;
543
+ blockers.clear();
544
+ for (size_t m = n + 1; m < solids.size(); ++m) {
545
+ if (boxesOverlap(boxes[n], boxes[m])) blockers.push_back(solids[m].man);
532
546
  }
533
- claimed = claimed ? (*claimed + s.man) : s.man;
547
+ manifold::Manifold piece = s.man;
548
+ if (!blockers.empty()) piece = s.man - addAll(blockers);
534
549
  if (piece.IsEmpty()) continue;
535
550
  Claimed c;
536
551
  c.man = std::move(piece);
@@ -79,20 +79,56 @@ size_t countUnwelded(const M& m) {
79
79
  // The faces around `v`, as (prev, next) pairs on the opposite edge. The link
80
80
  // is one cycle when following those pairs from any starting corner visits
81
81
  // all of them; more than one walk means the surface pinches at v.
82
+ //
83
+ // `vertTris` is CSR: the triangles around vertex v are
84
+ // vertTriList[vertTriStart[v] .. vertTriStart[v+1]).
85
+ //
86
+ // Every buffer here is hoisted out of the loop and cleared per vertex. The
87
+ // straightforward spelling -- an unordered_map of vectors for the link and a
88
+ // std::set for the walk, both built fresh per vertex -- allocated on the
89
+ // order of a dozen blocks for each of a mesh's vertices, millions of them on
90
+ // a 200K-triangle model, to answer a question about roughly six neighbours.
91
+ // Same reason the edge and face counts below collect flat and sort; see
92
+ // their comment.
82
93
  template <typename M>
83
94
  size_t countPinched(const M& m,
84
- const std::vector<std::vector<size_t>>& vertTris) {
95
+ const std::vector<size_t>& vertTriStart,
96
+ const std::vector<size_t>& vertTriList) {
85
97
  size_t pinched = 0;
86
- for (Vert v = 0; v < vertTris.size(); ++v) {
87
- const auto& tris = vertTris[v];
88
- if (tris.size() < 2) continue;
89
- // next[a] = b for the corner opposite v in each face, undirected so
90
- // a reversed neighbour still links up -- winding is checked
91
- // separately and should not show up as a pinch too.
92
- std::unordered_map<Vert, std::vector<Vert>> link;
93
- for (size_t t : tris) {
98
+ const size_t nVerts = vertTriStart.empty() ? 0 : vertTriStart.size() - 1;
99
+
100
+ std::vector<Vert> nodes; // the distinct neighbours of v
101
+ std::vector<size_t> parent; // union-find over indices into `nodes`
102
+ std::vector<std::pair<uint32_t, uint32_t>> edges;
103
+
104
+ // Linear search, not a map: a vertex has a handful of neighbours, and at
105
+ // that size a scan beats hashing outright -- no allocation, no hashing,
106
+ // and the whole thing stays in cache.
107
+ const auto indexOf = [&](Vert w) -> uint32_t {
108
+ for (size_t i = 0; i < nodes.size(); ++i) {
109
+ if (nodes[i] == w) return static_cast<uint32_t>(i);
110
+ }
111
+ nodes.push_back(w);
112
+ parent.push_back(parent.size());
113
+ return static_cast<uint32_t>(nodes.size() - 1);
114
+ };
115
+ const auto find = [&](size_t i) {
116
+ while (parent[i] != i) {
117
+ parent[i] = parent[parent[i]]; // path halving
118
+ i = parent[i];
119
+ }
120
+ return i;
121
+ };
122
+
123
+ for (Vert v = 0; v < nVerts; ++v) {
124
+ const size_t begin = vertTriStart[v], end = vertTriStart[v + 1];
125
+ if (end - begin < 2) continue;
126
+ nodes.clear();
127
+ parent.clear();
128
+ edges.clear();
129
+ for (size_t k = begin; k < end; ++k) {
94
130
  Vert w[3];
95
- triVerts(m, t, w);
131
+ triVerts(m, vertTriList[k], w);
96
132
  Vert a = 0, b = 0;
97
133
  int found = 0;
98
134
  for (int i = 0; i < 3; ++i) {
@@ -100,21 +136,20 @@ size_t countPinched(const M& m,
100
136
  (found++ == 0 ? a : b) = w[i];
101
137
  }
102
138
  if (found < 2) continue; // degenerate; counted elsewhere
103
- link[a].push_back(b);
104
- link[b].push_back(a);
139
+ edges.emplace_back(indexOf(a), indexOf(b));
105
140
  }
106
- if (link.empty()) continue;
107
- std::set<Vert> visited;
108
- std::vector<Vert> stack{link.begin()->first};
109
- while (!stack.empty()) {
110
- Vert cur = stack.back();
111
- stack.pop_back();
112
- if (!visited.insert(cur).second) continue;
113
- for (Vert nxt : link[cur]) {
114
- if (!visited.count(nxt)) stack.push_back(nxt);
115
- }
141
+ if (nodes.empty()) continue;
142
+ // Undirected, so a reversed neighbour still links up -- winding is
143
+ // checked separately and should not show up as a pinch too.
144
+ for (const auto& e : edges) {
145
+ const size_t ra = find(e.first), rb = find(e.second);
146
+ if (ra != rb) parent[ra] = rb;
147
+ }
148
+ size_t components = 0;
149
+ for (size_t i = 0; i < parent.size(); ++i) {
150
+ if (find(i) == i) ++components;
116
151
  }
117
- if (visited.size() < link.size()) ++pinched;
152
+ if (components > 1) ++pinched;
118
153
  }
119
154
  return pinched;
120
155
  }
@@ -158,10 +193,16 @@ MeshDiagnosis checkMesh(const M& mesh) {
158
193
  // index below; see its comment for the measurement.
159
194
  std::vector<std::pair<uint64_t, bool>> uses; // {undirected edge, traversed a->b}
160
195
  uses.reserve(tris * 3);
161
- std::vector<std::vector<size_t>> vertTris(nVerts);
162
196
  std::vector<std::array<Vert, 3>> seenFaces; // sorted-corner triples, deduplicated below
163
197
  seenFaces.reserve(tris);
164
198
 
199
+ // The triangles around each vertex, CSR rather than a vector per vertex:
200
+ // one inner vector per vertex is an allocation per vertex, and they are
201
+ // only ever read back as a flat run. Counted here, filled after the scan.
202
+ std::vector<size_t> vertTriStart(nVerts + 1, 0);
203
+ std::vector<size_t> vertTriList;
204
+ vertTriList.reserve(tris * 3);
205
+
165
206
  for (size_t t = 0; t < tris; ++t) {
166
207
  if (degenerate(mesh, t)) ++d.degenerateFaces;
167
208
  // Counted, but NOT removed from the topology. A zero-area triangle
@@ -181,12 +222,26 @@ MeshDiagnosis checkMesh(const M& mesh) {
181
222
  seenFaces.push_back(sorted);
182
223
 
183
224
  for (int i = 0; i < 3; ++i) {
184
- if (v[i] < nVerts) vertTris[v[i]].push_back(t);
225
+ if (v[i] < nVerts) ++vertTriStart[v[i] + 1];
185
226
  const Vert a = v[i], b = v[(i + 1) % 3];
186
227
  uses.emplace_back(edgeKey(undirected(a, b)), a < b);
187
228
  }
188
229
  }
189
230
 
231
+ for (size_t i = 0; i < nVerts; ++i) vertTriStart[i + 1] += vertTriStart[i];
232
+ vertTriList.resize(vertTriStart[nVerts]);
233
+ {
234
+ std::vector<size_t> fill(vertTriStart.begin(), vertTriStart.end() - 1);
235
+ for (size_t t = 0; t < tris; ++t) {
236
+ if (repeatsAVertex(mesh, t)) continue; // skipped above too
237
+ Vert v[3];
238
+ triVerts(mesh, t, v);
239
+ for (int i = 0; i < 3; ++i) {
240
+ if (v[i] < nVerts) vertTriList[fill[v[i]]++] = t;
241
+ }
242
+ }
243
+ }
244
+
190
245
  // A face seen N times is N-1 duplicates, which is what insert().second
191
246
  // counted one at a time.
192
247
  std::sort(seenFaces.begin(), seenFaces.end());
@@ -214,7 +269,7 @@ MeshDiagnosis checkMesh(const M& mesh) {
214
269
  i = j;
215
270
  }
216
271
 
217
- d.pinchedVertices = countPinched(mesh, vertTris);
272
+ d.pinchedVertices = countPinched(mesh, vertTriStart, vertTriList);
218
273
  d.unweldedVertices = countUnwelded(mesh);
219
274
  return d;
220
275
  }
@@ -5,6 +5,7 @@
5
5
  #include "test_helpers.hpp"
6
6
 
7
7
  #include <algorithm>
8
+ #include <array>
8
9
  #include <cstdint>
9
10
  #include <cstdio>
10
11
  #include <filesystem>
@@ -755,6 +756,69 @@ TEST(SplitColors, SingleMaterialGivesOneObject) {
755
756
  EXPECT_TRUE(objs[0].triColors.empty()); // no per-triangle colour to carry
756
757
  }
757
758
 
759
+ // Each body is cut only by the later bodies that actually reach it, rather
760
+ // than by a running union of all of them (#177). These pin the two things
761
+ // that rewrite must not change.
762
+
763
+ TEST(SplitColors, AFarAwayBodyChangesNothing) {
764
+ // A body nowhere near the others cannot take volume from them, so adding
765
+ // one must leave every other object exactly as it was. The old code
766
+ // subtracted the union of ALL later bodies and leaned on a bounding-box
767
+ // test against that union to stay correct -- once the union spanned the
768
+ // model, the test stopped discriminating.
769
+ const std::string pair =
770
+ "color(\"red\") cube(10); color(\"blue\") translate([5,0,0]) cube(10);";
771
+ std::vector<ColoredBody> without = evalToBodies(pair);
772
+ std::vector<ColoredBody> with =
773
+ evalToBodies(pair + " color(\"green\") translate([500,0,0]) cube(10);");
774
+
775
+ const std::vector<ExportObject> a = splitBodiesForExport(without, nullptr, false, true);
776
+ const std::vector<ExportObject> b = splitBodiesForExport(with, nullptr, false, true);
777
+ ASSERT_EQ(a.size(), 2u);
778
+ ASSERT_EQ(b.size(), 3u);
779
+ for (size_t i = 0; i < a.size(); ++i) {
780
+ EXPECT_EQ(a[i].tris.size(), b[i].tris.size()) << "object " << i << " was re-cut";
781
+ EXPECT_EQ(a[i].verts.size(), b[i].verts.size()) << "object " << i << " was re-cut";
782
+ }
783
+ }
784
+
785
+ TEST(SplitColors, LaterWinsAcrossAnInterveningColour) {
786
+ // red, blue, red -- all overlapping, in that order. The later body wins,
787
+ // so the second red takes its volume whole, blue keeps only what that
788
+ // red does not cover, and the first red keeps only what neither does.
789
+ //
790
+ // Worth pinning because the obvious way to make this cheaper -- union
791
+ // the same-coloured bodies first, then subtract per colour -- gets it
792
+ // wrong: it would let the first red beat the blue that comes after it.
793
+ std::vector<ColoredBody> bodies = evalToBodies(
794
+ "color(\"red\") cube(10);"
795
+ "color(\"blue\") translate([5,0,0]) cube(10);"
796
+ "color(\"red\") translate([10,0,0]) cube(10);");
797
+ const std::vector<ExportObject> objs = splitBodiesForExport(bodies, nullptr, false, true);
798
+ ASSERT_EQ(objs.size(), 2u); // one per colour, the two reds merged
799
+
800
+ const auto volumeOf = [](const ExportObject& o) {
801
+ double v = 0.0;
802
+ for (size_t t = 0; t + 2 < o.tris.size(); t += 3) {
803
+ const auto at = [&](size_t k) {
804
+ const size_t b = static_cast<size_t>(o.tris[t + k]) * 3;
805
+ return std::array<double, 3>{o.verts[b], o.verts[b + 1], o.verts[b + 2]};
806
+ };
807
+ const std::array<double, 3> p = at(0), q = at(1), r = at(2);
808
+ v += (p[0] * (q[1] * r[2] - q[2] * r[1]) - p[1] * (q[0] * r[2] - q[2] * r[0]) +
809
+ p[2] * (q[0] * r[1] - q[1] * r[0])) / 6.0;
810
+ }
811
+ return v;
812
+ };
813
+
814
+ // Blue spans x 5..15 and the later red takes x 10..20, leaving x 5..10:
815
+ // 5 x 10 x 10. The reds keep everything else: 2000 - 500.
816
+ double red = 0.0, blue = 0.0;
817
+ for (const ExportObject& o : objs) (o.color[2] > o.color[0] ? blue : red) += volumeOf(o);
818
+ EXPECT_NEAR(blue, 500.0, 1e-6);
819
+ EXPECT_NEAR(red, 1500.0, 1e-6);
820
+ }
821
+
758
822
  TEST(SplitColors, SingleMaterialWeldsTouchingColourRegions) {
759
823
  // Unioned, not concatenated: two touching cubes of different colours
760
824
  // must come out as one solid without the coincident interior faces
@@ -36,6 +36,28 @@ TEST(ManifoldCacheKey, BoolAndNumberParamsProduceDifferentCacheKeys) {
36
36
  EXPECT_NE(cacheKey(a), cacheKey(b));
37
37
  }
38
38
 
39
+ TEST(ManifoldCacheKey, TwoFontsWithTheSameHandleDoNotShareAKey) {
40
+ // A FontHandle indexes the EVALUATOR's own FontProvider, so the first
41
+ // font any evaluator resolves is handle 0 whatever it is. Two
42
+ // evaluators sharing one ManifoldCache -- two tabs in a GUI -- agreed
43
+ // on "font_handle=0" for entirely different fonts, and the second was
44
+ // served the first one's glyphs. text() puts the SPEC in the params
45
+ // too, so the key tells them apart.
46
+ CSGNode a;
47
+ a.kind = "text";
48
+ a.isBuiltin = true;
49
+ a.params["font_handle"] = Value{0.0};
50
+ a.params["font_spec"] = Value{std::string("Arial:style=Regular")};
51
+
52
+ CSGNode b;
53
+ b.kind = "text";
54
+ b.isBuiltin = true;
55
+ b.params["font_handle"] = Value{0.0}; // same handle, different font
56
+ b.params["font_spec"] = Value{std::string("Arial:style=Bold")};
57
+
58
+ EXPECT_NE(cacheKey(a), cacheKey(b));
59
+ }
60
+
39
61
  TEST(ManifoldCacheKey, IdenticalStructureProducesIdenticalKeyRegardlessOfParamInsertionOrder) {
40
62
  CSGNode a;
41
63
  a.kind = "cube";
@@ -76,6 +76,26 @@ TEST(MeshCheck, TwoTetrahedraSharingOneVertexPinchThere) {
76
76
  EXPECT_FALSE(d.manifold());
77
77
  }
78
78
 
79
+ TEST(MeshCheck, ThreeTetrahedraOnOneVertexAreStillOnePinchedVertex) {
80
+ // The link at vertex 0 falls into THREE pieces, not two. The count is of
81
+ // pinched VERTICES, so this is still 1 -- worth pinning because the walk
82
+ // only has to notice that the link is not one piece, and an
83
+ // implementation that counts pieces instead would say 2 here and agree
84
+ // with the two-tetrahedron case by luck.
85
+ manifold::MeshGL m = tetra();
86
+ for (int k = 0; k < 2; ++k) {
87
+ const uint32_t base = static_cast<uint32_t>(m.vertProperties.size() / 3);
88
+ const float z = -1.0f - static_cast<float>(k) * 2.0f;
89
+ m.vertProperties.insert(m.vertProperties.end(),
90
+ {0, 0, z, 1, 0, z, 0, 1, z});
91
+ m.triVerts.insert(m.triVerts.end(), {
92
+ 0u, base + 1, base, 0u, base, base + 2,
93
+ 0u, base + 2, base + 1, base, base + 1, base + 2});
94
+ }
95
+ const MeshDiagnosis d = checkMesh(m);
96
+ EXPECT_EQ(d.pinchedVertices, 1u) << d.summary();
97
+ }
98
+
79
99
  TEST(MeshCheck, AReversedFaceIsCaughtAsInconsistentWinding) {
80
100
  manifold::MeshGL m = tetra();
81
101
  std::swap(m.triVerts[1], m.triVerts[2]); // flip one face
@@ -422,3 +422,30 @@ TEST(TextArgs, FontIsPositionalButTheRestAreNot) {
422
422
  // NOTHING, and halign by name does move it.
423
423
  EXPECT_EQ(echoes[0], "ECHO: true, false, true, false");
424
424
  }
425
+
426
+ namespace oscadeval {
427
+
428
+ // Why text() must put the font SPEC in its CSG params and not just the
429
+ // handle: a handle numbers faces WITHIN one provider, and every Evaluator
430
+ // makes its own. Two evaluators sharing a ManifoldCache -- two GUI tabs --
431
+ // hand the cache the same number for different fonts (BelfrySCAD#434).
432
+ TEST(FontHandles, AreOnlyMeaningfulWithinOneProvider) {
433
+ FreetypeFontProvider a, b;
434
+ const FontHandle aReg = a.resolveFont("Arial:style=Regular");
435
+ const FontHandle aBold = a.resolveFont("Arial:style=Bold");
436
+ if (aReg == aBold) {
437
+ GTEST_SKIP() << "this machine resolves both specs to one face";
438
+ }
439
+ EXPECT_NE(aReg, aBold) << "within one provider, two fonts get two handles";
440
+ // What a second provider numbers them is NOT asserted: it depends on
441
+ // which faces that machine has and the order they are opened in. On
442
+ // macOS a fresh provider resolving Bold first returns 4 -- the same
443
+ // number the first provider gave REGULAR, which is the collision this
444
+ // whole change is about. On a Linux runner with only the bundled faces
445
+ // the numbering happens to line up instead (0 and 1 either way). That
446
+ // it CAN collide is the point, and it is not a property to pin to a
447
+ // number here; the end-to-end regression test lives in BelfrySCAD,
448
+ // where the collision reproduces in both directions.
449
+ }
450
+
451
+ } // namespace oscadeval