Skip to content

Commit e5609d0

Browse files
committed
- new style
1 parent 2b9fc58 commit e5609d0

2 files changed

Lines changed: 193 additions & 202 deletions

File tree

examples/mass_spring_system.html

Lines changed: 130 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
<!doctype html>
2-
<html class="no-js" lang="en">
2+
<html lang="en">
33
<head>
44
<meta charset="utf-8">
5-
<style>
6-
body {font-family: Helvetica, sans-serif;}
7-
table {background-color:#CCDDEE;text-align:left}
8-
</style>
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<link rel="stylesheet" href="https://interactivecomputergraphics.github.io/physics-simulation/examples/style.css">
97
<script type="text/x-mathjax-config">
108
MathJax.Hub.Config({
119
extensions: ["tex2jax.js"],
@@ -18,148 +16,145 @@
1816
"HTML-CSS": { fonts: ["TeX"] }
1917
});
2018
</script>
21-
<script type="text/javascript" aync src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js"></script>
19+
<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js"></script>
2220
<title>Mass Spring System</title>
2321
</head>
2422
<body>
23+
24+
<header class="page-header">
25+
<h1>Mass Spring System</h1>
26+
</header>
27+
2528
<main>
26-
<h1 style="text-align:center">Mass Spring System</h1>
29+
<!-- Simulation panel: canvas + controls -->
2730
<table style="align_center;border-radius: 20px;padding: 20px;margin:auto">
28-
<col width="1100">
29-
<col width="400">
31+
<col width="75%">
32+
<col width="25%">
3033
<tr>
3134
<td>
32-
<canvas id="simCanvas" width="1024" height="768" style="border:2px solid #000000;border-radius: 20px;background-color:#EEEEEE">Your browser does not support the HTML5 canvas tag.</canvas>
35+
<div class="card sim-panel">
36+
<div class="sim-canvas-wrap">
37+
<canvas id="simCanvas" width="1024" height="960" style="border:2px solid #000000;border-radius: 20px;background-color:#EEEEEE">Your browser does not support the HTML5 canvas tag.</canvas>
38+
</div>
39+
</div>
3340
</td>
3441
<td>
35-
<table>
36-
<col width="180" style="padding-right:10px">
37-
<col width="100">
38-
<tr>
39-
<td><label>Current time</label></td>
40-
<td><span id="time">0.00</span> s</td>
41-
</tr>
42-
<tr>
43-
<td><label>Time per sim. step</label></td>
44-
<td><span id="timePerStep">0.00</span> ms</td>
45-
</tr>
46-
<tr>
47-
<td><label># particles</label></td>
48-
<td><span id="numParticles">0</span></td>
49-
</tr>
50-
<tr>
51-
<td><label># springs</label></td>
52-
<td><span id="numSprings">0</span></td>
53-
</tr>
54-
<tr>
55-
<td><label for="widthInput">Width</label></td>
56-
<td><input onchange="gui.restart()" id="widthInput" type="number" value="40" step="1"></td>
57-
</tr>
58-
<tr>
59-
<td><label for="heightInput">Height</label></td>
60-
<td><input onchange="gui.restart()" id="heightInput" type="number" value="30" step="1"></td>
61-
</tr>
62-
<tr>
63-
<td><label for="fixedParticlesInput"># fixed particles</label></td>
64-
<td><select onchange="gui.restart()" id="fixedParticlesInput">
65-
<option value="1">1</option>
66-
<option value="2" selected="selected">2</option>
67-
<option value="4">4</option>
68-
</select></td>
69-
</tr>
70-
<tr>
71-
<td><label for="timeStepSizeInput">Time step size</label></td>
72-
<td><input onchange="gui.sim.timeStepSize=parseFloat(value)" id="timeStepSizeInput" type="number" value="0.005" step="0.001"></td>
73-
</tr>
74-
<tr>
75-
<td><label for="stiffnessInput">Stiffness</label></td>
76-
<td><input onchange="gui.sim.stiffness=parseFloat(value)" id="stiffnessInput" type="number" value="5000.0" step="1.0"></td>
77-
</tr>
78-
<tr>
79-
<td><label for="dampingInput">Damping</label></td>
80-
<td><input onchange="gui.sim.damping=parseFloat(value)" id="dampingInput" type="number" value="10.0" step="0.1"></td>
81-
</tr>
82-
<tr>
83-
<td><label for="gravityInput">Gravity</label></td>
84-
<td><input onchange="gui.sim.gravity=parseFloat(value)" id="gravityInput" type="number" value="-9.81" step="0.01"></td>
85-
</tr>
86-
<tr>
87-
<td><label for="massInput">Mass</label></td>
88-
<td><input onchange="gui.sim.mass=parseFloat(value)" id="massInput" type="number" value="0.5" step="0.01"></td>
89-
</tr>
90-
<tr>
91-
<td></td>
92-
<td><button onclick="gui.restart()" type="button" id="restart">Restart</button></td>
93-
</tr>
94-
<tr>
95-
<td></td>
96-
<td><button onclick="gui.doPause()" type="button" id="Pause">Pause</button></td>
97-
</tr>
98-
</table>
42+
<div class="controls-panel">
43+
<h3>Controls</h3>
44+
<div class="controls-grid">
45+
<label>Current time</label>
46+
<span class="stat-value"><span id="time">0.00</span> s</span>
47+
48+
<label>Time per sim. step</label>
49+
<span class="stat-value"><span id="timePerStep">0.00</span> ms</span>
50+
51+
<label># particles</label>
52+
<span class="stat-value"><span id="numParticles">0</span></span>
53+
54+
<label># springs</label>
55+
<span class="stat-value"><span id="numSprings">0</span></span>
56+
57+
<label for="widthInput">Width</label>
58+
<input onchange="gui.restart()" id="widthInput" type="number" value="40" step="1">
59+
60+
<label for="heightInput">Height</label>
61+
<input onchange="gui.restart()" id="heightInput" type="number" value="30" step="1">
62+
63+
<label for="fixedParticlesInput"># fixed particles</label>
64+
<select onchange="gui.restart()" id="fixedParticlesInput">
65+
<option value="1">1</option>
66+
<option value="2" selected="selected">2</option>
67+
<option value="4">4</option>
68+
</select>
69+
70+
<label for="timeStepSizeInput">Time step size</label>
71+
<input onchange="gui.sim.timeStepSize=parseFloat(value)" id="timeStepSizeInput" type="number" value="0.005" step="0.001">
72+
73+
<label for="stiffnessInput">Stiffness</label>
74+
<input onchange="gui.sim.stiffness=parseFloat(value)" id="stiffnessInput" type="number" value="5000.0" step="1.0">
75+
76+
<label for="dampingInput">Damping</label>
77+
<input onchange="gui.sim.damping=parseFloat(value)" id="dampingInput" type="number" value="10.0" step="0.1">
78+
79+
<label for="gravityInput">Gravity</label>
80+
<input onchange="gui.sim.gravity=parseFloat(value)" id="gravityInput" type="number" value="-9.81" step="0.01">
81+
82+
<label for="massInput">Mass</label>
83+
<input onchange="gui.sim.mass=parseFloat(value)" id="massInput" type="number" value="0.5" step="0.01">
84+
85+
<div class="full-width">
86+
<button onclick="gui.restart()" id="restart">&#9654; Restart</button>
87+
</div>
88+
<div class="full-width">
89+
<button onclick="gui.doPause()" id="Pause" class="btn-secondary">&#9646;&#9646; Pause</button>
90+
</div>
91+
</div>
92+
</div>
9993
</td>
10094
</tr>
101-
<tr><td>
102-
<h2>Mass spring algorithm:</h2>
103-
This example shows a mass spring system that consists of particles linked by damped springs:
104-
<ol>
105-
<li>compute spring forces</li>
106-
<li>time integration to get new particle positions and velocities</li>
107-
</ol>
108-
109-
Note that the simulation is only conditionally stable since a conditionally stable explicit time integration method is used.
110-
111-
<h3>1. Compute spring forces</h3>
95+
</table>
96+
97+
<!-- Theory section -->
98+
<div class="card theory">
99+
<h2>Mass spring algorithm:</h2>
100+
This example shows a mass spring system that consists of particles linked by damped springs:
101+
<ol>
102+
<li>compute spring forces</li>
103+
<li>time integration to get new particle positions and velocities</li>
104+
</ol>
112105

113-
<p>In general a spring force can be obtained for any holonomic constraint.
114-
In this example we use distance constraints
115-
$$C_i(\mathbf{x}_{i_1}, \mathbf{x}_{i_2}) = \| \mathbf x_{i_1} -\mathbf x_{i_2} \|-d,$$
116-
where $d$ is the rest length between particles $\mathbf{x}_{i_1}$ and $\mathbf{x}_{i_2}$.</p>
117-
118-
<h4>Potential energy</h4>
119-
<p>For a scalar constraint we can define a potential energy as:
120-
$$E(\mathbf x) = \frac k 2 C(\mathbf x)^2,$$
121-
where $k$ is the stiffness of the spring.
122-
123-
<h4>General spring force</h4>
124-
The spring force for a particle $j$ is then determined by the negative gradient of the potential energy function:
125-
$$\mathbf F_j = - \frac{\partial E(\mathbf x)}{\partial \mathbf x_j} = -k \frac{\partial C(\mathbf x)}{\partial \mathbf x_j} C(\mathbf x).$$
126-
127-
<h4>General damping force</h4>
128-
The corresponding damping force is obtained by using the time derivative of the constraint function:
129-
$$\mathbf F^D_j = -\mu \frac{\partial C(\mathbf x)}{\partial \mathbf x_j} \dot{C}(\mathbf x).$$
106+
Note that the simulation is only conditionally stable since a conditionally stable explicit time integration method is used.
130107

131-
132-
<h4>Constraint gradients:</h4>
133-
<p>To compute the spring and damping forces, the constraint gradients are required which are computed as: </p>
134-
$$\begin{align*}
135-
\frac{\partial C_i}{\partial \mathbf x_{i_1}} &= \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \\
136-
\frac{\partial C_i}{\partial \mathbf x_{i_2}} &= - \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|}
137-
\end{align*}$$
138-
139-
<h4>Spring force</h4>
140-
So finally we get the following spring forces for a distance constraint $C_i(\mathbf{x}_{i_1}, \mathbf{x}_{i_2})$:
141-
$$\begin{align*}
142-
\mathbf F_{i_1} = -k (\| \mathbf x_{i_1} -\mathbf x_{i_2} \|-d) \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \\
143-
\mathbf F_{i_2} = +k (\| \mathbf x_{i_1} -\mathbf x_{i_2} \|-d) \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|}.
144-
\end{align*}$$
145-
146-
<h4>Damping force</h4>
147-
The damping forces for a distance constraint $C_i(\mathbf{x}_{i_1}, \mathbf{x}_{i_2})$ are determined as:
148-
$$\begin{align*}
149-
\mathbf F^D_{i_1} = -k \left ((\mathbf v_{i_1}- \mathbf v_{i_2}) \cdot \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \right ) \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \\
150-
\mathbf F^D_{i_2} = +k \left ((\mathbf v_{i_1}- \mathbf v_{i_2}) \cdot \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \right ) \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|}.
151-
\end{align*}$$
108+
<h3>1. Compute spring forces</h3>
109+
110+
<p>In general a spring force can be obtained for any holonomic constraint.
111+
In this example we use distance constraints
112+
$$C_i(\mathbf{x}_{i_1}, \mathbf{x}_{i_2}) = \| \mathbf x_{i_1} -\mathbf x_{i_2} \|-d,$$
113+
where $d$ is the rest length between particles $\mathbf{x}_{i_1}$ and $\mathbf{x}_{i_2}$.</p>
114+
115+
<h4>Potential energy</h4>
116+
<p>For a scalar constraint we can define a potential energy as:
117+
$$E(\mathbf x) = \frac k 2 C(\mathbf x)^2,$$
118+
where $k$ is the stiffness of the spring.
152119

