diff options
Diffstat (limited to 'src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx')
-rw-r--r-- | src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx | 168 |
1 files changed, 52 insertions, 116 deletions
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx index a0b47f13f..d3e1c6fd2 100644 --- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx @@ -6,7 +6,7 @@ import QuestionMarkIcon from '@mui/icons-material/QuestionMark'; import ReplayIcon from '@mui/icons-material/Replay'; import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControl, FormControlLabel, FormGroup, IconButton, LinearProgress, Stack } from '@mui/material'; import Typography from '@mui/material/Typography'; -import { computed, reaction } from 'mobx'; +import { computed, IReactionDisposer, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { NumListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; @@ -31,7 +31,6 @@ interface IForce { description: string; magnitude: number; directionInDegrees: number; - component: boolean; } interface VectorTemplate { top: number; @@ -76,20 +75,23 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP return FieldView.LayoutString(PhysicsSimulationBox, fieldKey); } + _widthDisposer: IReactionDisposer | undefined; + + // semi-Constants + xMin = 0; + yMin = 0; + xMax = this.props.PanelWidth() * 0.6; + yMax = this.props.PanelHeight(); + color = `rgba(0,0,0,0.5)`; + radius = 50; + wallPositions: IWallProps[] = []; + @computed get circularMotionRadius() { return NumCast(this.dataDoc.circularMotionRadius, 150); } @computed get gravity() { return NumCast(this.dataDoc.simulation_gravity, -9.81); } - gravityForce = (mass: number): IForce => { - return { - description: 'Gravity', - magnitude: mass * Math.abs(this.gravity), - directionInDegrees: 270, - component: false, - }; - }; @computed get simulationType() { return StrCast(this.dataDoc.simulation_type, 'Inclined Plane'); } @@ -138,13 +140,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP } @computed get mass1Radius() { - return NumCast(this.dataDoc.mass1_radius, 60); + return NumCast(this.dataDoc.mass1_radius, 30); } @computed get mass1PosXStart() { - return NumCast(this.dataDoc.mass1_positionXstart, Math.round((this.xMax * 0.5 - 200) * 10) / 10); + return NumCast(this.dataDoc.mass1_positionXstart); } @computed get mass1PosYStart() { - return NumCast(this.dataDoc.mass1_positionYstart, this.getDisplayYPos((400 - 0.08 * this.props.PanelHeight()) * Math.tan((26 * Math.PI) / 180) + Math.sqrt(26))); + return NumCast(this.dataDoc.mass1_positionYstart); } @computed get mass1VelXStart() { return NumCast(this.dataDoc.mass1_velocityXstart); @@ -157,7 +159,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP return NumCast(this.dataDoc.mass2_positionXstart); } @computed get mass2PosYStart() { - return NumCast(this.dataDoc.mass2_positionYstart, this.getDisplayYPos((400 - 0.08 * this.props.PanelHeight()) * Math.tan((26 * Math.PI) / 180) + Math.sqrt(26))); + return NumCast(this.dataDoc.mass2_positionYstart); } @computed get mass2VelXStart() { return NumCast(this.dataDoc.mass2_velocityXstart); @@ -182,18 +184,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP return StrCast(this.dataDoc.questionPartTwo); } - // Constants - xMin = 0; - yMin = 0; - xMax = this.props.PanelWidth() * 0.6; - yMax = this.props.PanelHeight(); - color = `rgba(0,0,0,0.5)`; - radius = 50; - wallPositions: IWallProps[] = []; + componentWillUnmount() { + this._widthDisposer?.(); + } componentDidMount() { // Setup and update simulation - reaction(() => [this.props.PanelWidth(), this.props.PanelHeight()], this.setupSimulation, { fireImmediately: true }); + this._widthDisposer = reaction(() => [this.props.PanelWidth(), this.props.PanelHeight()], this.setupSimulation, { fireImmediately: true }); // Create walls this.wallPositions = [ @@ -212,6 +209,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP } } + gravityForce = (mass: number): IForce => ({ + description: 'Gravity', + magnitude: mass * Math.abs(this.gravity), + directionInDegrees: 270, + }); + setupSimulation = () => { const simulationType = this.simulationType; const mode = this.simulationMode; @@ -228,10 +231,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP switch (simulationType) { case 'One Weight': this.dataDoc.simulation_showComponentForces = false; - this.dataDoc.mass1_positionYstart = this.yMin + 0.08 * this.props.PanelHeight(); - this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - 0.08 * this.props.PanelHeight(); - this.dataDoc.mass1_positionY = this.getDisplayYPos(this.yMin + 0.08 * this.props.PanelHeight()); - this.dataDoc.mass1_positionX = (this.xMax + this.xMin) / 2 - 0.08 * this.props.PanelHeight(); + this.dataDoc.mass1_positionYstart = this.yMin + this.mass1Radius; + this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius; + this.dataDoc.mass1_positionY = this.getDisplayYPos(this.yMin + this.mass1Radius); + this.dataDoc.mass1_positionX = (this.xMax + this.xMin) / 2 - this.mass1Radius; this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]); this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]); this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset; @@ -256,10 +259,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP switch (simulationType) { case 'One Weight' : break;// TODO - one weight review problems case 'Spring': this.setupSpring(); break; // TODO - spring review problems - case 'Inclined Plane': - this.dataDoc.mass1_forcesUpdated = ''; - this.dataDoc.mass1_forcesStart = ''; - break; + case 'Inclined Plane': this.dataDoc.mass1_forcesUpdated = this.dataDoc.mass1_forcesStart = ''; break; case 'Pendulum': this.setupPendulum(); break; // TODO - pendulum review problems case 'Circular Motion': this.setupCircular(0); break; // TODO - circular motion review problems case 'Pulley': this.setupPulley(); break; // TODO - pulley tutorial review problems @@ -283,7 +283,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP case 'One Weight': this.dataDoc.simulation_showForces = true; this.dataDoc.mass1_positionYstart = this.yMax - 100; - this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - 0.08 * this.props.PanelHeight(); + this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius; this.dataDoc.tutorial = JSON.stringify(tutorials.freeWeight); this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.freeWeight.steps[0].forces); this.dataDoc.simulation_showForceMagnitudes = tutorials.freeWeight.steps[0].showMagnitude; @@ -292,7 +292,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP this.dataDoc.simulation_showForces = true; this.setupSpring(); this.dataDoc.mass1_positionYstart = this.yMin + 200 + 19.62; - this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - 0.08 * this.props.PanelHeight(); + this.dataDoc.mass1_positionXstart = (this.xMax + this.xMin) / 2 - this.mass1Radius; this.dataDoc.tutorial = JSON.stringify(tutorials.spring); this.dataDoc.mass1_forcesStart = JSON.stringify(tutorials.spring.steps[0].forces); this.dataDoc.simulation_showForceMagnitudes = tutorials.spring.steps[0].showMagnitude; @@ -338,12 +338,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP }; // Helper function to go between display and real values - getDisplayYPos = (yPos: number) => { - return this.yMax - yPos - 2 * (0.08 * this.props.PanelHeight()) + 5; - }; - getYPosFromDisplay = (yDisplay: number) => { - return this.yMax - yDisplay - 2 * (0.08 * this.props.PanelHeight()) + 5; - }; + getDisplayYPos = (yPos: number) => this.yMax - yPos - 2 * this.mass1Radius + 5; + getYPosFromDisplay = (yDisplay: number) => this.yMax - yDisplay - 2 * this.mass1Radius + 5; // Update forces when coefficient of static friction changes in freeform mode updateForcesWithFriction = (coefficient: number, width = this.wedgeWidth, height = this.wedgeHeight) => { @@ -351,13 +347,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP description: 'Normal Force', magnitude: Math.abs(this.gravity) * Math.cos(Math.atan(height / width)) * this.mass1, directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI, - component: false, }; let frictionForce: IForce = { description: 'Static Friction Force', magnitude: coefficient * Math.abs(this.gravity) * Math.cos(Math.atan(height / width)) * this.mass1, directionInDegrees: 180 - (Math.atan(height / width) * 180) / Math.PI, - component: false, }; // reduce magnitude or friction force if necessary such that block cannot slide up plane let yForce = -Math.abs(this.gravity) * this.mass1; @@ -366,35 +360,27 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP if (yForce > 0) { frictionForce.magnitude = (-normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + Math.abs(this.gravity) * this.mass1) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180); } - const frictionForceComponent: IForce = { - description: 'Static Friction Force', - magnitude: coefficient * Math.abs(this.gravity) * Math.cos(Math.atan(height / width)), - directionInDegrees: 180 - (Math.atan(height / width) * 180) / Math.PI, - component: true, - }; + const normalForceComponent: IForce = { description: 'Normal Force', - magnitude: Math.abs(this.gravity) * Math.cos(Math.atan(height / width)), + magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(Math.atan(height / width)), directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI, - component: true, }; const gravityParallel: IForce = { description: 'Gravity Parallel Component', magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin(Math.PI / 2 - Math.atan(height / width)), directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI + 180, - component: true, }; const gravityPerpendicular: IForce = { description: 'Gravity Perpendicular Component', magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(Math.PI / 2 - Math.atan(height / width)), directionInDegrees: 360 - (Math.atan(height / width) * 180) / Math.PI, - component: true, }; const gravityForce = this.gravityForce(this.mass1); if (coefficient != 0) { this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce, normalForce, frictionForce]); this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce, normalForce, frictionForce]); - this.dataDoc.mass1_componentForces = JSON.stringify([frictionForceComponent, normalForceComponent, gravityParallel, gravityPerpendicular]); + this.dataDoc.mass1_componentForces = JSON.stringify([frictionForce, normalForceComponent, gravityParallel, gravityPerpendicular]); } else { this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce, normalForce]); this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce, normalForce]); @@ -549,24 +535,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP } } if (showAlert) { - if (!error) { - this.dataDoc.simulation_paused = false; - setTimeout(() => { - this.dataDoc.simulation_paused = true; - }, 3000); - } else { - this.dataDoc.simulation_paused = false; - setTimeout(() => { - this.dataDoc.simulation_paused = true; - }, 3000); - } + this.dataDoc.simulation_paused = false; + setTimeout(() => (this.dataDoc.simulation_paused = true), 3000); } if (this.selectedQuestion.goal == 'noMovement') { - if (!error) { - this.dataDoc.noMovement = true; - } else { - this.dataDoc.roMovement = false; - } + this.dataDoc.noMovement = !error; } }; @@ -590,11 +563,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP let question: QuestionTemplate = questions.inclinePlane[0]; if (this.simulationType === 'Inclined Plane') { - if (this.dataDoc.questionNumber === questions.inclinePlane.length - 1) { - this.dataDoc.questionNumber = 0; - } else { - this.dataDoc.questionNumber = NumCast(this.dataDoc.questionNumber) + 1; - } + this.dataDoc.questionNumber = (NumCast(this.dataDoc.questionNumber) + 1) % questions.inclinePlane.length; question = questions.inclinePlane[NumCast(this.dataDoc.questionNumber)]; let coefficient = 0; @@ -639,15 +608,14 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP this.dataDoc.simulation_showComponentForces = false; this.dataDoc.mass1_velocityYstart = 0; this.dataDoc.mass1_velocityXstart = value; - let xPos = (this.xMax + this.xMin) / 2 - 0.08 * this.props.PanelHeight(); - let yPos = (this.yMax + this.yMin) / 2 + this.circularMotionRadius - 0.08 * this.props.PanelHeight(); + let xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius; + let yPos = (this.yMax + this.yMin) / 2 + this.circularMotionRadius - this.mass1Radius; this.dataDoc.mass1_positionYstart = yPos; this.dataDoc.mass1_positionXstart = xPos; const tensionForce: IForce = { description: 'Centripetal Force', magnitude: (this.dataDoc.mass1_velocityXstart ** 2 * this.mass1) / this.circularMotionRadius, directionInDegrees: 90, - component: false, }; this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce]); this.dataDoc.mass1_forcesStart = JSON.stringify([tensionForce]); @@ -666,38 +634,27 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP const angle = 30; const x = length * Math.cos(((90 - angle) * Math.PI) / 180); const y = length * Math.sin(((90 - angle) * Math.PI) / 180); - const xPos = this.xMax / 2 - x - 0.08 * this.props.PanelHeight(); - const yPos = y - 0.08 * this.props.PanelHeight() - 5; + const xPos = this.xMax / 2 - x - this.mass1Radius; + const yPos = y - this.mass1Radius - 5; this.dataDoc.mass1_positionXstart = xPos; this.dataDoc.mass1_positionYstart = yPos; - const mag = this.mass1 * Math.abs(this.gravity) * Math.sin((60 * Math.PI) / 180); const forceOfTension: IForce = { description: 'Tension', - magnitude: mag, - directionInDegrees: 90 - angle, - component: false, - }; - - const tensionComponent: IForce = { - description: 'Tension', - magnitude: mag, + magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin((60 * Math.PI) / 180), directionInDegrees: 90 - angle, - component: true, }; const gravityParallel: IForce = { description: 'Gravity Parallel Component', magnitude: this.mass1 * Math.abs(this.gravity) * Math.sin(((90 - angle) * Math.PI) / 180), directionInDegrees: -angle - 90, - component: true, }; const gravityPerpendicular: IForce = { description: 'Gravity Perpendicular Component', magnitude: this.mass1 * Math.abs(this.gravity) * Math.cos(((90 - angle) * Math.PI) / 180), directionInDegrees: -angle, - component: true, }; - this.dataDoc.mass1_componentForces = JSON.stringify([tensionComponent, gravityParallel, gravityPerpendicular]); + this.dataDoc.mass1_componentForces = JSON.stringify([forceOfTension, gravityParallel, gravityPerpendicular]); this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]); this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]); this.dataDoc.pendulum_angle = this.dataDoc.pendulum_angleStart = 30; @@ -709,7 +666,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP this.dataDoc.simulation_showComponentForces = false; this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]); this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]); - this.dataDoc.mass1_positionXstart = this.xMax / 2 - 0.08 * this.props.PanelHeight(); + this.dataDoc.mass1_positionXstart = this.xMax / 2 - this.mass1Radius; this.dataDoc.mass1_positionYstart = 200; this.dataDoc.spring_constant = 0.5; this.dataDoc.spring_lengthRest = 200; @@ -719,7 +676,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP // Default setup for suspension simulation setupSuspension = () => { - let xPos = (this.xMax + this.xMin) / 2 - 0.08 * this.props.PanelHeight(); + let xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius; let yPos = this.yMin + 200; this.dataDoc.mass1_positionYstart = yPos; this.dataDoc.mass1_positionXstart = xPos; @@ -730,13 +687,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP description: 'Tension', magnitude: tensionMag, directionInDegrees: 45, - component: false, }; const tensionForce2: IForce = { description: 'Tension', magnitude: tensionMag, directionInDegrees: 135, - component: false, }; const gravity = this.gravityForce(this.mass1); this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]); @@ -757,7 +712,6 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP description: 'Tension', magnitude: this.mass1 * a + this.mass1 * Math.abs(this.gravity), directionInDegrees: 90, - component: false, }; this.dataDoc.mass1_forcesUpdated = JSON.stringify([gravityForce1, tensionForce1]); this.dataDoc.mass1_forcesStart = JSON.stringify([gravityForce1, tensionForce1]); @@ -767,7 +721,6 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP description: 'Tension', magnitude: -this.mass2 * a + this.mass2 * Math.abs(this.gravity), directionInDegrees: 90, - component: false, }; this.dataDoc.mass2_positionYstart = (this.yMax + this.yMin) / 2; this.dataDoc.mass2_positionXstart = (this.xMin + this.xMax) / 2 + 5; @@ -789,19 +742,16 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP description: 'Gravity', magnitude: NumCast(this.dataDoc.review_GravityMagnitude), directionInDegrees: NumCast(this.dataDoc.review_GravityAngle), - component: false, }; const normalForceReview: IForce = { description: 'Normal Force', magnitude: NumCast(this.dataDoc.review_NormalMagnitude), directionInDegrees: NumCast(this.dataDoc.review_NormalAngle), - component: false, }; const staticFrictionForceReview: IForce = { description: 'Static Friction Force', magnitude: NumCast(this.dataDoc.review_StaticMagnitude), directionInDegrees: NumCast(this.dataDoc.review_StaticAngle), - component: false, }; this.dataDoc.mass1_forcesStart = JSON.stringify([forceOfGravityReview, normalForceReview, staticFrictionForceReview]); this.dataDoc.mass1_forcesUpdated = JSON.stringify([forceOfGravityReview, normalForceReview, staticFrictionForceReview]); @@ -1378,7 +1328,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP /> {(this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') && ( <FormControlLabel - control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showForces)} onChange={() => (this.dataDoc.simulation_showComponentForces = !this.dataDoc.simulation_showComponentForces)} />} + control={<Checkbox checked={BoolCast(this.dataDoc.simulation_showComponentForces)} onChange={() => (this.dataDoc.simulation_showComponentForces = !this.dataDoc.simulation_showComponentForces)} />} label="Show component force vectors" labelPlacement="start" /> @@ -1616,26 +1566,16 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP description: 'Tension', magnitude: mag, directionInDegrees: 90 - value, - component: false, - }; - - const tensionComponent: IForce = { - description: 'Tension', - magnitude: mag, - directionInDegrees: 90 - value, - component: true, }; const gravityParallel: IForce = { description: 'Gravity Parallel Component', magnitude: Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180), directionInDegrees: 270 - value, - component: true, }; const gravityPerpendicular: IForce = { description: 'Gravity Perpendicular Component', magnitude: Math.abs(this.gravity) * Math.sin((value * Math.PI) / 180), directionInDegrees: -value, - component: true, }; const length = this.pendulumLength; @@ -1648,7 +1588,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]); this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]); - this.dataDoc.mass1_componentForces = JSON.stringify([tensionComponent, gravityParallel, gravityPerpendicular]); + this.dataDoc.mass1_componentForces = JSON.stringify([forceOfTension, gravityParallel, gravityPerpendicular]); this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset; } }} @@ -1736,13 +1676,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP description: 'Tension', magnitude: tensionMag1, directionInDegrees: dir1T, - component: false, }; const tensionForce2: IForce = { description: 'Tension', magnitude: tensionMag2, directionInDegrees: dir2T, - component: false, }; const gravity = this.gravityForce(this.mass1); this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]); @@ -1787,13 +1725,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP description: 'Tension', magnitude: tensionMag1, directionInDegrees: dir1T, - component: false, }; const tensionForce2: IForce = { description: 'Tension', magnitude: tensionMag2, directionInDegrees: dir2T, - component: false, }; const gravity = this.gravityForce(this.mass1); this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]); |