added pre defined paths in rough
This commit is contained in:
0
app/src/functions/findShortestPath.ts
Normal file
0
app/src/functions/findShortestPath.ts
Normal file
@@ -1,26 +1,35 @@
|
||||
import * as THREE from 'three'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import ReferenceAisle from './referenceAisle';
|
||||
import ReferencePoint from '../../point/reference/referencePoint';
|
||||
import { getUserData } from '../../../../functions/getUserData';
|
||||
import * as THREE from "three";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import {
|
||||
useActiveLayer,
|
||||
useSocketStore,
|
||||
useToggleView,
|
||||
useToolMode,
|
||||
} from "../../../../store/builder/store";
|
||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useVersionContext } from "../../version/versionContext";
|
||||
import { useSceneContext } from "../../../scene/sceneContext";
|
||||
import ReferenceAisle from "./referenceAisle";
|
||||
import ReferencePoint from "../../point/reference/referencePoint";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
|
||||
// import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi';
|
||||
|
||||
function AisleCreator() {
|
||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const plane = useMemo(
|
||||
() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
|
||||
[]
|
||||
);
|
||||
const { toggleView } = useToggleView();
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { aisleStore, undoRedo2DStore } = useSceneContext();
|
||||
const { addAisle, getAislePointById } = aisleStore();
|
||||
const { aisles, addAisle, getAislePointById } = aisleStore();
|
||||
console.log("aisles: ", aisles);
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
@@ -31,7 +40,20 @@ function AisleCreator() {
|
||||
|
||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, snappedPosition, snappedPoint, setSnappedPosition, setSnappedPoint } = useBuilderStore();
|
||||
const {
|
||||
aisleType,
|
||||
aisleWidth,
|
||||
aisleColor,
|
||||
dashLength,
|
||||
gapLength,
|
||||
dotRadius,
|
||||
aisleLength,
|
||||
isFlipped,
|
||||
snappedPosition,
|
||||
snappedPoint,
|
||||
setSnappedPosition,
|
||||
setSnappedPoint,
|
||||
} = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
@@ -63,13 +85,15 @@ function AisleCreator() {
|
||||
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
if (!position) return;
|
||||
|
||||
const intersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Aisle-Point');
|
||||
const intersects = raycaster
|
||||
.intersectObjects(scene.children)
|
||||
.find((intersect) => intersect.object.name === "Aisle-Point");
|
||||
|
||||
const newPoint: Point = {
|
||||
pointUuid: THREE.MathUtils.generateUUID(),
|
||||
pointType: 'Aisle',
|
||||
pointType: "Aisle",
|
||||
position: [position.x, position.y, position.z],
|
||||
layer: activeLayer
|
||||
layer: activeLayer,
|
||||
};
|
||||
|
||||
if (snappedPosition && snappedPoint) {
|
||||
@@ -78,7 +102,9 @@ function AisleCreator() {
|
||||
newPoint.layer = snappedPoint.layer;
|
||||
}
|
||||
|
||||
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) { return }
|
||||
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (snappedPosition && !snappedPoint) {
|
||||
newPoint.position = snappedPosition;
|
||||
@@ -93,8 +119,7 @@ function AisleCreator() {
|
||||
}
|
||||
}
|
||||
|
||||
if (aisleType === 'solid-aisle') {
|
||||
|
||||
if (aisleType === "solid-aisle") {
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
@@ -103,50 +128,48 @@ function AisleCreator() {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'solid-aisle',
|
||||
aisleType: "solid-aisle",
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth
|
||||
}
|
||||
aisleWidth: aisleWidth,
|
||||
},
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
type: "Draw",
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
actionType: "Line-Create",
|
||||
point: {
|
||||
type: 'Aisle',
|
||||
type: "Aisle",
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
socket.emit("v1:model-aisle:add", {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
type: aisle.type,
|
||||
});
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'dashed-aisle') {
|
||||
|
||||
} else if (aisleType === "dashed-aisle") {
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
@@ -155,52 +178,50 @@ function AisleCreator() {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'dashed-aisle',
|
||||
aisleType: "dashed-aisle",
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
dashLength: dashLength,
|
||||
gapLength: gapLength
|
||||
}
|
||||
gapLength: gapLength,
|
||||
},
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
type: "Draw",
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
actionType: "Line-Create",
|
||||
point: {
|
||||
type: 'Aisle',
|
||||
type: "Aisle",
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
socket.emit("v1:model-aisle:add", {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
type: aisle.type,
|
||||
});
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'dotted-aisle') {
|
||||
|
||||
} else if (aisleType === "dotted-aisle") {
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
@@ -209,51 +230,49 @@ function AisleCreator() {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'dotted-aisle',
|
||||
aisleType: "dotted-aisle",
|
||||
aisleColor: aisleColor,
|
||||
dotRadius: dotRadius,
|
||||
gapLength: gapLength
|
||||
}
|
||||
gapLength: gapLength,
|
||||
},
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
type: "Draw",
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
actionType: "Line-Create",
|
||||
point: {
|
||||
type: 'Aisle',
|
||||
type: "Aisle",
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
socket.emit("v1:model-aisle:add", {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
type: aisle.type,
|
||||
});
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'arrow-aisle') {
|
||||
|
||||
} else if (aisleType === "arrow-aisle") {
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
@@ -262,50 +281,48 @@ function AisleCreator() {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'arrow-aisle',
|
||||
aisleType: "arrow-aisle",
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth
|
||||
}
|
||||
aisleWidth: aisleWidth,
|
||||
},
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
type: "Draw",
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
actionType: "Line-Create",
|
||||
point: {
|
||||
type: 'Aisle',
|
||||
type: "Aisle",
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
socket.emit("v1:model-aisle:add", {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
type: aisle.type,
|
||||
});
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'arrows-aisle') {
|
||||
|
||||
} else if (aisleType === "arrows-aisle") {
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
@@ -314,52 +331,50 @@ function AisleCreator() {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'arrows-aisle',
|
||||
aisleType: "arrows-aisle",
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
aisleLength: aisleLength,
|
||||
gapLength: gapLength
|
||||
}
|
||||
gapLength: gapLength,
|
||||
},
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
type: "Draw",
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
actionType: "Line-Create",
|
||||
point: {
|
||||
type: 'Aisle',
|
||||
type: "Aisle",
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
socket.emit("v1:model-aisle:add", {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
type: aisle.type,
|
||||
});
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'arc-aisle') {
|
||||
|
||||
} else if (aisleType === "arc-aisle") {
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
@@ -368,51 +383,49 @@ function AisleCreator() {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'arc-aisle',
|
||||
aisleType: "arc-aisle",
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
isFlipped: isFlipped
|
||||
}
|
||||
isFlipped: isFlipped,
|
||||
},
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
type: "Draw",
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
actionType: "Line-Create",
|
||||
point: {
|
||||
type: 'Aisle',
|
||||
type: "Aisle",
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
socket.emit("v1:model-aisle:add", {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
type: aisle.type,
|
||||
});
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'circle-aisle') {
|
||||
|
||||
} else if (aisleType === "circle-aisle") {
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
@@ -421,98 +434,95 @@ function AisleCreator() {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'circle-aisle',
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth
|
||||
}
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
point: {
|
||||
type: 'Aisle',
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === 'junction-aisle') {
|
||||
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: 'junction-aisle',
|
||||
aisleType: "circle-aisle",
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
isFlipped: isFlipped
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
type: "Draw",
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
actionType: "Line-Create",
|
||||
point: {
|
||||
type: 'Aisle',
|
||||
type: "Aisle",
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit('v1:model-aisle:add', {
|
||||
socket.emit("v1:model-aisle:add", {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type
|
||||
})
|
||||
type: aisle.type,
|
||||
});
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
} else if (aisleType === "junction-aisle") {
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
} else {
|
||||
const aisle: Aisle = {
|
||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||
points: [tempPoints[0], newPoint],
|
||||
type: {
|
||||
aisleType: "junction-aisle",
|
||||
aisleColor: aisleColor,
|
||||
aisleWidth: aisleWidth,
|
||||
isFlipped: isFlipped,
|
||||
},
|
||||
};
|
||||
|
||||
addAisle(aisle);
|
||||
|
||||
push2D({
|
||||
type: "Draw",
|
||||
actions: [
|
||||
{
|
||||
actionType: "Line-Create",
|
||||
point: {
|
||||
type: "Aisle",
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||
|
||||
// SOCKET
|
||||
|
||||
socket.emit("v1:model-aisle:add", {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: aisle.aisleUuid,
|
||||
points: aisle.points,
|
||||
type: aisle.type,
|
||||
});
|
||||
}
|
||||
setTempPoints([newPoint]);
|
||||
}
|
||||
@@ -556,23 +566,46 @@ function AisleCreator() {
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addAisle, getAislePointById, aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, snappedPosition, snappedPoint, selectedVersion?.versionId]);
|
||||
}, [
|
||||
gl,
|
||||
camera,
|
||||
scene,
|
||||
raycaster,
|
||||
pointer,
|
||||
plane,
|
||||
toggleView,
|
||||
toolMode,
|
||||
activeLayer,
|
||||
socket,
|
||||
tempPoints,
|
||||
isCreating,
|
||||
addAisle,
|
||||
getAislePointById,
|
||||
aisleType,
|
||||
aisleWidth,
|
||||
aisleColor,
|
||||
dashLength,
|
||||
gapLength,
|
||||
dotRadius,
|
||||
aisleLength,
|
||||
snappedPosition,
|
||||
snappedPoint,
|
||||
selectedVersion?.versionId,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toggleView &&
|
||||
{toggleView && (
|
||||
<>
|
||||
<group name='Aisle-Reference-Points-Group'>
|
||||
<group name="Aisle-Reference-Points-Group">
|
||||
{tempPoints.map((point) => (
|
||||
<ReferencePoint key={point.pointUuid} point={point} />
|
||||
))}
|
||||
</group>
|
||||
|
||||
{tempPoints.length > 0 &&
|
||||
<ReferenceAisle tempPoints={tempPoints} />
|
||||
}
|
||||
{tempPoints.length > 0 && <ReferenceAisle tempPoints={tempPoints} />}
|
||||
</>
|
||||
}
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,586 @@
|
||||
// import React, { useRef, useState } from "react";
|
||||
// import { useThree, useFrame } from "@react-three/fiber";
|
||||
// import * as THREE from "three";
|
||||
// import { Line } from "@react-three/drei";
|
||||
|
||||
// interface PointProps {
|
||||
// point: any;
|
||||
// pointIndex: number;
|
||||
// groupIndex: number;
|
||||
// selected: number[];
|
||||
// setPointsGroups: React.Dispatch<React.SetStateAction<any[][]>>;
|
||||
// }
|
||||
|
||||
// export default function EditablePoint({
|
||||
// point,
|
||||
// pointIndex,
|
||||
// groupIndex,
|
||||
// selected,
|
||||
// setPointsGroups,
|
||||
// }: PointProps) {
|
||||
// const meshRef = useRef<THREE.Mesh>(null);
|
||||
// const handleARef = useRef<THREE.Mesh>(null);
|
||||
// const handleBRef = useRef<THREE.Mesh>(null);
|
||||
// const lineRef = useRef<THREE.Line>(null!);
|
||||
|
||||
// const { camera, gl, controls } = useThree();
|
||||
// const [dragging, setDragging] = useState<
|
||||
// null | "main" | "handleA" | "handleB"
|
||||
// >(null);
|
||||
// const dragOffset = useRef(new THREE.Vector3());
|
||||
|
||||
// /** Handle clicking the point */
|
||||
// const onPointClick = (e: any) => {
|
||||
// e.stopPropagation();
|
||||
|
||||
// if (e.ctrlKey) {
|
||||
// // Toggle curve handles
|
||||
// setPointsGroups((prev) => {
|
||||
// const newGroups = [...prev];
|
||||
// const group = [...newGroups[groupIndex]];
|
||||
// const idx = group.findIndex((p) => p.pointId === point.pointId);
|
||||
// const updated = { ...group[idx] };
|
||||
|
||||
// if (!updated.handleA && !updated.handleB) {
|
||||
// updated.handleA = [
|
||||
// updated.position[0] + 1,
|
||||
// updated.position[1],
|
||||
// updated.position[2],
|
||||
// ];
|
||||
// updated.handleB = [
|
||||
// updated.position[0] - 1,
|
||||
// updated.position[1],
|
||||
// updated.position[2],
|
||||
// ];
|
||||
// updated.isCurved = true;
|
||||
// } else {
|
||||
// updated.handleA = null;
|
||||
// updated.handleB = null;
|
||||
// updated.isCurved = false;
|
||||
// }
|
||||
|
||||
// group[idx] = updated;
|
||||
// newGroups[groupIndex] = group;
|
||||
// return newGroups;
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
// /** Pointer down for dragging */
|
||||
// const startDrag = (target: "main" | "handleA" | "handleB", e: any) => {
|
||||
// e.stopPropagation();
|
||||
// setDragging(target);
|
||||
// const targetRef =
|
||||
// target === "main"
|
||||
// ? meshRef.current
|
||||
// : target === "handleA"
|
||||
// ? handleARef.current
|
||||
// : handleBRef.current;
|
||||
// if (targetRef) {
|
||||
// dragOffset.current.copy(targetRef.position).sub(e.point);
|
||||
// }
|
||||
// if (controls) (controls as any).enabled = false;
|
||||
// gl.domElement.style.cursor = "grabbing";
|
||||
// };
|
||||
|
||||
// /** Pointer up stops dragging */
|
||||
// const stopDrag = () => {
|
||||
// setDragging(null);
|
||||
// gl.domElement.style.cursor = "auto";
|
||||
// if (controls) (controls as any).enabled = true;
|
||||
// };
|
||||
|
||||
// /** Handle dragging logic */
|
||||
// useFrame(({ raycaster, mouse }) => {
|
||||
// if (!dragging) return;
|
||||
|
||||
// const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
// raycaster.setFromCamera(mouse, camera);
|
||||
// const intersection = new THREE.Vector3();
|
||||
|
||||
// if (raycaster.ray.intersectPlane(plane, intersection)) {
|
||||
// const newPos = intersection.add(dragOffset.current);
|
||||
|
||||
// setPointsGroups((prev) => {
|
||||
// const newGroups = [...prev];
|
||||
// const group = [...newGroups[groupIndex]];
|
||||
// const idx = group.findIndex((p) => p.pointId === point.pointId);
|
||||
// const updated = { ...group[idx] };
|
||||
|
||||
// if (dragging === "main") {
|
||||
// const delta = new THREE.Vector3()
|
||||
// .fromArray(newPos.toArray())
|
||||
// .sub(new THREE.Vector3().fromArray(updated.position));
|
||||
// updated.position = newPos.toArray() as [number, number, number];
|
||||
|
||||
// // Move handles along with the main point
|
||||
// if (updated.handleA) {
|
||||
// updated.handleA = new THREE.Vector3()
|
||||
// .fromArray(updated.handleA)
|
||||
// .add(delta)
|
||||
// .toArray() as [number, number, number];
|
||||
// }
|
||||
// if (updated.handleB) {
|
||||
// updated.handleB = new THREE.Vector3()
|
||||
// .fromArray(updated.handleB)
|
||||
// .add(delta)
|
||||
// .toArray() as [number, number, number];
|
||||
// }
|
||||
// } else {
|
||||
// updated[dragging] = newPos.toArray() as [number, number, number];
|
||||
|
||||
// // Mirror opposite handle
|
||||
// if (updated.isCurved) {
|
||||
// const mainPos = new THREE.Vector3().fromArray(updated.position);
|
||||
// const thisHandle = new THREE.Vector3().fromArray(
|
||||
// updated[dragging]!
|
||||
// );
|
||||
// const mirrorHandle = mainPos
|
||||
// .clone()
|
||||
// .sub(thisHandle.clone().sub(mainPos));
|
||||
|
||||
// if (dragging === "handleA")
|
||||
// updated.handleB = mirrorHandle.toArray() as [
|
||||
// number,
|
||||
// number,
|
||||
// number
|
||||
// ];
|
||||
// if (dragging === "handleB")
|
||||
// updated.handleA = mirrorHandle.toArray() as [
|
||||
// number,
|
||||
// number,
|
||||
// number
|
||||
// ];
|
||||
// }
|
||||
// }
|
||||
|
||||
// group[idx] = updated;
|
||||
// newGroups[groupIndex] = group;
|
||||
// return newGroups;
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
// /** Update line between handles each frame */
|
||||
// useFrame(() => {
|
||||
// if (lineRef.current && point.handleA && point.handleB) {
|
||||
// const positions = lineRef.current.geometry.attributes.position
|
||||
// .array as Float32Array;
|
||||
// positions[0] = point.handleA[0];
|
||||
// positions[1] = point.handleA[1];
|
||||
// positions[2] = point.handleA[2];
|
||||
// positions[3] = point.handleB[0];
|
||||
// positions[4] = point.handleB[1];
|
||||
// positions[5] = point.handleB[2];
|
||||
// lineRef.current.geometry.attributes.position.needsUpdate = true;
|
||||
// }
|
||||
// });
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {/* Main point */}
|
||||
// <mesh
|
||||
// ref={meshRef}
|
||||
// position={point.position}
|
||||
// onClick={onPointClick}
|
||||
// onPointerDown={(e) => startDrag("main", e)}
|
||||
// onPointerUp={stopDrag}
|
||||
// >
|
||||
// <sphereGeometry args={[0.3, 16, 16]} />
|
||||
// <meshStandardMaterial
|
||||
// color={selected.includes(pointIndex) ? "red" : "pink"}
|
||||
// />
|
||||
// </mesh>
|
||||
|
||||
// {/* Handles + line */}
|
||||
// {point.isCurved && point.handleA && point.handleB && (
|
||||
// <>
|
||||
// {/* Line between handles */}
|
||||
// {point.handleA && point.handleB && (
|
||||
// <Line
|
||||
// points={[point.handleA, point.handleB]}
|
||||
// color="gray"
|
||||
// lineWidth={1} // optional, works in WebGL2
|
||||
// />
|
||||
// )}
|
||||
|
||||
// {/* Handle A */}
|
||||
// <mesh
|
||||
// ref={handleARef}
|
||||
// position={point.handleA}
|
||||
// onPointerDown={(e) => startDrag("handleA", e)}
|
||||
// onPointerUp={stopDrag}
|
||||
// >
|
||||
// <sphereGeometry args={[0.15, 8, 8]} />
|
||||
// <meshStandardMaterial color="orange" />
|
||||
// </mesh>
|
||||
|
||||
// {/* Handle B */}
|
||||
// <mesh
|
||||
// ref={handleBRef}
|
||||
// position={point.handleB}
|
||||
// onPointerDown={(e) => startDrag("handleB", e)}
|
||||
// onPointerUp={stopDrag}
|
||||
// >
|
||||
// <sphereGeometry args={[0.15, 8, 8]} />
|
||||
// <meshStandardMaterial color="green" />
|
||||
// </mesh>
|
||||
// </>
|
||||
// )}
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { useThree, useFrame } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import { Line } from "@react-three/drei";
|
||||
|
||||
interface PointProps {
|
||||
point: any;
|
||||
pointIndex: number;
|
||||
groupIndex: number;
|
||||
selected: number[];
|
||||
setSelected: React.Dispatch<React.SetStateAction<number[]>>;
|
||||
pointsGroups: any[][];
|
||||
setPointsGroups: React.Dispatch<React.SetStateAction<any[][]>>;
|
||||
shortestPath: number[]; // <- add this
|
||||
setShortestPath: React.Dispatch<React.SetStateAction<number[]>>; // <- add this
|
||||
setShortestDistance?: React.Dispatch<React.SetStateAction<number>>; // optional
|
||||
}
|
||||
|
||||
export default function PointHandle({
|
||||
point,
|
||||
pointIndex,
|
||||
groupIndex,
|
||||
selected,
|
||||
setSelected,
|
||||
pointsGroups,
|
||||
setPointsGroups,
|
||||
setShortestDistance,
|
||||
shortestPath,
|
||||
setShortestPath,
|
||||
}: PointProps) {
|
||||
|
||||
const meshRef = useRef<THREE.Mesh>(null);
|
||||
const handleARef = useRef<THREE.Mesh>(null);
|
||||
const handleBRef = useRef<THREE.Mesh>(null);
|
||||
const lineRef = useRef<THREE.Line>(null!);
|
||||
// const pathLineRef = useRef<THREE.Line>(null!);
|
||||
|
||||
const { camera, gl, controls } = useThree();
|
||||
const [dragging, setDragging] = useState<
|
||||
null | "main" | "handleA" | "handleB"
|
||||
>(null);
|
||||
const dragOffset = useRef(new THREE.Vector3());
|
||||
// const [shortestPath, setShortestPath] = useState<number[]>([]);
|
||||
|
||||
/** Shift-click or ctrl-click handling */
|
||||
const onPointClick = (e: any) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (e.ctrlKey) {
|
||||
// Toggle handles
|
||||
setPointsGroups((prev) => {
|
||||
const newGroups = [...prev];
|
||||
const group = [...newGroups[groupIndex]];
|
||||
const idx = group.findIndex((p) => p.pointId === point.pointId);
|
||||
const updated = { ...group[idx] };
|
||||
|
||||
if (!updated.handleA && !updated.handleB) {
|
||||
updated.handleA = [
|
||||
updated.position[0] + 1,
|
||||
updated.position[1],
|
||||
updated.position[2],
|
||||
];
|
||||
updated.handleB = [
|
||||
updated.position[0] - 1,
|
||||
updated.position[1],
|
||||
updated.position[2],
|
||||
];
|
||||
updated.isCurved = true;
|
||||
} else {
|
||||
updated.handleA = null;
|
||||
updated.handleB = null;
|
||||
updated.isCurved = false;
|
||||
}
|
||||
|
||||
group[idx] = updated;
|
||||
newGroups[groupIndex] = group;
|
||||
return newGroups;
|
||||
});
|
||||
} else if (e.shiftKey) {
|
||||
// Shift-click for multi-select
|
||||
setSelected((prev) => {
|
||||
if (prev.includes(pointIndex)) return prev; // keep selection
|
||||
const newSelection = [...prev, pointIndex];
|
||||
return newSelection.slice(-2); // keep only 2 points
|
||||
});
|
||||
} else {
|
||||
// Single selection
|
||||
setSelected([pointIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Dragging logic */
|
||||
const startDrag = (target: "main" | "handleA" | "handleB", e: any) => {
|
||||
e.stopPropagation();
|
||||
setDragging(target);
|
||||
const targetRef =
|
||||
target === "main"
|
||||
? meshRef.current
|
||||
: target === "handleA"
|
||||
? handleARef.current
|
||||
: handleBRef.current;
|
||||
if (targetRef) dragOffset.current.copy(targetRef.position).sub(e.point);
|
||||
if (controls) (controls as any).enabled = false;
|
||||
gl.domElement.style.cursor = "grabbing";
|
||||
};
|
||||
|
||||
const stopDrag = () => {
|
||||
setDragging(null);
|
||||
gl.domElement.style.cursor = "auto";
|
||||
if (controls) (controls as any).enabled = true;
|
||||
};
|
||||
|
||||
useFrame(({ raycaster, mouse }) => {
|
||||
if (!dragging) return;
|
||||
|
||||
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersection = new THREE.Vector3();
|
||||
|
||||
if (raycaster.ray.intersectPlane(plane, intersection)) {
|
||||
const newPos = intersection.add(dragOffset.current);
|
||||
|
||||
setPointsGroups((prev) => {
|
||||
const newGroups = [...prev];
|
||||
const group = [...newGroups[groupIndex]];
|
||||
const idx = group.findIndex((p) => p.pointId === point.pointId);
|
||||
const updated = { ...group[idx] };
|
||||
|
||||
if (dragging === "main") {
|
||||
const delta = new THREE.Vector3()
|
||||
.fromArray(newPos.toArray())
|
||||
.sub(new THREE.Vector3().fromArray(updated.position));
|
||||
updated.position = newPos.toArray() as [number, number, number];
|
||||
|
||||
if (updated.handleA) {
|
||||
updated.handleA = new THREE.Vector3()
|
||||
.fromArray(updated.handleA)
|
||||
.add(delta)
|
||||
.toArray() as [number, number, number];
|
||||
}
|
||||
if (updated.handleB) {
|
||||
updated.handleB = new THREE.Vector3()
|
||||
.fromArray(updated.handleB)
|
||||
.add(delta)
|
||||
.toArray() as [number, number, number];
|
||||
}
|
||||
} else {
|
||||
updated[dragging] = newPos.toArray() as [number, number, number];
|
||||
if (updated.isCurved) {
|
||||
const mainPos = new THREE.Vector3().fromArray(updated.position);
|
||||
const thisHandle = new THREE.Vector3().fromArray(
|
||||
updated[dragging]!
|
||||
);
|
||||
const mirrorHandle = mainPos
|
||||
.clone()
|
||||
.sub(thisHandle.clone().sub(mainPos));
|
||||
|
||||
if (dragging === "handleA")
|
||||
updated.handleB = mirrorHandle.toArray() as [
|
||||
number,
|
||||
number,
|
||||
number
|
||||
];
|
||||
if (dragging === "handleB")
|
||||
updated.handleA = mirrorHandle.toArray() as [
|
||||
number,
|
||||
number,
|
||||
number
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
group[idx] = updated;
|
||||
newGroups[groupIndex] = group;
|
||||
return newGroups;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/** Update handle lines */
|
||||
useFrame(() => {
|
||||
if (lineRef.current && point.handleA && point.handleB) {
|
||||
const positions = lineRef.current.geometry.attributes.position
|
||||
.array as Float32Array;
|
||||
positions[0] = point.handleA[0];
|
||||
positions[1] = point.handleA[1];
|
||||
positions[2] = point.handleA[2];
|
||||
positions[3] = point.handleB[0];
|
||||
positions[4] = point.handleB[1];
|
||||
positions[5] = point.handleB[2];
|
||||
lineRef.current.geometry.attributes.position.needsUpdate = true;
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (selected.length === 2) {
|
||||
const groupPoints = pointsGroups[groupIndex];
|
||||
if (!groupPoints) return;
|
||||
|
||||
const pathPoints = selected
|
||||
.map((i) => groupPoints[i])
|
||||
.filter((p) => p !== undefined)
|
||||
.map((p) => p.position);
|
||||
|
||||
setShortestPath(pathPoints);
|
||||
|
||||
// compute distance
|
||||
let totalDistance = 0;
|
||||
for (let i = 0; i < pathPoints.length - 1; i++) {
|
||||
const p1 = new THREE.Vector3().fromArray(pathPoints[i]);
|
||||
const p2 = new THREE.Vector3().fromArray(pathPoints[i + 1]);
|
||||
totalDistance += p1.distanceTo(p2);
|
||||
}
|
||||
setShortestDistance?.(totalDistance);
|
||||
} else {
|
||||
setShortestPath([]);
|
||||
setShortestDistance?.(0);
|
||||
}
|
||||
}, [selected, pointsGroups]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Main point */}
|
||||
<mesh
|
||||
ref={meshRef}
|
||||
position={point.position}
|
||||
onClick={onPointClick}
|
||||
onPointerDown={(e) => startDrag("main", e)}
|
||||
onPointerUp={stopDrag}
|
||||
>
|
||||
<sphereGeometry args={[0.3, 16, 16]} />
|
||||
<meshStandardMaterial
|
||||
color={selected.includes(pointIndex) ? "red" : "pink"}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Handles + line */}
|
||||
{point.isCurved && point.handleA && point.handleB && (
|
||||
<>
|
||||
<Line
|
||||
points={[point.handleA, point.handleB]}
|
||||
color="gray"
|
||||
lineWidth={1}
|
||||
/>
|
||||
<mesh
|
||||
ref={handleARef}
|
||||
position={point.handleA}
|
||||
onPointerDown={(e) => startDrag("handleA", e)}
|
||||
onPointerUp={stopDrag}
|
||||
>
|
||||
<sphereGeometry args={[0.15, 8, 8]} />
|
||||
<meshStandardMaterial color="orange" />
|
||||
</mesh>
|
||||
<mesh
|
||||
ref={handleBRef}
|
||||
position={point.handleB}
|
||||
onPointerDown={(e) => startDrag("handleB", e)}
|
||||
onPointerUp={stopDrag}
|
||||
>
|
||||
<sphereGeometry args={[0.15, 8, 8]} />
|
||||
<meshStandardMaterial color="green" />
|
||||
</mesh>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Highlight shortest path */}
|
||||
{shortestPath.length > 1 && (
|
||||
<Line
|
||||
points={shortestPath} // <- just use the positions array
|
||||
color="blue"
|
||||
lineWidth={2}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/** Build adjacency list for shortest path */
|
||||
// const buildGraph = (points: any[]) => {
|
||||
// const graph: Record<number, { neighbor: number; distance: number }[]> = {};
|
||||
// points.forEach((p, idx) => {
|
||||
// graph[idx] = [];
|
||||
// points.forEach((q, j) => {
|
||||
// if (idx !== j) {
|
||||
// const d = new THREE.Vector3()
|
||||
// .fromArray(p.position)
|
||||
// .distanceTo(new THREE.Vector3().fromArray(q.position));
|
||||
// graph[idx].push({ neighbor: j, distance: d });
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// return graph;
|
||||
// };
|
||||
|
||||
// /** Dijkstra shortest path */
|
||||
// const findShortestPath = (graph: any, startIdx: number, endIdx: number) => {
|
||||
// const distances: number[] = Array(Object.keys(graph).length).fill(Infinity);
|
||||
// const previous: (number | null)[] = Array(distances.length).fill(null);
|
||||
// distances[startIdx] = 0;
|
||||
// const queue = new Set(Object.keys(graph).map(Number));
|
||||
|
||||
// while (queue.size) {
|
||||
// let current = [...queue].reduce((a, b) =>
|
||||
// distances[a] < distances[b] ? a : b
|
||||
// );
|
||||
// if (current === endIdx) break;
|
||||
// queue.delete(current);
|
||||
|
||||
// for (const { neighbor, distance } of graph[current]) {
|
||||
// const alt = distances[current] + distance;
|
||||
// if (alt < distances[neighbor]) {
|
||||
// distances[neighbor] = alt;
|
||||
// previous[neighbor] = current;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// const path: number[] = [];
|
||||
// let u: number | null = endIdx;
|
||||
// while (u !== null) {
|
||||
// path.unshift(u);
|
||||
// u = previous[u];
|
||||
// }
|
||||
//
|
||||
// return path;
|
||||
// };
|
||||
|
||||
// /** Calculate shortest path when 2 points are selected */
|
||||
// useEffect(() => {
|
||||
// if (selected.length === 2) {
|
||||
// const groupPoints = pointsGroups[groupIndex];
|
||||
// const graph = buildGraph(groupPoints);
|
||||
// const path = findShortestPath(graph, selected[0], selected[1]);
|
||||
// setShortestPath(path);
|
||||
|
||||
// // Calculate distance
|
||||
// if (setShortestDistance) {
|
||||
// let totalDistance = 0;
|
||||
// for (let i = 0; i < path.length - 1; i++) {
|
||||
// const p1 = new THREE.Vector3().fromArray(
|
||||
// groupPoints[path[i]].position
|
||||
// );
|
||||
// const p2 = new THREE.Vector3().fromArray(
|
||||
// groupPoints[path[i + 1]].position
|
||||
// );
|
||||
// totalDistance += p1.distanceTo(p2);
|
||||
// }
|
||||
// setShortestDistance?.(totalDistance);
|
||||
// }
|
||||
// } else {
|
||||
// setShortestPath([]);
|
||||
// if (setShortestDistance) setShortestDistance(0);
|
||||
// }
|
||||
// }, [selected, pointsGroups]);
|
||||
@@ -0,0 +1,22 @@
|
||||
export function findShortestPath(
|
||||
startIndex: number,
|
||||
endIndex: number,
|
||||
adjacency: number[][]
|
||||
) {
|
||||
const queue = [[startIndex]];
|
||||
const visited = new Set<number>();
|
||||
|
||||
while (queue.length > 0) {
|
||||
const path = queue.shift()!;
|
||||
const node = path[path.length - 1];
|
||||
if (node === endIndex) return path;
|
||||
|
||||
if (!visited.has(node)) {
|
||||
visited.add(node);
|
||||
for (const neighbor of adjacency[node]) {
|
||||
queue.push([...path, neighbor]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Line } from "@react-three/drei";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { CubicBezierCurve3, LineCurve3, Plane, Vector3 } from "three";
|
||||
|
||||
export default function LineSegment({
|
||||
index,
|
||||
createdPoints,
|
||||
updatePoints,
|
||||
insertPoint,
|
||||
}: {
|
||||
index: number;
|
||||
createdPoints: any[]; // Array of points with position, isCurved, handleA, handleB
|
||||
updatePoints: (i0: number, p0: Vector3, i1: number, p1: Vector3) => void;
|
||||
insertPoint?: (index: number, point: Vector3) => void;
|
||||
}) {
|
||||
const { gl, raycaster, camera, controls } = useThree();
|
||||
const plane = new Plane(new Vector3(0, 1, 0), 0);
|
||||
const dragStart = useRef<Vector3 | null>(null);
|
||||
|
||||
// ======== Curve or Line Points ========
|
||||
const curvePoints = useMemo(() => {
|
||||
if (!createdPoints || index + 1 >= createdPoints.length) return [];
|
||||
|
||||
const current = createdPoints[index];
|
||||
const next = createdPoints[index + 1];
|
||||
|
||||
const starts = new Vector3(...current.position);
|
||||
const ends = new Vector3(...next.position);
|
||||
|
||||
const useCurve =
|
||||
(current.isCurved && current.handleB) || (next.isCurved && next.handleA);
|
||||
|
||||
const hB = current.handleB ? new Vector3(...current.handleB) : starts;
|
||||
const hA = next.handleA ? new Vector3(...next.handleA) : ends;
|
||||
|
||||
const curve = useCurve
|
||||
? new CubicBezierCurve3(starts, hB, hA, ends)
|
||||
: new LineCurve3(starts, ends);
|
||||
|
||||
return curve.getPoints(useCurve ? 100 : 2);
|
||||
}, [createdPoints, index]);
|
||||
|
||||
// ======== Events ========
|
||||
const onPointerUp = () => {
|
||||
dragStart.current = null;
|
||||
gl.domElement.style.cursor = "default";
|
||||
if (controls) (controls as any).enabled = true;
|
||||
};
|
||||
|
||||
const onClickLine = () => {
|
||||
const intersection = new Vector3();
|
||||
if (raycaster.ray.intersectPlane(plane, intersection)) {
|
||||
const start = new Vector3(...createdPoints[index].position);
|
||||
const end = new Vector3(...createdPoints[index + 1].position);
|
||||
const segLen = start.distanceTo(end);
|
||||
const distToStart = start.distanceTo(intersection);
|
||||
const distToEnd = end.distanceTo(intersection);
|
||||
|
||||
if (
|
||||
distToStart > 0.01 &&
|
||||
distToEnd > 0.01 &&
|
||||
distToStart + distToEnd <= segLen + 0.01
|
||||
) {
|
||||
insertPoint?.(index + 1, intersection);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
gl.domElement.addEventListener("pointerup", onPointerUp);
|
||||
return () => {
|
||||
gl.domElement.removeEventListener("pointerup", onPointerUp);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ======== Render ========
|
||||
return (
|
||||
<Line
|
||||
points={curvePoints}
|
||||
color="purple"
|
||||
lineWidth={2}
|
||||
onPointerDown={onClickLine}
|
||||
onPointerUp={onPointerUp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,717 @@
|
||||
import { Line, Plane } from "@react-three/drei";
|
||||
import { ThreeEvent, useFrame, useThree } from "@react-three/fiber";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Matrix4, Mesh, Quaternion, Vector3 } from "three";
|
||||
import {
|
||||
useAnimationPlaySpeed,
|
||||
usePlayButtonStore,
|
||||
} from "../../../../store/usePlayButtonStore";
|
||||
import { useSceneContext } from "../../../scene/sceneContext";
|
||||
import { findShortestPath } from "./functions/findShortestPath";
|
||||
import * as THREE from "three";
|
||||
import PointHandle from "./PointHandle";
|
||||
import LineSegment from "./lineSegment";
|
||||
|
||||
// export default function PreDefinedPath() {
|
||||
// const { gl, raycaster } = useThree();
|
||||
// const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0));
|
||||
// const [points, setPoints] = useState<[number, number, number][]>([]);
|
||||
// const [selected, setSelected] = useState<number[]>([]);
|
||||
// const downPosition = useRef<{ x: number; y: number } | null>(null);
|
||||
// const hasClicked = useRef(false);
|
||||
|
||||
// const handleMouseDown = useCallback((e: MouseEvent) => {
|
||||
// hasClicked.current = false;
|
||||
// downPosition.current = { x: e.clientX, y: e.clientY };
|
||||
// }, []);
|
||||
|
||||
// const handleClick = useCallback(
|
||||
// (e: MouseEvent) => {
|
||||
// if (hasClicked.current) return;
|
||||
// hasClicked.current = true;
|
||||
|
||||
// if (
|
||||
// !downPosition.current ||
|
||||
// Math.abs(downPosition.current.x - e.clientX) > 2 ||
|
||||
// Math.abs(downPosition.current.y - e.clientY) > 2
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const intersection = new THREE.Vector3();
|
||||
// if (raycaster.ray.intersectPlane(plane.current, intersection)) {
|
||||
// const pointArray = intersection.toArray() as [number, number, number];
|
||||
// const alreadyExists = points.some(
|
||||
// (p) =>
|
||||
// Math.abs(p[0] - pointArray[0]) < 0.01 &&
|
||||
// Math.abs(p[1] - pointArray[1]) < 0.01 &&
|
||||
// Math.abs(p[2] - pointArray[2]) < 0.01
|
||||
// );
|
||||
|
||||
// if (!alreadyExists) {
|
||||
// setPoints((prev) => [...prev, pointArray]);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// [raycaster, points]
|
||||
// );
|
||||
|
||||
// useEffect(() => {
|
||||
// const domElement = gl.domElement;
|
||||
// domElement.addEventListener("mousedown", handleMouseDown);
|
||||
// domElement.addEventListener("mouseup", handleClick);
|
||||
// return () => {
|
||||
// domElement.removeEventListener("mousedown", handleMouseDown);
|
||||
// domElement.removeEventListener("mouseup", handleClick);
|
||||
// };
|
||||
// }, [handleClick, handleMouseDown]);
|
||||
|
||||
// // Update two existing points
|
||||
// const updatePoints = (
|
||||
// i0: number,
|
||||
// p0: THREE.Vector3,
|
||||
// i1: number,
|
||||
// p1: THREE.Vector3
|
||||
// ) => {
|
||||
// const updated = [...points];
|
||||
// updated[i0] = p0.toArray() as [number, number, number];
|
||||
// updated[i1] = p1.toArray() as [number, number, number];
|
||||
// setPoints(updated);
|
||||
// };
|
||||
|
||||
// const insertPoint = (index: number, point: THREE.Vector3) => {
|
||||
// const updated = [...points];
|
||||
// updated.splice(index, 0, point.toArray() as [number, number, number]);
|
||||
// setPoints(updated);
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {points.map((pos, idx) => (
|
||||
// <mesh
|
||||
// key={idx}
|
||||
// position={pos}
|
||||
// onClick={() => {
|
||||
// setSelected((prev) => (prev.length === 2 ? [idx] : [...prev, idx]));
|
||||
// }}
|
||||
// >
|
||||
// <sphereGeometry args={[0.3, 16, 16]} />
|
||||
// <meshStandardMaterial
|
||||
// color={selected.includes(idx) ? "red" : "pink"}
|
||||
// />
|
||||
// </mesh>
|
||||
// ))}
|
||||
|
||||
// {points.map((pos, i) => {
|
||||
// if (i < points.length - 1) {
|
||||
// return (
|
||||
// <DraggableLineSegment
|
||||
// key={i}
|
||||
// index={i}
|
||||
// start={new THREE.Vector3(...points[i])}
|
||||
// end={new THREE.Vector3(...points[i + 1])}
|
||||
// updatePoints={updatePoints}
|
||||
// insertPoint={insertPoint}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
// return null;
|
||||
// })}
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
|
||||
///crcted
|
||||
// export default function PreDefinedPath() {
|
||||
// const { gl, raycaster } = useThree();
|
||||
// const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0));
|
||||
|
||||
// const [pointsGroups, setPointsGroups] = useState<
|
||||
// [number, number, number][][]
|
||||
// >([[]]);
|
||||
//
|
||||
// const [selected, setSelected] = useState<number[]>([]);
|
||||
// const downPosition = useRef<{ x: number; y: number } | null>(null);
|
||||
// const hasClicked = useRef(false);
|
||||
|
||||
// const handleMouseDown = useCallback((e: MouseEvent) => {
|
||||
// hasClicked.current = false;
|
||||
// downPosition.current = { x: e.clientX, y: e.clientY };
|
||||
// }, []);
|
||||
|
||||
// const handleClick = useCallback(
|
||||
// (e: MouseEvent) => {
|
||||
// // Right click → start new group
|
||||
// if (e.button === 2) {
|
||||
// setPointsGroups((prev) => [...prev, []]);
|
||||
// setSelected([]);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Left click only
|
||||
// if (e.button !== 0) return;
|
||||
// if (hasClicked.current) return;
|
||||
// hasClicked.current = true;
|
||||
|
||||
// if (
|
||||
// !downPosition.current ||
|
||||
// Math.abs(downPosition.current.x - e.clientX) > 2 ||
|
||||
// Math.abs(downPosition.current.y - e.clientY) > 2
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const intersection = new THREE.Vector3();
|
||||
// if (raycaster.ray.intersectPlane(plane.current, intersection)) {
|
||||
// const pointArray = intersection.toArray() as [number, number, number];
|
||||
// setPointsGroups((prev) => {
|
||||
// const newGroups = [...prev];
|
||||
// const currentGroup = [...newGroups[newGroups.length - 1]];
|
||||
|
||||
// const alreadyExists = currentGroup.some(
|
||||
// (p) =>
|
||||
// Math.abs(p[0] - pointArray[0]) < 0.01 &&
|
||||
// Math.abs(p[1] - pointArray[1]) < 0.01 &&
|
||||
// Math.abs(p[2] - pointArray[2]) < 0.01
|
||||
// );
|
||||
// if (alreadyExists) return prev;
|
||||
|
||||
// if (selected.length === 2) {
|
||||
// const [startIdx, endIdx] = selected;
|
||||
// const insertIndex = startIdx < endIdx ? startIdx + 1 : endIdx + 1;
|
||||
|
||||
// currentGroup.splice(insertIndex, 0, pointArray);
|
||||
// newGroups[newGroups.length - 1] = currentGroup;
|
||||
// return newGroups;
|
||||
// } else {
|
||||
// currentGroup.push(pointArray);
|
||||
// newGroups[newGroups.length - 1] = currentGroup;
|
||||
// return newGroups;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// [raycaster, selected]
|
||||
// );
|
||||
|
||||
// useEffect(() => {
|
||||
// const domElement = gl.domElement;
|
||||
// domElement.addEventListener("contextmenu", (e) => e.preventDefault()); // disable browser menu
|
||||
// domElement.addEventListener("mousedown", handleMouseDown);
|
||||
// domElement.addEventListener("mouseup", handleClick);
|
||||
// return () => {
|
||||
// domElement.removeEventListener("mousedown", handleMouseDown);
|
||||
// domElement.removeEventListener("mouseup", handleClick);
|
||||
// };
|
||||
// }, [handleClick, handleMouseDown]);
|
||||
|
||||
// const updatePoints = (
|
||||
// groupIndex: number,
|
||||
// i0: number,
|
||||
// p0: THREE.Vector3,
|
||||
// i1: number,
|
||||
// p1: THREE.Vector3
|
||||
// ) => {
|
||||
// setPointsGroups((prev) => {
|
||||
// const newGroups = [...prev];
|
||||
// const group = [...newGroups[groupIndex]];
|
||||
// group[i0] = p0.toArray() as [number, number, number];
|
||||
// group[i1] = p1.toArray() as [number, number, number];
|
||||
// newGroups[groupIndex] = group;
|
||||
// return newGroups;
|
||||
// });
|
||||
// };
|
||||
|
||||
// const insertPoint = (
|
||||
// groupIndex: number,
|
||||
// index: number,
|
||||
// point: THREE.Vector3
|
||||
// ) => {
|
||||
// setPointsGroups((prev) => {
|
||||
// const newGroups = [...prev];
|
||||
// const group = [...newGroups[groupIndex]];
|
||||
// group.splice(index, 0, point.toArray() as [number, number, number]);
|
||||
// newGroups[groupIndex] = group;
|
||||
// return newGroups;
|
||||
// });
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {pointsGroups.map((group, gIdx) => (
|
||||
// <React.Fragment key={gIdx}>
|
||||
// {group.map((pos, idx) => (
|
||||
// <mesh
|
||||
// key={idx}
|
||||
// position={pos}
|
||||
// onClick={(e) => {
|
||||
// e.stopPropagation();
|
||||
// setSelected((prev) =>
|
||||
// prev.length === 2 ? [idx] : [...prev, idx]
|
||||
// );
|
||||
// }}
|
||||
// >
|
||||
// <sphereGeometry args={[0.3, 16, 16]} />
|
||||
// <meshStandardMaterial
|
||||
// color={selected.includes(idx) ? "red" : "pink"}
|
||||
// />
|
||||
// </mesh>
|
||||
// ))}
|
||||
|
||||
// {group.map((pos, i) => {
|
||||
// if (i < group.length - 1) {
|
||||
// return (
|
||||
// <DraggableLineSegment
|
||||
// key={i}
|
||||
// index={i}
|
||||
// start={new THREE.Vector3(...group[i])}
|
||||
// end={new THREE.Vector3(...group[i + 1])}
|
||||
// updatePoints={(i0, p0, i1, p1) =>
|
||||
// updatePoints(gIdx, i0, p0, i1, p1)
|
||||
// }
|
||||
// insertPoint={(index, point) =>
|
||||
// insertPoint(gIdx, index, point)
|
||||
// }
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
// return null;
|
||||
// })}
|
||||
// </React.Fragment>
|
||||
// ))}
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
// const handleClick = useCallback(
|
||||
// (e: any) => {
|
||||
// if (e.ctrlKey) return;
|
||||
// if (e.button === 2) {
|
||||
// setPointsGroups((prev) => [...prev, []]);
|
||||
// setSelected([]);
|
||||
// return;
|
||||
// }
|
||||
// if (e.button !== 0) return;
|
||||
// if (hasClicked.current) return;
|
||||
// hasClicked.current = true;
|
||||
|
||||
// if (
|
||||
// !downPosition.current ||
|
||||
// Math.abs(downPosition.current.x - e.clientX) > 2 ||
|
||||
// Math.abs(downPosition.current.y - e.clientY) > 2
|
||||
// )
|
||||
// return;
|
||||
|
||||
// const intersection = new THREE.Vector3();
|
||||
// if (raycaster.ray.intersectPlane(plane.current, intersection)) {
|
||||
// const pointArray = intersection.toArray() as [number, number, number];
|
||||
|
||||
// setPointsGroups((prev) => {
|
||||
// const newGroups = [...prev];
|
||||
// const currentGroup = [...newGroups[newGroups.length - 1]];
|
||||
|
||||
// const alreadyExists = currentGroup.some(
|
||||
// (p) =>
|
||||
// Math.abs(p.position[0] - pointArray[0]) < 0.01 &&
|
||||
// Math.abs(p.position[1] - pointArray[1]) < 0.01 &&
|
||||
// Math.abs(p.position[2] - pointArray[2]) < 0.01
|
||||
// );
|
||||
// if (alreadyExists) return prev;
|
||||
|
||||
// const newPoint: PointData = {
|
||||
// pointId: crypto.randomUUID(),
|
||||
// position: pointArray,
|
||||
// isCurved: false,
|
||||
// handleA: null,
|
||||
// handleB: null,
|
||||
// };
|
||||
|
||||
// if (selected.length === 2) {
|
||||
// const [startIdx, endIdx] = selected;
|
||||
// const insertIndex = startIdx < endIdx ? startIdx + 1 : endIdx + 1;
|
||||
// currentGroup.splice(insertIndex, 0, newPoint);
|
||||
// } else {
|
||||
// currentGroup.push(newPoint);
|
||||
// }
|
||||
|
||||
// newGroups[newGroups.length - 1] = currentGroup;
|
||||
// return newGroups;
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// [raycaster, selected]
|
||||
// );
|
||||
|
||||
type PointData = {
|
||||
pointId: string;
|
||||
position: [number, number, number];
|
||||
isCurved: boolean;
|
||||
handleA: [number, number, number] | null;
|
||||
handleB: [number, number, number] | null;
|
||||
};
|
||||
export default function PreDefinedPath() {
|
||||
const { gl, raycaster } = useThree();
|
||||
const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0));
|
||||
|
||||
const [mainShapeOnly, setMainShapeOnly] = useState<PointData[][]>([]);
|
||||
console.log("mainShapeOnly: ", mainShapeOnly);
|
||||
const [pointsGroups, setPointsGroups] = useState<PointData[][]>([[]]);
|
||||
console.log("pointsGroups: ", pointsGroups);
|
||||
const [definedPath, setDefinedPath] = useState<PointData[][] | PointData[]>(
|
||||
[]
|
||||
);
|
||||
console.log("definedPath: ", definedPath);
|
||||
const [selected, setSelected] = useState<number[]>([]);
|
||||
|
||||
const downPosition = useRef<{ x: number; y: number } | null>(null);
|
||||
const hasClicked = useRef(false);
|
||||
|
||||
const handleMouseDown = useCallback((e: any) => {
|
||||
console.log("e.ctrlKey: ", e.ctrlKey);
|
||||
hasClicked.current = false;
|
||||
downPosition.current = { x: e.clientX, y: e.clientY };
|
||||
}, []);
|
||||
|
||||
const SNAP_DISTANCE = 0.5;
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e: any) => {
|
||||
console.log("e.ctrlKey: ", e.ctrlKey);
|
||||
if (e.ctrlKey) return;
|
||||
if (e.button === 2) {
|
||||
setPointsGroups((prev) => [...prev, []]);
|
||||
setSelected([]);
|
||||
return;
|
||||
}
|
||||
if (e.button !== 0) return;
|
||||
if (hasClicked.current) return;
|
||||
hasClicked.current = true;
|
||||
|
||||
if (
|
||||
!downPosition.current ||
|
||||
Math.abs(downPosition.current.x - e.clientX) > 2 ||
|
||||
Math.abs(downPosition.current.y - e.clientY) > 2
|
||||
)
|
||||
return;
|
||||
|
||||
const intersection = new THREE.Vector3();
|
||||
if (raycaster.ray.intersectPlane(plane.current, intersection)) {
|
||||
const pointArray = intersection.toArray() as [number, number, number];
|
||||
|
||||
setPointsGroups((prev) => {
|
||||
const newGroups = [...prev];
|
||||
const currentGroup = [...newGroups[newGroups.length - 1]];
|
||||
|
||||
// 1️⃣ Find nearest existing point
|
||||
let nearestPos: [number, number, number] | null = null;
|
||||
newGroups.forEach((group) => {
|
||||
group.forEach((p) => {
|
||||
const dist = Math.sqrt(
|
||||
Math.pow(p.position[0] - pointArray[0], 2) +
|
||||
Math.pow(p.position[1] - pointArray[1], 2) +
|
||||
Math.pow(p.position[2] - pointArray[2], 2)
|
||||
);
|
||||
if (dist <= SNAP_DISTANCE && !nearestPos) {
|
||||
nearestPos = p.position; // take only the position
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (nearestPos) {
|
||||
// 2️⃣ Reuse the position, but create NEW pointId
|
||||
const snapPoint: PointData = {
|
||||
pointId: crypto.randomUUID(),
|
||||
position: nearestPos,
|
||||
isCurved: false,
|
||||
handleA: null,
|
||||
handleB: null,
|
||||
};
|
||||
currentGroup.push(snapPoint);
|
||||
newGroups[newGroups.length - 1] = currentGroup;
|
||||
return newGroups;
|
||||
}
|
||||
|
||||
// 3️⃣ Otherwise, create brand new point
|
||||
const newPoint: PointData = {
|
||||
pointId: crypto.randomUUID(),
|
||||
position: pointArray,
|
||||
isCurved: false,
|
||||
handleA: null,
|
||||
handleB: null,
|
||||
};
|
||||
currentGroup.push(newPoint);
|
||||
newGroups[newGroups.length - 1] = currentGroup;
|
||||
return newGroups;
|
||||
});
|
||||
}
|
||||
},
|
||||
[raycaster]
|
||||
);
|
||||
|
||||
function findConnectedComponents(groups: PointData[][]) {
|
||||
const visited = new Set<string>();
|
||||
const components: PointData[][] = [];
|
||||
|
||||
const arePointsEqual = (p1: PointData, p2: PointData) =>
|
||||
Math.abs(p1.position[0] - p2.position[0]) < 0.001 &&
|
||||
Math.abs(p1.position[1] - p2.position[1]) < 0.001 &&
|
||||
Math.abs(p1.position[2] - p2.position[2]) < 0.001;
|
||||
|
||||
const dfs = (point: PointData, component: PointData[][]) => {
|
||||
if (visited.has(point.pointId)) return;
|
||||
visited.add(point.pointId);
|
||||
|
||||
for (const group of groups) {
|
||||
if (group.some((gp) => arePointsEqual(gp, point))) {
|
||||
if (!component.includes(group)) {
|
||||
component.push(group);
|
||||
for (const gp of group) dfs(gp, component);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const group of groups) {
|
||||
for (const point of group) {
|
||||
if (!visited.has(point.pointId)) {
|
||||
const newComponent: PointData[][] = [];
|
||||
dfs(point, newComponent);
|
||||
if (newComponent.length > 0) components.push(newComponent.flat());
|
||||
}
|
||||
}
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const newDefinedPath = pointsGroups.filter((g) => g.length > 0);
|
||||
setDefinedPath(newDefinedPath);
|
||||
|
||||
const connected = findConnectedComponents(newDefinedPath);
|
||||
if (connected.length > 0) {
|
||||
let mainShape = [...connected[0]];
|
||||
const isolatedPoints = connected
|
||||
.slice(1)
|
||||
.filter((arr) => arr.length === 1);
|
||||
const updatedMainShapeOnly = [mainShape, ...isolatedPoints];
|
||||
setMainShapeOnly(updatedMainShapeOnly);
|
||||
} else {
|
||||
setMainShapeOnly([]);
|
||||
}
|
||||
}, [pointsGroups]);
|
||||
|
||||
useEffect(() => {
|
||||
setDefinedPath(() => {
|
||||
if (pointsGroups.length === 1) {
|
||||
return [...pointsGroups[0]];
|
||||
} else {
|
||||
return pointsGroups.filter((group) => group.length > 0);
|
||||
}
|
||||
});
|
||||
}, [pointsGroups]);
|
||||
const [shortestPath, setShortestPath] = useState<number[]>([]);
|
||||
const [shortestDistance, setShortestDistance] = useState<number>(0);
|
||||
useEffect(() => {
|
||||
const domElement = gl.domElement;
|
||||
domElement.addEventListener("contextmenu", (e) => e.preventDefault());
|
||||
domElement.addEventListener("mousedown", handleMouseDown);
|
||||
domElement.addEventListener("mouseup", handleClick);
|
||||
return () => {
|
||||
domElement.removeEventListener("mousedown", handleMouseDown);
|
||||
domElement.removeEventListener("mouseup", handleClick);
|
||||
};
|
||||
}, [handleClick, handleMouseDown]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{pointsGroups.map((group, gIdx) => (
|
||||
<React.Fragment key={gIdx}>
|
||||
{group.map((point, idx) => (
|
||||
<PointHandle
|
||||
key={point.pointId}
|
||||
point={point}
|
||||
groupIndex={gIdx}
|
||||
pointIndex={idx}
|
||||
setPointsGroups={setPointsGroups}
|
||||
pointsGroups={pointsGroups} // <-- pass the full groups
|
||||
selected={selected}
|
||||
setSelected={setSelected} // <-- pass setter for multi-selection
|
||||
shortestPath={shortestPath}
|
||||
setShortestPath={setShortestPath}
|
||||
setShortestDistance={setShortestDistance}
|
||||
/>
|
||||
))}
|
||||
|
||||
{group.map((point, i) => {
|
||||
if (i < group.length - 1) {
|
||||
return (
|
||||
<LineSegment
|
||||
key={i}
|
||||
index={i}
|
||||
createdPoints={group} // pass the whole group here
|
||||
updatePoints={(i0, p0, i1, p1) => {
|
||||
setPointsGroups((prev) => {
|
||||
const newGroups = [...prev];
|
||||
const newGroup = [...newGroups[gIdx]];
|
||||
newGroup[i0] = {
|
||||
...newGroup[i0],
|
||||
position: p0.toArray() as [number, number, number],
|
||||
};
|
||||
newGroup[i1] = {
|
||||
...newGroup[i1],
|
||||
position: p1.toArray() as [number, number, number],
|
||||
};
|
||||
newGroups[gIdx] = newGroup;
|
||||
return newGroups;
|
||||
});
|
||||
}}
|
||||
// insertPoint={(index, pointVec) => {
|
||||
// setPointsGroups((prev) => {
|
||||
// const newGroups = [...prev];
|
||||
// const groupToSplit = newGroups[gIdx];
|
||||
|
||||
// // Create the new point
|
||||
// const newPoint = {
|
||||
// pointId: crypto.randomUUID(),
|
||||
// position: pointVec.toArray() as [
|
||||
// number,
|
||||
// number,
|
||||
// number
|
||||
// ],
|
||||
// isCurved: false,
|
||||
// handleA: null,
|
||||
// handleB: null,
|
||||
// };
|
||||
|
||||
// // First half: everything from start to clicked segment
|
||||
// const firstHalf = [
|
||||
// ...groupToSplit.slice(0, index),
|
||||
// newPoint,
|
||||
// ];
|
||||
|
||||
// // Second half: new point + everything after clicked segment
|
||||
// const secondHalf = [
|
||||
// newPoint,
|
||||
// ...groupToSplit.slice(index),
|
||||
// ];
|
||||
|
||||
// // Replace the original group with the first half
|
||||
// newGroups[gIdx] = firstHalf;
|
||||
|
||||
// // Insert the second half as a new group right after
|
||||
// newGroups.splice(gIdx + 1, 0, secondHalf);
|
||||
|
||||
// return newGroups;
|
||||
// });
|
||||
// }}
|
||||
insertPoint={(index: number, pointVec: THREE.Vector3) => {
|
||||
setPointsGroups((prev) => {
|
||||
const newGroups = [...prev];
|
||||
const group = [...newGroups[gIdx]];
|
||||
|
||||
// Create the new point
|
||||
const newPoint: PointData = {
|
||||
pointId: crypto.randomUUID(),
|
||||
position: pointVec.toArray() as [
|
||||
number,
|
||||
number,
|
||||
number
|
||||
],
|
||||
isCurved: false,
|
||||
handleA: null,
|
||||
handleB: null,
|
||||
};
|
||||
|
||||
// Find best place to insert based on index (insert between points)
|
||||
group.splice(index, 0, newPoint); // insert at index
|
||||
newGroups[gIdx] = group;
|
||||
return newGroups;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// const handleClick = useCallback(
|
||||
// (e: MouseEvent) => {
|
||||
// if (e.ctrlKey) return;
|
||||
// if (e.button === 2) {
|
||||
// setPointsGroups((prev) => [...prev, []]);
|
||||
// setSelected([]);
|
||||
// return;
|
||||
// }
|
||||
// if (e.button !== 0) return;
|
||||
|
||||
// // Check small movement
|
||||
// if (
|
||||
// !downPosition.current ||
|
||||
// Math.abs(downPosition.current.x - e.clientX) > 2 ||
|
||||
// Math.abs(downPosition.current.y - e.clientY) > 2
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const intersection = new THREE.Vector3();
|
||||
// if (raycaster.ray.intersectPlane(plane.current, intersection)) {
|
||||
// const pointArray = intersection.toArray() as [number, number, number];
|
||||
|
||||
// setPointsGroups((prev) => {
|
||||
// const newGroups = [...prev];
|
||||
// const currentGroup = [...newGroups[newGroups.length - 1]];
|
||||
|
||||
// // Search for existing point in ALL groups
|
||||
// let existingPoint: PointData | null = null;
|
||||
// for (const group of prev) {
|
||||
// for (const p of group) {
|
||||
// if (
|
||||
// Math.abs(p.position[0] - pointArray[0]) < 0.01 &&
|
||||
// Math.abs(p.position[1] - pointArray[1]) < 0.01 &&
|
||||
// Math.abs(p.position[2] - pointArray[2]) < 0.01
|
||||
// ) {
|
||||
// existingPoint = p;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (existingPoint) break;
|
||||
// }
|
||||
|
||||
// if (existingPoint) {
|
||||
// // Just connect to existing point without duplicating
|
||||
// if (
|
||||
// currentGroup.length === 0 ||
|
||||
// currentGroup[currentGroup.length - 1].pointId !==
|
||||
// existingPoint.pointId
|
||||
// ) {
|
||||
// currentGroup.push(existingPoint);
|
||||
// }
|
||||
// } else {
|
||||
// // Create new point
|
||||
// const newPoint: PointData = {
|
||||
// pointId: crypto.randomUUID(),
|
||||
// position: pointArray,
|
||||
// isCurved: false,
|
||||
// handleA: null,
|
||||
// handleB: null,
|
||||
// };
|
||||
// currentGroup.push(newPoint);
|
||||
// }
|
||||
|
||||
// newGroups[newGroups.length - 1] = currentGroup;
|
||||
// return newGroups;
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// [raycaster]
|
||||
// );
|
||||
@@ -4,6 +4,7 @@ import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||
import VehicleInstances from "./instances/vehicleInstances";
|
||||
import VehicleUI from "../spatialUI/vehicle/vehicleUI";
|
||||
import { useSceneContext } from "../../scene/sceneContext";
|
||||
import PreDefinedPath from "./preDefinedPath/preDefinedPath";
|
||||
|
||||
function Vehicles() {
|
||||
const { vehicleStore } = useSceneContext();
|
||||
@@ -14,24 +15,22 @@ function Vehicles() {
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedEventSphere) {
|
||||
const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid);
|
||||
const selectedVehicle = getVehicleById(
|
||||
selectedEventSphere.userData.modelUuid
|
||||
);
|
||||
if (selectedVehicle) {
|
||||
setIsVehicleSelected(true);
|
||||
} else {
|
||||
setIsVehicleSelected(false);
|
||||
}
|
||||
}
|
||||
}, [getVehicleById, selectedEventSphere])
|
||||
}, [getVehicleById, selectedEventSphere]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<VehicleInstances />
|
||||
|
||||
{isVehicleSelected && selectedEventSphere && !isPlaying &&
|
||||
<VehicleUI />
|
||||
}
|
||||
|
||||
<PreDefinedPath />
|
||||
{/* <VehicleInstances /> */}
|
||||
{isVehicleSelected && selectedEventSphere && !isPlaying && <VehicleUI />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user