Enhance builder functionality by implementing drag-and-drop for lines and points, adding hover effects, and improving wall distance display in the UI.
This commit is contained in:
@@ -1,13 +1,26 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import * as Constants from '../../../types/world/worldConstants';
|
||||
import { Tube } from '@react-three/drei';
|
||||
import { DragControls, Tube } from '@react-three/drei';
|
||||
import { useToolMode } from '../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import { useWallStore } from '../../../store/builder/useWallStore';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
|
||||
interface LineProps {
|
||||
points: [Point, Point];
|
||||
}
|
||||
|
||||
function Line({ points }: Readonly<LineProps>) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const { raycaster, camera, pointer, gl } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const [isDeletable, setIsDeletable] = useState(false);
|
||||
const { toolMode } = useToolMode();
|
||||
const { removeWallByPoints, setPosition } = useWallStore();
|
||||
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||
const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore();
|
||||
|
||||
const path = useMemo(() => {
|
||||
const [start, end] = points.map(p => new THREE.Vector3(...p.position));
|
||||
return new THREE.LineCurve3(start, end);
|
||||
@@ -44,16 +57,119 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (toolMode === '2D-Delete') {
|
||||
if (isHovered && !hoveredPoint) {
|
||||
setIsDeletable(true);
|
||||
} else {
|
||||
setIsDeletable(false);
|
||||
}
|
||||
} else {
|
||||
setIsDeletable(false);
|
||||
}
|
||||
}, [isHovered, colors.defaultLineColor, colors.defaultDeleteColor, toolMode, hoveredPoint]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hoveredLine && (hoveredLine[0].pointUuid !== points[0].pointUuid || hoveredLine[1].pointUuid !== points[1].pointUuid)) {
|
||||
setIsHovered(false);
|
||||
}
|
||||
}, [hoveredLine])
|
||||
|
||||
const handlePointClick = (points: [Point, Point]) => {
|
||||
if (toolMode === '2D-Delete') {
|
||||
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
||||
removeWallByPoints(points);
|
||||
}
|
||||
gl.domElement.style.cursor = 'default';
|
||||
}
|
||||
}
|
||||
|
||||
const handleDrag = (points: [Point, Point]) => {
|
||||
if (toolMode === 'move' && isHovered && dragOffset) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
if (hit) {
|
||||
gl.domElement.style.cursor = 'move';
|
||||
const positionWithOffset = new THREE.Vector3().addVectors(hit, dragOffset);
|
||||
|
||||
const start = new THREE.Vector3(...points[0].position);
|
||||
const end = new THREE.Vector3(...points[1].position);
|
||||
const midPoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
|
||||
|
||||
const delta = new THREE.Vector3().subVectors(positionWithOffset, midPoint);
|
||||
|
||||
const newStart = new THREE.Vector3().addVectors(start, delta);
|
||||
const newEnd = new THREE.Vector3().addVectors(end, delta);
|
||||
|
||||
setPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
|
||||
setPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragStart = (points: [Point, Point]) => {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
if (hit && !hoveredPoint) {
|
||||
const start = new THREE.Vector3(...points[0].position);
|
||||
const end = new THREE.Vector3(...points[1].position);
|
||||
const midPoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
|
||||
|
||||
const offset = new THREE.Vector3().subVectors(midPoint, hit);
|
||||
setDragOffset(offset);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragEnd = (points: [Point, Point]) => {
|
||||
gl.domElement.style.cursor = 'default';
|
||||
setDragOffset(null);
|
||||
if (toolMode !== 'move') return;
|
||||
if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
|
||||
// console.log('Wall after drag: ', points);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Tube
|
||||
name={`${points[0].pointType}-Line`}
|
||||
key={`${points[0].pointUuid}-${points[1].pointUuid}`}
|
||||
uuid={`${points[0].pointUuid}-${points[1].pointUuid}`}
|
||||
userData={{ points, path }}
|
||||
args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
|
||||
<DragControls
|
||||
axisLock="y"
|
||||
autoTransform={false}
|
||||
onDragStart={() => handleDragStart(points)}
|
||||
onDrag={() => handleDrag(points)}
|
||||
onDragEnd={() => handleDragEnd(points)}
|
||||
>
|
||||
<meshStandardMaterial color={colors.defaultLineColor} />
|
||||
</Tube>
|
||||
<Tube
|
||||
name={`${points[0].pointType}-Line`}
|
||||
key={`${points[0].pointUuid}-${points[1].pointUuid}`}
|
||||
uuid={`${points[0].pointUuid}-${points[1].pointUuid}`}
|
||||
userData={{ points, path }}
|
||||
args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
|
||||
onClick={() => {
|
||||
handlePointClick(points);
|
||||
}}
|
||||
onPointerOver={() => {
|
||||
if (!hoveredLine) {
|
||||
setHoveredLine(points);
|
||||
setIsHovered(true)
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
gl.domElement.style.cursor = 'pointer';
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerOut={() => {
|
||||
if (hoveredLine) {
|
||||
setHoveredLine(null);
|
||||
gl.domElement.style.cursor = 'default';
|
||||
}
|
||||
setIsHovered(false)
|
||||
}}
|
||||
>
|
||||
<meshStandardMaterial color={isDeletable ? colors.defaultDeleteColor : colors.defaultLineColor} />
|
||||
</Tube>
|
||||
</DragControls >
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user