@srsergio/taptapp-ar 1.0.42 → 1.0.50
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/README.md +42 -45
- package/dist/compiler/aframe.js +8 -8
- package/dist/compiler/controller.d.ts +50 -76
- package/dist/compiler/controller.js +72 -116
- package/dist/compiler/detector/detector-lite.js +82 -99
- package/dist/compiler/index.js +3 -3
- package/dist/compiler/matching/hamming-distance.d.ts +8 -0
- package/dist/compiler/matching/hamming-distance.js +35 -16
- package/dist/compiler/matching/hierarchical-clustering.d.ts +9 -0
- package/dist/compiler/matching/hierarchical-clustering.js +76 -56
- package/dist/compiler/matching/matching.js +3 -3
- package/dist/compiler/node-worker.js +144 -18
- package/dist/compiler/offline-compiler.d.ts +34 -83
- package/dist/compiler/offline-compiler.js +92 -96
- package/dist/compiler/simple-ar.d.ts +31 -57
- package/dist/compiler/simple-ar.js +32 -73
- package/dist/compiler/three.d.ts +13 -8
- package/dist/compiler/three.js +6 -6
- package/dist/compiler/tracker/extract.js +17 -14
- package/dist/compiler/utils/images.js +11 -16
- package/dist/compiler/utils/lsh-direct.d.ts +12 -0
- package/dist/compiler/utils/lsh-direct.js +76 -0
- package/dist/compiler/utils/worker-pool.js +10 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/react/types.d.ts +1 -1
- package/dist/react/types.js +1 -1
- package/package.json +2 -1
- package/src/compiler/aframe.js +8 -8
- package/src/compiler/controller.ts +512 -0
- package/src/compiler/detector/detector-lite.js +87 -107
- package/src/compiler/index.js +3 -3
- package/src/compiler/matching/hamming-distance.js +39 -16
- package/src/compiler/matching/hierarchical-clustering.js +85 -57
- package/src/compiler/matching/matching.js +3 -3
- package/src/compiler/node-worker.js +163 -18
- package/src/compiler/offline-compiler.ts +513 -0
- package/src/compiler/{simple-ar.js → simple-ar.ts} +64 -91
- package/src/compiler/three.js +6 -6
- package/src/compiler/tracker/extract.js +18 -15
- package/src/compiler/utils/images.js +11 -21
- package/src/compiler/utils/lsh-direct.js +86 -0
- package/src/compiler/utils/worker-pool.js +9 -1
- package/src/index.ts +2 -2
- package/src/react/types.ts +2 -2
- package/src/compiler/controller.js +0 -554
- package/src/compiler/offline-compiler.js +0 -515
|
@@ -1,515 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Compilador Offline Optimizado - Sin TensorFlow para máxima velocidad
|
|
3
|
-
*
|
|
4
|
-
* Este módulo implementa un compilador de imágenes AR ultrarrápido
|
|
5
|
-
* que NO depende de TensorFlow, eliminando todos los problemas de
|
|
6
|
-
* inicialización, bloqueos y compatibilidad.
|
|
7
|
-
*
|
|
8
|
-
* Usa JavaScript puro para:
|
|
9
|
-
* - Extracción de features de tracking (extract.js)
|
|
10
|
-
* - Detección de features para matching (DetectorLite)
|
|
11
|
-
* - Clustering jerárquico para features
|
|
12
|
-
*
|
|
13
|
-
* Funciona en:
|
|
14
|
-
* - Node.js (con workers opcionales)
|
|
15
|
-
* - Browser (sin workers)
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { buildTrackingImageList, buildImageList } from "./image-list.js";
|
|
19
|
-
import { extractTrackingFeatures } from "./tracker/extract-utils.js";
|
|
20
|
-
import { DetectorLite } from "./detector/detector-lite.js";
|
|
21
|
-
import { build as hierarchicalClusteringBuild } from "./matching/hierarchical-clustering.js";
|
|
22
|
-
import * as msgpack from "@msgpack/msgpack";
|
|
23
|
-
import { WorkerPool } from "./utils/worker-pool.js";
|
|
24
|
-
|
|
25
|
-
// Detect environment
|
|
26
|
-
const isNode = typeof process !== "undefined" &&
|
|
27
|
-
process.versions != null &&
|
|
28
|
-
process.versions.node != null;
|
|
29
|
-
|
|
30
|
-
const CURRENT_VERSION = 6; // Protocol v6: Moonshot - LSH 64-bit
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Compilador offline optimizado sin TensorFlow
|
|
34
|
-
*/
|
|
35
|
-
export class OfflineCompiler {
|
|
36
|
-
constructor() {
|
|
37
|
-
this.data = null;
|
|
38
|
-
this.workerPool = null;
|
|
39
|
-
|
|
40
|
-
// Workers only in Node.js (no en browser)
|
|
41
|
-
if (isNode) {
|
|
42
|
-
// Lazy init workers only when needed
|
|
43
|
-
} else {
|
|
44
|
-
console.log("🌐 OfflineCompiler: Browser mode (no workers)");
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async _initNodeWorkers() {
|
|
49
|
-
try {
|
|
50
|
-
// Use variables to prevent bundlers from trying to bundle these
|
|
51
|
-
const pathModule = "path";
|
|
52
|
-
const urlModule = "url";
|
|
53
|
-
const osModule = "os";
|
|
54
|
-
const workerThreadsModule = "node:worker_threads";
|
|
55
|
-
|
|
56
|
-
const [path, url, os, { Worker }] = await Promise.all([
|
|
57
|
-
import(pathModule),
|
|
58
|
-
import(urlModule),
|
|
59
|
-
import(osModule),
|
|
60
|
-
import(workerThreadsModule)
|
|
61
|
-
]);
|
|
62
|
-
|
|
63
|
-
const __filename = url.fileURLToPath(import.meta.url);
|
|
64
|
-
const __dirname = path.dirname(__filename);
|
|
65
|
-
const workerPath = path.join(__dirname, "node-worker.js");
|
|
66
|
-
|
|
67
|
-
// Limit workers to avoid freezing system
|
|
68
|
-
const numWorkers = Math.min(os.cpus().length, 4);
|
|
69
|
-
|
|
70
|
-
this.workerPool = new WorkerPool(workerPath, numWorkers, Worker);
|
|
71
|
-
} catch (e) {
|
|
72
|
-
console.log("⚡ OfflineCompiler: Running without workers (initialization failed)", e);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Compila una lista de imágenes objetivo
|
|
78
|
-
* @param {Array} images - Lista de imágenes {width, height, data}
|
|
79
|
-
* @param {Function} progressCallback - Callback de progreso (0-100)
|
|
80
|
-
* @returns {Promise<Array>} Datos compilados
|
|
81
|
-
*/
|
|
82
|
-
async compileImageTargets(images, progressCallback) {
|
|
83
|
-
console.time("⏱️ Compilación total");
|
|
84
|
-
|
|
85
|
-
const targetImages = [];
|
|
86
|
-
|
|
87
|
-
// Preparar imágenes
|
|
88
|
-
for (let i = 0; i < images.length; i++) {
|
|
89
|
-
const img = images[i];
|
|
90
|
-
|
|
91
|
-
if (!img || !img.width || !img.height || !img.data) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
`Imagen inválida en posición ${i}. Debe tener propiedades width, height y data.`
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Convertir a escala de grises
|
|
98
|
-
const greyImageData = new Uint8Array(img.width * img.height);
|
|
99
|
-
|
|
100
|
-
if (img.data.length === img.width * img.height) {
|
|
101
|
-
greyImageData.set(img.data);
|
|
102
|
-
} else if (img.data.length === img.width * img.height * 4) {
|
|
103
|
-
for (let j = 0; j < greyImageData.length; j++) {
|
|
104
|
-
const offset = j * 4;
|
|
105
|
-
greyImageData[j] = Math.floor(
|
|
106
|
-
(img.data[offset] + img.data[offset + 1] + img.data[offset + 2]) / 3
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
throw new Error(`Formato de datos de imagen no soportado en posición ${i}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
targetImages.push({
|
|
114
|
-
data: greyImageData,
|
|
115
|
-
width: img.width,
|
|
116
|
-
height: img.height,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Compilar Match y Track por separado
|
|
121
|
-
const matchingDataList = await this._compileMatch(targetImages, (p) => {
|
|
122
|
-
progressCallback(p * 0.7); // 70% Match
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const trackingDataList = await this._compileTrack(targetImages, (p) => {
|
|
126
|
-
progressCallback(70 + p * 0.3); // 30% Track
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
this.data = targetImages.map((img, i) => ({
|
|
130
|
-
targetImage: img,
|
|
131
|
-
matchingData: matchingDataList[i],
|
|
132
|
-
trackingData: trackingDataList[i],
|
|
133
|
-
}));
|
|
134
|
-
|
|
135
|
-
console.timeEnd("⏱️ Compilación total");
|
|
136
|
-
return this.data;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async _compileMatch(targetImages, progressCallback) {
|
|
140
|
-
const percentPerImage = 100 / targetImages.length;
|
|
141
|
-
let currentPercent = 0;
|
|
142
|
-
|
|
143
|
-
// Use workers if available
|
|
144
|
-
if (isNode) await this._initNodeWorkers();
|
|
145
|
-
if (this.workerPool) {
|
|
146
|
-
const progressMap = new Float32Array(targetImages.length);
|
|
147
|
-
|
|
148
|
-
const wrappedPromises = targetImages.map((targetImage, index) => {
|
|
149
|
-
return this.workerPool.runTask({
|
|
150
|
-
type: 'match',
|
|
151
|
-
targetImage,
|
|
152
|
-
percentPerImage,
|
|
153
|
-
basePercent: 0,
|
|
154
|
-
onProgress: (p) => {
|
|
155
|
-
progressMap[index] = p;
|
|
156
|
-
const sum = progressMap.reduce((a, b) => a + b, 0);
|
|
157
|
-
progressCallback(sum);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
return Promise.all(wrappedPromises);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Serial Fallback
|
|
166
|
-
const results = [];
|
|
167
|
-
for (let i = 0; i < targetImages.length; i++) {
|
|
168
|
-
const targetImage = targetImages[i];
|
|
169
|
-
const imageList = buildImageList(targetImage);
|
|
170
|
-
const percentPerScale = percentPerImage / imageList.length;
|
|
171
|
-
|
|
172
|
-
const keyframes = [];
|
|
173
|
-
|
|
174
|
-
for (const image of imageList) {
|
|
175
|
-
const detector = new DetectorLite(image.width, image.height, { useLSH: true });
|
|
176
|
-
const { featurePoints: ps } = detector.detect(image.data);
|
|
177
|
-
|
|
178
|
-
const maximaPoints = ps.filter((p) => p.maxima);
|
|
179
|
-
const minimaPoints = ps.filter((p) => !p.maxima);
|
|
180
|
-
const maximaPointsCluster = hierarchicalClusteringBuild({ points: maximaPoints });
|
|
181
|
-
const minimaPointsCluster = hierarchicalClusteringBuild({ points: minimaPoints });
|
|
182
|
-
|
|
183
|
-
keyframes.push({
|
|
184
|
-
maximaPoints,
|
|
185
|
-
minimaPoints,
|
|
186
|
-
maximaPointsCluster,
|
|
187
|
-
minimaPointsCluster,
|
|
188
|
-
width: image.width,
|
|
189
|
-
height: image.height,
|
|
190
|
-
scale: image.scale,
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
currentPercent += percentPerScale;
|
|
194
|
-
progressCallback(currentPercent);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
results.push(keyframes);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return results;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
async _compileTrack(targetImages, progressCallback) {
|
|
204
|
-
const percentPerImage = 100 / targetImages.length;
|
|
205
|
-
let currentPercent = 0;
|
|
206
|
-
|
|
207
|
-
if (this.workerPool) {
|
|
208
|
-
const progressMap = new Float32Array(targetImages.length);
|
|
209
|
-
const wrappedPromises = targetImages.map((targetImage, index) => {
|
|
210
|
-
return this.workerPool.runTask({
|
|
211
|
-
type: 'compile',
|
|
212
|
-
targetImage,
|
|
213
|
-
percentPerImage,
|
|
214
|
-
basePercent: 0,
|
|
215
|
-
onProgress: (p) => {
|
|
216
|
-
progressMap[index] = p;
|
|
217
|
-
const sum = progressMap.reduce((a, b) => a + b, 0);
|
|
218
|
-
progressCallback(sum);
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
return Promise.all(wrappedPromises);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Serial Fallback
|
|
226
|
-
const results = [];
|
|
227
|
-
for (let i = 0; i < targetImages.length; i++) {
|
|
228
|
-
const targetImage = targetImages[i];
|
|
229
|
-
const imageList = buildTrackingImageList(targetImage);
|
|
230
|
-
const percentPerScale = percentPerImage / imageList.length;
|
|
231
|
-
|
|
232
|
-
const trackingData = extractTrackingFeatures(imageList, () => {
|
|
233
|
-
currentPercent += percentPerScale;
|
|
234
|
-
progressCallback(currentPercent);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
results.push(trackingData);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return results;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
async compileTrack({ progressCallback, targetImages, basePercent = 0 }) {
|
|
244
|
-
return this._compileTrack(targetImages, (percent) => {
|
|
245
|
-
progressCallback(basePercent + percent * (100 - basePercent) / 100);
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async compileMatch({ progressCallback, targetImages, basePercent = 0 }) {
|
|
250
|
-
return this._compileMatch(targetImages, (percent) => {
|
|
251
|
-
progressCallback(basePercent + percent * (50 - basePercent) / 100);
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
exportData() {
|
|
256
|
-
if (!this.data) {
|
|
257
|
-
throw new Error("No hay datos compilados para exportar");
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const dataList = this.data.map((item) => {
|
|
261
|
-
const matchingData = item.matchingData.map((kf) => this._packKeyframe(kf));
|
|
262
|
-
|
|
263
|
-
const trackingData = item.trackingData.map((td) => {
|
|
264
|
-
const count = td.points.length;
|
|
265
|
-
// Packed Coords - Float32 for now as in current import logic
|
|
266
|
-
const px = new Float32Array(count);
|
|
267
|
-
const py = new Float32Array(count);
|
|
268
|
-
for (let i = 0; i < count; i++) {
|
|
269
|
-
px[i] = td.points[i].x;
|
|
270
|
-
py[i] = td.points[i].y;
|
|
271
|
-
}
|
|
272
|
-
return {
|
|
273
|
-
w: td.width,
|
|
274
|
-
h: td.height,
|
|
275
|
-
s: td.scale,
|
|
276
|
-
px,
|
|
277
|
-
py,
|
|
278
|
-
d: td.data, // Grayscale pixel data (Uint8Array)
|
|
279
|
-
};
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
return {
|
|
283
|
-
targetImage: {
|
|
284
|
-
width: item.targetImage.width,
|
|
285
|
-
height: item.targetImage.height,
|
|
286
|
-
},
|
|
287
|
-
trackingData,
|
|
288
|
-
matchingData,
|
|
289
|
-
};
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
return msgpack.encode({
|
|
293
|
-
v: CURRENT_VERSION,
|
|
294
|
-
dataList,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
_getMorton(x, y) {
|
|
299
|
-
// Interleave bits of x and y
|
|
300
|
-
let x_int = x | 0;
|
|
301
|
-
let y_int = y | 0;
|
|
302
|
-
|
|
303
|
-
x_int = (x_int | (x_int << 8)) & 0x00FF00FF;
|
|
304
|
-
x_int = (x_int | (x_int << 4)) & 0x0F0F0F0F;
|
|
305
|
-
x_int = (x_int | (x_int << 2)) & 0x33333333;
|
|
306
|
-
x_int = (x_int | (x_int << 1)) & 0x55555555;
|
|
307
|
-
|
|
308
|
-
y_int = (y_int | (y_int << 8)) & 0x00FF00FF;
|
|
309
|
-
y_int = (y_int | (y_int << 4)) & 0x0F0F0F0F;
|
|
310
|
-
y_int = (y_int | (y_int << 2)) & 0x33333333;
|
|
311
|
-
y_int = (y_int | (y_int << 1)) & 0x55555555;
|
|
312
|
-
|
|
313
|
-
return x_int | (y_int << 1);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
_packKeyframe(kf) {
|
|
317
|
-
// Step 2.1: Morton Sorting - Sort points spatially to improve Delta-Descriptor XOR
|
|
318
|
-
const sortPoints = (points) => {
|
|
319
|
-
return [...points].sort((a, b) => {
|
|
320
|
-
return this._getMorton(a.x, a.y) - this._getMorton(b.x, b.y);
|
|
321
|
-
});
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
const sortedMaxima = sortPoints(kf.maximaPoints);
|
|
325
|
-
const sortedMinima = sortPoints(kf.minimaPoints);
|
|
326
|
-
|
|
327
|
-
// Rebuild clusters with sorted indices
|
|
328
|
-
const sortedMaximaCluster = hierarchicalClusteringBuild({ points: sortedMaxima });
|
|
329
|
-
const sortedMinimaCluster = hierarchicalClusteringBuild({ points: sortedMinima });
|
|
330
|
-
|
|
331
|
-
return {
|
|
332
|
-
w: kf.width,
|
|
333
|
-
h: kf.height,
|
|
334
|
-
s: kf.scale,
|
|
335
|
-
max: this._columnarize(sortedMaxima, sortedMaximaCluster, kf.width, kf.height),
|
|
336
|
-
min: this._columnarize(sortedMinima, sortedMinimaCluster, kf.width, kf.height),
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
_columnarize(points, tree, width, height) {
|
|
341
|
-
const count = points.length;
|
|
342
|
-
// Step 1: Packed Coords - Normalize to 16-bit
|
|
343
|
-
const x = new Uint16Array(count);
|
|
344
|
-
const y = new Uint16Array(count);
|
|
345
|
-
// Step 1.1: Angle Quantization - Int16
|
|
346
|
-
const angle = new Int16Array(count);
|
|
347
|
-
// Step 1.2: Scale Indexing - Uint8
|
|
348
|
-
const scale = new Uint8Array(count);
|
|
349
|
-
|
|
350
|
-
// Step 3: LSH 64-bit Descriptors - Uint32Array (2 elements per point)
|
|
351
|
-
const descriptors = new Uint32Array(count * 2);
|
|
352
|
-
|
|
353
|
-
for (let i = 0; i < count; i++) {
|
|
354
|
-
x[i] = Math.round((points[i].x / width) * 65535);
|
|
355
|
-
y[i] = Math.round((points[i].y / height) * 65535);
|
|
356
|
-
angle[i] = Math.round((points[i].angle / Math.PI) * 32767);
|
|
357
|
-
scale[i] = Math.round(Math.log2(points[i].scale || 1));
|
|
358
|
-
|
|
359
|
-
if (points[i].descriptors && points[i].descriptors.length >= 2) {
|
|
360
|
-
descriptors[i * 2] = points[i].descriptors[0];
|
|
361
|
-
descriptors[(i * 2) + 1] = points[i].descriptors[1];
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return {
|
|
366
|
-
x,
|
|
367
|
-
y,
|
|
368
|
-
a: angle,
|
|
369
|
-
s: scale,
|
|
370
|
-
d: descriptors,
|
|
371
|
-
t: this._compactTree(tree.rootNode),
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
_compactTree(node) {
|
|
376
|
-
if (node.leaf) {
|
|
377
|
-
return [1, node.centerPointIndex || 0, node.pointIndexes];
|
|
378
|
-
}
|
|
379
|
-
return [0, node.centerPointIndex || 0, node.children.map((c) => this._compactTree(c))];
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
importData(buffer) {
|
|
383
|
-
const content = msgpack.decode(new Uint8Array(buffer));
|
|
384
|
-
|
|
385
|
-
const version = content.v || 0;
|
|
386
|
-
if (version !== CURRENT_VERSION && version !== 5) {
|
|
387
|
-
console.error(`Incompatible .mind version: ${version}. This engine only supports Protocol V5/V6.`);
|
|
388
|
-
return [];
|
|
389
|
-
}
|
|
390
|
-
const descSize = version >= 6 ? 2 : 4;
|
|
391
|
-
|
|
392
|
-
// Restore TypedArrays from Uint8Arrays returned by msgpack
|
|
393
|
-
const dataList = content.dataList;
|
|
394
|
-
for (let i = 0; i < dataList.length; i++) {
|
|
395
|
-
const item = dataList[i];
|
|
396
|
-
|
|
397
|
-
// Unpack Tracking Data
|
|
398
|
-
for (const td of item.trackingData) {
|
|
399
|
-
let px = td.px;
|
|
400
|
-
let py = td.py;
|
|
401
|
-
|
|
402
|
-
if (px instanceof Uint8Array) {
|
|
403
|
-
px = new Float32Array(px.buffer.slice(px.byteOffset, px.byteOffset + px.byteLength));
|
|
404
|
-
}
|
|
405
|
-
if (py instanceof Uint8Array) {
|
|
406
|
-
py = new Float32Array(py.buffer.slice(py.byteOffset, py.byteOffset + py.byteLength));
|
|
407
|
-
}
|
|
408
|
-
td.px = px;
|
|
409
|
-
td.py = py;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Unpack Matching Data
|
|
413
|
-
for (const kf of item.matchingData) {
|
|
414
|
-
for (const col of [kf.max, kf.min]) {
|
|
415
|
-
let xRaw = col.x;
|
|
416
|
-
let yRaw = col.y;
|
|
417
|
-
|
|
418
|
-
if (xRaw instanceof Uint8Array) {
|
|
419
|
-
xRaw = new Uint16Array(xRaw.buffer.slice(xRaw.byteOffset, xRaw.byteOffset + xRaw.byteLength));
|
|
420
|
-
}
|
|
421
|
-
if (yRaw instanceof Uint8Array) {
|
|
422
|
-
yRaw = new Uint16Array(yRaw.buffer.slice(yRaw.byteOffset, yRaw.byteOffset + yRaw.byteLength));
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Rescale for compatibility with Matcher
|
|
426
|
-
const count = xRaw.length;
|
|
427
|
-
const x = new Float32Array(count);
|
|
428
|
-
const y = new Float32Array(count);
|
|
429
|
-
for (let k = 0; k < count; k++) {
|
|
430
|
-
x[k] = (xRaw[k] / 65535) * kf.w;
|
|
431
|
-
y[k] = (yRaw[k] / 65535) * kf.h;
|
|
432
|
-
}
|
|
433
|
-
col.x = x;
|
|
434
|
-
col.y = y;
|
|
435
|
-
|
|
436
|
-
if (col.a instanceof Uint8Array) {
|
|
437
|
-
const aRaw = new Int16Array(col.a.buffer.slice(col.a.byteOffset, col.a.byteOffset + col.a.byteLength));
|
|
438
|
-
const a = new Float32Array(count);
|
|
439
|
-
for (let k = 0; k < count; k++) {
|
|
440
|
-
a[k] = (aRaw[k] / 32767) * Math.PI;
|
|
441
|
-
}
|
|
442
|
-
col.a = a;
|
|
443
|
-
}
|
|
444
|
-
if (col.s instanceof Uint8Array) {
|
|
445
|
-
const sRaw = col.s;
|
|
446
|
-
const s = new Float32Array(count);
|
|
447
|
-
for (let k = 0; k < count; k++) {
|
|
448
|
-
s[k] = Math.pow(2, sRaw[k]);
|
|
449
|
-
}
|
|
450
|
-
col.s = s;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// Restore LSH descriptors (Uint32Array)
|
|
454
|
-
if (col.d instanceof Uint8Array) {
|
|
455
|
-
col.d = new Uint32Array(col.d.buffer.slice(col.d.byteOffset, col.d.byteOffset + col.d.byteLength));
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
this.data = dataList;
|
|
462
|
-
return { version, dataList };
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
_unpackKeyframe(kf) {
|
|
466
|
-
return {
|
|
467
|
-
width: kf.w,
|
|
468
|
-
height: kf.h,
|
|
469
|
-
scale: kf.s,
|
|
470
|
-
maximaPoints: this._decolumnarize(kf.max, kf.w, kf.h),
|
|
471
|
-
minimaPoints: this._decolumnarize(kf.min, kf.w, kf.h),
|
|
472
|
-
maximaPointsCluster: { rootNode: this._expandTree(kf.max.t) },
|
|
473
|
-
minimaPointsCluster: { rootNode: this._expandTree(kf.min.t) },
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
_decolumnarize(col, width, height) {
|
|
478
|
-
const points = [];
|
|
479
|
-
const count = col.x.length;
|
|
480
|
-
const descSize = col.d.length / count;
|
|
481
|
-
|
|
482
|
-
for (let i = 0; i < count; i++) {
|
|
483
|
-
points.push({
|
|
484
|
-
x: (col.x[i] / 65535) * width,
|
|
485
|
-
y: (col.y[i] / 65535) * height,
|
|
486
|
-
angle: col.a[i],
|
|
487
|
-
scale: col.s ? col.s[i] : 1.0,
|
|
488
|
-
descriptors: col.d.slice(i * descSize, (i + 1) * descSize),
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
return points;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
_expandTree(node) {
|
|
495
|
-
const isLeaf = node[0] === 1;
|
|
496
|
-
if (isLeaf) {
|
|
497
|
-
return {
|
|
498
|
-
leaf: true,
|
|
499
|
-
centerPointIndex: node[1],
|
|
500
|
-
pointIndexes: node[2],
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
return {
|
|
504
|
-
leaf: false,
|
|
505
|
-
centerPointIndex: node[1],
|
|
506
|
-
children: node[2].map((c) => this._expandTree(c)),
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
async destroy() {
|
|
511
|
-
if (this.workerPool) {
|
|
512
|
-
await this.workerPool.destroy();
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|