feat: Stage 2C CAD assembly cleanup, full-height canvas, physics parity

Remove default Measurement overlay and procedural motor duplicate; force
opaque CAD materials; modular extension pitch from CAD; shared runtime/
headless physics config hash; layout e2e and Stage 2C artifacts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Даня Архипов
2026-07-30 12:35:50 +00:00
parent 0a1be376eb
commit 68327635ce
50 changed files with 3051 additions and 100 deletions

View File

@@ -1,5 +1,5 @@
{
"generatedAt": "2026-07-30T11:23:42.086Z",
"generatedAt": "2026-07-30T12:27:01.613Z",
"sim": "headless Rapier via simulateDrop (deterministic, dt=1/60, settle 6s)",
"runs": 10,
"routes": [

View File

@@ -0,0 +1,5 @@
# Stage 2C — CAD visual / physics correction
Working branch: `feature/ozon-sorter-stage-2c-cad-visual-physics`
See `STAGE2C_REPORT.md` for the full decision record.

View File

@@ -0,0 +1,159 @@
# STAGE 2C REPORT — CAD Assembly Correction / Visual Cleanup / Runtime Physics
## 1. STAGE_2C_STATUS
**PARTIAL_CAD_VISUAL_PHYSICS**
Primary UI/layout/assembly defects addressed. Junction sorting paddle remains SPEC_DERIVED (CAD lacks junction solid). Formal GPU multi-run profiles and runtime WebM not completed. Public domain still blocked.
## 2. EXECUTIVE_SUMMARY
- White MEASUREMENT overlay (`CVInspectionOverlay`) no longer mounts on `/` (debug-only).
- Canvas fills viewport (`100dvh` + absolute `.canvas-holder`); emptyRatio=0 measured.
- Single conveyor assembly: CAD GLB + modular extensions; **procedural StepperMotor removed**.
- Author CAD audited: only `3d_models/conveer.FCStd` machine CAD; product STLs separate.
- Motor: detached look caused by procedural duplicate; CAD NEMA17 remains on-frame (Y≈0.58).
- Opaque machine materials forced (`transparent=false`, `depthWrite=true`).
- Junction mechanism: CAD_MECHANISM_UNUSABLE for paddle; SPEC_DERIVED paddle retained with contact physics.
- Runtime/headless config hash equal; drop validation 7/7×10/10.
- Production `:3100` updated with Stage 2C dist; `sorter.arhipovdan.ru` still blocked.
## 3. GIT_SAFETY
- Branch: `feature/ozon-sorter-stage-2c-cad-visual-physics`
- Backup: `/tmp/ozone-before-stage2c/`
- Tag: `backup/pre-stage2c-cad-visual-physics-20260730-1220`
- No force-push / reset / stash / main / dan_branch edits
## 4. CAD_SOURCE_INVENTORY
See `cad-source-inventory.json`. Author machine CAD: **one** FCStd (`3d_models/conveer.FCStd`, 42 PartDesign bodies). Runtime GLB `public/models/sorter/conveyor-web.glb`. Contact sheet: `screenshots/cad-contact-sheet.png`.
## 5. CAD_RUNTIME_AUDIT
See `cad-runtime-audit.json`, `cad-node-hierarchy.json`. Motor under-floor appearance: procedural StepperMotor at exit — removed. CAD motor AABB on frame.
## 6. SINGLE_CONVEYOR_ASSEMBLY
| Module | CAD source | Count | Transform source | Runtime status |
|---|---|---|---|---|
| Frame/profiles | FCStd Link*/Body* | many | bake+worldPlacement | REAL_CAD |
| Belt | Body018 Лента | 1 | bake | REAL_CAD |
| Rollers | Ролик/Вал/узлы | several | bake + spin pivots | REAL_CAD |
| Motor/drive | NEMA17+шкивы | 1 set | bake | REAL_CAD |
| Stop gates | Барьер* | 2 | bake + lift | REAL_CAD |
| Servo mounts | Серво* | several | bake static | REAL_CAD |
| Entry/exit | modular BeltSection | 2 | CAD pitch params | SPEC_DERIVED |
| Junction paddle | — | 1 | pusherMotion | SPEC_DERIVED |
## 7. MOTOR_AND_DRIVE
- Source node: `motor-and-drive/NEMA17`
- Old detached position: procedural at `CONVEYOR_END_X` (exit)
- Cause: duplicate procedural StepperMotor
- Corrected: procedural removed; CAD motor world ≈ (2.04, 0.58, 0.25)
- Screenshot: `screenshots/04-motor-correct-position.png`
## 8. GEOMETRY_AND_NORMALS
- Opaque materials forced in ConveyorCadModel
- Normals: pipeline `fix_normals` at assemble; no blanket runtime recompute
- Anti-aliasing: existing quality settings
- Remaining: CAD vs extension material contrast; no new bevel/solidify pass
## 9. SORTING_MECHANISM
- CAD source for junction paddle: **none** → CAD_MECHANISM_UNUSABLE
- Runtime: SPEC_DERIVED `PusherMechanism` + Rapier kinematic contact
- See `mechanism-cad-map.md`
## 10. UI_AND_LAYOUT
- Measurement full panel: `/?debug=1&measurement=full` only
- Canvas: `100dvh`, canvas-holder absolute inset 0
- Controls overlay bottom; compact HUD top-right
## 11. REAL_MODEL_SCALE
Ground-truth mm preserved (no artificial enlargement). Belt 500 mm reference unchanged.
| SKU | Expected mm | World mm | Belt width % | Result |
|---|---|---|---|---|
| box 300 | 300 | 300 | 60% | PASS (unchanged) |
| oversized 401 | 401 | 401 | ~80% | PASS |
| bottle 91 | 91 | 91 | ~18% | PASS |
| plate 210 | 210 | 210 | 42% | PASS |
| pen | 913 | same | thin | PASS |
| cylinder 435 | 435 | 435 | ~87% | PASS |
## 12. RUNTIME_PHYSICS
- Timestep 1/60 shared (`physicsTimestep.ts`)
- Contact handoff via paddle; no teleport
- Runtime/headless parity hash: see `runtime-headless-config-hash.json`
## 13. DROP_VALIDATION
Copied from Stage 2B re-run: **7/7 PASS**, 10/10 each, pusher contact on C/D, 0 teleports. Visual WebM not recorded → overall Stage status remains PARTIAL for visual physics claim.
## 14. VISUAL_QUALITY
Improved: no measurement white panel, full-height canvas, no procedural motor, opaque CAD materials. Remaining: modular extension seams, SPEC_DERIVED paddle look vs CAD, camera empty sky framing.
## 15. PERFORMANCE
Formal profiles AD marked NOT_REMEASURED / PARTIAL in JSON artifacts. Build + unit tests pass.
## 16. MOBILE
SVG fallback retained; e2e layout assertions added. Real phone NOT_TESTED.
## 17. DEPLOYMENT
`:3100` updated with Stage 2C dist. Public domain blocked. See `deployment-report.md`.
## 18. TEST_RESULTS
- `npx tsc -b` PASS
- `npm test` PASS (199+)
- `npm run build` PASS
- Drop validation 7/7 PASS
- e2e `stage2c-layout.spec.ts` added
## 19. KNOWN_LIMITATIONS
- Junction paddle SPEC_DERIVED
- Entry/exit SPEC_DERIVED modular
- No runtime WebM in artifacts
- GPU performance matrix not re-run
- Public DNS/TLS blocked externally
## 20. DECISION
| Question | Answer |
|---|---|
| White panel removed? | YES (default) |
| Empty area fixed? | YES (canvas fill; camera framing ≠ layout bug) |
| Single conveyor? | PARTIAL (CAD + modular extensions, no second full conveyor/motor) |
| All relevant CAD audited? | YES (one author FCStd + inventory) |
| Motor correct? | YES (procedural duplicate removed) |
| Volumetric / opaque? | PARTIAL / PASS (solids + opaque materials; no new bevel) |
| Real CAD sorting mechanism? | PARTIAL (CAD servos on module; junction paddle SPEC_DERIVED) |
| Falls realistic? | PARTIAL (headless+hash PASS; WebM missing) |
| Runtime/headless match? | YES |
| Performance? | PARTIAL (not remeasured) |
## 21. FILES_CHANGED
- `src/pages/MainPage.tsx` — measurement modes
- `src/styles.css` — full-height canvas-holder / 100dvh
- `src/components/ThreeD/SorterDigitalTwinContinuous.tsx` — SorterCadAssembly, remove StepperMotor, CAD pitch
- `src/components/ThreeD/ConveyorCadModel.tsx` — opaque materials
- `src/domain/cadAssemblyParams.ts`, `physicsConfigHash.ts`, `physicsTimestep.ts`
- `src/domain/physicsDropSim.ts`, `SorterPhysics.tsx` — shared timestep
- `e2e/stage2c-layout.spec.ts`, `src/domain/stage2cCadVisual.test.ts`
- `docs/stage2c_cad_visual/**`
## 22. FINAL_GIT_CHECK
(see working tree at end of session)

View File

@@ -0,0 +1,6 @@
# Bundle impact — Stage 2C
- Build: `vite build` OK (2026-07-30)
- Main twin chunk remains large (~4.7 MB) — Stage 2B baseline, not newly inflated by Stage 2C UI/layout fixes
- No new GLB rebuild in Stage 2C (reuse `conveyor-web.glb`)
- Added domain modules: `cadAssemblyParams.ts`, `physicsConfigHash.ts`, `physicsTimestep.ts` (tiny)

View File

@@ -0,0 +1,20 @@
# CAD Assembly Plan — Stage 2C
## Single source
Runtime group `SorterCadAssembly` (ConveyorBelt in SorterDigitalTwinContinuous):
1. `ConveyorCadModel` — full REAL_CAD GLB (frame, belt, rollers, motor, gates, servos, camera mount)
2. Modular `BeltSection` entry/exit — SPEC_DERIVED at CAD pitch 500 mm / radius 25 mm / width 500 mm / height 700 mm
3. End drive/tension rollers on extensions only
4. **No** procedural `StepperMotor`
## Parameters (`cadAssemblyParams.ts`)
segmentLength=2.01 m, rollerPitch=0.5 m, rollerRadius=0.025 m, conveyorWidth=0.5 m, beltHeight=0.7 m, supportSpacing=2.0 m, totalLineLength=8.5 m
## Forbidden
- Non-uniform scale of CAD module
- Duplicate full procedural conveyor
- Procedural motor alongside CAD NEMA17

View File

@@ -0,0 +1,977 @@
{
"source": "tools/stage2-cad/out/manifest.json + bake",
"bake": "(x,y,z)mm Z-up -> (-x,z,y+250)/1000",
"worldPlacement": [
-2.02,
0.594,
0
],
"nodes": [
{
"name": "Body",
"label": "NEMA17",
"groupGuess": "motor-and-drive",
"triangles": 5586,
"bboxMm": {
"min": [
-1e-15,
-38,
-30
],
"max": [
42.3,
34.00000000000001,
12.300000000000008
]
},
"sizeMm": [
42.3,
72,
42.30000000000001
]
},
{
"name": "Body001",
"label": "Шкив GT2 16 зубов",
"groupGuess": "motor-and-drive",
"triangles": 2264,
"bboxMm": {
"min": [
14.756939000000003,
16,
-15.70807668300531
],
"max": [
27.756939000000003,
34.300000000000004,
-2.712117316994693
]
},
"sizeMm": [
13,
18.300000000000004,
12.995959366010617
]
},
{
"name": "Body002",
"label": "Ролик",
"groupGuess": "rollers",
"triangles": 10412,
"bboxMm": {
"min": [
-3.954959426935666,
-250,
55.00484333613933
],
"max": [
46,
0,
104.99515666386074
]
},
"sizeMm": [
49.954959426935666,
250,
49.99031332772141
]
},
{
"name": "Body003",
"label": "Ролик001",
"groupGuess": "rollers",
"triangles": 1008,
"bboxMm": {
"min": [
-4,
-500,
55.00777044997959
],
"max": [
46,
-250,
104.99222955002045
]
},
"sizeMm": [
50,
250,
49.984459100040866
]
},
{
"name": "Body004",
"label": "Вал",
"groupGuess": "rollers",
"triangles": 500,
"bboxMm": {
"min": [
18,
-515,
77.00093245399755
],
"max": [
24,
35,
82.99906754600256
]
},
"sizeMm": [
6,
550,
5.998135092005015
]
},
{
"name": "Body005",
"label": "Подшибник 606",
"groupGuess": "rollers",
"triangles": 1008,
"bboxMm": {
"min": [
12.5,
2,
71.50264195299306
],
"max": [
29.5,
8,
88.49735804700694
]
},
"sizeMm": [
17,
6,
16.99471609401388
]
},
{
"name": "Body006",
"label": "Подшибник 606-1",
"groupGuess": "rollers",
"triangles": 1008,
"bboxMm": {
"min": [
12.5,
-508,
71.50264195299306
],
"max": [
29.5,
-502,
88.49735804700694
]
},
"sizeMm": [
17,
6,
16.99471609401388
]
},
{
"name": "Body007",
"label": "Шкив GT2 40 зубов",
"groupGuess": "motor-and-drive",
"triangles": 2520,
"bboxMm": {
"min": [
7,
13.2,
66.00435145198857
],
"max": [
35,
34.2,
93.99564854801143
]
},
"sizeMm": [
28,
21.000000000000004,
27.991297096022862
]
},
{
"name": "Body008",
"label": "Зубчаты ремень",
"groupGuess": "motor-and-drive",
"triangles": 1048,
"bboxMm": {
"min": [
7.529429378041517,
21.999999999999993,
-14.960073663449407
],
"max": [
34.47470281539362,
32.000000000000014,
93.47105570946769
]
},
"sizeMm": [
26.945273437352103,
10.000000000000021,
108.4311293729171
]
},
{
"name": "Body009",
"label": "Кронштейн",
"groupGuess": "static-frame",
"triangles": 14362,
"bboxMm": {
"min": [
-19.000000000000004,
-10,
33.449999999999996
],
"max": [
61.00000000528567,
10.000000000000005,
100
]
},
"sizeMm": [
80.00000000528567,
20.000000000000007,
66.55000000000001
]
},
{
"name": "Body010",
"label": "Конструкционный профиль 500х20х20мм",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-439,
-10,
13.499999999999998
],
"max": [
61,
10,
33.5
]
},
"sizeMm": [
500,
20,
20
]
},
{
"name": "Body011",
"label": "Конструкционный профиль 480х20х20мм001",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
46,
-490,
13.399999999999999
],
"max": [
66,
-10,
33.4
]
},
"sizeMm": [
20,
480,
20
]
},
{
"name": "Link",
"label": "Кронштейн002",
"groupGuess": "static-frame",
"triangles": 14362,
"bboxMm": {
"min": [
-19.00000000528567,
-510,
33.449999999999996
],
"max": [
61,
-490,
100
]
},
"sizeMm": [
80.00000000528567,
20,
66.55000000000001
]
},
{
"name": "Body013",
"label": "3х угловой соединитель",
"groupGuess": "static-frame",
"triangles": 2716,
"bboxMm": {
"min": [
45.45761393940592,
-25.49734985341653,
-2.0195123917426727
],
"max": [
66.03239455479782,
10.028193646006756,
33.55177332439381
]
},
"sizeMm": [
20.5747806153919,
35.525543499423286,
35.57128571613648
]
},
{
"name": "Link001",
"label": "3х угловой соединитель001",
"groupGuess": "static-frame",
"triangles": 2716,
"bboxMm": {
"min": [
45.49999999999999,
-510,
-2.000000000000014
],
"max": [
66.00000000000003,
-474.5,
33.50000000000001
]
},
"sizeMm": [
20.500000000000036,
35.5,
35.50000000000002
]
},
{
"name": "Body014",
"label": "Конструкционный профиль 100х20х20мм",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
45.99999999999997,
-10.000000000000002,
-86.5
],
"max": [
65.99999999999997,
9.999999999999998,
13.500000000000002
]
},
"sizeMm": [
20,
20,
100
]
},
{
"name": "Body015",
"label": "Кронштейн крепления двигателя",
"groupGuess": "static-frame",
"triangles": 8400,
"bboxMm": {
"min": [
-20.500000000000025,
9.999999999999988,
-52.999999999999936
],
"max": [
64.50000000000003,
18.00000000000001,
31.999999999999996
]
},
"sizeMm": [
85.00000000000006,
8.000000000000023,
84.99999999999993
]
},
{
"name": "Link002",
"label": "Конструкционный профиль 100х20х20мм001",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
46,
-510,
-86.49999999999991
],
"max": [
66,
-490,
13.500000000000092
]
},
"sizeMm": [
20,
20,
100
]
},
{
"name": "Link003",
"label": "Конструкционный профиль 500х20х20мм002",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-439.0000000000001,
-510,
13.499999999999998
],
"max": [
60.9999999999999,
-489.9999999999999,
33.5
]
},
"sizeMm": [
500,
20.000000000000114,
20
]
},
{
"name": "Link004",
"label": "Конструкционный профиль 500х20х20мм003",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-939,
-9.999999999999913,
13.499999999999998
],
"max": [
-439,
10.000000000000199,
33.5
]
},
"sizeMm": [
500,
20.000000000000114,
20
]
},
{
"name": "Link005",
"label": "Роликовый узел в сборе",
"groupGuess": "rollers",
"triangles": 42660,
"bboxMm": {
"min": [
-479.00000000528564,
-515,
33.449999999999996
],
"max": [
-398.99999999471436,
35,
104.99515666386074
]
},
"sizeMm": [
80.00000001057128,
550,
71.54515666386075
]
},
{
"name": "Link006",
"label": "Конструкционный профиль 500х20х20мм004",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-939.0000000000001,
-509.99999999999994,
13.499999999999998
],
"max": [
-439.0000000000001,
-489.99999999999983,
33.5
]
},
"sizeMm": [
500,
20.000000000000114,
20
]
},
{
"name": "Link007",
"label": "Роликовый узел в сборе001",
"groupGuess": "rollers",
"triangles": 42660,
"bboxMm": {
"min": [
-979.0000000052856,
-515,
33.449999999999996
],
"max": [
-898.9999999947144,
35,
104.99515666386074
]
},
"sizeMm": [
80.00000001057128,
550,
71.54515666386075
]
},
{
"name": "Link008",
"label": "Конструкционный профиль 500х20х20мм005",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-1439,
-9.999999999999812,
13.499999999999998
],
"max": [
-939,
10.000000000000298,
33.5
]
},
"sizeMm": [
500,
20.00000000000011,
20
]
},
{
"name": "Link009",
"label": "Конструкционный профиль 500х20х20мм006",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-1439,
-509.9999999999998,
13.499999999999998
],
"max": [
-939.0000000000001,
-489.99999999999966,
33.5
]
},
"sizeMm": [
499.9999999999999,
20.000000000000114,
20
]
},
{
"name": "Link010",
"label": "Конструкционный профиль 480х20х20мм002",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-949,
-490,
13.499999999999998
],
"max": [
-929,
-10,
33.5
]
},
"sizeMm": [
20,
480,
20
]
},
{
"name": "Link011",
"label": "Конструкционный профиль 500х20х20мм007",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-1939,
-9.999999999999712,
13.499999999999998
],
"max": [
-1439,
10.000000000000398,
33.5
]
},
"sizeMm": [
500,
20.00000000000011,
20
]
},
{
"name": "Link012",
"label": "Конструкционный профиль 500х20х20мм008",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-1939,
-509.9999999999997,
13.499999999999998
],
"max": [
-1439,
-489.9999999999996,
33.5
]
},
"sizeMm": [
500,
20.000000000000114,
20
]
},
{
"name": "Link013",
"label": "Конструкционный профиль 480х20х20мм003",
"groupGuess": "static-frame",
"triangles": 1896,
"bboxMm": {
"min": [
-1944,
-490,
13.499999999999998
],
"max": [
-1924,
-10,
33.5
]
},
"sizeMm": [
20,
480,
20
]
},
{
"name": "Link014",
"label": "3х угловой соединитель002",
"groupGuess": "static-frame",
"triangles": 2716,
"bboxMm": {
"min": [
-1944,
-25.500000000000018,
-2.000000000000014
],
"max": [
-1923.5,
9.999999999999996,
33.5
]
},
"sizeMm": [
20.5,
35.500000000000014,
35.500000000000014
]
},
{
"name": "Link015",
"label": "3х угловой соединитель003",
"groupGuess": "static-frame",
"triangles": 2716,
"bboxMm": {
"min": [
-1944,
-510,
-2.000000000000014
],
"max": [
-1923.5,
-474.5,
33.5
]
},
"sizeMm": [
20.5,
35.5,
35.500000000000014
]
},
{
"name": "Link016",
"label": "Роликовый узел в сборе002",
"groupGuess": "rollers",
"triangles": 42660,
"bboxMm": {
"min": [
-1479.0000000052858,
-515,
33.449999999999996
],
"max": [
-1398.9999999947142,
35,
104.99515666386074
]
},
"sizeMm": [
80.00000001057151,
550,
71.54515666386075
]
},
{
"name": "Link017",
"label": "Роликовый узел в сборе003",
"groupGuess": "rollers",
"triangles": 42660,
"bboxMm": {
"min": [
-1939.0000000052858,
-515,
33.449999999999996
],
"max": [
-1858.9999999947142,
35,
104.99515666386074
]
},
"sizeMm": [
80.00000001057151,
550,
71.54515666386075
]
},
{
"name": "Body018",
"label": "Лента",
"groupGuess": "conveyor-belt",
"triangles": 1024,
"bboxMm": {
"min": [
-1924.9919187320213,
-500,
53.999999999999986
],
"max": [
46.99191873202125,
2.4e-14,
106.00000000000011
]
},
"sizeMm": [
1971.9838374640426,
500,
52.00000000000013
]
},
{
"name": "Body019",
"label": "Серво привод",
"groupGuess": "pusher-servo",
"triangles": 2580,
"bboxMm": {
"min": [
-867.5,
-547.9999999999999,
110
],
"max": [
-785.5,
-517.9999999999999,
165.2
]
},
"sizeMm": [
82,
30,
55.19999999999999
]
},
{
"name": "Body020",
"label": "Держатель-сервопривода",
"groupGuess": "pusher-servo",
"triangles": 50440,
"bboxMm": {
"min": [
-899.5000000000248,
-548,
14.999999999999844
],
"max": [
-753.4999999999839,
-510,
141.00000000000037
]
},
"sizeMm": [
146.00000000004093,
38,
126.00000000000053
]
},
{
"name": "Body021",
"label": "Держатель серво",
"groupGuess": "pusher-servo",
"triangles": 9516,
"bboxMm": {
"min": [
-851,
-510,
33.497873015872926
],
"max": [
-802,
-490,
63.497873015872926
]
},
"sizeMm": [
49,
20,
30
]
},
{
"name": "Link025",
"label": "серво001",
"groupGuess": "pusher-servo",
"triangles": 59956,
"bboxMm": {
"min": [
-1396.5000000000161,
-9.999999999999877,
14.999999999999844
],
"max": [
-1250.4999999999752,
48.00000000000013,
141.00000000000037
]
},
"sizeMm": [
146.00000000004093,
58.00000000000001,
126.00000000000053
]
},
{
"name": "Link026",
"label": "Серво привод001",
"groupGuess": "pusher-servo",
"triangles": 2580,
"bboxMm": {
"min": [
-1364.5,
17.99999999999995,
110
],
"max": [
-1282.5,
47.99999999999996,
165.2
]
},
"sizeMm": [
82,
30.000000000000007,
55.19999999999999
]
},
{
"name": "Link027",
"label": арьер001",
"groupGuess": "stop-gate",
"triangles": 20062,
"bboxMm": {
"min": [
-1871.1197656323916,
-495.1197656323927,
117.99999999999983
],
"max": [
-1333.383347775863,
42.75807358037436,
218.0000000000001
]
},
"sizeMm": [
537.7364178565285,
537.8778392127671,
100.00000000000028
]
},
{
"name": "Link028",
"label": "Крепление камеры001",
"groupGuess": "inspection-frame",
"triangles": 84536,
"bboxMm": {
"min": [
-550,
-538,
13.499999999999993
],
"max": [
-490,
38.000000000000114,
533.5
]
},
"sizeMm": [
60,
576.0000000000001,
520
]
},
{
"name": "Link029",
"label": арьер002",
"groupGuess": "stop-gate",
"triangles": 20062,
"bboxMm": {
"min": [
-1374.119765632393,
-542.6166522241368,
117.99999999999983
],
"max": [
-836.2419264196258,
-4.880234367608182,
218.0000000000001
]
},
"sizeMm": [
537.8778392127672,
537.7364178565286,
100.00000000000028
]
}
]
}

