@tileflow/core 0.1.0-alpha.32 → 0.1.0-alpha.34

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.
@@ -168,4 +168,124 @@ type NormalizedTileflowCaptureScene = {
168
168
  declare function normalizeTileflowCaptureScene(input: TileflowCaptureScene): NormalizedTileflowCaptureScene;
169
169
  declare function normalizeTileflowCaptureId(value: string | undefined): string | undefined;
170
170
 
171
- export { type NormalizedTileflowCaptureScene as N, type TileflowCaptureScene as T, type TileflowCaptureApplicationTarget as a, type TileflowCaptureBoundsCamera as b, type TileflowCaptureCamera as c, type TileflowCaptureCenterCamera as d, type TileflowCaptureMapTarget as e, type TileflowCaptureTarget as f, type TileflowCaptureViewport as g, normalizeTileflowCaptureScene as h, tileflowCaptureBoundsCameraSchema as i, tileflowCaptureCameraSchema as j, tileflowCaptureCenterCameraSchema as k, tileflowCaptureIdSchema as l, tileflowCaptureMapTargetSchema as m, normalizeTileflowCaptureId as n, tileflowCaptureSceneLimits as o, tileflowCaptureSceneNameSchema as p, tileflowCaptureSceneSchema as q, tileflowCaptureSceneSchemaVersion as r, tileflowCaptureTargetSchema as s, tileflowCaptureApplicationTargetSchema as t, tileflowCaptureViewportSchema as u, tileflowCaptureSceneFitIssue as v };
171
+ /** A camera between keyframes: always the center form, with bearing and pitch resolved. */
172
+ type TileflowShotCamera = {
173
+ type: 'center';
174
+ center: [number, number];
175
+ zoom: number;
176
+ bearing: number;
177
+ pitch: number;
178
+ };
179
+
180
+ declare const tileflowCaptureShotSchemaVersion: 1;
181
+ declare const tileflowCaptureShotLimits: Readonly<{
182
+ blendThemes: Readonly<{
183
+ maximum: 8;
184
+ minimum: 2;
185
+ }>;
186
+ defaultFps: 30;
187
+ durationMs: Readonly<{
188
+ maximum: 60000;
189
+ }>;
190
+ fps: readonly [24, 25, 30, 50, 60];
191
+ keyframes: Readonly<{
192
+ maximum: 64;
193
+ minimum: 2;
194
+ }>;
195
+ maximumFrames: 3600;
196
+ }>;
197
+ /** Shot names follow the same portable artifact-name rules as scene names. */
198
+ declare const tileflowCaptureShotNameSchema: z.ZodString;
199
+ declare const tileflowCaptureShotBlendSchema: z.ZodObject<{
200
+ themes: z.ZodArray<z.ZodString>;
201
+ position: z.ZodNumber;
202
+ }, z.core.$strict>;
203
+ declare const tileflowCaptureShotKeyframeSchema: z.ZodObject<{
204
+ at: z.ZodNumber;
205
+ camera: z.ZodObject<{
206
+ type: z.ZodLiteral<"center">;
207
+ center: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
208
+ zoom: z.ZodNumber;
209
+ bearing: z.ZodOptional<z.ZodNumber>;
210
+ pitch: z.ZodOptional<z.ZodNumber>;
211
+ }, z.core.$strict>;
212
+ blend: z.ZodOptional<z.ZodObject<{
213
+ themes: z.ZodArray<z.ZodString>;
214
+ position: z.ZodNumber;
215
+ }, z.core.$strict>>;
216
+ }, z.core.$strict>;
217
+ declare const tileflowCaptureShotSchema: z.ZodObject<{
218
+ theme: z.ZodString;
219
+ viewport: z.ZodObject<{
220
+ width: z.ZodNumber;
221
+ height: z.ZodNumber;
222
+ dpr: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>]>>;
223
+ }, z.core.$strict>;
224
+ fps: z.ZodOptional<z.ZodLiteral<24 | 25 | 30 | 50 | 60>>;
225
+ keyframes: z.ZodArray<z.ZodObject<{
226
+ at: z.ZodNumber;
227
+ camera: z.ZodObject<{
228
+ type: z.ZodLiteral<"center">;
229
+ center: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
230
+ zoom: z.ZodNumber;
231
+ bearing: z.ZodOptional<z.ZodNumber>;
232
+ pitch: z.ZodOptional<z.ZodNumber>;
233
+ }, z.core.$strict>;
234
+ blend: z.ZodOptional<z.ZodObject<{
235
+ themes: z.ZodArray<z.ZodString>;
236
+ position: z.ZodNumber;
237
+ }, z.core.$strict>>;
238
+ }, z.core.$strict>>;
239
+ easing: z.ZodOptional<z.ZodEnum<{
240
+ linear: "linear";
241
+ smooth: "smooth";
242
+ }>>;
243
+ map: z.ZodString;
244
+ }, z.core.$strict>;
245
+ type TileflowCaptureShotBlend = z.infer<typeof tileflowCaptureShotBlendSchema>;
246
+ type TileflowCaptureShotKeyframe = z.infer<typeof tileflowCaptureShotKeyframeSchema>;
247
+ type TileflowCaptureShot = z.infer<typeof tileflowCaptureShotSchema>;
248
+ type TileflowCaptureShotEasing = 'linear' | 'smooth';
249
+ type NormalizedTileflowCaptureShotCamera = TileflowShotCamera;
250
+ type NormalizedTileflowCaptureShot = {
251
+ map: string;
252
+ theme: string;
253
+ viewport: {
254
+ width: number;
255
+ height: number;
256
+ dpr: 1 | 2;
257
+ };
258
+ fps: 24 | 25 | 30 | 50 | 60;
259
+ easing: TileflowCaptureShotEasing;
260
+ keyframes: Array<{
261
+ at: number;
262
+ camera: NormalizedTileflowCaptureShotCamera;
263
+ blend?: {
264
+ themes: string[];
265
+ position: number;
266
+ };
267
+ }>;
268
+ };
269
+ /** One rendered frame of a shot: the camera and blend position at `at` milliseconds. */
270
+ type TileflowCaptureShotFrame = {
271
+ index: number;
272
+ at: number;
273
+ camera: NormalizedTileflowCaptureShotCamera;
274
+ /** Present when the shot blends themes. */
275
+ blendPosition?: number;
276
+ };
277
+ declare function normalizeTileflowCaptureShot(input: TileflowCaptureShot): NormalizedTileflowCaptureShot;
278
+ /**
279
+ * The number of frames a shot renders: one at the start and one every `1 / fps` seconds up to its
280
+ * last keyframe. The last keyframe is a frame when its time is a whole number of frame intervals.
281
+ */
282
+ declare function tileflowCaptureShotFrameCount(shot: Pick<TileflowCaptureShot, 'fps' | 'keyframes'>): number;
283
+ /**
284
+ * Interpolates every frame of a shot. Frame `i` shows the shot at `i * 1000 / fps` milliseconds.
285
+ * Within each keyframe segment the center moves in Web Mercator coordinates along the shorter way
286
+ * around the world, zoom and pitch change linearly, bearing turns along the shorter arc, and the
287
+ * blend position changes linearly; `easing: 'smooth'` eases each segment in and out.
288
+ */
289
+ declare function tileflowCaptureShotFrames(shot: TileflowCaptureShot | Omit<TileflowCaptureShot, 'map'>): TileflowCaptureShotFrame[];
290
+
291
+ export { tileflowCaptureSceneSchemaVersion as A, tileflowCaptureShotBlendSchema as B, tileflowCaptureShotFrameCount as C, tileflowCaptureShotFrames as D, tileflowCaptureShotKeyframeSchema as E, tileflowCaptureShotLimits as F, tileflowCaptureShotNameSchema as G, tileflowCaptureShotSchema as H, tileflowCaptureShotSchemaVersion as I, tileflowCaptureTargetSchema as J, tileflowCaptureViewportSchema as K, tileflowCaptureSceneFitIssue as L, type NormalizedTileflowCaptureScene as N, type TileflowCaptureScene as T, type TileflowCaptureShot as a, type NormalizedTileflowCaptureShot as b, type NormalizedTileflowCaptureShotCamera as c, type TileflowCaptureApplicationTarget as d, type TileflowCaptureBoundsCamera as e, type TileflowCaptureCamera as f, type TileflowCaptureCenterCamera as g, type TileflowCaptureMapTarget as h, type TileflowCaptureShotBlend as i, type TileflowCaptureShotEasing as j, type TileflowCaptureShotFrame as k, type TileflowCaptureShotKeyframe as l, type TileflowCaptureTarget as m, type TileflowCaptureViewport as n, normalizeTileflowCaptureId as o, normalizeTileflowCaptureScene as p, normalizeTileflowCaptureShot as q, tileflowCaptureBoundsCameraSchema as r, tileflowCaptureCameraSchema as s, tileflowCaptureApplicationTargetSchema as t, tileflowCaptureCenterCameraSchema as u, tileflowCaptureIdSchema as v, tileflowCaptureMapTargetSchema as w, tileflowCaptureSceneLimits as x, tileflowCaptureSceneNameSchema as y, tileflowCaptureSceneSchema as z };
package/dist/capture.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { N as NormalizedTileflowCaptureScene, a as TileflowCaptureApplicationTarget, b as TileflowCaptureBoundsCamera, c as TileflowCaptureCamera, d as TileflowCaptureCenterCamera, e as TileflowCaptureMapTarget, T as TileflowCaptureScene, f as TileflowCaptureTarget, g as TileflowCaptureViewport, n as normalizeTileflowCaptureId, h as normalizeTileflowCaptureScene, t as tileflowCaptureApplicationTargetSchema, i as tileflowCaptureBoundsCameraSchema, j as tileflowCaptureCameraSchema, k as tileflowCaptureCenterCameraSchema, l as tileflowCaptureIdSchema, m as tileflowCaptureMapTargetSchema, v as tileflowCaptureSceneFitIssue, o as tileflowCaptureSceneLimits, p as tileflowCaptureSceneNameSchema, q as tileflowCaptureSceneSchema, r as tileflowCaptureSceneSchemaVersion, s as tileflowCaptureTargetSchema, u as tileflowCaptureViewportSchema } from './capture-scene-BaAJjGq7.js';
1
+ export { N as NormalizedTileflowCaptureScene, b as NormalizedTileflowCaptureShot, c as NormalizedTileflowCaptureShotCamera, d as TileflowCaptureApplicationTarget, e as TileflowCaptureBoundsCamera, f as TileflowCaptureCamera, g as TileflowCaptureCenterCamera, h as TileflowCaptureMapTarget, T as TileflowCaptureScene, a as TileflowCaptureShot, i as TileflowCaptureShotBlend, j as TileflowCaptureShotEasing, k as TileflowCaptureShotFrame, l as TileflowCaptureShotKeyframe, m as TileflowCaptureTarget, n as TileflowCaptureViewport, o as normalizeTileflowCaptureId, p as normalizeTileflowCaptureScene, q as normalizeTileflowCaptureShot, t as tileflowCaptureApplicationTargetSchema, r as tileflowCaptureBoundsCameraSchema, s as tileflowCaptureCameraSchema, u as tileflowCaptureCenterCameraSchema, v as tileflowCaptureIdSchema, w as tileflowCaptureMapTargetSchema, L as tileflowCaptureSceneFitIssue, x as tileflowCaptureSceneLimits, y as tileflowCaptureSceneNameSchema, z as tileflowCaptureSceneSchema, A as tileflowCaptureSceneSchemaVersion, B as tileflowCaptureShotBlendSchema, C as tileflowCaptureShotFrameCount, D as tileflowCaptureShotFrames, E as tileflowCaptureShotKeyframeSchema, F as tileflowCaptureShotLimits, G as tileflowCaptureShotNameSchema, H as tileflowCaptureShotSchema, I as tileflowCaptureShotSchemaVersion, J as tileflowCaptureTargetSchema, K as tileflowCaptureViewportSchema } from './capture-shot-ChPQvPyi.js';
2
2
  import { z } from 'zod';
