22< html class ="no-js " 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" ] ,
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 > Impulse-Based Dynamic Simulation</ title >
2321</ head >
2422< body >
23+
24+ < header class ="page-header ">
25+ < h1 > Impulse-Based Dynamic Simulation</ h1 >
26+ </ header >
27+
2528< main >
26- < h1 style =" text-align:center " > Impulse-Based Dynamic Simulation</ 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 > # constraints</ label > </ td >
52- < td > < span id ="numConstraints "> 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 ="iterationsInput "> Iterations</ label > </ td >
76- < td > < input onchange ="gui.sim.numIterations=parseInt(value) " id ="iterationsInput " type ="number " value ="2 " step ="1 "> </ td >
77- </ tr >
78- < tr >
79- < td > < label for ="gravityInput "> Gravity</ label > </ td >
80- < td > < input onchange ="gui.sim.gravity=parseFloat(value) " id ="gravityInput " type ="number " value ="-9.81 " step ="0.01 "> </ td >
81- </ tr >
82- < tr >
83- < td > < label for ="massInput "> Mass</ label > </ td >
84- < td > < input onchange ="gui.sim.mass=parseFloat(value) " id ="massInput " type ="number " value ="0.5 " step ="0.01 "> </ td >
85- </ tr >
86- < tr >
87- < td > </ td >
88- < td > < button onclick ="gui.restart() " type ="button " id ="restart "> Restart</ button > </ td >
89- </ tr >
90- < tr >
91- < td > </ td >
92- < td > < button onclick ="gui.doPause() " type ="button " id ="Pause "> Pause</ button > </ td >
93- </ tr >
94- </ 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 > # constraints</ label >
55+ < span class ="stat-value "> < span id ="numConstraints "> 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 ="iterationsInput "> Iterations</ label >
74+ < input onchange ="gui.sim.numIterations=parseInt(value) " id ="iterationsInput " type ="number " value ="2 " step ="1 ">
75+
76+ < label for ="gravityInput "> Gravity</ label >
77+ < input onchange ="gui.sim.gravity=parseFloat(value) " id ="gravityInput " type ="number " value ="-9.81 " step ="0.01 ">
78+
79+ < label for ="massInput "> Mass</ label >
80+ < input onchange ="gui.sim.mass=parseFloat(value) " id ="massInput " type ="number " value ="0.5 " step ="0.01 ">
81+ < div class ="full-width ">
82+ < button onclick ="gui.restart() " id ="restart "> ▶ Restart</ button >
83+ </ div >
84+ < div class ="full-width ">
85+ < button onclick ="gui.doPause() " id ="Pause " class ="btn-secondary "> ▮▮ Pause</ button >
86+ </ div >
87+ </ div >
88+ </ div >
9589 </ td >
9690 </ tr >
97- < tr > < td >
91+ </ table >
92+
93+ <!-- Theory section -->
94+ < div class ="card theory ">
9895 < h2 > IBDS algorithm:</ h2 >
9996 This example shows the impulse-based dynamic simulation (IBDS) method [BFS05, BS06].
10097 IBDS is a predictor-corrector method to perform a simulation step for a constrained system.
@@ -165,13 +162,13 @@ <h3>5. Velocity update:</h3>
165162 Finally, the velocity of a constrained particle is updated:
166163 $$\Delta \mathbf v = \frac{1}{m} \mathbf p$$
167164
168- < h3 > Velocity constraint solver loop</ h3 >
165+ < h3 > 6. Velocity constraint solver loop</ h3 >
169166 The velocity constraints $\dot C(\mathbf x) = 0$ are solved analogously to the position constraints.
170167 The Lagrange multipliers are determined by solving:
171168 $$\mathbf J \mathbf M^{-1} \mathbf J^T \boldsymbol \lambda = -\mathbf {\dot C}(\mathbf x).$$
172169 However, note that no position prediction is required since the solve is performed after the time integration step.
173170
174- < h3 > Time integration</ h3 >
171+ < h3 > 7. Time integration</ h3 >
175172 The particles are advected by numerical time integration. In our case we use a symplectic Euler method:
176173 $$\begin{align*}
177174 \mathbf v(t + \Delta t) &= \mathbf v(t) + \frac{\Delta t}{m} \mathbf f^{\text{ext}}(t) \\
@@ -185,8 +182,7 @@ <h3>References</h3>
185182 < li > [BFS05] Jan Bender, Dieter Finkenzeller and Alfred Schmitt. An impulse-based dynamic simulation system for VR applications. In Proceedings of Virtual Concept 2005 </ li >
186183 </ ul >
187184
188- </ td > </ tr >
189- </ table >
185+ </ div >
190186
191187</ main >
192188
@@ -618,10 +614,12 @@ <h3>References</h3>
618614
619615 getMousePos ( canvas , event )
620616 {
621- let rect = canvas . getBoundingClientRect ( ) ;
617+ const rect = canvas . getBoundingClientRect ( ) ;
618+ const scaleX = canvas . width / rect . width ; // buffer / displayed size
619+ const scaleY = canvas . height / rect . height ;
622620 return {
623- x : event . clientX - rect . left ,
624- y : event . clientY - rect . top
621+ x : ( event . clientX - rect . left ) * scaleX ,
622+ y : ( event . clientY - rect . top ) * scaleY
625623 } ;
626624 }
627625
0 commit comments