@vosjs/studio-core 0.35.3 → 0.35.4

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/dist/index.d.ts CHANGED
@@ -3630,6 +3630,17 @@ declare function spanOutputExtent(segments: readonly (Segment & {
3630
3630
  start: number;
3631
3631
  end: number;
3632
3632
  } | null;
3633
+ /** What a span→track expansion is told about the time around it. */
3634
+ interface TrackOptions {
3635
+ /**
3636
+ * No span motion before this output time: the card's entrance owns it
3637
+ * (`prependEntrance` drops what the track put there). A span already
3638
+ * under way at it ramps in FROM it, at its full duration where the span
3639
+ * has the room, instead of losing its ramp and creeping from the
3640
+ * entrance's last pose to the span's end in one straight line.
3641
+ */
3642
+ notBefore?: number;
3643
+ }
3633
3644
  /**
3634
3645
  * Expand the doc's source-anchored zoom spans into a standard @vosjs/timeline
3635
3646
  * keyframe track in OUTPUT time (values are [level, cx, cy] vectors):
@@ -3647,7 +3658,7 @@ declare function spanOutputExtent(segments: readonly (Segment & {
3647
3658
  interface LoweredZoomSpan extends ZoomSpan {
3648
3659
  followEvents?: FollowEvent[];
3649
3660
  }
3650
- declare function zoomTrackFromDoc(zoom: LoweredZoomSpan[], segments: Segment[], style?: ZoomStyleParams): KeyframeTrack<number[]>;
3661
+ declare function zoomTrackFromDoc(zoom: LoweredZoomSpan[], segments: Segment[], style?: ZoomStyleParams, opts?: TrackOptions): KeyframeTrack<number[]>;
3651
3662
  /**
3652
3663
  * Expand the doc's source-anchored tilt spans into an OUTPUT-time keyframe
3653
3664
  * track of [rx, ry] DEGREES (ON_FRAME converts to radians at the mesh):
@@ -3670,7 +3681,7 @@ declare function tiltTrackFromDoc(tilt: TiltSpan[], segments: Segment[], motion?
3670
3681
  rampOut?: number;
3671
3682
  chainGap?: number;
3672
3683
  pan?: number;
3673
- }): KeyframeTrack<number[]>;
3684
+ }, opts?: TrackOptions): KeyframeTrack<number[]>;
3674
3685
  /**
3675
3686
  * The bubble's rest pose as [x, y, size] frame fractions, resolved through the
3676
3687
  * SAME oracle the picking layer uses (camBubbleRect) so the corner math can
package/dist/index.js CHANGED
@@ -6041,7 +6041,20 @@ function trackEmitter() {
6041
6041
  };
6042
6042
  return { keyframes, push: push2 };
6043
6043
  }
6044
- function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE]) {
6044
+ function connectedWindow(o) {
6045
+ const start = Math.max(o.arrived, Math.min(o.tOut, o.landBy - o.dur));
6046
+ const land = Math.max(
6047
+ start + o.floor,
6048
+ Math.min(start + o.dur, Math.max(o.latest, o.landBy))
6049
+ );
6050
+ return { start, land };
6051
+ }
6052
+ function withEntrance(head, build) {
6053
+ return prependEntrance(build({ notBefore: head.at(-1)?.t }), head);
6054
+ }
6055
+ var spillLimit = (tIn, tOut) => tIn + (tOut - tIn) / 2;
6056
+ function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE], opts = {}) {
6057
+ const notBefore = opts.notBefore ?? -Infinity;
6045
6058
  const panEase = style.panEase;
6046
6059
  const mapped = [...zoom].sort((a, b) => a.in - b.in).flatMap((z) => {
6047
6060
  const ext = spanOutputExtent(segments, z.in, z.out, z.media);
@@ -6062,19 +6075,35 @@ function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE
6062
6075
  let cur = entry;
6063
6076
  const m = transitionMult(z.transition);
6064
6077
  if (!chained) {
6078
+ if (tOut <= notBefore + RAMP_FLOOR) continue;
6065
6079
  const startWanted = tIn - (style.rampIn - style.rampInOverlap) * m;
6066
- const start = push2(startWanted, [1, z.cx, z.cy, 0, ...sc(z)], "none");
6080
+ const start = push2(
6081
+ Math.max(startWanted, notBefore),
6082
+ [1, z.cx, z.cy, 0, ...sc(z)],
6083
+ "none"
6084
+ );
6067
6085
  push2(
6068
6086
  rampLanding(start, startWanted, style.rampIn * m, tOut),
6069
6087
  entry,
6070
6088
  spanEase(z.ease, style.ease)
6071
6089
  );
6072
6090
  }
6091
+ const arrived = keyframes.at(-1)?.t ?? tIn;
6092
+ const next = mapped.at(i + 1);
6093
+ const pan = next && next.tIn - tOut <= style.chainGap ? connectedWindow({
6094
+ arrived,
6095
+ tOut,
6096
+ landBy: next.tIn + style.rampInOverlap * transitionMult(next.z.transition),
6097
+ latest: spillLimit(next.tIn, next.tOut),
6098
+ dur: style.pan * transitionMult(next.z.transition),
6099
+ floor: RAMP_FLOOR
6100
+ }) : null;
6101
+ const holdEnd = pan ? pan.start : tOut;
6073
6102
  let inPath = false;
6074
6103
  for (const e of z.followEvents ?? []) {
6075
6104
  const eOut = sourceToTimeline(segments, e.t);
6076
- if (eOut === null || eOut <= tIn || eOut >= tOut) continue;
6077
- if (eOut - tIn < RAMP_FLOOR || tOut - eOut < RAMP_FLOOR) continue;
6105
+ if (eOut === null || eOut <= tIn || eOut >= holdEnd) continue;
6106
+ if (eOut - tIn < RAMP_FLOOR || holdEnd - eOut < RAMP_FLOOR) continue;
6078
6107
  const next2 = [cur[0], e.cx, e.cy, 1, ...cur.slice(4)];
6079
6108
  if (e.path) {
6080
6109
  if (!inPath) {
@@ -6087,14 +6116,14 @@ function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE
6087
6116
  } else {
6088
6117
  inPath = false;
6089
6118
  push2(eOut, cur, "none");
6090
- push2(Math.min(eOut + style.followRecenter, tOut), next2, panEase);
6119
+ push2(Math.min(eOut + style.followRecenter, holdEnd), next2, panEase);
6091
6120
  }
6092
6121
  cur = next2;
6093
6122
  }
6094
6123
  const drift = Math.max(0, style.holdDrift ?? 0);
6095
6124
  const landed = keyframes.at(-1)?.t;
6096
- if (drift > 0 && z.focusMode !== "auto" && !(z.followEvents ?? []).length && landed !== void 0 && tOut > landed) {
6097
- const grow = Math.min(HOLD_DRIFT_MAX, drift * (tOut - landed));
6125
+ if (drift > 0 && z.focusMode !== "auto" && !(z.followEvents ?? []).length && landed !== void 0 && holdEnd > landed) {
6126
+ const grow = Math.min(HOLD_DRIFT_MAX, drift * (holdEnd - landed));
6098
6127
  cur = [
6099
6128
  clampZoomLevel(cur[0] * (1 + grow)),
6100
6129
  cur[1],
@@ -6102,13 +6131,11 @@ function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE
6102
6131
  cur[3],
6103
6132
  ...cur.slice(4)
6104
6133
  ];
6105
- push2(tOut, cur, "sine.inOut");
6134
+ push2(holdEnd, cur, "sine.inOut");
6106
6135
  } else {
6107
- push2(tOut, cur, "none");
6136
+ push2(holdEnd, cur, "none");
6108
6137
  }
6109
- const next = mapped.at(i + 1);
6110
- if (next && next.tIn - tOut <= style.chainGap) {
6111
- const mNext = transitionMult(next.z.transition);
6138
+ if (next && pan) {
6112
6139
  const nextValue = [
6113
6140
  clampZoomLevel(next.z.level),
6114
6141
  next.z.cx,
@@ -6116,17 +6143,7 @@ function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE
6116
6143
  1,
6117
6144
  ...sc(next.z)
6118
6145
  ];
6119
- push2(
6120
- Math.max(
6121
- tOut + RAMP_FLOOR,
6122
- Math.min(
6123
- tOut + style.pan * mNext,
6124
- next.tIn + style.rampInOverlap * mNext
6125
- )
6126
- ),
6127
- nextValue,
6128
- panEase
6129
- );
6146
+ push2(pan.land, nextValue, panEase);
6130
6147
  chained = true;
6131
6148
  } else {
6132
6149
  push2(
@@ -6142,7 +6159,8 @@ function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE
6142
6159
  function sameVec(a, b) {
6143
6160
  return a.length === b.length && a.every((v, i) => Math.abs(v - b[i]) < 1e-6);
6144
6161
  }
6145
- function tiltTrackFromDoc(tilt, segments, motion = {}) {
6162
+ function tiltTrackFromDoc(tilt, segments, motion = {}, opts = {}) {
6163
+ const notBefore = opts.notBefore ?? -Infinity;
6146
6164
  const rampInDur = motion.rampIn ?? TILT_RAMP_IN;
6147
6165
  const rampOutDur = motion.rampOut ?? TILT_RAMP_OUT;
6148
6166
  const chainGap = motion.chainGap ?? TILT_CHAIN_GAP;
@@ -6160,24 +6178,30 @@ function tiltTrackFromDoc(tilt, segments, motion = {}) {
6160
6178
  const pose = [clampTiltDeg(z.rx), clampTiltDeg(z.ry)];
6161
6179
  const m = transitionMult(z.transition);
6162
6180
  if (!chained) {
6181
+ if (tOut <= notBefore + RAMP_FLOOR) continue;
6163
6182
  const startWanted = tIn - rampInDur * m;
6164
- const start = push2(startWanted, rest, "none");
6183
+ const start = push2(Math.max(startWanted, notBefore), rest, "none");
6165
6184
  push2(
6166
6185
  rampLanding(start, startWanted, rampInDur * m, tOut),
6167
6186
  pose,
6168
6187
  spanEase(z.ease, TILT_EASE)
6169
6188
  );
6170
6189
  }
6171
- push2(tOut, pose, "none");
6190
+ const arrived = keyframes.at(-1)?.t ?? tIn;
6172
6191
  const next = mapped.at(i + 1);
6173
- if (next && next.tIn - tOut <= chainGap) {
6174
- const nextPose = [clampTiltDeg(next.z.rx), clampTiltDeg(next.z.ry)];
6192
+ const swing = next && next.tIn - tOut <= chainGap ? connectedWindow({
6193
+ arrived,
6194
+ tOut,
6195
+ landBy: next.tIn,
6196
+ latest: spillLimit(next.tIn, next.tOut),
6197
+ dur: panDur * transitionMult(next.z.transition),
6198
+ floor: RAMP_FLOOR
6199
+ }) : null;
6200
+ push2(swing ? swing.start : tOut, pose, "none");
6201
+ if (next && swing) {
6175
6202
  push2(
6176
- Math.max(
6177
- tOut + RAMP_FLOOR,
6178
- Math.min(tOut + panDur * transitionMult(next.z.transition), next.tIn)
6179
- ),
6180
- nextPose,
6203
+ swing.land,
6204
+ [clampTiltDeg(next.z.rx), clampTiltDeg(next.z.ry)],
6181
6205
  panEase
6182
6206
  );
6183
6207
  chained = true;
@@ -6214,14 +6238,19 @@ function camTrackFromDoc(cam, spans, segments, W, H = 1080) {
6214
6238
  const start = push2(tIn - CAM_RAMP_IN * m, rest, "none");
6215
6239
  push2(start + CAM_RAMP_IN * m, pose, spanEase(z.ease, CAM_EASE));
6216
6240
  }
6217
- push2(tOut, pose, "none");
6241
+ const arrived = keyframes.at(-1)?.t ?? tIn;
6218
6242
  const next = mapped.at(i + 1);
6219
- if (next && next.tIn - tOut <= CAM_CHAIN_GAP) {
6220
- push2(
6221
- Math.min(tOut + CAM_PAN * transitionMult(next.z.transition), next.tIn),
6222
- poseOf(next.z),
6223
- panEase
6224
- );
6243
+ const morph = next && next.tIn - tOut <= CAM_CHAIN_GAP ? connectedWindow({
6244
+ arrived,
6245
+ tOut,
6246
+ landBy: next.tIn,
6247
+ latest: spillLimit(next.tIn, next.tOut),
6248
+ dur: CAM_PAN * transitionMult(next.z.transition),
6249
+ floor: 0
6250
+ }) : null;
6251
+ push2(morph ? morph.start : tOut, pose, "none");
6252
+ if (next && morph) {
6253
+ push2(morph.land, poseOf(next.z), panEase);
6225
6254
  chained = true;
6226
6255
  } else {
6227
6256
  push2(tOut + CAM_RAMP_OUT * m, rest, spanEase(z.ease, CAM_EASE));
@@ -8369,9 +8398,9 @@ function lowerToComposition(input) {
8369
8398
  camera,
8370
8399
  zoomStyle
8371
8400
  );
8372
- const zoomTrack = prependEntrance(
8373
- zoomTrackFromDoc(zoomSpans, rated, zoomStyle),
8374
- entranceZoomKeyframes(cardEnter(doc.frame))
8401
+ const zoomTrack = withEntrance(
8402
+ entranceZoomKeyframes(cardEnter(doc.frame)),
8403
+ (opts) => zoomTrackFromDoc(zoomSpans, rated, zoomStyle, opts)
8375
8404
  );
8376
8405
  const pins = resolvePins(doc, layout, camera, zoomTrack);
8377
8406
  const pinsData = pinsDataOf(doc, pins);
@@ -8470,8 +8499,8 @@ function lowerToComposition(input) {
8470
8499
  transitions,
8471
8500
  TILT_SPAN_MIN
8472
8501
  ) : [];
8473
- const spans = tilts.length ? tiltTrackFromDoc(tilts, rated, zoomStyle.tilt) : void 0;
8474
- const track = prependEntrance(spans, head);
8502
+ const spans = tilts.length ? (opts) => tiltTrackFromDoc(tilts, rated, zoomStyle.tilt, opts) : () => void 0;
8503
+ const track = withEntrance(head, spans);
8475
8504
  return track ? { tiltTrack: track } : {};
8476
8505
  })(),
8477
8506
  // The card's pose through its enter and its exit: [scale, dy, opacity]
@@ -8567,9 +8596,9 @@ function docZoomTrack(input) {
8567
8596
  camera,
8568
8597
  zoomStyle
8569
8598
  );
8570
- return prependEntrance(
8571
- zoomTrackFromDoc(spans, rated, zoomStyle),
8572
- entranceZoomKeyframes(cardEnter(doc.frame))
8599
+ return withEntrance(
8600
+ entranceZoomKeyframes(cardEnter(doc.frame)),
8601
+ (opts) => zoomTrackFromDoc(spans, rated, zoomStyle, opts)
8573
8602
  );
8574
8603
  }
8575
8604
  function resolvePins(doc, layout, camera, zoomTrack) {