openscad-cpp-evaluator 1.46.0__tar.gz → 1.48.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/CLAUDE.md +6 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/function_builtins.cpp +1 -1
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/topology.cpp +333 -84
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_booleans.cpp +278 -10
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/uv.lock +1 -1
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/cmake/patch_manifold.cmake +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/LICENSE +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/.DS_Store +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/import/scene_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/xkcd_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/xkcd_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_discretizer.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_openscad_parity.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_undef_args.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.48.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.46.0 → openscad_cpp_evaluator-1.48.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()
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
#include <manifold/polygon.h>
|
|
8
8
|
|
|
9
|
+
#include <algorithm>
|
|
10
|
+
#include <utility>
|
|
11
|
+
|
|
9
12
|
namespace oscadeval {
|
|
10
13
|
|
|
11
14
|
// simplify()'s default tolerance, as a fraction of the body's bounding-box
|
|
@@ -663,14 +666,68 @@ std::vector<ColoredBody> generateMeshRepair(Evaluator& ev, const CSGParams& para
|
|
|
663
666
|
// The trade is memory: ~67 bytes per cell, so 200^3 is about 550 MB and that
|
|
664
667
|
// is the practical ceiling. A callback samples lazily and has no ceiling.
|
|
665
668
|
//
|
|
666
|
-
// Accuracy is bounded by the GRID, not by Manifold.
|
|
667
|
-
//
|
|
668
|
-
//
|
|
669
|
-
//
|
|
670
|
-
//
|
|
669
|
+
// Accuracy is bounded by the GRID, not by Manifold. At the grid's own
|
|
670
|
+
// spacing (the default) the grid is marched directly, as BOSL2's
|
|
671
|
+
// isosurface() does -- see marchingCubes(). An edge= asking for a different
|
|
672
|
+
// spacing resamples it through LevelSet instead, which samples a
|
|
673
|
+
// body-centred cubic lattice (sdf.cpp:440-446) that a plain cubic grid does
|
|
674
|
+
// not line up with, so the lambda interpolates trilinearly between samples.
|
|
671
675
|
|
|
672
676
|
namespace {
|
|
673
677
|
|
|
678
|
+
// A sample as the field arrays and field functions hand it over. +-INF is a
|
|
679
|
+
// legal sample -- a metaball field is 1/0 wherever a grid point lands on a
|
|
680
|
+
// centre (#744 on BelfrySCAD) -- but it cannot be used as is: the trilinear
|
|
681
|
+
// sampler weights corners, and 0 * INF is NaN. So it becomes a huge FINITE
|
|
682
|
+
// value, which is the limit BOSL2's isosurface() takes (an edge to an INF
|
|
683
|
+
// corner crosses at the other end). A quarter of DBL_MAX so that sums of
|
|
684
|
+
// weighted corners and bandDistance's subtraction cannot overflow. NaN, or
|
|
685
|
+
// anything not a number, has no meaning and is nullopt.
|
|
686
|
+
std::optional<double> fieldSample(const Value& item) {
|
|
687
|
+
const double* d = std::get_if<double>(&item);
|
|
688
|
+
if (!d || std::isnan(*d)) return std::nullopt;
|
|
689
|
+
constexpr double kHuge = std::numeric_limits<double>::max() / 4;
|
|
690
|
+
return std::clamp(*d, -kHuge, kHuge);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// bounds for a field ARRAY given edge= and no bounds (#749 on BelfrySCAD):
|
|
694
|
+
// the samples edge apart, centred on the origin, as BOSL2's isosurface()
|
|
695
|
+
// places an array given voxel_size. The array's depth (field[i][j] or
|
|
696
|
+
// field[i][j][k]) says 2D or 3D; the sizes come from its first row, and
|
|
697
|
+
// readField/readPlane check the rest later. nullopt when edge is missing
|
|
698
|
+
// or does not fit, leaving the caller's "bounds must be ..." warning.
|
|
699
|
+
std::optional<std::pair<std::vector<double>, std::vector<double>>>
|
|
700
|
+
boundsFromEdge(const Value& field, const Value& edge) {
|
|
701
|
+
std::vector<size_t> dims;
|
|
702
|
+
for (const Value* cur = &field; dims.size() < 3;) {
|
|
703
|
+
const ListPtr* l = std::get_if<ListPtr>(cur);
|
|
704
|
+
if (!l || !*l || (*l)->items.empty()) break;
|
|
705
|
+
dims.push_back((*l)->items.size());
|
|
706
|
+
cur = &(*l)->items[0];
|
|
707
|
+
}
|
|
708
|
+
if (dims.size() < 2) return std::nullopt;
|
|
709
|
+
std::vector<double> step;
|
|
710
|
+
if (const double* e = std::get_if<double>(&edge)) {
|
|
711
|
+
step.assign(dims.size(), *e);
|
|
712
|
+
} else if (const ListPtr* l = std::get_if<ListPtr>(&edge); l && *l && (*l)->items.size() == dims.size()) {
|
|
713
|
+
for (const Value& item : (*l)->items) {
|
|
714
|
+
const double* d = std::get_if<double>(&item);
|
|
715
|
+
if (!d) return std::nullopt;
|
|
716
|
+
step.push_back(*d);
|
|
717
|
+
}
|
|
718
|
+
} else {
|
|
719
|
+
return std::nullopt;
|
|
720
|
+
}
|
|
721
|
+
std::vector<double> lo, hi;
|
|
722
|
+
for (size_t a = 0; a < dims.size(); ++a) {
|
|
723
|
+
if (!std::isfinite(step[a]) || step[a] <= 0.0 || dims[a] < 2) return std::nullopt;
|
|
724
|
+
const double half = 0.5 * step[a] * static_cast<double>(dims[a] - 1);
|
|
725
|
+
lo.push_back(-half);
|
|
726
|
+
hi.push_back(half);
|
|
727
|
+
}
|
|
728
|
+
return std::make_pair(lo, hi);
|
|
729
|
+
}
|
|
730
|
+
|
|
674
731
|
struct ScalarField {
|
|
675
732
|
std::vector<double> v; // flat, x fastest
|
|
676
733
|
size_t nx = 0, ny = 0, nz = 0;
|
|
@@ -698,8 +755,8 @@ std::optional<ScalarField> readField(const Value& val) {
|
|
|
698
755
|
return std::nullopt;
|
|
699
756
|
}
|
|
700
757
|
for (size_t k = 0; k < f.nz; ++k) {
|
|
701
|
-
const double
|
|
702
|
-
if (!d
|
|
758
|
+
const std::optional<double> d = fieldSample((*zs)->items[k]);
|
|
759
|
+
if (!d) return std::nullopt;
|
|
703
760
|
f.v[(k * f.ny + j) * f.nx + i] = *d;
|
|
704
761
|
}
|
|
705
762
|
}
|
|
@@ -722,8 +779,8 @@ std::optional<std::vector<std::vector<double>>> readPlane(const Value& val) {
|
|
|
722
779
|
std::vector<double> row;
|
|
723
780
|
row.reserve(ny);
|
|
724
781
|
for (const Value& item : (*ys)->items) {
|
|
725
|
-
const double
|
|
726
|
-
if (!d
|
|
782
|
+
const std::optional<double> d = fieldSample(item);
|
|
783
|
+
if (!d) return std::nullopt;
|
|
727
784
|
row.push_back(*d);
|
|
728
785
|
}
|
|
729
786
|
out.push_back(std::move(row));
|
|
@@ -890,6 +947,162 @@ manifold::Polygons marchingSquares(const Contours2d& g) {
|
|
|
890
947
|
}
|
|
891
948
|
|
|
892
949
|
|
|
950
|
+
// -- 3D: marching cubes on the grid itself -----------------------------------
|
|
951
|
+
//
|
|
952
|
+
// For a grid sampled at the spacing it was given, Manifold's LevelSet is the
|
|
953
|
+
// wrong tool: it samples its own body-centred lattice, which does not line up
|
|
954
|
+
// with the grid, so it meshes a trilinear blend of the data rather than the
|
|
955
|
+
// data. Measured on #745 on BelfrySCAD, that left a metaball field visibly
|
|
956
|
+
// more faceted than BOSL2's isosurface(), which marches the grid itself. So
|
|
957
|
+
// this does too.
|
|
958
|
+
//
|
|
959
|
+
// No 256-case table. The table's hard part is the ambiguous face (two
|
|
960
|
+
// inside corners on a diagonal), and a table that resolves it per CELL can
|
|
961
|
+
// disagree with the neighbouring cell and leave a crack -- fatal here, since
|
|
962
|
+
// Manifold needs a closed mesh. So the triangles are built per FACE instead:
|
|
963
|
+
// each face's crossings are paired into segments by a rule that reads only
|
|
964
|
+
// that face's four values (the face-centre average decides a saddle, as the
|
|
965
|
+
// 2D path does), so the two cells sharing a face always agree. A cell's
|
|
966
|
+
// segments then link into closed loops, one polygon each. Vertices are keyed
|
|
967
|
+
// on GRID EDGE identity, so neighbours share them by construction, exactly as
|
|
968
|
+
// marchingSquares keys on edges rather than coordinates.
|
|
969
|
+
//
|
|
970
|
+
// Orientation: walking a face's corners anticlockwise as seen from outside
|
|
971
|
+
// the cell, a segment runs from an outside->inside crossing to an
|
|
972
|
+
// inside->outside one. A loop of such segments, read in order, is a polygon
|
|
973
|
+
// whose normal points from inside to outside -- Manifold's convention, with
|
|
974
|
+
// inside the positive side.
|
|
975
|
+
//
|
|
976
|
+
// The field arrives as band distance (positive inside) with a ring of
|
|
977
|
+
// "outside" round it, like the 2D path, so every surface closes; the caller
|
|
978
|
+
// clips the overhang back to the bounds.
|
|
979
|
+
manifold::MeshGL64 marchingCubes(const std::vector<double>& d, const size_t n[3],
|
|
980
|
+
const std::array<double, 3>& org, const std::array<double, 3>& h) {
|
|
981
|
+
const auto P = [&](size_t i, size_t j, size_t k) { return (k * n[1] + j) * n[0] + i; };
|
|
982
|
+
// corner c of a cell is offset (c&1, c>>1&1, c>>2&1). Each face's
|
|
983
|
+
// corners, anticlockwise as seen from outside.
|
|
984
|
+
static constexpr int kFaces[6][4] = {
|
|
985
|
+
{0, 2, 3, 1}, {4, 5, 7, 6}, // -z, +z
|
|
986
|
+
{0, 4, 6, 2}, {1, 3, 7, 5}, // -x, +x
|
|
987
|
+
{0, 1, 5, 4}, {2, 6, 7, 3}, // -y, +y
|
|
988
|
+
};
|
|
989
|
+
manifold::MeshGL64 mesh;
|
|
990
|
+
mesh.numProp = 3;
|
|
991
|
+
// ponytail: one int per grid edge, ~12 bytes a sample next to the
|
|
992
|
+
// field's own ~67; key the vertices per slab if this ever binds.
|
|
993
|
+
std::vector<int64_t> edgeVert(3 * n[0] * n[1] * n[2], -1);
|
|
994
|
+
const auto addVert = [&](double x, double y, double z) {
|
|
995
|
+
mesh.vertProperties.insert(mesh.vertProperties.end(), {x, y, z});
|
|
996
|
+
return static_cast<int64_t>(mesh.vertProperties.size() / 3 - 1);
|
|
997
|
+
};
|
|
998
|
+
for (size_t k = 0; k + 1 < n[2]; ++k) {
|
|
999
|
+
for (size_t j = 0; j + 1 < n[1]; ++j) {
|
|
1000
|
+
for (size_t i = 0; i + 1 < n[0]; ++i) {
|
|
1001
|
+
double v[8];
|
|
1002
|
+
size_t p[8];
|
|
1003
|
+
int inside = 0;
|
|
1004
|
+
for (int c = 0; c < 8; ++c) {
|
|
1005
|
+
p[c] = P(i + (c & 1), j + (c >> 1 & 1), k + (c >> 2 & 1));
|
|
1006
|
+
v[c] = d[p[c]];
|
|
1007
|
+
inside += v[c] > 0.0;
|
|
1008
|
+
}
|
|
1009
|
+
if (inside == 0 || inside == 8) continue;
|
|
1010
|
+
// The vertex on the edge between corners a and b.
|
|
1011
|
+
const auto vert = [&](int a, int b) -> int64_t {
|
|
1012
|
+
if (a > b) std::swap(a, b); // a is the lower end
|
|
1013
|
+
const int axis = (b - a) == 1 ? 0 : (b - a) == 2 ? 1 : 2;
|
|
1014
|
+
int64_t& id = edgeVert[3 * p[a] + static_cast<size_t>(axis)];
|
|
1015
|
+
if (id < 0) {
|
|
1016
|
+
const double t = v[a] / (v[a] - v[b]);
|
|
1017
|
+
const double x = org[0] + h[0] * (static_cast<double>(i + (a & 1)) + (axis == 0 ? t : 0.0));
|
|
1018
|
+
const double y = org[1] + h[1] * (static_cast<double>(j + (a >> 1 & 1)) + (axis == 1 ? t : 0.0));
|
|
1019
|
+
const double z = org[2] + h[2] * (static_cast<double>(k + (a >> 2 & 1)) + (axis == 2 ? t : 0.0));
|
|
1020
|
+
id = addVert(x, y, z);
|
|
1021
|
+
}
|
|
1022
|
+
return id;
|
|
1023
|
+
};
|
|
1024
|
+
// Segments start->end, at most two per face.
|
|
1025
|
+
int64_t segFrom[12], segTo[12];
|
|
1026
|
+
int nSeg = 0;
|
|
1027
|
+
for (const auto& f : kFaces) {
|
|
1028
|
+
int64_t cross[4];
|
|
1029
|
+
bool outIn[4];
|
|
1030
|
+
int nc = 0;
|
|
1031
|
+
for (int m = 0; m < 4; ++m) {
|
|
1032
|
+
const int a = f[m], b = f[(m + 1) % 4];
|
|
1033
|
+
if ((v[a] > 0.0) != (v[b] > 0.0)) {
|
|
1034
|
+
cross[nc] = vert(a, b);
|
|
1035
|
+
outIn[nc++] = v[b] > 0.0;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
if (nc == 0) continue;
|
|
1039
|
+
// A saddle joins its inside corners when the face centre
|
|
1040
|
+
// is inside: each out->in crossing then pairs with the
|
|
1041
|
+
// PREVIOUS crossing instead of the next.
|
|
1042
|
+
const bool joined =
|
|
1043
|
+
nc == 4 && v[f[0]] + v[f[1]] + v[f[2]] + v[f[3]] > 0.0;
|
|
1044
|
+
for (int m = 0; m < nc; ++m) {
|
|
1045
|
+
if (!outIn[m]) continue;
|
|
1046
|
+
segFrom[nSeg] = cross[m];
|
|
1047
|
+
segTo[nSeg++] = cross[(m + (joined ? nc - 1 : 1)) % nc];
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
// Link into loops; each vertex starts exactly one segment.
|
|
1051
|
+
bool used[12] = {};
|
|
1052
|
+
for (int s0 = 0; s0 < nSeg; ++s0) {
|
|
1053
|
+
if (used[s0]) continue;
|
|
1054
|
+
int64_t loop[12];
|
|
1055
|
+
int len = 0;
|
|
1056
|
+
for (int s = s0; s >= 0 && !used[s];) {
|
|
1057
|
+
used[s] = true;
|
|
1058
|
+
loop[len++] = segFrom[s];
|
|
1059
|
+
const int64_t want = segTo[s];
|
|
1060
|
+
s = -1;
|
|
1061
|
+
for (int q = 0; q < nSeg; ++q)
|
|
1062
|
+
if (!used[q] && segFrom[q] == want) { s = q; break; }
|
|
1063
|
+
}
|
|
1064
|
+
const auto tri = [&](int64_t a, int64_t b, int64_t c) {
|
|
1065
|
+
mesh.triVerts.insert(mesh.triVerts.end(), {static_cast<uint64_t>(a), static_cast<uint64_t>(b),
|
|
1066
|
+
static_cast<uint64_t>(c)});
|
|
1067
|
+
};
|
|
1068
|
+
const auto at = [&](int64_t id, int axis) {
|
|
1069
|
+
return mesh.vertProperties[static_cast<size_t>(id) * 3 + static_cast<size_t>(axis)];
|
|
1070
|
+
};
|
|
1071
|
+
const auto dist2 = [&](int64_t a, int64_t b) {
|
|
1072
|
+
double s2 = 0;
|
|
1073
|
+
for (int ax = 0; ax < 3; ++ax) s2 += (at(a, ax) - at(b, ax)) * (at(a, ax) - at(b, ax));
|
|
1074
|
+
return s2;
|
|
1075
|
+
};
|
|
1076
|
+
if (len == 3) {
|
|
1077
|
+
tri(loop[0], loop[1], loop[2]);
|
|
1078
|
+
} else if (len == 4) {
|
|
1079
|
+
// Split along the shorter diagonal.
|
|
1080
|
+
if (dist2(loop[0], loop[2]) <= dist2(loop[1], loop[3])) {
|
|
1081
|
+
tri(loop[0], loop[1], loop[2]);
|
|
1082
|
+
tri(loop[0], loop[2], loop[3]);
|
|
1083
|
+
} else {
|
|
1084
|
+
tri(loop[1], loop[2], loop[3]);
|
|
1085
|
+
tri(loop[1], loop[3], loop[0]);
|
|
1086
|
+
}
|
|
1087
|
+
} else if (len > 4) {
|
|
1088
|
+
// A fan from one corner of a 5-7 sided, non-planar
|
|
1089
|
+
// loop can fold; a fan from its centroid cannot.
|
|
1090
|
+
double cx = 0, cy = 0, cz = 0;
|
|
1091
|
+
for (int m = 0; m < len; ++m) {
|
|
1092
|
+
cx += at(loop[m], 0);
|
|
1093
|
+
cy += at(loop[m], 1);
|
|
1094
|
+
cz += at(loop[m], 2);
|
|
1095
|
+
}
|
|
1096
|
+
const int64_t c = addVert(cx / len, cy / len, cz / len);
|
|
1097
|
+
for (int m = 0; m < len; ++m) tri(loop[m], loop[(m + 1) % len], c);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return mesh;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
893
1106
|
CSGParams resolveLevelSet(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
|
|
894
1107
|
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
895
1108
|
CSGParams params;
|
|
@@ -934,12 +1147,20 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
934
1147
|
// matches how the caller already has to think about the box.
|
|
935
1148
|
const ListPtr* bb = std::get_if<ListPtr>(¶ms.at("bounds"));
|
|
936
1149
|
std::optional<std::vector<double>> bLo, bHi;
|
|
1150
|
+
const bool noBounds = std::holds_alternative<std::monostate>(params.at("bounds"));
|
|
937
1151
|
if (bb && *bb && (*bb)->items.size() == 2) {
|
|
938
1152
|
bLo = numbersOrInf((*bb)->items[0]);
|
|
939
1153
|
bHi = numbersOrInf((*bb)->items[1]);
|
|
1154
|
+
} else if (noBounds && !fieldFn) {
|
|
1155
|
+
if (const auto inferred = boundsFromEdge(fieldArg, params.at("edge"))) {
|
|
1156
|
+
bLo = inferred->first;
|
|
1157
|
+
bHi = inferred->second;
|
|
1158
|
+
}
|
|
940
1159
|
}
|
|
941
1160
|
if (!bLo || !bHi || bLo->size() != bHi->size() || (bLo->size() != 2 && bLo->size() != 3)) {
|
|
942
|
-
ev.warn(
|
|
1161
|
+
ev.warn(noBounds && !fieldFn
|
|
1162
|
+
? "levelset(): a field array needs bounds=, or edge= (its sample spacing) to place it"
|
|
1163
|
+
: "levelset(): bounds must be [[x0,y0],[x1,y1]] or [[x0,y0,z0],[x1,y1,z1]]",
|
|
943
1164
|
&node.position());
|
|
944
1165
|
return {};
|
|
945
1166
|
}
|
|
@@ -1037,75 +1258,82 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1037
1258
|
|
|
1038
1259
|
// ---- 2D: sample onto a padded grid, contour it, hand to CrossSection.
|
|
1039
1260
|
if (is2d) {
|
|
1040
|
-
size_t nx = 0, ny = 0;
|
|
1041
|
-
double sx = 0, sy = 0;
|
|
1042
1261
|
if (field) {
|
|
1043
1262
|
ev.warn("levelset(): a 2D field must be field[i][j]; a 3D array was given",
|
|
1044
1263
|
&node.position());
|
|
1045
1264
|
return {};
|
|
1046
1265
|
}
|
|
1266
|
+
const double x0 = (*bLo)[0], y0 = (*bLo)[1];
|
|
1267
|
+
const double wx = (*bHi)[0] - x0, wy = (*bHi)[1] - y0;
|
|
1268
|
+
double ex = edgeVec ? edgeV[0] : edge, ey = edgeVec ? edgeV[1] : edge;
|
|
1269
|
+
std::optional<std::vector<std::vector<double>>> plane;
|
|
1270
|
+
size_t px = 0, py = 0;
|
|
1271
|
+
bool ownSamples = false;
|
|
1047
1272
|
if (!fieldFn) {
|
|
1048
|
-
|
|
1273
|
+
plane = readPlane(fieldArg);
|
|
1049
1274
|
if (!plane || plane->size() < 2 || (*plane)[0].size() < 2) {
|
|
1050
1275
|
ev.warn("levelset(): 2D field must be a rectangular field[i][j] of numbers",
|
|
1051
1276
|
&node.position());
|
|
1052
1277
|
return {};
|
|
1053
1278
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
g.dx = sx;
|
|
1062
|
-
g.dy = sy;
|
|
1063
|
-
g.ox = (*bLo)[0] - sx;
|
|
1064
|
-
g.oy = (*bLo)[1] - sy;
|
|
1065
|
-
// Padded with a ring that is firmly OUTSIDE, so a contour meeting
|
|
1066
|
-
// the box edge closes along it instead of running off. This is
|
|
1067
|
-
// BOSL2's closed=true, and the only behaviour offered.
|
|
1068
|
-
g.v.assign(g.nx * g.ny, -1.0);
|
|
1069
|
-
for (size_t i = 0; i < nx; ++i)
|
|
1070
|
-
for (size_t j = 0; j < ny; ++j)
|
|
1071
|
-
g.v[(j + 1) * g.nx + (i + 1)] = bandDistance((*plane)[i][j], isoLo, isoHi, invert);
|
|
1072
|
-
const manifold::Polygons polys = marchingSquares(g);
|
|
1073
|
-
if (polys.empty()) return {};
|
|
1074
|
-
ColoredBody b;
|
|
1075
|
-
b.section = clipToBounds(manifold::CrossSection(polys, manifold::CrossSection::FillRule::EvenOdd),
|
|
1076
|
-
*bLo, *bHi);
|
|
1077
|
-
if (b.section->IsEmpty()) return {};
|
|
1078
|
-
std::vector<ColoredBody> out;
|
|
1079
|
-
out.push_back(std::move(b));
|
|
1080
|
-
return out;
|
|
1279
|
+
px = plane->size();
|
|
1280
|
+
py = (*plane)[0].size();
|
|
1281
|
+
// Contoured on its own samples unless edge= asks for another
|
|
1282
|
+
// spacing, as the 3D grid is marched; then re-sampled bilinearly.
|
|
1283
|
+
const double gx = wx / static_cast<double>(px - 1), gy = wy / static_cast<double>(py - 1);
|
|
1284
|
+
const auto same = [](double a, double b) { return std::abs(a - b) <= 1e-9 * std::max(a, b); };
|
|
1285
|
+
ownSamples = ex <= 0.0 || (same(ex, gx) && same(ey, gy));
|
|
1081
1286
|
}
|
|
1082
|
-
//
|
|
1083
|
-
|
|
1084
|
-
nx = static_cast<size_t>(std::floor(
|
|
1085
|
-
ny = static_cast<size_t>(std::floor(
|
|
1287
|
+
// Counted from the array, not floor(width / spacing): rounding there
|
|
1288
|
+
// could drop the last sample row.
|
|
1289
|
+
const size_t nx = ownSamples ? px : static_cast<size_t>(std::floor(wx / ex)) + 1;
|
|
1290
|
+
const size_t ny = ownSamples ? py : static_cast<size_t>(std::floor(wy / ey)) + 1;
|
|
1086
1291
|
if (nx < 2 || ny < 2) {
|
|
1087
1292
|
ev.warn("levelset(): edge is larger than the bounds", &node.position());
|
|
1088
1293
|
return {};
|
|
1089
1294
|
}
|
|
1090
|
-
sx = (
|
|
1091
|
-
|
|
1295
|
+
const double sx = wx / static_cast<double>(nx - 1), sy = wy / static_cast<double>(ny - 1);
|
|
1296
|
+
// The plane at a point between its samples. Clamped at the edges,
|
|
1297
|
+
// like the 3D grid sampler, so the clip below cuts a clean line.
|
|
1298
|
+
const auto bilinear = [&](double x, double y) {
|
|
1299
|
+
const auto cell = [](double u, size_t n, size_t& i0) {
|
|
1300
|
+
i0 = static_cast<size_t>(std::clamp(std::floor(u), 0.0, static_cast<double>(n - 2)));
|
|
1301
|
+
return std::clamp(u - static_cast<double>(i0), 0.0, 1.0);
|
|
1302
|
+
};
|
|
1303
|
+
size_t i, j;
|
|
1304
|
+
const double tx = cell((x - x0) / wx * static_cast<double>(px - 1), px, i);
|
|
1305
|
+
const double ty = cell((y - y0) / wy * static_cast<double>(py - 1), py, j);
|
|
1306
|
+
const auto& p = *plane;
|
|
1307
|
+
return (1 - tx) * ((1 - ty) * p[i][j] + ty * p[i][j + 1])
|
|
1308
|
+
+ tx * ((1 - ty) * p[i + 1][j] + ty * p[i + 1][j + 1]);
|
|
1309
|
+
};
|
|
1092
1310
|
Contours2d g;
|
|
1093
1311
|
g.nx = nx + 2;
|
|
1094
1312
|
g.ny = ny + 2;
|
|
1095
1313
|
g.dx = sx;
|
|
1096
1314
|
g.dy = sy;
|
|
1097
|
-
g.ox =
|
|
1098
|
-
g.oy =
|
|
1315
|
+
g.ox = x0 - sx;
|
|
1316
|
+
g.oy = y0 - sy;
|
|
1317
|
+
// Padded with a ring that is firmly OUTSIDE, so a contour meeting
|
|
1318
|
+
// the box edge closes along it instead of running off. This is
|
|
1319
|
+
// BOSL2's closed=true, and the only behaviour offered.
|
|
1099
1320
|
g.v.assign(g.nx * g.ny, -1.0);
|
|
1100
1321
|
for (size_t i = 0; i < nx; ++i) {
|
|
1101
1322
|
for (size_t j = 0; j < ny; ++j) {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1323
|
+
const double x = x0 + static_cast<double>(i) * sx, y = y0 + static_cast<double>(j) * sy;
|
|
1324
|
+
double val;
|
|
1325
|
+
if (ownSamples) {
|
|
1326
|
+
val = (*plane)[i][j];
|
|
1327
|
+
} else if (plane) {
|
|
1328
|
+
val = bilinear(x, y);
|
|
1329
|
+
} else {
|
|
1330
|
+
BoundArgs bound;
|
|
1331
|
+
bound.set(fnParams2[0], Value{x});
|
|
1332
|
+
bound.set(fnParams2[1], Value{y});
|
|
1333
|
+
const Value outv =
|
|
1334
|
+
ev.evalFunctionLiteralFromBound(**fieldFn, std::move(bound), *fnCtx, &node.position());
|
|
1335
|
+
val = fieldSample(outv).value_or(std::numeric_limits<double>::max());
|
|
1336
|
+
}
|
|
1109
1337
|
g.v[(j + 1) * g.nx + (i + 1)] = bandDistance(val, isoLo, isoHi, invert);
|
|
1110
1338
|
}
|
|
1111
1339
|
}
|
|
@@ -1121,6 +1349,7 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1121
1349
|
}
|
|
1122
1350
|
|
|
1123
1351
|
|
|
1352
|
+
bool marchGrid = false;
|
|
1124
1353
|
if (!fieldFn) {
|
|
1125
1354
|
field = readField(fieldArg);
|
|
1126
1355
|
if (!field) {
|
|
@@ -1135,6 +1364,15 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1135
1364
|
spacing = {((*bHi)[0] - (*bLo)[0]) / static_cast<double>(field->nx - 1),
|
|
1136
1365
|
((*bHi)[1] - (*bLo)[1]) / static_cast<double>(field->ny - 1),
|
|
1137
1366
|
((*bHi)[2] - (*bLo)[2]) / static_cast<double>(field->nz - 1)};
|
|
1367
|
+
// Marched on the grid itself unless edge= asks for a different
|
|
1368
|
+
// spacing. A 1e-9 relative slack so an edge typed as the spacing,
|
|
1369
|
+
// or computed from the same bounds, still counts as it.
|
|
1370
|
+
const auto same = [](double a, double b) { return std::abs(a - b) <= 1e-9 * std::max(a, b); };
|
|
1371
|
+
marchGrid = edge <= 0.0;
|
|
1372
|
+
if (!marchGrid) {
|
|
1373
|
+
marchGrid = true;
|
|
1374
|
+
for (int a = 0; a < 3; ++a) marchGrid = marchGrid && same(edgeVec ? edgeV[a] : edge, spacing[a]);
|
|
1375
|
+
}
|
|
1138
1376
|
// Also when edge= was given but unusable (warned above): edge 0
|
|
1139
1377
|
// would ask Manifold for an infinitely fine lattice.
|
|
1140
1378
|
if (edge <= 0.0) edge = std::min({spacing[0], spacing[1], spacing[2]});
|
|
@@ -1151,10 +1389,9 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1151
1389
|
const double xyz[3] = {p.x, p.y, p.z};
|
|
1152
1390
|
for (int a = 0; a < 3; ++a) bound.set(fnParams2[static_cast<size_t>(a)], Value{xyz[a]});
|
|
1153
1391
|
const Value out = ev.evalFunctionLiteralFromBound(**fieldFn, std::move(bound), *fnCtx, &node.position());
|
|
1154
|
-
const double* d = std::get_if<double>(&out);
|
|
1155
1392
|
// A field that returns junk at one point must not abort the whole
|
|
1156
|
-
// build;
|
|
1157
|
-
const double v = (
|
|
1393
|
+
// build; it becomes a huge value instead. +-INF keeps its sign.
|
|
1394
|
+
const double v = fieldSample(out).value_or(std::numeric_limits<double>::max());
|
|
1158
1395
|
return bandDistance(v, isoLo, isoHi, invert);
|
|
1159
1396
|
};
|
|
1160
1397
|
|
|
@@ -1178,18 +1415,13 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1178
1415
|
if (i0[a] > n[a] - 2) i0[a] = n[a] - 2;
|
|
1179
1416
|
t[a] = g[a] - static_cast<double>(i0[a]);
|
|
1180
1417
|
}
|
|
1181
|
-
// Outside the sampled block the
|
|
1182
|
-
//
|
|
1183
|
-
//
|
|
1184
|
-
//
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
const double raw = (pos[a] - origin[a]) / spacing[a];
|
|
1189
|
-
if (raw < 0.0) outside = std::max(outside, -raw);
|
|
1190
|
-
else if (raw > maxg) outside = std::max(outside, raw - maxg);
|
|
1191
|
-
}
|
|
1192
|
-
if (outside > 0.0) return invert ? (1.0 + outside) : -(1.0 + outside);
|
|
1418
|
+
// Outside the sampled block the clamping above extends the boundary
|
|
1419
|
+
// values outward, so a surface meeting the box runs straight on into
|
|
1420
|
+
// the padding and the clip to `bounds` below leaves a clean plane.
|
|
1421
|
+
// Answering "outside" there instead (as this once did) made Manifold
|
|
1422
|
+
// close the surface right AT the box face, along its own lattice: a
|
|
1423
|
+
// waffle-textured staircase INSIDE the bounds, which the clip could
|
|
1424
|
+
// not remove (#745 on BelfrySCAD).
|
|
1193
1425
|
|
|
1194
1426
|
double acc = 0.0;
|
|
1195
1427
|
for (int c = 0; c < 8; ++c) {
|
|
@@ -1214,11 +1446,15 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1214
1446
|
const manifold::vec3 pad = 2.0 * E;
|
|
1215
1447
|
manifold::Box bounds(manifold::vec3(origin[0], origin[1], origin[2]) - pad,
|
|
1216
1448
|
manifold::vec3((*hi)[0], (*hi)[1], (*hi)[2]) + pad);
|
|
1217
|
-
// tolerance
|
|
1218
|
-
//
|
|
1219
|
-
//
|
|
1220
|
-
//
|
|
1221
|
-
//
|
|
1449
|
+
// tolerance: Manifold root-finds along each crossing lattice edge until
|
|
1450
|
+
// the bracket is within it (a distance along the edge, so independent of
|
|
1451
|
+
// the field's units), instead of interpolating linearly between the two
|
|
1452
|
+
// lattice samples. 1% of the edge. Measured on #745 on BelfrySCAD: the
|
|
1453
|
+
// function form's vertices go from up to 0.2 off the true surface to on
|
|
1454
|
+
// it, and the mean face-normal error on a per-axis edge halves to
|
|
1455
|
+
// BOSL2's own; a grid gains too, since the trilinear field between
|
|
1456
|
+
// samples is not linear along a lattice edge. Costs 11-20% more work.
|
|
1457
|
+
//
|
|
1222
1458
|
// canParallel is the crux: safe for a grid because the lambda is pure
|
|
1223
1459
|
// C++, and NOT safe for a function, which re-enters the evaluator.
|
|
1224
1460
|
// Manifold: parallel policies "will crash language runtimes with runtime
|
|
@@ -1226,19 +1462,32 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1226
1462
|
//
|
|
1227
1463
|
// LevelSet takes a single edge length, so a per-axis edge meshes a field
|
|
1228
1464
|
// squashed to unit spacing on every axis and stretches the result back.
|
|
1229
|
-
//
|
|
1230
|
-
|
|
1231
|
-
// with an axis scale.
|
|
1465
|
+
// The tolerance there is 1% of the unit edge, so 1% of each axis's own.
|
|
1466
|
+
const double tol = 0.01 * (edgeVec ? 1.0 : edge);
|
|
1232
1467
|
manifold::Manifold solid;
|
|
1233
|
-
if (
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1468
|
+
if (marchGrid) {
|
|
1469
|
+
const size_t n[3] = {field->nx + 2, field->ny + 2, field->nz + 2};
|
|
1470
|
+
std::vector<double> d(n[0] * n[1] * n[2], -1.0); // the "outside" ring
|
|
1471
|
+
for (size_t k = 0; k < field->nz; ++k)
|
|
1472
|
+
for (size_t j = 0; j < field->ny; ++j)
|
|
1473
|
+
for (size_t i = 0; i < field->nx; ++i)
|
|
1474
|
+
d[((k + 1) * n[1] + (j + 1)) * n[0] + (i + 1)] =
|
|
1475
|
+
bandDistance(field->at(i, j, k), isoLo, isoHi, invert);
|
|
1476
|
+
const std::array<double, 3> org{origin[0] - spacing[0], origin[1] - spacing[1], origin[2] - spacing[2]};
|
|
1477
|
+
solid = manifold::Manifold(marchingCubes(d, n, org, spacing));
|
|
1478
|
+
// Never expected; but a mesh Manifold refuses must not cost the
|
|
1479
|
+
// user their shape, so fall back to resampling it.
|
|
1480
|
+
if (solid.Status() != manifold::Manifold::Error::NoError) marchGrid = false;
|
|
1481
|
+
}
|
|
1482
|
+
if (!marchGrid && !edgeVec) {
|
|
1483
|
+
solid = fieldFn ? manifold::Manifold::LevelSet(sampleFn, bounds, edge, 0.0, tol, /*canParallel=*/false)
|
|
1484
|
+
: manifold::Manifold::LevelSet(sampleGrid, bounds, edge, 0.0, tol, /*canParallel=*/true);
|
|
1485
|
+
} else if (!marchGrid) {
|
|
1237
1486
|
const manifold::Box unit(bounds.min / E, bounds.max / E);
|
|
1238
1487
|
solid = fieldFn ? manifold::Manifold::LevelSet([&](manifold::vec3 q) { return sampleFn(q * E); },
|
|
1239
|
-
unit, 1.0, 0.0,
|
|
1488
|
+
unit, 1.0, 0.0, tol, /*canParallel=*/false)
|
|
1240
1489
|
: manifold::Manifold::LevelSet([&](manifold::vec3 q) { return sampleGrid(q * E); },
|
|
1241
|
-
unit, 1.0, 0.0,
|
|
1490
|
+
unit, 1.0, 0.0, tol, /*canParallel=*/true);
|
|
1242
1491
|
solid = solid.Scale(E);
|
|
1243
1492
|
}
|
|
1244
1493
|
|