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 * as THREE from "three";
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useThree } from '@react-three/fiber';
|
import { useThree } from "@react-three/fiber";
|
||||||
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
import {
|
||||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
useActiveLayer,
|
||||||
import { useParams } from 'react-router-dom';
|
useSocketStore,
|
||||||
import { useVersionContext } from '../../version/versionContext';
|
useToggleView,
|
||||||
import { useSceneContext } from '../../../scene/sceneContext';
|
useToolMode,
|
||||||
import ReferenceAisle from './referenceAisle';
|
} from "../../../../store/builder/store";
|
||||||
import ReferencePoint from '../../point/reference/referencePoint';
|
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||||
import { getUserData } from '../../../../functions/getUserData';
|
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';
|
// import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi';
|
||||||
|
|
||||||
function AisleCreator() {
|
function AisleCreator() {
|
||||||
const { scene, camera, raycaster, gl, pointer } = useThree();
|
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 { toggleView } = useToggleView();
|
||||||
const { toolMode } = useToolMode();
|
const { toolMode } = useToolMode();
|
||||||
const { activeLayer } = useActiveLayer();
|
const { activeLayer } = useActiveLayer();
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { aisleStore, undoRedo2DStore } = useSceneContext();
|
const { aisleStore, undoRedo2DStore } = useSceneContext();
|
||||||
const { addAisle, getAislePointById } = aisleStore();
|
const { aisles, addAisle, getAislePointById } = aisleStore();
|
||||||
|
console.log("aisles: ", aisles);
|
||||||
const { push2D } = undoRedo2DStore();
|
const { push2D } = undoRedo2DStore();
|
||||||
const drag = useRef(false);
|
const drag = useRef(false);
|
||||||
const isLeftMouseDown = useRef(false);
|
const isLeftMouseDown = useRef(false);
|
||||||
@@ -31,7 +40,20 @@ function AisleCreator() {
|
|||||||
|
|
||||||
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
const [tempPoints, setTempPoints] = useState<Point[]>([]);
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
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(() => {
|
useEffect(() => {
|
||||||
const canvasElement = gl.domElement;
|
const canvasElement = gl.domElement;
|
||||||
@@ -63,13 +85,15 @@ function AisleCreator() {
|
|||||||
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||||
if (!position) return;
|
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 = {
|
const newPoint: Point = {
|
||||||
pointUuid: THREE.MathUtils.generateUUID(),
|
pointUuid: THREE.MathUtils.generateUUID(),
|
||||||
pointType: 'Aisle',
|
pointType: "Aisle",
|
||||||
position: [position.x, position.y, position.z],
|
position: [position.x, position.y, position.z],
|
||||||
layer: activeLayer
|
layer: activeLayer,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (snappedPosition && snappedPoint) {
|
if (snappedPosition && snappedPoint) {
|
||||||
@@ -78,7 +102,9 @@ function AisleCreator() {
|
|||||||
newPoint.layer = snappedPoint.layer;
|
newPoint.layer = snappedPoint.layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) { return }
|
if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (snappedPosition && !snappedPoint) {
|
if (snappedPosition && !snappedPoint) {
|
||||||
newPoint.position = snappedPosition;
|
newPoint.position = snappedPosition;
|
||||||
@@ -93,8 +119,7 @@ function AisleCreator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aisleType === 'solid-aisle') {
|
if (aisleType === "solid-aisle") {
|
||||||
|
|
||||||
if (tempPoints.length === 0) {
|
if (tempPoints.length === 0) {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -103,50 +128,48 @@ function AisleCreator() {
|
|||||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||||
points: [tempPoints[0], newPoint],
|
points: [tempPoints[0], newPoint],
|
||||||
type: {
|
type: {
|
||||||
aisleType: 'solid-aisle',
|
aisleType: "solid-aisle",
|
||||||
aisleColor: aisleColor,
|
aisleColor: aisleColor,
|
||||||
aisleWidth: aisleWidth
|
aisleWidth: aisleWidth,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
addAisle(aisle);
|
addAisle(aisle);
|
||||||
|
|
||||||
push2D({
|
push2D({
|
||||||
type: 'Draw',
|
type: "Draw",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
actionType: 'Line-Create',
|
actionType: "Line-Create",
|
||||||
point: {
|
point: {
|
||||||
type: 'Aisle',
|
type: "Aisle",
|
||||||
lineData: aisle,
|
lineData: aisle,
|
||||||
timeStamp: new Date().toISOString(),
|
timeStamp: new Date().toISOString(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||||
|
|
||||||
// SOCKET
|
// SOCKET
|
||||||
|
|
||||||
socket.emit('v1:model-aisle:add', {
|
socket.emit("v1:model-aisle:add", {
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
versionId: selectedVersion?.versionId || '',
|
versionId: selectedVersion?.versionId || "",
|
||||||
userId: userId,
|
userId: userId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
aisleUuid: aisle.aisleUuid,
|
aisleUuid: aisle.aisleUuid,
|
||||||
points: aisle.points,
|
points: aisle.points,
|
||||||
type: aisle.type
|
type: aisle.type,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
}
|
}
|
||||||
} else if (aisleType === 'dashed-aisle') {
|
} else if (aisleType === "dashed-aisle") {
|
||||||
|
|
||||||
if (tempPoints.length === 0) {
|
if (tempPoints.length === 0) {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -155,52 +178,50 @@ function AisleCreator() {
|
|||||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||||
points: [tempPoints[0], newPoint],
|
points: [tempPoints[0], newPoint],
|
||||||
type: {
|
type: {
|
||||||
aisleType: 'dashed-aisle',
|
aisleType: "dashed-aisle",
|
||||||
aisleColor: aisleColor,
|
aisleColor: aisleColor,
|
||||||
aisleWidth: aisleWidth,
|
aisleWidth: aisleWidth,
|
||||||
dashLength: dashLength,
|
dashLength: dashLength,
|
||||||
gapLength: gapLength
|
gapLength: gapLength,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
addAisle(aisle);
|
addAisle(aisle);
|
||||||
|
|
||||||
push2D({
|
push2D({
|
||||||
type: 'Draw',
|
type: "Draw",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
actionType: 'Line-Create',
|
actionType: "Line-Create",
|
||||||
point: {
|
point: {
|
||||||
type: 'Aisle',
|
type: "Aisle",
|
||||||
lineData: aisle,
|
lineData: aisle,
|
||||||
timeStamp: new Date().toISOString(),
|
timeStamp: new Date().toISOString(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||||
|
|
||||||
// SOCKET
|
// SOCKET
|
||||||
|
|
||||||
socket.emit('v1:model-aisle:add', {
|
socket.emit("v1:model-aisle:add", {
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
versionId: selectedVersion?.versionId || '',
|
versionId: selectedVersion?.versionId || "",
|
||||||
userId: userId,
|
userId: userId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
aisleUuid: aisle.aisleUuid,
|
aisleUuid: aisle.aisleUuid,
|
||||||
points: aisle.points,
|
points: aisle.points,
|
||||||
type: aisle.type
|
type: aisle.type,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
}
|
}
|
||||||
} else if (aisleType === 'dotted-aisle') {
|
} else if (aisleType === "dotted-aisle") {
|
||||||
|
|
||||||
if (tempPoints.length === 0) {
|
if (tempPoints.length === 0) {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -209,51 +230,49 @@ function AisleCreator() {
|
|||||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||||
points: [tempPoints[0], newPoint],
|
points: [tempPoints[0], newPoint],
|
||||||
type: {
|
type: {
|
||||||
aisleType: 'dotted-aisle',
|
aisleType: "dotted-aisle",
|
||||||
aisleColor: aisleColor,
|
aisleColor: aisleColor,
|
||||||
dotRadius: dotRadius,
|
dotRadius: dotRadius,
|
||||||
gapLength: gapLength
|
gapLength: gapLength,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
addAisle(aisle);
|
addAisle(aisle);
|
||||||
|
|
||||||
push2D({
|
push2D({
|
||||||
type: 'Draw',
|
type: "Draw",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
actionType: 'Line-Create',
|
actionType: "Line-Create",
|
||||||
point: {
|
point: {
|
||||||
type: 'Aisle',
|
type: "Aisle",
|
||||||
lineData: aisle,
|
lineData: aisle,
|
||||||
timeStamp: new Date().toISOString(),
|
timeStamp: new Date().toISOString(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||||
|
|
||||||
// SOCKET
|
// SOCKET
|
||||||
|
|
||||||
socket.emit('v1:model-aisle:add', {
|
socket.emit("v1:model-aisle:add", {
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
versionId: selectedVersion?.versionId || '',
|
versionId: selectedVersion?.versionId || "",
|
||||||
userId: userId,
|
userId: userId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
aisleUuid: aisle.aisleUuid,
|
aisleUuid: aisle.aisleUuid,
|
||||||
points: aisle.points,
|
points: aisle.points,
|
||||||
type: aisle.type
|
type: aisle.type,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
}
|
}
|
||||||
} else if (aisleType === 'arrow-aisle') {
|
} else if (aisleType === "arrow-aisle") {
|
||||||
|
|
||||||
if (tempPoints.length === 0) {
|
if (tempPoints.length === 0) {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -262,50 +281,48 @@ function AisleCreator() {
|
|||||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||||
points: [tempPoints[0], newPoint],
|
points: [tempPoints[0], newPoint],
|
||||||
type: {
|
type: {
|
||||||
aisleType: 'arrow-aisle',
|
aisleType: "arrow-aisle",
|
||||||
aisleColor: aisleColor,
|
aisleColor: aisleColor,
|
||||||
aisleWidth: aisleWidth
|
aisleWidth: aisleWidth,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
addAisle(aisle);
|
addAisle(aisle);
|
||||||
|
|
||||||
push2D({
|
push2D({
|
||||||
type: 'Draw',
|
type: "Draw",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
actionType: 'Line-Create',
|
actionType: "Line-Create",
|
||||||
point: {
|
point: {
|
||||||
type: 'Aisle',
|
type: "Aisle",
|
||||||
lineData: aisle,
|
lineData: aisle,
|
||||||
timeStamp: new Date().toISOString(),
|
timeStamp: new Date().toISOString(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||||
|
|
||||||
// SOCKET
|
// SOCKET
|
||||||
|
|
||||||
socket.emit('v1:model-aisle:add', {
|
socket.emit("v1:model-aisle:add", {
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
versionId: selectedVersion?.versionId || '',
|
versionId: selectedVersion?.versionId || "",
|
||||||
userId: userId,
|
userId: userId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
aisleUuid: aisle.aisleUuid,
|
aisleUuid: aisle.aisleUuid,
|
||||||
points: aisle.points,
|
points: aisle.points,
|
||||||
type: aisle.type
|
type: aisle.type,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
}
|
}
|
||||||
} else if (aisleType === 'arrows-aisle') {
|
} else if (aisleType === "arrows-aisle") {
|
||||||
|
|
||||||
if (tempPoints.length === 0) {
|
if (tempPoints.length === 0) {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -314,52 +331,50 @@ function AisleCreator() {
|
|||||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||||
points: [tempPoints[0], newPoint],
|
points: [tempPoints[0], newPoint],
|
||||||
type: {
|
type: {
|
||||||
aisleType: 'arrows-aisle',
|
aisleType: "arrows-aisle",
|
||||||
aisleColor: aisleColor,
|
aisleColor: aisleColor,
|
||||||
aisleWidth: aisleWidth,
|
aisleWidth: aisleWidth,
|
||||||
aisleLength: aisleLength,
|
aisleLength: aisleLength,
|
||||||
gapLength: gapLength
|
gapLength: gapLength,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
addAisle(aisle);
|
addAisle(aisle);
|
||||||
|
|
||||||
push2D({
|
push2D({
|
||||||
type: 'Draw',
|
type: "Draw",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
actionType: 'Line-Create',
|
actionType: "Line-Create",
|
||||||
point: {
|
point: {
|
||||||
type: 'Aisle',
|
type: "Aisle",
|
||||||
lineData: aisle,
|
lineData: aisle,
|
||||||
timeStamp: new Date().toISOString(),
|
timeStamp: new Date().toISOString(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||||
|
|
||||||
// SOCKET
|
// SOCKET
|
||||||
|
|
||||||
socket.emit('v1:model-aisle:add', {
|
socket.emit("v1:model-aisle:add", {
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
versionId: selectedVersion?.versionId || '',
|
versionId: selectedVersion?.versionId || "",
|
||||||
userId: userId,
|
userId: userId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
aisleUuid: aisle.aisleUuid,
|
aisleUuid: aisle.aisleUuid,
|
||||||
points: aisle.points,
|
points: aisle.points,
|
||||||
type: aisle.type
|
type: aisle.type,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
}
|
}
|
||||||
} else if (aisleType === 'arc-aisle') {
|
} else if (aisleType === "arc-aisle") {
|
||||||
|
|
||||||
if (tempPoints.length === 0) {
|
if (tempPoints.length === 0) {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -368,51 +383,49 @@ function AisleCreator() {
|
|||||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||||
points: [tempPoints[0], newPoint],
|
points: [tempPoints[0], newPoint],
|
||||||
type: {
|
type: {
|
||||||
aisleType: 'arc-aisle',
|
aisleType: "arc-aisle",
|
||||||
aisleColor: aisleColor,
|
aisleColor: aisleColor,
|
||||||
aisleWidth: aisleWidth,
|
aisleWidth: aisleWidth,
|
||||||
isFlipped: isFlipped
|
isFlipped: isFlipped,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
addAisle(aisle);
|
addAisle(aisle);
|
||||||
|
|
||||||
push2D({
|
push2D({
|
||||||
type: 'Draw',
|
type: "Draw",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
actionType: 'Line-Create',
|
actionType: "Line-Create",
|
||||||
point: {
|
point: {
|
||||||
type: 'Aisle',
|
type: "Aisle",
|
||||||
lineData: aisle,
|
lineData: aisle,
|
||||||
timeStamp: new Date().toISOString(),
|
timeStamp: new Date().toISOString(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||||
|
|
||||||
// SOCKET
|
// SOCKET
|
||||||
|
|
||||||
socket.emit('v1:model-aisle:add', {
|
socket.emit("v1:model-aisle:add", {
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
versionId: selectedVersion?.versionId || '',
|
versionId: selectedVersion?.versionId || "",
|
||||||
userId: userId,
|
userId: userId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
aisleUuid: aisle.aisleUuid,
|
aisleUuid: aisle.aisleUuid,
|
||||||
points: aisle.points,
|
points: aisle.points,
|
||||||
type: aisle.type
|
type: aisle.type,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
}
|
}
|
||||||
} else if (aisleType === 'circle-aisle') {
|
} else if (aisleType === "circle-aisle") {
|
||||||
|
|
||||||
if (tempPoints.length === 0) {
|
if (tempPoints.length === 0) {
|
||||||
setTempPoints([newPoint]);
|
setTempPoints([newPoint]);
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -421,98 +434,95 @@ function AisleCreator() {
|
|||||||
aisleUuid: THREE.MathUtils.generateUUID(),
|
aisleUuid: THREE.MathUtils.generateUUID(),
|
||||||
points: [tempPoints[0], newPoint],
|
points: [tempPoints[0], newPoint],
|
||||||
type: {
|
type: {
|
||||||
aisleType: 'circle-aisle',
|
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',
|
|
||||||
aisleColor: aisleColor,
|
aisleColor: aisleColor,
|
||||||
aisleWidth: aisleWidth,
|
aisleWidth: aisleWidth,
|
||||||
isFlipped: isFlipped
|
},
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
addAisle(aisle);
|
addAisle(aisle);
|
||||||
|
|
||||||
push2D({
|
push2D({
|
||||||
type: 'Draw',
|
type: "Draw",
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
actionType: 'Line-Create',
|
actionType: "Line-Create",
|
||||||
point: {
|
point: {
|
||||||
type: 'Aisle',
|
type: "Aisle",
|
||||||
lineData: aisle,
|
lineData: aisle,
|
||||||
timeStamp: new Date().toISOString(),
|
timeStamp: new Date().toISOString(),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
// upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '')
|
||||||
|
|
||||||
// SOCKET
|
// SOCKET
|
||||||
|
|
||||||
socket.emit('v1:model-aisle:add', {
|
socket.emit("v1:model-aisle:add", {
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
versionId: selectedVersion?.versionId || '',
|
versionId: selectedVersion?.versionId || "",
|
||||||
userId: userId,
|
userId: userId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
aisleUuid: aisle.aisleUuid,
|
aisleUuid: aisle.aisleUuid,
|
||||||
points: aisle.points,
|
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]);
|
setTempPoints([newPoint]);
|
||||||
}
|
}
|
||||||
@@ -556,23 +566,46 @@ function AisleCreator() {
|
|||||||
canvasElement.removeEventListener("click", onMouseClick);
|
canvasElement.removeEventListener("click", onMouseClick);
|
||||||
canvasElement.removeEventListener("contextmenu", onContext);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{toggleView &&
|
{toggleView && (
|
||||||
<>
|
<>
|
||||||
<group name='Aisle-Reference-Points-Group'>
|
<group name="Aisle-Reference-Points-Group">
|
||||||
{tempPoints.map((point) => (
|
{tempPoints.map((point) => (
|
||||||
<ReferencePoint key={point.pointUuid} point={point} />
|
<ReferencePoint key={point.pointUuid} point={point} />
|
||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
{tempPoints.length > 0 &&
|
{tempPoints.length > 0 && <ReferenceAisle tempPoints={tempPoints} />}
|
||||||
<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 VehicleInstances from "./instances/vehicleInstances";
|
||||||
import VehicleUI from "../spatialUI/vehicle/vehicleUI";
|
import VehicleUI from "../spatialUI/vehicle/vehicleUI";
|
||||||
import { useSceneContext } from "../../scene/sceneContext";
|
import { useSceneContext } from "../../scene/sceneContext";
|
||||||
|
import PreDefinedPath from "./preDefinedPath/preDefinedPath";
|
||||||
|
|
||||||
function Vehicles() {
|
function Vehicles() {
|
||||||
const { vehicleStore } = useSceneContext();
|
const { vehicleStore } = useSceneContext();
|
||||||
@@ -14,24 +15,22 @@ function Vehicles() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedEventSphere) {
|
if (selectedEventSphere) {
|
||||||
const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid);
|
const selectedVehicle = getVehicleById(
|
||||||
|
selectedEventSphere.userData.modelUuid
|
||||||
|
);
|
||||||
if (selectedVehicle) {
|
if (selectedVehicle) {
|
||||||
setIsVehicleSelected(true);
|
setIsVehicleSelected(true);
|
||||||
} else {
|
} else {
|
||||||
setIsVehicleSelected(false);
|
setIsVehicleSelected(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [getVehicleById, selectedEventSphere])
|
}, [getVehicleById, selectedEventSphere]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PreDefinedPath />
|
||||||
<VehicleInstances />
|
{/* <VehicleInstances /> */}
|
||||||
|
{isVehicleSelected && selectedEventSphere && !isPlaying && <VehicleUI />}
|
||||||
{isVehicleSelected && selectedEventSphere && !isPlaying &&
|
|
||||||
<VehicleUI />
|
|
||||||
}
|
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user