feat: Refactor HumanUi and VehicleUI components; introduce MarkerPrimitive for improved marker handling and interaction

This commit is contained in:
2025-08-04 15:16:35 +05:30
parent c9cc8d3534
commit c976533297
2 changed files with 236 additions and 75 deletions

View File

@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from 'react'
import { useGLTF } from '@react-three/drei';
import { useEffect, useMemo, useRef, useState } from 'react'
import * as THREE from 'three'
import { Line, useGLTF } from '@react-three/drei';
import { useFrame, useThree } from '@react-three/fiber';
import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore';
import { useProductContext } from '../../../products/productContext';
@@ -27,7 +28,7 @@ function HumanUi() {
const { humanStore, productStore } = useSceneContext();
const { selectedProduct } = selectedProductStore();
const { humans, getHumanById } = humanStore();
const { updateEvent, updateAction, getActionByUuid } = productStore();
const { updateEvent, getActionByUuid } = productStore();
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
const [assemblyPosition, setAssemblyPosition] = useState<[number, number, number]>([0, 1, 0]);
@@ -313,64 +314,49 @@ function HumanUi() {
rotation={[0, Math.PI, 0]}
>
{isAssembly ? (
<primitive
ref={assemblyMarker}
<MarkerPrimitive
name="assemblyMarker"
refProp={assemblyMarker}
object={assemblyScene}
position={assemblyPosition}
rotation={assemblyRotation}
onPointerDown={(e: any) => {
if (e.object.parent.name === "handle") {
handlePointerDown(e, "assembly", "assembly");
} else {
handlePointerDown(e, "assembly", "assembly");
}
}}
onPointerMissed={() => {
setIsDragging(null);
setIsRotating(null);
if (controls) (controls as any).enabled = true;
}}
outerGroupRef={outerGroup}
type="assembly"
subtype="assembly"
color="#0f87f7"
setIsDragging={setIsDragging}
setIsRotating={setIsRotating}
handlePointerDown={handlePointerDown}
/>
) : (
<>
<primitive
<MarkerPrimitive
name="startMarker"
refProp={startMarker}
object={startScene}
ref={startMarker}
position={startPosition}
rotation={startRotation}
onPointerDown={(e: any) => {
if (e.object.parent.name === "handle") {
handlePointerDown(e, "start", "start");
} else {
handlePointerDown(e, "start", "start");
}
}}
onPointerMissed={() => {
setIsDragging(null);
setIsRotating(null);
if (controls) (controls as any).enabled = true;
}}
outerGroupRef={outerGroup}
type="start"
subtype="start"
color="green"
setIsDragging={setIsDragging}
setIsRotating={setIsRotating}
handlePointerDown={handlePointerDown}
/>
<primitive
<MarkerPrimitive
name="endMarker"
refProp={endMarker}
object={endScene}
ref={endMarker}
position={endPosition}
rotation={endRotation}
onPointerDown={(e: any) => {
if (e.object.parent.name === "handle") {
handlePointerDown(e, "end", "end");
} else {
handlePointerDown(e, "end", "end");
}
}}
onPointerMissed={() => {
setIsDragging(null);
setIsRotating(null);
if (controls) (controls as any).enabled = true;
}}
outerGroupRef={outerGroup}
type="end"
subtype="end"
color="orange"
setIsDragging={setIsDragging}
setIsRotating={setIsRotating}
handlePointerDown={handlePointerDown}
/>
</>
)}
@@ -380,4 +366,99 @@ function HumanUi() {
);
}
export default HumanUi;
export default HumanUi;
const MarkerPrimitive = ({
name,
refProp,
object,
position,
rotation,
outerGroupRef,
type,
subtype,
color,
setIsDragging,
setIsRotating,
handlePointerDown,
}: {
name: string;
refProp: any;
object: THREE.Object3D;
position: [number, number, number];
rotation: [number, number, number];
outerGroupRef: React.RefObject<THREE.Group>;
type: string;
subtype: string;
color: string;
setIsDragging: (val: any) => void;
setIsRotating: (val: any) => void;
handlePointerDown: any;
}) => {
const { controls, scene } = useThree();
const [hitPoint, setHitPoint] = useState<THREE.Vector3 | null>(null);
const lineRef = useRef<THREE.BufferGeometry>(null);
useFrame(() => {
if (!refProp.current || !outerGroupRef.current) return;
const worldPos = new THREE.Vector3();
refProp.current.getWorldPosition(worldPos);
const localMarkerPos = outerGroupRef.current.worldToLocal(worldPos.clone());
const rayOrigin = worldPos.clone();
const direction = new THREE.Vector3(0, -1, 0);
const raycaster = new THREE.Raycaster(rayOrigin, direction, 0.1, 1000);
const intersects = raycaster.intersectObjects(scene.children, true);
const hit = intersects.find(i => i.object.name !== name);
if (hit) {
const localHit = outerGroupRef.current.worldToLocal(hit.point.clone());
setHitPoint(localHit);
if (lineRef.current) {
const positions = new Float32Array([localMarkerPos.x, localMarkerPos.y, localMarkerPos.z, localHit.x, localHit.y, localHit.z]);
lineRef.current.setAttribute('position', new THREE.BufferAttribute(positions, 3));
lineRef.current.attributes.position.needsUpdate = true;
}
} else {
setHitPoint(null);
}
});
return (
<>
<primitive
name={name}
ref={refProp}
object={object}
position={position}
rotation={rotation}
onPointerDown={(e: any) => {
handlePointerDown(e, type, subtype);
}}
onPointerMissed={() => {
setIsDragging(null);
setIsRotating(null);
if (controls) (controls as any).enabled = true;
}}
/>
{hitPoint && (
<>
<mesh name={name} position={hitPoint} rotation={[-Math.PI / 2, 0, 0]}>
<torusGeometry args={[0.15, 0.02, 3, 32]} />
<meshBasicMaterial color={color} transparent opacity={0.8} depthWrite={false} />
</mesh>
<line>
<bufferGeometry ref={lineRef} />
<lineBasicMaterial color={color} />
</line>
</>
)}
</>
);
};

View File

@@ -1,6 +1,7 @@
import { useEffect, useRef, useState } from "react";
import * as Types from "../../../../types/world/worldTypes";
import { useGLTF } from "@react-three/drei";
import * as THREE from "three";
import { useFrame, useThree } from "@react-three/fiber";
import { useSelectedEventSphere, useIsDragging, useIsRotating, } from "../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
@@ -342,41 +343,120 @@ const VehicleUI = () => {
</group>
{/* Start Marker */}
<primitive
<VehicleMarkerPrimitive
name="startMarker"
object={startScene}
ref={startMarker}
position={startPosition}
rotation={startRotation}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "start", "start");
}}
outerGroupRef={outerGroup}
color="green"
handlePointerDown={handlePointerDown}
setIsDragging={setIsDragging}
setIsRotating={setIsRotating}
/>
{/* End Marker */}
<VehicleMarkerPrimitive
name="endMarker"
object={endScene}
position={endPosition}
rotation={endRotation}
outerGroupRef={outerGroup}
color="red"
handlePointerDown={handlePointerDown}
setIsDragging={setIsDragging}
setIsRotating={setIsRotating}
/>
</group>
) : null;
};
export default VehicleUI;
export const VehicleMarkerPrimitive = ({
name,
object,
position,
rotation,
outerGroupRef,
color,
handlePointerDown,
setIsDragging,
setIsRotating,
}: {
name: string;
object: THREE.Object3D;
position: [number, number, number];
rotation: [number, number, number];
outerGroupRef: React.RefObject<THREE.Group>;
color: string;
handlePointerDown: (e: any, type: "start" | "end", rotation: "start" | "end") => void;
setIsDragging: (val: any) => void;
setIsRotating: (val: any) => void;
}) => {
const { scene } = useThree();
const markerRef = useRef<THREE.Group>(null);
const lineRef = useRef<THREE.BufferGeometry>(null);
const [hitPoint, setHitPoint] = useState<THREE.Vector3 | null>(null);
useFrame(() => {
if (!markerRef.current || !outerGroupRef.current) return;
const worldPos = new THREE.Vector3();
markerRef.current.getWorldPosition(worldPos);
const localMarkerPos = outerGroupRef.current.worldToLocal(worldPos.clone());
const rayOrigin = worldPos.clone();
const direction = new THREE.Vector3(0, -1, 0);
const raycaster = new THREE.Raycaster(rayOrigin, direction, 0.1, 1000);
const intersects = raycaster.intersectObjects(scene.children, true);
const hit = intersects.find(i => i.object.name !== name);
if (hit) {
const localHit = outerGroupRef.current.worldToLocal(hit.point.clone());
setHitPoint(localHit);
if (lineRef.current) {
const positions = new Float32Array([
localMarkerPos.x, localMarkerPos.y, localMarkerPos.z,
localHit.x, localHit.y, localHit.z
]);
lineRef.current.setAttribute('position', new THREE.BufferAttribute(positions, 3));
lineRef.current.attributes.position.needsUpdate = true;
}
} else {
setHitPoint(null);
}
});
return (
<>
<primitive
name={name}
ref={markerRef}
object={object}
position={position}
rotation={rotation}
onPointerDown={(e: any) => handlePointerDown(e, name === "startMarker" ? "start" : "end", name === "startMarker" ? "start" : "end")}
onPointerMissed={() => {
controls.enabled = true;
setIsDragging(null);
setIsRotating(null);
}}
/>
{/* End Marker */}
<primitive
name="endMarker"
object={endScene}
ref={endMarker}
position={endPosition}
rotation={endRotation}
onPointerDown={(e: any) => {
e.stopPropagation();
handlePointerDown(e, "end", "end");
}}
onPointerMissed={() => {
controls.enabled = true;
setIsDragging(null);
setIsRotating(null);
}}
/>
</group>
) : null;
};
export default VehicleUI;
{hitPoint && (
<>
<mesh name={`${name}-torus`} position={hitPoint} rotation={[-Math.PI / 2, 0, 0]}>
<torusGeometry args={[0.15, 0.02, 3, 32]} />
<meshBasicMaterial color={color} transparent opacity={0.8} depthWrite={false} />
</mesh>
<line>
<bufferGeometry ref={lineRef} />
<lineBasicMaterial color={color} />
</line>
</>
)}
</>
);
};