effect-motion 0.2.0 → 0.3.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.
Files changed (103) hide show
  1. package/dist/Camera.d.ts +50 -28
  2. package/dist/Camera.js +71 -19
  3. package/dist/CameraHelpers.d.ts +70 -0
  4. package/dist/CameraHelpers.js +239 -0
  5. package/dist/CanvasExporter.d.ts +12 -0
  6. package/dist/CanvasExporter.js +40 -0
  7. package/dist/Color.d.ts +428 -0
  8. package/dist/Color.js +535 -0
  9. package/dist/EffectMotionError.d.ts +10 -0
  10. package/dist/EffectMotionError.js +6 -0
  11. package/dist/Entity.d.ts +9 -8
  12. package/dist/Entity.js +1 -4
  13. package/dist/Fonts.d.ts +10 -0
  14. package/dist/Fonts.js +16 -0
  15. package/dist/Images.d.ts +33 -0
  16. package/dist/Images.js +24 -0
  17. package/dist/Instance.d.ts +4 -4
  18. package/dist/Motion.d.ts +24 -7
  19. package/dist/Motion.js +54 -9
  20. package/dist/Phaser.d.ts +1 -1
  21. package/dist/Phaser.js +13 -9
  22. package/dist/Physics.d.ts +3 -3
  23. package/dist/Physics.js +4 -4
  24. package/dist/PngExporter.d.ts +6 -0
  25. package/dist/PngExporter.js +85 -0
  26. package/dist/Projection.d.ts +225 -0
  27. package/dist/Projection.js +473 -0
  28. package/dist/Renderer.d.ts +102 -53
  29. package/dist/Renderer.js +369 -78
  30. package/dist/Runner.d.ts +126 -42
  31. package/dist/Runner.js +42 -17
  32. package/dist/Scene.d.ts +88 -59
  33. package/dist/Scene.js +24 -15
  34. package/dist/Shapes.d.ts +11 -0
  35. package/dist/Shapes.js +11 -0
  36. package/dist/demo.d.ts +3 -186
  37. package/dist/demo.js +24 -35
  38. package/dist/index.d.ts +14 -13
  39. package/dist/index.js +14 -13
  40. package/dist/particles/Particle.d.ts +4 -3
  41. package/dist/particles/ParticleField.d.ts +11 -48
  42. package/dist/particles/ParticleField.js +5 -4
  43. package/dist/particles/constructors.d.ts +8 -7
  44. package/dist/particles/constructors.js +2 -2
  45. package/dist/particles/index.d.ts +5 -6
  46. package/dist/particles/index.js +5 -6
  47. package/dist/particles/overLife.d.ts +1 -1
  48. package/dist/particles/overLife.js +1 -1
  49. package/dist/particles/simulate.d.ts +2 -2
  50. package/dist/particles/simulate.js +6 -6
  51. package/dist/particles/step.d.ts +2 -2
  52. package/dist/particles/step.js +5 -3
  53. package/dist/render/dof.d.ts +27 -0
  54. package/dist/render/dof.js +37 -0
  55. package/dist/render/paint.d.ts +30 -0
  56. package/dist/render/paint.js +36 -0
  57. package/dist/render/shapes.d.ts +42 -0
  58. package/dist/render/shapes.js +310 -0
  59. package/dist/shapes/Circle.d.ts +10 -15
  60. package/dist/shapes/Circle.js +2 -2
  61. package/dist/shapes/Ellipse.d.ts +10 -16
  62. package/dist/shapes/Ellipse.js +2 -2
  63. package/dist/shapes/Group.d.ts +5 -39
  64. package/dist/shapes/Group.js +4 -4
  65. package/dist/shapes/Hud.d.ts +38 -0
  66. package/dist/shapes/Hud.js +35 -0
  67. package/dist/shapes/Image.d.ts +45 -0
  68. package/dist/shapes/Image.js +28 -0
  69. package/dist/shapes/Line.d.ts +12 -12
  70. package/dist/shapes/Line.js +11 -6
  71. package/dist/shapes/Path.d.ts +87 -18
  72. package/dist/shapes/Path.js +25 -6
  73. package/dist/shapes/Rect.d.ts +33 -23
  74. package/dist/shapes/Rect.js +14 -2
  75. package/dist/shapes/Shape2D.d.ts +13 -4
  76. package/dist/shapes/Shape2D.js +16 -5
  77. package/dist/shapes/Shapes.d.ts +11 -0
  78. package/dist/shapes/Shapes.js +11 -0
  79. package/dist/shapes/Square.d.ts +10 -15
  80. package/dist/shapes/Square.js +2 -2
  81. package/dist/shapes/Text.d.ts +10 -19
  82. package/dist/shapes/Text.js +2 -2
  83. package/package.json +14 -4
  84. package/dist/particles/render.d.ts +0 -13
  85. package/dist/particles/render.js +0 -33
  86. package/dist/shapes/Layer.d.ts +0 -9
  87. package/dist/shapes/Layer.js +0 -23
  88. package/dist/shapes/index.d.ts +0 -10
  89. package/dist/shapes/index.js +0 -10
  90. package/dist/svg/SvgDomRenderer.d.ts +0 -28
  91. package/dist/svg/SvgDomRenderer.js +0 -50
  92. package/dist/svg/SvgNode.d.ts +0 -13
  93. package/dist/svg/SvgNode.js +0 -18
  94. package/dist/svg/SvgRenderer.d.ts +0 -22
  95. package/dist/svg/SvgRenderer.js +0 -20
  96. package/dist/svg/camera.d.ts +0 -21
  97. package/dist/svg/camera.js +0 -43
  98. package/dist/svg/index.d.ts +0 -6
  99. package/dist/svg/index.js +0 -6
  100. package/dist/svg/layers.d.ts +0 -18
  101. package/dist/svg/layers.js +0 -13
  102. package/dist/svg/shapes.d.ts +0 -1089
  103. package/dist/svg/shapes.js +0 -119
