@redwilly/anima 0.1.2 → 0.1.22

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +47 -51
  2. package/dist/index.js +194 -661
  3. package/package.json +9 -1
package/dist/index.d.ts CHANGED
@@ -275,6 +275,7 @@ declare class AnimationQueue {
275
275
  enqueueAnimation(animation: Animation<Mobject>): void;
276
276
  setLastDuration(seconds: number): void;
277
277
  setLastEasing(easing: EasingFunction): void;
278
+ setLastDelay(seconds: number): void;
278
279
  isEmpty(): boolean;
279
280
  popLastAnimation(): Animation<Mobject> | null;
280
281
  toAnimation(): Animation<Mobject>;
@@ -343,6 +344,7 @@ declare class Mobject {
343
344
  };
344
345
  duration(seconds: number): this;
345
346
  ease(easing: EasingFunction): this;
347
+ delay(seconds: number): this;
346
348
  toAnimation(): Animation<Mobject>;
347
349
  getQueuedDuration(): number;
348
350
  hasQueuedAnimations(): boolean;
@@ -1022,6 +1024,16 @@ declare class Scene {
1022
1024
  * Handles composition animations (Sequence, Parallel) by processing children.
1023
1025
  */
1024
1026
  private validateAndRegisterAnimation;
1027
+ /**
1028
+ * Checks if a Mobject is in the scene.
1029
+ * An object is "in scene" if:
1030
+ * - It's directly registered in this.mobjects, OR
1031
+ * - Any ancestor in its parent chain is registered
1032
+ *
1033
+ * This respects the scene graph hierarchy - children of registered
1034
+ * VGroups are implicitly in scene via their parent.
1035
+ */
1036
+ isInScene(mobject: Mobject): boolean;
1025
1037
  /**
1026
1038
  * Gets children of composition animations (Sequence, Parallel).
1027
1039
  * Returns empty array for non-composition animations.
@@ -1297,12 +1309,16 @@ declare class Text extends VGroup {
1297
1309
  readonly text: string;
1298
1310
  private style;
1299
1311
  private fontPath;
1300
- constructor(text: string, fontPath: string, options?: Partial<TextStyle>);
1312
+ constructor(text: string, fontPath?: string, options?: Partial<TextStyle>);
1301
1313
  private buildGlyphs;
1302
1314
  private getCharacterForGlyph;
1303
1315
  private applyStyle;
1304
1316
  setStyle(options: Partial<TextStyle>): this;
1305
1317
  getStyle(): TextStyle;
1318
+ /** Override stroke to propagate to all Glyph children. */
1319
+ stroke(color: Color, width?: number): this;
1320
+ /** Override fill to propagate to all Glyph children. */
1321
+ fill(color: Color, opacity?: number): this;
1306
1322
  getGlyph(index: number): Glyph | undefined;
1307
1323
  }
1308
1324
 
@@ -1578,66 +1594,61 @@ declare class MorphTo<T extends VMobject = VMobject> extends TransformativeAnima
1578
1594
  }
1579
1595
 
1580
1596
  /**
1581
- * Animation that first draws the border progressively, then fills the shape.
1582
- * The first 50% of the animation draws the stroke, the second 50% fades in the fill.
1597
+ * Animation that first draws the stroke progressively, then fades in the fill.
1598
+ * - First 50%: stroke draws progressively
1599
+ * - Second 50%: fill fades in
1583
1600
  *
1584
- * Supports VGroup (including Text): animates each child's paths progressively.
1601
+ * - Single VMobject: stroke then fill
1602
+ * - VGroup: all children animate together
1603
+ * - Text (VGroup of Glyphs): Glyphs animate sequentially for handwriting effect
1585
1604
  *
1586
1605
  * This is an introductory animation - it auto-registers the target with the scene.
1587
- *
1588
- * @example
1589
- * const rect = new Rectangle(2, 1);
1590
- * scene.play(new Draw(rect)); // Border draws, then fill fades in
1591
1606
  */
1592
1607
  declare class Draw<T extends VMobject = VMobject> extends IntroductoryAnimation<T> {
1593
- private readonly isVGroup;
1594
- /** For non-VGroup targets: original paths on the target itself. */
1595
1608
  private readonly originalPaths;
1596
- private readonly originalFillOpacity;
1597
1609
  private readonly originalOpacity;
1598
- /** For VGroup targets: original state of each child. */
1610
+ private readonly originalStrokeColor;
1611
+ private readonly originalStrokeWidth;
1612
+ private readonly originalFillColor;
1613
+ private readonly originalFillOpacity;
1599
1614
  private readonly childStates;
1615
+ /** Glyph states for Text children, keyed by the Text VMobject reference. */
1616
+ private readonly glyphStates;
1600
1617
  constructor(target: T);
1601
- /** Interpolates stroke drawing (0-0.5) and then fill fade (0.5-1). */
1618
+ private createState;
1602
1619
  interpolate(progress: number): void;
1603
- /** Interpolates a single VMobject (non-VGroup). */
1604
- private interpolateVMobject;
1605
- /** Interpolates a VGroup by animating each child's paths. */
1606
1620
  private interpolateVGroup;
1621
+ private interpolateGlyphs;
1622
+ /** Interpolates a single VMobject: stroke (0-0.5), then fill (0.5-1). */
1623
+ private interpolateVMobject;
1607
1624
  }
1608
1625
 
1609
1626
  /**
1610
- * Animation that progressively draws a VMobject's paths from start to end.
1611
- * Preserves both stroke and fill properties - the complete object is visible at the end.
1627
+ * Animation that progressively draws VMobject paths from start to end.
1612
1628
  *
1613
- * This is the canonical animation for progressive path drawing.
1614
- * At progress 0, nothing is shown. At progress 1, the complete path is shown.
1615
- *
1616
- * Supports VGroup (including Text): animates each child's paths progressively.
1629
+ * - Single VMobject: paths draw progressively
1630
+ * - VGroup: all children animate together with same progress
1631
+ * - Text (VGroup of Glyphs): Glyphs animate sequentially for handwriting effect
1617
1632
  *
1618
1633
  * This is an introductory animation - it auto-registers the target with the scene.
1619
- *
1620
- * @example
1621
- * const circle = new Circle(1);
1622
- * scene.play(new Write(circle)); // Circle is drawn progressively
1623
- *
1624
- * const text = new Text('Hello');
1625
- * scene.play(new Write(text)); // Text is written progressively
1626
1634
  */