153-
<h3>2. Time integration</h3>
154-
Finally, the particles are advected by numerical time integration. In our case we use a symplectic Euler method:
120+
<h4>General spring force</h4>
121+
The spring force for a particle $j$ is then determined by the negative gradient of the potential energy function:
122+
$$\mathbf F_j = - \frac{\partial E(\mathbf x)}{\partial \mathbf x_j} = -k \frac{\partial C(\mathbf x)}{\partial \mathbf x_j} C(\mathbf x).$$
123+
124+
<h4>General damping force</h4>
125+
The corresponding damping force is obtained by using the time derivative of the constraint function:
126+
$$\mathbf F^D_j = -\mu \frac{\partial C(\mathbf x)}{\partial \mathbf x_j} \dot{C}(\mathbf x).$$
127+
128+
129+
<h4>Constraint gradients:</h4>
130+
<p>To compute the spring and damping forces, the constraint gradients are required which are computed as: </p>
155131
$$\begin{align*}
156-
\mathbf v(t + \Delta t) &= \mathbf v(t) + \frac{\Delta t}{m} \left (\mathbf F(t) + \mathbf F^D + \mathbf F^{\text{ext}} \right ) \\
157-
\mathbf x(t + \Delta t) &= \mathbf x(t) + \Delta t \mathbf v(t + \Delta t),
132+
\frac{\partial C_i}{\partial \mathbf x_{i_1}} &= \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \\
133+
\frac{\partial C_i}{\partial \mathbf x_{i_2}} &= - \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|}
158134
\end{align*}$$
159-
where $\mathbf F^{\text{ext}}$ are the external forces.
135+
136+
<h4>Spring force</h4>
137+
So finally we get the following spring forces for a distance constraint $C_i(\mathbf{x}_{i_1}, \mathbf{x}_{i_2})$:
138+
$$\begin{align*}
139+
\mathbf F_{i_1} = -k (\| \mathbf x_{i_1} -\mathbf x_{i_2} \|-d) \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \\
140+
\mathbf F_{i_2} = +k (\| \mathbf x_{i_1} -\mathbf x_{i_2} \|-d) \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|}.
141+
\end{align*}$$
160142

