openscad-cpp-evaluator 1.51.0__tar.gz → 1.52.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/CLAUDE.md +76 -10
  2. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/CMakeLists.txt +9 -3
  3. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/PKG-INFO +1 -1
  4. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/bindings/module.cpp +28 -0
  5. openscad_cpp_evaluator-1.52.0/include/openscad_cpp_evaluator/offset3d.hpp +16 -0
  6. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/pyproject.toml +1 -1
  7. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/python/openscad_cpp_evaluator/__init__.py +7 -0
  8. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/CMakeLists.txt +1 -0
  9. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/extrude.cpp +36 -6
  10. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/function_builtins.cpp +1 -0
  11. openscad_cpp_evaluator-1.52.0/src/builtins/offset3d.cpp +1141 -0
  12. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/csg_generate.cpp +3 -1
  13. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/CMakeLists.txt +1 -0
  14. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_booleans.cpp +3 -2
  15. openscad_cpp_evaluator-1.52.0/tests/test_offset3d.cpp +128 -0
  16. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_python_bindings.py +16 -0
  17. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/.github/workflows/ci.yml +0 -0
  18. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/.github/workflows/wheels.yml +0 -0
  19. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/.gitignore +0 -0
  20. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/.gitmodules +0 -0
  21. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/LICENSE +0 -0
  22. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/README.md +0 -0
  23. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/bindings/CMakeLists.txt +0 -0
  24. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/bindings/ast_to_py.cpp +0 -0
  25. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/cmake/embed_font.cmake +0 -0
  26. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/cmake/patch_manifold.cmake +0 -0
  27. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/examples/CMakeLists.txt +0 -0
  28. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/examples/eval_perf_benchmark.cpp +0 -0
  29. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/examples/manifold_cache_reuse.cpp +0 -0
  30. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/examples/minimal_debugger.cpp +0 -0
  31. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  32. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/.gitignore +0 -0
  33. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  34. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  35. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/LICENSE +0 -0
  36. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/README.md +0 -0
  37. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  38. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  39. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  40. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  41. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  42. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  43. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  44. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  45. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  46. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  47. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  48. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  49. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  50. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  51. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  52. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  53. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/.DS_Store +0 -0
  54. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  55. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
  56. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  57. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  58. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  59. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  60. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  61. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  62. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  63. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  64. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  65. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  66. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  67. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  68. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  69. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  70. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  71. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  72. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  73. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  74. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  75. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  76. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  77. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  78. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  79. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  80. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  81. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  82. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
  83. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  84. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  85. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  86. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  87. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  88. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  89. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  90. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  91. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  92. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  93. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
  94. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  95. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  96. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  97. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  98. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  99. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  100. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  101. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  102. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  103. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  104. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  105. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  106. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  107. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  108. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  109. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  110. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  111. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  112. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  113. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  114. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  115. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  116. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  117. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  118. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  119. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  120. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  121. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  122. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  123. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  124. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  125. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  126. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  127. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  128. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  129. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  130. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  131. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  132. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  133. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/reassign.hpp +0 -0
  134. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  135. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  136. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  137. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  138. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  139. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  140. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  141. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/resources/fonts/LICENSE +0 -0
  142. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  143. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  144. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  145. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  146. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/alpha_wrap.cpp +0 -0
  147. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/booleans.cpp +0 -0
  148. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/builtins.hpp +0 -0
  149. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/call_args.cpp +0 -0
  150. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/color.cpp +0 -0
  151. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/control.cpp +0 -0
  152. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/dxf_dim.cpp +0 -0
  153. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/export_as.cpp +0 -0
  154. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/import.cpp +0 -0
  155. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/modifiers.cpp +0 -0
  156. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/primitives_2d.cpp +0 -0
  157. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/primitives_3d.cpp +0 -0
  158. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/profile_time.cpp +0 -0
  159. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/registry.cpp +0 -0
  160. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/roof.cpp +0 -0
  161. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/segments.cpp +0 -0
  162. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/surface.cpp +0 -0
  163. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/text.cpp +0 -0
  164. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/topology.cpp +0 -0
  165. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/builtins/transforms.cpp +0 -0
  166. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/bytecode_compiler.cpp +0 -0
  167. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/bytecode_vm.cpp +0 -0
  168. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/colored_body.cpp +0 -0
  169. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/coverage.cpp +0 -0
  170. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/csg_resolve.cpp +0 -0
  171. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/css_colors.cpp +0 -0
  172. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/debug/debug_repl.cpp +0 -0
  173. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/debug_profile.cpp +0 -0
  174. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/eval_context.cpp +0 -0
  175. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/eval_error.cpp +0 -0
  176. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/eval_use.cpp +0 -0
  177. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/export.cpp +0 -0
  178. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/expr_eval.cpp +0 -0
  179. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/format_closure.cpp +0 -0
  180. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/import/dxf_import.cpp +0 -0
  181. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/import/mesh_import.cpp +0 -0
  182. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/import/scene_import.cpp +0 -0
  183. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/import/surface_load.cpp +0 -0
  184. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/import/svg_import.cpp +0 -0
  185. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/manifold_cache.cpp +0 -0
  186. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/measure_geometry.cpp +0 -0
  187. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/mesh_check.cpp +0 -0
  188. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/stmt_eval.cpp +0 -0
  189. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/text/font_match.cpp +0 -0
  190. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/text/freetype_font_provider.cpp +0 -0
  191. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/text/text_metrics.cpp +0 -0
  192. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/user_calls.cpp +0 -0
  193. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/utf8.cpp +0 -0
  194. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/value.cpp +0 -0
  195. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/xkcd_colors.cpp +0 -0
  196. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/xkcd_colors.hpp +0 -0
  197. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/src/zip_stored.cpp +0 -0
  198. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_alpha_wrap.cpp +0 -0
  199. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_bytecode_compiler.cpp +0 -0
  200. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_children_separate.cpp +0 -0
  201. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_cli.cpp +0 -0
  202. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_control_flow.cpp +0 -0
  203. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_coverage.cpp +0 -0
  204. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_csg_tree.cpp +0 -0
  205. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_debug_hooks.cpp +0 -0
  206. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_discretizer.cpp +0 -0
  207. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_dxf_svg_import.cpp +0 -0
  208. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_eval_context.cpp +0 -0
  209. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_export.cpp +0 -0
  210. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_export_as.cpp +0 -0
  211. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_expr_eval.cpp +0 -0
  212. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_extrude_roof.cpp +0 -0
  213. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_feature_detection.cpp +0 -0
  214. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_function_builtins.cpp +0 -0
  215. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_helpers.hpp +0 -0
  216. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_import_export.cpp +0 -0
  217. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_include_cache.cpp +0 -0
  218. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_manifold_cache.cpp +0 -0
  219. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_mesh_check.cpp +0 -0
  220. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_multi_color_merge.cpp +0 -0
  221. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_openscad_parity.cpp +0 -0
  222. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_primitives.cpp +0 -0
  223. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_profile_time.cpp +0 -0
  224. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_profiling.cpp +0 -0
  225. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_render_expr.cpp +0 -0
  226. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_scope_trail.cpp +0 -0
  227. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_scoping.cpp +0 -0
  228. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_surface.cpp +0 -0
  229. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_tail_calls.cpp +0 -0
  230. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_text.cpp +0 -0
  231. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_transforms.cpp +0 -0
  232. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_undef_args.cpp +0 -0
  233. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_unexpected_args.cpp +0 -0
  234. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_use.cpp +0 -0
  235. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_value.cpp +0 -0
  236. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_viewport_params.cpp +0 -0
  237. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tests/test_zip_stored.cpp +0 -0
  238. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/third_party/predicates/predicates.c +0 -0
  239. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/third_party/predicates/predicates.h +0 -0
  240. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tools/cli/CMakeLists.txt +0 -0
  241. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tools/cli/cli_lib.cpp +0 -0
  242. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tools/cli/cli_lib.hpp +0 -0
  243. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/tools/cli/main.cpp +0 -0
  244. {openscad_cpp_evaluator-1.51.0 → openscad_cpp_evaluator-1.52.0}/uv.lock +0 -0
