@react-three/drei 9.119.0 → 9.120.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/drei",
3
- "version": "9.119.0",
3
+ "version": "9.120.0",
4
4
  "private": false,
5
5
  "description": "useful add-ons for react-three-fiber",
6
6
  "keywords": [
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),t=require("three"),r=require("react"),a=require("@react-three/fiber"),o=require("maath"),n=require("suspend-react"),c=require("../core/VideoTexture.cjs.js"),u=require("./Facemesh.cjs.js"),s=require("./FaceLandmarker.cjs.js");function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function l(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var a=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,a.get?a:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}require("hls.js"),require("../core/Line.cjs.js"),require("three-stdlib");var d=i(e),f=l(t),p=l(r);function m(e,t){return e.clone().add(t).multiplyScalar(.5)}function v(e,t,r){const a=e.localToWorld(t);return r.worldToLocal(a)}const b=r.createContext({}),y=r.forwardRef((({camera:e,autostart:t=!0,webcam:n=!0,webcamVideoTextureSrc:c,manualUpdate:i=!1,manualDetect:l=!1,onVideoFrame:y,smoothTime:h=.25,offset:x=!0,offsetScalar:T=80,eyes:j=!1,eyesAsOrigin:E=!0,depth:V=.15,debug:g=!1,facemesh:k,makeDefault:F},R)=>{var S,q;const O=a.useThree((e=>e.scene)),C=a.useThree((e=>e.camera)),M=a.useThree((e=>e.set)),D=a.useThree((e=>e.get)),A=e||C,L=r.useRef(null),P=r.useRef(null),[I]=r.useState((()=>new f.Object3D)),[_]=r.useState((()=>new f.Vector3)),[B]=r.useState((()=>new f.Vector3)),[H]=r.useState((()=>new f.Vector3)),[W]=r.useState((()=>new f.Vector3)),z=r.useCallback((()=>{I.parent=A.parent;const e=P.current;if(e){const{outerRef:t,eyeRightRef:r,eyeLeftRef:a}=e;if(r.current&&a.current){const{irisDirRef:e}=r.current,{irisDirRef:o}=a.current;e.current&&o.current&&t.current&&(_.copy(v(e.current,new f.Vector3(0,0,0),t.current)),B.copy(v(o.current,new f.Vector3(0,0,0),t.current)),I.position.copy(v(t.current,m(_,B),A.parent||O)),H.copy(v(e.current,new f.Vector3(0,0,1),t.current)),W.copy(v(o.current,new f.Vector3(0,0,1),t.current)),I.lookAt(t.current.localToWorld(m(H,W))))}else t.current&&(I.position.copy(v(t.current,new f.Vector3(0,0,0),A.parent||O)),I.lookAt(t.current.localToWorld(new f.Vector3(0,0,1))))}return I}),[A,B,W,_,H,O,I]),[U]=r.useState((()=>new f.Object3D)),G=r.useCallback((function(e,t){if(A){var r;if(null!==(r=t)&&void 0!==r||(t=z()),h>0){const r=1e-9;o.easing.damp3(U.position,t.position,h,e,void 0,void 0,r),o.easing.dampE(U.rotation,t.rotation,h,e,void 0,void 0,r)}else U.position.copy(t.position),U.rotation.copy(t.rotation);A.position.copy(U.position),A.rotation.copy(U.rotation)}}),[A,z,h,U.position,U.rotation]),[J,K]=r.useState(),N=s.useFaceLandmarker(),Q=r.useCallback(((e,t)=>{const r=null==N?void 0:N.detectForVideo(e,t);return K(r),r}),[N]);a.useFrame(((e,t)=>{i||G(t)}));const X=r.useMemo((()=>Object.assign(Object.create(f.EventDispatcher.prototype),{detect:Q,computeTarget:z,update:G,facemeshApiRef:P,webcamApiRef:L,play:()=>{var e;null==(e=L.current)||null==(e=e.videoTextureApiRef.current)||e.texture.source.data.play()},pause:()=>{var e;null==(e=L.current)||null==(e=e.videoTextureApiRef.current)||e.texture.source.data.pause()}})),[Q,z,G]);r.useImperativeHandle(R,(()=>X),[X]),r.useEffect((()=>{const e=e=>{l||Q(e.texture.source.data,e.time),y&&y(e)};return X.addEventListener("videoFrame",e),()=>{X.removeEventListener("videoFrame",e)}}),[X,Q,N,l,y]),r.useEffect((()=>{if(F){const e=D().controls;return M({controls:X}),()=>M({controls:e})}}),[F,X,D,M]);const Y=null==J?void 0:J.faceLandmarks[0],Z=null==J||null==(S=J.facialTransformationMatrixes)?void 0:S[0],$=null==J||null==(q=J.faceBlendshapes)?void 0:q[0];return p.createElement(b.Provider,{value:X},n&&p.createElement(r.Suspense,{fallback:null},p.createElement(w,{ref:L,autostart:t,videoTextureSrc:c})),p.createElement(u.Facemesh,d.default({ref:P},k,{points:Y,depth:V,facialTransformationMatrix:Z,faceBlendshapes:$,eyes:j,eyesAsOrigin:E,offset:x,offsetScalar:T,debug:g,"rotation-z":Math.PI,visible:g}),p.createElement("meshBasicMaterial",{side:f.DoubleSide})))})),h=()=>r.useContext(b),w=r.forwardRef((({videoTextureSrc:e,autostart:t=!0},a)=>{const o=r.useRef(null),c=h(),u=n.suspend((async()=>e?Promise.resolve(null):await navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:"user"}})),[e]);r.useEffect((()=>(c.dispatchEvent({type:"stream",stream:u}),()=>{null==u||u.getTracks().forEach((e=>e.stop())),n.clear([e])})),[u,c,e]);const s=r.useMemo((()=>({videoTextureApiRef:o})),[]);return r.useImperativeHandle(a,(()=>s),[s]),p.createElement(r.Suspense,{fallback:null},p.createElement(x,{ref:o,src:e||u,start:t}))})),x=r.forwardRef((({src:e,start:t},a)=>{const o=c.useVideoTexture(e,{start:t}),n=o.source.data,u=h(),s=r.useCallback((e=>{u.dispatchEvent({type:"videoFrame",texture:o,time:e})}),[o,u]);T(n,s);const i=r.useMemo((()=>({texture:o})),[o]);return r.useImperativeHandle(a,(()=>i),[i]),p.createElement(p.Fragment,null)})),T=(e,t)=>{r.useEffect((()=>{if(!e||!e.requestVideoFrameCallback)return;let r;return e.requestVideoFrameCallback((function a(...o){t(...o),r=e.requestVideoFrameCallback(a)})),()=>e.cancelVideoFrameCallback(r)}),[e,t])};exports.FaceControls=y,exports.useFaceControls=h;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),t=require("three"),r=require("react"),o=require("@react-three/fiber"),n=require("maath"),c=require("./Facemesh.cjs.js"),a=require("./FaceLandmarker.cjs.js"),s=require("./WebcamVideoTexture.cjs.js"),u=require("../core/VideoTexture.cjs.js");function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function l(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}require("../core/Line.cjs.js"),require("three-stdlib"),require("suspend-react"),require("hls.js");var f=i(e),d=l(t),p=l(r);function m(e,t){return e.clone().add(t).multiplyScalar(.5)}function b(e,t,r){const o=e.localToWorld(t);return r.worldToLocal(o)}const h=r.createContext({}),y=r.forwardRef((({camera:e,videoTexture:t={start:!0},manualDetect:i=!1,faceLandmarkerResult:l,manualUpdate:y=!1,makeDefault:j,smoothTime:v=.25,offset:T=!0,offsetScalar:V=80,eyes:w=!1,eyesAsOrigin:g=!0,depth:q=.15,debug:x=!1,facemesh:O},k)=>{var S,R;const E=o.useThree((e=>e.scene)),D=o.useThree((e=>e.camera)),F=o.useThree((e=>e.set)),C=o.useThree((e=>e.get)),L=e||D,M=r.useRef(null),[A]=r.useState((()=>new d.Object3D)),[P]=r.useState((()=>new d.Vector3)),[W]=r.useState((()=>new d.Vector3)),[_]=r.useState((()=>new d.Vector3)),[B]=r.useState((()=>new d.Vector3)),z=r.useCallback((()=>{A.parent=L.parent;const e=M.current;if(e){const{outerRef:t,eyeRightRef:r,eyeLeftRef:o}=e;if(r.current&&o.current){const{irisDirRef:e}=r.current,{irisDirRef:n}=o.current;e.current&&n.current&&t.current&&(P.copy(b(e.current,new d.Vector3(0,0,0),t.current)),W.copy(b(n.current,new d.Vector3(0,0,0),t.current)),A.position.copy(b(t.current,m(P,W),L.parent||E)),_.copy(b(e.current,new d.Vector3(0,0,1),t.current)),B.copy(b(n.current,new d.Vector3(0,0,1),t.current)),A.lookAt(t.current.localToWorld(m(_,B))))}else t.current&&(A.position.copy(b(t.current,new d.Vector3(0,0,0),L.parent||E)),A.lookAt(t.current.localToWorld(new d.Vector3(0,0,1))))}return A}),[L,W,B,P,_,E,A]),[I]=r.useState((()=>new d.Object3D)),H=r.useCallback((function(e,t){if(L){var r;if(null!==(r=t)&&void 0!==r||(t=z()),v>0){const r=1e-9;n.easing.damp3(I.position,t.position,v,e,void 0,void 0,r),n.easing.dampE(I.rotation,t.rotation,v,e,void 0,void 0,r)}else I.position.copy(t.position),I.rotation.copy(t.rotation);L.position.copy(I.position),L.rotation.copy(I.rotation)}}),[L,z,v,I.position,I.rotation]);o.useFrame(((e,t)=>{y||H(t)}));const U=r.useRef(null),[G,J]=r.useState(),K=a.useFaceLandmarker(),N=r.useCallback(((e,t)=>{const r=U.current;if(!r)return;const o=r.source.data,n=null==K?void 0:K.detectForVideo(o,e);J(n)}),[K]),Q=r.useMemo((()=>Object.assign(Object.create(d.EventDispatcher.prototype),{computeTarget:z,update:H,facemeshApiRef:M})),[z,H]);r.useImperativeHandle(k,(()=>Q),[Q]),r.useEffect((()=>{if(j){const e=C().controls;return F({controls:Q}),()=>F({controls:e})}}),[j,Q,C,F]);const X=null!=l?l:G,Y=null==X?void 0:X.faceLandmarks[0],Z=null==X||null==(S=X.facialTransformationMatrixes)?void 0:S[0],$=null==X||null==(R=X.faceBlendshapes)?void 0:R[0],ee={onVideoFrame:N,...t};return p.createElement(h.Provider,{value:Q},!i&&p.createElement(r.Suspense,{fallback:null},"src"in ee?p.createElement(u.VideoTexture,f.default({ref:U},ee)):p.createElement(s.WebcamVideoTexture,f.default({ref:U},ee))),p.createElement(c.Facemesh,f.default({ref:M},O,{points:Y,depth:q,facialTransformationMatrix:Z,faceBlendshapes:$,eyes:w,eyesAsOrigin:g,offset:T,offsetScalar:V,debug:x,"rotation-z":Math.PI,visible:x}),p.createElement("meshBasicMaterial",{side:d.DoubleSide})))}));exports.FaceControls=y,exports.useFaceControls=()=>r.useContext(h);
@@ -2,17 +2,14 @@ import * as THREE from 'three';
2
2
  import * as React from 'react';
