# Cloth & Soft Bodies

## Learning goal
A soft surface is a network of connected particles. Pull, release or cut it and watch the solver restore their distances.

## Model and limits
Position-based dynamics with Verlet prediction, structural and shear constraints, eight solver passes and a 1/120 s internal step. The cloth cut removes both crossing constraints and matching faces. The lattice has no volume constraint and is not an incompressible material. No self-collision, bending elasticity, tearing stress law or engineering material calibration.

## Primary references
- [Muller et al.: Position Based Dynamics](https://matthias-research.github.io/pages/publications/posBasedDyn.pdf)
- [Interactive PBD reference](https://learn.physics-simulation.org/examples/pbd.html)

## Experiment design
- **Wind in a sheet**: Gravity and wind act on a pinned mesh. Parameters: `{"form":"cloth","wind":2}`.
- **Drop the cloth**: The sheet lands on the sphere and floor. Parameters: `{"form":"cloth","pins":0,"wind":0}`.
- **Soft lattice**: Inspect a braced 3D particle structure from every angle. Parameters: `{"form":"lattice","wind":1.5}`.
- **Pull the anchors**: Moving an anchor stretches nearby constraints. Parameters: `{"form":"cloth","pull":0.6,"stiffness":0.5}`.

These presets are design predictions to investigate, not claims from a usability study. All readouts, animation positions and exported observations come from the same local model. The 2D view uses the same state. Saved setups restart from their parameters, rather than claiming to resume exact progress. Captured baselines are stored separately and survive resets and preset changes.

## Verification
Run `node --test apps/shared/discovery/tests/models.test.cjs` for numerical invariants, limiting cases, determinism and failure-state checks. Run `node apps/shared/tests/catalog.test.cjs` for routes and references. Browser and keyboard validation must be reported separately from numerical tests; automated tests are not user research.

## Discovery studio interaction layer
The studio adds selectable model objects, ray-based 3D picking, keyboard-accessible object selection, an inspector, scene editing where appropriate, model-specific stepping, and 24 measured challenges across the twelve labs. This is an interaction redesign, not a claim of measured learning improvement.

Most parameter changes now preserve current state. Structural changes explicitly start a new run; the exact per-app list is in `apps/shared/discovery/studio-model.js`. Current settings, independently captured baselines and challenge progress retain separate storage. Undo/redo keeps up to 30 edit checkpoints; the timeline keeps the latest 80 model snapshots. Resuming from a historical state creates a new branch. Saved setups still restart from parameters; they do not claim to retain the timeline or local particle pin edits.

Additional instruments derive from the existing model and cited references. Camera and light sweeps hold the remaining inputs fixed; neural curves separate training and validation; compression uses actual coefficients and recomputed quality samples; traffic uses recorded car positions/speeds; factory queues and utilisation use actual events and busy time. Visible instruments can continue collecting data while the hidden 3D view stops drawing.

Run `node --test apps/shared/discovery/tests/studio.test.cjs` for live edits, preserved state, all challenge solutions, direct manipulation, rewind, picking, instrument exports and controller integration. These DOM/renderer-double checks do not replace rendered browser and device review.


## Detailed 3D apparatus
Shared procedural geometry in `apps/shared/discovery/apparatus.js` adds mechanical housings, lens and iris parts, cables, wheels, connectors, moving machine heads and subject-specific markings. The scene uses bevelled housings, smoother curved geometry, instanced material families, a key/fill/rim light arrangement and bounded soft shadows. Cloth has a procedural weave bump map attached to its original model coordinates.

Full detail can be switched to Essential detail without resetting the model. All movements use model state or model time: wheel markers use road position, machine heads use scheduled job progress, conveyors use accumulated worker time, iris blades use the illustrated aperture, and network packets use their scheduled flights. Signal and energy markers illustrate direction, not measured transit speed. The fan is an indicator of the wind setting; it does not add a fluid simulation. Shells, grippers, cables and surface textures do not change the underlying collision model. The 2D fallback retains the same state and geometry.

Additional checks: `node --test apps/shared/discovery/tests/apparatus.test.cjs`. Geometry, state preservation, bounded batching, semantic selection, pause/rewind consistency and fallback behavior are checked with renderer doubles; browser pixels and device frame rates remain unverified.


## Authored model assets
The physical-object labs now load original Blender-authored GLB assets through `model-assets.js` and `model-worlds.js`: articulated industrial arm, mirrorless camera, car (overview and inspection versions), network router, gantry station, optical laser and companion character. The geometry, authored material surfaces and packed texture maps replace corresponding primitive bodies only after loading succeeds. Source `.blend` files, reproducible Python generators, manifests and inspected Blender renders are retained in `assets/models/` and `scripts/`. Mathematical data surfaces stay tied to their original calculations.

The robot uses actual solved joints; camera iris hinges respond to the aperture and a Cutaway toggle exposes the sensor/rays; car wheels and brake lights follow model position/acceleration; factory heads use scheduled job progress. GLB equipment covers remain illustrative, not new collision or optical physics. Camera cutaway distances are display distances; numerical thin-lens readings remain authoritative. The companion uses the motion model's translation and squash/stretch.

Assets load on demand, repeated equipment shares instanced geometry/materials, and only the selected close-up car upgrades from the lighter road mesh. Failure preserves the diagram and exposes Retry model. Inspect model fits the selected geometry rather than applying a fixed camera distance. Full/Essential detail, pause, rewind and existing models/data remain available. A procedural studio reflection environment lights PBR surfaces without external network requests.

Run `node --test apps/shared/discovery/tests/model-assets.test.cjs` for real GLB parsing, file loading, texture/UV presence, hierarchy transforms, exact solver alignment, cutaways, pause/rewind, fitting and car detail selection. Asset product renders were visually inspected in Blender. Browser pixels and device frame rates are still unverified; product renders are not screenshots of the web app.

## Bespoke rebuild (2026-10)
The shared discovery template is replaced by a page built for this topic. `model.js` and `config.js` are unchanged; every number on the page comes from the model state or from a formula in `cloth-derive.js`.

**Files**
- `index.html`: SEO head kept; new body. It has the hero, six "Start with a question" presets, a toolbar (grab or cut tool, play/pause, ¼× slow motion, seam cut, repair, reset), six readouts, and a bench grid: a controls rail beside the 3D stage and the constraint map, so sliders and the picture are on screen together at 1366×900. Below that come "What's happening", three checked challenges, how the model works, honest limits and sources.
- `cloth-derive.js`: adapter. Link stretch (length/rest − 1), mean |stretch|, top-edge sag (pin line to lowest top-row particle), implied speed (|x − x_prev|/Δt), floor and ball contacts, and lattice volume (64 cubes × 6 tetrahedra). Also grab (temporary pin at the pointer, using an anchor corrected for the model's `pull` offset), single-corner pin toggles, free cut (removes links whose projected segment crosses the stroke, then faces that lost an edge, as the model's own seam cut does), a flat-drop start for the drape question, and ball-radius easing.
- `cloth3d.js`: three.js stage with orbit, zoom, pan, keyboard control and reset view. It renders on demand, pauses when hidden or off-screen, caps pixel ratio on phones and falls back to the map if WebGL fails. The surface is the model's triangle list with a procedural linen tea-towel texture, weave bump and sheen; links and lattice nodes are instanced cylinders and spheres coloured by stretch. It also draws brass pins and wires, the collision ball and wind streaks (off for reduced motion).
- `constraint-map.js`: the 2D view, a real diagram. It is the constraint network projected front, side or top at a fixed labelled scale in model units. Each link is coloured and thickened by stretch; it also shows pins, the ball outline, floor, gravity and wind arrows, a sag dimension line, removed links as dashed red ghosts and a hover read-out per link. Grab and cut work on the map too. Under it sit a stretch histogram (√ scale) and a 20 s line chart of largest and average stretch.
- `app.js`, `styles.css`: controller and styles. Phones show a 3D or map switch, with the view sticky at the top while the controls scroll under it.

**Input schedules and limits added by the page**
- The ball radius eases toward the slider at 0.8 units per model second. A run starts with the radius at 0 and grows it. Without this, the ball, which overlaps the hanging sheet at (0, 1.5, 0.2), would fling particles out in one 1/120 s step.
- The drape question starts the sheet flat 0.35 units above the ball with every pin off.
- With zero wind, moving the pins inward jams the perfectly flat sheet in its own plane rather than sagging, because no force points out of the plane. The page explains this when it happens, and the sag question uses a 0.3 breeze.
- A grabbed particle just right of centre (0 < x ≤ pull) cannot be placed exactly, because the model adds `pull` to any anchor with x > 0.
- The ball and floor have no friction, so draped cloth slides off. There is no self-collision or bending stiffness, and no volume constraint in the soft block.
- Challenges were checked as solvable in Node and in the browser: wind ≥ 3 with largest stretch < 2% (gravity 5, stiffness 1); sag 0.40–0.50 (pins −0.6 to −0.7 with light wind, or pins −0.3 with stiffness 0.4); 60 or more particles on the floor with one pin left (cut the seam, unpin one corner).
