Prompt utilisé pour régénérer cette page :
Page: Cloth Simulation
Description: "Verlet integration cloth physics with interactive tearing"
Category: canvas
Icon: grid
Tags: physics, simulation, verlet
Status: new
Front matter (index.md):
title: "Cloth Simulation"
description: "Verlet integration cloth physics with interactive tearing"
icon: "grid"
tags: ["physics", "simulation", "verlet"]
status: ["new"]
HTML structure (index.md):
<section class="container visual size-800 ratio-1-1 canvas-contain">
<canvas id="cloth-canvas"></canvas>
</section>
Widget files:
- _stats.right.md (weight: 10): div.cloth-stats with:
##### Statistics — <dl> with Points/Constraints/Iterations stats
Stat IDs: stat-points, stat-constraints, stat-iterations
- _controls.right.md (weight: 20): Contains controls, options, and help:
div.cloth-controls with 3 native <button> elements (not shortcode buttons):
<button id="btn-play" class="item button is-play"> with {{< icon name="play" >}}
<button id="btn-pause" class="item button is-pause"> with {{< icon name="pause" >}}
<button id="btn-reset" class="item button"> with {{< icon name="refresh" >}}
div.cloth-options with 2 fieldsets:
Physics fieldset:
Gravity: slider-gravity (0-2, step 0.05, value 0.5), display span#display-gravity
Stiffness: slider-stiffness (0.1-1, step 0.05, value 0.8), display span#display-stiffness
Iterations: slider-iterations (1-15, step 1, value 5), display span#display-iterations
Wind fieldset:
Enable wind: checkbox toggle-wind
Strength: slider-wind (0-1, step 0.05, value 0.3), display span#display-wind
div.cloth-help: "**Drag** to pull cloth · **Shift+click** to tear"
- _algorithm.after.md (weight: 90): Explains Verlet integration formula (x(t+dt) = 2*x(t) - x(t-dt) + a*dt²), constraint relaxation with correction formula, tearing threshold at 200%, interaction propagation.
Architecture (single file default.js):
- IIFE, imports panic from /_lib/panic_v3.js
- No other external dependencies
SCSS file (default.scss):
- CSS custom properties: --cloth-color-normal (var(--draw-color-primary)), --cloth-color-stress (var(--color-red)), --cloth-color-pin (var(--color-yellow))
- .cloth-controls: flex row, .is-play/.is-pause toggled by .is-running, native button reset styling
- .cloth-options: flex column with fieldset/legend sections, .option with label+range/checkbox, label has span with monospace+primary color for value display
- .cloth-stats: dl grid (1fr auto), dt secondary color, dd monospace right-aligned
- .cloth-help: centered secondary text, margin-top
CONFIG defaults:
canvasSize: 800, gridCols: 30, gridRows: 25, spacing: 15,
gravity: 0.5, stiffness: 0.8, damping: 0.99,
constraintIterations: 5, tearThreshold: 2.0,
windEnabled: false, windStrength: 0.3, mouseRadius: 30
Cloth data structure:
- Points array: objects with {x, y, oldX, oldY, pinned}
- Constraints array: objects with {p1, p2, restLength, torn?}
- Grid: 30×25 points, top row pinned every 3rd point (col % 3 === 0)
- Horizontal constraints: between adjacent points in same row
- Vertical constraints: between adjacent points in same column
- Start position: centered horizontally ((canvasWidth - cols*spacing)/2), 50px from top
Verlet integration (verletIntegrate):
- Skips pinned points
- Implicit velocity from position difference: vx = (x - oldX) * damping (0.99)
- Store current as old, then: x += vx, y += vy + gravity
- Wind: sinusoidal force = windStrength * sin(time + y * 0.01), applied to x axis
- Boundary constraints: floor at h-5, left wall at x=5, right wall at x=w-5
- Time from Date.now() * 0.001
Constraint solver (solveConstraints):
- Iterates constraintIterations times (default 5)
- For each constraint (iterated in reverse):
- Skip if already torn
- Calculate distance between p1 and p2
- If distance > restLength * tearThreshold (2.0): mark torn, skip
- If distance === 0: skip (degenerate case)
- Push/pull both points: diff = (restLength - dist) / dist * stiffness
- Offset applied to each point by half (if not pinned)
- After all iterations: batch filter out torn constraints
Rendering:
- HD canvas: 800px × devicePixelRatio, ctx.scale(dpr, dpr)
- Background: cachedColors.background (from --background-color-surface)
- Constraints drawn as lines colored by stretch stress:
- stretch > 1.3: stress color (red, cachedColors.stress)
- stretch > 1.1 to 1.3: RGB interpolation from normal→stress (inline calc: t = (stretch-1.1)/0.2)
- stretch <= 1.1: normal color (cachedColors.normal)
- Line width 1.5, round caps
- Pinned points: cachedColors.pin (yellow) dots (radius 3)
Mouse interaction:
- getCanvasCoords(e): converts event to canvas logical coordinates via getBoundingClientRect
- findNearestPoint(mx, my): finds closest point within mouseRadius (30px)
- Drag mode (default mousedown without shift):
- mousedown: find nearest point, store as dragPoint
- mousemove: if mouseDown and shiftDown, tear instead; otherwise drag updates position
- dragPoint's x/y and oldX/oldY set to mouse position (prevents velocity accumulation)
- Tear mode (shift held):
- tearAt(mx, my): removes constraints whose midpoint is within mouseRadius of mouse
- Midpoint = average of p1 and p2 positions
- Continuous tearing while shift+drag
- mouseup/mouseleave: release dragPoint, clear mouseDown
- Shift key tracked via document keydown/keyup events
- Context menu prevented on canvas
Animation loop:
- tick() runs continuously via requestAnimationFrame (always rendering)
- Drag interaction works even when physics is paused
- Physics (verletIntegrate + solveConstraints) only runs when isRunning = true
- Stats updated every frame via updateStats()
Controls architecture:
- start(): sets isRunning = true, updates button visibility
- pause(): sets isRunning = false, updates button visibility
- reset(): stops physics, clears dragPoint/mouseDown, recreates cloth from scratch
- updateControlState(): toggles .is-running on .cloth-controls element
- bindSlider(sliderId, displayId, setter, decimals): generic helper for all range inputs
- Gravity slider: updates gravity variable
- Stiffness slider: updates stiffness variable
- Iterations slider: updates constraintIterations (rounded to integer)
- Wind toggle: updates windEnabled boolean
- Wind strength slider: updates windStrength variable
Color caching:
- cachedColors: background, normal, stress, pin
- cacheColors() reads from CSS vars:
- background: --background-color-surface (fallback #1a1a1a)
- normal: --cloth-color-normal or --draw-color-primary (fallback #5eaadd)
- stress: --cloth-color-stress or --color-red (fallback #e74c3c)
- pin: --cloth-color-pin or --color-yellow (fallback #f1c40f)
- Refreshed on prefers-color-scheme change
Initialization:
- Gets cloth-canvas element
- Caches colors, sets up theme listener
- initCanvas(): sets physical resolution and DPI scaling
- createCloth(): generates point grid and constraints
- setupControls(): binds buttons and sliders
- setupMouse(): binds all mouse/keyboard events
- Starts render loop immediately (tick via rAF)
- Updates stats, logs initialization
Auto-init on DOM ready (DOMContentLoaded or immediate if already loaded).
Render loop starts immediately on init (animation always runs, physics toggled separately).
Page entièrement générée et maintenue par IA, sans intervention humaine.