Prompt utilise pour regenerer cette page :
Page: Double Pendulum - Chaotic Physics Simulation
Description: "Chaotic motion simulation of a double pendulum system"
Icon: "pendulum"
Tags: physics, simulation, chaos, dynamics
Status: new
=== FILE STRUCTURE ===
Files in this page directory:
- index.md (front matter + main HTML with ARIA)
- _stats.right.md (weight: 10) - State display (angles, velocities, energy, trail)
- _controls.right.md (weight: 20) - Play/Pause/Reset icon buttons
- _options.right.md (weight: 30) - Physics parameter sliders in 3 fieldsets
- _legend.right.md (weight: 40) - Color legend for primary + ghost pendulums
- _algorithm.after.md (weight: 90) - Lagrangian mechanics explanation
- _physics-pendulum.lib.js - DoublePendulum class (ES module, default export)
- default.js - Main orchestrator (IIFE, imports panic + DoublePendulum)
- default.scss - All styles with CSS custom properties for pendulum colors
=== INDEX.MD ===
Front matter:
title: "Double Pendulum"
description: "Chaotic motion simulation of a double pendulum system"
icon: "pendulum"
tags: ["physics", "simulation", "chaos", "dynamics"]
status: ["new"]
Body HTML:
<section class="container visual size-800 ratio-1-1 canvas-contain" aria-label="Double pendulum visualization">
<canvas id="pendulum-canvas" role="img" aria-label="Interactive double pendulum simulation">
Double pendulum simulation canvas. Enable JavaScript to view the animation.
</canvas>
</section>
Note: ARIA attributes for accessibility, fallback text inside canvas.
=== WIDGET: _stats.right.md (weight: 10) ===
Title: "Statistics"
Contains <div class="pendulum-stats"> with ##### State heading and <dl>:
- Angle 1: id="stat-angle1" (initial: 90.0 degrees)
- Angle 2: id="stat-angle2" (initial: 90.0 degrees)
- Velocity 1: id="stat-velocity1" (initial: 0.000)
- Velocity 2: id="stat-velocity2" (initial: 0.000)
- Energy: id="stat-energy" (initial: 0)
- Trail points: id="stat-trail" (initial: 0)
=== WIDGET: _controls.right.md (weight: 20) ===
Title: "Controls"
Contains <div class="pendulum-controls" role="group" aria-label="Simulation controls">:
- <button type="button" id="btn-play" class="item button is-play"> with {{< icon name="play" >}}
- <button type="button" id="btn-pause" class="item button is-pause"> with {{< icon name="pause" >}}
- <button type="button" id="btn-reset" class="item button"> with {{< icon name="refresh" >}}
All have aria-label attributes.
=== WIDGET: _options.right.md (weight: 30) ===
Title: "Parameters"
Contains <div class="pendulum-options"> with 3 fieldsets:
Fieldset "Pendulum 1":
- slider-length1: Length, range 50-250, default 150
- slider-mass1: Mass, range 1-20, step 0.5, default 10
Fieldset "Pendulum 2":
- slider-length2: Length, range 50-250, default 150
- slider-mass2: Mass, range 1-20, step 0.5, default 10
Fieldset "Physics":
- slider-gravity: Gravity, range 1-20, step 0.1, default 9.81
- slider-damping: Damping, range 0.99-1, step 0.0001, default 0.9999
Each slider in <div class="option"> with label (showing value in span) and aria-describedby.
Display IDs: display-length1, display-mass1, display-length2, display-mass2, display-gravity, display-damping.
=== WIDGET: _legend.right.md (weight: 40) ===
Title: "Legend"
Contains <div class="pendulum-legend"> with 2 sections:
##### Primary Pendulum:
<ul class="legend-list" role="list"> with 3 items:
- swatch.bob1 + "First bob (m<sub>1</sub>)"
- swatch.bob2 + "Second bob (m<sub>2</sub>)"
- swatch.trail + "Motion trail"
##### Ghost Pendulum (+0.001 rad):
<ul class="legend-list" role="list"> with 3 items:
- swatch.bob1-ghost + "First bob (m<sub>1</sub>)"
- swatch.bob2-ghost + "Second bob (m<sub>2</sub>)"
- swatch.trail-ghost + "Motion trail"
=== WIDGET: _algorithm.after.md (weight: 90) ===
Title: "Algorithm"
Wrapped in <div class="algorithm">, contains:
- ### Lagrangian Mechanics: explains chaotic dynamical system
- #### Equations of Motion: L = T - V
Two <code class="equation"> blocks with full theta1'' and theta2'' formulas
(derived from Lagrangian, showing g, m1, m2, L1, L2, sin/cos terms)
- #### Numerical Integration: explains 4th-order Runge-Kutta (RK4) vs Euler
- #### Chaos: deterministic chaos, butterfly effect, exponential divergence
=== JAVASCRIPT: _physics-pendulum.lib.js (ES Module) ===
Class DoublePendulum, exported as default.
Constructor(id, angle1, angle2, colors={}):
Stores id, initialAngle1/2, colors. State: angle1, angle2, angularVelocity1=0, angularVelocity2=0, trail=[].
Methods:
calculateAccelerations(a1, a2, av1, av2, params):
Lagrangian mechanics equations. params = { gravity, length1, length2, mass1, mass2 }.
delta = a1-a2, denom = 2*m1 + m2 - m2*cos(2*delta).
alpha1 = [-g(2m1+m2)*sin(a1) - m2*g*sin(a1-2*a2) - 2*sin(delta)*m2*(av2^2*L2 + av1^2*L1*cos(delta))] / (L1*denom)
alpha2 = [2*sin(delta)*(av1^2*L1*(m1+m2) + g*(m1+m2)*cos(a1) + av2^2*L2*m2*cos(delta))] / (L2*denom)
Returns { alpha1, alpha2 }.
step(dt, params):
Full 4th-order Runge-Kutta: computes k1-k4 for both angle and angular velocity.
Each k stage calls calculateAccelerations with intermediate state.
Updates: angle += weighted sum of k_a * dt/6, velocity += weighted sum of k_av * dt/6.
Applies damping: velocity *= params.damping (if defined).
totalEnergy(params):
Kinetic: 0.5*m1*L1^2*av1^2 + 0.5*m2*(L1^2*av1^2 + L2^2*av2^2 + 2*L1*L2*av1*av2*cos(a1-a2))
Potential: -m1*g*L1*cos(a1) - m2*g*(L1*cos(a1) + L2*cos(a2))
Returns kinetic + potential.
getBobPositions(pivotX, pivotY, scale, params):
x1 = pivotX + L1*scale*sin(a1), y1 = pivotY + L1*scale*cos(a1)
x2 = x1 + L2*scale*sin(a2), y2 = y1 + L2*scale*cos(a2)
Returns { x1, y1, x2, y2 }.
recordTrail(x, y, maxLength): push + shift if over limit.
clearTrail(): empties trail array.
reset(): restores initial angles, zeros velocities, empties trail.
=== JAVASCRIPT: default.js ===
IIFE pattern, 'use strict'.
Imports: panic from '/_lib/panic_v3.js', DoublePendulum from './_physics-pendulum.lib.js'
Author: Cylian
CONFIG:
gravity=9.81, length1/2=150, mass1/2=10, damping=0.9999
initialAngle1/2=Math.PI/2 (90 degrees)
perturbation=0.001 (tiny offset for ghost pendulum)
trailLength=500, trailFadeStart=0.3, bobRadius=8, rodWidth=3, pivotRadius=6
timeStep=0.05, stepsPerFrame=2
State:
canvas, ctx, pendulums[] (array of DoublePendulum instances)
gravity, length1, length2, mass1, mass2, damping (modifiable via sliders)
isRunning=false, animationId=null
centerX, centerY, scale
baseColors={}, resizeHandler=null
Functions - Rendering:
- getParams(): returns { gravity, length1, length2, mass1, mass2, damping }
- getCSSVar(name): reads CSS custom property
- drawTrail(pendulum): draws trail polyline with progressive alpha. Alpha ramps from 0 to 0.3 during
trailFadeStart portion, then 0.3 to 1.0 for remainder. Uses pendulum.colors.trail, lineWidth 2.
- drawPendulumInstance(pendulum, drawPivot): draws 2 rods (strokeStyle=colors.rod, lineWidth=CONFIG.rodWidth,
lineCap round), pivot circle (if drawPivot, baseColors.pivot, CONFIG.pivotRadius),
bob1 (pendulum.colors.bob1, radius=CONFIG.bobRadius+mass1*0.3),
bob2 (pendulum.colors.bob2, radius=CONFIG.bobRadius+mass2*0.3)
- clearCanvas(): fills with baseColors.background
- render(): clears, draws all trails first, then all pendulums (first one draws pivot)
Functions - Statistics:
- updateStats(): reads primary pendulum (pendulums[0]): angle1/2 in degrees (modulo 360, 1 decimal),
velocity1/2 (3 decimals), energy (integer), trail length
Functions - Animation:
- animate(): if isRunning: runs stepsPerFrame=2 physics steps per frame on all pendulums,
records trails (bob2 position), updates stats. Always renders. Always schedules next RAF.
Functions - Controls:
- start()/pause()/reset(): standard play/pause/reset
- updateControlState(): toggles .is-running on .pendulum-controls
- bindSlider(sliderId, displayId, setter, decimals=0): validates parseFloat>0 before applying
- setupControls(): binds btn-play/pause/reset and 6 sliders:
length1 (0 decimals), length2, mass1 (1 decimal), mass2, gravity (1 decimal), damping (4 decimals)
Functions - Init:
- updateColors(): caches baseColors (pivot=color-grey-500, background=background-color-surface).
Updates pendulums[0].colors: trail=color-teal, rod=color-text-secondary, bob1=color-blue, bob2=color-red.
Updates pendulums[1].colors: trail=color-orange, rod=color-text-tertiary, bob1=color-purple, bob2=color-yellow.
- createPendulums(): creates 2 DoublePendulum instances:
Primary: id='primary', angles=PI/2 for both, colors=teal/blue/red
Ghost: id='ghost', angle1=PI/2+0.001 (perturbation), angle2=PI/2, colors=orange/purple/yellow
- setupCanvas(): 800px fixed, DPR scaling, centerX=size/2, centerY=size/3 (upper third),
scale=min(1, availableHeight/maxLength), clears trails, updates colors
- debounce(fn, delay): simple debounce
- cleanup(): cancelAnimationFrame + remove resize handler
- init(): cleanup, get canvas, createPendulums, setupCanvas+Controls, render, start animate loop
=== SCSS: default.scss ===
CSS custom properties in :root:
--pendulum-color-rod: var(--text-color-secondary)
--pendulum-color-pivot: var(--color-grey-500)
--pendulum-color-bob1: var(--color-blue)
--pendulum-color-bob2: var(--color-red)
--pendulum-color-trail: var(--color-teal)
--pendulum-color-bob1-ghost: var(--color-purple)
--pendulum-color-bob2-ghost: var(--color-yellow)
--pendulum-color-trail-ghost: var(--color-orange)
Sections:
1. Controls (.pendulum-controls):
flex center, gap var(--layout-spacing), margin-top spacing.
button.button: reset native styles.
.is-play: display flex. .is-pause: display none. Reversed when &.is-running.
2. Options (.pendulum-options):
flex column, gap 0.75*spacing.
fieldset: no border/padding/margin, legend 0.875rem weight 600 primary color.
.option: flex column gap 0.25rem. label: flex space-between 0.875rem.
span: secondary color, mono font, min-width 4rem, right-aligned.
input[type="range"]: 4px track, 2px radius, 14px thumb with hover scale(1.2).
Focus outline 2px primary, :focus:not(:focus-visible) removes it.
Both webkit and moz thumb styles.
3. Statistics (.pendulum-stats dl):
grid (1fr auto), gap 0.5rem 1rem. dt: secondary 0.875rem. dd: margin 0, mono, right-aligned, primary.
4. Legend (.pendulum-legend):
flex column gap 0.5rem. .legend-list: no list-style, flex column gap 0.5rem.
.legend-item: flex center gap 0.75rem, 0.875rem.
.swatch: 12px circle, color-coded: .bob1=--pendulum-color-bob1, .bob2=--pendulum-color-bob2,
.trail=--pendulum-color-trail. Ghost variants: .bob1-ghost, .bob2-ghost, .trail-ghost.
5. Algorithm (layout-after .algorithm):
h4: margin + primary color. p: secondary, 1.6 line-height.
code: mono, bg surface, padding, 3px radius. .equation: block centered mono, bg surface, overflow-x auto.
Page entierement generee et maintenue par IA, sans intervention humaine.