aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbrynnchernosky <56202540+brynnchernosky@users.noreply.github.com>2023-02-12 15:34:16 -0500
committerbrynnchernosky <56202540+brynnchernosky@users.noreply.github.com>2023-02-12 15:34:16 -0500
commitcb19032c67d810d3683e101896e20df7eecec77c (patch)
treec7dc6ffab467ae33346e91fd2f8dc87f6b2d7fae /src
parent456d91f480ed96ac963c9f17164eb23e76fb4320 (diff)
start making everything persistent
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/PhysicsSimulationBox.tsx573
1 files changed, 556 insertions, 17 deletions
diff --git a/src/client/views/nodes/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsSimulationBox.tsx
index 4a68ba5aa..3ea4fba9f 100644
--- a/src/client/views/nodes/PhysicsSimulationBox.tsx
+++ b/src/client/views/nodes/PhysicsSimulationBox.tsx
@@ -3,33 +3,572 @@ import { FieldView, FieldViewProps } from './FieldView';
import React = require('react');
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { observer } from 'mobx-react';
-import App from './PhysicsSimulationApp';
+import "./PhysicsSimulationBox.scss";
+import Weight from "./PhysicsSimulationWeight";
+import Wall from "./PhysicsSimulationWall"
+import Wedge from "./PhysicsSimulationWedge"
export interface IForce {
- description: string;
- magnitude: number;
- directionInDegrees: number;
+ description: string;
+ magnitude: number;
+ directionInDegrees: number;
}
export interface IWallProps {
- length: number;
- xPos: number;
- yPos: number;
- angleInDegrees: number;
+ length: number;
+ xPos: number;
+ yPos: number;
+ angleInDegrees: number;
+}
+
+interface PhysicsVectorTemplate {
+ top: number;
+ left: number;
+ width: number;
+ height: number;
+ x1: number;
+ y1: number;
+ x2: number;
+ y2: number;
+ weightX: number;
+ weightY: number;
+}
+
+interface IState {
+ accelerationXDisplay: number,
+ accelerationYDisplay: number,
+ adjustPendulumAngle: {angle: number, length: number},
+ coefficientOfKineticFriction: number,
+ coefficientOfStaticFriction: number,
+ currentForceSketch: PhysicsVectorTemplate | null,
+ deleteMode: boolean,
+ displayChange: {xDisplay: number, yDisplay: number},
+ elasticCollisions: boolean,
+ forceSketches: PhysicsVectorTemplate[],
+ pendulum: boolean,
+ pendulumAngle: number,
+ pendulumLength: number,
+ positionXDisplay: number,
+ positionYDisplay: number,
+ showAcceleration: boolean,
+ showForceMagnitudes: boolean,
+ showForces: boolean,
+ showVelocity: boolean,
+ simulationPaused: boolean,
+ simulationReset: boolean,
+ simulationType: "Inclined Plane",
+ sketching: boolean,
+ startForces: IForce[],
+ startPendulumAngle: number,
+ startPosX: number,
+ startPosY: number,
+ stepNumber: number,
+ timer: number,
+ updatedForces: IForce[],
+ velocityXDisplay: number,
+ velocityYDisplay: number,
+ wallPositions: IWallProps[],
+ wedge: boolean,
+ wedgeAngle: number,
+ wedgeHeight: number,
+ wedgeWidth: number,
+ weight: boolean,
}
@observer
export default class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PhysicsSimulationBox, fieldKey); }
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PhysicsSimulationBox, fieldKey); }
+
+ // Constants
+ gravityMagnitude = 9.81;
+ forceOfGravity: IForce = {
+ description: "Gravity",
+ magnitude: this.gravityMagnitude,
+ directionInDegrees: 270,
+ };
+ xMin = 0;
+ yMin = 0;
+ xMax = 300;
+ yMax = 300;
+ color = `rgba(0,0,0,0.5)`;
+ radius = 0.1*this.yMax
+
+ constructor(props: any) {
+ super(props);
+ this.dataDoc.accelerationXDisplay = 0;
+ this.dataDoc.accelerationYDisplay = 0;
+ this.dataDoc.adjustPendulumAngle = {angle: 0, length: 0};
+ this.dataDoc.coefficientOfKineticFriction = 0;
+ this.dataDoc.coefficientOfStaticFriction = 0;
+ this.dataDoc.currentForceSketch = [];
+ this.dataDoc.deleteMode = false;
+ this.dataDoc.displayChange = {xDisplay: 0, yDisplay: 0};
+ this.dataDoc.elasticCollisions = false;
+ this.dataDoc.forceSketches = [];
+ this.dataDoc.pendulum = false;
+ this.dataDoc.pendulumAngle = 0;
+ this.dataDoc.pendulumLength = 300;
+ this.dataDoc.positionXDisplay = 0;
+ this.dataDoc.positionYDisplay = 0;
+ this.dataDoc.showAcceleration = false;
+ this.dataDoc.showForceMagnitudes = false;
+ this.dataDoc.showForces = false;
+ this.dataDoc.showVelocity = false;
+ this.dataDoc.simulationPaused = true;
+ this.dataDoc.simulationReset = false;
+ this.dataDoc.simulationType = "Inclined Plane";
+ this.dataDoc.sketching = false;
+ this.dataDoc.startForces = [this.forceOfGravity];
+ this.dataDoc.startPendulumAngle = 0;
+ this.dataDoc.startPosX = 0;
+ this.dataDoc.startPosY = 0;
+ this.dataDoc.stepNumber = 0;
+ this.dataDoc.timer = 0;
+ this.dataDoc.updatedForces = [this.forceOfGravity];
+ this.dataDoc.velocityXDisplay = 0;
+ this.dataDoc.velocityYDisplay = 0;
+ this.dataDoc.wallPositions = [];
+ this.dataDoc.wedge = false;
+ this.dataDoc.wedgeAngle = 26;
+ this.dataDoc.wedgeHeight = Math.tan((26 * Math.PI) / 180) * this.xMax*0.6;
+ this.dataDoc.wedgeWidth = this.xMax*0.6;
+ this.dataDoc.weight = false;
+ }
+
+ // Add one weight to the simulation
+ addWeight () {
+ this.dataDoc.weight = true;
+ this.dataDoc.wedge = false;
+ this.dataDoc.pendulum = false;
+ this.dataDoc.startPosY = this.yMin+this.radius;
+ this.dataDoc.startPosX = (this.xMax+this.xMin-this.radius)/2;
+ this.dataDoc.updatedForces = [this.forceOfGravity];
+ this.dataDoc.startForces = [this.forceOfGravity];
+ this.addWalls();
+ this.dataDoc.simulationReset = !this.dataDoc.simulationReset;
+ };
+
+ // Add a wedge with a One Weight to the simulation
+ addWedge () {
+ this.dataDoc.weight = true;
+ this.dataDoc.wedge = true;
+ this.dataDoc.pendulum = false;
+ this.changeWedgeBasedOnNewAngle(26);
+ this.addWalls();
+ this.dataDoc.startForces = [this.forceOfGravity];
+ this.updateForcesWithFriction(this.dataDoc.coefficientOfStaticFriction);
+ };
+
+ // Add a simple pendulum to the simulation
+ addPendulum = () => {
+ this.dataDoc.weight = true;
+ this.dataDoc.wedge = false;
+ this.dataDoc.pendulum = true;
+ let length = this.xMax*0.7;
+ let angle = 35;
+ let x = length * Math.cos(((90 - angle) * Math.PI) / 180);
+ let y = length * Math.sin(((90 - angle) * Math.PI) / 180);
+ let xPos = this.xMax / 2 - x - this.radius;
+ let yPos = y - this.radius;
+ this.dataDoc.startPosX = xPos;
+ this.dataDoc.startPosY = yPos;
+ let mag = 9.81 * Math.cos((angle * Math.PI) / 180);
+ let forceOfTension: IForce = {
+ description: "Tension",
+ magnitude: mag,
+ directionInDegrees: 90 - angle,
+ };
+ this.dataDoc.updatedForces = [this.forceOfGravity, forceOfTension];
+ this.dataDoc.startForces = [this.forceOfGravity, forceOfTension];
+ this.dataDoc.pendulumAngle = angle;
+ this.dataDoc.pendulumLength = length;
+ this.dataDoc.adjustPendulumAngle = {angle: angle, length: length};
+ this.removeWalls();
+ };
+
+ // Update forces when coefficient of static friction changes in freeform mode
+ updateForcesWithFriction (
+ coefficient: number,
+ width: number = this.dataDoc.wedgeWidth,
+ height: number = this.dataDoc.wedgeHeight
+ ) {
+ let normalForce = {
+ description: "Normal Force",
+ magnitude: this.forceOfGravity.magnitude * Math.cos(Math.atan(height / width)),
+ directionInDegrees:
+ 180 - 90 - (Math.atan(height / width) * 180) / Math.PI,
+ };
+ let frictionForce: IForce = {
+ description: "Static Friction Force",
+ magnitude:
+ coefficient *
+ this.forceOfGravity.magnitude *
+ Math.cos(Math.atan(height / width)),
+ directionInDegrees: 180 - (Math.atan(height / width) * 180) / Math.PI,
+ };
+ // reduce magnitude of friction force if necessary such that block cannot slide up plane
+ let yForce = -this.forceOfGravity.magnitude;
+ 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) +
+ this.forceOfGravity.magnitude) /
+ Math.sin((frictionForce.directionInDegrees * Math.PI) / 180);
+ }
+ if (coefficient != 0) {
+ this.dataDoc.startForces = [this.forceOfGravity, normalForce, frictionForce];
+ this.dataDoc.updatedForces = [this.forceOfGravity, normalForce, frictionForce];
+ } else {
+ this.dataDoc.startForces = [this.forceOfGravity, normalForce];
+ this.dataDoc.updatedForces = [this.forceOfGravity, normalForce];
+ }
+ };
+
+ // 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 = this.xMax*0.6;
+ height = Math.tan((angle * Math.PI) / 180) * width;
+ this.dataDoc.wedgeWidth = width;
+ this.dataDoc.wedgeHeight = height;
+ } else if (angle < 70) {
+ width = this.xMax*0.3;
+ height = Math.tan((angle * Math.PI) / 180) * width;
+ this.dataDoc.wedgeWidth = width;
+ this.dataDoc.wedgeHeight = height;
+ } else {
+ width = this.xMax*0.15;
+ height = Math.tan((angle * Math.PI) / 180) * width;
+ this.dataDoc.wedgeWidth = width;
+ this.dataDoc.wedgeHeight = height;
+ }
+
+ // update weight position based on updated wedge width/height
+ let xPos = (this.xMax * 0.2)-this.radius;
+ let yPos = width * Math.tan((angle * Math.PI) / 180) - this.radius;
+
+ this.dataDoc.startPosX = xPos;
+ this.dataDoc.startPosY = this.getDisplayYPos(yPos);
+ this.updateForcesWithFriction(
+ Number(this.dataDoc.coefficientOfStaticFriction),
+ width,
+ height
+ );
+ };
+
+ // Helper function to go between display and real values
+ getDisplayYPos = (yPos: number) => {
+ return this.yMax - yPos - 2 * 50 + 5;
+ };
+
+ // In review mode, edit force arrow sketch on mouse movement
+ editForce = (element: PhysicsVectorTemplate) => {
+ if (!this.dataDoc.sketching) {
+ let sketches = this.dataDoc.forceSketches.filter((sketch: PhysicsVectorTemplate) => sketch != element);
+ this.dataDoc.forceSketches = sketches;
+ this.dataDoc.currentForceSketch = element;
+ this.dataDoc.sketching = true;
+ }
+ };
+
+ // In review mode, used to delete force arrow sketch on SHIFT+click
+ deleteForce = (element: PhysicsVectorTemplate) => {
+ if (!this.dataDoc.sketching) {
+ let sketches = this.dataDoc.forceSketches.filter((sketch: PhysicsVectorTemplate) => sketch != element);
+ this.dataDoc.forceSketches = sketches;
+ }
+ };
+
+ // Remove floor and walls from simulation
+ removeWalls = () => {
+ this.dataDoc.wallPositions = []
+ };
+
+ // Add floor and walls to simulation
+ addWalls = () => {
+ if (this.dataDoc.wallPositions.length == 0) {
+ let walls = [];
+ walls.push({ length: 100, xPos: 0, yPos: 97, angleInDegrees: 0 });
+ walls.push({ length: 100, xPos: 0, yPos: 0, angleInDegrees: 90 });
+ walls.push({ length: 100, xPos: 97, yPos: 0, angleInDegrees: 90 });
+ this.dataDoc.wallPositions = walls
+ }
+ };
+
+
+ componentDidMount() {
+ // Add weight
+ this.addPendulum()
+
+ // Add listener for SHIFT key, which determines if sketch force arrow will be edited or deleted on click
+ document.addEventListener("keydown", (e) => {
+ if (e.shiftKey) {
+ this.dataDoc.deleteMode = true;
+ }
+ });
+ document.addEventListener("keyup", (e) => {
+ if (e.shiftKey) {
+ this.dataDoc.deleteMode = false;
+ }
+ });
- constructor(props: any) {
- super(props);
- }
+ // Timer for animating the simulation
+ setInterval(() => {
+ this.dataDoc.timer = this.dataDoc.timer+1;
+ }, 60);
+ }
render () {
return (
- <div className = "physicsSimulationContainer">
- <App/>
- </div>
- );
- }
+ <div>
+ <div className="mechanicsSimulationContainer">
+ <div
+ className="mechanicsSimulationContentContainer"
+ onPointerMove={(e) => {
+ // if (sketching) {
+ // x1 = positionXDisplay + 50;
+ // y1 = yMax - positionYDisplay - 2 * 50 + 5 + 50;
+ // x2 = e.clientX;
+ // y2 = e.clientY;
+ // height = Math.abs(y1 - y2) + 120;
+ // width = Math.abs(x1 - x2) + 120;
+ // top = Math.min(y1, y2) - 60;
+ // left = Math.min(x1, x2) - 60;
+ // x1Updated = x1 - left;
+ // x2Updated = x2 - left;
+ // y1Updated = y1 - top;
+ // y2Updated = y2 - top;
+ // setCurrentForceSketch({
+ // top: top,
+ // left: left,
+ // width: width,
+ // height: height,
+ // x1: x1Updated,
+ // y1: y1Updated,
+ // x2: x2Updated,
+ // y2: y2Updated,
+ // weightX: positionXDisplay,
+ // weightY: positionYDisplay,
+ // });
+ // }
+ }}
+ onPointerDown={(e) => {
+ // if (sketching && currentForceSketch) {
+ // setSketching(false);
+ // sketches = forceSketches;
+ // sketches.push(currentForceSketch);
+ // setForceSketches(sketches);
+ // setCurrentForceSketch(null);
+ // }
+ }}
+ >
+ <div className="mechanicsSimulationButtonsAndElements">
+ <div className="mechanicsSimulationElements">
+ {/* {showForces && currentForceSketch && simulationPaused && (
+ <div
+ style={{
+ position: "fixed",
+ top: currentForceSketch.top,
+ left: currentForceSketch.left,
+ }}
+ >
+ <svg
+ width={currentForceSketch.width + "px"}
+ height={currentForceSketch.height + "px"}
+ >
+ <defs>
+ <marker
+ id="sketchArrow"
+ markerWidth="10"
+ markerHeight="10"
+ refX="0"
+ refY="2"
+ orient="auto"
+ markerUnits="strokeWidth"
+ >
+ <path d="M0,0 L0,4 L6,2 z" fill={color} />
+ </marker>
+ </defs>
+ <line
+ x1={currentForceSketch.x1}
+ y1={currentForceSketch.y1}
+ x2={currentForceSketch.x2}
+ y2={currentForceSketch.y2}
+ stroke={color}
+ strokeWidth="10"
+ markerEnd="url(#sketchArrow)"
+ />
+ </svg>
+ </div>
+ )} */}
+ {/* {showForces &&
+ forceSketches.length > 0 &&
+ simulationPaused &&
+ forceSketches.map((element: PhysicsVectorTemplate, index) => {
+ return (
+ <div
+ key={index}
+ style={{
+ position: "fixed",
+ top: element.top + (positionYDisplay - element.weightY),
+ left:
+ element.left + (positionXDisplay - element.weightX),
+ }}
+ >
+ <svg
+ width={element.width + "px"}
+ height={element.height + "px"}
+ >
+ <defs>
+ <marker
+ id="sketchArrow"
+ markerWidth="10"
+ markerHeight="10"
+ refX="0"
+ refY="2"
+ orient="auto"
+ markerUnits="strokeWidth"
+ >
+ <path d="M0,0 L0,4 L6,2 z" fill={color} />
+ </marker>
+ </defs>
+ <line
+ x1={element.x1}
+ y1={element.y1}
+ x2={element.x2}
+ y2={element.y2}
+ stroke={color}
+ strokeWidth="10"
+ markerEnd="url(#sketchArrow)"
+ onClick={() => {
+ if (deleteMode) {
+ deleteForce(element);
+ } else {
+ editForce(element);
+ }
+ }}
+ />
+ </svg>
+ </div>
+ );
+ })} */}
+ {this.dataDoc.weight && (
+ <Weight
+ adjustPendulumAngle={this.dataDoc.adjustPendulumAngle}
+ color={"red"}
+ displayXPosition={this.dataDoc.positionXDisplay}
+ displayXVelocity={this.dataDoc.velocityXDisplay}
+ displayYPosition={this.dataDoc.positionYDisplay}
+ displayYVelocity={this.dataDoc.velocityYDisplay}
+ elasticCollisions={this.dataDoc.elasticCollisions}
+ incrementTime={this.dataDoc.timer}
+ mass={1}
+ paused={this.dataDoc.simulationPaused}
+ pendulum={this.dataDoc.pendulum}
+ pendulumAngle={this.dataDoc.pendulumAngle}
+ pendulumLength={this.dataDoc.pendulumLength}
+ radius={this.radius}
+ reset={this.dataDoc.simulationReset}
+ showForceMagnitudes={this.dataDoc.showForceMagnitudes}
+ setSketching={(val: boolean) => {this.dataDoc.sketching = val}}
+ setDisplayXAcceleration={(val: number) => {this.dataDoc.accelerationXDisplay = val}}
+ setDisplayXPosition={(val: number) => {this.dataDoc.positionXDisplay = val}}
+ setDisplayXVelocity={(val: number) => {this.dataDoc.velocityXDisplay = val}}
+ setDisplayYAcceleration={(val: number) => {this.dataDoc.accelerationYDisplay = val}}
+ setDisplayYPosition={(val: number) => {this.dataDoc.positionYDisplay = val}}
+ setDisplayYVelocity={(val: number) => {this.dataDoc.velocityYDisplay = val}}
+ setPaused={(val: boolean) => {this.dataDoc.simulationPaused = val}}
+ setPendulumAngle={(val: number) => {this.dataDoc.pendulumAngle = val}}
+ setPendulumLength={(val: number) => {this.dataDoc.pendulumLength = val}}
+ setStartPendulumAngle={(val: number) => {this.dataDoc.startPendulumAngle = val}}
+ setUpdatedForces={(val: IForce[]) => {this.dataDoc.updatedForces = val}}
+ showAcceleration={this.dataDoc.showAcceleration}
+ showForces={this.dataDoc.showForces}
+ showVelocity={this.dataDoc.showVelocity}
+ startForces={this.dataDoc.startForces}
+ startPosX={this.dataDoc.startPosX}
+ startPosY={this.dataDoc.startPosY}
+ timestepSize={0.002}
+ updateDisplay={this.dataDoc.displayChange}
+ updatedForces={this.dataDoc.updatedForces}
+ walls={this.dataDoc.wallPositions}
+ wedge={this.dataDoc.wedge}
+ wedgeHeight={this.dataDoc.wedgeHeight}
+ wedgeWidth={this.dataDoc.wedgeWidth}
+ coefficientOfKineticFriction={this.dataDoc.coefficientOfKineticFriction}
+ xMax={this.xMax}
+ yMax={this.yMax}
+ xMin={this.xMin}
+ yMin={this.yMin}
+ />
+ )}
+ {this.dataDoc.wedge && (
+ <Wedge
+ startWidth={this.dataDoc.wedgeWidth}
+ startHeight={this.dataDoc.wedgeHeight}
+ startLeft={this.xMax * 0.2}
+ xMax={this.xMax}
+ yMax={this.yMax}
+ />
+ )}
+ </div>
+ <div>
+ {this.dataDoc.wallPositions.map((element: { length: number; xPos: number; yPos: number; angleInDegrees: number; }, index: React.Key | null | undefined) => {
+ return (
+ <div key={index}>
+ <Wall
+ length={element.length}
+ xPos={element.xPos}
+ yPos={element.yPos}
+ angleInDegrees={element.angleInDegrees}
+ />
+ </div>
+ );
+ })}
+ </div>
+ </div>
+ </div>
+ <div className="mechanicsSimulationEquationContainer">
+ <div className="mechanicsSimulationControls">
+ <div>
+ {this.dataDoc.simulationPaused && (
+ <button onClick={() => {
+ this.dataDoc.simulationPaused = false}
+ } >START</button>
+ )}
+ {!this.dataDoc.simulationPaused && (
+ <button onClick={() => {
+ this.dataDoc.simulationPaused = true}
+ } >PAUSE</button>
+ )}
+ {this.dataDoc.simulationPaused && (
+ <button onClick={() => {
+ this.dataDoc.simulationReset = !this.dataDoc.simulationReset}
+ } >RESET</button>
+ )}
+ <button onClick={() => {
+ if (!this.dataDoc.pendulum && !this.dataDoc.wedge) {
+ this.addWedge()
+ }
+ else if (!this.dataDoc.pendulum && this.dataDoc.wedge) {
+ this.addPendulum()
+ }
+ else {
+ this.addWeight()
+ }
+ }} >TYPE</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
} \ No newline at end of file