face-validator-sdk 1.2.1 → 1.3.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.1] – 2026-02-23
9
+
10
+ ### Added
11
+
12
+ - **Container-first API**: New `container`, `ui`, `autoStart`, `mirror`, and `videoConstraints` options to let the SDK manage media elements and UI automatically.
13
+ - **Managed UI styling**: Default UI now injects its own minimal styles when `ui: 'default'` is used.
14
+ - **Lifecycle cleanup**: New `destroy()` method to release resources, stop camera streams, and clean up managed DOM elements.
15
+
16
+ ### Changed
17
+
18
+ - **Callbacks optional**: `onStatusUpdate`, `onCaptureSuccess`, and `onError` are now optional.
19
+ - **Camera initialization**: Internal camera setup now awaits readiness before starting detection loops.
20
+ - **ReactSelfieCapture**: Uses SDK-managed camera lifecycle and `destroy()` for cleanup.
21
+
8
22
  ## [1.2.1] – 2026-02-18
9
23
 
10
24
  ### Fixed
package/README.md CHANGED
@@ -41,33 +41,26 @@ npm install face-validator-sdk
41
41
  ```typescript
42
42
  import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
43
43
 
44
- const video = document.querySelector('video');
45
- const canvas = document.querySelector('canvas');
46
-
47
44
  const validator = new FaceValidator({
48
- videoElement: video,
49
- overlayCanvasElement: canvas,
50
- locale: 'pt-BR', // 'pt-BR' | 'en' | 'es'
45
+ container: '#selfieContainer',
46
+ ui: 'default',
47
+ locale: 'pt-BR',
51
48
  debugMode: false,
52
-
49
+ mirror: true,
53
50
  onStatusUpdate: (status, message) => {
54
51
  console.log(status, message);
55
- // Update UI with validation status
56
52
  },
57
-
58
53
  onCaptureSuccess: (blob) => {
59
- // Upload or preview the captured selfie
60
54
  const url = URL.createObjectURL(blob);
61
- document.querySelector('img').src = url;
55
+ document.querySelector('img')!.src = url;
62
56
  },
63
-
64
57
  onError: (errorType, error) => {
65
58
  console.error(errorType, error);
66
59
  }
67
60
  });
68
61
 
69
- // Validator starts automatically
70
- // To stop: validator.stop();
62
+ // The validator starts automatically.
63
+ // To stop and release resources: validator.destroy();
71
64
  ```
72
65
 
73
66
  ## 📊 Validation Status
