From d807508201d778ed83786f39dbd35fc6f05e2f40 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 16:53:25 +0000 Subject: [PATCH] 42-editor-mesh-repair: plan diagnosis, repair, decimate, smooth, measure, analysis Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_0182QWEHEoWzZux4ViurCaKy --- openspec/ROADMAP.md | 2 +- .../42-editor-mesh-repair/.openspec.yaml | 2 + .../changes/42-editor-mesh-repair/design.md | 44 ++++++++++ .../changes/42-editor-mesh-repair/proposal.md | 33 ++++++++ .../specs/editor-measure-analysis/spec.md | 53 ++++++++++++ .../specs/editor-mesh-health/spec.md | 46 ++++++++++ .../specs/editor-mesh-repair/spec.md | 83 +++++++++++++++++++ .../changes/42-editor-mesh-repair/tasks.md | 43 ++++++++++ 8 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 openspec/changes/42-editor-mesh-repair/.openspec.yaml create mode 100644 openspec/changes/42-editor-mesh-repair/design.md create mode 100644 openspec/changes/42-editor-mesh-repair/proposal.md create mode 100644 openspec/changes/42-editor-mesh-repair/specs/editor-measure-analysis/spec.md create mode 100644 openspec/changes/42-editor-mesh-repair/specs/editor-mesh-health/spec.md create mode 100644 openspec/changes/42-editor-mesh-repair/specs/editor-mesh-repair/spec.md create mode 100644 openspec/changes/42-editor-mesh-repair/tasks.md diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index 013ee04..9162058 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -52,7 +52,7 @@ | 39 | `39-editor-foundation` | 29, 31a | v1.10.0. **Свободный редактор** (главный режим, см. `openspec/EDITOR.md`): продукт `/editor`, оболочка (outliner, свойства, панель инструментов), сцена из объектов, импорт STL, примитивы, выбор, гизмо и числовые трансформации, привязка, undo/redo, экспорт сцены; Viewport переиспользуется и расширяется | | 40 | `40-editor-sketch` | 39 | v1.11.0. Эскиз на плоскости: инструменты (прямоугольник с радиусом углов), Fillet/Chamfer 2D, ограничения и размеры, степени свободы, импорт SVG, Extrude/Revolve в тела (join/cut/intersect, фаска/скругление краёв); тело = эскиз + список операций, сетка производная (`EDITOR.md` 2a) | | 41 | `41-editor-op-stack` | 40 | v1.12.0. Неразрушающий стек операций (пересчёт от источников, `EDITOR.md` 2a), дерево/timeline, булевы, Mirror, Pattern, Hollow, Offset | -| 42 | `42-editor-mesh-repair` | 39 | v1.13.0 (план). Диагностика и ремонт STL, Decimate, Smooth, Lay on Face, измерения, анализ нависаний | +| 42 | `42-editor-mesh-repair` | 41 | v1.13.0. Диагностика и ремонт STL, Decimate, Smooth, Lay on Face, измерения, анализ нависаний | | 43 | `43-editor-cut-split` | 41, 42 | v1.14.0 (план). Plane Cut, Split с соединителями, отверстия под магниты/винты, текст на грани | | 44 | `44-editor-face-tools` | 41 | v1.15.0 (план). Выбор граней/рёбер, Push/Pull, Fillet/Chamfer, эскиз на грани, Hole wizard | | 45 | `45-editor-project-default` | 43, 44 | v2.0.0 (план, major: `/` ведёт в редактор). Файл проекта `.pkit` (ZIP: scene.json + сетки), автосохранение, редактор — вход по умолчанию, генераторы как вставляемые объекты | diff --git a/openspec/changes/42-editor-mesh-repair/.openspec.yaml b/openspec/changes/42-editor-mesh-repair/.openspec.yaml new file mode 100644 index 0000000..97e3642 --- /dev/null +++ b/openspec/changes/42-editor-mesh-repair/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-27 diff --git a/openspec/changes/42-editor-mesh-repair/design.md b/openspec/changes/42-editor-mesh-repair/design.md new file mode 100644 index 0000000..ee8a177 --- /dev/null +++ b/openspec/changes/42-editor-mesh-repair/design.md @@ -0,0 +1,44 @@ +# Design + +## Context + +`39` provides `meshHealth` (counts), mesh sources in IndexedDB addressed by content hash, and `41` refuses booleans on open meshes. Repairs are *destructive* mesh edits (they change the source), unlike stack operations; the stack model of `41` handles that by Flatten and by applying repairs only to mesh sources: a repair produces a new immutable mesh and swaps the object's `source.meshId`, with undo restoring the old id. The reference tools are Meshmixer (Inspector, Make Solid, Reduce, Smooth), netfabb/3D Builder (auto repair) and slicer repair, plus the analysis views of PrusaSlicer/Bambu Studio. + +## Goals / Non-Goals + +**Goals**: reliable repair of typical downloaded STLs; honest reports (what was fixed, what was not); everything responsive on 500k-triangle meshes. +**Non-Goals**: geometry-preserving guarantees for broken input, quality remeshing, mesh sculpting. + +## Decisions + +1. **Half-edge-free analysis.** `analyze(mesh)` builds an edge map keyed by sorted vertex pairs (typed arrays, no objects per edge): per edge the incident triangle count and orientation consistency → open edges (1), manifold (2 consistent), non-manifold (>2), inconsistent winding (2 same direction). Boundary loops come from linking open edges; shells from union-find over triangles; volume from the signed tetrahedra; degenerate = area < 1e-12 mm² or duplicate vertex ids; duplicates = same sorted vertex triple. Output is a plain `Report` with index lists (for highlighting), no geometry objects. +2. **Self-intersections.** Triangle–triangle tests with a uniform grid broad phase (cell ≈ average edge length), Möller test, ignoring pairs that share a vertex or edge; capped at 5 M candidate pairs with a progress job and abort; reports count and the first 1000 pairs for highlighting. Large meshes can skip the check ("analyse intersections" is a button, not automatic). +3. **Repairs as pure functions** `(mesh, args) → {mesh, report}` in `geometry/mesh/`: + - `weld(tolerance)`: spatial hash welding; default 1e-4 mm relative to the bounding diagonal. + - `removeDegenerate`, `removeDuplicates`. + - `fixOrientation`: BFS over shells propagating consistent winding across manifold edges, then flip the shell if its signed volume is negative; non-manifold edges break the propagation (reported). + - `fillHoles(loops, {mode: 'flat'|'smooth', maxPerimeter})`: ear clipping on the loop projected on its best-fit plane for 'flat' (checking for self-overlap and falling back to a fan around the centroid); 'smooth' adds interior vertices by subdivision and relaxes them (Laplacian with the boundary fixed); a loop larger than the limit is skipped and reported; the existing `fillHoles` of `stl-import.ts` becomes the simple fallback. + - `removeShells({maxTriangles, maxVolume, keepLargest})`. + - `makeSolid({resolution})`: the winding-number field of the (possibly broken) mesh sampled on a grid with cell = resolution (default bbox diagonal / 400, min 0.05 mm), then `Manifold.levelSet` extraction at 0.5 — closed, manifold, self-intersection-free, slightly softened sharp edges; the memory estimate (cells × 4 bytes) is checked before starting and the resolution is raised or the job refused with the number. Marked in the UI as "rebuilds the surface". + - `autoRepair`: weld → remove degenerate/duplicates → fix orientation → fill holes (default limit 20 mm perimeter) → remove tiny shells (< 0.1 % of the largest) → re-analyse; if still not manifold and the user allowed it, Make Solid. A report lists each step with counts. +4. **Decimate.** The manifold solid path uses Manifold's `simplify(tolerance)`; the UI takes a target ratio and searches the tolerance by bisection (≤ 8 evaluations) showing the achieved triangle count and the maximum deviation estimate (≤ tolerance). A non-manifold mesh is first run through `autoRepair` or refused with the reason. The result is a new mesh; the deviation preview colours vertices by distance to the original (sampled). +5. **Smooth.** Taubin (λ|μ) smoothing on the vertex graph with cotangent-free uniform weights; parameters strength 0–1, iterations 1–50; vertices on boundary loops and on sharp edges (dihedral > angle threshold, default 40°) are fixed; area-preserving by construction (μ < 0). Applied to mesh sources only. +6. **Orientation.** `layOnFace(mesh, triIndex, tol)` finds the connected planar region (normal within tolerance 1°, dihedral growth) and returns the rotation taking its normal to −Z; the same region function `planarRegion` is exported for `44`. `bestOrientations(mesh)` evaluates up to 24 candidates (the six axis-aligned flips plus the largest flat regions' normals) and scores `a·overhangArea + b·height − c·contactArea` (weights from slicer practice, documented), returning the top three as selectable previews; applying is one history step (a transform, not a mesh change). +7. **Measure.** Picking uses the raycaster against the mesh (BVH from `three-mesh-bvh` is added here if the foundation did not need it; MIT) and snaps to the nearest vertex, edge midpoint or face centre within 8 px. Circle fit is a least-squares fit in the plane of the picked points (≥ 3 points), reported with the fit residual; wall thickness casts a ray from the picked point along the inward normal to the first back-face hit. Results are `Measurement {kind, value, points}` in mm in world space, displayed with the unit setting and rendered as lines and labels; not stored in the document. +8. **Analysis overlays.** Overhang: per-triangle angle between the face normal and the bed normal after the object's matrix, coloured by a ramp beyond the threshold, drawn as a vertex-colour attribute (non-indexed copy only for the shown object; recomputed on transform commit, not during drag). Thin wall: per-vertex inward ray distance sampled on a subset (≤ 100k rays, worker), threshold = 2 × nozzle from the export settings. Open edges/flipped faces: line segments and colour set from the report. Section view: a clipping plane on the scene materials with a slider and an axis selector; no cap geometry. +9. **Worker.** `mesh.worker.ts` runs analyses and repairs with a `MeshClient` (latest wins per lane, cancel recreates the worker, progress through the job API of `31a`), like the photo worker. Meshes are transferred, not copied, and the result mesh is stored and registered like imports. +10. **Document.** A repair changes `source.meshId`; `source.originMeshId` (new optional field) remembers the first imported id so "Revert to imported" works across sessions; unused meshes are collected at load time except those referenced by `originMeshId`. +11. **UI.** A "Mesh" section in Properties for mesh objects (Health summary → Details expands lists with Show, Repair actions with options, Decimate, Smooth, Orientation, Revert); Measure and Analysis are tool bar toggles with their own small panels. No artboard exists: built from `ui/` components and tokens. + +## Risks / Trade-offs + +- **Self-intersection and Make Solid cost** on big meshes: limits, estimates, abort and clear messages; the first is optional, the second needs a confirmation with the estimate. +- **Fill holes quality** on large or twisted loops: size limit and the 'smooth' option; the report lists skipped loops. +- **Make Solid softens details**: the UI says so and recommends the finest resolution that fits the budget; it is never applied automatically unless Auto repair is allowed to. +- **Decimate on non-manifold input** can fail or tear; repair first, refuse otherwise. +- **Overhang map is orientation dependent** and ignores bridges; it is a visual aid, not a print simulation. + +## Open Questions + +- Whether `three-mesh-bvh` is needed already (decided by the performance task in `39`/here). +- Default weights of the best-orientation score (tuned on three reference models in task 4.3). diff --git a/openspec/changes/42-editor-mesh-repair/proposal.md b/openspec/changes/42-editor-mesh-repair/proposal.md new file mode 100644 index 0000000..c97e21c --- /dev/null +++ b/openspec/changes/42-editor-mesh-repair/proposal.md @@ -0,0 +1,33 @@ +# Proposal + +## Why + +Most STL files found online are not clean: holes, flipped faces, duplicate vertices, tiny floating shells, self-intersections. They print badly or cannot be used in booleans (`41` refuses open meshes). Desktop tools (Meshmixer, 3D Builder, netfabb, slicer repair) exist largely for this. The foundation (`39`) already shows a health summary; this change adds the diagnosis in detail, the repairs, the mesh-reducing operations (Decimate, Smooth), the orientation helpers (Lay on Face, best orientation) and the measuring and analysis tools that every printing workflow uses (distance, angle, hole diameter, wall thickness, overhangs, cross-section view). + +## What Changes + +- **Detailed diagnosis** on a mesh object: open edges grouped into boundary loops (count, perimeter), non-manifold edges and vertices, degenerate and duplicate triangles, flipped triangles and inverted volume, disconnected shells (count, volumes), self-intersections (count and a progress/abort job), small shells under a threshold. Problems are listed with *Show* (highlighted in the view and framed). +- **Repairs** (each one undoable, each a new immutable mesh): Weld vertices (tolerance), Remove degenerate / duplicate triangles, Fix orientation (consistent winding, outward by volume), Fill holes (flat or smoothed fill, per loop or all, size limit), Remove small shells, Resolve self-intersections and **Make Solid** (remesh through a winding-number level set at a chosen resolution, as the last resort), and **Auto repair** (the sequence with a report and a before/after summary). +- **Decimate** (target triangle count or tolerance in mm, with before/after and a preview of the deviation) and **Smooth** (Taubin smoothing with strength and iterations, boundaries and sharp edges preserved). +- **Orientation:** **Lay on Face** (click a flat region, it goes to the bed), **Best orientation** (suggests up to three orientations scored by overhang area, height and contact area), Drop to bed is already in `39`. +- **Measure tool:** distance between two picked points (vertex, edge midpoint, face centre or free), angle between three points or two faces, circle fit (diameter of a hole or boss from three or more points), wall thickness at a point; results in a list in mm/in with copy. Measurements are session-only. +- **Analysis overlays:** overhang map for the current orientation (angle threshold, default 45°), thin-wall map below the nozzle-based threshold taken from the export settings, open-edge and flipped-face highlights, a **section view** (clipping plane along X/Y/Z with a slider). +- **Heavy work in a worker** (`mesh.worker.ts` with progress and cancel through the log jobs of `31a`), so the UI stays responsive on large meshes. + +## Capabilities + +### New Capabilities +- `editor-mesh-health`: diagnosis, listing, highlighting, shells and self-intersections. +- `editor-mesh-repair`: repairs, Make Solid, Auto repair, Decimate, Smooth, orientation helpers. +- `editor-measure-analysis`: measuring tool, overhang and thin-wall maps, section view. + +### Modified Capabilities + +## Impact + +- New `src/geometry/mesh/{analyze,weld,orient,fillHoles,shells,intersections,makeSolid,decimate,smooth,orientation}.ts` (React-free, tested with fixtures), `mesh.worker.ts` + `MeshClient`, `src/features/editor/{HealthPanel,RepairPanel,MeasureTool,AnalysisPanel,SectionSlider}`, viewport overlay hooks (highlight sets, clipping plane, per-vertex colours for maps), strings in en and ru, tests. Repaired meshes replace the object's source mesh id (the previous id stays in history; `originMeshId` keeps the import for "Revert to imported"). +- `fillHoles` of `stl-import.ts` is reused and generalised; `describeMesh`/`flatFaces` are reused for flat regions. +- The "not a closed solid" messages of `41` get a "Repair" action pointing here. +- Out of scope: sculpting-type edits, remeshing for quality (isotropic), texture/colour repair, face and edge editing (`44`), cutting and splitting (`43`), automatic support generation, slicing-based analysis, AI repair. +- Version 1.13.0 (minor: a new capability). +- Depends on: `41-editor-op-stack` (the not-solid messages and Flatten link; mesh sources and stacks), which depends on the foundation `39`. diff --git a/openspec/changes/42-editor-mesh-repair/specs/editor-measure-analysis/spec.md b/openspec/changes/42-editor-mesh-repair/specs/editor-measure-analysis/spec.md new file mode 100644 index 0000000..74c83ee --- /dev/null +++ b/openspec/changes/42-editor-mesh-repair/specs/editor-measure-analysis/spec.md @@ -0,0 +1,53 @@ +# Spec Delta + +## Purpose +Defines measuring and the analysis views. + +## ADDED Requirements + +### Requirement: Measure tool +The Editor SHALL measure the distance between two picked points, the angle between three points, the diameter of a circle fitted to three or more points, and the wall thickness at a point, snapping to vertices, edge midpoints and face centres, and showing results in the selected unit with a copy action. + +#### Scenario: Distance +- **WHEN** the user picks two opposite corners of a 40 × 30 × 20 mm block +- **THEN** the distance is shown as 53.85 mm + +#### Scenario: Hole diameter +- **WHEN** the user picks three points on the edge of a 6 mm hole +- **THEN** the diameter is shown as 6.0 mm with the fit residual + +#### Scenario: Thickness +- **WHEN** the user picks a point on the outer face of a 2 mm wall +- **THEN** the thickness is shown as 2.0 mm + +#### Scenario: Inches +- **WHEN** the unit is inches +- **THEN** the values are shown in inches and the stored millimetre values are unchanged + +### Requirement: Overhang map +The Editor SHALL colour surfaces that overhang more than a chosen angle for the current orientation, updating after a transform is committed. + +#### Scenario: Tilted cube +- **WHEN** a cube is tilted and the map is on at 45° +- **THEN** the downward faces beyond 45° are coloured and the others are not + +### Requirement: Thin-wall map +The Editor SHALL colour regions whose wall thickness is below a threshold that defaults to twice the nozzle diameter from the export settings. + +#### Scenario: Thin rib +- **WHEN** a model has a 0.5 mm rib and the nozzle is 0.4 mm +- **THEN** the rib is coloured as too thin + +### Requirement: Section view +The Editor SHALL clip the view with a plane along X, Y or Z controlled by a slider, without changing the model or exports. + +#### Scenario: Section +- **WHEN** the user moves the Z section slider to the middle of a hollow part +- **THEN** the cavity is visible and exporting still produces the whole part + +### Requirement: Analysis is view-only +Measurements and overlays SHALL NOT be saved in the document or create history steps. + +#### Scenario: Reload +- **WHEN** the page is reloaded +- **THEN** no measurements or overlays remain diff --git a/openspec/changes/42-editor-mesh-repair/specs/editor-mesh-health/spec.md b/openspec/changes/42-editor-mesh-repair/specs/editor-mesh-health/spec.md new file mode 100644 index 0000000..b78729f --- /dev/null +++ b/openspec/changes/42-editor-mesh-repair/specs/editor-mesh-health/spec.md @@ -0,0 +1,46 @@ +# Spec Delta + +## Purpose +Defines the detailed diagnosis of a mesh and how problems are listed and shown. + +## ADDED Requirements + +### Requirement: Detailed diagnosis +For a mesh object the Editor SHALL report the number of triangles and vertices, open edges grouped into boundary loops with their perimeter, non-manifold edges and vertices, degenerate and duplicate triangles, triangles with inconsistent or flipped orientation, whether the volume is negative, the number of separate shells with their volumes, and the surface area and volume. + +#### Scenario: Cube with a hole +- **WHEN** a cube with one triangle missing is analysed +- **THEN** one boundary loop of three edges is reported and the mesh is marked open + +#### Scenario: Two shells +- **WHEN** a mesh consists of a cube and a floating small sphere +- **THEN** two shells are reported with their volumes + +#### Scenario: Clean mesh +- **WHEN** the mesh is a closed manifold solid +- **THEN** the panel says it is a valid solid and shows volume and area + +### Requirement: Show problems +Each reported problem SHALL be listed and, when selected, highlighted in the view and framed by the camera. + +#### Scenario: Show a hole +- **WHEN** the user clicks a boundary loop in the list +- **THEN** its edges are drawn highlighted, and the camera frames the loop + +### Requirement: Self-intersections +The Editor SHALL detect self-intersecting triangles on request, report their count and highlight them, showing progress and allowing cancellation. + +#### Scenario: Cancel +- **WHEN** the user cancels a running intersection check +- **THEN** the job ends, the log says cancelled, and the editor stays responsive + +#### Scenario: Large mesh +- **WHEN** the candidate pair count exceeds the limit +- **THEN** the check stops with a message that gives the limit and does not freeze the page + +### Requirement: Diagnosis does not change the mesh +Analysis SHALL NOT modify the mesh or create history steps. + +#### Scenario: Analyse +- **WHEN** the user opens the health details +- **THEN** the undo history is unchanged diff --git a/openspec/changes/42-editor-mesh-repair/specs/editor-mesh-repair/spec.md b/openspec/changes/42-editor-mesh-repair/specs/editor-mesh-repair/spec.md new file mode 100644 index 0000000..065b931 --- /dev/null +++ b/openspec/changes/42-editor-mesh-repair/specs/editor-mesh-repair/spec.md @@ -0,0 +1,83 @@ +# Spec Delta + +## Purpose +Defines repairs, Make Solid, Auto repair, Decimate, Smooth and the orientation helpers. + +## ADDED Requirements + +### Requirement: Individual repairs +The Editor SHALL provide Weld vertices (with tolerance), Remove degenerate and duplicate triangles, Fix orientation, Fill holes (flat or smooth, one loop or all, with a size limit) and Remove small shells, each producing a new mesh in one undo step and reporting what changed. + +#### Scenario: Fill a hole +- **WHEN** the user fills the hole of the cube with a missing triangle +- **THEN** the mesh becomes a closed solid with the original volume + +#### Scenario: Weld +- **WHEN** a mesh has duplicated vertices at the same positions +- **THEN** after welding the edge counts are consistent and the report gives the number of merged vertices + +#### Scenario: Flipped faces +- **WHEN** a shell has inward normals +- **THEN** Fix orientation flips it and the volume becomes positive + +#### Scenario: Skipped loop +- **WHEN** a hole loop is longer than the limit +- **THEN** it is left open and listed as skipped + +### Requirement: Make Solid +The Editor SHALL offer Make Solid, which rebuilds a closed, manifold, non-self-intersecting surface at a chosen resolution, after showing the estimated memory and warning that fine details may soften. + +#### Scenario: Broken mesh +- **WHEN** a mesh with self-intersections and holes is made solid +- **THEN** the result is a valid solid usable in a Boolean + +#### Scenario: Too fine +- **WHEN** the chosen resolution would exceed the memory budget +- **THEN** the action is refused with the estimate and a coarser suggestion + +### Requirement: Auto repair +The Editor SHALL provide Auto repair that runs the standard sequence and shows a report with a count per step and the before and after status. + +#### Scenario: Typical download +- **WHEN** Auto repair runs on a mesh with duplicate vertices, one hole and flipped normals +- **THEN** the result is a valid solid, the report lists the three fixes, and one undo restores the original + +#### Scenario: Not repairable +- **WHEN** problems remain +- **THEN** the report lists them and offers Make Solid + +### Requirement: Revert to imported +The Editor SHALL keep the first imported mesh of an object and allow reverting to it. + +#### Scenario: Revert +- **WHEN** the user repaired and decimated a mesh and chooses Revert to imported +- **THEN** the original mesh returns, also after a reload + +### Requirement: Decimate +The Editor SHALL reduce the triangle count to a target or by a tolerance in millimetres, showing the before and after counts and the maximum deviation, and SHALL refuse non-manifold meshes with the reason. + +#### Scenario: Reduce +- **WHEN** a 200k-triangle sphere is decimated to 20k triangles +- **THEN** the triangle count is about 20k and the maximum deviation is within the stated tolerance + +#### Scenario: Open mesh +- **WHEN** the mesh is open +- **THEN** a message offers Auto repair first + +### Requirement: Smooth +The Editor SHALL smooth the surface with a strength and iteration count, keeping boundary loops and sharp edges, without shrinking the volume noticeably. + +#### Scenario: Noisy surface +- **WHEN** a noisy scan is smoothed +- **THEN** the noise reduces, sharp edges stay, and the volume changes by less than 1 % + +### Requirement: Lay on Face and best orientation +The Editor SHALL lay a picked flat region on the bed and suggest up to three orientations scored by overhang, height and contact area. + +#### Scenario: Lay on face +- **WHEN** the user clicks a flat side of a tilted block with Lay on Face +- **THEN** the block rotates until that side is on the bed, as one history step + +#### Scenario: Suggestions +- **WHEN** the user opens Best orientation +- **THEN** up to three previews are listed with their scores, and choosing one applies the rotation diff --git a/openspec/changes/42-editor-mesh-repair/tasks.md b/openspec/changes/42-editor-mesh-repair/tasks.md new file mode 100644 index 0000000..2cd6cc3 --- /dev/null +++ b/openspec/changes/42-editor-mesh-repair/tasks.md @@ -0,0 +1,43 @@ +# Tasks + +## 1. Spike + +- [ ] 1.1 Level set: check `Manifold.levelSet` in the installed `manifold-3d`; prototype Make Solid on a broken mesh (holes, flipped, self-intersecting) with winding-number sampling at three resolutions; record time, memory and output validity; set the memory budget and the default resolution in `design.md` Decision 3. If it cannot produce valid solids, stop for `/opsx:update`. +- [ ] 1.2 Decimate: check `simplify` behaviour on a 200k-triangle sphere and a bracket; record deviation vs tolerance and the bisection count (Decision 4). + +## 2. Analysis (React-free, `src/geometry/mesh/`) + +- [ ] 2.1 `analyze.ts`: edge map, open edges and boundary loops, non-manifold, degenerate, duplicates, orientation, shells with volumes, area and volume. Fixtures: closed cube, cube minus one triangle, two cubes, flipped cube, duplicated vertices, non-manifold edge. Test each count. +- [ ] 2.2 `intersections.ts`: grid broad phase and triangle–triangle test, pair cap, progress and abort callbacks. Test: two interpenetrating cubes, touching cubes (no report), cap message, abort. +- [ ] 2.3 `planarRegion.ts` (shared with `44`): connected flat region from a triangle with tolerance. Test: box faces, a cylinder side is not flat, tolerance behaviour. + +## 3. Repairs + +- [ ] 3.1 `weld.ts`, `removeDegenerate.ts`, `orient.ts`, `shells.ts` with reports. Test: each with counts, the weld tolerance, flipped shell only, small shells kept/removed by thresholds. +- [ ] 3.2 `fillHoles.ts` (flat with ear clipping and fan fallback, smooth with relaxation, size limit, skipped list). Test: triangle hole, square hole, a ring-shaped open end, a skipped loop; result closed and volume within tolerance. +- [ ] 3.3 `makeSolid.ts` per the spike with the memory estimate. Test: broken fixtures become valid Manifold input, volume within tolerance, refusal over budget. +- [ ] 3.4 `autoRepair.ts` and `report` text keys. Test: the typical broken fixture end to end, unrepairable case listing leftovers. +- [ ] 3.5 `decimate.ts` and `smooth.ts`. Test: triangle counts and deviation bounds, volume change under 1 % for smooth, boundary and sharp edges fixed, open mesh refused. +- [ ] 3.6 `orientation.ts`: `layOnFace`, `bestOrientations` with the scoring. Test: a tilted block gets the flat side down, the scores order axis-aligned flips of an L-shaped part as expected. + +## 4. Worker and plumbing + +- [ ] 4.1 `mesh.worker.ts` + `MeshClient` (lanes, cancel, progress to the jobs of `31a`, transfer of buffers). Test with the inline fallback like `PhotoClient`: latest wins, cancel, progress. +- [ ] 4.2 Source swap: a repair stores the new mesh (content hash), sets `source.meshId` and `originMeshId`, undo restores; garbage collection keeps referenced ids. Test: undo/redo, revert after reload, GC. +- [ ] 4.3 Tune the best-orientation weights on three reference models and record them in `design.md`. + +## 5. UI (no artboard exists: built from `ui/` components and tokens) + +- [ ] 5.1 "Mesh" section in Properties: health summary and details lists with Show (highlight sets and camera framing in `Viewport`), repair buttons with options dialogs, Auto repair report, Make Solid confirmation with the estimate, Decimate and Smooth forms, Revert. Component tests for each action and the messages; stepper assertion. +- [ ] 5.2 Measure tool: picking with snapping (add `three-mesh-bvh` if picking exceeds 50 ms on a 500k mesh), measurement list, copy, unit display, overlay lines and labels. Test the snapping and math helpers (distance, angle, circle fit, thickness) and the list component. +- [ ] 5.3 Analysis panel: overhang and thin-wall maps (vertex colours), open-edge and flipped highlights, section slider with clipping planes. Test the colour ramp and thresholds as pure functions; browser check on a tilted part and a hollow part. +- [ ] 5.4 The "not a closed solid" messages of `41` get a Repair action; strings in en and ru including `mesh.*`, `log.editor.mesh.*`. Test: key parity and the action opens the Mesh section. + +## 6. Version + +- [ ] 6.1 Bump `package.json` to 1.13.0 and add `1.13.0 — 42-editor-mesh-repair` to `CHANGELOG.md`. Test: `site.test.ts`. + +## 7. Verification + +- [ ] 7.1 `npm test` and `npm run build` are green, the dev server starts, no console errors; earlier editor features unchanged. +- [ ] 7.2 In the browser, en and ru: import three real broken STLs, read the diagnosis, repair step by step and with Auto repair, Make Solid the worst one, use the repaired mesh in a Boolean, decimate and smooth, Lay on Face and Best orientation, measure a hole and a wall against known sizes, view the overhang and thin-wall maps and the section; check the 500k-triangle performance and cancellation; undo through all and revert to imported.