@react-three/drei 9.88.17 → 9.89.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("three"),o=require("@react-three/fiber");function r(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(o){if("default"!==o){var r=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,r.get?r:{enumerable:!0,get:function(){return e[o]}})}})),t.default=e,Object.freeze(t)}var a=r(e),n=r(t);const i=e=>e&&e.isOrthographicCamera,c=a.createContext(null);exports.Bounds=function({children:e,damping:t=6,fit:r,clip:s,observe:u,margin:m=1.2,eps:f=.01,onFit:p}){const l=a.useRef(null),{camera:x,invalidate:y,size:z,controls:d}=o.useThree(),h=d,g=a.useRef(p);function M(e,t){return Math.abs(e.x-t.x)<f&&Math.abs(e.y-t.y)<f&&Math.abs(e.z-t.z)<f}function b(e,t,o,r){e.x=n.MathUtils.damp(e.x,t.x,o,r),e.y=n.MathUtils.damp(e.y,t.y,o,r),e.z=n.MathUtils.damp(e.z,t.z,o,r)}g.current=p;const[w]=a.useState((()=>({animating:!1,focus:new n.Vector3,camera:new n.Vector3,zoom:1}))),[v]=a.useState((()=>({focus:new n.Vector3,camera:new n.Vector3,zoom:1}))),[V]=a.useState((()=>new n.Box3)),j=a.useMemo((()=>{function e(){const e=V.getSize(new n.Vector3),t=V.getCenter(new n.Vector3),o=Math.max(e.x,e.y,e.z),r=i(x)?4*o:o/(2*Math.atan(Math.PI*x.fov/360)),a=i(x)?4*o:r/x.aspect,c=m*Math.max(r,a);return{box:V,size:e,center:t,distance:c}}return{getSize:e,refresh(t){if((o=t)&&o.isBox3)V.copy(t);else{const e=t||l.current;if(!e)return this;e.updateWorldMatrix(!0,!0),V.setFromObject(e)}var o;if(V.isEmpty()){const e=x.position.length()||10;V.setFromCenterAndSize(new n.Vector3,new n.Vector3(e,e,e))}if("OrthographicTrackballControls"===(null==h?void 0:h.constructor.name)){const{distance:t}=e(),o=x.position.clone().sub(h.target).normalize().multiplyScalar(t),r=h.target.clone().add(o);x.position.copy(r)}return this},clip(){const{distance:t}=e();return h&&(h.maxDistance=10*t),x.near=t/100,x.far=100*t,x.updateProjectionMatrix(),h&&h.update(),y(),this},to({position:o,target:r}){w.camera.copy(x.position);const{center:a}=e();return v.camera.set(...o),r?v.focus.set(...r):v.focus.copy(a),t?w.animating=!0:x.position.set(...o),this},fit(){w.camera.copy(x.position),h&&w.focus.copy(h.target);const{center:o,distance:r}=e(),a=o.clone().sub(x.position).normalize().multiplyScalar(r);if(v.camera.copy(o).sub(a),v.focus.copy(o),i(x)){w.zoom=x.zoom;let e=0,r=0;const a=[new n.Vector3(V.min.x,V.min.y,V.min.z),new n.Vector3(V.min.x,V.max.y,V.min.z),new n.Vector3(V.min.x,V.min.y,V.max.z),new n.Vector3(V.min.x,V.max.y,V.max.z),new n.Vector3(V.max.x,V.max.y,V.max.z),new n.Vector3(V.max.x,V.max.y,V.min.z),new n.Vector3(V.max.x,V.min.y,V.max.z),new n.Vector3(V.max.x,V.min.y,V.min.z)];o.applyMatrix4(x.matrixWorldInverse);for(const t of a)t.applyMatrix4(x.matrixWorldInverse),e=Math.max(e,Math.abs(t.y-o.y)),r=Math.max(r,Math.abs(t.x-o.x));e*=2,r*=2;const i=(x.top-x.bottom)/e,c=(x.right-x.left)/r;v.zoom=Math.min(i,c)/m,t||(x.zoom=v.zoom,x.updateProjectionMatrix())}return t?w.animating=!0:(x.position.copy(v.camera),x.lookAt(v.focus),h&&(h.target.copy(v.focus),h.update())),g.current&&g.current(this.getSize()),y(),this}}}),[V,x,h,m,t,y]);a.useLayoutEffect((()=>{if(h){const e=()=>w.animating=!1;return h.addEventListener("start",e),()=>h.removeEventListener("start",e)}}),[h]);const O=a.useRef(0);return a.useLayoutEffect((()=>{(u||0==O.current++)&&(j.refresh(),r&&j.fit(),s&&j.clip())}),[z,s,r,u,x,h]),o.useFrame(((e,o)=>{if(w.animating){if(b(w.focus,v.focus,t,o),b(w.camera,v.camera,t,o),w.zoom=n.MathUtils.damp(w.zoom,v.zoom,t,o),x.position.copy(w.camera),i(x)&&(x.zoom=w.zoom,x.updateProjectionMatrix()),h?(h.target.copy(w.focus),h.update()):x.lookAt(w.focus),y(),i(x)&&!(Math.abs(w.zoom-v.zoom)<f))return;if(!i(x)&&!M(w.camera,v.camera))return;if(h&&!M(w.focus,v.focus))return;w.animating=!1}})),a.createElement("group",{ref:l},a.createElement(c.Provider,{value:j},e))},exports.useBounds=function(){return a.useContext(c)};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("three"),r=require("@react-three/fiber");function n(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var o=n(e),c=n(t),a=function(e){return e[e.NONE=0]="NONE",e[e.START=1]="START",e[e.ACTIVE=2]="ACTIVE",e}(a||{});const u=e=>e&&e.isOrthographicCamera,i=e=>1-Math.exp(-5*e)+.007*e,s=o.createContext(null);exports.Bounds=function({children:e,maxDuration:t=1,margin:n=1.2,observe:m,fit:p,clip:l,interpolateFunc:x=i,onFit:d}){const f=o.useRef(null),{camera:h,size:y,invalidate:g}=r.useThree(),v=r.useThree((e=>e.controls)),R=o.useRef(d);R.current=d;const M=o.useRef({camPos:new c.Vector3,camRot:new c.Quaternion,camZoom:1}),P=o.useRef({camPos:void 0,camRot:void 0,camZoom:void 0,camUp:void 0,target:void 0}),w=o.useRef(a.NONE),T=o.useRef(0),[V]=o.useState((()=>new c.Box3)),A=o.useMemo((()=>{function e(){const e=V.getSize(new c.Vector3),t=V.getCenter(new c.Vector3),r=Math.max(e.x,e.y,e.z),o=u(h)?4*r:r/(2*Math.atan(Math.PI*h.fov/360)),a=u(h)?4*r:o/h.aspect,i=n*Math.max(o,a);return{box:V,size:e,center:t,distance:i}}return{getSize:e,refresh(e){if((t=e)&&t.isBox3)V.copy(e);else{const t=e||f.current;if(!t)return this;t.updateWorldMatrix(!0,!0),V.setFromObject(t)}var t;if(V.isEmpty()){const e=h.position.length()||10;V.setFromCenterAndSize(new c.Vector3,new c.Vector3(e,e,e))}return M.current.camPos.copy(h.position),M.current.camRot.copy(h.quaternion),u(h)&&(M.current.camZoom=h.zoom),P.current.camPos=void 0,P.current.camRot=void 0,P.current.camZoom=void 0,P.current.camUp=void 0,P.current.target=void 0,this},reset(){const{center:t,distance:r}=e(),n=h.position.clone().sub(t).normalize();P.current.camPos=t.clone().addScaledVector(n,r),P.current.target=t.clone();const o=(new c.Matrix4).lookAt(P.current.camPos,P.current.target,h.up);return P.current.camRot=(new c.Quaternion).setFromRotationMatrix(o),w.current=a.START,T.current=0,this},moveTo(e){return P.current.camPos=Array.isArray(e)?new c.Vector3(...e):e.clone(),w.current=a.START,T.current=0,this},lookAt({target:e,up:t}){P.current.target=Array.isArray(e)?new c.Vector3(...e):e.clone(),P.current.camUp=t?Array.isArray(t)?new c.Vector3(...t):t.clone():h.up.clone();const r=(new c.Matrix4).lookAt(P.current.camPos||h.position,P.current.target,P.current.camUp);return P.current.camRot=(new c.Quaternion).setFromRotationMatrix(r),w.current=a.START,T.current=0,this},to({position:e,target:t}){return this.moveTo(e).lookAt({target:t})},fit(){if(!u(h))return this;let e=0,t=0;const r=[new c.Vector3(V.min.x,V.min.y,V.min.z),new c.Vector3(V.min.x,V.max.y,V.min.z),new c.Vector3(V.min.x,V.min.y,V.max.z),new c.Vector3(V.min.x,V.max.y,V.max.z),new c.Vector3(V.max.x,V.max.y,V.max.z),new c.Vector3(V.max.x,V.max.y,V.min.z),new c.Vector3(V.max.x,V.min.y,V.max.z),new c.Vector3(V.max.x,V.min.y,V.min.z)],o=P.current.camPos||h.position,i=P.current.target||(null==v?void 0:v.target),s=P.current.camUp||h.up,m=i?(new c.Matrix4).lookAt(o,i,s).setPosition(o).invert():h.matrixWorldInverse;for(const n of r)n.applyMatrix4(m),e=Math.max(e,Math.abs(n.y)),t=Math.max(t,Math.abs(n.x));e*=2,t*=2;const p=(h.top-h.bottom)/e,l=(h.right-h.left)/t;return P.current.camZoom=Math.min(p,l)/n,w.current=a.START,T.current=0,R.current&&R.current(this.getSize()),this},clip(){const{distance:t}=e();return h.near=t/100,h.far=100*t,h.updateProjectionMatrix(),v&&(v.maxDistance=10*t,v.update()),g(),this}}}),[V,h,v,n,g]);o.useLayoutEffect((()=>{if(v){const e=()=>{if(v&&P.current.target&&w.current!==a.NONE){const e=(new c.Vector3).setFromMatrixColumn(h.matrix,2),t=M.current.camPos.distanceTo(v.target),r=(P.current.camPos||M.current.camPos).distanceTo(P.current.target),n=(1-T.current)*t+T.current*r;v.target.copy(h.position).addScaledVector(e,-n),v.update()}w.current=a.NONE};return v.addEventListener("start",e),()=>v.removeEventListener("start",e)}}),[v]);const z=o.useRef(0);return o.useLayoutEffect((()=>{(m||0==z.current++)&&(A.refresh(),p&&A.fit(),l&&A.clip())}),[y,l,p,m,h,v]),r.useFrame(((e,r)=>{if(w.current===a.START)w.current=a.ACTIVE,g();else if(w.current===a.ACTIVE){if(T.current+=r/t,T.current>=1)P.current.camPos&&h.position.copy(P.current.camPos),P.current.camRot&&h.quaternion.copy(P.current.camRot),P.current.camUp&&h.up.copy(P.current.camUp),P.current.camZoom&&u(h)&&(h.zoom=P.current.camZoom),h.updateMatrixWorld(),h.updateProjectionMatrix(),v&&P.current.target&&(v.target.copy(P.current.target),v.update()),w.current=a.NONE;else{const e=x(T.current);P.current.camPos&&h.position.lerpVectors(M.current.camPos,P.current.camPos,e),P.current.camRot&&h.quaternion.slerpQuaternions(M.current.camRot,P.current.camRot,e),P.current.camZoom&&u(h)&&(h.zoom=(1-e)*M.current.camZoom+e*P.current.camZoom),h.updateMatrixWorld(),h.updateProjectionMatrix()}g()}})),o.createElement("group",{ref:f},o.createElement(s.Provider,{value:A},e))},exports.useBounds=function(){return o.useContext(s)};
package/core/Bounds.d.ts CHANGED
@@ -9,21 +9,27 @@ export type SizeProps = {
9
9
  export type BoundsApi = {
10
10
  getSize: () => SizeProps;
11
11
  refresh(object?: THREE.Object3D | THREE.Box3): any;
12
- clip(): any;
13
- fit(): any;
14
- to: ({ position, target }: {
12
+ reset(): any;
13
+ moveTo(position: THREE.Vector3 | [number, number, number]): any;
14
+ lookAt({ target, up, }: {
15
+ target?: THREE.Vector3 | [number, number, number];
16
+ up?: THREE.Vector3 | [number, number, number];
17
+ }): any;
18
+ to({ position, target }: {
15
19
  position: [number, number, number];
16
- target?: [number, number, number];
17
- }) => any;
20
+ target: [number, number, number];
21
+ }): any;
22
+ fit(): any;
23
+ clip(): any;
18
24
  };
19
25
  export type BoundsProps = JSX.IntrinsicElements['group'] & {
20
- damping?: number;
26
+ maxDuration?: number;
27
+ margin?: number;
28
+ observe?: boolean;
21
29
  fit?: boolean;
22
30
  clip?: boolean;
23
- observe?: boolean;
24
- margin?: number;
25
- eps?: number;
31
+ interpolateFunc?: (t: number) => number;
26
32
  onFit?: (data: SizeProps) => void;
27
33
  };
28
- export declare function Bounds({ children, damping, fit, clip, observe, margin, eps, onFit }: BoundsProps): React.JSX.Element;
34
+ export declare function Bounds({ children, maxDuration, margin, observe, fit, clip, interpolateFunc, onFit, }: BoundsProps): React.JSX.Element;
29
35
  export declare function useBounds(): BoundsApi;
package/core/Bounds.js CHANGED
@@ -2,60 +2,66 @@ import * as React from 'react';
2
2
  import * as THREE from 'three';
3
3
  import { useThree, useFrame } from '@react-three/fiber';
4
4
 
5
+ // eslint-disable-next-line no-shadow
6
+ var AnimationState = /*#__PURE__*/function (AnimationState) {
7
+ AnimationState[AnimationState["NONE"] = 0] = "NONE";
8
+ AnimationState[AnimationState["START"] = 1] = "START";
9
+ AnimationState[AnimationState["ACTIVE"] = 2] = "ACTIVE";
10
+ return AnimationState;
11
+ }(AnimationState || {});
5
12
  const isOrthographic = def => def && def.isOrthographicCamera;
6
13
  const isBox3 = def => def && def.isBox3;
7
- const context = /* @__PURE__ */React.createContext(null);
14
+ const interpolateFuncDefault = t => {
15
+ // Imitates the previously used THREE.MathUtils.damp
16
+ return 1 - Math.exp(-5 * t) + 0.007 * t;
17
+ };
18
+ const context = /*#__PURE__*/React.createContext(null);
8
19
  function Bounds({
9
20
  children,
10
- damping = 6,
21
+ maxDuration = 1.0,
22
+ margin = 1.2,
23
+ observe,
11
24
  fit,
12
25
  clip,
13
- observe,
14
- margin = 1.2,
15
- eps = 0.01,
26
+ interpolateFunc = interpolateFuncDefault,
16
27
  onFit
17
28
  }) {
18
29
  const ref = React.useRef(null);
19
30
  const {
20
31
  camera,
21
- invalidate,
22
32
  size,
23
- controls: controlsImpl
33
+ invalidate
24
34
  } = useThree();
25
- const controls = controlsImpl;
35
+ const controls = useThree(state => state.controls);
26
36
  const onFitRef = React.useRef(onFit);
27
37
  onFitRef.current = onFit;
28
- function equals(a, b) {
29
- return Math.abs(a.x - b.x) < eps && Math.abs(a.y - b.y) < eps && Math.abs(a.z - b.z) < eps;
30
- }
31
- function damp(v, t, lambda, delta) {
32
- v.x = THREE.MathUtils.damp(v.x, t.x, lambda, delta);
33
- v.y = THREE.MathUtils.damp(v.y, t.y, lambda, delta);
34
- v.z = THREE.MathUtils.damp(v.z, t.z, lambda, delta);
35
- }
36
- const [current] = React.useState(() => ({
37
- animating: false,
38
- focus: new THREE.Vector3(),
39
- camera: new THREE.Vector3(),
40
- zoom: 1
41
- }));
42
- const [goal] = React.useState(() => ({
43
- focus: new THREE.Vector3(),
44
- camera: new THREE.Vector3(),
45
- zoom: 1
46
- }));
38
+ const origin = React.useRef({
39
+ camPos: new THREE.Vector3(),
40
+ camRot: new THREE.Quaternion(),
41
+ camZoom: 1
42
+ });
43
+ const goal = React.useRef({
44
+ camPos: undefined,
45
+ camRot: undefined,
46
+ camZoom: undefined,
47
+ camUp: undefined,
48
+ target: undefined
49
+ });
50
+ const animationState = React.useRef(AnimationState.NONE);
51
+ const t = React.useRef(0); // represent animation state from 0 to 1
52
+
47
53
  const [box] = React.useState(() => new THREE.Box3());
48
54
  const api = React.useMemo(() => {
49
55
  function getSize() {
50
- const size = box.getSize(new THREE.Vector3());
56
+ const boxSize = box.getSize(new THREE.Vector3());
51
57
  const center = box.getCenter(new THREE.Vector3());
52
- const maxSize = Math.max(size.x, size.y, size.z);
58
+ const maxSize = Math.max(boxSize.x, boxSize.y, boxSize.z);
53
59
  const fitHeightDistance = isOrthographic(camera) ? maxSize * 4 : maxSize / (2 * Math.atan(Math.PI * camera.fov / 360));
54
60
  const fitWidthDistance = isOrthographic(camera) ? maxSize * 4 : fitHeightDistance / camera.aspect;
55
61
  const distance = margin * Math.max(fitHeightDistance, fitWidthDistance);
56
62
  return {
57
63
  box,
58
- size,
64
+ size: boxSize,
59
65
  center,
60
66
  distance
61
67
  };
@@ -73,102 +79,122 @@ function Bounds({
73
79
  const max = camera.position.length() || 10;
74
80
  box.setFromCenterAndSize(new THREE.Vector3(), new THREE.Vector3(max, max, max));
75
81
  }
76
- if ((controls == null ? void 0 : controls.constructor.name) === 'OrthographicTrackballControls') {
77
- // Put camera on a sphere along which it should move
78
- const {
79
- distance
80
- } = getSize();
81
- const direction = camera.position.clone().sub(controls.target).normalize().multiplyScalar(distance);
82
- const newPos = controls.target.clone().add(direction);
83
- camera.position.copy(newPos);
84
- }
82
+ origin.current.camPos.copy(camera.position);
83
+ origin.current.camRot.copy(camera.quaternion);
84
+ isOrthographic(camera) && (origin.current.camZoom = camera.zoom);
85
+ goal.current.camPos = undefined;
86
+ goal.current.camRot = undefined;
87
+ goal.current.camZoom = undefined;
88
+ goal.current.camUp = undefined;
89
+ goal.current.target = undefined;
85
90
  return this;
86
91
  },
87
- clip() {
92
+ reset() {
88
93
  const {
94
+ center,
89
95
  distance
90
96
  } = getSize();
91
- if (controls) controls.maxDistance = distance * 10;
92
- camera.near = distance / 100;
93
- camera.far = distance * 100;
94
- camera.updateProjectionMatrix();
95
- if (controls) controls.update();
96
- invalidate();
97
+ const direction = camera.position.clone().sub(center).normalize();
98
+ goal.current.camPos = center.clone().addScaledVector(direction, distance);
99
+ goal.current.target = center.clone();
100
+ const mCamRot = new THREE.Matrix4().lookAt(goal.current.camPos, goal.current.target, camera.up);
101
+ goal.current.camRot = new THREE.Quaternion().setFromRotationMatrix(mCamRot);
102
+ animationState.current = AnimationState.START;
103
+ t.current = 0;
104
+ return this;
105
+ },
106
+ moveTo(position) {
107
+ goal.current.camPos = Array.isArray(position) ? new THREE.Vector3(...position) : position.clone();
108
+ animationState.current = AnimationState.START;
109
+ t.current = 0;
110
+ return this;
111
+ },
112
+ lookAt({
113
+ target,
114
+ up
115
+ }) {
116
+ goal.current.target = Array.isArray(target) ? new THREE.Vector3(...target) : target.clone();
117
+ if (up) {
118
+ goal.current.camUp = Array.isArray(up) ? new THREE.Vector3(...up) : up.clone();
119
+ } else {
120
+ goal.current.camUp = camera.up.clone();
121
+ }
122
+ const mCamRot = new THREE.Matrix4().lookAt(goal.current.camPos || camera.position, goal.current.target, goal.current.camUp);
123
+ goal.current.camRot = new THREE.Quaternion().setFromRotationMatrix(mCamRot);
124
+ animationState.current = AnimationState.START;
125
+ t.current = 0;
97
126
  return this;
98
127
  },
128
+ /**
129
+ * @deprecated Use moveTo and lookAt instead
130
+ */
99
131
  to({
100
132
  position,
101
133
  target
102
134
  }) {
103
- current.camera.copy(camera.position);
104
- const {
105
- center
106
- } = getSize();
107
- goal.camera.set(...position);
108
- if (target) {
109
- goal.focus.set(...target);
110
- } else {
111
- goal.focus.copy(center);
135
+ return this.moveTo(position).lookAt({
136
+ target
137
+ });
138
+ },
139
+ fit() {
140
+ if (!isOrthographic(camera)) {
141
+ // Fit should only modify zoom now. Thus avoid executing it if the camera isn't orthographic
142
+ return this;
112
143
  }
113
- if (damping) {
114
- current.animating = true;
115
- } else {
116
- camera.position.set(...position);
144
+ let maxHeight = 0,
145
+ maxWidth = 0;
146
+ const vertices = [new THREE.Vector3(box.min.x, box.min.y, box.min.z), new THREE.Vector3(box.min.x, box.max.y, box.min.z), new THREE.Vector3(box.min.x, box.min.y, box.max.z), new THREE.Vector3(box.min.x, box.max.y, box.max.z), new THREE.Vector3(box.max.x, box.max.y, box.max.z), new THREE.Vector3(box.max.x, box.max.y, box.min.z), new THREE.Vector3(box.max.x, box.min.y, box.max.z), new THREE.Vector3(box.max.x, box.min.y, box.min.z)];
147
+
148
+ // Transform the center and each corner to camera space
149
+ const pos = goal.current.camPos || camera.position;
150
+ const target = goal.current.target || (controls == null ? void 0 : controls.target);
151
+ const up = goal.current.camUp || camera.up;
152
+ const mCamWInv = target ? new THREE.Matrix4().lookAt(pos, target, up).setPosition(pos).invert() : camera.matrixWorldInverse;
153
+ for (const v of vertices) {
154
+ v.applyMatrix4(mCamWInv);
155
+ maxHeight = Math.max(maxHeight, Math.abs(v.y));
156
+ maxWidth = Math.max(maxWidth, Math.abs(v.x));
117
157
  }
158
+ maxHeight *= 2;
159
+ maxWidth *= 2;
160
+ const zoomForHeight = (camera.top - camera.bottom) / maxHeight;
161
+ const zoomForWidth = (camera.right - camera.left) / maxWidth;
162
+ goal.current.camZoom = Math.min(zoomForHeight, zoomForWidth) / margin;
163
+ animationState.current = AnimationState.START;
164
+ t.current = 0;
165
+ onFitRef.current && onFitRef.current(this.getSize());
118
166
  return this;
119
167
  },
120
- fit() {
121
- current.camera.copy(camera.position);
122
- if (controls) current.focus.copy(controls.target);
168
+ clip() {
123
169
  const {
124
- center,
125
170
  distance
126
171
  } = getSize();
127
- const direction = center.clone().sub(camera.position).normalize().multiplyScalar(distance);
128
- goal.camera.copy(center).sub(direction);
129
- goal.focus.copy(center);
130
- if (isOrthographic(camera)) {
131
- current.zoom = camera.zoom;
132
- let maxHeight = 0,
133
- maxWidth = 0;
134
- const vertices = [new THREE.Vector3(box.min.x, box.min.y, box.min.z), new THREE.Vector3(box.min.x, box.max.y, box.min.z), new THREE.Vector3(box.min.x, box.min.y, box.max.z), new THREE.Vector3(box.min.x, box.max.y, box.max.z), new THREE.Vector3(box.max.x, box.max.y, box.max.z), new THREE.Vector3(box.max.x, box.max.y, box.min.z), new THREE.Vector3(box.max.x, box.min.y, box.max.z), new THREE.Vector3(box.max.x, box.min.y, box.min.z)];
135
- // Transform the center and each corner to camera space
136
- center.applyMatrix4(camera.matrixWorldInverse);
137
- for (const v of vertices) {
138
- v.applyMatrix4(camera.matrixWorldInverse);
139
- maxHeight = Math.max(maxHeight, Math.abs(v.y - center.y));
140
- maxWidth = Math.max(maxWidth, Math.abs(v.x - center.x));
141
- }
142
- maxHeight *= 2;
143
- maxWidth *= 2;
144
- const zoomForHeight = (camera.top - camera.bottom) / maxHeight;
145
- const zoomForWidth = (camera.right - camera.left) / maxWidth;
146
- goal.zoom = Math.min(zoomForHeight, zoomForWidth) / margin;
147
- if (!damping) {
148
- camera.zoom = goal.zoom;
149
- camera.updateProjectionMatrix();
150
- }
151
- }
152
- if (damping) {
153
- current.animating = true;
154
- } else {
155
- camera.position.copy(goal.camera);
156
- camera.lookAt(goal.focus);
157
- if (controls) {
158
- controls.target.copy(goal.focus);
159
- controls.update();
160
- }
172
+ camera.near = distance / 100;
173
+ camera.far = distance * 100;
174
+ camera.updateProjectionMatrix();
175
+ if (controls) {
176
+ controls.maxDistance = distance * 10;
177
+ controls.update();
161
178
  }
162
- if (onFitRef.current) onFitRef.current(this.getSize());
163
179
  invalidate();
164
180
  return this;
165
181
  }
166
182
  };
167
- }, [box, camera, controls, margin, damping, invalidate]);
183
+ }, [box, camera, controls, margin, invalidate]);
168
184
  React.useLayoutEffect(() => {
169
185
  if (controls) {
170
186
  // Try to prevent drag hijacking
171
- const callback = () => current.animating = false;
187
+ const callback = () => {
188
+ if (controls && goal.current.target && animationState.current !== AnimationState.NONE) {
189
+ const front = new THREE.Vector3().setFromMatrixColumn(camera.matrix, 2);
190
+ const d0 = origin.current.camPos.distanceTo(controls.target);
191
+ const d1 = (goal.current.camPos || origin.current.camPos).distanceTo(goal.current.target);
192
+ const d = (1 - t.current) * d0 + t.current * d1;
193
+ controls.target.copy(camera.position).addScaledVector(front, -d);
194
+ controls.update();
195
+ }
196
+ animationState.current = AnimationState.NONE;
197
+ };
172
198
  controls.addEventListener('start', callback);
173
199
  return () => controls.removeEventListener('start', callback);
174
200
  }
@@ -184,26 +210,33 @@ function Bounds({
184
210
  }
185
211
  }, [size, clip, fit, observe, camera, controls]);
186
212
  useFrame((state, delta) => {
187
- if (current.animating) {
188
- damp(current.focus, goal.focus, damping, delta);
189
- damp(current.camera, goal.camera, damping, delta);
190
- current.zoom = THREE.MathUtils.damp(current.zoom, goal.zoom, damping, delta);
191
- camera.position.copy(current.camera);
192
- if (isOrthographic(camera)) {
193
- camera.zoom = current.zoom;
213
+ // This [additional animation step START] is needed to guarantee that delta used in animation isn't absurdly high (2-3 seconds) which is actually possible if rendering happens on demand...
214
+ if (animationState.current === AnimationState.START) {
215
+ animationState.current = AnimationState.ACTIVE;
216
+ invalidate();
217
+ } else if (animationState.current === AnimationState.ACTIVE) {
218
+ t.current += delta / maxDuration;
219
+ if (t.current >= 1) {
220
+ goal.current.camPos && camera.position.copy(goal.current.camPos);
221
+ goal.current.camRot && camera.quaternion.copy(goal.current.camRot);
222
+ goal.current.camUp && camera.up.copy(goal.current.camUp);
223
+ goal.current.camZoom && isOrthographic(camera) && (camera.zoom = goal.current.camZoom);
224
+ camera.updateMatrixWorld();
194
225
  camera.updateProjectionMatrix();
195
- }
196
- if (!controls) {
197
- camera.lookAt(current.focus);
226
+ if (controls && goal.current.target) {
227
+ controls.target.copy(goal.current.target);
228
+ controls.update();
229
+ }
230
+ animationState.current = AnimationState.NONE;
198
231
  } else {
199
- controls.target.copy(current.focus);
200
- controls.update();
232
+ const k = interpolateFunc(t.current);
233
+ goal.current.camPos && camera.position.lerpVectors(origin.current.camPos, goal.current.camPos, k);
234
+ goal.current.camRot && camera.quaternion.slerpQuaternions(origin.current.camRot, goal.current.camRot, k);
235
+ goal.current.camZoom && isOrthographic(camera) && (camera.zoom = (1 - k) * origin.current.camZoom + k * goal.current.camZoom);
236
+ camera.updateMatrixWorld();
237
+ camera.updateProjectionMatrix();
201
238
  }
202
239
  invalidate();
203
- if (isOrthographic(camera) && !(Math.abs(current.zoom - goal.zoom) < eps)) return;
204
- if (!isOrthographic(camera) && !equals(current.camera, goal.camera)) return;
205
- if (controls && !equals(current.focus, goal.focus)) return;
206
- current.animating = false;
207
240
  }
208
241
  });
209
242
  return /*#__PURE__*/React.createElement("group", {
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),n=require("three"),t=require("react"),r=require("./shaderMaterial.cjs.js"),i=require("@react-three/fiber"),o=require("three-stdlib"),c=require("../helpers/constants.cjs.js");function s(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function a(e){if(e&&e.__esModule)return e;var n=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var r=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,r.get?r:{enumerable:!0,get:function(){return e[t]}})}})),n.default=e,Object.freeze(n)}var l=s(e),u=a(n),d=a(t);const f=r.shaderMaterial({screenspace:!1,color:new u.Color("black"),opacity:1,thickness:.05,size:new u.Vector2},"#include <common>\n #include <morphtarget_pars_vertex>\n #include <skinning_pars_vertex>\n uniform float thickness;\n uniform float screenspace;\n uniform vec2 size;\n void main() {\n #if defined (USE_SKINNING)\n\t #include <beginnormal_vertex>\n #include <morphnormal_vertex>\n #include <skinbase_vertex>\n #include <skinnormal_vertex>\n #include <defaultnormal_vertex>\n #endif\n #include <begin_vertex>\n\t #include <morphtarget_vertex>\n\t #include <skinning_vertex>\n #include <project_vertex>\n vec4 tNormal = vec4(normal, 0.0);\n vec4 tPosition = vec4(transformed, 1.0);\n #ifdef USE_INSTANCING\n tNormal = instanceMatrix * tNormal;\n tPosition = instanceMatrix * tPosition;\n #endif\n if (screenspace == 0.0) {\n vec3 newPosition = tPosition.xyz + tNormal.xyz * thickness;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); \n } else {\n vec4 clipPosition = projectionMatrix * modelViewMatrix * tPosition;\n vec4 clipNormal = projectionMatrix * modelViewMatrix * tNormal;\n vec2 offset = normalize(clipNormal.xy) * thickness / size * clipPosition.w * 2.0;\n clipPosition.xy += offset;\n gl_Position = clipPosition;\n }\n }",`uniform vec3 color;\n uniform float opacity;\n void main(){\n gl_FragColor = vec4(color, opacity);\n #include <tonemapping_fragment>\n #include <${c.version>=154?"colorspace_fragment":"encodings_fragment"}>\n }`);exports.Outlines=function({color:e="black",opacity:n=1,transparent:t=!1,screenspace:r=!1,toneMapped:c=!0,polygonOffset:s=!1,polygonOffsetFactor:a=0,renderOrder:p=0,thickness:m=.05,angle:g=Math.PI,...v}){const x=d.useRef(null),[y]=d.useState((()=>new f({side:u.BackSide}))),{gl:h}=i.useThree(),_=h.getDrawingBufferSize(new u.Vector2);d.useMemo((()=>i.extend({OutlinesMaterial:f})),[]);const M=d.useRef(0),b=d.useRef(null);return d.useLayoutEffect((()=>{const e=x.current,n=e.parent;if(n&&n.geometry&&(M.current!==g||b.current!==n.geometry)){M.current=g,b.current=n.geometry;let t=e.children[0];t&&(g&&t.geometry.dispose(),e.remove(t)),n.skeleton?(t=new u.SkinnedMesh,t.material=y,t.bind(n.skeleton,n.bindMatrix),e.add(t)):n.isInstancedMesh?(t=new u.InstancedMesh(n.geometry,y,n.count),t.instanceMatrix=n.instanceMatrix,e.add(t)):(t=new u.Mesh,t.material=y,e.add(t)),t.geometry=g?o.toCreasedNormals(n.geometry,g):n.geometry}})),d.useLayoutEffect((()=>{const o=x.current.children[0];o&&(o.renderOrder=p,i.applyProps(o.material,{transparent:t,thickness:m,color:e,opacity:n,size:_,screenspace:r,toneMapped:c,polygonOffset:s,polygonOffsetFactor:a}))})),d.useEffect((()=>()=>{const e=x.current;let n=e.children[0];n&&(g&&n.geometry.dispose(),e.remove(n))}),[]),d.createElement("group",l.default({ref:x},v))};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),n=require("three"),t=require("react"),r=require("./shaderMaterial.cjs.js"),i=require("@react-three/fiber"),o=require("three-stdlib"),c=require("../helpers/constants.cjs.js");function s(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function a(e){if(e&&e.__esModule)return e;var n=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var r=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,r.get?r:{enumerable:!0,get:function(){return e[t]}})}})),n.default=e,Object.freeze(n)}var l=s(e),u=a(n),d=a(t);const f=r.shaderMaterial({screenspace:!1,color:new u.Color("black"),opacity:1,thickness:.05,size:new u.Vector2},"#include <common>\n #include <morphtarget_pars_vertex>\n #include <skinning_pars_vertex>\n uniform float thickness;\n uniform float screenspace;\n uniform vec2 size;\n void main() {\n #if defined (USE_SKINNING)\n\t #include <beginnormal_vertex>\n #include <morphnormal_vertex>\n #include <skinbase_vertex>\n #include <skinnormal_vertex>\n #include <defaultnormal_vertex>\n #endif\n #include <begin_vertex>\n\t #include <morphtarget_vertex>\n\t #include <skinning_vertex>\n #include <project_vertex>\n vec4 tNormal = vec4(normal, 0.0);\n vec4 tPosition = vec4(transformed, 1.0);\n #ifdef USE_INSTANCING\n tNormal = instanceMatrix * tNormal;\n tPosition = instanceMatrix * tPosition;\n #endif\n if (screenspace == 0.0) {\n vec3 newPosition = tPosition.xyz + tNormal.xyz * thickness;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); \n } else {\n vec4 clipPosition = projectionMatrix * modelViewMatrix * tPosition;\n vec4 clipNormal = projectionMatrix * modelViewMatrix * tNormal;\n vec2 offset = normalize(clipNormal.xy) * thickness / size * clipPosition.w * 2.0;\n clipPosition.xy += offset;\n gl_Position = clipPosition;\n }\n }",`uniform vec3 color;\n uniform float opacity;\n void main(){\n gl_FragColor = vec4(color, opacity);\n #include <tonemapping_fragment>\n #include <${c.version>=154?"colorspace_fragment":"encodings_fragment"}>\n }`);exports.Outlines=function({color:e="black",opacity:n=1,transparent:t=!1,screenspace:r=!1,toneMapped:c=!0,polygonOffset:s=!1,polygonOffsetFactor:a=0,renderOrder:p=0,thickness:m=.05,angle:g=Math.PI,...v}){const x=d.useRef(),[y]=d.useState((()=>new f({side:u.BackSide}))),{gl:h}=i.useThree(),_=h.getDrawingBufferSize(new u.Vector2);d.useMemo((()=>i.extend({OutlinesMaterial:f})),[]);const M=d.useRef(0),b=d.useRef();return d.useLayoutEffect((()=>{const e=x.current;if(!e)return;const n=e.parent;if(n&&n.geometry&&(M.current!==g||b.current!==n.geometry)){M.current=g,b.current=n.geometry;let t=e.children[0];t&&(g&&t.geometry.dispose(),e.remove(t)),n.skeleton?(t=new u.SkinnedMesh,t.material=y,t.bind(n.skeleton,n.bindMatrix),e.add(t)):n.isInstancedMesh?(t=new u.InstancedMesh(n.geometry,y,n.count),t.instanceMatrix=n.instanceMatrix,e.add(t)):(t=new u.Mesh,t.material=y,e.add(t)),t.geometry=g?o.toCreasedNormals(n.geometry,g):n.geometry}})),d.useLayoutEffect((()=>{const o=x.current;if(!o)return;const l=o.children[0];l&&(l.renderOrder=p,i.applyProps(l.material,{transparent:t,thickness:m,color:e,opacity:n,size:_,screenspace:r,toneMapped:c,polygonOffset:s,polygonOffsetFactor:a}))})),d.useEffect((()=>()=>{const e=x.current;if(!e)return;const n=e.children[0];n&&(g&&n.geometry.dispose(),e.remove(n))}),[]),d.createElement("group",l.default({ref:x},v))};
package/core/Outlines.js CHANGED
@@ -66,7 +66,7 @@ function Outlines({
66
66
  angle = Math.PI,
67
67
  ...props
68
68
  }) {
69
- const ref = React.useRef(null);
69
+ const ref = React.useRef();
70
70
  const [material] = React.useState(() => new OutlinesMaterial({
71
71
  side: THREE.BackSide
72
72
  }));
@@ -78,9 +78,10 @@ function Outlines({
78
78
  OutlinesMaterial
79
79
  }), []);
80
80
  const oldAngle = React.useRef(0);
81
- const oldGeometry = React.useRef(null);
81
+ const oldGeometry = React.useRef();
82
82
  React.useLayoutEffect(() => {
83
83
  const group = ref.current;
84
+ if (!group) return;
84
85
  const parent = group.parent;
85
86
  if (parent && parent.geometry) {
86
87
  if (oldAngle.current !== angle || oldGeometry.current !== parent.geometry) {
@@ -113,6 +114,7 @@ function Outlines({
113
114
  });
114
115
  React.useLayoutEffect(() => {
115
116
  const group = ref.current;
117
+ if (!group) return;
116
118
  const mesh = group.children[0];
117
119
  if (mesh) {
118
120
  mesh.renderOrder = renderOrder;
@@ -133,7 +135,8 @@ function Outlines({
133
135
  return () => {
134
136
  // Dispose everything on unmount
135
137
  const group = ref.current;
136
- let mesh = group.children[0];
138
+ if (!group) return;
139
+ const mesh = group.children[0];
137
140
  if (mesh) {
138
141
  if (angle) mesh.geometry.dispose();
139
142
  group.remove(mesh);
package/core/useGLTF.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { GLTFLoader } from 'three-stdlib';
2
- export declare function useGLTF<T extends string | string[]>(path: T, useDraco?: boolean | string, useMeshOpt?: boolean, extendLoader?: (loader: GLTFLoader) => void): T extends any[] ? import("three-stdlib").GLTF[] : import("three-stdlib").GLTF;
1
+ import { type GLTF, GLTFLoader } from 'three-stdlib';
2
+ import { type ObjectMap } from '@react-three/fiber';
3
+ export declare function useGLTF<T extends string | string[]>(path: T, useDraco?: boolean | string, useMeshOpt?: boolean, extendLoader?: (loader: GLTFLoader) => void): T extends any[] ? (GLTF & ObjectMap)[] : GLTF & ObjectMap;
3
4
  export declare namespace useGLTF {
4
5
  var preload: (path: string | string[], useDraco?: string | boolean, useMeshOpt?: boolean, extendLoader?: ((loader: GLTFLoader) => void) | undefined) => undefined;
5
6
  var clear: (input: string | string[]) => void;
package/core/useGLTF.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { GLTFLoader, DRACOLoader, MeshoptDecoder } from 'three-stdlib';
2
2
  import { useLoader } from '@react-three/fiber';
3
3
 
4
- // @ts-ignore
5
4
  let dracoLoader = null;
6
5
  let decoderPath = 'https://www.gstatic.com/draco/versioned/decoders/1.5.5/';
7
6
  function extensions(useDraco, useMeshopt, extendLoader) {
@@ -22,8 +21,7 @@ function extensions(useDraco, useMeshopt, extendLoader) {
22
21
  };
23
22
  }
24
23
  function useGLTF(path, useDraco = true, useMeshOpt = true, extendLoader) {
25
- const gltf = useLoader(GLTFLoader, path, extensions(useDraco, useMeshOpt, extendLoader));
26
- return gltf;
24
+ return useLoader(GLTFLoader, path, extensions(useDraco, useMeshOpt, extendLoader));
27
25
  }
28
26
  useGLTF.preload = (path, useDraco = true, useMeshOpt = true, extendLoader) => useLoader.preload(GLTFLoader, path, extensions(useDraco, useMeshOpt, extendLoader));
29
27
  useGLTF.clear = input => useLoader.clear(GLTFLoader, input);