@@ -535,11 +535,12 @@ cmake -S . -B build -DFETCHCONTENT_FULLY_DISCONNECTED=ON -DBUILD_TESTING=OFF \
535
535
  -DFETCHCONTENT_SOURCE_DIR_BOOST_CONFIG=... -DFETCHCONTENT_SOURCE_DIR_BOOST_POLYGON=... \
536
536
  -DFETCHCONTENT_SOURCE_DIR_FREETYPE=... -DFETCHCONTENT_SOURCE_DIR_HARFBUZZ=... \
537
537
  -DFETCHCONTENT_SOURCE_DIR_STB=... -DFETCHCONTENT_SOURCE_DIR_CPP_LINENOISE=... \
538
- -DFETCHCONTENT_SOURCE_DIR_NLOHMANN_JSON=...
538
+ -DFETCHCONTENT_SOURCE_DIR_NLOHMANN_JSON=... -DFETCHCONTENT_SOURCE_DIR_TBB=...
539
539
  ```
540
540
 
541
- That is the whole list for a wheel build (tests off; GoogleTest is the only other download, and
542
- TBB only with `MANIFOLD_PAR`). Clipper2 is Manifold's own download, pinned in Manifold's
541
+ That is the whole list for a wheel build (tests off; GoogleTest is the only other download).
542
+ Clipper2 and oneTBB (v2022.3.0, for `MANIFOLD_PAR`, which the root `CMakeLists.txt` forces on
543
+ with Manifold's builtin static TBB) are Manifold's own downloads, pinned in Manifold's
543
544
  `cmake/manifoldDeps.cmake`; nlohmann_json is the parser's. FetchContent skips `PATCH_COMMAND` for a
544
545
  supplied source, so the root `CMakeLists.txt` runs `cmake/patch_manifold.cmake` itself when
545
546
  `FETCHCONTENT_SOURCE_DIR_MANIFOLD` is set -- otherwise an offline build would quietly ship the heap
@@ -1070,9 +1071,10 @@ grep for `ponytail:`.
1070
1071
  reference's `isValidDim` + `collectChildren2D`/`collectChildren3D`: a node's dimension is fixed by
1071
1072
  its first non-background, non-empty child, and every child of the other dimension is dropped with
1072
1073
  `Mixing 2D and 3D objects is not supported` followed by `Ignoring {N}D child object for {M}D
