openscad-cpp-evaluator 0.47.0__tar.gz → 0.48.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.
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/CLAUDE.md +14 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/function_builtins.cpp +174 -1
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_feature_detection.cpp +1 -1
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_function_builtins.cpp +99 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/uv.lock +0 -0
|
@@ -460,6 +460,20 @@ grep for `ponytail:`.
|
|
|
460
460
|
unknown arguments -- `children(separate=true)` renders the wrong shape there with no warning --
|
|
461
461
|
and both read as `undef` in OpenSCAD, so a guard written against them works in both.
|
|
462
462
|
|
|
463
|
+
- **`matrix_solve(A, b)`** (`src/builtins/function_builtins.cpp`, feature name `matrix-solve`) —
|
|
464
|
+
one pivoted LU answering three questions from the same factorisation: `.x` (solution; a vector
|
|
465
|
+
for a vector `b`, a matrix for a matrix `b`, `undef` when `b` is omitted), `.det`, `.singular`.
|
|
466
|
+
Square systems only — BOSL2's `linear_solve` also does least-squares/minimum-norm via QR, which
|
|
467
|
+
LU cannot, and those paths are already fast in script. A non-square or non-numeric matrix warns
|
|
468
|
+
and returns `undef`; a **singular** one is reported (`singular=true`, `det=0`, `x=undef`), not
|
|
469
|
+
warned about, because asking whether a matrix is singular is a fair question.
|
|
470
|
+
Why it exists: BOSL2's `determinant()` is a cofactor expansion, O(n!) in time *and* in
|
|
471
|
+
intermediate list allocation. Measured through the CLI on identical matrices, with identical
|
|
472
|
+
results: **n=10 3.17s → 0.33s, n=11 31.49s → 0.33s**. Its `linear_solve` is not the problem
|
|
473
|
+
(128×128 in ~0.18s since its Apr 2026 Householder rewrite) — but its singularity test is a fixed
|
|
474
|
+
**absolute** `1e-9` on R's diagonal with no scaling by ‖A‖, so `2*I` scaled by 1e-10 is declared
|
|
475
|
+
singular there and solves correctly here. This one uses a relative threshold.
|
|
476
|
+
|
|
463
477
|
- `include/openscad_cpp_evaluator/value.hpp`, `src/value.cpp` — the `Value` variant (OpenSCAD's
|
|
464
478
|
dynamic value type) and its free-function arithmetic/comparison helpers. See the plan's §1 for
|
|
465
479
|
the type-mapping table and why there's no numpy-equivalent library.
|
{openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -169,6 +169,172 @@ Value builtinCross(const Value& aArg, const Value& bArg) {
|
|
|
169
169
|
return Value{};
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
// -- matrix_solve(A, b) --------------------------------------------------
|
|
173
|
+
//
|
|
174
|
+
// One pivoted LU answers three questions at once -- the solution, the
|
|
175
|
+
// determinant, and whether A is singular -- because all three fall out of
|
|
176
|
+
// the same factorisation.
|
|
177
|
+
//
|
|
178
|
+
// The motivation is BOSL2's determinant() (linalg.scad), a cofactor
|
|
179
|
+
// expansion: O(n!) time AND O(n!) intermediate list allocation. Measured
|
|
180
|
+
// through this evaluator: 0.04s at n=8, 0.38s at n=9, 2.7s at n=10, and
|
|
181
|
+
// projecting to minutes at n=12. This is O(n^3). BOSL2's linear_solve, by
|
|
182
|
+
// contrast, was already fast (128x128 in ~0.18s after its Apr 2026
|
|
183
|
+
// Householder rewrite), so speed is not the argument for the solve half --
|
|
184
|
+
// a correct singularity test is. See below.
|
|
185
|
+
//
|
|
186
|
+
// Square systems only. BOSL2's linear_solve also handles overdetermined
|
|
187
|
+
// (least-squares) and underdetermined (minimum-norm) systems via QR; LU
|
|
188
|
+
// cannot, and those paths are quick enough in script. A non-square matrix
|
|
189
|
+
// warns rather than silently doing something else.
|
|
190
|
+
|
|
191
|
+
// Defined further down, next to the other object() helpers.
|
|
192
|
+
Value objectOf(std::vector<std::pair<std::string, Value>> items);
|
|
193
|
+
|
|
194
|
+
// A rectangular numeric matrix as (row-major values, column count).
|
|
195
|
+
// nullopt for anything that is not a non-empty list of equal-length,
|
|
196
|
+
// non-empty numeric lists.
|
|
197
|
+
std::optional<std::pair<std::vector<double>, size_t>> numericMatrix(const Value& v) {
|
|
198
|
+
const ListPtr* rows = std::get_if<ListPtr>(&v);
|
|
199
|
+
if (!rows || !*rows || (*rows)->items.empty()) return std::nullopt;
|
|
200
|
+
std::vector<double> flat;
|
|
201
|
+
size_t cols = 0;
|
|
202
|
+
for (size_t r = 0; r < (*rows)->items.size(); ++r) {
|
|
203
|
+
const std::optional<std::vector<double>> row = allNumericList((*rows)->items[r]);
|
|
204
|
+
if (!row || row->empty()) return std::nullopt;
|
|
205
|
+
if (r == 0) {
|
|
206
|
+
cols = row->size();
|
|
207
|
+
} else if (row->size() != cols) {
|
|
208
|
+
return std::nullopt;
|
|
209
|
+
}
|
|
210
|
+
flat.insert(flat.end(), row->begin(), row->end());
|
|
211
|
+
}
|
|
212
|
+
return std::make_pair(std::move(flat), cols);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
Value builtinMatrixSolve(Evaluator& ev, const Value& aArg, const Value& bArg, bool haveB,
|
|
216
|
+
const oscad::ASTNode& node) {
|
|
217
|
+
const auto parsed = numericMatrix(aArg);
|
|
218
|
+
if (!parsed) {
|
|
219
|
+
ev.warn("matrix_solve() requires a matrix of numbers", &node.position());
|
|
220
|
+
return Value{};
|
|
221
|
+
}
|
|
222
|
+
std::vector<double> lu = parsed->first;
|
|
223
|
+
const size_t cols = parsed->second;
|
|
224
|
+
const size_t n = lu.size() / cols;
|
|
225
|
+
if (n != cols) {
|
|
226
|
+
ev.warn("matrix_solve() requires a square matrix, found " + std::to_string(n) + " x " +
|
|
227
|
+
std::to_string(cols),
|
|
228
|
+
&node.position());
|
|
229
|
+
return Value{};
|
|
230
|
+
}
|
|
231
|
+
double maxAbs = 0.0;
|
|
232
|
+
for (double x : lu) {
|
|
233
|
+
if (!std::isfinite(x)) {
|
|
234
|
+
ev.warn("matrix_solve() matrix contains a non-finite value", &node.position());
|
|
235
|
+
return Value{};
|
|
236
|
+
}
|
|
237
|
+
maxAbs = std::max(maxAbs, std::abs(x));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Right-hand side: a vector of n is one column; a matrix of n rows is
|
|
241
|
+
// one column per column. Kept row-major alongside the factorisation.
|
|
242
|
+
size_t k = 0;
|
|
243
|
+
bool bWasVector = false;
|
|
244
|
+
std::vector<double> rhs;
|
|
245
|
+
if (haveB) {
|
|
246
|
+
if (const std::optional<std::vector<double>> vec = allNumericList(bArg); vec && vec->size() == n) {
|
|
247
|
+
bWasVector = true;
|
|
248
|
+
k = 1;
|
|
249
|
+
rhs = *vec;
|
|
250
|
+
} else if (const auto bm = numericMatrix(bArg); bm && bm->first.size() / bm->second == n) {
|
|
251
|
+
k = bm->second;
|
|
252
|
+
rhs = bm->first;
|
|
253
|
+
} else {
|
|
254
|
+
ev.warn("matrix_solve() right-hand side must be a vector of " + std::to_string(n) +
|
|
255
|
+
" numbers, or a matrix with that many rows",
|
|
256
|
+
&node.position());
|
|
257
|
+
return Value{};
|
|
258
|
+
}
|
|
259
|
+
for (double x : rhs) {
|
|
260
|
+
if (!std::isfinite(x)) {
|
|
261
|
+
ev.warn("matrix_solve() right-hand side contains a non-finite value", &node.position());
|
|
262
|
+
return Value{};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Relative singularity threshold. BOSL2 compares R's diagonal against a
|
|
268
|
+
// fixed ABSOLUTE 1e-9 (its _EPSILON) with no scaling by the size of the
|
|
269
|
+
// matrix, so a perfectly well-conditioned system scaled down by 1e-10
|
|
270
|
+
// is declared singular there. Scaling by maxAbs is what makes
|
|
271
|
+
// matrix_solve(A*1e-10) still solvable.
|
|
272
|
+
const double tol = std::numeric_limits<double>::epsilon() * static_cast<double>(n) * std::max(maxAbs, 1.0);
|
|
273
|
+
|
|
274
|
+
double det = 1.0;
|
|
275
|
+
bool singular = false;
|
|
276
|
+
for (size_t col = 0; col < n && !singular; ++col) {
|
|
277
|
+
size_t pivot = col;
|
|
278
|
+
for (size_t r = col + 1; r < n; ++r) {
|
|
279
|
+
if (std::abs(lu[r * n + col]) > std::abs(lu[pivot * n + col])) pivot = r;
|
|
280
|
+
}
|
|
281
|
+
if (std::abs(lu[pivot * n + col]) <= tol) {
|
|
282
|
+
singular = true;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
if (pivot != col) {
|
|
286
|
+
for (size_t c = 0; c < n; ++c) std::swap(lu[col * n + c], lu[pivot * n + c]);
|
|
287
|
+
for (size_t c = 0; c < k; ++c) std::swap(rhs[col * k + c], rhs[pivot * k + c]);
|
|
288
|
+
det = -det;
|
|
289
|
+
}
|
|
290
|
+
const double p = lu[col * n + col];
|
|
291
|
+
det *= p;
|
|
292
|
+
for (size_t r = col + 1; r < n; ++r) {
|
|
293
|
+
const double f = lu[r * n + col] / p;
|
|
294
|
+
if (f == 0.0) continue;
|
|
295
|
+
lu[r * n + col] = 0.0;
|
|
296
|
+
for (size_t c = col + 1; c < n; ++c) lu[r * n + c] -= f * lu[col * n + c];
|
|
297
|
+
for (size_t c = 0; c < k; ++c) rhs[r * k + c] -= f * rhs[col * k + c];
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
std::vector<std::pair<std::string, Value>> out;
|
|
302
|
+
if (singular) {
|
|
303
|
+
// Not a misuse -- "is this matrix singular?" is a legitimate
|
|
304
|
+
// question to ask matrix_solve, so it answers rather than warning.
|
|
305
|
+
out.emplace_back("x", Value{});
|
|
306
|
+
out.emplace_back("det", Value{0.0});
|
|
307
|
+
out.emplace_back("singular", Value{true});
|
|
308
|
+
return objectOf(std::move(out));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (haveB) {
|
|
312
|
+
for (size_t col = n; col-- > 0;) {
|
|
313
|
+
for (size_t c = 0; c < k; ++c) {
|
|
314
|
+
double acc = rhs[col * k + c];
|
|
315
|
+
for (size_t j = col + 1; j < n; ++j) acc -= lu[col * n + j] * rhs[j * k + c];
|
|
316
|
+
rhs[col * k + c] = acc / lu[col * n + col];
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (bWasVector) {
|
|
320
|
+
out.emplace_back("x", numList(rhs));
|
|
321
|
+
} else {
|
|
322
|
+
std::vector<Value> rowsOut;
|
|
323
|
+
rowsOut.reserve(n);
|
|
324
|
+
for (size_t r = 0; r < n; ++r) {
|
|
325
|
+
rowsOut.push_back(numList(std::vector<double>(rhs.begin() + static_cast<long>(r * k),
|
|
326
|
+
rhs.begin() + static_cast<long>((r + 1) * k))));
|
|
327
|
+
}
|
|
328
|
+
out.emplace_back("x", listOf(std::move(rowsOut)));
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
out.emplace_back("x", Value{});
|
|
332
|
+
}
|
|
333
|
+
out.emplace_back("det", Value{det});
|
|
334
|
+
out.emplace_back("singular", Value{false});
|
|
335
|
+
return objectOf(std::move(out));
|
|
336
|
+
}
|
|
337
|
+
|
|
172
338
|
Value builtinRands(double minv, double maxv, double nArg, const Value& seedArg) {
|
|
173
339
|
// ponytail: doesn't reproduce Python's Mersenne-Twister bit-for-bit --
|
|
174
340
|
// no script should depend on cross-language RNG equality, only on
|
|
@@ -450,6 +616,7 @@ bool isBuiltinFunctionName(const std::string& name) {
|
|
|
450
616
|
"chr", "ord", "is_undef", "is_num", "is_bool", "is_string", "is_list", "is_function", "is_object",
|
|
451
617
|
"search", "lookup", "has_key", "version", "version_num", "parent_module",
|
|
452
618
|
"object", "textmetrics", "fontmetrics", "dxf_dim", "dxf_cross", "supported_feature",
|
|
619
|
+
"matrix_solve",
|
|
453
620
|
};
|
|
454
621
|
return names.count(name) > 0;
|
|
455
622
|
}
|
|
@@ -591,7 +758,7 @@ enum class BuiltinFnId {
|
|
|
591
758
|
TextMetrics, FontMetrics, Abs, Sign, Ceil, Floor, Round, Sqrt, Ln, Log, Exp, Sin, Cos, Tan,
|
|
592
759
|
Asin, Acos, Atan, Atan2, Max, Min, Pow, Norm, Cross, Rands, Concat, Len, Str, Chr, Ord,
|
|
593
760
|
IsUndef, IsNum, IsBool, IsString, IsList, IsFunction, IsObject, Search, Lookup, HasKey,
|
|
594
|
-
Version, VersionNum, ParentModule, DxfDim, DxfCross, SupportedFeature,
|
|
761
|
+
Version, VersionNum, ParentModule, DxfDim, DxfCross, SupportedFeature, MatrixSolve,
|
|
595
762
|
};
|
|
596
763
|
|
|
597
764
|
// supported_feature("name") -> the level at which this build implements that
|
|
@@ -611,6 +778,7 @@ enum class BuiltinFnId {
|
|
|
611
778
|
const std::unordered_map<std::string, double>& featureLevels() {
|
|
612
779
|
static const std::unordered_map<std::string, double> levels = {
|
|
613
780
|
{"render-expr", 1.0}, // render() in expression position
|
|
781
|
+
{"matrix-solve", 1.0}, // matrix_solve(A, b) -> {x, det, singular}
|
|
614
782
|
{"polyhedron-vnf", 1.0}, // polyhedron(vnf) / polyhedron(object)
|
|
615
783
|
{"separate-children", 1.0}, // children(..., separate=true)
|
|
616
784
|
{"minkowski-diff", 1.0}, // minkowski_difference()
|
|
@@ -645,6 +813,7 @@ const std::unordered_map<std::string, BuiltinFnId>& builtinFnIds() {
|
|
|
645
813
|
{"parent_module", BuiltinFnId::ParentModule},
|
|
646
814
|
{"dxf_dim", BuiltinFnId::DxfDim}, {"dxf_cross", BuiltinFnId::DxfCross},
|
|
647
815
|
{"supported_feature", BuiltinFnId::SupportedFeature},
|
|
816
|
+
{"matrix_solve", BuiltinFnId::MatrixSolve},
|
|
648
817
|
};
|
|
649
818
|
return ids;
|
|
650
819
|
}
|
|
@@ -726,6 +895,7 @@ const std::unordered_map<int, BuiltinCheck>& builtinChecks() {
|
|
|
726
895
|
add(BuiltinFnId::ParentModule, {0, 1, "1", {kNum}});
|
|
727
896
|
add(BuiltinFnId::HasKey, {-1, -1, nullptr, {{TObj, "object"}, {TStr, "string"}}});
|
|
728
897
|
add(BuiltinFnId::SupportedFeature, {1, 1, "1", {}});
|
|
898
|
+
add(BuiltinFnId::MatrixSolve, {1, 2, "1 or 2", {kVec}});
|
|
729
899
|
for (BuiltinFnId id : {BuiltinFnId::IsUndef, BuiltinFnId::IsNum, BuiltinFnId::IsBool,
|
|
730
900
|
BuiltinFnId::IsString, BuiltinFnId::IsList, BuiltinFnId::IsFunction,
|
|
731
901
|
BuiltinFnId::IsObject}) {
|
|
@@ -1040,6 +1210,9 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
1040
1210
|
// The OpenSCAD release we track. version_num() is that same
|
|
1041
1211
|
// year/month/day folded as y * 10000 + m * 100 + d, exactly like the
|
|
1042
1212
|
// reference's own builtin_version_num (builtin_functions.cc).
|
|
1213
|
+
case BuiltinFnId::MatrixSolve:
|
|
1214
|
+
return builtinMatrixSolve(ev, getArg(args, 0, "A", Value{}), getArg(args, 1, "b", Value{}),
|
|
1215
|
+
args.findPositional(1) != nullptr || args.findNamed("b") != nullptr, node);
|
|
1043
1216
|
case BuiltinFnId::SupportedFeature: {
|
|
1044
1217
|
const Value name = getArg(args, 0, "feature", Value{});
|
|
1045
1218
|
const std::string* s = std::get_if<std::string>(&name);
|
{openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_feature_detection.cpp
RENAMED
|
@@ -35,7 +35,7 @@ std::vector<std::string> echoesFrom(const std::string& src) {
|
|
|
35
35
|
// in featureLevels() -- shows up as a 0.
|
|
36
36
|
const char* kFeatures[] = {"separate-children", "minkowski-diff", "sphere-styles", "export-name",
|
|
37
37
|
"simplify-op", "expr-import", "object-function", "roof-op",
|
|
38
|
-
"render-expr", "polyhedron-vnf"};
|
|
38
|
+
"render-expr", "polyhedron-vnf", "matrix-solve"};
|
|
39
39
|
|
|
40
40
|
} // namespace
|
|
41
41
|
|
{openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/tests/test_function_builtins.cpp
RENAMED
|
@@ -725,3 +725,102 @@ TEST(StringListBuiltins, ChrIsVariadicAndRangeChecked) {
|
|
|
725
725
|
EXPECT_EQ(asStr(evalSrc("chr(55296)", ev)), ""); // 0xD800, a surrogate
|
|
726
726
|
EXPECT_EQ(asStr(evalSrc("chr(1114112)", ev)), ""); // 0x110000, one past the top
|
|
727
727
|
}
|
|
728
|
+
|
|
729
|
+
// -- matrix_solve(A, b) --------------------------------------------------
|
|
730
|
+
//
|
|
731
|
+
// One pivoted LU, three answers: x, det, singular. Oracle is BOSL2 plus
|
|
732
|
+
// arithmetic -- OpenSCAD has no equivalent, so there is no reference
|
|
733
|
+
// binary to compare against.
|
|
734
|
+
//
|
|
735
|
+
// Measured against BOSL2 through the CLI, same matrices, identical
|
|
736
|
+
// determinants: n=10 3.17s -> 0.33s, n=11 31.49s -> 0.33s. BOSL2's
|
|
737
|
+
// determinant() is a cofactor expansion, growing x10 per step; this is
|
|
738
|
+
// O(n^3).
|
|
739
|
+
|
|
740
|
+
TEST(MatrixSolve, SolvesASquareSystem) {
|
|
741
|
+
Evaluator ev;
|
|
742
|
+
// asList() returns a reference INTO v, so v has to outlive it -- taking
|
|
743
|
+
// asList(evalSrc(...)) directly dangles the moment the temporary dies.
|
|
744
|
+
const Value v = evalSrc("matrix_solve([[2,1],[1,3]], [5,10]).x", ev);
|
|
745
|
+
const std::vector<Value>& x = asList(v);
|
|
746
|
+
ASSERT_EQ(x.size(), 2u);
|
|
747
|
+
EXPECT_NEAR(asNum(x[0]), 1.0, 1e-12);
|
|
748
|
+
EXPECT_NEAR(asNum(x[1]), 3.0, 1e-12);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
TEST(MatrixSolve, DeterminantComesFreeFromTheSameFactorisation) {
|
|
752
|
+
Evaluator ev;
|
|
753
|
+
EXPECT_NEAR(asNum(evalSrc("matrix_solve([[2,1],[1,3]]).det", ev)), 5.0, 1e-12);
|
|
754
|
+
EXPECT_NEAR(asNum(evalSrc("matrix_solve([[1,0,0],[0,1,0],[0,0,1]]).det", ev)), 1.0, 1e-12);
|
|
755
|
+
// Sign follows the row swaps, not just the magnitude.
|
|
756
|
+
EXPECT_NEAR(asNum(evalSrc("matrix_solve([[0,1],[1,0]]).det", ev)), -1.0, 1e-12);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
TEST(MatrixSolve, WithNoRightHandSideThereIsNoSolution) {
|
|
760
|
+
Evaluator ev;
|
|
761
|
+
EXPECT_TRUE(isUndef(evalSrc("matrix_solve([[2,1],[1,3]]).x", ev)));
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
TEST(MatrixSolve, ASingularMatrixAnswersRatherThanWarning) {
|
|
765
|
+
// "Is this matrix singular?" is a fair question to ask, so it is
|
|
766
|
+
// reported, not warned about: det 0, singular true, no solution.
|
|
767
|
+
Evaluator ev;
|
|
768
|
+
EXPECT_TRUE(asBool(evalSrc("matrix_solve([[1,2],[2,4]], [1,2]).singular", ev)));
|
|
769
|
+
EXPECT_NEAR(asNum(evalSrc("matrix_solve([[1,2],[2,4]], [1,2]).det", ev)), 0.0, 1e-12);
|
|
770
|
+
EXPECT_TRUE(isUndef(evalSrc("matrix_solve([[1,2],[2,4]], [1,2]).x", ev)));
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
TEST(MatrixSolve, SingularityIsRelativeToTheMatrixNotAbsolute) {
|
|
774
|
+
// 2*I scaled by 1e-10 is perfectly conditioned, and BOSL2's
|
|
775
|
+
// linear_solve calls it singular -- its test is a fixed absolute 1e-9
|
|
776
|
+
// on R's diagonal with no scaling by the size of the matrix. Verified
|
|
777
|
+
// through the CLI: BOSL2 says SINGULAR at 1e-10 and 1e-12 where this
|
|
778
|
+
// solves correctly.
|
|
779
|
+
Evaluator ev;
|
|
780
|
+
EXPECT_FALSE(asBool(evalSrc("matrix_solve([[2e-10,0],[0,2e-10]], [1e-10,1e-10]).singular", ev)));
|
|
781
|
+
const Value v = evalSrc("matrix_solve([[2e-10,0],[0,2e-10]], [1e-10,1e-10]).x", ev);
|
|
782
|
+
const std::vector<Value>& x = asList(v);
|
|
783
|
+
ASSERT_EQ(x.size(), 2u);
|
|
784
|
+
EXPECT_NEAR(asNum(x[0]), 0.5, 1e-9);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
TEST(MatrixSolve, AMatrixRightHandSideSolvesEveryColumn) {
|
|
788
|
+
Evaluator ev;
|
|
789
|
+
// Solving against the identity is the inverse; [[2,1],[1,3]] has
|
|
790
|
+
// det 5, so the inverse is [[0.6,-0.2],[-0.2,0.4]].
|
|
791
|
+
const Value v = evalSrc("matrix_solve([[2,1],[1,3]], [[1,0],[0,1]]).x", ev);
|
|
792
|
+
const std::vector<Value>& rows = asList(v);
|
|
793
|
+
ASSERT_EQ(rows.size(), 2u);
|
|
794
|
+
const std::vector<Value>& r0 = asList(rows[0]);
|
|
795
|
+
ASSERT_EQ(r0.size(), 2u);
|
|
796
|
+
EXPECT_NEAR(asNum(r0[0]), 0.6, 1e-12);
|
|
797
|
+
EXPECT_NEAR(asNum(r0[1]), -0.2, 1e-12);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
TEST(MatrixSolve, RejectsWhatItCannotFactor) {
|
|
801
|
+
Evaluator ev;
|
|
802
|
+
EXPECT_TRUE(isUndef(evalSrc("matrix_solve([[1,2,3],[4,5,6]], [1,2])", ev))); // not square
|
|
803
|
+
EXPECT_TRUE(isUndef(evalSrc("matrix_solve([[1,2],[3,\"x\"]], [1,2])", ev))); // not numeric
|
|
804
|
+
EXPECT_TRUE(isUndef(evalSrc("matrix_solve([[1,2],[3,4]], [1,2,3])", ev))); // wrong rhs length
|
|
805
|
+
EXPECT_TRUE(isUndef(evalSrc("matrix_solve([[1,2],[3,4,5]], [1,2])", ev))); // ragged
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
TEST(MatrixSolve, NonSquareWarnsWithItsShape) {
|
|
809
|
+
const std::vector<std::string> w = builtinWarnings("matrix_solve([[1,2,3],[4,5,6]], [1,2])");
|
|
810
|
+
ASSERT_EQ(w.size(), 1u);
|
|
811
|
+
EXPECT_NE(w[0].find("matrix_solve() requires a square matrix, found 2 x 3"), std::string::npos)
|
|
812
|
+
<< "got " << w[0];
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
TEST(MatrixSolve, WorksUnderBothEngines) {
|
|
816
|
+
for (bool vm : {false, true}) {
|
|
817
|
+
ScopedVm guard(vm);
|
|
818
|
+
const std::vector<std::string> e =
|
|
819
|
+
echoesOf("r = matrix_solve([[2,1],[1,3]], [5,10]);\necho(r.x, r.det, r.singular);");
|
|
820
|
+
ASSERT_EQ(e.size(), 1u) << "vm=" << vm;
|
|
821
|
+
EXPECT_NE(e[0].find("[1, 3]"), std::string::npos) << "vm=" << vm << ", got " << e[0];
|
|
822
|
+
EXPECT_NE(e[0].find("5"), std::string::npos) << "vm=" << vm << ", got " << e[0];
|
|
823
|
+
EXPECT_NE(e[0].find("false"), std::string::npos) << "vm=" << vm << ", got " << e[0];
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
File without changes
|
{openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/.github/workflows/wheels.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/examples/eval_perf_benchmark.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/examples/manifold_cache_reuse.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-0.47.0 → openscad_cpp_evaluator-0.48.0}/examples/minimal_debugger.cpp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|