3
3
 
4
4
  /** Canonical, filesystem-safe identity shared by authored and emitted Tileflow resources. */
package/dist/capture.js CHANGED
@@ -267,9 +267,219 @@ function hasControlCharacter(value) {
267
267
  }
268
268
  return false;
269
269
  }
270
+
271
+ // src/capture-shot.ts
272
+ import { z as z4 } from "zod";
273
+
274
+ // src/capture-shot-motion.ts
275
+ function tileflowShotStateAt(shot, at) {
276
+ const { keyframes } = shot;
277
+ const first = keyframes[0];
278
+ const last = keyframes.at(-1);
279
+ if (!first || !last || keyframes.length < 2)
280
+ throw new TypeError("A shot needs at least two keyframes.");
281
+ const time = Math.min(last.at, Math.max(first.at, Number.isFinite(at) ? at : first.at));
282
+ let segment = 0;
283
+ while (segment < keyframes.length - 2 && time > keyframes[segment + 1].at) segment += 1;
284
+ const from = keyframes[segment];
285
+ const to = keyframes[segment + 1];
286
+ const linear = Math.min(1, Math.max(0, (time - from.at) / (to.at - from.at)));
287
+ const t = shot.easing === "linear" ? linear : smoothstep(linear);
288
+ return {
289
+ at: time,
290
+ camera: interpolateCamera(cameraOf(from), cameraOf(to), t),
291
+ ...from.blend && to.blend ? { blendPosition: interpolateNumber(from.blend.position, to.blend.position, t) } : {}
292
+ };
293
+ }
294
+ function cameraOf({ camera }) {
295
+ return {
296
+ type: "center",
297
+ center: [camera.center[0], camera.center[1]],
298
+ zoom: camera.zoom,
299
+ bearing: camera.bearing ?? 0,
300
+ pitch: camera.pitch ?? 0
301
+ };
302
+ }
303
+ var maximumMercatorLatitude = 85.051129;
304
+ function interpolateCamera(from, to, t) {
305
+ return {
306
+ type: "center",
307
+ center: interpolateCenter(from.center, to.center, t),
308
+ zoom: interpolateNumber(from.zoom, to.zoom, t),
309
+ bearing: interpolateBearing(from.bearing, to.bearing, t),
310
+ pitch: interpolateNumber(from.pitch, to.pitch, t)
311
+ };
312
+ }
313
+ function interpolateNumber(from, to, t) {
314
+ if (t <= 0 || from === to) return from;
315
+ if (t >= 1) return to;
316
+ return from + (to - from) * t;
317
+ }
318
+ function interpolateCenter(from, to, t) {
319
+ if (t <= 0 || from[0] === to[0] && from[1] === to[1]) return [from[0], from[1]];
320
+ if (t >= 1) return [to[0], to[1]];
321
+ const start = mercatorFromLngLat(from);
322
+ const end = mercatorFromLngLat(to);
323
+ let dx = end[0] - start[0];
324
+ if (dx > 0.5) dx -= 1;
325
+ else if (dx < -0.5) dx += 1;
326
+ return lngLatFromMercator([start[0] + dx * t, start[1] + (end[1] - start[1]) * t]);
327
+ }
328
+ function interpolateBearing(from, to, t) {
329
+ if (t <= 0 || from === to) return from;
330
+ if (t >= 1) return to;
331
+ const delta = ((to - from) % 360 + 540) % 360 - 180;
332
+ return wrapDegrees(from + delta * t);
333
+ }
334
+ function mercatorFromLngLat([longitude, latitude]) {
335
+ const clamped = Math.max(-maximumMercatorLatitude, Math.min(maximumMercatorLatitude, latitude));
336
+ return [
337
+ (180 + longitude) / 360,
338
+ (180 - 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + clamped * Math.PI / 360))) / 360
339
+ ];
340
+ }
341
+ function lngLatFromMercator([x, y]) {
342
+ const latitude = 360 / Math.PI * Math.atan(Math.exp((180 - y * 360) * Math.PI / 180)) - 90;
343
+ return [wrapDegrees(x * 360 - 180), latitude];
344
+ }
345
+ function wrapDegrees(value) {
346
+ if (value >= -180 && value <= 180) return value;
347
+ return ((value + 180) % 360 + 360) % 360 - 180;
348
+ }
349
+ function smoothstep(t) {
350
+ return t * t * (3 - 2 * t);
351
+ }
352
+
353
+ // src/capture-shot.ts
354
+ var tileflowCaptureShotSchemaVersion = 1;
355
+ var tileflowCaptureShotLimits = Object.freeze({
356
+ blendThemes: Object.freeze({ maximum: 8, minimum: 2 }),
357
+ defaultFps: 30,
358
+ durationMs: Object.freeze({ maximum: 6e4 }),
359
+ fps: Object.freeze([24, 25, 30, 50, 60]),
360
+ keyframes: Object.freeze({ maximum: 64, minimum: 2 }),
361
+ maximumFrames: 3600
362
+ });
363
+ var tileflowCaptureShotNameSchema = tileflowCaptureSceneNameSchema;
364
+ var tileflowCaptureShotBlendSchema = z4.object({
365
+ themes: z4.array(tileflowThemeNameSchema).min(tileflowCaptureShotLimits.blendThemes.minimum).max(tileflowCaptureShotLimits.blendThemes.maximum),
366
+ position: z4.number().finite().min(0)
367
+ }).strict().superRefine((blend, context) => {
368
+ if (new Set(blend.themes).size !== blend.themes.length) {
369
+ context.addIssue({ code: "custom", message: "Expected distinct themes", path: ["themes"] });
370
+ }
371
+ if (blend.position > blend.themes.length - 1) {
372
+ context.addIssue({
373
+ code: "custom",
374
+ message: `Expected a position from 0 to ${blend.themes.length - 1}`,
375
+ path: ["position"]
376
+ });
377
+ }
378
+ });
379
+ var tileflowCaptureShotKeyframeSchema = z4.object({
380
+ at: z4.number().int().min(0).max(tileflowCaptureShotLimits.durationMs.maximum),
381
+ camera: tileflowCaptureCenterCameraSchema,
382
+ blend: tileflowCaptureShotBlendSchema.optional()
383
+ }).strict();
384
+ var tileflowCaptureShotFields = {
385
+ theme: tileflowThemeNameSchema,
386
+ viewport: tileflowCaptureViewportSchema,
387
+ fps: z4.literal(tileflowCaptureShotLimits.fps).optional(),
388
+ keyframes: z4.array(tileflowCaptureShotKeyframeSchema).min(tileflowCaptureShotLimits.keyframes.minimum).max(tileflowCaptureShotLimits.keyframes.maximum),
389
+ easing: z4.enum(["smooth", "linear"]).optional()
390
+ };
391
+ var tileflowCaptureShotSchema = z4.object({ map: tileflowPortableIdSchema, ...tileflowCaptureShotFields }).strict().superRefine(checkShotTiming);
392
+ var tileflowMapShotSchema = z4.object(tileflowCaptureShotFields).strict().superRefine(checkShotTiming);
393
+ function normalizeTileflowCaptureShot(input) {
394
+ const shot = tileflowCaptureShotSchema.parse(input);
395
+ return { map: shot.map, ...normalizeShotFields(shot) };
396
+ }
397
+ function tileflowCaptureShotFrameCount(shot) {
398
+ const fps = shot.fps ?? tileflowCaptureShotLimits.defaultFps;
399
+ const duration = shot.keyframes.at(-1)?.at ?? 0;
400
+ return Math.floor(duration * fps / 1e3 + 1e-9) + 1;
401
+ }
402
+ function tileflowCaptureShotFrames(shot) {
403
+ const { map: _map, ...fields } = shot;
404
+ const normalized = normalizeShotFields(tileflowMapShotSchema.parse(fields));
405
+ const count = tileflowCaptureShotFrameCount(normalized);
406
+ const duration = normalized.keyframes.at(-1).at;
407
+ const frames = [];
408
+ for (let index = 0; index < count; index += 1) {
409
+ const at = Math.min(duration, index * 1e3 / normalized.fps);
410
+ frames.push({ index, ...tileflowShotStateAt(normalized, at) });
411
+ }
412
+ return frames;
413
+ }
414
+ function normalizeShotFields(shot) {
415
+ return {
416
+ theme: shot.theme,
417
+ viewport: { ...shot.viewport, dpr: shot.viewport.dpr ?? 1 },
418
+ fps: shot.fps ?? tileflowCaptureShotLimits.defaultFps,
419
+ easing: shot.easing ?? "smooth",
420
+ keyframes: shot.keyframes.map((keyframe) => ({
421
+ at: keyframe.at,
422
+ camera: {
423
+ type: "center",
424
+ center: keyframe.camera.center,
425
+ zoom: keyframe.camera.zoom,
426
+ bearing: keyframe.camera.bearing ?? 0,
427
+ pitch: keyframe.camera.pitch ?? 0
428
+ },
429
+ ...keyframe.blend ? { blend: { themes: [...keyframe.blend.themes], position: keyframe.blend.position } } : {}
430
+ }))
431
+ };
432
+ }
433
+ function checkShotTiming(shot, context) {
434
+ const { keyframes } = shot;
435
+ if (keyframes[0] && keyframes[0].at !== 0) {
436
+ context.addIssue({
437
+ code: "custom",
438
+ message: "Expected the first keyframe at 0 milliseconds",
439
+ path: ["keyframes", 0, "at"]
440
+ });
441
+ }
442
+ for (let index = 1; index < keyframes.length; index += 1) {
443
+ if (keyframes[index].at <= keyframes[index - 1].at) {
444
+ context.addIssue({
445
+ code: "custom",
446
+ message: "Expected a time after the previous keyframe",
447
+ path: ["keyframes", index, "at"]
448
+ });
449
+ }
450
+ }
451
+ const blended = keyframes.filter((keyframe) => keyframe.blend !== void 0);
452
+ if (blended.length > 0) {
453
+ const themes = JSON.stringify(blended[0].blend.themes);
454
+ keyframes.forEach((keyframe, index) => {
455
+ if (!keyframe.blend) {
456
+ context.addIssue({
457
+ code: "custom",
458
+ message: "Expected a blend on every keyframe when one keyframe blends themes",
459
+ path: ["keyframes", index, "blend"]
460
+ });
461
+ } else if (JSON.stringify(keyframe.blend.themes) !== themes) {
462
+ context.addIssue({
463
+ code: "custom",
464
+ message: "Expected the same themes, in the same order, on every keyframe",
465
+ path: ["keyframes", index, "blend", "themes"]
466
+ });
467
+ }
468
+ });
469
+ }
470
+ const frames = tileflowCaptureShotFrameCount(shot);
471
+ if (frames > tileflowCaptureShotLimits.maximumFrames) {
472
+ context.addIssue({
473
+ code: "custom",
474
+ message: `Expected at most ${tileflowCaptureShotLimits.maximumFrames} frames; this shot renders ${frames}`,
475
+ path: ["keyframes", keyframes.length - 1, "at"]
476
+ });
477
+ }
478
+ }
270
479
  export {
271
480
  normalizeTileflowCaptureId,
272
481
  normalizeTileflowCaptureScene,
482
+ normalizeTileflowCaptureShot,
273
483
  tileflowCaptureApplicationTargetSchema,
274
484
  tileflowCaptureBoundsCameraSchema,
275
485
  tileflowCaptureCameraSchema,
@@ -281,6 +491,14 @@ export {
281
491
  tileflowCaptureSceneNameSchema,
282
492
  tileflowCaptureSceneSchema,
283
493
  tileflowCaptureSceneSchemaVersion,
494
+ tileflowCaptureShotBlendSchema,
495
+ tileflowCaptureShotFrameCount,
496
+ tileflowCaptureShotFrames,
497
+ tileflowCaptureShotKeyframeSchema,
498
+ tileflowCaptureShotLimits,
499
+ tileflowCaptureShotNameSchema,
500
+ tileflowCaptureShotSchema,
501
+ tileflowCaptureShotSchemaVersion,
284
502
  tileflowCaptureTargetSchema,
285
503
  tileflowCaptureViewportSchema,
286
504
  tileflowPortableIdSchema,