openscad-cpp-evaluator 1.28.5__tar.gz → 1.29.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 (226) hide show
  1. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/CLAUDE.md +9 -3
  2. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/PKG-INFO +1 -1
  3. openscad_cpp_evaluator-1.29.0/external/openscad_cpp_parser/src/.DS_Store +0 -0
  4. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/api.cpp +24 -9
  5. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +1 -0
  6. openscad_cpp_evaluator-1.29.0/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +56 -0
  7. openscad_cpp_evaluator-1.29.0/include/openscad_cpp_evaluator/css_colors.hpp +20 -0
  8. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/pyproject.toml +1 -1
  9. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/CMakeLists.txt +1 -0
  10. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/color.cpp +44 -17
  11. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/css_colors.cpp +39 -31
  12. openscad_cpp_evaluator-1.29.0/src/xkcd_colors.cpp +967 -0
  13. openscad_cpp_evaluator-1.29.0/src/xkcd_colors.hpp +13 -0
  14. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_python_bindings.py +34 -0
  15. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_transforms.cpp +103 -30
  16. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/uv.lock +1 -1
  17. openscad_cpp_evaluator-1.28.5/include/openscad_cpp_evaluator/css_colors.hpp +0 -19
  18. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/.github/workflows/ci.yml +0 -0
  19. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/.github/workflows/wheels.yml +0 -0
  20. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/.gitignore +0 -0
  21. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/.gitmodules +0 -0
  22. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/CMakeLists.txt +0 -0
  23. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/LICENSE +0 -0
  24. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/README.md +0 -0
  25. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/bindings/CMakeLists.txt +0 -0
  26. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/bindings/ast_to_py.cpp +0 -0
  27. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/bindings/module.cpp +0 -0
  28. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/cmake/embed_font.cmake +0 -0
  29. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/examples/CMakeLists.txt +0 -0
  30. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/examples/eval_perf_benchmark.cpp +0 -0
  31. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/examples/manifold_cache_reuse.cpp +0 -0
  32. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/examples/minimal_debugger.cpp +0 -0
  33. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
  34. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/.gitignore +0 -0
  35. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
  36. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
  37. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/README.md +0 -0
  38. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
  39. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
  40. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
  41. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
  42. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
  43. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
  44. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
  45. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
  46. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
  47. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
  48. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
  49. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
  50. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
  51. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
  52. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
  53. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
  54. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
  55. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
  56. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
  57. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
  58. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
  59. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
  60. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
  61. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
  62. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
  63. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
  64. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
  65. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
  66. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
  67. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
  68. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
  69. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
  70. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
  71. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
  72. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
  73. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
  74. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
  75. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
  76. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
  77. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
  78. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
  79. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
  80. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
  81. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
  82. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
  83. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
  84. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
  85. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
  86. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
  87. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
  88. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
  89. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
  90. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
  91. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
  92. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
  93. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
  94. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
  95. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
  96. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
  97. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
  98. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
  99. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
  100. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
  101. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
  102. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
  103. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
  104. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
  105. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
  106. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
  107. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
  108. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
  109. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
  110. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
  111. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
  112. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
  113. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
  114. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
  115. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
  116. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
  117. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
  118. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
  119. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
  120. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
  121. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
  122. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
  123. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
  124. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
  125. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
  126. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
  127. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
  128. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
  129. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
  130. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
  131. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
  132. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
  133. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
  134. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
  135. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
  136. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/resources/fonts/LICENSE +0 -0
  137. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
  138. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
  139. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
  140. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
  141. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/booleans.cpp +0 -0
  142. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/builtins.hpp +0 -0
  143. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/call_args.cpp +0 -0
  144. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/control.cpp +0 -0
  145. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/dxf_dim.cpp +0 -0
  146. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/extrude.cpp +0 -0
  147. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/function_builtins.cpp +0 -0
  148. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/import.cpp +0 -0
  149. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/modifiers.cpp +0 -0
  150. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/primitives_2d.cpp +0 -0
  151. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/primitives_3d.cpp +0 -0
  152. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/registry.cpp +0 -0
  153. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/roof.cpp +0 -0
  154. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/segments.cpp +0 -0
  155. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/surface.cpp +0 -0
  156. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/text.cpp +0 -0
  157. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/topology.cpp +0 -0
  158. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/builtins/transforms.cpp +0 -0
  159. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/bytecode_compiler.cpp +0 -0
  160. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/bytecode_vm.cpp +0 -0
  161. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/colored_body.cpp +0 -0
  162. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/coverage.cpp +0 -0
  163. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/csg_generate.cpp +0 -0
  164. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/csg_resolve.cpp +0 -0
  165. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/debug/debug_repl.cpp +0 -0
  166. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/debug_profile.cpp +0 -0
  167. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/eval_context.cpp +0 -0
  168. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/eval_error.cpp +0 -0
  169. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/eval_use.cpp +0 -0
  170. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/export.cpp +0 -0
  171. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/expr_eval.cpp +0 -0
  172. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/format_closure.cpp +0 -0
  173. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/import/dxf_import.cpp +0 -0
  174. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/import/mesh_import.cpp +0 -0
  175. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/import/surface_load.cpp +0 -0
  176. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/import/svg_import.cpp +0 -0
  177. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/manifold_cache.cpp +0 -0
  178. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/measure_geometry.cpp +0 -0
  179. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/mesh_check.cpp +0 -0
  180. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/stmt_eval.cpp +0 -0
  181. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/text/font_match.cpp +0 -0
  182. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/text/freetype_font_provider.cpp +0 -0
  183. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/text/text_metrics.cpp +0 -0
  184. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/user_calls.cpp +0 -0
  185. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/utf8.cpp +0 -0
  186. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/value.cpp +0 -0
  187. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/src/zip_stored.cpp +0 -0
  188. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/CMakeLists.txt +0 -0
  189. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_booleans.cpp +0 -0
  190. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_bytecode_compiler.cpp +0 -0
  191. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_children_separate.cpp +0 -0
  192. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_cli.cpp +0 -0
  193. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_control_flow.cpp +0 -0
  194. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_coverage.cpp +0 -0
  195. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_csg_tree.cpp +0 -0
  196. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_debug_hooks.cpp +0 -0
  197. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_dxf_svg_import.cpp +0 -0
  198. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_eval_context.cpp +0 -0
  199. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_export.cpp +0 -0
  200. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_expr_eval.cpp +0 -0
  201. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_extrude_roof.cpp +0 -0
  202. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_feature_detection.cpp +0 -0
  203. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_function_builtins.cpp +0 -0
  204. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_helpers.hpp +0 -0
  205. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_import_export.cpp +0 -0
  206. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_include_cache.cpp +0 -0
  207. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_manifold_cache.cpp +0 -0
  208. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_mesh_check.cpp +0 -0
  209. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_multi_color_merge.cpp +0 -0
  210. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_primitives.cpp +0 -0
  211. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_profiling.cpp +0 -0
  212. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_render_expr.cpp +0 -0
  213. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_scope_trail.cpp +0 -0
  214. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_scoping.cpp +0 -0
  215. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_surface.cpp +0 -0
  216. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_tail_calls.cpp +0 -0
  217. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_text.cpp +0 -0
  218. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_unexpected_args.cpp +0 -0
  219. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_use.cpp +0 -0
  220. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_value.cpp +0 -0
  221. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_viewport_params.cpp +0 -0
  222. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tests/test_zip_stored.cpp +0 -0
  223. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tools/cli/CMakeLists.txt +0 -0
  224. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tools/cli/cli_lib.cpp +0 -0
  225. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tools/cli/cli_lib.hpp +0 -0
  226. {openscad_cpp_evaluator-1.28.5 → openscad_cpp_evaluator-1.29.0}/tools/cli/main.cpp +0 -0
