diff options
Diffstat (limited to 'src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx')
-rw-r--r-- | src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx | 1302 |
1 files changed, 1302 insertions, 0 deletions
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx index 88338d9b8..101cf1d4a 100644 --- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx @@ -1178,7 +1178,1309 @@ export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<Fi 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, + ]); + } + + // Timer for animating the simulation, update every 0.05 seconds + setInterval(() => { + setTimer(timer + 1); + }, 50); + render () { + <div className="physicsSimApp"> + <div className="mechanicsSimulationContainer"> + <div + className="mechanicsSimulationContentContainer" + > + <div className="mechanicsSimulationButtonsAndElements"> + <div className="mechanicsSimulationButtons"> + {!this.dataDoc.simulationPaused && ( + <div + style={{ + position: "fixed", + left: "10vw", + top: "95vh", + width: "50vw", + }} + > + <LinearProgress /> + </div> + )} + <div + style={{ + position: "fixed", + top: 1 + "em", + left: xMin + 12 + "px", + }} + > + <div className="dropdownMenu"> + <select + value={this.dataDoc.simulationType} + onChange={(event) => { + this.dataDoc.simulationType = (event.target.value); + // TODO change simulation type based on mode/type + }} + 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> + </div> + <div className="mechanicsSimulationElements"> + <Weight + dataDoc={this.dataDoc} + 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} + incrementTime={this.dataDoc.timer} + 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={this.radius} + 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} + 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} + 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} + incrementTime={this.dataDoc.timer} + 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={this.dataDoc.radius} + 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.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={mode} + onChange={(event) => { + this.dataDoc.mode = (event.target.value); + + // TODO setup simulation based on type and mode + }} + 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> + </div> + )} + {this.dataDoc.mode == "Review" && this.dataDoc.simulationType == "Inclined Plane" && ( + <div> + {!this.dataDoc.hintDialogueOpen && ( + <IconButton + onClick={() => { + this.dataDoc.hintDialogueOpen = (true); + }} + sx={{ + position: "fixed", + left: xMax - 50 + "px", + top: 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.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="answer">{this.dataDoc.answerInputFields}</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={stepNumber == 0} + > + <ArrowLeftIcon /> + </IconButton> + <div> + <h3> + Step {this.dataDoc.stepNumber + 1}:{" "} + {this.dataDoc.selectedTutorial.steps[stepNumber].description} + </h3> + <p>{this.dataDoc.selectedTutorial.steps[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.generateInputFieldsForQuestion(true); + }} + variant="outlined" + > + <p>Submit</p> + </Button> + <Button + onClick={() => this.generateNewQuestion()} + 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} + changeValue={setSimulationSpeed} + step={1} + unit={"x"} + upperBound={10} + value={simulationSpeed} + labelWidth={"5em"} + /> + {simulationPaused && simulationType != "Circular Motion" && ( + <InputField + label={<Box>Gravity</Box>} + lowerBound={-30} + changeValue={setGravity} + step={0.01} + unit={"m/s2"} + upperBound={0} + value={gravity} + effect={(val: number) => { + setResetAll(!resetAll); + }} + labelWidth={"5em"} + /> + )} + {simulationPaused && simulationType != "Pulley" && ( + <InputField + label={<Box>Mass</Box>} + lowerBound={1} + changeValue={setMass} + step={0.1} + unit={"kg"} + upperBound={5} + value={mass} + effect={(val: number) => { + setResetAll(!resetAll); + }} + labelWidth={"5em"} + /> + )} + {simulationPaused && simulationType == "Pulley" && ( + <InputField + label={<Box>Red mass</Box>} + lowerBound={1} + changeValue={setMass} + step={0.1} + unit={"kg"} + upperBound={5} + value={mass} + effect={(val: number) => { + setResetAll(!resetAll); + }} + labelWidth={"5em"} + /> + )} + {simulationPaused && simulationType == "Pulley" && ( + <InputField + label={<Box>Blue mass</Box>} + lowerBound={1} + changeValue={setMass2} + step={0.1} + unit={"kg"} + upperBound={5} + value={mass2} + effect={(val: number) => { + setResetAll(!resetAll); + }} + labelWidth={"5em"} + /> + )} + {simulationPaused && simulationType == "Circular Motion" && ( + <InputField + label={<Box>Rod length</Box>} + lowerBound={100} + changeValue={setCircularMotionRadius} + step={5} + unit={"kg"} + upperBound={250} + value={circularMotionRadius} + effect={(val: number) => { + setResetAll(!resetAll); + }} + labelWidth={"5em"} + /> + )} + </FormGroup> + </FormControl> + {simulationType == "Spring" && simulationPaused && ( + <div> + <InputField + label={ + <Typography color="inherit">Spring stiffness</Typography> + } + lowerBound={0.1} + changeValue={setSpringConstant} + step={1} + unit={"N/m"} + upperBound={500} + value={springConstant} + effect={(val: number) => { + setSimulationReset(!simulationReset); + }} + radianEquivalent={false} + mode={"Freeform"} + labelWidth={"7em"} + /> + <InputField + label={<Typography color="inherit">Rest length</Typography>} + lowerBound={10} + changeValue={setSpringRestLength} + step={100} + unit={""} + upperBound={500} + value={springRestLength} + effect={(val: number) => { + setSimulationReset(!simulationReset); + }} + radianEquivalent={false} + mode={"Freeform"} + labelWidth={"7em"} + /> + <InputField + label={ + <Typography color="inherit"> + Starting displacement + </Typography> + } + lowerBound={-(springRestLength - 10)} + changeValue={(val: number) => {}} + step={10} + unit={""} + upperBound={springRestLength} + value={springStartLength - springRestLength} + effect={(val: number) => { + setStartPosY(springRestLength + val); + setSpringStartLength(springRestLength + val); + setSimulationReset(!simulationReset); + }} + radianEquivalent={false} + mode={"Freeform"} + labelWidth={"7em"} + /> + </div> + )} + {simulationType == "Inclined Plane" && simulationPaused && ( + <div> + <InputField + label={<Box>θ</Box>} + lowerBound={0} + changeValue={setWedgeAngle} + step={1} + unit={"°"} + upperBound={49} + value={wedgeAngle} + effect={(val: number) => { + changeWedgeBasedOnNewAngle(val); + setSimulationReset(!simulationReset); + }} + radianEquivalent={true} + mode={"Freeform"} + labelWidth={"2em"} + /> + <InputField + label={ + <Box> + μ<sub>s</sub> + </Box> + } + lowerBound={0} + changeValue={setCoefficientOfStaticFriction} + step={0.1} + unit={""} + upperBound={1} + value={coefficientOfStaticFriction} + effect={(val: number) => { + updateForcesWithFriction(val); + if (val < Number(coefficientOfKineticFriction)) { + setCoefficientOfKineticFriction(val); + } + setSimulationReset(!simulationReset); + }} + mode={"Freeform"} + labelWidth={"2em"} + /> + <InputField + label={ + <Box> + μ<sub>k</sub> + </Box> + } + lowerBound={0} + changeValue={setCoefficientOfKineticFriction} + step={0.1} + unit={""} + upperBound={Number(coefficientOfStaticFriction)} + value={coefficientOfKineticFriction} + effect={(val: number) => { + setSimulationReset(!simulationReset); + }} + mode={"Freeform"} + labelWidth={"2em"} + /> + </div> + )} + {simulationType == "Inclined Plane" && !simulationPaused && ( + <Typography> + θ: {Math.round(Number(wedgeAngle) * 100) / 100}° ≈{" "} + {Math.round(((Number(wedgeAngle) * Math.PI) / 180) * 100) / + 100}{" "} + rad + <br /> + μ <sub>s</sub>: {coefficientOfStaticFriction} + <br /> + μ <sub>k</sub>: {coefficientOfKineticFriction} + </Typography> + )} + {simulationType == "Pendulum" && !simulationPaused && ( + <Typography> + θ: {Math.round(pendulumAngle * 100) / 100}° ≈{" "} + {Math.round(((pendulumAngle * Math.PI) / 180) * 100) / 100}{" "} + rad + </Typography> + )} + {simulationType == "Pendulum" && simulationPaused && ( + <div> + <InputField + label={<Box>Angle</Box>} + lowerBound={0} + changeValue={setPendulumAngle} + step={1} + unit={"°"} + upperBound={59} + value={pendulumAngle} + effect={(value) => { + setStartPendulumAngle(value); + if (simulationType == "Pendulum") { + const mag = + mass * + Math.abs(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(gravity) * + Math.cos((value * Math.PI) / 180), + directionInDegrees: 270 - value, + component: true, + }; + const gravityPerpendicular: IForce = { + description: "Gravity Perpendicular Component", + magnitude: + Math.abs(gravity) * + Math.sin((value * Math.PI) / 180), + directionInDegrees: -value, + component: true, + }; + + const length = pendulumLength; + const x = + length * Math.cos(((90 - value) * Math.PI) / 180); + const y = + length * Math.sin(((90 - value) * Math.PI) / 180); + const xPos = xMax / 2 - x - radius; + const yPos = y - radius - 5; + setStartPosX(xPos); + setStartPosY(yPos); + + setStartForces([ + { + description: "Gravity", + magnitude: Math.abs(gravity) * mass, + directionInDegrees: 270, + component: false, + }, + forceOfTension, + ]); + setUpdatedForces([ + { + description: "Gravity", + magnitude: Math.abs(gravity) * mass, + directionInDegrees: 270, + component: false, + }, + forceOfTension, + ]); + setComponentForces([ + tensionComponent, + gravityParallel, + gravityPerpendicular, + ]); + setAdjustPendulumAngle({ + angle: value, + length: pendulumLength, + }); + setSimulationReset(!simulationReset); + } + }} + radianEquivalent={true} + mode={"Freeform"} + labelWidth={"5em"} + /> + <InputField + label={<Box>Rod length</Box>} + lowerBound={0} + changeValue={setPendulumLength} + step={1} + unit={"m"} + upperBound={400} + value={Math.round(pendulumLength)} + effect={(value) => { + if (simulationType == "Pendulum") { + setAdjustPendulumAngle({ + angle: pendulumAngle, + length: value, + }); + setSimulationReset(!simulationReset); + } + }} + radianEquivalent={false} + mode={"Freeform"} + labelWidth={"5em"} + /> + </div> + )} + </div> + )} + <div className="mechanicsSimulationEquation"> + {mode == "Freeform" && ( + <table> + <tbody> + <tr> + <td>{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> + {(!simulationPaused || + simulationType == "Inclined Plane" || + simulationType == "Suspension" || + simulationType == "Circular Motion" || + simulationType == "Pulley") && ( + <td style={{ cursor: "default" }}> + {positionXDisplay} m + </td> + )}{" "} + {simulationPaused && + simulationType != "Inclined Plane" && + simulationType != "Suspension" && + simulationType != "Circular Motion" && + simulationType != "Pulley" && ( + <td + style={{ + cursor: "default", + }} + > + <InputField + lowerBound={0} + changeValue={setPositionXDisplay} + step={1} + unit={"m"} + upperBound={xMax - 110} + value={positionXDisplay} + effect={(value) => { + setDisplayChange({ + xDisplay: value, + yDisplay: positionYDisplay, + }); + }} + small={true} + mode={"Freeform"} + /> + </td> + )}{" "} + {(!simulationPaused || + simulationType == "Inclined Plane" || + simulationType == "Suspension" || + simulationType == "Circular Motion" || + simulationType == "Pulley") && ( + <td style={{ cursor: "default" }}> + {positionYDisplay} m + </td> + )}{" "} + {simulationPaused && + simulationType != "Inclined Plane" && + simulationType != "Suspension" && + simulationType != "Circular Motion" && + simulationType != "Pulley" && ( + <td + style={{ + cursor: "default", + }} + > + <InputField + lowerBound={0} + changeValue={setPositionYDisplay} + step={1} + unit={"m"} + upperBound={yMax - 110} + value={positionYDisplay} + effect={(value) => { + setDisplayChange({ + xDisplay: positionXDisplay, + yDisplay: value, + }); + }} + 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> + {(!simulationPaused || + (simulationType != "One Weight" && + simulationType != "Circular Motion")) && ( + <td style={{ cursor: "default" }}> + {velocityXDisplay} m/s + </td> + )}{" "} + {simulationPaused && + (simulationType == "One Weight" || + simulationType == "Circular Motion") && ( + <td + style={{ + cursor: "default", + }} + > + <InputField + lowerBound={-50} + changeValue={setVelocityXDisplay} + step={1} + unit={"m/s"} + upperBound={50} + value={velocityXDisplay} + effect={(value) => { + setStartVelX(value); + setSimulationReset(!simulationReset); + }} + small={true} + mode={"Freeform"} + /> + </td> + )}{" "} + {(!simulationPaused || simulationType != "One Weight") && ( + <td style={{ cursor: "default" }}> + {velocityYDisplay} m/s + </td> + )}{" "} + {simulationPaused && simulationType == "One Weight" && ( + <td + style={{ + cursor: "default", + }} + > + <InputField + lowerBound={-50} + changeValue={setVelocityYDisplay} + step={1} + unit={"m/s"} + upperBound={50} + value={velocityYDisplay} + effect={(value) => { + setStartVelY(-value); + setDisplayChange({ + xDisplay: positionXDisplay, + yDisplay: 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" }}> + {accelerationXDisplay} m/s<sup>2</sup> + </td> + <td style={{ cursor: "default" }}> + {accelerationYDisplay} m/s<sup>2</sup> + </td> + </tr> + <tr> + <td> + <Box>Momentum</Box> + </td> + <td> + {Math.round(velocityXDisplay * mass * 10) / 10} kg*m/s + </td> + <td> + {Math.round(velocityYDisplay * mass * 10) / 10} kg*m/s + </td> + </tr> + </tbody> + </table> + )} + {mode == "Freeform" && 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" }}>{positionXDisplay2} m</td> + <td style={{ cursor: "default" }}>{positionYDisplay2} m</td> + </tr> + <tr> + <td> + <Box>Velocity</Box> + </td> + <td style={{ cursor: "default" }}> + {velocityXDisplay2} m/s + </td> + + <td style={{ cursor: "default" }}> + {velocityYDisplay2} m/s + </td> + </tr> + <tr> + <td> + <Box>Acceleration</Box> + </td> + <td style={{ cursor: "default" }}> + {accelerationXDisplay2} m/s<sup>2</sup> + </td> + <td style={{ cursor: "default" }}> + {accelerationYDisplay2} m/s<sup>2</sup> + </td> + </tr> + <tr> + <td> + <Box>Momentum</Box> + </td> + <td> + {Math.round(velocityXDisplay2 * mass * 10) / 10} kg*m/s + </td> + <td> + {Math.round(velocityYDisplay2 * mass * 10) / 10} kg*m/s + </td> + </tr> + </tbody> + </table> + )} + </div> + {simulationType != "Pendulum" && 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> + )} + {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> + )} + {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 + style={{ + position: "fixed", + top: window.innerHeight - 120 + 20 + "px", + left: 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: window.innerHeight - 120 + 40 + "px", + left: xMin + 90 - 80 + "px", + }} + > + {simulationType == "Circular Motion" ? "Z" : "Y"} + </p> + <p + style={{ + position: "fixed", + top: window.innerHeight - 120 + 80 + "px", + left: xMin + 90 - 40 + "px", + }} + > + X + </p> + </div> + </div> }
\ No newline at end of file |