128 lines
4.8 KiB
JavaScript
128 lines
4.8 KiB
JavaScript
import _extends from '@babel/runtime/helpers/esm/extends';
|
|
import * as React from 'react';
|
|
import * as THREE from 'three';
|
|
import { useThree } from '@react-three/fiber';
|
|
import { useGesture } from '@use-gesture/react';
|
|
|
|
const initialModelPosition = /* @__PURE__ */new THREE.Vector3();
|
|
const mousePosition2D = /* @__PURE__ */new THREE.Vector2();
|
|
const mousePosition3D = /* @__PURE__ */new THREE.Vector3();
|
|
const dragOffset = /* @__PURE__ */new THREE.Vector3();
|
|
const dragPlaneNormal = /* @__PURE__ */new THREE.Vector3();
|
|
const dragPlane = /* @__PURE__ */new THREE.Plane();
|
|
const DragControls = /*#__PURE__*/React.forwardRef(({
|
|
autoTransform = true,
|
|
matrix,
|
|
axisLock,
|
|
dragLimits,
|
|
onHover,
|
|
onDragStart,
|
|
onDrag,
|
|
onDragEnd,
|
|
children,
|
|
dragConfig,
|
|
...props
|
|
}, fRef) => {
|
|
const defaultControls = useThree(state => state.controls);
|
|
const {
|
|
camera,
|
|
size,
|
|
raycaster,
|
|
invalidate
|
|
} = useThree();
|
|
const ref = React.useRef(null);
|
|
const bind = useGesture({
|
|
onHover: ({
|
|
hovering
|
|
}) => onHover && onHover(hovering !== null && hovering !== void 0 ? hovering : false),
|
|
onDragStart: ({
|
|
event
|
|
}) => {
|
|
if (defaultControls) defaultControls.enabled = false;
|
|
const {
|
|
point
|
|
} = event;
|
|
ref.current.matrix.decompose(initialModelPosition, new THREE.Quaternion(), new THREE.Vector3());
|
|
mousePosition3D.copy(point);
|
|
dragOffset.copy(mousePosition3D).sub(initialModelPosition);
|
|
onDragStart && onDragStart(initialModelPosition);
|
|
invalidate();
|
|
},
|
|
onDrag: ({
|
|
xy: [dragX, dragY],
|
|
intentional
|
|
}) => {
|
|
if (!intentional) return;
|
|
const normalizedMouseX = (dragX - size.left) / size.width * 2 - 1;
|
|
const normalizedMouseY = -((dragY - size.top) / size.height) * 2 + 1;
|
|
mousePosition2D.set(normalizedMouseX, normalizedMouseY);
|
|
raycaster.setFromCamera(mousePosition2D, camera);
|
|
if (!axisLock) {
|
|
camera.getWorldDirection(dragPlaneNormal).negate();
|
|
} else {
|
|
switch (axisLock) {
|
|
case 'x':
|
|
dragPlaneNormal.set(1, 0, 0);
|
|
break;
|
|
case 'y':
|
|
dragPlaneNormal.set(0, 1, 0);
|
|
break;
|
|
case 'z':
|
|
dragPlaneNormal.set(0, 0, 1);
|
|
break;
|
|
}
|
|
}
|
|
dragPlane.setFromNormalAndCoplanarPoint(dragPlaneNormal, mousePosition3D);
|
|
raycaster.ray.intersectPlane(dragPlane, mousePosition3D);
|
|
const previousLocalMatrix = ref.current.matrix.clone();
|
|
const previousWorldMatrix = ref.current.matrixWorld.clone();
|
|
const intendedNewPosition = new THREE.Vector3(mousePosition3D.x - dragOffset.x, mousePosition3D.y - dragOffset.y, mousePosition3D.z - dragOffset.z);
|
|
if (dragLimits) {
|
|
intendedNewPosition.x = dragLimits[0] ? Math.max(Math.min(intendedNewPosition.x, dragLimits[0][1]), dragLimits[0][0]) : intendedNewPosition.x;
|
|
intendedNewPosition.y = dragLimits[1] ? Math.max(Math.min(intendedNewPosition.y, dragLimits[1][1]), dragLimits[1][0]) : intendedNewPosition.y;
|
|
intendedNewPosition.z = dragLimits[2] ? Math.max(Math.min(intendedNewPosition.z, dragLimits[2][1]), dragLimits[2][0]) : intendedNewPosition.z;
|
|
}
|
|
if (autoTransform) {
|
|
ref.current.matrix.setPosition(intendedNewPosition);
|
|
const deltaLocalMatrix = ref.current.matrix.clone().multiply(previousLocalMatrix.invert());
|
|
const deltaWorldMatrix = ref.current.matrix.clone().multiply(previousWorldMatrix.invert());
|
|
onDrag && onDrag(ref.current.matrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
|
|
} else {
|
|
const tempMatrix = new THREE.Matrix4().copy(ref.current.matrix);
|
|
tempMatrix.setPosition(intendedNewPosition);
|
|
const deltaLocalMatrix = tempMatrix.clone().multiply(previousLocalMatrix.invert());
|
|
const deltaWorldMatrix = tempMatrix.clone().multiply(previousWorldMatrix.invert());
|
|
onDrag && onDrag(tempMatrix, deltaLocalMatrix, ref.current.matrixWorld, deltaWorldMatrix);
|
|
}
|
|
invalidate();
|
|
},
|
|
onDragEnd: () => {
|
|
if (defaultControls) defaultControls.enabled = true;
|
|
onDragEnd && onDragEnd();
|
|
invalidate();
|
|
}
|
|
}, {
|
|
drag: {
|
|
filterTaps: true,
|
|
threshold: 1,
|
|
...(typeof dragConfig === 'object' ? dragConfig : {})
|
|
}
|
|
});
|
|
React.useImperativeHandle(fRef, () => ref.current, []);
|
|
React.useLayoutEffect(() => {
|
|
if (!matrix) return;
|
|
|
|
// If the matrix is a real matrix4 it means that the user wants to control the gizmo
|
|
// In that case it should just be set, as a bare prop update would merely copy it
|
|
ref.current.matrix = matrix;
|
|
}, [matrix]);
|
|
return /*#__PURE__*/React.createElement("group", _extends({
|
|
ref: ref
|
|
}, bind(), {
|
|
matrix: matrix,
|
|
matrixAutoUpdate: false
|
|
}, props), children);
|
|
});
|
|
|
|
export { DragControls };
|