openscad-cpp-evaluator 1.22.0__tar.gz → 1.24.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 (221) hide show
  1. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +8 -3
  3. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/CMakeLists.txt +10 -1
  4. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/api.cpp +118 -28
  5. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +86 -0
  6. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/evaluator.hpp +12 -0
  7. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +17 -3
  8. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/pyproject.toml +1 -1
  9. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/CMakeLists.txt +34 -0
  10. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/function_builtins.cpp +0 -32
  11. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/primitives_3d.cpp +49 -6
  12. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/topology.cpp +0 -12
  13. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/csg_generate.cpp +33 -3
  14. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/expr_eval.cpp +7 -3
  15. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/manifold_cache.cpp +3 -3
  16. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/value.cpp +0 -30
  17. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/CMakeLists.txt +4 -1
  18. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_csg_tree.cpp +49 -0
  19. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_manifold_cache.cpp +63 -0
  20. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/.github/workflows/ci.yml +0 -0
  21. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/.github/workflows/wheels.yml +0 -0
  22. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/.gitignore +0 -0
  23. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/.gitmodules +0 -0
  24. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/CLAUDE.md +0 -0
  25. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/CMakeLists.txt +0 -0
  26. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/LICENSE +0 -0
  27. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/README.md +0 -0
  28. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/bindings/CMakeLists.txt +0 -0
  29. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/bindings/ast_to_py.cpp +0 -0
  30. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/bindings/module.cpp +0 -0
  31. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/cmake/embed_font.cmake +0 -0
  32. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/examples/CMakeLists.txt +0 -0
  33. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/examples/eval_perf_benchmark.cpp +0 -0
  34. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/examples/manifold_cache_reuse.cpp +0 -0
  35. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/examples/minimal_debugger.cpp +0 -0
  36. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  37. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/.gitignore +0 -0
  38. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  39. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  40. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/README.md +0 -0
  41. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  42. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  43. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  44. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  45. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  46. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  47. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  48. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  49. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  50. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  51. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  52. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  53. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  54. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  55. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  56. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  57. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  58. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  59. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  60. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  61. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  62. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  63. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  64. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  65. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  66. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  67. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  68. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  69. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  70. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  71. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  72. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  73. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  74. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  75. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  76. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  77. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  78. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  79. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  80. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  81. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  82. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  83. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  84. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  85. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  86. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  87. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  88. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  89. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  90. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  91. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  92. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  93. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  94. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  95. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  96. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  97. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  98. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  99. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  100. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  101. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  102. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  103. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  104. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  105. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  106. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  107. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  108. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  109. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  110. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  111. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  112. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  113. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  114. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  115. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  116. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  117. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  118. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  119. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  120. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  121. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  122. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  123. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  124. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  125. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  126. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  127. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  128. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  129. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  130. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  131. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  132. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  133. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  134. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  135. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  136. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/resources/fonts/LICENSE +0 -0
  137. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  138. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  139. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  140. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  141. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/booleans.cpp +0 -0
  142. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/builtins.hpp +0 -0
  143. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/call_args.cpp +0 -0
  144. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/color.cpp +0 -0
  145. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/control.cpp +0 -0
  146. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/dxf_dim.cpp +0 -0
  147. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/extrude.cpp +0 -0
  148. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/import.cpp +0 -0
  149. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/modifiers.cpp +0 -0
  150. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/primitives_2d.cpp +0 -0
  151. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/registry.cpp +0 -0
  152. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/roof.cpp +0 -0
  153. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/segments.cpp +0 -0
  154. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/surface.cpp +0 -0
  155. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/text.cpp +0 -0
  156. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/builtins/transforms.cpp +0 -0
  157. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/bytecode_compiler.cpp +0 -0
  158. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/bytecode_vm.cpp +0 -0
  159. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/colored_body.cpp +0 -0
  160. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/coverage.cpp +0 -0
  161. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/csg_resolve.cpp +0 -0
  162. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/css_colors.cpp +0 -0
  163. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/debug/debug_repl.cpp +0 -0
  164. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/debug_profile.cpp +0 -0
  165. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/eval_context.cpp +0 -0
  166. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/eval_error.cpp +0 -0
  167. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/eval_use.cpp +0 -0
  168. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/export.cpp +0 -0
  169. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/format_closure.cpp +0 -0
  170. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/import/dxf_import.cpp +0 -0
  171. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/import/mesh_import.cpp +0 -0
  172. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/import/surface_load.cpp +0 -0
  173. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/import/svg_import.cpp +0 -0
  174. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/measure_geometry.cpp +0 -0
  175. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/mesh_check.cpp +0 -0
  176. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/stmt_eval.cpp +0 -0
  177. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/text/font_match.cpp +0 -0
  178. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/text/freetype_font_provider.cpp +0 -0
  179. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/text/text_metrics.cpp +0 -0
  180. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/user_calls.cpp +0 -0
  181. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/utf8.cpp +0 -0
  182. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/src/zip_stored.cpp +0 -0
  183. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_booleans.cpp +0 -0
  184. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_bytecode_compiler.cpp +0 -0
  185. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_children_separate.cpp +0 -0
  186. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_cli.cpp +0 -0
  187. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_control_flow.cpp +0 -0
  188. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_coverage.cpp +0 -0
  189. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_debug_hooks.cpp +0 -0
  190. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_dxf_svg_import.cpp +0 -0
  191. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_eval_context.cpp +0 -0
  192. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_export.cpp +0 -0
  193. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_expr_eval.cpp +0 -0
  194. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_extrude_roof.cpp +0 -0
  195. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_feature_detection.cpp +0 -0
  196. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_function_builtins.cpp +0 -0
  197. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_helpers.hpp +0 -0
  198. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_import_export.cpp +0 -0
  199. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_include_cache.cpp +0 -0
  200. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_mesh_check.cpp +0 -0
  201. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_multi_color_merge.cpp +0 -0
  202. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_primitives.cpp +0 -0
  203. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_profiling.cpp +0 -0
  204. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_python_bindings.py +0 -0
  205. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_render_expr.cpp +0 -0
  206. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_scope_trail.cpp +0 -0
  207. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_scoping.cpp +0 -0
  208. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_surface.cpp +0 -0
  209. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_tail_calls.cpp +0 -0
  210. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_text.cpp +0 -0
  211. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_transforms.cpp +0 -0
  212. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_unexpected_args.cpp +0 -0
  213. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_use.cpp +0 -0
  214. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_value.cpp +0 -0
  215. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_viewport_params.cpp +0 -0
  216. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tests/test_zip_stored.cpp +0 -0
  217. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tools/cli/CMakeLists.txt +0 -0
  218. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tools/cli/cli_lib.cpp +0 -0
  219. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tools/cli/cli_lib.hpp +0 -0
  220. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/tools/cli/main.cpp +0 -0
  221. {openscad_cpp_evaluator-1.22.0 → openscad_cpp_evaluator-1.24.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.22.0
3
+ Version: 1.24.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -136,9 +136,14 @@ struct LibraryFileResult {
136
136
  LibraryFileResult getASTFromLibraryFile(const std::string& currFile, const std::string& libFile,
137
137
  bool includeComments = false, bool processIncludes = true);
138
138
 
139
- // OpenSCAD's library search path: (1) directory of currFile, (2)
140
- // OPENSCADPATH env var (':'-separated on POSIX, ';' on Windows), (3)
141
- // platform default library dir.
139
+ // OpenSCAD's library search path, in order: (1) directory of currFile,
140
+ // (2) each OPENSCADPATH entry (':'-separated on POSIX, ';' on Windows),
141
+ // (3) the user's own libraries folder, (4) libraries shipped beside this
142
+ // binary. OPENSCADPATH ADDS to (3) and (4) rather than replacing them,
143
+ // which is what OpenSCAD's own parser_init() does.
144
+ std::vector<std::string> librarySearchDirs(const std::string& currFile);
145
+
146
+ // The first entry of librarySearchDirs() that holds `libFile`.
142
147
  std::optional<std::string> findLibraryFile(const std::string& currFile, const std::string& libFile);
143
148
 
144
149
  // A file's statements with its `include <...>` directives resolved, where
@@ -38,7 +38,16 @@ target_include_directories(openscad_cpp_parser PRIVATE
38
38
  ${CMAKE_CURRENT_SOURCE_DIR}/grammar
39
39
  ${CMAKE_CURRENT_BINARY_DIR}
40
40
  )
41
- target_link_libraries(openscad_cpp_parser PUBLIC nlohmann_json::nlohmann_json)
41
+ # CMAKE_DL_LIBS is -ldl for api.cpp's dladdr() -- empty on macOS and
42
+ # Windows, where it lives in libSystem/kernel32. A local macOS build
43
+ # therefore links fine and manylinux (glibc < 2.34, dl still a separate
44
+ # library) does not: caught only by a wheel build.
45
+ target_link_libraries(openscad_cpp_parser PUBLIC nlohmann_json::nlohmann_json ${CMAKE_DL_LIBS})
46
+ # SHGetFolderPathW, for the real My Documents rather than an assumed
47
+ # %USERPROFILE%\Documents -- see api.cpp userLibraryDir().
48
+ if(WIN32)
49
+ target_link_libraries(openscad_cpp_parser PUBLIC shell32)
50
+ endif()
42
51
  target_compile_options(openscad_cpp_parser PRIVATE
43
52
  $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall;-Wextra>
44
53
  )
@@ -7,6 +7,17 @@
7
7
 
8
8
  #include <cstdlib>
9
9
  #include <filesystem>
10
+ #if defined(_WIN32)
11
+ // NOMINMAX or windows.h's max()/min() macros eat std::max below -- MSVC
12
+ // caught it as "illegal token on right side of '::'", which names neither
13
+ // the macro nor the header.
14
+ #define NOMINMAX
15
+ #define WIN32_LEAN_AND_MEAN
16
+ #include <windows.h>
17
+ #include <shlobj.h>
18
+ #else
19
+ #include <dlfcn.h>
20
+ #endif
10
21
  #include <fstream>
11
22
  #include <set>
12
23
  #include <system_error>
@@ -133,6 +144,22 @@ std::vector<std::unique_ptr<ASTNode>> getASTFromString(const std::string& code,
133
144
 
134
145
  namespace {
135
146
 
147
+ // A "not found" that says where we actually looked. The old message named
148
+ // only the includer, so "searched relative to X" read as "only X was
149
+ // searched" -- which is how #503 came to be filed against a search that
150
+ // does cover the libraries folder.
151
+ std::string notFound(const char* noun, const std::string& filename, const std::string& currentFile) {
152
+ std::string msg = std::string(noun) + " '" + filename + "' not found. Searched:";
153
+ for (const auto& d : librarySearchDirs(currentFile)) {
154
+ msg += "\n " + d;
155
+ }
156
+ return msg;
157
+ }
158
+
159
+ } // namespace
160
+
161
+ namespace {
162
+
136
163
  std::string readFile(const std::string& path) {
137
164
  std::ifstream in(path, std::ios::binary);
138
165
  if (!in) {
@@ -163,8 +190,7 @@ std::vector<std::unique_ptr<ASTNode>> resolveIncludes(std::vector<std::unique_pt
163
190
  const std::string& filename = inc.filepath->val;
164
191
  auto libFile = findLibraryFile(currentFile, filename);
165
192
  if (!libFile) {
166
- throw std::runtime_error("Included file '" + filename + "' not found. Searched relative to: " +
167
- (currentFile.empty() ? "current directory" : currentFile));
193
+ throw std::runtime_error(notFound("Included file", filename, currentFile));
168
194
  }
169
195
  std::string absLib = fs::absolute(*libFile).string();
170
196
  if (visited.count(absLib) != 0) {
@@ -185,40 +211,98 @@ std::vector<std::unique_ptr<ASTNode>> resolveIncludes(std::vector<std::unique_pt
185
211
 
186
212
  } // namespace
187
213
 
188
- std::optional<std::string> findLibraryFile(const std::string& currFile, const std::string& libFile) {
189
- std::vector<fs::path> dirs;
190
- if (!currFile.empty()) {
191
- dirs.push_back(fs::absolute(currFile).parent_path());
192
- }
214
+ namespace {
193
215
 
194
- char pathsep = ':';
195
- std::string dfltPath;
196
- const char* home = std::getenv("HOME");
216
+ // The user's own libraries folder -- OpenSCAD's
217
+ // PlatformUtils::userLibraryPath(), and the one an installer or a git clone
218
+ // of BOSL2 lands in.
219
+ std::string userLibraryDir() {
197
220
  #if defined(_WIN32)
198
- pathsep = ';';
221
+ // ASK Windows where My Documents is rather than assuming
222
+ // %USERPROFILE%\Documents. OneDrive's Known Folder Move -- on by default
223
+ // on a new machine -- relocates it to %USERPROFILE%\OneDrive\Documents,
224
+ // and a library OpenSCAD itself installed then sat somewhere we never
225
+ // looked (BelfrySCAD #503). Same call and same flag OpenSCAD makes
226
+ // (PlatformUtils-win.cc getFolderPath).
227
+ wchar_t buf[MAX_PATH] = {0};
228
+ if (SHGetFolderPathW(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, buf) == S_OK) {
229
+ return (fs::path(buf) / "OpenSCAD" / "libraries").string();
230
+ }
199
231
  const char* userProfile = std::getenv("USERPROFILE");
200
- if (userProfile) {
201
- dfltPath = std::string(userProfile) + "\\Documents\\OpenSCAD\\libraries";
232
+ return userProfile ? (fs::path(userProfile) / "Documents" / "OpenSCAD" / "libraries").string()
233
+ : std::string();
234
+ #else
235
+ const char* home = std::getenv("HOME");
236
+ if (!home) {
237
+ return {};
202
238
  }
203
- #elif defined(__APPLE__)
204
- if (home) {
205
- dfltPath = std::string(home) + "/Documents/OpenSCAD/libraries";
239
+ #if defined(__APPLE__)
240
+ return (fs::path(home) / "Documents" / "OpenSCAD" / "libraries").string();
241
+ #else
242
+ return (fs::path(home) / ".local" / "share" / "OpenSCAD" / "libraries").string();
243
+ #endif
244
+ #endif
245
+ }
246
+
247
+ // Libraries shipped alongside this build -- OpenSCAD's
248
+ // PlatformUtils::resourcePath("libraries"). "Alongside" means beside the
249
+ // binary this code was linked into: the CLI executable, or the Python
250
+ // extension inside its installed package.
251
+ // ponytail: one candidate directory, no ../share/openscad/libraries walk --
252
+ // add the walk if a packaging layout ever puts the libraries somewhere
253
+ // other than next to the binary.
254
+ std::string bundledLibraryDir() {
255
+ fs::path self;
256
+ #if defined(_WIN32)
257
+ HMODULE mod = nullptr;
258
+ if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
259
+ | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
260
+ reinterpret_cast<LPCWSTR>(&userLibraryDir), &mod)) {
261
+ wchar_t buf[MAX_PATH] = {0};
262
+ if (GetModuleFileNameW(mod, buf, MAX_PATH)) {
263
+ self = buf;
264
+ }
206
265
  }
207
266
  #else
208
- if (home) {
209
- dfltPath = std::string(home) + "/.local/share/OpenSCAD/libraries";
267
+ Dl_info info;
268
+ if (dladdr(reinterpret_cast<void*>(&userLibraryDir), &info) && info.dli_fname) {
269
+ self = info.dli_fname;
210
270
  }
211
271
  #endif
272
+ if (self.empty()) {
273
+ return {};
274
+ }
275
+ std::error_code ec;
276
+ return (fs::absolute(self, ec).parent_path() / "libraries").string();
277
+ }
278
+
279
+ } // namespace
212
280
 
281
+ std::vector<std::string> librarySearchDirs(const std::string& currFile) {
282
+ std::vector<std::string> dirs;
283
+ if (!currFile.empty()) {
284
+ dirs.push_back(fs::absolute(currFile).parent_path().string());
285
+ }
286
+
287
+ #if defined(_WIN32)
288
+ const char pathsep = ';';
289
+ #else
290
+ const char pathsep = ':';
291
+ #endif
292
+
293
+ // OPENSCADPATH comes FIRST and adds to the built-in paths rather than
294
+ // replacing them -- exactly what OpenSCAD's parser_init() does. It used
295
+ // to replace them, so setting the variable for one library hid every
296
+ // other, including a BOSL2 sitting in the default folder (#503).
213
297
  const char* envPath = std::getenv("OPENSCADPATH");
214
- std::string env = envPath ? std::string(envPath) : dfltPath;
215
- if (!env.empty()) {
298
+ if (envPath) {
299
+ std::string env(envPath);
216
300
  size_t start = 0;
217
301
  while (start <= env.size()) {
218
302
  size_t pos = env.find(pathsep, start);
219
303
  std::string part = (pos == std::string::npos) ? env.substr(start) : env.substr(start, pos - start);
220
304
  if (!part.empty()) {
221
- dirs.emplace_back(part);
305
+ dirs.push_back(part);
222
306
  }
223
307
  if (pos == std::string::npos) {
224
308
  break;
@@ -227,8 +311,17 @@ std::optional<std::string> findLibraryFile(const std::string& currFile, const st
227
311
  }
228
312
  }
229
313
 
230
- for (const auto& d : dirs) {
231
- fs::path candidate = d / libFile;
314
+ for (const std::string& d : {userLibraryDir(), bundledLibraryDir()}) {
315
+ if (!d.empty()) {
316
+ dirs.push_back(d);
317
+ }
318
+ }
319
+ return dirs;
320
+ }
321
+
322
+ std::optional<std::string> findLibraryFile(const std::string& currFile, const std::string& libFile) {
323
+ for (const auto& d : librarySearchDirs(currFile)) {
324
+ fs::path candidate = fs::path(d) / libFile;
232
325
  std::error_code ec;
233
326
  if (fs::is_regular_file(candidate, ec)) {
234
327
  return candidate.string();
@@ -255,9 +348,7 @@ LibraryFileResult getASTFromLibraryFile(const std::string& currFile, const std::
255
348
  bool processIncludes) {
256
349
  auto found = findLibraryFile(currFile, libFile);
257
350
  if (!found) {
258
- throw std::runtime_error("Library file '" + libFile +
259
- "' not found in search paths. Searched in: current file directory, OPENSCADPATH, and "
260
- "platform default paths.");
351
+ throw std::runtime_error(notFound("Library file", libFile, currFile));
261
352
  }
262
353
  auto ast = getASTFromFile(*found, includeComments, processIncludes);
263
354
  return LibraryFileResult{std::move(ast), *found};
@@ -346,8 +437,7 @@ void collectProgram(const FileAst& nodes, const std::string& currentFile, bool i
346
437
  const std::string& filename = inc.filepath->val;
347
438
  auto libFile = findLibraryFile(currentFile, filename);
348
439
  if (!libFile) {
349
- throw std::runtime_error("Included file '" + filename + "' not found. Searched relative to: " +
350
- (currentFile.empty() ? "current directory" : currentFile));
440
+ throw std::runtime_error(notFound("Included file", filename, currentFile));
351
441
  }
352
442
  std::string absLib = fs::absolute(*libFile).string();
353
443
  if (!visited.insert(absLib).second) continue;
@@ -2,6 +2,8 @@
2
2
 
3
3
  #include <gtest/gtest.h>
4
4
 
5
+ #include <algorithm>
6
+ #include <cstdlib>
5
7
  #include <filesystem>
6
8
  #include <fstream>
7
9
  #include <random>
@@ -138,3 +140,87 @@ TEST(FileApi, FindLibraryFileReturnsNulloptWhenMissing) {
138
140
  auto found = findLibraryFile("", "definitely_missing_file_xyz.scad");
139
141
  EXPECT_FALSE(found.has_value());
140
142
  }
143
+
144
+ namespace {
145
+
146
+ // RAII OPENSCADPATH, so one test's setting cannot leak into the next.
147
+ class ScopedOpenscadPath {
148
+ public:
149
+ explicit ScopedOpenscadPath(const std::string& value) {
150
+ const char* prev = std::getenv("OPENSCADPATH");
151
+ had_ = prev != nullptr;
152
+ if (had_) prev_ = prev;
153
+ set(value.c_str());
154
+ }
155
+ ~ScopedOpenscadPath() {
156
+ if (had_) {
157
+ set(prev_.c_str());
158
+ } else {
159
+ #if defined(_WIN32)
160
+ _putenv_s("OPENSCADPATH", "");
161
+ #else
162
+ unsetenv("OPENSCADPATH");
163
+ #endif
164
+ }
165
+ }
166
+ private:
167
+ static void set(const char* v) {
168
+ #if defined(_WIN32)
169
+ _putenv_s("OPENSCADPATH", v);
170
+ #else
171
+ setenv("OPENSCADPATH", v, 1);
172
+ #endif
173
+ }
174
+ bool had_ = false;
175
+ std::string prev_;
176
+ };
177
+
178
+ } // namespace
179
+
180
+ TEST(FileApi, OpenscadPathAddsToTheBuiltInDirsRatherThanReplacingThem) {
181
+ // BelfrySCAD #503: `env = envPath ? envPath : dfltPath` meant setting
182
+ // OPENSCADPATH for one library hid every library in the default folder.
183
+ std::vector<std::string> without = librarySearchDirs("");
184
+ TempDir dir;
185
+ ScopedOpenscadPath scoped(dir.path().string());
186
+ std::vector<std::string> with = librarySearchDirs("");
187
+
188
+ EXPECT_EQ(with.size(), without.size() + 1);
189
+ EXPECT_EQ(with.front(), dir.path().string()); // and it is searched FIRST
190
+ for (const auto& d : without) {
191
+ EXPECT_NE(std::find(with.begin(), with.end(), d), with.end()) << d;
192
+ }
193
+ }
194
+
195
+ TEST(FileApi, FindLibraryFileSearchesOpenscadPath) {
196
+ TempDir libs;
197
+ fs::create_directories(libs.path() / "MYLIB");
198
+ fs::path libFile = libs.path() / "MYLIB" / "std.scad";
199
+ writeFile(libFile, "x = 1;\n");
200
+
201
+ TempDir docs;
202
+ fs::path mainFile = docs.path() / "main.scad";
203
+ writeFile(mainFile, "x = 1;\n");
204
+
205
+ ScopedOpenscadPath scoped(libs.path().string());
206
+ auto found = findLibraryFile(mainFile.string(), "MYLIB/std.scad");
207
+ ASSERT_TRUE(found.has_value());
208
+ EXPECT_EQ(fs::path(*found), libFile);
209
+ }
210
+
211
+ TEST(FileApi, MissingIncludeErrorNamesEveryDirectorySearched) {
212
+ TempDir dir;
213
+ fs::path mainFile = dir.path() / "main.scad";
214
+ writeFile(mainFile, "include <NOPE/std.scad>\n");
215
+
216
+ try {
217
+ getASTFromFile(mainFile.string());
218
+ FAIL() << "expected a missing-include error";
219
+ } catch (const std::runtime_error& e) {
220
+ std::string msg = e.what();
221
+ EXPECT_NE(msg.find("NOPE/std.scad"), std::string::npos) << msg;
222
+ for (const auto& d : librarySearchDirs(mainFile.string())) {
223
+ EXPECT_NE(msg.find(d), std::string::npos) << msg;
224
+ }
225
+ }
226
+ }
@@ -398,6 +398,14 @@ public:
398
398
  // than anything reentrancy-aware, since generateTreeImpl recurses
399
399
  // depth-first on one thread.
400
400
  const oscad::Position* generateWarnEntry = nullptr;
401
+ // Non-null while generateTreeImpl() is generating a subtree whose result
402
+ // it intends to cache: warn() appends each formatted line here as well as
403
+ // emitting it, so the entry can replay them on a later hit. Saved and
404
+ // restored around each node rather than left set, and the inner vector is
405
+ // merged into the outer one on the way out, so a parent's entry carries
406
+ // everything its whole subtree said -- which is what a parent-level hit
407
+ // has to reproduce, since it skips all of it. See ManifoldCache.
408
+ std::vector<std::string>* warnCapture = nullptr;
401
409
  // The same republish for CSGNode::callChain, read by tagGenerated to
402
410
  // fill idToCallChain.
403
411
  uint32_t generateCallChain = kNoCallChain;
@@ -530,6 +538,10 @@ public:
530
538
  // Public: builtins/import.cpp's not-manifold warning is emitted from a
531
539
  // free function, same reasoning as tagGenerated()/builtinChildren().
532
540
  void warn(const std::string& message, const oscad::Position* position);
541
+ // Hands one already-formatted warning line to echoFn_, recording it in
542
+ // warnCapture first when one is active. Every warning path goes through
543
+ // here, including a ManifoldCache hit replaying a cached line.
544
+ void emitWarning(const std::string& formatted);
533
545
 
534
546
  // Applies OpenSCAD's mixed-2D/3D rules to a node's children just
535
547
  // before its GenerateFn runs -- see csg_generate.cpp.
@@ -3,6 +3,7 @@
3
3
  #include "openscad_cpp_evaluator/csg_node.hpp"
4
4
 
5
5
  #include <mutex>
6
+ #include <vector>
6
7
  #include <optional>
7
8
  #include <string>
8
9
  #include <unordered_map>
@@ -22,15 +23,28 @@ namespace oscadeval {
22
23
  // threading.Lock -- a host may run renders/evaluates on a background
23
24
  // thread while another operation reads/clears the same cache). Mirrors
24
25
  // the reference's ManifoldCache class.
26
+ // What one cache entry holds. The warnings matter as much as the bodies:
27
+ // a hit skips the whole subtree's GenerateFn, and every generate-time
28
+ // diagnostic is raised *inside* one of those (polyhedron's "mesh is not
29
+ // closed", import()'s failures, the dimension rules). Cached without them,
30
+ // a second render of unchanged source silently dropped every warning the
31
+ // first one raised, so a real defect looked intermittent and the author
32
+ // went hunting for a phantom. A cache has to be transparent; that includes
33
+ // what it says, not only what it draws. See BelfrySCAD issue #521.
34
+ struct CachedSubtree {
35
+ std::vector<ColoredBody> bodies;
36
+ std::vector<std::string> warnings; // already formatted, replayed verbatim
37
+ };
38
+
25
39
  class ManifoldCache {
26
40
  public:
27
- std::optional<std::vector<ColoredBody>> get(const std::string& key) const;
28
- void put(std::string key, std::vector<ColoredBody> bodies);
41
+ std::optional<CachedSubtree> get(const std::string& key) const;
42
+ void put(std::string key, std::vector<ColoredBody> bodies, std::vector<std::string> warnings);
29
43
  void clear();
30
44
 
31
45
  private:
32
46
  mutable std::mutex mutex_;
33
- std::unordered_map<std::string, std::vector<ColoredBody>> entries_;
47
+ std::unordered_map<std::string, CachedSubtree> entries_;
34
48
  };
35
49
 
36
50
  // Structural content-hash key for `node`, used by generateTree()'s
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.22.0"
7
+ version = "1.24.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -69,3 +69,37 @@ target_link_libraries(openscad_cpp_evaluator PRIVATE linenoise freetype harfbuzz
69
69
  target_compile_options(openscad_cpp_evaluator PRIVATE
70
70
  $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall;-Wextra>
71
71
  )
72
+
73
+ # -Wall -Wextra above is about OUR code, but two TUs pull a vendored header
74
+ # in wholesale and inherit its warnings: 27 from Boost.Polygon's voronoi
75
+ # headers and 9 from stb_image_write's implementation, against 5 that were
76
+ # real. Silenced per source file, per warning.
77
+ #
78
+ # Deliberately NOT -isystem, which looks like the tidier fix and is a trap:
79
+ # clang searches -isystem AFTER /usr/local/include, so a Homebrew Boost
80
+ # 1.90 silently won over the pinned 1.86 (measured, not assumed). Keeping
81
+ # -I keeps the fetched copy winning.
82
+ include(CheckCXXCompilerFlag)
83
+ foreach(_w deprecated-enum-float-conversion missing-field-initializers deprecated-declarations)
84
+ check_cxx_compiler_flag("-Wno-${_w}" OSCAD_HAS_WNO_${_w})
85
+ endforeach()
86
+
87
+ if(OSCAD_HAS_WNO_deprecated-enum-float-conversion)
88
+ # <boost/polygon/voronoi.hpp> does floating-point arithmetic on an
89
+ # unnamed enum, deprecated in C++20.
90
+ set_source_files_properties(builtins/roof.cpp PROPERTIES
91
+ COMPILE_OPTIONS "-Wno-deprecated-enum-float-conversion")
92
+ endif()
93
+
94
+ # zip_stored.cpp defines STB_IMAGE_WRITE_IMPLEMENTATION, so stb's C compiles
95
+ # as ours: `= { 0 }` partial initializers, and one sprintf().
96
+ set(_stb_quiet "")
97
+ if(OSCAD_HAS_WNO_missing-field-initializers)
98
+ list(APPEND _stb_quiet "-Wno-missing-field-initializers")
99
+ endif()
100
+ if(OSCAD_HAS_WNO_deprecated-declarations)
101
+ list(APPEND _stb_quiet "-Wno-deprecated-declarations")
102
+ endif()
103
+ if(_stb_quiet)
104
+ set_source_files_properties(zip_stored.cpp PROPERTIES COMPILE_OPTIONS "${_stb_quiet}")
105
+ endif()
@@ -837,38 +837,6 @@ Value builtinFontmetrics(Evaluator& ev, const CallArgs& args) {
837
837
  });
838
838
  }
839
839
 
840
- // Take only scalar numbers, mapped to how many leading positional
841
- // arguments must each actually BE a number. Anything else there (undef, a
842
- // string, a list, a bool, an object) is a type error yielding undef, and
843
- // so is supplying fewer arguments than the arity -- matching the
844
- // reference, which rejects the call outright rather than coercing.
845
- //
846
- // This has to be checked up front rather than left to each case below:
847
- // toDoubleLenient() silently turns undef/string/list into 0.0, which
848
- // would make `cos(misspelled_var)` quietly 1 instead of undef, and a
849
- // typo'd variable then produces plausible-looking geometry instead of an
850
- // obvious failure. Arity is deliberately a floor, not an exact match --
851
- // the reference also rejects EXTRA arguments, but tightening that too
852
- // risks breaking working library code for no correctness gain here.
853
- const std::unordered_map<std::string, size_t>& scalarNumericArity() {
854
- static const std::unordered_map<std::string, size_t> arity = {
855
- {"abs", 1}, {"sign", 1}, {"ceil", 1}, {"floor", 1}, {"round", 1}, {"sqrt", 1},
856
- {"ln", 1}, {"log", 1}, {"exp", 1}, {"sin", 1}, {"cos", 1}, {"tan", 1},
857
- {"asin", 1}, {"acos", 1}, {"atan", 1}, {"atan2", 2}, {"pow", 2}, {"rands", 3},
858
- };
859
- return arity;
860
- }
861
-
862
- // Numeric functions that legitimately take a LIST argument, so the
863
- // all-arguments-must-be-numbers rule above can't apply -- each validates
864
- // its own operands, and only needs the bool case caught here (Value's
865
- // bool and double are distinct alternatives, but the reference rejects
866
- // `max(true, 1)` as a type error rather than treating true as 1).
867
- const std::unordered_set<std::string>& numericOnlyNames() {
868
- static const std::unordered_set<std::string> names = {"max", "min", "norm", "cross"};
869
- return names;
870
- }
871
-
872
840
  } // namespace
873
841
 
874
842
  bool isBuiltinFunctionName(const std::string& name) {
@@ -11,7 +11,10 @@
11
11
  #include <cstdint>
12
12
  #include <map>
13
13
  #include <numbers>
14
+ #include <optional>
14
15
  #include <set>
16
+ #include <string>
17
+ #include <utility>
15
18
 
16
19
  namespace oscadeval {
17
20
 
@@ -42,8 +45,18 @@ bool isDrawableFailure(const manifold::Manifold& body, const M& mesh) {
42
45
  [](float v) { return std::isfinite(v); });
43
46
  }
44
47
 
48
+ struct BoundaryEdges {
49
+ size_t count = 0;
50
+ // Vertex indices of one representative open edge, or nullopt when the
51
+ // mesh is closed. A count alone is enough on a hand-written face list,
52
+ // where you can just read it; it is useless on generated geometry --
53
+ // three bad edges in a few thousand triangles cannot be found by eye,
54
+ // which is what happened in BelfrySCAD #521 / issue #187.
55
+ std::optional<std::pair<uint32_t, uint32_t>> first;
56
+ };
57
+
45
58
  template <typename M>
46
- size_t countBoundaryEdges(const M& mesh) {
59
+ BoundaryEdges findBoundaryEdges(const M& mesh) {
47
60
  std::map<std::pair<uint32_t, uint32_t>, int> uses;
48
61
  for (size_t i = 0; i + 2 < mesh.triVerts.size(); i += 3) {
49
62
  const uint32_t v[3] = {static_cast<uint32_t>(mesh.triVerts[i]),
@@ -54,11 +67,34 @@ size_t countBoundaryEdges(const M& mesh) {
54
67
  ++uses[{std::min(a, b), std::max(a, b)}];
55
68
  }
56
69
  }
57
- size_t open = 0;
70
+ BoundaryEdges out;
71
+ // std::map iterates in vertex-index order, which is arbitrary from the
72
+ // author's point of view but DETERMINISTIC: the same mesh names the same
73
+ // edge on every run. That matters more than which edge gets picked --
74
+ // an identifier that moves between renders is worse than no identifier.
75
+ // A cluster of open edges is nearly always one hole, so the first is
76
+ // usually representative of the rest.
58
77
  for (const auto& [edge, count] : uses) {
59
- if (count % 2 != 0) ++open;
78
+ if (count % 2 == 0) continue;
79
+ ++out.count;
80
+ if (!out.first) out.first = edge;
60
81
  }
61
- return open;
82
+ return out;
83
+ }
84
+
85
+ // "[1, 2.5, 0] - [1, 3, 0]" for one edge, using the same number formatting
86
+ // echo() uses, so a coordinate can be pasted straight back into a script.
87
+ template <typename M>
88
+ std::string describeEdge(const M& mesh, std::pair<uint32_t, uint32_t> edge) {
89
+ const size_t stride = mesh.numProp > 0 ? static_cast<size_t>(mesh.numProp) : 3;
90
+ auto point = [&](uint32_t v) {
91
+ const size_t base = static_cast<size_t>(v) * stride;
92
+ if (base + 2 >= mesh.vertProperties.size()) return std::string("?");
93
+ return "[" + formatNumber(static_cast<double>(mesh.vertProperties[base])) + ", " +
94
+ formatNumber(static_cast<double>(mesh.vertProperties[base + 1])) + ", " +
95
+ formatNumber(static_cast<double>(mesh.vertProperties[base + 2])) + "]";
96
+ };
97
+ return point(edge.first) + " - " + point(edge.second);
62
98
  }
63
99
  } // namespace
64
100
 
@@ -810,8 +846,15 @@ std::vector<ColoredBody> generatePolyhedron(Evaluator& ev, const CSGParams& para
810
846
  // self-intersections builds perfectly well, and only an OPEN one
811
847
  // reaches this branch. The count points straight at the problem.
812
848
  if (!ev.insideHull) {
813
- ev.warn("polyhedron: mesh is not closed -- " + std::to_string(countBoundaryEdges(mesh)) +
814
- " boundary edge(s); drawing it as a surface. hull() can still "
849
+ const BoundaryEdges open = findBoundaryEdges(mesh);
850
+ // Name one of them. Where the hole IS is the only part of this
851
+ // message an author can act on; the count alone just says to go
852
+ // looking, across the whole mesh.
853
+ const std::string where =
854
+ open.first ? ", first at " + describeEdge(mesh, *open.first) : std::string();
855
+ ev.warn("polyhedron: mesh is not closed -- " + std::to_string(open.count) +
856
+ " boundary edge(s)" + where +
857
+ "; drawing it as a surface. hull() can still "
815
858
  "use its points, but it cannot take part in union/difference/intersection",
816
859
  &node.position());
817
860
  }
@@ -621,18 +621,6 @@ double bandDistance(double v, double lo, double hi, bool invert) {
621
621
  return invert ? -d : d;
622
622
  }
623
623
 
624
- std::optional<std::array<double, 3>> readVec3(const Value& v) {
625
- const ListPtr* l = std::get_if<ListPtr>(&v);
626
- if (!l || !*l || (*l)->items.size() != 3) return std::nullopt;
627
- std::array<double, 3> out{};
628
- for (size_t i = 0; i < 3; ++i) {
629
- const double* d = std::get_if<double>(&(*l)->items[i]);
630
- if (!d || !std::isfinite(*d)) return std::nullopt;
631
- out[i] = *d;
632
- }
633
- return out;
634
- }
635
-
636
624
  } // namespace
637
625
 
638
626
  // -- 2D: marching squares --------------------------------------------------