Merge remote-tracking branch 'origin/main-dev' into main-demo
This commit is contained in:
@@ -94,9 +94,9 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
|||||||
|
|
||||||
if (!wallVisibility && wallType.type === "room") {
|
if (!wallVisibility && wallType.type === "room") {
|
||||||
meshRef.current.getWorldDirection(v);
|
meshRef.current.getWorldDirection(v);
|
||||||
camera.getWorldDirection(u);
|
u.subVectors(camera.position, meshRef.current.position);
|
||||||
if (!u || !v) return;
|
if (!u || !v) return;
|
||||||
nextVisible = 2 * v.dot(u) <= 0.1;
|
nextVisible = v.dot(u) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevVisibleRef.current !== nextVisible) {
|
if (prevVisibleRef.current !== nextVisible) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useMemo, useRef, useState } from "react";
|
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { useToolMode } from "../../../../store/builder/store";
|
import { useToolMode } from "../../../../store/builder/store";
|
||||||
import { useSceneContext } from "../../../scene/sceneContext";
|
import { useSceneContext } from "../../../scene/sceneContext";
|
||||||
@@ -8,6 +8,8 @@ import { useSocketStore } from "../../../../store/socket/useSocketStore";
|
|||||||
|
|
||||||
import { updateEventToBackend } from "../../../../components/layout/sidebarRight/properties/eventProperties/functions/handleUpdateEventToBackend";
|
import { updateEventToBackend } from "../../../../components/layout/sidebarRight/properties/eventProperties/functions/handleUpdateEventToBackend";
|
||||||
|
|
||||||
|
import { QuadraticBezierLine } from "@react-three/drei";
|
||||||
|
|
||||||
interface ConnectionLine {
|
interface ConnectionLine {
|
||||||
id: string;
|
id: string;
|
||||||
startPointUuid: string;
|
startPointUuid: string;
|
||||||
@@ -15,9 +17,15 @@ interface ConnectionLine {
|
|||||||
trigger: TriggerSchema;
|
trigger: TriggerSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Arrows({ connections }: { readonly connections: ConnectionLine[] }) {
|
interface PreviewLine {
|
||||||
|
start: THREE.Vector3;
|
||||||
|
mid: THREE.Vector3;
|
||||||
|
end: THREE.Vector3;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Arrows({ connections, previewLine }: { readonly connections: ConnectionLine[]; readonly previewLine?: PreviewLine | null }) {
|
||||||
const [hoveredArrowTrigger, setHoveredArrowTrigger] = useState<string | null>(null);
|
const [hoveredArrowTrigger, setHoveredArrowTrigger] = useState<string | null>(null);
|
||||||
const groupRef = useRef<THREE.Group>(null);
|
|
||||||
const { simulationSocket } = useSocketStore();
|
const { simulationSocket } = useSocketStore();
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
@@ -38,6 +46,15 @@ export function Arrows({ connections }: { readonly connections: ConnectionLine[]
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeConnection = (trigger: TriggerSchema) => {
|
||||||
|
if (trigger.triggerUuid) {
|
||||||
|
const event = peekRemoveTrigger(selectedProduct.productUuid, trigger.triggerUuid);
|
||||||
|
if (event) {
|
||||||
|
updateBackend(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getWorldPositionFromScene = (uuid: string): THREE.Vector3 | null => {
|
const getWorldPositionFromScene = (uuid: string): THREE.Vector3 | null => {
|
||||||
const obj = scene.getObjectByProperty("uuid", uuid);
|
const obj = scene.getObjectByProperty("uuid", uuid);
|
||||||
if (!obj) return null;
|
if (!obj) return null;
|
||||||
@@ -46,137 +63,110 @@ export function Arrows({ connections }: { readonly connections: ConnectionLine[]
|
|||||||
return pos;
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
const processedArrows = new Set<string>();
|
const { arrowsData, linesData } = useMemo(() => {
|
||||||
|
const processedArrows = new Set<string>();
|
||||||
|
const arrowDataList: {
|
||||||
|
key: string;
|
||||||
|
trigger: TriggerSchema;
|
||||||
|
subCurve: THREE.Curve<THREE.Vector3>;
|
||||||
|
scale: number;
|
||||||
|
matrix: THREE.Matrix4;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
const createArrow = (key: string, fullCurve: THREE.QuadraticBezierCurve3, centerT: number, segmentSize: number, scale: number, reverse: boolean, trigger: TriggerSchema) => {
|
const lineDataList: {
|
||||||
if (processedArrows.has(trigger.triggerUuid)) return [];
|
key: string;
|
||||||
processedArrows.add(trigger.triggerUuid);
|
start: THREE.Vector3;
|
||||||
|
end: THREE.Vector3;
|
||||||
|
mid: THREE.Vector3;
|
||||||
|
trigger: TriggerSchema;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
const t1 = Math.max(0, centerT - segmentSize / 2);
|
const getSubCurve = (curve: THREE.Curve<THREE.Vector3>, t1: number, t2: number, reverse = false) => {
|
||||||
const t2 = Math.min(1, centerT + segmentSize / 2);
|
const divisions = 10;
|
||||||
const subCurve = getSubCurve(fullCurve, t1, t2, reverse);
|
const subPoints = Array.from({ length: divisions + 1 }, (_, i) => {
|
||||||
|
const t = THREE.MathUtils.lerp(t1, t2, i / divisions);
|
||||||
|
return curve.getPoint(t);
|
||||||
|
});
|
||||||
|
|
||||||
const shaftGeometry = new THREE.TubeGeometry(subCurve, 8, 0.01 * scale, 8, false);
|
if (reverse) subPoints.reverse();
|
||||||
|
|
||||||
const end = subCurve.getPoint(1);
|
return new THREE.CatmullRomCurve3(subPoints);
|
||||||
const tangent = subCurve.getTangent(1).normalize();
|
|
||||||
|
|
||||||
const arrowHeadLength = 0.15 * scale;
|
|
||||||
const arrowRadius = 0.01 * scale;
|
|
||||||
const arrowHeadRadius = arrowRadius * 2.5;
|
|
||||||
|
|
||||||
const headGeometry = new THREE.ConeGeometry(arrowHeadRadius, arrowHeadLength, 8);
|
|
||||||
headGeometry.translate(0, arrowHeadLength / 2, 0);
|
|
||||||
|
|
||||||
const rotation = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), tangent);
|
|
||||||
|
|
||||||
const removeConnection = (trigger: TriggerSchema) => {
|
|
||||||
if (trigger.triggerUuid) {
|
|
||||||
const event = peekRemoveTrigger(selectedProduct.productUuid, trigger.triggerUuid);
|
|
||||||
if (event) {
|
|
||||||
updateBackend(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
connections.forEach((connection) => {
|
||||||
<group
|
const { startPointUuid, endPointUuid, trigger, id } = connection;
|
||||||
key={key}
|
|
||||||
onPointerOver={() => {
|
|
||||||
if (toolMode === "3D-Delete") {
|
|
||||||
setHoveredArrowTrigger(trigger.triggerUuid);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onPointerOut={() => {
|
|
||||||
if (toolMode === "3D-Delete") {
|
|
||||||
setHoveredArrowTrigger(null);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
if (toolMode === "3D-Delete") {
|
|
||||||
removeConnection(trigger);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<mesh geometry={shaftGeometry}>
|
|
||||||
<meshStandardMaterial color={toolMode === "3D-Delete" && hoveredArrowTrigger === trigger.triggerUuid ? "red" : "#42a5f5"} />
|
|
||||||
</mesh>
|
|
||||||
<mesh position={end} quaternion={rotation} geometry={headGeometry}>
|
|
||||||
<meshStandardMaterial color={toolMode === "3D-Delete" && hoveredArrowTrigger === trigger.triggerUuid ? "red" : "#42a5f5"} />
|
|
||||||
</mesh>
|
|
||||||
</group>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSubCurve = (curve: THREE.Curve<THREE.Vector3>, t1: number, t2: number, reverse = false) => {
|
if (processedArrows.has(trigger.triggerUuid)) return;
|
||||||
const divisions = 10;
|
processedArrows.add(trigger.triggerUuid);
|
||||||
const subPoints = Array.from({ length: divisions + 1 }, (_, i) => {
|
|
||||||
const t = THREE.MathUtils.lerp(t1, t2, i / divisions);
|
const start = getWorldPositionFromScene(startPointUuid);
|
||||||
return curve.getPoint(t);
|
const end = getWorldPositionFromScene(endPointUuid);
|
||||||
|
if (!start || !end) return;
|
||||||
|
|
||||||
|
const isBidirectional = connections.some((other) => other.startPointUuid === endPointUuid && other.endPointUuid === startPointUuid);
|
||||||
|
|
||||||
|
const distance = start.distanceTo(end);
|
||||||
|
const heightFactor = Math.max(0.5, distance * 0.2);
|
||||||
|
const control = new THREE.Vector3((start.x + end.x) / 2, Math.max(start.y, end.y) + heightFactor, (start.z + end.z) / 2);
|
||||||
|
|
||||||
|
lineDataList.push({
|
||||||
|
key: id,
|
||||||
|
start: start.clone(),
|
||||||
|
end: end.clone(),
|
||||||
|
mid: control.clone(),
|
||||||
|
trigger,
|
||||||
|
});
|
||||||
|
|
||||||
|
const curve = new THREE.QuadraticBezierCurve3(start, control, end);
|
||||||
|
const scale = THREE.MathUtils.clamp(distance * 0.75, 0.5, 5);
|
||||||
|
|
||||||
|
const addArrow = (suffix: string, centerT: number, segmentSize: number, reverse: boolean) => {
|
||||||
|
const t1 = Math.max(0, centerT - segmentSize / 2);
|
||||||
|
const t2 = Math.min(1, centerT + segmentSize / 2);
|
||||||
|
const subCurve = getSubCurve(curve, t1, t2, reverse);
|
||||||
|
|
||||||
|
const endPoint = subCurve.getPoint(1);
|
||||||
|
const tangent = subCurve.getTangent(1).normalize();
|
||||||
|
|
||||||
|
const rotation = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), tangent);
|
||||||
|
|
||||||
|
const matrix = new THREE.Matrix4();
|
||||||
|
matrix.compose(endPoint, rotation, new THREE.Vector3(scale, scale, scale));
|
||||||
|
|
||||||
|
arrowDataList.push({
|
||||||
|
key: id + suffix,
|
||||||
|
trigger,
|
||||||
|
subCurve,
|
||||||
|
scale,
|
||||||
|
matrix,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isBidirectional) {
|
||||||
|
addArrow("", 0.66, 0.25, false);
|
||||||
|
} else {
|
||||||
|
addArrow("", 0.5, 0.3, false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (reverse) subPoints.reverse();
|
return { arrowsData: arrowDataList, linesData: lineDataList };
|
||||||
|
}, [connections, scene]);
|
||||||
|
|
||||||
return new THREE.CatmullRomCurve3(subPoints);
|
// Preview Line Logic
|
||||||
};
|
const previewData = useMemo(() => {
|
||||||
|
if (!previewLine) return null;
|
||||||
|
|
||||||
const arrowGroups = connections.flatMap((connection) => {
|
const { start, mid, end, color } = previewLine;
|
||||||
const { startPointUuid, endPointUuid } = connection;
|
const startVec = start;
|
||||||
const start = getWorldPositionFromScene(startPointUuid);
|
const midVec = mid;
|
||||||
const end = getWorldPositionFromScene(endPointUuid);
|
const endVec = end;
|
||||||
if (!start || !end) return [];
|
|
||||||
|
|
||||||
const isBidirectional = connections.some((other) => other.startPointUuid === endPointUuid && other.endPointUuid === startPointUuid);
|
const fullCurve = new THREE.QuadraticBezierCurve3(startVec, midVec, endVec);
|
||||||
|
const distance = startVec.distanceTo(endVec);
|
||||||
const distance = start.distanceTo(end);
|
|
||||||
const heightFactor = Math.max(0.5, distance * 0.2);
|
|
||||||
const control = new THREE.Vector3((start.x + end.x) / 2, Math.max(start.y, end.y) + heightFactor, (start.z + end.z) / 2);
|
|
||||||
const curve = new THREE.QuadraticBezierCurve3(start, control, end);
|
|
||||||
const scale = THREE.MathUtils.clamp(distance * 0.75, 0.5, 5);
|
const scale = THREE.MathUtils.clamp(distance * 0.75, 0.5, 5);
|
||||||
|
|
||||||
if (isBidirectional) {
|
// Arrow part
|
||||||
return [createArrow(connection.id + "-fwd", curve, 0.33, 0.25, scale, true, connection.trigger), createArrow(connection.id + "-bwd", curve, 0.66, 0.25, scale, false, connection.trigger)];
|
const segmentSize = 0.3;
|
||||||
} else {
|
|
||||||
return [createArrow(connection.id, curve, 0.5, 0.3, scale, false, connection.trigger)];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<group ref={groupRef} name="connectionArrows">
|
|
||||||
{arrowGroups}
|
|
||||||
</group>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArrowOnQuadraticBezier({
|
|
||||||
start,
|
|
||||||
mid,
|
|
||||||
end,
|
|
||||||
color = "#42a5f5",
|
|
||||||
}: Readonly<{
|
|
||||||
start: number[];
|
|
||||||
mid: number[];
|
|
||||||
end: number[];
|
|
||||||
color?: string;
|
|
||||||
}>) {
|
|
||||||
const minScale = 0.5;
|
|
||||||
const maxScale = 5;
|
|
||||||
const segmentSize = 0.3;
|
|
||||||
|
|
||||||
const startVec = useMemo(() => new THREE.Vector3(...start), [start]);
|
|
||||||
const midVec = useMemo(() => new THREE.Vector3(...mid), [mid]);
|
|
||||||
const endVec = useMemo(() => new THREE.Vector3(...end), [end]);
|
|
||||||
|
|
||||||
const fullCurve = useMemo(() => new THREE.QuadraticBezierCurve3(startVec, midVec, endVec), [startVec, midVec, endVec]);
|
|
||||||
|
|
||||||
const distance = useMemo(() => startVec.distanceTo(endVec), [startVec, endVec]);
|
|
||||||
const scale = useMemo(() => THREE.MathUtils.clamp(distance * 0.75, minScale, maxScale), [distance]);
|
|
||||||
|
|
||||||
const arrowHeadLength = 0.15 * scale;
|
|
||||||
const arrowRadius = 0.01 * scale;
|
|
||||||
const arrowHeadRadius = arrowRadius * 2.5;
|
|
||||||
|
|
||||||
const subCurve = useMemo(() => {
|
|
||||||
const centerT = 0.5;
|
const centerT = 0.5;
|
||||||
const t1 = Math.max(0, centerT - segmentSize / 2);
|
const t1 = Math.max(0, centerT - segmentSize / 2);
|
||||||
const t2 = Math.min(1, centerT + segmentSize / 2);
|
const t2 = Math.min(1, centerT + segmentSize / 2);
|
||||||
@@ -186,32 +176,159 @@ export function ArrowOnQuadraticBezier({
|
|||||||
const t = THREE.MathUtils.lerp(t1, t2, i / divisions);
|
const t = THREE.MathUtils.lerp(t1, t2, i / divisions);
|
||||||
return fullCurve.getPoint(t);
|
return fullCurve.getPoint(t);
|
||||||
});
|
});
|
||||||
return new THREE.CatmullRomCurve3(subPoints);
|
const subCurve = new THREE.CatmullRomCurve3(subPoints);
|
||||||
}, [fullCurve, segmentSize]);
|
|
||||||
|
|
||||||
const tubeGeometry = useMemo(() => new THREE.TubeGeometry(subCurve, 20, arrowRadius, 8, false), [subCurve, arrowRadius]);
|
const endPoint = subCurve.getPoint(1);
|
||||||
|
const tangent = subCurve.getTangent(1).normalize();
|
||||||
|
|
||||||
const arrowPosition = useMemo(() => subCurve.getPoint(1), [subCurve]);
|
const rotation = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), tangent);
|
||||||
const arrowTangent = useMemo(() => subCurve.getTangent(1).normalize(), [subCurve]);
|
const matrix = new THREE.Matrix4();
|
||||||
|
matrix.compose(endPoint, rotation, new THREE.Vector3(scale, scale, scale));
|
||||||
|
|
||||||
const arrowRotation = useMemo(() => {
|
return {
|
||||||
return new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), arrowTangent);
|
subCurve,
|
||||||
}, [arrowTangent]);
|
scale,
|
||||||
|
matrix,
|
||||||
|
startVec,
|
||||||
|
midVec,
|
||||||
|
endVec,
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
}, [previewLine]);
|
||||||
|
|
||||||
|
const instancedMeshRef = useRef<THREE.InstancedMesh>(null);
|
||||||
|
const tempColor = useMemo(() => new THREE.Color(), []);
|
||||||
|
|
||||||
const coneGeometry = useMemo(() => {
|
const coneGeometry = useMemo(() => {
|
||||||
const geom = new THREE.ConeGeometry(arrowHeadRadius, arrowHeadLength, 8);
|
const geom = new THREE.ConeGeometry(0.025, 0.15, 8);
|
||||||
geom.translate(0, arrowHeadLength / 2, 0);
|
geom.translate(0, 0.075, 0);
|
||||||
return geom;
|
return geom;
|
||||||
}, [arrowHeadRadius, arrowHeadLength]);
|
}, []);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (instancedMeshRef.current) {
|
||||||
|
let index = 0;
|
||||||
|
// Main arrows
|
||||||
|
arrowsData.forEach((data) => {
|
||||||
|
instancedMeshRef.current!.setMatrixAt(index++, data.matrix);
|
||||||
|
});
|
||||||
|
// Preview arrow
|
||||||
|
if (previewData) {
|
||||||
|
instancedMeshRef.current!.setMatrixAt(index, previewData.matrix);
|
||||||
|
}
|
||||||
|
instancedMeshRef.current.instanceMatrix.needsUpdate = true;
|
||||||
|
}
|
||||||
|
}, [arrowsData, previewData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (instancedMeshRef.current) {
|
||||||
|
let index = 0;
|
||||||
|
// Main arrows
|
||||||
|
arrowsData.forEach((data) => {
|
||||||
|
const isHovered = toolMode === "3D-Delete" && hoveredArrowTrigger === data.trigger.triggerUuid;
|
||||||
|
instancedMeshRef.current!.setColorAt(index++, tempColor.set(isHovered ? "red" : "#42a5f5"));
|
||||||
|
});
|
||||||
|
// Preview arrow
|
||||||
|
if (previewData) {
|
||||||
|
instancedMeshRef.current!.setColorAt(index, tempColor.set(previewData.color));
|
||||||
|
}
|
||||||
|
if (instancedMeshRef.current.instanceColor) instancedMeshRef.current.instanceColor.needsUpdate = true;
|
||||||
|
}
|
||||||
|
}, [arrowsData, previewData, hoveredArrowTrigger, toolMode, tempColor]);
|
||||||
|
|
||||||
|
const totalInstances = arrowsData.length + (previewData ? 1 : 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group name="ArrowWithTube">
|
<group name="connectionArrows">
|
||||||
<mesh name="ArrowWithTube" geometry={tubeGeometry}>
|
{/* Connection Lines */}
|
||||||
<meshStandardMaterial color={color} />
|
{linesData.map((line) => (
|
||||||
</mesh>
|
<QuadraticBezierLine
|
||||||
<mesh name="ArrowWithTube" position={arrowPosition} quaternion={arrowRotation} geometry={coneGeometry}>
|
key={line.key}
|
||||||
<meshStandardMaterial color={color} />
|
start={line.start}
|
||||||
</mesh>
|
end={line.end}
|
||||||
|
mid={line.mid}
|
||||||
|
color={toolMode === "3D-Delete" && hoveredArrowTrigger === line.trigger.triggerUuid ? "red" : "#42a5f5"}
|
||||||
|
lineWidth={4}
|
||||||
|
dashed={toolMode === "3D-Delete" && hoveredArrowTrigger === line.trigger.triggerUuid ? false : true}
|
||||||
|
dashSize={0.75}
|
||||||
|
dashScale={20}
|
||||||
|
onPointerOver={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (toolMode === "3D-Delete") setHoveredArrowTrigger(line.trigger.triggerUuid);
|
||||||
|
}}
|
||||||
|
onPointerOut={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (toolMode === "3D-Delete") setHoveredArrowTrigger(null);
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (toolMode === "3D-Delete") removeConnection(line.trigger);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Preview Line */}
|
||||||
|
{previewData && (
|
||||||
|
<QuadraticBezierLine start={previewData.startVec} end={previewData.endVec} mid={previewData.midVec} color={previewData.color} lineWidth={4} dashed dashSize={1} dashScale={20} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Shafts (Main) */}
|
||||||
|
{arrowsData.map((data) => (
|
||||||
|
<mesh
|
||||||
|
key={data.key}
|
||||||
|
onPointerOver={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (toolMode === "3D-Delete") setHoveredArrowTrigger(data.trigger.triggerUuid);
|
||||||
|
}}
|
||||||
|
onPointerOut={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (toolMode === "3D-Delete") setHoveredArrowTrigger(null);
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (toolMode === "3D-Delete") removeConnection(data.trigger);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<tubeGeometry args={[data.subCurve, 8, 0.01 * data.scale, 8, false]} />
|
||||||
|
<meshStandardMaterial color={toolMode === "3D-Delete" && hoveredArrowTrigger === data.trigger.triggerUuid ? "red" : "#42a5f5"} />
|
||||||
|
</mesh>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Shaft (Preview) */}
|
||||||
|
{previewData && (
|
||||||
|
<mesh>
|
||||||
|
<tubeGeometry args={[previewData.subCurve, 8, 0.01 * previewData.scale, 8, false]} />
|
||||||
|
<meshStandardMaterial color={previewData.color} />
|
||||||
|
</mesh>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Heads (Instanced) */}
|
||||||
|
{totalInstances > 0 && (
|
||||||
|
<instancedMesh
|
||||||
|
ref={instancedMeshRef}
|
||||||
|
args={[coneGeometry, undefined, totalInstances]}
|
||||||
|
onPointerOver={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const id = e.instanceId;
|
||||||
|
if (id !== undefined && id < arrowsData.length && toolMode === "3D-Delete") {
|
||||||
|
setHoveredArrowTrigger(arrowsData[id].trigger.triggerUuid);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onPointerOut={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (toolMode === "3D-Delete") setHoveredArrowTrigger(null);
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const id = e.instanceId;
|
||||||
|
if (id !== undefined && id < arrowsData.length && toolMode === "3D-Delete") {
|
||||||
|
removeConnection(arrowsData[id].trigger);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<meshStandardMaterial />
|
||||||
|
</instancedMesh>
|
||||||
|
)}
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { QuadraticBezierLine } from "@react-three/drei";
|
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
import { useSubModuleStore } from "../../../../store/ui/useModuleStore";
|
import { useSubModuleStore } from "../../../../store/ui/useModuleStore";
|
||||||
import { useSelectedAction, useSelectedAsset, useSelectedEventData } from "../../../../store/simulation/useSimulationStore";
|
import { useSelectedAction, useSelectedAsset, useSelectedEventData } from "../../../../store/simulation/useSimulationStore";
|
||||||
import { handleAddEventToProduct } from "../../functions/handleAddEventToProduct";
|
import { handleAddEventToProduct } from "../../functions/handleAddEventToProduct";
|
||||||
import { usePlayButtonStore } from "../../../../store/ui/usePlayButtonStore";
|
import { usePlayButtonStore } from "../../../../store/ui/usePlayButtonStore";
|
||||||
import { useSocketStore } from "../../../../store/socket/useSocketStore";
|
import { useSocketStore } from "../../../../store/socket/useSocketStore";
|
||||||
import { ArrowOnQuadraticBezier, Arrows } from "../arrows/arrows";
|
import { Arrows } from "../arrows/arrows";
|
||||||
import { useToolMode } from "../../../../store/builder/store";
|
import { useToolMode } from "../../../../store/builder/store";
|
||||||
import { useSceneContext } from "../../../scene/sceneContext";
|
import { useSceneContext } from "../../../scene/sceneContext";
|
||||||
|
|
||||||
@@ -483,61 +483,11 @@ function TriggerConnector() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const previewLine = currentLine ? { start: currentLine.start, mid: currentLine.mid, end: currentLine.end, color: helperLineColor } : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group name="simulationConnectionGroup" visible={!isPlaying}>
|
<group name="simulationConnectionGroup" visible={!isPlaying}>
|
||||||
{connections.map((connection) => {
|
<Arrows connections={connections} previewLine={previewLine} />
|
||||||
const startPoint = getWorldPositionFromScene(connection.startPointUuid);
|
|
||||||
const endPoint = getWorldPositionFromScene(connection.endPointUuid);
|
|
||||||
|
|
||||||
if (!startPoint || !endPoint) return null;
|
|
||||||
|
|
||||||
const distance = startPoint.distanceTo(endPoint);
|
|
||||||
const heightFactor = Math.max(0.5, distance * 0.2);
|
|
||||||
const midPoint = new THREE.Vector3((startPoint.x + endPoint.x) / 2, Math.max(startPoint.y, endPoint.y) + heightFactor, (startPoint.z + endPoint.z) / 2);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<QuadraticBezierLine
|
|
||||||
key={connection.id}
|
|
||||||
ref={(el) => (groupRefs.current[connection.id] = el!)}
|
|
||||||
start={startPoint.toArray()}
|
|
||||||
end={endPoint.toArray()}
|
|
||||||
mid={midPoint.toArray()}
|
|
||||||
color={toolMode === "3D-Delete" && hoveredLineKey === connection.id ? "red" : "#42a5f5"}
|
|
||||||
lineWidth={4}
|
|
||||||
dashed={toolMode === "3D-Delete" && hoveredLineKey === connection.id ? false : true}
|
|
||||||
dashSize={0.75}
|
|
||||||
dashScale={20}
|
|
||||||
// onPointerOver={() => setHoveredLineKey(connection.id)}
|
|
||||||
// onPointerOut={() => setHoveredLineKey(null)}
|
|
||||||
// onClick={() => {
|
|
||||||
// if (toolMode === '3D-Delete') {
|
|
||||||
// setHoveredLineKey(null);
|
|
||||||
// setCurrentLine(null);
|
|
||||||
// removeConnection(connection);
|
|
||||||
// }
|
|
||||||
// }}
|
|
||||||
userData={connection.trigger}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
<Arrows connections={connections} />
|
|
||||||
|
|
||||||
{currentLine && (
|
|
||||||
<>
|
|
||||||
<QuadraticBezierLine
|
|
||||||
start={currentLine.start.toArray()}
|
|
||||||
end={currentLine.end.toArray()}
|
|
||||||
mid={currentLine.mid.toArray()}
|
|
||||||
color={helperLineColor}
|
|
||||||
lineWidth={4}
|
|
||||||
dashed
|
|
||||||
dashSize={1}
|
|
||||||
dashScale={20}
|
|
||||||
/>
|
|
||||||
<ArrowOnQuadraticBezier start={currentLine.start.toArray()} mid={currentLine.mid.toArray()} end={currentLine.end.toArray()} color={helperLineColor} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user