From 6d557e8fde03149a8b1e8f597d8e3e75b43cc1a4 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Thu, 28 Aug 2025 14:56:38 +0530 Subject: [PATCH] feat: enhance conveyor collider components with direction change functionality and refactor state management --- .../scene/physics/conveyor/ribbonCollider.tsx | 6 + .../conveyor/types/curvedConveyorCollider.tsx | 90 +++---- .../conveyor/types/normalConveyorCollider.tsx | 147 ++++++----- .../conveyor/types/ySplitConveyorCollider.tsx | 233 +++++++----------- .../scene/physics/ui/ColliderProperties.tsx | 1 - app/src/modules/scene/scene.tsx | 6 +- 6 files changed, 218 insertions(+), 265 deletions(-) diff --git a/app/src/modules/scene/physics/conveyor/ribbonCollider.tsx b/app/src/modules/scene/physics/conveyor/ribbonCollider.tsx index a4f5e35..5702a20 100644 --- a/app/src/modules/scene/physics/conveyor/ribbonCollider.tsx +++ b/app/src/modules/scene/physics/conveyor/ribbonCollider.tsx @@ -15,10 +15,12 @@ function RibbonCollider({ }) { const [forward, setForward] = useState(false); + // console.log('ribbonData: ', ribbonData); return ( <> {ribbonData.type === 'normal' && ( )} {ribbonData.type === 'y-Split' && ( )} diff --git a/app/src/modules/scene/physics/conveyor/types/curvedConveyorCollider.tsx b/app/src/modules/scene/physics/conveyor/types/curvedConveyorCollider.tsx index d68668e..fa67e0f 100644 --- a/app/src/modules/scene/physics/conveyor/types/curvedConveyorCollider.tsx +++ b/app/src/modules/scene/physics/conveyor/types/curvedConveyorCollider.tsx @@ -3,23 +3,25 @@ import { CollisionPayload, RigidBody } from '@react-three/rapier'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useFrame } from '@react-three/fiber'; -function CurvedConveyorCollider({ - points, - boundingBox, - asset, - forward: initialForward, +function CurvedConveyorCollider({ + points, + boundingBox, + asset, + forward, isPaused, + onDirectionChange }: { points: [number, number, number][][]; boundingBox: THREE.Box3 | null; asset: Asset; forward: boolean; isPaused: boolean; + onDirectionChange?: (newDirection: boolean) => void; }) { const conveyorRef = useRef(null); const [objectsOnConveyor, setObjectsOnConveyor] = useState>(new Set()); const conveyorDirection = useRef(new THREE.Vector3()); - const [forward, setForward] = useState(initialForward); + // const [forward, setForward] = useState(initialForward); const [showDirection, setShowDirection] = useState(false); const [hoverState, setHoverState] = useState(false); const conveyorSpeed = 2; @@ -32,9 +34,11 @@ function CurvedConveyorCollider({ const handleClick = (e: MouseEvent) => { if (e.button === 2 && hoverState) { // Right click and hovering over conveyor const now = Date.now(); - if (now - lastClickTime.current < 300) { - - setForward(prev => !prev); + if (now - lastClickTime.current < 300) { + if (onDirectionChange) { + console.log('forwardcurve: ', forward); + onDirectionChange(!forward); + } } lastClickTime.current = now; } @@ -44,7 +48,7 @@ function CurvedConveyorCollider({ return () => window.removeEventListener('mousedown', handleClick); }, [forward, hoverState]); - + const bezierPoints = useMemo(() => { const segments = 20; const allPoints: THREE.Vector3[] = []; @@ -72,7 +76,7 @@ function CurvedConveyorCollider({ return allPoints; }, [points, forward]); - + const geometries = useMemo(() => { const width = 1; const segments = 20; @@ -131,15 +135,15 @@ function CurvedConveyorCollider({ return geos; }, [points, asset.position, asset.rotation]); -useEffect(() => { - if (bezierPoints.length >= 2) { - const start = bezierPoints[0]; - const end = bezierPoints[bezierPoints.length - 1]; - conveyorDirection.current.copy(end).sub(start).normalize(); - const rotation = new THREE.Euler().fromArray(asset.rotation || [0, 0, 0]); - conveyorDirection.current.applyEuler(rotation); - } -}, [bezierPoints, forward, asset.rotation]); + useEffect(() => { + if (bezierPoints.length >= 2) { + const start = bezierPoints[0]; + const end = bezierPoints[bezierPoints.length - 1]; + conveyorDirection.current.copy(end).sub(start).normalize(); + const rotation = new THREE.Euler().fromArray(asset.rotation || [0, 0, 0]); + conveyorDirection.current.applyEuler(rotation); + } + }, [bezierPoints, forward, asset.rotation]); const handleMaterialEnter = (e: CollisionPayload) => { @@ -165,7 +169,7 @@ useEffect(() => { useFrame(({ clock }) => { if (isPaused) return; - + // Physics simulation const assetPos = new THREE.Vector3(...(asset.position || [0, 0, 0])); const assetRot = new THREE.Euler(...(asset.rotation || [0, 0, 0])); @@ -210,7 +214,7 @@ useEffect(() => { // Pulse animation const pulseScale = 0.9 + 0.1 * Math.sin(elapsedTime * 5 + index * 0.5); arrowGroup.scale.setScalar(pulseScale); - + // Flow animation (color intensity) const intensity = 0.7 + 0.3 * Math.sin(elapsedTime * 3 + index * 0.3); arrowGroup.children.forEach(child => { @@ -231,12 +235,12 @@ useEffect(() => { // Create curved direction indicators const directionArrows = useMemo(() => { if (!showDirection) return null; - + const arrows: THREE.Group[] = []; const arrowHeight = 0.2; const arrowRadius = 0.05; const segments = 8; // Fewer arrows for curved conveyors - + points.forEach(segment => { let vectorPoints = segment.map(p => new THREE.Vector3(...p)); if (!forward) vectorPoints.reverse(); @@ -263,25 +267,25 @@ useEffect(() => { // Create arrow group const arrowGroup = new THREE.Group(); - + // Arrow shaft (cylinder) const shaftLength = arrowHeight * 0.7; const shaftGeometry = new THREE.CylinderGeometry(arrowRadius * 0.3, arrowRadius * 0.3, shaftLength, 8); - const shaftMaterial = new THREE.MeshBasicMaterial({ - color: forward ? 0x00ff00 : 0xff0000 + const shaftMaterial = new THREE.MeshBasicMaterial({ + color: forward ? 0x00ff00 : 0xff0000 }); const shaft = new THREE.Mesh(shaftGeometry, shaftMaterial); shaft.position.y = shaftLength / 2; shaft.rotation.x = Math.PI / 2; - + // Arrow head (cone) const headGeometry = new THREE.ConeGeometry(arrowRadius, arrowHeight * 0.3, 8); - const headMaterial = new THREE.MeshBasicMaterial({ - color: forward ? 0x00ff00 : 0xff0000 + const headMaterial = new THREE.MeshBasicMaterial({ + color: forward ? 0x00ff00 : 0xff0000 }); const head = new THREE.Mesh(headGeometry, headMaterial); head.position.y = shaftLength; - + // Position and orient the entire arrow arrowGroup.add(shaft); arrowGroup.add(head); @@ -291,12 +295,12 @@ useEffect(() => { new THREE.Vector3(0, 1, 0), new THREE.Vector3(tangent.x, 0.1, tangent.z) ); - + arrows.push(arrowGroup); } } }); - + arrowRefs.current = arrows; return arrows; }, [points, showDirection, forward]); @@ -326,22 +330,22 @@ useEffect(() => { > {geometries.map((geometry, index) => ( - ))} )} - + {/* Direction indicators */} {showDirection && directionArrows?.map((arrow, i) => ( ))} - + {/* Hover highlight */} {hoverState && ( @@ -351,9 +355,9 @@ useEffect(() => { geometry={geometry} position={[0, 0.002, 0]} // Slightly above conveyor > - diff --git a/app/src/modules/scene/physics/conveyor/types/normalConveyorCollider.tsx b/app/src/modules/scene/physics/conveyor/types/normalConveyorCollider.tsx index 227736a..7766b83 100644 --- a/app/src/modules/scene/physics/conveyor/types/normalConveyorCollider.tsx +++ b/app/src/modules/scene/physics/conveyor/types/normalConveyorCollider.tsx @@ -3,36 +3,32 @@ import { CollisionPayload, RigidBody } from '@react-three/rapier'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useFrame } from '@react-three/fiber'; -function NormalConveyorCollider({ - points, - boundingBox, - asset, - forward, - isPaused, - onDirectionChange -}: { +interface NormalConveyorColliderProps { points: [number, number, number][][]; boundingBox: THREE.Box3 | null; asset: Asset; forward: boolean; isPaused: boolean; onDirectionChange?: (newDirection: boolean) => void; -}) { +} + +function NormalConveyorCollider({ points, boundingBox, asset, forward, isPaused, onDirectionChange }: NormalConveyorColliderProps) { const conveyorRefs = useRef<(any)[]>([]); - const [objectsOnConveyor, setObjectsOnConveyor] = useState>(new Set()); + const [objectsOnGeometry, setObjectsOnGeometry] = useState>>(new Map()); const conveyorDirection = useRef(new THREE.Vector3()); const [showDirection, setShowDirection] = useState(false); const conveyorSpeed = 2; const lastClickTime = useRef(0); const [hoverState, setHoverState] = useState(false); + const[localForward,setLocalForward]=useState() - // Toggle direction on double right click useEffect(() => { const handleClick = (e: MouseEvent) => { - if (e.button === 2) { // Right click + if (e.button === 2) { const now = Date.now(); - if (now - lastClickTime.current < 300) { // Double click within 300ms + if (now - lastClickTime.current < 300) { if (onDirectionChange) { + console.log('forwardnormal: ', forward); onDirectionChange(!forward); } } @@ -53,22 +49,25 @@ function NormalConveyorCollider({ conveyorDirection.current.applyEuler(rotation); }, [boundingBox, asset.rotation, forward]); - const handleMaterialEnter = (e: CollisionPayload) => { + const handleMaterialEnter = (e: CollisionPayload, index: number) => { if (e.other.rigidBody) { - setObjectsOnConveyor(prev => { - const newSet = new Set(prev); - newSet.add(e.other.rigidBody); - return newSet; + setObjectsOnGeometry(prev => { + const newMap = new Map(prev); + if (!newMap.has(index)) newMap.set(index, new Set()); + newMap.get(index)!.add(e.other.rigidBody); + return newMap; }); } }; - const handleMaterialExit = (e: CollisionPayload) => { + const handleMaterialExit = (e: CollisionPayload, index: number) => { if (e.other.rigidBody) { - setObjectsOnConveyor(prev => { - const newSet = new Set(prev); - newSet.delete(e.other.rigidBody); - return newSet; + setObjectsOnGeometry(prev => { + const newMap = new Map(prev); + if (newMap.has(index)) { + newMap.get(index)!.delete(e.other.rigidBody); + } + return newMap; }); } }; @@ -81,51 +80,50 @@ function NormalConveyorCollider({ const assetQuat = new THREE.Quaternion().setFromEuler(assetRot); const inverseQuat = assetQuat.clone().invert(); - const allCurvePoints: THREE.Vector3[] = []; - const segmentCurves: THREE.Vector3[][] = []; + points.forEach((segment, index) => { + if (segment.length < 2) return; - points.forEach(segment => { const curve = new THREE.CatmullRomCurve3(segment.map(p => new THREE.Vector3(...p))); const curvePoints = curve.getPoints((segment.length - 1) * 30); - segmentCurves.push(curvePoints); - allCurvePoints.push(...curvePoints); - }); + if (!forward) curvePoints.reverse(); - if (!forward) allCurvePoints.reverse(); + const bodies = objectsOnGeometry.get(index); + if (!bodies) return; - objectsOnConveyor.forEach(rigidBody => { - const worldPos = new THREE.Vector3().copy(rigidBody.translation()); - const localPos = worldPos.clone().sub(assetPos).applyQuaternion(inverseQuat); + bodies.forEach(rigidBody => { + const worldPos = new THREE.Vector3().copy(rigidBody.translation()); + const localPos = worldPos.clone().sub(assetPos).applyQuaternion(inverseQuat); - let closestIndex = 0; - let minDist = Infinity; - for (let i = 0; i < allCurvePoints.length; i++) { - const dist = allCurvePoints[i].distanceToSquared(localPos); - if (dist < minDist) { - minDist = dist; - closestIndex = i; + let closestIndex = 0; + let minDist = Infinity; + for (let i = 0; i < curvePoints.length; i++) { + const dist = curvePoints[i].distanceToSquared(localPos); + if (dist < minDist) { + minDist = dist; + closestIndex = i; + } } - } - const point = allCurvePoints[closestIndex]; - const prev = allCurvePoints[closestIndex - 1] || point; - const next = allCurvePoints[closestIndex + 1] || point; - const tangent = new THREE.Vector3().subVectors(next, prev).normalize(); - const side = new THREE.Vector3().crossVectors(tangent, new THREE.Vector3(0, 1, 0)).normalize(); - const relative = new THREE.Vector3().subVectors(localPos, point); - const sideOffset = relative.dot(side); - const centeringForce = side.clone().multiplyScalar(-sideOffset * 10); - const forwardForce = tangent.clone().multiplyScalar(conveyorSpeed); - const totalForce = forwardForce.add(centeringForce).applyQuaternion(assetQuat); + const point = curvePoints[closestIndex]; + const prev = curvePoints[closestIndex - 1] || point; + const next = curvePoints[closestIndex + 1] || point; + const tangent = new THREE.Vector3().subVectors(next, prev).normalize(); + const side = new THREE.Vector3().crossVectors(tangent, new THREE.Vector3(0, 1, 0)).normalize(); + const relative = new THREE.Vector3().subVectors(localPos, point); + const sideOffset = relative.dot(side); + const centeringForce = side.clone().multiplyScalar(-sideOffset * 10); + const forwardForce = tangent.clone().multiplyScalar(conveyorSpeed); + const totalForce = forwardForce.add(centeringForce).applyQuaternion(assetQuat); - rigidBody.setAngvel({ x: 0, y: 0, z: 0 }, true); - rigidBody.setLinvel(totalForce, true); + rigidBody.setAngvel({ x: 0, y: 0, z: 0 }, true); + rigidBody.setLinvel(totalForce, true); + }); }); - }); + }) const geometries = useMemo(() => { const width = 1; - const segments = 30; + const segments = 1; return points.map(segment => { if (segment.length < 2) return null; const curve = new THREE.CatmullRomCurve3(segment.map(p => new THREE.Vector3(...p))); @@ -156,11 +154,9 @@ function NormalConveyorCollider({ geo.computeVertexNormals(); return geo; }).filter((geo): geo is THREE.BufferGeometry => geo !== null); - }, [points, asset.position, asset.rotation]); + }, [points, asset.position, asset.rotation, forward]); - // Create direction indicators const directionArrows = useMemo(() => { - if (!showDirection) return null; const arrows: THREE.Mesh[] = []; @@ -171,21 +167,26 @@ function NormalConveyorCollider({ if (segment.length < 2) return; const curve = new THREE.CatmullRomCurve3(segment.map(p => new THREE.Vector3(...p))); - const curvePoints = curve.getPoints(10); // Fewer points for arrows + const curvePoints = curve.getPoints(10); for (let i = 0; i < curvePoints.length; i++) { const point = curvePoints[i]; - const next = curvePoints[i + 1] || point; - const direction = new THREE.Vector3().subVectors(next, point).normalize(); + + let direction: THREE.Vector3; + if (i < curvePoints.length - 1) { + direction = new THREE.Vector3().subVectors(curvePoints[i + 1], point).normalize(); + } else { + direction = new THREE.Vector3().subVectors(point, curvePoints[i - 1]).normalize(); + } + + if (!forward) { + direction.multiplyScalar(-1); + } const arrow = new THREE.Mesh(arrowGeometry, arrowMaterial); arrow.position.copy(point); - arrow.quaternion.setFromUnitVectors( - new THREE.Vector3(0, forward ? 1 : -1, 0), - new THREE.Vector3(direction.x, 0.1, direction.z) - ); + arrow.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); arrows.push(arrow); - // } }); @@ -204,8 +205,8 @@ function NormalConveyorCollider({ type="fixed" position={[0, 0.001, 0]} userData={{ isConveyor: true }} - onCollisionEnter={handleMaterialEnter} - onCollisionExit={handleMaterialExit} + onCollisionEnter={e => handleMaterialEnter(e, index)} + onCollisionExit={e => handleMaterialExit(e, index)} colliders="trimesh" > @@ -220,20 +221,16 @@ function NormalConveyorCollider({ ))} {showDirection && directionArrows?.map((arrow, i) => ( - + ))} - {/* Hover highlight */} {hoverState && ( {geometries.map((geometry, index) => ( void; } -function YSplitConveyorCollider({ - points, - boundingBox, - asset, - forward: initialForward, - isPaused, - onDirectionChange -}: YSplitConveyorColliderProps) { - const conveyorRefs = useRef<(any | null)[]>([]); - const [objectsOnConveyor, setObjectsOnConveyor] = useState>(new Set()); +function YSplitConveyorCollider({ points, boundingBox, asset, forward, isPaused, onDirectionChange }: YsplitConveyorColliderProps) { + const conveyorRefs = useRef<(any)[]>([]); + const [objectsOnGeometry, setObjectsOnGeometry] = useState>>(new Map()); const conveyorDirection = useRef(new THREE.Vector3()); - const [forward, setForward] = useState(initialForward); const [showDirection, setShowDirection] = useState(false); - const [hoverState, setHoverState] = useState(false); const conveyorSpeed = 2; const lastClickTime = useRef(0); - const arrowRefs = useRef([]); + const [hoverState, setHoverState] = useState(false); useEffect(() => { const handleClick = (e: MouseEvent) => { - if (e.button === 2 && hoverState) { + if (e.button === 2) { const now = Date.now(); if (now - lastClickTime.current < 300) { - const newDirection = !forward; - setForward(newDirection); - if (onDirectionChange) { - onDirectionChange(newDirection); + if (onDirectionChange) { + console.log('forwardySplit: ', forward); + onDirectionChange(!forward); } } lastClickTime.current = now; @@ -47,7 +37,7 @@ function YSplitConveyorCollider({ window.addEventListener('mousedown', handleClick); return () => window.removeEventListener('mousedown', handleClick); - }, [forward, hoverState, onDirectionChange]); + }, [forward]); useEffect(() => { if (!boundingBox) return; @@ -58,95 +48,81 @@ function YSplitConveyorCollider({ conveyorDirection.current.applyEuler(rotation); }, [boundingBox, asset.rotation, forward]); - const handleMaterialEnter = (e: CollisionPayload) => { + const handleMaterialEnter = (e: CollisionPayload, index: number) => { if (e.other.rigidBody) { - setObjectsOnConveyor(prev => { - const newSet = new Set(prev); - newSet.add(e.other.rigidBody); - return newSet; + setObjectsOnGeometry(prev => { + const newMap = new Map(prev); + if (!newMap.has(index)) newMap.set(index, new Set()); + newMap.get(index)!.add(e.other.rigidBody); + return newMap; }); } }; - const handleMaterialExit = (e: CollisionPayload) => { + const handleMaterialExit = (e: CollisionPayload, index: number) => { if (e.other.rigidBody) { - setObjectsOnConveyor(prev => { - const newSet = new Set(prev); - newSet.delete(e.other.rigidBody); - return newSet; + setObjectsOnGeometry(prev => { + const newMap = new Map(prev); + if (newMap.has(index)) { + newMap.get(index)!.delete(e.other.rigidBody); + } + return newMap; }); } }; - useFrame(({ clock }) => { + useFrame(() => { if (isPaused) return; + const assetPos = new THREE.Vector3(...(asset.position || [0, 0, 0])); const assetRot = new THREE.Euler(...(asset.rotation || [0, 0, 0])); const assetQuat = new THREE.Quaternion().setFromEuler(assetRot); const inverseQuat = assetQuat.clone().invert(); - const allCurvePoints: THREE.Vector3[] = []; - points.forEach(segment => { + points.forEach((segment, index) => { + if (segment.length < 2) return; + const curve = new THREE.CatmullRomCurve3(segment.map(p => new THREE.Vector3(...p))); - allCurvePoints.push(...curve.getPoints((segment.length - 1) * 30)); - }); + const curvePoints = curve.getPoints((segment.length - 1) * 30); + if (!forward) curvePoints.reverse(); - if (!forward) allCurvePoints.reverse(); + const bodies = objectsOnGeometry.get(index); + if (!bodies) return; - objectsOnConveyor.forEach(rigidBody => { - const worldPos = new THREE.Vector3().copy(rigidBody.translation()); - const localPos = worldPos.clone().sub(assetPos).applyQuaternion(inverseQuat); + bodies.forEach(rigidBody => { + const worldPos = new THREE.Vector3().copy(rigidBody.translation()); + const localPos = worldPos.clone().sub(assetPos).applyQuaternion(inverseQuat); - let closestIndex = 0; - let minDist = Infinity; - for (let i = 0; i < allCurvePoints.length; i++) { - const dist = allCurvePoints[i].distanceToSquared(localPos); - if (dist < minDist) { - minDist = dist; - closestIndex = i; - } - } - - const point = allCurvePoints[closestIndex]; - const prev = allCurvePoints[closestIndex - 1] || point; - const next = allCurvePoints[closestIndex + 1] || point; - const tangent = new THREE.Vector3().subVectors(next, prev).normalize(); - const side = new THREE.Vector3().crossVectors(tangent, new THREE.Vector3(0, 1, 0)).normalize(); - const relative = new THREE.Vector3().subVectors(localPos, point); - const sideOffset = relative.dot(side); - const centeringForce = side.clone().multiplyScalar(-sideOffset * 10); - const forwardForce = tangent.clone().multiplyScalar(conveyorSpeed); - const totalForce = forwardForce.add(centeringForce).applyQuaternion(assetQuat); - - rigidBody.setAngvel({ x: 0, y: 0, z: 0 }, true); - rigidBody.setLinvel(totalForce, true); - }); - - if (showDirection && arrowRefs.current.length > 0) { - const elapsedTime = clock.getElapsedTime(); - arrowRefs.current.forEach((arrowGroup, index) => { - - const pulseScale = 0.9 + 0.1 * Math.sin(elapsedTime * 5 + index * 0.5); - arrowGroup.scale.setScalar(pulseScale); - - const intensity = 0.7 + 0.3 * Math.sin(elapsedTime * 3 + index * 0.3); - arrowGroup.children.forEach(child => { - if (child instanceof THREE.Mesh) { - const material = child.material as THREE.MeshBasicMaterial; - if (forward) { - material.color.setRGB(0, intensity, 0); - } else { - material.color.setRGB(intensity, 0, 0); - } + let closestIndex = 0; + let minDist = Infinity; + for (let i = 0; i < curvePoints.length; i++) { + const dist = curvePoints[i].distanceToSquared(localPos); + if (dist < minDist) { + minDist = dist; + closestIndex = i; } - }); + } + + const point = curvePoints[closestIndex]; + const prev = curvePoints[closestIndex - 1] || point; + const next = curvePoints[closestIndex + 1] || point; + const tangent = new THREE.Vector3().subVectors(next, prev).normalize(); + const side = new THREE.Vector3().crossVectors(tangent, new THREE.Vector3(0, 1, 0)).normalize(); + const relative = new THREE.Vector3().subVectors(localPos, point); + const sideOffset = relative.dot(side); + const centeringForce = side.clone().multiplyScalar(-sideOffset * 10); + const forwardForce = tangent.clone().multiplyScalar(conveyorSpeed); + const totalForce = forwardForce.add(centeringForce).applyQuaternion(assetQuat); + + rigidBody.setAngvel({ x: 0, y: 0, z: 0 }, true); + rigidBody.setLinvel(totalForce, true); }); - } - }); + }); + }) const geometries = useMemo(() => { const width = 1; - const segments = 30; + const segments = 1; return points.map(segment => { if (segment.length < 2) return null; const curve = new THREE.CatmullRomCurve3(segment.map(p => new THREE.Vector3(...p))); @@ -177,113 +153,86 @@ function YSplitConveyorCollider({ geo.computeVertexNormals(); return geo; }).filter((geo): geo is THREE.BufferGeometry => geo !== null); - }, [points, asset.position, asset.rotation]); + }, [points, asset.position, asset.rotation, forward]); - // Create direction indicators const directionArrows = useMemo(() => { if (!showDirection) return null; - const arrows: THREE.Group[] = []; - const arrowHeight = 0.2; - const arrowRadius = 0.05; + const arrows: THREE.Mesh[] = []; + const arrowGeometry = new THREE.ConeGeometry(0.05, 0.2, 8); + const arrowMaterial = new THREE.MeshBasicMaterial({ color: forward ? 0x00ff00 : 0xff0000 }); points.forEach(segment => { if (segment.length < 2) return; const curve = new THREE.CatmullRomCurve3(segment.map(p => new THREE.Vector3(...p))); - const curvePoints = curve.getPoints(8); // Fewer points for arrows + const curvePoints = curve.getPoints(10); for (let i = 0; i < curvePoints.length; i++) { const point = curvePoints[i]; - const next = curvePoints[i + 1] || point; - const direction = new THREE.Vector3().subVectors(next, point).normalize(); - const arrowGroup = new THREE.Group(); - const shaftLength = arrowHeight * 0.7; - const shaftGeometry = new THREE.CylinderGeometry(arrowRadius * 0.3, arrowRadius * 0.3, shaftLength, 8); - const shaftMaterial = new THREE.MeshBasicMaterial({ - color: forward ? 0x00ff00 : 0xff0000 - }); - const shaft = new THREE.Mesh(shaftGeometry, shaftMaterial); - shaft.position.y = shaftLength / 2; - shaft.rotation.x = Math.PI / 2; - const headGeometry = new THREE.ConeGeometry(arrowRadius, arrowHeight * 0.3, 8); - const headMaterial = new THREE.MeshBasicMaterial({ - color: forward ? 0x00ff00 : 0xff0000 - }); - const head = new THREE.Mesh(headGeometry, headMaterial); - head.position.y = shaftLength; - arrowGroup.add(shaft); - arrowGroup.add(head); - arrowGroup.position.copy(point); - arrowGroup.position.y += 0.1; - arrowGroup.quaternion.setFromUnitVectors( - new THREE.Vector3(0, 1, 0), - new THREE.Vector3(direction.x, 0.1, direction.z) - ); - arrows.push(arrowGroup); + + let direction: THREE.Vector3; + if (i < curvePoints.length - 1) { + direction = new THREE.Vector3().subVectors(curvePoints[i + 1], point).normalize(); + } else { + direction = new THREE.Vector3().subVectors(point, curvePoints[i - 1]).normalize(); + } + + if (!forward) { + direction.multiplyScalar(-1); + } + + const arrow = new THREE.Mesh(arrowGeometry, arrowMaterial); + arrow.position.copy(point); + arrow.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); + arrows.push(arrow); } }); - arrowRefs.current = arrows; return arrows; }, [points, showDirection, forward]); return ( { - setShowDirection(true); - setHoverState(true); - }} - onPointerOut={() => { - setShowDirection(false); - setHoverState(false); - }} + onPointerEnter={() => { setShowDirection(true); setHoverState(true); }} + onPointerLeave={() => { setShowDirection(false); setHoverState(false); }} > - {/* Conveyor surface */} {geometries.map((geometry, index) => ( (conveyorRefs.current[index] = el)} type="fixed" position={[0, 0.001, 0]} userData={{ isConveyor: true }} - onCollisionEnter={handleMaterialEnter} - onCollisionExit={handleMaterialExit} + onCollisionEnter={e => handleMaterialEnter(e, index)} + onCollisionExit={e => handleMaterialExit(e, index)} colliders="trimesh" > ))} {showDirection && directionArrows?.map((arrow, i) => ( - + ))} - {/* Hover highlight */} {hoverState && ( {geometries.map((geometry, index) => ( @@ -295,4 +244,4 @@ function YSplitConveyorCollider({ ); } -export default YSplitConveyorCollider; \ No newline at end of file +export default YSplitConveyorCollider; diff --git a/app/src/modules/scene/physics/ui/ColliderProperties.tsx b/app/src/modules/scene/physics/ui/ColliderProperties.tsx index 13a3d34..25ee14a 100644 --- a/app/src/modules/scene/physics/ui/ColliderProperties.tsx +++ b/app/src/modules/scene/physics/ui/ColliderProperties.tsx @@ -138,7 +138,6 @@ const ColliderProperties: React.FC = () => { }; useEffect(() => { let collider = getColliderCondition(selectedCollider?.id || "") - console.log('collider: ', collider); }, [colliders, selectedCollider]); diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index a8f9661..6ff4b97 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -72,12 +72,10 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co > - {/* */} - + - + {/* */} -