void simulation_draw(...);
⚠ TYPO in hw.cpp: the pins should be in _yellow_, not dark blue
int num_nodes;
int num_springs;
int num_pins;
vec2 *X;
is the rest position of the mesh.
vec2 *x;
is the current position of the mesh.
int2 *springs;
int *pins;
e.g., node k has rest position X[k]
e.g., the k-th spring is attached to nodes springs[k].i
and springs[k].j
e.g., the k-th pin is attached to node pins[k]
✨ read this explanation of
void compute_and_add_to(...);
U
is the "energy" $U$
U_x
is the gradient $\frac{dU}{d\mathbf{x}},$ which is a long row vector
U_xx
is the Hessian $\frac{d^2U}{d\mathbf{x}^2},$ which is a big square matrix (in code we are storing it as a sparse matrix; will discuss Thursday)
we are given a pointer to the energy; don't forget to dereference.
(*U) += d;
adds double $d$ to the energy.
instead of adding energy terms to the gradient & Hessian one entry at a time...
...it can be very convenient to add "segments" (vec2's) to the gradient and blocks (mat2's) to the Hessian.
add(U_x, i, s);
adds vec2 $\mathbf{s}$ to $\frac{dU}{d\mathbf{x}_i}$ // NOTE $\frac{dU}{d\mathbf{x}_i}$ is the $i$-th "2-segment" of the gradient.
here are the equivalent entry-wise operations:
U_x[2 * i + 0] += s.x;
U_x[2 * i + 1] += s.y;
add(U_xx, i, j, M);
adds mat2 $\mathbf{M}$ to $\frac{d^2U}{d\mathbf{x}_id\mathbf{x}_j}$ // NOTE $\frac{d^2U}{d\mathbf{x}_id\mathbf{x}_j}$ is the $(i, j)$-th 2x2 block of the Hessian.
here are the equivalent entry-wise operations (don't do this):
sbuff_push_back(&U_xx, { 2 * i + 0, 2 * j + 0, M(0, 0) });
sbuff_push_back(&U_xx, { 2 * i + 1, 2 * j + 0, M(1, 0) });
sbuff_push_back(&U_xx, { 2 * i + 1, 2 * j + 1, M(1, 1) });
sbuff_push_back(&U_xx, { 2 * i + 0, 2 * j + 1, M(0, 1) });
🚨 important! you must guard all modifications to U
, U_x
, and U_xx
(the usage of compute_and_add_to(...)
allows for U, U_x, U_xx to be NULL)
e.g. if (U) { (*U) += ...; }
e.g. if (U_x) { add(U_x, ...); }
e.g. if (U_xx) { add(U_xx, ...); }
// NOTE to check if your derivatives are correct, play the simulation with check_derivatives turned on -- i.e. press 'a' // NOTE (though it should be pretty obvious just from dragging nodes around etc.) implement gravity where indicated by the TODO in of
compute_and_add_to(...)
consider node $i$
it has current position $\mathbf{x}_i=(x_i^x, x_i^y)$
its gravity energy contribution is $E^{\text{gravity}}_i = mgx_i^y$
its gravity gradient contribution is $\frac{dE^{\text{gravity}}_i}{d\mathbf{x}_i} = (0, mg)^T$
✨ SPOILER
if (U_x) { add(U_x, i, V2(0, m * g)); }
compute_and_add_to(...)
say node $i$ is pinned
it has current position $\mathbf{x}_i$ and rest position $\mathbf{X}_i$
its deformation is $\Delta_i = \mathbf{x}_i - \mathbf{X}_i$
its pin energy contribution is $E^{\text{pin}}_i = k^\text{pin} |\Delta_i|^2 / 2$ // NOTE use squaredNorm
its pin gradient contribution is $\frac{dE^{\text{pin}}_i}{d\mathbf{x}_i} = k^\text{pin} \Delta_i$
its pin Hessian contribution is $\frac{d^2E^{\text{pin}}_i}{d\mathbf{x}_i^2} = k^\text{pin} \mathbf{I}$ // NOTE $\mathbf{I}$ is the 2x2 identity
extend your gravity implementation to support gravity pointing in any direction
the angle of the gravity vector (from the positive $x$-axis) is tweaks.theta_gravity
// NOTE i draw a yellow arrow to visualize $\theta_{\text{gravity}}.$
✨ HINT
consider a constant external force $\mathbf{f}$ acting on node $i$ its energy contribution is $E^{\text{force}}_i = -(\mathbf{f} \cdot \mathbf{x}_i)$ its gradient contribution is $\frac{dE^{\text{force}}_i}{d\mathbf{x}_i} = -\mathbf{f}$(optional) implement another energy (as well as its gradient and Hessian) e.g., force that pulls every node towards your mouse e.g., very simple buoyancy model (draw some nice transparent water, remove the pins, and watch the bar float) (very optional) delete my implementation of the springs energy, gradient, and Hessian and implement it yourself (will explain math to anyone who is interested 🙂👍)