aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-05-23 22:21:04 -0400
committerbobzel <zzzman@gmail.com>2023-05-23 22:21:04 -0400
commitef36e8f0a71d09ea0ed3d106220ef9a8f9c19c3f (patch)
tree8c527dc6c51dc6a00ece9c9de733fd51f43524c7
parent1413ba695ee3efcf8686f8a6c618ff0cdda634db (diff)
more phys cleanup
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx508
2 files changed, 205 insertions, 305 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 13c27d182..515a870b5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -690,7 +690,7 @@ export namespace Docs {
{
data: '',
layout: { view: PhysicsSimulationBox, dataField: defaultDataKey, _width: 1000, _height: 800 },
- options: { _height: 100, layout_forceReflow: true, nativeHeightUnfrozen: true, nativeDimModifiable: true, position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: '' },
+ options: { _height: 100, layout_forceReflow: true, nativeHeightUnfrozen: true, mass1: '', mass2: '', nativeDimModifiable: true, position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: '' },
},
],
]);
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
index c036f1041..7175daf12 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -1,7 +1,8 @@
-import { computed, trace } from 'mobx';
+import { computed } from 'mobx';
import { observer } from 'mobx-react';
import './PhysicsSimulationBox.scss';
import React = require('react');
+import _ = require('lodash');
interface IWallProps {
length: number;
@@ -118,9 +119,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
componentDidMount() {
// Timer for animating the simulation
- setInterval(() => {
- this.setState({ timer: this.state.timer + 1 });
- }, 50);
+ setInterval(() => this.setState({ timer: this.state.timer + 1 }), 50);
}
// Constants
@@ -158,57 +157,46 @@ export default class Weight extends React.Component<IWeightProps, IState> {
const xAccel = Math.round(this.getNewAccelerationX(this.props.forcesUpdated()) * 100) / 100;
const yAccel = (-1 * Math.round(this.getNewAccelerationY(this.props.forcesUpdated()) * 100)) / 100;
this.props.setAcceleration(xAccel, yAccel);
- this.setState({ xAccel });
- this.setState({ yAccel });
+ this.setState({ xAccel, yAccel });
};
componentDidUpdate(prevProps: Readonly<IWeightProps>, prevState: Readonly<IState>, snapshot?: any): void {
if (prevProps.simulationType != this.props.simulationType) {
- this.setState({ xVelocity: this.props.startVelX });
- this.setState({ yVelocity: this.props.startVelY });
+ this.setState({ xVelocity: this.props.startVelX, yVelocity: this.props.startVelY });
this.setDisplayValues();
}
// Change pendulum angle from input field
if (prevProps.startPendulumAngle != this.props.startPendulumAngle || prevProps.startPendulumLength !== this.props.startPendulumLength) {
- let length = this.props.startPendulumLength;
+ const length = this.props.startPendulumLength;
const x = length * Math.cos(((90 - this.props.startPendulumAngle) * Math.PI) / 180);
const y = length * Math.sin(((90 - this.props.startPendulumAngle) * Math.PI) / 180);
- const xPos = this.props.xMax / 2 - x - this.props.radius;
- const yPos = y - this.props.radius - 5;
- this.setState({ xPosition: xPos });
- this.setState({ yPosition: yPos });
- this.setState({ updatedStartPosX: xPos });
- this.setState({ updatedStartPosY: yPos });
+ const xPosition = this.props.xMax / 2 - x - this.props.radius;
+ const yPosition = y - this.props.radius - 5;
+ this.setState({ xPosition, yPosition, updatedStartPosX: xPosition, updatedStartPosY: yPosition });
this.props.setPendulumAngle(this.props.startPendulumAngle, this.props.startPendulumLength);
}
// When display values updated by user, update real value
if (prevProps.updateMassPosX !== this.props.updateMassPosX) {
- let x = this.props.updateMassPosX;
- x = Math.max(0, x);
- x = Math.min(x, this.props.xMax - 2 * this.props.radius);
- this.setState({ updatedStartPosX: x });
- this.setState({ xPosition: x });
+ const x = Math.min(Math.max(0, this.props.updateMassPosX), this.props.xMax - 2 * this.props.radius);
+ this.setState({ updatedStartPosX: x, xPosition: x });
this.props.setPosition(x, undefined);
}
if (prevProps.updateMassPosY != this.props.updateMassPosY) {
- let y = this.props.updateMassPosY;
- y = Math.max(0, y);
- y = Math.min(y, this.props.yMax - 2 * this.props.radius);
- let coordinatePosition = this.getDisplayYPos(y);
- this.setState({ updatedStartPosY: coordinatePosition });
- this.setState({ yPosition: coordinatePosition });
+ const y = Math.min(Math.max(0, this.props.updateMassPosY), this.props.yMax - 2 * this.props.radius);
+ const coordinatePosition = this.getDisplayYPos(y);
+ this.setState({ yPosition: coordinatePosition, updatedStartPosY: coordinatePosition });
this.props.setPosition(undefined, this.getDisplayYPos(y));
if (this.props.displayXVelocity != this.state.xVelocity) {
- let x = this.props.displayXVelocity;
+ const x = this.props.displayXVelocity;
this.setState({ xVelocity: x });
this.props.setVelocity(x, undefined);
}
if (this.props.displayYVelocity != -this.state.yVelocity) {
- let y = this.props.displayYVelocity;
+ const y = this.props.displayYVelocity;
this.setState({ yVelocity: -y });
this.props.setVelocity(undefined, y);
}
@@ -221,34 +209,28 @@ export default class Weight extends React.Component<IWeightProps, IState> {
if (this.props.startVelY != 0) {
maxYPos -= (this.props.startVelY * this.props.startVelY) / (2 * Math.abs(this.props.gravity));
}
- if (maxYPos < 0) {
- maxYPos = 0;
- }
+ if (maxYPos < 0) maxYPos = 0;
+
this.setState({ maxPosYConservation: maxYPos });
}
}
// Check for collisions and update
- if (!this.props.paused) {
- if (prevState.timer != this.state.timer) {
- if (!this.props.noMovement) {
- let collisions = false;
- if (this.props.simulationType == 'One Weight' || this.props.simulationType == 'Inclined Plane') {
- const collisionsWithGround = this.checkForCollisionsWithGround();
- const collisionsWithWalls = this.checkForCollisionsWithWall();
- collisions = collisionsWithGround || collisionsWithWalls;
- }
- if (this.props.simulationType == 'Pulley') {
- if (this.state.yPosition <= this.props.yMin + 100 || this.state.yPosition >= this.props.yMax - 100) {
- collisions = true;
- }
- }
- if (!collisions) {
- this.update();
- }
- this.setDisplayValues();
+ if (!this.props.paused && !this.props.noMovement && prevState.timer != this.state.timer) {
+ let collisions = false;
+ if (this.props.simulationType == 'One Weight' || this.props.simulationType == 'Inclined Plane') {
+ const collisionsWithGround = this.checkForCollisionsWithGround();
+ const collisionsWithWalls = this.checkForCollisionsWithWall();
+ collisions = collisionsWithGround || collisionsWithWalls;
+ }
+ if (this.props.simulationType == 'Pulley') {
+ if (this.state.yPosition <= this.props.yMin + 100 || this.state.yPosition >= this.props.yMax - 100) {
+ collisions = true;
}
}
+ if (!collisions) this.update();
+
+ this.setDisplayValues();
}
// Reset everything on reset button click
@@ -266,23 +248,23 @@ export default class Weight extends React.Component<IWeightProps, IState> {
directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
component: false,
};
- let frictionForce: IForce = {
+ const frictionForce: IForce = {
description: 'Kinetic Friction Force',
magnitude: this.props.mass * this.props.coefficientOfKineticFriction * Math.abs(this.props.gravity) * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
directionInDegrees: 180 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
component: false,
};
// reduce magnitude of friction force if necessary such that block cannot slide up plane
- let yForce = -Math.abs(this.props.gravity);
- yForce += normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180);
- yForce += frictionForce.magnitude * Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
+ // prettier-ignore
+ const yForce = -Math.abs(this.props.gravity) +
+ normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) +
+ frictionForce.magnitude * Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
if (yForce > 0) {
frictionForce.magnitude = (-normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + Math.abs(this.props.gravity)) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
}
const frictionForceComponent: IForce = {
description: 'Kinetic Friction Force',
-
magnitude: this.props.mass * this.props.coefficientOfKineticFriction * Math.abs(this.props.gravity) * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)),
directionInDegrees: 180 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
component: true,
@@ -323,18 +305,13 @@ export default class Weight extends React.Component<IWeightProps, IState> {
// Add/remove walls when simulation type changes
if (prevProps.simulationType != this.props.simulationType) {
- let w: IWallProps[] = [];
- if (this.props.simulationType == 'One Weight' || this.props.simulationType == 'Inclined Plane') {
- w = this.props.wallPositions;
- }
- this.setState({ walls: w });
+ this.setState({ walls: ['One Weight', 'Inclined Plane'].includes(this.props.simulationType) ? this.props.wallPositions : [] });
}
// Update x position when start pos x changes
if (prevProps.startPosX != this.props.startPosX) {
if (this.props.paused && !isNaN(this.props.startPosX)) {
- this.setState({ xPosition: this.props.startPosX });
- this.setState({ updatedStartPosX: this.props.startPosX });
+ this.setState({ xPosition: this.props.startPosX, updatedStartPosX: this.props.startPosX });
this.props.setPosition(this.props.startPosX, undefined);
}
}
@@ -342,8 +319,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
// Update y position when start pos y changes TODO debug
if (prevProps.startPosY != this.props.startPosY) {
if (this.props.paused && !isNaN(this.props.startPosY)) {
- this.setState({ yPosition: this.props.startPosY });
- this.setState({ updatedStartPosY: this.props.startPosY ?? 0 });
+ this.setState({ yPosition: this.props.startPosY, updatedStartPosY: this.props.startPosY ?? 0 });
this.props.setPosition(undefined, this.getDisplayYPos(this.props.startPosY));
}
}
@@ -380,78 +356,53 @@ export default class Weight extends React.Component<IWeightProps, IState> {
// Reset simulation on reset button click
resetEverything = () => {
- this.setState({ kineticFriction: false });
- this.setState({ xPosition: this.state.updatedStartPosX });
- this.setState({ yPosition: this.state.updatedStartPosY });
- this.setState({ xVelocity: this.props.startVelX });
- this.setState({ yVelocity: this.props.startVelY });
+ this.setState({
+ kineticFriction: false,
+ xPosition: this.state.updatedStartPosX,
+ yPosition: this.state.updatedStartPosY,
+ xVelocity: this.props.startVelX,
+ yVelocity: this.props.startVelY,
+ angleLabel: Math.round(this.props.pendulumAngle * 100) / 100,
+ });
this.props.setPendulumAngle(this.props.startPendulumAngle, undefined);
this.props.setForcesUpdated(this.props.startForces());
this.props.setPosition(this.state.updatedStartPosX, this.state.updatedStartPosY);
this.props.setVelocity(this.props.startVelX, this.props.startVelY);
this.props.setAcceleration(0, 0);
- this.setState({ angleLabel: Math.round(this.props.pendulumAngle * 100) / 100 });
};
// Compute x acceleration from forces, F=ma
getNewAccelerationX = (forceList: IForce[]) => {
- let newXAcc = 0;
- if (forceList) {
- forceList.forEach(force => {
- newXAcc += (force.magnitude * Math.cos((force.directionInDegrees * Math.PI) / 180)) / this.props.mass;
- });
- }
- return newXAcc;
+ // prettier-ignore
+ return forceList.reduce((newXacc, force) =>
+ newXacc + (force.magnitude * Math.cos((force.directionInDegrees * Math.PI) / 180)) / this.props.mass, 0);
};
// Compute y acceleration from forces, F=ma
getNewAccelerationY = (forceList: IForce[]) => {
- let newYAcc = 0;
- if (forceList) {
- forceList.forEach(force => {
- newYAcc += (-1 * (force.magnitude * Math.sin((force.directionInDegrees * Math.PI) / 180))) / this.props.mass;
- });
- }
- return newYAcc;
+ // prettier-ignore
+ return forceList.reduce((newYacc, force) =>
+ newYacc + (-1 * (force.magnitude * Math.sin((force.directionInDegrees * Math.PI) / 180))) / this.props.mass, 0);
};
// Compute uniform circular motion forces given x, y positions
- getNewCircularMotionForces = (xPos: number, yPos: number) => {
- let deltaX = (this.props.xMin + this.props.xMax) / 2 - (xPos + this.props.radius);
- let deltaY = yPos + this.props.radius - (this.props.yMin + this.props.yMax) / 2;
- let dir = (Math.atan2(deltaY, deltaX) * 180) / Math.PI;
- const tensionForce: IForce = {
- description: 'Centripetal Force',
- magnitude: (this.props.startVelX ** 2 * this.props.mass) / this.props.circularMotionRadius,
- directionInDegrees: dir,
- component: false,
- };
- return [tensionForce];
+ getNewCircularMotionForces = (xPos: number, yPos: number): IForce[] => {
+ const deltaX = (this.props.xMin + this.props.xMax) / 2 - (xPos + this.props.radius);
+ const deltaY = yPos + this.props.radius - (this.props.yMin + this.props.yMax) / 2;
+ return [
+ {
+ description: 'Centripetal Force',
+ magnitude: (this.props.startVelX ** 2 * this.props.mass) / this.props.circularMotionRadius,
+ directionInDegrees: (Math.atan2(deltaY, deltaX) * 180) / Math.PI,
+ component: false,
+ },
+ ];
};
// Compute spring forces given y position
- getNewSpringForces = (yPos: number) => {
- let springForce: IForce = {
- description: 'Spring Force',
- magnitude: 0,
- directionInDegrees: 90,
- component: false,
- };
- if (yPos - this.props.springRestLength > 0) {
- springForce = {
- description: 'Spring Force',
- magnitude: this.props.springConstant * (yPos - this.props.springRestLength),
- directionInDegrees: 90,
- component: false,
- };
- } else if (yPos - this.props.springRestLength < 0) {
- springForce = {
- description: 'Spring Force',
- magnitude: this.props.springConstant * (this.props.springRestLength - yPos),
- directionInDegrees: 270,
- component: false,
- };
- }
+ getNewSpringForces = (yPos: number): IForce[] => {
+ const yPosPlus = yPos - this.props.springRestLength > 0;
+ const yPosMinus = yPos - this.props.springRestLength < 0;
return [
{
description: 'Gravity',
@@ -459,12 +410,17 @@ export default class Weight extends React.Component<IWeightProps, IState> {
directionInDegrees: 270,
component: false,
},
- springForce,
+ {
+ description: 'Spring Force',
+ magnitude: this.props.springConstant * (yPos - this.props.springRestLength) * (yPosPlus ? 1 : yPosMinus ? -1 : 0),
+ directionInDegrees: yPosPlus ? 90 : 270,
+ component: false,
+ },
];
};
// Compute pendulum forces given position, velocity
- getNewPendulumForces = (xPos: number, yPos: number, xVel: number, yVel: number) => {
+ getNewPendulumForces = (xPos: number, yPos: number, xVel: number, yVel: number): IForce[] => {
const x = this.props.xMax / 2 - xPos - this.props.radius;
const y = yPos + this.props.radius + 5;
let angle = (Math.atan(y / x) * 180) / Math.PI;
@@ -481,13 +437,6 @@ export default class Weight extends React.Component<IWeightProps, IState> {
const mag = this.props.mass * Math.abs(this.props.gravity) * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength;
- const forceOfTension: IForce = {
- description: 'Tension',
- magnitude: mag,
- directionInDegrees: angle,
- component: false,
- };
-
return [
{
description: 'Gravity',
@@ -495,42 +444,33 @@ export default class Weight extends React.Component<IWeightProps, IState> {
directionInDegrees: 270,
component: false,
},
- forceOfTension,
+ {
+ description: 'Tension',
+ magnitude: mag,
+ directionInDegrees: angle,
+ component: false,
+ },
];
};
// Check for collisions in x direction
checkForCollisionsWithWall = () => {
let collision = false;
- const minX = this.state.xPosition;
- const maxX = this.state.xPosition + 2 * this.props.radius;
- if (this.state.xVelocity != 0) {
- this.state.walls.forEach(wall => {
- if (wall.angleInDegrees == 90) {
+ if (this.state.xVelocity !== 0) {
+ this.state.walls
+ .filter(wall => wall.angleInDegrees === 90)
+ .forEach(wall => {
const wallX = (wall.xPos / 100) * this.props.panelWidth();
- if (wall.xPos < 0.35) {
- if (minX <= wallX) {
- this.setState({ xPosition: wallX + 0.01 });
- if (this.props.elasticCollisions) {
- this.setState({ xVelocity: -this.state.xVelocity });
- } else {
- this.setState({ xVelocity: 0 });
- }
- collision = true;
- }
- } else {
- if (maxX >= wallX) {
- this.setState({ xPosition: wallX - 2 * this.props.radius - 0.01 });
- if (this.props.elasticCollisions) {
- this.setState({ xVelocity: -this.state.xVelocity });
- } else {
- this.setState({ xVelocity: 0 });
- }
- collision = true;
- }
+ const minX = this.state.xPosition < wallX && wall.xPos < 0.35;
+ const maxX = this.state.xPosition + 2 * this.props.radius >= wallX && wall.xPos > 0.35;
+ if (minX || maxX) {
+ this.setState({
+ xPosition: minX ? wallX + 0.01 : wallX - 2 * this.props.radius - 0.01,
+ xVelocity: this.props.elasticCollisions ? -this.state.xVelocity : 0,
+ });
+ collision = true;
}
- }
- });
+ });
}
return collision;
};
@@ -544,41 +484,27 @@ export default class Weight extends React.Component<IWeightProps, IState> {
this.state.walls.forEach(wall => {
if (wall.angleInDegrees == 0 && wall.yPos > 0.4) {
const groundY = (wall.yPos / 100) * this.props.panelHeight();
+ const gravity: IForce = {
+ description: 'Gravity',
+ magnitude: Math.abs(this.props.gravity) * this.props.mass,
+ directionInDegrees: 270,
+ component: false,
+ };
if (maxY > groundY) {
this.setState({ yPosition: groundY - 2 * this.props.radius - 0.01 });
if (this.props.elasticCollisions) {
this.setState({ yVelocity: -this.state.yVelocity });
} else {
this.setState({ yVelocity: 0 });
- if (this.props.simulationType != 'Two Weights') {
- const forceOfGravity: IForce = {
- description: 'Gravity',
- magnitude: Math.abs(this.props.gravity) * this.props.mass,
- directionInDegrees: 270,
- component: false,
- };
- const normalForce: IForce = {
- description: 'Normal force',
- magnitude: Math.abs(this.props.gravity) * this.props.mass,
- directionInDegrees: wall.angleInDegrees + 90,
- component: false,
- };
- this.props.setForcesUpdated([forceOfGravity, normalForce]);
- if (this.props.simulationType == 'Inclined Plane') {
- const forceOfGravityC: IForce = {
- description: 'Gravity',
- magnitude: Math.abs(this.props.gravity) * this.props.mass,
- directionInDegrees: 270,
- component: true,
- };
- const normalForceC: IForce = {
- description: 'Normal force',
- magnitude: Math.abs(this.props.gravity) * this.props.mass,
- directionInDegrees: wall.angleInDegrees + 90,
- component: true,
- };
- this.props.setComponentForces([forceOfGravityC, normalForceC]);
- }
+ const normalForce: IForce = {
+ description: 'Normal force',
+ magnitude: gravity.magnitude,
+ directionInDegrees: -gravity.directionInDegrees,
+ component: false,
+ };
+ this.props.setForcesUpdated([gravity, normalForce]);
+ if (this.props.simulationType === 'Inclined Plane') {
+ this.props.setComponentForces([gravity, { ...normalForce, component: true }]);
}
}
collision = true;
@@ -591,12 +517,10 @@ export default class Weight extends React.Component<IWeightProps, IState> {
if (wall.angleInDegrees == 0 && wall.yPos < 0.4) {
const groundY = (wall.yPos / 100) * this.props.panelHeight();
if (minY < groundY) {
- this.setState({ yPosition: groundY + 0.01 });
- if (this.props.elasticCollisions) {
- this.setState({ yVelocity: -this.state.yVelocity });
- } else {
- this.setState({ yVelocity: 0 });
- }
+ this.setState({
+ yPosition: groundY + 0.01,
+ yVelocity: this.props.elasticCollisions ? -this.state.yVelocity : 0,
+ });
collision = true;
}
}
@@ -606,51 +530,43 @@ export default class Weight extends React.Component<IWeightProps, IState> {
};
// Called at each RK4 step
- evaluate = (currentXPos: number, currentYPos: number, currentXVel: number, currentYVel: number, deltaXPos: number, deltaYPos: number, deltaXVel: number, deltaYVel: number, dt: number) => {
- const newXPos = currentXPos + deltaXPos * dt;
- const newYPos = currentYPos + deltaYPos * dt;
- const newXVel = currentXVel + deltaXVel * dt;
- const newYVel = currentYVel + deltaYVel * dt;
- const newDeltaXPos = newXVel;
- const newDeltaYPos = newYVel;
- let forces = this.props.forcesUpdated();
- if (this.props.simulationType == 'Pendulum') {
- forces = this.getNewPendulumForces(newXPos, newYPos, newXVel, newYVel);
- } else if (this.props.simulationType == 'Spring') {
- forces = this.getNewSpringForces(newYPos);
- } else if (this.props.simulationType == 'Circular Motion') {
- forces = this.getNewCircularMotionForces(newXPos, newYPos);
- }
- const newDeltaXVel = this.getNewAccelerationX(forces);
- const newDeltaYVel = this.getNewAccelerationY(forces);
+ evaluate = (currentXPos: number, currentYPos: number, currentXVel: number, currentYVel: number, currdeltaXPos: number, currdeltaYPos: number, currdeltaXVel: number, currdeltaYVel: number, dt: number) => {
+ const xPos = currentXPos + currdeltaXPos * dt;
+ const yPos = currentYPos + currdeltaYPos * dt;
+ const xVel = currentXVel + currdeltaXVel * dt;
+ const yVel = currentYVel + currdeltaYVel * dt;
+ const forces = this.getForces(xPos, yPos, xVel, yVel);
return {
- xPos: newXPos,
- yPos: newYPos,
- xVel: newXVel,
- yVel: newYVel,
- deltaXPos: newDeltaXPos,
- deltaYPos: newDeltaYPos,
- deltaXVel: newDeltaXVel,
- deltaYVel: newDeltaYVel,
+ xPos,
+ yPos,
+ xVel,
+ yVel,
+ deltaXPos: xVel,
+ deltaYPos: yVel,
+ deltaXVel: this.getNewAccelerationX(forces),
+ deltaYVel: this.getNewAccelerationY(forces),
};
};
+ getForces = (xPos: number, yPos: number, xVel: number, yVel: number) => {
+ // prettier-ignore
+ switch (this.props.simulationType) {
+ case 'Pendulum': return this.getNewPendulumForces(xPos, yPos, xVel, yVel);
+ case 'Spring' : return this.getNewSpringForces(yPos);
+ case 'Circular Motion': return this.getNewCircularMotionForces(xPos, yPos);
+ default: return this.props.forcesUpdated();
+ }
+ };
+
// Update position, velocity using RK4 method
update = () => {
- let startXVel = this.state.xVelocity;
- let startYVel = this.state.yVelocity;
+ const startXVel = this.state.xVelocity;
+ const startYVel = this.state.yVelocity;
let xPos = this.state.xPosition;
let yPos = this.state.yPosition;
let xVel = this.state.xVelocity;
let yVel = this.state.yVelocity;
- let forces = this.props.forcesUpdated();
- if (this.props.simulationType == 'Pendulum') {
- forces = this.getNewPendulumForces(xPos, yPos, xVel, yVel);
- } else if (this.props.simulationType == 'Spring') {
- forces = this.getNewSpringForces(yPos);
- } else if (this.props.simulationType == 'Circular Motion') {
- forces = this.getNewCircularMotionForces(xPos, yPos);
- }
+ const forces = this.getForces(xPos, yPos, xVel, yVel);
const xAcc = this.getNewAccelerationX(forces);
const yAcc = this.getNewAccelerationY(forces);
for (let i = 0; i < this.props.simulationSpeed; i++) {
@@ -665,56 +581,48 @@ export default class Weight extends React.Component<IWeightProps, IState> {
yPos += ((this.props.timestepSize * 1.0) / 6.0) * (k1.deltaYPos + 2 * (k2.deltaYPos + k3.deltaYPos) + k4.deltaYPos);
}
// make sure harmonic motion maintained and errors don't propagate
- if (this.props.simulationType == 'Spring') {
- if (startYVel < 0 && yVel > 0 && yPos < this.props.springRestLength) {
- let equilibriumPos = this.props.springRestLength + (this.props.mass * Math.abs(this.props.gravity)) / this.props.springConstant;
- let amplitude = Math.abs(equilibriumPos - this.props.springStartLength);
- yPos = equilibriumPos - amplitude;
- } else if (startYVel > 0 && yVel < 0 && yPos > this.props.springRestLength) {
- let equilibriumPos = this.props.springRestLength + (this.props.mass * Math.abs(this.props.gravity)) / this.props.springConstant;
- let amplitude = Math.abs(equilibriumPos - this.props.springStartLength);
- yPos = equilibriumPos + amplitude;
- }
- }
- if (this.props.simulationType == 'Pendulum') {
- let startX = this.state.updatedStartPosX;
- if (startXVel <= 0 && xVel > 0) {
- xPos = this.state.updatedStartPosX;
- if (this.state.updatedStartPosX > this.props.xMax / 2) {
- xPos = this.props.xMax / 2 + (this.props.xMax / 2 - startX) - 2 * this.props.radius;
+ switch (this.props.simulationType) {
+ case 'Spring':
+ if (startYVel < 0 && yVel > 0 && yPos < this.props.springRestLength) {
+ const equilibriumPos = this.props.springRestLength + (this.props.mass * Math.abs(this.props.gravity)) / this.props.springConstant;
+ const amplitude = Math.abs(equilibriumPos - this.props.springStartLength);
+ yPos = equilibriumPos - amplitude;
+ } else if (startYVel > 0 && yVel < 0 && yPos > this.props.springRestLength) {
+ const equilibriumPos = this.props.springRestLength + (this.props.mass * Math.abs(this.props.gravity)) / this.props.springConstant;
+ const amplitude = Math.abs(equilibriumPos - this.props.springStartLength);
+ yPos = equilibriumPos + amplitude;
}
- yPos = this.props.startPosY;
- } else if (startXVel >= 0 && xVel < 0) {
- xPos = this.state.updatedStartPosX;
- if (this.state.updatedStartPosX < this.props.xMax / 2) {
- xPos = this.props.xMax / 2 + (this.props.xMax / 2 - startX) - 2 * this.props.radius;
+ break;
+ case 'Pendulum':
+ const startX = this.state.updatedStartPosX;
+ if (startXVel <= 0 && xVel > 0) {
+ xPos = this.state.updatedStartPosX;
+ if (this.state.updatedStartPosX > this.props.xMax / 2) {
+ xPos = this.props.xMax / 2 + (this.props.xMax / 2 - startX) - 2 * this.props.radius;
+ }
+ yPos = this.props.startPosY;
+ } else if (startXVel >= 0 && xVel < 0) {
+ xPos = this.state.updatedStartPosX;
+ if (this.state.updatedStartPosX < this.props.xMax / 2) {
+ xPos = this.props.xMax / 2 + (this.props.xMax / 2 - startX) - 2 * this.props.radius;
+ }
+ yPos = this.props.startPosY;
+ }
+ break;
+ case 'One Weight':
+ if (yPos < this.state.maxPosYConservation) {
+ yPos = this.state.maxPosYConservation;
}
- yPos = this.props.startPosY;
- }
- }
- if (this.props.simulationType == 'One Weight') {
- if (yPos < this.state.maxPosYConservation) {
- yPos = this.state.maxPosYConservation;
- }
- }
- this.setState({ xVelocity: xVel });
- this.setState({ yVelocity: yVel });
- this.setState({ xPosition: xPos });
- this.setState({ yPosition: yPos });
- let forcesn = this.props.forcesUpdated();
- if (this.props.simulationType == 'Pendulum') {
- forcesn = this.getNewPendulumForces(xPos, yPos, xVel, yVel);
- } else if (this.props.simulationType == 'Spring') {
- forcesn = this.getNewSpringForces(yPos);
- } else if (this.props.simulationType == 'Circular Motion') {
- forcesn = this.getNewCircularMotionForces(xPos, yPos);
}
+ this.setState({ xVelocity: xVel, yVelocity: yVel, xPosition: xPos, yPosition: yPos });
+
+ const forcesn = this.getForces(xPos, yPos, xVel, yVel);
this.props.setForcesUpdated(forcesn);
// set component forces if they change
if (this.props.simulationType == 'Pendulum') {
- let x = this.props.xMax / 2 - xPos - this.props.radius;
- let y = yPos + this.props.radius + 5;
+ const x = this.props.xMax / 2 - xPos - this.props.radius;
+ const y = yPos + this.props.radius + 5;
let angle = (Math.atan(y / x) * 180) / Math.PI;
if (angle < 0) {
angle += 180;
@@ -756,7 +664,6 @@ export default class Weight extends React.Component<IWeightProps, IState> {
// Render weight, spring, rod(s), vectors
render() {
- trace();
return (
<div>
<div
@@ -764,9 +671,11 @@ export default class Weight extends React.Component<IWeightProps, IState> {
onPointerDown={e => {
if (this.draggable) {
this.props.pause();
- this.setState({ dragging: true });
- this.setState({ clickPositionX: e.clientX });
- this.setState({ clickPositionY: e.clientY });
+ this.setState({
+ dragging: true,
+ clickPositionX: e.clientX,
+ clickPositionY: e.clientY,
+ });
}
}}
onPointerMove={e => {
@@ -804,8 +713,10 @@ export default class Weight extends React.Component<IWeightProps, IState> {
}
this.setState({ updatedStartPosY: newY });
}
- this.setState({ clickPositionX: e.clientX });
- this.setState({ clickPositionY: e.clientY });
+ this.setState({
+ clickPositionX: e.clientX,
+ clickPositionY: e.clientY,
+ });
this.setDisplayValues();
}
}}
@@ -832,27 +743,25 @@ export default class Weight extends React.Component<IWeightProps, IState> {
this.props.setSpringLength(newY);
}
if (this.props.simulationType == 'Suspension') {
- let x1rod = (this.props.xMax + this.props.xMin) / 2 - this.props.radius - this.props.yMin - 200;
- let x2rod = (this.props.xMax + this.props.xMin) / 2 + this.props.yMin + 200 + this.props.radius;
- let deltaX1 = this.state.xPosition + this.props.radius - x1rod;
- let deltaX2 = x2rod - (this.state.xPosition + this.props.radius);
- let deltaY = this.state.yPosition + this.props.radius;
- let dir1T = Math.PI - Math.atan(deltaY / deltaX1);
- let dir2T = Math.atan(deltaY / deltaX2);
- let tensionMag2 = (this.props.mass * Math.abs(this.props.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T));
- let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T);
- dir1T = (dir1T * 180) / Math.PI;
- dir2T = (dir2T * 180) / Math.PI;
+ const x1rod = (this.props.xMax + this.props.xMin) / 2 - this.props.radius - this.props.yMin - 200;
+ const x2rod = (this.props.xMax + this.props.xMin) / 2 + this.props.yMin + 200 + this.props.radius;
+ const deltaX1 = this.state.xPosition + this.props.radius - x1rod;
+ const deltaX2 = x2rod - (this.state.xPosition + this.props.radius);
+ const deltaY = this.state.yPosition + this.props.radius;
+ const dir1T = Math.PI - Math.atan(deltaY / deltaX1);
+ const dir2T = Math.atan(deltaY / deltaX2);
+ const tensionMag2 = (this.props.mass * Math.abs(this.props.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T));
+ const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T);
const tensionForce1: IForce = {
description: 'Tension',
magnitude: tensionMag1,
- directionInDegrees: dir1T,
+ directionInDegrees: (dir1T * 180) / Math.PI,
component: false,
};
const tensionForce2: IForce = {
description: 'Tension',
magnitude: tensionMag2,
- directionInDegrees: dir2T,
+ directionInDegrees: (dir2T * 180) / Math.PI,
component: false,
};
const grav: IForce = {
@@ -881,19 +790,10 @@ export default class Weight extends React.Component<IWeightProps, IState> {
<svg width={this.props.xMax + 'px'} height={this.props.panelHeight() + 'px'}>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(val => {
const count = 10;
- let xPos1;
- let yPos1;
- let xPos2;
- let yPos2;
- if (val % 2 == 0) {
- xPos1 = this.state.xPosition + this.props.radius - 20;
- xPos2 = this.state.xPosition + this.props.radius + 20;
- } else {
- xPos1 = this.state.xPosition + this.props.radius + 20;
- xPos2 = this.state.xPosition + this.props.radius - 20;
- }
- yPos1 = (val * this.state.yPosition) / count;
- yPos2 = ((val + 1) * this.state.yPosition) / count;
+ const xPos1 = this.state.xPosition + this.props.radius + (val % 2 === 0 ? -20 : 20);
+ const yPos1 = (val * this.state.yPosition) / count;
+ const xPos2 = this.state.xPosition + this.props.radius + (val % 2 === 0 ? 20 : -20);
+ const yPos2 = ((val + 1) * this.state.yPosition) / count;
return <line key={val} x1={xPos1} y1={yPos1} x2={xPos2} y2={yPos2} stroke={'#808080'} strokeWidth="10" />;
})}
</svg>
@@ -1154,12 +1054,12 @@ export default class Weight extends React.Component<IWeightProps, IState> {
if (force.magnitude < this.epsilon) {
return;
}
- let arrowStartY: number = this.state.yPosition + this.props.radius;
+ const arrowStartY: number = this.state.yPosition + this.props.radius;
const arrowStartX: number = this.state.xPosition + this.props.radius;
- let arrowEndY: number = arrowStartY - Math.abs(force.magnitude) * 20 * Math.sin((force.directionInDegrees * Math.PI) / 180);
+ const arrowEndY: number = arrowStartY - Math.abs(force.magnitude) * 20 * Math.sin((force.directionInDegrees * Math.PI) / 180);
const arrowEndX: number = arrowStartX + Math.abs(force.magnitude) * 20 * Math.cos((force.directionInDegrees * Math.PI) / 180);
- let color = '#0d0d0d';
+ const color = '#0d0d0d';
let labelTop = arrowEndY;
let labelLeft = arrowEndX;
@@ -1219,12 +1119,12 @@ export default class Weight extends React.Component<IWeightProps, IState> {
if (force.magnitude < this.epsilon) {
return;
}
- let arrowStartY: number = this.state.yPosition + this.props.radius;
+ const arrowStartY: number = this.state.yPosition + this.props.radius;
const arrowStartX: number = this.state.xPosition + this.props.radius;
- let arrowEndY: number = arrowStartY - Math.abs(force.magnitude) * 20 * Math.sin((force.directionInDegrees * Math.PI) / 180);
+ const arrowEndY: number = arrowStartY - Math.abs(force.magnitude) * 20 * Math.sin((force.directionInDegrees * Math.PI) / 180);
const arrowEndX: number = arrowStartX + Math.abs(force.magnitude) * 20 * Math.cos((force.directionInDegrees * Math.PI) / 180);
- let color = '#0d0d0d';
+ const color = '#0d0d0d';
let labelTop = arrowEndY;
let labelLeft = arrowEndX;