openscad-cpp-evaluator 1.47.0__tar.gz → 1.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-1.47.0 → openscad_cpp_evaluator-1.49.0}/CLAUDE.md +6 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/function_builtins.cpp +1 -1
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/topology.cpp +171 -131
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_booleans.cpp +101 -10
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/uv.lock +1 -1
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/cmake/patch_manifold.cmake +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/LICENSE +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/.DS_Store +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/import/scene_import.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/xkcd_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/xkcd_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_discretizer.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_openscad_parity.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_undef_args.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/tools/cli/main.cpp +0 -0
|
@@ -606,6 +606,12 @@ grep for `ponytail:`.
|
|
|
606
606
|
field squashed to unit spacing and `Scale`s the result back -- exact, since with tolerance -1 a
|
|
607
607
|
vertex is a linear interpolation along a lattice edge, which commutes with an axis scale. A plain
|
|
608
608
|
number takes the old path unchanged. `supported_feature("levelset")` is **2** from this change.
|
|
609
|
+
**A field array needs `bounds` OR `edge`**: given only `edge` (number or vector), the samples sit
|
|
610
|
+
`edge` apart **centred on the origin**, as BOSL2's `isosurface()` places an array given
|
|
611
|
+
`voxel_size`, and the array's depth (`field[i][j]` vs `field[i][j][k]`) decides 2D or 3D
|
|
612
|
+
(`boundsFromEdge`, #749 on BelfrySCAD). Both given: `edge` re-samples the field inside `bounds`
|
|
613
|
+
(trilinear in 3D, bilinear in 2D -- 2D used to ignore `edge` here) unless it equals the grid's own spacing. A function field still needs `bounds`.
|
|
614
|
+
`supported_feature("levelset")` is **3** from this change.
|
|
609
615
|
**The field is a grid OR a `function(x,y,z)`, and the two trade off against each other** —
|
|
610
616
|
neither is simply better:
|
|
611
617
|
|
{openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.49.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -987,7 +987,7 @@ const std::unordered_map<std::string, double>& featureLevels() {
|
|
|
987
987
|
static const std::unordered_map<std::string, double> levels = {
|
|
988
988
|
{"render-expr", 1.0}, // render() in expression position
|
|
989
989
|
{"linear-solve", 1.0}, // linear_solve(A, b) -> {x, det, singular}
|
|
990
|
-
{"levelset",
|
|
990
|
+
{"levelset", 3.0}, // levelset(field, bounds, isovalue); 2: edge may be a vector; 3: edge alone places a field array
|
|
991
991
|
{"polyhedron-vnf", 1.0}, // polyhedron(vnf) / polyhedron(object)
|
|
992
992
|
{"separate-children", 1.0}, // children(..., separate=true)
|
|
993
993
|
{"minkowski-diff", 1.0}, // minkowski_difference()
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <manifold/polygon.h>
|
|
8
8
|
|
|
9
9
|
#include <algorithm>
|
|
10
|
+
#include <utility>
|
|
10
11
|
|
|
11
12
|
namespace oscadeval {
|
|
12
13
|
|
|
@@ -650,27 +651,22 @@ std::vector<ColoredBody> generateMeshRepair(Evaluator& ev, const CSGParams& para
|
|
|
650
651
|
|
|
651
652
|
// -- levelset(field, bounds, isovalue) -------------------------------------
|
|
652
653
|
//
|
|
653
|
-
// A solid from a grid of sampled values
|
|
654
|
-
//
|
|
654
|
+
// A solid from a grid of sampled values: the field array the script handed
|
|
655
|
+
// us, or a function sampled onto a grid at edge= spacing. The grid is then
|
|
656
|
+
// marched directly, as BOSL2's isosurface() does -- see marchingCubes() and
|
|
657
|
+
// the end of generateLevelSet for why Manifold::LevelSet is no longer used.
|
|
655
658
|
//
|
|
656
|
-
// Why
|
|
657
|
-
//
|
|
658
|
-
// 0.
|
|
659
|
-
//
|
|
660
|
-
// means the C++ side never re-enters the evaluator, so canParallel can be
|
|
661
|
-
// TRUE -- Manifold's docs warn that parallel policies "will crash language
|
|
662
|
-
// runtimes with runtime locks that expect to not be called back by
|
|
663
|
-
// unregistered threads", which is exactly what a callback design forfeits.
|
|
659
|
+
// Why offer an array at all, when a function is the obvious mapping:
|
|
660
|
+
// measured, building the field in OpenSCAD costs 0.41 us/sample against
|
|
661
|
+
// 0.96 us for a closure call per sample, because the arithmetic inlines into
|
|
662
|
+
// the list comprehension.
|
|
664
663
|
//
|
|
665
|
-
// The trade is memory: ~67 bytes per cell, so 200^3 is about
|
|
666
|
-
//
|
|
664
|
+
// The trade is memory: ~67 bytes per cell for the array, so 200^3 is about
|
|
665
|
+
// 550 MB and that is the practical ceiling.
|
|
667
666
|
//
|
|
668
|
-
// Accuracy is bounded by the GRID
|
|
669
|
-
//
|
|
670
|
-
//
|
|
671
|
-
// spacing resamples it through LevelSet instead, which samples a
|
|
672
|
-
// body-centred cubic lattice (sdf.cpp:440-446) that a plain cubic grid does
|
|
673
|
-
// not line up with, so the lambda interpolates trilinearly between samples.
|
|
667
|
+
// Accuracy is bounded by the GRID. An array at its own spacing (the default)
|
|
668
|
+
// is marched as is; an edge= asking for a different spacing re-samples it
|
|
669
|
+
// trilinearly onto a grid at that spacing first.
|
|
674
670
|
|
|
675
671
|
namespace {
|
|
676
672
|
|
|
@@ -689,6 +685,44 @@ std::optional<double> fieldSample(const Value& item) {
|
|
|
689
685
|
return std::clamp(*d, -kHuge, kHuge);
|
|
690
686
|
}
|
|
691
687
|
|
|
688
|
+
// bounds for a field ARRAY given edge= and no bounds (#749 on BelfrySCAD):
|
|
689
|
+
// the samples edge apart, centred on the origin, as BOSL2's isosurface()
|
|
690
|
+
// places an array given voxel_size. The array's depth (field[i][j] or
|
|
691
|
+
// field[i][j][k]) says 2D or 3D; the sizes come from its first row, and
|
|
692
|
+
// readField/readPlane check the rest later. nullopt when edge is missing
|
|
693
|
+
// or does not fit, leaving the caller's "bounds must be ..." warning.
|
|
694
|
+
std::optional<std::pair<std::vector<double>, std::vector<double>>>
|
|
695
|
+
boundsFromEdge(const Value& field, const Value& edge) {
|
|
696
|
+
std::vector<size_t> dims;
|
|
697
|
+
for (const Value* cur = &field; dims.size() < 3;) {
|
|
698
|
+
const ListPtr* l = std::get_if<ListPtr>(cur);
|
|
699
|
+
if (!l || !*l || (*l)->items.empty()) break;
|
|
700
|
+
dims.push_back((*l)->items.size());
|
|
701
|
+
cur = &(*l)->items[0];
|
|
702
|
+
}
|
|
703
|
+
if (dims.size() < 2) return std::nullopt;
|
|
704
|
+
std::vector<double> step;
|
|
705
|
+
if (const double* e = std::get_if<double>(&edge)) {
|
|
706
|
+
step.assign(dims.size(), *e);
|
|
707
|
+
} else if (const ListPtr* l = std::get_if<ListPtr>(&edge); l && *l && (*l)->items.size() == dims.size()) {
|
|
708
|
+
for (const Value& item : (*l)->items) {
|
|
709
|
+
const double* d = std::get_if<double>(&item);
|
|
710
|
+
if (!d) return std::nullopt;
|
|
711
|
+
step.push_back(*d);
|
|
712
|
+
}
|
|
713
|
+
} else {
|
|
714
|
+
return std::nullopt;
|
|
715
|
+
}
|
|
716
|
+
std::vector<double> lo, hi;
|
|
717
|
+
for (size_t a = 0; a < dims.size(); ++a) {
|
|
718
|
+
if (!std::isfinite(step[a]) || step[a] <= 0.0 || dims[a] < 2) return std::nullopt;
|
|
719
|
+
const double half = 0.5 * step[a] * static_cast<double>(dims[a] - 1);
|
|
720
|
+
lo.push_back(-half);
|
|
721
|
+
hi.push_back(half);
|
|
722
|
+
}
|
|
723
|
+
return std::make_pair(lo, hi);
|
|
724
|
+
}
|
|
725
|
+
|
|
692
726
|
struct ScalarField {
|
|
693
727
|
std::vector<double> v; // flat, x fastest
|
|
694
728
|
size_t nx = 0, ny = 0, nz = 0;
|
|
@@ -974,6 +1008,13 @@ manifold::MeshGL64 marchingCubes(const std::vector<double>& d, const size_t n[3]
|
|
|
974
1008
|
const int axis = (b - a) == 1 ? 0 : (b - a) == 2 ? 1 : 2;
|
|
975
1009
|
int64_t& id = edgeVert[3 * p[a] + static_cast<size_t>(axis)];
|
|
976
1010
|
if (id < 0) {
|
|
1011
|
+
// Linear, as BOSL2 interpolates. Root-finding each
|
|
1012
|
+
// vertex onto the true surface of a function field was
|
|
1013
|
+
// tried and measured (#745 on BelfrySCAD): 16% less mean
|
|
1014
|
+
// normal error on a sphere, but where the grid is too
|
|
1015
|
+
// coarse for the field it put real ripples on vertices
|
|
1016
|
+
// the grid cannot connect -- jagged, sliver-strewn --
|
|
1017
|
+
// where interpolating smooths them away, as BOSL2 does.
|
|
977
1018
|
const double t = v[a] / (v[a] - v[b]);
|
|
978
1019
|
const double x = org[0] + h[0] * (static_cast<double>(i + (a & 1)) + (axis == 0 ? t : 0.0));
|
|
979
1020
|
const double y = org[1] + h[1] * (static_cast<double>(j + (a >> 1 & 1)) + (axis == 1 ? t : 0.0));
|
|
@@ -1083,14 +1124,10 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1083
1124
|
const oscad::ASTNode& node) {
|
|
1084
1125
|
// Two shapes of field, and the choice matters beyond taste:
|
|
1085
1126
|
//
|
|
1086
|
-
// a GRID -- faster to sample (0.41 vs 0.96 us)
|
|
1087
|
-
//
|
|
1088
|
-
//
|
|
1089
|
-
//
|
|
1090
|
-
// the true surface, so it is MORE accurate, with no
|
|
1091
|
-
// memory ceiling. But every sample is a closure call, and
|
|
1092
|
-
// canParallel must be false or the evaluator gets called
|
|
1093
|
-
// back from threads it knows nothing about.
|
|
1127
|
+
// a GRID -- faster to sample (0.41 vs 0.96 us). Accuracy is capped
|
|
1128
|
+
// by the grid; memory is the ceiling.
|
|
1129
|
+
// a FUNCTION -- sampled onto a grid at edge= spacing, one closure call
|
|
1130
|
+
// per sample. Needs edge=, since nothing else says how fine.
|
|
1094
1131
|
const Value& fieldArg = params.at("field");
|
|
1095
1132
|
const ClosurePtr* fieldFn = std::get_if<ClosurePtr>(&fieldArg);
|
|
1096
1133
|
// The array is NOT parsed yet: whether it should be field[i][j] or
|
|
@@ -1108,12 +1145,20 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1108
1145
|
// matches how the caller already has to think about the box.
|
|
1109
1146
|
const ListPtr* bb = std::get_if<ListPtr>(¶ms.at("bounds"));
|
|
1110
1147
|
std::optional<std::vector<double>> bLo, bHi;
|
|
1148
|
+
const bool noBounds = std::holds_alternative<std::monostate>(params.at("bounds"));
|
|
1111
1149
|
if (bb && *bb && (*bb)->items.size() == 2) {
|
|
1112
1150
|
bLo = numbersOrInf((*bb)->items[0]);
|
|
1113
1151
|
bHi = numbersOrInf((*bb)->items[1]);
|
|
1152
|
+
} else if (noBounds && !fieldFn) {
|
|
1153
|
+
if (const auto inferred = boundsFromEdge(fieldArg, params.at("edge"))) {
|
|
1154
|
+
bLo = inferred->first;
|
|
1155
|
+
bHi = inferred->second;
|
|
1156
|
+
}
|
|
1114
1157
|
}
|
|
1115
1158
|
if (!bLo || !bHi || bLo->size() != bHi->size() || (bLo->size() != 2 && bLo->size() != 3)) {
|
|
1116
|
-
ev.warn(
|
|
1159
|
+
ev.warn(noBounds && !fieldFn
|
|
1160
|
+
? "levelset(): a field array needs bounds=, or edge= (its sample spacing) to place it"
|
|
1161
|
+
: "levelset(): bounds must be [[x0,y0],[x1,y1]] or [[x0,y0,z0],[x1,y1,z1]]",
|
|
1117
1162
|
&node.position());
|
|
1118
1163
|
return {};
|
|
1119
1164
|
}
|
|
@@ -1211,74 +1256,82 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1211
1256
|
|
|
1212
1257
|
// ---- 2D: sample onto a padded grid, contour it, hand to CrossSection.
|
|
1213
1258
|
if (is2d) {
|
|
1214
|
-
size_t nx = 0, ny = 0;
|
|
1215
|
-
double sx = 0, sy = 0;
|
|
1216
1259
|
if (field) {
|
|
1217
1260
|
ev.warn("levelset(): a 2D field must be field[i][j]; a 3D array was given",
|
|
1218
1261
|
&node.position());
|
|
1219
1262
|
return {};
|
|
1220
1263
|
}
|
|
1264
|
+
const double x0 = (*bLo)[0], y0 = (*bLo)[1];
|
|
1265
|
+
const double wx = (*bHi)[0] - x0, wy = (*bHi)[1] - y0;
|
|
1266
|
+
double ex = edgeVec ? edgeV[0] : edge, ey = edgeVec ? edgeV[1] : edge;
|
|
1267
|
+
std::optional<std::vector<std::vector<double>>> plane;
|
|
1268
|
+
size_t px = 0, py = 0;
|
|
1269
|
+
bool ownSamples = false;
|
|
1221
1270
|
if (!fieldFn) {
|
|
1222
|
-
|
|
1271
|
+
plane = readPlane(fieldArg);
|
|
1223
1272
|
if (!plane || plane->size() < 2 || (*plane)[0].size() < 2) {
|
|
1224
1273
|
ev.warn("levelset(): 2D field must be a rectangular field[i][j] of numbers",
|
|
1225
1274
|
&node.position());
|
|
1226
1275
|
return {};
|
|
1227
1276
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
g.dx = sx;
|
|
1236
|
-
g.dy = sy;
|
|
1237
|
-
g.ox = (*bLo)[0] - sx;
|
|
1238
|
-
g.oy = (*bLo)[1] - sy;
|
|
1239
|
-
// Padded with a ring that is firmly OUTSIDE, so a contour meeting
|
|
1240
|
-
// the box edge closes along it instead of running off. This is
|
|
1241
|
-
// BOSL2's closed=true, and the only behaviour offered.
|
|
1242
|
-
g.v.assign(g.nx * g.ny, -1.0);
|
|
1243
|
-
for (size_t i = 0; i < nx; ++i)
|
|
1244
|
-
for (size_t j = 0; j < ny; ++j)
|
|
1245
|
-
g.v[(j + 1) * g.nx + (i + 1)] = bandDistance((*plane)[i][j], isoLo, isoHi, invert);
|
|
1246
|
-
const manifold::Polygons polys = marchingSquares(g);
|
|
1247
|
-
if (polys.empty()) return {};
|
|
1248
|
-
ColoredBody b;
|
|
1249
|
-
b.section = clipToBounds(manifold::CrossSection(polys, manifold::CrossSection::FillRule::EvenOdd),
|
|
1250
|
-
*bLo, *bHi);
|
|
1251
|
-
if (b.section->IsEmpty()) return {};
|
|
1252
|
-
std::vector<ColoredBody> out;
|
|
1253
|
-
out.push_back(std::move(b));
|
|
1254
|
-
return out;
|
|
1277
|
+
px = plane->size();
|
|
1278
|
+
py = (*plane)[0].size();
|
|
1279
|
+
// Contoured on its own samples unless edge= asks for another
|
|
1280
|
+
// spacing, as the 3D grid is marched; then re-sampled bilinearly.
|
|
1281
|
+
const double gx = wx / static_cast<double>(px - 1), gy = wy / static_cast<double>(py - 1);
|
|
1282
|
+
const auto same = [](double a, double b) { return std::abs(a - b) <= 1e-9 * std::max(a, b); };
|
|
1283
|
+
ownSamples = ex <= 0.0 || (same(ex, gx) && same(ey, gy));
|
|
1255
1284
|
}
|
|
1256
|
-
//
|
|
1257
|
-
|
|
1258
|
-
nx = static_cast<size_t>(std::floor(
|
|
1259
|
-
ny = static_cast<size_t>(std::floor(
|
|
1285
|
+
// Counted from the array, not floor(width / spacing): rounding there
|
|
1286
|
+
// could drop the last sample row.
|
|
1287
|
+
const size_t nx = ownSamples ? px : static_cast<size_t>(std::floor(wx / ex)) + 1;
|
|
1288
|
+
const size_t ny = ownSamples ? py : static_cast<size_t>(std::floor(wy / ey)) + 1;
|
|
1260
1289
|
if (nx < 2 || ny < 2) {
|
|
1261
1290
|
ev.warn("levelset(): edge is larger than the bounds", &node.position());
|
|
1262
1291
|
return {};
|
|
1263
1292
|
}
|
|
1264
|
-
sx = (
|
|
1265
|
-
|
|
1293
|
+
const double sx = wx / static_cast<double>(nx - 1), sy = wy / static_cast<double>(ny - 1);
|
|
1294
|
+
// The plane at a point between its samples. Clamped at the edges,
|
|
1295
|
+
// like the 3D grid sampler, so the clip below cuts a clean line.
|
|
1296
|
+
const auto bilinear = [&](double x, double y) {
|
|
1297
|
+
const auto cell = [](double u, size_t n, size_t& i0) {
|
|
1298
|
+
i0 = static_cast<size_t>(std::clamp(std::floor(u), 0.0, static_cast<double>(n - 2)));
|
|
1299
|
+
return std::clamp(u - static_cast<double>(i0), 0.0, 1.0);
|
|
1300
|
+
};
|
|
1301
|
+
size_t i, j;
|
|
1302
|
+
const double tx = cell((x - x0) / wx * static_cast<double>(px - 1), px, i);
|
|
1303
|
+
const double ty = cell((y - y0) / wy * static_cast<double>(py - 1), py, j);
|
|
1304
|
+
const auto& p = *plane;
|
|
1305
|
+
return (1 - tx) * ((1 - ty) * p[i][j] + ty * p[i][j + 1])
|
|
1306
|
+
+ tx * ((1 - ty) * p[i + 1][j] + ty * p[i + 1][j + 1]);
|
|
1307
|
+
};
|
|
1266
1308
|
Contours2d g;
|
|
1267
1309
|
g.nx = nx + 2;
|
|
1268
1310
|
g.ny = ny + 2;
|
|
1269
1311
|
g.dx = sx;
|
|
1270
1312
|
g.dy = sy;
|
|
1271
|
-
g.ox =
|
|
1272
|
-
g.oy =
|
|
1313
|
+
g.ox = x0 - sx;
|
|
1314
|
+
g.oy = y0 - sy;
|
|
1315
|
+
// Padded with a ring that is firmly OUTSIDE, so a contour meeting
|
|
1316
|
+
// the box edge closes along it instead of running off. This is
|
|
1317
|
+
// BOSL2's closed=true, and the only behaviour offered.
|
|
1273
1318
|
g.v.assign(g.nx * g.ny, -1.0);
|
|
1274
1319
|
for (size_t i = 0; i < nx; ++i) {
|
|
1275
1320
|
for (size_t j = 0; j < ny; ++j) {
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1321
|
+
const double x = x0 + static_cast<double>(i) * sx, y = y0 + static_cast<double>(j) * sy;
|
|
1322
|
+
double val;
|
|
1323
|
+
if (ownSamples) {
|
|
1324
|
+
val = (*plane)[i][j];
|
|
1325
|
+
} else if (plane) {
|
|
1326
|
+
val = bilinear(x, y);
|
|
1327
|
+
} else {
|
|
1328
|
+
BoundArgs bound;
|
|
1329
|
+
bound.set(fnParams2[0], Value{x});
|
|
1330
|
+
bound.set(fnParams2[1], Value{y});
|
|
1331
|
+
const Value outv =
|
|
1332
|
+
ev.evalFunctionLiteralFromBound(**fieldFn, std::move(bound), *fnCtx, &node.position());
|
|
1333
|
+
val = fieldSample(outv).value_or(std::numeric_limits<double>::max());
|
|
1334
|
+
}
|
|
1282
1335
|
g.v[(j + 1) * g.nx + (i + 1)] = bandDistance(val, isoLo, isoHi, invert);
|
|
1283
1336
|
}
|
|
1284
1337
|
}
|
|
@@ -1360,13 +1413,8 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1360
1413
|
if (i0[a] > n[a] - 2) i0[a] = n[a] - 2;
|
|
1361
1414
|
t[a] = g[a] - static_cast<double>(i0[a]);
|
|
1362
1415
|
}
|
|
1363
|
-
//
|
|
1364
|
-
//
|
|
1365
|
-
// the padding and the clip to `bounds` below leaves a clean plane.
|
|
1366
|
-
// Answering "outside" there instead (as this once did) made Manifold
|
|
1367
|
-
// close the surface right AT the box face, along its own lattice: a
|
|
1368
|
-
// waffle-textured staircase INSIDE the bounds, which the clip could
|
|
1369
|
-
// not remove (#745 on BelfrySCAD).
|
|
1416
|
+
// Clamped, though every grid node lies inside the block: the
|
|
1417
|
+
// floating-point position of the last node can land a hair past it.
|
|
1370
1418
|
|
|
1371
1419
|
double acc = 0.0;
|
|
1372
1420
|
for (int c = 0; c < 8; ++c) {
|
|
@@ -1378,62 +1426,54 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1378
1426
|
return bandDistance(acc, isoLo, isoHi, invert);
|
|
1379
1427
|
};
|
|
1380
1428
|
|
|
1381
|
-
//
|
|
1382
|
-
//
|
|
1383
|
-
//
|
|
1384
|
-
//
|
|
1385
|
-
// there, and cut back to the box actually asked for. The cut is then a
|
|
1386
|
-
// clean plane.
|
|
1429
|
+
// Every 3D field is marched on a REGULAR grid whose outermost nodes lie
|
|
1430
|
+
// exactly on `bounds`, as BOSL2's isosurface() marches: an array at its
|
|
1431
|
+
// own spacing as is, anything else sampled onto one first (a function by
|
|
1432
|
+
// calling it, an array re-sampled trilinearly).
|
|
1387
1433
|
//
|
|
1388
|
-
//
|
|
1389
|
-
//
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
//
|
|
1395
|
-
// the bracket is within it (a distance along the edge, so independent of
|
|
1396
|
-
// the field's units), instead of interpolating linearly between the two
|
|
1397
|
-
// lattice samples. 1% of the edge. Measured on #745 on BelfrySCAD: the
|
|
1398
|
-
// function form's vertices go from up to 0.2 off the true surface to on
|
|
1399
|
-
// it, and the mean face-normal error on a per-axis edge halves to
|
|
1400
|
-
// BOSL2's own; a grid gains too, since the trilinear field between
|
|
1401
|
-
// samples is not linear along a lattice edge. Costs 11-20% more work.
|
|
1434
|
+
// This replaced Manifold::LevelSet for the function form and for an
|
|
1435
|
+
// array at another spacing (#745 on BelfrySCAD). LevelSet samples its own
|
|
1436
|
+
// body-centred lattice, whose irregular triangles shade as a field of
|
|
1437
|
+
// dimples even where every vertex is on the surface -- worse with a
|
|
1438
|
+
// per-axis edge, which meshed a squashed field and stretched it back. And
|
|
1439
|
+
// that lattice does not land on the box, so where the clip below cut it
|
|
1440
|
+
// the cut face did not follow the same grid as the rest of the surface.
|
|
1402
1441
|
//
|
|
1403
|
-
//
|
|
1404
|
-
//
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
for (size_t k = 0; k < field->nz; ++k)
|
|
1417
|
-
for (size_t j = 0; j < field->ny; ++j)
|
|
1418
|
-
for (size_t i = 0; i < field->nx; ++i)
|
|
1419
|
-
d[((k + 1) * n[1] + (j + 1)) * n[0] + (i + 1)] =
|
|
1420
|
-
bandDistance(field->at(i, j, k), isoLo, isoHi, invert);
|
|
1421
|
-
const std::array<double, 3> org{origin[0] - spacing[0], origin[1] - spacing[1], origin[2] - spacing[2]};
|
|
1422
|
-
solid = manifold::Manifold(marchingCubes(d, n, org, spacing));
|
|
1423
|
-
// Never expected; but a mesh Manifold refuses must not cost the
|
|
1424
|
-
// user their shape, so fall back to resampling it.
|
|
1425
|
-
if (solid.Status() != manifold::Manifold::Error::NoError) marchGrid = false;
|
|
1442
|
+
// A per-axis edge that does not divide the box is shrunk to the nearest
|
|
1443
|
+
// one that does, never grown: never coarser than the caller asked.
|
|
1444
|
+
std::array<double, 3> h = spacing;
|
|
1445
|
+
size_t cells[3];
|
|
1446
|
+
for (int a = 0; a < 3; ++a) {
|
|
1447
|
+
const double w = (*hi)[a] - origin[a];
|
|
1448
|
+
if (marchGrid) {
|
|
1449
|
+
cells[a] = static_cast<size_t>(std::llround(w / spacing[a]));
|
|
1450
|
+
} else {
|
|
1451
|
+
const double e = edgeVec ? edgeV[a] : edge;
|
|
1452
|
+
cells[a] = std::max<size_t>(1, static_cast<size_t>(std::ceil(w / e - 1e-9)));
|
|
1453
|
+
h[a] = w / static_cast<double>(cells[a]);
|
|
1454
|
+
}
|
|
1426
1455
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1456
|
+
// One "outside" node beyond the box on every side, so every surface
|
|
1457
|
+
// closes; the clip below cuts that overhang back to the box.
|
|
1458
|
+
const size_t n[3] = {cells[0] + 3, cells[1] + 3, cells[2] + 3};
|
|
1459
|
+
std::vector<double> d(n[0] * n[1] * n[2], -1.0);
|
|
1460
|
+
for (size_t k = 0; k + 2 < n[2]; ++k)
|
|
1461
|
+
for (size_t j = 0; j + 2 < n[1]; ++j)
|
|
1462
|
+
for (size_t i = 0; i + 2 < n[0]; ++i) {
|
|
1463
|
+
const double x = origin[0] + h[0] * static_cast<double>(i);
|
|
1464
|
+
const double y = origin[1] + h[1] * static_cast<double>(j);
|
|
1465
|
+
const double z = origin[2] + h[2] * static_cast<double>(k);
|
|
1466
|
+
d[((k + 1) * n[1] + (j + 1)) * n[0] + (i + 1)] =
|
|
1467
|
+
marchGrid ? bandDistance(field->at(i, j, k), isoLo, isoHi, invert)
|
|
1468
|
+
: fieldFn ? sampleFn(manifold::vec3(x, y, z))
|
|
1469
|
+
: sampleGrid(manifold::vec3(x, y, z));
|
|
1470
|
+
}
|
|
1471
|
+
const std::array<double, 3> org{origin[0] - h[0], origin[1] - h[1], origin[2] - h[2]};
|
|
1472
|
+
manifold::Manifold solid(marchingCubes(d, n, org, h));
|
|
1473
|
+
if (solid.Status() != manifold::Manifold::Error::NoError) {
|
|
1474
|
+
// Never expected: marchingCubes closes by construction.
|
|
1475
|
+
ev.warn("levelset(): could not build a closed mesh from this field", &node.position());
|
|
1476
|
+
return {};
|
|
1437
1477
|
}
|
|
1438
1478
|
|
|
1439
1479
|
if (solid.IsEmpty()) return {};
|
|
@@ -1374,11 +1374,12 @@ TEST(LevelSetFn, AVectorEdgeOfTheWrongSizeWarns) {
|
|
|
1374
1374
|
|
|
1375
1375
|
TEST(LevelSetFn, SupportedFeatureSaysAVectorEdgeWorks) {
|
|
1376
1376
|
// Level 2 is how a library such as BOSL2 tells a build with per-axis
|
|
1377
|
-
// edge= from one that would reject it
|
|
1377
|
+
// edge= from one that would reject it; level 3, one that places a
|
|
1378
|
+
// field array from edge= alone (#749 on BelfrySCAD).
|
|
1378
1379
|
std::vector<std::string> out;
|
|
1379
1380
|
evalSrc("echo(supported_feature(\"levelset\"));", [&](const std::string& m) { out.push_back(m); });
|
|
1380
1381
|
ASSERT_EQ(out.size(), 1u);
|
|
1381
|
-
EXPECT_NE(out[0].find("
|
|
1382
|
+
EXPECT_NE(out[0].find("3"), std::string::npos) << out[0];
|
|
1382
1383
|
}
|
|
1383
1384
|
|
|
1384
1385
|
TEST(LevelSetFn, AFunctionOfTheWrongArityWarns) {
|
|
@@ -1605,13 +1606,13 @@ TEST(LevelSet, AGridFieldIsAlsoCutCleanlyAtTheBox) {
|
|
|
1605
1606
|
EXPECT_NEAR(capArea, 3.14159265358979 * r * r, 0.03 * 3.14159265358979 * r * r);
|
|
1606
1607
|
}
|
|
1607
1608
|
|
|
1608
|
-
TEST(LevelSetFn,
|
|
1609
|
-
//
|
|
1610
|
-
//
|
|
1611
|
-
//
|
|
1612
|
-
//
|
|
1613
|
-
// the
|
|
1614
|
-
// worst.
|
|
1609
|
+
TEST(LevelSetFn, VerticesInterpolateAsBOSL2Does) {
|
|
1610
|
+
// #745 on BelfrySCAD, second round: a function field is sampled onto a
|
|
1611
|
+
// regular grid and marched, as BOSL2's isosurface() does, rather than
|
|
1612
|
+
// meshed by Manifold::LevelSet, whose body-centred lattice shaded as
|
|
1613
|
+
// dimples. Vertices are interpolated linearly along grid edges, so they
|
|
1614
|
+
// are near the surface, not on it: the 1/r field at edge=2 is where that
|
|
1615
|
+
// is worst. A per-axis edge must mesh as well as a plain one.
|
|
1615
1616
|
for (const char* edge : {"2", "[1,2,0.4]"}) {
|
|
1616
1617
|
Evaluated e = evalSrc(std::string("levelset(function(x,y,z) 5 / norm([x,y,z]), "
|
|
1617
1618
|
"bounds=[[-8,-8,-8],[8,8,8]], isovalue=[1, 1/0], edge=") +
|
|
@@ -1622,7 +1623,8 @@ TEST(LevelSetFn, VerticesLieOnTheTrueSurface) {
|
|
|
1622
1623
|
for (size_t v = 0; v + 2 < m.vertProperties.size(); v += m.numProp)
|
|
1623
1624
|
worst = std::max(worst, std::abs(manifold::la::length(manifold::vec3(m.vertProperties[v], m.vertProperties[v + 1],
|
|
1624
1625
|
m.vertProperties[v + 2])) - 5.0));
|
|
1625
|
-
EXPECT_LT(worst, 0.
|
|
1626
|
+
EXPECT_LT(worst, 0.3) << edge;
|
|
1627
|
+
EXPECT_EQ(soleBody(e).Genus(), 0) << edge;
|
|
1626
1628
|
}
|
|
1627
1629
|
}
|
|
1628
1630
|
|
|
@@ -1674,6 +1676,95 @@ TEST(LevelSetGrid, TheDefaultMarchesTheGridItself) {
|
|
|
1674
1676
|
EXPECT_EQ(soleBody(same).NumTri(), soleBody(e).NumTri());
|
|
1675
1677
|
}
|
|
1676
1678
|
|
|
1679
|
+
TEST(LevelSetGrid, EdgeWithoutBoundsCentresTheGridOnTheOrigin) {
|
|
1680
|
+
// #749 on BelfrySCAD: edge= alone places a field array, its samples
|
|
1681
|
+
// edge apart and centred on the origin, as BOSL2's isosurface() places
|
|
1682
|
+
// an array given voxel_size. Same mesh as the explicit bounds.
|
|
1683
|
+
Evaluated e = evalSrc(sphereFieldSrc(41, 20) + "levelset(f, isovalue=15, edge=1);");
|
|
1684
|
+
Evaluated ref = evalSrc(sphereFieldSrc(41, 20) +
|
|
1685
|
+
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=15);");
|
|
1686
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1687
|
+
ASSERT_EQ(ref.bodies.size(), 1u);
|
|
1688
|
+
EXPECT_EQ(soleBody(e).NumTri(), soleBody(ref).NumTri());
|
|
1689
|
+
EXPECT_NEAR(soleBody(e).Volume(), soleBody(ref).Volume(), 1e-9 * soleBody(ref).Volume());
|
|
1690
|
+
EXPECT_GT(onGridLines(soleBody(e), manifold::vec3(-20.0), manifold::vec3(1.0)), 0.5);
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
TEST(LevelSetGrid, AVectorEdgeWithoutBoundsSpacesEachAxis) {
|
|
1694
|
+
// Every sample inside, so the solid is the whole box: 5 x 3 x 4 samples
|
|
1695
|
+
// at [1, 2, 0.5] span 4 x 4 x 1.5, centred.
|
|
1696
|
+
Evaluated e = evalSrc("f = [for (i=[0:4]) [for (j=[0:2]) [for (k=[0:3]) -1 ]]];\n"
|
|
1697
|
+
"levelset(f, edge=[1, 2, 0.5]);");
|
|
1698
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1699
|
+
const manifold::Box b = soleBody(e).BoundingBox();
|
|
1700
|
+
EXPECT_NEAR(b.min.x, -2.0, 1e-9); EXPECT_NEAR(b.max.x, 2.0, 1e-9);
|
|
1701
|
+
EXPECT_NEAR(b.min.y, -2.0, 1e-9); EXPECT_NEAR(b.max.y, 2.0, 1e-9);
|
|
1702
|
+
EXPECT_NEAR(b.min.z, -0.75, 1e-9); EXPECT_NEAR(b.max.z, 0.75, 1e-9);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
TEST(LevelSetGrid, AFunctionIsMarchedOnAGridLandingOnTheBounds) {
|
|
1706
|
+
// Per-axis edge [1, 2, 0.4] over a 12 x 16 x 7 box: x and y divide it,
|
|
1707
|
+
// z does not (17.5 cells), so z is shrunk to 7/18 -- never coarsened --
|
|
1708
|
+
// and the grid's last nodes lie exactly on the box. Every vertex is then
|
|
1709
|
+
// on a grid edge.
|
|
1710
|
+
Evaluated e = evalSrc("levelset(function(x,y,z) 5 / norm([x,y,z]), isovalue=[1, 1/0], "
|
|
1711
|
+
"bounds=[[-6,-8,0],[6,8,7]], edge=[1,2,0.4]);");
|
|
1712
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1713
|
+
EXPECT_GT(onGridLines(soleBody(e), manifold::vec3(-6, -8, 0), manifold::vec3(1, 2, 7.0 / 18.0)), 0.5);
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
TEST(LevelSetGrid, AnArrayAtAnotherEdgeIsResampledOntoAGridLandingOnTheBounds) {
|
|
1717
|
+
// #745 on BelfrySCAD: an array resampled at an edge= not dividing the box
|
|
1718
|
+
// cut faces that did not follow the rest of the surface, since
|
|
1719
|
+
// LevelSet's lattice did not land on the box. 40/2.7 is 14.8 cells, so
|
|
1720
|
+
// the grid is 15 cells of 40/15 and the cut faces are its own.
|
|
1721
|
+
Evaluated e = evalSrc(sphereFieldSrc(41, 20) +
|
|
1722
|
+
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=25, edge=2.7);");
|
|
1723
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1724
|
+
EXPECT_GT(onGridLines(soleBody(e), manifold::vec3(-20.0), manifold::vec3(40.0 / 15.0)), 0.5);
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
TEST(LevelSetGrid, A2DFieldArrayIsPlacedByEdgeToo) {
|
|
1728
|
+
// field[i][j] says 2D without a bounds to say so.
|
|
1729
|
+
Evaluated e = evalSrc("f = [for (i=[0:4]) [for (j=[0:2]) -1 ]];\nlevelset(f, edge=2);");
|
|
1730
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1731
|
+
ASSERT_TRUE(e.bodies[0].section.has_value());
|
|
1732
|
+
const manifold::Rect r = e.bodies[0].section->Bounds();
|
|
1733
|
+
EXPECT_NEAR(r.min.x, -4.0, 1e-9); EXPECT_NEAR(r.max.x, 4.0, 1e-9);
|
|
1734
|
+
EXPECT_NEAR(r.min.y, -2.0, 1e-9); EXPECT_NEAR(r.max.y, 2.0, 1e-9);
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
TEST(LevelSetGrid, A2DGridIsResampledAtADifferentEdge) {
|
|
1738
|
+
// As in 3D: edge= beside bounds re-samples the plane (bilinearly), where
|
|
1739
|
+
// it used to be ignored. A cone sampled every 2, contoured at r=7.
|
|
1740
|
+
const std::string field =
|
|
1741
|
+
"f = [for (i=[0:10]) [for (j=[0:10]) let(x=-10+2*i, y=-10+2*j) sqrt(x*x+y*y) ]];\n";
|
|
1742
|
+
const std::string box = "bounds=[[-10,-10],[10,10]], isovalue=7";
|
|
1743
|
+
Evaluated own = evalSrc(field + "levelset(f, " + box + ");");
|
|
1744
|
+
Evaluated same = evalSrc(field + "levelset(f, " + box + ", edge=2);");
|
|
1745
|
+
Evaluated fine = evalSrc(field + "levelset(f, " + box + ", edge=0.25);");
|
|
1746
|
+
for (Evaluated* e : {&own, &same, &fine}) {
|
|
1747
|
+
ASSERT_EQ(e->bodies.size(), 1u);
|
|
1748
|
+
ASSERT_TRUE(e->bodies[0].section.has_value());
|
|
1749
|
+
}
|
|
1750
|
+
EXPECT_EQ(same.bodies[0].section->NumVert(), own.bodies[0].section->NumVert());
|
|
1751
|
+
EXPECT_GT(fine.bodies[0].section->NumVert(), 4 * own.bodies[0].section->NumVert());
|
|
1752
|
+
const double disc = 3.14159265358979 * 49;
|
|
1753
|
+
EXPECT_NEAR(fine.bodies[0].section->Area(), disc, 0.03 * disc);
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
TEST(LevelSetGrid, NeitherBoundsNorEdgeSaysWhatIsMissing) {
|
|
1757
|
+
const std::vector<std::string> w =
|
|
1758
|
+
levelsetWarnings("f = [for (i=[0:4]) [for (j=[0:2]) [for (k=[0:3]) -1 ]]];\nlevelset(f);");
|
|
1759
|
+
ASSERT_EQ(w.size(), 1u);
|
|
1760
|
+
EXPECT_NE(w[0].find("bounds=, or edge="), std::string::npos) << w[0];
|
|
1761
|
+
// A function field has no sizes to infer from: bounds stays required.
|
|
1762
|
+
const std::vector<std::string> fw =
|
|
1763
|
+
levelsetWarnings("levelset(function(x,y,z) x, edge=1);");
|
|
1764
|
+
ASSERT_EQ(fw.size(), 1u);
|
|
1765
|
+
EXPECT_NE(fw[0].find("bounds must be"), std::string::npos) << fw[0];
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1677
1768
|
TEST(LevelSetGrid, ADifferentEdgeStillResamples) {
|
|
1678
1769
|
Evaluated e = evalSrc(sphereFieldSrc(41, 20) +
|
|
1679
1770
|
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=15, edge=0.7);");
|
|
File without changes
|
{openscad_cpp_evaluator-1.47.0 → openscad_cpp_evaluator-1.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
|