From 4c96bbd25d84964811838d005ff4e40487e1ec41 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 24 May 2023 15:04:13 -0400
Subject: more phys code streamlining

---
 .../nodes/PhysicsBox/PhysicsSimulationWeight.tsx   | 251 +++++----------------
 1 file changed, 59 insertions(+), 192 deletions(-)

(limited to 'src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx')

diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
index d062cc8c5..98b0e3f04 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -14,7 +14,6 @@ interface IForce {
     description: string;
     magnitude: number;
     directionInDegrees: number;
-    component: boolean;
 }
 export interface IWeightProps {
     pause: () => void;
@@ -250,13 +249,11 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                     description: 'Normal Force',
                     magnitude: this.props.mass * Math.abs(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,
-                    component: false,
                 };
                 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)),
                     directionInDegrees: 180 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
-                    component: false,
                 };
                 // reduce magnitude of friction force if necessary such that block cannot slide up plane
                 // prettier-ignore
@@ -271,31 +268,26 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                     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,
-                    component: true,
                 };
                 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)),
                     directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
-                    component: true,
                 };
                 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)),
                     directionInDegrees: 180 - 90 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI + 180,
-                    component: true,
                 };
                 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)),
                     directionInDegrees: 360 - (Math.atan(this.props.wedgeHeight / this.props.wedgeWidth) * 180) / Math.PI,
-                    component: true,
                 };
                 const gravityForce: IForce = {
                     description: 'Gravity',
                     magnitude: this.props.mass * Math.abs(this.props.gravity),
                     directionInDegrees: 270,
-                    component: false,
                 };
                 const kineticFriction = this.props.coefficientOfKineticFriction != 0;
                 this.props.setForcesUpdated([gravityForce, normalForce, ...(kineticFriction ? [frictionForce] : [])]);
@@ -394,7 +386,6 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                 description: 'Centripetal Force',
                 magnitude: (this.props.startVelX ** 2 * this.props.mass) / this.props.circularMotionRadius,
                 directionInDegrees: (Math.atan2(deltaY, deltaX) * 180) / Math.PI,
-                component: false,
             },
         ];
     };
@@ -408,13 +399,11 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                 description: 'Gravity',
                 magnitude: Math.abs(this.props.gravity) * this.props.mass,
                 directionInDegrees: 270,
-                component: false,
             },
             {
                 description: 'Spring Force',
                 magnitude: this.props.springConstant * (yPos - this.props.springRestLength) * (yPosPlus ? 1 : yPosMinus ? -1 : 0),
                 directionInDegrees: yPosPlus ? 90 : 270,
-                component: false,
             },
         ];
     };
@@ -442,13 +431,11 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                 description: 'Gravity',
                 magnitude: Math.abs(this.props.gravity) * this.props.mass,
                 directionInDegrees: 270,
-                component: false,
             },
             {
                 description: 'Tension',
                 magnitude: mag,
                 directionInDegrees: angle,
-                component: false,
             },
         ];
     };
@@ -488,7 +475,6 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                         description: 'Gravity',
                         magnitude: Math.abs(this.props.gravity) * this.props.mass,
                         directionInDegrees: 270,
-                        component: false,
                     };
                     if (maxY > groundY) {
                         this.setState({ yPosition: groundY - 2 * this.props.radius - 0.01 });
@@ -500,11 +486,10 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                                 description: 'Normal force',
                                 magnitude: gravity.magnitude,
                                 directionInDegrees: -gravity.directionInDegrees,
-                                component: false,
                             };
                             this.props.setForcesUpdated([gravity, normalForce]);
                             if (this.props.simulationType === 'Inclined Plane') {
-                                this.props.setComponentForces([gravity, { ...normalForce, component: true }]);
+                                this.props.setComponentForces([gravity, normalForce]);
                             }
                         }
                         collision = true;
@@ -641,19 +626,16 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                 description: 'Tension',
                 magnitude: mag,
                 directionInDegrees: angle,
