aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbrynnchernosky <56202540+brynnchernosky@users.noreply.github.com>2023-05-01 14:34:04 -0400
committerbrynnchernosky <56202540+brynnchernosky@users.noreply.github.com>2023-05-01 14:34:04 -0400
commitabeb2b59b46e2c6de224d53b341495c162c75c7a (patch)
tree0ffb4bee4315eae99fc5b88c3fd56d7510ce16a2
parenta936f7c60c56e4a92aed99cae8e4194fa7fdeda5 (diff)
add box
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx1302
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>&theta;</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>
+ &mu;<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>
+ &mu;<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>
+ &theta;: {Math.round(Number(wedgeAngle) * 100) / 100}° ≈{" "}
+ {Math.round(((Number(wedgeAngle) * Math.PI) / 180) * 100) /
+ 100}{" "}
+ rad
+ <br />
+ &mu; <sub>s</sub>: {coefficientOfStaticFriction}
+ <br />
+ &mu; <sub>k</sub>: {coefficientOfKineticFriction}
+ </Typography>
+ )}
+ {simulationType == "Pendulum" && !simulationPaused && (
+ <Typography>
+ &theta;: {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>&frasl;
+ <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&pi;&#8730;<sup>m</sup>&frasl;
+ <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>&frasl;
+ <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>&frasl;
+ <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&pi;&#8730;<sup>l</sup>&frasl;
+ <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