From 324b7aa40cd3f31160a9aa8f65fa3ab72f4977d0 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Wed, 20 Aug 2025 10:37:39 +0530 Subject: [PATCH] refactor: Clean up MaterialSpawner component and improve drag handling logic --- .../conveyor/types/curvedConveyorCollider.tsx | 179 +++++++++--------- .../modules/scene/physics/materialSpawner.tsx | 113 +++++++---- .../scene/physics/physicsSimulator.tsx | 12 +- 3 files changed, 173 insertions(+), 131 deletions(-) diff --git a/app/src/modules/scene/physics/conveyor/types/curvedConveyorCollider.tsx b/app/src/modules/scene/physics/conveyor/types/curvedConveyorCollider.tsx index 8235a27..d68668e 100644 --- a/app/src/modules/scene/physics/conveyor/types/curvedConveyorCollider.tsx +++ b/app/src/modules/scene/physics/conveyor/types/curvedConveyorCollider.tsx @@ -33,7 +33,7 @@ function CurvedConveyorCollider({ if (e.button === 2 && hoverState) { // Right click and hovering over conveyor const now = Date.now(); if (now - lastClickTime.current < 300) { - console.log("log"); + setForward(prev => !prev); } lastClickTime.current = now; @@ -44,35 +44,7 @@ function CurvedConveyorCollider({ return () => window.removeEventListener('mousedown', handleClick); }, [forward, hoverState]); - useEffect(() => { - if (!boundingBox) return; - const size = boundingBox.getSize(new THREE.Vector3()); - const [width, depth] = [size.x, size.z]; - conveyorDirection.current.set(width < depth ? 0 : 1, 0, width < depth ? 1 : 0); - const rotation = new THREE.Euler().fromArray(asset.rotation || [0, 0, 0]); - conveyorDirection.current.applyEuler(rotation); - }, [boundingBox, asset.rotation, forward]); - - const handleMaterialEnter = (e: CollisionPayload) => { - if (e.other.rigidBody) { - setObjectsOnConveyor(prev => { - const newSet = new Set(prev); - newSet.add(e.other.rigidBody); - return newSet; - }); - } - }; - - const handleMaterialExit = (e: CollisionPayload) => { - if (e.other.rigidBody) { - setObjectsOnConveyor(prev => { - const newSet = new Set(prev); - newSet.delete(e.other.rigidBody); - return newSet; - }); - } - }; - + const bezierPoints = useMemo(() => { const segments = 20; const allPoints: THREE.Vector3[] = []; @@ -100,6 +72,96 @@ function CurvedConveyorCollider({ return allPoints; }, [points, forward]); + + const geometries = useMemo(() => { + const width = 1; + const segments = 20; + const geos: THREE.BufferGeometry[] = []; + + points.forEach(segment => { + const vertices: number[] = []; + const indices: number[] = []; + + const vectorPoint = segment.map(p => new THREE.Vector3(...p)); + if (vectorPoint.length < 3) return; + + for (let group = 0; group + 2 < vectorPoint.length; group += 2) { + const p0 = vectorPoint[group]; + const p1 = vectorPoint[group + 1]; + const p2 = vectorPoint[group + 2]; + + for (let i = 0; i <= segments; i++) { + const t = i / segments; + const point = new THREE.Vector3() + .copy(p0) + .multiplyScalar((1 - t) ** 2) + .addScaledVector(p1, 2 * (1 - t) * t) + .addScaledVector(p2, t ** 2); + + const tangent = new THREE.Vector3() + .copy(p0) + .multiplyScalar(-2 * (1 - t)) + .addScaledVector(p1, 2 - 4 * t) + .addScaledVector(p2, 2 * t) + .normalize(); + + const normal = new THREE.Vector3().crossVectors(tangent, new THREE.Vector3(0, 1, 0)).normalize(); + const left = new THREE.Vector3().copy(point).addScaledVector(normal, -width / 2); + const right = new THREE.Vector3().copy(point).addScaledVector(normal, width / 2); + + vertices.push(...left.toArray(), ...right.toArray()); + } + } + + const totalSegments = ((vectorPoint.length - 1) / 2) * segments; + for (let i = 0; i < totalSegments; i++) { + const base = i * 2; + indices.push(base, base + 1, base + 2); + indices.push(base + 1, base + 3, base + 2); + } + + const ribbonGeometry = new THREE.BufferGeometry(); + ribbonGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + ribbonGeometry.setIndex(indices); + ribbonGeometry.computeVertexNormals(); + geos.push(ribbonGeometry); + }); + + setGeometryKey(k => k + 1); + 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]); + + + const handleMaterialEnter = (e: CollisionPayload) => { + if (e.other.rigidBody) { + setObjectsOnConveyor(prev => { + const newSet = new Set(prev); + newSet.add(e.other.rigidBody); + return newSet; + }); + } + }; + + const handleMaterialExit = (e: CollisionPayload) => { + if (e.other.rigidBody) { + setObjectsOnConveyor(prev => { + const newSet = new Set(prev); + newSet.delete(e.other.rigidBody); + return newSet; + }); + } + }; + useFrame(({ clock }) => { if (isPaused) return; @@ -165,63 +227,6 @@ function CurvedConveyorCollider({ } }); - const geometries = useMemo(() => { - const width = 1; - const segments = 20; - const geos: THREE.BufferGeometry[] = []; - - points.forEach(segment => { - const vertices: number[] = []; - const indices: number[] = []; - - const vectorPoint = segment.map(p => new THREE.Vector3(...p)); - if (vectorPoint.length < 3) return; - - for (let group = 0; group + 2 < vectorPoint.length; group += 2) { - const p0 = vectorPoint[group]; - const p1 = vectorPoint[group + 1]; - const p2 = vectorPoint[group + 2]; - - for (let i = 0; i <= segments; i++) { - const t = i / segments; - const point = new THREE.Vector3() - .copy(p0) - .multiplyScalar((1 - t) ** 2) - .addScaledVector(p1, 2 * (1 - t) * t) - .addScaledVector(p2, t ** 2); - - const tangent = new THREE.Vector3() - .copy(p0) - .multiplyScalar(-2 * (1 - t)) - .addScaledVector(p1, 2 - 4 * t) - .addScaledVector(p2, 2 * t) - .normalize(); - - const normal = new THREE.Vector3().crossVectors(tangent, new THREE.Vector3(0, 1, 0)).normalize(); - const left = new THREE.Vector3().copy(point).addScaledVector(normal, -width / 2); - const right = new THREE.Vector3().copy(point).addScaledVector(normal, width / 2); - - vertices.push(...left.toArray(), ...right.toArray()); - } - } - - const totalSegments = ((vectorPoint.length - 1) / 2) * segments; - for (let i = 0; i < totalSegments; i++) { - const base = i * 2; - indices.push(base, base + 1, base + 2); - indices.push(base + 1, base + 3, base + 2); - } - - const ribbonGeometry = new THREE.BufferGeometry(); - ribbonGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); - ribbonGeometry.setIndex(indices); - ribbonGeometry.computeVertexNormals(); - geos.push(ribbonGeometry); - }); - - setGeometryKey(k => k + 1); - return geos; - }, [points, asset.position, asset.rotation]); // Create curved direction indicators const directionArrows = useMemo(() => { diff --git a/app/src/modules/scene/physics/materialSpawner.tsx b/app/src/modules/scene/physics/materialSpawner.tsx index ea342ae..a752e03 100644 --- a/app/src/modules/scene/physics/materialSpawner.tsx +++ b/app/src/modules/scene/physics/materialSpawner.tsx @@ -4,7 +4,7 @@ import { useLoadingProgress } from '../../../store/builder/store'; import { MaterialModel } from '../../simulation/materials/instances/material/materialModel'; import { useThree } from '@react-three/fiber'; import * as THREE from 'three'; -import { CameraControls, TransformControls } from '@react-three/drei'; +import { CameraControls, } from '@react-three/drei'; import { generateUniqueId } from '../../../functions/generateUniqueId'; type MaterialSpawnerProps = { @@ -28,9 +28,11 @@ function MaterialSpawner({ position: initialPos, spawnInterval, spawnCount }: Ma const dragOffset = useRef(new THREE.Vector3()); const initialDepth = useRef(0); const materialTypes = ['Default material', 'Material 1', 'Material 2', 'Material 3']; - const [boxPosition, setBoxPosition] = useState<[number, number, number]>(initialPos); const spawnerRef = useRef(null!); - const newPositionRef = useRef<[number, number, number]>([...initialPos]); + const [isDraggingSpawner, setIsDraggingSpawner] = useState(false); + const spawnerDragOffset = useRef(new THREE.Vector3()); + const spawnerInitialDepth = useRef(0); + const [boxPosition, setBoxPosition] = useState<[number, number, number]>(initialPos); useEffect(() => { if (loadingProgress !== 0) return; @@ -49,11 +51,8 @@ function MaterialSpawner({ position: initialPos, spawnInterval, spawnCount }: Ma } spawnedCount.current++; - const randomMaterialType = - materialTypes[Math.floor(Math.random() * materialTypes.length)]; + const randomMaterialType = materialTypes[Math.floor(Math.random() * materialTypes.length)]; - - console.log('boxPosition: ', boxPosition); return [ ...prev, { @@ -94,7 +93,6 @@ function MaterialSpawner({ position: initialPos, spawnInterval, spawnCount }: Ma }, [loadingProgress, spawnInterval, spawnCount, spawningPaused, boxPosition]); - const handleSleep = (id: string) => { setSpawned(prev => prev.filter(obj => obj.id !== id)); }; @@ -181,44 +179,83 @@ function MaterialSpawner({ position: initialPos, spawnInterval, spawnCount }: Ma }; }, [draggedId, spawned, controls, camera, gl]); + + const handleSpawnerPointerDown = (e: any) => { + if (e.button !== 2) return; // right click only + e.stopPropagation(); + + if (controls) (controls as CameraControls).enabled = false; + setIsDraggingSpawner(true); + + const worldPos = spawnerRef.current.getWorldPosition(new THREE.Vector3()); + const screenPos = worldPos.clone().project(camera); + + spawnerDragOffset.current.set( + screenPos.x - pointer.x, + 0, + screenPos.y - pointer.y + ); + + spawnerInitialDepth.current = worldPos.clone().sub(camera.position).length(); + }; + + const handleSpawnerPointerMove = () => { + if (!isDraggingSpawner) return; + + const targetScreenPos = new THREE.Vector3( + pointer.x + spawnerDragOffset.current.x, + 0, + pointer.y + spawnerDragOffset.current.z + ); + + const targetWorldPos = new THREE.Vector3( + targetScreenPos.x, + targetScreenPos.z, + 0.5 + ).unproject(camera); + + const direction = targetWorldPos.sub(camera.position).normalize(); + const finalPosition = camera.position.clone().add(direction.multiplyScalar(spawnerInitialDepth.current)); + + spawnerRef.current.position.copy(finalPosition); + }; + + const handleSpawnerPointerUp = () => { + if (controls) (controls as CameraControls).enabled = true; + setIsDraggingSpawner(false); + + if (spawnerRef.current) { + const worldPos = spawnerRef.current.getWorldPosition(new THREE.Vector3()); + setBoxPosition([worldPos.x, worldPos.y, worldPos.z] as [number, number, number]); + } + }; + + useEffect(() => { + const canvas = gl.domElement; + canvas.addEventListener("pointermove", handleSpawnerPointerMove); + canvas.addEventListener("pointerup", handleSpawnerPointerUp); + return () => { + canvas.removeEventListener("pointermove", handleSpawnerPointerMove); + canvas.removeEventListener("pointerup", handleSpawnerPointerUp); + }; + }, [isDraggingSpawner, camera, gl, controls]); + const handleBoxClick = () => { setSpawningPaused(prev => !prev); }; - const handleBoxContextMenu = () => { - }; - - return ( <> - { - if (controls) (controls as CameraControls).enabled = false; - }} - onMouseUp={() => { - if (controls) (controls as CameraControls).enabled = true; - setBoxPosition(newPositionRef.current); - }} - onObjectChange={() => { - if (spawnerRef.current) { - const pos = spawnerRef.current.position; - newPositionRef.current = [pos.x, pos.y, pos.z]; // Save latest - } - }} + onClick={handleBoxClick} + onPointerDown={handleSpawnerPointerDown} > - - - - - + + + {spawned.map(({ id, position, materialType, ref }) => ( - {/* */} + {/* */} {/*