openscad-cpp-evaluator 0.48.0__tar.gz → 0.49.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.48.0 → openscad_cpp_evaluator-0.49.0}/CLAUDE.md +52 -13
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/function_builtins.cpp +160 -26
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_feature_detection.cpp +1 -1
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_function_builtins.cpp +89 -30
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/uv.lock +0 -0
|
@@ -460,19 +460,58 @@ 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
|
-
- **`
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
463
|
+
- **`linear_solve(A, b)`** (`src/builtins/function_builtins.cpp`, feature name `linear-solve`) —
|
|
464
|
+
returns `.x` (solution), `.det`, `.singular`. Dispatches on shape:
|
|
465
|
+
|
|
466
|
+
| shape | method | `.x` | `.det` |
|
|
467
|
+
|---|---|---|---|
|
|
468
|
+
| m = n | pivoted **LU** | the solution | product of U's diagonal, signed by row swaps |
|
|
469
|
+
| m > n | **Householder QR** | least squares | `undef` |
|
|
470
|
+
| m < n | **QR of Aᵀ** | minimum norm | `undef` |
|
|
471
|
+
|
|
472
|
+
Q is never formed. The reflectors are stored below the diagonal (v[0]
|
|
473
|
+
normalised to 1, scale in `taus`) and applied in forward order for `Qᵀb`,
|
|
474
|
+
reverse order for `Qy` — which is why this is far smaller and quicker than
|
|
475
|
+
BOSL2, whose QR materialises an m×m Q *and* an n×n permutation matrix and
|
|
476
|
+
then multiplies by it. **Minimum norm** comes from padding `y` with zeros to
|
|
477
|
+
length n before applying Q; a residual check alone will NOT catch getting
|
|
478
|
+
that wrong, since any solution passes it — assert the norm.
|
|
479
|
+
Rank deficiency without column pivoting cannot distinguish rank-deficient
|
|
480
|
+
from very ill-conditioned. It is a relative-tolerance heuristic on the
|
|
481
|
+
diagonal; say so rather than implying it is exact.
|
|
482
|
+
|
|
483
|
+
**Named to collide, deliberately.** BOSL2 defines `linear_solve()`, and since
|
|
484
|
+
0.47.0 a user function *shadows* a builtin — so this is unreachable from any
|
|
485
|
+
script that includes BOSL2 until BOSL2 drops its own. That is the intended
|
|
486
|
+
path (BelfrySCAD's author maintains BOSL2); callers then gain a `.x`, since
|
|
487
|
+
BOSL2's returns the bare solution and `[]` for singular. **Do not "fix" the
|
|
488
|
+
shadowing to make this reachable** — the shadowing is the correct, reference-
|
|
489
|
+
matching behaviour, and was itself a bug fix.
|
|
490
|
+
|
|
491
|
+
Measured per call, BOSL2 load subtracted, enough repetitions to clear noise
|
|
492
|
+
(harness checked: 200/2000/20000 reps at n=64 give 43/45/43 µs):
|
|
493
|
+
|
|
494
|
+
| n | BOSL2 `linear_solve` | ours | | n | BOSL2 `determinant` | ours |
|
|
495
|
+
|---:|---:|---:|---:|---:|---:|---:|
|
|
496
|
+
| 4 | 280 µs | 1.5 µs | 187× | 10 | 3.17 s | 0.33 s |
|
|
497
|
+
| 16 | 2.58 ms | 4.0 µs | 644× | 11 | 31.49 s | 0.33 s |
|
|
498
|
+
| 64 | 42.5 ms | 43 µs | 988× | | *cofactor expansion, O(n!)* | |
|
|
499
|
+
|
|
500
|
+
**Do not repeat the claim this replaces**: that BOSL2's solve "is not the
|
|
501
|
+
problem (128×128 in ~0.18s)". That was one-shot, with the solve lost inside
|
|
502
|
+
BOSL2's own load time. Keep the comparison fair though — BOSL2 does QR where
|
|
503
|
+
the square path here does LU, and it validates arguments every call.
|
|
504
|
+
Also correctness, not only speed: BOSL2's singularity test is a fixed
|
|
505
|
+
**absolute** `1e-9` on R's diagonal, unscaled by ‖A‖, so `2*I` scaled by
|
|
506
|
+
1e-10 is called singular there and solves correctly here.
|
|
507
|
+
|
|
508
|
+
**Profiling that shaped the scope** (don't redo it): NURBS *evaluation* is
|
|
509
|
+
0.6% linear algebra — basis eval 44%, type checks 25%. NURBS *interpolation*
|
|
510
|
+
is 35% at n=400, and every hot solve is **square**, so the QR paths are a
|
|
511
|
+
capability, not a measured win. `plane_intersection` (2×3, underdetermined)
|
|
512
|
+
is 0.1% of a `rounded_prism` model. The recurring cost across every workload
|
|
513
|
+
profiled is BOSL2's **argument type-checking**: 25–45%, millions of
|
|
514
|
+
`is_finite`/`is_num`/`is_nan` calls.
|
|
476
515
|
|
|
477
516
|
- `include/openscad_cpp_evaluator/value.hpp`, `src/value.cpp` — the `Value` variant (OpenSCAD's
|
|
478
517
|
dynamic value type) and its free-function arithmetic/comparison helpers. See the plan's §1 for
|
{openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -169,7 +169,7 @@ Value builtinCross(const Value& aArg, const Value& bArg) {
|
|
|
169
169
|
return Value{};
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
// --
|
|
172
|
+
// -- linear_solve(A, b) --------------------------------------------------
|
|
173
173
|
//
|
|
174
174
|
// One pivoted LU answers three questions at once -- the solution, the
|
|
175
175
|
// determinant, and whether A is singular -- because all three fall out of
|
|
@@ -212,26 +212,96 @@ std::optional<std::pair<std::vector<double>, size_t>> numericMatrix(const Value&
|
|
|
212
212
|
return std::make_pair(std::move(flat), cols);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
// Householder QR of a tall matrix `a` (m rows, n cols, m >= n), in place.
|
|
216
|
+
//
|
|
217
|
+
// On return the upper triangle of `a` is R. Below the diagonal, column j
|
|
218
|
+
// holds the reflector vector v_j from element 1 onwards -- v_j[0] is
|
|
219
|
+
// normalised to 1 and therefore not stored -- and `taus[j]` is its scale,
|
|
220
|
+
// so H_j = I - tau_j v_j v_j^T. Q is never formed: everything that needs
|
|
221
|
+
// it applies the reflectors instead, which is the whole reason this is
|
|
222
|
+
// smaller and quicker than materialising an m x m matrix.
|
|
223
|
+
//
|
|
224
|
+
// The sign of alpha is chosen away from x[0] so that v[0] can never cancel
|
|
225
|
+
// to zero -- the textbook guard against catastrophic cancellation here.
|
|
226
|
+
//
|
|
227
|
+
// Returns false if a diagonal falls at or below `tol`, i.e. rank-deficient
|
|
228
|
+
// to the tolerance given. Without column pivoting this cannot tell
|
|
229
|
+
// rank-deficient from merely very ill-conditioned; see the caller's note.
|
|
230
|
+
bool householderQr(std::vector<double>& a, size_t m, size_t n, std::vector<double>& taus, double tol) {
|
|
231
|
+
taus.assign(n, 0.0);
|
|
232
|
+
for (size_t j = 0; j < n; ++j) {
|
|
233
|
+
double normx = 0.0;
|
|
234
|
+
for (size_t r = j; r < m; ++r) normx += a[r * n + j] * a[r * n + j];
|
|
235
|
+
normx = std::sqrt(normx);
|
|
236
|
+
if (normx <= tol) return false;
|
|
237
|
+
|
|
238
|
+
const double x0 = a[j * n + j];
|
|
239
|
+
const double alpha = (x0 >= 0.0) ? -normx : normx;
|
|
240
|
+
const double v0 = x0 - alpha;
|
|
241
|
+
// Store v scaled so v[0] == 1; the below-diagonal entries are the tail.
|
|
242
|
+
double vv = 1.0;
|
|
243
|
+
for (size_t r = j + 1; r < m; ++r) {
|
|
244
|
+
a[r * n + j] /= v0;
|
|
245
|
+
vv += a[r * n + j] * a[r * n + j];
|
|
246
|
+
}
|
|
247
|
+
taus[j] = 2.0 / vv;
|
|
248
|
+
a[j * n + j] = alpha;
|
|
249
|
+
|
|
250
|
+
// Apply H_j to the trailing columns.
|
|
251
|
+
for (size_t c = j + 1; c < n; ++c) {
|
|
252
|
+
double dot = a[j * n + c];
|
|
253
|
+
for (size_t r = j + 1; r < m; ++r) dot += a[r * n + j] * a[r * n + c];
|
|
254
|
+
const double f = taus[j] * dot;
|
|
255
|
+
a[j * n + c] -= f;
|
|
256
|
+
for (size_t r = j + 1; r < m; ++r) a[r * n + c] -= f * a[r * n + j];
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// b <- Q^T b, for b with `k` columns. Reflectors applied in forward order.
|
|
263
|
+
void applyQtranspose(const std::vector<double>& a, size_t m, size_t n, const std::vector<double>& taus,
|
|
264
|
+
std::vector<double>& b, size_t k) {
|
|
265
|
+
for (size_t j = 0; j < n; ++j) {
|
|
266
|
+
for (size_t c = 0; c < k; ++c) {
|
|
267
|
+
double dot = b[j * k + c];
|
|
268
|
+
for (size_t r = j + 1; r < m; ++r) dot += a[r * n + j] * b[r * k + c];
|
|
269
|
+
const double f = taus[j] * dot;
|
|
270
|
+
b[j * k + c] -= f;
|
|
271
|
+
for (size_t r = j + 1; r < m; ++r) b[r * k + c] -= f * a[r * n + j];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// z <- Q z. Same reflectors, reverse order -- that is the only difference
|
|
277
|
+
// between applying Q and applying Q^T.
|
|
278
|
+
void applyQ(const std::vector<double>& a, size_t m, size_t n, const std::vector<double>& taus,
|
|
279
|
+
std::vector<double>& z, size_t k) {
|
|
280
|
+
for (size_t jj = n; jj-- > 0;) {
|
|
281
|
+
for (size_t c = 0; c < k; ++c) {
|
|
282
|
+
double dot = z[jj * k + c];
|
|
283
|
+
for (size_t r = jj + 1; r < m; ++r) dot += a[r * n + jj] * z[r * k + c];
|
|
284
|
+
const double f = taus[jj] * dot;
|
|
285
|
+
z[jj * k + c] -= f;
|
|
286
|
+
for (size_t r = jj + 1; r < m; ++r) z[r * k + c] -= f * a[r * n + jj];
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
Value builtinLinearSolve(Evaluator& ev, const Value& aArg, const Value& bArg, bool haveB,
|
|
216
292
|
const oscad::ASTNode& node) {
|
|
217
293
|
const auto parsed = numericMatrix(aArg);
|
|
218
294
|
if (!parsed) {
|
|
219
|
-
ev.warn("
|
|
295
|
+
ev.warn("linear_solve() requires a matrix of numbers", &node.position());
|
|
220
296
|
return Value{};
|
|
221
297
|
}
|
|
222
298
|
std::vector<double> lu = parsed->first;
|
|
223
|
-
const size_t
|
|
224
|
-
const size_t
|
|
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
|
-
}
|
|
299
|
+
const size_t n = parsed->second; // columns == unknowns
|
|
300
|
+
const size_t m = lu.size() / n; // rows == equations
|
|
231
301
|
double maxAbs = 0.0;
|
|
232
302
|
for (double x : lu) {
|
|
233
303
|
if (!std::isfinite(x)) {
|
|
234
|
-
ev.warn("
|
|
304
|
+
ev.warn("linear_solve() matrix contains a non-finite value", &node.position());
|
|
235
305
|
return Value{};
|
|
236
306
|
}
|
|
237
307
|
maxAbs = std::max(maxAbs, std::abs(x));
|
|
@@ -243,22 +313,22 @@ Value builtinMatrixSolve(Evaluator& ev, const Value& aArg, const Value& bArg, bo
|
|
|
243
313
|
bool bWasVector = false;
|
|
244
314
|
std::vector<double> rhs;
|
|
245
315
|
if (haveB) {
|
|
246
|
-
if (const std::optional<std::vector<double>> vec = allNumericList(bArg); vec && vec->size() ==
|
|
316
|
+
if (const std::optional<std::vector<double>> vec = allNumericList(bArg); vec && vec->size() == m) {
|
|
247
317
|
bWasVector = true;
|
|
248
318
|
k = 1;
|
|
249
319
|
rhs = *vec;
|
|
250
|
-
} else if (const auto bm = numericMatrix(bArg); bm && bm->first.size() / bm->second ==
|
|
320
|
+
} else if (const auto bm = numericMatrix(bArg); bm && bm->first.size() / bm->second == m) {
|
|
251
321
|
k = bm->second;
|
|
252
322
|
rhs = bm->first;
|
|
253
323
|
} else {
|
|
254
|
-
ev.warn("
|
|
324
|
+
ev.warn("linear_solve() right-hand side must be a vector of " + std::to_string(m) +
|
|
255
325
|
" numbers, or a matrix with that many rows",
|
|
256
326
|
&node.position());
|
|
257
327
|
return Value{};
|
|
258
328
|
}
|
|
259
329
|
for (double x : rhs) {
|
|
260
330
|
if (!std::isfinite(x)) {
|
|
261
|
-
ev.warn("
|
|
331
|
+
ev.warn("linear_solve() right-hand side contains a non-finite value", &node.position());
|
|
262
332
|
return Value{};
|
|
263
333
|
}
|
|
264
334
|
}
|
|
@@ -268,8 +338,72 @@ Value builtinMatrixSolve(Evaluator& ev, const Value& aArg, const Value& bArg, bo
|
|
|
268
338
|
// fixed ABSOLUTE 1e-9 (its _EPSILON) with no scaling by the size of the
|
|
269
339
|
// matrix, so a perfectly well-conditioned system scaled down by 1e-10
|
|
270
340
|
// is declared singular there. Scaling by maxAbs is what makes
|
|
271
|
-
//
|
|
272
|
-
const double tol =
|
|
341
|
+
// linear_solve(A*1e-10) still solvable.
|
|
342
|
+
const double tol =
|
|
343
|
+
std::numeric_limits<double>::epsilon() * static_cast<double>(std::max(m, n)) * std::max(maxAbs, 1.0);
|
|
344
|
+
|
|
345
|
+
// Non-square: QR, and no determinant to report.
|
|
346
|
+
//
|
|
347
|
+
// m > n overdetermined -> least squares. Factor A, apply Q^T to b, then
|
|
348
|
+
// back-substitute the leading n x n block of R.
|
|
349
|
+
// m < n underdetermined -> minimum norm. Factor A^T instead, forward-solve
|
|
350
|
+
// R^T y = b, and return Q [y; 0]. Padding with zeros is what makes
|
|
351
|
+
// it the SMALLEST solution rather than just any solution.
|
|
352
|
+
if (m != n) {
|
|
353
|
+
std::vector<std::pair<std::string, Value>> outNs;
|
|
354
|
+
std::vector<double> taus;
|
|
355
|
+
const auto answer = [&](Value x, bool sing) {
|
|
356
|
+
outNs.emplace_back("x", std::move(x));
|
|
357
|
+
outNs.emplace_back("det", Value{}); // undefined for a non-square matrix
|
|
358
|
+
outNs.emplace_back("singular", Value{sing});
|
|
359
|
+
return objectOf(std::move(outNs));
|
|
360
|
+
};
|
|
361
|
+
const auto shape = [&](const std::vector<double>& v, size_t rows, size_t cols) {
|
|
362
|
+
if (bWasVector) return numList(v);
|
|
363
|
+
std::vector<Value> rowsOut;
|
|
364
|
+
rowsOut.reserve(rows);
|
|
365
|
+
for (size_t r = 0; r < rows; ++r) {
|
|
366
|
+
rowsOut.push_back(numList(std::vector<double>(v.begin() + static_cast<long>(r * cols),
|
|
367
|
+
v.begin() + static_cast<long>((r + 1) * cols))));
|
|
368
|
+
}
|
|
369
|
+
return listOf(std::move(rowsOut));
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
if (m > n) {
|
|
373
|
+
if (!householderQr(lu, m, n, taus, tol)) return answer(Value{}, true);
|
|
374
|
+
if (!haveB) return answer(Value{}, false);
|
|
375
|
+
applyQtranspose(lu, m, n, taus, rhs, k);
|
|
376
|
+
for (size_t row = n; row-- > 0;) {
|
|
377
|
+
for (size_t c = 0; c < k; ++c) {
|
|
378
|
+
double acc = rhs[row * k + c];
|
|
379
|
+
for (size_t j = row + 1; j < n; ++j) acc -= lu[row * n + j] * rhs[j * k + c];
|
|
380
|
+
rhs[row * k + c] = acc / lu[row * n + row];
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
rhs.resize(n * k);
|
|
384
|
+
return answer(shape(rhs, n, k), false);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// m < n: factor the transpose, which is the tall one.
|
|
388
|
+
std::vector<double> at(n * m);
|
|
389
|
+
for (size_t r = 0; r < m; ++r)
|
|
390
|
+
for (size_t c = 0; c < n; ++c) at[c * m + r] = lu[r * n + c];
|
|
391
|
+
if (!householderQr(at, n, m, taus, tol)) return answer(Value{}, true);
|
|
392
|
+
if (!haveB) return answer(Value{}, false);
|
|
393
|
+
// Forward-solve R^T y = b (R is m x m upper, so R^T is lower).
|
|
394
|
+
for (size_t row = 0; row < m; ++row) {
|
|
395
|
+
for (size_t c = 0; c < k; ++c) {
|
|
396
|
+
double acc = rhs[row * k + c];
|
|
397
|
+
for (size_t j = 0; j < row; ++j) acc -= at[j * m + row] * rhs[j * k + c];
|
|
398
|
+
rhs[row * k + c] = acc / at[row * m + row];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
std::vector<double> z(n * k, 0.0);
|
|
402
|
+
for (size_t r = 0; r < m; ++r)
|
|
403
|
+
for (size_t c = 0; c < k; ++c) z[r * k + c] = rhs[r * k + c];
|
|
404
|
+
applyQ(at, n, m, taus, z, k);
|
|
405
|
+
return answer(shape(z, n, k), false);
|
|
406
|
+
}
|
|
273
407
|
|
|
274
408
|
double det = 1.0;
|
|
275
409
|
bool singular = false;
|
|
@@ -301,7 +435,7 @@ Value builtinMatrixSolve(Evaluator& ev, const Value& aArg, const Value& bArg, bo
|
|
|
301
435
|
std::vector<std::pair<std::string, Value>> out;
|
|
302
436
|
if (singular) {
|
|
303
437
|
// Not a misuse -- "is this matrix singular?" is a legitimate
|
|
304
|
-
// question to ask
|
|
438
|
+
// question to ask linear_solve, so it answers rather than warning.
|
|
305
439
|
out.emplace_back("x", Value{});
|
|
306
440
|
out.emplace_back("det", Value{0.0});
|
|
307
441
|
out.emplace_back("singular", Value{true});
|
|
@@ -616,7 +750,7 @@ bool isBuiltinFunctionName(const std::string& name) {
|
|
|
616
750
|
"chr", "ord", "is_undef", "is_num", "is_bool", "is_string", "is_list", "is_function", "is_object",
|
|
617
751
|
"search", "lookup", "has_key", "version", "version_num", "parent_module",
|
|
618
752
|
"object", "textmetrics", "fontmetrics", "dxf_dim", "dxf_cross", "supported_feature",
|
|
619
|
-
"
|
|
753
|
+
"linear_solve",
|
|
620
754
|
};
|
|
621
755
|
return names.count(name) > 0;
|
|
622
756
|
}
|
|
@@ -758,7 +892,7 @@ enum class BuiltinFnId {
|
|
|
758
892
|
TextMetrics, FontMetrics, Abs, Sign, Ceil, Floor, Round, Sqrt, Ln, Log, Exp, Sin, Cos, Tan,
|
|
759
893
|
Asin, Acos, Atan, Atan2, Max, Min, Pow, Norm, Cross, Rands, Concat, Len, Str, Chr, Ord,
|
|
760
894
|
IsUndef, IsNum, IsBool, IsString, IsList, IsFunction, IsObject, Search, Lookup, HasKey,
|
|
761
|
-
Version, VersionNum, ParentModule, DxfDim, DxfCross, SupportedFeature,
|
|
895
|
+
Version, VersionNum, ParentModule, DxfDim, DxfCross, SupportedFeature, LinearSolve,
|
|
762
896
|
};
|
|
763
897
|
|
|
764
898
|
// supported_feature("name") -> the level at which this build implements that
|
|
@@ -778,7 +912,7 @@ enum class BuiltinFnId {
|
|
|
778
912
|
const std::unordered_map<std::string, double>& featureLevels() {
|
|
779
913
|
static const std::unordered_map<std::string, double> levels = {
|
|
780
914
|
{"render-expr", 1.0}, // render() in expression position
|
|
781
|
-
{"
|
|
915
|
+
{"linear-solve", 1.0}, // linear_solve(A, b) -> {x, det, singular}
|
|
782
916
|
{"polyhedron-vnf", 1.0}, // polyhedron(vnf) / polyhedron(object)
|
|
783
917
|
{"separate-children", 1.0}, // children(..., separate=true)
|
|
784
918
|
{"minkowski-diff", 1.0}, // minkowski_difference()
|
|
@@ -813,7 +947,7 @@ const std::unordered_map<std::string, BuiltinFnId>& builtinFnIds() {
|
|
|
813
947
|
{"parent_module", BuiltinFnId::ParentModule},
|
|
814
948
|
{"dxf_dim", BuiltinFnId::DxfDim}, {"dxf_cross", BuiltinFnId::DxfCross},
|
|
815
949
|
{"supported_feature", BuiltinFnId::SupportedFeature},
|
|
816
|
-
{"
|
|
950
|
+
{"linear_solve", BuiltinFnId::LinearSolve},
|
|
817
951
|
};
|
|
818
952
|
return ids;
|
|
819
953
|
}
|
|
@@ -895,7 +1029,7 @@ const std::unordered_map<int, BuiltinCheck>& builtinChecks() {
|
|
|
895
1029
|
add(BuiltinFnId::ParentModule, {0, 1, "1", {kNum}});
|
|
896
1030
|
add(BuiltinFnId::HasKey, {-1, -1, nullptr, {{TObj, "object"}, {TStr, "string"}}});
|
|
897
1031
|
add(BuiltinFnId::SupportedFeature, {1, 1, "1", {}});
|
|
898
|
-
add(BuiltinFnId::
|
|
1032
|
+
add(BuiltinFnId::LinearSolve, {1, 2, "1 or 2", {kVec}});
|
|
899
1033
|
for (BuiltinFnId id : {BuiltinFnId::IsUndef, BuiltinFnId::IsNum, BuiltinFnId::IsBool,
|
|
900
1034
|
BuiltinFnId::IsString, BuiltinFnId::IsList, BuiltinFnId::IsFunction,
|
|
901
1035
|
BuiltinFnId::IsObject}) {
|
|
@@ -1210,8 +1344,8 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
1210
1344
|
// The OpenSCAD release we track. version_num() is that same
|
|
1211
1345
|
// year/month/day folded as y * 10000 + m * 100 + d, exactly like the
|
|
1212
1346
|
// reference's own builtin_version_num (builtin_functions.cc).
|
|
1213
|
-
case BuiltinFnId::
|
|
1214
|
-
return
|
|
1347
|
+
case BuiltinFnId::LinearSolve:
|
|
1348
|
+
return builtinLinearSolve(ev, getArg(args, 0, "A", Value{}), getArg(args, 1, "b", Value{}),
|
|
1215
1349
|
args.findPositional(1) != nullptr || args.findNamed("b") != nullptr, node);
|
|
1216
1350
|
case BuiltinFnId::SupportedFeature: {
|
|
1217
1351
|
const Value name = getArg(args, 0, "feature", Value{});
|
{openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.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", "linear-solve"};
|
|
39
39
|
|
|
40
40
|
} // namespace
|
|
41
41
|
|
{openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/tests/test_function_builtins.cpp
RENAMED
|
@@ -726,7 +726,7 @@ TEST(StringListBuiltins, ChrIsVariadicAndRangeChecked) {
|
|
|
726
726
|
EXPECT_EQ(asStr(evalSrc("chr(1114112)", ev)), ""); // 0x110000, one past the top
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
-
// --
|
|
729
|
+
// -- linear_solve(A, b) --------------------------------------------------
|
|
730
730
|
//
|
|
731
731
|
// One pivoted LU, three answers: x, det, singular. Oracle is BOSL2 plus
|
|
732
732
|
// arithmetic -- OpenSCAD has no equivalent, so there is no reference
|
|
@@ -737,58 +737,58 @@ TEST(StringListBuiltins, ChrIsVariadicAndRangeChecked) {
|
|
|
737
737
|
// determinant() is a cofactor expansion, growing x10 per step; this is
|
|
738
738
|
// O(n^3).
|
|
739
739
|
|
|
740
|
-
TEST(
|
|
740
|
+
TEST(LinearSolve, SolvesASquareSystem) {
|
|
741
741
|
Evaluator ev;
|
|
742
742
|
// asList() returns a reference INTO v, so v has to outlive it -- taking
|
|
743
743
|
// asList(evalSrc(...)) directly dangles the moment the temporary dies.
|
|
744
|
-
const Value v = evalSrc("
|
|
744
|
+
const Value v = evalSrc("linear_solve([[2,1],[1,3]], [5,10]).x", ev);
|
|
745
745
|
const std::vector<Value>& x = asList(v);
|
|
746
746
|
ASSERT_EQ(x.size(), 2u);
|
|
747
747
|
EXPECT_NEAR(asNum(x[0]), 1.0, 1e-12);
|
|
748
748
|
EXPECT_NEAR(asNum(x[1]), 3.0, 1e-12);
|
|
749
749
|
}
|
|
750
750
|
|
|
751
|
-
TEST(
|
|
751
|
+
TEST(LinearSolve, DeterminantComesFreeFromTheSameFactorisation) {
|
|
752
752
|
Evaluator ev;
|
|
753
|
-
EXPECT_NEAR(asNum(evalSrc("
|
|
754
|
-
EXPECT_NEAR(asNum(evalSrc("
|
|
753
|
+
EXPECT_NEAR(asNum(evalSrc("linear_solve([[2,1],[1,3]]).det", ev)), 5.0, 1e-12);
|
|
754
|
+
EXPECT_NEAR(asNum(evalSrc("linear_solve([[1,0,0],[0,1,0],[0,0,1]]).det", ev)), 1.0, 1e-12);
|
|
755
755
|
// Sign follows the row swaps, not just the magnitude.
|
|
756
|
-
EXPECT_NEAR(asNum(evalSrc("
|
|
756
|
+
EXPECT_NEAR(asNum(evalSrc("linear_solve([[0,1],[1,0]]).det", ev)), -1.0, 1e-12);
|
|
757
757
|
}
|
|
758
758
|
|
|
759
|
-
TEST(
|
|
759
|
+
TEST(LinearSolve, WithNoRightHandSideThereIsNoSolution) {
|
|
760
760
|
Evaluator ev;
|
|
761
|
-
EXPECT_TRUE(isUndef(evalSrc("
|
|
761
|
+
EXPECT_TRUE(isUndef(evalSrc("linear_solve([[2,1],[1,3]]).x", ev)));
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
-
TEST(
|
|
764
|
+
TEST(LinearSolve, ASingularMatrixAnswersRatherThanWarning) {
|
|
765
765
|
// "Is this matrix singular?" is a fair question to ask, so it is
|
|
766
766
|
// reported, not warned about: det 0, singular true, no solution.
|
|
767
767
|
Evaluator ev;
|
|
768
|
-
EXPECT_TRUE(asBool(evalSrc("
|
|
769
|
-
EXPECT_NEAR(asNum(evalSrc("
|
|
770
|
-
EXPECT_TRUE(isUndef(evalSrc("
|
|
768
|
+
EXPECT_TRUE(asBool(evalSrc("linear_solve([[1,2],[2,4]], [1,2]).singular", ev)));
|
|
769
|
+
EXPECT_NEAR(asNum(evalSrc("linear_solve([[1,2],[2,4]], [1,2]).det", ev)), 0.0, 1e-12);
|
|
770
|
+
EXPECT_TRUE(isUndef(evalSrc("linear_solve([[1,2],[2,4]], [1,2]).x", ev)));
|
|
771
771
|
}
|
|
772
772
|
|
|
773
|
-
TEST(
|
|
773
|
+
TEST(LinearSolve, SingularityIsRelativeToTheMatrixNotAbsolute) {
|
|
774
774
|
// 2*I scaled by 1e-10 is perfectly conditioned, and BOSL2's
|
|
775
775
|
// linear_solve calls it singular -- its test is a fixed absolute 1e-9
|
|
776
776
|
// on R's diagonal with no scaling by the size of the matrix. Verified
|
|
777
777
|
// through the CLI: BOSL2 says SINGULAR at 1e-10 and 1e-12 where this
|
|
778
778
|
// solves correctly.
|
|
779
779
|
Evaluator ev;
|
|
780
|
-
EXPECT_FALSE(asBool(evalSrc("
|
|
781
|
-
const Value v = evalSrc("
|
|
780
|
+
EXPECT_FALSE(asBool(evalSrc("linear_solve([[2e-10,0],[0,2e-10]], [1e-10,1e-10]).singular", ev)));
|
|
781
|
+
const Value v = evalSrc("linear_solve([[2e-10,0],[0,2e-10]], [1e-10,1e-10]).x", ev);
|
|
782
782
|
const std::vector<Value>& x = asList(v);
|
|
783
783
|
ASSERT_EQ(x.size(), 2u);
|
|
784
784
|
EXPECT_NEAR(asNum(x[0]), 0.5, 1e-9);
|
|
785
785
|
}
|
|
786
786
|
|
|
787
|
-
TEST(
|
|
787
|
+
TEST(LinearSolve, AMatrixRightHandSideSolvesEveryColumn) {
|
|
788
788
|
Evaluator ev;
|
|
789
789
|
// Solving against the identity is the inverse; [[2,1],[1,3]] has
|
|
790
790
|
// det 5, so the inverse is [[0.6,-0.2],[-0.2,0.4]].
|
|
791
|
-
const Value v = evalSrc("
|
|
791
|
+
const Value v = evalSrc("linear_solve([[2,1],[1,3]], [[1,0],[0,1]]).x", ev);
|
|
792
792
|
const std::vector<Value>& rows = asList(v);
|
|
793
793
|
ASSERT_EQ(rows.size(), 2u);
|
|
794
794
|
const std::vector<Value>& r0 = asList(rows[0]);
|
|
@@ -797,26 +797,85 @@ TEST(MatrixSolve, AMatrixRightHandSideSolvesEveryColumn) {
|
|
|
797
797
|
EXPECT_NEAR(asNum(r0[1]), -0.2, 1e-12);
|
|
798
798
|
}
|
|
799
799
|
|
|
800
|
-
TEST(
|
|
800
|
+
TEST(LinearSolve, RejectsWhatItCannotFactor) {
|
|
801
801
|
Evaluator ev;
|
|
802
|
-
EXPECT_TRUE(isUndef(evalSrc("
|
|
803
|
-
EXPECT_TRUE(isUndef(evalSrc("
|
|
804
|
-
EXPECT_TRUE(isUndef(evalSrc("
|
|
805
|
-
EXPECT_TRUE(isUndef(evalSrc("matrix_solve([[1,2],[3,4,5]], [1,2])", ev))); // ragged
|
|
802
|
+
EXPECT_TRUE(isUndef(evalSrc("linear_solve([[1,2],[3,\"x\"]], [1,2])", ev))); // not numeric
|
|
803
|
+
EXPECT_TRUE(isUndef(evalSrc("linear_solve([[1,2],[3,4]], [1,2,3])", ev))); // wrong rhs length
|
|
804
|
+
EXPECT_TRUE(isUndef(evalSrc("linear_solve([[1,2],[3,4,5]], [1,2])", ev))); // ragged
|
|
806
805
|
}
|
|
807
806
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
807
|
+
// -- non-square: QR ------------------------------------------------------
|
|
808
|
+
//
|
|
809
|
+
// m > n is least squares, m < n is minimum norm -- the same two answers
|
|
810
|
+
// BOSL2's linear_solve gives, and cross-checked against it.
|
|
811
|
+
|
|
812
|
+
TEST(LinearSolve, OverdeterminedGivesTheLeastSquaresFit) {
|
|
813
|
+
// Fit y = a + b*x to (0,1) (1,3) (2,5) (3,7): exactly on the line
|
|
814
|
+
// y = 1 + 2x, so the residual is zero and a=1, b=2.
|
|
815
|
+
Evaluator ev;
|
|
816
|
+
const Value v = evalSrc("linear_solve([[1,0],[1,1],[1,2],[1,3]], [1,3,5,7]).x", ev);
|
|
817
|
+
const std::vector<Value>& x = asList(v);
|
|
818
|
+
ASSERT_EQ(x.size(), 2u);
|
|
819
|
+
EXPECT_NEAR(asNum(x[0]), 1.0, 1e-10);
|
|
820
|
+
EXPECT_NEAR(asNum(x[1]), 2.0, 1e-10);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
TEST(LinearSolve, OverdeterminedWithNoExactSolutionMinimisesTheResidual) {
|
|
824
|
+
// (0,0) (1,1) (2,2) (3,10): no line fits. The normal equations put the
|
|
825
|
+
// least-squares line at y = -1.4 + 3.1x -- computed independently, not
|
|
826
|
+
// read off this implementation.
|
|
827
|
+
Evaluator ev;
|
|
828
|
+
const Value v = evalSrc("linear_solve([[1,0],[1,1],[1,2],[1,3]], [0,1,2,10]).x", ev);
|
|
829
|
+
const std::vector<Value>& x = asList(v);
|
|
830
|
+
ASSERT_EQ(x.size(), 2u);
|
|
831
|
+
EXPECT_NEAR(asNum(x[0]), -1.4, 1e-9);
|
|
832
|
+
EXPECT_NEAR(asNum(x[1]), 3.1, 1e-9);
|
|
813
833
|
}
|
|
814
834
|
|
|
815
|
-
TEST(
|
|
835
|
+
TEST(LinearSolve, UnderdeterminedGivesTheMinimUmNormSolution) {
|
|
836
|
+
// x + y = 2 has infinitely many solutions; the smallest is [1,1].
|
|
837
|
+
// Anything satisfying the equation passes a residual check, so this
|
|
838
|
+
// asserts the NORM -- that is the whole claim of a minimum-norm solve.
|
|
839
|
+
Evaluator ev;
|
|
840
|
+
const Value v = evalSrc("linear_solve([[1,1]], [2]).x", ev);
|
|
841
|
+
const std::vector<Value>& x = asList(v);
|
|
842
|
+
ASSERT_EQ(x.size(), 2u);
|
|
843
|
+
EXPECT_NEAR(asNum(x[0]), 1.0, 1e-12);
|
|
844
|
+
EXPECT_NEAR(asNum(x[1]), 1.0, 1e-12);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
TEST(LinearSolve, UnderdeterminedSatisfiesEveryEquation) {
|
|
848
|
+
// 2 x 3: the plane_intersection shape BOSL2 relies on.
|
|
849
|
+
Evaluator ev;
|
|
850
|
+
const Value v = evalSrc("linear_solve([[1,0,1],[0,1,1]], [3,5]).x", ev);
|
|
851
|
+
const std::vector<Value>& x = asList(v);
|
|
852
|
+
ASSERT_EQ(x.size(), 3u);
|
|
853
|
+
const double a = asNum(x[0]), b = asNum(x[1]), c = asNum(x[2]);
|
|
854
|
+
EXPECT_NEAR(a + c, 3.0, 1e-12);
|
|
855
|
+
EXPECT_NEAR(b + c, 5.0, 1e-12);
|
|
856
|
+
// minimum norm: the component along the null direction [1,1,-1] is zero
|
|
857
|
+
EXPECT_NEAR(a + b - c, 0.0, 1e-9);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
TEST(LinearSolve, NonSquareHasNoDeterminant) {
|
|
861
|
+
Evaluator ev;
|
|
862
|
+
EXPECT_TRUE(isUndef(evalSrc("linear_solve([[1,0],[1,1],[1,2]], [1,2,3]).det", ev)));
|
|
863
|
+
EXPECT_FALSE(asBool(evalSrc("linear_solve([[1,0],[1,1],[1,2]], [1,2,3]).singular", ev)));
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
TEST(LinearSolve, RankDeficientNonSquareIsReportedNotWrong) {
|
|
867
|
+
// Two identical columns: rank 1, not 2.
|
|
868
|
+
Evaluator ev;
|
|
869
|
+
EXPECT_TRUE(asBool(evalSrc("linear_solve([[1,1],[2,2],[3,3]], [1,2,3]).singular", ev)));
|
|
870
|
+
EXPECT_TRUE(isUndef(evalSrc("linear_solve([[1,1],[2,2],[3,3]], [1,2,3]).x", ev)));
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
TEST(LinearSolve, WorksUnderBothEngines) {
|
|
816
875
|
for (bool vm : {false, true}) {
|
|
817
876
|
ScopedVm guard(vm);
|
|
818
877
|
const std::vector<std::string> e =
|
|
819
|
-
echoesOf("r =
|
|
878
|
+
echoesOf("r = linear_solve([[2,1],[1,3]], [5,10]);\necho(r.x, r.det, r.singular);");
|
|
820
879
|
ASSERT_EQ(e.size(), 1u) << "vm=" << vm;
|
|
821
880
|
EXPECT_NE(e[0].find("[1, 3]"), std::string::npos) << "vm=" << vm << ", got " << e[0];
|
|
822
881
|
EXPECT_NE(e[0].find("5"), std::string::npos) << "vm=" << vm << ", got " << e[0];
|
|
File without changes
|
{openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.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.48.0 → openscad_cpp_evaluator-0.49.0}/examples/eval_perf_benchmark.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.0}/examples/manifold_cache_reuse.cpp
RENAMED
|
File without changes
|
{openscad_cpp_evaluator-0.48.0 → openscad_cpp_evaluator-0.49.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
|