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 { 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 questions from "./PhysicsSimulationQuestions.json"; import tutorials from "./PhysicsSimulationTutorial.json"; import Wall from "./PhysicsSimulationWall"; import IWall from "./PhysicsSimulationWall"; import Weight from "./PhysicsSimulationWeight"; import IForce from "./PhysicsSimulationWeight"; interface VectorTemplate { 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 }[]; } interface TutorialTemplate { question: string; steps: { description: string; content: string; forces: { description: string; magnitude: number; directionInDegrees: number; component: boolean; }[]; showMagnitude: boolean; }[]; } @observer export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent() { 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[] = []; componentDidMount() { this.wallPositions.push({ length: 70, xPos: 0, yPos: 0, angleInDegrees: 0 }); this.wallPositions.push({ length: 70, xPos: 0, yPos: 80, angleInDegrees: 0 }); this.wallPositions.push({ length: 80, xPos: 0, yPos: 0, angleInDegrees: 90 }); this.wallPositions.push({ length: 80, xPos: 69.5, yPos: 0, angleInDegrees: 90 }); // Used throughout sims this.xMax = this.layoutDoc._width*0.08 ?? 500; this.yMax = this.layoutDoc._height*0.08 ?? 500; this.radius = 0.1*this.layoutDoc._height ?? 50; 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 = this.dataDoc.mode ?? "Freeform"; this.dataDoc.positionXDisplay = this.dataDoc.positionXDisplay ?? 0; this.dataDoc.positionYDisplay = this.dataDoc.positionYDisplay ?? 0; this.dataDoc.resetAll = this.dataDoc.resetAll ?? true; 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 ?? 0; this.dataDoc.startPosY = this.dataDoc.startPosY ?? 0; 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 review mode this.dataDoc.answerInputFields = this.dataDoc.answerInputFields ??
; this.dataDoc.currentForceSketch = this.dataDoc.currentForceSketch ?? null; this.dataDoc.deleteMode = this.dataDoc.deleteMode ?? false; this.dataDoc.forceSketches = this.dataDoc.forceSketches ?? []; this.dataDoc.hintDialogueOpen = this.dataDoc.hintDialogueOpen ?? false; this.dataDoc.noMovement = this.dataDoc.noMovement ?? false; this.dataDoc.questionNumber = this.dataDoc.questionNumber ?? 0; this.dataDoc.questionPartOne = this.dataDoc.questionPartOne ?? ""; this.dataDoc.questionPartTwo = this.dataDoc.questionPartTwo ?? ""; this.dataDoc.reviewGravityAngle = this.dataDoc.reviewGravityAngle ?? 0; this.dataDoc.reviewGravityMagnitude = this.dataDoc.reviewGravityMagnitude ?? 0; this.dataDoc.reviewNormalAngle = this.dataDoc.reviewNormalAngle ?? 0; this.dataDoc.reviewNormalMagnitude = this.dataDoc.reviewNormalMagnitude ?? 0; this.dataDoc.reviewStaticAngle = this.dataDoc.reviewStaticAngle ?? 0; this.dataDoc.reviewStaticMagnitude = this.dataDoc.reviewStaticMagnitude ?? 0; this.dataDoc.selectedSolutions = this.dataDoc.selectedSolutions ?? []; this.dataDoc.selectedQuestion = this.dataDoc.selectedQuestion ?? questions.inclinePlane[0]; this.dataDoc.sketching = this.dataDoc.sketching ?? false; // Used for tutorial mode this.dataDoc.selectedTutorial = this.dataDoc.selectedTutorial ?? tutorials.inclinePlane; // Used for uniform circular motion this.dataDoc.circularMotionRadius = this.dataDoc.circularMotionRadius ?? 150; // 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 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 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) * 400; this.dataDoc.wedgeWidth = this.dataDoc.wedgeWidth ?? 400; // 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; // Setup simulation if (this.dataDoc.simulationType != "Circular Motion") { this.dataDoc.startVelX = 0; this.dataDoc.setStartVelY = 0; this.dataDoc.velocityXDisplay = (0); this.dataDoc.velocityYDisplay = (0); } if (this.dataDoc.mode == "Freeform") { this.dataDoc.showForceMagnitudes = (true); if (this.dataDoc.simulationType == "One Weight") { this.dataDoc.showComponentForces = false; this.dataDoc.startPosY = (this.yMin + this.radius); this.dataDoc.startPosX = ((this.xMax + this.xMin) / 2 - this.radius); this.dataDoc.positionYDisplay = (this.getDisplayYPos(this.yMin + this.radius)); this.dataDoc.positionXDisplay((this.xMax + this.xMin) / 2 - this.radius); 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 (this.dataDoc.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 (this.dataDoc.simulationType == "Pendulum") { this.setupPendulum(); } else if (this.dataDoc.simulationType == "Spring") { this.setupSpring(); } else if (this.dataDoc.simulationType == "Circular Motion") { this.setupCircular(0); } else if (this.dataDoc.simulationType == "Pulley") { this.setupPulley(); } else if (this.dataDoc.simulationType == "Suspension") { this.setupSuspension(); } } else if (this.dataDoc.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 (this.dataDoc.simulationType == "One Weight") { // TODO - one weight review problems } else if (this.dataDoc.simulationType == "Spring") { this.setupSpring(); // TODO - spring review problems } else if (this.dataDoc.simulationType == "Inclined Plane") { this.dataDoc.updatedForces = ([]); this.dataDoc.startForces = ([]); } else if (this.dataDoc.simulationType == "Pendulum") { this.setupPendulum(); // TODO - pendulum review problems } else if (this.dataDoc.simulationType == "Circular Motion") { this.setupCircular(0); // TODO - circular motion review problems } else if (this.dataDoc.simulationType == "Pulley") { this.setupPulley(); // TODO - pulley tutorial review problems } else if (this.dataDoc.simulationType == "Suspension") { this.setupSuspension(); // TODO - suspension tutorial review problems } } else if (this.dataDoc.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 - this.radius); 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 - this.radius); 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 - this.radius; const yPos = y - this.radius - 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() { this.xMax = this.layoutDoc._width*0.08 ?? 500; this.yMax = this.layoutDoc._height*0.08 ?? 500; this.radius = 0.1*this.layoutDoc._height ?? 50; } // Helper function to go between display and real values getDisplayYPos = (yPos: number) => { return this.yMax - this.dataDoc.yPos - 2 * this.radius + 5; }; getYPosFromDisplay = (yDisplay: number) => { return this.yMax - this.dataDoc.yDisplay - 2 * this.radius + 5; }; // 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, ]; } }; // Change wedge height and width and weight position to match new wedge angle changeWedgeBasedOnNewAngle = (angle: number) => { let width = 0; let height = 0; if (angle < 50) { width = 400; height = Math.tan((angle * Math.PI) / 180) * 400; this.dataDoc.wedgeWidth = width; this.dataDoc.wedgeHeight = height; } else if (angle < 70) { width = 200; height = Math.tan((angle * Math.PI) / 180) * 200; this.dataDoc.wedgeWidth = width; this.dataDoc.wedgeHeight = height; } else { width = 100; height = Math.tan((angle * Math.PI) / 180) * 100; this.dataDoc.wedgeWidth = width; this.dataDoc.wedgeHeight = height; } // update weight position based on updated wedge width/height let yPos = (width - this.radius) * Math.tan((angle * Math.PI) / 180); if (angle < 40) { yPos += Math.sqrt(angle); } else if (angle < 58) { yPos += angle / 2; } else if (angle < 68) { yPos += angle; } else if (angle < 70) { yPos += angle * 1.3; } else if (angle < 75) { yPos += angle * 1.5; } else if (angle < 78) { yPos += angle * 2; } else if (angle < 79) { yPos += angle * 2.25; } else if (angle < 80) { yPos += angle * 2.6; } else { yPos += angle * 3; } this.dataDoc.startPosX = Math.round((this.xMax * 0.5 - 200) * 10) / 10; this.dataDoc.startPosY = this.getDisplayYPos(yPos); if (this.dataDoc.mode == "Freeform") { this.updateForcesWithFriction( Number(this.dataDoc.coefficientOfStaticFriction), width, height ); } }; // 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); }; // 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); }; // Solve for the correct answers to the generated problem getAnswersToQuestion = ( question: QuestionTemplate, questionVars: number[] ) => { const solutions: number[] = []; 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]; } 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; }; // 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); } } }; // 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); this.dataDoc.answerInputFields = (
); }; // In review mode, reset problem variables and generate a new question generateNewQuestion = () => { this.resetReviewValuesToDefault(); 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]; 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; } } 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)"; } } } this.dataDoc.questionVariables = vars; this.dataDoc.selectedQuestion = (question); this.dataDoc.questionPartOne = (q); this.dataDoc.questionPartTwo = (question.question); const answers = this.getAnswersToQuestion(question, vars); this.generateInputFieldsForQuestion(false, question, answers); this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); }; // Generate answerInputFields for new review question generateInputFieldsForQuestion = ( showIcon: boolean = false, question: QuestionTemplate = this.dataDoc.selectedQuestion, answers: number[] = this.dataDoc.selectedSolutions ) => { let answerInput = []; const d = new Date(); for (let i = 0; i < question.answerParts.length; i++) { if (question.answerParts[i] == "force of gravity") { this.dataDoc.reviewGravityMagnitude = (0); answerInput.push(
Gravity magnitude

} lowerBound={0} changeValue={this.dataDoc.reviewGravityMagnitude} step={0.1} unit={"N"} upperBound={50} value={this.dataDoc.reviewGravityMagnitude} showIcon={showIcon} correctValue={answers[i]} labelWidth={"7em"} />
); } else if (question.answerParts[i] == "angle of gravity") { this.dataDoc.reviewGravityAngle = (0); answerInput.push(
Gravity angle

} lowerBound={0} changeValue={this.dataDoc.reviewGravityAngle} step={1} unit={"°"} upperBound={360} value={this.dataDoc.reviewGravityAngle} radianEquivalent={true} showIcon={showIcon} correctValue={answers[i]} labelWidth={"7em"} />
); } else if (question.answerParts[i] == "normal force") { this.dataDoc.reviewNormalMagnitude = (0); answerInput.push(
Normal force magnitude

} lowerBound={0} changeValue={this.dataDoc.reviewNormalMagnitude} step={0.1} unit={"N"} upperBound={50} value={this.dataDoc.reviewNormalMagnitude} showIcon={showIcon} correctValue={answers[i]} labelWidth={"7em"} />
); } else if (question.answerParts[i] == "angle of normal force") { this.dataDoc.reviewNormalAngle = (0); answerInput.push(
Normal force angle

} lowerBound={0} changeValue={this.dataDoc.reviewNormalAngle} step={1} unit={"°"} upperBound={360} value={this.dataDoc.reviewNormalAngle} radianEquivalent={true} showIcon={showIcon} correctValue={answers[i]} labelWidth={"7em"} />
); } else if (question.answerParts[i] == "force of static friction") { this.dataDoc.reviewStaticMagnitude = (0); answerInput.push(
Static friction magnitude

} lowerBound={0} changeValue={this.dataDoc.reviewStaticMagnitude} step={0.1} unit={"N"} upperBound={50} value={this.dataDoc.reviewStaticMagnitude} showIcon={showIcon} correctValue={answers[i]} labelWidth={"7em"} />
); } else if (question.answerParts[i] == "angle of static friction") { this.dataDoc.reviewStaticAngle = (0); answerInput.push(
Static friction angle

} lowerBound={0} changeValue={this.dataDoc.reviewStaticAngle} step={1} unit={"°"} upperBound={360} value={this.dataDoc.reviewStaticAngle} radianEquivalent={true} showIcon={showIcon} correctValue={answers[i]} labelWidth={"7em"} />
); } else if (question.answerParts[i] == "coefficient of static friction") { this.updateReviewForcesBasedOnCoefficient(0); answerInput.push(
μs } lowerBound={0} changeValue={this.dataDoc.coefficientOfStaticFriction} step={0.1} unit={""} upperBound={1} value={this.dataDoc.coefficientOfStaticFriction} effect={this.updateReviewForcesBasedOnCoefficient} showIcon={showIcon} correctValue={answers[i]} />
); } else if (question.answerParts[i] == "wedge angle") { this.updateReviewForcesBasedOnAngle(0); answerInput.push(
θ} lowerBound={0} changeValue={this.dataDoc.qedgeAngle} step={1} unit={"°"} upperBound={49} value={this.dataDoc.wedgeAngle} effect={(val: number) => { this.changeWedgeBasedOnNewAngle(val); this.updateReviewForcesBasedOnAngle(val); }} radianEquivalent={true} showIcon={showIcon} correctValue={answers[i]} />
); } } this.dataDoc.answerInputFields = (
{answerInput}
); }; // 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 - this.radius; let yPos = (this.yMax + this.yMin) / 2 + this.dataDoc.circularMotionRadius - this.radius; 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); }; // 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 - this.radius; const yPos = y - this.radius - 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, }; 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, }; 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 }); }; // 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 - this.radius); 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 - this.radius; 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); }; // 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 - 105); this.dataDoc.positionYDisplay = (this.getDisplayYPos((this.yMax + this.yMin) / 2)); this.dataDoc.positionXDisplay = ((this.xMin + this.xMax) / 2 - 105); 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, }; this.dataDoc.updatedForces = ([gravityForce1, tensionForce1]); this.dataDoc.startForces = ([gravityForce1, tensionForce1]); this.dataDoc.startPosY2 = ((this.props.yMax + this.props.yMin) / 2); this.dataDoc.startPosX2 = ((this.props.xMin + this.props.xMax) / 2 + 5); this.dataDoc.positionYDisplay2 = (this.getDisplayYPos((this.props.yMax + this.props.yMin) / 2)); this.dataDoc.positionXDisplay2 = ((this.props.xMin + this.props.xMax) / 2 + 5); this.dataDoc.updatedForces2 = ([gravityForce2, tensionForce2]); this.dataDoc.startForces2 = ([gravityForce2, tensionForce2]); this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); }; // 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; }; // 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, ]); } render () { return
{!this.dataDoc.simulationPaused && (
)}
{this.dataDoc.simulationType == "Pulley" && ( )}
{(this.dataDoc.simulationType == "One Weight" || this.dataDoc.simulationType == "Inclined Plane") && this.wallPositions.map((element, index) => { return ( ); })}
{this.dataDoc.simulationPaused && this.dataDoc.mode != "Tutorial" && ( { this.dataDoc.simulationPaused = (false); }} > )} {!this.dataDoc.simulationPaused && this.dataDoc.mode != "Tutorial" && ( { this.dataDoc.simulationPaused = (true); }} > )} {this.dataDoc.simulationPaused && this.dataDoc.mode != "Tutorial" && ( { this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); }} > )}
{this.dataDoc.mode == "Review" && this.dataDoc.simulationType != "Inclined Plane" && (

{this.dataDoc.simulationType} review problems in progress!

)} {this.dataDoc.mode == "Review" && this.dataDoc.simulationType == "Inclined Plane" && (
{!this.dataDoc.hintDialogueOpen && ( { this.dataDoc.hintDialogueOpen = (true); }} sx={{ position: "fixed", left: xMax - 50 + "px", top: yMin + 14 + "px", }} > )} this.dataDoc.hintDialogueOpen = (false)} > Hints {this.dataDoc.selectedQuestion.hints.map((hint, index) => { return (
Hint {index + 1}: {hint.description} {hint.content}
); })}