1073
- operation`. `linear_extrude`/`rotate_extrude`/`offset` are always-2D and `projection` always-3D, so
1074
+ operation`. `linear_extrude`/`rotate_extrude` are always-2D and `projection` always-3D, so
1074
1075
  they skip the "mixing" line and emit only the second; `roof()` emits neither, matching the
1075
- reference. Background (`%`) children are exempt.
1076
+ reference. `offset()` takes either dimension (the solid offset of `offset3d.cpp`), so it follows
1077
+ the boolean rule: the first real child decides. Background (`%`) children are exempt.
1076
1078
  **The emptiness test is cached on the body (`ColoredBody::knownEmpty`, v1.17.0).** It used to
1077
1079
  call `Manifold::NumTri()` on every child at every level, and every Manifold accessor goes through
1078
1080
  `GetImpl()`, which materialises a lazy transform (a full mesh + collider copy) and runs a lazy
@@ -1123,10 +1125,8 @@ grep for `ponytail:`.
1123
1125
  (`Manifold::MinkowskiDifference`). 3D only, matching `minkowski()`'s own 3D-wins dispatch, since
1124
1126
  Manifold has no CrossSection erosion and 2D already has one in `offset(r=-N)`.
1125
1127
 
1126
- Worth knowing why this is a module rather than an extension of `offset()`: a *positive* 3D offset
1127
- is already expressible as `minkowski() { X; sphere(r); }`, so `offset(r=N)` on a solid would have
1128
- been pure sugar. Erosion is the half that is genuinely unavailable, and naming it after the
1129
- operation beats borrowing a 2D name for it. Eroding a cube by a cube is exact (no curved tool to
1128
+ It predates the 3D `offset()` (see `## offset() in 3D`), which now erodes a solid too, and is kept
1129
+ because it erodes by any tool, not only a ball, cube or octahedron. Eroding a cube by a cube is exact (no curved tool to
1130
1130
  tessellate), which is what the tests assert on; a sphere tool lands a little large, and that gap
1131
1131
  is the sphere's tessellation rather than an error in the erosion.
1132
1132
  - `src/builtins/extrude.cpp` — `linear_extrude()`/`rotate_extrude()`/`projection(cut=)`. All three
@@ -1140,7 +1140,9 @@ grep for `ponytail:`.
1140
1140
  `generateOffset`, also in this file) is the other CrossSection-transform-shaped builtin sharing
