2025-05-29 12:47:24 +00:00
|
|
|
import * as THREE from 'three';
|
|
|
|
import * as Constants from '../../../types/world/worldConstants';
|
|
|
|
import { useRef, useState, useEffect, useMemo } from 'react';
|
2025-05-30 06:04:57 +00:00
|
|
|
import { useDeletePointOrLine, useToolMode } from '../../../store/builder/store';
|
2025-05-30 04:51:45 +00:00
|
|
|
import { DragControls } from '@react-three/drei';
|
|
|
|
import { useAisleStore } from '../../../store/builder/useAisleStore';
|
|
|
|
import { useThree } from '@react-three/fiber';
|
|
|
|
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
2025-05-30 10:57:28 +00:00
|
|
|
import { usePointSnapping } from './helpers/usePointSnapping';
|
2025-05-29 12:47:24 +00:00
|
|
|
|
2025-05-30 09:03:55 +00:00
|
|
|
function Point({ point }: { readonly point: Point }) {
|
2025-05-29 12:47:24 +00:00
|
|
|
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
2025-05-30 06:04:57 +00:00
|
|
|
const { raycaster, camera, pointer } = useThree();
|
2025-05-30 04:51:45 +00:00
|
|
|
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
2025-05-29 12:47:24 +00:00
|
|
|
const [isHovered, setIsHovered] = useState(false);
|
|
|
|
const { toolMode } = useToolMode();
|
2025-05-30 06:04:57 +00:00
|
|
|
const { setPosition, removePoint } = useAisleStore();
|
2025-05-30 10:57:28 +00:00
|
|
|
const { checkSnapForAisle } = usePointSnapping({ uuid: point.uuid, pointType: point.pointType, position: point.position });
|
2025-05-30 04:51:45 +00:00
|
|
|
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
|
2025-05-30 06:04:57 +00:00
|
|
|
const { deletePointOrLine } = useDeletePointOrLine();
|
2025-05-29 12:47:24 +00:00
|
|
|
|
|
|
|
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
|
|
|
const defaultInnerColor = Constants.pointConfig.defaultInnerColor;
|
2025-05-30 06:04:57 +00:00
|
|
|
const defaultOuterColor = Constants.pointConfig.aisleOuterColor;
|
|
|
|
const defaultDeleteColor = Constants.pointConfig.deleteColor;
|
2025-05-29 12:47:24 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
2025-05-30 06:04:57 +00:00
|
|
|
if (materialRef.current && (toolMode === 'move' || deletePointOrLine)) {
|
|
|
|
let innerColor;
|
|
|
|
let outerColor;
|
|
|
|
if (isHovered) {
|
|
|
|
innerColor = deletePointOrLine ? defaultDeleteColor : defaultOuterColor;
|
|
|
|
outerColor = deletePointOrLine ? defaultDeleteColor : defaultOuterColor;
|
|
|
|
} else {
|
|
|
|
innerColor = defaultInnerColor;
|
|
|
|
outerColor = defaultOuterColor;
|
|
|
|
}
|
|
|
|
materialRef.current.uniforms.uInnerColor.value.set(innerColor);
|
|
|
|
materialRef.current.uniforms.uOuterColor.value.set(outerColor);
|
2025-05-29 12:47:24 +00:00
|
|
|
materialRef.current.uniformsNeedUpdate = true;
|
|
|
|
} else if (materialRef.current && toolMode !== 'move') {
|
|
|
|
materialRef.current.uniforms.uInnerColor.value.set(defaultInnerColor);
|
2025-05-30 06:04:57 +00:00
|
|
|
materialRef.current.uniforms.uOuterColor.value.set(defaultOuterColor);
|
2025-05-29 12:47:24 +00:00
|
|
|
materialRef.current.uniformsNeedUpdate = true;
|
|
|
|
}
|
2025-05-30 06:04:57 +00:00
|
|
|
}, [isHovered, defaultInnerColor, defaultOuterColor, toolMode, deletePointOrLine, defaultDeleteColor]);
|
2025-05-29 12:47:24 +00:00
|
|
|
|
|
|
|
const uniforms = useMemo(() => ({
|
2025-05-30 06:04:57 +00:00
|
|
|
uOuterColor: { value: new THREE.Color(defaultOuterColor) },
|
2025-05-29 12:47:24 +00:00
|
|
|
uInnerColor: { value: new THREE.Color(defaultInnerColor) },
|
2025-05-30 06:04:57 +00:00
|
|
|
}), [defaultInnerColor, defaultInnerColor]);
|
2025-05-29 12:47:24 +00:00
|
|
|
|
2025-05-30 04:51:45 +00:00
|
|
|
const handleDrag = (point: Point) => {
|
|
|
|
if (toolMode === 'move' && isHovered) {
|
|
|
|
raycaster.setFromCamera(pointer, camera);
|
|
|
|
const intersectionPoint = new THREE.Vector3();
|
|
|
|
const position = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
2025-05-30 09:03:55 +00:00
|
|
|
if (point.pointType === 'Aisle') {
|
2025-05-30 04:51:45 +00:00
|
|
|
if (position) {
|
2025-05-30 09:03:55 +00:00
|
|
|
const newPosition: [number, number, number] = [position.x, position.y, position.z];
|
|
|
|
const snappedPosition = checkSnapForAisle(newPosition);
|
|
|
|
|
2025-05-30 10:57:28 +00:00
|
|
|
setPosition(point.uuid, snappedPosition.position);
|
2025-05-30 04:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const handleDragEnd = (point: Point) => {
|
2025-05-30 06:04:57 +00:00
|
|
|
if (deletePointOrLine) return;
|
2025-05-30 04:51:45 +00:00
|
|
|
console.log('point: ', point);
|
|
|
|
}
|
|
|
|
|
2025-05-30 06:04:57 +00:00
|
|
|
const handlePointClick = (point: Point) => {
|
|
|
|
if (deletePointOrLine) {
|
|
|
|
const removedAisles = removePoint(point.uuid);
|
|
|
|
if (removedAisles.length > 0) {
|
|
|
|
setHoveredPoint(null);
|
|
|
|
console.log(removedAisles);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-30 04:51:45 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (hoveredPoint && hoveredPoint.uuid !== point.uuid) {
|
|
|
|
setIsHovered(false);
|
|
|
|
}
|
|
|
|
}, [hoveredPoint])
|
|
|
|
|
2025-05-29 12:47:24 +00:00
|
|
|
if (!point) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2025-05-30 04:51:45 +00:00
|
|
|
<DragControls
|
|
|
|
axisLock='y'
|
|
|
|
autoTransform={false}
|
|
|
|
onDrag={() => { handleDrag(point) }}
|
|
|
|
onDragEnd={() => { handleDragEnd(point) }}
|
2025-05-29 12:47:24 +00:00
|
|
|
>
|
2025-05-30 04:51:45 +00:00
|
|
|
<mesh
|
2025-05-30 06:04:57 +00:00
|
|
|
key={point.uuid}
|
2025-05-30 04:51:45 +00:00
|
|
|
uuid={point.uuid}
|
2025-05-30 06:04:57 +00:00
|
|
|
name='Aisle-Point'
|
2025-05-30 04:51:45 +00:00
|
|
|
position={new THREE.Vector3(...point.position)}
|
2025-05-30 06:04:57 +00:00
|
|
|
onClick={() => {
|
|
|
|
handlePointClick(point);
|
|
|
|
}}
|
2025-05-30 04:51:45 +00:00
|
|
|
onPointerOver={() => {
|
|
|
|
if (!hoveredPoint) {
|
|
|
|
setHoveredPoint(point);
|
|
|
|
setIsHovered(true)
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
onPointerOut={() => {
|
|
|
|
if (hoveredPoint && hoveredPoint.uuid === point.uuid) {
|
|
|
|
setHoveredPoint(null);
|
|
|
|
}
|
|
|
|
setIsHovered(false)
|
|
|
|
}}
|
2025-05-30 09:03:55 +00:00
|
|
|
userData={point}
|
2025-05-30 04:51:45 +00:00
|
|
|
>
|
|
|
|
<boxGeometry args={boxScale} />
|
|
|
|
<shaderMaterial
|
|
|
|
ref={materialRef}
|
|
|
|
uniforms={uniforms}
|
|
|
|
vertexShader={
|
|
|
|
`
|
|
|
|
varying vec2 vUv;
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
vUv = uv;
|
|
|
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
|
|
}
|
|
|
|
`
|
2025-05-29 12:47:24 +00:00
|
|
|
}
|
2025-05-30 04:51:45 +00:00
|
|
|
fragmentShader={
|
|
|
|
`
|
|
|
|
varying vec2 vUv;
|
2025-05-30 06:04:57 +00:00
|
|
|
uniform vec3 uOuterColor;
|
2025-05-30 04:51:45 +00:00
|
|
|
uniform vec3 uInnerColor;
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
// Define the size of the white square as a proportion of the face
|
|
|
|
float borderThickness = 0.2; // Adjust this value for border thickness
|
|
|
|
if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) {
|
|
|
|
gl_FragColor = vec4(uInnerColor, 1.0); // Inner square
|
|
|
|
} else {
|
2025-05-30 06:04:57 +00:00
|
|
|
gl_FragColor = vec4(uOuterColor, 1.0); // Border
|
2025-05-30 04:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
`
|
2025-05-29 12:47:24 +00:00
|
|
|
}
|
2025-05-30 04:51:45 +00:00
|
|
|
/>
|
|
|
|
</mesh>
|
|
|
|
</DragControls>
|
2025-05-29 12:47:24 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default Point;
|