@vosjs/studio-core 0.7.1 → 0.8.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/README.md CHANGED
@@ -36,7 +36,7 @@ The shared layers lower as one engine `stack` entry, `STUDIO_ENTRY_ID` (`'vosso.
36
36
  - **Seconds everywhere.** Zoom, speed, tilt and cam-motion spans and the cursor samples are anchored in source time, so they follow footage through trims. Overlays, audio clips and click effects are anchored in output time, so a title keeps its perceived length through a speed change. `ratedSegments`, `effectiveSegments`, `docOutputDuration` and `outputRangeToSource` are the remaps.
37
37
  - **Normalized coordinates.** A zoom's `cx`/`cy` and an overlay's `transform.x`/`y` are fractions of the frame in `[0, 1]`, never pixels, so a document survives an aspect-ratio switch. `focusBounds` and `clampFocus` keep a focus inside the card at a given `level`.
38
38
  - **Camera styles.** `ZOOM_STYLES` names eight camera personalities (`glide`, the default, then `focus`, `cinema`, `snappy`, `cut`, `keynote`, `drift`, `none`); a style parametrizes both the planner and the camera motion, and `doc.zoomParams` layers per-document overrides.
39
- - **The stage.** `CARD_FOV`, `CARD_Z`, `OVERLAY_Z`, `BACKGROUND_Z` and `planeSizeAtDepth` define the frustum-filling planes the program draws (background, the tiltable card, the overlay); `computeCardLayout` and `docCardLayout` are the host-side mirror of the program's card math, pinned to it by tests.
39
+ - **The stage.** `CARD_FOV`, `CARD_Z`, `OVERLAY_Z`, `BACKGROUND_Z` and `planeSizeAtDepth` define the frustum-filling planes the program draws (background, the tiltable card, the overlay); `cardVisibleExtent` and `cardOverscanFor` are how far a posed card is seen past the frame, which is what the card layer's canvas and plane grow by (so a lean over zoomed footage never shows the texture's edge); `computeCardLayout` and `docCardLayout` are the host-side mirror of the program's card math, pinned to it by tests.
40
40
  - **Backdrops.** `withBackdrop(frame, backdrop)` and `backdropMedia` put a looping video or a still behind the card, output-anchored modulo its duration. The package carries the mechanism and `BASE_FRAME_STYLE`; which loop a host opens on is the host's choice.
41
41
 
42
42
  ## Seeing a recording: the digest
package/dist/index.js CHANGED
@@ -1266,7 +1266,8 @@ var CARD_FOV = 30;
1266
1266
  var OVERLAY_Z = -2;
1267
1267
  var CARD_Z = -4;
1268
1268
  var BACKGROUND_Z = -6;
1269
- var CARD_OVERSCAN = 1.25;
1269
+ var CARD_OVERSCAN_STEP = 0.05;
1270
+ var CARD_OVERSCAN_MAX = 4;
1270
1271
  var CAMERA_NEAR = 0.1;
1271
1272
  var CAMERA_FAR = 100;
1272
1273
  function planeSizeAtDepth(distance, fovDeg, aspect) {
@@ -3582,30 +3583,81 @@ var ON_FRAME = `(ctx, content, dt) => {
3582
3583
  // the GPU texture allocated at the original dims and re-uploads the new canvas
3583
3584
  // against stale dims \u2192 stretch/duplicate; dispose() forces a full realloc) and
3584
3585
  // rebuilding each frustum-filling plane at the new aspect + syncing the camera.
3585
- // Card OVERSCAN (stage.ts CARD_OVERSCAN): a frame whose inset bleeds the
3586
- // card past an edge paints it on a canvas and a plane larger than the
3587
- // frame by ck about the centre, so a tilt that turns the bled edge back
3588
- // toward the viewer shows card there, not the texture's edge. Every doc
3589
- // that keeps its card inside the frame (and the flat stub, which has no
3590
- // card layer) stays at 1: byte- and pixel-identical.
3586
+ // Card OVERSCAN: the card's pose (the tilt track, the card-pose track)
3587
+ // lets the frame see MORE plane than the frame is wide on a receding
3588
+ // side, and the card's content (zoomed footage, a bled card, the shadow)
3589
+ // runs past the frame, so a plane that ends at the frame edge shows its
3590
+ // texture's edge, and the backdrop through it, as soon as it leans. The
3591
+ // card layer's canvas and plane therefore grow about the frame's centre
3592
+ // by the extent the two tracks can reach at the LIVE aspect: each frame
3593
+ // corner's viewing ray meets the posed plane and reads back in plane
3594
+ // units; the extremes of both tracks (largest lean per axis, smallest
3595
+ // scale, largest rise, every sign) bound every interpolated frame. This
3596
+ // is the twin of stage.ts cardVisibleExtent/cardOverscanFor (pinned by
3597
+ // stage.test.ts + cardTilt.test.ts; change them together). A card that
3598
+ // never moves, and the flat stub (no card layer), stay at 1: byte- and
3599
+ // pixel-identical. The factor is quantised up in steps so a scrubbed
3600
+ // pose grows the canvas a few times, not per tick.
3591
3601
  var d = ctx.data || {}
3592
- var ckIns = (d.frame && d.frame.inset) || {}
3593
- var ck = r.card && (ckIns.left < 0 || ckIns.right < 0 || ckIns.top < 0 || ckIns.bottom < 0) ? ${CARD_OVERSCAN} : 1
3594
- var cW = Math.round(W * ck), cH = Math.round(H * ck)
3602
+ var okx = 1, oky = 1
3603
+ if (r.card) {
3604
+ var okT = d.tiltTrack, okP = d.cardPoseTrack, okKf, oki, okv
3605
+ var okRx = 0, okRy = 0, okSc = 1, okDy = 0
3606
+ okKf = okT && okT.keyframes || []
3607
+ for (oki = 0; oki < okKf.length; oki++) { okv = okKf[oki].value || []; okRx = Math.max(okRx, Math.abs(okv[0] || 0)); okRy = Math.max(okRy, Math.abs(okv[1] || 0)) }
3608
+ okKf = okP && okP.keyframes || []
3609
+ for (oki = 0; oki < okKf.length; oki++) { okv = okKf[oki].value || []; okSc = Math.min(okSc, okv[0] == null ? 1 : okv[0]); okDy = Math.max(okDy, Math.abs(okv[1] || 0)) }
3610
+ if (okRx || okRy || okSc < 1 || okDy) {
3611
+ var okD = ${Math.abs(CARD_Z)}, okMax = ${CARD_OVERSCAN_MAX}, okStep = ${CARD_OVERSCAN_STEP}
3612
+ var okHh = okD * Math.tan(${CARD_FOV} * Math.PI / 180 / 2), okHw = okHh * W / H
3613
+ // [max |x|, max |y|] of the frame corners on the posed plane, in
3614
+ // half-plane units (R = Rx \xB7 Ry, three.js Euler XYZ with rz = 0).
3615
+ var okE = function (rxd, ryd, sc, dy) {
3616
+ var rx = rxd * Math.PI / 180, ry = ryd * Math.PI / 180
3617
+ var cx = Math.cos(rx), sx = Math.sin(rx), cy = Math.cos(ry), sy = Math.sin(ry)
3618
+ var scl = Math.max(0.05, sc), py = dy * 2 * okHh
3619
+ var n0 = sy, n1 = -sx * cy, n2 = cx * cy
3620
+ var nPos = n1 * py - n2 * okD
3621
+ var ex = 0, ey = 0
3622
+ for (var kx = -1; kx <= 1; kx += 2) for (var ky = -1; ky <= 1; ky += 2) {
3623
+ var d0 = kx * okHw, d1 = ky * okHh, d2 = -okD
3624
+ var nd = n0 * d0 + n1 * d1 + n2 * d2
3625
+ var lam = nd === 0 ? -1 : nPos / nd
3626
+ if (!(lam > 0)) { ex = okMax; ey = okMax; continue }
3627
+ var q0 = lam * d0, q1 = lam * d1 - py, q2 = lam * d2 + okD
3628
+ ex = Math.max(ex, Math.abs((cy * q0 + sx * sy * q1 - cx * sy * q2) / scl / okHw))
3629
+ ey = Math.max(ey, Math.abs((cx * q1 + sx * q2) / scl / okHh))
3630
+ }
3631
+ return [Math.min(okMax, ex), Math.min(okMax, ey)]
3632
+ }
3633
+ for (var okA = -1; okA <= 1; okA += 2) for (var okB = -1; okB <= 1; okB += 2) for (var okC = -1; okC <= 1; okC += 2) {
3634
+ var okR = okE(okA * okRx, okB * okRy, okSc, okC * okDy)
3635
+ okx = Math.max(okx, okR[0]); oky = Math.max(oky, okR[1])
3636
+ }
3637
+ var okQ = function (k) { if (!(k > 1)) return 1; var q = Math.ceil(Math.round((k + okStep / 10) * 1000) / (okStep * 1000)) * okStep; return Math.min(okMax, Math.round(q * 1000) / 1000) }
3638
+ okx = okQ(okx); oky = okQ(oky)
3639
+ }
3640
+ }
3641
+ // The canvas grows by an EVEN number of pixels on each axis so the frame
3642
+ // region sits on whole pixels inside it (a half-pixel base translation
3643
+ // would resample every draw).
3644
+ var cW = W + 2 * Math.round(W * (okx - 1) / 2), cH = H + 2 * Math.round(H * (oky - 1) / 2)
3595
3645
  if (cv.width !== cW || cv.height !== cH) {
3596
3646
  var THREE = ctx.THREE
3597
3647
  var aspect = W / H
3598
3648
  var cm = ctx.camera
3599
3649
  if (cm && cm.isPerspectiveCamera) { cm.aspect = aspect; cm.updateProjectionMatrix() }
3600
- var lyr = [[bg, ${BACKGROUND_Z}, 1], [card, ${CARD_Z}, ck], [ov, ${OVERLAY_Z}, 1]]
3650
+ // Each layer's canvas size and its plane's per-axis growth; the card's
3651
+ // plane grows by exactly its canvas's ratio, so a texel stays a pixel.
3652
+ var lyr = [[bg, ${BACKGROUND_Z}, W, H], [card, ${CARD_Z}, cW, cH], [ov, ${OVERLAY_Z}, W, H]]
3601
3653
  for (var Li = 0; Li < lyr.length; Li++) {
3602
- var Ly = lyr[Li][0], Lz = lyr[Li][1], Lk = lyr[Li][2]
3603
- if (Ly.canvas) { Ly.canvas.width = Math.round(W * Lk); Ly.canvas.height = Math.round(H * Lk) }
3654
+ var Ly = lyr[Li][0], Lz = lyr[Li][1], Lw = lyr[Li][2], Lhp = lyr[Li][3]
3655
+ if (Ly.canvas) { Ly.canvas.width = Lw; Ly.canvas.height = Lhp }
3604
3656
  if (Ly.texture && Ly.texture.dispose) Ly.texture.dispose()
3605
3657
  if (Ly.mesh && THREE) {
3606
3658
  if (Ly.mesh.geometry && Ly.mesh.geometry.dispose) Ly.mesh.geometry.dispose()
3607
- var Lh = 2 * Math.abs(Lz) * Math.tan(${CARD_FOV} * Math.PI / 180 / 2) * Lk
3608
- Ly.mesh.geometry = new THREE.PlaneGeometry(Lh * aspect, Lh)
3659
+ var Lh = 2 * Math.abs(Lz) * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
3660
+ Ly.mesh.geometry = new THREE.PlaneGeometry(Lh * aspect * (Lw / W), Lh * (Lhp / H))
3609
3661
  }
3610
3662
  }
3611
3663
  }
@@ -3929,9 +3981,10 @@ var ON_FRAME = `(ctx, content, dt) => {
3929
3981
  // the contain-fit card shows the background layer through the plane's alpha.
3930
3982
  // Under overscan the canvas is larger than the frame and every frame-space
3931
3983
  // draw below rides a base translation to its centre.
3932
- if (ck !== 1) c.setTransform(1, 0, 0, 1, 0, 0)
3984
+ var okOn = cW !== W || cH !== H
3985
+ if (okOn) c.setTransform(1, 0, 0, 1, 0, 0)
3933
3986
  c.clearRect(0, 0, cv.width, cv.height)
3934
- if (ck !== 1) c.setTransform(1, 0, 0, 1, (cW - W) / 2, (cH - H) / 2)
3987
+ if (okOn) c.setTransform(1, 0, 0, 1, (cW - W) / 2, (cH - H) / 2)
3935
3988
  // Footage is nearly always drawn SMALLER than it was recorded (a 1920
3936
3989
  // recording in a padded 1080 card, or a 440-wide tile): the default
3937
3990
  // bilinear tap aliases and blurs a large downscale; 'high' resamples.
@@ -4442,8 +4495,10 @@ var ON_FRAME = `(ctx, content, dt) => {
4442
4495
  card.mesh.rotation.y = ry
4443
4496
  // The card's pose through an entrance or an end card: [scale, dy,
4444
4497
  // opacity]; absent = the rest pose, exactly as before the track existed.
4498
+ // dy is a fraction of the FRAME plane's height (not the geometry's,
4499
+ // which the overscan grows).
4445
4500
  var cpk = d.cardPoseTrack
4446
- var cpH = card.mesh.geometry && card.mesh.geometry.parameters ? card.mesh.geometry.parameters.height : 0
4501
+ var cpH = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
4447
4502
  if (cpk && cpk.keyframes && cpk.keyframes.length && card.mesh.scale && card.mesh.position) {
4448
4503
  var cpv = TL.sample(cpk, t, TL.lerpArray)
4449
4504
  card.mesh.scale.x = cpv[0]; card.mesh.scale.y = cpv[0]