1627
1635
  declare class Write<T extends VMobject = VMobject> extends IntroductoryAnimation<T> {
1628
- private readonly isVGroup;
1629
- /** For non-VGroup targets: original paths on the target itself. */
1630
1636
  private readonly originalPaths;
1631
1637
  private readonly originalOpacity;
1638
+ private readonly originalStrokeColor;
1639
+ private readonly originalStrokeWidth;
1640
+ private readonly originalFillColor;
1632
1641
  private readonly originalFillOpacity;
1633
- /** For VGroup targets: original state of each child. */
1634
1642
  private readonly childStates;
1643
+ /** Glyph states for Text children, keyed by the Text VMobject reference. */
1644
+ private readonly glyphStates;
1635
1645
  constructor(target: T);
1646
+ private createState;
1636
1647
  interpolate(progress: number): void;
1637
- /** Interpolates a single VMobject (non-VGroup). */
1638
- private interpolateVMobject;
1639
- /** Interpolates a VGroup by animating each child's paths. */
1640
1648
  private interpolateVGroup;
1649
+ private interpolateGlyphs;
1650
+ /** Applies progress to a single VMobject, updating its paths and style. */
1651
+ private applyProgress;
1641
1652
  }
1642
1653
 
1643
1654
  /**
@@ -1901,21 +1912,6 @@ declare class Shake extends TransformativeAnimation<CameraFrame> {
1901
1912
  private noise;
1902
1913
  }
1903
1914
 
1904
- /**
1905
- * Scene serialization - the main entry point for serializing/deserializing Scenes.
1906
- */
1907
-
1908
- /**
1909
- * Serializes a Scene into a JSON-formatted string.
1910
- *
1911
- * Useful for saving scene state to a file or transmitting it over a network.
1912
- */
1913
- declare function serialize(scene: Scene): string;
1914
- /**
1915
- * Restores a complete Scene from a JSON-formatted string.
1916
- */
1917
- declare function deserialize(json: string): Scene;
1918
-
1919
1915
  /**
1920
1916
  * Supported render output formats.
1921
1917
  * - sprite: PNG sequence (frame_0000.png, frame_0001.png, ...)
@@ -2083,4 +2079,4 @@ declare class ProgressReporter {
2083
2079
  complete(): void;
2084
2080
  }
2085
2081
 
2086
- export { Animation, type AnimationConfig, Arc, Arrow, Camera, type CameraConfig, CameraFrame, Circle, Color, Draw, type EasingFunction, type EdgeConfig, FadeIn, FadeOut, Follow, FrameRenderer, Glyph, Graph, GraphEdge, GraphNode, type GraphNodeId, type Keyframe, KeyframeAnimation, KeyframeTrack, type LayoutConfig, type LayoutType, Line, Mobject, MorphTo, MoveTo, type NodeConfig, Parallel, Point, Polygon, type ProgressCallback, ProgressReporter, Rectangle, type RenderConfig, type RenderFormat, type RenderProgress, type RenderQuality, Renderer, Resolution, type ResolvedCameraConfig, Rotate, Scale, Scene, type SceneConfig, type ScheduledAnimation, Sequence, Shake, Text, type TextStyle, Timeline, type TimelineConfig, Unwrite, VGroup, VMobject, Vector2, Write, clearRegistry, smooth as defaultEasing, deserialize, doubleSmooth, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, exponentialDecay, getEasing, hasEasing, linear, lingering, notQuiteThere, registerEasing, runningStart, rushFrom, rushInto, serialize, slowInto, smooth, thereAndBack, thereAndBackWithPause, unregisterEasing, wiggle };
2082
+ export { Animation, type AnimationConfig, Arc, Arrow, Camera, type CameraConfig, CameraFrame, Circle, Color, Draw, type EasingFunction, type EdgeConfig, FadeIn, FadeOut, Follow, FrameRenderer, Glyph, Graph, GraphEdge, GraphNode, type GraphNodeId, type Keyframe, KeyframeAnimation, KeyframeTrack, type LayoutConfig, type LayoutType, Line, Mobject, MorphTo, MoveTo, type NodeConfig, Parallel, Point, Polygon, type ProgressCallback, ProgressReporter, Rectangle, type RenderConfig, type RenderFormat, type RenderProgress, type RenderQuality, Renderer, Resolution, type ResolvedCameraConfig, Rotate, Scale, Scene, type SceneConfig, type ScheduledAnimation, Sequence, Shake, Text, type TextStyle, Timeline, type TimelineConfig, Unwrite, VGroup, VMobject, Vector2, Write, clearRegistry, smooth as defaultEasing, doubleSmooth, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, exponentialDecay, getEasing, hasEasing, linear, lingering, notQuiteThere, registerEasing, runningStart, rushFrom, rushInto, slowInto, smooth, thereAndBack, thereAndBackWithPause, unregisterEasing, wiggle };
package/dist/index.js CHANGED
@@ -978,6 +978,18 @@ var AnimationQueue = class {
978
978
  last.config.easing = easing;
979
979
  }
980
980
  }
981
+ setLastDelay(seconds) {
982
+ if (seconds < 0) {
983
+ throw new Error("Delay must be non-negative");
984
+ }
985
+ const last = this.queue[this.queue.length - 1];
986
+ if (!last) return;
987
+ if (isPrebuilt(last)) {
988
+ last.animation.delay(seconds);
989
+ } else {
990
+ last.config.delaySeconds = seconds;
991
+ }
992
+ }
981
993
  isEmpty() {
982
994
  return this.queue.length === 0;
983
995
  }
@@ -1228,6 +1240,10 @@ var Mobject = class {
1228
1240
  this.getQueue().setLastEasing(easing);
1229
1241
  return this;
1230
1242
  }
1243
+ delay(seconds) {
1244
+ this.getQueue().setLastDelay(seconds);
1245
+ return this;
1246
+ }
1231
1247
  toAnimation() {
1232
1248
  return this.getQueue().toAnimation();
1233
1249
  }
@@ -1944,12 +1960,34 @@ var Scene = class {
1944
1960
  if (target instanceof CameraFrame) {
1945
1961
  break;
1946
1962
  }
1947
- if (!this.mobjects.has(target)) {
1963
+ if (!this.isInScene(target)) {
1948
1964
  throw new AnimationTargetNotInSceneError(anim, target);
1949
1965
  }
1950
1966
  break;
1951
1967
  }
1952
1968
  }
1969
+ /**
1970
+ * Checks if a Mobject is in the scene.
1971
+ * An object is "in scene" if:
1972
+ * - It's directly registered in this.mobjects, OR
1973
+ * - Any ancestor in its parent chain is registered
1974
+ *
1975
+ * This respects the scene graph hierarchy - children of registered
1976
+ * VGroups are implicitly in scene via their parent.
1977
+ */
1978
+ isInScene(mobject) {
1979
+ if (this.mobjects.has(mobject)) {
1980
+ return true;
1981
+ }
1982
+ let current = mobject.parent;
1983
+ while (current !== null) {
1984
+ if (this.mobjects.has(current)) {
1985
+ return true;
1986
+ }
1987
+ current = current.parent;
1988
+ }
1989
+ return false;
1990
+ }
1953
1991
  /**
1954
1992
  * Gets children of composition animations (Sequence, Parallel).
1955
1993
  * Returns empty array for non-composition animations.
@@ -2635,94 +2673,103 @@ function appendPartialCommand(result, cmd, cursor, subpathStart, localT) {
2635
2673
  function isVGroup(target) {
2636
2674
  return typeof target.getChildren === "function";
2637
2675
  }
2676
+ function isGlyph(target) {
2677
+ return typeof target.character === "string";
2678
+ }
2679
+ function isText(target) {
2680
+ if (!isVGroup(target)) return false;
2681
+ const children = target.getChildren();
2682
+ return children.length > 0 && children.every(isGlyph);
2683
+ }
2638
2684
  var Draw = class extends IntroductoryAnimation {
2639
- isVGroup;
2640
- /** For non-VGroup targets: original paths on the target itself. */
2641
2685
  originalPaths;