package/dist/Scene.d.ts CHANGED
@@ -7,31 +7,24 @@ import type * as Schedule from "effect/Schedule";
7
7
  import type * as Schema from "effect/Schema";
8
8
  import type * as Scope from "effect/Scope";
9
9
  import * as Stream from "effect/Stream";
10
- import type * as Entity from "./Entity";
11
- import type * as Instance from "./Instance";
12
- import * as Phaser from "./Phaser";
13
- import * as Runner from "./Runner";
10
+ import type * as Camera from "./Camera.js";
11
+ import type * as Color from "./Color.js";
12
+ import type * as Entity from "./Entity.js";
13
+ import type * as Instance from "./Instance.js";
14
+ import * as Phaser from "./Phaser.js";
15
+ import * as Runner from "./Runner.js";
14
16
  export declare const TypeId: "~motion/Scene";
15
- export interface Scene<E, R, Entities> {
17
+ export interface Scene<E = never, R = never> {
16
18
  readonly [TypeId]: typeof TypeId;
17
19
  readonly runner: Effect.Effect<void, E, R | Scope.Scope>;
18
- readonly "~entities": Entities;
19
20
  /** tooling-facing metadata; never read by the runtime */
20
21
  readonly annotations: Context.Context<never>;
21
- annotate<I, S>(key: Context.Key<I, S>, value: S): Scene<E, R, Entities>;
22
- annotateMerge(context: Context.Context<never>): Scene<E, R, Entities>;
22
+ annotate<I, S>(key: Context.Key<I, S>, value: S): Scene<E, R>;
23
+ annotateMerge(context: Context.Context<never>): Scene<E, R>;
23
24
  }
24
- type MakeEffect<Eff extends Effect.Effect<any, any, any>, AEff> = Effect.Effect<AEff, [
25
- Eff
26
- ] extends [never] ? never : [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E : never, [
27
- Eff
28
- ] extends [never] ? never : [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R : never>;
29
- export declare const make: <const Eff extends Effect.Effect<any, any, any>, AEff>(f: () => Generator<Eff, void, never>) => MakeEffect<Eff, AEff> extends Effect.Effect<AEff, infer E, infer R> ? Scene<E, Exclude<R, {
30
- readonly [Entity.TypeId]: typeof Entity.TypeId;
31
- }> | Scope.Scope, Extract<R, {
32
- readonly [Entity.TypeId]: typeof Entity.TypeId;
33
- }>> : never;
34
- export declare const instantiate: <Name extends string, Data extends Schema.Top, Traits extends Partial<Entity.EntityTraits<Data["Type"]>>, MakeInput>(entity: Entity.Entity<Name, Data, Traits, MakeInput>, props: Runner.InstantiateProps<MakeInput>) => Effect.Effect<Instance.Instance<Name, Data, Traits>, unknown, Entity.Entity<Name, Data, Traits, MakeInput> | Runner.Runner>;
25
+ export type AnyScene = Scene<never, never>;
26
+ export declare const make: <const Eff extends Effect.Effect<any, any, any>, const AEff>(f: () => Generator<Eff, AEff, never>) => Scene<[Eff] extends [never] ? never : [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R : never>;
27
+ export declare const instantiate: <Name extends string, Data extends Schema.Top, Traits extends Partial<Entity.EntityTraits<Data["Type"]>>>(entity: Entity.Entity<Name, Data, Traits>, props: Runner.InstantiateProps<Data["~type.make.in"]>) => Effect.Effect<Instance.Instance<Name, Data, Traits>, never, Runner.Runner>;
35
28
  export declare const tick: Effect.Effect<void, never, Runner.Runner>;
36
29
  /**
37
30
  * Hold the scene for `duration` of scene time (frames at the runner's
@@ -52,7 +45,7 @@ export interface FrameEntry<Entity extends Entity.AnyEntity> {
52
45
  export type EntriesFromEntities<Entities> = Entities extends Entity.AnyEntity ? {
53
46
  [K in Entities as K["name"]]: FrameEntry<K>;
54
47
  }[Entities["name"]] : never;
55
- export interface Frame<Entities extends Entity.AnyEntity> {
48
+ export interface Frame<Entities extends Entity.AnyEntity = Entity.AnyEntity> {
56
49
  instances: Record<string, EntriesFromEntities<Entities>>;
57
50
  /** id of the root group (conventionally "root"); never rendered itself */
58
51
  root: string;
@@ -60,25 +53,21 @@ export interface Frame<Entities extends Entity.AnyEntity> {
60
53
  frameRate: number;
61
54
  width: number;
62
55
  height: number;
63
- backgroundColor: string;
64
- /** the active camera's view; identity `{0,0,1}` when unused */
65
- camera: {
66
- x: number;
67
- y: number;
68
- zoom: number;
69
- };
56
+ backgroundColor: Color.Color;
57
+ /** the active camera's view; Camera.IDENTITY when unused */
58
+ camera: Camera.CameraState;
70
59
  }
71
- export declare const step: <E, R, Entities extends Entity.AnyEntity>(runningScene: RunningScene<E, R, Entities>) => Effect.Effect<Frame<Entities> | null, E, never>;
72
- interface RunningScene<E, R, Entities> {
60
+ export declare const step: <E, R>(runningScene: RunningScene<E, R>) => Effect.Effect<Frame<Entity.AnyEntity> | null, E, never>;
61
+ export interface RunningScene<E, R> {
73
62
  readonly runner: Runner.Runner["Service"];
74
- readonly scene: Scene<E, R, Entities>;
63
+ readonly scene: Scene<E, R>;
75
64
  readonly fiber: Fiber.Fiber<void, E>;
76
65
  readonly done: boolean;
77
66
  /** frames delivered so far — mutated by `step` for the maxFrames cap */
78
67
  framesDelivered: number;
79
68
  }
80
- export declare const run: <E, R, Entities>(scene: Scene<E, R, Entities>, settings?: Partial<Runner.Settings>) => Effect.Effect<RunningScene<E, R, Entities>, never, Exclude<Exclude<Exclude<Exclude<R, Scope.Scope>, never>, Phaser.Phaser>, Runner.Runner>>;
81
- export declare const stream: <E, R, Entities extends Entity.AnyEntity>(scene: Scene<E, R, Entities>, settings?: Partial<Runner.Settings>) => Stream.Stream<Frame<Entities>, Exclude<E, Cause.Done<any>>, Exclude<Exclude<Exclude<Exclude<Exclude<R, Scope.Scope>, never>, Phaser.Phaser>, Runner.Runner>, Scope.Scope>>;
69
+ export declare const run: <E, R>(scene: Scene<E, R>, settings?: Partial<Runner.Settings>) => Effect.Effect<RunningScene<E, R>, never, Scope.Scope | Exclude<Exclude<Exclude<Exclude<R, Scope.Scope>, never>, Phaser.Phaser>, Runner.Runner>>;
70
+ export declare const stream: <E = never, R = never>(scene: Scene<E, R>, settings?: Partial<Runner.Settings>) => Stream.Stream<Frame<Entity.AnyEntity>, Exclude<E, Cause.Done<any>>, Exclude<Exclude<Exclude<Exclude<Exclude<R, Scope.Scope>, never>, Phaser.Phaser>, Runner.Runner>, Scope.Scope>>;
82
71
  type Updater<Data> = Data | ((data: Data) => Data);
83
72
  export declare const data: <Name extends string, Data extends Schema.Top>(instance: Instance.Instance<Name, Data>) => Effect.Effect<Data["Type"] & ({} | undefined), never, Runner.Runner>;
84
73
  export declare const update: <Name extends string, Data extends Schema.Top>(instance: Instance.Instance<Name, Data>, props: Updater<Data["Type"]>) => Effect.Effect<void | false, never, Runner.Runner>;
@@ -95,41 +84,81 @@ export declare const settings: () => Effect.Effect<{
95
84
  frameRate: number;
96
85
  width: number;
97
86
  height: number;
98
- backgroundColor: string;
87
+ backgroundColor: Color.Color;
99
88
  seed: Runner.Seed;
100
89
  maxFrames: number;
101
90
  }, never, Runner.Runner>;
102
91
  /**
103
92
  * The active camera instance — an ordinary instance carrying `~position`
104
- * (x/y pan) and a `zoom` field, so the existing animators drive it:
105
- * `Scene.make(function* () { const cam = yield* Scene.camera; yield*
106
- * cam.pipe(Motion.moveTo({ x: 400 })) })`. A default identity camera is
107
- * always present; animate it directly, or `Scene.setCamera` to swap in
108
- * another instance. The camera is never drawn.
93
+ * (world x/y/z), Euler orientation (`rotX`/`rotY`/`rotZ`), and
94
+ * `focalLength` (perspective strength — see Projection.defaultFocalLength),
95
+ * so the existing animators drive it: `Scene.make(function* () { const cam
96
+ * = yield* Scene.camera; yield* cam.pipe(Motion.moveTo({ z: -400 })) })`.
97
+ * A default resting camera is always present (width-relative 50mm-equivalent
98
+ * focal length, projecting z=0 content to plain-2D placement); animate it
99
+ * directly, or `Scene.setCamera` to swap in another instance. The camera is
100
+ * never drawn.
109
101
  */
110
102
  export declare const camera: Effect.Effect<Instance.Instance<"Camera", Schema.Struct<{
111
103
  x: Schema.withConstructorDefault<Schema.Number>;
112
104
  y: Schema.withConstructorDefault<Schema.Number>;
113
- zoom: Schema.withConstructorDefault<Schema.Number>;
105
+ z: Schema.withConstructorDefault<Schema.Number>;
106
+ rotX: Schema.withConstructorDefault<Schema.Number>;
107
+ rotY: Schema.withConstructorDefault<Schema.Number>;
108
+ rotZ: Schema.withConstructorDefault<Schema.Number>;
109
+ focalLength: Schema.withConstructorDefault<Schema.Number>;
110
+ focusDistance: Schema.withConstructorDefault<Schema.Number>;
111
+ aperture: Schema.withConstructorDefault<Schema.Number>;
112
+ poiX: Schema.optionalKey<Schema.Number>;
113
+ poiY: Schema.optionalKey<Schema.Number>;
114
+ poiZ: Schema.optionalKey<Schema.Number>;
114
115
  }>, {
115
116
  readonly "~position": {
116
- get: (data: Schema.Struct.ReadonlySide<{
117
- x: Schema.withConstructorDefault<Schema.Number>;
118
- y: Schema.withConstructorDefault<Schema.Number>;
119
- zoom: Schema.withConstructorDefault<Schema.Number>;
120
- }, "Type">) => {
117
+ get: (data: {
118
+ readonly x: number;
119
+ readonly y: number;
120
+ readonly z: number;
121
+ readonly rotX: number;
122
+ readonly rotY: number;
123
+ readonly rotZ: number;
124
+ readonly focalLength: number;
125
+ readonly focusDistance: number;
126
+ readonly aperture: number;
127
+ readonly poiX?: number;
128
+ readonly poiY?: number;
129
+ readonly poiZ?: number;
130
+ }) => {
121
131
  x: number;
122
132
  y: number;
133
+ z: number;
134
+ };
135
+ set: (data: {
136
+ readonly x: number;
137
+ readonly y: number;
138
+ readonly z: number;
139
+ readonly rotX: number;
140
+ readonly rotY: number;
141
+ readonly rotZ: number;
142
+ readonly focalLength: number;
143
+ readonly focusDistance: number;
144
+ readonly aperture: number;
145
+ readonly poiX?: number;
146
+ readonly poiY?: number;
147
+ readonly poiZ?: number;
148
+ }, value: Entity.Position) => {
149
+ readonly x: number;
150
+ readonly y: number;
151
+ readonly z: number;
152
+ readonly rotX: number;
153
+ readonly rotY: number;
154
+ readonly rotZ: number;
155
+ readonly focalLength: number;
156
+ readonly focusDistance: number;
157
+ readonly aperture: number;
158
+ readonly poiX?: number;
159
+ readonly poiY?: number;
160
+ readonly poiZ?: number;
123
161
  };
124
- set: (data: Schema.Struct.ReadonlySide<{
125
- x: Schema.withConstructorDefault<Schema.Number>;
126
- y: Schema.withConstructorDefault<Schema.Number>;
127
- zoom: Schema.withConstructorDefault<Schema.Number>;
128
- }, "Type">, value: Entity.Position) => Schema.Struct.ReadonlySide<{
129
- x: Schema.withConstructorDefault<Schema.Number>;
130
- y: Schema.withConstructorDefault<Schema.Number>;
131
- zoom: Schema.withConstructorDefault<Schema.Number>;
132
- }, "Type">;
133
162
  };
134
163
  }>, never, Runner.Runner>;
135
164
  /** Swap the active camera to `instance`; its live data becomes the view. */
@@ -142,7 +171,7 @@ export declare const setCamera: (instance: Instance.Instance) => Effect.Effect<v
142
171
  * the rest of the scene stays live while you wait. `fiber` is the
143
172
  * branch's physical execution — interrupt it to bound a tail.
144
173
  */
145
- export interface BranchHandle<A = unknown, E = unknown> {
174
+ export interface BranchHandle<A = unknown, E = never> {
146
175
  readonly finished: Effect.Effect<void, never, Runner.Runner>;
147
176
  readonly fiber: Fiber.Fiber<A, E>;
148
177
  }
@@ -178,7 +207,7 @@ export declare const repeat: <A, E, R, Output, ScheduleE, ScheduleR>(effect: Eff
178
207
  * instead. Forks are supervised by the fiber that spawned them: a fork
179
208
  * made inside another fork is interrupted when its spawner completes.
180
209
  */
181
- export declare const fork: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<BranchHandle<A, E>, never, Runner.Runner | Exclude<Exclude<R, never>, Phaser.Phaser>>;
210
+ export declare const fork: <A, E = never, R = never>(effect: Effect.Effect<A, E, R>) => Effect.Effect<BranchHandle<A, E>, never, Runner.Runner | Exclude<Exclude<R, never>, Phaser.Phaser>>;
182
211
  /**
183
212
  * Like {@link fork}, but the fiber is INTERRUPTED at scene end instead
184
213
  * of awaited — for indefinite work (`Scene.repeat(…, Schedule.forever)`)
@@ -186,7 +215,7 @@ export declare const fork: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.E
186
215
  * alive. "Scene end" includes the fork drain: backgrounds keep animating
187
216
  * while awaited forks finish, and are stopped after the last one.
188
217
  */
189
- export declare const background: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<BranchHandle<A, E>, never, Runner.Runner | Exclude<Exclude<R, never>, Phaser.Phaser>>;
218
+ export declare const background: <A, E = never, R = never>(effect: Effect.Effect<A, E, R>) => Effect.Effect<BranchHandle<A, E>, never, Runner.Runner | Exclude<Exclude<R, never>, Phaser.Phaser>>;
190
219
  export interface PlayOptions {
191
220
  /** group to mount the scene's instances under (default: the root) */
192
221
  readonly parent?: Runner.GroupInstance;
@@ -202,14 +231,14 @@ export interface PlayOptions {
202
231
  * like a fork — `yield* handle.finished` for sequential nesting, or
203
232
  * don't await for concurrent scenes.
204
233
  */
205
- export declare const play: <E, R, Entities>(scene: Scene<E, R, Entities>, options?: PlayOptions) => Effect.Effect<BranchHandle<void, E>, never, Runner.Runner | Exclude<R, Scope.Scope>>;
234
+ export declare const play: <E, R>(scene: Scene<E, R>, options?: PlayOptions) => Effect.Effect<BranchHandle<void, E>, never, Runner.Runner | Exclude<R, Scope.Scope>>;
206
235
  /**
207
236
  * Run effects in lockstep parallel, sharing frame phases — the public
208
237
  * counterpart to the low-level `Phaser.all`. Takes no schedule: pacing a
209
238
  * list sequentially belongs to {@link chain}, overlapping staggered
210
239
  * starts to {@link stagger}.
211
240
  */
212
- export declare const all: typeof Phaser.all;
241
+ export declare const all: <Eff extends Effect.Effect<any, any, any>>(effects: Iterable<Eff>) => Effect.Effect<void, Eff extends Effect.Effect<any, infer E, any> ? E : never, Runner.Runner | Exclude<Eff extends Effect.Effect<any, any, infer R> ? R : never, Phaser.Phaser>>;
213
242
  /**
214
243
  * Run items one at a time, in order — items NEVER overlap, mirroring
215
244
  * Effect's guarantee for scheduled effects. The first item runs
@@ -237,5 +266,5 @@ export declare const chain: <Eff extends Effect.Effect<any, any, any>, ScheduleE
237
266
  */
238
267
  export declare const stagger: <Eff extends Effect.Effect<any, any, any>, ScheduleE = never, ScheduleR = never>(effects: Iterable<Eff>, schedule: Schedule.Schedule<unknown, void, ScheduleE, ScheduleR>) => Effect.Effect<{
239
268
  released: number;
240
- }, (Eff extends Effect.Effect<any, infer E, any> ? E : never) | ScheduleE, (Eff extends Effect.Effect<any, any, infer R> ? R : never) | Phaser.Phaser | Runner.Runner | ScheduleR>;
269
+ }, (Eff extends Effect.Effect<any, infer E, any> ? E : never) | ScheduleE, (Eff extends Effect.Effect<any, any, infer R> ? R : never) | Runner.Runner | ScheduleR>;
241
270
  export {};
package/dist/Scene.js CHANGED
@@ -5,9 +5,9 @@ import * as Exit from "effect/Exit";
5
5
  import * as Fiber from "effect/Fiber";
6
6
  import * as Random from "effect/Random";
7
7
  import * as Stream from "effect/Stream";
8
- import * as Phaser from "./Phaser";
9
- import * as Runner from "./Runner";
10
- import * as Time from "./Time";
8
+ import * as Phaser from "./Phaser.js";
9
+ import * as Runner from "./Runner.js";
10
+ import * as Time from "./Time.js";
11
11
  export const TypeId = "~motion/Scene";
12
12
  export const make = (f) => {
13
13
  return makeScene(Effect.scoped(Effect.gen(f)), Context.empty());
@@ -126,13 +126,13 @@ export const run = (scene, settings = {}) => Effect.gen(function* () {
126
126
  // interrupt can cut this fiber before failCause runs,
127
127
  // and the root branch records like any other branch
128
128
  onFailure: (cause) => Effect.sync(() => runner.recordFailure(cause)).pipe(Effect.andThen(releaseRoot), Effect.andThen(Fiber.interruptAll([...runner.forks, ...runner.backgrounds].map((b) => b.fiber))), Effect.andThen(Effect.failCause(cause))),
129
- }))).pipe(Effect.ensuring(releaseRoot), Effect.provideService(CurrentBranch, rootBranch),
129
+ }))).pipe(Effect.ensuring(releaseRoot), Effect.provideService(currentBranch(), rootBranch),
130
130
  // success, failure, or interrupt: the scene is over either way
131
131
  Effect.ensuring(Effect.sync(() => {
132
132
  done = true;
133
133
  })), Effect.provide(Layer.succeed(Phaser.Phaser, runner.phaser)), Effect.provide(Layer.succeed(Runner.Runner, runner)),
134
134
  // seeded pseudo-randomness, scoped to the scene fiber
135
- Random.withSeed(runner.settings.seed), Effect.forkChild);
135
+ Random.withSeed(runner.settings.seed), Effect.forkScoped);
136
136
  }));
137
137
  const runningScene = {
138
138
  runner,
@@ -191,11 +191,14 @@ export const settings = Effect.fnUntraced(function* () {
191
191
  });
192
192
  /**
193
193
  * The active camera instance — an ordinary instance carrying `~position`
194
- * (x/y pan) and a `zoom` field, so the existing animators drive it:
195
- * `Scene.make(function* () { const cam = yield* Scene.camera; yield*
196
- * cam.pipe(Motion.moveTo({ x: 400 })) })`. A default identity camera is
197
- * always present; animate it directly, or `Scene.setCamera` to swap in
198
- * another instance. The camera is never drawn.
194
+ * (world x/y/z), Euler orientation (`rotX`/`rotY`/`rotZ`), and
195
+ * `focalLength` (perspective strength — see Projection.defaultFocalLength),
196
+ * so the existing animators drive it: `Scene.make(function* () { const cam
197
+ * = yield* Scene.camera; yield* cam.pipe(Motion.moveTo({ z: -400 })) })`.
198
+ * A default resting camera is always present (width-relative 50mm-equivalent
199
+ * focal length, projecting z=0 content to plain-2D placement); animate it
200
+ * directly, or `Scene.setCamera` to swap in another instance. The camera is
201
+ * never drawn.
199
202
  */
200
203
  export const camera = Effect.gen(function* () {
201
204
  const runner = yield* Runner.Runner;
@@ -207,7 +210,9 @@ export const setCamera = (instance) => Effect.gen(function* () {
207
210
  runner.setCamera(instance);
208
211
  });
209
212
  /** the innermost enclosing branch; null = outside any running scene */
210
- const CurrentBranch = Context.Reference("motion/Scene/CurrentBranch", { defaultValue: () => null });
213
+ const currentBranch = () => Context.Reference("motion/Scene/CurrentBranch", {
214
+ defaultValue: () => null,
215
+ });
211
216
  const makeBranch = (runner, kind) => {
212
217
  const latch = Latch.makeUnsafe();
213
218
  let finished = false;
@@ -247,7 +252,7 @@ const makeBranch = (runner, kind) => {
247
252
  * reported — by then nothing is listening.
248
253
  */
249
254
  export const finish = Effect.gen(function* () {
250
- const branch = yield* CurrentBranch;
255
+ const branch = yield* currentBranch();
251
256
  if (branch === null) {
252
257
  return yield* Effect.die(new Error("Scene.finish called outside a running scene"));
253
258
  }
@@ -270,7 +275,7 @@ const forkBranch = (runner, effect, kind) => Effect.gen(function* () {
270
275
  runner.recordFailure(exit.cause);
271
276
  }
272
277
  branch.finishUnsafe();
273
- })), Effect.provideService(CurrentBranch, branch)));
278
+ })), Effect.provideService(currentBranch(), branch)));
274
279
  branch.entry.fiber = fiber;
