| import * as React from 'react'; |
| import * as THREE from 'three'; |
| import { useThree } from '@react-three/fiber'; |
| import { Line } from '../../core/Line.js'; |
| import { Html } from '../Html.js'; |
| import { context } from './context.js'; |
|
|
| const vec1 = new THREE.Vector3(); |
| const vec2 = new THREE.Vector3(); |
| const calculateOffset = (clickPoint, normal, rayStart, rayDir) => { |
| const e1 = normal.dot(normal); |
| const e2 = normal.dot(clickPoint) - normal.dot(rayStart); |
| const e3 = normal.dot(rayDir); |
| if (e3 === 0) { |
| return -e2 / e1; |
| } |
| vec1.copy(rayDir).multiplyScalar(e1 / e3).sub(normal); |
| vec2.copy(rayDir).multiplyScalar(e2 / e3).add(rayStart).sub(clickPoint); |
| const offset = -vec1.dot(vec2) / vec1.dot(vec1); |
| return offset; |
| }; |
| const upV = new THREE.Vector3(0, 1, 0); |
| const offsetMatrix = new THREE.Matrix4(); |
| const AxisArrow = ({ |
| direction, |
| axis |
| }) => { |
| const { |
| translation, |
| translationLimits, |
| annotations, |
| annotationsClass, |
| depthTest, |
| scale, |
| lineWidth, |
| fixed, |
| axisColors, |
| hoveredColor, |
| opacity, |
| onDragStart, |
| onDrag, |
| onDragEnd, |
| userData |
| } = React.useContext(context); |
| const camControls = useThree(state => state.controls); |
| const divRef = React.useRef(null); |
| const objRef = React.useRef(null); |
| const clickInfo = React.useRef(null); |
| const offset0 = React.useRef(0); |
| const [isHovered, setIsHovered] = React.useState(false); |
| const onPointerDown = React.useCallback(e => { |
| if (annotations) { |
| divRef.current.innerText = `${translation.current[axis].toFixed(2)}`; |
| divRef.current.style.display = 'block'; |
| } |
| e.stopPropagation(); |
| const rotation = new THREE.Matrix4().extractRotation(objRef.current.matrixWorld); |
| const clickPoint = e.point.clone(); |
| const origin = new THREE.Vector3().setFromMatrixPosition(objRef.current.matrixWorld); |
| const dir = direction.clone().applyMatrix4(rotation).normalize(); |
| clickInfo.current = { |
| clickPoint, |
| dir |
| }; |
| offset0.current = translation.current[axis]; |
| onDragStart({ |
| component: 'Arrow', |
| axis, |
| origin, |
| directions: [dir] |
| }); |
| camControls && (camControls.enabled = false); |
| |
| e.target.setPointerCapture(e.pointerId); |
| }, [annotations, direction, camControls, onDragStart, translation, axis]); |
| const onPointerMove = React.useCallback(e => { |
| e.stopPropagation(); |
| if (!isHovered) setIsHovered(true); |
| if (clickInfo.current) { |
| const { |
| clickPoint, |
| dir |
| } = clickInfo.current; |
| const [min, max] = (translationLimits == null ? void 0 : translationLimits[axis]) || [undefined, undefined]; |
| let offset = calculateOffset(clickPoint, dir, e.ray.origin, e.ray.direction); |
| if (min !== undefined) { |
| offset = Math.max(offset, min - offset0.current); |
| } |
| if (max !== undefined) { |
| offset = Math.min(offset, max - offset0.current); |
| } |
| translation.current[axis] = offset0.current + offset; |
| if (annotations) { |
| divRef.current.innerText = `${translation.current[axis].toFixed(2)}`; |
| } |
| offsetMatrix.makeTranslation(dir.x * offset, dir.y * offset, dir.z * offset); |
| onDrag(offsetMatrix); |
| } |
| }, [annotations, onDrag, isHovered, translation, translationLimits, axis]); |
| const onPointerUp = React.useCallback(e => { |
| if (annotations) { |
| divRef.current.style.display = 'none'; |
| } |
| e.stopPropagation(); |
| clickInfo.current = null; |
| onDragEnd(); |
| camControls && (camControls.enabled = true); |
| |
| e.target.releasePointerCapture(e.pointerId); |
| }, [annotations, camControls, onDragEnd]); |
| const onPointerOut = React.useCallback(e => { |
| e.stopPropagation(); |
| setIsHovered(false); |
| }, []); |
| const { |
| cylinderLength, |
| coneWidth, |
| coneLength, |
| matrixL |
| } = React.useMemo(() => { |
| const coneWidth = fixed ? lineWidth / scale * 1.6 : scale / 20; |
| const coneLength = fixed ? 0.2 : scale / 5; |
| const cylinderLength = fixed ? 1 - coneLength : scale - coneLength; |
| const quaternion = new THREE.Quaternion().setFromUnitVectors(upV, direction.clone().normalize()); |
| const matrixL = new THREE.Matrix4().makeRotationFromQuaternion(quaternion); |
| return { |
| cylinderLength, |
| coneWidth, |
| coneLength, |
| matrixL |
| }; |
| }, [direction, scale, lineWidth, fixed]); |
| const color_ = isHovered ? hoveredColor : axisColors[axis]; |
| return React.createElement("group", { |
| ref: objRef |
| }, React.createElement("group", { |
| matrix: matrixL, |
| matrixAutoUpdate: false, |
| onPointerDown: onPointerDown, |
| onPointerMove: onPointerMove, |
| onPointerUp: onPointerUp, |
| onPointerOut: onPointerOut |
| }, annotations && React.createElement(Html, { |
| position: [0, -coneLength, 0] |
| }, React.createElement("div", { |
| style: { |
| display: 'none', |
| background: '#151520', |
| color: 'white', |
| padding: '6px 8px', |
| borderRadius: 7, |
| whiteSpace: 'nowrap' |
| }, |
| className: annotationsClass, |
| ref: divRef |
| })), React.createElement("mesh", { |
| visible: false, |
| position: [0, (cylinderLength + coneLength) / 2.0, 0], |
| userData: userData |
| }, React.createElement("cylinderGeometry", { |
| args: [coneWidth * 1.4, coneWidth * 1.4, cylinderLength + coneLength, 8, 1] |
| })), React.createElement(Line, { |
| transparent: true, |
| raycast: () => null, |
| depthTest: depthTest, |
| points: [0, 0, 0, 0, cylinderLength, 0], |
| lineWidth: lineWidth, |
| side: THREE.DoubleSide, |
| color: color_, |
| opacity: opacity, |
| polygonOffset: true, |
| renderOrder: 1, |
| polygonOffsetFactor: -10, |
| fog: false |
| }), React.createElement("mesh", { |
| raycast: () => null, |
| position: [0, cylinderLength + coneLength / 2.0, 0], |
| renderOrder: 500 |
| }, React.createElement("coneGeometry", { |
| args: [coneWidth, coneLength, 24, 1] |
| }), React.createElement("meshBasicMaterial", { |
| transparent: true, |
| depthTest: depthTest, |
| color: color_, |
| opacity: opacity, |
| polygonOffset: true, |
| polygonOffsetFactor: -10, |
| fog: false |
| })))); |
| }; |
|
|
| export { AxisArrow, calculateOffset }; |
|
|