openscad-cpp-evaluator 1.46.0__tar.gz → 1.47.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.47.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/topology.cpp +234 -40
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_booleans.cpp +208 -8
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/uv.lock +1 -1
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/cmake/patch_manifold.cmake +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/LICENSE +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.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.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.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.47.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.47.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.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/.DS_Store +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/import/scene_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/xkcd_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/xkcd_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_discretizer.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_openscad_parity.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_profile_time.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_undef_args.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/tools/cli/main.cpp +0 -0
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
#include <manifold/polygon.h>
|
|
8
8
|
|
|
9
|
+
#include <algorithm>
|
|
10
|
+
|
|
9
11
|
namespace oscadeval {
|
|
10
12
|
|
|
11
13
|
// simplify()'s default tolerance, as a fraction of the body's bounding-box
|
|
@@ -663,14 +665,30 @@ std::vector<ColoredBody> generateMeshRepair(Evaluator& ev, const CSGParams& para
|
|
|
663
665
|
// The trade is memory: ~67 bytes per cell, so 200^3 is about 550 MB and that
|
|
664
666
|
// is the practical ceiling. A callback samples lazily and has no ceiling.
|
|
665
667
|
//
|
|
666
|
-
// Accuracy is bounded by the GRID, not by Manifold.
|
|
667
|
-
//
|
|
668
|
-
//
|
|
669
|
-
//
|
|
670
|
-
//
|
|
668
|
+
// Accuracy is bounded by the GRID, not by Manifold. At the grid's own
|
|
669
|
+
// spacing (the default) the grid is marched directly, as BOSL2's
|
|
670
|
+
// isosurface() does -- see marchingCubes(). An edge= asking for a different
|
|
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.
|
|
671
674
|
|
|
672
675
|
namespace {
|
|
673
676
|
|
|
677
|
+
// A sample as the field arrays and field functions hand it over. +-INF is a
|
|
678
|
+
// legal sample -- a metaball field is 1/0 wherever a grid point lands on a
|
|
679
|
+
// centre (#744 on BelfrySCAD) -- but it cannot be used as is: the trilinear
|
|
680
|
+
// sampler weights corners, and 0 * INF is NaN. So it becomes a huge FINITE
|
|
681
|
+
// value, which is the limit BOSL2's isosurface() takes (an edge to an INF
|
|
682
|
+
// corner crosses at the other end). A quarter of DBL_MAX so that sums of
|
|
683
|
+
// weighted corners and bandDistance's subtraction cannot overflow. NaN, or
|
|
684
|
+
// anything not a number, has no meaning and is nullopt.
|
|
685
|
+
std::optional<double> fieldSample(const Value& item) {
|
|
686
|
+
const double* d = std::get_if<double>(&item);
|
|
687
|
+
if (!d || std::isnan(*d)) return std::nullopt;
|
|
688
|
+
constexpr double kHuge = std::numeric_limits<double>::max() / 4;
|
|
689
|
+
return std::clamp(*d, -kHuge, kHuge);
|
|
690
|
+
}
|
|
691
|
+
|
|
674
692
|
struct ScalarField {
|
|
675
693
|
std::vector<double> v; // flat, x fastest
|
|
676
694
|
size_t nx = 0, ny = 0, nz = 0;
|
|
@@ -698,8 +716,8 @@ std::optional<ScalarField> readField(const Value& val) {
|
|
|
698
716
|
return std::nullopt;
|
|
699
717
|
}
|
|
700
718
|
for (size_t k = 0; k < f.nz; ++k) {
|
|
701
|
-
const double
|
|
702
|
-
if (!d
|
|
719
|
+
const std::optional<double> d = fieldSample((*zs)->items[k]);
|
|
720
|
+
if (!d) return std::nullopt;
|
|
703
721
|
f.v[(k * f.ny + j) * f.nx + i] = *d;
|
|
704
722
|
}
|
|
705
723
|
}
|
|
@@ -722,8 +740,8 @@ std::optional<std::vector<std::vector<double>>> readPlane(const Value& val) {
|
|
|
722
740
|
std::vector<double> row;
|
|
723
741
|
row.reserve(ny);
|
|
724
742
|
for (const Value& item : (*ys)->items) {
|
|
725
|
-
const double
|
|
726
|
-
if (!d
|
|
743
|
+
const std::optional<double> d = fieldSample(item);
|
|
744
|
+
if (!d) return std::nullopt;
|
|
727
745
|
row.push_back(*d);
|
|
728
746
|
}
|
|
729
747
|
out.push_back(std::move(row));
|
|
@@ -890,6 +908,162 @@ manifold::Polygons marchingSquares(const Contours2d& g) {
|
|
|
890
908
|
}
|
|
891
909
|
|
|
892
910
|
|
|
911
|
+
// -- 3D: marching cubes on the grid itself -----------------------------------
|
|
912
|
+
//
|
|
913
|
+
// For a grid sampled at the spacing it was given, Manifold's LevelSet is the
|
|
914
|
+
// wrong tool: it samples its own body-centred lattice, which does not line up
|
|
915
|
+
// with the grid, so it meshes a trilinear blend of the data rather than the
|
|
916
|
+
// data. Measured on #745 on BelfrySCAD, that left a metaball field visibly
|
|
917
|
+
// more faceted than BOSL2's isosurface(), which marches the grid itself. So
|
|
918
|
+
// this does too.
|
|
919
|
+
//
|
|
920
|
+
// No 256-case table. The table's hard part is the ambiguous face (two
|
|
921
|
+
// inside corners on a diagonal), and a table that resolves it per CELL can
|
|
922
|
+
// disagree with the neighbouring cell and leave a crack -- fatal here, since
|
|
923
|
+
// Manifold needs a closed mesh. So the triangles are built per FACE instead:
|
|
924
|
+
// each face's crossings are paired into segments by a rule that reads only
|
|
925
|
+
// that face's four values (the face-centre average decides a saddle, as the
|
|
926
|
+
// 2D path does), so the two cells sharing a face always agree. A cell's
|
|
927
|
+
// segments then link into closed loops, one polygon each. Vertices are keyed
|
|
928
|
+
// on GRID EDGE identity, so neighbours share them by construction, exactly as
|
|
929
|
+
// marchingSquares keys on edges rather than coordinates.
|
|
930
|
+
//
|
|
931
|
+
// Orientation: walking a face's corners anticlockwise as seen from outside
|
|
932
|
+
// the cell, a segment runs from an outside->inside crossing to an
|
|
933
|
+
// inside->outside one. A loop of such segments, read in order, is a polygon
|
|
934
|
+
// whose normal points from inside to outside -- Manifold's convention, with
|
|
935
|
+
// inside the positive side.
|
|
936
|
+
//
|
|
937
|
+
// The field arrives as band distance (positive inside) with a ring of
|
|
938
|
+
// "outside" round it, like the 2D path, so every surface closes; the caller
|
|
939
|
+
// clips the overhang back to the bounds.
|
|
940
|
+
manifold::MeshGL64 marchingCubes(const std::vector<double>& d, const size_t n[3],
|
|
941
|
+
const std::array<double, 3>& org, const std::array<double, 3>& h) {
|
|
942
|
+
const auto P = [&](size_t i, size_t j, size_t k) { return (k * n[1] + j) * n[0] + i; };
|
|
943
|
+
// corner c of a cell is offset (c&1, c>>1&1, c>>2&1). Each face's
|
|
944
|
+
// corners, anticlockwise as seen from outside.
|
|
945
|
+
static constexpr int kFaces[6][4] = {
|
|
946
|
+
{0, 2, 3, 1}, {4, 5, 7, 6}, // -z, +z
|
|
947
|
+
{0, 4, 6, 2}, {1, 3, 7, 5}, // -x, +x
|
|
948
|
+
{0, 1, 5, 4}, {2, 6, 7, 3}, // -y, +y
|
|
949
|
+
};
|
|
950
|
+
manifold::MeshGL64 mesh;
|
|
951
|
+
mesh.numProp = 3;
|
|
952
|
+
// ponytail: one int per grid edge, ~12 bytes a sample next to the
|
|
953
|
+
// field's own ~67; key the vertices per slab if this ever binds.
|
|
954
|
+
std::vector<int64_t> edgeVert(3 * n[0] * n[1] * n[2], -1);
|
|
955
|
+
const auto addVert = [&](double x, double y, double z) {
|
|
956
|
+
mesh.vertProperties.insert(mesh.vertProperties.end(), {x, y, z});
|
|
957
|
+
return static_cast<int64_t>(mesh.vertProperties.size() / 3 - 1);
|
|
958
|
+
};
|
|
959
|
+
for (size_t k = 0; k + 1 < n[2]; ++k) {
|
|
960
|
+
for (size_t j = 0; j + 1 < n[1]; ++j) {
|
|
961
|
+
for (size_t i = 0; i + 1 < n[0]; ++i) {
|
|
962
|
+
double v[8];
|
|
963
|
+
size_t p[8];
|
|
964
|
+
int inside = 0;
|
|
965
|
+
for (int c = 0; c < 8; ++c) {
|
|
966
|
+
p[c] = P(i + (c & 1), j + (c >> 1 & 1), k + (c >> 2 & 1));
|
|
967
|
+
v[c] = d[p[c]];
|
|
968
|
+
inside += v[c] > 0.0;
|
|
969
|
+
}
|
|
970
|
+
if (inside == 0 || inside == 8) continue;
|
|
971
|
+
// The vertex on the edge between corners a and b.
|
|
972
|
+
const auto vert = [&](int a, int b) -> int64_t {
|
|
973
|
+
if (a > b) std::swap(a, b); // a is the lower end
|
|
974
|
+
const int axis = (b - a) == 1 ? 0 : (b - a) == 2 ? 1 : 2;
|
|
975
|
+
int64_t& id = edgeVert[3 * p[a] + static_cast<size_t>(axis)];
|
|
976
|
+
if (id < 0) {
|
|
977
|
+
const double t = v[a] / (v[a] - v[b]);
|
|
978
|
+
const double x = org[0] + h[0] * (static_cast<double>(i + (a & 1)) + (axis == 0 ? t : 0.0));
|
|
979
|
+
const double y = org[1] + h[1] * (static_cast<double>(j + (a >> 1 & 1)) + (axis == 1 ? t : 0.0));
|
|
980
|
+
const double z = org[2] + h[2] * (static_cast<double>(k + (a >> 2 & 1)) + (axis == 2 ? t : 0.0));
|
|
981
|
+
id = addVert(x, y, z);
|
|
982
|
+
}
|
|
983
|
+
return id;
|
|
984
|
+
};
|
|
985
|
+
// Segments start->end, at most two per face.
|
|
986
|
+
int64_t segFrom[12], segTo[12];
|
|
987
|
+
int nSeg = 0;
|
|
988
|
+
for (const auto& f : kFaces) {
|
|
989
|
+
int64_t cross[4];
|
|
990
|
+
bool outIn[4];
|
|
991
|
+
int nc = 0;
|
|
992
|
+
for (int m = 0; m < 4; ++m) {
|
|
993
|
+
const int a = f[m], b = f[(m + 1) % 4];
|
|
994
|
+
if ((v[a] > 0.0) != (v[b] > 0.0)) {
|
|
995
|
+
cross[nc] = vert(a, b);
|
|
996
|
+
outIn[nc++] = v[b] > 0.0;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
if (nc == 0) continue;
|
|
1000
|
+
// A saddle joins its inside corners when the face centre
|
|
1001
|
+
// is inside: each out->in crossing then pairs with the
|
|
1002
|
+
// PREVIOUS crossing instead of the next.
|
|
1003
|
+
const bool joined =
|
|
1004
|
+
nc == 4 && v[f[0]] + v[f[1]] + v[f[2]] + v[f[3]] > 0.0;
|
|
1005
|
+
for (int m = 0; m < nc; ++m) {
|
|
1006
|
+
if (!outIn[m]) continue;
|
|
1007
|
+
segFrom[nSeg] = cross[m];
|
|
1008
|
+
segTo[nSeg++] = cross[(m + (joined ? nc - 1 : 1)) % nc];
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
// Link into loops; each vertex starts exactly one segment.
|
|
1012
|
+
bool used[12] = {};
|
|
1013
|
+
for (int s0 = 0; s0 < nSeg; ++s0) {
|
|
1014
|
+
if (used[s0]) continue;
|
|
1015
|
+
int64_t loop[12];
|
|
1016
|
+
int len = 0;
|
|
1017
|
+
for (int s = s0; s >= 0 && !used[s];) {
|
|
1018
|
+
used[s] = true;
|
|
1019
|
+
loop[len++] = segFrom[s];
|
|
1020
|
+
const int64_t want = segTo[s];
|
|
1021
|
+
s = -1;
|
|
1022
|
+
for (int q = 0; q < nSeg; ++q)
|
|
1023
|
+
if (!used[q] && segFrom[q] == want) { s = q; break; }
|
|
1024
|
+
}
|
|
1025
|
+
const auto tri = [&](int64_t a, int64_t b, int64_t c) {
|
|
1026
|
+
mesh.triVerts.insert(mesh.triVerts.end(), {static_cast<uint64_t>(a), static_cast<uint64_t>(b),
|
|
1027
|
+
static_cast<uint64_t>(c)});
|
|
1028
|
+
};
|
|
1029
|
+
const auto at = [&](int64_t id, int axis) {
|
|
1030
|
+
return mesh.vertProperties[static_cast<size_t>(id) * 3 + static_cast<size_t>(axis)];
|
|
1031
|
+
};
|
|
1032
|
+
const auto dist2 = [&](int64_t a, int64_t b) {
|
|
1033
|
+
double s2 = 0;
|
|
1034
|
+
for (int ax = 0; ax < 3; ++ax) s2 += (at(a, ax) - at(b, ax)) * (at(a, ax) - at(b, ax));
|
|
1035
|
+
return s2;
|
|
1036
|
+
};
|
|
1037
|
+
if (len == 3) {
|
|
1038
|
+
tri(loop[0], loop[1], loop[2]);
|
|
1039
|
+
} else if (len == 4) {
|
|
1040
|
+
// Split along the shorter diagonal.
|
|
1041
|
+
if (dist2(loop[0], loop[2]) <= dist2(loop[1], loop[3])) {
|
|
1042
|
+
tri(loop[0], loop[1], loop[2]);
|
|
1043
|
+
tri(loop[0], loop[2], loop[3]);
|
|
1044
|
+
} else {
|
|
1045
|
+
tri(loop[1], loop[2], loop[3]);
|
|
1046
|
+
tri(loop[1], loop[3], loop[0]);
|
|
1047
|
+
}
|
|
1048
|
+
} else if (len > 4) {
|
|
1049
|
+
// A fan from one corner of a 5-7 sided, non-planar
|
|
1050
|
+
// loop can fold; a fan from its centroid cannot.
|
|
1051
|
+
double cx = 0, cy = 0, cz = 0;
|
|
1052
|
+
for (int m = 0; m < len; ++m) {
|
|
1053
|
+
cx += at(loop[m], 0);
|
|
1054
|
+
cy += at(loop[m], 1);
|
|
1055
|
+
cz += at(loop[m], 2);
|
|
1056
|
+
}
|
|
1057
|
+
const int64_t c = addVert(cx / len, cy / len, cz / len);
|
|
1058
|
+
for (int m = 0; m < len; ++m) tri(loop[m], loop[(m + 1) % len], c);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return mesh;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
893
1067
|
CSGParams resolveLevelSet(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
|
|
894
1068
|
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
895
1069
|
CSGParams params;
|
|
@@ -1104,8 +1278,7 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1104
1278
|
bound.set(fnParams2[1], Value{(*bLo)[1] + static_cast<double>(j) * sy});
|
|
1105
1279
|
const Value outv =
|
|
1106
1280
|
ev.evalFunctionLiteralFromBound(**fieldFn, std::move(bound), *fnCtx, &node.position());
|
|
1107
|
-
const double
|
|
1108
|
-
const double val = (d && std::isfinite(*d)) ? *d : std::numeric_limits<double>::max();
|
|
1281
|
+
const double val = fieldSample(outv).value_or(std::numeric_limits<double>::max());
|
|
1109
1282
|
g.v[(j + 1) * g.nx + (i + 1)] = bandDistance(val, isoLo, isoHi, invert);
|
|
1110
1283
|
}
|
|
1111
1284
|
}
|
|
@@ -1121,6 +1294,7 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1121
1294
|
}
|
|
1122
1295
|
|
|
1123
1296
|
|
|
1297
|
+
bool marchGrid = false;
|
|
1124
1298
|
if (!fieldFn) {
|
|
1125
1299
|
field = readField(fieldArg);
|
|
1126
1300
|
if (!field) {
|
|
@@ -1135,6 +1309,15 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1135
1309
|
spacing = {((*bHi)[0] - (*bLo)[0]) / static_cast<double>(field->nx - 1),
|
|
1136
1310
|
((*bHi)[1] - (*bLo)[1]) / static_cast<double>(field->ny - 1),
|
|
1137
1311
|
((*bHi)[2] - (*bLo)[2]) / static_cast<double>(field->nz - 1)};
|
|
1312
|
+
// Marched on the grid itself unless edge= asks for a different
|
|
1313
|
+
// spacing. A 1e-9 relative slack so an edge typed as the spacing,
|
|
1314
|
+
// or computed from the same bounds, still counts as it.
|
|
1315
|
+
const auto same = [](double a, double b) { return std::abs(a - b) <= 1e-9 * std::max(a, b); };
|
|
1316
|
+
marchGrid = edge <= 0.0;
|
|
1317
|
+
if (!marchGrid) {
|
|
1318
|
+
marchGrid = true;
|
|
1319
|
+
for (int a = 0; a < 3; ++a) marchGrid = marchGrid && same(edgeVec ? edgeV[a] : edge, spacing[a]);
|
|
1320
|
+
}
|
|
1138
1321
|
// Also when edge= was given but unusable (warned above): edge 0
|
|
1139
1322
|
// would ask Manifold for an infinitely fine lattice.
|
|
1140
1323
|
if (edge <= 0.0) edge = std::min({spacing[0], spacing[1], spacing[2]});
|
|
@@ -1151,10 +1334,9 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1151
1334
|
const double xyz[3] = {p.x, p.y, p.z};
|
|
1152
1335
|
for (int a = 0; a < 3; ++a) bound.set(fnParams2[static_cast<size_t>(a)], Value{xyz[a]});
|
|
1153
1336
|
const Value out = ev.evalFunctionLiteralFromBound(**fieldFn, std::move(bound), *fnCtx, &node.position());
|
|
1154
|
-
const double* d = std::get_if<double>(&out);
|
|
1155
1337
|
// A field that returns junk at one point must not abort the whole
|
|
1156
|
-
// build;
|
|
1157
|
-
const double v = (
|
|
1338
|
+
// build; it becomes a huge value instead. +-INF keeps its sign.
|
|
1339
|
+
const double v = fieldSample(out).value_or(std::numeric_limits<double>::max());
|
|
1158
1340
|
return bandDistance(v, isoLo, isoHi, invert);
|
|
1159
1341
|
};
|
|
1160
1342
|
|
|
@@ -1178,18 +1360,13 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1178
1360
|
if (i0[a] > n[a] - 2) i0[a] = n[a] - 2;
|
|
1179
1361
|
t[a] = g[a] - static_cast<double>(i0[a]);
|
|
1180
1362
|
}
|
|
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);
|
|
1363
|
+
// Outside the sampled block the clamping above extends the boundary
|
|
1364
|
+
// values outward, so a surface meeting the box runs straight on into
|
|
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).
|
|
1193
1370
|
|
|
1194
1371
|
double acc = 0.0;
|
|
1195
1372
|
for (int c = 0; c < 8; ++c) {
|
|
@@ -1214,11 +1391,15 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1214
1391
|
const manifold::vec3 pad = 2.0 * E;
|
|
1215
1392
|
manifold::Box bounds(manifold::vec3(origin[0], origin[1], origin[2]) - pad,
|
|
1216
1393
|
manifold::vec3((*hi)[0], (*hi)[1], (*hi)[2]) + pad);
|
|
1217
|
-
// tolerance
|
|
1218
|
-
//
|
|
1219
|
-
//
|
|
1220
|
-
//
|
|
1221
|
-
//
|
|
1394
|
+
// tolerance: Manifold root-finds along each crossing lattice edge until
|
|
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.
|
|
1402
|
+
//
|
|
1222
1403
|
// canParallel is the crux: safe for a grid because the lambda is pure
|
|
1223
1404
|
// C++, and NOT safe for a function, which re-enters the evaluator.
|
|
1224
1405
|
// Manifold: parallel policies "will crash language runtimes with runtime
|
|
@@ -1226,19 +1407,32 @@ std::vector<ColoredBody> generateLevelSet(Evaluator& ev, const CSGParams& params
|
|
|
1226
1407
|
//
|
|
1227
1408
|
// LevelSet takes a single edge length, so a per-axis edge meshes a field
|
|
1228
1409
|
// squashed to unit spacing on every axis and stretches the result back.
|
|
1229
|
-
//
|
|
1230
|
-
|
|
1231
|
-
// with an axis scale.
|
|
1410
|
+
// The tolerance there is 1% of the unit edge, so 1% of each axis's own.
|
|
1411
|
+
const double tol = 0.01 * (edgeVec ? 1.0 : edge);
|
|
1232
1412
|
manifold::Manifold solid;
|
|
1233
|
-
if (
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1413
|
+
if (marchGrid) {
|
|
1414
|
+
const size_t n[3] = {field->nx + 2, field->ny + 2, field->nz + 2};
|
|
1415
|
+
std::vector<double> d(n[0] * n[1] * n[2], -1.0); // the "outside" ring
|
|
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;
|
|
1426
|
+
}
|
|
1427
|
+
if (!marchGrid && !edgeVec) {
|
|
1428
|
+
solid = fieldFn ? manifold::Manifold::LevelSet(sampleFn, bounds, edge, 0.0, tol, /*canParallel=*/false)
|
|
1429
|
+
: manifold::Manifold::LevelSet(sampleGrid, bounds, edge, 0.0, tol, /*canParallel=*/true);
|
|
1430
|
+
} else if (!marchGrid) {
|
|
1237
1431
|
const manifold::Box unit(bounds.min / E, bounds.max / E);
|
|
1238
1432
|
solid = fieldFn ? manifold::Manifold::LevelSet([&](manifold::vec3 q) { return sampleFn(q * E); },
|
|
1239
|
-
unit, 1.0, 0.0,
|
|
1433
|
+
unit, 1.0, 0.0, tol, /*canParallel=*/false)
|
|
1240
1434
|
: manifold::Manifold::LevelSet([&](manifold::vec3 q) { return sampleGrid(q * E); },
|
|
1241
|
-
unit, 1.0, 0.0,
|
|
1435
|
+
unit, 1.0, 0.0, tol, /*canParallel=*/true);
|
|
1242
1436
|
solid = solid.Scale(E);
|
|
1243
1437
|
}
|
|
1244
1438
|
|
|
@@ -1192,6 +1192,60 @@ TEST(LevelSet, MalformedFieldsWarn) {
|
|
|
1192
1192
|
}
|
|
1193
1193
|
}
|
|
1194
1194
|
|
|
1195
|
+
TEST(LevelSet, InfinityIsASampleNotAMalformedField) {
|
|
1196
|
+
// INF is BOSL2's name for 1/0, not a builtin, so each script defines it.
|
|
1197
|
+
// #744 on BelfrySCAD: a metaball field is 1/0 where a grid point lands on
|
|
1198
|
+
// a centre. The field is rectangular; INF made it read as malformed and
|
|
1199
|
+
// nothing was drawn. A metaball r/d around the origin, centre sample INF:
|
|
1200
|
+
// [1, INF] is the ball of radius r.
|
|
1201
|
+
const std::string field =
|
|
1202
|
+
"INF = 1/0; N = 21; H = 10;\nfunction co(t) = -H + 2*H*t/(N-1);\n"
|
|
1203
|
+
"f = [for (i=[0:N-1]) [for (j=[0:N-1]) [for (k=[0:N-1])\n"
|
|
1204
|
+
" let(d = norm([co(i), co(j), co(k)])) d == 0 ? INF : 6 / d ]]];\n";
|
|
1205
|
+
const std::vector<std::string> w =
|
|
1206
|
+
levelsetWarnings(field + "levelset(f, bounds=[[-H,-H,-H],[H,H,H]], isovalue=[1, INF]);");
|
|
1207
|
+
EXPECT_TRUE(w.empty()) << w.front();
|
|
1208
|
+
Evaluated e = evalSrc(field + "levelset(f, bounds=[[-H,-H,-H],[H,H,H]], isovalue=[1, INF]);");
|
|
1209
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1210
|
+
const double analytic = 4.0 / 3.0 * 3.14159265358979 * 6 * 6 * 6;
|
|
1211
|
+
EXPECT_NEAR(soleBody(e).Volume(), analytic, 0.05 * analytic);
|
|
1212
|
+
EXPECT_EQ(soleBody(e).Genus(), 0);
|
|
1213
|
+
|
|
1214
|
+
// -INF keeps its sign: the same ball, as a field that is -INF at the centre.
|
|
1215
|
+
Evaluated neg = evalSrc(
|
|
1216
|
+
"INF = 1/0; N = 21; H = 10;\nfunction co(t) = -H + 2*H*t/(N-1);\n"
|
|
1217
|
+
"f = [for (i=[0:N-1]) [for (j=[0:N-1]) [for (k=[0:N-1])\n"
|
|
1218
|
+
" let(d = norm([co(i), co(j), co(k)])) d == 0 ? -INF : -6 / d ]]];\n"
|
|
1219
|
+
"levelset(f, bounds=[[-H,-H,-H],[H,H,H]], isovalue=-1);");
|
|
1220
|
+
ASSERT_EQ(neg.bodies.size(), 1u);
|
|
1221
|
+
EXPECT_NEAR(soleBody(neg).Volume(), analytic, 0.05 * analytic);
|
|
1222
|
+
|
|
1223
|
+
// And the 2D reader the same way.
|
|
1224
|
+
EXPECT_TRUE(levelsetWarnings(
|
|
1225
|
+
"INF = 1/0; f = [for (i=[-5:5]) [for (j=[-5:5]) let(d = norm([i,j])) d == 0 ? INF : 3 / d]];\n"
|
|
1226
|
+
"levelset(f, bounds=[[-5,-5],[5,5]], isovalue=[1, INF]);")
|
|
1227
|
+
.empty());
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
TEST(LevelSet, NanIsStillAMalformedField) {
|
|
1231
|
+
EXPECT_FALSE(levelsetWarnings(
|
|
1232
|
+
"levelset([[[1,2],[3,0/0]],[[5,6],[7,8]]], bounds=[[0,0,0],[1,1,1]]);").empty());
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
TEST(LevelSet, AFunctionReturningMinusInfinityIsFarBelowNotFarAbove) {
|
|
1236
|
+
// Every non-finite value used to become +DBL_MAX, so -INF counted as
|
|
1237
|
+
// ABOVE every isovalue -- outside a scalar one. -INF over a ball of
|
|
1238
|
+
// radius 6 and 0 elsewhere: at or below -1 is that ball, not nothing.
|
|
1239
|
+
const std::string src =
|
|
1240
|
+
"INF = 1/0; levelset(function(x,y,z) norm([x,y,z]) < 6 ? -INF : 0,\n"
|
|
1241
|
+
" bounds=[[-10,-10,-10],[10,10,10]], isovalue=-1, edge=0.5);";
|
|
1242
|
+
EXPECT_TRUE(levelsetWarnings(src).empty());
|
|
1243
|
+
Evaluated e = evalSrc(src);
|
|
1244
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1245
|
+
const double analytic = 4.0 / 3.0 * 3.14159265358979 * 6 * 6 * 6;
|
|
1246
|
+
EXPECT_NEAR(soleBody(e).Volume(), analytic, 0.1 * analytic);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1195
1249
|
// -- levelset, function form ----------------------------------------------
|
|
1196
1250
|
//
|
|
1197
1251
|
// Same builtin, field given as function(x,y,z) instead of a grid. Slower --
|
|
@@ -1283,8 +1337,10 @@ TEST(LevelSetFn, AVectorEdgeIsExactlyAStretchedLattice) {
|
|
|
1283
1337
|
}
|
|
1284
1338
|
|
|
1285
1339
|
TEST(LevelSetFn, AVectorEdgeAlsoAppliesToAGrid) {
|
|
1340
|
+
// Compared with an isotropic resample, not the default: the default
|
|
1341
|
+
// marches the grid itself, which is a different mesher.
|
|
1286
1342
|
Evaluated fine = evalSrc(sphereFieldSrc(50, 30) +
|
|
1287
|
-
"levelset(f, bounds=[[-30,-30,-30],[30,30,30]], isovalue=20);");
|
|
1343
|
+
"levelset(f, bounds=[[-30,-30,-30],[30,30,30]], isovalue=20, edge=1.2);");
|
|
1288
1344
|
Evaluated coarseZ = evalSrc(sphereFieldSrc(50, 30) +
|
|
1289
1345
|
"levelset(f, bounds=[[-30,-30,-30],[30,30,30]], isovalue=20, edge=[1.2,1.2,4]);");
|
|
1290
1346
|
ASSERT_EQ(coarseZ.bodies.size(), 1u);
|
|
@@ -1511,15 +1567,159 @@ TEST(LevelSet, PaddingDoesNotDisturbAShapeInsideTheBox) {
|
|
|
1511
1567
|
}
|
|
1512
1568
|
|
|
1513
1569
|
TEST(LevelSet, AGridFieldIsAlsoCutCleanlyAtTheBox) {
|
|
1514
|
-
// The grid intake cannot sample beyond its data, so the
|
|
1515
|
-
//
|
|
1516
|
-
//
|
|
1570
|
+
// The grid intake cannot sample beyond its data, so past the block the
|
|
1571
|
+
// sampler clamps -- the edge values run on into the padding -- and the
|
|
1572
|
+
// clip to the bounds makes the cut. It used to read "outside" there
|
|
1573
|
+
// instead, which closed the surface AT the box face along Manifold's
|
|
1574
|
+
// lattice: a waffle-textured staircase inside the bounds that the clip
|
|
1575
|
+
// could not reach (#745 on BelfrySCAD). This test then needed one
|
|
1576
|
+
// sample layer of slack; a clean cut needs none.
|
|
1577
|
+
for (const char* edge : {"", ", edge=2", ", edge=[1,2,0.5]"}) {
|
|
1578
|
+
Evaluated e = evalSrc(
|
|
1579
|
+
std::string("N = 41;\nfunction co(t) = -20 + 40*t/(N-1);\n"
|
|
1580
|
+
"f = [for (i=[0:N-1]) [for (j=[0:N-1]) [for (k=[0:N-1]) co(i) ]]];\n"
|
|
1581
|
+
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=[-1e18,0]") + edge + ");");
|
|
1582
|
+
ASSERT_EQ(e.bodies.size(), 1u) << edge;
|
|
1583
|
+
EXPECT_NEAR(soleBody(e).Volume(), 32000.0, 1e-6) << edge;
|
|
1584
|
+
}
|
|
1585
|
+
// And a curved surface the box cuts through: a sphere of radius 25 in a
|
|
1586
|
+
// box of half-width 20. The faces lying flat on x=20 together make the
|
|
1587
|
+
// full disc the plane cuts, not a staircase short of it.
|
|
1588
|
+
Evaluated s = evalSrc(sphereFieldSrc(41, 20) +
|
|
1589
|
+
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=25);");
|
|
1590
|
+
ASSERT_EQ(s.bodies.size(), 1u);
|
|
1591
|
+
const manifold::MeshGL m = soleBody(s).GetMeshGL();
|
|
1592
|
+
double capArea = 0;
|
|
1593
|
+
for (size_t t = 0; t + 2 < m.triVerts.size(); t += 3) {
|
|
1594
|
+
manifold::vec3 p[3];
|
|
1595
|
+
for (int k = 0; k < 3; ++k) {
|
|
1596
|
+
const size_t v = static_cast<size_t>(m.triVerts[t + static_cast<size_t>(k)]) * m.numProp;
|
|
1597
|
+
p[k] = manifold::vec3(m.vertProperties[v], m.vertProperties[v + 1], m.vertProperties[v + 2]);
|
|
1598
|
+
}
|
|
1599
|
+
if (std::abs(p[0].x - 20) < 1e-6 && std::abs(p[1].x - 20) < 1e-6 && std::abs(p[2].x - 20) < 1e-6)
|
|
1600
|
+
capArea += 0.5 * manifold::la::length(manifold::la::cross(p[1] - p[0], p[2] - p[0]));
|
|
1601
|
+
}
|
|
1602
|
+
// The plane x=20 cuts the sphere in a disc of radius sqrt(25^2 - 20^2)
|
|
1603
|
+
// = 15, which fits inside the 40x40 face.
|
|
1604
|
+
const double r = 15.0;
|
|
1605
|
+
EXPECT_NEAR(capArea, 3.14159265358979 * r * r, 0.03 * 3.14159265358979 * r * r);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
TEST(LevelSetFn, VerticesLieOnTheTrueSurface) {
|
|
1609
|
+
// A positive tolerance makes Manifold root-find along each lattice edge
|
|
1610
|
+
// instead of interpolating linearly between its two samples, so a
|
|
1611
|
+
// function field's vertices land on the surface itself. Measured on
|
|
1612
|
+
// #745 on BelfrySCAD: 0.2 off at edge=2 before, on the surface after;
|
|
1613
|
+
// the coarse, curved 1/r field there is where linear interpolation is
|
|
1614
|
+
// worst.
|
|
1615
|
+
for (const char* edge : {"2", "[1,2,0.4]"}) {
|
|
1616
|
+
Evaluated e = evalSrc(std::string("levelset(function(x,y,z) 5 / norm([x,y,z]), "
|
|
1617
|
+
"bounds=[[-8,-8,-8],[8,8,8]], isovalue=[1, 1/0], edge=") +
|
|
1618
|
+
edge + ");");
|
|
1619
|
+
ASSERT_EQ(e.bodies.size(), 1u) << edge;
|
|
1620
|
+
const manifold::MeshGL m = soleBody(e).GetMeshGL();
|
|
1621
|
+
double worst = 0;
|
|
1622
|
+
for (size_t v = 0; v + 2 < m.vertProperties.size(); v += m.numProp)
|
|
1623
|
+
worst = std::max(worst, std::abs(manifold::la::length(manifold::vec3(m.vertProperties[v], m.vertProperties[v + 1],
|
|
1624
|
+
m.vertProperties[v + 2])) - 5.0));
|
|
1625
|
+
EXPECT_LT(worst, 0.03) << edge;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// -- levelset, grid marched directly ---------------------------------------
|
|
1630
|
+
//
|
|
1631
|
+
// At its own spacing a grid is marched as it stands (marchingCubes), not
|
|
1632
|
+
// resampled through Manifold's body-centred lattice -- which meshed a
|
|
1633
|
+
// trilinear blend of the data and left #745 on BelfrySCAD's metaballs more
|
|
1634
|
+
// faceted than BOSL2's isosurface(). The signature of marching the grid: a
|
|
1635
|
+
// vertex sits on a grid EDGE, so at least two of its coordinates are grid
|
|
1636
|
+
// lines. Manifold's lattice puts most of them on diagonals. The mesher also
|
|
1637
|
+
// falls back to resampling if Manifold ever refused its mesh, and this is
|
|
1638
|
+
// what would catch that happening silently.
|
|
1639
|
+
|
|
1640
|
+
namespace {
|
|
1641
|
+
|
|
1642
|
+
// Fraction of the body's vertices with two or more coordinates on the grid
|
|
1643
|
+
// lines lo + i*h. Polygons of 5+ sides get a centroid vertex, which does not
|
|
1644
|
+
// qualify, so marching the grid scores 0.66-0.81 on the fields below.
|
|
1645
|
+
// Resampling at an edge 1e-6 away from the grid's scores 0.001-0.024.
|
|
1646
|
+
double onGridLines(const manifold::Manifold& m, manifold::vec3 lo, manifold::vec3 h) {
|
|
1647
|
+
const manifold::MeshGL g = m.GetMeshGL();
|
|
1648
|
+
size_t on = 0, all = 0;
|
|
1649
|
+
for (size_t v = 0; v + 2 < g.vertProperties.size(); v += g.numProp, ++all) {
|
|
1650
|
+
int lines = 0;
|
|
1651
|
+
for (int a = 0; a < 3; ++a) {
|
|
1652
|
+
const double t = (g.vertProperties[v + static_cast<size_t>(a)] - lo[a]) / h[a];
|
|
1653
|
+
lines += std::abs(t - std::round(t)) < 1e-4;
|
|
1654
|
+
}
|
|
1655
|
+
on += lines >= 2;
|
|
1656
|
+
}
|
|
1657
|
+
return all ? static_cast<double>(on) / static_cast<double>(all) : 0.0;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
} // namespace
|
|
1661
|
+
|
|
1662
|
+
TEST(LevelSetGrid, TheDefaultMarchesTheGridItself) {
|
|
1663
|
+
Evaluated e = evalSrc(sphereFieldSrc(41, 20) +
|
|
1664
|
+
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=15);");
|
|
1665
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1666
|
+
EXPECT_GT(onGridLines(soleBody(e), manifold::vec3(-20.0), manifold::vec3(1.0)), 0.5);
|
|
1667
|
+
EXPECT_EQ(soleBody(e).Genus(), 0);
|
|
1668
|
+
const double analytic = 4.0 / 3.0 * 3.14159265358979 * 15 * 15 * 15;
|
|
1669
|
+
EXPECT_NEAR(soleBody(e).Volume(), analytic, 0.01 * analytic);
|
|
1670
|
+
// And edge= naming the grid's own spacing is the same request.
|
|
1671
|
+
Evaluated same = evalSrc(sphereFieldSrc(41, 20) +
|
|
1672
|
+
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=15, edge=[1,1,1]);");
|
|
1673
|
+
ASSERT_EQ(same.bodies.size(), 1u);
|
|
1674
|
+
EXPECT_EQ(soleBody(same).NumTri(), soleBody(e).NumTri());
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
TEST(LevelSetGrid, ADifferentEdgeStillResamples) {
|
|
1678
|
+
Evaluated e = evalSrc(sphereFieldSrc(41, 20) +
|
|
1679
|
+
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=15, edge=0.7);");
|
|
1680
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1681
|
+
EXPECT_LT(onGridLines(soleBody(e), manifold::vec3(-20.0), manifold::vec3(1.0)), 0.1);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
TEST(LevelSetGrid, SaddlesEverywhereStillCloseTheMesh) {
|
|
1685
|
+
// Random samples: ambiguous faces in every direction, which is where a
|
|
1686
|
+
// per-cell table cracks. A crack would fail Manifold's check and fall
|
|
1687
|
+
// back to resampling, so the vertices would leave the grid lines.
|
|
1688
|
+
for (const char* iso : {"0", "[-0.3, 0.4], invert=true"}) {
|
|
1689
|
+
Evaluated e = evalSrc(std::string("r = rands(-1, 1, 20*20*20, 42);\n"
|
|
1690
|
+
"f = [for (i=[0:19]) [for (j=[0:19]) [for (k=[0:19]) r[(i*20+j)*20+k] ]]];\n"
|
|
1691
|
+
"levelset(f, bounds=[[0,0,0],[19,19,19]], isovalue=") + iso + ");");
|
|
1692
|
+
ASSERT_EQ(e.bodies.size(), 1u) << iso;
|
|
1693
|
+
EXPECT_GT(onGridLines(soleBody(e), manifold::vec3(0.0), manifold::vec3(1.0)), 0.5) << iso;
|
|
1694
|
+
EXPECT_GT(soleBody(e).Volume(), 0.0) << iso;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
TEST(LevelSetGrid, SamplesExactlyOnTheIsovalueStayManifold) {
|
|
1699
|
+
// Integer data at an integer isovalue: crossings land ON grid corners,
|
|
1700
|
+
// so several edges share a vertex position. The old resample of this
|
|
1701
|
+
// exported 4 non-manifold edges and 2 duplicate faces.
|
|
1517
1702
|
Evaluated e = evalSrc(
|
|
1518
|
-
"
|
|
1519
|
-
"f =
|
|
1520
|
-
"levelset(f, bounds=[[-20,-20,-20],[20,20,20]], isovalue=[-1e18,0]);");
|
|
1703
|
+
"f = [for (i=[0:9]) [for (j=[0:9]) [for (k=[0:9]) (i+j+k) % 3 ]]];\n"
|
|
1704
|
+
"levelset(f, bounds=[[0,0,0],[9,9,9]], isovalue=1);");
|
|
1521
1705
|
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1522
|
-
|
|
1706
|
+
EXPECT_EQ(soleBody(e).Status(), manifold::Manifold::Error::NoError);
|
|
1707
|
+
EXPECT_GT(onGridLines(soleBody(e), manifold::vec3(0.0), manifold::vec3(1.0)), 0.5);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
TEST(LevelSetGrid, UnequalSpacingPerAxisIsMarchedAsIs) {
|
|
1711
|
+
// 31 samples over 20 x 40 x 10: three different spacings, no common edge
|
|
1712
|
+
// to resample at. A unit-ball field becomes an ellipsoid of semi-axes
|
|
1713
|
+
// 8, 16 and 4 at isovalue 0.8.
|
|
1714
|
+
Evaluated e = evalSrc(
|
|
1715
|
+
"N = 31;\nf = [for (i=[0:N-1]) [for (j=[0:N-1]) [for (k=[0:N-1])\n"
|
|
1716
|
+
" norm([(i-15)/15, (j-15)/15, (k-15)/15]) ]]];\n"
|
|
1717
|
+
"levelset(f, bounds=[[-10,-20,-5],[10,20,5]], isovalue=0.8);");
|
|
1718
|
+
ASSERT_EQ(e.bodies.size(), 1u);
|
|
1719
|
+
EXPECT_GT(onGridLines(soleBody(e), manifold::vec3(-10, -20, -5), manifold::vec3(20.0 / 30, 40.0 / 30, 10.0 / 30)),
|
|
1720
|
+
0.5);
|
|
1721
|
+
const double analytic = 4.0 / 3.0 * 3.14159265358979 * 8 * 16 * 4;
|
|
1722
|
+
EXPECT_NEAR(soleBody(e).Volume(), analytic, 0.02 * analytic);
|
|
1523
1723
|
}
|
|
1524
1724
|
|
|
1525
1725
|
// -- 2D union keeps every colour (bug report: "multiple colors inside a
|
|
File without changes
|
{openscad_cpp_evaluator-1.46.0 → openscad_cpp_evaluator-1.47.0}/.github/workflows/wheels.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|