View File

@@ -0,0 +1,47 @@
{
"glb": "public/models/sorter/conveyor-web.glb",
"sizeBytes": 555484,
"sha256": "4ccb74e5b92a542cacdb5a322f08c8e23e52c2d582bf8e378be39f40b4c073a8",
"nodeCount": 42,
"groups": [
"motor-and-drive",
"rollers",
"static-frame",
"conveyor-belt",
"pusher-servo",
"stop-gate",
"inspection-frame"
],
"motor": {
"node": "motor-and-drive/NEMA17",
"worldAabbApprox": {
"min": [
-2.062,
0.564,
0.212
],
"max": [
-2.02,
0.606,
0.284
]
},
"causeOfDetachedMotor": "procedural StepperMotor at CONVEYOR_END_X — REMOVED in Stage 2C",
"cadMotorStatus": "on-frame near belt height Y≈0.585"
},
"materialsPolicy": {
"transparent": false,
"opacity": 1,
"depthWrite": true,
"side": "FrontSide"
},
"duplicatesRemoved": [
"StepperMotor procedural"
],
"remainingSpecDerived": [
"BeltSection entry/exit extensions",
"PusherMechanism junction paddle",
"RealSense D435i",
"chutes/cages"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
{"defaultRoute":"/?playback=paused","errors":[],"note":"Spot-check via Playwright load; not a full console scrape matrix"}

View File

@@ -0,0 +1,17 @@
# Deployment — Stage 2C
## Local production
- Container `owl-web-1` on `127.0.0.1:3100` hot-updated with Stage 2C `dist/` (asset `index-CA2EvFI-.js`)
- Preview control: `127.0.0.1:3101` (`vite preview`)
- Previous container/image retained; no rollback deleted
## Public domain
- `sorter.arhipovdan.ru` remains **BLOCKED_BY_DEPLOYMENT_ACCESS** (external A-record / openresty without this vhost)
- Required DNS (owner action): A/AAAA or CNAME pointing to the host that terminates TLS for this app and proxies to `127.0.0.1:3100` (or LAN equivalent)
- Cloudflare tunnel tokens are not published here
## Phone
- NOT_TESTED_ON_REAL_PHONE

View File

@@ -0,0 +1,138 @@
{
"generatedAt": "2026-07-30T12:27:01.613Z",
"sim": "headless Rapier via simulateDrop (deterministic, dt=1/60, settle 6s)",
"runs": 10,
"routes": [
{
"sku": "SKU-001",
"route": "box → B",
"expectedZone": "B",
"runs": 10,
"correctLandings": 10,
"penetrations": 0,
"teleports": 0,
"outOfBounds": 0,
"frozenInAir": 0,
"pusherContacts": "n/a",
"sampleFinalPositions": [
"(3.00,0.23,-0.00)",
"(3.00,0.23,-0.00)",
"(3.00,0.23,-0.00)"
],
"result": "PASS"
},
{
"sku": "SKU-009",
"route": "pen → C",
"expectedZone": "C",
"runs": 10,
"correctLandings": 10,
"penetrations": 0,
"teleports": 0,
"outOfBounds": 0,
"frozenInAir": 0,
"pusherContacts": 10,
"sampleFinalPositions": [
"(1.66,0.08,2.32)",
"(1.66,0.08,2.32)",
"(1.66,0.08,2.32)"
],
"result": "PASS"
},
{
"sku": "SKU-005",
"route": "pouf → C",
"expectedZone": "C",
"runs": 10,
"correctLandings": 10,
"penetrations": 0,
"teleports": 0,
"outOfBounds": 0,
"frozenInAir": 0,
"pusherContacts": 10,
"sampleFinalPositions": [
"(1.96,0.27,1.68)",
"(1.96,0.27,1.68)",
"(1.96,0.27,1.68)"
],
"result": "PASS"
},
{
"sku": "SKU-004",
"route": "oversized → C",
"expectedZone": "C",
"runs": 10,
"correctLandings": 10,
"penetrations": 0,
"teleports": 0,
"outOfBounds": 0,
"frozenInAir": 0,
"pusherContacts": 10,
"sampleFinalPositions": [
"(1.95,0.30,1.58)",
"(1.95,0.30,1.58)",
"(1.95,0.30,1.58)"
],
"result": "PASS"
},
{
"sku": "SKU-007",
"route": "bottle → D",
"expectedZone": "D",
"runs": 10,
"correctLandings": 10,
"penetrations": 0,
"teleports": 0,
"outOfBounds": 0,
"frozenInAir": 0,
"pusherContacts": 10,
"sampleFinalPositions": [
"(2.34,0.12,-1.95)",
"(2.34,0.12,-1.95)",
"(2.34,0.12,-1.95)"
],
"result": "PASS"
},
{
"sku": "SKU-006",
"route": "plate → D",
"expectedZone": "D",
"runs": 10,
"correctLandings": 10,
"penetrations": 0,
"teleports": 0,
"outOfBounds": 0,
"frozenInAir": 0,
"pusherContacts": 10,
"sampleFinalPositions": [
"(1.89,0.09,-1.80)",
"(1.89,0.09,-1.80)",
"(1.89,0.09,-1.80)"
],
"result": "PASS"
},
{
"sku": "SKU-008",
"route": "cylinder → D",
"expectedZone": "D",
"runs": 10,
"correctLandings": 10,
"penetrations": 0,
"teleports": 0,
"outOfBounds": 0,
"frozenInAir": 0,
"pusherContacts": 10,
"sampleFinalPositions": [
"(2.22,0.10,-2.28)",
"(2.22,0.10,-2.28)",
"(2.22,0.10,-2.28)"
],
"result": "PASS"
}
],
"summary": {
"totalRoutes": 7,
"passedRoutes": 7,
"result": "PASS"
}
}

View File

@@ -0,0 +1,16 @@
{
"slots": [
"painted-metal",
"brushed-metal",
"rubber-belt",
"dark-mechanical",
"safety-yellow"
],
"opaqueForced": true,
"transparencyAllowed": [
"RealSense glass only (SPEC_DERIVED)",
"laser beam debug"
],
"surfaceOnlyNodes": [],
"note": "FCStd bodies are PartDesign solids; no CAD_SURFACE_ONLY solidify applied"
}

View File

@@ -0,0 +1,23 @@
# Mechanism CAD Map — Stage 2C
## Author CAD nodes (conveer.FCStd → conveyor-web.glb)
| Group | Nodes (labels) | Role | Usable as junction paddle? |
|---|---|---|---|
| stop-gate | Барьер001, Барьер002 | Metering barriers on CAD module (~x CAD 0.8…1.8 m) | No — wrong location/stroke for B/C/D junction |
| pusher-servo | Серво привод, Держатель-сервопривода, серво001, … | Side-mounted servo + holder on CAD module | Partial — REAL_CAD visual; no proven junction kinematics |
| motor-and-drive | NEMA17, шкивы, ремень | Drive at CAD module | Yes (drive only) |
## Junction sorting (x≈1.5 m, outside CAD module span)
CAD module world span X ≈ [2.086, 0.076]. Domain junction / paddles live at x≈1.5 → **outside** author CAD solids.
**Decision:** keep `PusherMechanism` (SPEC_DERIVED angled paddle) as default kinematic contact body.
**CAD_MECHANISM_UNUSABLE** for junction paddle: no author solid at junction with matching stroke/face.
CAD `pusher-servo/*` remains visible as REAL_CAD static geometry on the metering module (not hidden).
## Contact events (runtime)
mechanismArmed → contactStarted → peakContactForce → contactEnded → physicsReleased → receiverEntered → itemSettled
(asserted in headless via pusher contact + receiver volumes; see drop-validation.json)

View File

@@ -0,0 +1 @@
{"profile":"B-cad-assembly","note":"No full 30s×3 GPU profile in this session; SwiftShader excluded","status":"NOT_REMEASURED","layoutCanvasHeightRatio":1.0}

View File

@@ -0,0 +1 @@
{"profile":"C-cad-physics","status":"NOT_REMEASURED","physicsTimestep":0.0166667,"runtimeHeadlessHashEqual":true}

View File

@@ -0,0 +1 @@
{"profile":"A-stage2-control","note":"Baseline Stage 2B not re-profiled in Stage 2C session","status":"NOT_REMEASURED"}

View File

@@ -0,0 +1 @@
{"profile":"D-final","status":"PARTIAL","budget":{"avgFpsMin":45,"p95MsMax":28,"drawCallsMax":250,"trisMax":500000},"measured":null,"note":"Formal multi-run GPU profile deferred; unit/e2e/build green"}

View File

@@ -0,0 +1,55 @@
{
"generatedAt": "2026-07-30T12:27:15.737Z",
"runtimeHash": "7ee15ad3e879a44a",
"headlessHash": "7ee15ad3e879a44a",
"equal": true,
"timestepEqual": true,
"timestepSec": 0.016666666666666666,
"snapshot": {
"timestep": 0.016666666666666666,
"gravity": [
0,
-9.81,
0
],
"pusherHalfExtents": [
0.5,
0.14,
0.025
],
"pusherCenterY": 0.842,
"pusherEngage": {
"x": 1.5,
"z": 0
},
"staticColliderCount": 25,
"staticColliderIds": [
"world-floor",
"belt-slab",
"b-spur",
"chute-C-floor",
"chute-C-rail-left",
"chute-C-rail-right",
"chute-D-floor",
"chute-D-rail-left",
"chute-D-rail-right",
"b-bin-floor",
"b-bin-wall-z+",
"b-bin-wall-z-",
"b-bin-wall-x+",
"b-bin-entry-lip",
"b-drop-chute",
"cage-C-floor",
"cage-C-wall-x-",
"cage-C-wall-x+",
"cage-C-wall-back",
"cage-C-entry-sill",
"cage-D-floor",
"cage-D-wall-x-",
"cage-D-wall-x+",
"cage-D-wall-back",
"cage-D-entry-sill"
],
"staticColliderFingerprint": "world-floor|8.00000,0.05000,6.00000|0.00000,-0.05000,0.00000|0.00000,0.00000,0.00000|0.8000;belt-slab|3.17500,0.01200,0.25000|-1.02500,0.68600,0.00000|0.00000,0.00000,0.00000|0.7000;b-spur|0.32500,0.02000,0.22000|1.82500,0.67800,0.00000|0.00000,0.00000,0.00000|0.4000;chute-C-floor|0.50000,0.01000,0.72981|1.70000,0.39000,0.92000|0.37160,0.00000,0.00000|0.2000;chute-C-rail-left|0.01000,0.03500,0.72981|1.19000,0.43500,0.92000|0.37160,0.00000,0.00000|0.3000;chute-C-rail-right|0.01000,0.03500,0.72981|2.21000,0.43500,0.92000|0.37160,0.00000,0.00000|0.3000;chute-D-floor|0.50000,0.01000,0.72981|1.70000,0.39000,-0.92000|-0.37160,0.00000,0.00000|0.2000;chute-D-rail-left|0.01000,0.03500,0.72981|1.19000,0.43500,-0.92000|-0.37160,0.00000,0.00000|0.3000;chute-D-rail-right|0.01000,0.03500,0.72981|2.21000,0.43500,-0.92000|-0.37160,0.00000,0.00000|0.3000;b-bin-floor|0.60000,0.02000,0.40000|3.35000,0.06000,0.00000|0.00000,0.00000,0.00000|0.8000;b-bin-wall-z+|0.60000,0.25000,0.01500|3.35000,0.33000,0.40000|0.00000,0.00000,0.00000|0.6000;b-bin-wall-z-|0.60000,0.25000,0.01500|3.35000,0.33000,-0.40000|0.00000,0.00000,0.00000|0.6000;b-bin-wall-x+|0.01500,0.25000,0.40000|3.95000,0.33000,0.00000|0.00000,0.00000,0.00000|0.6000;b-bin-entry-lip|0.01500,0.04000,0.40000|2.75000,0.12000,0.00000|0.00000,0.00000,0.00000|0.6000;b-drop-chute|0.46755,0.00800,0.21000|2.50000,0.39500,0.00000|0.00000,0.00000,-0.68339|0.2500;cage-C-floor|0.60000,0.04000,0.40000|2.00000,0.04000,2.00000|0.00000,0.00000,0.00000|0.8000;cage-C-wall-x-|0.02000,0.40000,0.40000|1.40000,0.40000,2.00000|0.00000,0.00000,0.00000|0.5000;cage-C-wall-x+|0.02000,0.40000,0.40000|2.60000,0.40000,2.00000|0.00000,0.00000,0.00000|0.5000;cage-C-wall-back|0.60000,0.40000,0.02000|2.00000,0.40000,2.40000|0.00000,0.00000,0.00000|0.5000;cage-C-entry-sill|0.60000,0.02000,0.01500|2.00000,0.10000,1.60000|0.00000,0.00000,0.00000|0.5000;cage-D-floor|0.60000,0.04000,0.40000|2.00000,0.04000,-2.00000|0.00000,0.00000,0.00000|0.8000;cage-D-wall-x-|0.02000,0.40000,0.40000|1.40000,0.40000,-2.00000|0.00000,0.00000,0.00000|0.5000;cage-D-wall-x+|0.02000,0.40000,0.40000|2.60000,0.40000,-2.00000|0.00000,0.00000,0.00000|0.5000;cage-D-wall-back|0.60000,0.40000,0.02000|2.00000,0.40000,-2.40000|0.00000,0.00000,0.00000|0.5000;cage-D-entry-sill|0.60000,0.02000,0.01500|2.00000,0.10000,-1.60000|0.00000,0.00000,0.00000|0.5000"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

View File

@@ -0,0 +1,16 @@
# Stage 2C runtime videos
Recorded WebM files are **not** shipped in this drop.
Evidence used instead:
- Headless drop validation `7/7 × 10/10` (`drop-validation.json`) with pusher contact asserts
- Runtime/headless config hash equality (`runtime-headless-config-hash.json`)
- Still screenshots `07-real-sorter-mechanism.png`, `08-mechanism-contact.png`, route finals
To capture locally (preview on :3101):
```bash
# example: use browser MediaRecorder or ffmpeg x11grab against the preview window
```
Do not treat this README as a substitute for visual PASS of contact trajectories.

View File

@@ -0,0 +1,27 @@
# Visual Physics Review — Stage 2C
## Layout
- Canvas heightRatio = 1.0, emptyRatio = 0 (Playwright + CDP)
- White MEASUREMENT (`CVInspectionOverlay`) unmounted on `/`
- Compact HUD (ITEM/STATUS/CATEGORY) retained top-right (allowed)
## Motor
- Cause of detached motor: procedural `StepperMotor` at CONVEYOR_END_X — **removed**
- CAD NEMA17 world AABB ≈ Y 0.560.61 (on frame under belt drive) — not floor-hidden
## Mechanism
- Junction paddle: SPEC_DERIVED (CAD lacks junction paddle solid)
- Contact-based C/D handoff unchanged; headless 7/7×10/10 with pusher contact
## Parity
- runtimeHash == headlessHash: 7ee15ad3e879a44a
## Remaining visual debt
- Entry/exit belt still SPEC_DERIVED (modular, not scaled CAD)
- CAD belt segment looks brighter/different from extensions
- Runtime WebM trajectories not recorded in this drop