3
3
  import { RefObject } from 'react';
4
4
  import type { FaceLandmarkerResult } from '@mediapipe/tasks-vision';
5
- import { useVideoTexture } from '../core/VideoTexture';
5
+ import { VideoTextureProps } from '..';
6
6
  import { FacemeshApi, FacemeshProps } from './Facemesh';
7
- type VideoTextureSrc = Parameters<typeof useVideoTexture>[0];
8
7
  export type FaceControlsProps = {
9
8
  camera?: THREE.Camera;
10
- autostart?: boolean;
11
- webcam?: boolean;
12
- webcamVideoTextureSrc?: VideoTextureSrc;
13
- manualUpdate?: boolean;
9
+ videoTexture: VideoTextureProps;
14
10
  manualDetect?: boolean;
15
- onVideoFrame?: (e: THREE.Event) => void;
11
+ faceLandmarkerResult?: FaceLandmarkerResult;
12
+ manualUpdate?: boolean;
16
13
  makeDefault?: boolean;
17
14
  smoothTime?: number;
18
15
  offset?: boolean;
@@ -24,20 +21,9 @@ export type FaceControlsProps = {
24
21
  facemesh?: FacemeshProps;
25
22
  };
26
23
  export type FaceControlsApi = THREE.EventDispatcher & {
27
- detect: (video: HTMLVideoElement, time: number) => FaceLandmarkerResult | undefined;
28
24
  computeTarget: () => THREE.Object3D;
29
25
  update: (delta: number, target?: THREE.Object3D) => void;
30
26
  facemeshApiRef: RefObject<FacemeshApi>;
31
- webcamApiRef: RefObject<WebcamApi>;
32
- play: () => void;
33
- pause: () => void;
34
27
  };
35
28
  export declare const FaceControls: React.ForwardRefExoticComponent<FaceControlsProps & React.RefAttributes<FaceControlsApi>>;
36
29
  export declare const useFaceControls: () => FaceControlsApi;
37
- type WebcamApi = {
38
- videoTextureApiRef: RefObject<VideoTextureApi>;
39
- };
40
- type VideoTextureApi = {
41
- texture: THREE.VideoTexture;
42
- };
43
- export {};
@@ -4,12 +4,10 @@ import * as React from 'react';
4
4
  import { forwardRef, useRef, useState, useCallback, useMemo, useImperativeHandle, useEffect, Suspense, useContext, createContext } from 'react';
5
5
  import { useThree, useFrame } from '@react-three/fiber';
6
6
  import { easing } from 'maath';
7
- import { suspend, clear } from 'suspend-react';
8
- import { useVideoTexture } from '../core/VideoTexture.js';
9
7
  import { Facemesh } from './Facemesh.js';
10
8
  import { useFaceLandmarker } from './FaceLandmarker.js';
11
-
12
- // useVideoTexture 1st arg `src` type
9
+ import { WebcamVideoTexture } from './WebcamVideoTexture.js';
10
+ import { VideoTexture } from '../core/VideoTexture.js';
13
11
 
14
12
  function mean(v1, v2) {
15
13
  return v1.clone().add(v2).multiplyScalar(0.5);
@@ -38,12 +36,13 @@ const FaceControlsContext = /* @__PURE__ */createContext({});
38
36
 
39
37
  const FaceControls = /* @__PURE__ */forwardRef(({
40
38
  camera,
41
- autostart = true,
42
- webcam = true,
43
- webcamVideoTextureSrc,
44
- manualUpdate = false,
39
+ videoTexture = {
40
+ start: true
41
+ },
45
42
  manualDetect = false,
46
- onVideoFrame,
43
+ faceLandmarkerResult,
44
+ manualUpdate = false,
45
+ makeDefault,
47
46
  smoothTime = 0.25,
48
47
  offset = true,
49
48
  offsetScalar = 80,
@@ -51,16 +50,14 @@ const FaceControls = /* @__PURE__ */forwardRef(({
51
50
  eyesAsOrigin = true,
52
51
  depth = 0.15,
53
52
  debug = false,
54
- facemesh,
55
- makeDefault
53
+ facemesh
56
54
  }, fref) => {
57
- var _faces$facialTransfor, _faces$faceBlendshape;
55
+ var _result$facialTransfo, _result$faceBlendshap;
58
56
  const scene = useThree(state => state.scene);
59
57
  const defaultCamera = useThree(state => state.camera);
60
58
  const set = useThree(state => state.set);
61
59
  const get = useThree(state => state.get);
62
60
  const explCamera = camera || defaultCamera;
63
- const webcamApiRef = useRef(null);
64
61
  const facemeshApiRef = useRef(null);
65
62
 
66
63
  //
@@ -148,59 +145,41 @@ const FaceControls = /* @__PURE__ */forwardRef(({
148
145
  explCamera.rotation.copy(current.rotation);
149
146
  }
150
147
  }, [explCamera, computeTarget, smoothTime, current.position, current.rotation]);
148
+ useFrame((_, delta) => {
149
+ if (manualUpdate) return;
150
+ update(delta);
151
+ });
151
152
 
152
153
  //
153
- // detect()
154
+ // onVideoFrame (only used if !manualDetect)
154
155
  //
155
156
 
156
- const [faces, setFaces] = useState();
157
+ const videoTextureRef = useRef(null);
158
+ const [_faceLandmarkerResult, setFaceLandmarkerResult] = useState();
157
159
  const faceLandmarker = useFaceLandmarker();
158
- const detect = useCallback((video, time) => {
159
- const result = faceLandmarker == null ? void 0 : faceLandmarker.detectForVideo(video, time);
160
- setFaces(result);
161
- return result;
160
+ const onVideoFrame = useCallback((now, metadata) => {
161
+ const texture = videoTextureRef.current;
162
+ if (!texture) return;
163
+ const videoFrame = texture.source.data;
164
+ const result = faceLandmarker == null ? void 0 : faceLandmarker.detectForVideo(videoFrame, now);
165
+ setFaceLandmarkerResult(result);
162
166
  }, [faceLandmarker]);
163
- useFrame((_, delta) => {
164
- if (!manualUpdate) {
165
- update(delta);
166
- }
167
- });
168
167
 
168
+ //
169
169
  // Ref API
170
+ //
171
+
170
172
  const api = useMemo(() => Object.assign(Object.create(THREE.EventDispatcher.prototype), {
171
- detect,
172
173
  computeTarget,
173
174
  update,
174
- facemeshApiRef,
175
- webcamApiRef,
176
- // shorthands
177
- play: () => {
178
- var _webcamApiRef$current;
179
- (_webcamApiRef$current = webcamApiRef.current) == null || (_webcamApiRef$current = _webcamApiRef$current.videoTextureApiRef.current) == null || _webcamApiRef$current.texture.source.data.play();
180
- },
181
- pause: () => {
182
- var _webcamApiRef$current2;
183
- (_webcamApiRef$current2 = webcamApiRef.current) == null || (_webcamApiRef$current2 = _webcamApiRef$current2.videoTextureApiRef.current) == null || _webcamApiRef$current2.texture.source.data.pause();
184
- }
185
- }), [detect, computeTarget, update]);
175
+ facemeshApiRef
176
+ }), [computeTarget, update]);
186
177
  useImperativeHandle(fref, () => api, [api]);
187
178
 
188
179
  //
189
- // events callbacks
180
+ // makeDefault (`controls` global state)
190
181
  //
191
182
 
192
- useEffect(() => {
193
- const onVideoFrameCb = e => {
194
- if (!manualDetect) detect(e.texture.source.data, e.time);
195
- if (onVideoFrame) onVideoFrame(e);
196
- };
197
- api.addEventListener('videoFrame', onVideoFrameCb);
198
- return () => {
199
- api.removeEventListener('videoFrame', onVideoFrameCb);
200
- };
201
- }, [api, detect, faceLandmarker, manualDetect, onVideoFrame]);
202
-
203
- // `controls` global state
204
183
  useEffect(() => {
205
184
  if (makeDefault) {
206
185
  const old = get().controls;
@@ -212,18 +191,28 @@ const FaceControls = /* @__PURE__ */forwardRef(({
212
191
  });
213
192
  }
214
193
  }, [makeDefault, api, get, set]);
215
- const points = faces == null ? void 0 : faces.faceLandmarks[0];
216
- const facialTransformationMatrix = faces == null || (_faces$facialTransfor = faces.facialTransformationMatrixes) == null ? void 0 : _faces$facialTransfor[0];
217
- const faceBlendshapes = faces == null || (_faces$faceBlendshape = faces.faceBlendshapes) == null ? void 0 : _faces$faceBlendshape[0];
194
+
195
+ //
196
+ //
197
+ //
198
+
199
+ const result = faceLandmarkerResult !== null && faceLandmarkerResult !== void 0 ? faceLandmarkerResult : _faceLandmarkerResult;
200
+ const points = result == null ? void 0 : result.faceLandmarks[0];
201
+ const facialTransformationMatrix = result == null || (_result$facialTransfo = result.facialTransformationMatrixes) == null ? void 0 : _result$facialTransfo[0];
202
+ const faceBlendshapes = result == null || (_result$faceBlendshap = result.faceBlendshapes) == null ? void 0 : _result$faceBlendshap[0];
203
+ const videoTextureProps = {
204
+ onVideoFrame,
205
+ ...videoTexture
206
+ };
218
207
  return /*#__PURE__*/React.createElement(FaceControlsContext.Provider, {
219
208
  value: api
220
- }, webcam && /*#__PURE__*/React.createElement(Suspense, {
209
+ }, !manualDetect && /*#__PURE__*/React.createElement(Suspense, {
221
210
  fallback: null
222
- }, /*#__PURE__*/React.createElement(Webcam, {
223
- ref: webcamApiRef,
224
- autostart: autostart,
225
- videoTextureSrc: webcamVideoTextureSrc
226
- })), /*#__PURE__*/React.createElement(Facemesh, _extends({
211
+ }, 'src' in videoTextureProps ? /*#__PURE__*/React.createElement(VideoTexture, _extends({
212
+ ref: videoTextureRef
213
+ }, videoTextureProps)) : /*#__PURE__*/React.createElement(WebcamVideoTexture, _extends({
214
+ ref: videoTextureRef
215
+ }, videoTextureProps))), /*#__PURE__*/React.createElement(Facemesh, _extends({
227
216
  ref: facemeshApiRef
228
217
  }, facemesh, {
229
218
  points: points,
@@ -243,91 +232,4 @@ const FaceControls = /* @__PURE__ */forwardRef(({
243
232
  });
244
233
  const useFaceControls = () => useContext(FaceControlsContext);
245
234
 
246
- //
247
- // Webcam
248
- //
249
-
250
- const Webcam = /* @__PURE__ */forwardRef(({
251
- videoTextureSrc,
252
- autostart = true
253
- }, fref) => {
254
- const videoTextureApiRef = useRef(null);
255
- const faceControls = useFaceControls();
256
- const stream = suspend(async () => {
257
- return !videoTextureSrc ? await navigator.mediaDevices.getUserMedia({
258
- audio: false,
259
- video: {
260
- facingMode: 'user'
261
- }
262
- }) : Promise.resolve(null);
263
- }, [videoTextureSrc]);
264
- useEffect(() => {
265
- faceControls.dispatchEvent({
266
- type: 'stream',
267
- stream
268
- });
269
- return () => {
270
- stream == null || stream.getTracks().forEach(track => track.stop());
271
- clear([videoTextureSrc]);
272
- };
273
- }, [stream, faceControls, videoTextureSrc]);
274
-
275
- // ref-api
276
- const api = useMemo(() => ({
277
- videoTextureApiRef
278
- }), []);
279
- useImperativeHandle(fref, () => api, [api]);
280
- return /*#__PURE__*/React.createElement(Suspense, {
281
- fallback: null
282
- }, /*#__PURE__*/React.createElement(VideoTexture, {
283
- ref: videoTextureApiRef,
284
- src: videoTextureSrc || stream,
285
- start: autostart
286
- }));
287
- });
288
-
289
- //
290
- // VideoTexture
291
- //
292
-
293
- const VideoTexture = /* @__PURE__ */forwardRef(({
294
- src,
295
- start
296
- }, fref) => {
297
- const texture = useVideoTexture(src, {
298
- start
299
- });
300
- const video = texture.source.data;
301
- const faceControls = useFaceControls();
302
- const onVideoFrame = useCallback(time => {
303
- faceControls.dispatchEvent({
304
- type: 'videoFrame',
305
- texture,
306
- time
307
- });
308
- }, [texture, faceControls]);
309
- useVideoFrame(video, onVideoFrame);
310
-
311
- // ref-api
312
- const api = useMemo(() => ({
313
- texture
314
- }), [texture]);
315
- useImperativeHandle(fref, () => api, [api]);
316
- return /*#__PURE__*/React.createElement(React.Fragment, null);
317
- });
318
- const useVideoFrame = (video, f) => {
319
- // https://web.dev/requestvideoframecallback-rvfc/
320
- // https://www.remotion.dev/docs/video-manipulation
321
- useEffect(() => {
322
- if (!video || !video.requestVideoFrameCallback) return;
323
- let handle;
324
- function callback(...args) {
325
- f(...args);
326
- handle = video.requestVideoFrameCallback(callback);
327
- }
328
- video.requestVideoFrameCallback(callback);
329
- return () => video.cancelVideoFrameCallback(handle);
330
- }, [video, f]);
331
- };
332
-
333
235
  export { FaceControls, useFaceControls };
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("suspend-react");function r(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var a=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,a.get?a:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var a=r(e);const n=e.createContext({}),s={basePath:"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.17/wasm",options:{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",outputFaceBlendshapes:!0,outputFacialTransformationMatrixes:!0}};exports.FaceLandmarker=function({basePath:o=s.basePath,options:i=s.options,children:c}){const u=JSON.stringify(i),l=t.suspend((async()=>{const{FilesetResolver:e,FaceLandmarker:t}=await Promise.resolve().then((function(){return r(require("@mediapipe/tasks-vision"))})),a=await e.forVisionTasks(o);return t.createFromOptions(a,i)}),[o,u]);return e.useEffect((()=>()=>{null==l||l.close(),t.clear([o,u])}),[l,o,u]),a.createElement(n.Provider,{value:l},c)},exports.FaceLandmarkerDefaults=s,exports.useFaceLandmarker=function(){return e.useContext(n)};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("suspend-react");function r(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var a=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,a.get?a:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var a=r(e);const s=e.createContext({}),n={basePath:"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.17/wasm",options:{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",outputFaceBlendshapes:!0,outputFacialTransformationMatrixes:!0}},o=e.forwardRef((({basePath:o=n.basePath,options:i=n.options,children:c},u)=>{const l=JSON.stringify(i),d=t.suspend((async()=>{const{FilesetResolver:e,FaceLandmarker:t}=await Promise.resolve().then((function(){return r(require("@mediapipe/tasks-vision"))})),a=await e.forVisionTasks(o);return t.createFromOptions(a,i)}),[o,l]);return e.useEffect((()=>()=>{null==d||d.close(),t.clear([o,l])}),[d,o,l]),e.useImperativeHandle(u,(()=>d),[d]),a.createElement(s.Provider,{value:d},c)}));exports.FaceLandmarker=o,exports.FaceLandmarkerDefaults=n,exports.useFaceLandmarker=function(){return e.useContext(s)};
@@ -10,6 +10,6 @@ export declare const FaceLandmarkerDefaults: {
10
10
  basePath: string;
11
11
  options: FaceLandmarkerOptions;
12
12
  };
13
- export declare function FaceLandmarker({ basePath, options, children, }: FaceLandmarkerProps): React.JSX.Element;
13
+ export declare const FaceLandmarker: React.ForwardRefExoticComponent<FaceLandmarkerProps & React.RefAttributes<FaceLandmarkerImpl>>;
14
14
  export declare function useFaceLandmarker(): FaceLandmarkerImpl | undefined;
15
15
  export {};
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useEffect, useContext, createContext } from 'react';
2
+ import { forwardRef, useEffect, useImperativeHandle, useContext, createContext } from 'react';
3
3
  import { suspend, clear } from 'suspend-react';
4
4
 
5
5
  /* eslint react-hooks/exhaustive-deps: 1 */
@@ -16,11 +16,11 @@ const FaceLandmarkerDefaults = {
16
16
  outputFacialTransformationMatrixes: true
17
17
  }
18
18
  };
19
- function FaceLandmarker({
19
+ const FaceLandmarker = /*#__PURE__*/forwardRef(({
20
20
  basePath = FaceLandmarkerDefaults.basePath,
21
21
  options = FaceLandmarkerDefaults.options,
22
22
  children
23
- }) {
23
+ }, fref) => {
24
24
  const opts = JSON.stringify(options);
25
25
  const faceLandmarker = suspend(async () => {
26
26
  const {
@@ -36,10 +36,12 @@ function FaceLandmarker({
36
36
  clear([basePath, opts]);
37
37
  };
38
38
  }, [faceLandmarker, basePath, opts]);
39
+ useImperativeHandle(fref, () => faceLandmarker, [faceLandmarker]); // expose faceLandmarker through ref
40
+
39
41
  return /*#__PURE__*/React.createElement(FaceLandmarkerContext.Provider, {
40
42
  value: faceLandmarker
41
43
  }, children);
42
- }
44
+ });
43
45
  function useFaceLandmarker() {
44
46
  return useContext(FaceLandmarkerContext);
45
47
  }