diff options
author | bobzel <zzzman@gmail.com> | 2023-05-22 11:25:32 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2023-05-22 11:25:32 -0400 |
commit | bed3309e1fda6597b2a8fea10ad82cd3a0402051 (patch) | |
tree | fe599bbdc5fca2c221e1e0f7a60995b7cd39f870 /src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx | |
parent | 887a4f7e0fc25fde87b20a5de2e7b0aee561cc78 (diff) | |
parent | 3d26d5b2654841a9b92f3d66b28d1dc8e36cca6a (diff) |
merged physics with master
Diffstat (limited to 'src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx')
-rw-r--r-- | src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx | 4533 |
1 files changed, 2086 insertions, 2447 deletions
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx index aaea988a2..ab93583df 100644 --- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx @@ -1,2531 +1,2170 @@ -import "./PhysicsSimulationBox.scss"; +import './PhysicsSimulationBox.scss'; import { FieldView, FieldViewProps } from './../FieldView'; import React = require('react'); import { ViewBoxAnnotatableComponent } from '../../DocComponent'; import { observer } from 'mobx-react'; -import "./PhysicsSimulationBox.scss"; +import './PhysicsSimulationBox.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { CheckBox } from "../../search/CheckBox"; -import PauseIcon from "@mui/icons-material/Pause"; -import PlayArrowIcon from "@mui/icons-material/PlayArrow"; -import ReplayIcon from "@mui/icons-material/Replay"; -import QuestionMarkIcon from "@mui/icons-material/QuestionMark"; -import ArrowLeftIcon from "@mui/icons-material/ArrowLeft"; -import ArrowRightIcon from "@mui/icons-material/ArrowRight"; -import EditIcon from "@mui/icons-material/Edit"; -import EditOffIcon from "@mui/icons-material/EditOff"; -import { - Box, - Button, - Checkbox, - Dialog, - DialogTitle, - DialogContent, - DialogContentText, - DialogActions, - FormControl, - FormControlLabel, - FormGroup, - IconButton, - LinearProgress, - Stack, -} from "@mui/material"; -import Typography from "@mui/material/Typography"; -import "./PhysicsSimulationBox.scss"; -import InputField from "./PhysicsSimulationInputField"; -import * as questions from "./PhysicsSimulationQuestions.json"; -import * as tutorials from "./PhysicsSimulationTutorial.json"; -import Wall from "./PhysicsSimulationWall"; -import Weight from "./PhysicsSimulationWeight"; +import { CheckBox } from '../../search/CheckBox'; +import PauseIcon from '@mui/icons-material/Pause'; +import PlayArrowIcon from '@mui/icons-material/PlayArrow'; +import ReplayIcon from '@mui/icons-material/Replay'; +import QuestionMarkIcon from '@mui/icons-material/QuestionMark'; +import ArrowLeftIcon from '@mui/icons-material/ArrowLeft'; +import ArrowRightIcon from '@mui/icons-material/ArrowRight'; +import EditIcon from '@mui/icons-material/Edit'; +import EditOffIcon from '@mui/icons-material/EditOff'; +import { Box, Button, Checkbox, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, FormControl, FormControlLabel, FormGroup, IconButton, LinearProgress, Stack } from '@mui/material'; +import Typography from '@mui/material/Typography'; +import './PhysicsSimulationBox.scss'; +import InputField from './PhysicsSimulationInputField'; +import * as questions from './PhysicsSimulationQuestions.json'; +import * as tutorials from './PhysicsSimulationTutorial.json'; +import Wall from './PhysicsSimulationWall'; +import Weight from './PhysicsSimulationWeight'; +import { NumCast } from '../../../../fields/Types'; +import { HeightSym, WidthSym } from '../../../../fields/Doc'; interface IWallProps { - length: number; - xPos: number; - yPos: number; - angleInDegrees: number; + length: number; + xPos: number; + yPos: number; + angleInDegrees: number; } interface IForce { - description: string; - magnitude: number; - directionInDegrees: number; - component: boolean; + description: string; + magnitude: number; + directionInDegrees: number; + component: boolean; } interface VectorTemplate { - top: number; - left: number; - width: number; - height: number; - x1: number; - y1: number; - x2: number; - y2: number; - weightX: number; - weightY: number; + top: number; + left: number; + width: number; + height: number; + x1: number; + y1: number; + x2: number; + y2: number; + weightX: number; + weightY: number; } interface QuestionTemplate { - questionSetup: string[]; - variablesForQuestionSetup: string[]; - question: string; - answerParts: string[]; - answerSolutionDescriptions: string[]; - goal: string; - hints: { description: string; content: string }[]; + questionSetup: string[]; + variablesForQuestionSetup: string[]; + question: string; + answerParts: string[]; + answerSolutionDescriptions: string[]; + goal: string; + hints: { description: string; content: string }[]; } interface TutorialTemplate { - question: string; - steps: { - description: string; - content: string; - forces: { - description: string; - magnitude: number; - directionInDegrees: number; - component: boolean; + question: string; + steps: { + description: string; + content: string; + forces: { + description: string; + magnitude: number; + directionInDegrees: number; + component: boolean; + }[]; + showMagnitude: boolean; }[]; - showMagnitude: boolean; - }[]; } @observer -export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { - - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PhysicsSimulationBox, fieldKey); } - - constructor(props: any) { - super(props); - } - - // Constants - xMin = 0; - yMin = 0; - xMax = 100; - yMax = 100; - color = `rgba(0,0,0,0.5)`; - radius = 50; - wallPositions: IWallProps[] = []; +export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(PhysicsSimulationBox, fieldKey); + } - componentDidMount() { - // Used throughout sims - this.layoutDoc._width = 1000; - this.layoutDoc._height = 800; - this.xMax = this.layoutDoc._width*0.6; - this.yMax = this.layoutDoc._height*0.9; - this.dataDoc.reviewCoefficient = this.dataDoc.reviewCoefficient ?? 0; - this.dataDoc.questionVariables = this.dataDoc.questionVariables ?? []; - this.dataDoc.accelerationXDisplay = this.dataDoc.accelerationXDisplay ?? 0; - this.dataDoc.accelerationYDisplay = this.dataDoc.accelerationYDisplay ?? 0; - this.dataDoc.componentForces = this.dataDoc.componentForces ?? []; - this.dataDoc.displayChange = this.dataDoc.displayChange ?? { xDisplay: 0, yDisplay: 0 }; - this.dataDoc.elasticCollisions = this.dataDoc.elasticCollisions ?? false; - this.dataDoc.gravity = this.dataDoc.gravity ?? -9.81; - this.dataDoc.mass = this.dataDoc.mass ?? 1; - this.dataDoc.mode = "Freeform"; - this.dataDoc.positionXDisplay = this.dataDoc.positionXDisplay ?? 0; - this.dataDoc.positionYDisplay = this.dataDoc.positionYDisplay ?? 0; - this.dataDoc.showAcceleration = this.dataDoc.showAcceleration ?? false; - this.dataDoc.showComponentForces = this.dataDoc.showComponentForces ?? false; - this.dataDoc.showForces = this.dataDoc.showForces ?? true; - this.dataDoc.showForceMagnitudes = this.dataDoc.showForceMagnitudes ?? true; - this.dataDoc.showVelocity = this.dataDoc.showVelocity ?? false; - this.dataDoc.simulationPaused = this.dataDoc.simulationPaused ?? true; - this.dataDoc.simulationReset = this.dataDoc.simulationReset ?? false; - this.dataDoc.simulationSpeed = this.dataDoc.simulationSpeed ?? 2; - this.dataDoc.simulationType = this.dataDoc.simulationType ?? "Inclined Plane"; - this.dataDoc.startForces = this.dataDoc.startForces ?? []; - this.dataDoc.startPosX = this.dataDoc.startPosX ?? Math.round((this.xMax * 0.5 - 200) * 10) / 10; - this.dataDoc.startPosY = this.dataDoc.startPosY ?? this.getDisplayYPos( - (400 - (0.08*this.layoutDoc._height)) * Math.tan((26 * Math.PI) / 180) + Math.sqrt(26) - ); - this.dataDoc.startVelX = this.dataDoc.startVelX ?? 0; - this.dataDoc.startVelY = this.dataDoc.startVelY ?? 0; - this.dataDoc.stepNumber = this.dataDoc.stepNumber ?? 0; - this.dataDoc.updatedForces = this.dataDoc.updatedForces ?? []; - this.dataDoc.velocityXDisplay = this.dataDoc.velocityXDisplay ?? 0; - this.dataDoc.velocityYDisplay = this.dataDoc.velocityYDisplay ?? 0; + constructor(props: any) { + super(props); + } - // Used for review mode - // this.dataDoc.currentForceSketch = this.dataDoc.currentForceSketch ?? null; - // this.dataDoc.deleteMode = this.dataDoc.deleteMode ?? false; - // this.dataDoc.forceSketches = this.dataDoc.forceSketches ?? []; - this.dataDoc.answers = []; - this.dataDoc.showIcon = false; - this.dataDoc.hintDialogueOpen = false; - this.dataDoc.noMovement = false; - this.dataDoc.questionNumber = 0; - this.dataDoc.questionPartOne = ""; - this.dataDoc.questionPartTwo = ""; - this.dataDoc.reviewGravityAngle = 0; - this.dataDoc.reviewGravityMagnitude = 0; - this.dataDoc.reviewNormalAngle = 0; - this.dataDoc.reviewNormalMagnitude = 0; - this.dataDoc.reviewStaticAngle = 0; - this.dataDoc.reviewStaticMagnitude = 0; - this.dataDoc.selectedSolutions = []; - this.dataDoc.selectedQuestion = this.dataDoc.selectedQuestion ?? questions.inclinePlane[0]; - // this.dataDoc.sketching = this.dataDoc.sketching ?? false; + // Constants + xMin = 0; + yMin = 0; + xMax = 100; + yMax = 100; + color = `rgba(0,0,0,0.5)`; + radius = 50; + wallPositions: IWallProps[] = []; - // Used for tutorial mode - this.dataDoc.selectedTutorial = this.dataDoc.selectedTutorial ?? tutorials.inclinePlane; + componentDidMount() { + // Used throughout sims + this.layoutDoc._width = 1000; + this.layoutDoc._height = 800; + this.xMax = this.layoutDoc._width * 0.6; + this.yMax = this.layoutDoc._height * 0.9; + this.dataDoc.reviewCoefficient = this.dataDoc.reviewCoefficient ?? 0; + this.dataDoc.questionVariables = this.dataDoc.questionVariables ?? []; + this.dataDoc.accelerationXDisplay = this.dataDoc.accelerationXDisplay ?? 0; + this.dataDoc.accelerationYDisplay = this.dataDoc.accelerationYDisplay ?? 0; + this.dataDoc.componentForces = this.dataDoc.componentForces ?? []; + this.dataDoc.displayChange = this.dataDoc.displayChange ?? { xDisplay: 0, yDisplay: 0 }; + this.dataDoc.elasticCollisions = this.dataDoc.elasticCollisions ?? false; + this.dataDoc.gravity = this.dataDoc.gravity ?? -9.81; + this.dataDoc.mass = this.dataDoc.mass ?? 1; + this.dataDoc.mode = 'Freeform'; + this.dataDoc.positionXDisplay = this.dataDoc.positionXDisplay ?? 0; + this.dataDoc.positionYDisplay = this.dataDoc.positionYDisplay ?? 0; + this.dataDoc.showAcceleration = this.dataDoc.showAcceleration ?? false; + this.dataDoc.showComponentForces = this.dataDoc.showComponentForces ?? false; + this.dataDoc.showForces = this.dataDoc.showForces ?? true; + this.dataDoc.showForceMagnitudes = this.dataDoc.showForceMagnitudes ?? true; + this.dataDoc.showVelocity = this.dataDoc.showVelocity ?? false; + this.dataDoc.simulationPaused = this.dataDoc.simulationPaused ?? true; + this.dataDoc.simulationReset = this.dataDoc.simulationReset ?? false; + this.dataDoc.simulationSpeed = this.dataDoc.simulationSpeed ?? 2; + this.dataDoc.simulationType = this.dataDoc.simulationType ?? 'Inclined Plane'; + this.dataDoc.startForces = this.dataDoc.startForces ?? []; + this.dataDoc.startPosX = this.dataDoc.startPosX ?? Math.round((this.xMax * 0.5 - 200) * 10) / 10; + this.dataDoc.startPosY = this.dataDoc.startPosY ?? this.getDisplayYPos((400 - 0.08 * this.layoutDoc._height) * Math.tan((26 * Math.PI) / 180) + Math.sqrt(26)); + this.dataDoc.startVelX = this.dataDoc.startVelX ?? 0; + this.dataDoc.startVelY = this.dataDoc.startVelY ?? 0; + this.dataDoc.stepNumber = this.dataDoc.stepNumber ?? 0; + this.dataDoc.updatedForces = this.dataDoc.updatedForces ?? []; + this.dataDoc.velocityXDisplay = this.dataDoc.velocityXDisplay ?? 0; + this.dataDoc.velocityYDisplay = this.dataDoc.velocityYDisplay ?? 0; - // Used for uniform circular motion - this.dataDoc.circularMotionRadius = this.dataDoc.circularMotionRadius ?? 150; + // Used for review mode + // this.dataDoc.currentForceSketch = this.dataDoc.currentForceSketch ?? null; + // this.dataDoc.deleteMode = this.dataDoc.deleteMode ?? false; + // this.dataDoc.forceSketches = this.dataDoc.forceSketches ?? []; + this.dataDoc.answers = []; + this.dataDoc.showIcon = false; + this.dataDoc.hintDialogueOpen = false; + this.dataDoc.noMovement = false; + this.dataDoc.questionNumber = 0; + this.dataDoc.questionPartOne = ''; + this.dataDoc.questionPartTwo = ''; + this.dataDoc.reviewGravityAngle = 0; + this.dataDoc.reviewGravityMagnitude = 0; + this.dataDoc.reviewNormalAngle = 0; + this.dataDoc.reviewNormalMagnitude = 0; + this.dataDoc.reviewStaticAngle = 0; + this.dataDoc.reviewStaticMagnitude = 0; + this.dataDoc.selectedSolutions = []; + this.dataDoc.selectedQuestion = this.dataDoc.selectedQuestion ?? questions.inclinePlane[0]; + // this.dataDoc.sketching = this.dataDoc.sketching ?? false; - // Used for spring simulation - this.dataDoc.springConstant = this.dataDoc.springConstant ?? 0.5; - this.dataDoc.springRestLength = this.dataDoc.springRestLength ?? 200; - this.dataDoc.springStartLength = this.dataDoc.springStartLength ?? 200; + // Used for tutorial mode + this.dataDoc.selectedTutorial = this.dataDoc.selectedTutorial ?? tutorials.inclinePlane; - // Used for pendulum simulation - this.dataDoc.adjustPendulumAngle = this.dataDoc.adjustPendulumAngle ?? { angle: 0, length: 0 }; - this.dataDoc.pendulumAngle = this.dataDoc.pendulumAngle ?? 0; - this.dataDoc.pendulumLength = this.dataDoc.pendulumLength ?? 300; - this.dataDoc.startPendulumAngle = this.dataDoc.startPendulumAngle ?? 0; + // Used for uniform circular motion + this.dataDoc.circularMotionRadius = this.dataDoc.circularMotionRadius ?? 150; - // Used for wedge simulation - this.dataDoc.coefficientOfKineticFriction = this.dataDoc.coefficientOfKineticFriction ?? 0; - this.dataDoc.coefficientOfStaticFriction = this.dataDoc.coefficientOfStaticFriction ?? 0; - this.dataDoc.wedgeAngle = this.dataDoc.wedgeAngle ?? 26; - this.dataDoc.wedgeHeight = this.dataDoc.wedgeHeight ?? Math.tan((26 * Math.PI) / 180) * this.xMax*0.5; - this.dataDoc.wedgeWidth = this.dataDoc.wedgeWidth ?? this.xMax*0.5; + // Used for spring simulation + this.dataDoc.springConstant = this.dataDoc.springConstant ?? 0.5; + this.dataDoc.springRestLength = this.dataDoc.springRestLength ?? 200; + this.dataDoc.springStartLength = this.dataDoc.springStartLength ?? 200; - // Used for pulley simulation - this.dataDoc.positionXDisplay2 = this.dataDoc.positionXDisplay2 ?? 0; - this.dataDoc.velocityXDisplay2 = this.dataDoc.velocityXDisplay2 ?? 0; - this.dataDoc.accelerationXDisplay2 = this.dataDoc.accelerationXDisplay2 ?? 0; - this.dataDoc.positionYDisplay2 = this.dataDoc.positionYDisplay2 ?? 0; - this.dataDoc.velocityYDisplay2 = this.dataDoc.velocityYDisplay2 ?? 0; - this.dataDoc.accelerationYDisplay2 = this.dataDoc.accelerationYDisplay2 ?? 0; - this.dataDoc.startPosX2 = this.dataDoc.startPosX2 ?? 0; - this.dataDoc.startPosY2 = this.dataDoc.startPosY2 ?? 0; - this.dataDoc.displayChange2 = this.dataDoc.displayChange2 ?? { xDisplay: 0, yDisplay: 0 }; - this.dataDoc.startForces2 = this.dataDoc.startForces2 ?? []; - this.dataDoc.updatedForces2 = this.dataDoc.updatedForces2 ?? []; - this.dataDoc.mass2 = this.dataDoc.mass2 ?? 1; + // Used for pendulum simulation + this.dataDoc.adjustPendulumAngle = this.dataDoc.adjustPendulumAngle ?? { angle: 0, length: 0 }; + this.dataDoc.pendulumAngle = this.dataDoc.pendulumAngle ?? 0; + this.dataDoc.pendulumLength = this.dataDoc.pendulumLength ?? 300; + this.dataDoc.startPendulumAngle = this.dataDoc.startPendulumAngle ?? 0; - // Setup simulation - this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode) + // Used for wedge simulation + this.dataDoc.coefficientOfKineticFriction = this.dataDoc.coefficientOfKineticFriction ?? 0; + this.dataDoc.coefficientOfStaticFriction = this.dataDoc.coefficientOfStaticFriction ?? 0; + this.dataDoc.wedgeAngle = this.dataDoc.wedgeAngle ?? 26; + this.dataDoc.wedgeHeight = this.dataDoc.wedgeHeight ?? Math.tan((26 * Math.PI) / 180) * this.xMax * 0.5; + this.dataDoc.wedgeWidth = this.dataDoc.wedgeWidth ?? this.xMax * 0.5; - // Create walls - let walls = [] - walls.push({ length: this.xMax / this.layoutDoc._width * 100, xPos: 0, yPos: 0, angleInDegrees: 0 }); - walls.push({ length: this.xMax / this.layoutDoc._width * 100, xPos: 0, yPos: this.yMax / this.layoutDoc._height * 100, angleInDegrees: 0 }); - walls.push({ length: this.yMax / this.layoutDoc._height * 100, xPos: 0, yPos: 0, angleInDegrees: 90 }); - walls.push({ length: this.yMax / this.layoutDoc._height * 100, xPos: this.xMax / this.layoutDoc._width * 100, yPos: 0, angleInDegrees: 90 }); - this.wallPositions = walls - } + // Used for pulley simulation + this.dataDoc.positionXDisplay2 = this.dataDoc.positionXDisplay2 ?? 0; + this.dataDoc.velocityXDisplay2 = this.dataDoc.velocityXDisplay2 ?? 0; + this.dataDoc.accelerationXDisplay2 = this.dataDoc.accelerationXDisplay2 ?? 0; + this.dataDoc.positionYDisplay2 = this.dataDoc.positionYDisplay2 ?? 0; + this.dataDoc.velocityYDisplay2 = this.dataDoc.velocityYDisplay2 ?? 0; + this.dataDoc.accelerationYDisplay2 = this.dataDoc.accelerationYDisplay2 ?? 0; + this.dataDoc.startPosX2 = this.dataDoc.startPosX2 ?? 0; + this.dataDoc.startPosY2 = this.dataDoc.startPosY2 ?? 0; + this.dataDoc.displayChange2 = this.dataDoc.displayChange2 ?? { xDisplay: 0, yDisplay: 0 }; + this.dataDoc.startForces2 = this.dataDoc.startForces2 ?? []; + this.dataDoc.updatedForces2 = this.dataDoc.updatedForces2 ?? []; + this.dataDoc.mass2 = this.dataDoc.mass2 ?? 1; - componentDidUpdate() { - if (this.xMax != this.layoutDoc._width*0.6 || this.yMax != this.layoutDoc._height*0.9) { - this.layoutDoc._width = Math.max(this.layoutDoc._width, 800) - this.layoutDoc._height = Math.max(this.layoutDoc._height, 600) - this.xMax = this.layoutDoc._width*0.6; - this.yMax = this.layoutDoc._height*0.9; - this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode) - } - } + // Setup simulation + this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode); - setupSimulation = (simulationType: string, mode: string) => { - this.dataDoc.simulationPaused = true; - if (simulationType != "Circular Motion") { - this.dataDoc.startVelX = 0; - this.dataDoc.setStartVelY = 0; - this.dataDoc.velocityXDisplay = (0); - this.dataDoc.velocityYDisplay = (0); + // Create walls + let walls = []; + walls.push({ length: (this.xMax / this.layoutDoc._width) * 100, xPos: 0, yPos: 0, angleInDegrees: 0 }); + walls.push({ length: (this.xMax / this.layoutDoc._width) * 100, xPos: 0, yPos: (this.yMax / this.layoutDoc._height) * 100, angleInDegrees: 0 }); + walls.push({ length: (this.yMax / this.layoutDoc._height) * 100, xPos: 0, yPos: 0, angleInDegrees: 90 }); + walls.push({ length: (this.yMax / this.layoutDoc._height) * 100, xPos: (this.xMax / this.layoutDoc._width) * 100, yPos: 0, angleInDegrees: 90 }); + this.wallPositions = walls; } - if (mode == "Freeform") { - this.dataDoc.showForceMagnitudes = (true); - if (simulationType == "One Weight") { - this.dataDoc.showComponentForces = false; - this.dataDoc.startPosY = (this.yMin + (0.08*this.layoutDoc._height)); - this.dataDoc.startPosX = ((this.xMax + this.xMin) / 2 - (0.08*this.layoutDoc._height)); - this.dataDoc.positionYDisplay = (this.getDisplayYPos(this.yMin + (0.08*this.layoutDoc._height))); - this.dataDoc.positionXDisplay = ((this.xMax + this.xMin) / 2 - (0.08*this.layoutDoc._height)); - this.dataDoc.updatedForces = ([ - { - description: "Gravity", - magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, - directionInDegrees: 270, - component: false, - }, - ]); - this.dataDoc.startForces = ([ - { - description: "Gravity", - magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, - directionInDegrees: 270, - component: false, - }, - ]); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - } else if (simulationType == "Inclined Plane") { - this.changeWedgeBasedOnNewAngle(26); - this.dataDoc.startForces = ([ - { - description: "Gravity", - magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, - directionInDegrees: 270, - component: false, - }, - ]); - this.updateForcesWithFriction(Number(this.dataDoc.coefficientOfStaticFriction)); - } else if (simulationType == "Pendulum") { - this.setupPendulum(); - } else if (simulationType == "Spring") { - this.setupSpring(); - } else if (simulationType == "Circular Motion") { - this.setupCircular(0); - } else if (simulationType == "Pulley") { - this.setupPulley(); - } else if (simulationType == "Suspension") { - this.setupSuspension(); - } - } else if (mode == "Review") { - this.dataDoc.showComponentForces = (false); - this.dataDoc.showForceMagnitudes = (true); - this.dataDoc.showAcceleration = (false); - this.dataDoc.showVelocity = (false); - this.dataDoc.showForces= (true); - this.generateNewQuestion(); - if (simulationType == "One Weight") { - // TODO - one weight review problems - } else if (simulationType == "Spring") { - this.setupSpring(); - // TODO - spring review problems - } else if (simulationType == "Inclined Plane") { - this.dataDoc.updatedForces = ([]); - this.dataDoc.startForces = ([]); - } else if (simulationType == "Pendulum") { - this.setupPendulum(); - // TODO - pendulum review problems - } else if (simulationType == "Circular Motion") { - this.setupCircular(0); - // TODO - circular motion review problems - } else if (simulationType == "Pulley") { - this.setupPulley(); - // TODO - pulley tutorial review problems - } else if (simulationType == "Suspension") { - this.setupSuspension(); - // TODO - suspension tutorial review problems - } - } else if (mode == "Tutorial") { - this.dataDoc.showComponentForces = (false); - this.dataDoc.stepNumber = (0); - this.dataDoc.showAcceleration = (false); - if (this.dataDoc.simulationType != "Circular Motion") { - this.dataDoc.velocityXDisplay = (0); - this.dataDoc.velocityYDisplay = (0); - this.dataDoc.showVelocity = (false); - } else { - this.dataDoc.velocityXDisplay = (20); - this.dataDoc.velocityYDisplay = (0); - this.dataDoc.showVelocity = (true); - } - if (this.dataDoc.simulationType == "One Weight") { - this.dataDoc.showForces = (true); - this.dataDoc.startPosY = (this.yMax - 100); - this.dataDoc.startPosX = ((this.xMax + this.xMin) / 2 - (0.08*this.layoutDoc._height)); - this.dataDoc.selectedTutorial = (tutorials.freeWeight); - this.dataDoc.startForces = (this.getForceFromJSON(tutorials.freeWeight.steps[0].forces)); - this.dataDoc.showForceMagnitudes = (tutorials.freeWeight.steps[0].showMagnitude); - } else if (this.dataDoc.simulationType == "Spring") { - this.dataDoc.showForces = (true); - this.setupSpring(); - this.dataDoc.startPosY = (this.yMin + 200 + 19.62); - this.dataDoc.startPosX = ((this.xMax + this.xMin) / 2 - (0.08*this.layoutDoc._height)); - this.dataDoc.selectedTutorial = (tutorials.spring); - this.dataDoc.startForces = (this.getForceFromJSON(tutorials.spring.steps[0].forces)); - this.dataDoc.showForceMagnitudes = (tutorials.spring.steps[0].showMagnitude); - } else if (this.dataDoc.simulationType == "Pendulum") { - this.dataDoc.showForces = (true); - const length = 300; - 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.layoutDoc._height); - const yPos = y - (0.08*this.layoutDoc._height) - 5; - this.dataDoc.startPosX = (xPos); - this.dataDoc.startPosY = (yPos); - this.dataDoc.selectedTutorial = (tutorials.pendulum); - this.dataDoc.startForces = (this.getForceFromJSON(tutorials.pendulum.steps[0].forces)); - this.dataDoc.showForceMagnitudes = (tutorials.pendulum.steps[0].showMagnitude); - this.dataDoc.pendulumAngle = (30); - this.dataDoc.pendulumLength = (300); - this.dataDoc.adjustPendulumAngle = ({ angle: 30, length: 300 }); - } else if (this.dataDoc.simulationType == "Inclined Plane") { - this.dataDoc.showForces = (true); - this.dataDoc.wedgeAngle = (26); - this.changeWedgeBasedOnNewAngle(26); - this.dataDoc.selectedTutorial = (tutorials.inclinePlane); - this.dataDoc.startForces = ( - this.getForceFromJSON(tutorials.inclinePlane.steps[0].forces) - ); - this.dataDoc.showForceMagnitudes = (tutorials.inclinePlane.steps[0].showMagnitude); - } else if (this.dataDoc.simulationType == "Circular Motion") { - this.dataDoc.showForces = (true); - this.setupCircular(40); - this.dataDoc.selectedTutorial = (tutorials.circular); - this.dataDoc.startForces = (this.getForceFromJSON(tutorials.circular.steps[0].forces)); - this.dataDoc.showForceMagnitudes = (tutorials.circular.steps[0].showMagnitude); - } else if (this.dataDoc.simulationType == "Pulley") { - this.dataDoc.showForces = (true); - this.setupPulley(); - this.dataDoc.selectedTutorial = (tutorials.pulley); - this.dataDoc.startForces = (this.getForceFromJSON(tutorials.pulley.steps[0].forces)); - this.dataDoc.showForceMagnitudes = (tutorials.pulley.steps[0].showMagnitude); - } else if (this.dataDoc.simulationType == "Suspension") { - this.dataDoc.showForces = (true); - this.setupSuspension(); - this.dataDoc.selectedTutorial = (tutorials.suspension); - this.dataDoc.startForces = (this.getForceFromJSON(tutorials.suspension.steps[0].forces)); - this.dataDoc.showForceMagnitudes = (tutorials.suspension.steps[0].showMagnitude); - } - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); + componentDidUpdate() { + if (this.xMax !== this.layoutDoc[WidthSym]() * 0.6 || this.yMax != this.layoutDoc[HeightSym]() * 0.9) { + this.layoutDoc._width = Math.max(this.layoutDoc[WidthSym](), 800); + this.layoutDoc._height = Math.max(this.layoutDoc[HeightSym](), 600); + this.xMax = this.layoutDoc._width * 0.6; + this.yMax = this.layoutDoc._height * 0.9; + this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode); + } } - } - // Helper function to go between display and real values - getDisplayYPos = (yPos: number) => { - return this.yMax - yPos - 2 * (0.08*this.layoutDoc._height) + 5; - }; - getYPosFromDisplay = (yDisplay: number) => { - return this.yMax - yDisplay - 2 * (0.08*this.layoutDoc._height) + 5; - }; + setupSimulation = (simulationType: string, mode: string) => { + this.dataDoc.simulationPaused = true; + if (simulationType != 'Circular Motion') { + this.dataDoc.startVelX = 0; + this.dataDoc.setStartVelY = 0; + this.dataDoc.velocityXDisplay = 0; + this.dataDoc.velocityYDisplay = 0; + } + if (mode == 'Freeform') { + this.dataDoc.showForceMagnitudes = true; + if (simulationType == 'One Weight') { + this.dataDoc.showComponentForces = false; + this.dataDoc.startPosY = this.yMin + 0.08 * this.layoutDoc[HeightSym](); + this.dataDoc.startPosX = (this.xMax + this.xMin) / 2 - 0.08 * this.layoutDoc[HeightSym](); + this.dataDoc.positionYDisplay = this.getDisplayYPos(this.yMin + 0.08 * this.layoutDoc[HeightSym]()); + this.dataDoc.positionXDisplay = (this.xMax + this.xMin) / 2 - 0.08 * this.layoutDoc[HeightSym](); + this.dataDoc.updatedForces = [ + { + description: 'Gravity', + magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, + directionInDegrees: 270, + component: false, + }, + ]; + this.dataDoc.startForces = [ + { + description: 'Gravity', + magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, + directionInDegrees: 270, + component: false, + }, + ]; + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + } else if (simulationType == 'Inclined Plane') { + this.changeWedgeBasedOnNewAngle(26); + this.dataDoc.startForces = [ + { + description: 'Gravity', + magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, + directionInDegrees: 270, + component: false, + }, + ]; + this.updateForcesWithFriction(Number(this.dataDoc.coefficientOfStaticFriction)); + } else if (simulationType == 'Pendulum') { + this.setupPendulum(); + } else if (simulationType == 'Spring') { + this.setupSpring(); + } else if (simulationType == 'Circular Motion') { + this.setupCircular(0); + } else if (simulationType == 'Pulley') { + this.setupPulley(); + } else if (simulationType == 'Suspension') { + this.setupSuspension(); + } + } else if (mode == 'Review') { + this.dataDoc.showComponentForces = false; + this.dataDoc.showForceMagnitudes = true; + this.dataDoc.showAcceleration = false; + this.dataDoc.showVelocity = false; + this.dataDoc.showForces = true; + this.generateNewQuestion(); + if (simulationType == 'One Weight') { + // TODO - one weight review problems + } else if (simulationType == 'Spring') { + this.setupSpring(); + // TODO - spring review problems + } else if (simulationType == 'Inclined Plane') { + this.dataDoc.updatedForces = []; + this.dataDoc.startForces = []; + } else if (simulationType == 'Pendulum') { + this.setupPendulum(); + // TODO - pendulum review problems + } else if (simulationType == 'Circular Motion') { + this.setupCircular(0); + // TODO - circular motion review problems + } else if (simulationType == 'Pulley') { + this.setupPulley(); + // TODO - pulley tutorial review problems + } else if (simulationType == 'Suspension') { + this.setupSuspension(); + // TODO - suspension tutorial review problems + } + } else if (mode == 'Tutorial') { + this.dataDoc.showComponentForces = false; + this.dataDoc.stepNumber = 0; + this.dataDoc.showAcceleration = false; + if (this.dataDoc.simulationType != 'Circular Motion') { + this.dataDoc.velocityXDisplay = 0; + this.dataDoc.velocityYDisplay = 0; + this.dataDoc.showVelocity = false; + } else { + this.dataDoc.velocityXDisplay = 20; + this.dataDoc.velocityYDisplay = 0; + this.dataDoc.showVelocity = true; + } - // Update forces when coefficient of static friction changes in freeform mode - updateForcesWithFriction = ( - coefficient: number, - width: number = this.dataDoc.wedgeWidth, - height: number = this.dataDoc.wedgeHeight - ) => { - const normalForce: IForce = { - description: "Normal Force", - magnitude: Math.abs(this.dataDoc.gravity) * Math.cos(Math.atan(height / width)) * this.dataDoc.mass, - directionInDegrees: - 180 - 90 - (Math.atan(height / width) * 180) / Math.PI, - component: false, - }; - let frictionForce: IForce = { - description: "Static Friction Force", - magnitude: - coefficient * - Math.abs(this.dataDoc.gravity) * - Math.cos(Math.atan(height / width)) * - this.dataDoc.mass, - 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.dataDoc.gravity) * this.dataDoc.mass; - 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(this.dataDoc.gravity) * this.dataDoc.mass) / - Math.sin((frictionForce.directionInDegrees * Math.PI) / 180); - } - const frictionForceComponent: IForce = { - description: "Static Friction Force", - magnitude: - coefficient * Math.abs(this.dataDoc.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.dataDoc.gravity) * Math.cos(Math.atan(height / width)), - directionInDegrees: - 180 - 90 - (Math.atan(height / width) * 180) / Math.PI, - component: true, + if (this.dataDoc.simulationType == 'One Weight') { + this.dataDoc.showForces = true; + this.dataDoc.startPosY = this.yMax - 100; + this.dataDoc.startPosX = (this.xMax + this.xMin) / 2 - 0.08 * this.layoutDoc[HeightSym](); + this.dataDoc.selectedTutorial = tutorials.freeWeight; + this.dataDoc.startForces = this.getForceFromJSON(tutorials.freeWeight.steps[0].forces); + this.dataDoc.showForceMagnitudes = tutorials.freeWeight.steps[0].showMagnitude; + } else if (this.dataDoc.simulationType == 'Spring') { + this.dataDoc.showForces = true; + this.setupSpring(); + this.dataDoc.startPosY = this.yMin + 200 + 19.62; + this.dataDoc.startPosX = (this.xMax + this.xMin) / 2 - 0.08 * this.layoutDoc[HeightSym](); + this.dataDoc.selectedTutorial = tutorials.spring; + this.dataDoc.startForces = this.getForceFromJSON(tutorials.spring.steps[0].forces); + this.dataDoc.showForceMagnitudes = tutorials.spring.steps[0].showMagnitude; + } else if (this.dataDoc.simulationType == 'Pendulum') { + this.dataDoc.showForces = true; + const length = 300; + 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.layoutDoc[HeightSym](); + const yPos = y - 0.08 * this.layoutDoc[HeightSym]() - 5; + this.dataDoc.startPosX = xPos; + this.dataDoc.startPosY = yPos; + this.dataDoc.selectedTutorial = tutorials.pendulum; + this.dataDoc.startForces = this.getForceFromJSON(tutorials.pendulum.steps[0].forces); + this.dataDoc.showForceMagnitudes = tutorials.pendulum.steps[0].showMagnitude; + this.dataDoc.pendulumAngle = 30; + this.dataDoc.pendulumLength = 300; + this.dataDoc.adjustPendulumAngle = { angle: 30, length: 300 }; + } else if (this.dataDoc.simulationType == 'Inclined Plane') { + this.dataDoc.showForces = true; + this.dataDoc.wedgeAngle = 26; + this.changeWedgeBasedOnNewAngle(26); + this.dataDoc.selectedTutorial = tutorials.inclinePlane; + this.dataDoc.startForces = this.getForceFromJSON(tutorials.inclinePlane.steps[0].forces); + this.dataDoc.showForceMagnitudes = tutorials.inclinePlane.steps[0].showMagnitude; + } else if (this.dataDoc.simulationType == 'Circular Motion') { + this.dataDoc.showForces = true; + this.setupCircular(40); + this.dataDoc.selectedTutorial = tutorials.circular; + this.dataDoc.startForces = this.getForceFromJSON(tutorials.circular.steps[0].forces); + this.dataDoc.showForceMagnitudes = tutorials.circular.steps[0].showMagnitude; + } else if (this.dataDoc.simulationType == 'Pulley') { + this.dataDoc.showForces = true; + this.setupPulley(); + this.dataDoc.selectedTutorial = tutorials.pulley; + this.dataDoc.startForces = this.getForceFromJSON(tutorials.pulley.steps[0].forces); + this.dataDoc.showForceMagnitudes = tutorials.pulley.steps[0].showMagnitude; + } else if (this.dataDoc.simulationType == 'Suspension') { + this.dataDoc.showForces = true; + this.setupSuspension(); + this.dataDoc.selectedTutorial = tutorials.suspension; + this.dataDoc.startForces = this.getForceFromJSON(tutorials.suspension.steps[0].forces); + this.dataDoc.showForceMagnitudes = tutorials.suspension.steps[0].showMagnitude; + } + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + } }; - const gravityParallel: IForce = { - description: "Gravity Parallel Component", - magnitude: - this.dataDoc.mass * - Math.abs(this.dataDoc.gravity) * - Math.sin(Math.PI / 2 - Math.atan(height / width)), - directionInDegrees: - 180 - 90 - (Math.atan(height / width) * 180) / Math.PI + 180, - component: true, + + // Helper function to go between display and real values + getDisplayYPos = (yPos: number) => { + return this.yMax - yPos - 2 * (0.08 * this.layoutDoc[HeightSym]()) + 5; }; - const gravityPerpendicular: IForce = { - description: "Gravity Perpendicular Component", - magnitude: - this.dataDoc.mass * - Math.abs(this.dataDoc.gravity) * - Math.cos(Math.PI / 2 - Math.atan(height / width)), - directionInDegrees: 360 - (Math.atan(height / width) * 180) / Math.PI, - component: true, + getYPosFromDisplay = (yDisplay: number) => { + return this.yMax - yDisplay - 2 * (0.08 * this.layoutDoc[HeightSym]()) + 5; }; - const gravityForce: IForce = { - description: "Gravity", - magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), - directionInDegrees: 270, - component: false, + + // Update forces when coefficient of static friction changes in freeform mode + updateForcesWithFriction = (coefficient: number, width: number = this.dataDoc.wedgeWidth, height: number = this.dataDoc.wedgeHeight) => { + const normalForce: IForce = { + description: 'Normal Force', + magnitude: Math.abs(this.dataDoc.gravity) * Math.cos(Math.atan(height / width)) * this.dataDoc.mass, + directionInDegrees: 180 - 90 - (Math.atan(height / width) * 180) / Math.PI, + component: false, + }; + let frictionForce: IForce = { + description: 'Static Friction Force', + magnitude: coefficient * Math.abs(this.dataDoc.gravity) * Math.cos(Math.atan(height / width)) * this.dataDoc.mass, + 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.dataDoc.gravity) * this.dataDoc.mass; + 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(this.dataDoc.gravity) * this.dataDoc.mass) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180); + } + const frictionForceComponent: IForce = { + description: 'Static Friction Force', + magnitude: coefficient * Math.abs(this.dataDoc.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.dataDoc.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.dataDoc.mass * Math.abs(this.dataDoc.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.dataDoc.mass * Math.abs(this.dataDoc.gravity) * Math.cos(Math.PI / 2 - Math.atan(height / width)), + directionInDegrees: 360 - (Math.atan(height / width) * 180) / Math.PI, + component: true, + }; + const gravityForce: IForce = { + description: 'Gravity', + magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), + directionInDegrees: 270, + component: false, + }; + if (coefficient != 0) { + this.dataDoc.startForces = [gravityForce, normalForce, frictionForce]; + this.dataDoc.updatedForces = [gravityForce, normalForce, frictionForce]; + this.dataDoc.componentForces = [frictionForceComponent, normalForceComponent, gravityParallel, gravityPerpendicular]; + } else { + this.dataDoc.startForces = [gravityForce, normalForce]; + this.dataDoc.updatedForces = [gravityForce, normalForce]; + this.dataDoc.componentForces = [normalForceComponent, gravityParallel, gravityPerpendicular]; + } }; - if (coefficient != 0) { - this.dataDoc.startForces = [gravityForce, normalForce, frictionForce]; - this.dataDoc.updatedForces = [gravityForce, normalForce, frictionForce]; - this.dataDoc.componentForces = [ - frictionForceComponent, - normalForceComponent, - gravityParallel, - gravityPerpendicular, - ]; - } else { - this.dataDoc.startForces = [gravityForce, normalForce]; - this.dataDoc.updatedForces = [gravityForce, normalForce]; - this.dataDoc.componentForces = [ - normalForceComponent, - gravityParallel, - gravityPerpendicular, - ]; - } - }; - // Change wedge height and width and weight position to match new wedge angle - changeWedgeBasedOnNewAngle = (angle: number) => { - this.dataDoc.wedgeWidth = this.xMax*0.5; - this.dataDoc.wedgeHeight = Math.tan((angle * Math.PI) / 180) * this.xMax*0.5; + // Change wedge height and width and weight position to match new wedge angle + changeWedgeBasedOnNewAngle = (angle: number) => { + this.dataDoc.wedgeWidth = this.xMax * 0.5; + this.dataDoc.wedgeHeight = Math.tan((angle * Math.PI) / 180) * this.xMax * 0.5; - // update weight position based on updated wedge width/height - let yPos = this.yMax - (0.08*this.layoutDoc._height*2) - Math.tan((angle * Math.PI) / 180) * this.xMax*0.5; + // update weight position based on updated wedge width/height + let yPos = this.yMax - 0.08 * this.layoutDoc[HeightSym]() * 2 - Math.tan((angle * Math.PI) / 180) * this.xMax * 0.5; - // adjust y position - if (angle >= 5 && angle < 10) { - yPos += 0.08*this.layoutDoc._height*0.1 - } else if (angle >= 10 && angle < 15) { - yPos += 0.08*this.layoutDoc._height*0.23 - } else if (angle >= 15 && angle < 20) { - yPos += 0.08*this.layoutDoc._height*0.26 - } else if (angle >= 20 && angle < 25) { - yPos += 0.08*this.layoutDoc._height*0.33 - } else if (angle >= 25 && angle < 30) { - yPos += 0.08*this.layoutDoc._height*0.35 - } else if (angle >= 30 && angle < 35) { - yPos += 0.08*this.layoutDoc._height*0.40 - } else if (angle >= 35 && angle < 40) { - yPos += 0.08*this.layoutDoc._height*0.45 - } else if (angle >= 40 && angle < 45) { - yPos += 0.08*this.layoutDoc._height*0.47 - } else if (angle >= 45) { - yPos += 0.08*this.layoutDoc._height*0.52 - } - - - this.dataDoc.startPosX = this.xMax*0.25; - this.dataDoc.startPosY = yPos; - if (this.dataDoc.mode == "Freeform") { - this.updateForcesWithFriction( - Number(this.dataDoc.coefficientOfStaticFriction), - this.xMax*0.5, - Math.tan((angle * Math.PI) / 180) * this.xMax*0.5 - ); - } - }; + // adjust y position + if (angle >= 5 && angle < 10) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.1; + } else if (angle >= 10 && angle < 15) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.23; + } else if (angle >= 15 && angle < 20) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.26; + } else if (angle >= 20 && angle < 25) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.33; + } else if (angle >= 25 && angle < 30) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.35; + } else if (angle >= 30 && angle < 35) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.4; + } else if (angle >= 35 && angle < 40) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.45; + } else if (angle >= 40 && angle < 45) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.47; + } else if (angle >= 45) { + yPos += 0.08 * this.layoutDoc[HeightSym]() * 0.52; + } - // In review mode, update forces when coefficient of static friction changed - updateReviewForcesBasedOnCoefficient = (coefficient: number) => { - let theta: number = Number(this.dataDoc.wedgeAngle); - let index = - this.dataDoc.selectedQuestion.variablesForQuestionSetup.indexOf("theta - max 45"); - if (index >= 0) { - theta = this.dataDoc.questionVariables[index]; - } - if (isNaN(theta)) { - return; - } - this.dataDoc.reviewGravityMagnitude = (Math.abs(this.dataDoc.gravity)); - this.dataDoc.reviewGravityAngle = (270); - this.dataDoc.reviewNormalMagnitude = ( - Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180) - ); - this.dataDoc.reviewNormalAngle = (90 - theta); - let yForce = -Math.abs(this.dataDoc.gravity); - yForce += - Math.abs(this.dataDoc.gravity) * - Math.cos((theta * Math.PI) / 180) * - Math.sin(((90 - theta) * Math.PI) / 180); - yForce += - coefficient * - Math.abs(this.dataDoc.gravity) * - Math.cos((theta * Math.PI) / 180) * - Math.sin(((180 - theta) * Math.PI) / 180); - let friction = - coefficient * Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180); - if (yForce > 0) { - friction = - (-(Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180)) * - Math.sin(((90 - theta) * Math.PI) / 180) + - Math.abs(this.dataDoc.gravity)) / - Math.sin(((180 - theta) * Math.PI) / 180); - } - this.dataDoc.reviewStaticMagnitude = (friction); - this.dataDoc.reviewStaticAngle = (180 - theta); - }; + this.dataDoc.startPosX = this.xMax * 0.25; + this.dataDoc.startPosY = yPos; + if (this.dataDoc.mode == 'Freeform') { + this.updateForcesWithFriction(Number(this.dataDoc.coefficientOfStaticFriction), this.xMax * 0.5, Math.tan((angle * Math.PI) / 180) * this.xMax * 0.5); + } + }; - // In review mode, update forces when wedge angle changed - updateReviewForcesBasedOnAngle = (angle: number) => { - this.dataDoc.reviewGravityMagnitude = (Math.abs(this.dataDoc.gravity)); - this.dataDoc.reviewGravityAngle = (270); - this.dataDoc.reviewNormalMagnitude = ( - Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180) - ); - this.dataDoc.reviewNormalAngle = (90 - angle); - let yForce = -Math.abs(this.dataDoc.gravity); - yForce += - Math.abs(this.dataDoc.gravity) * - Math.cos((Number(angle) * Math.PI) / 180) * - Math.sin(((90 - Number(angle)) * Math.PI) / 180); - yForce += - this.dataDoc.reviewCoefficient * - Math.abs(this.dataDoc.gravity) * - Math.cos((Number(angle) * Math.PI) / 180) * - Math.sin(((180 - Number(angle)) * Math.PI) / 180); - let friction = - this.dataDoc.reviewCoefficient * - Math.abs(this.dataDoc.gravity) * - Math.cos((Number(angle) * Math.PI) / 180); - if (yForce > 0) { - friction = - (-(Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180)) * - Math.sin(((90 - Number(angle)) * Math.PI) / 180) + - Math.abs(this.dataDoc.gravity)) / - Math.sin(((180 - Number(angle)) * Math.PI) / 180); - } - this.dataDoc.reviewStaticMagnitude = (friction); - this.dataDoc.reviewStaticAngle = (180 - angle); - }; + // In review mode, update forces when coefficient of static friction changed + updateReviewForcesBasedOnCoefficient = (coefficient: number) => { + let theta: number = Number(this.dataDoc.wedgeAngle); + let index = this.dataDoc.selectedQuestion.variablesForQuestionSetup.indexOf('theta - max 45'); + if (index >= 0) { + theta = this.dataDoc.questionVariables[index]; + } + if (isNaN(theta)) { + return; + } + this.dataDoc.reviewGravityMagnitude = Math.abs(this.dataDoc.gravity); + this.dataDoc.reviewGravityAngle = 270; + this.dataDoc.reviewNormalMagnitude = Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180); + this.dataDoc.reviewNormalAngle = 90 - theta; + let yForce = -Math.abs(this.dataDoc.gravity); + yForce += Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180) * Math.sin(((90 - theta) * Math.PI) / 180); + yForce += coefficient * Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180) * Math.sin(((180 - theta) * Math.PI) / 180); + let friction = coefficient * Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180); + if (yForce > 0) { + friction = (-(Math.abs(this.dataDoc.gravity) * Math.cos((theta * Math.PI) / 180)) * Math.sin(((90 - theta) * Math.PI) / 180) + Math.abs(this.dataDoc.gravity)) / Math.sin(((180 - theta) * Math.PI) / 180); + } + this.dataDoc.reviewStaticMagnitude = friction; + this.dataDoc.reviewStaticAngle = 180 - theta; + }; - // Solve for the correct answers to the generated problem - getAnswersToQuestion = ( - question: QuestionTemplate, - questionVars: number[] - ) => { - const solutions: number[] = []; + // In review mode, update forces when wedge angle changed + updateReviewForcesBasedOnAngle = (angle: number) => { + this.dataDoc.reviewGravityMagnitude = Math.abs(this.dataDoc.gravity); + this.dataDoc.reviewGravityAngle = 270; + this.dataDoc.reviewNormalMagnitude = Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180); + this.dataDoc.reviewNormalAngle = 90 - angle; + let yForce = -Math.abs(this.dataDoc.gravity); + yForce += Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180) * Math.sin(((90 - Number(angle)) * Math.PI) / 180); + yForce += this.dataDoc.reviewCoefficient * Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180) * Math.sin(((180 - Number(angle)) * Math.PI) / 180); + let friction = this.dataDoc.reviewCoefficient * Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180); + if (yForce > 0) { + friction = (-(Math.abs(this.dataDoc.gravity) * Math.cos((Number(angle) * Math.PI) / 180)) * Math.sin(((90 - Number(angle)) * Math.PI) / 180) + Math.abs(this.dataDoc.gravity)) / Math.sin(((180 - Number(angle)) * Math.PI) / 180); + } + this.dataDoc.reviewStaticMagnitude = friction; + this.dataDoc.reviewStaticAngle = 180 - angle; + }; - let theta: number = Number(this.dataDoc.wedgeAngle); - let index = question.variablesForQuestionSetup.indexOf("theta - max 45"); - if (index >= 0) { - theta = questionVars[index]; - } - let muS: number = Number(this.dataDoc.coefficientOfStaticFriction); - index = question.variablesForQuestionSetup.indexOf( - "coefficient of static friction" - ); - if (index >= 0) { - muS = questionVars[index]; - } + // Solve for the correct answers to the generated problem + getAnswersToQuestion = (question: QuestionTemplate, questionVars: number[]) => { + const solutions: number[] = []; - for (let i = 0; i < question.answerSolutionDescriptions.length; i++) { - const description = question.answerSolutionDescriptions[i]; - if (!isNaN(Number(description))) { - solutions.push(Number(description)); - } else if (description == "solve normal force angle from wedge angle") { - solutions.push(90 - theta); - } else if ( - description == "solve normal force magnitude from wedge angle" - ) { - solutions.push(Math.abs(this.dataDoc.gravity) * Math.cos((theta / 180) * Math.PI)); - } else if ( - description == - "solve static force magnitude from wedge angle given equilibrium" - ) { - let normalForceMagnitude = - Math.abs(this.dataDoc.gravity) * Math.cos((theta / 180) * Math.PI); - let normalForceAngle = 90 - theta; - let frictionForceAngle = 180 - theta; - let frictionForceMagnitude = - (-normalForceMagnitude * - Math.sin((normalForceAngle * Math.PI) / 180) + - Math.abs(this.dataDoc.gravity)) / - Math.sin((frictionForceAngle * Math.PI) / 180); - solutions.push(frictionForceMagnitude); - } else if ( - description == - "solve static force angle from wedge angle given equilibrium" - ) { - solutions.push(180 - theta); - } else if ( - description == - "solve minimum static coefficient from wedge angle given equilibrium" - ) { - let normalForceMagnitude = - Math.abs(this.dataDoc.gravity) * Math.cos((theta / 180) * Math.PI); - let normalForceAngle = 90 - theta; - let frictionForceAngle = 180 - theta; - let frictionForceMagnitude = - (-normalForceMagnitude * - Math.sin((normalForceAngle * Math.PI) / 180) + - Math.abs(this.dataDoc.gravity)) / - Math.sin((frictionForceAngle * Math.PI) / 180); - let frictionCoefficient = frictionForceMagnitude / normalForceMagnitude; - solutions.push(frictionCoefficient); - } else if ( - description == - "solve maximum wedge angle from coefficient of static friction given equilibrium" - ) { - solutions.push((Math.atan(muS) * 180) / Math.PI); - } - } - this.dataDoc.selectedSolutions = (solutions); - return solutions; - }; + let theta: number = Number(this.dataDoc.wedgeAngle); + let index = question.variablesForQuestionSetup.indexOf('theta - max 45'); + if (index >= 0) { + theta = questionVars[index]; + } + let muS: number = Number(this.dataDoc.coefficientOfStaticFriction); + index = question.variablesForQuestionSetup.indexOf('coefficient of static friction'); + if (index >= 0) { + muS = questionVars[index]; + } - // In review mode, check if input answers match correct answers and optionally generate alert - checkAnswers = (showAlert: boolean = true) => { - let error: boolean = false; - let epsilon: number = 0.01; - if (this.dataDoc.selectedQuestion) { - for (let i = 0; i < this.dataDoc.selectedQuestion.answerParts.length; i++) { - if (this.dataDoc.selectedQuestion.answerParts[i] == "force of gravity") { - if ( - Math.abs(this.dataDoc.reviewGravityMagnitude - this.dataDoc.selectedSolutions[i]) > epsilon - ) { - error = true; - } - } else if (this.dataDoc.selectedQuestion.answerParts[i] == "angle of gravity") { - if (Math.abs(this.dataDoc.reviewGravityAngle - this.dataDoc.selectedSolutions[i]) > epsilon) { - error = true; - } - } else if (this.dataDoc.selectedQuestion.answerParts[i] == "normal force") { - if ( - Math.abs(this.dataDoc.reviewNormalMagnitude - this.dataDoc.selectedSolutions[i]) > epsilon - ) { - error = true; - } - } else if (this.dataDoc.selectedQuestion.answerParts[i] == "angle of normal force") { - if (Math.abs(this.dataDoc.reviewNormalAngle - this.dataDoc.selectedSolutions[i]) > epsilon) { - error = true; - } - } else if ( - this.dataDoc.selectedQuestion.answerParts[i] == "force of static friction" - ) { - if ( - Math.abs(this.dataDoc.reviewStaticMagnitude - this.dataDoc.selectedSolutions[i]) > epsilon - ) { - error = true; - } - } else if ( - this.dataDoc.selectedQuestion.answerParts[i] == "angle of static friction" - ) { - if (Math.abs(this.dataDoc.reviewStaticAngle - this.dataDoc.selectedSolutions[i]) > epsilon) { - error = true; - } - } else if ( - this.dataDoc.selectedQuestion.answerParts[i] == "coefficient of static friction" - ) { - if ( - Math.abs( - Number(this.dataDoc.coefficientOfStaticFriction) - this.dataDoc.selectedSolutions[i] - ) > epsilon - ) { - error = true; - } - } else if (this.dataDoc.selectedQuestion.answerParts[i] == "wedge angle") { - if (Math.abs(Number(this.dataDoc.wedgeAngle) - this.dataDoc.selectedSolutions[i]) > epsilon) { - error = true; - } + for (let i = 0; i < question.answerSolutionDescriptions.length; i++) { + const description = question.answerSolutionDescriptions[i]; + if (!isNaN(Number(description))) { + solutions.push(Number(description)); + } else if (description == 'solve normal force angle from wedge angle') { + solutions.push(90 - theta); + } else if (description == 'solve normal force magnitude from wedge angle') { + solutions.push(Math.abs(this.dataDoc.gravity) * Math.cos((theta / 180) * Math.PI)); + } else if (description == 'solve static force magnitude from wedge angle given equilibrium') { + let normalForceMagnitude = Math.abs(this.dataDoc.gravity) * Math.cos((theta / 180) * Math.PI); + let normalForceAngle = 90 - theta; + let frictionForceAngle = 180 - theta; + let frictionForceMagnitude = (-normalForceMagnitude * Math.sin((normalForceAngle * Math.PI) / 180) + Math.abs(this.dataDoc.gravity)) / Math.sin((frictionForceAngle * Math.PI) / 180); + solutions.push(frictionForceMagnitude); + } else if (description == 'solve static force angle from wedge angle given equilibrium') { + solutions.push(180 - theta); + } else if (description == 'solve minimum static coefficient from wedge angle given equilibrium') { + let normalForceMagnitude = Math.abs(this.dataDoc.gravity) * Math.cos((theta / 180) * Math.PI); + let normalForceAngle = 90 - theta; + let frictionForceAngle = 180 - theta; + let frictionForceMagnitude = (-normalForceMagnitude * Math.sin((normalForceAngle * Math.PI) / 180) + Math.abs(this.dataDoc.gravity)) / Math.sin((frictionForceAngle * Math.PI) / 180); + let frictionCoefficient = frictionForceMagnitude / normalForceMagnitude; + solutions.push(frictionCoefficient); + } else if (description == 'solve maximum wedge angle from coefficient of static friction given equilibrium') { + solutions.push((Math.atan(muS) * 180) / Math.PI); + } } - } - } - if (showAlert) { - if (!error) { - this.dataDoc.simulationPaused = (false); - setTimeout(() => { - this.dataDoc.simulationPaused = (true); - }, 3000); - } else { - this.dataDoc.simulationPaused = (false); - setTimeout(() => { - this.dataDoc.simulationPaused = (true); - }, 3000); - } - } - if (this.dataDoc.selectedQuestion.goal == "noMovement") { - if (!error) { - this.dataDoc.noMovement = (true); - } else { - this.dataDoc.roMovement = (false); - } - } - }; + this.dataDoc.selectedSolutions = solutions; + return solutions; + }; - // Reset all review values to default - resetReviewValuesToDefault = () => { - this.dataDoc.reviewGravityMagnitude = (0); - this.dataDoc.reviewGravityAngle = (0); - this.dataDoc.reviewNormalMagnitude = (0); - this.dataDoc.reviewNormalAngle = (0); - this.dataDoc.reviewStaticMagnitude = (0); - this.dataDoc.reviewStaticAngle = (0); - this.dataDoc.coefficientOfKineticFriction = (0); - this.dataDoc.simulationPaused = (true); - }; + // In review mode, check if input answers match correct answers and optionally generate alert + checkAnswers = (showAlert: boolean = true) => { + let error: boolean = false; + let epsilon: number = 0.01; + if (this.dataDoc.selectedQuestion) { + for (let i = 0; i < this.dataDoc.selectedQuestion.answerParts.length; i++) { + if (this.dataDoc.selectedQuestion.answerParts[i] == 'force of gravity') { + if (Math.abs(this.dataDoc.reviewGravityMagnitude - this.dataDoc.selectedSolutions[i]) > epsilon) { + error = true; + } + } else if (this.dataDoc.selectedQuestion.answerParts[i] == 'angle of gravity') { + if (Math.abs(this.dataDoc.reviewGravityAngle - this.dataDoc.selectedSolutions[i]) > epsilon) { + error = true; + } + } else if (this.dataDoc.selectedQuestion.answerParts[i] == 'normal force') { + if (Math.abs(this.dataDoc.reviewNormalMagnitude - this.dataDoc.selectedSolutions[i]) > epsilon) { + error = true; + } + } else if (this.dataDoc.selectedQuestion.answerParts[i] == 'angle of normal force') { + if (Math.abs(this.dataDoc.reviewNormalAngle - this.dataDoc.selectedSolutions[i]) > epsilon) { + error = true; + } + } else if (this.dataDoc.selectedQuestion.answerParts[i] == 'force of static friction') { + if (Math.abs(this.dataDoc.reviewStaticMagnitude - this.dataDoc.selectedSolutions[i]) > epsilon) { + error = true; + } + } else if (this.dataDoc.selectedQuestion.answerParts[i] == 'angle of static friction') { + if (Math.abs(this.dataDoc.reviewStaticAngle - this.dataDoc.selectedSolutions[i]) > epsilon) { + error = true; + } + } else if (this.dataDoc.selectedQuestion.answerParts[i] == 'coefficient of static friction') { + if (Math.abs(Number(this.dataDoc.coefficientOfStaticFriction) - this.dataDoc.selectedSolutions[i]) > epsilon) { + error = true; + } + } else if (this.dataDoc.selectedQuestion.answerParts[i] == 'wedge angle') { + if (Math.abs(Number(this.dataDoc.wedgeAngle) - this.dataDoc.selectedSolutions[i]) > epsilon) { + error = true; + } + } + } + } + if (showAlert) { + if (!error) { + this.dataDoc.simulationPaused = false; + setTimeout(() => { + this.dataDoc.simulationPaused = true; + }, 3000); + } else { + this.dataDoc.simulationPaused = false; + setTimeout(() => { + this.dataDoc.simulationPaused = true; + }, 3000); + } + } + if (this.dataDoc.selectedQuestion.goal == 'noMovement') { + if (!error) { + this.dataDoc.noMovement = true; + } else { + this.dataDoc.roMovement = false; + } + } + }; - // In review mode, reset problem variables and generate a new question - generateNewQuestion = () => { - this.resetReviewValuesToDefault(); + // Reset all review values to default + resetReviewValuesToDefault = () => { + this.dataDoc.reviewGravityMagnitude = 0; + this.dataDoc.reviewGravityAngle = 0; + this.dataDoc.reviewNormalMagnitude = 0; + this.dataDoc.reviewNormalAngle = 0; + this.dataDoc.reviewStaticMagnitude = 0; + this.dataDoc.reviewStaticAngle = 0; + this.dataDoc.coefficientOfKineticFriction = 0; + this.dataDoc.simulationPaused = true; + }; + + // In review mode, reset problem variables and generate a new question + generateNewQuestion = () => { + this.resetReviewValuesToDefault(); - const vars: number[] = []; - let question: QuestionTemplate = questions.inclinePlane[0]; + const vars: number[] = []; + let question: QuestionTemplate = questions.inclinePlane[0]; - if (this.dataDoc.simulationType == "Inclined Plane") { - if (this.dataDoc.questionNumber == questions.inclinePlane.length - 1) { - this.dataDoc.questionNumber = (0); - } else { - this.dataDoc.questionNumber =(this.dataDoc.questionNumber + 1); - } - question = questions.inclinePlane[this.dataDoc.questionNumber]; + if (this.dataDoc.simulationType == 'Inclined Plane') { + if (this.dataDoc.questionNumber == questions.inclinePlane.length - 1) { + this.dataDoc.questionNumber = 0; + } else { + this.dataDoc.questionNumber = this.dataDoc.questionNumber + 1; + } + question = questions.inclinePlane[this.dataDoc.questionNumber]; - let coefficient = 0; - let wedgeAngle = 0; + let coefficient = 0; + let wedgeAngle = 0; - for (let i = 0; i < question.variablesForQuestionSetup.length; i++) { - if (question.variablesForQuestionSetup[i] == "theta - max 45") { - let randValue = Math.floor(Math.random() * 44 + 1); - vars.push(randValue); - wedgeAngle = randValue; - } else if ( - question.variablesForQuestionSetup[i] == - "coefficient of static friction" - ) { - let randValue = Math.round(Math.random() * 1000) / 1000; - vars.push(randValue); - coefficient = randValue; + for (let i = 0; i < question.variablesForQuestionSetup.length; i++) { + if (question.variablesForQuestionSetup[i] == 'theta - max 45') { + let randValue = Math.floor(Math.random() * 44 + 1); + vars.push(randValue); + wedgeAngle = randValue; + } else if (question.variablesForQuestionSetup[i] == 'coefficient of static friction') { + let randValue = Math.round(Math.random() * 1000) / 1000; + vars.push(randValue); + coefficient = randValue; + } + } + this.dataDoc.wedgeAngle = wedgeAngle; + this.changeWedgeBasedOnNewAngle(wedgeAngle); + this.dataDoc.coefficientOfStaticFriction = coefficient; + this.dataDoc.reviewCoefficient = coefficient; } - } - this.dataDoc.wedgeAngle = (wedgeAngle); - this.changeWedgeBasedOnNewAngle(wedgeAngle); - this.dataDoc.coefficientOfStaticFriction = (coefficient); - this.dataDoc.reviewCoefficient = coefficient; - } - let q = ""; - for (let i = 0; i < question.questionSetup.length; i++) { - q += question.questionSetup[i]; - if (i != question.questionSetup.length - 1) { - q += vars[i]; - if (question.variablesForQuestionSetup[i].includes("theta")) { - q += - " degree (≈" + - Math.round((1000 * (vars[i] * Math.PI)) / 180) / 1000 + - " rad)"; + let q = ''; + for (let i = 0; i < question.questionSetup.length; i++) { + q += question.questionSetup[i]; + if (i != question.questionSetup.length - 1) { + q += vars[i]; + if (question.variablesForQuestionSetup[i].includes('theta')) { + q += ' degree (≈' + Math.round((1000 * (vars[i] * Math.PI)) / 180) / 1000 + ' rad)'; + } + } } - } - } - this.dataDoc.questionVariables = vars; - this.dataDoc.selectedQuestion = (question); - this.dataDoc.questionPartOne = (q); - this.dataDoc.questionPartTwo = (question.question); - this.dataDoc.answers = this.getAnswersToQuestion(question, vars); - //this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }; - - // Default setup for uniform circular motion simulation - setupCircular = (value: number) => { - this.dataDoc.showComponentForces = (false); - this.dataDoc.startVelY = (0); - this.dataDoc.startVelX = (value); - let xPos = (this.xMax + this.xMin) / 2 - (0.08*this.layoutDoc._height); - let yPos = (this.yMax + this.yMin) / 2 + this.dataDoc.circularMotionRadius - (0.08*this.layoutDoc._height); - this.dataDoc.startPosY = (yPos); - this.dataDoc.startPosX = (xPos); - const tensionForce: IForce = { - description: "Centripetal Force", - magnitude: (this.dataDoc.startVelX ** 2 * this.dataDoc.mass) / this.dataDoc.circularMotionRadius, - directionInDegrees: 90, - component: false, + this.dataDoc.questionVariables = vars; + this.dataDoc.selectedQuestion = question; + this.dataDoc.questionPartOne = q; + this.dataDoc.questionPartTwo = question.question; + this.dataDoc.answers = this.getAnswersToQuestion(question, vars); + //this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); }; - this.dataDoc.updatedForces = ([tensionForce]); - this.dataDoc.startForces = ([tensionForce]); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }; - // Default setup for pendulum simulation - setupPendulum = () => { - const length = 300; - 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.layoutDoc._height); - const yPos = y - (0.08*this.layoutDoc._height) - 5; - this.dataDoc.startPosX = (xPos); - this.dataDoc.startPosY = (yPos); - const mag = this.dataDoc.mass * Math.abs(this.dataDoc.gravity) * Math.sin((60 * Math.PI) / 180); - const forceOfTension: IForce = { - description: "Tension", - magnitude: mag, - directionInDegrees: 90 - angle, - component: false, + // Default setup for uniform circular motion simulation + setupCircular = (value: number) => { + this.dataDoc.showComponentForces = false; + this.dataDoc.startVelY = 0; + this.dataDoc.startVelX = value; + let xPos = (this.xMax + this.xMin) / 2 - 0.08 * this.layoutDoc[HeightSym](); + let yPos = (this.yMax + this.yMin) / 2 + this.dataDoc.circularMotionRadius - 0.08 * this.layoutDoc[HeightSym](); + this.dataDoc.startPosY = yPos; + this.dataDoc.startPosX = xPos; + const tensionForce: IForce = { + description: 'Centripetal Force', + magnitude: (this.dataDoc.startVelX ** 2 * this.dataDoc.mass) / this.dataDoc.circularMotionRadius, + directionInDegrees: 90, + component: false, + }; + this.dataDoc.updatedForces = [tensionForce]; + this.dataDoc.startForces = [tensionForce]; + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; }; - const tensionComponent: IForce = { - description: "Tension", - magnitude: mag, - directionInDegrees: 90 - angle, - component: true, - }; - const gravityParallel: IForce = { - description: "Gravity Parallel Component", - magnitude: - this.dataDoc.mass * Math.abs(this.dataDoc.gravity) * Math.sin(((90 - angle) * Math.PI) / 180), - directionInDegrees: -angle - 90, - component: true, - }; - const gravityPerpendicular: IForce = { - description: "Gravity Perpendicular Component", - magnitude: - this.dataDoc.mass * Math.abs(this.dataDoc.gravity) * Math.cos(((90 - angle) * Math.PI) / 180), - directionInDegrees: -angle, - component: true, - }; + // Default setup for pendulum simulation + setupPendulum = () => { + const length = 300; + 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.layoutDoc[HeightSym](); + const yPos = y - 0.08 * this.layoutDoc[HeightSym]() - 5; + this.dataDoc.startPosX = xPos; + this.dataDoc.startPosY = yPos; + const mag = this.dataDoc.mass * Math.abs(this.dataDoc.gravity) * Math.sin((60 * Math.PI) / 180); + const forceOfTension: IForce = { + description: 'Tension', + magnitude: mag, + directionInDegrees: 90 - angle, + component: false, + }; - this.dataDoc.componentForces = ([ - tensionComponent, - gravityParallel, - gravityPerpendicular, - ]); - this.dataDoc.updatedForces = ([ - { - description: "Gravity", - magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), - directionInDegrees: 270, - component: false, - }, - forceOfTension, - ]); - this.dataDoc.startForces = ([ - { - description: "Gravity", - magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), - directionInDegrees: 270, - component: false, - }, - forceOfTension, - ]); - this.dataDoc.startPendulumAngle = (30); - this.dataDoc.pendulumAngle = (30); - this.dataDoc.pendulumLength = (300); - this.dataDoc.adjustPendulumAngle = ({ angle: 30, length: 300 }); - }; + const tensionComponent: IForce = { + description: 'Tension', + magnitude: mag, + directionInDegrees: 90 - angle, + component: true, + }; + const gravityParallel: IForce = { + description: 'Gravity Parallel Component', + magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity) * Math.sin(((90 - angle) * Math.PI) / 180), + directionInDegrees: -angle - 90, + component: true, + }; + const gravityPerpendicular: IForce = { + description: 'Gravity Perpendicular Component', + magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity) * Math.cos(((90 - angle) * Math.PI) / 180), + directionInDegrees: -angle, + component: true, + }; - // Default setup for spring simulation - setupSpring = () => { - this.dataDoc.showComponentForces = (false); - const gravityForce: IForce = { - description: "Gravity", - magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, - directionInDegrees: 270, - component: false, + this.dataDoc.componentForces = [tensionComponent, gravityParallel, gravityPerpendicular]; + this.dataDoc.updatedForces = [ + { + description: 'Gravity', + magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), + directionInDegrees: 270, + component: false, + }, + forceOfTension, + ]; + this.dataDoc.startForces = [ + { + description: 'Gravity', + magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), + directionInDegrees: 270, + component: false, + }, + forceOfTension, + ]; + this.dataDoc.startPendulumAngle = 30; + this.dataDoc.pendulumAngle = 30; + this.dataDoc.pendulumLength = 300; + this.dataDoc.adjustPendulumAngle = { angle: 30, length: 300 }; }; - this.dataDoc.updatedForces = ([gravityForce]); - this.dataDoc.startForces = ([gravityForce]); - this.dataDoc.startPosX = (this.xMax / 2 - (0.08*this.layoutDoc._height)); - this.dataDoc.startPosY = (200); - this.dataDoc.springConstant = (0.5); - this.dataDoc.springRestLength = (200); - this.dataDoc.springStartLength = (200); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }; - // Default setup for suspension simulation - setupSuspension = () => { - let xPos = (this.xMax + this.xMin) / 2 - (0.08*this.layoutDoc._height); - let yPos = this.yMin + 200; - this.dataDoc.startPosY = (yPos); - this.dataDoc.startPosX = (xPos); - this.dataDoc.positionYDisplay = (this.getDisplayYPos(yPos)); - this.dataDoc.positionXDisplay = (xPos); - let tensionMag = (this.dataDoc.mass * Math.abs(this.dataDoc.gravity)) / (2 * Math.sin(Math.PI / 4)); - const tensionForce1: IForce = { - description: "Tension", - magnitude: tensionMag, - directionInDegrees: 45, - component: false, - }; - const tensionForce2: IForce = { - description: "Tension", - magnitude: tensionMag, - directionInDegrees: 135, - component: false, - }; - const grav: IForce = { - description: "Gravity", - magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), - directionInDegrees: 270, - component: false, + // Default setup for spring simulation + setupSpring = () => { + this.dataDoc.showComponentForces = false; + const gravityForce: IForce = { + description: 'Gravity', + magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, + directionInDegrees: 270, + component: false, + }; + this.dataDoc.updatedForces = [gravityForce]; + this.dataDoc.startForces = [gravityForce]; + this.dataDoc.startPosX = this.xMax / 2 - 0.08 * this.layoutDoc[HeightSym](); + this.dataDoc.startPosY = 200; + this.dataDoc.springConstant = 0.5; + this.dataDoc.springRestLength = 200; + this.dataDoc.springStartLength = 200; + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; }; - this.dataDoc.updatedForces = ([tensionForce1, tensionForce2, grav]); - this.dataDoc.startForces = ([tensionForce1, tensionForce2, grav]); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }; - // Default setup for pulley simulation - setupPulley = () => { - this.dataDoc.showComponentForces = (false); - this.dataDoc.startPosY = ((this.yMax + this.yMin) / 2); - this.dataDoc.startPosX = ((this.xMin + this.xMax) / 2 - 2*(0.08*this.layoutDoc._height)-5); - this.dataDoc.positionYDisplay = (this.getDisplayYPos((this.yMax + this.yMin) / 2)); - this.dataDoc.positionXDisplay = ((this.xMin + this.xMax) / 2 - 2*(0.08*this.layoutDoc._height)-5); - let a = (-1 * ((this.dataDoc.mass - this.dataDoc.mass2) * Math.abs(this.dataDoc.gravity))) / (this.dataDoc.mass + this.dataDoc.mass2); - const gravityForce1: IForce = { - description: "Gravity", - magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), - directionInDegrees: 270, - component: false, - }; - const tensionForce1: IForce = { - description: "Tension", - magnitude: this.dataDoc.mass * a + this.dataDoc.mass * Math.abs(this.dataDoc.gravity), - directionInDegrees: 90, - component: false, - }; - a *= -1; - const gravityForce2: IForce = { - description: "Gravity", - magnitude: this.dataDoc.mass2 * Math.abs(this.dataDoc.gravity), - directionInDegrees: 270, - component: false, - }; - const tensionForce2: IForce = { - description: "Tension", - magnitude: this.dataDoc.mass2 * a + this.dataDoc.mass2 * Math.abs(this.dataDoc.gravity), - directionInDegrees: 90, - component: false, + // Default setup for suspension simulation + setupSuspension = () => { + let xPos = (this.xMax + this.xMin) / 2 - 0.08 * this.layoutDoc[HeightSym](); + let yPos = this.yMin + 200; + this.dataDoc.startPosY = yPos; + this.dataDoc.startPosX = xPos; + this.dataDoc.positionYDisplay = this.getDisplayYPos(yPos); + this.dataDoc.positionXDisplay = xPos; + let tensionMag = (this.dataDoc.mass * Math.abs(this.dataDoc.gravity)) / (2 * Math.sin(Math.PI / 4)); + const tensionForce1: IForce = { + description: 'Tension', + magnitude: tensionMag, + directionInDegrees: 45, + component: false, + }; + const tensionForce2: IForce = { + description: 'Tension', + magnitude: tensionMag, + directionInDegrees: 135, + component: false, + }; + const grav: IForce = { + description: 'Gravity', + magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), + directionInDegrees: 270, + component: false, + }; + this.dataDoc.updatedForces = [tensionForce1, tensionForce2, grav]; + this.dataDoc.startForces = [tensionForce1, tensionForce2, grav]; + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; }; - this.dataDoc.updatedForces = ([gravityForce1, tensionForce1]); - this.dataDoc.startForces = ([gravityForce1, tensionForce1]); - this.dataDoc.startPosY2 = ((this.yMax + this.yMin) / 2); - this.dataDoc.startPosX2 = ((this.xMin + this.xMax) / 2 + 5 ); - this.dataDoc.positionYDisplay2 = (this.getDisplayYPos((this.yMax + this.yMin) / 2)); - this.dataDoc.positionXDisplay2 = ((this.xMin + this.xMax) / 2 + 5); - this.dataDoc.updatedForces2 = ([gravityForce2, tensionForce2]); - this.dataDoc.startForces2 = ([gravityForce2, tensionForce2]); - - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }; + // Default setup for pulley simulation + setupPulley = () => { + this.dataDoc.showComponentForces = false; + this.dataDoc.startPosY = (this.yMax + this.yMin) / 2; + this.dataDoc.startPosX = (this.xMin + this.xMax) / 2 - 2 * (0.08 * this.layoutDoc[HeightSym]()) - 5; + this.dataDoc.positionYDisplay = this.getDisplayYPos((this.yMax + this.yMin) / 2); + this.dataDoc.positionXDisplay = (this.xMin + this.xMax) / 2 - 2 * (0.08 * this.layoutDoc[HeightSym]()) - 5; + let a = (-1 * ((this.dataDoc.mass - this.dataDoc.mass2) * Math.abs(this.dataDoc.gravity))) / (this.dataDoc.mass + this.dataDoc.mass2); + const gravityForce1: IForce = { + description: 'Gravity', + magnitude: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), + directionInDegrees: 270, + component: false, + }; + const tensionForce1: IForce = { + description: 'Tension', + magnitude: this.dataDoc.mass * a + this.dataDoc.mass * Math.abs(this.dataDoc.gravity), + directionInDegrees: 90, + component: false, + }; + a *= -1; + const gravityForce2: IForce = { + description: 'Gravity', + magnitude: this.dataDoc.mass2 * Math.abs(this.dataDoc.gravity), + directionInDegrees: 270, + component: false, + }; + const tensionForce2: IForce = { + description: 'Tension', + magnitude: this.dataDoc.mass2 * a + this.dataDoc.mass2 * Math.abs(this.dataDoc.gravity), + directionInDegrees: 90, + component: false, + }; - // Helper function used for tutorial and review mode - getForceFromJSON = ( - json: { - description: string; - magnitude: number; - directionInDegrees: number; - component: boolean; - }[] - ): IForce[] => { - const forces: IForce[] = []; - for (let i = 0; i < json.length; i++) { - const force: IForce = { - description: json[i].description, - magnitude: json[i].magnitude, - directionInDegrees: json[i].directionInDegrees, - component: json[i].component, - }; - forces.push(force); - } - return forces; - }; + this.dataDoc.updatedForces = [gravityForce1, tensionForce1]; + this.dataDoc.startForces = [gravityForce1, tensionForce1]; + this.dataDoc.startPosY2 = (this.yMax + this.yMin) / 2; + this.dataDoc.startPosX2 = (this.xMin + this.xMax) / 2 + 5; + this.dataDoc.positionYDisplay2 = this.getDisplayYPos((this.yMax + this.yMin) / 2); + this.dataDoc.positionXDisplay2 = (this.xMin + this.xMax) / 2 + 5; + this.dataDoc.updatedForces2 = [gravityForce2, tensionForce2]; + this.dataDoc.startForces2 = [gravityForce2, tensionForce2]; - // Handle force change in review mode - updateReviewModeValues = () => { - const forceOfGravityReview: IForce = { - description: "Gravity", - magnitude: this.dataDoc.reviewGravityMagnitude, - directionInDegrees: this.dataDoc.reviewGravityAngle, - component: false, + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; }; - const normalForceReview: IForce = { - description: "Normal Force", - magnitude: this.dataDoc.reviewNormalMagnitude, - directionInDegrees: this.dataDoc.reviewNormalAngle, - component: false, + + // Helper function used for tutorial and review mode + getForceFromJSON = ( + json: { + description: string; + magnitude: number; + directionInDegrees: number; + component: boolean; + }[] + ): IForce[] => { + const forces: IForce[] = []; + for (let i = 0; i < json.length; i++) { + const force: IForce = { + description: json[i].description, + magnitude: json[i].magnitude, + directionInDegrees: json[i].directionInDegrees, + component: json[i].component, + }; + forces.push(force); + } + return forces; }; - const staticFrictionForceReview: IForce = { - description: "Static Friction Force", - magnitude: this.dataDoc.reviewStaticMagnitude, - directionInDegrees: this.dataDoc.reviewStaticAngle, - component: false, + + // Handle force change in review mode + updateReviewModeValues = () => { + const forceOfGravityReview: IForce = { + description: 'Gravity', + magnitude: this.dataDoc.reviewGravityMagnitude, + directionInDegrees: this.dataDoc.reviewGravityAngle, + component: false, + }; + const normalForceReview: IForce = { + description: 'Normal Force', + magnitude: this.dataDoc.reviewNormalMagnitude, + directionInDegrees: this.dataDoc.reviewNormalAngle, + component: false, + }; + const staticFrictionForceReview: IForce = { + description: 'Static Friction Force', + magnitude: this.dataDoc.reviewStaticMagnitude, + directionInDegrees: this.dataDoc.reviewStaticAngle, + component: false, + }; + this.dataDoc.startForces = [forceOfGravityReview, normalForceReview, staticFrictionForceReview]; + this.dataDoc.updatedForces = [forceOfGravityReview, normalForceReview, staticFrictionForceReview]; }; - this.dataDoc.startForces = ([ - forceOfGravityReview, - normalForceReview, - staticFrictionForceReview, - ]); - this.dataDoc.updatedForces = ([ - forceOfGravityReview, - normalForceReview, - staticFrictionForceReview, - ]); - } - render () { - return ( - <div className="physicsSimApp"> - <div className="mechanicsSimulationContainer"> - <div - className="mechanicsSimulationContentContainer" - > - <div className="mechanicsSimulationButtonsAndElements"> - <div className="mechanicsSimulationButtons"> - {!this.dataDoc.simulationPaused && ( - <div - style={{ - position: "fixed", - left: 0.10*this.layoutDoc._width + "px", - top: 0.95*this.layoutDoc._height + "px", - width: 0.50*this.layoutDoc._width + "px", - }} - > - <LinearProgress /> - </div> - )} - </div> - <div className="mechanicsSimulationElements"> - <Weight - dataDoc={this.dataDoc} - layoutDoc={this.layoutDoc} - wallPositions={this.wallPositions} - adjustPendulumAngle={this.dataDoc.adjustPendulumAngle} - gravity={this.dataDoc.gravity} - circularMotionRadius={this.dataDoc.circularMotionRadius} - componentForces={this.dataDoc.componentForces} - showComponentForces={this.dataDoc.showComponentForces} - color={"red"} - coefficientOfKineticFriction={Number( - this.dataDoc.coefficientOfKineticFriction - )} - displayXVelocity={this.dataDoc.velocityXDisplay} - displayYVelocity={this.dataDoc.velocityYDisplay} - elasticCollisions={this.dataDoc.elasticCollisions} - mass={this.dataDoc.mass} - mode={this.dataDoc.mode} - noMovement={this.dataDoc.noMovement} - paused={this.dataDoc.simulationPaused} - pendulumAngle={this.dataDoc.pendulumAngle} - pendulumLength={this.dataDoc.pendulumLength} - radius={(0.08*this.layoutDoc._height)} - reset={this.dataDoc.simulationReset} - simulationSpeed={this.dataDoc.simulationSpeed} - startPendulumAngle={this.dataDoc.startPendulumAngle} - showAcceleration={this.dataDoc.showAcceleration} - showForceMagnitudes={this.dataDoc.showForceMagnitudes} - showForces={this.dataDoc.showForces} - showVelocity={this.dataDoc.showVelocity} - simulationType={this.dataDoc.simulationType} - springConstant={this.dataDoc.springConstant} - springStartLength={this.dataDoc.springStartLength} - springRestLength={this.dataDoc.springRestLength} - startForces={this.dataDoc.startForces} - startPosX={this.dataDoc.startPosX} - startPosY={this.dataDoc.startPosY ?? 0} - startVelX={this.dataDoc.startVelX} - startVelY={this.dataDoc.startVelY} - timestepSize={0.05} - updateDisplay={this.dataDoc.displayChange} - updatedForces={this.dataDoc.updatedForces} - wedgeHeight={this.dataDoc.wedgeHeight} - wedgeWidth={this.dataDoc.wedgeWidth} - xMax={this.xMax} - xMin={this.xMin} - yMax={this.yMax} - yMin={this.yMin} - /> - {this.dataDoc.simulationType == "Pulley" && ( - <Weight - dataDoc={this.dataDoc} - layoutDoc={this.layoutDoc} - wallPositions={this.wallPositions} - adjustPendulumAngle={this.dataDoc.adjustPendulumAngle} - circularMotionRadius={this.dataDoc.circularMotionRadius} - gravity={this.dataDoc.gravity} - componentForces={this.dataDoc.componentForces} - showComponentForces={this.dataDoc.showComponentForces} - color={"blue"} - coefficientOfKineticFriction={Number( - this.dataDoc.coefficientOfKineticFriction - )} - displayXVelocity={this.dataDoc.velocityXDisplay2} - displayYVelocity={this.dataDoc.velocityYDisplay2} - elasticCollisions={this.dataDoc.elasticCollisions} - mass={this.dataDoc.mass2} - mode={this.dataDoc.mode} - noMovement={this.dataDoc.noMovement} - paused={this.dataDoc.simulationPaused} - pendulumAngle={this.dataDoc.pendulumAngle} - pendulumLength={this.dataDoc.pendulumLength} - radius={(0.08*this.layoutDoc._height)} - reset={this.dataDoc.simulationReset} - simulationSpeed={this.dataDoc.simulationSpeed} - startPendulumAngle={this.dataDoc.startPendulumAngle} - showAcceleration={this.dataDoc.showAcceleration} - showForceMagnitudes={this.dataDoc.showForceMagnitudes} - showForces={this.dataDoc.showForces} - showVelocity={this.dataDoc.showVelocity} - simulationType={this.dataDoc.simulationType} - springConstant={this.dataDoc.springConstant} - springStartLength={this.dataDoc.springStartLength} - springRestLength={this.dataDoc.springRestLength} - startForces={this.dataDoc.startForces2} - startPosX={this.dataDoc.startPosX2} - startPosY={this.dataDoc.startPosY2} - startVelX={this.dataDoc.startVelX} - startVelY={this.dataDoc.startVelY} - timestepSize={0.05} - updateDisplay={this.dataDoc.displayChange2} - updatedForces={this.dataDoc.updatedForces2} - wedgeHeight={this.dataDoc.wedgeHeight} - wedgeWidth={this.dataDoc.wedgeWidth} - xMax={this.xMax} - xMin={this.xMin} - yMax={this.yMax} - yMin={this.yMin} - /> - )} - </div> - <div> - {(this.dataDoc.simulationType == "One Weight" || - this.dataDoc.simulationType == "Inclined Plane") && this.wallPositions && - this.wallPositions.map((element, index) => { - return ( - <Wall - key={index} - length={element.length} - xPos={element.xPos} - yPos={element.yPos} - angleInDegrees={element.angleInDegrees} - /> - ); - })} - </div> - </div> - </div> - <div className="mechanicsSimulationEquationContainer"> - <div className="mechanicsSimulationControls"> - <Stack direction="row" spacing={1}> - {this.dataDoc.simulationPaused && this.dataDoc.mode != "Tutorial" && ( - <IconButton - onClick={() => { - this.dataDoc.simulationPaused = (false); - }} - > - <PlayArrowIcon /> - </IconButton> - )} - {!this.dataDoc.simulationPaused && this.dataDoc.mode != "Tutorial" && ( - <IconButton - onClick={() => { - this.dataDoc.simulationPaused = (true); - }} - > - <PauseIcon /> - </IconButton> - )} - {this.dataDoc.simulationPaused && this.dataDoc.mode != "Tutorial" && ( - <IconButton - onClick={() => { - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }} - > - <ReplayIcon /> - </IconButton> - )} - </Stack> - <div className="dropdownMenu"> - <select - value={this.dataDoc.simulationType} - onChange={(event) => { - this.dataDoc.simulationType = (event.target.value); - this.setupSimulation(event.target.value, this.dataDoc.mode) - }} - style={{ height: "2em", width: "100%", fontSize: "16px" }} - > - <option value="One Weight">Projectile</option> - <option value="Inclined Plane">Inclined Plane</option> - <option value="Pendulum">Pendulum</option> - <option value="Spring">Spring</option> - <option value="Circular Motion">Circular Motion</option> - <option value="Pulley">Pulley</option> - <option value="Suspension">Suspension</option> - </select> - </div> - <div className="dropdownMenu"> - <select - value={this.dataDoc.mode} - onChange={(event) => { - this.dataDoc.mode = (event.target.value); - this.setupSimulation(this.dataDoc.simulationType, event.target.value) - }} - style={{ height: "2em", width: "100%", fontSize: "16px" }} - > - <option value="Tutorial">Tutorial Mode</option> - <option value="Freeform">Freeform Mode</option> - <option value="Review">Review Mode</option> - </select> - </div> - </div> - {this.dataDoc.mode == "Review" && this.dataDoc.simulationType != "Inclined Plane" && ( - <div className="wordProblemBox"> - <p>{this.dataDoc.simulationType} review problems in progress!</p> - <hr/> - </div> - )} - {this.dataDoc.mode == "Review" && this.dataDoc.simulationType == "Inclined Plane" && ( - <div> - {!this.dataDoc.hintDialogueOpen && ( - <IconButton - onClick={() => { - this.dataDoc.hintDialogueOpen = (true); - }} - sx={{ - position: "fixed", - left: this.xMax - 50 + "px", - top: this.yMin + 14 + "px", - }} - > - <QuestionMarkIcon /> - </IconButton> - )} - <Dialog - maxWidth={"sm"} - fullWidth={true} - open={this.dataDoc.hintDialogueOpen} - onClose={() => this.dataDoc.hintDialogueOpen = (false)} - > - <DialogTitle>Hints</DialogTitle> - <DialogContent> - {this.dataDoc.selectedQuestion.hints && (this.dataDoc.selectedQuestion.hints.map((hint, index) => { - return ( - <div key={index}> - <DialogContentText> - <details> - <summary> - <b> - Hint {index + 1}: {hint.description} - </b> - </summary> - {hint.content} - </details> - </DialogContentText> - </div> - ); - }))} - </DialogContent> - <DialogActions> - <Button - onClick={() => { - this.dataDoc.hintDialogueOpen = (false); - }} - > - Close - </Button> - </DialogActions> - </Dialog> - <div className="wordProblemBox"> - <div className="question"> - <p>{this.dataDoc.questionPartOne}</p> - <p>{this.dataDoc.questionPartTwo}</p> - </div> - <div className="answers"> - {this.dataDoc.selectedQuestion.answerParts.includes("force of gravity") && ( - <InputField - label={<p>Gravity magnitude</p>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'reviewGravityMagnitude'} - step={0.1} - unit={"N"} - upperBound={50} - value={this.dataDoc.reviewGravityMagnitude} - showIcon={this.dataDoc.showIcon} - correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf("force of gravity")]} - labelWidth={"7em"} - /> - )} - {this.dataDoc.selectedQuestion.answerParts.includes("angle of gravity") && ( - <InputField - label={<p>Gravity angle</p>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'reviewGravityAngle'} - step={1} - unit={"°"} - upperBound={360} - value={this.dataDoc.reviewGravityAngle} - radianEquivalent={true} - showIcon={this.dataDoc.showIcon} - correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf("angle of gravity")]} - labelWidth={"7em"} - /> - )} - {this.dataDoc.selectedQuestion.answerParts.includes("normal force") && ( - <InputField - label={<p>Normal force magnitude</p>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'reviewNormalMagnitude'} - step={0.1} - unit={"N"} - upperBound={50} - value={this.dataDoc.reviewNormalMagnitude} - showIcon={this.dataDoc.showIcon} - correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf("normal force")]} - labelWidth={"7em"} - /> - )} - {this.dataDoc.selectedQuestion.answerParts.includes("angle of normal force") && ( - <InputField - label={<p>Normal force angle</p>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'reviewNormalAngle'} - step={1} - unit={"°"} - upperBound={360} - value={this.dataDoc.reviewNormalAngle} - radianEquivalent={true} - showIcon={this.dataDoc.showIcon} - correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf("angle of normal force")]} - labelWidth={"7em"} - /> - )} - {this.dataDoc.selectedQuestion.answerParts.includes("force of static friction") && ( - <InputField - label={<p>Static friction magnitude</p>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'reviewStaticMagnitude'} - step={0.1} - unit={"N"} - upperBound={50} - value={this.dataDoc.reviewStaticMagnitude} - showIcon={this.dataDoc.showIcon} - correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf("force of static friction")]} - labelWidth={"7em"} - /> - )} - {this.dataDoc.selectedQuestion.answerParts.includes("angle of static friction") && ( - <InputField - label={<p>Static friction angle</p>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'reviewStaticAngle'} - step={1} - unit={"°"} - upperBound={360} - value={this.dataDoc.reviewStaticAngle} - radianEquivalent={true} - showIcon={this.dataDoc.showIcon} - correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf("angle of static friction")]} - labelWidth={"7em"} - /> - )} - {this.dataDoc.selectedQuestion.answerParts.includes("coefficient of static friction") && ( - <InputField - label={ - <Box> - μ<sub>s</sub> - </Box> - } - lowerBound={0} - dataDoc={this.dataDoc} - prop={'coefficientOfStaticFriction'} - step={0.1} - unit={""} - upperBound={1} - value={this.dataDoc.coefficientOfStaticFriction} - effect={this.updateReviewForcesBasedOnCoefficient} - showIcon={this.dataDoc.showIcon} - correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf("coefficient of static friction")]} - /> - )} - {this.dataDoc.selectedQuestion.answerParts.includes("wedge angle") && ( - <InputField - label={<Box>θ</Box>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'wedgeAngle'} - step={1} - unit={"°"} - upperBound={49} - value={this.dataDoc.wedgeAngle ?? 26} - effect={(val: number) => { - this.changeWedgeBasedOnNewAngle(val); - this.updateReviewForcesBasedOnAngle(val); - }} - radianEquivalent={true} - showIcon={this.dataDoc.showIcon} - correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf("wedge angle")]} - /> - )} - </div> - </div> - </div> - )} - {this.dataDoc.mode == "Tutorial" && ( - <div className="wordProblemBox"> - <div className="question"> - <h2>Problem</h2> - <p>{this.dataDoc.selectedTutorial.question}</p> - </div> - <div - style={{ - display: "flex", - justifyContent: "spaceBetween", - width: "100%", - }} - > - <IconButton - onClick={() => { - let step = this.dataDoc.stepNumber - 1; - step = Math.max(step, 0); - step = Math.min(step, this.dataDoc.selectedTutorial.steps.length - 1); - this.dataDoc.stepNumber = (step); - this.dataDoc.startForces = ( - this.getForceFromJSON(this.dataDoc.selectedTutorial.steps[step].forces) - ); - this.dataDoc.updatedForces = ( - this.getForceFromJSON(this.dataDoc.selectedTutorial.steps[step].forces) - ); - this.dataDoc.showForceMagnitudes = ( - this.dataDoc.selectedTutorial.steps[step].showMagnitude - ); - }} - disabled={this.dataDoc.stepNumber == 0} - > - <ArrowLeftIcon /> - </IconButton> - <div> - <h3> - Step {this.dataDoc.stepNumber + 1}:{" "} - {this.dataDoc.selectedTutorial.steps[this.dataDoc.stepNumber].description} - </h3> - <p>{this.dataDoc.selectedTutorial.steps[this.dataDoc.stepNumber].content}</p> - </div> - <IconButton - onClick={() => { - let step = this.dataDoc.stepNumber + 1; - step = Math.max(step, 0); - step = Math.min(step, this.dataDoc.selectedTutorial.steps.length - 1); - this.dataDoc.stepNumber = (step); - this.dataDoc.startForces = ( - this.getForceFromJSON(this.dataDoc.selectedTutorial.steps[step].forces) - ); - this.dataDoc.updatedForces = ( - this.getForceFromJSON(this.dataDoc.selectedTutorial.steps[step].forces) - ); - this.dataDoc.showForceMagnitudes = ( - this.dataDoc.selectedTutorial.steps[step].showMagnitude - ); - }} - disabled={this.dataDoc.stepNumber == this.dataDoc.selectedTutorial.steps.length - 1} - > - <ArrowRightIcon /> - </IconButton> - </div> - <div> - {(this.dataDoc.simulationType == "One Weight" || - this.dataDoc.simulationType == "Inclined Plane" || - this.dataDoc.simulationType == "Pendulum") && <p>Resources</p>} - {this.dataDoc.simulationType == "One Weight" && ( - <ul> - <li> - <a - href="https://www.khanacademy.org/science/physics/one-dimensional-motion" - target="_blank" - rel="noreferrer" - style={{ - color: "blue", - textDecoration: "underline", - }} - > - Khan Academy - One Dimensional Motion - </a> - </li> - <li> - <a - href="https://www.khanacademy.org/science/physics/two-dimensional-motion" - target="_blank" - rel="noreferrer" - style={{ - color: "blue", - textDecoration: "underline", - }} - > - Khan Academy - Two Dimensional Motion - </a> - </li> - </ul> - )} - {this.dataDoc.simulationType == "Inclined Plane" && ( - <ul> - <li> - <a - href="https://www.khanacademy.org/science/physics/forces-newtons-laws#normal-contact-force" - target="_blank" - rel="noreferrer" - style={{ - color: "blue", - textDecoration: "underline", - }} - > - Khan Academy - Normal Force - </a> - </li> - <li> - <a - href="https://www.khanacademy.org/science/physics/forces-newtons-laws#inclined-planes-friction" - target="_blank" - rel="noreferrer" - style={{ - color: "blue", - textDecoration: "underline", - }} - > - Khan Academy - Inclined Planes - </a> - </li> - </ul> - )} - {this.dataDoc.simulationType == "Pendulum" && ( - <ul> - <li> - <a - href="https://www.khanacademy.org/science/physics/forces-newtons-laws#tension-tutorial" - target="_blank" - rel="noreferrer" - style={{ - color: "blue", - textDecoration: "underline", - }} - > - Khan Academy - Tension - </a> - </li> - </ul> - )} - </div> - </div> - )} - {this.dataDoc.mode == "Review" && this.dataDoc.simulationType == "Inclined Plane" && ( - <div - style={{ - display: "flex", - justifyContent: "space-between", - marginTop: "10px", - }} - > - <p - style={{ - color: "blue", - textDecoration: "underline", - cursor: "pointer", - }} - onClick={() => this.dataDoc.mode = ("Tutorial")} - > - {" "} - Go to walkthrough{" "} - </p> - <div style={{ display: "flex", flexDirection: "column" }}> - <Button - onClick={() => { - this.dataDoc.simulationReset= (!this.dataDoc.simulationReset); - this.checkAnswers(); - this.dataDoc.showIcon = true; - }} - variant="outlined" - > - <p>Submit</p> - </Button> - <Button - onClick={() => { - this.generateNewQuestion(); - this.dataDoc.showIcon = false; - }} - variant="outlined" - > - <p>New question</p> - </Button> - </div> - </div> - )} - {this.dataDoc.mode == "Freeform" && ( - <div className="vars"> - <FormControl component="fieldset"> - <FormGroup> - {this.dataDoc.simulationType == "One Weight" && ( - <FormControlLabel - control={ - <Checkbox - checked={this.dataDoc.elasticCollisions} - onChange={() => - this.dataDoc.elasticCollisions= (!this.dataDoc.elasticCollisions) - } - /> - } - label="Make collisions elastic" - labelPlacement="start" - /> - )} - <FormControlLabel - control={ - <Checkbox - checked={this.dataDoc.showForces} - onChange={() => this.dataDoc.showForces = (!this.dataDoc.showForces)} - /> - } - label="Show force vectors" - labelPlacement="start" - /> - {(this.dataDoc.simulationType == "Inclined Plane" || - this.dataDoc.simulationType == "Pendulum") && ( - <FormControlLabel - control={ - <Checkbox - checked={this.dataDoc.showForces} - onChange={() => - this.dataDoc.showComponentForces = (!this.dataDoc.showComponentForces) - } - /> - } - label="Show component force vectors" - labelPlacement="start" - /> - )} - <FormControlLabel - control={ - <Checkbox - checked={this.dataDoc.showAcceleration} - onChange={() => this.dataDoc.showAcceleration = (!this.dataDoc.showAcceleration)} - /> - } - label="Show acceleration vector" - labelPlacement="start" - /> - <FormControlLabel - control={ - <Checkbox - checked={this.dataDoc.showVelocity} - onChange={() => this.dataDoc.showVelocity = (!this.dataDoc.showVelocity)} - /> - } - label="Show velocity vector" - labelPlacement="start" - /> - <InputField - label={<Box>Speed</Box>} - lowerBound={1} - dataDoc={this.dataDoc} - prop={'simulationSpeed'} - step={1} - unit={"x"} - upperBound={10} - value={this.dataDoc.simulationSpeed ?? 2} - labelWidth={"5em"} - /> - {this.dataDoc.simulationPaused && this.dataDoc.simulationType != "Circular Motion" && ( - <InputField - label={<Box>Gravity</Box>} - lowerBound={-30} - dataDoc={this.dataDoc} - prop={'gravity'} - step={0.01} - unit={"m/s2"} - upperBound={0} - value={this.dataDoc.gravity ?? -9.81} - effect={(val: number) => { - this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode) - }} - labelWidth={"5em"} - /> - )} - {this.dataDoc.simulationPaused && this.dataDoc.simulationType != "Pulley" && ( - <InputField - label={<Box>Mass</Box>} - lowerBound={1} - dataDoc={this.dataDoc} - prop={'mass'} - step={0.1} - unit={"kg"} - upperBound={5} - value={this.dataDoc.mass ?? 1} - effect={(val: number) => { - this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode) - }} - labelWidth={"5em"} - /> - )} - {this.dataDoc.simulationPaused && this.dataDoc.simulationType == "Pulley" && ( - <InputField - label={<Box>Red mass</Box>} - lowerBound={1} - dataDoc={this.dataDoc} - prop={'mass'} - step={0.1} - unit={"kg"} - upperBound={5} - value={this.dataDoc.mass ?? 1} - effect={(val: number) => { - this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode) - }} - labelWidth={"5em"} - /> - )} - {this.dataDoc.simulationPaused && this.dataDoc.simulationType == "Pulley" && ( - <InputField - label={<Box>Blue mass</Box>} - lowerBound={1} - dataDoc={this.dataDoc} - prop={'mass2'} - step={0.1} - unit={"kg"} - upperBound={5} - value={this.dataDoc.mass2 ?? 1} - effect={(val: number) => { - this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode) - }} - labelWidth={"5em"} - /> - )} - {this.dataDoc.simulationPaused && this.dataDoc.simulationType == "Circular Motion" && ( - <InputField - label={<Box>Rod length</Box>} - lowerBound={100} - dataDoc={this.dataDoc} - prop={'circularMotionRadius'} - step={5} - unit={"m"} - upperBound={250} - value={this.dataDoc.circularMotionRadius ?? 100} - effect={(val: number) => { - this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode) - }} - labelWidth={"5em"} - /> - )} - </FormGroup> - </FormControl> - {this.dataDoc.simulationType == "Spring" && this.dataDoc.simulationPaused && ( - <div> - <InputField - label={ - <Typography color="inherit">Spring stiffness</Typography> - } - lowerBound={0.1} - dataDoc={this.dataDoc} - prop={'springConstant'} - step={1} - unit={"N/m"} - upperBound={500} - value={this.dataDoc.springConstant ?? 0.5} - effect={(val: number) => { - this.dataDoc.simulationReset(!this.dataDoc.simulationReset); - }} - radianEquivalent={false} - mode={"Freeform"} - labelWidth={"7em"} - /> - <InputField - label={<Typography color="inherit">Rest length</Typography>} - lowerBound={10} - dataDoc={this.dataDoc} - prop={'springRestLength'} - step={100} - unit={""} - upperBound={500} - value={this.dataDoc.springRestLength ?? 200} - effect={(val: number) => { - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }} - radianEquivalent={false} - mode={"Freeform"} - labelWidth={"7em"} - /> - <InputField - label={ - <Typography color="inherit"> - Starting displacement - </Typography> - } - lowerBound={-(this.dataDoc.springRestLength - 10)} - dataDoc={this.dataDoc} - prop={""} - step={10} - unit={""} - upperBound={this.dataDoc.springRestLength} - value={this.dataDoc.springStartLength - this.dataDoc.springRestLength ?? 0} - effect={(val: number) => { - this.dataDoc.startPosY = (this.dataDoc.springRestLength + val); - this.dataDoc.springStartLength = (this.dataDoc.springRestLength + val); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }} - radianEquivalent={false} - mode={"Freeform"} - labelWidth={"7em"} - /> - </div> - )} - {this.dataDoc.simulationType == "Inclined Plane" && this.dataDoc.simulationPaused && ( - <div> - <InputField - label={<Box>θ</Box>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'wedgeAngle'} - step={1} - unit={"°"} - upperBound={49} - value={this.dataDoc.wedgeAngle ?? 26} - effect={(val: number) => { - this.changeWedgeBasedOnNewAngle(val); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }} - radianEquivalent={true} - mode={"Freeform"} - labelWidth={"2em"} - /> - <InputField - label={ - <Box> - μ<sub>s</sub> - </Box> - } - lowerBound={0} - dataDoc={this.dataDoc} - prop={'coefficientOfStaticFriction'} - step={0.1} - unit={""} - upperBound={1} - value={this.dataDoc.coefficientOfStaticFriction ?? 0} - effect={(val: number) => { - this.updateForcesWithFriction(val); - if (val < Number(this.dataDoc.coefficientOfKineticFriction)) { - this.dataDoc.soefficientOfKineticFriction = (val); - } - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }} - mode={"Freeform"} - labelWidth={"2em"} - /> - <InputField - label={ - <Box> - μ<sub>k</sub> - </Box> - } - lowerBound={0} - dataDoc={this.dataDoc} - prop={'coefficientOfKineticFriction'} - step={0.1} - unit={""} - upperBound={Number(this.dataDoc.coefficientOfStaticFriction)} - value={this.dataDoc.coefficientOfKineticFriction ?? 0} - effect={(val: number) => { - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }} - mode={"Freeform"} - labelWidth={"2em"} - /> - </div> - )} - {this.dataDoc.simulationType == "Inclined Plane" && !this.dataDoc.simulationPaused && ( - <Typography> - θ: {Math.round(Number(this.dataDoc.wedgeAngle) * 100) / 100}° ≈{" "} - {Math.round(((Number(this.dataDoc.wedgeAngle) * Math.PI) / 180) * 100) / - 100}{" "} - rad - <br /> - μ <sub>s</sub>: {this.dataDoc.coefficientOfStaticFriction} - <br /> - μ <sub>k</sub>: {this.dataDoc.coefficientOfKineticFriction} - </Typography> - )} - {this.dataDoc.simulationType == "Pendulum" && !this.dataDoc.simulationPaused && ( - <Typography> - θ: {Math.round(this.dataDoc.pendulumAngle * 100) / 100}° ≈{" "} - {Math.round(((this.dataDoc.pendulumAngle * Math.PI) / 180) * 100) / 100}{" "} - rad - </Typography> - )} - {this.dataDoc.simulationType == "Pendulum" && this.dataDoc.simulationPaused && ( - <div> - <InputField - label={<Box>Angle</Box>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'pendulumAngle'} - step={1} - unit={"°"} - upperBound={59} - value={this.dataDoc.pendulumAngle ?? 30} - effect={(value) => { - this.dataDoc.startPendulumAngle = (value); - if (this.dataDoc.simulationType == "Pendulum") { - const mag = - this.dataDoc.mass * - Math.abs(this.dataDoc.gravity) * - Math.cos((value * Math.PI) / 180); + render() { + return ( + <div className="physicsSimApp"> + <div className="mechanicsSimulationContainer"> + <div className="mechanicsSimulationContentContainer"> + <div className="mechanicsSimulationButtonsAndElements"> + <div className="mechanicsSimulationButtons"> + {!this.dataDoc.simulationPaused && ( + <div + style={{ + position: 'fixed', + left: 0.1 * this.layoutDoc[WidthSym]() + 'px', + top: 0.95 * this.layoutDoc[HeightSym]() + 'px', + width: 0.5 * this.layoutDoc[WidthSym]() + 'px', + }}> + <LinearProgress /> + </div> + )} + </div> + <div className="mechanicsSimulationElements"> + <Weight + dataDoc={this.dataDoc} + layoutDoc={this.layoutDoc} + wallPositions={this.wallPositions} + adjustPendulumAngle={this.dataDoc.adjustPendulumAngle} + gravity={this.dataDoc.gravity} + circularMotionRadius={this.dataDoc.circularMotionRadius} + componentForces={this.dataDoc.componentForces} + showComponentForces={this.dataDoc.showComponentForces} + color={'red'} + coefficientOfKineticFriction={Number(this.dataDoc.coefficientOfKineticFriction)} + displayXVelocity={this.dataDoc.velocityXDisplay} + displayYVelocity={this.dataDoc.velocityYDisplay} + elasticCollisions={this.dataDoc.elasticCollisions} + mass={this.dataDoc.mass} + mode={this.dataDoc.mode} + noMovement={this.dataDoc.noMovement} + paused={this.dataDoc.simulationPaused} + pendulumAngle={this.dataDoc.pendulumAngle} + pendulumLength={this.dataDoc.pendulumLength} + radius={0.08 * this.layoutDoc[HeightSym]()} + reset={this.dataDoc.simulationReset} + simulationSpeed={this.dataDoc.simulationSpeed} + startPendulumAngle={this.dataDoc.startPendulumAngle} + showAcceleration={this.dataDoc.showAcceleration} + showForceMagnitudes={this.dataDoc.showForceMagnitudes} + showForces={this.dataDoc.showForces} + showVelocity={this.dataDoc.showVelocity} + simulationType={this.dataDoc.simulationType} + springConstant={this.dataDoc.springConstant} + springStartLength={this.dataDoc.springStartLength} + springRestLength={this.dataDoc.springRestLength} + startForces={this.dataDoc.startForces} + startPosX={this.dataDoc.startPosX} + startPosY={this.dataDoc.startPosY ?? 0} + startVelX={this.dataDoc.startVelX} + startVelY={this.dataDoc.startVelY} + timestepSize={0.05} + updateDisplay={this.dataDoc.displayChange} + updatedForces={this.dataDoc.updatedForces} + wedgeHeight={this.dataDoc.wedgeHeight} + wedgeWidth={this.dataDoc.wedgeWidth} + xMax={this.xMax} + xMin={this.xMin} + yMax={this.yMax} + yMin={this.yMin} + /> + {this.dataDoc.simulationType == 'Pulley' && ( + <Weight + dataDoc={this.dataDoc} + layoutDoc={this.layoutDoc} + wallPositions={this.wallPositions} + adjustPendulumAngle={this.dataDoc.adjustPendulumAngle} + circularMotionRadius={this.dataDoc.circularMotionRadius} + gravity={this.dataDoc.gravity} + componentForces={this.dataDoc.componentForces} + showComponentForces={this.dataDoc.showComponentForces} + color={'blue'} + coefficientOfKineticFriction={Number(this.dataDoc.coefficientOfKineticFriction)} + displayXVelocity={this.dataDoc.velocityXDisplay2} + displayYVelocity={this.dataDoc.velocityYDisplay2} + elasticCollisions={this.dataDoc.elasticCollisions} + mass={this.dataDoc.mass2} + mode={this.dataDoc.mode} + noMovement={this.dataDoc.noMovement} + paused={this.dataDoc.simulationPaused} + pendulumAngle={this.dataDoc.pendulumAngle} + pendulumLength={this.dataDoc.pendulumLength} + radius={0.08 * this.layoutDoc[HeightSym]()} + reset={this.dataDoc.simulationReset} + simulationSpeed={this.dataDoc.simulationSpeed} + startPendulumAngle={this.dataDoc.startPendulumAngle} + showAcceleration={this.dataDoc.showAcceleration} + showForceMagnitudes={this.dataDoc.showForceMagnitudes} + showForces={this.dataDoc.showForces} + showVelocity={this.dataDoc.showVelocity} + simulationType={this.dataDoc.simulationType} + springConstant={this.dataDoc.springConstant} + springStartLength={this.dataDoc.springStartLength} + springRestLength={this.dataDoc.springRestLength} + startForces={this.dataDoc.startForces2} + startPosX={this.dataDoc.startPosX2} + startPosY={this.dataDoc.startPosY2} + startVelX={this.dataDoc.startVelX} + startVelY={this.dataDoc.startVelY} + timestepSize={0.05} + updateDisplay={this.dataDoc.displayChange2} + updatedForces={this.dataDoc.updatedForces2} + wedgeHeight={this.dataDoc.wedgeHeight} + wedgeWidth={this.dataDoc.wedgeWidth} + xMax={this.xMax} + xMin={this.xMin} + yMax={this.yMax} + yMin={this.yMin} + /> + )} + </div> + <div> + {(this.dataDoc.simulationType == 'One Weight' || this.dataDoc.simulationType == 'Inclined Plane') && + this.wallPositions && + this.wallPositions.map((element, index) => { + return <Wall key={index} length={element.length} xPos={element.xPos} yPos={element.yPos} angleInDegrees={element.angleInDegrees} />; + })} + </div> + </div> + </div> + <div className="mechanicsSimulationEquationContainer"> + <div className="mechanicsSimulationControls"> + <Stack direction="row" spacing={1}> + {this.dataDoc.simulationPaused && this.dataDoc.mode != 'Tutorial' && ( + <IconButton + onClick={() => { + this.dataDoc.simulationPaused = false; + }}> + <PlayArrowIcon /> + </IconButton> + )} + {!this.dataDoc.simulationPaused && this.dataDoc.mode != 'Tutorial' && ( + <IconButton + onClick={() => { + this.dataDoc.simulationPaused = true; + }}> + <PauseIcon /> + </IconButton> + )} + {this.dataDoc.simulationPaused && this.dataDoc.mode != 'Tutorial' && ( + <IconButton + onClick={() => { + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + }}> + <ReplayIcon /> + </IconButton> + )} + </Stack> + <div className="dropdownMenu"> + <select + value={this.dataDoc.simulationType} + onChange={event => { + this.dataDoc.simulationType = event.target.value; + this.setupSimulation(event.target.value, this.dataDoc.mode); + }} + style={{ height: '2em', width: '100%', fontSize: '16px' }}> + <option value="One Weight">Projectile</option> + <option value="Inclined Plane">Inclined Plane</option> + <option value="Pendulum">Pendulum</option> + <option value="Spring">Spring</option> + <option value="Circular Motion">Circular Motion</option> + <option value="Pulley">Pulley</option> + <option value="Suspension">Suspension</option> + </select> + </div> + <div className="dropdownMenu"> + <select + value={this.dataDoc.mode} + onChange={event => { + this.dataDoc.mode = event.target.value; + this.setupSimulation(this.dataDoc.simulationType, event.target.value); + }} + style={{ height: '2em', width: '100%', fontSize: '16px' }}> + <option value="Tutorial">Tutorial Mode</option> + <option value="Freeform">Freeform Mode</option> + <option value="Review">Review Mode</option> + </select> + </div> + </div> + {this.dataDoc.mode == 'Review' && this.dataDoc.simulationType != 'Inclined Plane' && ( + <div className="wordProblemBox"> + <p>{this.dataDoc.simulationType} review problems in progress!</p> + <hr /> + </div> + )} + {this.dataDoc.mode == 'Review' && this.dataDoc.simulationType == 'Inclined Plane' && ( + <div> + {!this.dataDoc.hintDialogueOpen && ( + <IconButton + onClick={() => { + this.dataDoc.hintDialogueOpen = true; + }} + sx={{ + position: 'fixed', + left: this.xMax - 50 + 'px', + top: this.yMin + 14 + 'px', + }}> + <QuestionMarkIcon /> + </IconButton> + )} + <Dialog maxWidth={'sm'} fullWidth={true} open={this.dataDoc.hintDialogueOpen} onClose={() => (this.dataDoc.hintDialogueOpen = false)}> + <DialogTitle>Hints</DialogTitle> + <DialogContent> + {this.dataDoc.selectedQuestion.hints && + this.dataDoc.selectedQuestion.hints.map((hint: any, index: number) => { + return ( + <div key={index}> + <DialogContentText> + <details> + <summary> + <b> + Hint {index + 1}: {hint.description} + </b> + </summary> + {hint.content} + </details> + </DialogContentText> + </div> + ); + })} + </DialogContent> + <DialogActions> + <Button + onClick={() => { + this.dataDoc.hintDialogueOpen = false; + }}> + Close + </Button> + </DialogActions> + </Dialog> + <div className="wordProblemBox"> + <div className="question"> + <p>{this.dataDoc.questionPartOne}</p> + <p>{this.dataDoc.questionPartTwo}</p> + </div> + <div className="answers"> + {this.dataDoc.selectedQuestion.answerParts.includes('force of gravity') && ( + <InputField + label={<p>Gravity magnitude</p>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'reviewGravityMagnitude'} + step={0.1} + unit={'N'} + upperBound={50} + value={this.dataDoc.reviewGravityMagnitude} + showIcon={this.dataDoc.showIcon} + correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf('force of gravity')]} + labelWidth={'7em'} + /> + )} + {this.dataDoc.selectedQuestion.answerParts.includes('angle of gravity') && ( + <InputField + label={<p>Gravity angle</p>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'reviewGravityAngle'} + step={1} + unit={'°'} + upperBound={360} + value={this.dataDoc.reviewGravityAngle} + radianEquivalent={true} + showIcon={this.dataDoc.showIcon} + correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf('angle of gravity')]} + labelWidth={'7em'} + /> + )} + {this.dataDoc.selectedQuestion.answerParts.includes('normal force') && ( + <InputField + label={<p>Normal force magnitude</p>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'reviewNormalMagnitude'} + step={0.1} + unit={'N'} + upperBound={50} + value={this.dataDoc.reviewNormalMagnitude} + showIcon={this.dataDoc.showIcon} + correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf('normal force')]} + labelWidth={'7em'} + /> + )} + {this.dataDoc.selectedQuestion.answerParts.includes('angle of normal force') && ( + <InputField + label={<p>Normal force angle</p>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'reviewNormalAngle'} + step={1} + unit={'°'} + upperBound={360} + value={this.dataDoc.reviewNormalAngle} + radianEquivalent={true} + showIcon={this.dataDoc.showIcon} + correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf('angle of normal force')]} + labelWidth={'7em'} + /> + )} + {this.dataDoc.selectedQuestion.answerParts.includes('force of static friction') && ( + <InputField + label={<p>Static friction magnitude</p>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'reviewStaticMagnitude'} + step={0.1} + unit={'N'} + upperBound={50} + value={this.dataDoc.reviewStaticMagnitude} + showIcon={this.dataDoc.showIcon} + correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf('force of static friction')]} + labelWidth={'7em'} + /> + )} + {this.dataDoc.selectedQuestion.answerParts.includes('angle of static friction') && ( + <InputField + label={<p>Static friction angle</p>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'reviewStaticAngle'} + step={1} + unit={'°'} + upperBound={360} + value={this.dataDoc.reviewStaticAngle} + radianEquivalent={true} + showIcon={this.dataDoc.showIcon} + correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf('angle of static friction')]} + labelWidth={'7em'} + /> + )} + {this.dataDoc.selectedQuestion.answerParts.includes('coefficient of static friction') && ( + <InputField + label={ + <Box> + μ<sub>s</sub> + </Box> + } + lowerBound={0} + dataDoc={this.dataDoc} + prop={'coefficientOfStaticFriction'} + step={0.1} + unit={''} + upperBound={1} + value={this.dataDoc.coefficientOfStaticFriction} + effect={this.updateReviewForcesBasedOnCoefficient} + showIcon={this.dataDoc.showIcon} + correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf('coefficient of static friction')]} + /> + )} + {this.dataDoc.selectedQuestion.answerParts.includes('wedge angle') && ( + <InputField + label={<Box>θ</Box>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'wedgeAngle'} + step={1} + unit={'°'} + upperBound={49} + value={this.dataDoc.wedgeAngle ?? 26} + effect={(val: number) => { + this.changeWedgeBasedOnNewAngle(val); + this.updateReviewForcesBasedOnAngle(val); + }} + radianEquivalent={true} + showIcon={this.dataDoc.showIcon} + correctValue={this.dataDoc.answers[this.dataDoc.selectedQuestion.answerParts.indexOf('wedge angle')]} + /> + )} + </div> + </div> + </div> + )} + {this.dataDoc.mode == 'Tutorial' && ( + <div className="wordProblemBox"> + <div className="question"> + <h2>Problem</h2> + <p>{this.dataDoc.selectedTutorial.question}</p> + </div> + <div + style={{ + display: 'flex', + justifyContent: 'spaceBetween', + width: '100%', + }}> + <IconButton + onClick={() => { + let step = this.dataDoc.stepNumber - 1; + step = Math.max(step, 0); + step = Math.min(step, this.dataDoc.selectedTutorial.steps.length - 1); + this.dataDoc.stepNumber = step; + this.dataDoc.startForces = this.getForceFromJSON(this.dataDoc.selectedTutorial.steps[step].forces); + this.dataDoc.updatedForces = this.getForceFromJSON(this.dataDoc.selectedTutorial.steps[step].forces); + this.dataDoc.showForceMagnitudes = this.dataDoc.selectedTutorial.steps[step].showMagnitude; + }} + disabled={this.dataDoc.stepNumber == 0}> + <ArrowLeftIcon /> + </IconButton> + <div> + <h3> + Step {this.dataDoc.stepNumber + 1}: {this.dataDoc.selectedTutorial.steps[this.dataDoc.stepNumber].description} + </h3> + <p>{this.dataDoc.selectedTutorial.steps[this.dataDoc.stepNumber].content}</p> + </div> + <IconButton + onClick={() => { + let step = this.dataDoc.stepNumber + 1; + step = Math.max(step, 0); + step = Math.min(step, this.dataDoc.selectedTutorial.steps.length - 1); + this.dataDoc.stepNumber = step; + this.dataDoc.startForces = this.getForceFromJSON(this.dataDoc.selectedTutorial.steps[step].forces); + this.dataDoc.updatedForces = this.getForceFromJSON(this.dataDoc.selectedTutorial.steps[step].forces); + this.dataDoc.showForceMagnitudes = this.dataDoc.selectedTutorial.steps[step].showMagnitude; + }} + disabled={this.dataDoc.stepNumber == this.dataDoc.selectedTutorial.steps.length - 1}> + <ArrowRightIcon /> + </IconButton> + </div> + <div> + {(this.dataDoc.simulationType == 'One Weight' || this.dataDoc.simulationType == 'Inclined Plane' || this.dataDoc.simulationType == 'Pendulum') && <p>Resources</p>} + {this.dataDoc.simulationType == 'One Weight' && ( + <ul> + <li> + <a + href="https://www.khanacademy.org/science/physics/one-dimensional-motion" + target="_blank" + rel="noreferrer" + style={{ + color: 'blue', + textDecoration: 'underline', + }}> + Khan Academy - One Dimensional Motion + </a> + </li> + <li> + <a + href="https://www.khanacademy.org/science/physics/two-dimensional-motion" + target="_blank" + rel="noreferrer" + style={{ + color: 'blue', + textDecoration: 'underline', + }}> + Khan Academy - Two Dimensional Motion + </a> + </li> + </ul> + )} + {this.dataDoc.simulationType == 'Inclined Plane' && ( + <ul> + <li> + <a + href="https://www.khanacademy.org/science/physics/forces-newtons-laws#normal-contact-force" + target="_blank" + rel="noreferrer" + style={{ + color: 'blue', + textDecoration: 'underline', + }}> + Khan Academy - Normal Force + </a> + </li> + <li> + <a + href="https://www.khanacademy.org/science/physics/forces-newtons-laws#inclined-planes-friction" + target="_blank" + rel="noreferrer" + style={{ + color: 'blue', + textDecoration: 'underline', + }}> + Khan Academy - Inclined Planes + </a> + </li> + </ul> + )} + {this.dataDoc.simulationType == 'Pendulum' && ( + <ul> + <li> + <a + href="https://www.khanacademy.org/science/physics/forces-newtons-laws#tension-tutorial" + target="_blank" + rel="noreferrer" + style={{ + color: 'blue', + textDecoration: 'underline', + }}> + Khan Academy - Tension + </a> + </li> + </ul> + )} + </div> + </div> + )} + {this.dataDoc.mode == 'Review' && this.dataDoc.simulationType == 'Inclined Plane' && ( + <div + style={{ + display: 'flex', + justifyContent: 'space-between', + marginTop: '10px', + }}> + <p + style={{ + color: 'blue', + textDecoration: 'underline', + cursor: 'pointer', + }} + onClick={() => (this.dataDoc.mode = 'Tutorial')}> + {' '} + Go to walkthrough{' '} + </p> + <div style={{ display: 'flex', flexDirection: 'column' }}> + <Button + onClick={() => { + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + this.checkAnswers(); + this.dataDoc.showIcon = true; + }} + variant="outlined"> + <p>Submit</p> + </Button> + <Button + onClick={() => { + this.generateNewQuestion(); + this.dataDoc.showIcon = false; + }} + variant="outlined"> + <p>New question</p> + </Button> + </div> + </div> + )} + {this.dataDoc.mode == 'Freeform' && ( + <div className="vars"> + <FormControl component="fieldset"> + <FormGroup> + {this.dataDoc.simulationType == 'One Weight' && ( + <FormControlLabel + control={<Checkbox checked={this.dataDoc.elasticCollisions} onChange={() => (this.dataDoc.elasticCollisions = !this.dataDoc.elasticCollisions)} />} + label="Make collisions elastic" + labelPlacement="start" + /> + )} + <FormControlLabel control={<Checkbox checked={this.dataDoc.showForces} onChange={() => (this.dataDoc.showForces = !this.dataDoc.showForces)} />} label="Show force vectors" labelPlacement="start" /> + {(this.dataDoc.simulationType == 'Inclined Plane' || this.dataDoc.simulationType == 'Pendulum') && ( + <FormControlLabel + control={<Checkbox checked={this.dataDoc.showForces} onChange={() => (this.dataDoc.showComponentForces = !this.dataDoc.showComponentForces)} />} + label="Show component force vectors" + labelPlacement="start" + /> + )} + <FormControlLabel + control={<Checkbox checked={this.dataDoc.showAcceleration} onChange={() => (this.dataDoc.showAcceleration = !this.dataDoc.showAcceleration)} />} + label="Show acceleration vector" + labelPlacement="start" + /> + <FormControlLabel control={<Checkbox checked={this.dataDoc.showVelocity} onChange={() => (this.dataDoc.showVelocity = !this.dataDoc.showVelocity)} />} label="Show velocity vector" labelPlacement="start" /> + <InputField label={<Box>Speed</Box>} lowerBound={1} dataDoc={this.dataDoc} prop={'simulationSpeed'} step={1} unit={'x'} upperBound={10} value={this.dataDoc.simulationSpeed ?? 2} labelWidth={'5em'} /> + {this.dataDoc.simulationPaused && this.dataDoc.simulationType != 'Circular Motion' && ( + <InputField + label={<Box>Gravity</Box>} + lowerBound={-30} + dataDoc={this.dataDoc} + prop={'gravity'} + step={0.01} + unit={'m/s2'} + upperBound={0} + value={this.dataDoc.gravity ?? -9.81} + effect={(val: number) => { + this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode); + }} + labelWidth={'5em'} + /> + )} + {this.dataDoc.simulationPaused && this.dataDoc.simulationType != 'Pulley' && ( + <InputField + label={<Box>Mass</Box>} + lowerBound={1} + dataDoc={this.dataDoc} + prop={'mass'} + step={0.1} + unit={'kg'} + upperBound={5} + value={this.dataDoc.mass ?? 1} + effect={(val: number) => { + this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode); + }} + labelWidth={'5em'} + /> + )} + {this.dataDoc.simulationPaused && this.dataDoc.simulationType == 'Pulley' && ( + <InputField + label={<Box>Red mass</Box>} + lowerBound={1} + dataDoc={this.dataDoc} + prop={'mass'} + step={0.1} + unit={'kg'} + upperBound={5} + value={this.dataDoc.mass ?? 1} + effect={(val: number) => { + this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode); + }} + labelWidth={'5em'} + /> + )} + {this.dataDoc.simulationPaused && this.dataDoc.simulationType == 'Pulley' && ( + <InputField + label={<Box>Blue mass</Box>} + lowerBound={1} + dataDoc={this.dataDoc} + prop={'mass2'} + step={0.1} + unit={'kg'} + upperBound={5} + value={this.dataDoc.mass2 ?? 1} + effect={(val: number) => { + this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode); + }} + labelWidth={'5em'} + /> + )} + {this.dataDoc.simulationPaused && this.dataDoc.simulationType == 'Circular Motion' && ( + <InputField + label={<Box>Rod length</Box>} + lowerBound={100} + dataDoc={this.dataDoc} + prop={'circularMotionRadius'} + step={5} + unit={'m'} + upperBound={250} + value={this.dataDoc.circularMotionRadius ?? 100} + effect={(val: number) => { + this.setupSimulation(this.dataDoc.simulationType, this.dataDoc.mode); + }} + labelWidth={'5em'} + /> + )} + </FormGroup> + </FormControl> + {this.dataDoc.simulationType == 'Spring' && this.dataDoc.simulationPaused && ( + <div> + <InputField + label={<Typography color="inherit">Spring stiffness</Typography>} + lowerBound={0.1} + dataDoc={this.dataDoc} + prop={'springConstant'} + step={1} + unit={'N/m'} + upperBound={500} + value={this.dataDoc.springConstant ?? 0.5} + effect={(val: number) => { + this.dataDoc.simulationReset(!this.dataDoc.simulationReset); + }} + radianEquivalent={false} + mode={'Freeform'} + labelWidth={'7em'} + /> + <InputField + label={<Typography color="inherit">Rest length</Typography>} + lowerBound={10} + dataDoc={this.dataDoc} + prop={'springRestLength'} + step={100} + unit={''} + upperBound={500} + value={this.dataDoc.springRestLength ?? 200} + effect={(val: number) => { + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + }} + radianEquivalent={false} + mode={'Freeform'} + labelWidth={'7em'} + /> + <InputField + label={<Typography color="inherit">Starting displacement</Typography>} + lowerBound={-(this.dataDoc.springRestLength - 10)} + dataDoc={this.dataDoc} + prop={''} + step={10} + unit={''} + upperBound={this.dataDoc.springRestLength} + value={this.dataDoc.springStartLength - this.dataDoc.springRestLength ?? 0} + effect={(val: number) => { + this.dataDoc.startPosY = this.dataDoc.springRestLength + val; + this.dataDoc.springStartLength = this.dataDoc.springRestLength + val; + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + }} + radianEquivalent={false} + mode={'Freeform'} + labelWidth={'7em'} + /> + </div> + )} + {this.dataDoc.simulationType == 'Inclined Plane' && this.dataDoc.simulationPaused && ( + <div> + <InputField + label={<Box>θ</Box>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'wedgeAngle'} + step={1} + unit={'°'} + upperBound={49} + value={this.dataDoc.wedgeAngle ?? 26} + effect={(val: number) => { + this.changeWedgeBasedOnNewAngle(val); + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + }} + radianEquivalent={true} + mode={'Freeform'} + labelWidth={'2em'} + /> + <InputField + label={ + <Box> + μ<sub>s</sub> + </Box> + } + lowerBound={0} + dataDoc={this.dataDoc} + prop={'coefficientOfStaticFriction'} + step={0.1} + unit={''} + upperBound={1} + value={this.dataDoc.coefficientOfStaticFriction ?? 0} + effect={(val: number) => { + this.updateForcesWithFriction(val); + if (val < Number(this.dataDoc.coefficientOfKineticFriction)) { + this.dataDoc.soefficientOfKineticFriction = val; + } + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + }} + mode={'Freeform'} + labelWidth={'2em'} + /> + <InputField + label={ + <Box> + μ<sub>k</sub> + </Box> + } + lowerBound={0} + dataDoc={this.dataDoc} + prop={'coefficientOfKineticFriction'} + step={0.1} + unit={''} + upperBound={Number(this.dataDoc.coefficientOfStaticFriction)} + value={this.dataDoc.coefficientOfKineticFriction ?? 0} + effect={(val: number) => { + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + }} + mode={'Freeform'} + labelWidth={'2em'} + /> + </div> + )} + {this.dataDoc.simulationType == 'Inclined Plane' && !this.dataDoc.simulationPaused && ( + <Typography> + θ: {Math.round(Number(this.dataDoc.wedgeAngle) * 100) / 100}° ≈ {Math.round(((Number(this.dataDoc.wedgeAngle) * Math.PI) / 180) * 100) / 100} rad + <br /> + μ <sub>s</sub>: {this.dataDoc.coefficientOfStaticFriction} + <br /> + μ <sub>k</sub>: {this.dataDoc.coefficientOfKineticFriction} + </Typography> + )} + {this.dataDoc.simulationType == 'Pendulum' && !this.dataDoc.simulationPaused && ( + <Typography> + θ: {Math.round(this.dataDoc.pendulumAngle * 100) / 100}° ≈ {Math.round(((this.dataDoc.pendulumAngle * Math.PI) / 180) * 100) / 100} rad + </Typography> + )} + {this.dataDoc.simulationType == 'Pendulum' && this.dataDoc.simulationPaused && ( + <div> + <InputField + label={<Box>Angle</Box>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'pendulumAngle'} + step={1} + unit={'°'} + upperBound={59} + value={this.dataDoc.pendulumAngle ?? 30} + effect={value => { + this.dataDoc.startPendulumAngle = value; + if (this.dataDoc.simulationType == 'Pendulum') { + const mag = this.dataDoc.mass * Math.abs(this.dataDoc.gravity) * Math.cos((value * Math.PI) / 180); - const forceOfTension: IForce = { - description: "Tension", - magnitude: mag, - directionInDegrees: 90 - value, - component: false, - }; + const forceOfTension: IForce = { + 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.dataDoc.gravity) * - Math.cos((value * Math.PI) / 180), - directionInDegrees: 270 - value, - component: true, - }; - const gravityPerpendicular: IForce = { - description: "Gravity Perpendicular Component", - magnitude: - Math.abs(this.dataDoc.gravity) * - Math.sin((value * Math.PI) / 180), - directionInDegrees: -value, - component: true, - }; + const tensionComponent: IForce = { + description: 'Tension', + magnitude: mag, + directionInDegrees: 90 - value, + component: true, + }; + const gravityParallel: IForce = { + description: 'Gravity Parallel Component', + magnitude: Math.abs(this.dataDoc.gravity) * Math.cos((value * Math.PI) / 180), + directionInDegrees: 270 - value, + component: true, + }; + const gravityPerpendicular: IForce = { + description: 'Gravity Perpendicular Component', + magnitude: Math.abs(this.dataDoc.gravity) * Math.sin((value * Math.PI) / 180), + directionInDegrees: -value, + component: true, + }; - const length = this.dataDoc.pendulumLength; - const x = - length * Math.cos(((90 - value) * Math.PI) / 180); - const y = - length * Math.sin(((90 - value) * Math.PI) / 180); - const xPos = this.xMax / 2 - x - this.dataDoc.radius; - const yPos = y - this.dataDoc.radius - 5; - this.dataDoc.startPosX = (xPos); - this.dataDoc.startPosY= (yPos); + const length = this.dataDoc.pendulumLength; + const x = length * Math.cos(((90 - value) * Math.PI) / 180); + const y = length * Math.sin(((90 - value) * Math.PI) / 180); + const xPos = this.xMax / 2 - x - this.dataDoc.radius; + const yPos = y - this.dataDoc.radius - 5; + this.dataDoc.startPosX = xPos; + this.dataDoc.startPosY = yPos; - this.dataDoc.startForces = ([ - { - description: "Gravity", - magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, - directionInDegrees: 270, - component: false, - }, - forceOfTension, - ]); - this.dataDoc.updatedForces = ([ - { - description: "Gravity", - magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, - directionInDegrees: 270, - component: false, - }, - forceOfTension, - ]); - this.dataDoc.componentForces = ([ - tensionComponent, - gravityParallel, - gravityPerpendicular, - ]); - this.dataDoc.adjustPendulumAngle = ({ - angle: value, - length: this.dataDoc.pendulumLength, - }); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - } - }} - radianEquivalent={true} - mode={"Freeform"} - labelWidth={"5em"} - /> - <InputField - label={<Box>Rod length</Box>} - lowerBound={0} - dataDoc={this.dataDoc} - prop={'pendulumLength'} - step={1} - unit={"m"} - upperBound={400} - value={Math.round(this.dataDoc.pendulumLength)} - effect={(value) => { - if (this.dataDoc.simulationType == "Pendulum") { - this.dataDoc.adjustPendulumAngle = ({ - angle: this.dataDoc.pendulumAngle, - length: value, - }); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - } - }} - radianEquivalent={false} - mode={"Freeform"} - labelWidth={"5em"} - /> + this.dataDoc.startForces = [ + { + description: 'Gravity', + magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, + directionInDegrees: 270, + component: false, + }, + forceOfTension, + ]; + this.dataDoc.updatedForces = [ + { + description: 'Gravity', + magnitude: Math.abs(this.dataDoc.gravity) * this.dataDoc.mass, + directionInDegrees: 270, + component: false, + }, + forceOfTension, + ]; + this.dataDoc.componentForces = [tensionComponent, gravityParallel, gravityPerpendicular]; + this.dataDoc.adjustPendulumAngle = { + angle: value, + length: this.dataDoc.pendulumLength, + }; + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + } + }} + radianEquivalent={true} + mode={'Freeform'} + labelWidth={'5em'} + /> + <InputField + label={<Box>Rod length</Box>} + lowerBound={0} + dataDoc={this.dataDoc} + prop={'pendulumLength'} + step={1} + unit={'m'} + upperBound={400} + value={Math.round(this.dataDoc.pendulumLength)} + effect={value => { + if (this.dataDoc.simulationType == 'Pendulum') { + this.dataDoc.adjustPendulumAngle = { + angle: this.dataDoc.pendulumAngle, + length: value, + }; + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + } + }} + radianEquivalent={false} + mode={'Freeform'} + labelWidth={'5em'} + /> + </div> + )} + </div> + )} + <div className="mechanicsSimulationEquation"> + {this.dataDoc.mode == 'Freeform' && ( + <table> + <tbody> + <tr> + <td>{this.dataDoc.simulationType == 'Pulley' ? 'Red Weight' : ''}</td> + <td>X</td> + <td>Y</td> + </tr> + <tr> + <td + style={{ cursor: 'help' }} + // onClick={() => { + // window.open( + // "https://www.khanacademy.org/science/physics/two-dimensional-motion" + // ); + // }} + > + <Box>Position</Box> + </td> + {(!this.dataDoc.simulationPaused || this.dataDoc.simulationType == 'Inclined Plane' || this.dataDoc.simulationType == 'Circular Motion' || this.dataDoc.simulationType == 'Pulley') && ( + <td style={{ cursor: 'default' }}>{this.dataDoc.positionXDisplay} m</td> + )}{' '} + {this.dataDoc.simulationPaused && this.dataDoc.simulationType != 'Inclined Plane' && this.dataDoc.simulationType != 'Circular Motion' && this.dataDoc.simulationType != 'Pulley' && ( + <td + style={{ + cursor: 'default', + }}> + <InputField + lowerBound={this.dataDoc.simulationType == 'Projectile' ? 1 : (this.xMax + this.xMin) / 4 - this.radius - 15} + dataDoc={this.dataDoc} + prop={'positionXDisplay'} + step={1} + unit={'m'} + upperBound={this.dataDoc.simulationType == 'Projectile' ? this.xMax - 110 : (3 * (this.xMax + this.xMin)) / 4 - this.radius / 2 - 15} + value={this.dataDoc.positionXDisplay} + effect={value => { + this.dataDoc.displayChange = { + xDisplay: value, + yDisplay: this.dataDoc.positionYDisplay, + }; + if (this.dataDoc['simulationType'] == 'Suspension') { + let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; + let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; + let deltaX1 = value + this.radius - x1rod; + let deltaX2 = x2rod - (value + this.radius); + let deltaY = this.getYPosFromDisplay(this.dataDoc.positionYDisplay) + this.radius; + let dir1T = Math.PI - Math.atan(deltaY / deltaX1); + let dir2T = Math.atan(deltaY / deltaX2); + let tensionMag2 = (this.dataDoc.mass * Math.abs(this.dataDoc.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: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), + directionInDegrees: 270, + component: false, + }; + this.dataDoc['updatedForces'] = [tensionForce1, tensionForce2, grav]; + } + }} + small={true} + mode={'Freeform'} + /> + </td> + )}{' '} + {(!this.dataDoc.simulationPaused || this.dataDoc.simulationType == 'Inclined Plane' || this.dataDoc.simulationType == 'Circular Motion' || this.dataDoc.simulationType == 'Pulley') && ( + <td style={{ cursor: 'default' }}>{this.dataDoc.positionYDisplay} m</td> + )}{' '} + {this.dataDoc.simulationPaused && this.dataDoc.simulationType != 'Inclined Plane' && this.dataDoc.simulationType != 'Circular Motion' && this.dataDoc.simulationType != 'Pulley' && ( + <td + style={{ + cursor: 'default', + }}> + <InputField + lowerBound={1} + dataDoc={this.dataDoc} + prop={'positionYDisplay'} + step={1} + unit={'m'} + upperBound={this.yMax - 110} + value={this.dataDoc.positionYDisplay} + effect={value => { + this.dataDoc.displayChange = { + xDisplay: this.dataDoc.positionXDisplay, + yDisplay: value, + }; + if (this.dataDoc['simulationType'] == 'Suspension') { + let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; + let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; + let deltaX1 = this.dataDoc.positionXDisplay + this.radius - x1rod; + let deltaX2 = x2rod - (this.dataDoc.positionXDisplay + this.radius); + let deltaY = this.getYPosFromDisplay(value) + this.radius; + let dir1T = Math.PI - Math.atan(deltaY / deltaX1); + let dir2T = Math.atan(deltaY / deltaX2); + let tensionMag2 = (this.dataDoc.mass * Math.abs(this.dataDoc.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: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), + directionInDegrees: 270, + component: false, + }; + this.dataDoc['updatedForces'] = [tensionForce1, tensionForce2, grav]; + } + }} + small={true} + mode={'Freeform'} + /> + </td> + )}{' '} + </tr> + <tr> + <td + style={{ cursor: 'help' }} + // onClick={() => { + // window.open( + // "https://www.khanacademy.org/science/physics/two-dimensional-motion" + // ); + // }} + > + <Box>Velocity</Box> + </td> + {(!this.dataDoc.simulationPaused || (this.dataDoc.simulationType != 'One Weight' && this.dataDoc.simulationType != 'Circular Motion')) && ( + <td style={{ cursor: 'default' }}>{this.dataDoc.velocityXDisplay} m/s</td> + )}{' '} + {this.dataDoc.simulationPaused && (this.dataDoc.simulationType == 'One Weight' || this.dataDoc.simulationType == 'Circular Motion') && ( + <td + style={{ + cursor: 'default', + }}> + <InputField + lowerBound={-50} + dataDoc={this.dataDoc} + prop={'velocityXDisplay'} + step={1} + unit={'m/s'} + upperBound={50} + value={this.dataDoc.velocityXDisplay} + effect={value => { + this.dataDoc.startVelX = value; + this.dataDoc.simulationReset = !this.dataDoc.simulationReset; + }} + small={true} + mode={'Freeform'} + /> + </td> + )}{' '} + {(!this.dataDoc.simulationPaused || this.dataDoc.simulationType != 'One Weight') && <td style={{ cursor: 'default' }}>{this.dataDoc.velocityYDisplay} m/s</td>}{' '} + {this.dataDoc.simulationPaused && this.dataDoc.simulationType == 'One Weight' && ( + <td + style={{ + cursor: 'default', + }}> + <InputField + lowerBound={-50} + dataDoc={this.dataDoc} + prop={'velocityYDisplay'} + step={1} + unit={'m/s'} + upperBound={50} + value={this.dataDoc.velocityYDisplay} + effect={value => { + this.dataDoc.startVelY = -value; + this.dataDoc.displayChange = { + xDisplay: this.dataDoc.positionXDisplay, + yDisplay: this.dataDoc.positionYDisplay, + }; + }} + small={true} + mode={'Freeform'} + /> + </td> + )}{' '} + </tr> + <tr> + <td + style={{ cursor: 'help' }} + // onClick={() => { + // window.open( + // "https://www.khanacademy.org/science/physics/two-dimensional-motion" + // ); + // }} + > + <Box>Acceleration</Box> + </td> + <td style={{ cursor: 'default' }}> + {this.dataDoc.accelerationXDisplay} m/s<sup>2</sup> + </td> + <td style={{ cursor: 'default' }}> + {this.dataDoc.accelerationYDisplay} m/s<sup>2</sup> + </td> + </tr> + <tr> + <td> + <Box>Momentum</Box> + </td> + <td>{Math.round(this.dataDoc.velocityXDisplay * this.dataDoc.mass * 10) / 10} kg*m/s</td> + <td>{Math.round(this.dataDoc.velocityYDisplay * this.dataDoc.mass * 10) / 10} kg*m/s</td> + </tr> + </tbody> + </table> + )} + {this.dataDoc.mode == 'Freeform' && this.dataDoc.simulationType == 'Pulley' && ( + <table> + <tbody> + <tr> + <td>Blue Weight</td> + <td>X</td> + <td>Y</td> + </tr> + <tr> + <td> + <Box>Position</Box> + </td> + <td style={{ cursor: 'default' }}>{this.dataDoc.positionXDisplay2} m</td> + <td style={{ cursor: 'default' }}>{this.dataDoc.positionYDisplay2} m</td> + </tr> + <tr> + <td> + <Box>Velocity</Box> + </td> + <td style={{ cursor: 'default' }}>{this.dataDoc.velocityXDisplay2} m/s</td> + + <td style={{ cursor: 'default' }}>{this.dataDoc.velocityYDisplay2} m/s</td> + </tr> + <tr> + <td> + <Box>Acceleration</Box> + </td> + <td style={{ cursor: 'default' }}> + {this.dataDoc.accelerationXDisplay2} m/s<sup>2</sup> + </td> + <td style={{ cursor: 'default' }}> + {this.dataDoc.accelerationYDisplay2} m/s<sup>2</sup> + </td> + </tr> + <tr> + <td> + <Box>Momentum</Box> + </td> + <td>{Math.round(this.dataDoc.velocityXDisplay2 * this.dataDoc.mass * 10) / 10} kg*m/s</td> + <td>{Math.round(this.dataDoc.velocityYDisplay2 * this.dataDoc.mass * 10) / 10} kg*m/s</td> + </tr> + </tbody> + </table> + )} + </div> + {this.dataDoc.simulationType != 'Pendulum' && this.dataDoc.simulationType != 'Spring' && ( + <div> + <p>Kinematic Equations</p> + <ul> + <li> + Position: x<sub>1</sub>=x<sub>0</sub>+v<sub>0</sub>t+ + <sup>1</sup>⁄ + <sub>2</sub>at + <sup>2</sup> + </li> + <li> + Velocity: v<sub>1</sub>=v<sub>0</sub>+at + </li> + <li>Acceleration: a = F/m</li> + </ul> + </div> + )} + {this.dataDoc.simulationType == 'Spring' && ( + <div> + <p>Harmonic Motion Equations: Spring</p> + <ul> + <li> + Spring force: F<sub>s</sub>=kd + </li> + <li> + Spring period: T<sub>s</sub>=2π√<sup>m</sup>⁄ + <sub>k</sub> + </li> + <li>Equilibrium displacement for vertical spring: d = mg/k</li> + <li> + Elastic potential energy: U<sub>s</sub>=<sup>1</sup>⁄ + <sub>2</sub>kd<sup>2</sup> + </li> + <ul> + <li>Maximum when system is at maximum displacement, 0 when system is at 0 displacement</li> + </ul> + <li> + Translational kinetic energy: K=<sup>1</sup>⁄ + <sub>2</sub>mv<sup>2</sup> + </li> + <ul> + <li>Maximum when system is at maximum/minimum velocity (at 0 displacement), 0 when velocity is 0 (at maximum displacement)</li> + </ul> + </ul> + </div> + )} + {this.dataDoc.simulationType == 'Pendulum' && ( + <div> + <p>Harmonic Motion Equations: Pendulum</p> + <ul> + <li> + Pendulum period: T<sub>p</sub>=2π√<sup>l</sup>⁄ + <sub>g</sub> + </li> + </ul> + </div> + )} + </div> </div> - )} - </div> - )} - <div className="mechanicsSimulationEquation"> - {this.dataDoc.mode == "Freeform" && ( - <table> - <tbody> - <tr> - <td>{this.dataDoc.simulationType == "Pulley" ? "Red Weight" : ""}</td> - <td>X</td> - <td>Y</td> - </tr> - <tr> - <td - style={{ cursor: "help" }} - // onClick={() => { - // window.open( - // "https://www.khanacademy.org/science/physics/two-dimensional-motion" - // ); - // }} - > - <Box>Position</Box> - </td> - {(!this.dataDoc.simulationPaused || - this.dataDoc.simulationType == "Inclined Plane" || - this.dataDoc.simulationType == "Circular Motion" || - this.dataDoc.simulationType == "Pulley") && ( - <td style={{ cursor: "default" }}> - {this.dataDoc.positionXDisplay} m - </td> - )}{" "} - {this.dataDoc.simulationPaused && - this.dataDoc.simulationType != "Inclined Plane" && - this.dataDoc.simulationType != "Circular Motion" && - this.dataDoc.simulationType != "Pulley" && ( - <td - style={{ - cursor: "default", - }} - > - <InputField - lowerBound={this.dataDoc.simulationType == "Projectile" ? 1 : (this.xMax + this.xMin) / 4 - this.radius - 15} - dataDoc={this.dataDoc} - prop={'positionXDisplay'} - step={1} - unit={"m"} - upperBound={this.dataDoc.simulationType == "Projectile" ? this.xMax - 110 : (3 * (this.xMax + this.xMin)) / 4 - this.radius / 2 - 15} - value={this.dataDoc.positionXDisplay} - effect={(value) => { - this.dataDoc.displayChange = ({ - xDisplay: value, - yDisplay: this.dataDoc.positionYDisplay, - }); - if (this.dataDoc['simulationType'] == "Suspension") { - let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; - let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; - let deltaX1 = value + this.radius - x1rod; - let deltaX2 = x2rod - (value + this.radius); - let deltaY = this.getYPosFromDisplay(this.dataDoc.positionYDisplay) + this.radius; - let dir1T = Math.PI - Math.atan(deltaY / deltaX1); - let dir2T = Math.atan(deltaY / deltaX2); - let tensionMag2 = - (this.dataDoc.mass * Math.abs(this.dataDoc.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: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), - directionInDegrees: 270, - component: false, - }; - this.dataDoc['updatedForces'] = ([tensionForce1, tensionForce2, grav]); - } - }} - small={true} - mode={"Freeform"} - /> - </td> - )}{" "} - {(!this.dataDoc.simulationPaused || - this.dataDoc.simulationType == "Inclined Plane" || - this.dataDoc.simulationType == "Circular Motion" || - this.dataDoc.simulationType == "Pulley") && ( - <td style={{ cursor: "default" }}> - {this.dataDoc.positionYDisplay} m - </td> - )}{" "} - {this.dataDoc.simulationPaused && - this.dataDoc.simulationType != "Inclined Plane" && - this.dataDoc.simulationType != "Circular Motion" && - this.dataDoc.simulationType != "Pulley" && ( - <td - style={{ - cursor: "default", - }} - > - <InputField - lowerBound={1} - dataDoc={this.dataDoc} - prop={'positionYDisplay'} - step={1} - unit={"m"} - upperBound={this.yMax - 110} - value={this.dataDoc.positionYDisplay} - effect={(value) => { - this.dataDoc.displayChange = ({ - xDisplay: this.dataDoc.positionXDisplay, - yDisplay: value, - }); - if (this.dataDoc['simulationType'] == "Suspension") { - let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; - let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; - let deltaX1 = this.dataDoc.positionXDisplay + this.radius - x1rod; - let deltaX2 = x2rod - (this.dataDoc.positionXDisplay + this.radius); - let deltaY = this.getYPosFromDisplay(value) + this.radius; - let dir1T = Math.PI - Math.atan(deltaY / deltaX1); - let dir2T = Math.atan(deltaY / deltaX2); - let tensionMag2 = - (this.dataDoc.mass * Math.abs(this.dataDoc.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: this.dataDoc.mass * Math.abs(this.dataDoc.gravity), - directionInDegrees: 270, - component: false, - }; - this.dataDoc['updatedForces'] = ([tensionForce1, tensionForce2, grav]); - } - }} - small={true} - mode={"Freeform"} - /> - </td> - )}{" "} - </tr> - <tr> - <td - style={{ cursor: "help" }} - // onClick={() => { - // window.open( - // "https://www.khanacademy.org/science/physics/two-dimensional-motion" - // ); - // }} - > - <Box>Velocity</Box> - </td> - {(!this.dataDoc.simulationPaused || - (this.dataDoc.simulationType != "One Weight" && - this.dataDoc.simulationType != "Circular Motion")) && ( - <td style={{ cursor: "default" }}> - {this.dataDoc.velocityXDisplay} m/s - </td> - )}{" "} - {this.dataDoc.simulationPaused && - (this.dataDoc.simulationType == "One Weight" || - this.dataDoc.simulationType == "Circular Motion") && ( - <td - style={{ - cursor: "default", - }} - > - <InputField - lowerBound={-50} - dataDoc={this.dataDoc} - prop={'velocityXDisplay'} - step={1} - unit={"m/s"} - upperBound={50} - value={this.dataDoc.velocityXDisplay} - effect={(value) => { - this.dataDoc.startVelX = (value); - this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); - }} - small={true} - mode={"Freeform"} - /> - </td> - )}{" "} - {(!this.dataDoc.simulationPaused || this.dataDoc.simulationType != "One Weight") && ( - <td style={{ cursor: "default" }}> - {this.dataDoc.velocityYDisplay} m/s - </td> - )}{" "} - {this.dataDoc.simulationPaused && this.dataDoc.simulationType == "One Weight" && ( - <td + <div + style={{ + position: 'fixed', + top: this.yMax - 120 + 20 + 'px', + left: this.xMin + 90 - 80 + 'px', + zIndex: -10000, + }}> + <svg width={100 + 'px'} height={100 + 'px'}> + <defs> + <marker id="miniArrow" markerWidth="20" markerHeight="20" refX="0" refY="3" orient="auto" markerUnits="strokeWidth"> + <path d="M0,0 L0,6 L9,3 z" fill={'#000000'} /> + </marker> + </defs> + <line x1={20} y1={70} x2={70} y2={70} stroke={'#000000'} strokeWidth="2" markerEnd="url(#miniArrow)" /> + <line x1={20} y1={70} x2={20} y2={20} stroke={'#000000'} strokeWidth="2" markerEnd="url(#miniArrow)" /> + </svg> + <p style={{ - cursor: "default", - }} - > - <InputField - lowerBound={-50} - dataDoc={this.dataDoc} - prop={'velocityYDisplay'} - step={1} - unit={"m/s"} - upperBound={50} - value={this.dataDoc.velocityYDisplay} - effect={(value) => { - this.dataDoc.startVelY = (-value); - this.dataDoc.displayChange = ({ - xDisplay: this.dataDoc.positionXDisplay, - yDisplay: this.dataDoc.positionYDisplay, - }); - }} - small={true} - mode={"Freeform"} - /> - </td> - )}{" "} - </tr> - <tr> - <td - style={{ cursor: "help" }} - // onClick={() => { - // window.open( - // "https://www.khanacademy.org/science/physics/two-dimensional-motion" - // ); - // }} - > - <Box>Acceleration</Box> - </td> - <td style={{ cursor: "default" }}> - {this.dataDoc.accelerationXDisplay} m/s<sup>2</sup> - </td> - <td style={{ cursor: "default" }}> - {this.dataDoc.accelerationYDisplay} m/s<sup>2</sup> - </td> - </tr> - <tr> - <td> - <Box>Momentum</Box> - </td> - <td> - {Math.round(this.dataDoc.velocityXDisplay * this.dataDoc.mass * 10) / 10} kg*m/s - </td> - <td> - {Math.round(this.dataDoc.velocityYDisplay * this.dataDoc.mass * 10) / 10} kg*m/s - </td> - </tr> - </tbody> - </table> - )} - {this.dataDoc.mode == "Freeform" && this.dataDoc.simulationType == "Pulley" && ( - <table> - <tbody> - <tr> - <td>Blue Weight</td> - <td>X</td> - <td>Y</td> - </tr> - <tr> - <td> - <Box>Position</Box> - </td> - <td style={{ cursor: "default" }}>{this.dataDoc.positionXDisplay2} m</td> - <td style={{ cursor: "default" }}>{this.dataDoc.positionYDisplay2} m</td> - </tr> - <tr> - <td> - <Box>Velocity</Box> - </td> - <td style={{ cursor: "default" }}> - {this.dataDoc.velocityXDisplay2} m/s - </td> - - <td style={{ cursor: "default" }}> - {this.dataDoc.velocityYDisplay2} m/s - </td> - </tr> - <tr> - <td> - <Box>Acceleration</Box> - </td> - <td style={{ cursor: "default" }}> - {this.dataDoc.accelerationXDisplay2} m/s<sup>2</sup> - </td> - <td style={{ cursor: "default" }}> - {this.dataDoc.accelerationYDisplay2} m/s<sup>2</sup> - </td> - </tr> - <tr> - <td> - <Box>Momentum</Box> - </td> - <td> - {Math.round(this.dataDoc.velocityXDisplay2 * this.dataDoc.mass * 10) / 10} kg*m/s - </td> - <td> - {Math.round(this.dataDoc.velocityYDisplay2 * this.dataDoc.mass * 10) / 10} kg*m/s - </td> - </tr> - </tbody> - </table> - )} - </div> - {this.dataDoc.simulationType != "Pendulum" && this.dataDoc.simulationType != "Spring" && ( - <div> - <p>Kinematic Equations</p> - <ul> - <li> - Position: x<sub>1</sub>=x<sub>0</sub>+v<sub>0</sub>t+ - <sup>1</sup>⁄ - <sub>2</sub>at - <sup>2</sup> - </li> - <li> - Velocity: v<sub>1</sub>=v<sub>0</sub>+at - </li> - <li>Acceleration: a = F/m</li> - </ul> - </div> - )} - {this.dataDoc.simulationType == "Spring" && ( - <div> - <p>Harmonic Motion Equations: Spring</p> - <ul> - <li> - Spring force: F<sub>s</sub>=kd - </li> - <li> - Spring period: T<sub>s</sub>=2π√<sup>m</sup>⁄ - <sub>k</sub> - </li> - <li>Equilibrium displacement for vertical spring: d = mg/k</li> - <li> - Elastic potential energy: U<sub>s</sub>=<sup>1</sup>⁄ - <sub>2</sub>kd<sup>2</sup> - </li> - <ul> - <li> - Maximum when system is at maximum displacement, 0 when - system is at 0 displacement - </li> - </ul> - <li> - Translational kinetic energy: K=<sup>1</sup>⁄ - <sub>2</sub>mv<sup>2</sup> - </li> - <ul> - <li> - Maximum when system is at maximum/minimum velocity (at 0 - displacement), 0 when velocity is 0 (at maximum - displacement) - </li> - </ul> - </ul> - </div> - )} - {this.dataDoc.simulationType == "Pendulum" && ( - <div> - <p>Harmonic Motion Equations: Pendulum</p> - <ul> - <li> - Pendulum period: T<sub>p</sub>=2π√<sup>l</sup>⁄ - <sub>g</sub> - </li> - </ul> + position: 'fixed', + top: this.yMax - 120 + 40 + 'px', + left: this.xMin + 90 - 80 + 'px', + }}> + {this.dataDoc.simulationType == 'Circular Motion' ? 'Z' : 'Y'} + </p> + <p + style={{ + position: 'fixed', + top: this.yMax - 120 + 80 + 'px', + left: this.xMin + 90 - 40 + 'px', + }}> + X + </p> + </div> </div> - )} - </div> - </div> - <div - style={{ - position: "fixed", - top: this.yMax - 120 + 20 + "px", - left: this.xMin + 90 - 80 + "px", - zIndex: -10000, - }} - > - <svg width={100 + "px"} height={100 + "px"}> - <defs> - <marker - id="miniArrow" - markerWidth="20" - markerHeight="20" - refX="0" - refY="3" - orient="auto" - markerUnits="strokeWidth" - > - <path d="M0,0 L0,6 L9,3 z" fill={"#000000"} /> - </marker> - </defs> - <line - x1={20} - y1={70} - x2={70} - y2={70} - stroke={"#000000"} - strokeWidth="2" - markerEnd="url(#miniArrow)" - /> - <line - x1={20} - y1={70} - x2={20} - y2={20} - stroke={"#000000"} - strokeWidth="2" - markerEnd="url(#miniArrow)" - /> - </svg> - <p - style={{ - position: "fixed", - top: this.yMax - 120 + 40 + "px", - left: this.xMin + 90 - 80 + "px", - }} - > - {this.dataDoc.simulationType == "Circular Motion" ? "Z" : "Y"} - </p> - <p - style={{ - position: "fixed", - top: this.yMax - 120 + 80 + "px", - left: this.xMin + 90 - 40 + "px", - }} - > - X - </p> - </div> - </div> - ) - } -}
\ No newline at end of file + ); + } +} |