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 > Finite Element Method (FEM) - Corotated Linear Elasticity Model</ title >
2321</ head >
2422< body >
23+
24+ < header class ="page-header ">
25+ < h1 > Finite Element Method (FEM) - Corotated Linear Elasticity Model</ h1 >
26+ </ header >
27+
2528< main >
26- < h1 style =" text-align:center " > Finite Element Method (FEM) - Corotated Linear Elasticity Model </ 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 ="1100 " height ="1125 " 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 > # elements</ label > </ td >
52- < td > < span id ="numElements "> 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.001 " step ="0.001 "> </ td >
73- </ tr >
74- < tr >
75- < td > < label for ="youngsModulusInput "> Young's modulus</ label > </ td >
76- < td > < input onchange ="gui.sim.youngsModulus=parseFloat(value); gui.sim.updateLameParameters() " id ="youngsModulusInput " type ="number " value ="500000.0 " step ="1.0 "> </ td >
77- </ tr >
78- < tr >
79- < td > < label for ="poissonRatioInput "> Poisson ratio</ label > </ td >
80- < td > < input onchange ="gui.sim.poissonRatio=parseFloat(value); gui.sim.updateLameParameters() " id ="poissonRatioInput " type ="number " value ="0.3 " step ="0.1 "> </ td >
81- </ tr >
82- < tr >
83- < td > < label for ="dampingInput "> Simple damping</ label > </ td >
84- < td > < input onchange ="gui.sim.damping=parseFloat(value) " id ="dampingInput " type ="number " value ="0.001 " step ="0.001 "> </ td >
85- </ tr >
86- < tr >
87- < td > < label for ="gravityInput "> Gravity</ label > </ td >
88- < td > < input onchange ="gui.sim.gravity=parseFloat(value) " id ="gravityInput " type ="number " value ="-9.81 " step ="0.01 "> </ td >
89- </ tr >
90- < tr >
91- < td > < label for ="densityInput "> Density</ label > </ td >
92- < td > < input onchange ="gui.sim.density=parseFloat(value) " id ="densityInput " type ="number " value ="500.0 " step ="1.0 "> </ td >
93- </ tr >
94- < tr >
95- < td > < label for ="renderStress "> Visualize stress</ label > </ td >
96- < td > < input onchange ="gui.renderStress=this.checked " id ="renderStress " type ="checkbox "> </ td >
97- </ tr >
98- < tr >
99- < td > < label for ="maxStressInput "> Max. stress value</ label > </ td >
100- < td > < input onchange ="gui.maxStress=parseFloat(value) " id ="maxStressInput " type ="number " value ="1000000.0 " step ="1.0 "> </ td >
101- </ tr >
102- < tr >
103- < td > </ td >
104- < td > < button onclick ="gui.restart() " type ="button " id ="restart "> Restart</ button > </ td >
105- </ tr >
106- < tr >
107- < td > </ td >
108- < td > < button onclick ="gui.doPause() " type ="button " id ="Pause "> Pause</ button > </ td >
109- </ tr >
110- </ 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 > # elements</ label >
55+ < span class ="stat-value "> < span id ="numElements "> 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.001 " step ="0.001 ">
72+
73+ < label for ="youngsModulusInput "> Young's modulus</ label >
74+ < input onchange ="gui.sim.youngsModulus=parseFloat(value); gui.sim.updateLameParameters() " id ="youngsModulusInput " type ="number " value ="500000.0 " step ="1.0 ">
75+
76+ < label for ="poissonRatioInput "> Poisson ratio</ label >
77+ < input onchange ="gui.sim.poissonRatio=parseFloat(value); gui.sim.updateLameParameters() " id ="poissonRatioInput " type ="number " value ="0.3 " step ="0.1 ">
78+
79+ < label for ="dampingInput "> Simple damping</ label >
80+ < input onchange ="gui.sim.damping=parseFloat(value) " id ="dampingInput " type ="number " value ="0.001 " step ="0.001 ">
81+
82+ < label for ="gravityInput "> Gravity</ label >
83+ < input onchange ="gui.sim.gravity=parseFloat(value) " id ="gravityInput " type ="number " value ="-9.81 " step ="0.01 ">
84+
85+ < label for ="densityInput "> Density</ label >
86+ < input onchange ="gui.sim.density=parseFloat(value) " id ="densityInput " type ="number " value ="500.0 " step ="1.0 ">
87+
88+ < label for ="renderStress "> Visualize stress</ label >
89+ < input onchange ="gui.renderStress=this.checked " id ="renderStress " type ="checkbox ">
90+
91+ < label for ="maxStressInput "> Max. stress value</ label >
92+ < input onchange ="gui.maxStress=parseFloat(value) " id ="maxStressInput " type ="number " value ="1000000.0 " step ="1.0 ">
93+
94+ < div class ="full-width ">
95+ < button onclick ="gui.restart() " id ="restart "> ▶ Restart</ button >
96+ </ div >
97+ < div class ="full-width ">
98+ < button onclick ="gui.doPause() " id ="Pause " class ="btn-secondary "> ▮▮ Pause</ button >
99+ </ div >
100+ </ div >
101+ </ div >
111102 </ td >
112103 </ tr >
113- < tr > < td >
104+ </ table >
105+
106+ <!-- Theory section -->
107+ < div class ="card theory ">
114108 < h2 > FEM algorithm:</ h2 >
115109 This example shows a finite element simulation using the corotated linear elasticity model with triangular elements [SB12]:
116110 < ol >
@@ -162,7 +156,7 @@ <h3>4. Compute elastic forces</h3>
162156 \end{align*}$$
163157 where $A$ is the area of the triangular element in rest configuration and $\mathbf D_m^{-T}$ is the inverted and transposed the reference shape matrix.
164158
165- < h3 > 2 . Time integration</ h3 >
159+ < h3 > 5 . Time integration</ h3 >
166160 Finally, the particles are advected by numerical time integration. In our case we use a symplectic Euler method:
167161 $$\begin{align*}
168162 \mathbf v(t + \Delta t) &= \mathbf v(t) + \frac{\Delta t}{m} \left (\mathbf f(t) + \mathbf f^{\text{ext}}(t) \right ) \\
@@ -175,8 +169,7 @@ <h3>References</h3>
175169 < li > [SB12] Eftychios Sifakis, Jernej Barbic. FEM Simulation of 3D Deformable Solids. ACM SIGGRAPH Courses, 2012</ li >
176170 </ ul >
177171
178- </ td > </ tr >
179- </ table >
172+ </ div >
180173
181174</ main >
182175
@@ -760,10 +753,12 @@ <h3>References</h3>
760753
761754 getMousePos ( canvas , event )
762755 {
763- let rect = canvas . getBoundingClientRect ( ) ;
756+ const rect = canvas . getBoundingClientRect ( ) ;
757+ const scaleX = canvas . width / rect . width ; // buffer / displayed size
758+ const scaleY = canvas . height / rect . height ;
764759 return {
765- x : event . clientX - rect . left ,
766- y : event . clientY - rect . top
760+ x : ( event . clientX - rect . left ) * scaleX ,
761+ y : ( event . clientY - rect . top ) * scaleY
767762 } ;
768763 }
769764
0 commit comments