-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathBlogScratch.html
More file actions
341 lines (286 loc) · 13 KB
/
Copy pathBlogScratch.html
File metadata and controls
341 lines (286 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
BlogScratch.html
<p>
<script type="application/processing" data-processing-target="pjsPendSimp">
float PendulumLength = 1.0;
float PendulumInitTheta = radians( 75.0 );
float StatePendulumTheta = PendulumInitTheta;
float StatePendulumOmega = 0.0;
float DT = 1.0 / 5.0;
int WindowWidthHeight = 300;
float WorldSize = 2.0;
float PixelsPerMeter;
float OriginPixelsX;
float OriginPixelsY;
void setup()
{
// Set up normalized colors.
colorMode( RGB, 1.0 );
// Set up the stroke color and width.
stroke( 0.0 );
//strokeWeight( 0.01 );
// Create the window size, set up the transformation variables.
size( WindowWidthHeight, WindowWidthHeight );
PixelsPerMeter = (( float )WindowWidthHeight ) / WorldSize;
OriginPixelsX = 0.5 * ( float )WindowWidthHeight;
OriginPixelsY = 0.25 * ( float )WindowWidthHeight;
}
// The DrawState function assumes that the coordinate space is that of the
// simulation - namely, meters, with the pendulum pivot placed at the origin.
// Draw the arm, pivot, and bob!
// There is currently a bug in processing.js which requires us to do the
// pixels-per-meter scaling ourselves.
void DrawState()
{
// Compute end of arm.
float ArmEndX = PixelsPerMeter * PendulumLength * sin( StatePendulumTheta );
float ArmEndY = PixelsPerMeter * PendulumLength * cos( StatePendulumTheta );
// Draw the pendulum arm.
strokeWeight( 1.0 );
line( 0.0, 0.0, ArmEndX, ArmEndY );
// Draw the pendulum pivot
fill( 0.0 );
ellipse( 0.0, 0.0,
PixelsPerMeter * 0.03,
PixelsPerMeter * 0.03 );
// Draw the pendulum bob
fill( 1.0, 0.0, 0.0 );
ellipse( ArmEndX, ArmEndY,
PixelsPerMeter * 0.1,
PixelsPerMeter * 0.1 );
}
// Time Step function.
void TimeStep( float i_dt )
{
StatePendulumTheta += i_dt * StatePendulumOmega;
StatePendulumOmega += i_dt * ( -9.81 / PendulumLength ) *
sin( StatePendulumTheta );
}
// The draw function creates a transformation matrix between pixel space
// and simulation space, in meters, and then calls the DrawState function.
// Unfortunately, there is currently a bug in processing.js with concatenated
// transformation matrices, so we have to do the coordinate scaling ourselves
// in the draw function.
void draw()
{
background( 0.75 );
// Time Step.
//TimeStep( DT );
// Translate to the origin.
translate( OriginPixelsX, OriginPixelsY );
// Draw the simulation
DrawState();
}
</script>
<canvas id="pjsPendSimp"> </canvas>
<div class="LaTexEquation">
\(
\tau = I \alpha
\)
</div>
<h2>Equations of Motion</h2>
<a href="http://en.wikipedia.org/wiki/Newton's_laws_of_motion">
Newton's Laws of Motion</a>
are the basis of all
<a href="http://en.wikipedia.org/wiki/Classical_mechanics">
Classical Mechanics</a>
<a href="http://www.myphysicslab.com/pendulum1.html">My Physics Lab</a>
<h2>Equations of State</h2>
The equations of motion are a specific case of the more general equations of
state. An equation of state is simply a relationship, or a set of relationships,
between the measurements that a state is composed of.
Boyle's law relates different measurements of a gas to each other.
<a href="http://en.wikipedia.org/wiki/Classical_mechanics">
Classical Mechanics</a>
<a href="http://en.wikipedia.org/wiki/Dynamics_(mechanics)">
Dynamics</a>
<a href="http://en.wikipedia.org/wiki/Newton's_laws_of_motion">
Newton's Laws of Motion</a>
<p>
For this class, we will focus on the motion of simple
systems, beginning with our very simple pendulum example from the previous
class. The study of systems
of motion is generally called <i>Dynamics</i>, and a more limited
<span class="equation-label">LaTeX:</span>
\(
J_\alpha(x) = \sum\limits_{m=0}^\infty \frac{(-1)^m}{m! \, \Gamma(m + \alpha + 1)}{\left({\frac{x}{2}}\right)}^{2 m + \alpha}
\)
<h2>What is the Simulation State?</h2>
When we are creating a simulation of a phenomenon, the first and most important
step in our design is how we choose to encode a description of what we're
simulating as a set of numbers. The simulation state is a set of numbers that
describes the "STATE" of a physical system at a moment in time.
<p>
Simulation states come in many sizes and shapes. Generally, one endeavors to
represent a system with as few numbers as possible, because each separate
number is a measurement of the system that will have to have computational
work done to update its value for the next moment in the time evolution of
the simulation.
<p>
<h2>An Extremely Simple State</h2>
A very simple simulation we could create is one of a pendulum in two dimensions.
In this very simple example, the simulation state consists of just two numbers:
the angle of deviation of the pendulum from its rest position at the current
time, and the rate of change, or angular velocity, of that angle. Here is a
diagram showing a simple pendulum from Wikipedia:
<p>
<img src="http://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Simple_gravity_pendulum.svg/500px-Simple_gravity_pendulum.svg.png"></img>
<p>
Here's a simple way of representing this simulation state in code:
<pre><code>
// Length of the pendulum arm, in meters. This is constant, and therefore not
// really part of the simulation state
float PendulumLength;
// Angle of the pendulum at the current time, in radians
float StatePendulumTheta;
// Rate of change of the pendulum angle with respect to time, at the current
// time, in radians.
float StatePendulumDthetaDt;
</code></pre>
Note that the code takes care to define what units it is working in.
As a rule, it is a good idea to insist that your simulation work in SI
units, with conversion to and from other units done as part of scene
translation and export. In this case, we're using meters to specify our
pendulum length, and radians to specify our angles.
<h2>A Second, Simple State</h2>
The previous pendulum state is extremely simple, and to illustrate a different
type of state, let's imagine that we have a horizontal cross-section of an
ocean wave, and we want to represent the height of the wave at each point
horizontally. This example requires us to consider the notion of spatial
resolution - the higher resolution a simulation state is, the finer the details
it can represent, but generally the more computational work and resources
it will require. Let's imagine that our wave cross section covers ten meters
of space, and that we'd like to have a measurement of the wave's height every
ten centimeters. Using meters as a representational unit, our state description
for just the height part of the state will look like this:
<pre><code>
// Size of the world, in meters.
float WorldSize = 10.0;
// Distance between two adjacent measurements of height, in meters:
// (.1 meters is 10 centimeters)
float DX = 0.1;
// The array of heights representing the wave in this 10-meter
// world, at a given time.
int ArraySize = ( int )ceil( WorldSize / DX );
// Allocating the Height Array.
float[] StateHeightArray = new float[ArraySize];
</code></pre>
Notice that in this second state example, the size of the state arrays is
actually calculated from the size of the world we're simulating and the
resolution we've chosen to simulate at. Simulation code usually offers
multiple ways of determining the ultimate state size - either specifying the
number of elements in the state, corresponding to a given world size, which
will determine the spacing and resolution of the state, or alternatively
specifying the spacing resolution, which determines the number of elements
in the state, as in the above example. Here's what the code would look like
if we were specifying the number of elements in the array directly, instead
of the spacing:
<pre><code>
// Size of the world, in meters.
float WorldSize = 10.0;
// Array Size
int ArraySize = 100;
// Distance between two adjacent measurements of height, in meters:
float DX = WorldSize / ( float )ArraySize;
// Allocating the Height Array.
float[] StateHeightArray = new float[ArraySize];
</code></pre>
<h2>Two More Complex State Examples</h2>
The above two examples are both extremely simple, and have a small number of
state variables. Here is an example of a more complex state - in this case,
representing a number of particles which have a number of attributes per
particle - position, mass, velocity, etc. Some, but not all, of these attributes
have values at the current time AND at the previous simulated point in time,
all of which is stored as part of the simulation state. In this example,
there is a fixed maximum number of particles, and a number of them that are
considered currently alive.
<pre><code>
// Max number of particles.
int MAX_NUM_PARTICLES = 1024;
int ArraySize = MAX_NUM_PARTICLES;
// Number of active particles.
int N;
// Particle data arrays. Note that we make a separate array for each
// piece of particle data, separating even the vector components.
// This illustrates the simulation design idiom:
// "structs of arrays, rather than arrays of structs".
float[] StateMass = new float[ArraySize];
float[] StateRadius = new float[ArraySize];
float[] StatePosX = new float[ArraySize];
float[] StatePosY = new float[ArraySize];
float[] StateVelX = new float[ArraySize];
float[] StateVelY = new float[ArraySize];
float[] StatePrevPosX = new float[ArraySize];
float[] StatePrevPosY = new float[ArraySize];
</code></pre>
This second state example represents a height field in 1D, like above,
with multiple measurements at each point.
<pre><code>
// Size of the world, in meters.
float WorldSize = 10.0;
// Distance between two adjacent measurements of height, in meters:
// (.1 meters is 10 centimeters)
float DX = 0.1;
// The array of heights representing the wave in this 10-meter
// world, at a given time.
int ArraySize = ( int )ceil( WorldSize / DX );
// Allocating the Height Array.
float[] StateHeight = new float[ArraySize];
float[] StateDheightDt = new float[ArraySize];
float[] StatePressure = new float[ArraySize];
float[] StateFloorHeight = new float[ArraySize];
float[] StatePrevHeight = new float[ArraySize];
</code></pre>
<h2>Design Idioms and Best Practices</h2>
The examples have a lot of things in common, and indeed, these form the
basis for what I would call "good simulation design practices". Each example
above is representing a different physical
system - first a simple pendulum, then a simple height field, then finally
a system of many particles with multiple measurements per particle, and finally
a more complex wave example. However, they all use the same code conventions.
Each one calls its array sizes "ArraySize", each one prefaces the variable
names of its simulation state with the word "State". Each of the examples
that uses a grid of values uses the same conventions for its world description:
WorldSize, DX, and so on.
This is an extremely important part of simulation code design - coming up
with a very consistent standard for naming of arrays and variables, and
rigorously adhering to the standard. This will ultimately allow you to reuse
code easily, and also to rely on template patterns to apply common operations
across different simulation implementations. The lion's share of simulation
coding involves defining and employing these standards.
<h2>The State Vector</h2>
In each of the above examples, we've created a separate, specifically named
array to store each separate simulation measurement. Taken together, these
measurements collectively form what we consider the entire "state". Most
likely, your simulation will also feature some storage that is temporary,
representing quantities that are computed as part of the calculation of
permanent state properties - as an example, on the road to computing pressure
in a smoke simulation, a temporary measurement called "divergence" is
computed, but it is not usually considered formally part of the simulation state
because it can be wholly derived from other parts.
<p>
Suppose, instead of explicitly naming each separate field of our simulation
state, we instead created a single pool of storage for the state, with each
part of it concatenated together. We could use indexing expressions to get
to the right part of the large array for each individual property, and then
we would be able to see more clearly that our Simulation State was in fact
a single representational object.
<p>
The name of this single array of storage for a simulation state is the
"State Vector". Here's an example of how this might work in code for a
state consisting of several distinct fields, and using a multi-dimensional
array to represent the concatenation of the fields together.
<pre><code>
int ArraySize = 1024;
int NumStateArrays = 8;
float[][] State = new float[NumStateArrays][ArraySize];
int StateIndexMass = 0;
int StateIndexRadius = 1;
int StateIndexPosX = 2;
int StateIndexPosY = 3;
int StateIndexVelX = 4;
int StateIndexVelY = 5;
int StateIndexPrevPosX = 6;
int StateIndexPrevPosY = 7;
// To access the x-position of particle i:
float xPos = State[StateIndexPosX][i];
</code></pre>