275
280
  if (kind === "fork" && !branch.isFinished()) {
276
281
  runner.forks.add(branch.entry);
@@ -373,7 +378,11 @@ export const play = (scene, options) => Effect.gen(function* () {
373
378
  * list sequentially belongs to {@link chain}, overlapping staggered
374
379
  * starts to {@link stagger}.
375
380
  */
376
- export const all = (effects) => Phaser.all(effects);
381
+ export const all = Effect.fnUntraced(function* (effects) {
382
+ const runner = yield* Runner.Runner;
383
+ // runner.phaser
384
+ return yield* Phaser.all(effects).pipe(Effect.provideService(Phaser.Phaser, runner.phaser));
385
+ });
377
386
  /**
378
387
  * Run items one at a time, in order — items NEVER overlap, mirroring
379
388
  * Effect's guarantee for scheduled effects. The first item runs
@@ -449,6 +458,6 @@ export const stagger = (effects, schedule) => Effect.gen(function* () {
449
458
  // biome-ignore lint/style/noNonNullAssertion: same length as releaseFrames
450
459
  yield* list[i];
451
460
  }));
452
- yield* Phaser.all(branches);
461
+ yield* Phaser.all(branches).pipe(Effect.provideService(Phaser.Phaser, runner.phaser));
453
462
  return { released: releaseFrames.length };
454
463
  });
@@ -0,0 +1,11 @@
1
+ export { Circle } from "./shapes/Circle.js";
2
+ export { Ellipse } from "./shapes/Ellipse.js";
3
+ export * from "./shapes/Group.js";
4
+ export { Hud } from "./shapes/Hud.js";
5
+ export { Image } from "./shapes/Image.js";
6
+ export { Line } from "./shapes/Line.js";
7
+ export { Path } from "./shapes/Path.js";
8
+ export { Rect } from "./shapes/Rect.js";
9
+ export * as Shape2D from "./shapes/Shape2D.js";
10
+ export { Square } from "./shapes/Square.js";
11
+ export { Text } from "./shapes/Text.js";
package/dist/Shapes.js ADDED
@@ -0,0 +1,11 @@
1
+ export { Circle } from "./shapes/Circle.js";
2
+ export { Ellipse } from "./shapes/Ellipse.js";
3
+ export * from "./shapes/Group.js";
4
+ export { Hud } from "./shapes/Hud.js";
5
+ export { Image } from "./shapes/Image.js";
6
+ export { Line } from "./shapes/Line.js";
7
+ export { Path } from "./shapes/Path.js";
8
+ export { Rect } from "./shapes/Rect.js";
9
+ export * as Shape2D from "./shapes/Shape2D.js";
10
+ export { Square } from "./shapes/Square.js";
11
+ export { Text } from "./shapes/Text.js";
package/dist/demo.d.ts CHANGED
@@ -1,186 +1,3 @@
1
- import * as Schema from "effect/Schema";
2
- import * as Entity from "./Entity";
3
- import * as Scene from "./Scene";
4
- export declare const scene: Scene.Scene<unknown, import("./Runner").Runner | import("effect/Scope").Scope, Entity.Entity<"shapes/Group", Schema.Struct<{
5
- x: Schema.withConstructorDefault<Schema.Number>;
6
- y: Schema.withConstructorDefault<Schema.Number>;
7
- opacity: Schema.withConstructorDefault<Schema.Number>;
8
- transform: Schema.withConstructorDefault<Schema.Struct<{
9
- readonly a: Schema.Number;
10
- readonly b: Schema.Number;
11
- readonly c: Schema.Number;
12
- readonly d: Schema.Number;
13
- readonly e: Schema.Number;
14
- readonly f: Schema.Number;
15
- }>>;
16
- children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
17
- }>, {
18
- "~position": Entity.TraitLens<Schema.Struct.ReadonlySide<{
19
- x: Schema.withConstructorDefault<Schema.Number>;
20
- y: Schema.withConstructorDefault<Schema.Number>;
21
- opacity: Schema.withConstructorDefault<Schema.Number>;
22
- transform: Schema.withConstructorDefault<Schema.Struct<{
23
- readonly a: Schema.Number;
24
- readonly b: Schema.Number;
25
- readonly c: Schema.Number;
26
- readonly d: Schema.Number;
27
- readonly e: Schema.Number;
28
- readonly f: Schema.Number;
29
- }>>;
30
- children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
31
- }, "Type">, Entity.Position>;
32
- "~opacity": Entity.TraitLens<Schema.Struct.ReadonlySide<{
33
- x: Schema.withConstructorDefault<Schema.Number>;
34
- y: Schema.withConstructorDefault<Schema.Number>;
35
- opacity: Schema.withConstructorDefault<Schema.Number>;
36
- transform: Schema.withConstructorDefault<Schema.Struct<{
37
- readonly a: Schema.Number;
38
- readonly b: Schema.Number;
39
- readonly c: Schema.Number;
40
- readonly d: Schema.Number;
41
- readonly e: Schema.Number;
42
- readonly f: Schema.Number;
43
- }>>;
44
- children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
45
- }, "Type">, number>;
46
- }, Omit<{
47
- readonly x?: number;
48
- readonly y?: number;
49
- readonly opacity?: number;
50
- readonly transform?: Schema.Struct.ReadonlyMakeIn<{
51
- readonly a: Schema.Number;
52
- readonly b: Schema.Number;
53
- readonly c: Schema.Number;
54
- readonly d: Schema.Number;
55
- readonly e: Schema.Number;
56
- readonly f: Schema.Number;
57
- }>;
58
- readonly children?: readonly string[];
59
- }, "transform"> & {
60
- readonly transform?: readonly (Schema.Struct.ReadonlySide<{
61
- readonly _tag: Schema.tag<"transform/matrix">;
62
- readonly a: Schema.Number;
63
- readonly b: Schema.Number;
64
- readonly c: Schema.Number;
65
- readonly d: Schema.Number;
66
- readonly e: Schema.Number;
67
- readonly f: Schema.Number;
68
- }, "Type"> | Schema.Struct.ReadonlySide<{
69
- readonly _tag: Schema.tag<"transform/scale">;
70
- readonly x: Schema.Number;
71
- readonly y: Schema.Number;
72
- }, "Type"> | Schema.Struct.ReadonlySide<{
73
- readonly _tag: Schema.tag<"transform/translate">;
74
- readonly x: Schema.Number;
75
- readonly y: Schema.Number;
76
- }, "Type">)[];
77
- }>>;
78
- export declare const staggered: Scene.Scene<unknown, import("./Phaser").Phaser | import("./Runner").Runner | import("effect/Scope").Scope, Entity.Entity<"shapes/Circle", Schema.Struct<{
79
- x: Schema.withConstructorDefault<Schema.Number>;
80
- y: Schema.withConstructorDefault<Schema.Number>;
81
- opacity: Schema.withConstructorDefault<Schema.Number>;
82
- fill: Schema.withConstructorDefault<Schema.String>;
83
- stroke: Schema.optionalKey<Schema.String>;
84
- strokeWidth: Schema.optionalKey<Schema.Number>;
85
- radius: Schema.withConstructorDefault<Schema.Number>;
86
- }>, {
87
- readonly "~position": Entity.TraitLens<{
88
- readonly x: number;
89
- readonly y: number;
90
- readonly opacity: number;
91
- readonly fill: string;
92
- readonly stroke?: string;
93
- readonly strokeWidth?: number;
94
- readonly radius: number;
95
- }, Entity.Position>;
96
- readonly "~opacity": Entity.TraitLens<{
97
- readonly x: number;
98
- readonly y: number;
99
- readonly opacity: number;
100
- readonly fill: string;
101
- readonly stroke?: string;
102
- readonly strokeWidth?: number;
103
- readonly radius: number;
104
- }, number>;
105
- }, {
106
- readonly x?: number;
107
- readonly y?: number;
108
- readonly opacity?: number;
109
- readonly fill?: string;
110
- readonly stroke?: string;
111
- readonly strokeWidth?: number;
112
- readonly radius?: number;
113
- }> | Entity.Entity<"shapes/Group", Schema.Struct<{
114
- x: Schema.withConstructorDefault<Schema.Number>;
115
- y: Schema.withConstructorDefault<Schema.Number>;
116
- opacity: Schema.withConstructorDefault<Schema.Number>;
117
- transform: Schema.withConstructorDefault<Schema.Struct<{
118
- readonly a: Schema.Number;
119
- readonly b: Schema.Number;
120
- readonly c: Schema.Number;
121
- readonly d: Schema.Number;
122
- readonly e: Schema.Number;
123
- readonly f: Schema.Number;
124
- }>>;
125
- children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
126
- }>, {
127
- "~position": Entity.TraitLens<Schema.Struct.ReadonlySide<{
128
- x: Schema.withConstructorDefault<Schema.Number>;
129
- y: Schema.withConstructorDefault<Schema.Number>;
130
- opacity: Schema.withConstructorDefault<Schema.Number>;
131
- transform: Schema.withConstructorDefault<Schema.Struct<{
132
- readonly a: Schema.Number;
133
- readonly b: Schema.Number;
134
- readonly c: Schema.Number;
135
- readonly d: Schema.Number;
136
- readonly e: Schema.Number;
137
- readonly f: Schema.Number;
138
- }>>;
139
- children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
140
- }, "Type">, Entity.Position>;
141
- "~opacity": Entity.TraitLens<Schema.Struct.ReadonlySide<{
142
- x: Schema.withConstructorDefault<Schema.Number>;
143
- y: Schema.withConstructorDefault<Schema.Number>;
144
- opacity: Schema.withConstructorDefault<Schema.Number>;
145
- transform: Schema.withConstructorDefault<Schema.Struct<{
146
- readonly a: Schema.Number;
147
- readonly b: Schema.Number;
148
- readonly c: Schema.Number;
149
- readonly d: Schema.Number;
150
- readonly e: Schema.Number;
151
- readonly f: Schema.Number;
152
- }>>;
153
- children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
154
- }, "Type">, number>;
155
- }, Omit<{
156
- readonly x?: number;
157
- readonly y?: number;
158
- readonly opacity?: number;
159
- readonly transform?: Schema.Struct.ReadonlyMakeIn<{
160
- readonly a: Schema.Number;
161
- readonly b: Schema.Number;
162
- readonly c: Schema.Number;
163
- readonly d: Schema.Number;
164
- readonly e: Schema.Number;
165
- readonly f: Schema.Number;
166
- }>;
167
- readonly children?: readonly string[];
168
- }, "transform"> & {
169
- readonly transform?: readonly (Schema.Struct.ReadonlySide<{
170
- readonly _tag: Schema.tag<"transform/matrix">;
171
- readonly a: Schema.Number;
172
- readonly b: Schema.Number;
173
- readonly c: Schema.Number;
174
- readonly d: Schema.Number;
175
- readonly e: Schema.Number;
176
- readonly f: Schema.Number;
177
- }, "Type"> | Schema.Struct.ReadonlySide<{
178
- readonly _tag: Schema.tag<"transform/scale">;
179
- readonly x: Schema.Number;
180
- readonly y: Schema.Number;
181
- }, "Type"> | Schema.Struct.ReadonlySide<{
182
- readonly _tag: Schema.tag<"transform/translate">;
183
- readonly x: Schema.Number;
184
- readonly y: Schema.Number;
185
- }, "Type">)[];
186
- }>>;
1
+ import * as Scene from "./Scene.js";
2
+ export declare const scene: Scene.Scene<never, import("./Runner.js").Runner>;
3
+ export declare const staggered: Scene.Scene<never, import("./Runner.js").Runner>;
package/dist/demo.js CHANGED
@@ -1,9 +1,14 @@
1
+ import { NodeServices } from "@effect/platform-node";
2
+ import { Session } from "@effect-motion/thorvg";
3
+ import { EngineNode } from "@effect-motion/thorvg/node";
1
4
  import { Effect, Layer, Schedule, Stream } from "effect";
2
- import { Svg } from ".";
3
- import * as Motion from "./Motion";
4
- import * as Physics from "./Physics";
5
- import * as Scene from "./Scene";
6
- import * as Shapes from "./shapes";
5
+ import * as Color from "./Color.js";
6
+ import * as Motion from "./Motion.js";
7
+ import * as Physics from "./Physics.js";
8
+ import * as PngExporter from "./PngExporter.js";
9
+ import * as Renderer from "./Renderer.js";
10
+ import * as Scene from "./Scene.js";
11
+ import * as Shapes from "./Shapes.js";
7
12
  // children live in the group's local coordinates: one motion moves them all
8
13
  export const scene = Scene.make(function* () {
9
14
  const duo = yield* Scene.instantiate(Shapes.Group, {
@@ -14,35 +19,19 @@ export const scene = Scene.make(function* () {
14
19
  x: 0,
15
20
  y: 0,
16
21
  radius: 14,
17
- fill: "#e53170",
22
+ fill: Color.hex("#e53170"),
18
23
  }),
19
24
  Scene.instantiate(Shapes.Square, {
20
25
  x: 20,
21
26
  y: -16,
22
27
  size: 28,
23
- fill: "#a786df",
28
+ fill: Color.hex("#a786df"),
24
29
  }),
25
30
  ],
26
31
  });
27
- const moveA = Motion.moveTo({ x: 380 }, "1.5 seconds", "easeInOutCubic");
28
- const moveB = Motion.moveTo({ x: 70 }, "1.5 seconds", "easeInOutCubic");
29
32
  yield* Motion.wait("1.5 seconds");
30
33
  yield* duo.pipe(Motion.moveTo({ x: 380 }, "1.5 seconds", "easeInOutCubic"), Motion.moveTo({ x: 70 }, "1.5 seconds", "easeInOutCubic"), Motion.wait("1.5 seconds"), Physics.springTo({ y: 80 }, "jump"), Motion.fadeTo(0.15, "1 second"));
31
34
  });
32
- // const program = Effect.gen(function* () {
33
- // const renderer = yield* Svg.SvgRenderer.Context;
34
- // const sceneResult = yield* Scene.stream(scene, {
35
- // }).pipe(
36
- // Stream.runCollect,
37
- // Effect.provide(renderer)
38
- // )
39
- // console.log(sceneResult);
40
- // // renderer.render(scene, {
41
- // // width: 500,
42
- // // height: 300,
43
- // // });
44
- // }).pipe(Effect.provide(shapesLayer));
45
- // Effect.runPromise(program);
46
35
  // schedule-driven composition: a background pulse loops for the scene's
47
36
  // duration while three staggered dots define its actual length
48
37
  export const staggered = Scene.make(function* () {
@@ -53,7 +42,7 @@ export const staggered = Scene.make(function* () {
53
42
  x,
54
43
  y: 0,
55
44
  radius: 10,
56
- fill: "#e53170",
45
+ fill: Color.hex("#e53170"),
57
46
  });
58
47
  // reparent the freshly-born dot from root into the stage group
59
48
  yield* Scene.appendChild(stage, circle);
@@ -61,16 +50,16 @@ export const staggered = Scene.make(function* () {
61
50
  });
62
51
  yield* Scene.stagger([dot(0), dot(25), dot(50)], Schedule.spaced("250 millis"));
63
52
  });
53
+ // render the middle frame of the duo scene to a PNG through the single ThorVG
54
+ // renderer (Node adapter) — the end-to-end path: Scene.stream → renderToPng.
64
55
  const movie = Effect.gen(function* () {
65
- // const svgRenderer = yield* Svg.SvgRenderer.Context;
66
- // const frames = yield* Scene.stream(scene).pipe(Stream.runCollect);
67
- // let n = 0;
68
- // for (const frame of frames) {
69
- // console.log(`\x1b[36mframe ${n++}\x1b[0m`);
70
- // console.log(yield* svgRenderer.render(frame, { width: 500, height: 300 }));
71
- // }
72
- const frames = yield* Scene.stream(staggered).pipe(Stream.runCollect);
73
- console.log(`staggered scene: ${[...frames].length} frames`);
56
+ const frames = yield* Scene.stream(scene, { width: 500, height: 300 }).pipe(Stream.runCollect);
57
+ const list = [...frames];
58
+ const mid = list[Math.floor(list.length / 2)];
59
+ const framebuffer = yield* Renderer.render(mid);
60
+ const _png = yield* PngExporter.toBuffer(framebuffer);
61
+ yield* PngExporter.toFile(framebuffer, "output.png");
74
62
  });
75
- const layers = Svg.layer.pipe(Layer.provideMerge(Svg.shapesLayer));
76
- Effect.runPromise(movie.pipe(Effect.provide(layers)));
63
+ Effect.runPromise(movie.pipe(Effect.scoped, Effect.provide(Layer.provideMerge(
64
+ // a demo scene with declared assets would add fonts/images maps here
65
+ Session.layer({ width: 500, height: 300 }), EngineNode.layer("sw"))), Effect.provide(NodeServices.layer)));