fix(blender): fill automesh Dense + snap the interactive tools to Front Ortho#187
fix(blender): fill automesh Dense + snap the interactive tools to Front Ortho#187danko-nobre wants to merge 5 commits into
Conversation
…UDY + TODO) Two interactive-tool authoring fixes found while evaluating three reported issues: Automesh Dense collapsing onto Simple when the annulus is on, and the tools not locking to Front Orthographic (the meshes are 2D, authored on the Y=0 picture plane). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nt Ortho Automesh Dense: the eroded inner ring was auto-detected as a CDT hole (output_type=2) and the interior Steiner grid was clipped outside it, so Dense only densified the thin perimeter band while the centre stayed an empty ring - identical to Simple. The inner ring is now a constraint loop only (output_type=2 carves genuine alpha holes only) and the grid fills the whole silhouette, so Dense yields a filled dense mesh. margin_pixels reworded (no longer a ring/hole) with its pt-BR catalog entry updated. Front-ortho: ViewSnapshot moves to bpy_helpers/_shared with a FrontOrthoModalMixin; Automesh authoring, Manual Mesh and Edit Weights snap to Front Orthographic on invoke (default on, reusing Quick Armature's already-translated toggle string) and restore on exit, guarded so a no-region context is a no-op. Gates green: ruff, mypy, 1002 repo-root pytest, 299 in-Blender operator tests, 8/8 goldens byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📜 Recent review details⏰ Context from checks skipped due to timeout. (1)
|
| Layer / File(s) | Summary |
|---|---|
Interior fill logic apps/blender/core/bpy_helpers/automesh/bridge.py, apps/blender/core/bpy_helpers/automesh/cdt.py |
Interior point generation and CDT triangulation now treat the inner ring as filled instead of a hole. |
Margin wording and regression tests apps/blender/properties/scene_props.py, apps/blender/core/i18n_locales/pt_br.py, apps/blender/tests/operators/test_automesh_authoring.py |
Margin labels/translations now describe an edge loop, and new tests cover dense-fill behavior and real-hole pruning. |
Front-orthographic snap mixin
| Layer / File(s) | Summary |
|---|---|
Tagged view snapshot and mixin apps/blender/core/bpy_helpers/_shared/view_session.py, apps/blender/tests/operators/test_automesh_authoring.py |
ViewSnapshot now carries a tag, and FrontOrthoModalMixin adds enter/exit front-ortho helpers plus a headless lifecycle test. |
Quick Armature tagged snapshot apps/blender/operators/armature/quick_armature.py |
Quick Armature switches to the shared ViewSnapshot and tags its snapshot lifecycle. |
Automesh authoring front-ortho wiring apps/blender/operators/automesh/automesh_authoring.py |
Automesh authoring now enters and exits the front-ortho view session through the mixin. |
Draw Mesh Vertices front-ortho wiring apps/blender/operators/automesh/draw_mesh_vertices.py |
Draw Mesh Vertices now uses the mixin for front-ortho setup and teardown. |
Edit Weights front-ortho wiring apps/blender/operators/skinning/edit_weights.py |
Edit Weights now uses the mixin for front-ortho setup and teardown. |
Spec 078 documentation
| Layer / File(s) | Summary |
|---|---|
Spec 078 study, TODO, and index entry specs/078-authoring-dense-and-ortho/STUDY.md, specs/078-authoring-dense-and-ortho/TODO.md, specs/index.md |
New spec documents describe the dense-fill and front-ortho changes, and the spec index gains the planned entry. |
Estimated code review effort: 4 (Complex) | ~55 minutes
Possibly related PRs
- firebound/proscenio#121: Also changes
PROSCENIO_OT_edit_weights_modallifecycle and teardown behavior. - firebound/proscenio#144: Related to the
FrontOrthoModalMixinfront-ortho locking workflow. - firebound/proscenio#184: Also touches the shared
ViewSnapshotrefactor used by Quick Armature.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly summarizes both main changes: fixing Automesh Dense fill behavior and snapping interactive tools to Front Orthographic. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✨ Finishing Touches
📝 Generate docstrings
- Create stacked PR
- Commit on current branch
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
fix/spec-078-authoring-dense-and-ortho
✨ Simplify code
- Create PR with simplified code
- Commit simplified code in branch
fix/spec-078-authoring-dense-and-ortho
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/blender/core/bpy_helpers/automesh/cdt.py (1)
174-186: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftDon’t switch the CDT pass to hole mode when the margin ring is present
output_type=2still applies nested-loop hole detection across all constraints, so a non-emptyholes_worldcan make the erodedinner_worldbehave like a hole again; the laterdelete_faces_inside_holes()pass only removes faces forholes_worldand won’t restore missing margin-fill faces. Keep the margin-ring path separate and add a regression test formargin_pixels > 0plus an alpha hole or cut stroke.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/blender/core/bpy_helpers/automesh/cdt.py` around lines 174 - 186, The CDT selection in cdt.py is still switching to hole mode based on holes_world, which can cause the margin ring (inner_world) to be treated as a hole again. Keep the margin-ring path separate in the CDT flow around _build_cdt_inputs and the output_type decision so only true alpha holes trigger with-holes behavior, and ensure delete_faces_inside_holes() still handles only holes_world. Add a regression test covering margin_pixels > 0 with either an alpha hole or a cut stroke to verify the filled margin stays intact.
🧹 Nitpick comments (2)
specs/078-authoring-dense-and-ortho/TODO.md (1)
35-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSplit out the already-automated headless smoke check.
The no-
region_datano-op path is already asserted bytest_front_ortho_mixin_lifecycle_noops_without_a_viewport_region, so this bullet now overstates what still needs manual QA. Please keep the interactive round-trip as manual, but mark the headless guard done.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@specs/078-authoring-dense-and-ortho/TODO.md` around lines 35 - 38, The “Verify the snap lifecycle” checklist item mixes an already automated headless no-op assertion with the remaining manual QA, so update the TODO entry to reflect that the no-`region_data` guard is already covered by test_front_ortho_mixin_lifecycle_noops_without_a_viewport_region. Keep the interactive Front Ortho round-trip in Manual QA, but mark the headless smoke sub-bullet as done or split it into a completed automated check and a separate manual verification item.apps/blender/tests/operators/test_automesh_authoring.py (1)
353-399: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winGood regression coverage for the dense-fill fix.
Consider also adding a case that combines
margin_pixels>0with a genuine hole (alpha hole orcutstroke) - see the concern raised incdt.pyaboutoutput_typereverting to the even-odd "with holes" mode in that combination, which could re-collapse the inner-ring interior into a hole even after this fix.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/blender/tests/operators/test_automesh_authoring.py` around lines 353 - 399, Add a regression test in test_automesh_authoring that covers margin_pixels > 0 together with a real hole (for example an alpha hole or a cut stroke) so the dense-fill path is exercised alongside genuine CDT holes. Update the existing test around apply_mesh/StageParams to assert the behavior still holds when a hole is present, since cdt.py can switch output_type back to the even-odd “with holes” mode in that case and reintroduce the collapse.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@apps/blender/core/bpy_helpers/automesh/cdt.py`:
- Around line 174-186: The CDT selection in cdt.py is still switching to hole
mode based on holes_world, which can cause the margin ring (inner_world) to be
treated as a hole again. Keep the margin-ring path separate in the CDT flow
around _build_cdt_inputs and the output_type decision so only true alpha holes
trigger with-holes behavior, and ensure delete_faces_inside_holes() still
handles only holes_world. Add a regression test covering margin_pixels > 0 with
either an alpha hole or a cut stroke to verify the filled margin stays intact.
---
Nitpick comments:
In `@apps/blender/tests/operators/test_automesh_authoring.py`:
- Around line 353-399: Add a regression test in test_automesh_authoring that
covers margin_pixels > 0 together with a real hole (for example an alpha hole or
a cut stroke) so the dense-fill path is exercised alongside genuine CDT holes.
Update the existing test around apply_mesh/StageParams to assert the behavior
still holds when a hole is present, since cdt.py can switch output_type back to
the even-odd “with holes” mode in that case and reintroduce the collapse.
In `@specs/078-authoring-dense-and-ortho/TODO.md`:
- Around line 35-38: The “Verify the snap lifecycle” checklist item mixes an
already automated headless no-op assertion with the remaining manual QA, so
update the TODO entry to reflect that the no-`region_data` guard is already
covered by test_front_ortho_mixin_lifecycle_noops_without_a_viewport_region.
Keep the interactive Front Ortho round-trip in Manual QA, but mark the headless
smoke sub-bullet as done or split it into a completed automated check and a
separate manual verification item.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2d8f3b3e-71ec-4da9-b993-70d90635842a
📒 Files selected for processing (13)
apps/blender/core/bpy_helpers/_shared/view_session.pyapps/blender/core/bpy_helpers/automesh/bridge.pyapps/blender/core/bpy_helpers/automesh/cdt.pyapps/blender/core/i18n_locales/pt_br.pyapps/blender/operators/armature/quick_armature.pyapps/blender/operators/automesh/automesh_authoring.pyapps/blender/operators/automesh/draw_mesh_vertices.pyapps/blender/operators/skinning/edit_weights.pyapps/blender/properties/scene_props.pyapps/blender/tests/operators/test_automesh_authoring.pyspecs/078-authoring-dense-and-ortho/STUDY.mdspecs/078-authoring-dense-and-ortho/TODO.mdspecs/index.md
📜 Review details
⚠️ CI failures not shown inline (2)
GitHub Actions: CI / test-blender: fix(blender): fill automesh Dense + snap the interactive tools to Front Ortho
Conclusion: failure
##[group]Run ~/blender/blender --background \
�[36;1m~/blender/blender --background \�[0m
�[36;1m --python packages/validator/scripts/validate_automesh.py \�[0m
�[36;1m -- --ci-only�[0m
shell: /usr/bin/bash -e {0}
##[endgroup]
Traceback (most recent call last):
File "/home/runner/work/proscenio/proscenio/packages/validator/scripts/validate_automesh.py", line 50, in <module>
from proscenio_validator.cli import main # noqa: E402
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/proscenio/proscenio/packages/validator/src/proscenio_validator/cli.py", line 16, in <module>
from .measurement import load_fixture, run_validation
File "/home/runner/work/proscenio/proscenio/packages/validator/src/proscenio_validator/measurement.py", line 18, in <module>
from .coverage import measure_coverage
File "/home/runner/work/proscenio/proscenio/packages/validator/src/proscenio_validator/coverage.py", line 17, in <module>
from core.geometry_2d import point_in_triangle_xz
ModuleNotFoundError: No module named 'core.geometry_2d'
Blender 5.1.1 (hash b70da489d7f4 built )
Blender quit
GitHub Actions: CI / 0_test-blender.txt: fix(blender): fill automesh Dense + snap the interactive tools to Front Ortho
Conclusion: failure
Current runner version: '2.335.1'
##[group]Runner Image Provisioner
Hosted Compute Agent
Version: 20260624.560
Commit: 925d229a51159bc391ae97e54a2dd1fe20af789d
Build Date:
Worker ID: {5a5b7ab0-9349-4121-83c4-dd3683f2b32b}
Azure Region: westus
##[endgroup]
##[group]Operating System
Ubuntu
24.04.4
LTS
##[endgroup]
##[group]Runner Image
Image: ubuntu-24.04
Version: 20260628.225.1
Included Software: https://github.com/actions/runner-images/blob/ubuntu24/20260628.225/images/ubuntu/Ubuntu2404-Readme.md
Image Release: https://github.com/actions/runner-images/releases/tag/ubuntu24%2F20260628.225
##[endgroup]
##[group]GITHUB_TOKEN Permissions
Contents: read
Metadata: read
##[endgroup]
Secret source: Actions
Prepare workflow directory
Prepare all required actions
Getting action download info
Download action repository 'actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0' (SHA:9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)
Download action repository 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' (SHA:55cc8345863c7cc4c66a329aec7e433d2d1c52a9)
Complete job name: test-blender
##[group]Run actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
with:
lfs: true
repository: firebound/proscenio
***REDACTED***
ssh-strict: true
ssh-user: git
persist-credentials: true
clean: true
sparse-checkout-cone-mode: true
fetch-depth: 1
fetch-tags: false
show-progress: true
submodules: false
set-safe-directory: true
allow-unsafe-pr-checkout: false
##[endgroup]
Syncing repository: firebound/proscenio
##[group]Getting Git version info
Working directory is '/home/runner/work/proscenio/proscenio'
[command]/usr/bin/git version
git version 2.54.0
[command]/usr/bin/git lfs version
git-lfs/3.7.1 (GitHub; linux amd64; go 1.24.4)
##[endgroup]
Temporarily overriding HOME='/home/runner/work/_temp/329f164c-ae82-46bf-a48a-94d2f9c8899b' before making global git config changes
Adding repository directory to the te...
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2026-07-01T19:27:59.389Z
Learnt from: danko-nobre
Repo: firebound/proscenio PR: 183
File: apps/blender/tests/operators/test_spritesheet_slicer.py:0-0
Timestamp: 2026-07-01T19:27:59.389Z
Learning: In this repo, pre-commit pins an older ruff version while CI runs a newer ruff via `uvx ruff`, and the differing formatters can change how multiline `assert cond, "msg"` statements are wrapped (causing `ruff format --check` to fail in CI even if pre-commit passes). To avoid formatter drift, write these asserts in a form that both ruff versions preserve—e.g., keep the assert effectively single-line by assigning the condition first and then using `assert cond, "msg"` (such as `cond = <expr>; assert cond, "msg"`), rather than relying on multiline wrapping of the boolean expression or message.
Applied to files:
apps/blender/properties/scene_props.pyapps/blender/core/i18n_locales/pt_br.pyapps/blender/core/bpy_helpers/automesh/cdt.pyapps/blender/operators/automesh/draw_mesh_vertices.pyapps/blender/core/bpy_helpers/automesh/bridge.pyapps/blender/operators/automesh/automesh_authoring.pyapps/blender/operators/skinning/edit_weights.pyapps/blender/tests/operators/test_automesh_authoring.pyapps/blender/operators/armature/quick_armature.pyapps/blender/core/bpy_helpers/_shared/view_session.py
📚 Learning: 2026-07-04T04:02:16.849Z
Learnt from: danko-nobre
Repo: firebound/proscenio PR: 185
File: apps/blender/panels/weight_paint.py:396-403
Timestamp: 2026-07-04T04:02:16.849Z
Learning: In the Proscenio Blender add-on under apps/blender/, when passing UI text via keyword `text=` to Blender layout methods like `bpy.types.UILayout.label(...)`, `.prop(...)`, `.operator(...)`, and `.menu(...)`, do not wrap the literal string in `iface()` for translation. These APIs default to `translate=True`, and Blender will resolve the literal `text=` value through the registered i18n translation table (bl_rna) the same way static `bl_label`/property strings do. The i18n extraction script `scripts/blender/extract_i18n.py` catalogs these literal `text=` strings directly (guarded by `tests/test_i18n_coverage.py` for missing/stale entries), so only dynamic `text=` values (e.g., f-strings) should remain in the deferred unwrapped worklist requiring manual handling.
Applied to files:
apps/blender/properties/scene_props.pyapps/blender/core/i18n_locales/pt_br.pyapps/blender/core/bpy_helpers/automesh/cdt.pyapps/blender/operators/automesh/draw_mesh_vertices.pyapps/blender/core/bpy_helpers/automesh/bridge.pyapps/blender/operators/automesh/automesh_authoring.pyapps/blender/operators/skinning/edit_weights.pyapps/blender/tests/operators/test_automesh_authoring.pyapps/blender/operators/armature/quick_armature.pyapps/blender/core/bpy_helpers/_shared/view_session.py
📚 Learning: 2026-06-11T17:28:22.365Z
Learnt from: danko-nobre
Repo: firebound/proscenio PR: 107
File: apps/blender/operators/skinning/sidecar_io.py:1-1
Timestamp: 2026-06-11T17:28:22.365Z
Learning: In the proscenio firebound/proscenio Blender app tests (apps/blender/tests/operators/**/*.py), avoid writing asserts like `assert <very_long_boolean_expression>, "msg"` that depend on ruff version–specific line wrapping. Instead, extract the long boolean expression into a named variable and then assert on that variable (e.g., `cond = <long expr>; assert cond, "msg"`) so ruff pre-commit vs CI produces identical formatting/output.
Applied to files:
apps/blender/tests/operators/test_automesh_authoring.py
📚 Learning: 2026-06-25T06:48:46.567Z
Learnt from: danko-nobre
Repo: firebound/proscenio PR: 163
File: apps/blender/tests/operators/test_import_placement.py:19-28
Timestamp: 2026-06-25T06:48:46.567Z
Learning: In Blender operator test files (e.g., under apps/blender/tests/operators), ensure the test harness fresh-loads the fixture .blend for each individual test. This forces Blender to reset bpy.data between tests, bounding any temporary datablock/datablock accumulation created by helpers and preventing cross-test contamination/leaks.
Applied to files:
apps/blender/tests/operators/test_automesh_authoring.py
🪛 GitHub Actions: CI / 0_test-blender.txt
apps/blender/tests/operators/test_automesh_authoring.py
[warning] 24-24: DeprecationWarning: 'Material.use_nodes' is expected to be removed in Blender 6.0.
🪛 LanguageTool
specs/078-authoring-dense-and-ortho/TODO.md
[grammar] ~9-~9: Ensure spelling is correct
Context: ...'s interior is excluded (cdt.py:144-149). - [ ] [bridge.py:585](../../apps/blende...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🔇 Additional comments (14)
specs/078-authoring-dense-and-ortho/STUDY.md (1)
1-43: LGTM!specs/index.md (1)
166-166: LGTM!apps/blender/core/bpy_helpers/_shared/view_session.py (2)
30-45: LGTM!Also applies to: 48-129, 132-171
143-151: 🎯 Functional CorrectnessNo change needed for
lock_to_front_ortho
TheBoolPropertyonFrontOrthoModalMixinis inherited by each operator subclass, soself.lock_to_front_orthois available and the opt-out path works as intended.> Likely an incorrect or invalid review comment.apps/blender/operators/armature/quick_armature.py (1)
57-59: LGTM!Also applies to: 116-116, 200-200
apps/blender/operators/automesh/automesh_authoring.py (1)
33-35: LGTM!Also applies to: 223-223, 293-295, 1483-1484
apps/blender/operators/automesh/draw_mesh_vertices.py (1)
39-41: LGTM!Also applies to: 123-123, 193-195, 732-733
apps/blender/operators/skinning/edit_weights.py (1)
31-33: LGTM!Also applies to: 59-59, 103-105, 200-201
apps/blender/core/bpy_helpers/automesh/bridge.py (2)
556-618: Dense-fill grid change looks correct.Passing
[]instead ofinner_worldtointerior_points_for_annulusis consistent with the fixed CDT semantics incdt.py(inner ring is a constraint loop, not a fill/hole boundary), andboundary_for_filterstill correctly includesinner_worldfor the too-close-to-boundary filter.
621-661: 🎯 Functional Correctness
_merge_extra_steinersis not the blocker here.authoring_pipelinealready filters user extras againstinner_worldbefore they reach this helper, so removing this branch would not restore any points in the current flow.> Likely an incorrect or invalid review comment.apps/blender/core/bpy_helpers/automesh/cdt.py (1)
146-152: Docstring update is accurate.Correctly documents that only genuine alpha holes drive
output_type=2, and that the inner ring is a constraint-only loop.apps/blender/properties/scene_props.py (1)
106-121: LGTM!apps/blender/core/i18n_locales/pt_br.py (1)
197-197: LGTM!Also applies to: 783-789
apps/blender/tests/operators/test_automesh_authoring.py (1)
401-420: LGTM!
…rvives a real hole CodeRabbit flagged that the earlier output_type=2-when-holes still risked the eroded inner ring being re-detected as a hole whenever a genuine alpha hole (or cut stroke) was present - CDT auto-hole-detection is unreliable against the bridge's orientation flow (the module's own note). build_mesh_via_delaunay now always uses output_type=1 (CDT_INSIDE, fill every region); real holes are carved downstream by the deterministic delete_faces_inside_holes centroid prune. Adds test_cdt_keeps_the_inner_ring_filled_alongside_a_real_hole covering margin + hole. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rdening) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|



What changed
Two interactive-tool authoring fixes (spec 078), found while evaluating three reported issues.
1. Automesh "Dense" collapsing onto "Simple". With a boundary margin (
margin_pixels > 0) the eroded inner ring was auto-detected as a CDT hole (output_type=2) and the interior Steiner grid was clipped to outside it, so Dense only densified the thin perimeter band while the whole centre stayed an empty ring - visually identical to Simple. The inner ring is now a constraint loop only (output_type=2carves genuine alpha holes only) and the interior grid fills the whole silhouette, so Dense produces a filled dense mesh distinct from Simple.margin_pixelsis reworded (it adds a silhouette edge-density loop, not a ring/hole) and its pt-BR catalog entry updated. The density module and its pure tests are untouched.2. Interactive tools not locking to Front Orthographic. The meshes are 2D (no depth, authored on the Y=0 picture plane), but only Quick Armature snapped to front ortho.
ViewSnapshotmoves frombpy_helpers/armature/tobpy_helpers/_shared/and gains aFrontOrthoModalMixin; Automesh authoring, Manual Mesh, and Edit Weights now snap to Front Orthographic on invoke (default on) and restore the prior view on exit. The mixin reuses Quick Armature's exact toggle string, so it adds no new i18n entry. A per-tool opt-out panel toggle is a documented fast-follow.Why
Both were reported as authoring correctness/UX bugs. The annulus fix restores the meaning of the Dense/Simple toggle; the front-ortho snap keeps 2D authoring on the flat picture plane by default.
How to test
pnpm/Node not involved. Blender addon:uv run pytest tests/- 1002 passed (i18n reverse-coverage + the pure density suite unchanged).run_operator_tests.py) - 299 passed, including a newtest_boundary_margin_does_not_starve_dense_interior_fill(verified it fails without the fix: Dense and Simple both 128 verts) and aFrontOrthoModalMixinno-region smoke test.run_tests.py) - 8/8 byte-identical (the mesh-gen change is authoring-time; baked goldens unaffected).ruff+ruff-format+mypy (apps/blender)green (pre-commit).Checklist
format_versionchanged, migration documented (n/a)Spec:
specs/078-authoring-dense-and-ortho/. Third reported issue (leftover preview state cycling automesh <-> manual mesh with reverts) is out of scope pending a repro disambiguation.Generated with Claude Code
Summary by CodeRabbit