@@ -93,34 +86,124 @@ const validator = new FaceValidator({
93
86
 
94
87
  ```typescript
95
88
  interface FaceValidatorOptions {
96
- // Required
97
- videoElement: HTMLVideoElement;
98
- onStatusUpdate: (status: ValidationStatus, message: string) => void;
99
- onCaptureSuccess: (imageBlob: Blob) => void;
100
- onError: (errorType: ValidationStatus, error: Error) => void;
101
-
102
- // Optional
103
- overlayCanvasElement?: HTMLCanvasElement;
104
- locale?: 'pt-BR' | 'en' | 'es'; // Default: 'en'
105
- debugMode?: boolean; // Default: false
106
-
107
- // Validation thresholds
108
- minDetectionConfidence?: number; // Default: 0.5
109
- minIlluminationThreshold?: number; // Default: 70 (0-255)
110
- minFaceSizeFactor?: number; // Default: 0.25
111
- maxFaceSizeFactor?: number; // Default: 0.65
112
- stabilizationTimeThreshold?: number; // Default: 1000ms
113
- stabilityMovementThreshold?: number; // Default: 5px
114
- minFaceVisibilityScore?: number; // Default: 0.5
115
- maxHeadTiltDegrees?: number; // Default: 28°
116
- maxHandFaceDistance?: number; // Default: 0.15 (normalized)
117
-
118
- // Advanced
119
- modelPath?: string; // MediaPipe WASM path (auto-detected from CDN)
89
+ // UI structure
90
+ container?: HTMLElement | string; // Element or selector to auto-render video/canvas and status
91
+ ui?: 'default' | 'none'; // Default: 'default'
92
+ autoStart?: boolean; // Default: true
93
+ mirror?: boolean; // Default: true
94
+
95
+ // Camera
96
+ videoElement?: HTMLVideoElement; // Use if you want to control the video manually
97
+ overlayCanvasElement?: HTMLCanvasElement | null;
98
+ videoConstraints?: MediaTrackConstraints;
99
+ videoWidth?: number;
100
+ videoHeight?: number;
101
+
102
+ // Idioma e debug
103
+ locale?: 'pt-BR' | 'en' | 'es';
104
+ debugMode?: boolean;
120
105
  customMessages?: Partial<Record<ValidationStatus, string>>;
106
+
107
+ // Callbacks
108
+ onStatusUpdate?: (status: ValidationStatus, message: string) => void;
109
+ onCaptureSuccess?: (imageBlob: Blob) => void;
110
+ onError?: (errorType: ValidationStatus, error: Error) => void;
111
+
112
+ // Thresholds de validacao
113
+ minDetectionConfidence?: number;
114
+ minIlluminationThreshold?: number;
115
+ minFaceSizeFactor?: number;
116
+ maxFaceSizeFactor?: number;
117
+ stabilizationTimeThreshold?: number;
118
+ stabilityMovementThreshold?: number;
119
+ minFaceVisibilityScore?: number;
120
+ maxHeadTiltDegrees?: number;
121
+ maxHandFaceDistance?: number;
122
+
123
+ // Advanced
124
+ modelPath?: string; // Path to MediaPipe WASM (auto-detected via CDN)
121
125
  }
122
126
  ```
123
127
 
128
+ ## ✅ Usage with React (ReactSelfieCapture)
129
+
130
+ ```tsx
131
+ import { ReactSelfieCapture } from 'face-validator-sdk';
132
+
133
+ export function SelfieModal() {
134
+ const handleCapture = (imageBase64: string | null) => {
135
+ if (!imageBase64) return;
136
+ // Send the selfie to your API
137
+ };
138
+
139
+ return (
140
+ <ReactSelfieCapture
141
+ locale="pt-BR"
142
+ onCapture={handleCapture}
143
+ onDismiss={() => console.log('Modal closed')}
144
+ debugMode={false}
145
+ labels={{
146
+ previewQuestion: 'Check your selfie before saving',
147
+ savePhoto: 'Confirm selfie'
148
+ }}
149
+ />
150
+ );
151
+ }
152
+ ```
153
+
154
+ - `locale`: defines the language for messages and labels displayed.
155
+ - `onCapture`: main callback; receives the base64 image or `null` if the user cancels.
156
+ - `onDismiss`: used to close the modal/dialog when the user clicks cancel.
157
+ - `debugMode`: enables landmark visualization for debugging.
158
+ - `labels`: overrides default texts (e.g., preview title and button label).
159
+
160
+ ## ✅ Usage with Angular (Core API)
161
+
162
+ ```ts
163
+ import { AfterViewInit, Component, OnDestroy } from '@angular/core';
164
+ import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
165
+
166
+ @Component({
167
+ selector: 'app-selfie-dialog',
168
+ template: '<div id="selfieContainer"></div>'
169
+ })
170
+ export class SelfieDialogComponent implements AfterViewInit, OnDestroy {
171
+ private validator: FaceValidator | null = null;
172
+
173
+ ngAfterViewInit(): void {
174
+ this.validator = new FaceValidator({
175
+ container: '#selfieContainer',
176
+ ui: 'none',
177
+ locale: 'pt-BR',
178
+ debugMode: false,
179
+ mirror: true,
180
+ onStatusUpdate: (status: ValidationStatus, message: string) => {
181
+ console.log(status, message);
182
+ },
183
+ onCaptureSuccess: (blob: Blob) => {
184
+ console.log('Selfie capturada', blob);
185
+ },
186
+ onError: (errorType: ValidationStatus, error: Error) => {
187
+ console.error(errorType, error);
188
+ }
189
+ });
190
+ }
191
+
192
+ ngOnDestroy(): void {
193
+ this.validator?.destroy();
194
+ }
195
+ }
196
+ ```
197
+
198
+ - `container`: target element where the SDK automatically renders video and canvas.
199
+ - `ui`: use `none` to render your own UI (status, buttons, etc.).
200
+ - `locale`: defines the language for SDK messages.
201
+ - `debugMode`: displays landmarks for debugging during development.
202
+ - `mirror`: mirrors the camera (selfie default).
203
+ - `onStatusUpdate`: receives the status and message for you to update the UI.
204
+ - `onCaptureSuccess`: receives the `Blob` of the captured selfie for upload/preview.
205
+ - `onError`: captures camera or model loading errors.
206
+
124
207
  ---
125
208
 
126
209
  ## 🧩 React Component: `ReactSelfieCapture`
package/core.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './dist/types/core/core';
2
+ export { default } from './dist/types/core/core';
package/core.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/face-validator-sdk-core.cjs.js');
@@ -0,0 +1,2 @@
1
+ (()=>{"use strict";var e={d:(t,n)=>{for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{FaceValidator:()=>g,ValidationStatus:()=>a,default:()=>p,getLoadingModelsMessage:()=>l,getMessage:()=>r,getValidationMessages:()=>s});const n=require("@mediapipe/tasks-vision");var a;!function(e){e.INITIALIZING="INITIALIZING",e.NO_FACE_DETECTED="NO_FACE_DETECTED",e.FACE_DETECTED="FACE_DETECTED",e.TOO_CLOSE="TOO_CLOSE",e.TOO_FAR="TOO_FAR",e.OFF_CENTER="OFF_CENTER",e.FACE_OBSTRUCTED="FACE_OBSTRUCTED",e.HEAD_NOT_STRAIGHT="HEAD_NOT_STRAIGHT",e.MULTIPLE_FACES="MULTIPLE_FACES",e.POOR_ILLUMINATION="POOR_ILLUMINATION",e.NOT_NEUTRAL_EXPRESSION="NOT_NEUTRAL_EXPRESSION",e.DARK_GLASSES="DARK_GLASSES",e.STAY_STILL="STAY_STILL",e.CAPTURING="CAPTURING",e.SUCCESS="SUCCESS",e.ERROR="ERROR"}(a||(a={}));const i={"pt-BR":{[a.INITIALIZING]:"Inicializando câmera e detector...",[a.NO_FACE_DETECTED]:"Posicione seu rosto no centro do oval.",[a.FACE_DETECTED]:"Analisando...",[a.TOO_CLOSE]:"Afaste-se um pouco",[a.TOO_FAR]:"Aproxime-se da câmera",[a.OFF_CENTER]:"Centralize o rosto no centro do oval",[a.FACE_OBSTRUCTED]:"Mantenha o rosto totalmente visível. Remova as mãos do rosto.",[a.HEAD_NOT_STRAIGHT]:"Olhe diretamente para a câmera e mantenha a cabeça reta.",[a.MULTIPLE_FACES]:"Mantenha apenas uma pessoa no quadro.",[a.POOR_ILLUMINATION]:"Procure um ambiente com boa iluminação.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenha expressão neutra: boca fechada, sem sorrir e olhos abertos.",[a.DARK_GLASSES]:"Remova os óculos escuros. Óculos de grau são permitidos.",[a.STAY_STILL]:"Fique imóvel para capturar a foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"Captura realizada!",[a.ERROR]:"Ocorreu um erro."},en:{[a.INITIALIZING]:"Initializing camera and detector...",[a.NO_FACE_DETECTED]:"Position your face in the center of the oval.",[a.FACE_DETECTED]:"Analyzing...",[a.TOO_CLOSE]:"Move back a little",[a.TOO_FAR]:"Move closer to the camera",[a.OFF_CENTER]:"Center your face in the center of the oval",[a.FACE_OBSTRUCTED]:"Keep your face fully visible. Remove your hands from your face.",[a.HEAD_NOT_STRAIGHT]:"Look directly at the camera and keep your head straight.",[a.MULTIPLE_FACES]:"Keep only one person in the frame.",[a.POOR_ILLUMINATION]:"Find a well-lit environment and center your face in the oval.",[a.NOT_NEUTRAL_EXPRESSION]:"Keep a neutral expression: mouth closed, no smiling, and eyes open.",[a.DARK_GLASSES]:"Remove sunglasses. Prescription glasses are allowed.",[a.STAY_STILL]:"Stay still to capture the photo",[a.CAPTURING]:"Capturing...",[a.SUCCESS]:"Capture complete!",[a.ERROR]:"An error occurred."},es:{[a.INITIALIZING]:"Inicializando cámara y detector...",[a.NO_FACE_DETECTED]:"Coloque su rostro en el centro del óvalo.",[a.FACE_DETECTED]:"Analizando...",[a.TOO_CLOSE]:"Aléjese un poco",[a.TOO_FAR]:"Acérquese a la cámara",[a.OFF_CENTER]:"Centre el rostro en el centro del óvalo",[a.FACE_OBSTRUCTED]:"Mantenga el rostro totalmente visible. Quite las manos del rostro.",[a.HEAD_NOT_STRAIGHT]:"Mire directamente a la cámara y mantenga la cabeza recta.",[a.MULTIPLE_FACES]:"Mantenga solo una persona en el encuadre.",[a.POOR_ILLUMINATION]:"Busque un ambiente con buena iluminación y centre su rostro en el óvalo.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenga expresión neutra: boca cerrada, sin sonreír y ojos abiertos.",[a.DARK_GLASSES]:"Quite las gafas de sol. Las gafas graduadas están permitidas.",[a.STAY_STILL]:"Permanezca quieto para capturar la foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"¡Captura realizada!",[a.ERROR]:"Ocurrió un error."}},o={"pt-BR":"Status desconhecido.",en:"Unknown status.",es:"Estado desconhecido."};function s(e){return Object.assign({},i[e])}function r(e,t){var n;return null!==(n=i[t][e])&&void 0!==n?n:o[t]}function l(e){return{"pt-BR":"Carregando...",en:"Loading...",es:"Cargando..."}[e]}function d(e){const t=e.data;let n=0;for(let e=0;e<t.length;e+=4)n+=.2126*t[e]+.7152*t[e+1]+.0722*t[e+2];return n/(t.length/4)}const c=[33,133,159,145],h=[263,362,386,374],m=[61,291,0,17,39,269,270,409],u=.34;var E=function(e,t,n,a){return new(n||(n=Promise))(function(i,o){function s(e){try{l(a.next(e))}catch(e){o(e)}}function r(e){try{l(a.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(s,r)}l((a=a.apply(e,t||[])).next())})};const f={container:void 0,ui:"default",autoStart:!0,mirror:!0,videoConstraints:void 0,overlayCanvasElement:void 0,videoWidth:512,videoHeight:384,minDetectionConfidence:.4,minIlluminationThreshold:50,minFaceSizeFactor:.15,maxFaceSizeFactor:.75,stabilizationTimeThreshold:1e3,stabilityMovementThreshold:5,minFaceVisibilityScore:.4,maxHeadTiltDegrees:30,maxHandFaceDistance:.15,debugMode:!1,locale:"en",customMessages:{},onStatusUpdate:void 0,onCaptureSuccess:void 0,onError:void 0};class g{constructor(e){this.faceLandmarker=null,this.handLandmarker=null,this.animationFrameId=null,this.lastDetection=null,this.stableSince=null,this.isCapturing=!1,this.containerElement=null,this.statusElement=null,this.uiRootElement=null,this.cameraStream=null,this.cameraReadyPromise=null,this.managedElements=!1,this.managedCamera=!1,this.injectedStyleElement=null,this.options=this.resolveOptions(e),this.setupElements(),this.setStatus(a.INITIALIZING),this.cameraReadyPromise=this.options.autoStart?this.initCamera():Promise.resolve(),this.init()}resolveOptions(e){const t=e.modelPath||"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm";return Object.assign(Object.assign(Object.assign({},f),e),{modelPath:t,locale:e.locale||"en",customMessages:e.customMessages||{},onStatusUpdate:e.onStatusUpdate||(()=>{}),onCaptureSuccess:e.onCaptureSuccess||(()=>{}),onError:e.onError||(()=>{}),videoConstraints:e.videoConstraints||{width:{ideal:e.videoWidth||f.videoWidth},height:{ideal:e.videoHeight||f.videoHeight},facingMode:"user"}})}setupElements(){const e=this.resolveContainer(this.options.container);if(this.containerElement=e,"default"===this.options.ui&&e&&this.ensureDefaultUI(e),this.options.videoElement){if(!this.options.overlayCanvasElement&&e){const t=document.createElement("canvas");t.className="fv-sdk-canvas",this.attachMediaElements(e,this.options.videoElement,t),this.options.overlayCanvasElement=t,this.managedElements=!0}}else{if(!e)throw new Error("FaceValidator requires either videoElement or container.");const t=document.createElement("video");t.autoplay=!0,t.playsInline=!0,t.muted=!0,t.className="fv-sdk-video";const n=document.createElement("canvas");n.className="fv-sdk-canvas",this.attachMediaElements(e,t,n),this.options.videoElement=t,this.options.overlayCanvasElement=n,this.managedElements=!0}this.options.mirror&&this.applyMirrorStyles()}resolveContainer(e){return e?"string"==typeof e?document.querySelector(e):e:null}ensureDefaultUI(e){e.innerHTML="",e.classList.add("fv-sdk-root");const t=document.createElement("div");t.className="fv-sdk-media",e.appendChild(t);const n=document.createElement("div");n.className="fv-sdk-status",e.appendChild(n),this.statusElement=n,this.uiRootElement=e,this.injectDefaultStyles()}attachMediaElements(e,t,n){const a=e.querySelector(".fv-sdk-media");if(a)return a.appendChild(t),void a.appendChild(n);const i=document.createElement("div");i.className="fv-sdk-media",i.appendChild(t),i.appendChild(n),e.appendChild(i)}injectDefaultStyles(){if(this.injectedStyleElement||document.querySelector('style[data-fv-sdk="true"]'))return;const e=document.createElement("style");e.setAttribute("data-fv-sdk","true"),e.textContent="\n .fv-sdk-root { display: flex; flex-direction: column; gap: 12px; width: 100%; }\n .fv-sdk-media { position: relative; width: 100%; max-width: 512px; height: 384px; margin: 0 auto; background: #000; border-radius: 10px; overflow: hidden; }\n .fv-sdk-video, .fv-sdk-canvas { width: 100%; height: 100%; display: block; object-fit: contain; }\n .fv-sdk-canvas { position: absolute; top: 0; left: 0; }\n .fv-sdk-status { text-align: center; display: flex; align-items: center; justify-content: center; font-size: 14px; padding: 10px 12px; border-radius: 8px; font-weight: 600; background: #f8f9fa; color: #555; }\n .fv-sdk-status.success { background: #d4edda; color: #155724; }\n .fv-sdk-status.error { background: #f8d7da; color: #721c24; }\n .fv-sdk-status.warning { background: #fff3cd; color: #856404; }\n ",document.head.appendChild(e),this.injectedStyleElement=e}applyMirrorStyles(){const e=this.options.videoElement,t=this.options.overlayCanvasElement;e&&(e.style.transform="scaleX(-1)"),t&&(t.style.transform="scaleX(-1)")}init(){return E(this,void 0,void 0,function*(){try{const e=l(this.options.locale);this.setStatus(a.INITIALIZING,void 0,e);const t=yield n.FilesetResolver.forVisionTasks(this.options.modelPath);this.faceLandmarker=yield n.FaceLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numFaces:2,minFaceDetectionConfidence:this.options.minDetectionConfidence,minFacePresenceConfidence:this.options.minFaceVisibilityScore,minTrackingConfidence:this.options.minFaceVisibilityScore}),this.handLandmarker=yield n.HandLandmarker.createFromOptions(t,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5}),this.cameraReadyPromise&&(yield this.cameraReadyPromise),this.startDetectionLoop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}})}getMessageForStatus(e,t){return t||(this.options.customMessages[e]?this.options.customMessages[e]:r(e,this.options.locale))}setStatus(e,t,n){const i=this.getMessageForStatus(e,n);this.updateStatusUI(e,i),this.options.onStatusUpdate(e,i),e===a.ERROR&&t&&this.options.onError(e,t)}updateStatusUI(e,t){if(!this.statusElement)return;this.statusElement.textContent=t,this.statusElement.classList.remove("success","warning","error");const n=this.getStatusClass(e);n&&this.statusElement.classList.add(n)}getStatusClass(e){return e===a.SUCCESS?"success":e===a.ERROR?"error":[a.NO_FACE_DETECTED,a.MULTIPLE_FACES,a.TOO_CLOSE,a.TOO_FAR,a.OFF_CENTER,a.HEAD_NOT_STRAIGHT,a.FACE_OBSTRUCTED,a.POOR_ILLUMINATION,a.NOT_NEUTRAL_EXPRESSION,a.DARK_GLASSES,a.STAY_STILL,a.CAPTURING].includes(e)?"warning":""}initCamera(){return E(this,void 0,void 0,function*(){const e=this.options.videoElement;if(e&&!e.srcObject)try{const t=yield navigator.mediaDevices.getUserMedia({video:this.options.videoConstraints});this.cameraStream=t,this.managedCamera=!0,e.srcObject=t,yield this.waitForVideoReady(e),yield e.play();const n=this.options.overlayCanvasElement;n&&(n.width=e.videoWidth||this.options.videoWidth||f.videoWidth,n.height=e.videoHeight||this.options.videoHeight||f.videoHeight)}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}})}waitForVideoReady(e){return E(this,void 0,void 0,function*(){e.readyState>=2||(yield new Promise((t,n)=>{const a=()=>{e.removeEventListener("loadedmetadata",a),t()},i=t=>{e.removeEventListener("error",i),n(t)};e.addEventListener("loadedmetadata",a),e.addEventListener("error",i),setTimeout(()=>{e.removeEventListener("loadedmetadata",a),e.removeEventListener("error",i),t()},5e3)}))})}startDetectionLoop(){const e=this.getVideoElement(),t=this.options.videoWidth||640,n=this.options.videoHeight||480,i=()=>E(this,void 0,void 0,function*(){var o;if(this.faceLandmarker&&this.handLandmarker&&e.videoWidth){try{const i=performance.now();let s=a.NO_FACE_DETECTED,r=null,l=[];const E=this.faceLandmarker.detectForVideo(e,i),f=this.handLandmarker.detectForVideo(e,i);if(f.landmarks&&f.landmarks.length>0&&(l=f.landmarks.map((e,t)=>{var n,a,i;return{landmarks:e,handedness:(null===(i=null===(a=null===(n=f.handednesses)||void 0===n?void 0:n[t])||void 0===a?void 0:a[0])||void 0===i?void 0:i.categoryName)||"Unknown"}})),E.faceLandmarks&&E.faceLandmarks.length>1){s=a.MULTIPLE_FACES,this.stableSince=null;const e=E.faceLandmarks[0],t=(null===(o=E.faceBlendshapes)||void 0===o?void 0:o[0])?this.estimateBoundingBox(e):null;t&&(r={boundingBox:t,landmarks:e,timestamp:i})}else if(E.faceLandmarks&&1===E.faceLandmarks.length){const o=E.faceLandmarks[0],f=this.estimateBoundingBox(o);r={boundingBox:f,landmarks:o,timestamp:i};const g=function(e,t=.18,n=.7){const a=e.width;return a<t?"TOO_FAR":a>n?"TOO_CLOSE":"OK"}(f,this.options.minFaceSizeFactor,this.options.maxFaceSizeFactor);if("OK"!==g)s="TOO_CLOSE"===g?a.TOO_CLOSE:a.TOO_FAR,this.stableSince=null;else{const E=o[4],g=function(e,t,n,a){const i=(e*n-n/2)/(.2*n),o=(t*a-a/2)/(a*u);return i*i+o*o<=.6}(E.x,E.y,t,n),p=function(e,t,n){const a=t/2,i=n/2,o=.2*t,s=n*u,r=e.xMin*t,l=(e.xMin+e.width)*t,d=e.yMin*n,c=(e.yMin+e.height)*n,h=((r+l)/2-a)/o,m=((d+c)/2-i)/s;if(h*h+m*m>1)return!1;const E=[{x:r,y:d},{x:l,y:d},{x:r,y:c},{x:l,y:c}];let f=0;for(const e of E){const t=(e.x-a)/o,n=(e.y-i)/s;t*t+n*n>1&&f++}return 0===f}(f,t,n);if(g&&p)if(function(e,t){if(e.length<478)return!1;const n=e[4],a=m.map(t=>e[t]),i=a.reduce((e,t)=>e+t.y,0)/a.length,o=Math.min(...a.map(e=>e.y)),s=Math.max(...a.map(e=>e.y))-o,r=t.height;return!(i<n.y-.01||i-n.y<.06*r||s<.02*r)}(o,f))if(function(e,t=25){if(e.length<478)return!1;const n=e[c[0]],a=e[h[0]],i=e[4],o=e[13],s=e[14],r=e[152],l=e[10],d=Math.abs(n.y-a.y),m=Math.abs(n.x-a.x);if(m<.01)return!1;const u=d/m;if(Math.atan(u)*(180/Math.PI)>t)return!1;const E=(n.x+a.x)/2,f=i.x-E,g=Math.abs(n.x-a.x);if(g<.01)return!1;const p=Math.abs(f)/g;if(Math.atan(p)*(180/Math.PI)>t)return!1;if(!function(e){if(e.length<478)return!1;const t=e[234],n=e[454],a=e[4],i=Math.abs(t.x-a.x),o=Math.abs(n.x-a.x);return!((i>.01&&o>.01?Math.max(i,o)/Math.min(i,o):1)>1.4||void 0!==t.z&&void 0!==n.z&&Math.abs(t.z-n.z)>.05)}(e))return!1;const S=(n.y+a.y)/2,v=(o.y+s.y)/2,T=r.y-l.y;if(T<.1)return!1;if(l.y>S+.02)return!1;if(S>i.y+.02)return!1;if(i.y>v+.02)return!1;if(v>=r.y)return!1;const y=(S-l.y)/T,C=(i.y-S)/T,O=(v-i.y)/T,I=(r.y-v)/T;return!(y<.06||y>.38||C<.03||C>.3||O<.02||O>.25||I<.04||I>.38)}(o,this.options.maxHeadTiltDegrees))if(l.length>0&&function(e,t,n=.15){const a=t.xMin+t.width/2,i=t.yMin+t.height/2;for(const t of e.landmarks){const e=t.x-a,o=t.y-i;if(Math.sqrt(e*e+o*o)<n)return!0}return!1}(l[0],f,this.options.maxHandFaceDistance))s=a.FACE_OBSTRUCTED,this.stableSince=null;else if(function(e){if(e.length<478)return!1;const t=e[159],n=e[144],a=e[386],i=e[373],o=Math.abs(t.y-n.y),s=Math.abs(a.y-i.y);if(o<.01||s<.01)return!1;const r=e[13],l=e[14];if(Math.abs(r.y-l.y)>.025)return!1;const d=e[61],c=e[291],h=e[4];return!((d.y+c.y)/2-h.y<.05)}(o))if(function(e,t){if(t.length<478)return!1;try{const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)return!1;const i=e.videoWidth,o=e.videoHeight,s=[t[33],t[133],t[159],t[144],t[145]],r=[t[263],t[362],t[386],t[373],t[374]],l=e=>{const t=e.map(e=>e.x*i),n=e.map(e=>e.y*o),a=Math.max(0,Math.min(...t)-5),s=Math.min(i,Math.max(...t)+5),r=Math.max(0,Math.min(...n)-5);return{x:a,y:r,width:s-a,height:Math.min(o,Math.max(...n)+5)-r}},c=t=>(n.width=t.width,n.height=t.height,a.drawImage(e,t.x,t.y,t.width,t.height,0,0,t.width,t.height),d(a.getImageData(0,0,t.width,t.height))),h=l(s),m=l(r);return(c(h)+c(m))/2<35}catch(e){return console.warn("Erro ao detectar óculos escuros:",e),!1}}(e,o))s=a.DARK_GLASSES,this.stableSince=null;else{const o=document.createElement("canvas"),l=f.xMin*e.videoWidth,c=f.yMin*e.videoHeight,h=f.width*e.videoWidth,m=f.height*e.videoHeight;o.width=h,o.height=m;const u=o.getContext("2d",{willReadFrequently:!0});if(u){u.drawImage(e,l,c,h,m,0,0,h,m);d(u.getImageData(0,0,o.width,o.height))<this.options.minIlluminationThreshold?(s=a.POOR_ILLUMINATION,this.stableSince=null):function(e,t,n=5,a=512,i=384){if(!e||!t)return!1;const o=(e.boundingBox.xMin+e.boundingBox.width/2)*a,s=(e.boundingBox.yMin+e.boundingBox.height/2)*i,r=(t.boundingBox.xMin+t.boundingBox.width/2)*a,l=(t.boundingBox.yMin+t.boundingBox.height/2)*i,d=Math.abs(o-r),c=Math.abs(s-l),h=Math.abs(e.boundingBox.width-t.boundingBox.width)*a,m=Math.abs(e.boundingBox.height-t.boundingBox.height)*i;return d<=n&&c<=n&&h<=2*n&&m<=2*n}(r,this.lastDetection,this.options.stabilityMovementThreshold,t,n)?(this.stableSince||(this.stableSince=i),s=i-this.stableSince>=this.options.stabilizationTimeThreshold?a.CAPTURING:a.STAY_STILL):(this.stableSince=null,s=a.STAY_STILL)}else s=a.FACE_DETECTED,this.stableSince=null}else s=a.NOT_NEUTRAL_EXPRESSION,this.stableSince=null;else s=a.HEAD_NOT_STRAIGHT,this.stableSince=null;else s=a.FACE_OBSTRUCTED,this.stableSince=null;else s=a.OFF_CENTER,this.stableSince=null}}else this.lastDetection=null,this.stableSince=null;if(this.lastDetection=r,this.setStatus(s),this.options.overlayCanvasElement&&function(e,t,n,i,o){const s=e.getContext("2d");if(!s)return;const r=e.width,l=e.height,d=r/2,m=l/2;s.clearRect(0,0,r,l);const E=.2*r,f=l*u;if(s.fillStyle="rgba(255, 255, 255, 0.35)",s.fillRect(0,0,r,l),s.save(),s.beginPath(),s.ellipse(d,m,E,f,0,0,2*Math.PI),s.closePath(),s.globalCompositeOperation="destination-out",s.fill(),s.restore(),s.strokeStyle="rgba(255, 255, 255, 0.9)",s.lineWidth=3,s.beginPath(),s.ellipse(d,m,E,f,0,0,2*Math.PI),s.stroke(),s.strokeStyle="rgba(255, 255, 255, 0.45)",s.lineWidth=1,s.beginPath(),s.moveTo(d-6,m),s.lineTo(d+6,m),s.moveTo(d,m-6),s.lineTo(d,m+6),s.stroke(),t&&i){const e=i.landmarks;if(e.length>=478){const t=e[10],i=e[152],o=e[234],d=e[454],m=e.map(e=>e.x),u=e.map(e=>e.y),E=Math.min(...m),f=Math.max(...m),g=Math.min(...u),p=f-E,S=Math.max(...u)-g,v=.08,T=(E-p*v)*r,y=(g-S*v)*l,C=p*(1+2*v)*r,O=S*(1+2*v)*l;let I="red";n===a.STAY_STILL||n===a.CAPTURING?I="lime":n===a.FACE_DETECTED&&(I="yellow"),s.strokeStyle=I,s.lineWidth=3,s.strokeRect(T,y,C,O);const R=e[4];e[c[0]],e[h[0]],s.fillStyle="cyan",s.beginPath(),s.arc(R.x*r,R.y*l,5,0,2*Math.PI),s.fill(),s.fillStyle="magenta",s.beginPath(),s.arc(t.x*r,t.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="lime",s.beginPath(),s.arc(i.x*r,i.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="yellow",[e[33],e[133],e[159],e[144],e[145]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="yellow",[e[263],e[362],e[386],e[373],e[374]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="purple",s.beginPath(),s.arc(o.x*r,o.y*l,3,0,2*Math.PI),s.fill(),s.beginPath(),s.arc(d.x*r,d.y*l,3,0,2*Math.PI),s.fill()}}t&&o&&o.length>0&&o.forEach(e=>{s.fillStyle="orange",e.landmarks.forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()})})}(this.options.overlayCanvasElement,this.options.debugMode||!1,s,r||void 0,l.length>0?l:void 0),s===a.CAPTURING&&!this.isCapturing)return this.isCapturing=!0,yield this.captureImage(),this.setStatus(a.SUCCESS),void this.stop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}this.animationFrameId=requestAnimationFrame(i)}else this.animationFrameId=requestAnimationFrame(i)});this.animationFrameId=requestAnimationFrame(i)}estimateBoundingBox(e){const t=e.map(e=>e.x),n=e.map(e=>e.y),a=Math.min(...t),i=Math.max(...t),o=Math.min(...n);return{xMin:a,yMin:o,width:i-a,height:Math.max(...n)-o}}captureImage(){return E(this,void 0,void 0,function*(){const e=this.getVideoElement(),t=document.createElement("canvas");t.width=e.videoWidth,t.height=e.videoHeight;const n=t.getContext("2d");n?(n.drawImage(e,0,0,t.width,t.height),t.toBlob(e=>{e?this.options.onCaptureSuccess(e):this.setStatus(a.ERROR,new Error("Failed to generate image blob"))},"image/jpeg",.95)):this.setStatus(a.ERROR,new Error("Failed to get canvas context"))})}stop(){null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.faceLandmarker&&this.faceLandmarker.close(),this.handLandmarker&&this.handLandmarker.close(),this.managedCamera&&this.stopCamera()}destroy(){this.stop(),this.managedElements&&this.containerElement&&(this.containerElement.innerHTML=""),this.statusElement=null,this.uiRootElement=null}stopCamera(){this.cameraStream&&(this.cameraStream.getTracks().forEach(e=>e.stop()),this.cameraStream=null)}getVideoElement(){if(!this.options.videoElement)throw new Error("Video element is not available. Provide videoElement or container.");return this.options.videoElement}}const p=g;module.exports=t})();
2
+ //# sourceMappingURL=face-validator-sdk-core.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"face-validator-sdk-core.cjs.js","mappings":"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","sources":["webpack://face-validator-sdk/webpack/bootstrap","webpack://face-validator-sdk/webpack/runtime/define property getters","webpack://face-validator-sdk/webpack/runtime/hasOwnProperty shorthand","webpack://face-validator-sdk/webpack/runtime/make namespace object","webpack://face-validator-sdk/external commonjs2 \"@mediapipe/tasks-vision\"","webpack://face-validator-sdk/./src/types.ts","webpack://face-validator-sdk/./src/i18n.ts","webpack://face-validator-sdk/./src/utils.ts","webpack://face-validator-sdk/./src/FaceValidator.ts","webpack://face-validator-sdk/./src/core.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","const __WEBPACK_NAMESPACE_OBJECT__ = require(\"@mediapipe/tasks-vision\");","export var ValidationStatus;\n(function (ValidationStatus) {\n ValidationStatus[\"INITIALIZING\"] = \"INITIALIZING\";\n ValidationStatus[\"NO_FACE_DETECTED\"] = \"NO_FACE_DETECTED\";\n ValidationStatus[\"FACE_DETECTED\"] = \"FACE_DETECTED\";\n ValidationStatus[\"TOO_CLOSE\"] = \"TOO_CLOSE\";\n ValidationStatus[\"TOO_FAR\"] = \"TOO_FAR\";\n ValidationStatus[\"OFF_CENTER\"] = \"OFF_CENTER\";\n ValidationStatus[\"FACE_OBSTRUCTED\"] = \"FACE_OBSTRUCTED\";\n ValidationStatus[\"HEAD_NOT_STRAIGHT\"] = \"HEAD_NOT_STRAIGHT\";\n ValidationStatus[\"MULTIPLE_FACES\"] = \"MULTIPLE_FACES\";\n ValidationStatus[\"POOR_ILLUMINATION\"] = \"POOR_ILLUMINATION\";\n ValidationStatus[\"NOT_NEUTRAL_EXPRESSION\"] = \"NOT_NEUTRAL_EXPRESSION\";\n ValidationStatus[\"DARK_GLASSES\"] = \"DARK_GLASSES\";\n ValidationStatus[\"STAY_STILL\"] = \"STAY_STILL\";\n ValidationStatus[\"CAPTURING\"] = \"CAPTURING\";\n ValidationStatus[\"SUCCESS\"] = \"SUCCESS\";\n ValidationStatus[\"ERROR\"] = \"ERROR\";\n})(ValidationStatus || (ValidationStatus = {}));\n","import { ValidationStatus } from './types';\nconst messages = {\n 'pt-BR': {\n [ValidationStatus.INITIALIZING]: 'Inicializando câmera e detector...',\n [ValidationStatus.NO_FACE_DETECTED]: 'Posicione seu rosto no centro do oval.',\n [ValidationStatus.FACE_DETECTED]: 'Analisando...',\n [ValidationStatus.TOO_CLOSE]: 'Afaste-se um pouco',\n [ValidationStatus.TOO_FAR]: 'Aproxime-se da câmera',\n [ValidationStatus.OFF_CENTER]: 'Centralize o rosto no centro do oval',\n [ValidationStatus.FACE_OBSTRUCTED]: 'Mantenha o rosto totalmente visível. Remova as mãos do rosto.',\n [ValidationStatus.HEAD_NOT_STRAIGHT]: 'Olhe diretamente para a câmera e mantenha a cabeça reta.',\n [ValidationStatus.MULTIPLE_FACES]: 'Mantenha apenas uma pessoa no quadro.',\n [ValidationStatus.POOR_ILLUMINATION]: 'Procure um ambiente com boa iluminação.',\n [ValidationStatus.NOT_NEUTRAL_EXPRESSION]: 'Mantenha expressão neutra: boca fechada, sem sorrir e olhos abertos.',\n [ValidationStatus.DARK_GLASSES]: 'Remova os óculos escuros. Óculos de grau são permitidos.',\n [ValidationStatus.STAY_STILL]: 'Fique imóvel para capturar a foto',\n [ValidationStatus.CAPTURING]: 'Capturando...',\n [ValidationStatus.SUCCESS]: 'Captura realizada!',\n [ValidationStatus.ERROR]: 'Ocorreu um erro.',\n },\n en: {\n [ValidationStatus.INITIALIZING]: 'Initializing camera and detector...',\n [ValidationStatus.NO_FACE_DETECTED]: 'Position your face in the center of the oval.',\n [ValidationStatus.FACE_DETECTED]: 'Analyzing...',\n [ValidationStatus.TOO_CLOSE]: 'Move back a little',\n [ValidationStatus.TOO_FAR]: 'Move closer to the camera',\n [ValidationStatus.OFF_CENTER]: 'Center your face in the center of the oval',\n [ValidationStatus.FACE_OBSTRUCTED]: 'Keep your face fully visible. Remove your hands from your face.',\n [ValidationStatus.HEAD_NOT_STRAIGHT]: 'Look directly at the camera and keep your head straight.',\n [ValidationStatus.MULTIPLE_FACES]: 'Keep only one person in the frame.',\n [ValidationStatus.POOR_ILLUMINATION]: 'Find a well-lit environment and center your face in the oval.',\n [ValidationStatus.NOT_NEUTRAL_EXPRESSION]: 'Keep a neutral expression: mouth closed, no smiling, and eyes open.',\n [ValidationStatus.DARK_GLASSES]: 'Remove sunglasses. Prescription glasses are allowed.',\n [ValidationStatus.STAY_STILL]: 'Stay still to capture the photo',\n [ValidationStatus.CAPTURING]: 'Capturing...',\n [ValidationStatus.SUCCESS]: 'Capture complete!',\n [ValidationStatus.ERROR]: 'An error occurred.',\n },\n es: {\n [ValidationStatus.INITIALIZING]: 'Inicializando cámara y detector...',\n [ValidationStatus.NO_FACE_DETECTED]: 'Coloque su rostro en el centro del óvalo.',\n [ValidationStatus.FACE_DETECTED]: 'Analizando...',\n [ValidationStatus.TOO_CLOSE]: 'Aléjese un poco',\n [ValidationStatus.TOO_FAR]: 'Acérquese a la cámara',\n [ValidationStatus.OFF_CENTER]: 'Centre el rostro en el centro del óvalo',\n [ValidationStatus.FACE_OBSTRUCTED]: 'Mantenga el rostro totalmente visible. Quite las manos del rostro.',\n [ValidationStatus.HEAD_NOT_STRAIGHT]: 'Mire directamente a la cámara y mantenga la cabeza recta.',\n [ValidationStatus.MULTIPLE_FACES]: 'Mantenga solo una persona en el encuadre.',\n [ValidationStatus.POOR_ILLUMINATION]: 'Busque un ambiente con buena iluminación y centre su rostro en el óvalo.',\n [ValidationStatus.NOT_NEUTRAL_EXPRESSION]: 'Mantenga expresión neutra: boca cerrada, sin sonreír y ojos abiertos.',\n [ValidationStatus.DARK_GLASSES]: 'Quite las gafas de sol. Las gafas graduadas están permitidas.',\n [ValidationStatus.STAY_STILL]: 'Permanezca quieto para capturar la foto',\n [ValidationStatus.CAPTURING]: 'Capturando...',\n [ValidationStatus.SUCCESS]: '¡Captura realizada!',\n [ValidationStatus.ERROR]: 'Ocurrió un error.',\n },\n};\nconst unknownStatusByLocale = {\n 'pt-BR': 'Status desconhecido.',\n en: 'Unknown status.',\n es: 'Estado desconhecido.',\n};\n/**\n * Returns the validation messages for the given locale.\n */\nexport function getValidationMessages(locale) {\n return Object.assign({}, messages[locale]);\n}\n/**\n * Returns the message for a given status and locale.\n */\nexport function getMessage(status, locale) {\n var _a;\n return (_a = messages[locale][status]) !== null && _a !== void 0 ? _a : unknownStatusByLocale[locale];\n}\n/**\n * Returns the \"Loading models...\" message for a given locale (used during model load).\n */\nexport function getLoadingModelsMessage(locale) {\n const loading = {\n 'pt-BR': 'Carregando...',\n en: 'Loading...',\n es: 'Cargando...',\n };\n return loading[locale];\n}\n","import { ValidationStatus } from './types';\n/**\n * Calcula o brilho médio de uma região da imagem (0-255).\n */\nexport function calculateAverageBrightness(imageData) {\n const data = imageData.data;\n let sum = 0;\n for (let i = 0; i < data.length; i += 4) {\n const r = data[i];\n const g = data[i + 1];\n const b = data[i + 2];\n // Luminance formula: ITU-R BT.709\n const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;\n sum += luminance;\n }\n return sum / (data.length / 4);\n}\n/**\n * Verifica se a face está na distância adequada (baseado no tamanho do bounding box).\n */\nexport function checkFaceDistance(boundingBox, minFaceSizeFactor = 0.18, maxFaceSizeFactor = 0.70) {\n // boundingBox.width é normalizado (0-1)\n const faceWidthRatio = boundingBox.width;\n if (faceWidthRatio < minFaceSizeFactor)\n return 'TOO_FAR';\n if (faceWidthRatio > maxFaceSizeFactor)\n return 'TOO_CLOSE';\n return 'OK';\n}\n/**\n * MediaPipe Face Mesh landmark indices (478 pontos):\n * - Nose tip: 4\n * - Left eye: 33, 133, 159, 145\n * - Right eye: 263, 362, 386, 374\n * - Mouth: 61, 291, 0, 17 (outer lips)\n * - Face oval: contorno do rosto\n * - Ears: left ear (234), right ear (454)\n * - Eye iris: left (468-471), right (473-476)\n * - Eyelids: upper left (159, 145), lower left (144, 153), upper right (386, 374), lower right (373, 380)\n * - Mouth corners: left (61), right (291)\n * - Lips: upper (13), lower (14)\n */\nconst MEDIAPIPE_NOSE_TIP = 4;\nconst MEDIAPIPE_LEFT_EYE = [33, 133, 159, 145];\nconst MEDIAPIPE_RIGHT_EYE = [263, 362, 386, 374];\nconst MEDIAPIPE_MOUTH_OUTER = [61, 291, 0, 17, 39, 269, 270, 409];\nconst MEDIAPIPE_LEFT_EAR = 234;\nconst MEDIAPIPE_RIGHT_EAR = 454;\nconst MEDIAPIPE_LEFT_EYE_TOP = 159;\nconst MEDIAPIPE_LEFT_EYE_BOTTOM = 144;\nconst MEDIAPIPE_RIGHT_EYE_TOP = 386;\nconst MEDIAPIPE_RIGHT_EYE_BOTTOM = 373;\nconst MEDIAPIPE_MOUTH_LEFT_CORNER = 61;\nconst MEDIAPIPE_MOUTH_RIGHT_CORNER = 291;\nconst MEDIAPIPE_MOUTH_TOP = 13;\nconst MEDIAPIPE_MOUTH_BOTTOM = 14;\n/**\n * Proporções do oval da moldura para centralização.\n * Ajustado para o tamanho do container (512x384) e para não encostar\n * nas bordas do vídeo em integrações como o modal do datasync-front.\n */\nconst OVAL_RADIUS_X_FACTOR = 0.20; // Raio horizontal do oval (20% da largura)\nconst OVAL_RADIUS_Y_FACTOR = 0.34; // Raio vertical do oval (34% da altura)\n/**\n * Verifica se um ponto (normalizado 0-1) está dentro do oval de enquadramento.\n */\nexport function isPointInsideOval(pointX, pointY, frameWidth, frameHeight) {\n // Converter ponto normalizado para pixels\n const px = pointX * frameWidth;\n const py = pointY * frameHeight;\n const cx = frameWidth / 2;\n const cy = frameHeight / 2;\n const rx = frameWidth * OVAL_RADIUS_X_FACTOR;\n const ry = frameHeight * OVAL_RADIUS_Y_FACTOR;\n const dx = (px - cx) / rx;\n const dy = (py - cy) / ry;\n const distanceSquared = dx * dx + dy * dy;\n // Mais rigoroso: nariz deve ficar próximo do centro do oval,\n // não apenas \"em qualquer ponto dentro\" da borda.\n // 1.0 = borda exata do oval; 0.6 ≈ região central mais restrita.\n return distanceSquared <= 0.6;\n}\n/**\n * Verifica se o bounding box da face cabe dentro do oval de enquadramento.\n * Versão simplificada: verifica apenas o centro e limites principais sem margem excessiva.\n */\nexport function isFaceBoundingBoxInsideOval(boundingBox, frameWidth, frameHeight) {\n const cx = frameWidth / 2;\n const cy = frameHeight / 2;\n const rx = frameWidth * OVAL_RADIUS_X_FACTOR;\n const ry = frameHeight * OVAL_RADIUS_Y_FACTOR;\n // Converter bbox normalizado para pixels\n const faceLeft = boundingBox.xMin * frameWidth;\n const faceRight = (boundingBox.xMin + boundingBox.width) * frameWidth;\n const faceTop = boundingBox.yMin * frameHeight;\n const faceBottom = (boundingBox.yMin + boundingBox.height) * frameHeight;\n const faceCenterX = (faceLeft + faceRight) / 2;\n const faceCenterY = (faceTop + faceBottom) / 2;\n // 1. Centro da face deve estar dentro do oval (sem folga)\n const centerDx = (faceCenterX - cx) / rx;\n const centerDy = (faceCenterY - cy) / ry;\n if (centerDx * centerDx + centerDy * centerDy > 1.0) {\n return false;\n }\n // 2. Verificar todos os cantos SEM margem adicional\n const corners = [\n { x: faceLeft, y: faceTop }, // Top-left\n { x: faceRight, y: faceTop }, // Top-right\n { x: faceLeft, y: faceBottom }, // Bottom-left\n { x: faceRight, y: faceBottom }, // Bottom-right\n ];\n // Mais rigoroso: nenhum canto pode ficar significativamente fora do oval\n let cornersOutside = 0;\n for (const corner of corners) {\n const dx = (corner.x - cx) / rx;\n const dy = (corner.y - cy) / ry;\n if (dx * dx + dy * dy > 1.0) {\n cornersOutside++;\n }\n }\n // Não permitir cantos fora\n return cornersOutside === 0;\n}\n/**\n * Verifica se a cabeça está reta (sem inclinação lateral, horizontal ou vertical).\n * MediaPipe: usa landmarks dos olhos, nariz e boca.\n */\nexport function isHeadStraight(landmarks, maxTiltDegrees = 25) {\n if (landmarks.length < 478)\n return false;\n // Pontos dos olhos\n const leftEye = landmarks[MEDIAPIPE_LEFT_EYE[0]]; // 33\n const rightEye = landmarks[MEDIAPIPE_RIGHT_EYE[0]]; // 263\n const nose = landmarks[MEDIAPIPE_NOSE_TIP]; // 4\n // Pontos da boca para pitch\n const upperLip = landmarks[13]; // Lábio superior central\n const lowerLip = landmarks[14]; // Lábio inferior central\n const chin = landmarks[152]; // Queixo\n const forehead = landmarks[10]; // Testa\n // Roll: inclinação lateral (olhos desalinhados verticalmente)\n const eyeDeltaY = Math.abs(leftEye.y - rightEye.y);\n const eyeDeltaX = Math.abs(leftEye.x - rightEye.x);\n if (eyeDeltaX < 0.01)\n return false; // Proteção divisão por zero\n const rollRatio = eyeDeltaY / eyeDeltaX;\n const rollAngleDeg = Math.atan(rollRatio) * (180 / Math.PI);\n if (rollAngleDeg > maxTiltDegrees)\n return false;\n // Yaw: desvio horizontal (nariz deslocado do centro dos olhos)\n // NOTA: validação adicional usando orelhas é feita em isYawAcceptable()\n const midEyesX = (leftEye.x + rightEye.x) / 2;\n const noseOffsetX = nose.x - midEyesX;\n const eyeDist = Math.abs(leftEye.x - rightEye.x);\n if (eyeDist < 0.01)\n return false;\n const yawRatio = Math.abs(noseOffsetX) / eyeDist;\n const yawAngleDeg = Math.atan(yawRatio) * (180 / Math.PI);\n if (yawAngleDeg > maxTiltDegrees)\n return false;\n // Validação adicional de yaw usando orelhas (mais precisa para rostos na diagonal)\n if (!isYawAcceptable(landmarks))\n return false;\n // Pitch: inclinação vertical (cabeça para cima/baixo)\n const midEyesY = (leftEye.y + rightEye.y) / 2;\n const mouthY = (upperLip.y + lowerLip.y) / 2;\n // Verificações SIMPLIFICADAS apenas para inclinações EXTREMAS\n // Permitir variações naturais de postura\n // 1. Verificar altura total da face é plausível\n const faceHeight = chin.y - forehead.y;\n if (faceHeight < 0.10) {\n // Face extremamente \"achatada\" verticalmente = inclinação MUITO severa\n return false;\n }\n // 2. Verificar apenas ordem básica dos elementos principais\n // Apenas rejeitar casos EXTREMOS onde a ordem está completamente invertida\n // Testa deve estar ACIMA dos olhos (com margem de tolerância)\n if (forehead.y > midEyesY + 0.02) {\n return false; // Testa abaixo dos olhos = MUITO inclinado para trás\n }\n // Olhos devem estar ACIMA do nariz (com margem)\n if (midEyesY > nose.y + 0.02) {\n return false; // Olhos abaixo do nariz = inclinação extrema\n }\n // Nariz deve estar ACIMA da boca (com margem)\n if (nose.y > mouthY + 0.02) {\n return false; // Nariz abaixo da boca = inclinação extrema\n }\n // Boca deve estar ACIMA do queixo (sempre deve ser verdade)\n if (mouthY >= chin.y) {\n return false; // Geometria impossível\n }\n // 3. Verificar proporções - detectar inclinações extremas\n const foreheadToEyes = midEyesY - forehead.y;\n const eyesToNose = nose.y - midEyesY;\n const noseToMouth = mouthY - nose.y;\n const mouthToChin = chin.y - mouthY;\n const foreheadEyesRatio = foreheadToEyes / faceHeight;\n const eyesNoseRatio = eyesToNose / faceHeight;\n const noseMouthRatio = noseToMouth / faceHeight;\n const mouthChinRatio = mouthToChin / faceHeight;\n // Testa-olhos:\n // - Se MUITO GRANDE (>38%) = cabeça inclinada para FRENTE (testa dominante)\n // - Se MUITO PEQUENO (<6%) = cabeça inclinada para TRÁS (testa oculta)\n if (foreheadEyesRatio < 0.06 || foreheadEyesRatio > 0.38) {\n return false;\n }\n // Olhos-nariz: aceitar de 3% a 30%\n if (eyesNoseRatio < 0.03 || eyesNoseRatio > 0.30) {\n return false;\n }\n // Nariz-boca: aceitar de 2% a 25%\n if (noseMouthRatio < 0.02 || noseMouthRatio > 0.25) {\n return false;\n }\n // Boca-queixo: MUITO flexível (barba pode interferir)\n // Apenas rejeitar casos extremos\n if (mouthChinRatio < 0.04 || mouthChinRatio > 0.38) {\n return false;\n }\n return true;\n}\n/**\n * Verifica se a geometria do rosto é plausível (boca visível, não obstruída por mão).\n * MediaPipe: analisa distância nariz-boca e extensão vertical da boca.\n */\nexport function isFaceGeometryPlausible(landmarks, boundingBox) {\n if (landmarks.length < 478)\n return false;\n const nose = landmarks[MEDIAPIPE_NOSE_TIP];\n // Pontos da boca (contorno externo)\n const mouthPoints = MEDIAPIPE_MOUTH_OUTER.map(idx => landmarks[idx]);\n const mouthCenterY = mouthPoints.reduce((s, p) => s + p.y, 0) / mouthPoints.length;\n const mouthMinY = Math.min(...mouthPoints.map(p => p.y));\n const mouthMaxY = Math.max(...mouthPoints.map(p => p.y));\n const mouthVerticalSpread = mouthMaxY - mouthMinY;\n const boxHeight = boundingBox.height;\n // Boca deve estar abaixo do nariz (com margem de tolerância)\n if (mouthCenterY < nose.y - 0.01)\n return false;\n // Distância nariz–centro da boca: mínimo 6% da altura (reduzido de 10% para aceitar barbas e óculos)\n const noseToMouthDist = mouthCenterY - nose.y;\n if (noseToMouthDist < 0.06 * boxHeight)\n return false;\n // Extensão vertical da boca: mínimo 2% da altura (reduzido de 3%)\n if (mouthVerticalSpread < 0.02 * boxHeight)\n return false;\n return true;\n}\n/**\n * Detecta se a pessoa está usando óculos escuros através da análise de luminosidade dos olhos.\n * Óculos de grau geralmente não bloqueiam completamente a luz, permitindo ver os olhos.\n */\nexport function hasDarkGlasses(video, landmarks) {\n if (landmarks.length < 478)\n return false;\n try {\n // Criar canvas temporário para capturar regiões dos olhos\n const tempCanvas = document.createElement('canvas');\n const ctx = tempCanvas.getContext('2d');\n if (!ctx)\n return false;\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n // Definir landmarks dos olhos (área maior que inclui região ao redor)\n const leftEyeLandmarks = [\n landmarks[33], // Canto externo\n landmarks[133], // Canto interno\n landmarks[159], // Superior\n landmarks[144], // Inferior\n landmarks[145], // Centro\n ];\n const rightEyeLandmarks = [\n landmarks[263], // Canto externo\n landmarks[362], // Canto interno\n landmarks[386], // Superior\n landmarks[373], // Inferior\n landmarks[374], // Centro\n ];\n // Função para calcular bounding box de uma região\n const getBoundingBox = (eyeLandmarks) => {\n const xs = eyeLandmarks.map(l => l.x * videoWidth);\n const ys = eyeLandmarks.map(l => l.y * videoHeight);\n const minX = Math.max(0, Math.min(...xs) - 5);\n const maxX = Math.min(videoWidth, Math.max(...xs) + 5);\n const minY = Math.max(0, Math.min(...ys) - 5);\n const maxY = Math.min(videoHeight, Math.max(...ys) + 5);\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n };\n // Função para calcular luminosidade média de uma região\n const getRegionBrightness = (box) => {\n tempCanvas.width = box.width;\n tempCanvas.height = box.height;\n ctx.drawImage(video, box.x, box.y, box.width, box.height, 0, 0, box.width, box.height);\n const imageData = ctx.getImageData(0, 0, box.width, box.height);\n return calculateAverageBrightness(imageData);\n };\n // Analisar ambos os olhos\n const leftEyeBox = getBoundingBox(leftEyeLandmarks);\n const rightEyeBox = getBoundingBox(rightEyeLandmarks);\n const leftEyeBrightness = getRegionBrightness(leftEyeBox);\n const rightEyeBrightness = getRegionBrightness(rightEyeBox);\n const avgEyeBrightness = (leftEyeBrightness + rightEyeBrightness) / 2;\n // Threshold: se a região dos olhos está muito escura (< 40 em escala 0-255)\n // isso indica óculos escuros. Óculos de grau não bloqueiam tanto a luz.\n // Ajustado para 35 para ser mais sensível a óculos escuros\n return avgEyeBrightness < 35;\n }\n catch (error) {\n console.warn('Erro ao detectar óculos escuros:', error);\n return false; // Em caso de erro, não bloqueia a captura\n }\n}\n/**\n * Verifica se a expressão facial é neutra (sem sorriso, boca fechada, olhos abertos).\n * Rejeita: sorriso, boca aberta, olhos fechados.\n */\nexport function isNeutralExpression(landmarks) {\n if (landmarks.length < 478)\n return false;\n // 1. Verificar se olhos estão abertos\n const leftEyeTop = landmarks[MEDIAPIPE_LEFT_EYE_TOP];\n const leftEyeBottom = landmarks[MEDIAPIPE_LEFT_EYE_BOTTOM];\n const rightEyeTop = landmarks[MEDIAPIPE_RIGHT_EYE_TOP];\n const rightEyeBottom = landmarks[MEDIAPIPE_RIGHT_EYE_BOTTOM];\n const leftEyeOpenness = Math.abs(leftEyeTop.y - leftEyeBottom.y);\n const rightEyeOpenness = Math.abs(rightEyeTop.y - rightEyeBottom.y);\n // Olhos devem estar abertos (mínimo 1% de abertura em coordenadas normalizadas)\n if (leftEyeOpenness < 0.01 || rightEyeOpenness < 0.01) {\n return false; // Olho(s) fechado(s)\n }\n // 2. Verificar se boca está fechada\n const mouthTop = landmarks[MEDIAPIPE_MOUTH_TOP];\n const mouthBottom = landmarks[MEDIAPIPE_MOUTH_BOTTOM];\n const mouthOpenness = Math.abs(mouthTop.y - mouthBottom.y);\n // Boca deve estar relativamente fechada (máximo 2.5% de abertura)\n if (mouthOpenness > 0.025) {\n return false; // Boca aberta\n }\n // 3. Verificar se há sorriso (cantos da boca elevados)\n const mouthLeftCorner = landmarks[MEDIAPIPE_MOUTH_LEFT_CORNER];\n const mouthRightCorner = landmarks[MEDIAPIPE_MOUTH_RIGHT_CORNER];\n const nose = landmarks[MEDIAPIPE_NOSE_TIP];\n // Calcular posição vertical média dos cantos da boca relativo ao nariz\n const mouthCornersAvgY = (mouthLeftCorner.y + mouthRightCorner.y) / 2;\n const noseMouthDistance = mouthCornersAvgY - nose.y;\n // Se os cantos da boca estão muito elevados (próximos ao nariz), é sorriso\n // Em expressão neutra, os cantos devem estar significativamente abaixo do nariz\n if (noseMouthDistance < 0.05) {\n return false; // Sorriso (cantos da boca elevados)\n }\n return true;\n}\n/**\n * Verifica yaw (inclinação lateral) usando visibilidade das orelhas.\n * Quando o rosto está virado para o lado, uma orelha fica mais visível que a outra.\n */\nexport function isYawAcceptable(landmarks) {\n if (landmarks.length < 478)\n return false;\n const leftEar = landmarks[MEDIAPIPE_LEFT_EAR];\n const rightEar = landmarks[MEDIAPIPE_RIGHT_EAR];\n const nose = landmarks[MEDIAPIPE_NOSE_TIP];\n // Calcular distância de cada orelha ao nariz (em coordenadas normalizadas)\n const leftEarToNoseX = Math.abs(leftEar.x - nose.x);\n const rightEarToNoseX = Math.abs(rightEar.x - nose.x);\n // Calcular ratio de assimetria\n const asymmetryRatio = leftEarToNoseX > 0.01 && rightEarToNoseX > 0.01\n ? Math.max(leftEarToNoseX, rightEarToNoseX) / Math.min(leftEarToNoseX, rightEarToNoseX)\n : 1.0;\n // Se uma orelha está muito mais longe do nariz que a outra, o rosto está na diagonal\n // Permitir até 40% de assimetria (1.4 ratio)\n if (asymmetryRatio > 1.4) {\n return false; // Rosto muito virado para o lado\n }\n // Verificar visibilidade Z (profundidade) se disponível\n // Em MediaPipe, coordenada Z indica profundidade relativa\n if (leftEar.z !== undefined && rightEar.z !== undefined) {\n const zDifference = Math.abs(leftEar.z - rightEar.z);\n // Se a diferença de profundidade é muito grande, o rosto está na diagonal\n if (zDifference > 0.05) {\n return false; // Rosto virado lateralmente (detectado por profundidade)\n }\n }\n return true;\n}\n/**\n * Verifica se a face está estável (sem movimento significativo entre frames).\n */\nexport function isFaceStable(currentFace, previousFace, movementThreshold = 5, frameWidth = 512, frameHeight = 384) {\n if (!currentFace || !previousFace)\n return false;\n // Converter coordenadas normalizadas para pixels\n const currentCenterX = (currentFace.boundingBox.xMin + currentFace.boundingBox.width / 2) * frameWidth;\n const currentCenterY = (currentFace.boundingBox.yMin + currentFace.boundingBox.height / 2) * frameHeight;\n const previousCenterX = (previousFace.boundingBox.xMin + previousFace.boundingBox.width / 2) * frameWidth;\n const previousCenterY = (previousFace.boundingBox.yMin + previousFace.boundingBox.height / 2) * frameHeight;\n const deltaX = Math.abs(currentCenterX - previousCenterX);\n const deltaY = Math.abs(currentCenterY - previousCenterY);\n const deltaWidth = Math.abs(currentFace.boundingBox.width - previousFace.boundingBox.width) * frameWidth;\n const deltaHeight = Math.abs(currentFace.boundingBox.height - previousFace.boundingBox.height) * frameHeight;\n return (deltaX <= movementThreshold &&\n deltaY <= movementThreshold &&\n deltaWidth <= movementThreshold * 2 &&\n deltaHeight <= movementThreshold * 2);\n}\n/**\n * Calcula distância entre um ponto da mão e o centro do rosto (normalizado).\n * Retorna true se a mão estiver próxima ao rosto (indicando possível obstrução).\n */\nexport function isHandNearFace(handData, faceBoundingBox, maxDistance = 0.15) {\n const faceCenterX = faceBoundingBox.xMin + faceBoundingBox.width / 2;\n const faceCenterY = faceBoundingBox.yMin + faceBoundingBox.height / 2;\n // Verificar se algum ponto da mão está próximo ao centro do rosto\n for (const landmark of handData.landmarks) {\n const dx = landmark.x - faceCenterX;\n const dy = landmark.y - faceCenterY;\n const distance = Math.sqrt(dx * dx + dy * dy);\n if (distance < maxDistance) {\n return true;\n }\n }\n return false;\n}\n/**\n * Desenha overlay de feedback visual (oval, status, debug info).\n */\nexport function drawOverlay(canvas, debugMode, status, faceData, handData) {\n const ctx = canvas.getContext('2d');\n if (!ctx)\n return;\n const frameWidth = canvas.width;\n const frameHeight = canvas.height;\n const frameCenterX = frameWidth / 2;\n const frameCenterY = frameHeight / 2;\n ctx.clearRect(0, 0, frameWidth, frameHeight);\n // Oval: guia de enquadramento\n const radiusX = frameWidth * OVAL_RADIUS_X_FACTOR;\n const radiusY = frameHeight * OVAL_RADIUS_Y_FACTOR;\n // 1) Área fora do oval esmaecida (mais transparente)\n ctx.fillStyle = 'rgba(255, 255, 255, 0.35)';\n ctx.fillRect(0, 0, frameWidth, frameHeight);\n ctx.save();\n ctx.beginPath();\n ctx.ellipse(frameCenterX, frameCenterY, radiusX, radiusY, 0, 0, 2 * Math.PI);\n ctx.closePath();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.fill();\n ctx.restore();\n // 2) Borda do oval\n ctx.strokeStyle = 'rgba(255, 255, 255, 0.9)';\n ctx.lineWidth = 3;\n ctx.beginPath();\n ctx.ellipse(frameCenterX, frameCenterY, radiusX, radiusY, 0, 0, 2 * Math.PI);\n ctx.stroke();\n // 3) Mira central\n const crosshairLength = 6;\n ctx.strokeStyle = 'rgba(255, 255, 255, 0.45)';\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(frameCenterX - crosshairLength, frameCenterY);\n ctx.lineTo(frameCenterX + crosshairLength, frameCenterY);\n ctx.moveTo(frameCenterX, frameCenterY - crosshairLength);\n ctx.lineTo(frameCenterX, frameCenterY + crosshairLength);\n ctx.stroke();\n // Debug: desenhar face bounding box adaptável e landmarks\n if (debugMode && faceData) {\n // Calcular bounding box adaptável baseado nos landmarks reais\n const landmarks = faceData.landmarks;\n if (landmarks.length >= 478) {\n // Pontos importantes para definir limites da face\n const forehead = landmarks[10];\n const chin = landmarks[152];\n const leftEar = landmarks[234];\n const rightEar = landmarks[454];\n // Calcular limites da face com margem\n const allXCoords = landmarks.map(l => l.x);\n const allYCoords = landmarks.map(l => l.y);\n const minX = Math.min(...allXCoords);\n const maxX = Math.max(...allXCoords);\n const minY = Math.min(...allYCoords);\n const maxY = Math.max(...allYCoords);\n // Adicionar margem de 8% para incluir toda a cabeça\n const width = maxX - minX;\n const height = maxY - minY;\n const margin = 0.08;\n const x = (minX - width * margin) * frameWidth;\n const y = (minY - height * margin) * frameHeight;\n const w = width * (1 + 2 * margin) * frameWidth;\n const h = height * (1 + 2 * margin) * frameHeight;\n // Bounding box colorido por status (adaptável)\n let boxColor = 'red';\n if (status === ValidationStatus.STAY_STILL || status === ValidationStatus.CAPTURING) {\n boxColor = 'lime';\n }\n else if (status === ValidationStatus.FACE_DETECTED) {\n boxColor = 'yellow';\n }\n ctx.strokeStyle = boxColor;\n ctx.lineWidth = 3;\n ctx.strokeRect(x, y, w, h);\n // Desenhar pontos de referência chave\n const nose = landmarks[MEDIAPIPE_NOSE_TIP];\n const leftEyePoint = landmarks[MEDIAPIPE_LEFT_EYE[0]];\n const rightEyePoint = landmarks[MEDIAPIPE_RIGHT_EYE[0]];\n // Nariz (cyan)\n ctx.fillStyle = 'cyan';\n ctx.beginPath();\n ctx.arc(nose.x * frameWidth, nose.y * frameHeight, 5, 0, 2 * Math.PI);\n ctx.fill();\n // Testa (magenta) - importante para validação de inclinação\n ctx.fillStyle = 'magenta';\n ctx.beginPath();\n ctx.arc(forehead.x * frameWidth, forehead.y * frameHeight, 4, 0, 2 * Math.PI);\n ctx.fill();\n // Queixo (verde)\n ctx.fillStyle = 'lime';\n ctx.beginPath();\n ctx.arc(chin.x * frameWidth, chin.y * frameHeight, 4, 0, 2 * Math.PI);\n ctx.fill();\n // Landmarks dos olhos para visualização da detecção de óculos\n // Olho esquerdo (amarelo)\n ctx.fillStyle = 'yellow';\n const leftEyeLandmarks = [\n landmarks[33], // Canto externo\n landmarks[133], // Canto interno\n landmarks[159], // Superior (pálpebra superior)\n landmarks[144], // Inferior (pálpebra inferior)\n landmarks[145], // Centro\n ];\n leftEyeLandmarks.forEach(landmark => {\n ctx.beginPath();\n ctx.arc(landmark.x * frameWidth, landmark.y * frameHeight, 3, 0, 2 * Math.PI);\n ctx.fill();\n });\n // Olho direito (amarelo)\n ctx.fillStyle = 'yellow';\n const rightEyeLandmarks = [\n landmarks[263], // Canto externo\n landmarks[362], // Canto interno\n landmarks[386], // Superior (pálpebra superior)\n landmarks[373], // Inferior (pálpebra inferior)\n landmarks[374], // Centro\n ];\n rightEyeLandmarks.forEach(landmark => {\n ctx.beginPath();\n ctx.arc(landmark.x * frameWidth, landmark.y * frameHeight, 3, 0, 2 * Math.PI);\n ctx.fill();\n });\n // Orelhas (roxo) - usadas na detecção de yaw\n ctx.fillStyle = 'purple';\n ctx.beginPath();\n ctx.arc(leftEar.x * frameWidth, leftEar.y * frameHeight, 3, 0, 2 * Math.PI);\n ctx.fill();\n ctx.beginPath();\n ctx.arc(rightEar.x * frameWidth, rightEar.y * frameHeight, 3, 0, 2 * Math.PI);\n ctx.fill();\n }\n }\n // Debug: desenhar mãos\n if (debugMode && handData && handData.length > 0) {\n handData.forEach((hand) => {\n ctx.fillStyle = 'orange';\n hand.landmarks.forEach((landmark) => {\n ctx.beginPath();\n ctx.arc(landmark.x * frameWidth, landmark.y * frameHeight, 3, 0, 2 * Math.PI);\n ctx.fill();\n });\n });\n }\n}\n","var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nimport { FaceLandmarker, HandLandmarker, FilesetResolver } from '@mediapipe/tasks-vision';\nimport { ValidationStatus, } from './types';\nimport { getMessage, getLoadingModelsMessage } from './i18n';\nimport { calculateAverageBrightness, checkFaceDistance, isFaceStable, isHeadStraight, isFaceGeometryPlausible, isPointInsideOval, isFaceBoundingBoxInsideOval, isHandNearFace, isNeutralExpression, hasDarkGlasses, drawOverlay, } from './utils';\nconst DEFAULT_LOCALE = 'en';\nconst defaultOptions = {\n container: undefined,\n ui: 'default',\n autoStart: true,\n mirror: true,\n videoConstraints: undefined,\n overlayCanvasElement: undefined,\n videoWidth: 512,\n videoHeight: 384,\n minDetectionConfidence: 0.4,\n minIlluminationThreshold: 50,\n minFaceSizeFactor: 0.15,\n maxFaceSizeFactor: 0.75,\n stabilizationTimeThreshold: 1000,\n stabilityMovementThreshold: 5,\n minFaceVisibilityScore: 0.4,\n maxHeadTiltDegrees: 30,\n maxHandFaceDistance: 0.15,\n debugMode: false,\n locale: DEFAULT_LOCALE,\n customMessages: {},\n onStatusUpdate: undefined,\n onCaptureSuccess: undefined,\n onError: undefined,\n};\n/**\n * FaceValidator SDK - Real-time selfie validation with MediaPipe\n */\nexport class FaceValidator {\n constructor(options) {\n this.faceLandmarker = null;\n this.handLandmarker = null;\n this.animationFrameId = null;\n this.lastDetection = null;\n this.stableSince = null;\n this.isCapturing = false;\n this.containerElement = null;\n this.statusElement = null;\n this.uiRootElement = null;\n this.cameraStream = null;\n this.cameraReadyPromise = null;\n this.managedElements = false;\n this.managedCamera = false;\n this.injectedStyleElement = null;\n this.options = this.resolveOptions(options);\n this.setupElements();\n this.setStatus(ValidationStatus.INITIALIZING);\n this.cameraReadyPromise = this.options.autoStart ? this.initCamera() : Promise.resolve();\n this.init();\n }\n resolveOptions(options) {\n const modelPath = options.modelPath || 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm';\n return Object.assign(Object.assign(Object.assign({}, defaultOptions), options), { modelPath, locale: options.locale || DEFAULT_LOCALE, customMessages: options.customMessages || {}, onStatusUpdate: options.onStatusUpdate || (() => { }), onCaptureSuccess: options.onCaptureSuccess || (() => { }), onError: options.onError || (() => { }), videoConstraints: options.videoConstraints ||\n {\n width: { ideal: options.videoWidth || defaultOptions.videoWidth },\n height: { ideal: options.videoHeight || defaultOptions.videoHeight },\n facingMode: 'user',\n } });\n }\n setupElements() {\n const container = this.resolveContainer(this.options.container);\n this.containerElement = container;\n if (this.options.ui === 'default' && container) {\n this.ensureDefaultUI(container);\n }\n if (!this.options.videoElement) {\n if (!container) {\n throw new Error('FaceValidator requires either videoElement or container.');\n }\n const video = document.createElement('video');\n video.autoplay = true;\n video.playsInline = true;\n video.muted = true;\n video.className = 'fv-sdk-video';\n const canvas = document.createElement('canvas');\n canvas.className = 'fv-sdk-canvas';\n this.attachMediaElements(container, video, canvas);\n this.options.videoElement = video;\n this.options.overlayCanvasElement = canvas;\n this.managedElements = true;\n }\n else if (!this.options.overlayCanvasElement && container) {\n const canvas = document.createElement('canvas');\n canvas.className = 'fv-sdk-canvas';\n this.attachMediaElements(container, this.options.videoElement, canvas);\n this.options.overlayCanvasElement = canvas;\n this.managedElements = true;\n }\n if (this.options.mirror) {\n this.applyMirrorStyles();\n }\n }\n resolveContainer(container) {\n if (!container)\n return null;\n if (typeof container === 'string') {\n return document.querySelector(container);\n }\n return container;\n }\n ensureDefaultUI(container) {\n container.innerHTML = '';\n container.classList.add('fv-sdk-root');\n const mediaWrapper = document.createElement('div');\n mediaWrapper.className = 'fv-sdk-media';\n container.appendChild(mediaWrapper);\n const status = document.createElement('div');\n status.className = 'fv-sdk-status';\n container.appendChild(status);\n this.statusElement = status;\n this.uiRootElement = container;\n this.injectDefaultStyles();\n }\n attachMediaElements(container, video, canvas) {\n const mediaWrapper = container.querySelector('.fv-sdk-media');\n if (mediaWrapper) {\n mediaWrapper.appendChild(video);\n mediaWrapper.appendChild(canvas);\n return;\n }\n const wrapper = document.createElement('div');\n wrapper.className = 'fv-sdk-media';\n wrapper.appendChild(video);\n wrapper.appendChild(canvas);\n container.appendChild(wrapper);\n }\n injectDefaultStyles() {\n if (this.injectedStyleElement || document.querySelector('style[data-fv-sdk=\"true\"]'))\n return;\n const style = document.createElement('style');\n style.setAttribute('data-fv-sdk', 'true');\n style.textContent = `\n .fv-sdk-root { display: flex; flex-direction: column; gap: 12px; width: 100%; }\n .fv-sdk-media { position: relative; width: 100%; max-width: 512px; height: 384px; margin: 0 auto; background: #000; border-radius: 10px; overflow: hidden; }\n .fv-sdk-video, .fv-sdk-canvas { width: 100%; height: 100%; display: block; object-fit: contain; }\n .fv-sdk-canvas { position: absolute; top: 0; left: 0; }\n .fv-sdk-status { text-align: center; display: flex; align-items: center; justify-content: center; font-size: 14px; padding: 10px 12px; border-radius: 8px; font-weight: 600; background: #f8f9fa; color: #555; }\n .fv-sdk-status.success { background: #d4edda; color: #155724; }\n .fv-sdk-status.error { background: #f8d7da; color: #721c24; }\n .fv-sdk-status.warning { background: #fff3cd; color: #856404; }\n `;\n document.head.appendChild(style);\n this.injectedStyleElement = style;\n }\n applyMirrorStyles() {\n const video = this.options.videoElement;\n const canvas = this.options.overlayCanvasElement;\n if (video)\n video.style.transform = 'scaleX(-1)';\n if (canvas)\n canvas.style.transform = 'scaleX(-1)';\n }\n init() {\n return __awaiter(this, void 0, void 0, function* () {\n try {\n const loadingMsg = getLoadingModelsMessage(this.options.locale);\n this.setStatus(ValidationStatus.INITIALIZING, undefined, loadingMsg);\n // Initialize MediaPipe FilesetResolver\n const vision = yield FilesetResolver.forVisionTasks(this.options.modelPath);\n // Initialize FaceLandmarker\n this.faceLandmarker = yield FaceLandmarker.createFromOptions(vision, {\n baseOptions: {\n modelAssetPath: 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task',\n delegate: 'GPU'\n },\n runningMode: 'VIDEO',\n numFaces: 2, // Detectar até 2 faces para MULTIPLE_FACES\n minFaceDetectionConfidence: this.options.minDetectionConfidence,\n minFacePresenceConfidence: this.options.minFaceVisibilityScore,\n minTrackingConfidence: this.options.minFaceVisibilityScore,\n });\n // Initialize HandLandmarker\n this.handLandmarker = yield HandLandmarker.createFromOptions(vision, {\n baseOptions: {\n modelAssetPath: 'https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task',\n delegate: 'GPU'\n },\n runningMode: 'VIDEO',\n numHands: 2,\n minHandDetectionConfidence: 0.5,\n minHandPresenceConfidence: 0.5,\n minTrackingConfidence: 0.5,\n });\n if (this.cameraReadyPromise) {\n yield this.cameraReadyPromise;\n }\n this.startDetectionLoop();\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.setStatus(ValidationStatus.ERROR, error);\n }\n });\n }\n getMessageForStatus(status, messageOverride) {\n if (messageOverride)\n return messageOverride;\n if (this.options.customMessages[status]) {\n return this.options.customMessages[status];\n }\n return getMessage(status, this.options.locale);\n }\n setStatus(status, error, messageOverride) {\n const message = this.getMessageForStatus(status, messageOverride);\n this.updateStatusUI(status, message);\n this.options.onStatusUpdate(status, message);\n if (status === ValidationStatus.ERROR && error) {\n this.options.onError(status, error);\n }\n }\n updateStatusUI(status, message) {\n if (!this.statusElement)\n return;\n this.statusElement.textContent = message;\n this.statusElement.classList.remove('success', 'warning', 'error');\n const statusClass = this.getStatusClass(status);\n if (statusClass) {\n this.statusElement.classList.add(statusClass);\n }\n }\n getStatusClass(status) {\n if (status === ValidationStatus.SUCCESS)\n return 'success';\n if (status === ValidationStatus.ERROR)\n return 'error';\n const warningStatuses = [\n ValidationStatus.NO_FACE_DETECTED,\n ValidationStatus.MULTIPLE_FACES,\n ValidationStatus.TOO_CLOSE,\n ValidationStatus.TOO_FAR,\n ValidationStatus.OFF_CENTER,\n ValidationStatus.HEAD_NOT_STRAIGHT,\n ValidationStatus.FACE_OBSTRUCTED,\n ValidationStatus.POOR_ILLUMINATION,\n ValidationStatus.NOT_NEUTRAL_EXPRESSION,\n ValidationStatus.DARK_GLASSES,\n ValidationStatus.STAY_STILL,\n ValidationStatus.CAPTURING,\n ];\n return warningStatuses.includes(status) ? 'warning' : '';\n }\n initCamera() {\n return __awaiter(this, void 0, void 0, function* () {\n const video = this.options.videoElement;\n if (!video || video.srcObject)\n return;\n try {\n const stream = yield navigator.mediaDevices.getUserMedia({\n video: this.options.videoConstraints,\n });\n this.cameraStream = stream;\n this.managedCamera = true;\n // eslint-disable-next-line no-param-reassign\n video.srcObject = stream;\n yield this.waitForVideoReady(video);\n yield video.play();\n const canvas = this.options.overlayCanvasElement;\n if (canvas) {\n canvas.width = video.videoWidth || this.options.videoWidth || defaultOptions.videoWidth;\n canvas.height = video.videoHeight || this.options.videoHeight || defaultOptions.videoHeight;\n }\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.setStatus(ValidationStatus.ERROR, error);\n }\n });\n }\n waitForVideoReady(video) {\n return __awaiter(this, void 0, void 0, function* () {\n if (video.readyState >= 2)\n return;\n yield new Promise((resolve, reject) => {\n const onLoaded = () => {\n video.removeEventListener('loadedmetadata', onLoaded);\n resolve();\n };\n const onError = (event) => {\n video.removeEventListener('error', onError);\n reject(event);\n };\n video.addEventListener('loadedmetadata', onLoaded);\n video.addEventListener('error', onError);\n setTimeout(() => {\n video.removeEventListener('loadedmetadata', onLoaded);\n video.removeEventListener('error', onError);\n resolve();\n }, 5000);\n });\n });\n }\n startDetectionLoop() {\n const video = this.getVideoElement();\n const frameWidth = this.options.videoWidth || 640;\n const frameHeight = this.options.videoHeight || 480;\n const detect = () => __awaiter(this, void 0, void 0, function* () {\n var _a;\n if (!this.faceLandmarker || !this.handLandmarker || !video.videoWidth) {\n this.animationFrameId = requestAnimationFrame(detect);\n return;\n }\n try {\n const now = performance.now();\n let currentStatus = ValidationStatus.NO_FACE_DETECTED;\n let faceData = null;\n let handData = [];\n // Detectar faces\n const faceResults = this.faceLandmarker.detectForVideo(video, now);\n // Detectar mãos\n const handResults = this.handLandmarker.detectForVideo(video, now);\n // Processar mãos detectadas\n if (handResults.landmarks && handResults.landmarks.length > 0) {\n handData = handResults.landmarks.map((landmarks, idx) => {\n var _a, _b, _c;\n return ({\n landmarks,\n handedness: ((_c = (_b = (_a = handResults.handednesses) === null || _a === void 0 ? void 0 : _a[idx]) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.categoryName) || 'Unknown'\n });\n });\n }\n if (faceResults.faceLandmarks && faceResults.faceLandmarks.length > 1) {\n // Múltiplas faces detectadas\n currentStatus = ValidationStatus.MULTIPLE_FACES;\n this.stableSince = null;\n // Usar primeira face para overlay\n const landmarks = faceResults.faceLandmarks[0];\n const box = ((_a = faceResults.faceBlendshapes) === null || _a === void 0 ? void 0 : _a[0]) ? this.estimateBoundingBox(landmarks) : null;\n if (box) {\n faceData = { boundingBox: box, landmarks, timestamp: now };\n }\n }\n else if (faceResults.faceLandmarks && faceResults.faceLandmarks.length === 1) {\n // Uma face detectada\n const landmarks = faceResults.faceLandmarks[0];\n const boundingBox = this.estimateBoundingBox(landmarks);\n faceData = {\n boundingBox,\n landmarks,\n timestamp: now,\n };\n // Validações sequenciais\n const distanceStatus = checkFaceDistance(boundingBox, this.options.minFaceSizeFactor, this.options.maxFaceSizeFactor);\n if (distanceStatus !== 'OK') {\n currentStatus = distanceStatus === 'TOO_CLOSE' ? ValidationStatus.TOO_CLOSE : ValidationStatus.TOO_FAR;\n this.stableSince = null;\n }\n else {\n // Verificar centralização: nariz no oval E bounding box dentro do oval\n // Mais rigoroso: exige os dois critérios ao mesmo tempo\n const nose = landmarks[4]; // MediaPipe nose tip\n const isNoseCentered = isPointInsideOval(nose.x, nose.y, frameWidth, frameHeight);\n const isFaceInsideOval = isFaceBoundingBoxInsideOval(boundingBox, frameWidth, frameHeight);\n // Rejeitar se QUALQUER um dos critérios de centralização falhar\n // - Nariz deve estar dentro do oval\n // - Bounding box da face deve caber dentro do oval\n if (!isNoseCentered || !isFaceInsideOval) {\n currentStatus = ValidationStatus.OFF_CENTER;\n this.stableSince = null;\n }\n else if (!isFaceGeometryPlausible(landmarks, boundingBox)) {\n currentStatus = ValidationStatus.FACE_OBSTRUCTED;\n this.stableSince = null;\n }\n else if (!isHeadStraight(landmarks, this.options.maxHeadTiltDegrees)) {\n currentStatus = ValidationStatus.HEAD_NOT_STRAIGHT;\n this.stableSince = null;\n }\n else if (handData.length > 0 && isHandNearFace(handData[0], boundingBox, this.options.maxHandFaceDistance)) {\n // Mão detectada próxima ao rosto\n currentStatus = ValidationStatus.FACE_OBSTRUCTED;\n this.stableSince = null;\n }\n else if (!isNeutralExpression(landmarks)) {\n // Expressão não neutra (sorriso, boca aberta, olhos fechados)\n currentStatus = ValidationStatus.NOT_NEUTRAL_EXPRESSION;\n this.stableSince = null;\n }\n else if (hasDarkGlasses(video, landmarks)) {\n // Óculos escuros detectados\n currentStatus = ValidationStatus.DARK_GLASSES;\n this.stableSince = null;\n }\n else {\n // Verificar iluminação\n const tempCanvas = document.createElement('canvas');\n const boxX = boundingBox.xMin * video.videoWidth;\n const boxY = boundingBox.yMin * video.videoHeight;\n const boxW = boundingBox.width * video.videoWidth;\n const boxH = boundingBox.height * video.videoHeight;\n tempCanvas.width = boxW;\n tempCanvas.height = boxH;\n const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });\n if (tempCtx) {\n tempCtx.drawImage(video, boxX, boxY, boxW, boxH, 0, 0, boxW, boxH);\n const faceImageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);\n const brightness = calculateAverageBrightness(faceImageData);\n if (brightness < this.options.minIlluminationThreshold) {\n currentStatus = ValidationStatus.POOR_ILLUMINATION;\n this.stableSince = null;\n }\n else {\n // Verificar estabilidade\n if (isFaceStable(faceData, this.lastDetection, this.options.stabilityMovementThreshold, frameWidth, frameHeight)) {\n if (!this.stableSince)\n this.stableSince = now;\n if (now - this.stableSince >= this.options.stabilizationTimeThreshold) {\n currentStatus = ValidationStatus.CAPTURING;\n }\n else {\n currentStatus = ValidationStatus.STAY_STILL;\n }\n }\n else {\n this.stableSince = null;\n currentStatus = ValidationStatus.STAY_STILL;\n }\n }\n }\n else {\n currentStatus = ValidationStatus.FACE_DETECTED;\n this.stableSince = null;\n }\n }\n }\n }\n else {\n // Nenhuma face detectada\n this.lastDetection = null;\n this.stableSince = null;\n }\n this.lastDetection = faceData;\n this.setStatus(currentStatus);\n // Desenhar overlay\n if (this.options.overlayCanvasElement) {\n drawOverlay(this.options.overlayCanvasElement, this.options.debugMode || false, currentStatus, faceData || undefined, handData.length > 0 ? handData : undefined);\n }\n // Capturar se status é CAPTURING\n if (currentStatus === ValidationStatus.CAPTURING && !this.isCapturing) {\n this.isCapturing = true;\n yield this.captureImage();\n this.setStatus(ValidationStatus.SUCCESS);\n this.stop();\n return;\n }\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.setStatus(ValidationStatus.ERROR, error);\n }\n this.animationFrameId = requestAnimationFrame(detect);\n });\n this.animationFrameId = requestAnimationFrame(detect);\n }\n /**\n * Estima bounding box a partir dos landmarks (MediaPipe não fornece bbox diretamente).\n */\n estimateBoundingBox(landmarks) {\n const xs = landmarks.map((l) => l.x);\n const ys = landmarks.map((l) => l.y);\n const xMin = Math.min(...xs);\n const xMax = Math.max(...xs);\n const yMin = Math.min(...ys);\n const yMax = Math.max(...ys);\n return {\n xMin,\n yMin,\n width: xMax - xMin,\n height: yMax - yMin,\n };\n }\n captureImage() {\n return __awaiter(this, void 0, void 0, function* () {\n const video = this.getVideoElement();\n const canvas = document.createElement('canvas');\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n this.setStatus(ValidationStatus.ERROR, new Error('Failed to get canvas context'));\n return;\n }\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n canvas.toBlob((blob) => {\n if (blob) {\n this.options.onCaptureSuccess(blob);\n }\n else {\n this.setStatus(ValidationStatus.ERROR, new Error('Failed to generate image blob'));\n }\n }, 'image/jpeg', 0.95);\n });\n }\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n if (this.faceLandmarker) {\n this.faceLandmarker.close();\n }\n if (this.handLandmarker) {\n this.handLandmarker.close();\n }\n if (this.managedCamera) {\n this.stopCamera();\n }\n }\n destroy() {\n this.stop();\n if (this.managedElements && this.containerElement) {\n this.containerElement.innerHTML = '';\n }\n this.statusElement = null;\n this.uiRootElement = null;\n }\n stopCamera() {\n if (this.cameraStream) {\n this.cameraStream.getTracks().forEach(track => track.stop());\n this.cameraStream = null;\n }\n }\n getVideoElement() {\n if (!this.options.videoElement) {\n throw new Error('Video element is not available. Provide videoElement or container.');\n }\n return this.options.videoElement;\n }\n}\n","import { FaceValidator } from './FaceValidator';\nimport { ValidationStatus, } from './types';\nimport { getValidationMessages, getMessage, getLoadingModelsMessage, } from './i18n';\nexport { FaceValidator };\nexport { ValidationStatus, };\nexport { getValidationMessages, getMessage, getLoadingModelsMessage };\nexport default FaceValidator;\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","require","ValidationStatus","messages","INITIALIZING","NO_FACE_DETECTED","FACE_DETECTED","TOO_CLOSE","TOO_FAR","OFF_CENTER","FACE_OBSTRUCTED","HEAD_NOT_STRAIGHT","MULTIPLE_FACES","POOR_ILLUMINATION","NOT_NEUTRAL_EXPRESSION","DARK_GLASSES","STAY_STILL","CAPTURING","SUCCESS","ERROR","en","es","unknownStatusByLocale","getValidationMessages","locale","assign","getMessage","status","_a","getLoadingModelsMessage","calculateAverageBrightness","imageData","data","sum","i","length","MEDIAPIPE_LEFT_EYE","MEDIAPIPE_RIGHT_EYE","MEDIAPIPE_MOUTH_OUTER","OVAL_RADIUS_Y_FACTOR","__awaiter","thisArg","_arguments","P","generator","Promise","resolve","reject","fulfilled","step","next","e","rejected","result","done","then","apply","defaultOptions","container","undefined","ui","autoStart","mirror","videoConstraints","overlayCanvasElement","videoWidth","videoHeight","minDetectionConfidence","minIlluminationThreshold","minFaceSizeFactor","maxFaceSizeFactor","stabilizationTimeThreshold","stabilityMovementThreshold","minFaceVisibilityScore","maxHeadTiltDegrees","maxHandFaceDistance","debugMode","customMessages","onStatusUpdate","onCaptureSuccess","onError","FaceValidator","constructor","options","this","faceLandmarker","handLandmarker","animationFrameId","lastDetection","stableSince","isCapturing","containerElement","statusElement","uiRootElement","cameraStream","cameraReadyPromise","managedElements","managedCamera","injectedStyleElement","resolveOptions","setupElements","setStatus","initCamera","init","modelPath","width","ideal","height","facingMode","resolveContainer","ensureDefaultUI","videoElement","canvas","document","createElement","className","attachMediaElements","Error","video","autoplay","playsInline","muted","applyMirrorStyles","querySelector","innerHTML","classList","add","mediaWrapper","appendChild","injectDefaultStyles","wrapper","style","setAttribute","textContent","head","transform","loadingMsg","vision","FilesetResolver","forVisionTasks","FaceLandmarker","createFromOptions","baseOptions","modelAssetPath","delegate","runningMode","numFaces","minFaceDetectionConfidence","minFacePresenceConfidence","minTrackingConfidence","HandLandmarker","numHands","minHandDetectionConfidence","minHandPresenceConfidence","startDetectionLoop","err","error","String","getMessageForStatus","messageOverride","message","updateStatusUI","remove","statusClass","getStatusClass","includes","srcObject","stream","navigator","mediaDevices","getUserMedia","waitForVideoReady","play","readyState","onLoaded","removeEventListener","event","addEventListener","setTimeout","getVideoElement","frameWidth","frameHeight","detect","now","performance","currentStatus","faceData","handData","faceResults","detectForVideo","handResults","landmarks","map","idx","_b","_c","handedness","handednesses","categoryName","faceLandmarks","box","faceBlendshapes","estimateBoundingBox","boundingBox","timestamp","distanceStatus","faceWidthRatio","checkFaceDistance","nose","isNoseCentered","pointX","pointY","dx","dy","isPointInsideOval","x","y","isFaceInsideOval","cx","cy","rx","ry","faceLeft","xMin","faceRight","faceTop","yMin","faceBottom","centerDx","centerDy","corners","cornersOutside","corner","isFaceBoundingBoxInsideOval","mouthPoints","mouthCenterY","reduce","s","p","mouthMinY","Math","min","mouthVerticalSpread","max","boxHeight","isFaceGeometryPlausible","maxTiltDegrees","leftEye","rightEye","upperLip","lowerLip","chin","forehead","eyeDeltaY","abs","eyeDeltaX","rollRatio","atan","PI","midEyesX","noseOffsetX","eyeDist","yawRatio","leftEar","rightEar","leftEarToNoseX","rightEarToNoseX","z","isYawAcceptable","midEyesY","mouthY","faceHeight","foreheadEyesRatio","eyesNoseRatio","noseMouthRatio","mouthChinRatio","isHeadStraight","faceBoundingBox","maxDistance","faceCenterX","faceCenterY","landmark","sqrt","isHandNearFace","leftEyeTop","leftEyeBottom","rightEyeTop","rightEyeBottom","leftEyeOpenness","rightEyeOpenness","mouthTop","mouthBottom","mouthLeftCorner","mouthRightCorner","isNeutralExpression","tempCanvas","ctx","getContext","leftEyeLandmarks","rightEyeLandmarks","getBoundingBox","eyeLandmarks","xs","l","ys","minX","maxX","minY","getRegionBrightness","drawImage","getImageData","leftEyeBox","rightEyeBox","console","warn","hasDarkGlasses","boxX","boxY","boxW","boxH","tempCtx","willReadFrequently","currentFace","previousFace","movementThreshold","currentCenterX","currentCenterY","previousCenterX","previousCenterY","deltaX","deltaY","deltaWidth","deltaHeight","isFaceStable","frameCenterX","frameCenterY","clearRect","radiusX","radiusY","fillStyle","fillRect","save","beginPath","ellipse","closePath","globalCompositeOperation","fill","restore","strokeStyle","lineWidth","stroke","moveTo","lineTo","allXCoords","allYCoords","margin","w","h","boxColor","strokeRect","arc","forEach","hand","drawOverlay","captureImage","stop","requestAnimationFrame","xMax","toBlob","blob","cancelAnimationFrame","close","stopCamera","destroy","getTracks","track"],"sourceRoot":""}
@@ -0,0 +1,2 @@
1
+ import{FaceLandmarker as e,FilesetResolver as t,HandLandmarker as n}from"@mediapipe/tasks-vision";var a;(function(e){e.INITIALIZING="INITIALIZING",e.NO_FACE_DETECTED="NO_FACE_DETECTED",e.FACE_DETECTED="FACE_DETECTED",e.TOO_CLOSE="TOO_CLOSE",e.TOO_FAR="TOO_FAR",e.OFF_CENTER="OFF_CENTER",e.FACE_OBSTRUCTED="FACE_OBSTRUCTED",e.HEAD_NOT_STRAIGHT="HEAD_NOT_STRAIGHT",e.MULTIPLE_FACES="MULTIPLE_FACES",e.POOR_ILLUMINATION="POOR_ILLUMINATION",e.NOT_NEUTRAL_EXPRESSION="NOT_NEUTRAL_EXPRESSION",e.DARK_GLASSES="DARK_GLASSES",e.STAY_STILL="STAY_STILL",e.CAPTURING="CAPTURING",e.SUCCESS="SUCCESS",e.ERROR="ERROR"})(a||(a={}));const i={"pt-BR":{[a.INITIALIZING]:"Inicializando câmera e detector...",[a.NO_FACE_DETECTED]:"Posicione seu rosto no centro do oval.",[a.FACE_DETECTED]:"Analisando...",[a.TOO_CLOSE]:"Afaste-se um pouco",[a.TOO_FAR]:"Aproxime-se da câmera",[a.OFF_CENTER]:"Centralize o rosto no centro do oval",[a.FACE_OBSTRUCTED]:"Mantenha o rosto totalmente visível. Remova as mãos do rosto.",[a.HEAD_NOT_STRAIGHT]:"Olhe diretamente para a câmera e mantenha a cabeça reta.",[a.MULTIPLE_FACES]:"Mantenha apenas uma pessoa no quadro.",[a.POOR_ILLUMINATION]:"Procure um ambiente com boa iluminação.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenha expressão neutra: boca fechada, sem sorrir e olhos abertos.",[a.DARK_GLASSES]:"Remova os óculos escuros. Óculos de grau são permitidos.",[a.STAY_STILL]:"Fique imóvel para capturar a foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"Captura realizada!",[a.ERROR]:"Ocorreu um erro."},en:{[a.INITIALIZING]:"Initializing camera and detector...",[a.NO_FACE_DETECTED]:"Position your face in the center of the oval.",[a.FACE_DETECTED]:"Analyzing...",[a.TOO_CLOSE]:"Move back a little",[a.TOO_FAR]:"Move closer to the camera",[a.OFF_CENTER]:"Center your face in the center of the oval",[a.FACE_OBSTRUCTED]:"Keep your face fully visible. Remove your hands from your face.",[a.HEAD_NOT_STRAIGHT]:"Look directly at the camera and keep your head straight.",[a.MULTIPLE_FACES]:"Keep only one person in the frame.",[a.POOR_ILLUMINATION]:"Find a well-lit environment and center your face in the oval.",[a.NOT_NEUTRAL_EXPRESSION]:"Keep a neutral expression: mouth closed, no smiling, and eyes open.",[a.DARK_GLASSES]:"Remove sunglasses. Prescription glasses are allowed.",[a.STAY_STILL]:"Stay still to capture the photo",[a.CAPTURING]:"Capturing...",[a.SUCCESS]:"Capture complete!",[a.ERROR]:"An error occurred."},es:{[a.INITIALIZING]:"Inicializando cámara y detector...",[a.NO_FACE_DETECTED]:"Coloque su rostro en el centro del óvalo.",[a.FACE_DETECTED]:"Analizando...",[a.TOO_CLOSE]:"Aléjese un poco",[a.TOO_FAR]:"Acérquese a la cámara",[a.OFF_CENTER]:"Centre el rostro en el centro del óvalo",[a.FACE_OBSTRUCTED]:"Mantenga el rostro totalmente visible. Quite las manos del rostro.",[a.HEAD_NOT_STRAIGHT]:"Mire directamente a la cámara y mantenga la cabeza recta.",[a.MULTIPLE_FACES]:"Mantenga solo una persona en el encuadre.",[a.POOR_ILLUMINATION]:"Busque un ambiente con buena iluminación y centre su rostro en el óvalo.",[a.NOT_NEUTRAL_EXPRESSION]:"Mantenga expresión neutra: boca cerrada, sin sonreír y ojos abiertos.",[a.DARK_GLASSES]:"Quite las gafas de sol. Las gafas graduadas están permitidas.",[a.STAY_STILL]:"Permanezca quieto para capturar la foto",[a.CAPTURING]:"Capturando...",[a.SUCCESS]:"¡Captura realizada!",[a.ERROR]:"Ocurrió un error."}},o={"pt-BR":"Status desconhecido.",en:"Unknown status.",es:"Estado desconhecido."};function s(e){return Object.assign({},i[e])}function r(e,t){var n;return null!==(n=i[t][e])&&void 0!==n?n:o[t]}function l(e){return{"pt-BR":"Carregando...",en:"Loading...",es:"Cargando..."}[e]}function d(e){const t=e.data;let n=0;for(let e=0;e<t.length;e+=4)n+=.2126*t[e]+.7152*t[e+1]+.0722*t[e+2];return n/(t.length/4)}const c=[33,133,159,145],h=[263,362,386,374],m=[61,291,0,17,39,269,270,409],u=.34;var E=function(e,t,n,a){return new(n||(n=Promise))(function(i,o){function s(e){try{l(a.next(e))}catch(e){o(e)}}function r(e){try{l(a.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(s,r)}l((a=a.apply(e,t||[])).next())})};const f={container:void 0,ui:"default",autoStart:!0,mirror:!0,videoConstraints:void 0,overlayCanvasElement:void 0,videoWidth:512,videoHeight:384,minDetectionConfidence:.4,minIlluminationThreshold:50,minFaceSizeFactor:.15,maxFaceSizeFactor:.75,stabilizationTimeThreshold:1e3,stabilityMovementThreshold:5,minFaceVisibilityScore:.4,maxHeadTiltDegrees:30,maxHandFaceDistance:.15,debugMode:!1,locale:"en",customMessages:{},onStatusUpdate:void 0,onCaptureSuccess:void 0,onError:void 0};class p{constructor(e){this.faceLandmarker=null,this.handLandmarker=null,this.animationFrameId=null,this.lastDetection=null,this.stableSince=null,this.isCapturing=!1,this.containerElement=null,this.statusElement=null,this.uiRootElement=null,this.cameraStream=null,this.cameraReadyPromise=null,this.managedElements=!1,this.managedCamera=!1,this.injectedStyleElement=null,this.options=this.resolveOptions(e),this.setupElements(),this.setStatus(a.INITIALIZING),this.cameraReadyPromise=this.options.autoStart?this.initCamera():Promise.resolve(),this.init()}resolveOptions(e){const t=e.modelPath||"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm";return Object.assign(Object.assign(Object.assign({},f),e),{modelPath:t,locale:e.locale||"en",customMessages:e.customMessages||{},onStatusUpdate:e.onStatusUpdate||(()=>{}),onCaptureSuccess:e.onCaptureSuccess||(()=>{}),onError:e.onError||(()=>{}),videoConstraints:e.videoConstraints||{width:{ideal:e.videoWidth||f.videoWidth},height:{ideal:e.videoHeight||f.videoHeight},facingMode:"user"}})}setupElements(){const e=this.resolveContainer(this.options.container);if(this.containerElement=e,"default"===this.options.ui&&e&&this.ensureDefaultUI(e),this.options.videoElement){if(!this.options.overlayCanvasElement&&e){const t=document.createElement("canvas");t.className="fv-sdk-canvas",this.attachMediaElements(e,this.options.videoElement,t),this.options.overlayCanvasElement=t,this.managedElements=!0}}else{if(!e)throw new Error("FaceValidator requires either videoElement or container.");const t=document.createElement("video");t.autoplay=!0,t.playsInline=!0,t.muted=!0,t.className="fv-sdk-video";const n=document.createElement("canvas");n.className="fv-sdk-canvas",this.attachMediaElements(e,t,n),this.options.videoElement=t,this.options.overlayCanvasElement=n,this.managedElements=!0}this.options.mirror&&this.applyMirrorStyles()}resolveContainer(e){return e?"string"==typeof e?document.querySelector(e):e:null}ensureDefaultUI(e){e.innerHTML="",e.classList.add("fv-sdk-root");const t=document.createElement("div");t.className="fv-sdk-media",e.appendChild(t);const n=document.createElement("div");n.className="fv-sdk-status",e.appendChild(n),this.statusElement=n,this.uiRootElement=e,this.injectDefaultStyles()}attachMediaElements(e,t,n){const a=e.querySelector(".fv-sdk-media");if(a)return a.appendChild(t),void a.appendChild(n);const i=document.createElement("div");i.className="fv-sdk-media",i.appendChild(t),i.appendChild(n),e.appendChild(i)}injectDefaultStyles(){if(this.injectedStyleElement||document.querySelector('style[data-fv-sdk="true"]'))return;const e=document.createElement("style");e.setAttribute("data-fv-sdk","true"),e.textContent="\n .fv-sdk-root { display: flex; flex-direction: column; gap: 12px; width: 100%; }\n .fv-sdk-media { position: relative; width: 100%; max-width: 512px; height: 384px; margin: 0 auto; background: #000; border-radius: 10px; overflow: hidden; }\n .fv-sdk-video, .fv-sdk-canvas { width: 100%; height: 100%; display: block; object-fit: contain; }\n .fv-sdk-canvas { position: absolute; top: 0; left: 0; }\n .fv-sdk-status { text-align: center; display: flex; align-items: center; justify-content: center; font-size: 14px; padding: 10px 12px; border-radius: 8px; font-weight: 600; background: #f8f9fa; color: #555; }\n .fv-sdk-status.success { background: #d4edda; color: #155724; }\n .fv-sdk-status.error { background: #f8d7da; color: #721c24; }\n .fv-sdk-status.warning { background: #fff3cd; color: #856404; }\n ",document.head.appendChild(e),this.injectedStyleElement=e}applyMirrorStyles(){const e=this.options.videoElement,t=this.options.overlayCanvasElement;e&&(e.style.transform="scaleX(-1)"),t&&(t.style.transform="scaleX(-1)")}init(){return E(this,void 0,void 0,function*(){try{const i=l(this.options.locale);this.setStatus(a.INITIALIZING,void 0,i);const o=yield t.forVisionTasks(this.options.modelPath);this.faceLandmarker=yield e.createFromOptions(o,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numFaces:2,minFaceDetectionConfidence:this.options.minDetectionConfidence,minFacePresenceConfidence:this.options.minFaceVisibilityScore,minTrackingConfidence:this.options.minFaceVisibilityScore}),this.handLandmarker=yield n.createFromOptions(o,{baseOptions:{modelAssetPath:"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",delegate:"GPU"},runningMode:"VIDEO",numHands:2,minHandDetectionConfidence:.5,minHandPresenceConfidence:.5,minTrackingConfidence:.5}),this.cameraReadyPromise&&(yield this.cameraReadyPromise),this.startDetectionLoop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}})}getMessageForStatus(e,t){return t||(this.options.customMessages[e]?this.options.customMessages[e]:r(e,this.options.locale))}setStatus(e,t,n){const i=this.getMessageForStatus(e,n);this.updateStatusUI(e,i),this.options.onStatusUpdate(e,i),e===a.ERROR&&t&&this.options.onError(e,t)}updateStatusUI(e,t){if(!this.statusElement)return;this.statusElement.textContent=t,this.statusElement.classList.remove("success","warning","error");const n=this.getStatusClass(e);n&&this.statusElement.classList.add(n)}getStatusClass(e){return e===a.SUCCESS?"success":e===a.ERROR?"error":[a.NO_FACE_DETECTED,a.MULTIPLE_FACES,a.TOO_CLOSE,a.TOO_FAR,a.OFF_CENTER,a.HEAD_NOT_STRAIGHT,a.FACE_OBSTRUCTED,a.POOR_ILLUMINATION,a.NOT_NEUTRAL_EXPRESSION,a.DARK_GLASSES,a.STAY_STILL,a.CAPTURING].includes(e)?"warning":""}initCamera(){return E(this,void 0,void 0,function*(){const e=this.options.videoElement;if(e&&!e.srcObject)try{const t=yield navigator.mediaDevices.getUserMedia({video:this.options.videoConstraints});this.cameraStream=t,this.managedCamera=!0,e.srcObject=t,yield this.waitForVideoReady(e),yield e.play();const n=this.options.overlayCanvasElement;n&&(n.width=e.videoWidth||this.options.videoWidth||f.videoWidth,n.height=e.videoHeight||this.options.videoHeight||f.videoHeight)}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}})}waitForVideoReady(e){return E(this,void 0,void 0,function*(){e.readyState>=2||(yield new Promise((t,n)=>{const a=()=>{e.removeEventListener("loadedmetadata",a),t()},i=t=>{e.removeEventListener("error",i),n(t)};e.addEventListener("loadedmetadata",a),e.addEventListener("error",i),setTimeout(()=>{e.removeEventListener("loadedmetadata",a),e.removeEventListener("error",i),t()},5e3)}))})}startDetectionLoop(){const e=this.getVideoElement(),t=this.options.videoWidth||640,n=this.options.videoHeight||480,i=()=>E(this,void 0,void 0,function*(){var o;if(this.faceLandmarker&&this.handLandmarker&&e.videoWidth){try{const i=performance.now();let s=a.NO_FACE_DETECTED,r=null,l=[];const E=this.faceLandmarker.detectForVideo(e,i),f=this.handLandmarker.detectForVideo(e,i);if(f.landmarks&&f.landmarks.length>0&&(l=f.landmarks.map((e,t)=>{var n,a,i;return{landmarks:e,handedness:(null===(i=null===(a=null===(n=f.handednesses)||void 0===n?void 0:n[t])||void 0===a?void 0:a[0])||void 0===i?void 0:i.categoryName)||"Unknown"}})),E.faceLandmarks&&E.faceLandmarks.length>1){s=a.MULTIPLE_FACES,this.stableSince=null;const e=E.faceLandmarks[0],t=(null===(o=E.faceBlendshapes)||void 0===o?void 0:o[0])?this.estimateBoundingBox(e):null;t&&(r={boundingBox:t,landmarks:e,timestamp:i})}else if(E.faceLandmarks&&1===E.faceLandmarks.length){const o=E.faceLandmarks[0],f=this.estimateBoundingBox(o);r={boundingBox:f,landmarks:o,timestamp:i};const p=function(e,t=.18,n=.7){const a=e.width;return a<t?"TOO_FAR":a>n?"TOO_CLOSE":"OK"}(f,this.options.minFaceSizeFactor,this.options.maxFaceSizeFactor);if("OK"!==p)s="TOO_CLOSE"===p?a.TOO_CLOSE:a.TOO_FAR,this.stableSince=null;else{const E=o[4],p=function(e,t,n,a){const i=(e*n-n/2)/(.2*n),o=(t*a-a/2)/(a*u);return i*i+o*o<=.6}(E.x,E.y,t,n),g=function(e,t,n){const a=t/2,i=n/2,o=.2*t,s=n*u,r=e.xMin*t,l=(e.xMin+e.width)*t,d=e.yMin*n,c=(e.yMin+e.height)*n,h=((r+l)/2-a)/o,m=((d+c)/2-i)/s;if(h*h+m*m>1)return!1;const E=[{x:r,y:d},{x:l,y:d},{x:r,y:c},{x:l,y:c}];let f=0;for(const e of E){const t=(e.x-a)/o,n=(e.y-i)/s;t*t+n*n>1&&f++}return 0===f}(f,t,n);if(p&&g)if(function(e,t){if(e.length<478)return!1;const n=e[4],a=m.map(t=>e[t]),i=a.reduce((e,t)=>e+t.y,0)/a.length,o=Math.min(...a.map(e=>e.y)),s=Math.max(...a.map(e=>e.y))-o,r=t.height;return!(i<n.y-.01||i-n.y<.06*r||s<.02*r)}(o,f))if(function(e,t=25){if(e.length<478)return!1;const n=e[c[0]],a=e[h[0]],i=e[4],o=e[13],s=e[14],r=e[152],l=e[10],d=Math.abs(n.y-a.y),m=Math.abs(n.x-a.x);if(m<.01)return!1;const u=d/m;if(Math.atan(u)*(180/Math.PI)>t)return!1;const E=(n.x+a.x)/2,f=i.x-E,p=Math.abs(n.x-a.x);if(p<.01)return!1;const g=Math.abs(f)/p;if(Math.atan(g)*(180/Math.PI)>t)return!1;if(!function(e){if(e.length<478)return!1;const t=e[234],n=e[454],a=e[4],i=Math.abs(t.x-a.x),o=Math.abs(n.x-a.x);return!((i>.01&&o>.01?Math.max(i,o)/Math.min(i,o):1)>1.4||void 0!==t.z&&void 0!==n.z&&Math.abs(t.z-n.z)>.05)}(e))return!1;const S=(n.y+a.y)/2,v=(o.y+s.y)/2,T=r.y-l.y;if(T<.1)return!1;if(l.y>S+.02)return!1;if(S>i.y+.02)return!1;if(i.y>v+.02)return!1;if(v>=r.y)return!1;const C=(S-l.y)/T,y=(i.y-S)/T,O=(v-i.y)/T,I=(r.y-v)/T;return!(C<.06||C>.38||y<.03||y>.3||O<.02||O>.25||I<.04||I>.38)}(o,this.options.maxHeadTiltDegrees))if(l.length>0&&function(e,t,n=.15){const a=t.xMin+t.width/2,i=t.yMin+t.height/2;for(const t of e.landmarks){const e=t.x-a,o=t.y-i;if(Math.sqrt(e*e+o*o)<n)return!0}return!1}(l[0],f,this.options.maxHandFaceDistance))s=a.FACE_OBSTRUCTED,this.stableSince=null;else if(function(e){if(e.length<478)return!1;const t=e[159],n=e[144],a=e[386],i=e[373],o=Math.abs(t.y-n.y),s=Math.abs(a.y-i.y);if(o<.01||s<.01)return!1;const r=e[13],l=e[14];if(Math.abs(r.y-l.y)>.025)return!1;const d=e[61],c=e[291],h=e[4];return!((d.y+c.y)/2-h.y<.05)}(o))if(function(e,t){if(t.length<478)return!1;try{const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)return!1;const i=e.videoWidth,o=e.videoHeight,s=[t[33],t[133],t[159],t[144],t[145]],r=[t[263],t[362],t[386],t[373],t[374]],l=e=>{const t=e.map(e=>e.x*i),n=e.map(e=>e.y*o),a=Math.max(0,Math.min(...t)-5),s=Math.min(i,Math.max(...t)+5),r=Math.max(0,Math.min(...n)-5);return{x:a,y:r,width:s-a,height:Math.min(o,Math.max(...n)+5)-r}},c=t=>(n.width=t.width,n.height=t.height,a.drawImage(e,t.x,t.y,t.width,t.height,0,0,t.width,t.height),d(a.getImageData(0,0,t.width,t.height))),h=l(s),m=l(r);return(c(h)+c(m))/2<35}catch(e){return console.warn("Erro ao detectar óculos escuros:",e),!1}}(e,o))s=a.DARK_GLASSES,this.stableSince=null;else{const o=document.createElement("canvas"),l=f.xMin*e.videoWidth,c=f.yMin*e.videoHeight,h=f.width*e.videoWidth,m=f.height*e.videoHeight;o.width=h,o.height=m;const u=o.getContext("2d",{willReadFrequently:!0});if(u){u.drawImage(e,l,c,h,m,0,0,h,m);d(u.getImageData(0,0,o.width,o.height))<this.options.minIlluminationThreshold?(s=a.POOR_ILLUMINATION,this.stableSince=null):function(e,t,n=5,a=512,i=384){if(!e||!t)return!1;const o=(e.boundingBox.xMin+e.boundingBox.width/2)*a,s=(e.boundingBox.yMin+e.boundingBox.height/2)*i,r=(t.boundingBox.xMin+t.boundingBox.width/2)*a,l=(t.boundingBox.yMin+t.boundingBox.height/2)*i,d=Math.abs(o-r),c=Math.abs(s-l),h=Math.abs(e.boundingBox.width-t.boundingBox.width)*a,m=Math.abs(e.boundingBox.height-t.boundingBox.height)*i;return d<=n&&c<=n&&h<=2*n&&m<=2*n}(r,this.lastDetection,this.options.stabilityMovementThreshold,t,n)?(this.stableSince||(this.stableSince=i),s=i-this.stableSince>=this.options.stabilizationTimeThreshold?a.CAPTURING:a.STAY_STILL):(this.stableSince=null,s=a.STAY_STILL)}else s=a.FACE_DETECTED,this.stableSince=null}else s=a.NOT_NEUTRAL_EXPRESSION,this.stableSince=null;else s=a.HEAD_NOT_STRAIGHT,this.stableSince=null;else s=a.FACE_OBSTRUCTED,this.stableSince=null;else s=a.OFF_CENTER,this.stableSince=null}}else this.lastDetection=null,this.stableSince=null;if(this.lastDetection=r,this.setStatus(s),this.options.overlayCanvasElement&&function(e,t,n,i,o){const s=e.getContext("2d");if(!s)return;const r=e.width,l=e.height,d=r/2,m=l/2;s.clearRect(0,0,r,l);const E=.2*r,f=l*u;if(s.fillStyle="rgba(255, 255, 255, 0.35)",s.fillRect(0,0,r,l),s.save(),s.beginPath(),s.ellipse(d,m,E,f,0,0,2*Math.PI),s.closePath(),s.globalCompositeOperation="destination-out",s.fill(),s.restore(),s.strokeStyle="rgba(255, 255, 255, 0.9)",s.lineWidth=3,s.beginPath(),s.ellipse(d,m,E,f,0,0,2*Math.PI),s.stroke(),s.strokeStyle="rgba(255, 255, 255, 0.45)",s.lineWidth=1,s.beginPath(),s.moveTo(d-6,m),s.lineTo(d+6,m),s.moveTo(d,m-6),s.lineTo(d,m+6),s.stroke(),t&&i){const e=i.landmarks;if(e.length>=478){const t=e[10],i=e[152],o=e[234],d=e[454],m=e.map(e=>e.x),u=e.map(e=>e.y),E=Math.min(...m),f=Math.max(...m),p=Math.min(...u),g=f-E,S=Math.max(...u)-p,v=.08,T=(E-g*v)*r,C=(p-S*v)*l,y=g*(1+2*v)*r,O=S*(1+2*v)*l;let I="red";n===a.STAY_STILL||n===a.CAPTURING?I="lime":n===a.FACE_DETECTED&&(I="yellow"),s.strokeStyle=I,s.lineWidth=3,s.strokeRect(T,C,y,O);const R=e[4];e[c[0]],e[h[0]],s.fillStyle="cyan",s.beginPath(),s.arc(R.x*r,R.y*l,5,0,2*Math.PI),s.fill(),s.fillStyle="magenta",s.beginPath(),s.arc(t.x*r,t.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="lime",s.beginPath(),s.arc(i.x*r,i.y*l,4,0,2*Math.PI),s.fill(),s.fillStyle="yellow",[e[33],e[133],e[159],e[144],e[145]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="yellow",[e[263],e[362],e[386],e[373],e[374]].forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()}),s.fillStyle="purple",s.beginPath(),s.arc(o.x*r,o.y*l,3,0,2*Math.PI),s.fill(),s.beginPath(),s.arc(d.x*r,d.y*l,3,0,2*Math.PI),s.fill()}}t&&o&&o.length>0&&o.forEach(e=>{s.fillStyle="orange",e.landmarks.forEach(e=>{s.beginPath(),s.arc(e.x*r,e.y*l,3,0,2*Math.PI),s.fill()})})}(this.options.overlayCanvasElement,this.options.debugMode||!1,s,r||void 0,l.length>0?l:void 0),s===a.CAPTURING&&!this.isCapturing)return this.isCapturing=!0,yield this.captureImage(),this.setStatus(a.SUCCESS),void this.stop()}catch(e){const t=e instanceof Error?e:new Error(String(e));this.setStatus(a.ERROR,t)}this.animationFrameId=requestAnimationFrame(i)}else this.animationFrameId=requestAnimationFrame(i)});this.animationFrameId=requestAnimationFrame(i)}estimateBoundingBox(e){const t=e.map(e=>e.x),n=e.map(e=>e.y),a=Math.min(...t),i=Math.max(...t),o=Math.min(...n);return{xMin:a,yMin:o,width:i-a,height:Math.max(...n)-o}}captureImage(){return E(this,void 0,void 0,function*(){const e=this.getVideoElement(),t=document.createElement("canvas");t.width=e.videoWidth,t.height=e.videoHeight;const n=t.getContext("2d");n?(n.drawImage(e,0,0,t.width,t.height),t.toBlob(e=>{e?this.options.onCaptureSuccess(e):this.setStatus(a.ERROR,new Error("Failed to generate image blob"))},"image/jpeg",.95)):this.setStatus(a.ERROR,new Error("Failed to get canvas context"))})}stop(){null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null),this.faceLandmarker&&this.faceLandmarker.close(),this.handLandmarker&&this.handLandmarker.close(),this.managedCamera&&this.stopCamera()}destroy(){this.stop(),this.managedElements&&this.containerElement&&(this.containerElement.innerHTML=""),this.statusElement=null,this.uiRootElement=null}stopCamera(){this.cameraStream&&(this.cameraStream.getTracks().forEach(e=>e.stop()),this.cameraStream=null)}getVideoElement(){if(!this.options.videoElement)throw new Error("Video element is not available. Provide videoElement or container.");return this.options.videoElement}}const g=p;export{p as FaceValidator,a as ValidationStatus,g as default,l as getLoadingModelsMessage,r as getMessage,s as getValidationMessages};
2
+ //# sourceMappingURL=face-validator-sdk-core.esm.js.map