161-
</td></tr>
162-
</table>
143+
<h4>Damping force</h4>
144+
The damping forces for a distance constraint $C_i(\mathbf{x}_{i_1}, \mathbf{x}_{i_2})$ are determined as:
145+
$$\begin{align*}
146+
\mathbf F^D_{i_1} = -k \left ((\mathbf v_{i_1}- \mathbf v_{i_2}) \cdot \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \right ) \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \\
147+
\mathbf F^D_{i_2} = +k \left ((\mathbf v_{i_1}- \mathbf v_{i_2}) \cdot \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|} \right ) \frac{\mathbf x_{i_1} -\mathbf x_{i_2}}{\| \mathbf x_{i_1} -\mathbf x_{i_2} \|}.
148+
\end{align*}$$
149+
150+
<h3>2. Time integration</h3>
151+
Finally, the particles are advected by numerical time integration. In our case we use a symplectic Euler method:
152+
$$\begin{align*}
153+
\mathbf v(t + \Delta t) &= \mathbf v(t) + \frac{\Delta t}{m} \left (\mathbf F(t) + \mathbf F^D + \mathbf F^{\text{ext}} \right ) \\
154+
\mathbf x(t + \Delta t) &= \mathbf x(t) + \Delta t \mathbf v(t + \Delta t),
155+
\end{align*}$$
156+
where $\mathbf F^{\text{ext}}$ are the external forces.
157+
</div>
163158

164159
</main>
165160

@@ -509,10 +504,12 @@ <h3>2. Time integration</h3>
509504

510505
getMousePos(canvas, event)
511506
{
512-
let rect = canvas.getBoundingClientRect();
507+
const rect = canvas.getBoundingClientRect();
508+
const scaleX = canvas.width / rect.width; // buffer / displayed size
509+
const scaleY = canvas.height / rect.height;
513510
return {
514-
x: event.clientX - rect.left,
515-
y: event.clientY - rect.top
511+
x: (event.clientX - rect.left) * scaleX,
512+
y: (event.clientY - rect.top) * scaleY
516513
};
517514
}
518515

0 commit comments

Comments
 (0)