@@ -819,9 +819,15 @@ grep for `ponytail:`.
819
819
  geometric-correctness test in Phase 3, not a hypothetical.
820
820
  - `include/openscad_cpp_evaluator/segments.hpp`, `src/builtins/segments.cpp` — `$fn`/`$fa`/`$fs` ->
821
821
  circular-shape segment count (`fnSegments`/`fnSegmentsFromCtx`), used by sphere/cylinder/circle.
822
- - `include/openscad_cpp_evaluator/css_colors.hpp`, `src/css_colors.cpp` — the 148-entry CSS/SVG
823
- color-name table (mechanically ported from the Python reference's own generated table) plus hex
824
- parsing, backing `color()`'s string-name argument.
822
+ - `include/openscad_cpp_evaluator/css_colors.hpp`, `src/css_colors.cpp`, `src/xkcd_colors.{hpp,cpp}` —
823
+ `parseColor`, OpenSCAD's `parse_color` (ColorUtil.cc): CSS Color Level 4 names (148, with
824
+ `rebeccapurple`) plus `transparent` (alpha 0), `"xkcd:<name>"` over the 949 xkcd survey names
825
+ (generated from OpenSCAD's `XkcdColors.h`, CC0), all case-insensitive, then strict `#rgb` /
826
+ `#rgba` / `#rrggbb` / `#rrggbbaa`. `color()` (`src/builtins/color.cpp`) follows `builtin_color`:
827
+ an unreadable string warns `Unable to parse color "…"` and sets NO colour (children keep their
828
+ own, or the outer one) -- it used to draw white; a list fills missing channels with 1 and warns
829
+ outside 0..1; a numeric `alpha` replaces any alpha, a 4-list's included (checked on the 2026.02.01
830
+ binary's 3MF export). `cssColor` (white fallback) is only for SVG/PDF export options.
825
831
  - `src/builtins/{primitives_2d,primitives_3d,transforms,color,booleans,modifiers,control}.cpp` —
826
832
  one file per builtin group, each a `ResolveFn`/`GenerateFn` pair (or a few, where several builtin
827
833
  names share one pair — circle/square/polygon, translate/rotate/scale/mirror/multmatrix/resize,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_cpp_evaluator
3
- Version: 1.28.5
3
+ Version: 1.29.0
4
4
  Summary: C++ OpenSCAD evaluator with Python bindings
5
5
  Author: NeuSCAD Contributors
6
6
  License-Expression: MIT
@@ -119,10 +119,25 @@ std::vector<std::unique_ptr<ASTNode>> parseAst(const std::string& code, const st
119
119
 
120
120
  ParserDriver driver(origin);
121
121
  driver.strictCommas = g_strictCommas;
122
- lexerBeginString(code);
123
- yy::parser parser(driver);
124
- int rc = parser.parse();
125
- lexerEnd();
122
+ int rc;
123
+ {
124
+ // The flex scanner is global state (grammar/lexer_api.hpp), so two
125
+ // threads parsing at once lexed each other's input and crashed -- in
126
+ // BelfrySCAD, a render worker parsing its file while the UI thread
127
+ // parsed the buffer for the Customizer: an intermittent 0xC0000005
128
+ // on Windows. One parse at a time; a parse is milliseconds, and the
129
+ // shared file cache means a library is parsed once, not per thread.
130
+ // ponytail: a global lock; a reentrant scanner (%option reentrant +
131
+ // a yyscan_t through the driver) if parsing ever needs to scale.
132
+ static std::mutex scannerMutex;
133
+ std::lock_guard<std::mutex> lock(scannerMutex);
134
+ struct EndLexer { // lexerEnd() even when parse() throws
135
+ ~EndLexer() { lexerEnd(); }
136
+ } endLexer;
137
+ lexerBeginString(code);
138
+ yy::parser parser(driver);
139
+ rc = parser.parse();
140
+ }
126
141
 
127
142
  if (rc != 0 || driver.hadError) {
128
143
  throw ParseError(formatSyntaxError(driver, code, origin, sourceMap));
@@ -409,11 +424,11 @@ FileAstPtr parseFileShared(const std::string& absPath, bool includeComments) {
409
424
  return it->second.ast;
410
425
  }
411
426
 
412
- // Parsed OUTSIDE the lock: parsing a library takes tens of
413
- // milliseconds, and holding a global lock across it would serialise
414
- // every thread. Two threads racing the same file both parse and one
415
- // result is dropped -- wasteful once, never wrong, and far cheaper than
416
- // the alternative.
427
+ // Parsed outside THIS lock, so a cache lookup never waits behind a
428
+ // parse. (parseAst serialises the scanner itself: this comment once
429
+ // called concurrent parses "never wrong", and they were -- the scanner
430
+ // is global state.) Two threads racing the same file each parse it in
431
+ // turn and one result is dropped: wasteful once, never wrong.
417
432
  auto parsed = std::make_shared<const FileAst>(parseSingleFile(absPath, includeComments));
418
433
  std::lock_guard<std::mutex> lock(g_astCacheMutex);
419
434
  CacheEntry& entry = g_astCache[key];
@@ -27,6 +27,7 @@ add_executable(oscad_tests
27
27
  test_node_str.cpp
28
28
  test_render_expression.cpp
29
29
  test_strict_commas.cpp
30
+ test_concurrent_parse.cpp
30
31
  )
31
32
  target_link_libraries(oscad_tests PRIVATE openscad_cpp_parser GTest::gtest_main)
32
33
 
@@ -0,0 +1,56 @@
1
+ // The flex scanner is non-reentrant, global state (grammar/lexer_api.hpp),
2
+ // so two threads parsing at once used to lex each other's input. In
3
+ // BelfrySCAD that was an intermittent 0xC0000005 on Windows: a render worker
4
+ // parsing its file while the UI thread parsed the buffer for the Customizer.
5
+ // parseAst now serialises the scanner. Every thread here parses its OWN
6
+ // source and checks it got its own tree back, so corruption fails the test
7
+ // even where it does not crash.
8
+ #include "openscad_cpp_parser/api.hpp"
9
+
10
+ #include <gtest/gtest.h>
11
+
12
+ #include <atomic>
13
+ #include <string>
14
+ #include <thread>
15
+ #include <vector>
16
+
17
+ using namespace oscad;
18
+
19
+ namespace {
20
+ std::string sourceFor(int thread, int round) {
21
+ // Different identifiers and lengths per thread, so a lexer reading
22
+ // another thread's buffer produces a different tree, not an equal one.
23
+ std::string name = "v" + std::to_string(thread) + "_" + std::to_string(round);
24
+ std::string src;
25
+ for (int i = 0; i < 20 + thread; ++i) {
26
+ src += name + "_" + std::to_string(i) + " = [" + std::to_string(i) + ", " +
27
+ std::to_string(thread) + ", \"s" + std::to_string(round) + "\"];\n";
28
+ }
29
+ return src;
30
+ }
31
+ } // namespace
32
+
33
+ TEST(ConcurrentParse, ThreadsParsingAtOnceEachGetTheirOwnTree) {
34
+ constexpr int kThreads = 8, kRounds = 150;
35
+ std::atomic<int> wrong{0};
36
+ std::vector<std::thread> threads;
37
+ for (int t = 0; t < kThreads; ++t) {
38
+ threads.emplace_back([t, &wrong] {
39
+ for (int r = 0; r < kRounds; ++r) {
40
+ const std::string src = sourceFor(t, r);
41
+ try {
42
+ auto ast = parseAst(src);
43
+ const auto* first = ast.empty() ? nullptr : dynamic_cast<const Assignment*>(ast[0].get());
44
+ const std::string want = "v" + std::to_string(t) + "_" + std::to_string(r) + "_0";
45
+ if (ast.size() != static_cast<size_t>(20 + t) || !first || first->name->name != want) {
46
+ ++wrong;
47
+ }
48
+ } catch (...) {
49
+ ++wrong; // a syntax error from lexing another thread's input
50
+ }
51
+ }
52
+ });
53
+ }
54
+ for (auto& th : threads) th.join();
55
+ EXPECT_EQ(wrong.load(), 0);
56
+ }
@@ -0,0 +1,20 @@
1
+ #pragma once
2
+
3
+ #include <array>
4
+ #include <optional>
5
+ #include <string>
6
+
7
+ namespace oscadeval {
8
+
9
+ // A colour string as OpenSCAD's parse_color reads it (ColorUtil.cc):
10
+ // a CSS Color Level 4 name or OpenSCAD's "transparent" (alpha 0), an
11
+ // "xkcd:<name>" from the xkcd colour survey, all case-insensitive; else
12
+ // "#rgb", "#rgba", "#rrggbb" or "#rrggbbaa". Nothing if it is none of
13
+ // those -- color() then warns and leaves its children's colour alone.
14
+ std::optional<std::array<double, 4>> parseColor(const std::string& text);
15
+
16
+ // parseColor with the alpha replaced, and white for anything unparseable.
17
+ // For export options (SVG/PDF fill and stroke), which have no warning path.
18
+ std::array<double, 4> cssColor(const std::string& name, double alpha = 1.0);
19
+
20
+ } // namespace oscadeval
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_cpp_evaluator"
7
- version = "1.28.5"
7
+ version = "1.29.0"
8
8
  description = "C++ OpenSCAD evaluator with Python bindings"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -11,6 +11,7 @@ add_library(openscad_cpp_evaluator STATIC
11
11
  bytecode_vm.cpp
12
12
  colored_body.cpp
13
13
  css_colors.cpp
14
+ xkcd_colors.cpp
14
15
  csg_resolve.cpp
15
16
  coverage.cpp
16
17
  csg_generate.cpp
@@ -1,38 +1,63 @@
1
1
  #include "builtins.hpp"
2
2
 
3
+ #include <cstdio>
4
+
3
5
  #include "openscad_cpp_evaluator/call_args.hpp"
4
6
  #include "openscad_cpp_evaluator/css_colors.hpp"
5
7
  #include "openscad_cpp_evaluator/evaluator.hpp"
6
8
 
7
9
  namespace oscadeval {
8
10
 
9
- // color(c = [1,1,1,1], alpha = 1) -- c is a CSS/hex color name, an [r,g,b]
10
- // (alpha from the separate `alpha` argument), or an [r,g,b,a] (alpha taken
11
- // from the list itself, the separate `alpha` argument is ignored in that
12
- // case). Sets the color on ctx for every descendant statement. Mirrors
13
- // _resolve_color/_generate_color.
11
+ // color(c, alpha), as OpenSCAD's builtin_color (ColorNode.cc):
12
+ // - a string goes through parseColor (CSS / "xkcd:" names, hex with or
13
+ // without alpha); one it cannot read warns "Unable to parse color" and
14
+ // leaves the children's colour alone -- inherited from outside, not reset;
15
+ // - a list fills [r, g, b, a] in order, 1.0 for any it lacks, warning on a
16
+ // component outside 0..1;
17
+ // - a numeric `alpha` then replaces the alpha of whatever `c` gave -- even a
18
+ // 4-element list's, and even "#rrggbbaa"'s -- warning when outside 0..1;
19
+ // - anything else for `c`, or no `c` at all, sets no colour (OpenSCAD's
20
+ // unset Color4f is (-1,-1,-1,-1), which is not a colour).
21
+
22
+ namespace {
23
+ std::string oneDecimal(double v) {
24
+ char buf[64];
25
+ std::snprintf(buf, sizeof buf, "%.1f", v);
26
+ return buf;
27
+ }
28
+ } // namespace
14
29
 
15
30
  BuiltinWrapParams computeColorParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
16
31
  auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
17
- const Value defaultC =
18
- Value{std::make_shared<const ValueList>(ValueList{{Value{1.0}, Value{1.0}, Value{1.0}, Value{1.0}}})};
19
- Value cArg = getArg(args, 0, "c", defaultC);
20
- const double alpha = toDoubleLenient(getArg(args, 1, "alpha", Value{1.0}));
32
+ const Value cArg = getArg(args, 0, "c");
33
+ const Value alphaArg = getArg(args, 1, "alpha");
21
34
 
22
- std::array<double, 4> rgba{1.0, 1.0, 1.0, 1.0};
35
+ std::optional<std::array<double, 4>> rgba;
23
36
  if (const std::string* s = std::get_if<std::string>(&cArg)) {
24
- rgba = cssColor(*s, alpha);
37
+ rgba = parseColor(*s);
38
+ if (!rgba) ev.warn("Unable to parse color \"" + *s + "\"", &node.position());
25
39
  } else if (const ListPtr* l = std::get_if<ListPtr>(&cArg); l && *l) {
26
40
  const auto& items = (*l)->items;
27
- if (items.size() == 3) {
28
- rgba = {toDoubleLenient(items[0]), toDoubleLenient(items[1]), toDoubleLenient(items[2]), alpha};
29
- } else if (items.size() >= 4) {
30
- rgba = {toDoubleLenient(items[0]), toDoubleLenient(items[1]), toDoubleLenient(items[2]),
31
- toDoubleLenient(items[3])};
41
+ std::array<double, 4> c{1.0, 1.0, 1.0, 1.0};
42
+ for (size_t i = 0; i < 4; ++i) {
43
+ if (i < items.size()) c[i] = toDoubleLenient(items[i]);
44
+ if (c[i] > 1 || c[i] < 0)
45
+ ev.warn("color() expects numbers between 0.0 and 1.0. Value of " + oneDecimal(c[i]) +
46
+ " is out of range",
47
+ &node.position());
32
48
  }
49
+ rgba = c;
50
+ }
51
+ if (const double* a = std::get_if<double>(&alphaArg)) {
52
+ if (rgba) (*rgba)[3] = *a;
53
+ if (*a < 0.0 || *a > 1.0)
54
+ ev.warn("color() expects alpha between 0.0 and 1.0. Value of " + oneDecimal(*a) + " is out of range",
55
+ &node.position());
33
56
  }
34
57
 
35
- EvalContext childCtx = effCtx.childCtx(nullptr, std::optional<std::array<double, 4>>(rgba));
58
+ // No colour: the child context inherits the outer one (childCtx's
59
+ // nullopt), and generateColor passes the children through untouched.
60
+ EvalContext childCtx = effCtx.childCtx(nullptr, rgba);
36
61
  CSGParams params;
37
62
  params["rgba"] = colorToValue(rgba);
38
63
  return BuiltinWrapParams{std::move(params), std::move(childCtx)};
@@ -70,6 +95,8 @@ std::shared_ptr<const std::vector<ColoredBody>> recolorParts(const std::vector<C
70
95
  std::vector<ColoredBody> generateColor(Evaluator& ev, const CSGParams& params,
71
96
  const std::vector<std::unique_ptr<CSGNode>>& children, const oscad::ASTNode&) {
72
97
  const auto rgba = valueToColor(params.at("rgba"));
98
+ // An unreadable colour sets none: the children keep their own.
99
+ if (!rgba) return flattenCsgTree(children);
73
100
  std::vector<ColoredBody> result;
74
101
  for (ColoredBody b : flattenCsgTree(children)) {
75
102
  b.color = rgba;
@@ -1,16 +1,18 @@
1
1
  #include "openscad_cpp_evaluator/css_colors.hpp"
2
+ #include "xkcd_colors.hpp"
2
3
 
3
4
  #include <cctype>
5
+ #include <optional>
4
6
  #include <unordered_map>
5
7
 
6
8
  namespace oscadeval {
7
9
 
8
10
  namespace {
9
11
 
10
- // Generated (not hand-transcribed) from the Python reference's own
11
- // _css_colors.py, itself generated from a live PySide6 install -- see that
12
- // file's docstring for the regeneration recipe and the Qt-vs-CSS3 list
13
- // discrepancy (British-spelling aliases included, `rebeccapurple` excluded).
12
+ // Originally generated from the Python reference's _css_colors.py (itself
13
+ // from a live PySide6 install). Now OpenSCAD's own WebColors.h list: every
14
+ // RGB here matches it, `rebeccapurple` (CSS Color Level 4) is added, and
15
+ // `transparent`, which needs an alpha of 0, is handled in parseColor.
14
16
  const std::unordered_map<std::string, std::array<double, 3>>& colorTable() {
15
17
  static const std::unordered_map<std::string, std::array<double, 3>> table = {
16
18
  {"aliceblue", {0.9411764740943909, 0.9725490212440491, 1.0}},
@@ -132,6 +134,7 @@ const std::unordered_map<std::string, std::array<double, 3>>& colorTable() {
132
134
  {"plum", {0.8666666746139526, 0.6274510025978088, 0.8666666746139526}},
133
135
  {"powderblue", {0.6901960968971252, 0.8784313797950745, 0.9019607901573181}},
134
136
  {"purple", {0.501960813999176, 0.0, 0.501960813999176}},
137
+ {"rebeccapurple", {0.4, 0.2, 0.6}},
135
138
  {"red", {1.0, 0.0, 0.0}},
136
139
  {"rosybrown", {0.7372549176216125, 0.5607843399047852, 0.5607843399047852}},
137
140
  {"royalblue", {0.2549019753932953, 0.4117647111415863, 0.8823529481887817}},
@@ -153,7 +156,6 @@ const std::unordered_map<std::string, std::array<double, 3>>& colorTable() {
153
156
  {"teal", {0.0, 0.501960813999176, 0.501960813999176}},
154
157
  {"thistle", {0.8470588326454163, 0.7490196228027344, 0.8470588326454163}},
155
158
  {"tomato", {1.0, 0.38823530077934265, 0.27843138575553894}},
156
- {"transparent", {0.0, 0.0, 0.0}},
157
159
  {"turquoise", {0.250980406999588, 0.8784313797950745, 0.8156862854957581}},
158
160
  {"violet", {0.9333333373069763, 0.5098039507865906, 0.9333333373069763}},
159
161
  {"wheat", {0.9607843160629272, 0.8705882430076599, 0.7019608020782471}},
@@ -171,38 +173,44 @@ std::string toLower(const std::string& s) {
171
173
  return r;
172
174
  }
173
175
 
174
- int hexDigit(char c) {
175
- if (c >= '0' && c <= '9') return c - '0';
176
- if (c >= 'a' && c <= 'f') return c - 'a' + 10;
177
- if (c >= 'A' && c <= 'F') return c - 'A' + 10;
178
- return 0;
176
+ // OpenSCAD's parse_hex_color (ColorUtil.cc): "#rgb", "#rgba", "#rrggbb" or
177
+ // "#rrggbbaa", every character after '#' a hex digit, or nothing at all.
178
+ std::optional<std::array<double, 4>> parseHex(const std::string& hex) {
179
+ const bool shortForm = hex.size() == 4 || hex.size() == 5;
180
+ const bool longForm = hex.size() == 7 || hex.size() == 9;
181
+ if ((!shortForm && !longForm) || hex[0] != '#') return std::nullopt;
182
+ for (size_t i = 1; i < hex.size(); ++i)
183
+ if (!std::isxdigit(static_cast<unsigned char>(hex[i]))) return std::nullopt;
184
+ const size_t stride = shortForm ? 1 : 2;
185
+ const double channelMax = shortForm ? 15.0 : 255.0;
186
+ std::array<double, 4> rgba{0.0, 0.0, 0.0, 1.0};
187
+ for (size_t i = 0; i < (hex.size() - 1) / stride; ++i)
188
+ rgba[i] = std::stoi(hex.substr(1 + i * stride, stride), nullptr, 16) / channelMax;
189
+ return rgba;
179
190
  }
180
191
 
181
192
  } // namespace
182
193
 
183
- std::array<double, 4> cssColor(const std::string& name, double alpha) {
184
- if (!name.empty() && name[0] == '#') {
185
- const std::string h = name.substr(1);
186
- if (h.size() == 6) {
187
- const double r = (hexDigit(h[0]) * 16 + hexDigit(h[1])) / 255.0;
188
- const double g = (hexDigit(h[2]) * 16 + hexDigit(h[3])) / 255.0;
189
- const double b = (hexDigit(h[4]) * 16 + hexDigit(h[5])) / 255.0;
190
- return {r, g, b, alpha};
191
- }
192
- if (h.size() == 3) {
193
- const double r = hexDigit(h[0]) / 15.0;
194
- const double g = hexDigit(h[1]) / 15.0;
195
- const double b = hexDigit(h[2]) / 15.0;
196
- return {r, g, b, alpha};
197
- }
198
- return {1.0, 1.0, 1.0, alpha};
194
+ std::optional<std::array<double, 4>> parseColor(const std::string& text) {
195
+ // OpenSCAD's parse_color: a name first (case-insensitive; "xkcd:" reaches
196
+ // the xkcd survey table, falling back to the CSS table), then hex.
197
+ const std::string name = toLower(text);
198
+ if (name.rfind("xkcd:", 0) == 0) {
199
+ const auto& xk = xkcdColorTable();
200
+ if (auto it = xk.find(name.substr(5)); it != xk.end())
201
+ return std::array<double, 4>{it->second[0] / 255.0, it->second[1] / 255.0, it->second[2] / 255.0, 1.0};
199
202
  }
203
+ // OpenSCAD's own addition to the CSS list: fully transparent black.
204
+ if (name == "transparent") return std::array<double, 4>{0.0, 0.0, 0.0, 0.0};
205
+ if (auto it = colorTable().find(name); it != colorTable().end())
206
+ return std::array<double, 4>{it->second[0], it->second[1], it->second[2], 1.0};
207
+ return parseHex(text);
208
+ }
200
209
 
201
- auto it = colorTable().find(toLower(name));
202
- if (it != colorTable().end()) {
203
- return {it->second[0], it->second[1], it->second[2], alpha};
204
- }
205
- return {1.0, 1.0, 1.0, alpha};
210
+ std::array<double, 4> cssColor(const std::string& name, double alpha) {
211
+ auto c = parseColor(name);
212
+ if (!c) return {1.0, 1.0, 1.0, alpha};
213
+ return {(*c)[0], (*c)[1], (*c)[2], alpha};
206
214
  }
207
215
 
208
216
  } // namespace oscadeval