openscad-cpp-evaluator 0.13.10__tar.gz → 0.15.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.13.10 → openscad_cpp_evaluator-0.15.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/bindings/module.cpp +9 -3
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/colored_body.hpp +21 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/evaluator.hpp +12 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/booleans.cpp +15 -2
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/builtins.hpp +13 -1
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/extrude.cpp +12 -1
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/function_builtins.cpp +42 -6
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/import.cpp +33 -24
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/primitives_3d.cpp +90 -22
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/topology.cpp +2 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/transforms.cpp +44 -6
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/csg_generate.cpp +23 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_extrude_roof.cpp +21 -5
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_function_builtins.cpp +50 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_import_export.cpp +8 -1
- openscad_cpp_evaluator-0.15.0/tests/test_primitives.cpp +275 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_transforms.cpp +50 -0
- openscad_cpp_evaluator-0.15.0/uv.lock +65 -0
- openscad_cpp_evaluator-0.13.10/tests/test_primitives.cpp +0 -146
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/README.md +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/stb_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/text/stb_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tools/cli/main.cpp +0 -0
|
@@ -129,7 +129,11 @@ const char* roleToString(oscadeval::BodyRole role) {
|
|
|
129
129
|
// tri_verts (nTri, 3), run_original_id (nRun,), run_index (nRun+1,) -- the
|
|
130
130
|
// same flat 3*triangle units manifold3d's own run_index uses).
|
|
131
131
|
nb::dict bodyToDict(oscadeval::ColoredBody& cb) {
|
|
132
|
-
|
|
132
|
+
// A display-only body's Manifold is empty by construction, so its
|
|
133
|
+
// GetMeshGL() would hand back nothing -- take the raw triangle soup
|
|
134
|
+
// that failed to build instead. Everything downstream reads plain
|
|
135
|
+
// arrays, so the two cases are indistinguishable from here on.
|
|
136
|
+
manifold::MeshGL mesh = cb.isDisplayOnly() ? *cb.rawMesh : cb.body->GetMeshGL();
|
|
133
137
|
const size_t numProp = mesh.numProp;
|
|
134
138
|
const size_t numVert = numProp ? mesh.vertProperties.size() / numProp : 0;
|
|
135
139
|
const size_t numTri = mesh.triVerts.size() / 3;
|
|
@@ -184,7 +188,9 @@ void collectIdSpans(const oscadeval::Evaluator& ev, std::vector<IdSpan>& out) {
|
|
|
184
188
|
nb::list bodiesToList(std::vector<oscadeval::ColoredBody>& bodies) {
|
|
185
189
|
nb::list out;
|
|
186
190
|
for (oscadeval::ColoredBody& cb : bodies) {
|
|
187
|
-
|
|
191
|
+
// isDisplayOnly() bodies are ALWAYS IsEmpty() -- that's what made
|
|
192
|
+
// an open polyhedron() silently vanish here rather than render.
|
|
193
|
+
if (!cb.isDisplayOnly() && (!cb.body || cb.body->IsEmpty())) continue;
|
|
188
194
|
out.append(bodyToDict(cb));
|
|
189
195
|
}
|
|
190
196
|
return out;
|
|
@@ -210,7 +216,7 @@ nb::object csgNodeToPy(const oscadeval::CSGNode& node) {
|
|
|
210
216
|
for (const auto& [k, v] : node.params) params[nb::str(k.c_str())] = valueToPy(v);
|
|
211
217
|
nb::list bodies;
|
|
212
218
|
for (const oscadeval::ColoredBody& cb : node.bodies) {
|
|
213
|
-
if (!cb.body || cb.body->IsEmpty()) continue;
|
|
219
|
+
if (!cb.isDisplayOnly() && (!cb.body || cb.body->IsEmpty())) continue;
|
|
214
220
|
// ColoredBody here is const in this walk (tree ownership stays with
|
|
215
221
|
// the caller) -- GetMeshGL() itself is logically read-only, so a
|
|
216
222
|
// const_cast here is safe/local, mirroring bodyToDict's mutating
|
|
@@ -26,6 +26,27 @@ struct ColoredBody {
|
|
|
26
26
|
bool flatPreview = false;
|
|
27
27
|
BodyRole role = BodyRole::Normal;
|
|
28
28
|
std::optional<std::vector<std::array<float, 4>>> triColors; // per-triangle RGBA, multi-color CSG merges only
|
|
29
|
+
|
|
30
|
+
// Set ONLY when a mesh could not be built into a valid Manifold -- in
|
|
31
|
+
// practice a polyhedron() whose faces don't close the surface (it has
|
|
32
|
+
// boundary edges), which Manifold reports by returning an *empty* body
|
|
33
|
+
// with Status() == NotManifold rather than by failing. Note the status
|
|
34
|
+
// name is misleading: a closed mesh with reversed winding, non-manifold
|
|
35
|
+
// vertices or self-intersections builds fine; only an OPEN one lands
|
|
36
|
+
// here.
|
|
37
|
+
//
|
|
38
|
+
// Carrying the raw triangle soup alongside lets the renderer still draw
|
|
39
|
+
// what the script described, which is far more use for spotting the
|
|
40
|
+
// missing face than an empty viewport. `body` is deliberately left set
|
|
41
|
+
// (and empty), so every existing `cb.body->...` path keeps working.
|
|
42
|
+
//
|
|
43
|
+
// Such a body is display-only: it can never take part in a CSG
|
|
44
|
+
// operation, since there is no Manifold for Manifold to operate on.
|
|
45
|
+
// splitByRole() pulls it aside much as it does a `%` background body,
|
|
46
|
+
// and the CSG/hull/minkowski generators re-join it unchanged.
|
|
47
|
+
std::optional<manifold::MeshGL> rawMesh;
|
|
48
|
+
|
|
49
|
+
bool isDisplayOnly() const { return rawMesh.has_value(); }
|
|
29
50
|
};
|
|
30
51
|
|
|
31
52
|
// `EvalContext::color`/CSGParams round-trip: a resolve function reads
|
|
@@ -236,6 +236,18 @@ public:
|
|
|
236
236
|
// comment for why that reference exists at all).
|
|
237
237
|
ColoredBody tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue);
|
|
238
238
|
|
|
239
|
+
// tagGenerated()'s counterpart for a mesh Manifold refused to build --
|
|
240
|
+
// an open polyhedron(). Returns a display-only ColoredBody (see
|
|
241
|
+
// ColoredBody::rawMesh) carrying the triangle soup as-is, so it can
|
|
242
|
+
// still be drawn even though no CSG operation can ever touch it.
|
|
243
|
+
//
|
|
244
|
+
// A failed Manifold has no run IDs of its own, so one is reserved here
|
|
245
|
+
// and written onto the raw mesh -- otherwise the body would be
|
|
246
|
+
// invisible to selection and to the originalID -> AST node mapping
|
|
247
|
+
// that drives click-to-source, which is exactly when a user most wants
|
|
248
|
+
// to click the broken thing and be shown the line that made it.
|
|
249
|
+
ColoredBody tagDisplayOnly(manifold::MeshGL mesh, const oscad::ASTNode& node, const Value& colorValue);
|
|
250
|
+
|
|
239
251
|
// Raises an OpenSCAD-level runtime error: throws EvalError with the
|
|
240
252
|
// formatted "ERROR: {msg}{TRACE lines}" message (see eval_error.hpp),
|
|
241
253
|
// walking the real user function/module call stack for the TRACE lines
|
|
@@ -27,8 +27,19 @@ RoleSplit splitByRole(const std::vector<ColoredBody>& bodies) {
|
|
|
27
27
|
for (const ColoredBody& c : bodies) {
|
|
28
28
|
if (c.role == BodyRole::Background) r.background.push_back(c);
|
|
29
29
|
}
|
|
30
|
+
// Checked before the foreground sweep below and excluded from it: a
|
|
31
|
+
// display-only body has an empty Manifold, so letting it reach a
|
|
32
|
+
// boolean would silently zero the whole operation (the same failure
|
|
33
|
+
// mode invalid operands caused before they were filtered out).
|
|
30
34
|
for (const ColoredBody& c : bodies) {
|
|
31
|
-
if (c.role != BodyRole::Background && c.role != BodyRole::ShowOnly)
|
|
35
|
+
if (c.isDisplayOnly() && c.role != BodyRole::Background && c.role != BodyRole::ShowOnly) {
|
|
36
|
+
r.displayOnly.push_back(c);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const ColoredBody& c : bodies) {
|
|
40
|
+
if (c.role != BodyRole::Background && c.role != BodyRole::ShowOnly && !c.isDisplayOnly()) {
|
|
41
|
+
r.foreground.push_back(c);
|
|
42
|
+
}
|
|
32
43
|
}
|
|
33
44
|
for (const ColoredBody& c : r.foreground) {
|
|
34
45
|
if (c.role == BodyRole::Highlight) r.highlight.push_back(c);
|
|
@@ -160,7 +171,7 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
160
171
|
const std::string& op = std::get<std::string>(params.at("op"));
|
|
161
172
|
const auto& groupSizes = std::get<ListPtr>(params.at("group_sizes"))->items;
|
|
162
173
|
|
|
163
|
-
std::vector<ColoredBody> allBg, allHi, allSo;
|
|
174
|
+
std::vector<ColoredBody> allBg, allHi, allSo, allDo;
|
|
164
175
|
std::optional<ColoredBody> csgResult;
|
|
165
176
|
size_t idx = 0;
|
|
166
177
|
|
|
@@ -173,6 +184,7 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
173
184
|
allBg.insert(allBg.end(), split.background.begin(), split.background.end());
|
|
174
185
|
allHi.insert(allHi.end(), split.highlight.begin(), split.highlight.end());
|
|
175
186
|
allSo.insert(allSo.end(), split.showOnly.begin(), split.showOnly.end());
|
|
187
|
+
allDo.insert(allDo.end(), split.displayOnly.begin(), split.displayOnly.end());
|
|
176
188
|
|
|
177
189
|
std::vector<ColoredBody> bodies3d, sections2d;
|
|
178
190
|
for (const ColoredBody& c : split.foreground) {
|
|
@@ -251,6 +263,7 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params, con
|
|
|
251
263
|
result.insert(result.end(), allBg.begin(), allBg.end());
|
|
252
264
|
result.insert(result.end(), allHi.begin(), allHi.end());
|
|
253
265
|
result.insert(result.end(), allSo.begin(), allSo.end());
|
|
266
|
+
result.insert(result.end(), allDo.begin(), allDo.end());
|
|
254
267
|
return result;
|
|
255
268
|
}
|
|
256
269
|
|
|
@@ -77,11 +77,23 @@ std::vector<ColoredBody> generateCsg(Evaluator& ev, const CSGParams& params,
|
|
|
77
77
|
// splitting/2D-merge logic.
|
|
78
78
|
struct RoleSplit {
|
|
79
79
|
std::vector<ColoredBody> background;
|
|
80
|
-
std::vector<ColoredBody> foreground; // role != Background && role != ShowOnly
|
|
80
|
+
std::vector<ColoredBody> foreground; // role != Background && role != ShowOnly, and not display-only
|
|
81
81
|
std::vector<ColoredBody> highlight; // the subset of `foreground` that's role == Highlight
|
|
82
82
|
std::vector<ColoredBody> showOnly;
|
|
83
|
+
// Bodies with no Manifold to operate on (an open polyhedron -- see
|
|
84
|
+
// ColoredBody::rawMesh). Split out for the same reason showOnly is:
|
|
85
|
+
// they must survive a CSG/hull/minkowski step untouched rather than be
|
|
86
|
+
// merged into it, since there is nothing to merge. Kept separate from
|
|
87
|
+
// `background` because they render as ordinary geometry, not as a
|
|
88
|
+
// translucent ghost.
|
|
89
|
+
std::vector<ColoredBody> displayOnly;
|
|
83
90
|
};
|
|
84
91
|
RoleSplit splitByRole(const std::vector<ColoredBody>& bodies);
|
|
92
|
+
|
|
93
|
+
// manifold::ToString(Manifold::Error) only exists under MANIFOLD_DEBUG --
|
|
94
|
+
// not enabled in this build -- so this mirrors it. Shared by import.cpp and
|
|
95
|
+
// primitives_3d.cpp, both of which report why a mesh wouldn't build.
|
|
96
|
+
std::string manifoldErrorName(manifold::Manifold::Error e);
|
|
85
97
|
std::optional<manifold::CrossSection> toCrossSection(const std::vector<ColoredBody>& bodies);
|
|
86
98
|
|
|
87
99
|
// Merges a statement's bodies into one (3D union, else 2D union, else an
|
|
@@ -181,8 +181,19 @@ std::vector<ColoredBody> generateProjection(Evaluator&, const CSGParams& params,
|
|
|
181
181
|
BuiltinWrapParams computeOffsetParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
|
|
182
182
|
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
// `r` owns position 0 (offset(2) == offset(r=2)); delta/chamfer are
|
|
185
|
+
// named-only, matching the reference. `r` also wins outright when both
|
|
186
|
+
// are given -- the reference never even looks at delta in that case.
|
|
187
|
+
Value rArg = getArg(args, 0, "r", Value{});
|
|
185
188
|
const Value deltaArg = getArg(args, std::nullopt, "delta", Value{});
|
|
189
|
+
// A bare offset() is offset(r=1), not a pass-through: the reference
|
|
190
|
+
// initialises delta to 1 with a round join and only ever overwrites it
|
|
191
|
+
// from an argument, so "neither given" lands on the same state "r=1"
|
|
192
|
+
// does. Verified against OpenSCAD 2022.08.22 -- offset() and offset(r=1)
|
|
193
|
+
// produce a byte-identical mesh for the same input.
|
|
194
|
+
if (std::holds_alternative<std::monostate>(rArg) && std::holds_alternative<std::monostate>(deltaArg)) {
|
|
195
|
+
rArg = Value{1.0};
|
|
196
|
+
}
|
|
186
197
|
const bool chamfer = truthy(getArg(args, std::nullopt, "chamfer", Value{false}));
|
|
187
198
|
|
|
188
199
|
CSGParams params;
|
{openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -279,7 +279,13 @@ Value builtinSearch(const CallArgs& args) {
|
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
Value builtinLookup(const CallArgs& args) {
|
|
282
|
-
|
|
282
|
+
// Guarded here rather than via scalarNumericArity() -- the second
|
|
283
|
+
// argument is a table, so the all-arguments-are-numbers rule can't
|
|
284
|
+
// apply. Without this, toDoubleLenient() turns a non-numeric key into
|
|
285
|
+
// 0.0 and the lookup silently returns the table's first value.
|
|
286
|
+
const Value keyArg = getArg(args, 0, "key", Value{});
|
|
287
|
+
if (!std::holds_alternative<double>(keyArg)) return Value{};
|
|
288
|
+
const double key = std::get<double>(keyArg);
|
|
283
289
|
const Value tableArg = getArg(args, 1, "table", Value{});
|
|
284
290
|
const ListPtr* tablePtr = std::get_if<ListPtr>(&tableArg);
|
|
285
291
|
if (!tablePtr || !*tablePtr) return Value{};
|
|
@@ -359,11 +365,35 @@ Value builtinFontmetrics(Evaluator& ev, const CallArgs& args) {
|
|
|
359
365
|
});
|
|
360
366
|
}
|
|
361
367
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
368
|
+
// Take only scalar numbers, mapped to how many leading positional
|
|
369
|
+
// arguments must each actually BE a number. Anything else there (undef, a
|
|
370
|
+
// string, a list, a bool, an object) is a type error yielding undef, and
|
|
371
|
+
// so is supplying fewer arguments than the arity -- matching the
|
|
372
|
+
// reference, which rejects the call outright rather than coercing.
|
|
373
|
+
//
|
|
374
|
+
// This has to be checked up front rather than left to each case below:
|
|
375
|
+
// toDoubleLenient() silently turns undef/string/list into 0.0, which
|
|
376
|
+
// would make `cos(misspelled_var)` quietly 1 instead of undef, and a
|
|
377
|
+
// typo'd variable then produces plausible-looking geometry instead of an
|
|
378
|
+
// obvious failure. Arity is deliberately a floor, not an exact match --
|
|
379
|
+
// the reference also rejects EXTRA arguments, but tightening that too
|
|
380
|
+
// risks breaking working library code for no correctness gain here.
|
|
381
|
+
const std::unordered_map<std::string, size_t>& scalarNumericArity() {
|
|
382
|
+
static const std::unordered_map<std::string, size_t> arity = {
|
|
383
|
+
{"abs", 1}, {"sign", 1}, {"ceil", 1}, {"floor", 1}, {"round", 1}, {"sqrt", 1},
|
|
384
|
+
{"ln", 1}, {"log", 1}, {"exp", 1}, {"sin", 1}, {"cos", 1}, {"tan", 1},
|
|
385
|
+
{"asin", 1}, {"acos", 1}, {"atan", 1}, {"atan2", 2}, {"pow", 2}, {"rands", 3},
|
|
366
386
|
};
|
|
387
|
+
return arity;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Numeric functions that legitimately take a LIST argument, so the
|
|
391
|
+
// all-arguments-must-be-numbers rule above can't apply -- each validates
|
|
392
|
+
// its own operands, and only needs the bool case caught here (Value's
|
|
393
|
+
// bool and double are distinct alternatives, but the reference rejects
|
|
394
|
+
// `max(true, 1)` as a type error rather than treating true as 1).
|
|
395
|
+
const std::unordered_set<std::string>& numericOnlyNames() {
|
|
396
|
+
static const std::unordered_set<std::string> names = {"max", "min", "norm", "cross"};
|
|
367
397
|
return names;
|
|
368
398
|
}
|
|
369
399
|
|
|
@@ -479,7 +509,13 @@ Value evalBuiltinFunction(Evaluator& ev, const std::string& name, const CallArgs
|
|
|
479
509
|
// elsewhere before reaching here) -- mirrors the old chain's fallthrough.
|
|
480
510
|
if (idIt == ids.end()) return Value{};
|
|
481
511
|
|
|
482
|
-
if (
|
|
512
|
+
if (const auto arityIt = scalarNumericArity().find(name); arityIt != scalarNumericArity().end()) {
|
|
513
|
+
const std::vector<Value> positional = allPositional(args);
|
|
514
|
+
if (positional.size() < arityIt->second) return Value{};
|
|
515
|
+
for (size_t i = 0; i < arityIt->second; ++i) {
|
|
516
|
+
if (!std::holds_alternative<double>(positional[i])) return Value{};
|
|
517
|
+
}
|
|
518
|
+
} else if (numericOnlyNames().count(name)) {
|
|
483
519
|
for (const Value& v : allPositional(args)) {
|
|
484
520
|
if (isBoolOrListContainsBool(v)) return Value{};
|
|
485
521
|
}
|
|
@@ -49,29 +49,6 @@ LoadedMesh loadMeshByExt(const std::string& path, const std::string& ext) {
|
|
|
49
49
|
|
|
50
50
|
bool isMeshExt(const std::string& ext) { return ext == ".stl" || ext == ".obj" || ext == ".off" || ext == ".3mf"; }
|
|
51
51
|
|
|
52
|
-
// manifold::ToString(Manifold::Error) only exists under MANIFOLD_DEBUG --
|
|
53
|
-
// not enabled in this project's build -- so this mirrors it locally for the
|
|
54
|
-
// not-manifold warning message.
|
|
55
|
-
std::string manifoldErrorName(manifold::Manifold::Error e) {
|
|
56
|
-
switch (e) {
|
|
57
|
-
case manifold::Manifold::Error::NoError: return "NoError";
|
|
58
|
-
case manifold::Manifold::Error::NonFiniteVertex: return "NonFiniteVertex";
|
|
59
|
-
case manifold::Manifold::Error::NotManifold: return "NotManifold";
|
|
60
|
-
case manifold::Manifold::Error::VertexOutOfBounds: return "VertexOutOfBounds";
|
|
61
|
-
case manifold::Manifold::Error::PropertiesWrongLength: return "PropertiesWrongLength";
|
|
62
|
-
case manifold::Manifold::Error::MissingPositionProperties: return "MissingPositionProperties";
|
|
63
|
-
case manifold::Manifold::Error::MergeVectorsDifferentLengths: return "MergeVectorsDifferentLengths";
|
|
64
|
-
case manifold::Manifold::Error::MergeIndexOutOfBounds: return "MergeIndexOutOfBounds";
|
|
65
|
-
case manifold::Manifold::Error::TransformWrongLength: return "TransformWrongLength";
|
|
66
|
-
case manifold::Manifold::Error::RunIndexWrongLength: return "RunIndexWrongLength";
|
|
67
|
-
case manifold::Manifold::Error::FaceIDWrongLength: return "FaceIDWrongLength";
|
|
68
|
-
case manifold::Manifold::Error::InvalidConstruction: return "InvalidConstruction";
|
|
69
|
-
case manifold::Manifold::Error::ResultTooLarge: return "ResultTooLarge";
|
|
70
|
-
case manifold::Manifold::Error::InvalidTangents: return "InvalidTangents";
|
|
71
|
-
case manifold::Manifold::Error::Cancelled: return "Cancelled";
|
|
72
|
-
}
|
|
73
|
-
return "Unknown";
|
|
74
|
-
}
|
|
75
52
|
|
|
76
53
|
Value jsonToValue(const nlohmann::ordered_json& j) {
|
|
77
54
|
if (j.is_boolean()) return Value{j.get<bool>()};
|
|
@@ -135,6 +112,30 @@ Value meshToVnf(const LoadedMesh& mesh) {
|
|
|
135
112
|
|
|
136
113
|
} // namespace
|
|
137
114
|
|
|
115
|
+
// manifold::ToString(Manifold::Error) only exists under MANIFOLD_DEBUG --
|
|
116
|
+
// not enabled in this project's build -- so this mirrors it locally for the
|
|
117
|
+
// not-manifold warning message.
|
|
118
|
+
std::string manifoldErrorName(manifold::Manifold::Error e) {
|
|
119
|
+
switch (e) {
|
|
120
|
+
case manifold::Manifold::Error::NoError: return "NoError";
|
|
121
|
+
case manifold::Manifold::Error::NonFiniteVertex: return "NonFiniteVertex";
|
|
122
|
+
case manifold::Manifold::Error::NotManifold: return "NotManifold";
|
|
123
|
+
case manifold::Manifold::Error::VertexOutOfBounds: return "VertexOutOfBounds";
|
|
124
|
+
case manifold::Manifold::Error::PropertiesWrongLength: return "PropertiesWrongLength";
|
|
125
|
+
case manifold::Manifold::Error::MissingPositionProperties: return "MissingPositionProperties";
|
|
126
|
+
case manifold::Manifold::Error::MergeVectorsDifferentLengths: return "MergeVectorsDifferentLengths";
|
|
127
|
+
case manifold::Manifold::Error::MergeIndexOutOfBounds: return "MergeIndexOutOfBounds";
|
|
128
|
+
case manifold::Manifold::Error::TransformWrongLength: return "TransformWrongLength";
|
|
129
|
+
case manifold::Manifold::Error::RunIndexWrongLength: return "RunIndexWrongLength";
|
|
130
|
+
case manifold::Manifold::Error::FaceIDWrongLength: return "FaceIDWrongLength";
|
|
131
|
+
case manifold::Manifold::Error::InvalidConstruction: return "InvalidConstruction";
|
|
132
|
+
case manifold::Manifold::Error::ResultTooLarge: return "ResultTooLarge";
|
|
133
|
+
case manifold::Manifold::Error::InvalidTangents: return "InvalidTangents";
|
|
134
|
+
case manifold::Manifold::Error::Cancelled: return "Cancelled";
|
|
135
|
+
}
|
|
136
|
+
return "Unknown";
|
|
137
|
+
}
|
|
138
|
+
|
|
138
139
|
namespace {
|
|
139
140
|
|
|
140
141
|
Value contoursToValue(const std::vector<Contour2d>& contours) {
|
|
@@ -269,7 +270,15 @@ std::vector<ColoredBody> generateImport(Evaluator& ev, const CSGParams& params,
|
|
|
269
270
|
|
|
270
271
|
manifold::Manifold body(mesh);
|
|
271
272
|
if (body.Status() != manifold::Manifold::Error::NoError) {
|
|
272
|
-
|
|
273
|
+
// Same treatment as an open polyhedron() (see generatePolyhedron):
|
|
274
|
+
// keep the triangles so the file can still be LOOKED at. Previously
|
|
275
|
+
// this warned and then handed back an empty body, which was dropped
|
|
276
|
+
// downstream -- so a broken STL warned once and showed nothing,
|
|
277
|
+
// giving no way to see what was actually wrong with it.
|
|
278
|
+
ev.warn("import: mesh is not a closed solid (" + manifoldErrorName(body.Status()) +
|
|
279
|
+
"); drawing it as a surface, but it cannot take part in any CSG operation",
|
|
280
|
+
&node.position());
|
|
281
|
+
return {ev.tagDisplayOnly(std::move(mesh), node, params.at("color"))};
|
|
273
282
|
}
|
|
274
283
|
return {ev.tagGenerated(std::move(body), node, params.at("color"))};
|
|
275
284
|
}
|
{openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/primitives_3d.cpp
RENAMED
|
@@ -14,6 +14,45 @@ namespace oscadeval {
|
|
|
14
14
|
|
|
15
15
|
namespace {
|
|
16
16
|
bool isUndef(const Value& v) { return std::holds_alternative<std::monostate>(v); }
|
|
17
|
+
|
|
18
|
+
// Edges used by an odd number of triangles -- i.e. the mesh's open border.
|
|
19
|
+
// A closed solid has none; every edge is shared by exactly two faces.
|
|
20
|
+
//
|
|
21
|
+
// Counted on the UNDIRECTED edge (sorted endpoints) rather than the
|
|
22
|
+
// directed half-edge, so a hole reads as a hole regardless of winding:
|
|
23
|
+
// counting directed edges would also flag a merely back-to-front face,
|
|
24
|
+
// which builds into a perfectly valid solid and is not what this reports.
|
|
25
|
+
// Whether a mesh Manifold rejected is nonetheless safe to hand to a
|
|
26
|
+
// renderer. Only NotManifold qualifies: it means "the triangles are fine,
|
|
27
|
+
// they just don't close a solid", which is exactly the open-surface case
|
|
28
|
+
// worth drawing. Every other status (NonFiniteVertex above all, but also
|
|
29
|
+
// VertexOutOfBounds and friends) means the vertex data itself is broken --
|
|
30
|
+
// drawing NaN coordinates would poison the scene bounding box and send the
|
|
31
|
+
// camera auto-fit to infinity, which is worse than showing nothing. The
|
|
32
|
+
// explicit finite check is belt-and-braces, since nothing documents that a
|
|
33
|
+
// NotManifold mesh can't ALSO contain a NaN.
|
|
34
|
+
bool isDrawableFailure(const manifold::Manifold& body, const manifold::MeshGL& mesh) {
|
|
35
|
+
if (body.Status() != manifold::Manifold::Error::NotManifold) return false;
|
|
36
|
+
if (mesh.triVerts.empty()) return false;
|
|
37
|
+
return std::all_of(mesh.vertProperties.begin(), mesh.vertProperties.end(),
|
|
38
|
+
[](float v) { return std::isfinite(v); });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
size_t countBoundaryEdges(const manifold::MeshGL& mesh) {
|
|
42
|
+
std::map<std::pair<uint32_t, uint32_t>, int> uses;
|
|
43
|
+
for (size_t i = 0; i + 2 < mesh.triVerts.size(); i += 3) {
|
|
44
|
+
const uint32_t v[3] = {mesh.triVerts[i], mesh.triVerts[i + 1], mesh.triVerts[i + 2]};
|
|
45
|
+
for (int e = 0; e < 3; ++e) {
|
|
46
|
+
const uint32_t a = v[e], b = v[(e + 1) % 3];
|
|
47
|
+
++uses[{std::min(a, b), std::max(a, b)}];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
size_t open = 0;
|
|
51
|
+
for (const auto& [edge, count] : uses) {
|
|
52
|
+
if (count % 2 != 0) ++open;
|
|
53
|
+
}
|
|
54
|
+
return open;
|
|
55
|
+
}
|
|
17
56
|
} // namespace
|
|
18
57
|
|
|
19
58
|
// cube(size = 1, center = false) -- mirrors _resolve_cube/_generate_cube.
|
|
@@ -144,34 +183,38 @@ std::vector<ColoredBody> generateSphere(Evaluator& ev, const CSGParams& params,
|
|
|
144
183
|
CSGParams resolveCylinder(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
|
|
145
184
|
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
146
185
|
const double h = toDoubleLenient(getArg(args, 0, "h", Value{1.0}));
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
186
|
+
// Positional order is (h, r1, r2, center); r/d/d1/d2 are named-only.
|
|
187
|
+
// NOTE r1/r2 -- not r -- own positions 1 and 2, so `cylinder(10, 5, 2)`
|
|
188
|
+
// is a cone, and `cylinder(10, 5)` is a cone tapering to the r2 default
|
|
189
|
+
// of 1 rather than a straight r=5 cylinder. Both match the reference.
|
|
190
|
+
Value rV = getArg(args, std::nullopt, "r", Value{});
|
|
150
191
|
Value dV = getArg(args, std::nullopt, "d", Value{});
|
|
192
|
+
Value r1V = getArg(args, 1, "r1", Value{});
|
|
193
|
+
Value r2V = getArg(args, 2, "r2", Value{});
|
|
151
194
|
Value d1V = getArg(args, std::nullopt, "d1", Value{});
|
|
152
195
|
Value d2V = getArg(args, std::nullopt, "d2", Value{});
|
|
153
|
-
const bool center = truthy(getArg(args,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (!
|
|
167
|
-
if (!
|
|
168
|
-
|
|
169
|
-
const int segs = fnSegmentsFromCtx(effCtx, std::max(
|
|
196
|
+
const bool center = truthy(getArg(args, 3, "center", Value{false}));
|
|
197
|
+
|
|
198
|
+
// Application order is significant and mirrors the reference's own
|
|
199
|
+
// sequence of independent `if (x.isNumber()) ...` assignments: r, then
|
|
200
|
+
// d (which overrides r outright rather than deferring to it), then the
|
|
201
|
+
// per-end r1/r2, then the per-end d1/d2. So `cylinder(h=10, d=8, r1=5)`
|
|
202
|
+
// is r1=5/r2=4, and `cylinder(h=10, r=5, r2=2)` is r1=5/r2=2. Both ends
|
|
203
|
+
// default to 1 independently -- r2 does NOT fall back to r1.
|
|
204
|
+
double r1 = 1.0, r2 = 1.0;
|
|
205
|
+
if (!isUndef(rV)) r1 = r2 = toDoubleLenient(rV);
|
|
206
|
+
if (!isUndef(dV)) r1 = r2 = toDoubleLenient(dV) / 2.0;
|
|
207
|
+
if (!isUndef(r1V)) r1 = toDoubleLenient(r1V);
|
|
208
|
+
if (!isUndef(r2V)) r2 = toDoubleLenient(r2V);
|
|
209
|
+
if (!isUndef(d1V)) r1 = toDoubleLenient(d1V) / 2.0;
|
|
210
|
+
if (!isUndef(d2V)) r2 = toDoubleLenient(d2V) / 2.0;
|
|
211
|
+
|
|
212
|
+
const int segs = fnSegmentsFromCtx(effCtx, std::max(r1, r2));
|
|
170
213
|
|
|
171
214
|
CSGParams params;
|
|
172
215
|
params["h"] = Value{h};
|
|
173
|
-
params["r1"] = Value{
|
|
174
|
-
params["r2"] = Value{
|
|
216
|
+
params["r1"] = Value{r1};
|
|
217
|
+
params["r2"] = Value{r2};
|
|
175
218
|
params["center"] = Value{center};
|
|
176
219
|
params["segs"] = Value{static_cast<double>(segs)};
|
|
177
220
|
params["color"] = colorToValue(effCtx.color);
|
|
@@ -288,6 +331,31 @@ std::vector<ColoredBody> generatePolyhedron(Evaluator& ev, const CSGParams& para
|
|
|
288
331
|
mesh.triVerts.push_back(static_cast<uint32_t>(std::get<double>(t)));
|
|
289
332
|
}
|
|
290
333
|
manifold::Manifold body(mesh);
|
|
334
|
+
if (isDrawableFailure(body, mesh)) {
|
|
335
|
+
// An open surface -- faces that don't close the solid. Manifold
|
|
336
|
+
// signals that by returning an EMPTY body rather than throwing, so
|
|
337
|
+
// without this branch the polyhedron simply vanishes from the
|
|
338
|
+
// render with nothing said, leaving the author to guess which face
|
|
339
|
+
// they forgot.
|
|
340
|
+
//
|
|
341
|
+
// Report the boundary-edge count rather than Manifold's own
|
|
342
|
+
// "NotManifold" status string, which is actively misleading here: a
|
|
343
|
+
// closed mesh with reversed winding, non-manifold vertices or
|
|
344
|
+
// self-intersections builds perfectly well, and only an OPEN one
|
|
345
|
+
// reaches this branch. The count points straight at the problem.
|
|
346
|
+
ev.warn("polyhedron: mesh is not closed -- " + std::to_string(countBoundaryEdges(mesh)) +
|
|
347
|
+
" boundary edge(s); drawing it as a surface, but it cannot "
|
|
348
|
+
"take part in any CSG operation",
|
|
349
|
+
&node.position());
|
|
350
|
+
return {ev.tagDisplayOnly(std::move(mesh), node, params.at("color"))};
|
|
351
|
+
}
|
|
352
|
+
if (body.Status() != manifold::Manifold::Error::NoError) {
|
|
353
|
+
// Broken vertex data rather than merely-open topology (a NaN
|
|
354
|
+
// coordinate, an out-of-range index). Not drawable, so this keeps
|
|
355
|
+
// the old drop-it behaviour -- but says so, which it never used to.
|
|
356
|
+
ev.warn(std::string("polyhedron: ") + manifoldErrorName(body.Status()) + "; geometry discarded",
|
|
357
|
+
&node.position());
|
|
358
|
+
}
|
|
291
359
|
return {ev.tagGenerated(std::move(body), node, params.at("color"))};
|
|
292
360
|
}
|
|
293
361
|
|
|
@@ -61,6 +61,7 @@ std::vector<ColoredBody> generateHull(Evaluator&, const CSGParams&, const std::v
|
|
|
61
61
|
result.insert(result.end(), split.background.begin(), split.background.end());
|
|
62
62
|
result.insert(result.end(), split.highlight.begin(), split.highlight.end());
|
|
63
63
|
result.insert(result.end(), split.showOnly.begin(), split.showOnly.end());
|
|
64
|
+
result.insert(result.end(), split.displayOnly.begin(), split.displayOnly.end());
|
|
64
65
|
return result;
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -89,6 +90,7 @@ std::vector<ColoredBody> generateMinkowski(Evaluator& ev, const CSGParams&, cons
|
|
|
89
90
|
passthrough.insert(passthrough.end(), split.background.begin(), split.background.end());
|
|
90
91
|
passthrough.insert(passthrough.end(), split.highlight.begin(), split.highlight.end());
|
|
91
92
|
passthrough.insert(passthrough.end(), split.showOnly.begin(), split.showOnly.end());
|
|
93
|
+
passthrough.insert(passthrough.end(), split.displayOnly.begin(), split.displayOnly.end());
|
|
92
94
|
|
|
93
95
|
if (bodies3d.empty()) return passthrough;
|
|
94
96
|
if (bodies3d.size() == 1) {
|
{openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/src/builtins/transforms.cpp
RENAMED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include "openscad_cpp_evaluator/call_args.hpp"
|
|
4
4
|
#include "openscad_cpp_evaluator/evaluator.hpp"
|
|
5
5
|
|
|
6
|
+
#include <array>
|
|
6
7
|
#include <cmath>
|
|
7
8
|
#include <numbers>
|
|
8
9
|
|
|
@@ -32,6 +33,21 @@ manifold::vec2 toVec2(const Value& v, double defaultX = 0.0, double defaultY = 0
|
|
|
32
33
|
return manifold::vec2(defaultX, defaultY);
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
// resize()'s `auto` argument: a bare bool applies to all three axes, a
|
|
37
|
+
// list applies element-wise (missing elements are false), anything else
|
|
38
|
+
// (including undef) disables auto entirely. Mirrors the reference's
|
|
39
|
+
// Value::toBool()/vector handling.
|
|
40
|
+
std::array<bool, 3> toAutoAxes(const Value& v) {
|
|
41
|
+
if (const bool* b = std::get_if<bool>(&v)) return {*b, *b, *b};
|
|
42
|
+
if (const ListPtr* l = std::get_if<ListPtr>(&v); l && *l) {
|
|
43
|
+
const auto& items = (*l)->items;
|
|
44
|
+
std::array<bool, 3> out{false, false, false};
|
|
45
|
+
for (size_t i = 0; i < 3 && i < items.size(); ++i) out[i] = truthy(items[i]);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
return {false, false, false};
|
|
49
|
+
}
|
|
50
|
+
|
|
35
51
|
// Rodrigues' rotation formula as a 3x4 transform (rotate(angle, axis) --
|
|
36
52
|
// the axis form, as opposed to rotate([x,y,z]) Euler angles, which uses
|
|
37
53
|
// Manifold's own Rotate() directly and never needs this). Mirrors
|
|
@@ -117,13 +133,35 @@ manifold::Manifold applyTransform3d(const std::string& name, const CallArgs& arg
|
|
|
117
133
|
return body.Mirror(toVec3(getArg(args, 0, "v", Value{}), 1.0, 0.0, 0.0));
|
|
118
134
|
}
|
|
119
135
|
if (name == "resize") {
|
|
120
|
-
const manifold::vec3
|
|
136
|
+
const manifold::vec3 newSizeV = toVec3(getArg(args, 0, "newsize", Value{}));
|
|
137
|
+
std::array<bool, 3> autoAxes = toAutoAxes(getArg(args, 1, "auto", Value{}));
|
|
121
138
|
const manifold::Box bbox = body.BoundingBox();
|
|
122
|
-
const manifold::vec3
|
|
123
|
-
const double
|
|
124
|
-
const double
|
|
125
|
-
|
|
126
|
-
|
|
139
|
+
const manifold::vec3 spanV = bbox.max - bbox.min;
|
|
140
|
+
const double ns[3] = {newSizeV.x, newSizeV.y, newSizeV.z};
|
|
141
|
+
const double span[3] = {spanV.x, spanV.y, spanV.z};
|
|
142
|
+
|
|
143
|
+
// An `auto` axis takes the scale factor of whichever axis asked for
|
|
144
|
+
// the LARGEST new size -- not its own (it has none) and not the
|
|
145
|
+
// first specified one. Mirrors the reference's newsizemax_index:
|
|
146
|
+
// ties keep the earlier axis, and an all-zero newsize leaves the
|
|
147
|
+
// factor at 1 so `resize([0,0,0], true)` is a no-op.
|
|
148
|
+
int maxIdx = 0;
|
|
149
|
+
for (int i = 1; i < 3; ++i) {
|
|
150
|
+
if (ns[i] > ns[maxIdx]) maxIdx = i;
|
|
151
|
+
}
|
|
152
|
+
const double autoScale = (ns[maxIdx] != 0.0 && span[maxIdx] != 0.0) ? ns[maxIdx] / span[maxIdx] : 1.0;
|
|
153
|
+
|
|
154
|
+
double scale[3];
|
|
155
|
+
for (int i = 0; i < 3; ++i) {
|
|
156
|
+
if (ns[i] != 0.0 && span[i] != 0.0) {
|
|
157
|
+
scale[i] = ns[i] / span[i];
|
|
158
|
+
} else if (ns[i] == 0.0 && autoAxes[static_cast<size_t>(i)]) {
|
|
159
|
+
scale[i] = autoScale;
|
|
160
|
+
} else {
|
|
161
|
+
scale[i] = 1.0;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return body.Scale(manifold::vec3(scale[0], scale[1], scale[2]));
|
|
127
165
|
}
|
|
128
166
|
if (name == "multmatrix") {
|
|
129
167
|
Value m = getArg(args, 0, "m", Value{});
|
|
@@ -110,4 +110,27 @@ ColoredBody Evaluator::tagGenerated(manifold::Manifold body, const oscad::ASTNod
|
|
|
110
110
|
return cb;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
ColoredBody Evaluator::tagDisplayOnly(manifold::MeshGL mesh, const oscad::ASTNode& node, const Value& colorValue) {
|
|
114
|
+
const std::optional<std::array<float, 4>> color = valueToColor(colorValue);
|
|
115
|
+
|
|
116
|
+
// A Manifold that failed to build carries no run IDs, so reserve one
|
|
117
|
+
// and describe the whole soup as a single run. ReserveIDs() draws from
|
|
118
|
+
// the same global counter Manifold hands to real bodies, so an ID
|
|
119
|
+
// minted here can never collide with one of theirs.
|
|
120
|
+
const uint32_t originalId = manifold::Manifold::ReserveIDs(1);
|
|
121
|
+
mesh.runOriginalID = {originalId};
|
|
122
|
+
mesh.runIndex = {0, static_cast<uint32_t>(mesh.triVerts.size())};
|
|
123
|
+
idToNode[originalId] = &node;
|
|
124
|
+
idToColor[originalId] = color;
|
|
125
|
+
|
|
126
|
+
ColoredBody cb;
|
|
127
|
+
// Left set-but-empty rather than nullopt: consumers dereference
|
|
128
|
+
// cb.body unconditionally, and an empty Manifold is also the honest
|
|
129
|
+
// answer to "what solid is this?" -- there isn't one.
|
|
130
|
+
cb.body = manifold::Manifold();
|
|
131
|
+
cb.color = color;
|
|
132
|
+
cb.rawMesh = std::move(mesh);
|
|
133
|
+
return cb;
|
|
134
|
+
}
|
|
135
|
+
|
|
113
136
|
} // namespace oscadeval
|
{openscad_cpp_evaluator-0.13.10 → openscad_cpp_evaluator-0.15.0}/tests/test_extrude_roof.cpp
RENAMED
|
@@ -151,11 +151,27 @@ TEST(Offset, NegativeDeltaShrinksTheSquare) {
|
|
|
151
151
|
EXPECT_NEAR(e.bodies[0].section->Area(), 2.0 * 2.0, 1e-6);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
// `r` owns position 0, so offset(2) == offset(r=2). It used to be
|
|
155
|
+
// named-only, making a bare offset(2) a silent no-op.
|
|
156
|
+
TEST(Offset, FirstPositionalArgumentIsRadius) {
|
|
157
|
+
Evaluated positional = evalSrc("offset(1, $fn=64) square(4, center=true);");
|
|
158
|
+
Evaluated named = evalSrc("offset(r=1, $fn=64) square(4, center=true);");
|
|
159
|
+
ASSERT_TRUE(positional.bodies[0].section.has_value());
|
|
160
|
+
EXPECT_NEAR(positional.bodies[0].section->Area(), named.bodies[0].section->Area(), 1e-9);
|
|
161
|
+
EXPECT_GT(positional.bodies[0].section->Area(), 16.0);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// A bare offset() is offset(r=1), NOT a pass-through -- the reference
|
|
165
|
+
// initialises its delta to 1 with a round join and only overwrites it from
|
|
166
|
+
// an argument. Verified against OpenSCAD 2022.08.22, where offset() and
|
|
167
|
+
// offset(r=1) produce identical geometry.
|
|
168
|
+
TEST(Offset, NoRadiusOrDeltaDefaultsToRadiusOne) {
|
|
169
|
+
Evaluated bare = evalSrc("offset() square(4, center=true);");
|
|
170
|
+
Evaluated explicitR = evalSrc("offset(r=1) square(4, center=true);");
|
|
171
|
+
ASSERT_EQ(bare.bodies.size(), 1u);
|
|
172
|
+
ASSERT_TRUE(bare.bodies[0].section.has_value());
|
|
173
|
+
EXPECT_NEAR(bare.bodies[0].section->Area(), explicitR.bodies[0].section->Area(), 1e-9);
|
|
174
|
+
EXPECT_GT(bare.bodies[0].section->Area(), 16.0); // grew, rather than passing through
|
|
159
175
|
}
|
|
160
176
|
|
|
161
177
|
// -- roof -----------------------------------------------------------------
|