aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx10
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx1697
2 files changed, 1240 insertions, 467 deletions
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
index 446c4933b..bdc76048c 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
@@ -133,7 +133,6 @@ export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<Fi
this.dataDoc.startVelX = this.dataDoc.startVelX ?? 0;
this.dataDoc.startVelY = this.dataDoc.startVelY ?? 0;
this.dataDoc.stepNumber = this.dataDoc.stepNumber ?? 0;
- this.dataDoc.timer = this.dataDoc.timer ?? 0;
this.dataDoc.updatedForces = this.dataDoc.updatedForces ?? [];
this.dataDoc.velocityXDisplay = this.dataDoc.velocityXDisplay ?? 0;
this.dataDoc.velocityYDisplay = this.dataDoc.velocityYDisplay ?? 0;
@@ -1232,13 +1231,6 @@ export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<Fi
]);
}
- // Timer for animating the simulation, update every 0.05 seconds
- setInterval(() => {
- setTimer(timer + 1);
- }, 50);
-
-
-
render () {
<div className="physicsSimApp">
<div className="mechanicsSimulationContainer">
@@ -1301,7 +1293,6 @@ export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<Fi
displayXVelocity={this.dataDoc.velocityXDisplay}
displayYVelocity={this.dataDoc.velocityYDisplay}
elasticCollisions={this.dataDoc.elasticCollisions}
- incrementTime={this.dataDoc.timer}
mass={this.dataDoc.mass}
mode={this.dataDoc.mode}
noMovement={this.dataDoc.noMovement}
@@ -1350,7 +1341,6 @@ export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<Fi
displayXVelocity={this.dataDoc.velocityXDisplay2}
displayYVelocity={this.dataDoc.velocityYDisplay2}
elasticCollisions={this.dataDoc.elasticCollisions}
- incrementTime={this.dataDoc.timer}
mass={this.dataDoc.mass2}
mode={this.dataDoc.mode}
noMovement={this.dataDoc.noMovement}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
index 39b3249e8..3bc02cfdd 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -1,27 +1,54 @@
-import React = require('react');
import { Doc } from '../../../fields/Doc';
-import { IWallProps } from "./PhysicsSimulationWall";
+import React = require('react');
+import { useEffect, useState } from "react";
+import { IWallProps } from "./Wall";
+import "./Weight.scss";
export interface IForce {
description: string;
magnitude: number;
directionInDegrees: number;
+ component: boolean;
}
export interface IWeightProps {
- adjustPendulumAngle: boolean;
+ dataDoc: doc;
+ adjustPendulumAngle: { angle: number; length: number };
+ circularMotionRadius: number;
+ coefficientOfKineticFriction: number;
color: string;
- dataDoc: Doc;
+ componentForces: IForce[];
+ displayXVelocity: number;
+ displayYVelocity: number;
+ elasticCollisions: boolean;
+ gravity: number;
+ incrementTime: number;
mass: number;
+ mode: string;
+ noMovement: boolean;
+ paused: boolean;
+ pendulumAngle: number;
+ pendulumLength: number;
radius: number;
- simulationReset: boolean;
+ reset: boolean;
+ showAcceleration: boolean;
+ showComponentForces: boolean;
+ showForceMagnitudes: boolean;
+ showForces: boolean;
+ showVelocity: boolean;
+ simulationSpeed: number;
+ simulationType: string;
+ springConstant: number;
+ springRestLength: number;
+ springStartLength: number;
+ startForces: IForce[];
+ startPendulumAngle: number;
startPosX: number;
startPosY: number;
- startVelX?: number;
- startVelY?: number;
+ startVelX: number;
+ startVelY: number;
timestepSize: number;
- updateDisplay: boolean,
- walls: IWallProps[];
- wedge: boolean;
+ updateDisplay: { xDisplay: number; yDisplay: number };
+ updatedForces: IForce[];
wedgeHeight: number;
wedgeWidth: number;
xMax: number;
@@ -34,47 +61,64 @@ interface IState {
angleLabel: number,
clickPositionX: number,
clickPositionY: number,
+ coordinates: string,
dragging: boolean,
kineticFriction: boolean,
- timer: number;
+ maxPosYConservation: number,
+ timer: number,
update: boolean,
updatedStartPosX: number,
updatedStartPosY: number,
+ walls: IWallProps[],
xPosition: number,
xVelocity: number,
yPosition: number,
yVelocity: number,
+ xAccel: number,
+ yAccel: number,
}
+
export default class Weight extends React.Component<IWeightProps, IState> {
constructor(props: any) {
super(props)
this.state = {
+ angleLabel: 0,
clickPositionX: 0,
clickPositionY: 0,
+ coordinates: "",
dragging: false,
kineticFriction: false,
+ maxPosYConservation: 0,
timer: 0,
- angleLabel: 0,
updatedStartPosX: this.props.dataDoc['startPosX'],
updatedStartPosY: this.props.dataDoc['startPosY'],
+ walls: [],
xPosition: this.props.dataDoc['startPosX'],
xVelocity: this.props.startVelX ? this.props.startVelX: 0,
yPosition: this.props.dataDoc['startPosY'],
yVelocity: this.props.startVelY ? this.props.startVelY: 0,
+ xAccel: 0,
+ yAccel: 0,
}
}
+ componentDidMount() {
+ // Timer for animating the simulation
+ setInterval(() => {
+ this.setState({timer: this.state.timer + 1});
+ }, 50);
+ }
+
// Constants
- draggable = !this.props.dataDoc['wedge'] ;
- epsilon = 0.0001;
- forceOfGravity: IForce = {
- description: "Gravity",
- magnitude: this.props.mass * 9.81,
- directionInDegrees: 270,
- };
+ const draggable =
+ this.props.dataDoc['simulationType'] != "Inclined Plane" &&
+ this.props.dataDoc['simulationType'] != "Pendulum" &&
+ this.props.dataDoc['mode'] == "Freeform";
+ const epsilon = 0.0001;
+ const labelBackgroundColor = `rgba(255,255,255,0.5)`;
- // Var
+ // Variables
weightStyle = {
alignItems: "center",
backgroundColor: this.props.color,
@@ -115,6 +159,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
this.props.dataDoc['velocityXDisplay'] = Math.round(xVel * 100) / 100;
};
+ // Update display values when simulation updates
setDisplayValues = (
xPos: number = this.state.xPosition,
yPos: number = this.state.yPosition,
@@ -133,208 +178,121 @@ export default class Weight extends React.Component<IWeightProps, IState> {
;
};
- componentDidMount() {
- // Timer for animating the simulation
- setInterval(() => {
- this.setState({timer: this.state.timer + 1});
- }, 60);
+ componentDidUpdate(prevProps: Readonly<IWeightProps>, prevState: Readonly<IState>, snapshot?: any): void {
}
- componentDidUpdate(prevProps: Readonly<IWeightProps>, prevState: Readonly<IState>, snapshot?: any): void {
-
- // When display values updated by user, update real values
- if (this.props.updateDisplay != prevProps.updateDisplay) {
- if (this.props.dataDoc['positionXDisplay'] != this.state.xPosition) {
- let x = this.props.dataDoc['positionXDisplay'];
- x = Math.max(0, x);
- x = Math.min(x, this.props.xMax - 2 * this.props.radius);
- this.setState({updatedStartPosX: x})
- this.setState({xPosition: x})
- this.props.dataDoc['positionXDisplay'] = x;
- }
-
- if (this.props.dataDoc['positionYDisplay'] != this.getDisplayYPos(this.state.yPosition)) {
- let y = this.props.dataDoc['positionYDisplay'];
- y = Math.max(0, y);
- y = Math.min(y, this.props.yMax - 2 * this.props.radius);
- this.props.dataDoc['positionYDisplay'] = y;
- let coordinatePosition = this.getYPosFromDisplay(y);
- this.setState({updatedStartPosY: coordinatePosition})
- this.setState({yPosition: coordinatePosition})
- }
-
- if (this.props.dataDoc['velocityXDisplay'] != this.state.xVelocity) {
- let x = this.props.dataDoc['velocityXDisplay'];
- this.setState({xVelocity: x})
- this.props.dataDoc['velocityXDisplay'] = x;
- }
-
- if (this.props.dataDoc['velocityYDisplay'] != this.state.yVelocity) {
- let y = this.props.dataDoc['velocityYDisplay'];
- this.setState({yVelocity: -y})
- this.props.dataDoc['velocityYDisplay']
- }
- }
- // Update sim
- if (this.state.timer != prevState.timer) {
- if (!this.props.dataDoc['simulationPaused']) {
- let collisions = false;
- if (!this.props.dataDoc['pendulum']) {
- const collisionsWithGround = this.checkForCollisionsWithGround();
- const collisionsWithWalls = this.checkForCollisionsWithWall();
- collisions = collisionsWithGround || collisionsWithWalls;
- }
- if (!collisions) {
- this.update();
- }
- this.setDisplayValues();
- }
- }
- if (this.props.simulationReset != prevProps.simulationReset) {
- this.resetEverything();
- }
- if (this.props.adjustPendulumAngle != prevProps.adjustPendulumAngle) {
- console.log('update angle')
- // Change pendulum angle based on input field
- let length = this.props.dataDoc['pendulumLength'] ?? 0;
- const x =
- length * Math.cos(((90 - this.props.dataDoc['pendulumAngle']) * Math.PI) / 180);
- const y =
- length * Math.sin(((90 - this.props.dataDoc['pendulumAngle']) * 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})
- this.setState({angleLabel: Math.round(this.props.dataDoc['pendulumAngle'] * 100) / 100})
- }
- // Update x start position
- if (this.props.startPosX != prevProps.startPosX) {
- this.setState({updatedStartPosX: this.props.dataDoc['startPosX']})
- this.setState({xPosition: this.props.dataDoc['startPosX']})
- this.setXPosDisplay(this.props.dataDoc['startPosX']);
- }
- // Update y start position
- if (this.props.startPosY != prevProps.startPosY) {
- this.setState({updatedStartPosY: this.props.dataDoc['startPosY']})
- this.setState({yPosition: this.props.dataDoc['startPosY']})
- this.setYPosDisplay(this.props.dataDoc['startPosY']);
- }
- if (!this.props.dataDoc['simulationPaused']) {
- if (this.state.xVelocity != prevState.xVelocity) {
- if (this.props.dataDoc['wedge'] && this.state.xVelocity != 0 && !this.state.kineticFriction) {
- this.setState({kineticFriction: true});
- //switch from static to kinetic friction
- const normalForce: IForce = {
- description: "Normal Force",
- magnitude:
- this.forceOfGravity.magnitude *
- Math.cos(Math.atan(this.props.dataDoc['wedgeHeight'] / this.props.dataDoc['wedgeWidth'] )),
- directionInDegrees:
- 180 - 90 - (Math.atan(this.props.dataDoc['wedgeHeight'] / this.props.dataDoc['wedgeWidth'] ) * 180) / Math.PI,
- };
- let frictionForce: IForce = {
- description: "Kinetic Friction Force",
- magnitude:
- this.props.dataDoc['coefficientOfKineticFriction'] *
- this.forceOfGravity.magnitude *
- Math.cos(Math.atan(this.props.dataDoc['wedgeHeight'] / this.props.dataDoc['wedgeWidth'] )),
- directionInDegrees:
- 180 - (Math.atan(this.props.dataDoc['wedgeHeight'] / this.props.dataDoc['wedgeWidth'] ) * 180) / Math.PI,
- };
- // reduce magnitude of friction force if necessary such that block cannot slide up plane
- let yForce = -this.forceOfGravity.magnitude;
- yForce +=
- normalForce.magnitude *
- Math.sin((normalForce.directionInDegrees * Math.PI) / 180);
- yForce +=
- frictionForce.magnitude *
- Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
- if (yForce > 0) {
- frictionForce.magnitude =
- (-normalForce.magnitude *
- Math.sin((normalForce.directionInDegrees * Math.PI) / 180) +
- this.forceOfGravity.magnitude) /
- Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
- }
- if (this.props.dataDoc['coefficientOfKineticFriction'] != 0) {
- this.props.dataDoc['updatedForces'] = [this.forceOfGravity, normalForce, frictionForce];
- } else {
- this.props.dataDoc['updatedForces'] = ([this.forceOfGravity, normalForce]);
- }
- }
- }
- }
- this.weightStyle = {
- alignItems: "center",
- backgroundColor: this.props.color,
- borderColor: this.state.dragging ? "lightblue" : "black",
- borderRadius: 50 + "%",
- borderStyle: "solid",
- display: "flex",
- height: 2 * this.props.radius + "px",
- justifyContent: "center",
- left: this.state.xPosition + "px",
- position: "absolute" as "absolute",
- top: this.state.yPosition + "px",
- touchAction: "none",
- width: 2 * this.props.radius + "px",
- zIndex: 5,
- };
- }
+
+
+
+ // 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 ?? 0})
this.setState({yVelocity: this.props.startVelY ?? 0})
+ this.props.dataDoc['pendulumAngle'] = this.props.dataDoc['startPendulumAngle']
this.props.dataDoc['updatedForces'] = (this.props.dataDoc['startForces'])
+ this.props.dataDoc['accelerationXDisplay'] = 0
+ this.props.dataDoc['accelerationYDisplay'] = 0
+ this.props.dataDoc['positionXDisplay'] = this.state.updatedStartPosX
+ this.props.dataDoc['positionYDisplay'] = this.state.updatedStartPosY
+ this.props.dataDoc['velocityXDisplay'] = this.props.startVelX ?? 0
+ this.props.dataDoc['velocityYDisplay'] = this.props.startVelY ?? 0
this.setState({angleLabel: Math.round(this.props.dataDoc['pendulumAngle']* 100) / 100})
- this.setDisplayValues();
};
- getNewAccelerationX = (forceList: IForce[]) => {
+
+ // Compute x acceleration from forces, F=ma
+ const getNewAccelerationX = (forceList: IForce[]) => {
let newXAcc = 0;
- if (forceList) {
- forceList.forEach((force) => {
+ forceList.forEach((force) => {
+ if (force.component == false) {
newXAcc +=
(force.magnitude *
Math.cos((force.directionInDegrees * Math.PI) / 180)) /
- this.props.mass;
- });
- }
+ mass;
+ }
+ });
return newXAcc;
};
- getNewAccelerationY = (forceList: IForce[]) => {
+ // Compute y acceleration from forces, F=ma
+ const getNewAccelerationY = (forceList: IForce[]) => {
let newYAcc = 0;
- if (forceList) {
- forceList.forEach((force) => {
+ forceList.forEach((force) => {
+ if (force.component == false) {
newYAcc +=
(-1 *
(force.magnitude *
Math.sin((force.directionInDegrees * Math.PI) / 180))) /
- this.props.mass;
- });
- }
+ mass;
+ }
+ });
return newYAcc;
};
- getNewForces = (
+ // Compute uniform circular motion forces given x, y positions
+ const getNewCircularMotionForces = (xPos: number, yPos: number) => {
+ let deltaX = (xMin + xMax) / 2 - (xPos + radius);
+ let deltaY = yPos + radius - (yMin + yMax) / 2;
+ let dir = (Math.atan2(deltaY, deltaX) * 180) / Math.PI;
+ const tensionForce: IForce = {
+ description: "Centripetal Force",
+ magnitude: (startVelX ** 2 * mass) / circularMotionRadius,
+ directionInDegrees: dir,
+ component: false,
+ };
+ return [tensionForce];
+ };
+
+ // Compute spring forces given y position
+ const getNewSpringForces = (yPos: number) => {
+ let springForce: IForce = {
+ description: "Spring Force",
+ magnitude: 0,
+ directionInDegrees: 90,
+ component: false,
+ };
+ if (yPos - springRestLength > 0) {
+ springForce = {
+ description: "Spring Force",
+ magnitude: springConstant * (yPos - springRestLength),
+ directionInDegrees: 90,
+ component: false,
+ };
+ } else if (yPos - springRestLength < 0) {
+ springForce = {
+ description: "Spring Force",
+ magnitude: springConstant * (springRestLength - yPos),
+ directionInDegrees: 270,
+ component: false,
+ };
+ }
+
+ return [
+ {
+ description: "Gravity",
+ magnitude: Math.abs(gravity) * mass,
+ directionInDegrees: 270,
+ component: false,
+ },
+ springForce,
+ ];
+ };
+
+ // Compute pendulum forces given position, velocity
+ const getNewPendulumForces = (
xPos: number,
yPos: number,
xVel: number,
yVel: number
) => {
- if (!this.props.dataDoc['pendulum']) {
- return this.props.dataDoc['updatedForces'];
- }
- const x = this.props.xMax / 2 - xPos - this.props.radius;
- const y = yPos + this.props.radius + 5;
+ const x = xMax / 2 - xPos - radius;
+ const y = yPos + radius + 5;
let angle = (Math.atan(y / x) * 180) / Math.PI;
if (angle < 0) {
angle += 180;
@@ -345,265 +303,816 @@ export default class Weight extends React.Component<IWeightProps, IState> {
}
const pendulumLength = Math.sqrt(x * x + y * y);
- this.props.dataDoc['pendulumAngle'] = oppositeAngle;
- this.props.dataDoc['pendulumLength'] = Math.sqrt(x * x + y * y);
+ setPendulumAngle(oppositeAngle);
const mag =
- this.props.mass * 9.81 * Math.cos((oppositeAngle * Math.PI) / 180) +
- (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength;
+ mass * Math.abs(gravity) * Math.cos((oppositeAngle * Math.PI) / 180) +
+ (mass * (xVel * xVel + yVel * yVel)) / pendulumLength;
const forceOfTension: IForce = {
description: "Tension",
magnitude: mag,
directionInDegrees: angle,
+ component: false,
};
- this.setState({angleLabel: Math.round(this.props.dataDoc['pendulumAngle']* 100) / 100})
-
- return [this.forceOfGravity, forceOfTension];
- };
- getNewPosition = (pos: number, vel: number) => {
- return pos + vel * this.props.timestepSize;
+ return [
+ {
+ description: "Gravity",
+ magnitude: Math.abs(gravity) * mass,
+ directionInDegrees: 270,
+ component: false,
+ },
+ forceOfTension,
+ ];
};
- getNewVelocity = (vel: number, acc: number) => {
- return vel + acc * this.props.timestepSize;
- };
-
- checkForCollisionsWithWall = () => {
+ // Check for collisions in x direction
+ const checkForCollisionsWithWall = () => {
let collision = false;
- const minX = this.state.xPosition;
- const maxX = this.state.xPosition + 2 * this.props.radius;
- const containerWidth = 300;
- if (this.state.xVelocity != 0) {
- if (this.props.dataDoc.wallPositions) {
- this.props.dataDoc['wallPositions'].forEach((wall) => {
+ const minX = xPosition;
+ const maxX = xPosition + 2 * radius;
+ if (xVelocity != 0) {
+ walls.forEach((wall) => {
if (wall.angleInDegrees == 90) {
- const wallX = (wall.xPos / 100) * 300;
+ const wallX = (wall.xPos / 100) * window.innerWidth;
if (wall.xPos < 0.35) {
if (minX <= wallX) {
- if (this.props.dataDoc['elasticCollisions']) {
- this.setState({xVelocity: -this.state.xVelocity});
+ setXPosition(wallX + 0.01);
+ if (elasticCollisions) {
+ setXVelocity(-xVelocity);
} else {
- this.setState({xVelocity: 0});
- this.setState({xPosition: wallX+5});
+ setXVelocity(0);
}
collision = true;
}
} else {
if (maxX >= wallX) {
- if (this.props.dataDoc['elasticCollisions']) {
- this.setState({xVelocity: -this.state.xVelocity});
+ setXPosition(wallX - 2 * radius - 0.01);
+ if (elasticCollisions) {
+ setXVelocity(-xVelocity);
} else {
- this.setState({xVelocity: 0});
- this.setState({xPosition: wallX - 2 * this.props.radius + 5});
+ setXVelocity(0);
}
collision = true;
}
}
}
});
- }
}
return collision;
};
- checkForCollisionsWithGround = () => {
+ // Check for collisions in y direction
+ const checkForCollisionsWithGround = () => {
let collision = false;
- const maxY = this.state.yPosition + 2 * this.props.radius;
- if (this.state.yVelocity > 0) {
- if (this.props.dataDoc.wallPositions) {
- this.props.dataDoc['wallPositions'].forEach((wall) => {
- if (wall.angleInDegrees == 0) {
- const groundY = (wall.yPos / 100) * this.props.yMax;
- if (maxY >= groundY) {
- if (this.props.dataDoc['elasticCollisions']) {
- this.setState({yVelocity: -this.state.yVelocity})
+ const minY = yPosition;
+ const maxY = yPosition + 2 * radius;
+ if (yVelocity > 0) {
+ walls.forEach((wall) => {
+ if (wall.angleInDegrees == 0 && wall.yPos > 0.4) {
+ const groundY = (wall.yPos / 100) * window.innerHeight;
+ if (maxY > groundY) {
+ setYPosition(groundY - 2 * radius - 0.01);
+ if (elasticCollisions) {
+ setYVelocity(-yVelocity);
} else {
- this.setState({yVelocity: 0})
- this.setState({yPosition: groundY - 2 * this.props.radius + 5})
- const forceOfGravity: IForce = {
- description: "Gravity",
- magnitude: 9.81 * this.props.mass,
- directionInDegrees: 270,
- };
- const normalForce: IForce = {
- description: "Normal force",
- magnitude: 9.81 * this.props.mass,
- directionInDegrees: wall.angleInDegrees + 90,
- };
- this.props.dataDoc['updatedForces'] = ([forceOfGravity, normalForce]);
+ setYVelocity(0);
+ if (this.props.dataDoc['simulationType'] != "Two Weights") {
+ const forceOfGravity: IForce = {
+ description: "Gravity",
+ magnitude: Math.abs(gravity) * mass,
+ directionInDegrees: 270,
+ component: false,
+ };
+ const normalForce: IForce = {
+ description: "Normal force",
+ magnitude: Math.abs(gravity) * mass,
+ directionInDegrees: wall.angleInDegrees + 90,
+ component: false,
+ };
+ setUpdatedForces([forceOfGravity, normalForce]);
+ if (this.props.dataDoc['simulationType'] == "Inclined Plane") {
+ const forceOfGravityC: IForce = {
+ description: "Gravity",
+ magnitude: Math.abs(gravity) * mass,
+ directionInDegrees: 270,
+ component: true,
+ };
+ const normalForceC: IForce = {
+ description: "Normal force",
+ magnitude: Math.abs(gravity) * mass,
+ directionInDegrees: wall.angleInDegrees + 90,
+ component: true,
+ };
+ setComponentForces([forceOfGravityC, normalForceC]);
+ }
+ }
+ }
+ collision = true;
+ }
+ }
+ });
+ }
+ if (yVelocity < 0) {
+ walls.forEach((wall) => {
+ if (wall.angleInDegrees == 0 && wall.yPos < 0.4) {
+ const groundY = (wall.yPos / 100) * window.innerHeight;
+ if (minY < groundY) {
+ setYPosition(groundY + 5);
+ if (elasticCollisions) {
+ setYVelocity(-yVelocity);
+ } else {
+ setYVelocity(0);
}
collision = true;
}
}
});
- }
}
return collision;
};
- update = () => {
- // RK4 update
- let xPos = this.state.xPosition;
- let yPos = this.state.yPosition;
- let xVel = this.state.xVelocity;
- let yVel = this.state.yVelocity;
- for (let i = 0; i < 60; i++) {
- let forces1 = this.getNewForces(xPos, yPos, xVel, yVel);
- const xAcc1 = this.getNewAccelerationX(forces1);
- const yAcc1 = this.getNewAccelerationY(forces1);
- const xVel1 = this.getNewVelocity(xVel, xAcc1);
- const yVel1 = this.getNewVelocity(yVel, yAcc1);
-
- let xVel2 = this.getNewVelocity(xVel, xAcc1 / 2);
- let yVel2 = this.getNewVelocity(yVel, yAcc1 / 2);
- let xPos2 = this.getNewPosition(xPos, xVel1 / 2);
- let yPos2 = this.getNewPosition(yPos, yVel1 / 2);
- const forces2 = this.getNewForces(xPos2, yPos2, xVel2, yVel2);
- const xAcc2 = this.getNewAccelerationX(forces2);
- const yAcc2 = this.getNewAccelerationY(forces2);
- xVel2 = this.getNewVelocity(xVel2, xAcc2);
- yVel2 = this.getNewVelocity(yVel2, yAcc2);
- xPos2 = this.getNewPosition(xPos2, xVel2);
- yPos2 = this.getNewPosition(yPos2, yVel2);
-
- let xVel3 = this.getNewVelocity(xVel, xAcc2 / 2);
- let yVel3 = this.getNewVelocity(yVel, yAcc2 / 2);
- let xPos3 = this.getNewPosition(xPos, xVel2 / 2);
- let yPos3 = this.getNewPosition(yPos, yVel2 / 2);
- const forces3 = this.getNewForces(xPos3, yPos3, xVel3, yVel3);
- const xAcc3 = this.getNewAccelerationX(forces3);
- const yAcc3 = this.getNewAccelerationY(forces3);
- xVel3 = this.getNewVelocity(xVel3, xAcc3);
- yVel3 = this.getNewVelocity(yVel3, yAcc3);
- xPos3 = this.getNewPosition(xPos3, xVel3);
- yPos3 = this.getNewPosition(yPos3, yVel3);
-
- let xVel4 = this.getNewVelocity(xVel, xAcc3);
- let yVel4 = this.getNewVelocity(yVel, yAcc3);
- let xPos4 = this.getNewPosition(xPos, xVel3);
- let yPos4 = this.getNewPosition(yPos, yVel3);
- const forces4 = this.getNewForces(xPos4, yPos4, xVel4, yVel4);
- const xAcc4 = this.getNewAccelerationX(forces4);
- const yAcc4 = this.getNewAccelerationY(forces4);
- xVel4 = this.getNewVelocity(xVel4, xAcc4);
- yVel4 = this.getNewVelocity(yVel4, yAcc4);
- xPos4 = this.getNewPosition(xPos4, xVel4);
- yPos4 = this.getNewPosition(yPos4, yVel4);
+ // Called at each RK4 step
+ const 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 = updatedForces;
+ if (this.props.dataDoc['simulationType'] == "Pendulum") {
+ forces = getNewPendulumForces(newXPos, newYPos, newXVel, newYVel);
+ } else if (this.props.dataDoc['simulationType'] == "Spring") {
+ forces = getNewSpringForces(newYPos);
+ } else if (this.props.dataDoc['simulationType'] == "Circular Motion") {
+ forces = getNewCircularMotionForces(newXPos, newYPos);
+ }
+ const newDeltaXVel = getNewAccelerationX(forces);
+ const newDeltaYVel = getNewAccelerationY(forces);
+ return {
+ xPos: newXPos,
+ yPos: newYPos,
+ xVel: newXVel,
+ yVel: newYVel,
+ deltaXPos: newDeltaXPos,
+ deltaYPos: newDeltaYPos,
+ deltaXVel: newDeltaXVel,
+ deltaYVel: newDeltaYVel,
+ };
+ };
+
+ // Update position, velocity using RK4 method
+ const update = () => {
+ let startXVel = xVelocity;
+ let startYVel = yVelocity;
+ let xPos = xPosition;
+ let yPos = yPosition;
+ let xVel = xVelocity;
+ let yVel = yVelocity;
+ let forces = updatedForces;
+ if (this.props.dataDoc['simulationType'] == "Pendulum") {
+ forces = getNewPendulumForces(xPos, yPos, xVel, yVel);
+ } else if (this.props.dataDoc['simulationType'] == "Spring") {
+ forces = getNewSpringForces(yPos);
+ } else if (this.props.dataDoc['simulationType'] == "Circular Motion") {
+ forces = getNewCircularMotionForces(xPos, yPos);
+ }
+ const xAcc = getNewAccelerationX(forces);
+ const yAcc = getNewAccelerationY(forces);
+ for (let i = 0; i < simulationSpeed; i++) {
+ const k1 = evaluate(xPos, yPos, xVel, yVel, xVel, yVel, xAcc, yAcc, 0);
+ const k2 = evaluate(
+ xPos,
+ yPos,
+ xVel,
+ yVel,
+ k1.deltaXPos,
+ k1.deltaYPos,
+ k1.deltaXVel,
+ k1.deltaYVel,
+ timestepSize * 0.5
+ );
+ const k3 = evaluate(
+ xPos,
+ yPos,
+ xVel,
+ yVel,
+ k2.deltaXPos,
+ k2.deltaYPos,
+ k2.deltaXVel,
+ k2.deltaYVel,
+ timestepSize * 0.5
+ );
+ const k4 = evaluate(
+ xPos,
+ yPos,
+ xVel,
+ yVel,
+ k3.deltaXPos,
+ k3.deltaYPos,
+ k3.deltaXVel,
+ k3.deltaYVel,
+ timestepSize
+ );
xVel +=
- this.props.timestepSize * (xAcc1 / 6.0 + xAcc2 / 3.0 + xAcc3 / 3.0 + xAcc4 / 6.0);
+ ((timestepSize * 1.0) / 6.0) *
+ (k1.deltaXVel + 2 * (k2.deltaXVel + k3.deltaXVel) + k4.deltaXVel);
yVel +=
- this.props.timestepSize * (yAcc1 / 6.0 + yAcc2 / 3.0 + yAcc3 / 3.0 + yAcc4 / 6.0);
+ ((timestepSize * 1.0) / 6.0) *
+ (k1.deltaYVel + 2 * (k2.deltaYVel + k3.deltaYVel) + k4.deltaYVel);
xPos +=
- this.props.timestepSize * (xVel1 / 6.0 + xVel2 / 3.0 + xVel3 / 3.0 + xVel4 / 6.0);
+ ((timestepSize * 1.0) / 6.0) *
+ (k1.deltaXPos + 2 * (k2.deltaXPos + k3.deltaXPos) + k4.deltaXPos);
yPos +=
- this.props.timestepSize * (yVel1 / 6.0 + yVel2 / 3.0 + yVel3 / 3.0 + yVel4 / 6.0);
+ ((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.dataDoc['simulationType'] == "Spring") {
+ if (startYVel < 0 && yVel > 0 && yPos < springRestLength) {
+ let equilibriumPos =
+ springRestLength + (mass * Math.abs(gravity)) / springConstant;
+ let amplitude = Math.abs(equilibriumPos - springStartLength);
+ yPos = equilibriumPos - amplitude;
+ } else if (startYVel > 0 && yVel < 0 && yPos > springRestLength) {
+ let equilibriumPos =
+ springRestLength + (mass * Math.abs(gravity)) / springConstant;
+ let amplitude = Math.abs(equilibriumPos - springStartLength);
+ yPos = equilibriumPos + amplitude;
+ }
}
+ if (this.props.dataDoc['simulationType'] == "Pendulum") {
+ let startX = updatedStartPosX;
+ if (startXVel <= 0 && xVel > 0) {
+ xPos = updatedStartPosX;
+ if (updatedStartPosX > xMax / 2) {
+ xPos = xMax / 2 + (xMax / 2 - startX) - 2 * radius;
+ }
+ yPos = startPosY;
+ } else if (startXVel >= 0 && xVel < 0) {
+ xPos = updatedStartPosX;
+ if (updatedStartPosX < xMax / 2) {
+ xPos = xMax / 2 + (xMax / 2 - startX) - 2 * radius;
+ }
+ yPos = startPosY;
+ }
+ }
+ if (this.props.dataDoc['simulationType'] == "One Weight") {
+ if (yPos < maxPosYConservation) {
+ yPos = maxPosYConservation;
+ }
+ }
+ setXVelocity(xVel);
+ setYVelocity(yVel);
+ setXPosition(xPos);
+ setYPosition(yPos);
+ let forcesn = updatedForces;
+ if (this.props.dataDoc['simulationType'] == "Pendulum") {
+ forcesn = getNewPendulumForces(xPos, yPos, xVel, yVel);
+ } else if (this.props.dataDoc['simulationType'] == "Spring") {
+ forcesn = getNewSpringForces(yPos);
+ } else if (this.props.dataDoc['simulationType'] == "Circular Motion") {
+ forcesn = getNewCircularMotionForces(xPos, yPos);
+ }
+ setUpdatedForces(forcesn);
- this.setState({xVelocity: xVel});
- this.setState({yVelocity: yVel});
- this.setState({xPosition: xPos});
- this.setState({yPosition: yPos});
-
- this.props.dataDoc['updatedForces'] = (this.getNewForces(xPos, yPos, xVel, yVel));
+ // set component forces if they change
+ if (this.props.dataDoc['simulationType'] == "Pendulum") {
+ let x = xMax / 2 - xPos - radius;
+ let y = yPos + radius + 5;
+ let angle = (Math.atan(y / x) * 180) / Math.PI;
+ if (angle < 0) {
+ angle += 180;
+ }
+ let oppositeAngle = 90 - angle;
+ if (oppositeAngle < 0) {
+ oppositeAngle = 90 - (180 - angle);
+ }
+
+ const pendulumLength = Math.sqrt(x * x + y * y);
+
+ const mag =
+ mass * Math.abs(gravity) * Math.cos((oppositeAngle * Math.PI) / 180) +
+ (mass * (xVel * xVel + yVel * yVel)) / pendulumLength;
+
+ const tensionComponent: IForce = {
+ description: "Tension",
+ magnitude: mag,
+ directionInDegrees: angle,
+ component: true,
+ };
+ const gravityParallel: IForce = {
+ description: "Gravity Parallel Component",
+ magnitude: Math.abs(gravity) * Math.cos(((90 - angle) * Math.PI) / 180),
+ directionInDegrees: 270 - (90 - angle),
+ component: true,
+ };
+ const gravityPerpendicular: IForce = {
+ description: "Gravity Perpendicular Component",
+ magnitude: Math.abs(gravity) * Math.sin(((90 - angle) * Math.PI) / 180),
+ directionInDegrees: -(90 - angle),
+ component: true,
+ };
+ if (Math.abs(gravity) * Math.sin(((90 - angle) * Math.PI) / 180) < 0) {
+ gravityPerpendicular.magnitude = Math.abs(
+ Math.abs(gravity) * Math.sin(((90 - angle) * Math.PI) / 180)
+ );
+ gravityPerpendicular.directionInDegrees = 180 - (90 - angle);
+ }
+ setComponentForces([
+ tensionComponent,
+ gravityParallel,
+ gravityPerpendicular,
+ ]);
+ }
};
+ // Change pendulum angle based on input field
+ useEffect(() => {
+ let length = adjustPendulumAngle.length;
+ const x =
+ length * Math.cos(((90 - adjustPendulumAngle.angle) * Math.PI) / 180);
+ const y =
+ length * Math.sin(((90 - adjustPendulumAngle.angle) * Math.PI) / 180);
+ const xPos = xMax / 2 - x - radius;
+ const yPos = y - radius - 5;
+ setXPosition(xPos);
+ setYPosition(yPos);
+ setUpdatedStartPosX(xPos);
+ setUpdatedStartPosY(yPos);
+ setPendulumAngle(adjustPendulumAngle.angle);
+ setPendulumLength(adjustPendulumAngle.length);
+ }, [adjustPendulumAngle]);
+
+ // When display values updated by user, update real values
+ useEffect(() => {
+ if (updateDisplay.xDisplay != xPosition) {
+ let x = updateDisplay.xDisplay;
+ x = Math.max(0, x);
+ x = Math.min(x, xMax - 2 * radius);
+ setUpdatedStartPosX(x);
+ setXPosition(x);
+ setDisplayXPosition(x);
+ }
+
+ if (updateDisplay.yDisplay != getDisplayYPos(yPosition)) {
+ let y = updateDisplay.yDisplay;
+ y = Math.max(0, y);
+ y = Math.min(y, yMax - 2 * radius);
+ setDisplayYPosition(y);
+ let coordinatePosition = getYPosFromDisplay(y);
+ setUpdatedStartPosY(coordinatePosition);
+ setYPosition(coordinatePosition);
+ }
+
+ if (displayXVelocity != xVelocity) {
+ let x = displayXVelocity;
+ setXVelocity(x);
+ setDisplayXVelocity(x);
+ }
+
+ if (displayYVelocity != -yVelocity) {
+ let y = displayYVelocity;
+ setYVelocity(-y);
+ setDisplayYVelocity(y);
+ }
+ }, [updateDisplay]);
+
+ // Prevent bug when switching between sims
+ useEffect(() => {
+ setXVelocity(startVelX);
+ setYVelocity(startVelY);
+ setDisplayValues();
+ }, [startForces]);
+
+ // Make sure weight doesn't go above max height
+ useEffect(() => {
+ if (this.props.dataDoc['simulationType'] == "One Weight") {
+ let maxYPos = updatedStartPosY;
+ if (startVelY != 0) {
+ maxYPos -= (startVelY * startVelY) / (2 * Math.abs(gravity));
+ }
+ if (maxYPos < 0) {
+ maxYPos = 0;
+ }
+ setMaxPosYConservation(maxYPos);
+ }
+ }, [updatedStartPosY, startVelY]);
+
+ // Check for collisions and update
+ useEffect(() => {
+ if (!paused && !noMovement) {
+ let collisions = false;
+ if (
+ this.props.dataDoc['simulationType'] == "One Weight" ||
+ this.props.dataDoc['simulationType'] == "Inclined Plane"
+ ) {
+ const collisionsWithGround = checkForCollisionsWithGround();
+ const collisionsWithWalls = checkForCollisionsWithWall();
+ collisions = collisionsWithGround || collisionsWithWalls;
+ }
+ if (this.props.dataDoc['simulationType'] == "Pulley") {
+ if (yPosition <= yMin + 100 || yPosition >= yMax - 100) {
+ collisions = true;
+ }
+ }
+ if (!collisions) {
+ update();
+ }
+ setDisplayValues();
+ }
+ }, [incrementTime]);
+
+ // Reset everything on reset button click
+ useEffect(() => {
+ resetEverything();
+ }, [reset]);
+
+ // Convert from static to kinetic friction if/when weight slips on inclined plane
+ useEffect(() => {
+ if (
+ this.props.dataDoc['simulationType'] == "Inclined Plane" &&
+ Math.abs(xVelocity) > 0.1 &&
+ this.props.dataDoc['mode'] != "Review" &&
+ !kineticFriction
+ ) {
+ setKineticFriction(true);
+ //switch from static to kinetic friction
+ const normalForce: IForce = {
+ description: "Normal Force",
+ magnitude:
+ mass *
+ Math.abs(gravity) *
+ Math.cos(Math.atan(wedgeHeight / wedgeWidth)),
+ directionInDegrees:
+ 180 - 90 - (Math.atan(wedgeHeight / wedgeWidth) * 180) / Math.PI,
+ component: false,
+ };
+ let frictionForce: IForce = {
+ description: "Kinetic Friction Force",
+ magnitude:
+ mass *
+ coefficientOfKineticFriction *
+ Math.abs(gravity) *
+ Math.cos(Math.atan(wedgeHeight / wedgeWidth)),
+ directionInDegrees:
+ 180 - (Math.atan(wedgeHeight / wedgeWidth) * 180) / Math.PI,
+ component: false,
+ };
+ // reduce magnitude of friction force if necessary such that block cannot slide up plane
+ let yForce = -Math.abs(gravity);
+ yForce +=
+ normalForce.magnitude *
+ Math.sin((normalForce.directionInDegrees * Math.PI) / 180);
+ yForce +=
+ 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(gravity)) /
+ Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
+ }
+
+ const frictionForceComponent: IForce = {
+ description: "Kinetic Friction Force",
+
+ magnitude:
+ mass *
+ coefficientOfKineticFriction *
+ Math.abs(gravity) *
+ Math.cos(Math.atan(wedgeHeight / wedgeWidth)),
+ directionInDegrees:
+ 180 - (Math.atan(wedgeHeight / wedgeWidth) * 180) / Math.PI,
+ component: true,
+ };
+ const normalForceComponent: IForce = {
+ description: "Normal Force",
+ magnitude:
+ mass *
+ Math.abs(gravity) *
+ Math.cos(Math.atan(wedgeHeight / wedgeWidth)),
+ directionInDegrees:
+ 180 - 90 - (Math.atan(wedgeHeight / wedgeWidth) * 180) / Math.PI,
+ component: true,
+ };
+ const gravityParallel: IForce = {
+ description: "Gravity Parallel Component",
+ magnitude:
+ mass *
+ Math.abs(gravity) *
+ Math.sin(Math.PI / 2 - Math.atan(wedgeHeight / wedgeWidth)),
+ directionInDegrees:
+ 180 -
+ 90 -
+ (Math.atan(wedgeHeight / wedgeWidth) * 180) / Math.PI +
+ 180,
+ component: true,
+ };
+ const gravityPerpendicular: IForce = {
+ description: "Gravity Perpendicular Component",
+ magnitude:
+ mass *
+ Math.abs(gravity) *
+ Math.cos(Math.PI / 2 - Math.atan(wedgeHeight / wedgeWidth)),
+ directionInDegrees:
+ 360 - (Math.atan(wedgeHeight / wedgeWidth) * 180) / Math.PI,
+ component: true,
+ };
+ const gravityForce: IForce = {
+ description: "Gravity",
+ magnitude: mass * Math.abs(gravity),
+ directionInDegrees: 270,
+ component: false,
+ };
+ if (coefficientOfKineticFriction != 0) {
+ setUpdatedForces([gravityForce, normalForce, frictionForce]);
+ setComponentForces([
+ frictionForceComponent,
+ normalForceComponent,
+ gravityParallel,
+ gravityPerpendicular,
+ ]);
+ } else {
+ setUpdatedForces([gravityForce, normalForce]);
+ setComponentForces([
+ normalForceComponent,
+ gravityParallel,
+ gravityPerpendicular,
+ ]);
+ }
+ }
+ }, [xVelocity]);
+
+ // Add/remove walls when simulation type changes
+ useEffect(() => {
+ let w: IWallProps[] = [];
+ if (this.props.dataDoc['simulationType'] == "One Weight" || this.props.dataDoc['simulationType'] == "Inclined Plane") {
+ w.push({ length: 70, xPos: 0, yPos: 0, angleInDegrees: 0 });
+ w.push({ length: 70, xPos: 0, yPos: 80, angleInDegrees: 0 });
+ w.push({ length: 80, xPos: 0, yPos: 0, angleInDegrees: 90 });
+ w.push({ length: 80, xPos: 69.5, yPos: 0, angleInDegrees: 90 });
+ }
+ setWalls(w);
+ }, [this.props.dataDoc['simulationType']]);
+
+ // Update x position when start pos x changes
+ useEffect(() => {
+ if (paused) {
+ setUpdatedStartPosX(startPosX);
+ setXPosition(startPosX);
+ setXPosDisplay(startPosX);
+ }
+ }, [startPosX]);
+
+ // Update y position when start pos y changes
+ useEffect(() => {
+ if (paused) {
+ setUpdatedStartPosY(startPosY);
+ setYPosition(startPosY);
+ setYPosDisplay(startPosY);
+ }
+ }, [startPosY]);
- labelBackgroundColor = `rgba(255,255,255,0.5)`;
+ // Update wedge coordinates
+ useEffect(() => {
+ const left = xMax * 0.5 - 200;
+ const coordinatePair1 = Math.round(left) + "," + yMax + " ";
+ const coordinatePair2 = Math.round(left + wedgeWidth) + "," + yMax + " ";
+ const coordinatePair3 = Math.round(left) + "," + (yMax - wedgeHeight);
+ const coord = coordinatePair1 + coordinatePair2 + coordinatePair3;
+ setCoordinates(coord);
+ }, [wedgeWidth, wedgeHeight]);
- render () {
- return (
- <div>
+ // Render weight, spring, rod(s), vectors
+ return (
+ <div style={{ zIndex: -1000 }}>
<div
className="weightContainer"
- // onPointerDown={(e) => {
- // if (this.draggable) {
- // e.preventDefault();
- // this.props.dataDoc['simulationPaused'] = true;
- // this.setState({dragging: true});
- // this.setState({clickPositionX: e.clientX})
- // this.setState({clickPositionY: e.clientY})
- // }
- // }}
- // onPointerMove={(e) => {
- // e.preventDefault();
- // if (this.state.dragging) {
- // let newY = this.state.yPosition + e.clientY - this.state.clickPositionY;
- // if (newY > this.props.yMax - 2 * this.props.radius) {
- // newY = this.props.yMax - 2 * this.props.radius;
- // }
-
- // let newX = this.state.xPosition + e.clientX - this.state.clickPositionX;
- // if (newX > this.props.xMax - 2 * this.props.radius) {
- // newX = this.props.xMax - 2 * this.props.radius;
- // } else if (newX < 0) {
- // newX = 0;
- // }
- // this.setState({xPosition: newX})
- // this.setState({yPosition: newY})
- // this.setState({updatedStartPosX: newX})
- // this.setState({updatedStartPosY: newY})
- // this.props.dataDoc['positionYDisplay'] = Math.round((this.props.yMax - 2 * this.props.radius - newY + 5) * 100) / 100;
- // this.setState({clickPositionX: e.clientX})
- // this.setState({clickPositionY: e.clientY})
- // this.setDisplayValues();
- // }
- // }}
- // onPointerUp={(e) => {
- // if (this.state.dragging) {
- // e.preventDefault();
- // if (!this.props.dataDoc['pendulum']) {
- // this.resetEverything();
- // }
- // this.setState({dragging: false});
- // let newY = this.state.yPosition + e.clientY - this.state.clickPositionY;
- // if (newY > this.props.yMax - 2 * this.props.radius) {
- // newY = this.props.yMax - 2 * this.props.radius;
- // }
-
- // let newX = this.state.xPosition + e.clientX - this.state.clickPositionX;
- // if (newX > this.props.xMax - 2 * this.props.radius) {
- // newX = this.props.xMax - 2 * this.props.radius;
- // } else if (newX < 0) {
- // newX = 0;
- // }
- // if (this.props.dataDoc['pendulum']) {
- // const x = this.props.xMax / 2 - newX - this.props.radius;
- // const y = newY + this.props.radius + 5;
- // let angle = (Math.atan(y / x) * 180) / Math.PI;
- // if (angle < 0) {
- // angle += 180;
- // }
- // let oppositeAngle = 90 - angle;
- // if (oppositeAngle < 0) {
- // oppositeAngle = 90 - (180 - angle);
- // }
-
- // const pendulumLength = Math.sqrt(x * x + y * y);
- // this.props.dataDoc['pendulumAngle'] = oppositeAngle;
- // this.props.dataDoc['pendulumLength'] = Math.sqrt(x * x + y * y);
- // const mag = 9.81 * Math.cos((oppositeAngle * Math.PI) / 180);
- // const forceOfTension: IForce = {
- // description: "Tension",
- // magnitude: mag,
- // directionInDegrees: angle,
- // };
- // this.setState({kineticFriction: false})
- // this.setState({xVelocity: this.props.startVelX ?? 0})
- // this.setState({yVelocity: this.props.startVelY ?? 0})
- // this.setDisplayValues();
- // this.props.dataDoc['updatedForces'] = ([this.forceOfGravity, forceOfTension]);
- // }
- // }
- // }}
+ onPointerDown={(e) => {
+ if (draggable) {
+ e.preventDefault();
+ setPaused(true);
+ setDragging(true);
+ setClickPositionX(e.clientX);
+ setClickPositionY(e.clientY);
+ } else if (this.props.dataDoc['mode'] == "Review") {
+ setSketching(true);
+ }
+ }}
+ onPointerMove={(e) => {
+ e.preventDefault();
+ if (dragging) {
+ let newY = yPosition + e.clientY - clickPositionY;
+ if (newY > yMax - 2 * radius - 10) {
+ newY = yMax - 2 * radius - 10;
+ } else if (newY < 10) {
+ newY = 10;
+ }
+
+ let newX = xPosition + e.clientX - clickPositionX;
+ if (newX > xMax - 2 * radius - 10) {
+ newX = xMax - 2 * radius - 10;
+ } else if (newX < 10) {
+ newX = 10;
+ }
+ if (this.props.dataDoc['simulationType'] == "Suspension") {
+ if (newX < (xMax + xMin) / 4 - radius - 15) {
+ newX = (xMax + xMin) / 4 - radius - 15;
+ } else if (newX > (3 * (xMax + xMin)) / 4 - radius / 2 - 15) {
+ newX = (3 * (xMax + xMin)) / 4 - radius / 2 - 15;
+ }
+ }
+
+ setYPosition(newY);
+ setDisplayYPosition(
+ Math.round((yMax - 2 * radius - newY + 5) * 100) / 100
+ );
+ if (this.props.dataDoc['simulationType'] != "Pulley") {
+ setXPosition(newX);
+ setDisplayXPosition(newX);
+ }
+ if (this.props.dataDoc['simulationType'] != "Suspension") {
+ if (this.props.dataDoc['simulationType'] != "Pulley") {
+ setUpdatedStartPosX(newX);
+ }
+ setUpdatedStartPosY(newY);
+ }
+ setClickPositionX(e.clientX);
+ setClickPositionY(e.clientY);
+ setDisplayValues();
+ }
+ }}
+ onPointerUp={(e) => {
+ if (dragging) {
+ e.preventDefault();
+ if (
+ this.props.dataDoc['simulationType'] != "Pendulum" &&
+ this.props.dataDoc['simulationType'] != "Suspension"
+ ) {
+ resetEverything();
+ }
+ setDragging(false);
+ let newY = yPosition + e.clientY - clickPositionY;
+ if (newY > yMax - 2 * radius - 10) {
+ newY = yMax - 2 * radius - 10;
+ } else if (newY < 10) {
+ newY = 10;
+ }
+
+ let newX = xPosition + e.clientX - clickPositionX;
+ if (newX > xMax - 2 * radius - 10) {
+ newX = xMax - 2 * radius - 10;
+ } else if (newX < 10) {
+ newX = 10;
+ }
+ if (this.props.dataDoc['simulationType'] == "Spring") {
+ setSpringStartLength(newY);
+ }
+ if (this.props.dataDoc['simulationType'] == "Suspension") {
+ let x1rod = (xMax + xMin) / 2 - radius - yMin - 200;
+ let x2rod = (xMax + xMin) / 2 + yMin + 200 + radius;
+ let deltaX1 = xPosition + radius - x1rod;
+ let deltaX2 = x2rod - (xPosition + radius);
+ let deltaY = yPosition + radius;
+ let dir1T = Math.PI - Math.atan(deltaY / deltaX1);
+ let dir2T = Math.atan(deltaY / deltaX2);
+ let tensionMag2 =
+ (mass * Math.abs(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 tensionForce1: IForce = {
+ description: "Tension",
+ magnitude: tensionMag1,
+ directionInDegrees: dir1T,
+ component: false,
+ };
+ const tensionForce2: IForce = {
+ description: "Tension",
+ magnitude: tensionMag2,
+ directionInDegrees: dir2T,
+ component: false,
+ };
+ const grav: IForce = {
+ description: "Gravity",
+ magnitude: mass * Math.abs(gravity),
+ directionInDegrees: 270,
+ component: false,
+ };
+ setUpdatedForces([tensionForce1, tensionForce2, grav]);
+ }
+ }
+ }}
>
- <div className="weight" style={this.weightStyle}>
- <p className="weightLabel">{this.props.mass} kg</p>
+ <div className="weight" style={weightStyle}>
+ <p className="weightLabel">{mass} kg</p>
</div>
</div>
- {this.props.dataDoc['pendulum'] && (
+ {this.props.dataDoc['simulationType'] == "Spring" && (
+ <div
+ className="spring"
+ style={{
+ pointerEvents: "none",
+ position: "absolute",
+ left: 0,
+ top: 0,
+ zIndex: -2,
+ }}
+ >
+ <svg width={xMax + "px"} height={window.innerHeight + "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 = xPosition + radius - 20;
+ xPos2 = xPosition + radius + 20;
+ } else {
+ xPos1 = xPosition + radius + 20;
+ xPos2 = xPosition + radius - 20;
+ }
+ yPos1 = (val * yPosition) / count;
+ yPos2 = ((val + 1) * yPosition) / count;
+ return (
+ <line
+ key={val}
+ x1={xPos1}
+ y1={yPos1}
+ x2={xPos2}
+ y2={yPos2}
+ stroke={"#808080"}
+ strokeWidth="10"
+ />
+ );
+ })}
+ </svg>
+ </div>
+ )}
+ {this.props.dataDoc['simulationType'] == "Pulley" && (
+ <div
+ className="wheel"
+ style={{
+ pointerEvents: "none",
+ position: "absolute",
+ left: 0,
+ top: 0,
+ zIndex: -1,
+ }}
+ >
+ <svg width={xMax + "px"} height={window.innerHeight + "px"}>
+ <circle
+ cx={(xMax + xMin) / 2}
+ cy={radius}
+ r={radius * 1.5}
+ fill={"#808080"}
+ />
+ </svg>
+ </div>
+ )}
+ {this.props.dataDoc['simulationType'] == "Pulley" && (
+ <div
+ className="rod"
+ style={{
+ pointerEvents: "none",
+ position: "absolute",
+ left: 0,
+ top: 0,
+ zIndex: -2,
+ }}
+ >
+ <svg width={xMax + "px"} height={window.innerHeight + "px"}>
+ <line
+ x1={xPosition + radius}
+ y1={yPosition + radius}
+ x2={xPosition + radius}
+ y2={yMin}
+ stroke={"#deb887"}
+ strokeWidth="10"
+ />
+ </svg>
+ </div>
+ )}
+ {this.props.dataDoc['simulationType'] == "Suspension" && (
<div
className="rod"
style={{
@@ -611,45 +1120,200 @@ export default class Weight extends React.Component<IWeightProps, IState> {
position: "absolute",
left: 0,
top: 0,
+ zIndex: -2,
}}
>
- <svg width={this.props.xMax + "px"} height={300 + "px"}>
+ <svg width={xMax + "px"} height={window.innerHeight + "px"}>
<line
- x1={this.state.xPosition + this.props.radius}
- y1={this.state.yPosition + this.props.radius}
- x2={this.props.xMax / 2}
+ x1={xPosition + radius}
+ y1={yPosition + radius}
+ x2={(xMax + xMin) / 2 - radius - yMin - 200}
+ y2={yMin}
+ stroke={"#deb887"}
+ strokeWidth="10"
+ />
+ </svg>
+ <p
+ style={{
+ position: "absolute",
+ zIndex: 10,
+ left: (xMax + xMin) / 2 - radius - yMin - 200 + 80 + "px",
+ top: 10 + "px",
+ backgroundColor: labelBackgroundColor,
+ }}
+ >
+ {Math.round(
+ ((Math.atan(
+ (yPosition + radius) /
+ (xPosition +
+ radius -
+ ((xMax + xMin) / 2 - radius - yMin - 200))
+ ) *
+ 180) /
+ Math.PI) *
+ 100
+ ) / 100}
+ °
+ </p>
+ </div>
+ )}
+ {this.props.dataDoc['simulationType'] == "Suspension" && (
+ <div
+ className="rod"
+ style={{
+ pointerEvents: "none",
+ position: "absolute",
+ left: 0,
+ top: 0,
+ zIndex: -2,
+ }}
+ >
+ <svg width={xMax + "px"} height={window.innerHeight + "px"}>
+ <line
+ x1={xPosition + radius}
+ y1={yPosition + radius}
+ x2={(xMax + xMin) / 2 + yMin + 200 + radius}
+ y2={yMin}
+ stroke={"#deb887"}
+ strokeWidth="10"
+ />
+ </svg>
+
+ <p
+ style={{
+ position: "absolute",
+ zIndex: 10,
+ left: (xMax + xMin) / 2 + yMin + 200 + radius - 80 + "px",
+ top: 10 + "px",
+ backgroundColor: labelBackgroundColor,
+ }}
+ >
+ {Math.round(
+ ((Math.atan(
+ (yPosition + radius) /
+ ((xMax + xMin) / 2 +
+ yMin +
+ 200 +
+ radius -
+ (xPosition + radius))
+ ) *
+ 180) /
+ Math.PI) *
+ 100
+ ) / 100}
+ °
+ </p>
+ </div>
+ )}
+ {this.props.dataDoc['simulationType'] == "Circular Motion" && (
+ <div
+ className="rod"
+ style={{
+ pointerEvents: "none",
+ position: "absolute",
+ left: 0,
+ top: 0,
+ zIndex: -2,
+ }}
+ >
+ <svg width={xMax + "px"} height={window.innerHeight + "px"}>
+ <line
+ x1={xPosition + radius}
+ y1={yPosition + radius}
+ x2={(xMin + xMax) / 2}
+ y2={(yMin + yMax) / 2}
+ stroke={"#deb887"}
+ strokeWidth="10"
+ />
+ </svg>
+ </div>
+ )}
+ {this.props.dataDoc['simulationType'] == "Pendulum" && (
+ <div
+ className="rod"
+ style={{
+ pointerEvents: "none",
+ position: "absolute",
+ left: 0,
+ top: 0,
+ zIndex: -2,
+ }}
+ >
+ <svg width={xMax + "px"} height={window.innerHeight + "px"}>
+ <line
+ x1={xPosition + radius}
+ y1={yPosition + radius}
+ x2={xMax / 2}
y2={-5}
stroke={"#deb887"}
strokeWidth="10"
/>
</svg>
- {!this.state.dragging && (
+ {!dragging && (
<div>
<p
style={{
position: "absolute",
- left: this.props.xMax / 2 + "px",
+ zIndex: 5,
+ left: xPosition + "px",
+ top: yPosition - 70 + "px",
+ backgroundColor: labelBackgroundColor,
+ }}
+ >
+ {Math.round(pendulumLength)} m
+ </p>
+ <p
+ style={{
+ position: "absolute",
+ zIndex: -1,
+ left: xMax / 2 + "px",
top: 30 + "px",
- backgroundColor: this.labelBackgroundColor,
+ backgroundColor: labelBackgroundColor,
}}
>
- {this.state.angleLabel}°
+ {Math.round(pendulumAngle * 100) / 100}°
</p>
</div>
)}
</div>
)}
- {!this.state.dragging && this.props.dataDoc['showAcceleration'] && (
+ {this.props.dataDoc['simulationType'] == "Inclined Plane" && (
+ <div>
+ <div
+ style={{ position: "absolute", left: "0", top: "0", zIndex: -5 }}
+ >
+ <svg width={xMax + "px"} height={yMax + "px"}>
+ <polygon points={coordinates} style={{ fill: "burlywood" }} />
+ </svg>
+ </div>
+
+ <p
+ style={{
+ position: "absolute",
+ zIndex: 500,
+ left: Math.round(xMax * 0.5 - 200 + wedgeWidth - 80) + "px",
+ top: Math.round(window.innerHeight * 0.8 - 40) + "px",
+ }}
+ >
+ {Math.round(
+ ((Math.atan(wedgeHeight / wedgeWidth) * 180) / Math.PI) * 100
+ ) / 100}
+ °
+ </p>
+ </div>
+ )}
+ {!dragging && showAcceleration && (
<div>
<div
style={{
pointerEvents: "none",
position: "absolute",
+ zIndex: -1,
left: 0,
top: 0,
}}
>
- <svg width={this.props.xMax + "px"} height={300 + "px"}>
+ <svg width={xMax + "px"} height={window.innerHeight + "px"}>
<defs>
<marker
id="accArrow"
@@ -664,10 +1328,10 @@ export default class Weight extends React.Component<IWeightProps, IState> {
</marker>
</defs>
<line
- x1={this.state.xPosition + this.props.radius}
- y1={this.state.yPosition + this.props.radius}
- x2={this.state.xPosition + this.props.radius + this.getNewAccelerationX(this.props.dataDoc['updatedForces']) * 5}
- y2={this.state.yPosition + this.props.radius + this.getNewAccelerationY(this.props.dataDoc['updatedForces']) * 5}
+ x1={xPosition + radius}
+ y1={yPosition + radius}
+ x2={xPosition + radius + getNewAccelerationX(updatedForces) * 7}
+ y2={yPosition + radius + getNewAccelerationY(updatedForces) * 7}
stroke={"green"}
strokeWidth="5"
markerEnd="url(#accArrow)"
@@ -677,46 +1341,35 @@ export default class Weight extends React.Component<IWeightProps, IState> {
style={{
pointerEvents: "none",
position: "absolute",
- left:
- this.state.xPosition +
- this.props.radius +
- this.getNewAccelerationX(this.props.dataDoc['updatedForces']) * 5 +
- 25 +
- "px",
- top:
- this.state.yPosition +
- this.props.radius +
- this.getNewAccelerationY(this.props.dataDoc['updatedForces']) * 5 +
- 25 +
- "px",
+ left: xPosition + radius + xAccel * 3 + 25 + "px",
+ top: yPosition + radius + yAccel * 3 + 70 + "px",
+ zIndex: -1,
lineHeight: 0.5,
}}
>
<p>
{Math.round(
- 100 *
- Math.sqrt(
- Math.pow(this.getNewAccelerationX(this.props.dataDoc['updatedForces']) * 3, 2) +
- Math.pow(this.getNewAccelerationY(this.props.dataDoc['updatedForces']) * 3, 2)
- )
+ 100 * Math.sqrt(xAccel * xAccel + yAccel * yAccel)
) / 100}{" "}
- m/s<sup>2</sup>
+ m/s
+ <sup>2</sup>
</p>
</div>
</div>
</div>
)}
- {!this.state.dragging && this.props.dataDoc['showVelocity'] && (
+ {!dragging && showVelocity && (
<div>
<div
style={{
pointerEvents: "none",
position: "absolute",
+ zIndex: -1,
left: 0,
top: 0,
}}
>
- <svg width={this.props.xMax + "px"} height={300 + "px"}>
+ <svg width={xMax + "px"} height={window.innerHeight + "px"}>
<defs>
<marker
id="velArrow"
@@ -731,10 +1384,10 @@ export default class Weight extends React.Component<IWeightProps, IState> {
</marker>
</defs>
<line
- x1={this.state.xPosition + this.props.radius}
- y1={this.state.yPosition + this.props.radius}
- x2={this.state.xPosition + this.props.radius + this.state.xVelocity * 3}
- y2={this.state.yPosition + this.props.radius + this.state.yVelocity * 3}
+ x1={xPosition + radius}
+ y1={yPosition + radius}
+ x2={xPosition + radius + xVelocity * 3}
+ y2={yPosition + radius + yVelocity * 3}
stroke={"blue"}
strokeWidth="5"
markerEnd="url(#velArrow)"
@@ -744,14 +1397,19 @@ export default class Weight extends React.Component<IWeightProps, IState> {
style={{
pointerEvents: "none",
position: "absolute",
- left: this.state.xPosition + this.props.radius + this.state.xVelocity * 3 + 25 + "px",
- top: this.state.yPosition + this.props.radius + this.state.yVelocity * 3 + "px",
+ left: xPosition + radius + xVelocity * 3 + 25 + "px",
+ top: yPosition + radius + yVelocity * 3 + "px",
+ zIndex: -1,
lineHeight: 0.5,
}}
>
<p>
{Math.round(
- 100 * Math.sqrt(this.state.xVelocity**2 + this.state.yVelocity**2)
+ 100 *
+ Math.sqrt(
+ displayXVelocity * displayXVelocity +
+ displayYVelocity * displayYVelocity
+ )
) / 100}{" "}
m/s
</p>
@@ -759,23 +1417,134 @@ export default class Weight extends React.Component<IWeightProps, IState> {
</div>
</div>
)}
- {!this.state.dragging &&
- this.props.dataDoc['showForces'] &&
- this.props.dataDoc['updatedForces'].map((force, index) => {
- if (force.magnitude < this.epsilon) {
+ {!dragging &&
+ showComponentForces &&
+ componentForces.map((force, index) => {
+ if (force.magnitude < epsilon) {
+ return;
+ }
+ let arrowStartY: number = yPosition + radius;
+ const arrowStartX: number = xPosition + radius;
+ let 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";
+
+ let labelTop = arrowEndY;
+ let labelLeft = arrowEndX;
+ if (force.directionInDegrees > 90 && force.directionInDegrees < 270) {
+ labelLeft -= 120;
+ } else {
+ labelLeft += 30;
+ }
+ if (force.directionInDegrees >= 0 && force.directionInDegrees < 180) {
+ labelTop += 40;
+ } else {
+ labelTop -= 40;
+ }
+ labelTop = Math.min(labelTop, yMax + 50);
+ labelTop = Math.max(labelTop, yMin);
+ labelLeft = Math.min(labelLeft, xMax - 60);
+ labelLeft = Math.max(labelLeft, xMin);
+
+ return (
+ <div key={index}>
+ <div
+ style={{
+ pointerEvents: "none",
+ position: "absolute",
+ zIndex: -1,
+ left: xMin,
+ top: yMin,
+ }}
+ >
+ <svg
+ width={xMax - xMin + "px"}
+ height={window.innerHeight + "px"}
+ >
+ <defs>
+ <marker
+ id="forceArrow"
+ markerWidth="10"
+ markerHeight="10"
+ refX="0"
+ refY="3"
+ orient="auto"
+ markerUnits="strokeWidth"
+ >
+ <path d="M0,0 L0,6 L9,3 z" fill={color} />
+ </marker>
+ </defs>
+ {force.component == true && (
+ <line
+ x1={arrowStartX}
+ y1={arrowStartY}
+ x2={arrowEndX}
+ y2={arrowEndY}
+ stroke={color}
+ strokeWidth="5"
+ strokeDasharray="10,10"
+ markerEnd="url(#forceArrow)"
+ />
+ )}
+ {force.component == false && (
+ <line
+ x1={arrowStartX}
+ y1={arrowStartY}
+ x2={arrowEndX}
+ y2={arrowEndY}
+ stroke={color}
+ strokeWidth="5"
+ markerEnd="url(#forceArrow)"
+ />
+ )}
+ </svg>
+ </div>
+ <div
+ style={{
+ pointerEvents: "none",
+ position: "absolute",
+ left: labelLeft + "px",
+ top: labelTop + "px",
+ // zIndex: -1,
+ lineHeight: 0.5,
+ backgroundColor: labelBackgroundColor,
+ }}
+ >
+ {force.description && <p>{force.description}</p>}
+ {!force.description && <p>Force</p>}
+ {showForceMagnitudes && (
+ <p>{Math.round(100 * force.magnitude) / 100} N</p>
+ )}
+ </div>
+ </div>
+ );
+ })}
+ {!dragging &&
+ showForces &&
+ updatedForces.map((force, index) => {
+ if (force.magnitude < epsilon) {
return;
}
- let arrowStartY: number = this.state.yPosition + this.props.radius;
- const arrowStartX: number = this.state.xPosition + this.props.radius;
+ let arrowStartY: number = yPosition + radius;
+ const arrowStartX: number = xPosition + radius;
let arrowEndY: number =
arrowStartY -
Math.abs(force.magnitude) *
- 10 *
+ 20 *
Math.sin((force.directionInDegrees * Math.PI) / 180);
const arrowEndX: number =
arrowStartX +
Math.abs(force.magnitude) *
- 10 *
+ 20 *
Math.cos((force.directionInDegrees * Math.PI) / 180);
let color = "#0d0d0d";
@@ -792,10 +1561,10 @@ export default class Weight extends React.Component<IWeightProps, IState> {
} else {
labelTop -= 40;
}
- labelTop = Math.min(labelTop, this.props.yMax + 50);
- labelTop = Math.max(labelTop, this.props.yMin);
- labelLeft = Math.min(labelLeft, this.props.xMax - 60);
- labelLeft = Math.max(labelLeft, this.props.xMin);
+ labelTop = Math.min(labelTop, yMax + 50);
+ labelTop = Math.max(labelTop, yMin);
+ labelLeft = Math.min(labelLeft, xMax - 60);
+ labelLeft = Math.max(labelLeft, xMin);
return (
<div key={index}>
@@ -803,13 +1572,14 @@ export default class Weight extends React.Component<IWeightProps, IState> {
style={{
pointerEvents: "none",
position: "absolute",
- left: this.props.xMin,
- top: this.props.yMin,
+ zIndex: -1,
+ left: xMin,
+ top: yMin,
}}
>
<svg
- width={this.props.xMax - this.props.xMin + "px"}
- height={300 + "px"}
+ width={xMax - xMin + "px"}
+ height={window.innerHeight + "px"}
>
<defs>
<marker
@@ -824,15 +1594,29 @@ export default class Weight extends React.Component<IWeightProps, IState> {
<path d="M0,0 L0,6 L9,3 z" fill={color} />
</marker>
</defs>
- <line
- x1={arrowStartX}
- y1={arrowStartY}
- x2={arrowEndX}
- y2={arrowEndY}
- stroke={color}
- strokeWidth="5"
- markerEnd="url(#forceArrow)"
- />
+ {force.component == true && (
+ <line
+ x1={arrowStartX}
+ y1={arrowStartY}
+ x2={arrowEndX}
+ y2={arrowEndY}
+ stroke={color}
+ strokeWidth="5"
+ strokeDasharray="10,10"
+ markerEnd="url(#forceArrow)"
+ />
+ )}
+ {force.component == false && (
+ <line
+ x1={arrowStartX}
+ y1={arrowStartY}
+ x2={arrowEndX}
+ y2={arrowEndY}
+ stroke={color}
+ strokeWidth="5"
+ markerEnd="url(#forceArrow)"
+ />
+ )}
</svg>
</div>
<div
@@ -842,12 +1626,12 @@ export default class Weight extends React.Component<IWeightProps, IState> {
left: labelLeft + "px",
top: labelTop + "px",
lineHeight: 0.5,
- backgroundColor: this.labelBackgroundColor,
+ backgroundColor: labelBackgroundColor,
}}
>
{force.description && <p>{force.description}</p>}
{!force.description && <p>Force</p>}
- {this.props.dataDoc['showForceMagnitudes'] && (
+ {showForceMagnitudes && (
<p>{Math.round(100 * force.magnitude) / 100} N</p>
)}
</div>
@@ -855,6 +1639,5 @@ export default class Weight extends React.Component<IWeightProps, IState> {
);
})}
</div>
- );
- }
+ );
};