summaryrefslogtreecommitdiff
path: root/frontend/_rigidbody
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/_rigidbody')
-rw-r--r--frontend/_rigidbody/circle.js12
-rw-r--r--frontend/_rigidbody/pill.js0
-rw-r--r--frontend/_rigidbody/rigidbody_1.js211
-rw-r--r--frontend/_rigidbody/rigidbody_2.js260
-rw-r--r--frontend/_rigidbody/rigidbody_3.js158
-rw-r--r--frontend/_rigidbody/rigidbody_3a.js189
-rw-r--r--frontend/_rigidbody/rigidbody_3b.js192
7 files changed, 629 insertions, 393 deletions
diff --git a/frontend/_rigidbody/circle.js b/frontend/_rigidbody/circle.js
index 35044e3..1cec7ce 100644
--- a/frontend/_rigidbody/circle.js
+++ b/frontend/_rigidbody/circle.js
@@ -1,3 +1,6 @@
+/// <reference path="mat4.js" />
+/// <reference path="vec2.js" />
+
const BYTES_PER_FLOAT = 4;
/**
@@ -42,13 +45,14 @@ function circle(pGl, pRadius, pSegments, pColorList, pInitialPosition, pMass) {
vertexCount: vertexCount,
prevPos: vec2(),
position: pInitialPosition || vec2(),
+ prevVelocity: vec2(),
velocity: vec2(),
force: vec2(),
torque: 0,
- mass: pMass || 1,
+ mass: pMass === undefined ? 1 : pMass,
rotationVelocity: 0,
rotationRadians: 0,
- model: mat4(),
+ model: translateMatrix(mat4(), pInitialPosition ? pInitialPosition.x : 0, pInitialPosition ? pInitialPosition.y : 0, 0),
radius: pRadius
};
}
@@ -106,4 +110,8 @@ function getIntersectionDataForCircles(pFirst, pSecond) {
firstPointOfApplication: subVec2(lMedianIntersectingPoint, pFirst.position),
secondPointOfApplication: subVec2(lMedianIntersectingPoint, pSecond.position)
}
+}
+
+function freeCircle(pGl, pCircle) {
+ pGl.deleteBuffer(pCircle.buffer);
} \ No newline at end of file
diff --git a/frontend/_rigidbody/pill.js b/frontend/_rigidbody/pill.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/frontend/_rigidbody/pill.js
diff --git a/frontend/_rigidbody/rigidbody_1.js b/frontend/_rigidbody/rigidbody_1.js
index c5e4f0a..697822d 100644
--- a/frontend/_rigidbody/rigidbody_1.js
+++ b/frontend/_rigidbody/rigidbody_1.js
@@ -5,110 +5,113 @@
/// <reference path="circle.js" />
/// <reference path="program_common.js" />
-function main() {
- // Define Constants
- const CIRCLE_RADIUS = 16;
- const GRAVITY = 9.8;
-
- // Retrieve context
- const lProgramContext = getContext('#rigidbody_1');
-
- if (lProgramContext.gl === null) {
- console.error('Unable to initialize WebGL. Your browser or machine may not support it.');
- return;
- }
-
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
-
- function run() {
- console.log('Running Rigid Body 1');
- lProgramContext.load().then(function(pProgramInfo) {
- const lCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [{ x: 0.3, y: 0.3, z: 0.3, w: 1 }], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0));
-
- function update(pDeltaTimeSeconds) {
- // Physics updates
- const lGravityForce = vec2(0, -1.0 * (lCircle.mass * GRAVITY));
-
- // Add up the forces acting on the circle
- lCircle.force = addVec2(lCircle.force, lGravityForce);
-
- // Figure out acceleration (a = F / m)
- const lCurrentAcceleration = scaleVec2(lCircle.force, 1.0 / lCircle.mass);
-
- // Calculate the new velocity: v = v0 + a * t
- lCircle.velocity = addVec2(lCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds));
-
- // Update the position based on velocity: x = x0 + v * t
- lCircle.position = addVec2(lCircle.position, scaleVec2(lCircle.velocity, pDeltaTimeSeconds));
-
- // Update the model matrix accordingly
- lCircle.model = translateMatrix(mat4(), lCircle.position.x, lCircle.position.y, 0);
-
- // Report the current state to the frontend
- $('#rigidbody_1_force_field').text(vec2str(lCircle.force));
- $('#rigidbody_1_acceleration_field').text(vec2str(lCurrentAcceleration));
- $('#rigidbody_1_velocity_field').text(vec2str(lCircle.velocity));
- $('#rigidbody_1_position_field').text(vec2str(lCircle.position));
-
- // Reset the circle's force vector
- lCircle.force = vec2();
-
- // Render Code only
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
- lProgramContext.gl.clearDepth(1.0); // Clear everything
- lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST); // Enable depth testing
- lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL); // Near things obscure far things
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT);
-
- lProgramContext.gl.useProgram(pProgramInfo.program);
- lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective);
- lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.model, false, lCircle.model);
-
- renderCircle(lProgramContext.gl, pProgramInfo, lCircle);
- }
-
- function addForce(ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- let lXValue = $(this).find('.vec2_x_input').val(),
- lYValue = $(this).find('.vec2_y_input').val();
-
- if (lXValue.length === 0) {
- lXValue = 0;
+(function() {
+
+ function main() {
+ // Define Constants
+ const CIRCLE_RADIUS = 16;
+ const GRAVITY = 9.8;
+
+ // Retrieve context
+ const lProgramContext = getContext('#rigidbody_1');
+
+ if (lProgramContext.gl === null) {
+ console.error('Unable to initialize WebGL. Your browser or machine may not support it.');
+ return;
+ }
+
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
+
+ function run() {
+ console.log('Running Rigid Body 1');
+ lProgramContext.load().then(function(pProgramInfo) {
+ const lCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [{ x: 0.3, y: 0.3, z: 0.3, w: 1 }], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0));
+
+ function update(pDeltaTimeSeconds) {
+ // Physics updates
+ const lGravityForce = vec2(0, -1.0 * (lCircle.mass * GRAVITY));
+
+ // Add up the forces acting on the circle
+ lCircle.force = addVec2(lCircle.force, lGravityForce);
+
+ // Figure out acceleration (a = F / m)
+ const lCurrentAcceleration = scaleVec2(lCircle.force, 1.0 / lCircle.mass);
+
+ // Calculate the new velocity: v = v0 + a * t
+ lCircle.velocity = addVec2(lCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds));
+
+ // Update the position based on velocity: x = x0 + v * t
+ lCircle.position = addVec2(lCircle.position, scaleVec2(lCircle.velocity, pDeltaTimeSeconds));
+
+ // Update the model matrix accordingly
+ lCircle.model = translateMatrix(mat4(), lCircle.position.x, lCircle.position.y, 0);
+
+ // Report the current state to the frontend
+ $('#rigidbody_1_force_field').text(vec2str(lCircle.force));
+ $('#rigidbody_1_acceleration_field').text(vec2str(lCurrentAcceleration));
+ $('#rigidbody_1_velocity_field').text(vec2str(lCircle.velocity));
+ $('#rigidbody_1_position_field').text(vec2str(lCircle.position));
+
+ // Reset the circle's force vector
+ lCircle.force = vec2();
+
+ // Render Code only
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
+ lProgramContext.gl.clearDepth(1.0); // Clear everything
+ lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST); // Enable depth testing
+ lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL); // Near things obscure far things
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT);
+
+ lProgramContext.gl.useProgram(pProgramInfo.program);
+ lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective);
+ lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.model, false, lCircle.model);
+
+ renderCircle(lProgramContext.gl, pProgramInfo, lCircle);
}
-
- if (lYValue.length === 0) {
- lYValue = 5000;
+
+ function addForce(ev) {
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ let lXValue = $(this).find('.vec2_x_input').val(),
+ lYValue = $(this).find('.vec2_y_input').val();
+
+ if (lXValue.length === 0) {
+ lXValue = 0;
+ }
+
+ if (lYValue.length === 0) {
+ lYValue = 5000;
+ }
+
+ console.log('Applying force: ' + lXValue + ', ' + lYValue);
+ lCircle.force = addVec2(lCircle.force, vec2(Number(lXValue), Number(lYValue)));
}
-
- console.log('Applying force: ' + lXValue + ', ' + lYValue);
- lCircle.force = addVec2(lCircle.force, vec2(Number(lXValue), Number(lYValue)));
- }
-
- function cleanup() {
- lProgramContext.gl.deleteBuffer(lCircle.buffer);
- lProgramContext.gl.deleteProgram(pProgramInfo.program);
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
- }
-
- function reset() {
- lExitRequestFunc();
- lProgramContext.reset();
- $('#rigidbody_1_force_submit_button').unbind('submit').submit(false);
- }
-
- const lExitRequestFunc = requestUpdateLoop(update, cleanup);
- lProgramContext.stopButton.on('click', reset);
- $('#rigidbody_1_force_submit_button').submit(addForce);
- });
+
+ function cleanup() {
+ lProgramContext.gl.deleteBuffer(lCircle.buffer);
+ lProgramContext.gl.deleteProgram(pProgramInfo.program);
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
+ }
+
+ function reset() {
+ lExitRequestFunc();
+ lProgramContext.reset();
+ $('#rigidbody_1_force_submit_button').unbind('submit').submit(false);
+ }
+
+ const lExitRequestFunc = requestUpdateLoop(update, cleanup);
+ lProgramContext.stopButton.on('click', reset);
+ $('#rigidbody_1_force_submit_button').submit(addForce);
+ });
+ }
+
+ lProgramContext.playButton.on('click', run);
+ $('#rigidbody_1_force_submit_button').submit(false);
+
}
-
- lProgramContext.playButton.on('click', run);
- $('#rigidbody_1_force_submit_button').submit(false);
-
-}
-
-$(document).ready(main); \ No newline at end of file
+
+ $(document).ready(main);
+})() \ No newline at end of file
diff --git a/frontend/_rigidbody/rigidbody_2.js b/frontend/_rigidbody/rigidbody_2.js
index 1c6b79f..c878c1a 100644
--- a/frontend/_rigidbody/rigidbody_2.js
+++ b/frontend/_rigidbody/rigidbody_2.js
@@ -4,140 +4,142 @@
/// <reference path="shader.js" />
/// <reference path="circle.js" />
-function main() {
- // Define Constants
- const CIRCLE_RADIUS = 16;
- const GRAVITY = 9.8;
-
- // Retrieve context
- const lProgramContext = getContext('#rigidbody_2');
-
- if (lProgramContext.gl === null) {
- console.error('Unable to initialize WebGL. Your browser or machine may not support it.');
- return;
- }
-
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
-
- function run() {
- console.log('Running Rigid Body 2');
- lProgramContext.load().then(function(pProgramInfo) {
- const lCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [
- { x: 1, y: 1, z: 0, w: 1 },
- { x: 1, y: 0, z: 1, w: 1 },
- { x: 0, y: 1, z: 1, w: 1 },
- { x: 0, y: 1, z: 0, w: 1 }
- ], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0));
-
- function update(pDeltaTimeSeconds) {
- // Same physics updates from part 1
- applyForce(vec2(0, -1.0 * (lCircle.mass * GRAVITY)));
- const lCurrentAcceleration = scaleVec2(lCircle.force, 1.0 / lCircle.mass);
- lCircle.velocity = addVec2(lCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds));
- lCircle.position = addVec2(lCircle.position, scaleVec2(lCircle.velocity, pDeltaTimeSeconds));
- lCircle.force = vec2();
-
- // Angular code starts here
-
- // Retrieve the moment of inertia for our shape (Ours is a circle by default)
- const lMomentOfInertia = getMomentOfInertia(lCircle);
-
- // Calculate the angular acceperation (omega = T / I)
- const lAngularAcceleration = lCircle.torque / lMomentOfInertia;
-
- // Calculate the rotation in radians
- lCircle.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds;
- lCircle.rotationRadians += lCircle.rotationVelocity * pDeltaTimeSeconds;
- lCircle.torque = 0;
-
- // Calculate the model as previously, but this time, also rotate it
- lCircle.model = rotateMatrix2d(translateMatrix(mat4(), lCircle.position.x, lCircle.position.y, 0), lCircle.rotationRadians);
-
- // Render Code only
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
- lProgramContext.gl.clearDepth(1.0);
- lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST);
- lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL);
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT);
-
- lProgramContext.gl.useProgram(pProgramInfo.program);
- lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective);
- lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.model, false, lCircle.model);
-
- renderCircle(lProgramContext.gl, pProgramInfo, lCircle);
- }
-
- const TORQUE_MULTIPLIER = 100.0; // TODO: This may be unncessary
-
- function applyForce(pForceVector, pPointOfApplication) {
- if (pPointOfApplication !== undefined) {
- const lOriginToPointOfApp = subVec2(vec2(), pPointOfApplication), // The point of application is relative to the model (i.e. the center of the circle, not the scene)
- lPerpVec = vec2(-lOriginToPointOfApp.y, lOriginToPointOfApp.x); // Retrieve the perpendicular vector
+(function() {
+ function main() {
+ // Define Constants
+ const CIRCLE_RADIUS = 16;
+ const GRAVITY = 9.8;
- // Calculate the torque from the perp dot (T = r_perp . F)
- lCircle.torque += TORQUE_MULTIPLIER * dot2(lPerpVec, pForceVector);
- }
-
- lCircle.force = addVec2(lCircle.force, pForceVector);
- }
-
- function cleanup() {
- lProgramContext.gl.deleteBuffer(lCircle.buffer);
- lProgramContext.gl.deleteProgram(pProgramInfo.program);
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
- }
-
- function reset() {
- lExitRequestFunc();
- lProgramContext.reset();
- $('#rigidbody_2_force_submit_button').unbind('submit').submit(false);
- }
-
- const lExitRequestFunc = requestUpdateLoop(update, cleanup);
- lProgramContext.stopButton.on('click', reset);
- $('#rigidbody_2_force_submit_button').submit(function(pEv) {
- pEv.preventDefault();
- pEv.stopPropagation();
-
- // Read in the force vector from the form
- const lForceGroup = $('#rigidbody_2_force_input'),
- lPositionGroup = $('#rigidbody_2_position_input');
-
- let lForceVectorX = lForceGroup.find('.vec2_x_input').val(),
- lForceVectorY = lForceGroup.find('.vec2_y_input').val();
-
- if (lForceVectorX.length === 0) {
- lForceVectorX = 0;
+ // Retrieve context
+ const lProgramContext = getContext('#rigidbody_2');
+
+ if (lProgramContext.gl === null) {
+ console.error('Unable to initialize WebGL. Your browser or machine may not support it.');
+ return;
+ }
+
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
+
+ function run() {
+ console.log('Running Rigid Body 2');
+ lProgramContext.load().then(function(pProgramInfo) {
+ const lCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [
+ { x: 1, y: 1, z: 0, w: 1 },
+ { x: 1, y: 0, z: 1, w: 1 },
+ { x: 0, y: 1, z: 1, w: 1 },
+ { x: 0, y: 1, z: 0, w: 1 }
+ ], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0));
+
+ function update(pDeltaTimeSeconds) {
+ // Same physics updates from part 1
+ applyForce(vec2(0, -1.0 * (lCircle.mass * GRAVITY)));
+ const lCurrentAcceleration = scaleVec2(lCircle.force, 1.0 / lCircle.mass);
+ lCircle.velocity = addVec2(lCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds));
+ lCircle.position = addVec2(lCircle.position, scaleVec2(lCircle.velocity, pDeltaTimeSeconds));
+ lCircle.force = vec2();
+
+ // Angular code starts here
+
+ // Retrieve the moment of inertia for our shape (Ours is a circle by default)
+ const lMomentOfInertia = getMomentOfInertia(lCircle);
+
+ // Calculate the angular acceperation (omega = T / I)
+ const lAngularAcceleration = lCircle.torque / lMomentOfInertia;
+
+ // Calculate the rotation in radians
+ lCircle.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds;
+ lCircle.rotationRadians += lCircle.rotationVelocity * pDeltaTimeSeconds;
+ lCircle.torque = 0;
+
+ // Calculate the model as previously, but this time, also rotate it
+ lCircle.model = rotateMatrix2d(translateMatrix(mat4(), lCircle.position.x, lCircle.position.y, 0), lCircle.rotationRadians);
+
+ // Render Code only
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clearDepth(1.0);
+ lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST);
+ lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT);
+
+ lProgramContext.gl.useProgram(pProgramInfo.program);
+ lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective);
+ lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.model, false, lCircle.model);
+
+ renderCircle(lProgramContext.gl, pProgramInfo, lCircle);
}
-
- if (lForceVectorY.length === 0) {
- lForceVectorY = 5000;
+
+ const TORQUE_MULTIPLIER = 100.0; // TODO: This may be unncessary
+
+ function applyForce(pForceVector, pPointOfApplication) {
+ if (pPointOfApplication !== undefined) {
+ const lOriginToPointOfApp = subVec2(vec2(), pPointOfApplication), // The point of application is relative to the model (i.e. the center of the circle, not the scene)
+ lPerpVec = vec2(-lOriginToPointOfApp.y, lOriginToPointOfApp.x); // Retrieve the perpendicular vector
+
+ // Calculate the torque from the perp dot (T = r_perp . F)
+ lCircle.torque += TORQUE_MULTIPLIER * dot2(lPerpVec, pForceVector);
+ }
+
+ lCircle.force = addVec2(lCircle.force, pForceVector);
}
-
- // Read in the point of application vector from the form
- let lPositionGroupX = lPositionGroup.find('.vec2_x_input').val(),
- lPositionGroupY = lPositionGroup.find('.vec2_y_input').val();
-
- if (lPositionGroupX.length === 0) {
- lPositionGroupX = -Math.sqrt(2) / 2;
+
+ function cleanup() {
+ lProgramContext.gl.deleteBuffer(lCircle.buffer);
+ lProgramContext.gl.deleteProgram(pProgramInfo.program);
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
}
-
- if (lPositionGroupY.length === 0) {
- lPositionGroupY = -Math.sqrt(2) / 2;
+
+ function reset() {
+ lExitRequestFunc();
+ lProgramContext.reset();
+ $('#rigidbody_2_force_submit_button').unbind('submit').submit(false);
}
-
- const lForceVector = vec2(Number(lForceVectorX), Number(lForceVectorY));
- const lPointOfApplication = scaleVec2(normalize2(vec2(Number(lPositionGroupX), Number(lPositionGroupY))), lCircle.radius);
-
- applyForce(lForceVector, lPointOfApplication);
+
+ const lExitRequestFunc = requestUpdateLoop(update, cleanup);
+ lProgramContext.stopButton.on('click', reset);
+ $('#rigidbody_2_force_submit_button').submit(function(pEv) {
+ pEv.preventDefault();
+ pEv.stopPropagation();
+
+ // Read in the force vector from the form
+ const lForceGroup = $('#rigidbody_2_force_input'),
+ lPositionGroup = $('#rigidbody_2_position_input');
+
+ let lForceVectorX = lForceGroup.find('.vec2_x_input').val(),
+ lForceVectorY = lForceGroup.find('.vec2_y_input').val();
+
+ if (lForceVectorX.length === 0) {
+ lForceVectorX = 0;
+ }
+
+ if (lForceVectorY.length === 0) {
+ lForceVectorY = 5000;
+ }
+
+ // Read in the point of application vector from the form
+ let lPositionGroupX = lPositionGroup.find('.vec2_x_input').val(),
+ lPositionGroupY = lPositionGroup.find('.vec2_y_input').val();
+
+ if (lPositionGroupX.length === 0) {
+ lPositionGroupX = -Math.sqrt(2) / 2;
+ }
+
+ if (lPositionGroupY.length === 0) {
+ lPositionGroupY = -Math.sqrt(2) / 2;
+ }
+
+ const lForceVector = vec2(Number(lForceVectorX), Number(lForceVectorY));
+ const lPointOfApplication = scaleVec2(normalize2(vec2(Number(lPositionGroupX), Number(lPositionGroupY))), lCircle.radius);
+
+ applyForce(lForceVector, lPointOfApplication);
+ });
});
- });
+ }
+
+ lProgramContext.playButton.on('click', run);
+ $('#rigidbody_2_force_submit_button').submit(false);
}
- lProgramContext.playButton.on('click', run);
- $('#rigidbody_2_force_submit_button').submit(false);
-}
-
-$(document).ready(main); \ No newline at end of file
+ $(document).ready(main);
+})() \ No newline at end of file
diff --git a/frontend/_rigidbody/rigidbody_3.js b/frontend/_rigidbody/rigidbody_3.js
deleted file mode 100644
index 0316268..0000000
--- a/frontend/_rigidbody/rigidbody_3.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/// <reference path="../scripts/jquery-3.5.1.min.js"/>
-/// <reference path="vec2.js" />
-/// <reference path="mat4.js" />
-/// <reference path="shader.js" />
-/// <reference path="circle.js" />
-
-function main() {
- // Define Constants
- const CIRCLE_RADIUS = 16;
- const GRAVITY = 9.8;
- const COF_OF_RESTITUITION = 0.7;
-
- // Retrieve context
- const lProgramContext = getContext('#rigidbody_3');
-
- if (lProgramContext.gl === null) {
- console.error('Unable to initialize WebGL. Your browser or machine may not support it.');
- return;
- }
-
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
-
- function run() {
- console.log('Running Rigid Body 3');
- lProgramContext.load().then(function(pProgramInfo) {
- // Circile initialization
- const horizontalCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [
- { x: 1, y: 1, z: 0, w: 1 },
- { x: 1, y: 0, z: 1, w: 1 },
- { x: 0, y: 1, z: 1, w: 1 },
- { x: 0, y: 1, z: 0, w: 1 }
- ], vec2(400, lProgramContext.height / 2.0));
-
- const verticalCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [
- { x: 1, y: 0, z: 0, w: 1 },
- { x: 0, y: 1, z: 0, w: 1 },
- { x: 0, y: 0, z: 1, w: 1 }
- ], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0 + 100));
-
- horizontalCircle.velocity = vec2(-100, 0);
- verticalCircle.velocity = vec2(0, -100);
-
- lTimeStepScale = $('#time_step_slider').val();
-
- /**
- * Run the update method of a single circle
- *
- * @param {circle} pCircle
- * @param {number} pDeltaTimeSeconds
- */
- function updateCircle(pCircle, pDeltaTimeSeconds) {
- // Same physics updates from part 1
- applyForce(pCircle, vec2(0, -1.0 * (pCircle.mass * GRAVITY)));
- const lCurrentAcceleration = scaleVec2(pCircle.force, 1.0 / pCircle.mass);
- pCircle.velocity = addVec2(pCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds));
- pCircle.prevPos = { ...pCircle.position };
- pCircle.position = addVec2(pCircle.position, scaleVec2(pCircle.velocity, pDeltaTimeSeconds));
- pCircle.force = vec2();
-
- // Same physics updates from part 2
- const lMomentOfInertia = getMomentOfInertia(pCircle);
- const lAngularAcceleration = pCircle.torque / lMomentOfInertia;
- pCircle.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds;
- pCircle.rotationRadians += pCircle.rotationVelocity * pDeltaTimeSeconds;
- pCircle.torque = 0;
-
- pCircle.model = rotateMatrix2d(translateMatrix(mat4(), pCircle.position.x, pCircle.position.y, 0), pCircle.rotationRadians);
- }
-
- function update(pDeltaTimeSeconds) {
- pDeltaTimeSeconds = pDeltaTimeSeconds * lTimeStepScale;
- updateCircle(horizontalCircle, pDeltaTimeSeconds);
- updateCircle(verticalCircle, pDeltaTimeSeconds);
- collision();
- render();
- }
-
- function collision() {
- if (!doCirclesIntersect(horizontalCircle, verticalCircle)) {
- return false;
- }
-
- const lIntersectionResult = getIntersectionDataForCircles(horizontalCircle, verticalCircle);
-
- console.log('We have a collision');
- const lRelativeVelocity = lIntersectionResult.relativeVelocity,
- lCollisionNormal = lIntersectionResult.collisionNormal,
- lFirstPerp = getPerp2(lIntersectionResult.firstPointOfApplication),
- lSecondPerp = getPerp2(lIntersectionResult.secondPointOfApplication);
-
- console.log(lIntersectionResult);
-
- const lNumerator = dot2(scaleVec2(lRelativeVelocity, -(1.0 + COF_OF_RESTITUITION)), lCollisionNormal);
- const lLinearDenomPart = dot2(lCollisionNormal, (scaleVec2(lCollisionNormal, 1 / horizontalCircle.mass + 1 / verticalCircle.mass)));
- const lRotationalDenomPart = (Math.pow(dot2(lFirstPerp, lCollisionNormal), 2) / getMomentOfInertia(horizontalCircle))
- + (Math.pow(dot2(lSecondPerp, lCollisionNormal), 2) / getMomentOfInertia(verticalCircle))
-
- const lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart);
-
- horizontalCircle.velocity = addVec2(horizontalCircle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / horizontalCircle.mass));
- verticalCircle.velocity = subVec2(verticalCircle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / verticalCircle.mass));
-
- horizontalCircle.rotationVelocity = horizontalCircle.rotationVelocity + dot2(lFirstPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getMomentOfInertia(horizontalCircle);
- verticalCircle.rotationVelocity = verticalCircle.rotationVelocity - dot2(lSecondPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getMomentOfInertia(verticalCircle);
-
- return true;
- }
-
- function render() {
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
- lProgramContext.gl.clearDepth(1.0);
- lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST);
- lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL);
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT);
- lProgramContext.gl.useProgram(pProgramInfo.program);
- lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective);
-
- renderCircle(lProgramContext.gl, pProgramInfo, horizontalCircle);
- renderCircle(lProgramContext.gl, pProgramInfo, verticalCircle);
- }
-
- const TORQUE_MULTIPLIER = 100.0; // TODO: This may be unncessary
- function applyForce(pCircle, pForceVector, pPointOfApplication) {
- if (pPointOfApplication !== undefined) {
- const lOriginToPointOfApp = subVec2(vec2(), pPointOfApplication),
- lPerpVec = vec2(-lOriginToPointOfApp.y, lOriginToPointOfApp.x);
-
- pCircle.torque += TORQUE_MULTIPLIER * dot2(lPerpVec, pForceVector);
- }
-
- pCircle.force = addVec2(pCircle.force, pForceVector);
- }
-
- function cleanup() {
- lProgramContext.gl.deleteBuffer(horizontalCircle.buffer);
- lProgramContext.gl.deleteBuffer(verticalCircle.buffer);
- lProgramContext.gl.deleteProgram(pProgramInfo.program);
- lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
- lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
- }
-
- function reset() {
- lExitRequestFunc();
- lProgramContext.reset();
- $('#time_step_slider').unbind('change');
- }
-
- const lExitRequestFunc = requestUpdateLoop(update, cleanup);
- lProgramContext.stopButton.on('click', reset);
- $('#time_step_slider').on('change', function() { lTimeStepScale = $(this).val(); });
- });
- }
-
- lProgramContext.playButton.on('click', run);
-}
-
-$(document).ready(main); \ No newline at end of file
diff --git a/frontend/_rigidbody/rigidbody_3a.js b/frontend/_rigidbody/rigidbody_3a.js
new file mode 100644
index 0000000..74d309b
--- /dev/null
+++ b/frontend/_rigidbody/rigidbody_3a.js
@@ -0,0 +1,189 @@
+/// <reference path="../scripts/jquery-3.5.1.min.js"/>
+/// <reference path="vec2.js" />
+/// <reference path="mat4.js" />
+/// <reference path="shader.js" />
+/// <reference path="circle.js" />
+
+(function() {
+ function main() {
+ // Define Constants
+ const CIRCLE_RADIUS = 16;
+ const GRAVITY = 9.8;
+ const COF_OF_RESTITUITION = 0.7;
+
+ // Retrieve context
+ const lProgramContext = getContext('#rigidbody_3a');
+
+ if (lProgramContext.gl === null) {
+ console.error('Unable to initialize WebGL. Your browser or machine may not support it.');
+ return;
+ }
+
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
+
+ function run() {
+ console.log('Running Rigid Body 3a');
+ lProgramContext.load().then(function(pProgramInfo) {
+ // Circile initialization
+ const horizontalCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [
+ { x: 1, y: 1, z: 0, w: 1 },
+ { x: 1, y: 0, z: 1, w: 1 },
+ { x: 0, y: 1, z: 1, w: 1 },
+ { x: 0, y: 1, z: 0, w: 1 }
+ ], vec2(400, lProgramContext.height / 2.0));
+
+ const verticalCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [
+ { x: 1, y: 0, z: 0, w: 1 },
+ { x: 0, y: 1, z: 0, w: 1 },
+ { x: 0, y: 0, z: 1, w: 1 }
+ ], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0 + 100));
+
+ horizontalCircle.velocity = vec2(-100, 0);
+ verticalCircle.velocity = vec2(0, -100);
+
+ lTimeStepScale = $('#time_step_slider').val();
+
+ /**
+ * Run the update method of a single circle
+ *
+ * @param {circle} pCircle
+ * @param {number} pDeltaTimeSeconds
+ */
+ function updateCircle(pCircle, pDeltaTimeSeconds) {
+ // Same physics updates from part 1
+ applyForce(pCircle, vec2(0, -1.0 * (pCircle.mass * GRAVITY)));
+ const lCurrentAcceleration = scaleVec2(pCircle.force, 1.0 / pCircle.mass);
+ pCircle.prevVelocity = pCircle.velocity;
+ pCircle.velocity = addVec2(pCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds));
+ pCircle.prevPos = { ...pCircle.position };
+ pCircle.position = addVec2(pCircle.position, scaleVec2(pCircle.velocity, pDeltaTimeSeconds));
+ pCircle.force = vec2();
+
+ // Same physics updates from part 2
+ const lMomentOfInertia = getMomentOfInertia(pCircle);
+ const lAngularAcceleration = pCircle.torque / lMomentOfInertia;
+ pCircle.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds;
+ pCircle.rotationRadians += pCircle.rotationVelocity * pDeltaTimeSeconds;
+ pCircle.torque = 0;
+
+ pCircle.model = rotateMatrix2d(translateMatrix(mat4(), pCircle.position.x, pCircle.position.y, 0), pCircle.rotationRadians);
+ }
+
+ function update(pDeltaTimeSeconds) {
+ pDeltaTimeSeconds = pDeltaTimeSeconds * lTimeStepScale;
+ updateCircle(horizontalCircle, pDeltaTimeSeconds);
+ updateCircle(verticalCircle, pDeltaTimeSeconds);
+ collision(pDeltaTimeSeconds);
+ render();
+ }
+
+ function collision(pDeltaTimeSeconds) {
+ if (!doCirclesIntersect(horizontalCircle, verticalCircle)) {
+ return false;
+ }
+
+ // We have an intersection! Let's try and figure out precisely when that happened.
+ let lSubdividedDeltaTime = pDeltaTimeSeconds;
+ let lSubdivideHorizontalBall = undefined,
+ lSubdivideVerticalBall = undefined;
+
+ do {
+ lSubdivideHorizontalBall = JSON.parse(JSON.stringify(horizontalCircle));
+ lSubdivideHorizontalBall.position = {...horizontalCircle.prevPos};
+ lSubdivideHorizontalBall.velocity = {...horizontalCircle.prevVelocity};
+
+ lSubdivideVerticalBall = JSON.parse(JSON.stringify(verticalCircle));
+ lSubdivideVerticalBall.position = {...verticalCircle.prevPos};
+ lSubdivideVerticalBall.velocity = {...verticalCircle.prevVelocity};
+
+ lSubdividedDeltaTime = lSubdividedDeltaTime / 2.0;
+ updateCircle(lSubdivideHorizontalBall, lSubdividedDeltaTime);
+ updateCircle(lSubdivideVerticalBall, lSubdividedDeltaTime);
+
+ if (lSubdividedDeltaTime === 0) {
+ console.error('This should NOT be happening');
+ break;
+ }
+ } while (doCirclesIntersect(lSubdivideHorizontalBall, lSubdivideVerticalBall))
+
+ const lIntersectionResult = getIntersectionDataForCircles(lSubdivideHorizontalBall, lSubdivideVerticalBall);
+
+ console.log('We have a collision');
+ const lRelativeVelocity = lIntersectionResult.relativeVelocity,
+ lCollisionNormal = lIntersectionResult.collisionNormal,
+ lFirstPerp = getPerp2(lIntersectionResult.firstPointOfApplication),
+ lSecondPerp = getPerp2(lIntersectionResult.secondPointOfApplication);
+
+ const lNumerator = dot2(scaleVec2(lRelativeVelocity, -(1.0 + COF_OF_RESTITUITION)), lCollisionNormal);
+ const lLinearDenomPart = dot2(lCollisionNormal, (scaleVec2(lCollisionNormal, 1 / horizontalCircle.mass + 1 / verticalCircle.mass)));
+ const lRotationalDenomPart = (Math.pow(dot2(lFirstPerp, lCollisionNormal), 2) / getMomentOfInertia(horizontalCircle))
+ + (Math.pow(dot2(lSecondPerp, lCollisionNormal), 2) / getMomentOfInertia(verticalCircle))
+
+ const lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart);
+
+ horizontalCircle.position = lSubdivideHorizontalBall.position;
+ verticalCircle.position = lSubdivideVerticalBall.position;
+
+ horizontalCircle.velocity = addVec2(horizontalCircle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / horizontalCircle.mass));
+ verticalCircle.velocity = subVec2(verticalCircle.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / verticalCircle.mass));
+
+ horizontalCircle.rotationVelocity = horizontalCircle.rotationVelocity + dot2(lFirstPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getMomentOfInertia(horizontalCircle);
+ verticalCircle.rotationVelocity = verticalCircle.rotationVelocity - dot2(lSecondPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getMomentOfInertia(verticalCircle);
+
+ updateCircle(horizontalCircle, pDeltaTimeSeconds - lSubdividedDeltaTime);
+ updateCircle(verticalCircle, pDeltaTimeSeconds - lSubdividedDeltaTime);
+
+ return true;
+ }
+
+ function render() {
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clearDepth(1.0);
+ lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST);
+ lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT);
+ lProgramContext.gl.useProgram(pProgramInfo.program);
+ lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective);
+
+ renderCircle(lProgramContext.gl, pProgramInfo, horizontalCircle);
+ renderCircle(lProgramContext.gl, pProgramInfo, verticalCircle);
+ }
+
+ const TORQUE_MULTIPLIER = 100.0; // TODO: This may be unncessary
+ function applyForce(pCircle, pForceVector, pPointOfApplication) {
+ if (pPointOfApplication !== undefined) {
+ const lOriginToPointOfApp = subVec2(vec2(), pPointOfApplication),
+ lPerpVec = vec2(-lOriginToPointOfApp.y, lOriginToPointOfApp.x);
+
+ pCircle.torque += TORQUE_MULTIPLIER * dot2(lPerpVec, pForceVector);
+ }
+
+ pCircle.force = addVec2(pCircle.force, pForceVector);
+ }
+
+ function cleanup() {
+ lProgramContext.gl.deleteBuffer(horizontalCircle.buffer);
+ lProgramContext.gl.deleteBuffer(verticalCircle.buffer);
+ lProgramContext.gl.deleteProgram(pProgramInfo.program);
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
+ }
+
+ function reset() {
+ lExitRequestFunc();
+ lProgramContext.reset();
+ $('#time_step_slider').unbind('change');
+ }
+
+ const lExitRequestFunc = requestUpdateLoop(update, cleanup);
+ lProgramContext.stopButton.on('click', reset);
+ $('#time_step_slider').on('change', function() { lTimeStepScale = $(this).val(); });
+ });
+ }
+
+ lProgramContext.playButton.on('click', run);
+ }
+
+ $(document).ready(main);
+})() \ No newline at end of file
diff --git a/frontend/_rigidbody/rigidbody_3b.js b/frontend/_rigidbody/rigidbody_3b.js
new file mode 100644
index 0000000..df81582
--- /dev/null
+++ b/frontend/_rigidbody/rigidbody_3b.js
@@ -0,0 +1,192 @@
+/// <reference path="../scripts/jquery-3.5.1.min.js"/>
+/// <reference path="vec2.js" />
+/// <reference path="mat4.js" />
+/// <reference path="shader.js" />
+/// <reference path="circle.js" />
+/// <reference path="program_common.js" />
+
+(function() {
+ // Define Constants
+ const GRAVITY = 50.0;
+ const COF_OF_RESTITUITION = 0.2;
+
+ var lProgramContext = undefined;
+
+ function main() {
+ lProgramContext = getContext('#rigidbody_3b');
+
+ if (lProgramContext.gl === null) {
+ console.error('Unable to initialize WebGL. Your browser or machine may not support it.');
+ return;
+ }
+
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
+ lProgramContext.playButton.on('click', run);
+ }
+
+ function run() {
+ console.log('Running Rigid Body 3b');
+ lProgramContext.load().then(function(pProgramInfo) {
+ const lNumHorizontalPegs = lProgramContext.width / 80.0,
+ lNumVerticalPegs = lProgramContext.height / 80.0,
+ lBall = circle(lProgramContext.gl, 16.0, 16,
+ [ { x: 1, y: 0, z: 0, w: 1 }, { x: 0, y: 1, z: 0, w: 1 }, { x: 0, y: 0, z: 1, w: 1 } ],
+ vec2(38 * 8, lProgramContext.height - 24.0), 10.0),
+ lPegList = [];
+
+ lBall.velocity = vec2(0, -50.0);
+
+ // Generate a peg. These pegs will NOT be updated, so that they
+ // dont' fall due to gravity.
+ for (let lRowIdx = 1; lRowIdx < lNumVerticalPegs - 1; lRowIdx++) {
+ for (let lColIdx = 0; lColIdx < lNumHorizontalPegs -1; lColIdx++) {
+ lPegList.push(circle(lProgramContext.gl,
+ 16.0, 16,
+ [ { x: 165.0 / 255.0, y: 42.0 / 255.0, z: 42.0 / 255.0, w: 1.0 }],
+ vec2((lColIdx + 1) * 80.0, lRowIdx * 80.0),
+ 10000 // Real big so it's almost negligble
+ ));
+ }
+ }
+
+ function updateCircle(pCircle, pDeltaTimeSeconds) {
+ // Same physics updates from part 1
+ applyForce(pCircle, vec2(0, -1.0 * (pCircle.mass * GRAVITY)));
+ const lCurrentAcceleration = scaleVec2(pCircle.force, 1.0 / pCircle.mass);
+ pCircle.prevVelocity = pCircle.velocity;
+ pCircle.velocity = addVec2(pCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds));
+ pCircle.prevPos = { ...pCircle.position };
+ pCircle.position = addVec2(pCircle.position, scaleVec2(pCircle.velocity, pDeltaTimeSeconds));
+ pCircle.force = vec2();
+
+ // Same physics updates from part 2
+
+ const lMomentOfInertia = getMomentOfInertia(pCircle);
+ const lAngularAcceleration = pCircle.torque / lMomentOfInertia;
+ pCircle.rotationVelocity += lAngularAcceleration * pDeltaTimeSeconds;
+ pCircle.rotationRadians += pCircle.rotationVelocity * pDeltaTimeSeconds;
+ pCircle.torque = 0;
+
+ pCircle.model = rotateMatrix2d(translateMatrix(mat4(), pCircle.position.x, pCircle.position.y, 0), pCircle.rotationRadians);
+ }
+
+ function update(pDeltaTimeSeconds) {
+ updateCircle(lBall, pDeltaTimeSeconds);
+ collision(pDeltaTimeSeconds);
+ render();
+ }
+
+ function collision(pDeltaTimeSeconds) {
+ for (let lPegIdx = 0; lPegIdx < lPegList.length; lPegIdx++) {
+ const lPeg = lPegList[lPegIdx];
+
+ if (!doCirclesIntersect(lPeg, lBall)) {
+ continue;
+ }
+
+ // We have an intersection! Let's try and figure out precisely when that happened.
+ let lSubdividedDeltaTime = pDeltaTimeSeconds;
+
+ // Create a ball and move it back to the balls previous position
+ let lSubdividedBall = undefined;
+
+ do {
+ // Move the subdivided ball back to the previous position, and then
+ // advance its position by increasingly smaller timestep. This could be pretty
+ // slow in some circumstances, and it most definitely does not prevent tunneling,
+ // but - for now - this is not so bad.
+ lSubdividedBall = JSON.parse(JSON.stringify(lBall));
+ lSubdividedBall.position = {...lBall.prevPos};
+ lSubdividedBall.velocity = {...lBall.prevVelocity};
+ lSubdividedDeltaTime = lSubdividedDeltaTime / 2.0;
+ updateCircle(lSubdividedBall, lSubdividedDeltaTime);
+ if (lSubdividedDeltaTime === 0) {
+ console.error('This should NOT be happening');
+ break;
+ }
+ } while (doCirclesIntersect(lPeg, lSubdividedBall))
+
+ // The ball is no longer intersecting at the time presented here. That means this is
+ // (nearly) the precise point of intersection.
+
+ const lIntersectionResult = getIntersectionDataForCircles(lPeg, lSubdividedBall);
+
+ console.log(lIntersectionResult);
+
+ const lRelativeVelocity = lIntersectionResult.relativeVelocity,
+ lCollisionNormal = lIntersectionResult.collisionNormal,
+ lFirstPerp = getPerp2(lIntersectionResult.firstPointOfApplication),
+ lSecondPerp = getPerp2(lIntersectionResult.secondPointOfApplication);
+
+ const lNumerator = dot2(scaleVec2(lRelativeVelocity, -(1.0 + COF_OF_RESTITUITION)), lCollisionNormal);
+ const lLinearDenomPart = dot2(lCollisionNormal, (scaleVec2(lCollisionNormal, 1 / lBall.mass)));
+ const lRotationalDenomPart = (Math.pow(dot2(lFirstPerp, lCollisionNormal), 2) / getMomentOfInertia(lPeg))
+ + (Math.pow(dot2(lSecondPerp, lCollisionNormal), 2) / getMomentOfInertia(lBall))
+
+ const lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart);
+
+ lBall.position = lSubdividedBall.position; // Move the ball back to its proper subdivided position
+ lBall.velocity = subVec2(lBall.velocity, scaleVec2(lCollisionNormal, lImpulseMagnitude / lBall.mass));
+ lBall.rotationVelocity = lBall.rotationVelocity - dot2(lSecondPerp, scaleVec2(lCollisionNormal, lImpulseMagnitude)) / getMomentOfInertia(lBall);
+
+ console.log(lBall.rotationVelocity);
+
+ // Now we update in our new direction with the remaining time that we have left.
+ updateCircle(lBall, pDeltaTimeSeconds - lSubdividedDeltaTime);
+
+ break;
+ }
+ }
+
+ function render() {
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clearDepth(1.0);
+ lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST);
+ lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT);
+ lProgramContext.gl.useProgram(pProgramInfo.program);
+ lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective);
+
+ lPegList.forEach(function(pCircle) {
+ renderCircle(lProgramContext.gl, pProgramInfo, pCircle);
+ });
+
+ renderCircle(lProgramContext.gl, pProgramInfo, lBall);
+ }
+
+ const TORQUE_MULTIPLIER = 100.0; // TODO: This may be unncessary
+ function applyForce(pCircle, pForceVector, pPointOfApplication) {
+ if (pPointOfApplication !== undefined) {
+ const lOriginToPointOfApp = subVec2(vec2(), pPointOfApplication),
+ lPerpVec = vec2(-lOriginToPointOfApp.y, lOriginToPointOfApp.x);
+
+ pCircle.torque += TORQUE_MULTIPLIER * dot2(lPerpVec, pForceVector);
+ }
+
+ pCircle.force = addVec2(pCircle.force, pForceVector);
+ }
+
+ function cleanup() {
+ lPegList.forEach(function(pCircle) { freeCircle(lProgramContext.gl, pCircle); });
+ freeCircle(lProgramContext.gl, lBall);
+
+ lProgramContext.gl.deleteProgram(pProgramInfo.program);
+ lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT);
+ }
+
+ function reset() {
+ lExitRequestFunc();
+ lProgramContext.reset();
+ $('#time_step_slider').unbind('change');
+ }
+
+ const lExitRequestFunc = requestUpdateLoop(update, cleanup);
+ lProgramContext.stopButton.on('click', reset);
+ $('#time_step_slider').on('change', function() { lTimeStepScale = $(this).val(); });
+ });
+ }
+
+ $(document).ready(main);
+})() \ No newline at end of file