Skip to content

Commit

Permalink
still buggy
Browse files Browse the repository at this point in the history
  • Loading branch information
yhattav committed Dec 13, 2024
1 parent 7afe40c commit 76a1bbe
Show file tree
Hide file tree
Showing 8 changed files with 408 additions and 11 deletions.
41 changes: 31 additions & 10 deletions src/components/GravitySimulator/GravitySimulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ import {
} from "../../utils/types/physics";
import { toParticle, toSerializableParticle } from "../../types/particle";
import { GravityVision } from "../GravityVision/GravityVision";
import {
SimulatorPath,
toSerializableSimulatorPath,

Check failure on line 50 in src/components/GravitySimulator/GravitySimulator.tsx

View workflow job for this annotation

GitHub Actions / build

'toSerializableSimulatorPath' is defined but never used
toSimulatorPath,
} from "../../utils/types/path";
import { PathRenderer } from "../PathRenderer/PathRenderer";

const generatePastelColor = () => {
const r = Math.floor(Math.random() * 75 + 180);
Expand Down Expand Up @@ -146,6 +152,9 @@ export const GravitySimulator: React.FC<GravitySimulatorProps> = ({
const [isColorInverted, setIsColorInverted] = useState(false);
const [offset, setOffset] = useState<Vector>(new Point(0, 0));
const [shouldResetRenderer, setShouldResetRenderer] = useState(false);
const [paths, setPaths] = useState<SimulatorPath[]>(
initialScenario?.data.paths?.map(toSimulatorPath) || []
);

useEffect(() => {
const updateOffset = () => {
Expand Down Expand Up @@ -233,6 +242,7 @@ export const GravitySimulator: React.FC<GravitySimulatorProps> = ({
gravityPoints,
physicsConfig.POINTER_MASS,
otherParticles,
paths,
physicsConfig.PARTICLES_EXERT_GRAVITY
);
const force = new Point(calculatedForce.x, calculatedForce.y).add(
Expand Down Expand Up @@ -270,7 +280,7 @@ export const GravitySimulator: React.FC<GravitySimulatorProps> = ({
elasticity: particle.elasticity,
};
},
[gravityPoints, physicsConfig, gravityRef, offset, pointerPosRef]
[gravityPoints, paths, physicsConfig, gravityRef, offset, pointerPosRef]
);

useEffect(() => {
Expand Down Expand Up @@ -490,12 +500,15 @@ export const GravitySimulator: React.FC<GravitySimulatorProps> = ({
requestAnimationFrame(() => {
updateSettings(scenario.data.settings);
setGravityPoints(newGravityPoints.map(toGravityPoint));
console.log(scenario.data.particles);
setParticles(
scenario.data.particles.map((particle) => ({
scenario.data.particles?.map((particle) => ({
...toParticle(particle),
force: new Point(0, 0),
}))
);
setPaths(scenario.data.paths?.map(toSimulatorPath));
console.log(scenario.data.paths?.map(toSimulatorPath));
setIsSimulationStarted(true);
setIsScenarioPanelOpen(false);
});
Expand Down Expand Up @@ -802,14 +815,22 @@ export const GravitySimulator: React.FC<GravitySimulatorProps> = ({
))}

{isSimulationStarted && (
<ParticleRenderer
particles={particles}
showVelocityArrows={physicsConfig.SHOW_VELOCITY_ARROWS}
showForceArrows={physicsConfig.SHOW_FORCE_ARROWS}
shouldReset={shouldResetRenderer}
onResetComplete={() => setShouldResetRenderer(false)}
simulatorId={simulatorId}
/>
<>
<ParticleRenderer
particles={particles}
showVelocityArrows={physicsConfig.SHOW_VELOCITY_ARROWS}
showForceArrows={physicsConfig.SHOW_FORCE_ARROWS}
shouldReset={shouldResetRenderer}
onResetComplete={() => setShouldResetRenderer(false)}
simulatorId={simulatorId}
/>
<PathRenderer
paths={paths}
shouldReset={shouldResetRenderer}
onResetComplete={() => setShouldResetRenderer(false)}
simulatorId={simulatorId}
/>
</>
)}

{!removeOverlay && (
Expand Down
119 changes: 119 additions & 0 deletions src/components/PathRenderer/PathRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { useEffect, useRef } from "react";
import Paper from "paper";
import { SimulatorPath } from "../../utils/types/path";

interface PathRendererProps {
paths: SimulatorPath[];
simulatorId?: string;
shouldReset?: boolean;
onResetComplete?: () => void;
}

export const PathRenderer: React.FC<PathRendererProps> = ({
paths,
simulatorId = "default",
shouldReset = false,
onResetComplete,
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const scopeRef = useRef<paper.PaperScope>();

// Initialize Paper.js scope
useEffect(() => {
if (!canvasRef.current) return;

// Create a new Paper.js scope for this canvas
const scope = new Paper.PaperScope();
scopeRef.current = scope;

// Get container dimensions
const container = canvasRef.current.parentElement;
if (!container) return;

const rect = container.getBoundingClientRect();
const pixelRatio = 1;

canvasRef.current.width = rect.width * pixelRatio;
canvasRef.current.height = rect.height * pixelRatio;

// Setup with explicit scope
scope.setup(canvasRef.current);
scope.view.viewSize = new scope.Size(rect.width, rect.height);
scope.view.scale(pixelRatio, pixelRatio);

const handleResize = () => {
if (!container || !canvasRef.current || !scope.view) return;
scope.activate(); // Activate this scope before operations
const newRect = container.getBoundingClientRect();
canvasRef.current.width = newRect.width * pixelRatio;
canvasRef.current.height = newRect.height * pixelRatio;
scope.view.viewSize = new scope.Size(newRect.width, newRect.height);
};

window.addEventListener("resize", handleResize);

return () => {
window.removeEventListener("resize", handleResize);
scope.activate();
scope.project?.clear();
};
}, [simulatorId]);

// Handle reset
useEffect(() => {
const scope = scopeRef.current;
if (!scope || !scope.project) return;

if (shouldReset) {
scope.activate();
scope.project.activeLayer.removeChildren();
scope.view.update();
onResetComplete?.();
}
}, [shouldReset, onResetComplete]);

// Update paths
useEffect(() => {
const scope = scopeRef.current;
if (!scope || !scope.project) return;

scope.activate();

// Clear previous frame
scope.project.activeLayer.removeChildren();

// Draw all paths
paths.forEach((simulatorPath) => {
const pathCopy = simulatorPath.path.clone();
scope.project.activeLayer.addChild(pathCopy);

console.log(`[${simulatorId}] Path added:`, {
segments: pathCopy.segments.length,
visible: pathCopy.visible,
strokeColor: pathCopy.strokeColor,
position: pathCopy.position,
});
});

scope.view.update();
console.log(
`[${simulatorId}] Layer children:`,
scope.project.activeLayer.children.length
);
}, [paths, simulatorId]);

return (
<canvas
ref={canvasRef}
className={`paper-canvas-${simulatorId}`}
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
pointerEvents: "none",
}}
/>
);
};
2 changes: 2 additions & 0 deletions src/scenarios/defaults/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { orbit } from "./orbit";
import { react } from "./react";
import { pulsar } from "./pulsar";
import { negativeMass } from "./negativeMass";
import { pathTest } from "./pathTest";

export const defaultScenarios: Scenario[] = [
pathTest,
react,
orbit,
negativeMass,
Expand Down
66 changes: 66 additions & 0 deletions src/scenarios/defaults/pathTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Point } from "paper";

Check failure on line 1 in src/scenarios/defaults/pathTest.ts

View workflow job for this annotation

GitHub Actions / build

'Point' is defined but never used
import { Scenario } from "../../types/scenario";
import { SerializableSimulatorPath } from "../../utils/types/path";

const createCurvedDiagonalPath = (): SerializableSimulatorPath => {
return {
id: "curved-diagonal",
points: [
{
x: 100,
y: 100,
handleOut: { x: 100, y: 0 },
},
{
x: 300,
y: 300,
handleIn: { x: -100, y: 0 },
handleOut: { x: 100, y: 0 },
},
{
x: 500,
y: 500,
handleIn: { x: -100, y: 0 },
},
],
closed: false,
position: { x: 300, y: 300 },
label: "Curved Path",
mass: 50000,
strokeColor: "#ff0000",
strokeWidth: 5,
opacity: 1,
};
};

export const pathTest: Scenario = {
id: "path-test",
name: "Path Test",
description:
"A test scenario with a curved diagonal path that exerts gravity",
data: {
settings: {
SHOW_VELOCITY_ARROWS: true,
SHOW_FORCE_ARROWS: true,
NEW_PARTICLE_MASS: 0.01,
NEW_PARTICLE_ELASTICITY: 0.8,
POINTER_MASS: 0,
FRICTION: 1,
PARTICLES_EXERT_GRAVITY: false,
SOLID_BOUNDARIES: true,
},
gravityPoints: [],
particles: [
{
id: "test-particle",
position: { x: 50, y: 50 },
velocity: { x: 3, y: 0 },
mass: 0.1,
elasticity: 0.8,
color: "#ffffff",
size: 5,
},
],
paths: [createCurvedDiagonalPath()],
},
};
2 changes: 2 additions & 0 deletions src/types/scenario.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { SerializableGravityPoint } from "../utils/types/physics";
import { SerializableParticle } from "./particle";
import { PhysicsSettings } from "../constants/physics";
import { SerializableSimulatorPath } from "../utils/types/path";

export interface ScenarioData {
settings: Partial<PhysicsSettings>;
gravityPoints: Array<SerializableGravityPoint>;
particles: Array<SerializableParticle>;
paths: Array<SerializableSimulatorPath>;
}

export interface Scenario {
Expand Down
22 changes: 22 additions & 0 deletions src/utils/physics/physicsUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Point } from "paper";
import { Vector, Force, GravityPoint } from "../types/physics";
import { ParticleMechanics } from "../../types/particle";
import { SimulatorPath } from "../types/path";

// Newton's law of universal gravitation (simplified)
const calculateGravityMagnitude = (
Expand Down Expand Up @@ -67,6 +68,7 @@ export const calculateTotalForce = (
gravityPoints: GravityPoint[],
pointerMass = 50000,
particles: Array<ParticleMechanics> = [],
paths: Array<SimulatorPath> = [],
particlesExertGravity = false
): Force => {
let totalForce = new Point(0, 0);
Expand All @@ -90,6 +92,26 @@ export const calculateTotalForce = (
totalForce = totalForce.add(pointForce);
});

// Add path gravity
paths.forEach((path) => {
// Find the nearest point on the path to the particle
const nearestPoint = path.path.getNearestPoint(selfPosition);

// Calculate force using the nearest point instead of path center
const pathForce = calculateGravitationalForceVector(
selfPosition,
nearestPoint,
path.mass
);

// Optional: Scale force based on distance to path
// const distanceToPath = path.path.getOffsetOf(nearestPoint) / path.path.length;
// const scaleFactor = Math.exp(-distanceToPath * 2); // Exponential falloff
// pathForce.multiply(scaleFactor);

totalForce = totalForce.add(pathForce);
});

// Add particle gravity if enabled
if (particlesExertGravity) {
particles.forEach((particle) => {
Expand Down
Loading

0 comments on commit 76a1bbe

Please sign in to comment.