{this.dataDoc.questionPartOne}

{this.dataDoc.questionPartTwo}

{this.dataDoc.answerInputFields}
)} {this.dataDoc.mode == "Tutorial" && (

Problem

{this.dataDoc.selectedTutorial.question}

{ 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} >

Step {this.dataDoc.stepNumber + 1}:{" "} {this.dataDoc.selectedTutorial.steps[this.dataDoc.stepNumber].description}

{this.dataDoc.selectedTutorial.steps[this.dataDoc.stepNumber].content}

{ 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} >
{(this.dataDoc.simulationType == "One Weight" || this.dataDoc.simulationType == "Inclined Plane" || this.dataDoc.simulationType == "Pendulum") &&

Resources

} {this.dataDoc.simulationType == "One Weight" && ( )} {this.dataDoc.simulationType == "Inclined Plane" && ( )} {this.dataDoc.simulationType == "Pendulum" && ( )}
)} {this.dataDoc.mode == "Review" && this.dataDoc.simulationType == "Inclined Plane" && (

this.dataDoc.mode = ("Tutorial")} > {" "} Go to walkthrough{" "}

)} {this.dataDoc.mode == "Freeform" && (
{this.dataDoc.simulationType == "One Weight" && ( this.dataDoc.elasticCollisions= (!this.dataDoc.elasticCollisions) } /> } label="Make collisions elastic" labelPlacement="start" /> )} this.dataDoc.showForces = (!this.dataDoc.showForces)} /> } label="Show force vectors" labelPlacement="start" /> {(this.dataDoc.simulationType == "Inclined Plane" || this.dataDoc.simulationType == "Pendulum") && ( this.dataDoc.showComponentForces = (!this.dataDoc.showComponentForces) } /> } label="Show component force vectors" labelPlacement="start" /> )} this.dataDoc.showAcceleration = (!this.dataDoc.showAcceleration)} /> } label="Show acceleration vector" labelPlacement="start" /> this.dataDoc.showVelocity = (!this.dataDoc.showVelocity)} /> } label="Show velocity vector" labelPlacement="start" /> Speed} lowerBound={1} changeValue={this.dataDoc.simulationSpeed} //TODO - deal with input field change value now that datadoc is being used! step={1} unit={"x"} upperBound={10} value={this.dataDoc.simulationSpeed} labelWidth={"5em"} /> {this.dataDoc.simulationPaused && this.dataDoc.simulationType != "Circular Motion" && ( Gravity} lowerBound={-30} changeValue={this.dataDoc.gravity} step={0.01} unit={"m/s2"} upperBound={0} value={this.dataDoc.gravity} effect={(val: number) => { this.dataDoc.resetAll = (!this.dataDoc.resetAll); }} labelWidth={"5em"} /> )} {this.dataDoc.simulationPaused && this.dataDoc.simulationType != "Pulley" && ( Mass} lowerBound={1} changeValue={this.dataDoc.mass} step={0.1} unit={"kg"} upperBound={5} value={this.dataDoc.mass} effect={(val: number) => { this.dataDoc.resetAll = (!this.dataDoc.resetAll); }} labelWidth={"5em"} /> )} {this.dataDoc.simulationPaused && this.dataDoc.simulationType == "Pulley" && ( Red mass} lowerBound={1} changeValue={this.dataDoc.mass} step={0.1} unit={"kg"} upperBound={5} value={this.dataDoc.mass} effect={(val: number) => { this.dataDoc.resetAll = (!this.dataDoc.resetAll); }} labelWidth={"5em"} /> )} {this.dataDoc.simulationPaused && this.dataDoc.simulationType == "Pulley" && ( Blue mass} lowerBound={1} changeValue={this.dataDoc.mass2} step={0.1} unit={"kg"} upperBound={5} value={this.dataDoc.mass2} effect={(val: number) => { this.dataDoc.resetAll = (!this.dataDoc.resetAll); }} labelWidth={"5em"} /> )} {this.dataDoc.simulationPaused && this.dataDoc.simulationType == "Circular Motion" && ( Rod length} lowerBound={100} changeValue={this.dataDoc.circularMotionRadius} step={5} unit={"kg"} upperBound={250} value={this.dataDoc.circularMotionRadius} effect={(val: number) => { this.dataDoc.resetAll = (!this.dataDoc.resetAll); }} labelWidth={"5em"} /> )} {this.dataDoc.simulationType == "Spring" && this.dataDoc.simulationPaused && (
Spring stiffness } lowerBound={0.1} changeValue={this.dataDoc.springConstant} step={1} unit={"N/m"} upperBound={500} value={this.dataDoc.springConstant} effect={(val: number) => { this.dataDoc.simulationReset(!this.dataDoc.simulationReset); }} radianEquivalent={false} mode={"Freeform"} labelWidth={"7em"} /> Rest length} lowerBound={10} changeValue={this.dataDoc.springRestLength} step={100} unit={""} upperBound={500} value={this.dataDoc.springRestLength} effect={(val: number) => { this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); }} radianEquivalent={false} mode={"Freeform"} labelWidth={"7em"} /> Starting displacement } lowerBound={-(this.dataDoc.springRestLength - 10)} changeValue={(val: number) => {}} step={10} unit={""} upperBound={this.dataDoc.springRestLength} value={this.dataDoc.springStartLength - this.dataDoc.springRestLength} 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"} />
)} {this.dataDoc.simulationType == "Inclined Plane" && this.dataDoc.simulationPaused && (
θ} lowerBound={0} changeValue={this.dataDoc.wedgeAngle} step={1} unit={"°"} upperBound={49} value={this.dataDoc.wedgeAngle} effect={(val: number) => { this.changeWedgeBasedOnNewAngle(val); this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); }} radianEquivalent={true} mode={"Freeform"} labelWidth={"2em"} /> μs } lowerBound={0} changeValue={this.dataDoc.coefficientOfStaticFriction} step={0.1} unit={""} upperBound={1} value={this.dataDoc.coefficientOfStaticFriction} 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"} /> μk } lowerBound={0} changeValue={this.dataDoc.coefficientOfKineticFriction} step={0.1} unit={""} upperBound={Number(this.dataDoc.coefficientOfStaticFriction)} value={this.dataDoc.coefficientOfKineticFriction} effect={(val: number) => { this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); }} mode={"Freeform"} labelWidth={"2em"} />
)} {this.dataDoc.simulationType == "Inclined Plane" && !this.dataDoc.simulationPaused && ( θ: {Math.round(Number(this.dataDoc.wedgeAngle) * 100) / 100}° ≈{" "} {Math.round(((Number(this.dataDoc.wedgeAngle) * Math.PI) / 180) * 100) / 100}{" "} rad
μ s: {this.dataDoc.coefficientOfStaticFriction}
μ k: {this.dataDoc.coefficientOfKineticFriction}
)} {this.dataDoc.simulationType == "Pendulum" && !this.dataDoc.simulationPaused && ( θ: {Math.round(this.dataDoc.pendulumAngle * 100) / 100}° ≈{" "} {Math.round(((this.dataDoc.pendulumAngle * Math.PI) / 180) * 100) / 100}{" "} rad )} {this.dataDoc.simulationType == "Pendulum" && this.dataDoc.simulationPaused && (
Angle} lowerBound={0} changeValue={this.dataDoc.pendulumAngle} step={1} unit={"°"} upperBound={59} value={this.dataDoc.pendulumAngle} 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 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); 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"} /> Rod length} lowerBound={0} changeValue={this.dataDoc.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.mode == "Freeform" && ( {(!this.dataDoc.simulationPaused || this.dataDoc.simulationType == "Inclined Plane" || this.dataDoc.simulationType == "Suspension" || this.dataDoc.simulationType == "Circular Motion" || this.dataDoc.simulationType == "Pulley") && ( )}{" "} {this.dataDoc.simulationPaused && this.dataDoc.simulationType != "Inclined Plane" && this.dataDoc.simulationType != "Suspension" && this.dataDoc.simulationType != "Circular Motion" && this.dataDoc.simulationType != "Pulley" && ( )}{" "} {(!this.dataDoc.simulationPaused || this.dataDoc.simulationType == "Inclined Plane" || this.dataDoc.simulationType == "Suspension" || this.dataDoc.simulationType == "Circular Motion" || this.dataDoc.simulationType == "Pulley") && ( )}{" "} {this.dataDoc.simulationPaused && this.dataDoc.simulationType != "Inclined Plane" && this.dataDoc.simulationType != "Suspension" && this.dataDoc.simulationType != "Circular Motion" && this.dataDoc.simulationType != "Pulley" && ( )}{" "} {(!this.dataDoc.simulationPaused || (this.dataDoc.simulationType != "One Weight" && this.dataDoc.simulationType != "Circular Motion")) && ( )}{" "} {this.dataDoc.simulationPaused && (this.dataDoc.simulationType == "One Weight" || this.dataDoc.simulationType == "Circular Motion") && ( )}{" "} {(!this.dataDoc.simulationPaused || this.dataDoc.simulationType != "One Weight") && ( )}{" "} {this.dataDoc.simulationPaused && this.dataDoc.simulationType == "One Weight" && ( )}{" "}
{this.dataDoc.simulationType == "Pulley" ? "Red Weight" : ""} X Y
{ // window.open( // "https://www.khanacademy.org/science/physics/two-dimensional-motion" // ); // }} > Position {this.dataDoc.positionXDisplay} m { this.dataDoc.displayChange = ({ xDisplay: value, yDisplay: this.dataDoc.positionYDisplay, }); }} small={true} mode={"Freeform"} /> {this.dataDoc.positionYDisplay} m { this.dataDoc.displayChange = ({ xDisplay: this.dataDoc.positionXDisplay, yDisplay: value, }); }} small={true} mode={"Freeform"} />
{ // window.open( // "https://www.khanacademy.org/science/physics/two-dimensional-motion" // ); // }} > Velocity {this.dataDoc.velocityXDisplay} m/s { this.dataDoc.startVelX = (value); this.dataDoc.simulationReset = (!this.dataDoc.simulationReset); }} small={true} mode={"Freeform"} /> {this.dataDoc.velocityYDisplay} m/s { this.dataDoc.startVelY = (-value); this.dataDoc.displayChange = ({ xDisplay: this.dataDoc.positionXDisplay, yDisplay: this.dataDoc.positionYDisplay, }); }} small={true} mode={"Freeform"} />
{ // window.open( // "https://www.khanacademy.org/science/physics/two-dimensional-motion" // ); // }} > Acceleration {this.dataDoc.accelerationXDisplay} m/s2 {this.dataDoc.accelerationYDisplay} m/s2
Momentum {Math.round(this.dataDoc.velocityXDisplay * this.dataDoc.mass * 10) / 10} kg*m/s {Math.round(this.dataDoc.velocityYDisplay * this.dataDoc.mass * 10) / 10} kg*m/s
)} {this.dataDoc.mode == "Freeform" && this.dataDoc.simulationType == "Pulley" && (
Blue Weight X Y
Position {this.dataDoc.positionXDisplay2} m {this.dataDoc.positionYDisplay2} m
Velocity {this.dataDoc.velocityXDisplay2} m/s {this.dataDoc.velocityYDisplay2} m/s
Acceleration {this.dataDoc.accelerationXDisplay2} m/s2 {this.dataDoc.accelerationYDisplay2} m/s2
Momentum {Math.round(this.dataDoc.velocityXDisplay2 * this.dataDoc.mass * 10) / 10} kg*m/s {Math.round(this.dataDoc.velocityYDisplay2 * this.dataDoc.mass * 10) / 10} kg*m/s
)}
{this.dataDoc.simulationType != "Pendulum" && this.dataDoc.simulationType != "Spring" && (

Kinematic Equations

  • Position: x1=x0+v0t+ 12at 2
  • Velocity: v1=v0+at
  • Acceleration: a = F/m
)} {this.dataDoc.simulationType == "Spring" && (

Harmonic Motion Equations: Spring

  • Spring force: Fs=kd
  • Spring period: Ts=2π√mk
  • Equilibrium displacement for vertical spring: d = mg/k
  • Elastic potential energy: Us=12kd2
    • Maximum when system is at maximum displacement, 0 when system is at 0 displacement
  • Translational kinetic energy: K=12mv2
    • Maximum when system is at maximum/minimum velocity (at 0 displacement), 0 when velocity is 0 (at maximum displacement)
)} {this.dataDoc.simulationType == "Pendulum" && (

Harmonic Motion Equations: Pendulum

  • Pendulum period: Tp=2π√lg
)}

{this.dataDoc.simulationType == "Circular Motion" ? "Z" : "Y"}

X

} }