feat: add option to show constraint forces in pendulum simulation
This commit is contained in:
		
							parent
							
								
									0d3b84cc28
								
							
						
					
					
						commit
						d16d99d2fd
					
				
					 2 changed files with 18 additions and 3 deletions
				
			
		| 
						 | 
					@ -80,13 +80,17 @@ export function drawConstraint(ctx: CanvasRenderingContext2D, ball1: BallState,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function drawWorld(ctx: CanvasRenderingContext2D,
 | 
					export function drawWorld(ctx: CanvasRenderingContext2D,
 | 
				
			||||||
    world: PhysicalWorldState,
 | 
					    world: PhysicalWorldState,
 | 
				
			||||||
    ballDrawers: BallDrawer[]
 | 
					    ballDrawers: BallDrawer[],
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        showForces = false
 | 
				
			||||||
 | 
					    } = {}
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    for (const constraint of world.constraints) {
 | 
					    for (const constraint of world.constraints) {
 | 
				
			||||||
        const b1 = world.balls[constraint.p1Idx];
 | 
					        const b1 = world.balls[constraint.p1Idx];
 | 
				
			||||||
        const b2 = world.balls[constraint.p2Idx];
 | 
					        const b2 = world.balls[constraint.p2Idx];
 | 
				
			||||||
        drawConstraint(ctx, b1, b2);
 | 
					        drawConstraint(ctx, b1, b2);
 | 
				
			||||||
        // Draw force vectors for debugging
 | 
					        // Draw force vectors for debugging
 | 
				
			||||||
 | 
					        if (!showForces) continue;
 | 
				
			||||||
        const scale = 100; // Scale for visibility
 | 
					        const scale = 100; // Scale for visibility
 | 
				
			||||||
        if (constraint.p1ConstraintForce) {
 | 
					        if (constraint.p1ConstraintForce) {
 | 
				
			||||||
            ctx.beginPath();
 | 
					            ctx.beginPath();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ export default function Pendulum() {
 | 
				
			||||||
  const canvasRef = useRef<HTMLCanvasElement>(null);
 | 
					  const canvasRef = useRef<HTMLCanvasElement>(null);
 | 
				
			||||||
  const [subStep, setSubStep] = useState(10); // number of sub-steps per frame
 | 
					  const [subStep, setSubStep] = useState(10); // number of sub-steps per frame
 | 
				
			||||||
  const [constraintIterations, setConstraintIterations] = useState(1); // number of constraint iterations per sub-step
 | 
					  const [constraintIterations, setConstraintIterations] = useState(1); // number of constraint iterations per sub-step
 | 
				
			||||||
 | 
					  const [showForces, setShowForces] = useState(false); // whether to show constraint forces
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // refs to hold pendulum instances and animation state so we can reset from UI
 | 
					  // refs to hold pendulum instances and animation state so we can reset from UI
 | 
				
			||||||
  const worldRef = useRef<WorldState | null>(null);
 | 
					  const worldRef = useRef<WorldState | null>(null);
 | 
				
			||||||
| 
						 | 
					@ -45,7 +46,9 @@ export default function Pendulum() {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 | 
					      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 | 
				
			||||||
      if (worldRef.current) {
 | 
					      if (worldRef.current) {
 | 
				
			||||||
        drawWorld(ctx, worldRef.current, worldRef.current.ballDrawers);
 | 
					        drawWorld(ctx, worldRef.current, worldRef.current.ballDrawers, {
 | 
				
			||||||
 | 
					          showForces,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      rafRef.current = requestAnimationFrame(animate);
 | 
					      rafRef.current = requestAnimationFrame(animate);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					@ -119,7 +122,7 @@ export default function Pendulum() {
 | 
				
			||||||
      window.removeEventListener("pointermove", onPointerMove);
 | 
					      window.removeEventListener("pointermove", onPointerMove);
 | 
				
			||||||
      window.removeEventListener("pointerup", onPointerUp);
 | 
					      window.removeEventListener("pointerup", onPointerUp);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }, [constraintIterations, subStep]);
 | 
					  }, [constraintIterations, showForces, subStep]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // reset handler to re-create pendulums and avoid large dt on next frame
 | 
					  // reset handler to re-create pendulums and avoid large dt on next frame
 | 
				
			||||||
  const reset = () => {
 | 
					  const reset = () => {
 | 
				
			||||||
| 
						 | 
					@ -159,6 +162,14 @@ export default function Pendulum() {
 | 
				
			||||||
            className="ml-2 w-16 px-2 py-1 border border-gray-300 rounded focus:outline-none"
 | 
					            className="ml-2 w-16 px-2 py-1 border border-gray-300 rounded focus:outline-none"
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        </label>
 | 
					        </label>
 | 
				
			||||||
 | 
					        <label className="ml-4 text-sm">
 | 
				
			||||||
 | 
					          디버그 힘 보기:
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            type="checkbox"
 | 
				
			||||||
 | 
					            onChange={(e) => setShowForces(e.target.checked)}
 | 
				
			||||||
 | 
					            className="ml-2 w-4 h-4"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </label>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div className="flex-1 min-h-0">
 | 
					      <div className="flex-1 min-h-0">
 | 
				
			||||||
        <canvas
 | 
					        <canvas
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue