aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss2
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx80
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx39
3 files changed, 59 insertions, 62 deletions
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss
index 5f142df19..ac2c611c7 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss
@@ -15,7 +15,7 @@
left: 60%;
padding: 1em;
height: 100%;
- overflow: auto;
+ transform-origin: top left;
.mechanicsSimulationControls {
display: flex;
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
index f4acba2a6..be8dbbd40 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
@@ -6,7 +6,7 @@ import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
import ReplayIcon from '@mui/icons-material/Replay';
import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControl, FormControlLabel, FormGroup, IconButton, LinearProgress, Stack } from '@mui/material';
import Typography from '@mui/material/Typography';
-import { computed, IReactionDisposer, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { NumListCast } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
@@ -76,6 +76,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
}
_widthDisposer: IReactionDisposer | undefined;
+ @observable _simReset = 0;
// semi-Constants
xMin = 0;
@@ -215,6 +216,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
directionInDegrees: 270,
});
+ @action
setupSimulation = () => {
const simulationType = this.simulationType;
const mode = this.simulationMode;
@@ -237,8 +239,6 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
this.dataDoc.mass1_positionX = (this.xMax + this.xMin) / 2 - this.mass1Radius;
this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]);
this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1)]);
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
- ``;
break;
case 'Inclined Plane': this.setupInclinedPlane(); break;
case 'Pendulum': this.setupPendulum(); break;
@@ -247,7 +247,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
case 'Pulley': this.setupPulley(); break;
case 'Suspension': this.setupSuspension();break;
}
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ this._simReset++;
} else if (mode == 'Review') {
this.dataDoc.simulation_showComponentForces = false;
this.dataDoc.simulation_showForceMagnitudes = true;
@@ -333,7 +333,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
this.dataDoc.simulation_showForceMagnitudes = tutorials.suspension.steps[0].showMagnitude;
break;
}
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ this._simReset++;
}
};
@@ -604,6 +604,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
};
// Default setup for uniform circular motion simulation
+ @action
setupCircular = (value: number) => {
this.dataDoc.simulation_showComponentForces = false;
this.dataDoc.mass1_velocityYstart = 0;
@@ -619,7 +620,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
};
this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce]);
this.dataDoc.mass1_forcesStart = JSON.stringify([tensionForce]);
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ this._simReset++;
};
setupInclinedPlane = () => {
@@ -662,6 +663,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
};
// Default setup for spring simulation
+ @action
setupSpring = () => {
this.dataDoc.simulation_showComponentForces = false;
this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1)]);
@@ -671,10 +673,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
this.dataDoc.spring_constant = 0.5;
this.dataDoc.spring_lengthRest = 200;
this.dataDoc.spring_lengthStart = 200;
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ this._simReset++;
};
// Default setup for suspension simulation
+ @action
setupSuspension = () => {
let xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius;
let yPos = this.yMin + 200;
@@ -696,10 +699,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
const gravity = this.gravityForce(this.mass1);
this.dataDoc.mass1_forcesUpdated = JSON.stringify([tensionForce1, tensionForce2, gravity]);
this.dataDoc.mass1_forcesStart = JSON.stringify([tensionForce1, tensionForce2, gravity]);
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ this._simReset++;
};
// Default setup for pulley simulation
+ @action
setupPulley = () => {
this.dataDoc.simulation_showComponentForces = false;
this.dataDoc.mass1_positionYstart = (this.yMax + this.yMin) / 2;
@@ -728,8 +732,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
this.dataDoc.mass2_positionX = (this.xMin + this.xMax) / 2 + 5;
this.dataDoc.mass2_forcesUpdated = JSON.stringify([gravityForce2, tensionForce2]);
this.dataDoc.mass2_forcesStart = JSON.stringify([gravityForce2, tensionForce2]);
-
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ this._simReset++;
};
public static parseJSON(json: string) {
@@ -799,12 +802,14 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
setSpringLength = (length: number) => {
this.dataDoc.spring_lengthStart = length;
};
+ resetRequest = () => this._simReset;
render() {
const commonWeightProps = {
pause: this.pause,
paused: BoolCast(this.dataDoc.simulation_paused),
panelWidth: this.props.PanelWidth,
panelHeight: this.props.PanelHeight,
+ resetRequest: this.resetRequest,
xMax: this.xMax,
xMin: this.xMin,
yMax: this.yMax,
@@ -830,7 +835,6 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
startPendulumAngle: this.pendulumAngleStart,
startPendulumLength: this.pendulumLengthStart,
radius: this.mass1Radius,
- reset: BoolCast(this.dataDoc.simulation_reset),
simulationSpeed: NumCast(this.dataDoc.simulation_speed, 2),
showAcceleration: BoolCast(this.dataDoc.simulation_showAcceleration),
showForceMagnitudes: BoolCast(this.dataDoc.simulation_showForceMagnitudes),
@@ -916,7 +920,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
</div>
</div>
</div>
- <div className="mechanicsSimulationEquationContainer">
+ <div className="mechanicsSimulationEquationContainer" style={{ overflow: 'auto', height: `${(1000 / this.props.PanelWidth()) * 100}%`, transform: `scale(${this.props.PanelWidth() / 1000})` }}>
<div className="mechanicsSimulationControls">
<Stack direction="row" spacing={1}>
{this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && (
@@ -930,7 +934,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
</IconButton>
)}
{this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && (
- <IconButton onClick={() => (this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset)}>
+ <IconButton onClick={action(() => this._simReset++)}>
<ReplayIcon />
</IconButton>
)}
@@ -1291,11 +1295,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
</p>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Button
- onClick={() => {
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ onClick={action(() => {
+ this._simReset++;
this.checkAnswers();
this.dataDoc.simulation_showIcon = true;
- }}
+ })}
variant="outlined">
<p>Submit</p>
</Button>
@@ -1427,7 +1431,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit={'N/m'}
upperBound={500}
value={this.springConstant}
- effect={(val: number) => (this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset)}
+ effect={action(() => this._simReset++)}
radianEquivalent={false}
mode={'Freeform'}
labelWidth={'7em'}
@@ -1441,7 +1445,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit=""
upperBound={500}
value={this.springLengthRest}
- effect={(val: number) => (this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset)}
+ effect={action(() => this._simReset++)}
radianEquivalent={false}
mode="Freeform"
labelWidth={'7em'}
@@ -1455,11 +1459,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit=""
upperBound={this.springLengthRest}
value={this.springLengthStart - this.springLengthRest}
- effect={(val: number) => {
+ effect={action((val: number) => {
this.dataDoc.mass1_positionYstart = this.springLengthRest + val;
this.dataDoc.spring_lengthStart = this.springLengthRest + val;
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
- }}
+ this._simReset++;
+ })}
radianEquivalent={false}
mode="Freeform"
labelWidth={'7em'}
@@ -1477,10 +1481,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit={'°'}
upperBound={49}
value={this.wedgeAngle}
- effect={(val: number) => {
+ effect={action((val: number) => {
this.changeWedgeBasedOnNewAngle(val);
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
- }}
+ this._simReset++;
+ })}
radianEquivalent={true}
mode={'Freeform'}
labelWidth={'2em'}
@@ -1498,13 +1502,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit={''}
upperBound={1}
value={NumCast(this.dataDoc.coefficientOfStaticFriction) ?? 0}
- effect={(val: number) => {
+ effect={action((val: number) => {
this.updateForcesWithFriction(val);
if (val < NumCast(this.dataDoc.coefficientOfKineticFriction)) {
this.dataDoc.soefficientOfKineticFriction = val;
}
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
- }}
+ this._simReset++;
+ })}
mode={'Freeform'}
labelWidth={'2em'}
/>
@@ -1521,9 +1525,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit={''}
upperBound={NumCast(this.dataDoc.coefficientOfStaticFriction)}
value={NumCast(this.dataDoc.coefficientOfKineticFriction) ?? 0}
- effect={(val: number) => {
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
- }}
+ effect={action(() => this._simReset++)}
mode={'Freeform'}
labelWidth={'2em'}
/>
@@ -1556,7 +1558,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit={'°'}
upperBound={59}
value={NumCast(this.dataDoc.pendulum_angle, 30)}
- effect={value => {
+ effect={action(value => {
this.dataDoc.pendulum_angleStart = value;
this.dataDoc.pendulum_lengthStart = this.dataDoc.pendulum_length;
if (this.simulationType == 'Pendulum') {
@@ -1589,9 +1591,9 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
this.dataDoc.mass1_forcesStart = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]);
this.dataDoc.mass1_forcesUpdated = JSON.stringify([this.gravityForce(this.mass1), forceOfTension]);
this.dataDoc.mass1_componentForces = JSON.stringify([forceOfTension, gravityParallel, gravityPerpendicular]);
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ this._simReset++;
}
- }}
+ })}
radianEquivalent={true}
mode="Freeform"
labelWidth="5em"
@@ -1605,13 +1607,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit="m"
upperBound={400}
value={Math.round(this.pendulumLength)}
- effect={value => {
+ effect={action(value => {
if (this.simulationType == 'Pendulum') {
this.dataDoc.pendulum_angleStart = this.pendulumAngle;
this.dataDoc.pendulum_lengthStart = value;
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
+ this._simReset++;
}
- }}
+ })}
radianEquivalent={false}
mode="Freeform"
labelWidth="5em"
@@ -1768,10 +1770,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP
unit={'m/s'}
upperBound={50}
value={NumCast(this.dataDoc.mass1_velocityX)}
- effect={value => {
+ effect={action(value => {
this.dataDoc.mass1_velocityXstart = value;
- this.dataDoc.simulation_reset = !this.dataDoc.simulation_reset;
- }}
+ this._simReset++;
+ })}
small={true}
mode="Freeform"
/>
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
index f533df109..2165c8ba9 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -1,8 +1,7 @@
-import { computed } from 'mobx';
+import { computed, IReactionDisposer, reaction } from 'mobx';
import { observer } from 'mobx-react';
import './PhysicsSimulationBox.scss';
import React = require('react');
-import _ = require('lodash');
interface IWallProps {
length: number;
@@ -19,6 +18,7 @@ export interface IWeightProps {
pause: () => void;
panelWidth: () => number;
panelHeight: () => number;
+ resetRequest: () => number;
circularMotionRadius: number;
coefficientOfKineticFriction: number;
color: string;
@@ -35,7 +35,6 @@ export interface IWeightProps {
pendulumAngle: number;
pendulumLength: number;
radius: number;
- reset: boolean;
showAcceleration: boolean;
showComponentForces: boolean;
showForceMagnitudes: boolean;
@@ -83,7 +82,6 @@ interface IState {
timer: number;
updatedStartPosX: any;
updatedStartPosY: any;
- walls: IWallProps[];
xPosition: number;
xVelocity: number;
yPosition: number;
@@ -106,7 +104,6 @@ export default class Weight extends React.Component<IWeightProps, IState> {
timer: 0,
updatedStartPosX: this.props.startPosX,
updatedStartPosY: this.props.startPosY,
- walls: [],
xPosition: this.props.startPosX,
xVelocity: this.props.startVelX,
yPosition: this.props.startPosY,
@@ -117,12 +114,13 @@ export default class Weight extends React.Component<IWeightProps, IState> {
}
_timer: NodeJS.Timeout | undefined;
+ _resetDisposer: IReactionDisposer | undefined;
componentWillUnmount() {
this._timer && clearTimeout(this._timer);
+ this._resetDisposer?.();
}
componentWillUpdate(nextProps: Readonly<IWeightProps>, nextState: Readonly<IState>, nextContext: any): void {
- if (nextProps.simulationType !== this.props.simulationType) setTimeout(() => this.setState({ timer: this.state.timer + 1 }));
if (nextProps.paused) {
this._timer && clearTimeout(this._timer);
this._timer = undefined;
@@ -142,6 +140,10 @@ export default class Weight extends React.Component<IWeightProps, IState> {
@computed get panelWidth() {
return Math.max(1000, this.props.panelWidth()) + 'px';
}
+
+ @computed get walls() {
+ return ['One Weight', 'Inclined Plane'].includes(this.props.simulationType) ? this.props.wallPositions : [];
+ }
epsilon = 0.0001;
labelBackgroundColor = `rgba(255,255,255,0.5)`;
@@ -179,7 +181,9 @@ export default class Weight extends React.Component<IWeightProps, IState> {
this.props.setAcceleration(xAccel, yAccel);
this.setState({ xAccel, yAccel });
};
-
+ componentDidMount() {
+ this._resetDisposer = reaction(() => this.props.resetRequest(), this.resetEverything);
+ }
componentDidUpdate(prevProps: Readonly<IWeightProps>, prevState: Readonly<IState>, snapshot?: any): void {
if (prevProps.simulationType != this.props.simulationType) {
this.setState({ xVelocity: this.props.startVelX, yVelocity: this.props.startVelY });
@@ -251,11 +255,6 @@ export default class Weight extends React.Component<IWeightProps, IState> {
this.setDisplayValues();
}
- // Reset everything on reset button click
- if (prevProps.reset != this.props.reset) {
- this.resetEverything();
- }
-
// Convert from static to kinetic friction if/when weight slips on inclined plane
if (prevState.xVelocity != this.state.xVelocity) {
if (this.props.simulationType == 'Inclined Plane' && Math.abs(this.state.xVelocity) > 0.1 && this.props.simulationMode != 'Review' && !this.state.kineticFriction) {
@@ -300,11 +299,6 @@ export default class Weight extends React.Component<IWeightProps, IState> {
}
}
- // Add/remove walls when simulation type changes
- if (prevProps.simulationType != this.props.simulationType) {
- this.setState({ walls: ['One Weight', 'Inclined Plane'].includes(this.props.simulationType) ? this.props.wallPositions : [] });
- }
-
// Update x position when start pos x changes
if (prevProps.startPosX != this.props.startPosX) {
if (this.props.paused && !isNaN(this.props.startPosX)) {
@@ -322,7 +316,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
}
// Update wedge coordinates
- if (!this.state.coordinates || prevProps.wedgeWidth != this.props.wedgeWidth || prevProps.wedgeHeight != this.props.wedgeHeight) {
+ if (!this.state.coordinates || this.props.yMax !== prevProps.yMax || prevProps.wedgeWidth != this.props.wedgeWidth || prevProps.wedgeHeight != this.props.wedgeHeight) {
const left = this.props.xMax * 0.25;
const coordinatePair1 = Math.round(left) + ',' + this.props.yMax + ' ';
const coordinatePair2 = Math.round(left + this.props.wedgeWidth) + ',' + this.props.yMax + ' ';
@@ -365,6 +359,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
this.props.setPosition(this.state.updatedStartPosX, this.state.updatedStartPosY);
this.props.setVelocity(this.props.startVelX, this.props.startVelY);
this.props.setAcceleration(0, 0);
+ setTimeout(() => this.setState({ timer: this.state.timer + 1 }));
};
// Compute x acceleration from forces, F=ma
@@ -438,7 +433,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
checkForCollisionsWithWall = () => {
let collision = false;
if (this.state.xVelocity !== 0) {
- this.state.walls
+ this.walls
.filter(wall => wall.angleInDegrees === 90)
.forEach(wall => {
const wallX = (wall.xPos / 100) * this.props.panelWidth();
@@ -462,7 +457,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
const minY = this.state.yPosition;
const maxY = this.state.yPosition + 2 * this.props.radius;
if (this.state.yVelocity > 0) {
- this.state.walls.forEach(wall => {
+ this.walls.forEach(wall => {
if (wall.angleInDegrees == 0 && wall.yPos > 0.4) {
const groundY = (wall.yPos / 100) * this.props.panelHeight();
const gravity = this.gravityForce();
@@ -488,7 +483,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
});
}
if (this.state.yVelocity < 0) {
- this.state.walls.forEach(wall => {
+ this.walls.forEach(wall => {
if (wall.angleInDegrees == 0 && wall.yPos < 0.4) {
const groundY = (wall.yPos / 100) * this.props.panelHeight();
if (minY < groundY) {
@@ -708,7 +703,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
top: this.state.yPosition + this.props.radius + 2 * (magY / mag) * this.props.radius + magY + 'px',
lineHeight: 1,
}}>
- {/* <p>{label}</p> */}
+ <p style={{ background: 'white' }}>{label}</p>
</div>
</div>
);