-                component: true,
             };
             const gravityParallel: IForce = {
                 description: 'Gravity Parallel Component',
                 magnitude: Math.abs(this.props.gravity) * Math.cos(((90 - angle) * Math.PI) / 180),
                 directionInDegrees: 270 - (90 - angle),
-                component: true,
             };
             const gravityPerpendicular: IForce = {
                 description: 'Gravity Perpendicular Component',
                 magnitude: Math.abs(this.props.gravity) * Math.sin(((90 - angle) * Math.PI) / 180),
                 directionInDegrees: -(90 - angle),
-                component: true,
             };
             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));
@@ -663,6 +645,57 @@ export default class Weight extends React.Component<IWeightProps, IState> {
         }
     };
 
+    renderForce = (force: IForce, index: number, asComponent: boolean) => {
+        if (force.magnitude < this.epsilon) return;
+
+        const angle = (force.directionInDegrees * Math.PI) / 180;
+        const arrowStartY = this.state.yPosition + this.props.radius - this.props.radius * Math.sin(angle);
+        const arrowStartX = this.state.xPosition + this.props.radius + this.props.radius * Math.cos(angle);
+        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);
+
+        labelTop = Math.max(Math.min(labelTop, this.props.yMax + 50), this.props.yMin);
+        labelLeft = Math.max(Math.min(labelLeft, this.props.xMax - 60), this.props.xMin);
+
+        return (
+            <div key={index} style={{ zIndex: 6, position: 'absolute' }}>
+                <div
+                    style={{
+                        pointerEvents: 'none',
+                        position: 'absolute',
+                        left: this.props.xMin,
+                        top: this.props.yMin,
+                    }}>
+                    <svg width={this.props.xMax - this.props.xMin + 'px'} height={this.panelHeight}>
+                        <defs>
+                            <marker id="forceArrow" markerWidth="4" markerHeight="4" refX="0" refY="2" orient="auto" markerUnits="strokeWidth">
+                                <path d="M0,0 L0,4 L4,2 z" fill={color} />
+                            </marker>
+                        </defs>
+                        <line strokeDasharray={asComponent ? '10,10' : undefined} x1={arrowStartX} y1={arrowStartY} x2={arrowEndX} y2={arrowEndY} stroke={color} strokeWidth="5" markerEnd="url(#forceArrow)" />
+                    </svg>
+                </div>
+                <div
+                    style={{
+                        pointerEvents: 'none',
+                        position: 'absolute',
+                        left: labelLeft + 'px',
+                        top: labelTop + 'px',
+                        lineHeight: 1,
+                        backgroundColor: this.labelBackgroundColor,
+                    }}>
+                    <p>{force.description || 'Force'}</p>
+                    {this.props.showForceMagnitudes && <p>{Math.round(100 * force.magnitude) / 100} N</p>}
+                </div>
+            </div>
+        );
+    };
+
     // Render weight, spring, rod(s), vectors
     render() {
         return (
@@ -757,19 +790,16 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                                     description: 'Tension',
                                     magnitude: tensionMag1,
                                     directionInDegrees: (dir1T * 180) / Math.PI,
-                                    component: false,
                                 };
                                 const tensionForce2: IForce = {
                                     description: 'Tension',
                                     magnitude: tensionMag2,
                                     directionInDegrees: (dir2T * 180) / Math.PI,
-                                    component: false,
                                 };
                                 const grav: IForce = {
                                     description: 'Gravity',
                                     magnitude: this.props.mass * Math.abs(this.props.gravity),
                                     directionInDegrees: 270,
-                                    component: false,
                                 };
                                 this.props.setForcesUpdated([tensionForce1, tensionForce2, grav]);
                             }
@@ -802,14 +832,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                 )}
 
                 {this.props.simulationType == 'Pulley' && (
-                    <div
-                        className="rod"
-                        style={{
-                            pointerEvents: 'none',
-                            position: 'absolute',
-                            left: 0,
-                            top: 0,
-                        }}>
+                    <div className="rod">
                         <svg width={this.panelWidth} height={this.panelHeight}>
                             <line //
                                 x1={this.state.xPosition + this.props.radius}
@@ -837,14 +860,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                     </div>
                 )}
                 {this.props.simulationType == 'Suspension' && (
-                    <div
-                        className="rod"
-                        style={{
-                            pointerEvents: 'none',
-                            position: 'absolute',
-                            left: 0,
-                            top: 0,
-                        }}>
+                    <div className="rod">
                         <svg width={this.panelWidth} height={this.panelHeight}>
                             <line
                                 x1={this.state.xPosition + this.props.radius}
@@ -867,14 +883,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                             ) / 100}
                             °
                         </p>
