openscad-cpp-evaluator 1.19.1__tar.gz → 1.19.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/PKG-INFO +1 -1
  2. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/colored_body.hpp +5 -0
  3. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/pyproject.toml +1 -1
  4. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/booleans.cpp +0 -8
  5. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/primitives_3d.cpp +13 -6
  6. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/csg_generate.cpp +49 -9
  7. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/text/freetype_font_provider.cpp +9 -2
  8. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_manifold_cache.cpp +41 -0
  9. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_multi_color_merge.cpp +0 -1
  10. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_primitives.cpp +13 -0
  11. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_text.cpp +16 -0
  12. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/.github/workflows/ci.yml +0 -0
  13. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/.github/workflows/wheels.yml +0 -0
  14. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/.gitignore +0 -0
  15. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/.gitmodules +0 -0
  16. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/CLAUDE.md +0 -0
  17. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/CMakeLists.txt +0 -0
  18. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/LICENSE +0 -0
  19. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/README.md +0 -0
  20. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/bindings/CMakeLists.txt +0 -0
  21. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/bindings/ast_to_py.cpp +0 -0
  22. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/bindings/module.cpp +0 -0
  23. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/cmake/embed_font.cmake +0 -0
  24. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/examples/CMakeLists.txt +0 -0
  25. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/examples/eval_perf_benchmark.cpp +0 -0
  26. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/examples/manifold_cache_reuse.cpp +0 -0
  27. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/examples/minimal_debugger.cpp +0 -0
  28. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  29. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/.gitignore +0 -0
  30. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  31. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  32. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/README.md +0 -0
  33. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  34. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  35. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  36. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  37. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  38. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  39. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  40. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  41. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  42. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  43. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  44. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  45. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  46. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  47. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  48. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  49. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  50. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/api.cpp +0 -0
  51. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  52. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  53. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  54. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  55. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  56. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  57. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  58. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  59. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  60. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  61. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  62. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  63. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  64. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  65. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  66. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  67. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/position.cpp +0 -0
  68. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  69. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  70. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  71. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  72. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  73. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
  74. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  75. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  76. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  77. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  78. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  79. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  80. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  81. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  82. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  83. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  84. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  85. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  86. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  87. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  88. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  89. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  90. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  91. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  92. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  93. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  94. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  95. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  96. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  97. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  98. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  99. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  100. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  101. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  102. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  103. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  104. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
  105. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  106. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  107. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  108. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  109. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  110. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  111. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  112. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  113. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/export.hpp +0 -0
  114. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  115. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  116. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  117. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  118. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  119. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  120. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  121. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  122. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  123. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  124. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  125. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  126. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  127. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  128. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  129. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  130. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/value.hpp +0 -0
  131. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  132. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/python/openscad_cpp_evaluator/__init__.py +0 -0
  133. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/resources/fonts/LICENSE +0 -0
  134. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  135. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  136. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  137. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  138. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/.DS_Store +0 -0
  139. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/CMakeLists.txt +0 -0
  140. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/builtins.hpp +0 -0
  141. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/call_args.cpp +0 -0
  142. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/color.cpp +0 -0
  143. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/control.cpp +0 -0
  144. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/dxf_dim.cpp +0 -0
  145. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/extrude.cpp +0 -0
  146. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/function_builtins.cpp +0 -0
  147. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/import.cpp +0 -0
  148. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/modifiers.cpp +0 -0
  149. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/primitives_2d.cpp +0 -0
  150. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/registry.cpp +0 -0
  151. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/roof.cpp +0 -0
  152. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/segments.cpp +0 -0
  153. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/surface.cpp +0 -0
  154. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/text.cpp +0 -0
  155. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/topology.cpp +0 -0
  156. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/builtins/transforms.cpp +0 -0
  157. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/bytecode_compiler.cpp +0 -0
  158. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/bytecode_vm.cpp +0 -0
  159. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/colored_body.cpp +0 -0
  160. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/coverage.cpp +0 -0
  161. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/csg_resolve.cpp +0 -0
  162. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/css_colors.cpp +0 -0
  163. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/debug/debug_repl.cpp +0 -0
  164. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/debug_profile.cpp +0 -0
  165. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/eval_context.cpp +0 -0
  166. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/eval_error.cpp +0 -0
  167. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/eval_use.cpp +0 -0
  168. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/export.cpp +0 -0
  169. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/expr_eval.cpp +0 -0
  170. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/format_closure.cpp +0 -0
  171. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/import/dxf_import.cpp +0 -0
  172. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/import/mesh_import.cpp +0 -0
  173. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/import/surface_load.cpp +0 -0
  174. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/import/svg_import.cpp +0 -0
  175. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/manifold_cache.cpp +0 -0
  176. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/measure_geometry.cpp +0 -0
  177. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/mesh_check.cpp +0 -0
  178. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/stmt_eval.cpp +0 -0
  179. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/text/font_match.cpp +0 -0
  180. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/text/text_metrics.cpp +0 -0
  181. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/user_calls.cpp +0 -0
  182. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/utf8.cpp +0 -0
  183. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/value.cpp +0 -0
  184. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/src/zip_stored.cpp +0 -0
  185. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/CMakeLists.txt +0 -0
  186. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_booleans.cpp +0 -0
  187. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_bytecode_compiler.cpp +0 -0
  188. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_children_separate.cpp +0 -0
  189. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_cli.cpp +0 -0
  190. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_control_flow.cpp +0 -0
  191. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_coverage.cpp +0 -0
  192. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_csg_tree.cpp +0 -0
  193. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_debug_hooks.cpp +0 -0
  194. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_dxf_svg_import.cpp +0 -0
  195. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_eval_context.cpp +0 -0
  196. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_export.cpp +0 -0
  197. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_expr_eval.cpp +0 -0
  198. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_extrude_roof.cpp +0 -0
  199. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_feature_detection.cpp +0 -0
  200. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_function_builtins.cpp +0 -0
  201. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_helpers.hpp +0 -0
  202. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_import_export.cpp +0 -0
  203. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_include_cache.cpp +0 -0
  204. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_mesh_check.cpp +0 -0
  205. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_profiling.cpp +0 -0
  206. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_python_bindings.py +0 -0
  207. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_render_expr.cpp +0 -0
  208. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_scope_trail.cpp +0 -0
  209. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_scoping.cpp +0 -0
  210. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_surface.cpp +0 -0
  211. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_tail_calls.cpp +0 -0
  212. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_transforms.cpp +0 -0
  213. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_unexpected_args.cpp +0 -0
  214. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_use.cpp +0 -0
  215. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_value.cpp +0 -0
  216. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_viewport_params.cpp +0 -0
  217. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tests/test_zip_stored.cpp +0 -0
  218. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tools/cli/CMakeLists.txt +0 -0
  219. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tools/cli/cli_lib.cpp +0 -0
  220. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tools/cli/cli_lib.hpp +0 -0
  221. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/tools/cli/main.cpp +0 -0
  222. {openscad_cpp_evaluator-1.19.1 → openscad_cpp_evaluator-1.19.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.19.1
3
+ Version: 1.19.3
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -23,6 +23,11 @@ enum class BodyRole { Normal, Highlight, Background, ShowOnly };
23
23
  // `role`/`triColors` are later-phase fields (modifier tagging -- Phase 3;
24
24
  // multi-color CSG merges -- Phase 3/8) included now so the struct shape
25
25
  // doesn't need reworking when those land.
26
+ // The colour drawn for geometry with no explicit color() wherever a live
27
+ // theme cannot be consulted -- per-triangle colour arrays, which are baked.
28
+ // Matches the reference's SceneRenderer._default_color.
29
+ inline constexpr std::array<float, 4> kDefaultGeometryColor{0.9f, 0.85f, 0.1f, 1.0f};
30
+
26
31
  struct ColoredBody {
27
32
  std::optional<manifold::Manifold> body;
28
33
  std::optional<std::array<float, 4>> color; // RGBA; nullopt = "no explicit color() -- follow the live theme"
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.19.1"
7
+ version = "1.19.3"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -150,14 +150,6 @@ CSGParams resolveCsg(Evaluator& ev, const oscad::ModularCall& node, EvalContext&
150
150
 
151
151
  namespace {
152
152
 
153
- // Matches the reference's own SceneRenderer._default_color -- shown for
154
- // geometry with no explicit color() override. ColoredBody::color normally
155
- // stays nullopt for uncolored geometry so a live renderer can resolve it
156
- // against its own color theme, but a per-triangle triColors override has
157
- // no such live-resolution mechanism, so a concrete fallback is needed here
158
- // specifically.
159
- constexpr std::array<float, 4> kDefaultGeometryColor{0.9f, 0.85f, 0.1f, 1.0f};
160
-
161
153
  // After a real boolean merge, `cb.color` is just one arbitrary child's
162
154
  // color (the first contributing operand above) -- every other child's own
163
155
  // color is otherwise lost, e.g. union()-ing an opaque cube with a
@@ -409,13 +409,20 @@ CSGParams resolveCylinder(Evaluator& ev, const oscad::ModularCall& node, EvalCon
409
409
  // per-end r1/r2, then the per-end d1/d2. So `cylinder(h=10, d=8, r1=5)`
410
410
  // is r1=5/r2=4, and `cylinder(h=10, r=5, r2=2)` is r1=5/r2=2. Both ends
411
411
  // default to 1 independently -- r2 does NOT fall back to r1.
412
+ //
413
+ // NUMBERS only, as the reference's isNumber() tests: a stray positional
414
+ // in a radius slot -- `cylinder(30, r=5, true)`, a mistyped center= --
415
+ // is ignored, not coerced. Lenient conversion turned that `true` into
416
+ // r1=1 and drew a cone where OpenSCAD draws the r=5 cylinder
417
+ // (BelfrySCAD #409).
418
+ const auto isNum = [](const Value& v) { return std::holds_alternative<double>(v); };
412
419
  double r1 = 1.0, r2 = 1.0;
413
- if (!isUndef(rV)) r1 = r2 = toDoubleLenient(rV);
414
- if (!isUndef(dV)) r1 = r2 = toDoubleLenient(dV) / 2.0;
415
- if (!isUndef(r1V)) r1 = toDoubleLenient(r1V);
416
- if (!isUndef(r2V)) r2 = toDoubleLenient(r2V);
417
- if (!isUndef(d1V)) r1 = toDoubleLenient(d1V) / 2.0;
418
- if (!isUndef(d2V)) r2 = toDoubleLenient(d2V) / 2.0;
420
+ if (isNum(rV)) r1 = r2 = std::get<double>(rV);
421
+ if (isNum(dV)) r1 = r2 = std::get<double>(dV) / 2.0;
422
+ if (isNum(r1V)) r1 = std::get<double>(r1V);
423
+ if (isNum(r2V)) r2 = std::get<double>(r2V);
424
+ if (isNum(d1V)) r1 = std::get<double>(d1V) / 2.0;
425
+ if (isNum(d2V)) r2 = std::get<double>(d2V) / 2.0;
419
426
 
420
427
  const int segs = fnSegmentsFromCtx(effCtx, std::max(r1, r2));
421
428
 
@@ -338,28 +338,68 @@ void Evaluator::restampCachedIds(std::vector<ColoredBody>& bodies, const oscad::
338
338
  manifold::MeshGL mesh = cb.body->GetMeshGL();
339
339
  if (mesh.runOriginalID.empty()) continue;
340
340
  std::unordered_map<uint32_t, uint32_t> remap;
341
- for (uint32_t& id : mesh.runOriginalID) {
341
+ // fresh ID -> the colour its triangles carried, multi-colour
342
+ // bodies only; what triColors is rebuilt from below.
343
+ std::unordered_map<uint32_t, std::array<float, 4>> runColor;
344
+ for (size_t run = 0; run < mesh.runOriginalID.size(); ++run) {
345
+ uint32_t& id = mesh.runOriginalID[run];
342
346
  auto found = remap.find(id);
343
347
  if (found == remap.end()) {
344
348
  const uint32_t fresh = manifold::Manifold::ReserveIDs(1);
345
349
  auto old = idToNode.find(id);
346
350
  idToNode[fresh] =
347
351
  (old != idToNode.end() && old->second != producer) ? old->second : &node;
352
+ // The colour has to be re-recorded too, or a later merge
353
+ // cannot tell this body's runs apart from uncoloured ones
354
+ // (attachTriColors looks runs up here). Across renders
355
+ // the old ID was minted by an earlier Evaluator and this
356
+ // one's idToColor has never heard of it, so the cached
357
+ // body itself is the record: its own colour, or for a
358
+ // multi-colour merge the colour its triangles carry. This
359
+ // is what made `difference() { color("orange") cube();
360
+ // color("cyan") cylinder(); ... }` lose the cyan on the
361
+ // first re-render after an edit and go flat orange on the
362
+ // next (BelfrySCAD #412) -- every cached operand looked
363
+ // uncoloured, so the merge saw nothing to distinguish.
348
364
  auto color = idToColor.find(id);
349
- if (color != idToColor.end()) idToColor[fresh] = color->second;
365
+ if (color != idToColor.end()) {
366
+ idToColor[fresh] = color->second;
367
+ } else if (cb.triColors && run + 1 < mesh.runIndex.size() &&
368
+ mesh.runIndex[run] / 3 < cb.triColors->size()) {
369
+ // ponytail: a run whose triangles carry the default
370
+ // fallback colour reads back as that explicit colour
371
+ // rather than "follow the theme"; the tint is the same.
372
+ idToColor[fresh] = (*cb.triColors)[mesh.runIndex[run] / 3];
373
+ } else {
374
+ idToColor[fresh] = cb.color;
375
+ }
376
+ if (cb.triColors && run + 1 < mesh.runIndex.size() &&
377
+ mesh.runIndex[run] / 3 < cb.triColors->size()) {
378
+ runColor[fresh] = (*cb.triColors)[mesh.runIndex[run] / 3];
379
+ }
350
380
  found = remap.emplace(id, fresh).first;
351
381
  }
352
382
  id = found->second;
353
383
  }
354
- const size_t trisBefore = mesh.triVerts.size() / 3;
355
384
  cb.body = manifold::Manifold(mesh);
356
385
  // Rebuilding a Manifold from a mesh can clean it up (degenerate
357
- // triangles merged away), and triColors is indexed by triangle. An
358
- // array that no longer matches is worse than none: the renderer
359
- // masks its vertex arrays with it and raises IndexError rather
360
- // than drawing anything at all.
361
- if (cb.triColors && cb.body && cb.body->GetMeshGL().triVerts.size() / 3 != trisBefore) {
362
- cb.triColors.reset();
386
+ // triangles merged away), and triColors is indexed by triangle, so
387
+ // the old array no longer lines up. The RUNS survive the rebuild,
388
+ // and each run's colour was just recorded, so the array is
389
+ // rebuilt from them rather than dropped -- dropping it turned a
390
+ // fully cached multi-colour difference() flat (BelfrySCAD #412).
391
+ if (cb.triColors && cb.body) {
392
+ const manifold::MeshGL rebuilt = cb.body->GetMeshGL();
393
+ const size_t numTris = rebuilt.triVerts.size() / 3;
394
+ std::vector<std::array<float, 4>> triColors(numTris, cb.color.value_or(kDefaultGeometryColor));
395
+ for (size_t i = 0; i + 1 < rebuilt.runIndex.size() && i < rebuilt.runOriginalID.size(); ++i) {
396
+ auto it = runColor.find(rebuilt.runOriginalID[i]);
397
+ if (it == runColor.end()) continue;
398
+ const size_t start = rebuilt.runIndex[i] / 3;
399
+ const size_t end = std::min<size_t>(rebuilt.runIndex[i + 1] / 3, numTris);
400
+ for (size_t t = start; t < end; ++t) triColors[t] = it->second;
401
+ }
402
+ cb.triColors = std::move(triColors);
363
403
  }
364
404
  }
365
405
  }
@@ -238,8 +238,15 @@ FreetypeFontProvider::FreetypeFontProvider() : impl_(std::make_unique<Impl>()) {
238
238
  FreetypeFontProvider::~FreetypeFontProvider() = default;
239
239
 
240
240
  FontHandle FreetypeFontProvider::resolveFont(const std::string& spec) {
241
- const FontSpec parsed = parseFontSpec(spec);
242
- if (parsed.families.empty()) return 0;
241
+ FontSpec parsed = parseFontSpec(spec);
242
+ if (parsed.families.empty()) {
243
+ // font=":style=bold" -- a style with no family names the DEFAULT
244
+ // family in that style, which is what fontconfig does with it and
245
+ // what BOSL2's text3d() example relies on (BelfrySCAD #405).
246
+ // Returning handle 0 here silently drew Regular.
247
+ if (parsed.style.empty()) return 0;
248
+ parsed.families.push_back(impl_->faces[0].metrics.family);
249
+ }
243
250
 
244
251
  const auto cached = impl_->bySpec.find(spec);
245
252
  if (cached != impl_->bySpec.end()) return cached->second;
@@ -6,6 +6,7 @@
6
6
 
7
7
  #include <gtest/gtest.h>
8
8
  #include <algorithm>
9
+ #include <array>
9
10
  #include <iterator>
10
11
  #include <numbers>
11
12
  #include <set>
@@ -327,6 +328,46 @@ TEST(ManifoldCache, ACachedMultiPartSubtreeKeepsOneIdPerPart) {
327
328
  EXPECT_EQ(all.size(), 4u) << "parts of the reused subtree share IDs";
328
329
  }
329
330
 
331
+ namespace {
332
+ // Distinct RGB triples a body's triangles carry (its flat colour if it has
333
+ // no per-triangle colours).
334
+ std::set<std::array<float, 3>> distinctColors(const ColoredBody& cb) {
335
+ std::set<std::array<float, 3>> out;
336
+ if (cb.triColors) {
337
+ for (const auto& c : *cb.triColors) out.insert({c[0], c[1], c[2]});
338
+ } else if (cb.color) {
339
+ out.insert({(*cb.color)[0], (*cb.color)[1], (*cb.color)[2]});
340
+ }
341
+ return out;
342
+ }
343
+ } // namespace
344
+
345
+ TEST(ManifoldCache, CachedOperandsKeepTheirColoursInALaterMerge) {
346
+ // BelfrySCAD #412: edit one subtrahend's colour and re-render with the
347
+ // cache shared, as the GUI does. The unchanged operands are cache hits
348
+ // and their run colours were never re-recorded, so the merge saw them
349
+ // as uncoloured: first re-render lost the cyan, the next (every
350
+ // operand cached) collapsed to flat orange.
351
+ const std::string a =
352
+ "difference() { color(\"orange\") cube(20, center=true);"
353
+ " color(\"cyan\") cylinder(30, r=8, center=true, $fn=24);"
354
+ " color(\"magenta\") rotate([0,90,0]) cylinder(30, r=8, center=true, $fn=24); }";
355
+ const std::string b = [&] { std::string s = a; s.replace(s.find("magenta"), 7, "green"); return s; }();
356
+
357
+ const std::set<std::array<float, 3>> wantB = distinctColors(evalSrc(b).bodies[0]);
358
+ ASSERT_EQ(wantB.size(), 3u);
359
+
360
+ auto cache = std::make_shared<ManifoldCache>();
361
+ evalSrcWithCache(a, cache);
362
+ EXPECT_EQ(distinctColors(evalSrcWithCache(b, cache).bodies[0]), wantB) << "first re-render";
363
+ EXPECT_EQ(distinctColors(evalSrcWithCache(b, cache).bodies[0]), wantB) << "fully cached";
364
+ // And a cached multi-colour body merged again keeps every colour.
365
+ Evaluated wrapped = evalSrcWithCache("union() { " + b + " translate([30,0,0]) color(\"red\") cube(5); }", cache);
366
+ std::set<std::array<float, 3>> wantWrapped = wantB;
367
+ wantWrapped.insert({1.0f, 0.0f, 0.0f});
368
+ EXPECT_EQ(distinctColors(wrapped.bodies[0]), wantWrapped);
369
+ }
370
+
330
371
  TEST(ManifoldCache, OutputIsIdenticalCacheOnVsCacheOffAcrossBuiltinCategories) {
331
372
  const std::vector<std::string> scripts = {
332
373
  "cube(3);",
@@ -36,7 +36,6 @@ std::vector<std::array<float, 4>> distinctColors(const std::vector<std::array<fl
36
36
  return out;
37
37
  }
38
38
 
39
- constexpr std::array<float, 4> kDefaultGeometryColor{0.9f, 0.85f, 0.1f, 1.0f};
40
39
 
41
40
  } // namespace
42
41
 
@@ -51,6 +51,19 @@ TEST(Cylinder, StraightCylinderVolumeAndBoundsWhenCentered) {
51
51
  EXPECT_NEAR(e.bodies[0].body->Volume(), analytic, analytic * 0.02);
52
52
  }
53
53
 
54
+ TEST(Cylinder, NonNumericRadiusIsIgnoredNotCoerced) {
55
+ // cylinder(30, r=5, true): the `true` lands in r1's slot. The reference
56
+ // applies a radius only if it isNumber(), so this is the r=5 cylinder;
57
+ // lenient conversion made it a cone from r1=1 (BelfrySCAD #409).
58
+ Evaluated e = evalSrc("cylinder(30, r=5, true, $fn=32);");
59
+ ASSERT_TRUE(e.bodies[0].body.has_value());
60
+ const double analytic = std::numbers::pi * 5.0 * 5.0 * 30.0;
61
+ EXPECT_NEAR(e.bodies[0].body->Volume(), analytic, analytic * 0.02);
62
+ // Same rule for every radius spelling, and a string is not a number.
63
+ Evaluated s = evalSrc("cylinder(h=30, r=5, r2=\"7\", $fn=32);");
64
+ EXPECT_NEAR(s.bodies[0].body->Volume(), analytic, analytic * 0.02);
65
+ }
66
+
54
67
  TEST(Cylinder, UncenteredSitsOnZEqualsZero) {
55
68
  Evaluated e = evalSrc("cylinder(h=5, r=1, $fn=16);");
56
69
  manifold::Box bbox = e.bodies[0].body->BoundingBox();
@@ -380,6 +380,22 @@ TEST(FontArgs, TheLaterPositionalsLineUpToo) {
380
380
  EXPECT_EQ(echoes[0], "ECHO: true, true");
381
381
  }
382
382
 
383
+ TEST(TextArgs, StyleWithoutFamilyStylesTheDefaultFace) {
384
+ // font=":style=bold" -- BOSL2's text3d() example 2 -- drew Regular:
385
+ // no family, so resolveFont handed back the default handle and never
386
+ // looked at the style (BelfrySCAD #405). It means the default family
387
+ // in bold, the same face "Liberation Sans:style=Bold" names.
388
+ std::vector<std::string> echoes;
389
+ evalSrc(
390
+ "b = render() { linear_extrude(1) text(\"Hi\", 10, font=\":style=bold\"); };\n"
391
+ "n = render() { linear_extrude(1) text(\"Hi\", 10, font=\"Liberation Sans:style=Bold\"); };\n"
392
+ "d = render() { linear_extrude(1) text(\"Hi\", 10); };\n"
393
+ "echo(b.volume == n.volume, b.volume == d.volume);\n",
394
+ [&](const std::string& m) { echoes.push_back(m); });
395
+ ASSERT_EQ(echoes.size(), 1u);
396
+ EXPECT_EQ(echoes[0], "ECHO: true, false");
397
+ }
398
+
383
399
  TEST(TextArgs, FontIsPositionalButTheRestAreNot) {
384
400
  // text("Hi", 10, "Liberation Sans:style=Bold") silently drew the
385
401
  // DEFAULT face: `font` was name-only, so the third argument went