Files
Dwinzo_dev/app/src/modules/simulation/path/pathCreation.tsx

365 lines
12 KiB
TypeScript
Raw Normal View History

2025-04-02 19:12:14 +05:30
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import { useRef, useState, useEffect, useMemo } from "react";
import { Sphere, TransformControls } from "@react-three/drei";
import {
useEditingPoint,
useEyeDropMode,
useIsConnecting,
usePreviewPosition,
useRenderDistance,
useSelectedActionSphere,
useSelectedPath,
useSimulationPaths,
} from "../../../store/store";
import { useFrame, useThree } from "@react-three/fiber";
import { useSubModuleStore } from "../../../store/useModuleStore";
2025-04-03 18:24:06 +05:30
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
function PathCreation({
pathsGroupRef,
}: {
pathsGroupRef: React.MutableRefObject<THREE.Group>;
}) {
2025-04-03 18:24:06 +05:30
const { isPlaying } = usePlayButtonStore();
2025-04-02 19:12:14 +05:30
const { renderDistance } = useRenderDistance();
const { setSubModule } = useSubModuleStore();
const { setSelectedActionSphere, selectedActionSphere } =
useSelectedActionSphere();
const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
const { editingPoint, setEditingPoint } = useEditingPoint();
const { previewPosition, setPreviewPosition } = usePreviewPosition();
const { raycaster, camera, pointer, gl } = useThree();
const plane = useMemo(
() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
[]
);
const { setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { isConnecting } = useIsConnecting();
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const isMovingRef = useRef(false);
const transformRef = useRef<any>(null);
const [transformMode, setTransformMode] = useState<
"translate" | "rotate" | null
>(null);
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
useEffect(() => {
setTransformMode(null);
const handleKeyDown = (e: KeyboardEvent) => {
if (!selectedActionSphere) return;
if (e.key === "g") {
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
}
if (e.key === "r") {
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
}
};
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedActionSphere]);
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
useFrame(() => {
Object.values(groupRefs.current).forEach((group) => {
if (group) {
const distance = new THREE.Vector3(
...group.position.toArray()
).distanceTo(camera.position);
2025-04-03 18:24:06 +05:30
group.visible = ((distance <= renderDistance) && !isPlaying);
2025-04-02 19:12:14 +05:30
}
});
});
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
const updateSimulationPaths = () => {
if (!selectedActionSphere) return;
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
position: [
selectedActionSphere.point.position.x,
selectedActionSphere.point.position.y,
selectedActionSphere.point.position.z,
],
rotation: [
selectedActionSphere.point.rotation.x,
selectedActionSphere.point.rotation.y,
selectedActionSphere.point.rotation.z,
],
}
: point
),
};
}
return path;
}) as Types.ConveyorEventsSchema[];
2025-04-02 19:12:14 +05:30
setSimulationPaths(updatedPaths);
};
2025-04-02 19:12:14 +05:30
useFrame(() => {
if (eyeDropMode) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
2025-04-02 19:12:14 +05:30
if (point) {
setPreviewPosition({ x: point.x, y: point.z });
}
} else {
setPreviewPosition(null);
}
});
2025-04-02 19:12:14 +05:30
useEffect(() => {
if (!camera) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
2025-04-02 19:12:14 +05:30
const onPointerDown = () => {
isMovingRef.current = false;
};
2025-04-02 19:12:14 +05:30
const onPointerMove = () => {
isMovingRef.current = true;
};
2025-04-02 19:12:14 +05:30
const onPointerUp = (event: PointerEvent) => {
if (
!isMovingRef.current &&
eyeDropMode &&
event.button === 0 &&
previewPosition
) {
event.preventDefault();
if (editingPoint) {
handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y);
setEditingPoint(null);
setEyeDropMode(false);
}
2025-04-02 19:12:14 +05:30
}
};
2025-04-02 19:12:14 +05:30
if (eyeDropMode) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
}
2025-04-02 19:12:14 +05:30
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
};
}, [eyeDropMode, editingPoint, previewPosition]);
2025-04-02 19:12:14 +05:30
const handlePointUpdate = (
pointType: "start" | "end",
x: number,
z: number
) => {
if (!selectedActionSphere?.point?.uuid) return;
2025-04-02 19:12:14 +05:30
const updatedPaths = simulationPaths.map((path) => {
if (
path.type === "Vehicle" &&
path.point.uuid === selectedActionSphere.point.uuid
) {
return {
...path,
point: {
...path.point,
actions: {
...path.point.actions,
[pointType]: {
...path.point.actions[pointType],
x: x,
y: z,
},
},
},
};
}
return path;
});
setSimulationPaths(updatedPaths);
};
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
return (
2025-04-03 18:24:06 +05:30
<group visible={!isPlaying} name="simulation-simulationPaths-group" ref={pathsGroupRef}>
2025-04-02 19:12:14 +05:30
{simulationPaths.map((path) => {
if (path.type === "Conveyor") {
const points = path.points.map(
(point) => new THREE.Vector3(...point.position)
);
2025-04-02 19:12:14 +05:30
return (
<group
name={`${path.modeluuid}-event-path`}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
rotation={path.rotation}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({
path,
group: groupRefs.current[path.modeluuid],
});
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule("mechanics");
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule("properties");
}}
>
{path.points.map((point, index) => (
<Sphere
key={point.uuid}
uuid={point.uuid}
position={point.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[point.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial
color={
index === 0
? "orange"
: index === path.points.length - 1
? "blue"
: "green"
}
/>
</Sphere>
))}
2025-04-02 19:12:14 +05:30
{points.slice(0, -1).map((point, index) => {
const nextPoint = points[index + 1];
const segmentCurve = new THREE.CatmullRomCurve3([
point,
nextPoint,
]);
const tubeGeometry = new THREE.TubeGeometry(
segmentCurve,
20,
0.1,
16,
false
);
2025-04-02 19:12:14 +05:30
return (
<mesh
name="event-connection-tube"
key={`tube-${index}`}
geometry={tubeGeometry}
>
<meshStandardMaterial
transparent
opacity={0.9}
color="red"
/>
</mesh>
);
})}
</group>
);
} else if (path.type === "Vehicle") {
return (
<group
name={`${path.modeluuid}-vehicle-path`}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({
path,
group: groupRefs.current[path.modeluuid],
});
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule("mechanics");
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule("properties");
}}
>
<Sphere
key={path.point.uuid}
uuid={path.point.uuid}
position={path.point.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[path.point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[path.point.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ point: path.point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="purple" />
</Sphere>
</group>
);
}
return null;
})}
2025-03-25 17:34:20 +05:30
2025-04-02 19:12:14 +05:30
{selectedActionSphere && transformMode && (
<TransformControls
ref={transformRef}
object={selectedActionSphere.point}
mode={transformMode}
onMouseUp={updateSimulationPaths}
/>
)}
</group>
);
2025-03-25 17:34:20 +05:30
}
export default PathCreation;