-                        <div
-                            className="rod"
-                            style={{
-                                pointerEvents: 'none',
-                                position: 'absolute',
-                                left: 0,
-                                top: 0,
-                            }}>
+                        <div className="rod">
                             <svg width={this.props.panelWidth() + 'px'} height={this.panelHeight}>
                                 <line
                                     x1={this.state.xPosition + this.props.radius}
@@ -902,14 +911,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                     </div>
                 )}
                 {this.props.simulationType == 'Circular Motion' && (
-                    <div
-                        className="rod"
-                        style={{
-                            pointerEvents: 'none',
-                            position: 'absolute',
-                            left: 0,
-                            top: 0,
-                        }}>
+                    <div className="rod">
                         <svg width={this.panelWidth} height={this.panelHeight}>
                             <line
                                 x1={this.state.xPosition + this.props.radius}
@@ -923,14 +925,7 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                     </div>
                 )}
                 {this.props.simulationType == 'Pendulum' && (
-                    <div
-                        className="rod"
-                        style={{
-                            pointerEvents: 'none',
-                            position: 'absolute',
-                            left: 0,
-                            top: 0,
-                        }}>
+                    <div className="rod">
                         <svg width={this.panelWidth} height={this.panelHeight}>
                             <line x1={this.state.xPosition + this.props.radius} y1={this.state.yPosition + this.props.radius} x2={this.props.xMax / 2} y2={-5} stroke={'#deb887'} strokeWidth="10" />
                         </svg>
@@ -1056,136 +1051,8 @@ export default class Weight extends React.Component<IWeightProps, IState> {
                         </div>
                     </div>
                 )}
-                {!this.state.dragging &&
-                    this.props.showComponentForces &&
-                    this.props.componentForces().map((force, index) => {
-                        if (force.magnitude < this.epsilon) {
-                            return;
-                        }
-                        const arrowStartY = this.state.yPosition + this.props.radius;
-                        const arrowStartX = this.state.xPosition + this.props.radius;
-                        const arrowEndY = arrowStartY - Math.abs(force.magnitude) * 20 * Math.sin((force.directionInDegrees * Math.PI) / 180);
-                        const arrowEndX = arrowStartX + Math.abs(force.magnitude) * 20 * Math.cos((force.directionInDegrees * Math.PI) / 180);
-
-                        const color = '#0d0d0d';
-
-                        let labelTop = arrowEndY;
-                        let labelLeft = arrowEndX;
-                        if (force.directionInDegrees > 90 && force.directionInDegrees < 270) {
-                            labelLeft -= 120;
-                        } else {
-                            labelLeft += 30;
-                        }
-                        if (force.directionInDegrees >= 0 && force.directionInDegrees < 180) {
-                            labelTop += 40;
-                        } else {
-                            labelTop -= 40;
-                        }
-                        labelTop = Math.min(labelTop, this.props.yMax + 50);
-                        labelTop = Math.max(labelTop, this.props.yMin);
-                        labelLeft = Math.min(labelLeft, this.props.xMax - 60);
-                        labelLeft = Math.max(labelLeft, this.props.xMin);
-
-                        return (
-                            <div key={index}>
-                                <div
-                                    style={{
-                                        pointerEvents: 'none',
-                                        position: 'absolute',
-                                        left: this.props.xMin,
-                                        top: this.props.yMin,
-                                    }}>
-                                    <svg width={this.props.xMax - this.props.xMin + 'px'} height={this.panelHeight}>
-                                        <defs>
-                                            <marker id="forceArrow" 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>
-                                        {force.component == true && <line x1={arrowStartX} y1={arrowStartY} x2={arrowEndX} y2={arrowEndY} stroke={color} strokeWidth="5" strokeDasharray="10,10" markerEnd="url(#forceArrow)" />}
-                                        {force.component == false && <line x1={arrowStartX} y1={arrowStartY} x2={arrowEndX} y2={arrowEndY} stroke={color} strokeWidth="5" markerEnd="url(#forceArrow)" />}
-                                    </svg>
-                                </div>
-                                <div
-                                    style={{
-                                        pointerEvents: 'none',
-                                        position: 'absolute',
-                                        left: labelLeft + 'px',
-                                        top: labelTop + 'px',
-                                        lineHeight: 1,
-                                        backgroundColor: this.labelBackgroundColor,
-                                    }}>
-                                    {force.description && <p>{force.description}</p>}
-                                    {!force.description && <p>Force</p>}
-                                    {this.props.showForceMagnitudes && <p>{Math.round(100 * force.magnitude) / 100} N</p>}
-                                </div>
-                            </div>
-                        );
-                    })}
-                {!this.state.dragging &&
-                    this.props.showForces &&
-                    this.props.forcesUpdated().map((force, index) => {
-                        if (force.magnitude < this.epsilon) {
-                            return;
-                        }
-                        const arrowStartY = this.state.yPosition + this.props.radius;
-                        const arrowStartX = this.state.xPosition + this.props.radius;
-                        const arrowEndY = arrowStartY - Math.abs(force.magnitude) * 20 * Math.sin((force.directionInDegrees * Math.PI) / 180);
-                        const arrowEndX = arrowStartX + Math.abs(force.magnitude) * 20 * Math.cos((force.directionInDegrees * Math.PI) / 180);
-
-                        const color = '#0d0d0d';
-
-                        let labelTop = arrowEndY;
-                        let labelLeft = arrowEndX;
-                        if (force.directionInDegrees > 90 && force.directionInDegrees < 270) {
-                            labelLeft -= 120;
-                        } else {
-                            labelLeft += 30;
-                        }
-                        if (force.directionInDegrees >= 0 && force.directionInDegrees < 180) {
-                            labelTop += 40;
-                        } else {
-                            labelTop -= 40;
-                        }
-                        labelTop = Math.min(labelTop, this.props.yMax + 50);
-                        labelTop = Math.max(labelTop, this.props.yMin);
-                        labelLeft = Math.min(labelLeft, this.props.xMax - 60);
-                        labelLeft = Math.max(labelLeft, this.props.xMin);
-
-                        return (
-                            <div key={index}>
-                                <div
-                                    style={{
-                                        pointerEvents: 'none',
-                                        position: 'absolute',
-                                        left: this.props.xMin,
-                                        top: this.props.yMin,
-                                    }}>
-                                    <svg width={this.props.xMax - this.props.xMin + 'px'} height={this.panelHeight}>
-                                        <defs>
-                                            <marker id="forceArrow" 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>
-                                        {force.component == true && <line x1={arrowStartX} y1={arrowStartY} x2={arrowEndX} y2={arrowEndY} stroke={color} strokeWidth="5" strokeDasharray="10,10" markerEnd="url(#forceArrow)" />}
-                                        {force.component == false && <line x1={arrowStartX} y1={arrowStartY} x2={arrowEndX} y2={arrowEndY} stroke={color} strokeWidth="5" markerEnd="url(#forceArrow)" />}
-                                    </svg>
-                                </div>
-                                <div
-                                    style={{
-                                        pointerEvents: 'none',
-                                        position: 'absolute',
-                                        left: labelLeft + 'px',
-                                        top: labelTop + 'px',
-                                        lineHeight: 1,
-                                        backgroundColor: this.labelBackgroundColor,
-                                    }}>
-                                    {force.description && <p>{force.description}</p>}
-                                    {!force.description && <p>Force</p>}
-                                    {this.props.showForceMagnitudes && <p>{Math.round(100 * force.magnitude) / 100} N</p>}
-                                </div>
-                            </div>
-                        );
-                    })}
+                {!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>
         );
     }
-- 
cgit v1.2.3-70-g09d2