1141
1141
  `toCrossSection`; no children/generate-time-bounds dependency, so its resolve/generate split is
1142
1142
  the simple kind (segment count for `r=` IS resolvable early here, unlike `rotate_extrude`'s,
1143
- since `offset`'s own radius argument is already known at resolve time).
1143
+ since `offset`'s own radius argument is already known at resolve time). A 3D child takes
1144
+ `offset3d()` instead (`## offset() in 3D`): the foreground solids are unioned and offset as one,
1145
+ `join` round/miter/square selecting r/delta/chamfer, and the result tagged as generated.
1144
1146
  - `src/builtins/roof.cpp` — `roof()`: the default `method="voronoi"` built on Boost.Polygon's
1145
1147
  segment Voronoi diagram (see the `roof()` note above for the full story of why this replaced an
1146
1148
  earlier straight-skeleton approximation mid-Phase-6), and `method="straight"`'s straight-skeleton
@@ -2037,3 +2039,67 @@ formats have no object names and ignore it. Geometry outside any `export_as()` i
2037
2039
  `export_as()` and drops an unknown module's children, so a portable script guards on
2038
2040
  `supported_feature("export-as")`.
2039
2041
  - `src/builtins/export_as.cpp`; tests in `tests/test_export_as.cpp`.
2042
+
2043
+ ## `offset()` in 3D
2044
+
2045
+ `offset(r=)`, `offset(delta=)` and `offset(delta=, chamfer=true)` of a solid, outward and inward
2046
+ (`src/builtins/offset3d.cpp`, entry `offset3d()` in `include/openscad_cpp_evaluator/offset3d.hpp`;
2047
+ `supported_feature("offset-3d")` is 1). Upstream OpenSCAD's `offset()` is 2D only and drops a 3D
2048
+ child, so a portable script guards it. The modes mean what they mean in 2D: every face moves `d`
2049
+ along its normal; `delta=` keeps edges and corners sharp (**no miter limit**, as Clipper's 1e6 is
2050
+ none, so a sharp edge's miter reaches `d/sin(half its angle)`); `chamfer=true` cuts them flat
2051
+ (exactly `minkowski()` with an octahedron on an axis-aligned part); `r=` rounds them with a faceted
2052
+ ball **inscribed** in the true sphere (every vertex on it), `$fn`/`$fa`/`$fs` counted from `|r|` as
2053
+ in 2D -- so the default gives `r=1` five sides, and `$fn` is usually wanted. No published method
2054
+ covers all three modes (Cao 2024's mitered offset needs exact rationals; Zint 2023 and PCO round
2055
+ only), so the construction is our own, worked out in a Python prototype and checked by suites of
2056
+ exact volumes, topology, fuzzing, refine invariance and pathological meshes before the port.
2057
+
2058
+ **Construction.** Outward is `A ∪ pieces`: a slab per triangle (the face swept by `d`), a wedge per
2059
+ convex edge (hull of the two faces' offset points; plus the miter points for `delta=`; a capsule
2060
+ for `r=`) and a cap per vertex (the same at a corner). Inward builds the same pieces on A with
2061
+ every triangle flipped and subtracts their union from A -- no bounding box, and a feature thinner
2062
+ than `2d` vanishes by construction. Outward `r=` is Manifold's `MinkowskiSum` with the inscribed
2063
+ ball, which is this construction and faster. Each piece is convex, made by `hullChecked` (hull in a
2064
+ PCA-whitened frame, since Manifold's hull dropped corners of long thin point sets, then every input
2065
+ point verified against the hull's planes). Mitered pieces are trimmed by the shifted half-spaces
2066
+ of the faces at their end vertices -- the ends of the whole straight edge (`chainEnds`), not of the
2067
+ triangle edge, or `refine()`'s midpoints left a 0.6 spike -- and by far faces only within their
2068
+ own convex patch (faces joined through convex or flat edges), which keeps separate parts apart.
2069
+
2070
+ **Robustness rules, each from a failure:**
2071
+ - Pieces start `e = min(d, max(1e-5·d, 1e-9·scale))` behind the surface, so none shares a plane
2072
+ with A (coincident faces left a rounding-thin skin); slabs widen by `e` past concave and flat
2073
+ edges (otherwise zero-thickness fins, genus -87); wedges reach `1e-3·d` into both slabs and caps
2074
+ along their edges, as absolute distances (fractions of an edge made `refine(2)` change the result).
2075
+ - Every piece is grown by `1e-9·scale`, so offsets meeting exactly overlap rather than touch
2076
+ (touching counts as joined, as in Minkowski). Expect volumes ~1e-9 relative off exact.
2077
+ - The work runs at the origin (Manifold's union lost 0.35 of a chamfered cube at 1e5 from it), and
2078
+ the result is moved back as a bare mesh: `Translate()` of a lazy CSG result re-runs its booleans
2079
+ out there.
2080
+ - **`bare()` everywhere**: pieces and intermediate leaves are rebuilt from vertices and triangles
2081
+ alone. A Manifold rebuilt from `GetMeshGL64()` keeps face IDs and the like, and unions of such
2082
+ pieces lost whole wedges (adding six pieces once made the volume smaller). Manifold 3.5.4 does
2083
+ the same, so a bump does not help.
2084
+ - **`lostPieces()` checks the union against its pieces.** Manifold's union is order- and
2085
+ thread-dependent (TBB lost 0.045 of one piece where the single-threaded build did not). Pieces are
2086
+ coloured so that no two of a colour have overlapping boxes, each colour `Compose`d (exact for
2087
+ disjoint pieces) and tested with one boolean against the bare result -- against a lazy one,
2088
+ Manifold flattened `G - (A + pieces)` into `G - A - pieces` and hid every loss. A remnant thicker
2089
+ than `10e` (`2V/A`) marks its pieces lost; they are added again onto the bare result, up to 3 rounds.
2090
+ - `Simplify()` is not shape-safe (it moved 0.063 of a volume at 9e-12): each simplify is kept only
2091
+ if the volume and the count and genus of non-empty shells are unchanged. The last one, at
2092
+ `1e-9·scale` before moving back, collapses Manifold's own boolean slivers (vertices 1e-12 apart)
2093
+ while that is safe -- off the origin, Manifold collapsed them itself and once lost 0.114.
2094
+ - `cleanShells`: zero-volume shells are dropped, and each remaining shell is probed against the
2095
+ definition (in A, in a piece) so a spurious solid or void goes and a real one stays. A void is
2096
+ never `Compose`d back (Compose ignores an inside-out shell): solids minus voids turned right side
2097
+ out.
2098
+ - `|d|` below `1e-7` of the part's size returns the input: double precision cannot place it.
2099
+
2100
+ **Known limits.** Exact-contact chamfer cases (two faces meeting at precisely the distance where a
2101
+ chamfer closes) can come out with the wrong genus, 4 of the 96 in the review suite. Speed is about
2102
+ 2 s for a 32-segment sphere's `delta=1` (pieces built single-threaded 1.1 s, union 0.85 s, lost
2103
+ check 0.4 s), so it is a tool for parts, not for dense scans. `OFFSET3D_TIMING=1` prints each phase's
2104
+ wall time to stderr. `_offset3d(verts, tris, d, mode, segs)` in the Python facade is the test hook
2105
+ the prototype's suites run against; not public API. Tests: `tests/test_offset3d.cpp`.
@@ -120,9 +120,13 @@ target_include_directories(openscad_cpp_parser PUBLIC
120
120
  include(FetchContent)
121
121
 
122
122
  # MANIFOLD_TEST off: we don't want Manifold's own (large) test/sample/extras
123
- # suite built as part of this project. MANIFOLD_PAR stays at its own default
124
- # (OFF) -- no TBB parallel backend needed yet; revisit if profiling later
125
- # shows single-threaded Boolean ops are a bottleneck. MANIFOLD_CROSS_SECTION
123
+ # suite built as part of this project. MANIFOLD_PAR on: Manifold's TBB
124
+ # backend made 3D offset() 2.1-3.3x faster (sphere(10,32) delta=1: 3.6 s ->
125
+ # 1.7 s), and big booleans with it. TBB is always Manifold's own pinned copy
126
+ # (oneTBB v2022.3.0, fetched and linked statically): a system TBB found by
127
+ # find_package would make the wheel depend on a library it does not ship.
128
+ # Booleans are not order-independent under it -- the same pieces unioned
129
+ # differently -- which offset3d.cpp's lostPieces() checks for. MANIFOLD_CROSS_SECTION
126
130
  # stays at its own default (ON) -- 2D CrossSection support is needed from
127
131
  # Phase 3 onward.
128
132
  #
@@ -137,6 +141,8 @@ include(FetchContent)
137
141
  # fixes Windows and is a no-op everywhere else this project already worked.
138
142
  set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
139
143
  set(MANIFOLD_TEST OFF CACHE BOOL "" FORCE)
144
+ set(MANIFOLD_PAR ON CACHE BOOL "" FORCE)
145
+ set(MANIFOLD_USE_BUILTIN_TBB ON CACHE BOOL "" FORCE)
140
146
  FetchContent_Declare(
141
147
  manifold
142
148
  GIT_REPOSITORY https://github.com/elalish/manifold.git
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.51.0
3
+ Version: 1.52.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT AND OFL-1.1
@@ -25,6 +25,7 @@
25
25
  #include "openscad_cpp_evaluator/export.hpp"
26
26
  #include "openscad_cpp_evaluator/freetype_font_provider.hpp"
27
27
  #include "openscad_cpp_evaluator/mesh_check.hpp"
28
+ #include "openscad_cpp_evaluator/offset3d.hpp"
28
29
  #include "openscad_cpp_evaluator/manifold_cache.hpp"
29
30
  #include "openscad_cpp_evaluator/coverage.hpp"
30
31
  #include "openscad_cpp_evaluator/profile.hpp"
@@ -306,6 +307,30 @@ nb::tuple stripSliversPy(const std::vector<float>& verts, const std::vector<uint
306
307
  return nb::make_tuple(out.vertProperties, out.triVerts, rep);
307
308
  }
308
309
 
310
+ // offset() on one closed mesh, without a script round trip: what the 3D
311
+ // offset's test suites drive. Double precision both ways -- the float32
312
+ // MeshGL is ~0.008 of resolution at 1e5. mode is "r", "delta" or "chamfer".
313
+ nb::tuple offset3dPy(const std::vector<double>& verts, const std::vector<uint64_t>& tris, double d,
314
+ const std::string& mode, int segs) {
315
+ manifold::MeshGL64 m;
316
+ m.numProp = 3;
317
+ m.vertProperties = verts;
318
+ m.triVerts = tris;
319
+ const oscadeval::Offset3DMode md = mode == "r" ? oscadeval::Offset3DMode::Round
320
+ : mode == "chamfer" ? oscadeval::Offset3DMode::Chamfer
321
+ : oscadeval::Offset3DMode::Delta;
322
+ manifold::MeshGL64 out;
323
+ std::vector<double> ov;
324
+ {
325
+ nb::gil_scoped_release rel;
326
+ out = oscadeval::offset3d(manifold::Manifold(m), d, md, segs).GetMeshGL64();
327
+ ov.reserve(out.NumVert() * 3);
328
+ for (size_t i = 0; i < out.NumVert(); ++i)
329
+ for (size_t k = 0; k < 3; ++k) ov.push_back(out.vertProperties[i * out.numProp + k]);
330
+ }
331
+ return nb::make_tuple(ov, out.triVerts);
332
+ }
333
+
309
334
  nb::dict checkMeshPy(const std::vector<float>& verts, const std::vector<uint32_t>& tris) {
310
335
  manifold::MeshGL m;
311
336
  m.numProp = 3;
@@ -1052,6 +1077,9 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
1052
1077
  m.def("strip_slivers", &stripSliversPy, nb::arg("verts"), nb::arg("tris"),
1053
1078
  "Remove zero-area faces and repair the T-joints their removal "
1054
1079
  "exposes. Returns (verts, tris, report).");
1080
+ m.def("offset3d", &offset3dPy, nb::arg("verts"), nb::arg("tris"), nb::arg("d"), nb::arg("mode"),
1081
+ nb::arg("segs"),
1082
+ "3D offset of one closed mesh: returns (verts, tris) as flat lists.");
1055
1083
  m.def("check_mesh", &checkMeshPy, nb::arg("verts"), nb::arg("tris"),
1056
1084
  "Diagnose a triangle mesh against the manifoldness conditions. "
1057
1085
  "verts is a flat [x,y,z,...] list, tris a flat index list. Returns "
@@ -0,0 +1,16 @@
1
+ #pragma once
2
+
3
+ // offset() on a 3D solid: r= (rounded, an inscribed faceted ball), delta=
4
+ // (mitered, no limit, as 2D) and chamfer= (corners cut flat), outward for
5
+ // d > 0 and inward for d < 0. Pure geometry: no evaluator state. See
6
+ // offset3d.cpp for the construction and CLAUDE.md "offset() in 3D".
7
+
8
+ #include <manifold/manifold.h>
9
+
10
+ namespace oscadeval {
11
+
12
+ enum class Offset3DMode { Round, Delta, Chamfer };
13
+
14
+ manifold::Manifold offset3d(const manifold::Manifold& m, double d, Offset3DMode mode, int segs);
15
+
16
+ } // namespace oscadeval
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.51.0"
7
+ version = "1.52.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -419,6 +419,13 @@ def export_extensions() -> list:
419
419
  return list(_ext.export_extensions())
420
420
 
421
421
 
422
+ def _offset3d(verts, tris, d, mode="delta", segs=24):
423
+ """3D offset of one closed mesh, as offset() does it for a solid: flat
424
+ vertex/triangle lists in, flat lists out. mode is "r", "delta" or
425
+ "chamfer". A test hook for the offset's suites, not public API."""
426
+ return _ext.offset3d([float(v) for v in verts], [int(t) for t in tris], float(d), mode, int(segs))
427
+
428
+
422
429
  def check_mesh(verts, tris) -> dict:
423
430
  """Diagnose a triangle mesh against the manifoldness conditions.
424
431
 
@@ -48,6 +48,7 @@ add_library(openscad_cpp_evaluator STATIC
48
48
  builtins/import.cpp
49
49
  builtins/topology.cpp
50
50
  builtins/alpha_wrap.cpp
51
+ builtins/offset3d.cpp
51
52
  builtins/export_as.cpp
52
53
  ../third_party/predicates/predicates.c
53
54
  builtins/extrude.cpp
@@ -1,4 +1,5 @@
1
1
  #include "builtins.hpp"
2
+ #include "openscad_cpp_evaluator/offset3d.hpp"
2
3
 
3
4
  #include "openscad_cpp_evaluator/call_args.hpp"
4
5
  #include "openscad_cpp_evaluator/evaluator.hpp"
@@ -542,17 +543,46 @@ CSGParams resolveOffset(Evaluator& ev, const oscad::ModularCall& node, EvalConte
542
543
  return std::move(result.params);
543
544
  }
544
545
 
545
- std::vector<ColoredBody> generateOffset(Evaluator&, const CSGParams& params, const std::vector<std::unique_ptr<CSGNode>>& children,
546
- const oscad::ASTNode&) {
546
+ std::vector<ColoredBody> generateOffset(Evaluator& ev, const CSGParams& params,
547
+ const std::vector<std::unique_ptr<CSGNode>>& children,
548
+ const oscad::ASTNode& node) {
547
549
  const std::vector<ColoredBody> bodies = flattenCsgTree(children);
550
+ const double delta = std::get<double>(params.at("delta"));
551
+ const std::string& join = std::get<std::string>(params.at("join"));
552
+ const int segs = static_cast<int>(std::get<double>(params.at("segs")));
553
+
554
+ // 3D children (the dimension rule has already settled on one dimension
555
+ // and warned about the rest): the solid offset of offset3d.cpp.
556
+ const RoleSplit split = splitByRole(bodies);
557
+ std::vector<const ColoredBody*> solids;
558
+ for (const ColoredBody& b : split.foreground)
559
+ if (b.body && !b.body->IsEmpty()) solids.push_back(&b);
560
+ if (!solids.empty()) {
561
+ std::vector<ColoredBody> out;
562
+ if (std::isfinite(delta)) {
563
+ std::vector<manifold::Manifold> ms;
564
+ for (const ColoredBody* b : solids) ms.push_back(*b->body);
565
+ const manifold::Manifold a =
566
+ ms.size() == 1 ? ms[0] : manifold::Manifold::BatchBoolean(ms, manifold::OpType::Add);
567
+ const Offset3DMode mode = join == "round" ? Offset3DMode::Round
568
+ : join == "square" ? Offset3DMode::Chamfer
569
+ : Offset3DMode::Delta;
570
+ manifold::Manifold result = offset3d(a, delta, mode, segs);
571
+ if (!result.IsEmpty()) {
572
+ ColoredBody cb = ev.tagGenerated(std::move(result), node, Value{});
573
+ cb.color = valueToColor(params.at("color"));
574
+ out.push_back(std::move(cb));
575
+ }
576
+ }
577
+ for (const auto* group : {&split.background, &split.highlight, &split.showOnly, &split.displayOnly})
578
+ out.insert(out.end(), group->begin(), group->end());
579
+ return out;
580
+ }
581
+
548
582
  const std::optional<manifold::CrossSection> cs = toCrossSection(bodies);
549
583
  if (!cs) return {};
550
-
551
- const double delta = std::get<double>(params.at("delta"));
552
584
  // An infinite (or NaN) distance has no shape to give.
553
585
  if (!std::isfinite(delta)) return {};
554
- const std::string& join = std::get<std::string>(params.at("join"));
555
- const int segs = static_cast<int>(std::get<double>(params.at("segs")));
556
586
  using JT = manifold::CrossSection::JoinType;
557
587
  const JT jt = join == "round" ? JT::Round : join == "square" ? JT::Square : JT::Miter;
558
588
  // "miter" joins stay sharp however acute the corner: no realistic
@@ -1003,6 +1003,7 @@ const std::unordered_map<std::string, double>& featureLevels() {
1003
1003
  {"profile-time", 1.0}, // profile_time(label) { ... } and profile_time(label) expr
1004
1004
  {"alpha-wrap", 1.0}, // alpha_wrap(alpha, offset)
1005
1005
  {"export-as", 1.0}, // export_as(name) { ... }
1006
+ {"offset-3d", 1.0}, // offset(r=|delta=, chamfer=) on a solid, in and out
1006
1007
  };
1007
1008
  return levels;
1008
1009
  }