File size: 4,880 Bytes
bf237c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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 };