@displayxr/inline3d 1.44.0 → 1.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/camera.d.ts +5 -1
- package/cursor-depth.d.ts +151 -0
- package/dist/call.js +18 -4
- package/js/camera/pair-cursor.js +66 -32
- package/js/inline3d-cursor-depth-playcanvas.js +369 -69
- package/js/inline3d-cursor-depth.js +91 -20
- package/js/inline3d-cursor-option.js +24 -5
- package/js/inline3d-cursor-shape.js +962 -0
- package/js/inline3d-playcanvas-engine.js +1 -0
- package/js/inline3d-splat-layer-rig.js +3 -0
- package/js/inline3d-three.js +117 -6
- package/model.d.ts +1 -1
- package/package.json +2 -1
- package/splat.d.ts +1 -1
- package/three.d.ts +11 -1
package/js/camera/pair-cursor.js
CHANGED
|
@@ -29,13 +29,16 @@
|
|
|
29
29
|
|
|
30
30
|
import {
|
|
31
31
|
CursorPointer,
|
|
32
|
+
CursorShapeSelector,
|
|
32
33
|
cursorArrowFootprint,
|
|
33
|
-
|
|
34
|
+
cursorShapePolys,
|
|
35
|
+
cursorShapeRaster,
|
|
36
|
+
cursorSoftenPixels,
|
|
37
|
+
drawCursorShape2D,
|
|
34
38
|
cursorDefaultHeight,
|
|
35
39
|
cursorFilterStep,
|
|
36
40
|
cursorShouldDraw,
|
|
37
41
|
cursorSoften,
|
|
38
|
-
cursorSoftArrowRaster,
|
|
39
42
|
cursorTarget,
|
|
40
43
|
resolveCursorSoften,
|
|
41
44
|
CURSOR_ARROW,
|
|
@@ -218,42 +221,38 @@ export function pairCursorTarget(contentPx, pxPerBaseline, tuning = CURSOR_DEFAU
|
|
|
218
221
|
return -cursorTarget(tuning, true, -contentPx / pxPerBaseline) * pxPerBaseline || 0;
|
|
219
222
|
}
|
|
220
223
|
|
|
221
|
-
/** The
|
|
222
|
-
const
|
|
223
|
-
function
|
|
224
|
+
/** The shape's outline + fill inset by one output px (at least 1/19 of its height), cached per size. */
|
|
225
|
+
const _polysBySize = new Map();
|
|
226
|
+
function shapePolys(name, sizePx) {
|
|
224
227
|
const d = sizePx > 0 ? Math.max(1 / 19, 1 / sizePx) : 1 / 19;
|
|
225
|
-
const key = Math.round(d * 1e4)
|
|
226
|
-
let f =
|
|
228
|
+
const key = `${name}:${Math.round(d * 1e4)}`;
|
|
229
|
+
let f = _polysBySize.get(key);
|
|
227
230
|
if (!f) {
|
|
228
|
-
if (
|
|
229
|
-
|
|
231
|
+
if (_polysBySize.size > 64) _polysBySize.clear();
|
|
232
|
+
_polysBySize.set(key, (f = cursorShapePolys(name, d)));
|
|
230
233
|
}
|
|
231
234
|
return f;
|
|
232
235
|
}
|
|
233
236
|
|
|
234
237
|
/**
|
|
235
|
-
* Draw the
|
|
236
|
-
* the pointer, the right eye's as far left (crossed = in front). The shared
|
|
237
|
-
*
|
|
238
|
-
* own half, so
|
|
239
|
-
*
|
|
238
|
+
* Draw the cursor into both halves of a woven buffer: the left eye's copy `disparityPx / 2` right of
|
|
239
|
+
* the pointer, the right eye's as far left (crossed = in front). The shared sprite (the arrow by
|
|
240
|
+
* default, or `shape`): its dark outline, then the white fill inset by a px; the hotspot on the
|
|
241
|
+
* pointer. Each copy is clipped to its own half, so a sprite at the edge never bleeds into the
|
|
242
|
+
* other eye.
|
|
243
|
+
* @param {number} sizePx sprite height (one arrow height) in output px
|
|
244
|
+
* @param {string} [shape='arrow'] a CURSOR_SHAPES name
|
|
240
245
|
*/
|
|
241
|
-
export function drawPairArrow(g, outW, outH, u, v, disparityPx, sizePx) {
|
|
246
|
+
export function drawPairArrow(g, outW, outH, u, v, disparityPx, sizePx, shape = 'arrow') {
|
|
242
247
|
const cy = v * outH;
|
|
243
|
-
const
|
|
248
|
+
const polys = shapePolys(shape, sizePx);
|
|
244
249
|
for (const eye of [0, 1]) {
|
|
245
250
|
const cx = eye * outW + u * outW + (eye === 0 ? disparityPx / 2 : -disparityPx / 2);
|
|
246
251
|
g.save();
|
|
247
252
|
g.beginPath();
|
|
248
253
|
g.rect(eye * outW, 0, outW, outH);
|
|
249
254
|
g.clip();
|
|
250
|
-
|
|
251
|
-
g.beginPath();
|
|
252
|
-
poly.forEach(([x, y], k) => (k ? g.lineTo : g.moveTo).call(g, cx + x * sizePx, cy + y * sizePx));
|
|
253
|
-
g.closePath();
|
|
254
|
-
g.fillStyle = style;
|
|
255
|
-
g.fill();
|
|
256
|
-
}
|
|
255
|
+
drawCursorShape2D(g, shape, cx, cy, sizePx, polys);
|
|
257
256
|
g.restore();
|
|
258
257
|
}
|
|
259
258
|
}
|
|
@@ -282,13 +281,12 @@ function scratchCanvas(w, h) {
|
|
|
282
281
|
* arrow with `radius` (arrow heights) and `alpha` (opacity). Returns false (nothing drawn) when no
|
|
283
282
|
* scratch canvas is available, so the caller can fall back to the crisp arrow.
|
|
284
283
|
*/
|
|
285
|
-
export function drawPairArrowSoft(g, outW, outH, u, v, disparityPx, sizePx, radius, alpha) {
|
|
284
|
+
export function drawPairArrowSoft(g, outW, outH, u, v, disparityPx, sizePx, radius, alpha, shape = 'arrow') {
|
|
286
285
|
const cy = v * outH;
|
|
287
|
-
const
|
|
288
|
-
const polys = [CURSOR_ARROW, fill];
|
|
286
|
+
const polys = shapePolys(shape, sizePx);
|
|
289
287
|
const rasters = [0, 1].map((eye) => {
|
|
290
288
|
const cx = eye * outW + u * outW + (eye === 0 ? disparityPx / 2 : -disparityPx / 2);
|
|
291
|
-
return
|
|
289
|
+
return cursorShapeRaster(shape, cx, cy, sizePx, radius, alpha, [1, 1, 1, 1], [0, 0, 0, 1], polys);
|
|
292
290
|
});
|
|
293
291
|
const sg = scratchCanvas(Math.max(rasters[0].width, rasters[1].width), Math.max(rasters[0].height, rasters[1].height));
|
|
294
292
|
if (!sg || typeof sg.putImageData !== 'function' || typeof ImageData !== 'function') return false;
|
|
@@ -305,6 +303,34 @@ export function drawPairArrowSoft(g, outW, outH, u, v, disparityPx, sizePx, radi
|
|
|
305
303
|
return true;
|
|
306
304
|
}
|
|
307
305
|
|
|
306
|
+
/**
|
|
307
|
+
* A url() image cursor into both halves: at its CSS size (one sprite unit = `sizePx` output px per
|
|
308
|
+
* 19 CSS px), its hotspot on the pointer, softened in the PIXELS (cursorSoftenPixels: `radiusPx`
|
|
309
|
+
* blur, `alpha` opacity) — never a CSS filter or opacity. Returns false when no scratch canvas.
|
|
310
|
+
*/
|
|
311
|
+
export function drawPairImage(g, outW, outH, u, v, disparityPx, sizePx, sprite, radiusPx, alpha) {
|
|
312
|
+
const k = sizePx / 19; // output px per CSS px
|
|
313
|
+
const w = Math.max(1, Math.round(sprite.width * k)), h = Math.max(1, Math.round(sprite.height * k));
|
|
314
|
+
const sg = scratchCanvas(w + 2 * (Math.ceil(radiusPx) + 2), h + 2 * (Math.ceil(radiusPx) + 2));
|
|
315
|
+
if (!sg || typeof sg.getImageData !== 'function' || typeof ImageData !== 'function') return false;
|
|
316
|
+
sg.clearRect(0, 0, sg.canvas.width, sg.canvas.height);
|
|
317
|
+
sg.drawImage(sprite.entry.image, 0, 0, w, h);
|
|
318
|
+
const soft = cursorSoftenPixels(sg.getImageData(0, 0, w, h).data, w, h, radiusPx, alpha);
|
|
319
|
+
sg.clearRect(0, 0, sg.canvas.width, sg.canvas.height);
|
|
320
|
+
sg.putImageData(new ImageData(soft.data, soft.width, soft.height), 0, 0);
|
|
321
|
+
const cy = v * outH - sprite.hotspot[1] * k - soft.pad;
|
|
322
|
+
for (const eye of [0, 1]) {
|
|
323
|
+
const cx = eye * outW + u * outW + (eye === 0 ? disparityPx / 2 : -disparityPx / 2) - sprite.hotspot[0] * k - soft.pad;
|
|
324
|
+
g.save();
|
|
325
|
+
g.beginPath();
|
|
326
|
+
g.rect(eye * outW, 0, outW, outH);
|
|
327
|
+
g.clip();
|
|
328
|
+
g.drawImage(sg.canvas, 0, 0, soft.width, soft.height, Math.round(cx), Math.round(cy), soft.width, soft.height);
|
|
329
|
+
g.restore();
|
|
330
|
+
}
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
|
|
308
334
|
/**
|
|
309
335
|
* One cursor over one woven pair. The owner calls `draw()` after painting the pair each frame
|
|
310
336
|
* (`idle()` on any frame it does not paint a woven pair), and hands it the latest grayscale copy
|
|
@@ -312,8 +338,10 @@ export function drawPairArrowSoft(g, outW, outH, u, v, disparityPx, sizePx, radi
|
|
|
312
338
|
*/
|
|
313
339
|
export class PairCursor {
|
|
314
340
|
/** @param {HTMLElement} canvas the tile — the pointer counts over its box */
|
|
315
|
-
constructor(canvas, { height, pointerScope = 'window', soften } = {}) {
|
|
341
|
+
constructor(canvas, { height, pointerScope = 'window', soften, shape = 'auto', shapeFor = null } = {}) {
|
|
316
342
|
this.pointer = new CursorPointer(canvas, { scope: pointerScope });
|
|
343
|
+
/** Which sprite (CSS mirror / fixed / shapeFor); `.current` = the one drawn last. */
|
|
344
|
+
this.shapes = new CursorShapeSelector(this.pointer, { shape, shapeFor });
|
|
317
345
|
/** Crosstalk softening parameters (resolveCursorSoften). */
|
|
318
346
|
this.soften = resolveCursorSoften(soften, '@displayxr/inline3d pair cursor');
|
|
319
347
|
// A fraction of the tile height when given; by default the OS arrow's size (CSS px → tile).
|
|
@@ -365,6 +393,7 @@ export class PairCursor {
|
|
|
365
393
|
const drawn = cursorShouldDraw(d, pxPerBaseline);
|
|
366
394
|
const disparity = -d * pxPerBaseline; // output px, + = in front
|
|
367
395
|
let soften = null;
|
|
396
|
+
let shape = null;
|
|
368
397
|
if (drawn) {
|
|
369
398
|
// Softening in PANEL px: one output px of an eye spans (tile CSS width × dpr) / outW of them.
|
|
370
399
|
// The ghost offset of a pair IS its disparity (the two halves are the two views).
|
|
@@ -373,13 +402,17 @@ export class PairCursor {
|
|
|
373
402
|
const k = r && r.width > 0 ? (r.width * dpr) / geo.outW : 1;
|
|
374
403
|
const spritePx = r && r.height > 0 ? hf * r.height * dpr : sizePx;
|
|
375
404
|
const s = cursorSoften(d, disparity * k, spritePx, this.soften);
|
|
405
|
+
const sprite = this.shapes.pick({ u, v, point: null }, nowSec * 1000);
|
|
406
|
+
shape = this.shapes.current;
|
|
376
407
|
soften = { blurPx: s.blurPx, alpha: s.alpha, ghostPx: Math.abs(disparity * k), spritePx, radius: spritePx > 0 ? s.blurPx / spritePx : 0, soft: s.blurPx > 0 || s.alpha < 1 };
|
|
377
|
-
if (
|
|
378
|
-
|
|
379
|
-
}
|
|
408
|
+
if (sprite.kind === 'image') {
|
|
409
|
+
drawPairImage(g, geo.outW, geo.outH, u, v, disparity, sizePx, sprite, soften.soft ? soften.radius * sizePx : 0, soften.soft ? s.alpha : 1);
|
|
410
|
+
} else if (sprite.kind === 'shape' && (!soften.soft || !drawPairArrowSoft(g, geo.outW, geo.outH, u, v, disparity, sizePx, soften.radius, s.alpha, shape))) {
|
|
411
|
+
drawPairArrow(g, geo.outW, geo.outH, u, v, disparity, sizePx, shape);
|
|
412
|
+
} // 'none': the page hides its cursor; draw nothing (the OS cursor stays hidden)
|
|
380
413
|
}
|
|
381
414
|
this.pointer.hideCss(drawn); // the OS arrow IS the cursor on the plane
|
|
382
|
-
this.last = { contentPx: this._content, targetPx: -target * pxPerBaseline || 0, disparityPx: drawn ? disparity : 0, filteredPx: disparity || 0, drawn, pxPerBaseline, soften };
|
|
415
|
+
this.last = { contentPx: this._content, targetPx: -target * pxPerBaseline || 0, disparityPx: drawn ? disparity : 0, filteredPx: disparity || 0, drawn, pxPerBaseline, soften, shape };
|
|
383
416
|
return this.last;
|
|
384
417
|
}
|
|
385
418
|
|
|
@@ -414,5 +447,6 @@ export class PairCursor {
|
|
|
414
447
|
|
|
415
448
|
dispose() {
|
|
416
449
|
this.pointer.dispose();
|
|
450
|
+
this.shapes.dispose();
|
|
417
451
|
}
|
|
418
452
|
}
|