openscad-cpp-evaluator 1.37.2__tar.gz → 1.38.1__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.37.2 → openscad_cpp_evaluator-1.38.1}/CLAUDE.md +30 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +14 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/ast_node.cpp +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/expression.cpp +13 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/driver.cpp +5 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/driver.hpp +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/lexer.l +3 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/parser.y +12 -1
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +6 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/pretty_print.cpp +3 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/serialization/json_io.cpp +11 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/CMakeLists.txt +1 -0
- openscad_cpp_evaluator-1.38.1/external/openscad_cpp_parser/tests/test_profile_time.cpp +86 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/csg_node.hpp +7 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/evaluator.hpp +17 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/CMakeLists.txt +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/builtins.hpp +6 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/extrude.cpp +100 -12
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/function_builtins.cpp +1 -0
- openscad_cpp_evaluator-1.38.1/src/builtins/profile_time.cpp +63 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/registry.cpp +2 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/coverage.cpp +6 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/csg_generate.cpp +11 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/csg_resolve.cpp +19 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/expr_eval.cpp +15 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/format_closure.cpp +4 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/CMakeLists.txt +1 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_extrude_roof.cpp +129 -0
- openscad_cpp_evaluator-1.38.1/tests/test_profile_time.cpp +139 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/uv.lock +1 -1
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/README.md +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/bindings/module.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/cmake/embed_font.cmake +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/cmake/patch_manifold.cmake +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/.DS_Store +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_concurrent_parse.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/coverage.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/export.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/python/openscad_cpp_evaluator/__init__.py +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LiberationSans-Bold.ttf +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/text.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/export.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/import/scene_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/text/freetype_font_provider.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/xkcd_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/xkcd_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_coverage.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_discretizer.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_export.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_python_bindings.py +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_text.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_undef_args.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/tools/cli/main.cpp +0 -0
|
@@ -1866,3 +1866,33 @@ A compiled function keeps its parameters in frame slots, which no `EvalContext`
|
|
|
1866
1866
|
can see, and the children are statement opcodes that resolve names through the
|
|
1867
1867
|
context — without this, `function f(w) = render() { cube(w); }.volume;` finds no
|
|
1868
1868
|
`w` and silently measures nothing.
|
|
1869
|
+
|
|
1870
|
+
## `profile_time()`
|
|
1871
|
+
|
|
1872
|
+
`profile_time([label]) { ... }` prints `PROFILE: label: T ms (script S ms, geometry G ms)`;
|
|
1873
|
+
`x = profile_time([label]) expr;` prints `PROFILE: label: T ms` and evaluates to `expr`'s value.
|
|
1874
|
+
`supported_feature("profile-time")` is 1. The label is optional (default `line N of file`), and
|
|
1875
|
+
is evaluated before the clock starts. Times under 1 ms print in microseconds (`fmtMs`), so a fast part never reads `0.00 ms`.
|
|
1876
|
+
|
|
1877
|
+
### Implementation notes
|
|
1878
|
+
|
|
1879
|
+
- **Parser:** `profile_time` is a reserved keyword (`KW_PROFILE_TIME`) for the same reason as
|
|
1880
|
+
`render`. The statement form is a plain `ModularCall` named `profile_time`; the expression form
|
|
1881
|
+
is a `ProfileTimeOp` node shaped like `EchoOp`, with a required body.
|
|
1882
|
+
- **Script time** is taken around the resolve function's call in `Evaluator::evalModularCall`
|
|
1883
|
+
(csg_resolve.cpp), not inside `resolveProfileTime`, so argument evaluation and every child
|
|
1884
|
+
count. It is stored on `CSGNode::profileScriptMs`, which is deliberately not in `cacheKey()` --
|
|
1885
|
+
a timing in the key would make every profiled subtree a permanent miss.
|
|
1886
|
+
- **Geometry time** is taken in `generateTreeImpl`, from *before* the cache lookup, and printed
|
|
1887
|
+
when the node finishes, so a hit reports `geometry cached` rather than vanishing. A hit on an
|
|
1888
|
+
**ancestor** never visits the profile_time() node at all; `reportCachedProfiles` walks the hit
|
|
1889
|
+
subtree for them, but only when the run resolved any (`profileNodeCount_`), so a model without
|
|
1890
|
+
profile_time() pays nothing on its hits.
|
|
1891
|
+
- **Resolve-only runs** (`evaluate(..., generate=false)`) print `script S ms (no geometry built)`
|
|
1892
|
+
at resolve time instead (`generateRequested_`).
|
|
1893
|
+
- **Expression form** is interpreter-only: the bytecode compiler has no case for
|
|
1894
|
+
`ProfileTimeOp`, so its `default:` throws `NotCompilable` and the containing function falls back
|
|
1895
|
+
to the interpreter. It is also deliberately absent from `simplifyTailStep` -- the clock must stop
|
|
1896
|
+
after the body, which a tail step would not wait for.
|
|
1897
|
+
- `PROFILE:` lines go straight to `echoFn_`, not through `emitWarning`, so the ManifoldCache never
|
|
1898
|
+
stores or replays them (a replay would print a stale time as if it were current).
|
|
@@ -217,6 +217,20 @@ public:
|
|
|
217
217
|
void buildScope(Scope& parentScope) override;
|
|
218
218
|
};
|
|
219
219
|
|
|
220
|
+
// `profile_time(arguments...) body` -- same shape as EchoOp; times the body.
|
|
221
|
+
// Unlike echo/assert the body is required: timing nothing is meaningless.
|
|
222
|
+
class ProfileTimeOp : public Expression {
|
|
223
|
+
public:
|
|
224
|
+
ProfileTimeOp(Position position, std::vector<std::unique_ptr<Argument>> arguments, std::unique_ptr<Expression> body)
|
|
225
|
+
: Expression(NodeKind::ProfileTimeOp, std::move(position)), arguments(std::move(arguments)), body(std::move(body)) {}
|
|
226
|
+
|
|
227
|
+
std::vector<std::unique_ptr<Argument>> arguments;
|
|
228
|
+
std::unique_ptr<Expression> body;
|
|
229
|
+
|
|
230
|
+
std::string toString() const override;
|
|
231
|
+
void buildScope(Scope& parentScope) override;
|
|
232
|
+
};
|
|
233
|
+
|
|
220
234
|
// `assert(arguments...) body` -- same shape as EchoOp.
|
|
221
235
|
class AssertOp : public Expression {
|
|
222
236
|
public:
|
|
@@ -21,6 +21,7 @@ const char* nodeKindName(NodeKind kind) {
|
|
|
21
21
|
case NodeKind::LetOp: return "LetOp";
|
|
22
22
|
case NodeKind::EchoOp: return "EchoOp";
|
|
23
23
|
case NodeKind::AssertOp: return "AssertOp";
|
|
24
|
+
case NodeKind::ProfileTimeOp: return "ProfileTimeOp";
|
|
24
25
|
case NodeKind::FunctionLiteral: return "FunctionLiteral";
|
|
25
26
|
case NodeKind::UnaryMinusOp: return "UnaryMinusOp";
|
|
26
27
|
case NodeKind::AdditionOp: return "AdditionOp";
|
|
@@ -134,6 +134,18 @@ void EchoOp::buildScope(Scope& parentScope) {
|
|
|
134
134
|
body->buildScope(parentScope);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
std::string ProfileTimeOp::toString() const {
|
|
138
|
+
return "profile_time(" + joinToString(arguments, ", ") + ") " + body->toString();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void ProfileTimeOp::buildScope(Scope& parentScope) {
|
|
142
|
+
setScope(parentScope);
|
|
143
|
+
for (auto& a : arguments) {
|
|
144
|
+
a->buildScope(parentScope);
|
|
145
|
+
}
|
|
146
|
+
body->buildScope(parentScope);
|
|
147
|
+
}
|
|
148
|
+
|
|
137
149
|
std::string AssertOp::toString() const {
|
|
138
150
|
return "assert(" + joinToString(arguments, ", ") + ") " + body->toString();
|
|
139
151
|
}
|
|
@@ -219,6 +231,7 @@ int operatorPrecedence(NodeKind kind) {
|
|
|
219
231
|
case NodeKind::LetOp:
|
|
220
232
|
case NodeKind::EchoOp:
|
|
221
233
|
case NodeKind::AssertOp:
|
|
234
|
+
case NodeKind::ProfileTimeOp:
|
|
222
235
|
case NodeKind::FunctionLiteral:
|
|
223
236
|
return 0;
|
|
224
237
|
case NodeKind::LogicalOrOp:
|
|
@@ -88,6 +88,11 @@ NodePtr makeEchoOp(ParserDriver& driver, const OscadLocation& loc, NodeList argu
|
|
|
88
88
|
nodeCast<Expression>(std::move(body)));
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
NodePtr makeProfileTimeOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body) {
|
|
92
|
+
return std::make_unique<ProfileTimeOp>(driver.toPosition(loc), nodeListCast<Argument>(std::move(arguments)),
|
|
93
|
+
nodeCast<Expression>(std::move(body)));
|
|
94
|
+
}
|
|
95
|
+
|
|
91
96
|
NodePtr makeAssertOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body) {
|
|
92
97
|
return std::make_unique<AssertOp>(driver.toPosition(loc), nodeListCast<Argument>(std::move(arguments)),
|
|
93
98
|
nodeCast<Expression>(std::move(body)));
|
|
@@ -104,6 +104,7 @@ NodePtr makeAssignment(ParserDriver& driver, const OscadLocation& loc, const Osc
|
|
|
104
104
|
|
|
105
105
|
NodePtr makeLetOp(ParserDriver& driver, const OscadLocation& loc, NodeList assignments, NodePtr body);
|
|
106
106
|
NodePtr makeEchoOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body);
|
|
107
|
+
NodePtr makeProfileTimeOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body);
|
|
107
108
|
NodePtr makeAssertOp(ParserDriver& driver, const OscadLocation& loc, NodeList arguments, NodePtr body);
|
|
108
109
|
NodePtr makeFunctionLiteral(ParserDriver& driver, const OscadLocation& loc, NodeList parameters, NodePtr body);
|
|
109
110
|
|
|
@@ -52,6 +52,9 @@ const std::unordered_map<std::string, KeywordFactory>& keywordTable() {
|
|
|
52
52
|
// apart from a function call. Note `$render` still lexes as NAME --
|
|
53
53
|
// this table is keyed on the full IDENT text, including the `$`.
|
|
54
54
|
{"render", [](const OscadLocation& l) { return yy::parser::make_KW_RENDER(l); }},
|
|
55
|
+
// Reserved for the same reason as `render`: its expression form
|
|
56
|
+
// (`x = profile_time("label") expr;`) starts like a function call.
|
|
57
|
+
{"profile_time", [](const OscadLocation& l) { return yy::parser::make_KW_PROFILE_TIME(l); }},
|
|
55
58
|
{"undef", [](const OscadLocation& l) { return yy::parser::make_KW_UNDEF(l); }},
|
|
56
59
|
{"true", [](const OscadLocation& l) { return yy::parser::make_KW_TRUE(l); }},
|
|
57
60
|
{"false", [](const OscadLocation& l) { return yy::parser::make_KW_FALSE(l); }},
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
KW_INTERSECTION_FOR "intersection_for"
|
|
94
94
|
KW_EACH "each"
|
|
95
95
|
KW_RENDER "render"
|
|
96
|
+
KW_PROFILE_TIME "profile_time"
|
|
96
97
|
KW_UNDEF "undef"
|
|
97
98
|
KW_TRUE "true"
|
|
98
99
|
KW_FALSE "false"
|
|
@@ -158,7 +159,7 @@
|
|
|
158
159
|
%type <NodePtr> modifier_show_only modifier_highlight modifier_background modifier_disable
|
|
159
160
|
%type <NodePtr> if_statement ifelse_statement
|
|
160
161
|
%type <NodePtr> modular_for modular_intersection_for modular_let modular_assert modular_echo modular_call
|
|
161
|
-
%type <NodePtr> render_stmt render_expr
|
|
162
|
+
%type <NodePtr> render_stmt render_expr profile_time_stmt
|
|
162
163
|
%type <NodePtr> expr opchain postfix primary
|
|
163
164
|
%type <NodePtr> range_expr vector_expr vector_element
|
|
164
165
|
%type <NodePtr> listcomp_elements listcomp_paren_expr listcomp_let listcomp_each
|
|
@@ -330,6 +331,7 @@ single_module_instantiation:
|
|
|
330
331
|
| modular_echo { $$ = std::move($1); }
|
|
331
332
|
| modular_call { $$ = std::move($1); }
|
|
332
333
|
| render_stmt { $$ = std::move($1); }
|
|
334
|
+
| profile_time_stmt { $$ = std::move($1); }
|
|
333
335
|
;
|
|
334
336
|
|
|
335
337
|
modular_for:
|
|
@@ -378,6 +380,14 @@ render_stmt:
|
|
|
378
380
|
}
|
|
379
381
|
;
|
|
380
382
|
|
|
383
|
+
// Same arrangement as render_stmt: the keyword exists only for the
|
|
384
|
+
// expression form, and the statement form is an ordinary builtin call.
|
|
385
|
+
profile_time_stmt:
|
|
386
|
+
"profile_time" "(" arguments ")" child_statement {
|
|
387
|
+
$$ = makeModularCall(driver, @$, @1, "profile_time", std::move($3), std::move($5));
|
|
388
|
+
}
|
|
389
|
+
;
|
|
390
|
+
|
|
381
391
|
// -- Expressions ----------------------------------------------------------
|
|
382
392
|
//
|
|
383
393
|
// `expr` covers let/assert/echo/funclit_def/ternary plus the operator
|
|
@@ -393,6 +403,7 @@ expr:
|
|
|
393
403
|
| "assert" "(" arguments ")" { $$ = makeAssertOp(driver, @$, std::move($3), makeUndefinedLiteral(driver, @$)); }
|
|
394
404
|
| "echo" "(" arguments ")" expr { $$ = makeEchoOp(driver, @$, std::move($3), std::move($5)); }
|
|
395
405
|
| "echo" "(" arguments ")" { $$ = makeEchoOp(driver, @$, std::move($3), makeUndefinedLiteral(driver, @$)); }
|
|
406
|
+
| "profile_time" "(" arguments ")" expr { $$ = makeProfileTimeOp(driver, @$, std::move($3), std::move($5)); }
|
|
396
407
|
| "function" "(" parameters ")" expr { $$ = makeFunctionLiteral(driver, @$, std::move($3), std::move($5)); }
|
|
397
408
|
| opchain "?" expr ":" expr { $$ = makeTernaryOp(driver, @$, std::move($1), std::move($3), std::move($5)); }
|
|
398
409
|
| opchain { $$ = std::move($1); }
|
|
@@ -180,6 +180,12 @@ void classifyNode(ASTNode& node, std::vector<ExprSlot>& exprFields, std::vector<
|
|
|
180
180
|
addExpr(n.body, exprFields);
|
|
181
181
|
break;
|
|
182
182
|
}
|
|
183
|
+
case NodeKind::ProfileTimeOp: {
|
|
184
|
+
auto& n = static_cast<ProfileTimeOp&>(node);
|
|
185
|
+
addArgumentExprList(n.arguments, exprFields);
|
|
186
|
+
addExpr(n.body, exprFields);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
183
189
|
case NodeKind::AssertOp: {
|
|
184
190
|
auto& n = static_cast<AssertOp&>(node);
|
|
185
191
|
addArgumentExprList(n.arguments, exprFields);
|
|
@@ -486,6 +486,9 @@ std::string fmtExpr(const ASTNode& exprNode, int indent, int w) {
|
|
|
486
486
|
}
|
|
487
487
|
return "echo(" + args + ")\n" + pad + fmtExpr(*e->body, indent, w);
|
|
488
488
|
}
|
|
489
|
+
if (auto* p = dynamic_cast<const ProfileTimeOp*>(&exprNode)) {
|
|
490
|
+
return "profile_time(" + joinToString(p->arguments, ", ") + ")\n" + pad + fmtExpr(*p->body, indent, w);
|
|
491
|
+
}
|
|
489
492
|
if (auto* l = dynamic_cast<const LetOp*>(&exprNode)) {
|
|
490
493
|
return fmtLetOpExpr(*l, indent, w);
|
|
491
494
|
}
|
|
@@ -150,6 +150,12 @@ json toJsonImpl(const ASTNode& node, bool includePos) {
|
|
|
150
150
|
j["body"] = valueToJson(n.body.get(), includePos);
|
|
151
151
|
break;
|
|
152
152
|
}
|
|
153
|
+
case NodeKind::ProfileTimeOp: {
|
|
154
|
+
auto& n = static_cast<const ProfileTimeOp&>(node);
|
|
155
|
+
j["arguments"] = listToJson(n.arguments, includePos);
|
|
156
|
+
j["body"] = valueToJson(n.body.get(), includePos);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
153
159
|
case NodeKind::AssertOp: {
|
|
154
160
|
auto& n = static_cast<const AssertOp&>(node);
|
|
155
161
|
j["arguments"] = listToJson(n.arguments, includePos);
|
|
@@ -446,6 +452,11 @@ const std::unordered_map<std::string, Builder>& registry() {
|
|
|
446
452
|
return std::make_unique<EchoOp>(std::move(pos), listFromJson<Argument>(j, "arguments"),
|
|
447
453
|
childFromJson<Expression>(j, "body"));
|
|
448
454
|
}},
|
|
455
|
+
{"ProfileTimeOp",
|
|
456
|
+
[](const json& j, Position pos) -> std::unique_ptr<ASTNode> {
|
|
457
|
+
return std::make_unique<ProfileTimeOp>(std::move(pos), listFromJson<Argument>(j, "arguments"),
|
|
458
|
+
childFromJson<Expression>(j, "body"));
|
|
459
|
+
}},
|
|
449
460
|
{"AssertOp",
|
|
450
461
|
[](const json& j, Position pos) -> std::unique_ptr<ASTNode> {
|
|
451
462
|
return std::make_unique<AssertOp>(std::move(pos), listFromJson<Argument>(j, "arguments"),
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// `profile_time()`: the statement form times its children, the expression
|
|
2
|
+
// form (`x = profile_time("label") expr;`) its body. `profile_time` is a
|
|
3
|
+
// reserved keyword only because the expression form would otherwise be
|
|
4
|
+
// indistinguishable from a function call; the statement form is a plain
|
|
5
|
+
// ModularCall, so builtin dispatch downstream sees an ordinary module.
|
|
6
|
+
|
|
7
|
+
#include "openscad_cpp_parser/api.hpp"
|
|
8
|
+
#include "openscad_cpp_parser/pretty_print.hpp"
|
|
9
|
+
#include "openscad_cpp_parser/serialization.hpp"
|
|
10
|
+
#include "test_helpers.hpp"
|
|
11
|
+
|
|
12
|
+
#include <gtest/gtest.h>
|
|
13
|
+
|
|
14
|
+
using namespace oscad;
|
|
15
|
+
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
const ProfileTimeOp* profileOf(const std::vector<std::unique_ptr<ASTNode>>& ast) {
|
|
19
|
+
auto* a = dynamic_cast<Assignment*>(ast[0].get());
|
|
20
|
+
return a ? dynamic_cast<const ProfileTimeOp*>(a->expr.get()) : nullptr;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} // namespace
|
|
24
|
+
|
|
25
|
+
TEST(ProfileTime, StatementFormIsAPlainModularCall) {
|
|
26
|
+
auto ast = parseSrc("profile_time(\"gears\") { cube(1); sphere(2); }");
|
|
27
|
+
ASSERT_EQ(ast.size(), 1u);
|
|
28
|
+
auto* call = dynamic_cast<ModularCall*>(ast[0].get());
|
|
29
|
+
ASSERT_NE(call, nullptr);
|
|
30
|
+
EXPECT_EQ(call->name->name, "profile_time");
|
|
31
|
+
EXPECT_EQ(call->arguments.size(), 1u);
|
|
32
|
+
EXPECT_EQ(call->children.size(), 2u);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
TEST(ProfileTime, ExpressionFormWrapsItsBody) {
|
|
36
|
+
auto ast = parseSrc("x = profile_time(\"sum\") 1 + 2;");
|
|
37
|
+
const ProfileTimeOp* p = profileOf(ast);
|
|
38
|
+
ASSERT_NE(p, nullptr);
|
|
39
|
+
EXPECT_EQ(p->kind(), NodeKind::ProfileTimeOp);
|
|
40
|
+
EXPECT_EQ(p->arguments.size(), 1u);
|
|
41
|
+
EXPECT_EQ(p->body->kind(), NodeKind::AdditionOp);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
TEST(ProfileTime, ExpressionFormAllowsNoLabel) {
|
|
45
|
+
const ProfileTimeOp* p = nullptr;
|
|
46
|
+
auto ast = parseSrc("x = profile_time() f(3);");
|
|
47
|
+
p = profileOf(ast);
|
|
48
|
+
ASSERT_NE(p, nullptr);
|
|
49
|
+
EXPECT_TRUE(p->arguments.empty());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
TEST(ProfileTime, ExpressionFormNeedsABody) {
|
|
53
|
+
EXPECT_THROW(parseSrc("x = profile_time(\"a\");"), ParseError);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
TEST(ProfileTime, IsReserved) {
|
|
57
|
+
EXPECT_THROW(parseSrc("profile_time = 1;"), ParseError);
|
|
58
|
+
EXPECT_THROW(parseSrc("function profile_time() = 1;"), ParseError);
|
|
59
|
+
EXPECT_THROW(parseSrc("module profile_time() {}"), ParseError);
|
|
60
|
+
// `$profile_time` is a different identifier and stays usable.
|
|
61
|
+
EXPECT_NO_THROW(parseSrc("$profile_time = 1;"));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
TEST(ProfileTime, ToStringRoundTrips) {
|
|
65
|
+
auto ast = parseSrc("x = profile_time(\"a\") 1 + 2;");
|
|
66
|
+
EXPECT_EQ(ast[0]->toString(), "x = profile_time(\"a\") 1 + 2");
|
|
67
|
+
EXPECT_NE(profileOf(parseSrc(ast[0]->toString() + ";")), nullptr);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
TEST(ProfileTime, JsonRoundTrips) {
|
|
71
|
+
auto ast = parseSrc("x = profile_time(\"a\") 1 + 2;");
|
|
72
|
+
auto back = astFromJsonString(astToJsonString(ast));
|
|
73
|
+
const ProfileTimeOp* p = profileOf(back);
|
|
74
|
+
ASSERT_NE(p, nullptr);
|
|
75
|
+
EXPECT_EQ(p->body->kind(), NodeKind::AdditionOp);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
TEST(ProfileTime, PrettyPrintReparses) {
|
|
79
|
+
for (const char* src : {"x = profile_time(\"a\") 1 + 2;",
|
|
80
|
+
"profile_time(\"g\") { cube(1); }"}) {
|
|
81
|
+
auto ast = parseSrc(src);
|
|
82
|
+
std::string out = toOpenscad(ast);
|
|
83
|
+
EXPECT_NE(out.find("profile_time(\""), std::string::npos) << out;
|
|
84
|
+
EXPECT_NO_THROW(parseSrc(out)) << out;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -71,6 +71,13 @@ struct CSGNode {
|
|
|
71
71
|
// stack risk the moment this node is ever destroyed.
|
|
72
72
|
int treeDepth = 1;
|
|
73
73
|
|
|
74
|
+
// profile_time() only: how long resolving this node (its arguments and
|
|
75
|
+
// its children's script code) took, in ms; < 0 for every other node.
|
|
76
|
+
// Measured fresh on every run, so -- like warnEntry -- deliberately NOT
|
|
77
|
+
// part of cacheKey(): putting a timing in the key would make every
|
|
78
|
+
// profiled subtree a permanent cache miss.
|
|
79
|
+
double profileScriptMs = -1.0;
|
|
80
|
+
|
|
74
81
|
// Memoizes manifold_cache.cpp's cacheKey(*this) -- that function
|
|
75
82
|
// recurses into every descendant to build its own key, so without this
|
|
76
83
|
// memo a generate-time walk that calls cacheKey() at every node (not
|
|
@@ -422,6 +422,22 @@ public:
|
|
|
422
422
|
// subtree, not just direct children, since hull() consumes all of it.
|
|
423
423
|
bool insideHull = false;
|
|
424
424
|
|
|
425
|
+
// profile_time(). generateRequested_ is false only inside an
|
|
426
|
+
// evaluate(generate = false) run, where no geometry pass follows to
|
|
427
|
+
// report a profiled block, so resolve reports the script time alone.
|
|
428
|
+
// profileNodeCount_ counts this run's profile_time() nodes so a cache
|
|
429
|
+
// hit only walks its subtree for them when there are any (see
|
|
430
|
+
// reportCachedProfiles).
|
|
431
|
+
bool generateRequested_ = true;
|
|
432
|
+
std::size_t profileNodeCount_ = 0;
|
|
433
|
+
// Prints "PROFILE: <label>: ..." for a profile_time() node. geometryMs
|
|
434
|
+
// < 0 means no geometry pass; `cached` means its geometry came from the
|
|
435
|
+
// ManifoldCache rather than being built.
|
|
436
|
+
void reportProfile(const CSGNode& node, double geometryMs, bool cached);
|
|
437
|
+
// A cache hit at `node` skips its whole subtree, so a profile_time()
|
|
438
|
+
// inside it would otherwise print nothing at all.
|
|
439
|
+
void reportCachedProfiles(const CSGNode& node);
|
|
440
|
+
|
|
425
441
|
ColoredBody tagGenerated(manifold::Manifold body, const oscad::ASTNode& node, const Value& colorValue);
|
|
426
442
|
|
|
427
443
|
// tagGenerated()'s counterpart for a mesh Manifold refused to build --
|
|
@@ -837,6 +853,7 @@ private:
|
|
|
837
853
|
Value evalLetExpr(const oscad::LetOp& node, EvalContext& ctx);
|
|
838
854
|
Value evalEchoExpr(const oscad::EchoOp& node, EvalContext& ctx);
|
|
839
855
|
Value evalAssertExpr(const oscad::AssertOp& node, EvalContext& ctx);
|
|
856
|
+
Value evalProfileTimeExpr(const oscad::ProfileTimeOp& node, EvalContext& ctx);
|
|
840
857
|
void bindLetName(EvalContext& ctx, const std::string& name, const Value& v);
|
|
841
858
|
|
|
842
859
|
// Bracketed public in place: Stage 2's Op::NativeStatement
|
|
@@ -146,6 +146,12 @@ std::vector<ColoredBody> generateChildren(Evaluator& ev, const CSGParams& params
|
|
|
146
146
|
// generateTree()'s default child-concatenation, exactly reproducing
|
|
147
147
|
// passthrough).
|
|
148
148
|
CSGParams resolveRender(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
|
|
149
|
+
CSGParams resolveProfileTime(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx);
|
|
150
|
+
// profile_time()'s message pieces: "12.40 ms" (or "4.7 µs" under 1 ms), and the label it names --
|
|
151
|
+
// the label as written (a non-string formatted like echo() would), or
|
|
152
|
+
// "line N" (with the file when there is one) when none was given.
|
|
153
|
+
std::string fmtMs(double ms);
|
|
154
|
+
std::string profileLabel(const Value& label, const oscad::Position& pos);
|
|
149
155
|
|
|
150
156
|
// breakpoint() -- no-op as of Phase 4 (no debugger until Phase 9); still
|
|
151
157
|
// evaluates/validates its optional `condition` argument for parity.
|
|
@@ -118,34 +118,86 @@ manifold::Manifold extrudeTwisted(const manifold::Polygons& polys, double height
|
|
|
118
118
|
BuiltinWrapParams computeLinearExtrudeParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
|
|
119
119
|
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
120
120
|
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
// Positional order is upstream's: height, v, scale, center, twist,
|
|
122
|
+
// slices, segments. Only height used to be read positionally.
|
|
123
|
+
//
|
|
124
|
+
// Upstream's LinearExtrudeNode: the extrusion runs along a vector, (0,0,1)
|
|
125
|
+
// unless `v` gives one. Its length is `height` (`h` its alias, BOSL2's
|
|
126
|
+
// override forwards it) when given -- `v` then only sets the direction --
|
|
127
|
+
// else |v| when `v` is given, else 100. A vector pointing down (z <= 0)
|
|
128
|
+
// extrudes nothing. Bad values warn and fall back as upstream's do.
|
|
129
|
+
// `v` was ignored here, so an oblique extrusion came out straight.
|
|
130
|
+
double hv[3] = {0.0, 0.0, 1.0};
|
|
131
|
+
double length = 100.0;
|
|
132
|
+
const Value vArg = getArg(args, 1, "v", Value{});
|
|
133
|
+
if (!std::holds_alternative<std::monostate>(vArg)) {
|
|
134
|
+
const ListPtr* l = std::get_if<ListPtr>(&vArg);
|
|
135
|
+
bool ok = l && *l && (*l)->items.size() == 3;
|
|
136
|
+
double t[3] = {0, 0, 0};
|
|
137
|
+
for (int i = 0; ok && i < 3; ++i) {
|
|
138
|
+
const double* d = std::get_if<double>(&(*l)->items[i]);
|
|
139
|
+
ok = d && std::isfinite(*d);
|
|
140
|
+
if (ok) t[i] = *d;
|
|
141
|
+
}
|
|
142
|
+
if (ok) std::copy(t, t + 3, hv);
|
|
143
|
+
else ev.warn("v when specified should be a 3d vector", &node.position());
|
|
144
|
+
length = 1.0;
|
|
145
|
+
}
|
|
146
|
+
const Value heightArg = getArgOrAlias(ev, &node.position(), args, 0, "height", "h", Value{});
|
|
147
|
+
if (!std::holds_alternative<std::monostate>(heightArg)) {
|
|
148
|
+
const double* d = std::get_if<double>(&heightArg);
|
|
149
|
+
if (d && std::isfinite(*d)) {
|
|
150
|
+
length = *d;
|
|
151
|
+
} else {
|
|
152
|
+
ev.warn("height when specified should be a number", &node.position());
|
|
153
|
+
length = 100.0;
|
|
154
|
+
}
|
|
155
|
+
const double norm = std::sqrt(hv[0] * hv[0] + hv[1] * hv[1] + hv[2] * hv[2]);
|
|
156
|
+
for (double& c : hv) c /= norm;
|
|
157
|
+
}
|
|
158
|
+
for (double& c : hv) c *= length;
|
|
159
|
+
if (hv[2] <= 0) hv[2] = 0;
|
|
160
|
+
const double height = hv[2];
|
|
161
|
+
// Only a real boolean counts, as upstream: center="yes" is not true.
|
|
162
|
+
const Value centerArg = getArg(args, 3, "center", Value{false});
|
|
163
|
+
const bool* centerBool = std::get_if<bool>(¢erArg);
|
|
164
|
+
const bool center = centerBool && *centerBool;
|
|
165
|
+
const double twist = toDoubleLenient(getArg(args, 4, "twist", Value{0.0}));
|
|
126
166
|
// Upstream's validate_integral: any finite number counts as given, and is
|
|
127
167
|
// truncated and clamped (slices >= 1, segments >= 0). Not given, the
|
|
128
168
|
// discretizer decides.
|
|
129
|
-
const auto integral = [&](const char* name, double lo) -> std::optional<double> {
|
|
130
|
-
const Value v = getArg(args,
|
|
169
|
+
const auto integral = [&](int pos, const char* name, double lo) -> std::optional<double> {
|
|
170
|
+
const Value v = getArg(args, pos, name, Value{});
|
|
131
171
|
const double* d = std::get_if<double>(&v);
|
|
132
172
|
if (!d || !std::isfinite(*d)) return std::nullopt;
|
|
133
173
|
return *d < lo ? lo : std::trunc(*d);
|
|
134
174
|
};
|
|
135
|
-
const std::optional<double> slices = integral("slices", 1.0);
|
|
136
|
-
const std::optional<double> segments = integral("segments", 0.0);
|
|
137
|
-
const Value scaleArg = getArg(args,
|
|
175
|
+
const std::optional<double> slices = integral(5, "slices", 1.0);
|
|
176
|
+
const std::optional<double> segments = integral(6, "segments", 0.0);
|
|
177
|
+
const Value scaleArg = getArg(args, 2, "scale", Value{});
|
|
138
178
|
|
|
139
179
|
double scaleX = 1.0, scaleY = 1.0;
|
|
180
|
+
bool scaleOk = true;
|
|
140
181
|
if (const double* s = std::get_if<double>(&scaleArg)) {
|
|
141
182
|
scaleX = scaleY = *s;
|
|
183
|
+
scaleOk = std::isfinite(*s);
|
|
142
184
|
} else if (const ListPtr* l = std::get_if<ListPtr>(&scaleArg); l && *l && (*l)->items.size() >= 2) {
|
|
143
185
|
scaleX = toDoubleLenient((*l)->items[0]);
|
|
144
186
|
scaleY = toDoubleLenient((*l)->items[1]);
|
|
187
|
+
scaleOk = std::isfinite(scaleX) && std::isfinite(scaleY);
|
|
188
|
+
} else if (!std::holds_alternative<std::monostate>(scaleArg)) {
|
|
189
|
+
scaleOk = false;
|
|
190
|
+
}
|
|
191
|
+
if (!scaleOk) {
|
|
192
|
+
ev.warn("linear_extrude(..., scale=" + fmtValue(scaleArg) + ") could not be converted", &node.position());
|
|
193
|
+
scaleX = scaleY = 1.0;
|
|
145
194
|
}
|
|
146
195
|
|
|
147
196
|
CSGParams params;
|
|
148
197
|
params["height"] = Value{height};
|
|
198
|
+
// How far the top slides sideways per unit of height (0 unless `v`).
|
|
199
|
+
params["shear_x"] = Value{height > 0 ? hv[0] / height : 0.0};
|
|
200
|
+
params["shear_y"] = Value{height > 0 ? hv[1] / height : 0.0};
|
|
149
201
|
params["center"] = Value{center};
|
|
150
202
|
params["twist"] = Value{twist};
|
|
151
203
|
params["slices"] = slices ? Value{*slices} : Value{};
|
|
@@ -227,6 +279,14 @@ std::vector<ColoredBody> generateLinearExtrude(Evaluator& ev, const CSGParams& p
|
|
|
227
279
|
? extrudeTwisted(polys, height, slices, twist, scaleX, scaleY)
|
|
228
280
|
: manifold::Manifold::Extrude(polys, height, slices - 1, -twist, manifold::vec2(scaleX, scaleY));
|
|
229
281
|
if (std::get<bool>(params.at("center"))) body = body.Translate(manifold::vec3(0, 0, -height / 2));
|
|
282
|
+
// `v`: slide each point sideways in proportion to its height -- upstream
|
|
283
|
+
// places slice k at bottom + v*k/n, and centring subtracts v/2, so the
|
|
284
|
+
// same shear is right either way.
|
|
285
|
+
const double shearX = std::get<double>(params.at("shear_x"));
|
|
286
|
+
const double shearY = std::get<double>(params.at("shear_y"));
|
|
287
|
+
if (shearX != 0.0 || shearY != 0.0) {
|
|
288
|
+
body = body.Transform(manifold::mat3x4({1, 0, 0}, {0, 1, 0}, {shearX, shearY, 1}, {0, 0, 0}));
|
|
289
|
+
}
|
|
230
290
|
return {ev.tagGenerated(std::move(body), node, params.at("color"))};
|
|
231
291
|
}
|
|
232
292
|
|
|
@@ -241,11 +301,36 @@ std::vector<ColoredBody> generateLinearExtrude(Evaluator& ev, const CSGParams& p
|
|
|
241
301
|
BuiltinWrapParams computeRotateExtrudeParams(Evaluator& ev, const oscad::ModularCall& node, EvalContext& ctx) {
|
|
242
302
|
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
243
303
|
|
|
244
|
-
|
|
304
|
+
// Positional order is upstream's: angle, start.
|
|
305
|
+
//
|
|
306
|
+
// Upstream's RotateExtrudeNode: the sweep runs from `start` through
|
|
307
|
+
// `start + angle`. With an angle, start defaults to 0 (the +X axis) and
|
|
308
|
+
// an angle outside (-360, 360] becomes a full turn; with none, the turn
|
|
309
|
+
// is 360 and starts at 180 (-X), which upstream says will change, and
|
|
310
|
+
// says so for an odd $fn, where the start shows. An explicit start
|
|
311
|
+
// always wins. It was ignored here: a quarter turn from 90 drew 0..90.
|
|
312
|
+
const Value angleArg = getArgOrAlias(ev, &node.position(), args, 0, "angle", "a", Value{});
|
|
313
|
+
const Value startArg = getArg(args, 1, "start", Value{});
|
|
314
|
+
const bool hasAngle = !std::holds_alternative<std::monostate>(angleArg) && std::isfinite(toDoubleLenient(angleArg));
|
|
315
|
+
const bool hasStart = !std::holds_alternative<std::monostate>(startArg) && std::isfinite(toDoubleLenient(startArg));
|
|
316
|
+
double angle = 360.0;
|
|
317
|
+
double start = 180.0;
|
|
318
|
+
if (hasAngle) {
|
|
319
|
+
angle = toDoubleLenient(angleArg);
|
|
320
|
+
start = 0.0;
|
|
321
|
+
if (angle <= -360.0 || angle > 360.0) angle = 360.0;
|
|
322
|
+
}
|
|
323
|
+
if (hasStart) start = toDoubleLenient(startArg);
|
|
324
|
+
const Discretizer disc = Discretizer::fromCtx(effCtx);
|
|
325
|
+
if (!hasAngle && !hasStart && (static_cast<int>(disc.fn) & 1)) {
|
|
326
|
+
ev.emitWarning("DEPRECATED: In future releases, rotational extrusion without \"angle\" will start at zero, "
|
|
327
|
+
"the +X axis. Set start=180 to explicitly start on the -X axis.");
|
|
328
|
+
}
|
|
245
329
|
|
|
246
330
|
CSGParams params;
|
|
247
331
|
params["angle"] = Value{angle};
|
|
248
|
-
|
|
332
|
+
params["start"] = Value{start};
|
|
333
|
+
disc.store(params);
|
|
249
334
|
params["color"] = colorToValue(effCtx.color);
|
|
250
335
|
return BuiltinWrapParams{std::move(params), std::move(effCtx)};
|
|
251
336
|
}
|
|
@@ -299,6 +384,9 @@ std::vector<ColoredBody> generateRotateExtrude(Evaluator& ev, const CSGParams& p
|
|
|
299
384
|
v = manifold::vec3(v.x * std::cos(a), v.x * std::sin(a), sign * v.y);
|
|
300
385
|
});
|
|
301
386
|
}
|
|
387
|
+
// The arc above runs from +X; turn it to begin at `start`.
|
|
388
|
+
const double start = std::get<double>(params.at("start"));
|
|
389
|
+
if (start != 0.0) body = body.Rotate(0.0, 0.0, start);
|
|
302
390
|
return {ev.tagGenerated(std::move(body), node, params.at("color"))};
|
|
303
391
|
}
|
|
304
392
|
|
{openscad_cpp_evaluator-1.37.2 → openscad_cpp_evaluator-1.38.1}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -1018,6 +1018,7 @@ const std::unordered_map<std::string, double>& featureLevels() {
|
|
|
1018
1018
|
{"roof-op", 1.0}, // roof(), method="voronoi" only
|
|
1019
1019
|
{"svg-class", 1.0}, // import(svg, class=); id= is upstream's
|
|
1020
1020
|
{"discretization-by-error", 1.0}, // $fe, always on (upstream: behind --enable)
|
|
1021
|
+
{"profile-time", 1.0}, // profile_time(label) { ... } and profile_time(label) expr
|
|
1021
1022
|
};
|
|
1022
1023
|
return levels;
|
|
1023
1024
|
}
|