2642
- originalFillOpacity;
2643
2686
  originalOpacity;
2644
- /** For VGroup targets: original state of each child. */
2687
+ originalStrokeColor;
2688
+ originalStrokeWidth;
2689
+ originalFillColor;
2690
+ originalFillOpacity;
2645
2691
  childStates;
2692
+ /** Glyph states for Text children, keyed by the Text VMobject reference. */
2693
+ glyphStates = /* @__PURE__ */ new Map();
2646
2694
  constructor(target) {
2647
2695
  super(target);
2648
- this.isVGroup = isVGroup(target);
2649
2696
  this.originalOpacity = target.opacity;
2697
+ this.originalStrokeColor = target.getStrokeColor();
2698
+ this.originalStrokeWidth = target.getStrokeWidth();
2699
+ this.originalFillColor = target.getFillColor();
2650
2700
  this.originalFillOpacity = target.getFillOpacity();
2651
- if (this.isVGroup && isVGroup(target)) {
2701
+ if (isVGroup(target)) {
2652
2702
  this.originalPaths = [];
2653
- this.childStates = [];
2654
- for (const child of target.getChildren()) {
2655
- this.childStates.push({
2656
- child,
2657
- originalPaths: child.paths.map((p) => p.clone()),
2658
- originalOpacity: child.opacity,
2659
- originalFillOpacity: child.getFillOpacity()
2660
- });
2661
- }
2703
+ this.childStates = target.getChildren().map((child) => this.createState(child));
2662
2704
  } else {
2663
2705
  this.originalPaths = target.paths.map((p) => p.clone());
2664
2706
  this.childStates = [];
2665
2707
  }
2666
2708
  }
2667
- /** Interpolates stroke drawing (0-0.5) and then fill fade (0.5-1). */
2709
+ createState(child) {
2710
+ const state = {
2711
+ child,
2712
+ paths: child.paths.map((p) => p.clone()),
2713
+ opacity: child.opacity,
2714
+ strokeColor: child.getStrokeColor(),
2715
+ strokeWidth: child.getStrokeWidth(),
2716
+ fillColor: child.getFillColor(),
2717
+ fillOpacity: child.getFillOpacity()
2718
+ };
2719
+ if (isText(child)) {
2720
+ this.glyphStates.set(child, child.getChildren().map((g) => this.createState(g)));
2721
+ }
2722
+ return state;
2723
+ }
2668
2724
  interpolate(progress) {
2669
- if (this.isVGroup) {
2670
- this.interpolateVGroup(progress);
2725
+ if (this.childStates.length === 0) {
2726
+ this.interpolateVMobject(this.target, this.originalPaths, this.originalOpacity, this.originalStrokeColor, this.originalStrokeWidth, this.originalFillColor, this.originalFillOpacity, progress);
2671
2727
  } else {
2672
- this.interpolateVMobject(progress);
2728
+ this.interpolateVGroup(progress);
2673
2729
  }
2674
2730
  }
2675
- /** Interpolates a single VMobject (non-VGroup). */
2676
- interpolateVMobject(progress) {
2731
+ interpolateVGroup(progress) {
2677
2732
  const targetOpacity = this.originalOpacity === 0 ? 1 : this.originalOpacity;
2733
+ this.target.setOpacity(progress <= 0 ? 0 : targetOpacity);
2734
+ for (const state of this.childStates) {
2735
+ const glyphStates = this.glyphStates.get(state.child);
2736
+ if (glyphStates) {
2737
+ state.child.setOpacity(progress <= 0 ? 0 : state.opacity || 1);
2738
+ this.interpolateGlyphs(glyphStates, progress);
2739
+ } else {
2740
+ this.interpolateVMobject(state.child, state.paths, state.opacity, state.strokeColor, state.strokeWidth, state.fillColor, state.fillOpacity, progress);
2741
+ }
2742
+ }
2743
+ }
2744
+ interpolateGlyphs(glyphs, progress) {
2745
+ const lagRatio = 0.2;
2746
+ const totalSpan = 1 + (glyphs.length - 1) * lagRatio;
2747
+ glyphs.forEach((state, i) => {
2748
+ const start = i * lagRatio / totalSpan;
2749
+ const end = (i * lagRatio + 1) / totalSpan;
2750
+ const p = Math.max(0, Math.min(1, (progress - start) / (end - start)));
2751
+ this.interpolateVMobject(state.child, state.paths, state.opacity, state.strokeColor, state.strokeWidth, state.fillColor, state.fillOpacity, p);
2752
+ });
2753
+ }
2754
+ /** Interpolates a single VMobject: stroke (0-0.5), then fill (0.5-1). */
2755
+ interpolateVMobject(target, originalPaths, originalOpacity, originalStrokeColor, originalStrokeWidth, originalFillColor, originalFillOpacity, progress) {
2678
2756
  if (progress <= 0) {
2679
- this.target.paths = [];
2680
- this.target.setOpacity(0);
2681
- this.target.fill(this.target.getFillColor(), 0);
2757
+ target.paths = [];
2758
+ target.setOpacity(0);
2682
2759
  return;
2683
2760
  }
2684
- this.target.setOpacity(targetOpacity);
2761
+ const opacity = originalOpacity === 0 ? 1 : originalOpacity;
2762
+ target.setOpacity(opacity);
2685
2763
  if (progress < 0.5) {
2686
2764
  const strokeProgress = progress * 2;
2687
- const partialPaths = [];
2688
- for (const originalPath of this.originalPaths) {
2689
- partialPaths.push(getPartialPath(originalPath, strokeProgress));
2690
- }
2691
- this.target.paths = partialPaths;
2692
- this.target.fill(this.target.getFillColor(), 0);
2765
+ target.paths = originalPaths.map((p) => getPartialPath(p, strokeProgress));
2766
+ target.stroke(originalStrokeColor, originalStrokeWidth);
2767
+ target.fill(originalFillColor, 0);
2693
2768
  } else {
2694
- this.target.paths = this.originalPaths.map((p) => p.clone());
2695
2769
  const fillProgress = (progress - 0.5) * 2;
2696
- this.target.fill(this.target.getFillColor(), this.originalFillOpacity * fillProgress);
2697
- }
2698
- }
2699
- /** Interpolates a VGroup by animating each child's paths. */
2700
- interpolateVGroup(progress) {
2701
- const targetOpacity = this.originalOpacity === 0 ? 1 : this.originalOpacity;
2702
- this.target.setOpacity(progress <= 0 ? 0 : targetOpacity);
2703
- for (const state of this.childStates) {
2704
- const { child, originalPaths, originalOpacity, originalFillOpacity } = state;
2705
- const childOpacity = originalOpacity === 0 ? 1 : originalOpacity;
2706
- if (progress <= 0) {
2707
- child.paths = [];
2708
- child.setOpacity(0);
2709
- child.fill(child.getFillColor(), 0);
2710
- continue;
2711
- }
2712
- child.setOpacity(childOpacity);
2713
- if (progress < 0.5) {
2714
- const strokeProgress = progress * 2;
2715
- const partialPaths = [];
2716
- for (const originalPath of originalPaths) {
2717
- partialPaths.push(getPartialPath(originalPath, strokeProgress));
2718
- }
2719
- child.paths = partialPaths;
2720
- child.fill(child.getFillColor(), 0);
2721
- } else {
2722
- child.paths = originalPaths.map((p) => p.clone());
2723
- const fillProgress = (progress - 0.5) * 2;
2724
- child.fill(child.getFillColor(), originalFillOpacity * fillProgress);
2725
- }
2770
+ target.paths = originalPaths.map((p) => p.clone());
2771
+ target.stroke(originalStrokeColor, originalStrokeWidth);
2772
+ target.fill(originalFillColor, originalFillOpacity * fillProgress);
2726
2773
  }
2727
2774
  }
2728
2775
  };
@@ -2731,87 +2778,97 @@ var Draw = class extends IntroductoryAnimation {
2731
2778
  function isVGroup2(target) {
2732
2779
  return typeof target.getChildren === "function";
2733
2780
  }
2781
+ function isGlyph2(target) {
2782
+ return typeof target.character === "string";
2783
+ }
2784
+ function isText2(target) {
2785
+ if (!isVGroup2(target)) return false;
2786
+ const children = target.getChildren();
2787
+ return children.length > 0 && children.every(isGlyph2);
2788
+ }
2734
2789
  var Write = class extends IntroductoryAnimation {
2735
- isVGroup;
2736
- /** For non-VGroup targets: original paths on the target itself. */
2737
2790
  originalPaths;
2738
2791
  originalOpacity;
2792
+ originalStrokeColor;
2793
+ originalStrokeWidth;
2794
+ originalFillColor;
2739
2795
  originalFillOpacity;
2740
- /** For VGroup targets: original state of each child. */
2741
2796
  childStates;
2797
+ /** Glyph states for Text children, keyed by the Text VMobject reference. */
2798
+ glyphStates = /* @__PURE__ */ new Map();
2742
2799
  constructor(target) {
2743
2800
  super(target);
2744
- this.isVGroup = isVGroup2(target);
2745
2801
  this.originalOpacity = target.opacity;
2802
+ this.originalStrokeColor = target.getStrokeColor();
2803
+ this.originalStrokeWidth = target.getStrokeWidth();
2804
+ this.originalFillColor = target.getFillColor();
2746
2805
  this.originalFillOpacity = target.getFillOpacity();
2747
- if (this.isVGroup && isVGroup2(target)) {
2806
+ if (isVGroup2(target)) {
2748
2807
  this.originalPaths = [];
2749
- this.childStates = [];
2750
- for (const child of target.getChildren()) {
2751
- this.childStates.push({
2752
- child,
2753
- originalPaths: child.paths.map((p) => p.clone()),
2754
- originalOpacity: child.opacity,
2755
- originalFillOpacity: child.getFillOpacity()
2756
- });
2757
- }
2808
+ this.childStates = target.getChildren().map((child) => this.createState(child));
2758
2809
  } else {
2759
2810
  this.originalPaths = target.paths.map((p) => p.clone());
2760
2811
  this.childStates = [];
2761
2812
  }
2762
2813
  }
2763
- interpolate(progress) {
2764
- if (this.isVGroup) {
2765
- this.interpolateVGroup(progress);
2766
- } else {
2767
- this.interpolateVMobject(progress);
2814
+ createState(child) {
2815
+ const state = {
2816
+ child,
2817
+ paths: child.paths.map((p) => p.clone()),
2818
+ opacity: child.opacity,
2819
+ strokeColor: child.getStrokeColor(),
2820
+ strokeWidth: child.getStrokeWidth(),
2821
+ fillColor: child.getFillColor(),
2822
+ fillOpacity: child.getFillOpacity()
2823
+ };
2824
+ if (isText2(child)) {
2825
+ this.glyphStates.set(child, child.getChildren().map((g) => this.createState(g)));
2768
2826
  }
2827
+ return state;
2769
2828
  }
2770
- /** Interpolates a single VMobject (non-VGroup). */
2771
- interpolateVMobject(progress) {
2772
- if (progress <= 0) {
2773
- this.target.paths = [];
2774
- this.target.setOpacity(0);
2775
- return;
2776
- }
2777
- const targetOpacity = this.originalOpacity === 0 ? 1 : this.originalOpacity;
2778
- this.target.setOpacity(targetOpacity);
2779
- this.target.fill(this.target.getFillColor(), this.originalFillOpacity);
2780
- if (progress >= 1) {
2781
- this.target.paths = this.originalPaths.map((p) => p.clone());
2782
- return;
2783
- }
2784
- const partialPaths = [];
2785
- for (const originalPath of this.originalPaths) {
2786
- partialPaths.push(getPartialPath(originalPath, progress));
2829
+ interpolate(progress) {
2830
+ if (this.childStates.length === 0) {
2831
+ this.applyProgress(this.target, this.originalPaths, this.originalOpacity, this.originalStrokeColor, this.originalStrokeWidth, this.originalFillColor, this.originalFillOpacity, progress);
2832
+ } else {
2833
+ this.interpolateVGroup(progress);
2787
2834
  }
2788
- this.target.paths = partialPaths;
2789
2835
  }
2790
- /** Interpolates a VGroup by animating each child's paths. */
2791
2836
  interpolateVGroup(progress) {
2792
2837
  const targetOpacity = this.originalOpacity === 0 ? 1 : this.originalOpacity;
2793
2838
  this.target.setOpacity(progress <= 0 ? 0 : targetOpacity);
2794
2839
  for (const state of this.childStates) {
2795
- const { child, originalPaths, originalOpacity, originalFillOpacity } = state;
2796
- if (progress <= 0) {
2797
- child.paths = [];
2798
- child.setOpacity(0);
2799
- continue;
2800
- }
2801
- const childOpacity = originalOpacity === 0 ? 1 : originalOpacity;
2802
- child.setOpacity(childOpacity);
2803
- child.fill(child.getFillColor(), originalFillOpacity);
2804
- if (progress >= 1) {
2805
- child.paths = originalPaths.map((p) => p.clone());
2806
- continue;
2807
- }
2808
- const partialPaths = [];
2809
- for (const originalPath of originalPaths) {
2810
- partialPaths.push(getPartialPath(originalPath, progress));
2840
+ const glyphStates = this.glyphStates.get(state.child);
2841
+ if (glyphStates) {
2842
+ state.child.setOpacity(progress <= 0 ? 0 : state.opacity || 1);
2843
+ this.interpolateGlyphs(glyphStates, progress);
2844
+ } else {
2845
+ this.applyProgress(state.child, state.paths, state.opacity, state.strokeColor, state.strokeWidth, state.fillColor, state.fillOpacity, progress);
2811
2846
  }
2812
- child.paths = partialPaths;
2813
2847
  }
2814
2848
  }
2849
+ interpolateGlyphs(glyphs, progress) {
2850
+ const lagRatio = 0.2;
2851
+ const totalSpan = 1 + (glyphs.length - 1) * lagRatio;
2852
+ glyphs.forEach((state, i) => {
2853
+ const start = i * lagRatio / totalSpan;
2854
+ const end = (i * lagRatio + 1) / totalSpan;
2855
+ const p = Math.max(0, Math.min(1, (progress - start) / (end - start)));
2856
+ this.applyProgress(state.child, state.paths, state.opacity, state.strokeColor, state.strokeWidth, state.fillColor, state.fillOpacity, p);
2857
+ });
2858
+ }
2859
+ /** Applies progress to a single VMobject, updating its paths and style. */
2860
+ applyProgress(target, originalPaths, originalOpacity, originalStrokeColor, originalStrokeWidth, originalFillColor, originalFillOpacity, progress) {
2861
+ if (progress <= 0) {
2862
+ target.paths = [];
2863
+ target.setOpacity(0);
2864
+ return;
2865
+ }
2866
+ const opacity = originalOpacity === 0 ? 1 : originalOpacity;
2867
+ target.setOpacity(opacity);
2868
+ target.stroke(originalStrokeColor, originalStrokeWidth);
2869
+ target.fill(originalFillColor, originalFillOpacity);
2870
+ target.paths = progress >= 1 ? originalPaths.map((p) => p.clone()) : originalPaths.map((p) => getPartialPath(p, progress));
2871
+ }
2815
2872
  };
2816
2873
 
2817
2874
  // src/core/animations/draw/Unwrite.ts
@@ -3564,6 +3621,7 @@ var Glyph = class extends VMobject {
3564
3621
 
3565
3622
  // src/mobjects/text/Text.ts
3566
3623
  import * as fontkit from "fontkit";
3624
+ import { join, resolve } from "path";
3567
3625
 
3568
3626
  // src/mobjects/text/types.ts
3569
3627
  var DEFAULT_TEXT_STYLE = {
@@ -3572,6 +3630,7 @@ var DEFAULT_TEXT_STYLE = {
3572
3630
  };
3573
3631
 
3574
3632
  // src/mobjects/text/Text.ts
3633
+ var DEFAULT_FONT_PATH = resolve(join(__dirname, "..", "..", "fonts", "ComicSansMS3.ttf"));
3575
3634
  var Text = class extends VGroup {
3576
3635
  text;
3577
3636
  style;
@@ -3579,7 +3638,7 @@ var Text = class extends VGroup {
3579
3638
  constructor(text, fontPath, options = {}) {
3580
3639
  super();
3581
3640
  this.text = text;
3582
- this.fontPath = fontPath;
3641
+ this.fontPath = fontPath ?? DEFAULT_FONT_PATH;
3583
3642
  this.style = { ...DEFAULT_TEXT_STYLE, ...options };
3584
3643
  this.buildGlyphs();
3585
3644
  centerGroup(this);
@@ -3614,8 +3673,8 @@ var Text = class extends VGroup {
3614
3673
  applyStyle() {
3615
3674
  for (const child of this.getChildren()) {
3616
3675
  if (child instanceof Glyph) {
3617
- child.stroke(this.style.color, 0);
3618
- child.fill(this.style.color, 1);
3676
+ child.stroke(this.getStrokeColor(), this.getStrokeWidth());
3677
+ child.fill(this.getFillColor(), this.getFillOpacity());
3619
3678
  }
3620
3679
  }
3621
3680
  }
@@ -3627,6 +3686,18 @@ var Text = class extends VGroup {
3627
3686
  getStyle() {
3628
3687
  return { ...this.style };
3629
3688
  }
3689
+ /** Override stroke to propagate to all Glyph children. */
3690
+ stroke(color, width = 2) {
3691
+ super.stroke(color, width);
3692
+ this.applyStyle();
3693
+ return this;
3694
+ }
3695
+ /** Override fill to propagate to all Glyph children. */
3696
+ fill(color, opacity) {
3697
+ super.fill(color, opacity);
3698
+ this.applyStyle();
3699
+ return this;
3700
+ }
3630
3701
  getGlyph(index) {
3631
3702
  const child = this.get(index);
3632
3703
  return child instanceof Glyph ? child : void 0;
@@ -4306,542 +4377,6 @@ var Shake = class extends TransformativeAnimation {
4306
4377
  }
4307
4378
  };
4308
4379
 
4309
- // src/core/serialization/primitives.ts
4310
- function serializeVector2(v) {
4311
- return { x: v.x, y: v.y };
4312
- }
4313
- function deserializeVector2(data) {
4314
- return new Vector2(data.x, data.y);
4315
- }
4316
- function serializeMatrix3x3(m) {
4317
- return { values: Array.from(m.values) };
4318
- }
4319
- function deserializeMatrix3x3(data) {
4320
- const values = new Float32Array(data.values);
4321
- return new Matrix3x3(values);
4322
- }
4323
- function serializeColor(c) {
4324
- return { r: c.r, g: c.g, b: c.b, a: c.a };
4325
- }
4326
- function deserializeColor(data) {
4327
- return new Color(data.r, data.g, data.b, data.a);
4328
- }
4329
- function serializePathCommand(cmd) {
4330
- const result = {
4331
- type: cmd.type,
4332
- end: serializeVector2(cmd.end)
4333
- };
4334
- const withControl1 = cmd.control1 ? { ...result, control1: serializeVector2(cmd.control1) } : result;
4335
- const withControl2 = cmd.control2 ? { ...withControl1, control2: serializeVector2(cmd.control2) } : withControl1;
4336
- return withControl2;
4337
- }
4338
- function serializeBezierPath(path) {
4339
- const commands = path.getCommands().map(serializePathCommand);
4340
- return { commands };
4341
- }
4342
- function deserializeBezierPath(data) {
4343
- const path = new BezierPath();
4344
- for (const cmd of data.commands) {
4345
- const end = deserializeVector2(cmd.end);
4346
- switch (cmd.type) {
4347
- case "Move":
4348
- path.moveTo(end);
4349
- break;
4350
- case "Line":
4351
- path.lineTo(end);
4352
- break;
4353
- case "Quadratic":
4354
- if (cmd.control1) {
4355
- path.quadraticTo(deserializeVector2(cmd.control1), end);
4356
- }
4357
- break;
4358
- case "Cubic":
4359
- if (cmd.control1 && cmd.control2) {
4360
- path.cubicTo(
4361
- deserializeVector2(cmd.control1),
4362
- deserializeVector2(cmd.control2),
4363
- end
4364
- );
4365
- }
4366
- break;
4367
- case "Close":
4368
- path.closePath();
4369
- break;
4370
- }
4371
- }
4372
- return path;
4373
- }
4374
-
4375
- // src/core/serialization/registry.ts
4376
- var registry = /* @__PURE__ */ new Map();
4377
- function getSerializer(typeName) {
4378
- return registry.get(typeName);
4379
- }
4380
-
4381
- // src/core/serialization/mobject.ts
4382
- var idCounter = 0;
4383
- var mobjectIdMap = /* @__PURE__ */ new WeakMap();
4384
- function getMobjectId(m) {
4385
- let id = mobjectIdMap.get(m);
4386
- if (!id) {
4387
- id = `mob_${idCounter++}`;
4388
- mobjectIdMap.set(m, id);
4389
- }
4390
- return id;
4391
- }
4392
- function resetIdCounter() {
4393
- idCounter = 0;
4394
- }
4395
- function getMobjectType(m) {
4396
- if (m instanceof Circle) return "Circle";
4397
- if (m instanceof Rectangle) return "Rectangle";
4398
- if (m instanceof Arrow) return "Arrow";
4399
- if (m instanceof Line) return "Line";
4400
- if (m instanceof Arc) return "Arc";
4401
- if (m instanceof Polygon) return "Polygon";
4402
- if (m instanceof Point) return "Point";
4403
- if (m instanceof VGroup) return "VGroup";
4404
- if (m instanceof VMobject) return "VMobject";
4405
- return "Mobject";
4406
- }
4407
- function serializeMobjectBase(m) {
4408
- return {
4409
- type: getMobjectType(m),
4410
- id: getMobjectId(m),
4411
- matrix: serializeMatrix3x3(m.matrix),
4412
- opacity: m.opacity
4413
- };
4414
- }
4415
- function serializeVMobjectBase(v) {
4416
- const base = serializeMobjectBase(v);
4417
- return {
4418
- ...base,
4419
- paths: v.paths.map(serializeBezierPath),
4420
- strokeColor: serializeColor(v.getStrokeColor()),
4421
- strokeWidth: v.getStrokeWidth(),
4422
- fillColor: serializeColor(v.getFillColor()),
4423
- fillOpacity: v.getFillOpacity()
4424
- };
4425
- }
4426
- function serializeCircle(c) {
4427
- const base = serializeVMobjectBase(c);
4428
- const arc = c;
4429
- return { ...base, type: "Circle", radius: arc.radius };
4430
- }
4431
- function serializeRectangle(r) {
4432
- const base = serializeVMobjectBase(r);
4433
- return { ...base, type: "Rectangle", width: r.width, height: r.height };
4434
- }
4435
- function serializeLine(l) {
4436
- const base = serializeVMobjectBase(l);
4437
- return {
4438
- ...base,
4439
- type: "Line",
4440
- start: serializeVector2(l.start),
4441
- end: serializeVector2(l.end)
4442
- };
4443
- }
4444
- function serializeArc(a) {
4445
- const base = serializeVMobjectBase(a);
4446
- return {
4447
- ...base,
4448
- type: "Arc",
4449
- radius: a.radius,
4450
- startAngle: a.startAngle,
4451
- endAngle: a.endAngle
4452
- };
4453
- }
4454
- function serializePolygon(p) {
4455
- const base = serializeVMobjectBase(p);
4456
- return {
4457
- ...base,
4458
- type: "Polygon",
4459
- vertices: p.vertices.map(serializeVector2)
4460
- };
4461
- }
4462
- function serializePoint(p) {
4463
- const base = serializeVMobjectBase(p);
4464
- return { ...base, type: "Point" };
4465
- }
4466
- function serializeVGroup(g) {
4467
- const base = serializeMobjectBase(g);
4468
- return {
4469
- ...base,
4470
- type: "VGroup",
4471
- children: g.getChildren().map(serializeMobject)
4472
- };
4473
- }
4474
- function serializeMobject(m) {
4475
- const typeName = m.constructor.name;
4476
- const customSerializer = getSerializer(typeName);
4477
- if (customSerializer) {
4478
- return customSerializer.serialize(m);
4479
- }
4480
- if (m instanceof Circle) return serializeCircle(m);
4481
- if (m instanceof Rectangle) return serializeRectangle(m);
4482
- if (m instanceof Arrow) return serializeLine(m);
4483
- if (m instanceof Line) return serializeLine(m);
4484
- if (m instanceof Arc) return serializeArc(m);
4485
- if (m instanceof Polygon) return serializePolygon(m);
4486
- if (m instanceof Point) return serializePoint(m);
4487
- if (m instanceof VGroup) return serializeVGroup(m);
4488
- if (m instanceof VMobject) return serializeVMobjectBase(m);
4489
- return serializeMobjectBase(m);
4490
- }
4491
- function applyMobjectBase(m, data) {
4492
- const matrix = deserializeMatrix3x3(data.matrix);
4493
- m.applyMatrix(matrix);
4494
- m.setOpacity(data.opacity);
4495
- }
4496
- function applyVMobjectBase(v, data) {
4497
- applyMobjectBase(v, data);
4498
- v.paths = data.paths.map(deserializeBezierPath);
4499
- v.stroke(deserializeColor(data.strokeColor), data.strokeWidth);
4500
- v.fill(deserializeColor(data.fillColor), data.fillOpacity);
4501
- }
4502
- function deserializeMobject(data) {
4503
- const customSerializer = getSerializer(data.type);
4504
- if (customSerializer) {
4505
- return customSerializer.deserialize(data);
4506
- }
4507
- switch (data.type) {
4508
- case "Circle": {
4509
- const d = data;
4510
- const circle = new Circle(d.radius);
4511
- applyVMobjectBase(circle, d);
4512
- return circle;
4513
- }
4514
- case "Rectangle": {
4515
- const d = data;
4516
- const rect = new Rectangle(d.width, d.height);
4517
- applyVMobjectBase(rect, d);
4518
- return rect;
4519
- }
4520
- case "Line": {
4521
- const d = data;
4522
- const start = deserializeVector2(d.start);
4523
- const end = deserializeVector2(d.end);
4524
- const line = new Line(start.x, start.y, end.x, end.y);
4525
- applyVMobjectBase(line, d);
4526
- return line;
4527
- }
4528
- case "Arc": {
4529
- const d = data;
4530
- const arc = new Arc(d.radius, d.startAngle, d.endAngle);
4531
- applyVMobjectBase(arc, d);
4532
- return arc;
4533
- }
4534
- case "Polygon": {
4535
- const d = data;
4536
- const vertices = d.vertices.map(deserializeVector2);
4537
- const polygon = new Polygon(vertices);
4538
- applyVMobjectBase(polygon, d);
4539
- return polygon;
4540
- }
4541
- case "Point": {
4542
- const d = data;
4543
- const point = new Point();
4544
- applyVMobjectBase(point, d);
4545
- return point;
4546
- }
4547
- case "VGroup": {
4548
- const d = data;
4549
- const children = d.children.map(deserializeMobject);
4550
- const group = new VGroup(...children);
4551
- applyMobjectBase(group, d);
4552
- return group;
4553
- }
4554
- case "VMobject": {
4555
- const d = data;
4556
- const vmob = new VMobject();
4557
- applyVMobjectBase(vmob, d);
4558
- return vmob;
4559
- }
4560
- default: {
4561
- const mob = new VMobject();
4562
- if ("paths" in data) {
4563
- applyVMobjectBase(mob, data);
4564
- } else {
4565
- applyMobjectBase(mob, data);
4566
- }
4567
- return mob;
4568
- }
4569
- }
4570
- }
4571
-
4572
- // src/core/serialization/easingLookup.ts
4573
- var easingToName = /* @__PURE__ */ new Map();
4574
- var nameToEasing = /* @__PURE__ */ new Map();
4575
- function register(name, fn) {
4576
- easingToName.set(fn, name);
4577
- nameToEasing.set(name, fn);
4578
- }
4579
- register("linear", linear);
4580
- register("easeInQuad", easeInQuad);
4581
- register("easeOutQuad", easeOutQuad);
4582
- register("easeInOutQuad", easeInOutQuad);
4583
- register("easeInCubic", easeInCubic);
4584
- register("easeOutCubic", easeOutCubic);
4585
- register("easeInOutCubic", easeInOutCubic);
4586
- register("easeInQuart", easeInQuart);
4587
- register("easeOutQuart", easeOutQuart);
4588
- register("easeInOutQuart", easeInOutQuart);
4589
- register("easeInQuint", easeInQuint);
4590
- register("easeOutQuint", easeOutQuint);
4591
- register("easeInOutQuint", easeInOutQuint);
4592
- register("easeInSine", easeInSine);
4593
- register("easeOutSine", easeOutSine);
4594
- register("easeInOutSine", easeInOutSine);
4595
- register("easeInExpo", easeInExpo);
4596
- register("easeOutExpo", easeOutExpo);
4597
- register("easeInOutExpo", easeInOutExpo);
4598
- register("easeInCirc", easeInCirc);
4599
- register("easeOutCirc", easeOutCirc);
4600
- register("easeInOutCirc", easeInOutCirc);
4601
- register("easeInBack", easeInBack);
4602
- register("easeOutBack", easeOutBack);
4603
- register("easeInOutBack", easeInOutBack);
4604
- register("easeInElastic", easeInElastic);
4605
- register("easeOutElastic", easeOutElastic);
4606
- register("easeInOutElastic", easeInOutElastic);
4607
- register("easeInBounce", easeInBounce);
4608
- register("easeOutBounce", easeOutBounce);
4609
- register("easeInOutBounce", easeInOutBounce);
4610
- register("smooth", smooth);
4611
- register("doubleSmooth", doubleSmooth);
4612
- register("rushInto", rushInto);
4613
- register("rushFrom", rushFrom);
4614
- register("slowInto", slowInto);
4615
- register("thereAndBack", thereAndBack);
4616
- register("wiggle", wiggle);
4617
- register("lingering", lingering());
4618
- register("runningStart", runningStart());
4619
- function getEasingName(fn) {
4620
- return easingToName.get(fn) ?? "smooth";
4621
- }
4622
- function getEasingByName(name) {
4623
- return nameToEasing.get(name);
4624
- }
4625
-
4626
- // src/core/serialization/animation.ts
4627
- function serializeAnimationConfig(anim) {
4628
- return {
4629
- durationSeconds: anim.getDuration(),
4630
- delaySeconds: anim.getDelay(),
4631
- easingName: getEasingName(anim.getEasing())
4632
- };
4633
- }
4634
- function getAnimationType(anim) {
4635
- if (anim instanceof FadeIn) return "FadeIn";
4636
- if (anim instanceof FadeOut) return "FadeOut";
4637
- if (anim instanceof MoveTo) return "MoveTo";
4638
- if (anim instanceof Rotate) return "Rotate";
4639
- if (anim instanceof Scale) return "Scale";
4640
- if (anim instanceof MorphTo) return "MorphTo";
4641
- if (anim instanceof Draw) return "Draw";
4642
- if (anim instanceof Write) return "Write";
4643
- if (anim instanceof Unwrite) return "Unwrite";
4644
- if (anim instanceof Sequence) return "Sequence";
4645
- if (anim instanceof Parallel) return "Parallel";
4646
- return "Unknown";
4647
- }
4648
- function serializeAnimation(anim, getMobjectId2) {
4649
- const baseData = {
4650
- type: getAnimationType(anim),
4651
- targetId: getMobjectId2(anim.getTarget()),
4652
- config: serializeAnimationConfig(anim)
4653
- };
4654
- if (anim instanceof MoveTo) {
4655
- const moveData = {
4656
- ...baseData,
4657
- type: "MoveTo",
4658
- destination: serializeVector2(anim.getDestination())
4659
- };
4660
- return moveData;
4661
- }
4662
- if (anim instanceof Rotate) {
4663
- const rotateData = {
4664
- ...baseData,
4665
- type: "Rotate",
4666
- angle: anim.getAngle()
4667
- };
4668
- return rotateData;
4669
- }
4670
- if (anim instanceof Scale) {
4671
- const scaleData = {
4672
- ...baseData,
4673
- type: "Scale",
4674
- factor: anim.getFactor()
4675
- };
4676
- return scaleData;
4677
- }
4678
- if (anim instanceof Sequence) {
4679
- const seqData = {
4680
- ...baseData,
4681
- type: "Sequence",
4682
- animations: anim.getChildren().map((a) => serializeAnimation(a, getMobjectId2))
4683
- };
4684
- return seqData;
4685
- }
4686
- if (anim instanceof Parallel) {
4687
- const parData = {
4688
- ...baseData,
4689
- type: "Parallel",
4690
- animations: anim.getChildren().map((a) => serializeAnimation(a, getMobjectId2))
4691
- };
4692
- return parData;
4693
- }
4694
- return baseData;
4695
- }
4696
- function applyConfig(anim, config) {
4697
- anim.duration(config.durationSeconds);
4698
- anim.delay(config.delaySeconds);
4699
- const easing = getEasingByName(config.easingName);
4700
- if (easing) {
4701
- anim.ease(easing);
4702
- }
4703
- }
4704
- function deserializeAnimation(data, registry2) {
4705
- const target = registry2.get(data.targetId);
4706
- if (!target) {
4707
- throw new Error(`Mobject not found: ${data.targetId}`);
4708
- }
4709
- let anim;
4710
- switch (data.type) {
4711
- case "FadeIn":
4712
- anim = new FadeIn(target);
4713
- break;
4714
- case "FadeOut":
4715
- anim = new FadeOut(target);
4716
- break;
4717
- case "MoveTo": {
4718
- const d = data;
4719
- anim = new MoveTo(target, d.destination.x, d.destination.y);
4720
- break;
4721
- }
4722
- case "Rotate": {
4723
- const d = data;
4724
- anim = new Rotate(target, d.angle);
4725
- break;
4726
- }
4727
- case "Scale": {
4728
- const d = data;
4729
- anim = new Scale(target, d.factor);
4730
- break;
4731
- }
4732
- case "Create":
4733
- anim = new Write(target);
4734
- break;
4735
- case "Draw":
4736
- anim = new Draw(target);
4737
- break;
4738
- case "Write":
4739
- anim = new Write(target);
4740
- break;
4741
- case "Unwrite":
4742
- anim = new Unwrite(target);
4743
- break;
4744
- case "Sequence": {
4745
- const d = data;
4746
- const children = d.animations.map((a) => deserializeAnimation(a, registry2));
4747
- anim = new Sequence(children);
4748
- break;
4749
- }
4750
- case "Parallel": {
4751
- const d = data;
4752
- const children = d.animations.map((a) => deserializeAnimation(a, registry2));
4753
- anim = new Parallel(children);
4754
- break;
4755
- }
4756
- default:
4757
- throw new Error(`Unknown animation type: ${data.type}`);
4758
- }
4759
- applyConfig(anim, data.config);
4760
- return anim;
4761
- }
4762
-
4763
- // src/core/serialization/scene.ts
4764
- var SERIALIZATION_VERSION = "1.0.0";
4765
- var serializationIdMap = /* @__PURE__ */ new Map();
4766
- var serializationIdCounter = 0;
4767
- function getSerializationId(m) {
4768
- let id = serializationIdMap.get(m);
4769
- if (!id) {
4770
- id = `mob_${serializationIdCounter++}`;
4771
- serializationIdMap.set(m, id);
4772
- }
4773
- return id;
4774
- }
4775
- function resetSerializationState() {
4776
- serializationIdMap.clear();
4777
- serializationIdCounter = 0;
4778
- resetIdCounter();
4779
- }
4780
- function serializeSceneConfig(scene) {
4781
- return {
4782
- width: scene.getWidth(),
4783
- height: scene.getHeight(),
4784
- backgroundColor: serializeColor(scene.getBackgroundColor()),
4785
- frameRate: scene.getFrameRate()
4786
- };
4787
- }
4788
- function serializeTimeline(scene) {
4789
- const timeline = scene.getTimeline();
4790
- const scheduled = timeline.getScheduled();
4791
- const serializedScheduled = scheduled.map((s) => ({
4792
- animation: serializeAnimation(s.animation, getSerializationId),
4793
- startTime: s.startTime
4794
- }));
4795
- return {
4796
- loop: timeline.isLooping(),
4797
- scheduled: serializedScheduled
4798
- };
4799
- }
4800
- function serializeScene(scene) {
4801
- resetSerializationState();
4802
- const mobjects = scene.getMobjects();
4803
- const serializedMobjects = mobjects.map((m) => serializeMobject(m));
4804
- return {
4805
- version: SERIALIZATION_VERSION,
4806
- config: serializeSceneConfig(scene),
4807
- mobjects: serializedMobjects,
4808
- timeline: serializeTimeline(scene)
4809
- };
4810
- }
4811
- function serialize(scene) {
4812
- const data = serializeScene(scene);
4813
- return JSON.stringify(data);
4814
- }
4815
- function deserializeScene(data) {
4816
- if (!data.version) {
4817
- throw new Error("Missing serialization version");
4818
- }
4819
- const scene = new Scene({
4820
- width: data.config.width,
4821
- height: data.config.height,
4822
- backgroundColor: deserializeColor(data.config.backgroundColor),
4823
- frameRate: data.config.frameRate
4824
- });
4825
- const mobjectRegistry = /* @__PURE__ */ new Map();
4826
- const mobjects = [];
4827
- for (const serializedMob of data.mobjects) {
4828
- const mob = deserializeMobject(serializedMob);
4829
- mobjectRegistry.set(serializedMob.id, mob);
4830
- mobjects.push(mob);
4831
- }
4832
- scene.add(...mobjects);
4833
- const timeline = scene.getTimeline();
4834
- for (const scheduled of data.timeline.scheduled) {
4835
- const animation = deserializeAnimation(scheduled.animation, mobjectRegistry);
4836
- timeline.schedule(animation, scheduled.startTime);
4837
- }
4838
- return scene;
4839
- }
4840
- function deserialize(json) {
4841
- const data = JSON.parse(json);
4842
- return deserializeScene(data);
4843
- }
4844
-
4845
4380
  // src/core/renderer/FrameRenderer.ts
4846
4381
  import { createCanvas } from "@napi-rs/canvas";
4847
4382
 
@@ -5048,7 +4583,7 @@ async function writePng(canvas, outputPath) {
5048
4583
  }
5049
4584
 
5050
4585
  // src/core/renderer/formats/sprite.ts
5051
- import { join } from "path";
4586
+ import { join as join2 } from "path";
5052
4587
  function padNumber(n, length) {
5053
4588
  return n.toString().padStart(length, "0");
5054
4589
  }
@@ -5059,7 +4594,7 @@ async function renderSpriteSequence(frameRenderer, outputDir, frameRate, totalDu
5059
4594
  const time = frameIndex / frameRate;
5060
4595
  const canvas = frameRenderer.renderFrame(time);
5061
4596
  const filename = `frame_${padNumber(frameIndex, digitCount)}.png`;
5062
- const outputPath = join(outputDir, filename);
4597
+ const outputPath = join2(outputDir, filename);
5063
4598
  await writePng(canvas, outputPath);
5064
4599
  progressReporter.reportFrame(frameIndex);
5065
4600
  }
@@ -5291,7 +4826,6 @@ export {
5291
4826
  Write,
5292
4827
  clearRegistry,
5293
4828
  smooth as defaultEasing,
5294
- deserialize,
5295
4829
  doubleSmooth,
5296
4830
  easeInBack,
5297
4831
  easeInBounce,
@@ -5333,7 +4867,6 @@ export {
5333
4867
  runningStart,
5334
4868
  rushFrom,
5335
4869
  rushInto,
5336
- serialize,
5337
4870
  slowInto,
5338
4871
  smooth,
5339
4872
  thereAndBack,
package/package.json CHANGED
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "@redwilly/anima",
3
- "version": "0.1.2",
3
+ "version": "0.1.22",
4
4
  "description": "Animation library for mathematical visualizations",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/RedWilly/Anima.git"
8
+ },
9
+ "homepage": "https://github.com/RedWilly/Anima#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/RedWilly/Anima/issues"
12
+ },
5
13
  "type": "module",
6
14
  "main": "./dist/index.js",
7
15
  "types": "./dist/index.d.ts",