diff options
Diffstat (limited to 'src')
3 files changed, 120 insertions, 180 deletions
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss index 2e6ad413f..5f142df19 100644 --- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.scss @@ -22,7 +22,11 @@ justify-content: space-between; } } - .rod { + .rod, + .spring, + .wheel, + .showvecs, + .wedge { pointer-events: none; position: absolute; left: 0; diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx index d3e1c6fd2..f4acba2a6 100644 --- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx @@ -87,7 +87,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP wallPositions: IWallProps[] = []; @computed get circularMotionRadius() { - return NumCast(this.dataDoc.circularMotionRadius, 150); + return (NumCast(this.dataDoc.circularMotionRadius, 150) * this.props.PanelWidth()) / 1000; } @computed get gravity() { return NumCast(this.dataDoc.simulation_gravity, -9.81); @@ -757,7 +757,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP this.dataDoc.mass1_forcesUpdated = JSON.stringify([forceOfGravityReview, normalForceReview, staticFrictionForceReview]); }; - pause = () => (this.dataDoc.paused = true); + pause = () => (this.dataDoc.simulation_paused = true); componentForces1 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass1_componentForces)); setComponentForces1 = (forces: IForce[]) => (this.dataDoc.mass1_componentForces = JSON.stringify(forces)); componentForces2 = () => PhysicsSimulationBox.parseJSON(StrCast(this.dataDoc.mass2_componentForces)); @@ -810,7 +810,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent<FieldViewP yMax: this.yMax, yMin: this.yMin, wallPositions: this.wallPositions, - gravity: this.gravity, + gravity: Math.abs(this.gravity), timestepSize: 0.05, showComponentForces: BoolCast(this.dataDoc.simulation_showComponentForces), coefficientOfKineticFriction: NumCast(this.dataDoc.coefficientOfKineticFriction), diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx index 98b0e3f04..f533df109 100644 --- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx @@ -116,9 +116,20 @@ export default class Weight extends React.Component<IWeightProps, IState> { }; } - componentDidMount() { - // Timer for animating the simulation - setInterval(() => this.setState({ timer: this.state.timer + 1 }), 50); + _timer: NodeJS.Timeout | undefined; + + componentWillUnmount() { + this._timer && clearTimeout(this._timer); + } + 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; + } else if (this.props.paused) { + this._timer && clearTimeout(this._timer); + this._timer = setInterval(() => this.setState({ timer: this.state.timer + 1 }), 50); + } } // Constants @@ -154,7 +165,11 @@ export default class Weight extends React.Component<IWeightProps, IState> { // Helper function to go between display and real values getDisplayYPos = (yPos: number) => this.props.yMax - yPos - 2 * this.props.radius + 5; - + gravityForce = (): IForce => ({ + description: 'Gravity', + magnitude: this.props.mass * this.props.gravity, + directionInDegrees: 270, + }); // Update display values when simulation updates setDisplayValues = (xPos: number = this.state.xPosition, yPos: number = this.state.yPosition, xVel: number = this.state.xVelocity, yVel: number = this.state.yVelocity) => { this.props.setPosition(xPos, this.getDisplayYPos(yPos)); @@ -210,7 +225,7 @@ export default class Weight extends React.Component<IWeightProps, IState> { if (this.props.simulationType == 'One Weight') { let maxYPos = this.state.updatedStartPosY; if (this.props.startVelY != 0) { - maxYPos -= (this.props.startVelY * this.props.startVelY) / (2 * Math.abs(this.props.gravity)); + maxYPos -= (this.props.startVelY * this.props.startVelY) / (2 * this.props.gravity); } if (maxYPos < 0) maxYPos = 0; @@ -247,51 +262,41 @@ export default class Weight extends React.Component<IWeightProps, IState> { this.setState({ kineticFriction: true }); const normalForce: IForce = { description: 'Normal Force', - magnitude: this.props.mass * Math.abs(this.props.gravity) * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + magnitude: this.props.mass * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, }; const frictionForce: IForce = { description: 'Kinetic Friction Force', - magnitude: this.props.mass * this.props.coefficientOfKineticFriction * Math.abs(this.props.gravity) * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + magnitude: this.props.mass * this.props.coefficientOfKineticFriction * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), directionInDegrees: 180 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, }; // reduce magnitude of friction force if necessary such that block cannot slide up plane // prettier-ignore - const yForce = -Math.abs(this.props.gravity) + + const yForce = - this.props.gravity + normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + frictionForce.magnitude * Math.sin((frictionForce.directionInDegrees * Math.PI) / 180); if (yForce > 0) { - frictionForce.magnitude = (-normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + Math.abs(this.props.gravity)) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180); + frictionForce.magnitude = (-normalForce.magnitude * Math.sin((normalForce.directionInDegrees * Math.PI) / 180) + this.props.gravity) / Math.sin((frictionForce.directionInDegrees * Math.PI) / 180); } - const frictionForceComponent: IForce = { - description: 'Kinetic Friction Force', - magnitude: this.props.mass * this.props.coefficientOfKineticFriction * Math.abs(this.props.gravity) * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), - directionInDegrees: 180 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, - }; const normalForceComponent: IForce = { description: 'Normal Force', - magnitude: this.props.mass * Math.abs(this.props.gravity) * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + magnitude: this.props.mass * this.props.gravity * Math.cos(Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, }; const gravityParallel: IForce = { description: 'Gravity Parallel Component', - magnitude: this.props.mass * Math.abs(this.props.gravity) * Math.sin(Math.PI / 2 - Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + magnitude: this.props.mass * this.props.gravity * Math.sin(Math.PI / 2 - Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI + 180, }; const gravityPerpendicular: IForce = { description: 'Gravity Perpendicular Component', - magnitude: this.props.mass * Math.abs(this.props.gravity) * Math.cos(Math.PI / 2 - Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), + magnitude: this.props.mass * this.props.gravity * Math.cos(Math.PI / 2 - Math.atan(this.props.wedgeHeight / this.props.wedgeWidth)), directionInDegrees: 360 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI, }; - const gravityForce: IForce = { - description: 'Gravity', - magnitude: this.props.mass * Math.abs(this.props.gravity), - directionInDegrees: 270, - }; - const kineticFriction = this.props.coefficientOfKineticFriction != 0; - this.props.setForcesUpdated([gravityForce, normalForce, ...(kineticFriction ? [frictionForce] : [])]); - this.props.setComponentForces([normalForceComponent, gravityParallel, gravityPerpendicular, ...(kineticFriction ? [frictionForceComponent] : [])]); + const kineticFriction = this.props.coefficientOfKineticFriction != 0 ? [frictionForce] : []; + this.props.setForcesUpdated([this.gravityForce(), normalForce, ...kineticFriction]); + this.props.setComponentForces([normalForceComponent, gravityParallel, gravityPerpendicular, ...kineticFriction]); } } @@ -317,13 +322,12 @@ 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 || 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 + ' '; const coordinatePair3 = Math.round(left) + ',' + (this.props.yMax - this.props.wedgeHeight); - const coord = coordinatePair1 + coordinatePair2 + coordinatePair3; - this.setState({ coordinates: coord }); + this.setState({ coordinates: coordinatePair1 + coordinatePair2 + coordinatePair3 }); } if (this.state.xPosition != prevState.xPosition || this.state.yPosition != prevState.yPosition) { @@ -395,11 +399,7 @@ export default class Weight extends React.Component<IWeightProps, IState> { const yPosPlus = yPos - this.props.springRestLength > 0; const yPosMinus = yPos - this.props.springRestLength < 0; return [ - { - description: 'Gravity', - magnitude: Math.abs(this.props.gravity) * this.props.mass, - directionInDegrees: 270, - }, + this.gravityForce(), { description: 'Spring Force', magnitude: this.props.springConstant * (yPos - this.props.springRestLength) * (yPosPlus ? 1 : yPosMinus ? -1 : 0), @@ -412,10 +412,8 @@ export default class Weight extends React.Component<IWeightProps, IState> { getNewPendulumForces = (xPos: number, yPos: number, xVel: number, yVel: number): IForce[] => { const x = this.props.xMax / 2 - xPos - this.props.radius; const y = yPos + this.props.radius + 5; - let angle = (Math.atan(y / x) * 180) / Math.PI; - if (angle < 0) { - angle += 180; - } + const angle = (ang => (ang < 0 ? ang + 180 : ang))((Math.atan(y / x) * 180) / Math.PI); + let oppositeAngle = 90 - angle; if (oppositeAngle < 0) { oppositeAngle = 90 - (180 - angle); @@ -424,14 +422,10 @@ export default class Weight extends React.Component<IWeightProps, IState> { const pendulumLength = Math.sqrt(x * x + y * y); this.props.setPendulumAngle(oppositeAngle, undefined); - const mag = this.props.mass * Math.abs(this.props.gravity) * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength; + const mag = this.props.mass * this.props.gravity * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength; return [ - { - description: 'Gravity', - magnitude: Math.abs(this.props.gravity) * this.props.mass, - directionInDegrees: 270, - }, + this.gravityForce(), { description: 'Tension', magnitude: mag, @@ -471,11 +465,7 @@ export default class Weight extends React.Component<IWeightProps, IState> { this.state.walls.forEach(wall => { if (wall.angleInDegrees == 0 && wall.yPos > 0.4) { const groundY = (wall.yPos / 100) * this.props.panelHeight(); - const gravity: IForce = { - description: 'Gravity', - magnitude: Math.abs(this.props.gravity) * this.props.mass, - directionInDegrees: 270, - }; + const gravity = this.gravityForce(); if (maxY > groundY) { this.setState({ yPosition: groundY - 2 * this.props.radius - 0.01 }); if (this.props.elasticCollisions) { @@ -536,11 +526,11 @@ export default class Weight extends React.Component<IWeightProps, IState> { getForces = (xPos: number, yPos: number, xVel: number, yVel: number) => { // prettier-ignore switch (this.props.simulationType) { - case 'Pendulum': return this.getNewPendulumForces(xPos, yPos, xVel, yVel); - case 'Spring' : return this.getNewSpringForces(yPos); - case 'Circular Motion': return this.getNewCircularMotionForces(xPos, yPos); - default: return this.props.forcesUpdated(); - } + case 'Pendulum': return this.getNewPendulumForces(xPos, yPos, xVel, yVel); + case 'Spring' : return this.getNewSpringForces(yPos); + case 'Circular Motion': return this.getNewCircularMotionForces(xPos, yPos); + default: return this.props.forcesUpdated(); + } }; // Update position, velocity using RK4 method @@ -569,13 +559,11 @@ export default class Weight extends React.Component<IWeightProps, IState> { // make sure harmonic motion maintained and errors don't propagate switch (this.props.simulationType) { case 'Spring': + const equilibriumPos = this.props.springRestLength + (this.props.mass * this.props.gravity) / this.props.springConstant; + const amplitude = Math.abs(equilibriumPos - this.props.springStartLength); if (startYVel < 0 && yVel > 0 && yPos < this.props.springRestLength) { - const equilibriumPos = this.props.springRestLength + (this.props.mass * Math.abs(this.props.gravity)) / this.props.springConstant; - const amplitude = Math.abs(equilibriumPos - this.props.springStartLength); yPos = equilibriumPos - amplitude; } else if (startYVel > 0 && yVel < 0 && yPos > this.props.springRestLength) { - const equilibriumPos = this.props.springRestLength + (this.props.mass * Math.abs(this.props.gravity)) / this.props.springConstant; - const amplitude = Math.abs(equilibriumPos - this.props.springStartLength); yPos = equilibriumPos + amplitude; } break; @@ -620,32 +608,30 @@ export default class Weight extends React.Component<IWeightProps, IState> { const pendulumLength = Math.sqrt(x * x + y * y); - const mag = this.props.mass * Math.abs(this.props.gravity) * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength; - const tensionComponent: IForce = { description: 'Tension', - magnitude: mag, + magnitude: this.props.mass * this.props.gravity * Math.cos((oppositeAngle * Math.PI) / 180) + (this.props.mass * (xVel * xVel + yVel * yVel)) / pendulumLength, directionInDegrees: angle, }; const gravityParallel: IForce = { description: 'Gravity Parallel Component', - magnitude: Math.abs(this.props.gravity) * Math.cos(((90 - angle) * Math.PI) / 180), + magnitude: this.props.gravity * Math.cos(((90 - angle) * Math.PI) / 180), directionInDegrees: 270 - (90 - angle), }; const gravityPerpendicular: IForce = { description: 'Gravity Perpendicular Component', - magnitude: Math.abs(this.props.gravity) * Math.sin(((90 - angle) * Math.PI) / 180), + magnitude: this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180), directionInDegrees: -(90 - angle), }; - if (Math.abs(this.props.gravity) * Math.sin(((90 - angle) * Math.PI) / 180) < 0) { - gravityPerpendicular.magnitude = Math.abs(Math.abs(this.props.gravity) * Math.sin(((90 - angle) * Math.PI) / 180)); + if (this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180) < 0) { + gravityPerpendicular.magnitude = Math.abs(this.props.gravity * Math.sin(((90 - angle) * Math.PI) / 180)); gravityPerpendicular.directionInDegrees = 180 - (90 - angle); } this.props.setComponentForces([tensionComponent, gravityParallel, gravityPerpendicular]); } }; - renderForce = (force: IForce, index: number, asComponent: boolean) => { + renderForce = (force: IForce, index: number, asComponent: boolean, color = '#0d0d0d') => { if (force.magnitude < this.epsilon) return; const angle = (force.directionInDegrees * Math.PI) / 180; @@ -654,8 +640,6 @@ export default class Weight extends React.Component<IWeightProps, IState> { const arrowEndY = arrowStartY - Math.abs(force.magnitude) * Math.sin(angle) - this.props.radius * Math.sin(angle); const arrowEndX = arrowStartX + Math.abs(force.magnitude) * Math.cos(angle) + this.props.radius * Math.cos(angle); - const color = '#0d0d0d'; - let labelTop = arrowEndY + (force.directionInDegrees >= 0 && force.directionInDegrees < 180 ? 40 : -40); let labelLeft = arrowEndX + (force.directionInDegrees > 90 && force.directionInDegrees < 270 ? -120 : 30); @@ -696,6 +680,40 @@ export default class Weight extends React.Component<IWeightProps, IState> { ); }; + renderVector = (id: string, magX: number, magY: number, color: string, label: string) => { + const mag = Math.sqrt(magX * magX + magY * magY); + return ( + <div className="showvecs" style={{ zIndex: 6 }}> + <svg width={this.panelWidth} height={this.panelHeight}> + <defs> + <marker id={id} markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth"> + <path d="M0,0 L0,6 L9,3 z" fill={color} /> + </marker> + </defs> + <line + x1={this.state.xPosition + this.props.radius + (magX / mag) * this.props.radius} + y1={this.state.yPosition + this.props.radius + (magY / mag) * this.props.radius} + x2={this.state.xPosition + this.props.radius + (magX / mag) * this.props.radius + magX} + y2={this.state.yPosition + this.props.radius + (magY / mag) * this.props.radius + magY} + stroke={color} + strokeWidth="5" + markerEnd={`url(#${id})`} + /> + </svg> + <div + style={{ + pointerEvents: 'none', + position: 'absolute', + left: this.state.xPosition + this.props.radius + 2 * (magX / mag) * this.props.radius + magX + 'px', + top: this.state.yPosition + this.props.radius + 2 * (magY / mag) * this.props.radius + magY + 'px', + lineHeight: 1, + }}> + {/* <p>{label}</p> */} + </div> + </div> + ); + }; + // Render weight, spring, rod(s), vectors render() { return ( @@ -784,7 +802,7 @@ export default class Weight extends React.Component<IWeightProps, IState> { const deltaY = this.state.yPosition + this.props.radius; const dir1T = Math.PI - Math.atan(deltaY / deltaX1); const dir2T = Math.atan(deltaY / deltaX2); - const tensionMag2 = (this.props.mass * Math.abs(this.props.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); + const tensionMag2 = (this.props.mass * this.props.gravity) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); const tensionForce1: IForce = { description: 'Tension', @@ -796,12 +814,7 @@ export default class Weight extends React.Component<IWeightProps, IState> { magnitude: tensionMag2, directionInDegrees: (dir2T * 180) / Math.PI, }; - const grav: IForce = { - description: 'Gravity', - magnitude: this.props.mass * Math.abs(this.props.gravity), - directionInDegrees: 270, - }; - this.props.setForcesUpdated([tensionForce1, tensionForce2, grav]); + this.props.setForcesUpdated([tensionForce1, tensionForce2, this.gravityForce()]); } } }}> @@ -810,22 +823,15 @@ export default class Weight extends React.Component<IWeightProps, IState> { </div> </div> {this.props.simulationType == 'Spring' && ( - <div - className="spring" - style={{ - pointerEvents: 'none', - position: 'absolute', - left: 0, - top: 0, - }}> + <div className="spring"> <svg width={this.panelWidth} height={this.panelHeight}> - {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(val => { + {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(val => { const count = 10; const xPos1 = this.state.xPosition + this.props.radius + (val % 2 === 0 ? -20 : 20); + const xPos2 = this.state.xPosition + this.props.radius + (val === 10 ? 0 : val % 2 === 0 ? 20 : -20); const yPos1 = (val * this.state.yPosition) / count; - const xPos2 = this.state.xPosition + this.props.radius + (val % 2 === 0 ? 20 : -20); - const yPos2 = ((val + 1) * this.state.yPosition) / count; - return <line key={val} x1={xPos1} y1={yPos1} x2={xPos2} y2={yPos2} stroke={'#808080'} strokeWidth="10" />; + const yPos2 = val === 10 ? this.state.yPosition + this.props.radius : ((val + 1) * this.state.yPosition) / count; + return <line key={val} x1={xPos1} strokeLinecap="round" y1={yPos1} x2={xPos2} y2={yPos2} stroke={'#808080'} strokeWidth="10" />; })} </svg> </div> @@ -846,14 +852,7 @@ export default class Weight extends React.Component<IWeightProps, IState> { </div> )} {this.props.simulationType == 'Pulley' && ( - <div - className="wheel" - style={{ - pointerEvents: 'none', - position: 'absolute', - left: 0, - top: 0, - }}> + <div className="wheel"> <svg width={this.panelWidth} height={this.panelHeight}> <circle cx={(this.props.xMax + this.props.xMin) / 2} cy={this.props.radius} r={this.props.radius * 1.5} fill={'#808080'} /> </svg> @@ -895,7 +894,6 @@ export default class Weight extends React.Component<IWeightProps, IState> { /> </svg> </div> - <p style={{ position: 'absolute', @@ -956,12 +954,11 @@ export default class Weight extends React.Component<IWeightProps, IState> { )} {this.props.simulationType == 'Inclined Plane' && ( <div> - <div style={{ position: 'absolute', left: '0', top: '0' }}> + <div className="wedge"> <svg width={this.panelWidth} height={this.props.yMax + 'px'}> <polygon points={this.state.coordinates} style={{ fill: 'burlywood' }} /> </svg> </div> - <p style={{ position: 'absolute', @@ -972,85 +969,24 @@ export default class Weight extends React.Component<IWeightProps, IState> { </p> </div> )} - {!this.state.dragging && this.props.showAcceleration && ( - <div> - <div - style={{ - pointerEvents: 'none', - position: 'absolute', - left: 0, - top: 0, - }}> - <svg width={this.panelWidth} height={this.panelHeight}> - <defs> - <marker id="accArrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth"> - <path d="M0,0 L0,6 L9,3 z" fill="green" /> - </marker> - </defs> - <line - x1={this.state.xPosition + this.props.radius} - y1={this.state.yPosition + this.props.radius} - x2={this.state.xPosition + this.props.radius + this.getNewAccelerationX(this.props.forcesUpdated()) * 15} - y2={this.state.yPosition + this.props.radius + this.getNewAccelerationY(this.props.forcesUpdated()) * 15} - stroke={'green'} - strokeWidth="5" - markerEnd="url(#accArrow)" - /> - </svg> - <div - style={{ - pointerEvents: 'none', - position: 'absolute', - left: this.state.xPosition + this.props.radius + this.state.xAccel * 15 + 25 + 'px', - top: this.state.yPosition + this.props.radius + this.state.yAccel * 15 + 70 + 'px', - lineHeight: 1, - }}> - <p> - {Math.round(100 * Math.sqrt(this.state.xAccel * this.state.xAccel + this.state.yAccel * this.state.yAccel)) / 100} m/s - <sup>2</sup> - </p> - </div> - </div> - </div> - )} - {!this.state.dragging && this.props.showVelocity && ( - <div> - <div - style={{ - pointerEvents: 'none', - position: 'absolute', - left: 0, - top: 0, - }}> - <svg width={this.panelWidth} height={this.panelHeight}> - <defs> - <marker id="velArrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth"> - <path d="M0,0 L0,6 L9,3 z" fill="blue" /> - </marker> - </defs> - <line - x1={this.state.xPosition + this.props.radius} - y1={this.state.yPosition + this.props.radius} - x2={this.state.xPosition + this.props.radius + this.state.xVelocity * 7} - y2={this.state.yPosition + this.props.radius + this.state.yVelocity * 7} - stroke={'blue'} - strokeWidth="5" - markerEnd="url(#velArrow)" - /> - </svg> - <div - style={{ - pointerEvents: 'none', - position: 'absolute', - left: this.state.xPosition + this.props.radius + this.state.xVelocity * 7 + 25 + 'px', - top: this.state.yPosition + this.props.radius + this.state.yVelocity * 7 + 'px', - lineHeight: 1, - }}> - <p>{Math.round(100 * Math.sqrt(this.props.displayXVelocity * this.props.displayXVelocity + this.props.displayYVelocity * this.props.displayYVelocity)) / 100} m/s</p> - </div> - </div> - </div> - )} + {!this.state.dragging && + this.props.showAcceleration && + this.renderVector( + 'accArrow', + this.getNewAccelerationX(this.props.forcesUpdated()), + this.getNewAccelerationY(this.props.forcesUpdated()), + 'green', + `${Math.round(100 * Math.sqrt(this.state.xAccel * this.state.xAccel + this.state.yAccel * this.state.yAccel)) / 100} m/s^2` + )} + {!this.state.dragging && + this.props.showVelocity && + this.renderVector( + 'velArrow', + this.state.xVelocity, + this.state.yVelocity, + 'blue', + `${Math.round(100 * Math.sqrt(this.props.displayXVelocity * this.props.displayXVelocity + this.props.displayYVelocity * this.props.displayYVelocity)) / 100} m/s` + )} {!this.state.dragging && this.props.showComponentForces && this.props.componentForces().map((force, index) => this.renderForce(force, index, true))} {!this.state.dragging && this.props.showForces && this.props.forcesUpdated().map((force, index) => this.renderForce(force, index, false))} </div> |