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.
- package/dist/Camera.d.ts +50 -28
- package/dist/Camera.js +71 -19
- package/dist/CameraHelpers.d.ts +70 -0
- package/dist/CameraHelpers.js +239 -0
- package/dist/CanvasExporter.d.ts +12 -0
- package/dist/CanvasExporter.js +40 -0
- package/dist/Color.d.ts +428 -0
- package/dist/Color.js +535 -0
- package/dist/EffectMotionError.d.ts +10 -0
- package/dist/EffectMotionError.js +6 -0
- package/dist/Entity.d.ts +9 -8
- package/dist/Entity.js +1 -4
- package/dist/Fonts.d.ts +10 -0
- package/dist/Fonts.js +16 -0
- package/dist/Images.d.ts +33 -0
- package/dist/Images.js +24 -0
- package/dist/Instance.d.ts +4 -4
- package/dist/Motion.d.ts +24 -7
- package/dist/Motion.js +54 -9
- package/dist/Phaser.d.ts +1 -1
- package/dist/Phaser.js +13 -9
- package/dist/Physics.d.ts +3 -3
- package/dist/Physics.js +4 -4
- package/dist/PngExporter.d.ts +6 -0
- package/dist/PngExporter.js +85 -0
- package/dist/Projection.d.ts +225 -0
- package/dist/Projection.js +473 -0
- package/dist/Renderer.d.ts +102 -53
- package/dist/Renderer.js +369 -78
- package/dist/Runner.d.ts +126 -42
- package/dist/Runner.js +42 -17
- package/dist/Scene.d.ts +88 -59
- package/dist/Scene.js +24 -15
- package/dist/Shapes.d.ts +11 -0
- package/dist/Shapes.js +11 -0
- package/dist/demo.d.ts +3 -186
- package/dist/demo.js +24 -35
- package/dist/index.d.ts +14 -13
- package/dist/index.js +14 -13
- package/dist/particles/Particle.d.ts +4 -3
- package/dist/particles/ParticleField.d.ts +11 -48
- package/dist/particles/ParticleField.js +5 -4
- package/dist/particles/constructors.d.ts +8 -7
- package/dist/particles/constructors.js +2 -2
- package/dist/particles/index.d.ts +5 -6
- package/dist/particles/index.js +5 -6
- package/dist/particles/overLife.d.ts +1 -1
- package/dist/particles/overLife.js +1 -1
- package/dist/particles/simulate.d.ts +2 -2
- package/dist/particles/simulate.js +6 -6
- package/dist/particles/step.d.ts +2 -2
- package/dist/particles/step.js +5 -3
- package/dist/render/dof.d.ts +27 -0
- package/dist/render/dof.js +37 -0
- package/dist/render/paint.d.ts +30 -0
- package/dist/render/paint.js +36 -0
- package/dist/render/shapes.d.ts +42 -0
- package/dist/render/shapes.js +310 -0
- package/dist/shapes/Circle.d.ts +10 -15
- package/dist/shapes/Circle.js +2 -2
- package/dist/shapes/Ellipse.d.ts +10 -16
- package/dist/shapes/Ellipse.js +2 -2
- package/dist/shapes/Group.d.ts +5 -39
- package/dist/shapes/Group.js +4 -4
- package/dist/shapes/Hud.d.ts +38 -0
- package/dist/shapes/Hud.js +35 -0
- package/dist/shapes/Image.d.ts +45 -0
- package/dist/shapes/Image.js +28 -0
- package/dist/shapes/Line.d.ts +12 -12
- package/dist/shapes/Line.js +11 -6
- package/dist/shapes/Path.d.ts +87 -18
- package/dist/shapes/Path.js +25 -6
- package/dist/shapes/Rect.d.ts +33 -23
- package/dist/shapes/Rect.js +14 -2
- package/dist/shapes/Shape2D.d.ts +13 -4
- package/dist/shapes/Shape2D.js +16 -5
- package/dist/shapes/Shapes.d.ts +11 -0
- package/dist/shapes/Shapes.js +11 -0
- package/dist/shapes/Square.d.ts +10 -15
- package/dist/shapes/Square.js +2 -2
- package/dist/shapes/Text.d.ts +10 -19
- package/dist/shapes/Text.js +2 -2
- package/package.json +14 -4
- package/dist/particles/render.d.ts +0 -13
- package/dist/particles/render.js +0 -33
- package/dist/shapes/Layer.d.ts +0 -9
- package/dist/shapes/Layer.js +0 -23
- package/dist/shapes/index.d.ts +0 -10
- package/dist/shapes/index.js +0 -10
- package/dist/svg/SvgDomRenderer.d.ts +0 -28
- package/dist/svg/SvgDomRenderer.js +0 -50
- package/dist/svg/SvgNode.d.ts +0 -13
- package/dist/svg/SvgNode.js +0 -18
- package/dist/svg/SvgRenderer.d.ts +0 -22
- package/dist/svg/SvgRenderer.js +0 -20
- package/dist/svg/camera.d.ts +0 -21
- package/dist/svg/camera.js +0 -43
- package/dist/svg/index.d.ts +0 -6
- package/dist/svg/index.js +0 -6
- package/dist/svg/layers.d.ts +0 -18
- package/dist/svg/layers.js +0 -13
- package/dist/svg/shapes.d.ts +0 -1089
- package/dist/svg/shapes.js +0 -119
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-motion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Deterministic, frame-exact motion graphics in code, composed with Effect",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
".": {
|
|
24
24
|
"types": "./dist/index.d.ts",
|
|
25
25
|
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./*": {
|
|
28
|
+
"types": "./dist/*.d.ts",
|
|
29
|
+
"default": "./dist/*.js"
|
|
26
30
|
}
|
|
27
31
|
},
|
|
28
32
|
"files": [
|
|
@@ -32,15 +36,21 @@
|
|
|
32
36
|
"access": "public"
|
|
33
37
|
},
|
|
34
38
|
"peerDependencies": {
|
|
35
|
-
"effect": ">=4.0.0-beta.
|
|
39
|
+
"effect": ">=4.0.0-beta.98"
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
42
|
+
"@types/chroma-js": "^3.1.2",
|
|
38
43
|
"@types/node": "^26.1.1",
|
|
39
|
-
"effect": "4.0.0-beta.
|
|
44
|
+
"effect": "4.0.0-beta.98",
|
|
40
45
|
"happy-dom": "^20.10.6",
|
|
41
46
|
"tsx": "^4.23.0",
|
|
42
47
|
"typescript": "^7.0.2",
|
|
43
|
-
"vitest": "^4.1.10"
|
|
48
|
+
"vitest": "^4.1.10",
|
|
49
|
+
"@effect-motion/thorvg": "0.1.0"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@effect/platform-node": "4.0.0-beta.98",
|
|
53
|
+
"chroma-js": "^3.2.0"
|
|
44
54
|
},
|
|
45
55
|
"scripts": {
|
|
46
56
|
"build": "tsc -p tsconfig.build.json",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type * as Renderer from "../Renderer";
|
|
2
|
-
import type { SvgNode } from "../svg/SvgNode";
|
|
3
|
-
import type { ParticleField } from "./ParticleField";
|
|
4
|
-
/**
|
|
5
|
-
* Render a ParticleField: one <g> whose children are one <circle> per LIVE
|
|
6
|
-
* particle, each sized/faded by the over-life curves. Dead slots emit
|
|
7
|
-
* nothing. This is where the O(1)-simulation buffer expands back into N
|
|
8
|
-
* SVG nodes.
|
|
9
|
-
* ponytail: N SVG nodes is the RENDERING ceiling (the DOM/string cost), not
|
|
10
|
-
* the simulation ceiling. A batched/instanced sink is the upgrade path if
|
|
11
|
-
* node count becomes the wall.
|
|
12
|
-
*/
|
|
13
|
-
export declare const particleField: Renderer.RenderFunction<SvgNode, typeof ParticleField>;
|
package/dist/particles/render.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
|
-
import { renderOpacity, renderSize } from "./overLife";
|
|
3
|
-
/**
|
|
4
|
-
* Render a ParticleField: one <g> whose children are one <circle> per LIVE
|
|
5
|
-
* particle, each sized/faded by the over-life curves. Dead slots emit
|
|
6
|
-
* nothing. This is where the O(1)-simulation buffer expands back into N
|
|
7
|
-
* SVG nodes.
|
|
8
|
-
* ponytail: N SVG nodes is the RENDERING ceiling (the DOM/string cost), not
|
|
9
|
-
* the simulation ceiling. A batched/instanced sink is the upgrade path if
|
|
10
|
-
* node count becomes the wall.
|
|
11
|
-
*/
|
|
12
|
-
export const particleField = ({ data }) => Effect.succeed({
|
|
13
|
-
tag: "g",
|
|
14
|
-
props: data.x !== 0 || data.y !== 0
|
|
15
|
-
? { transform: `translate(${data.x} ${data.y})` }
|
|
16
|
-
: {},
|
|
17
|
-
children: data.buffer
|
|
18
|
-
.filter((p) => p.alive)
|
|
19
|
-
.map((p) => {
|
|
20
|
-
const r = renderSize(p, data.sizeOverLife);
|
|
21
|
-
const opacity = renderOpacity(p, data.opacityOverLife);
|
|
22
|
-
return {
|
|
23
|
-
tag: "circle",
|
|
24
|
-
props: {
|
|
25
|
-
cx: p.x,
|
|
26
|
-
cy: p.y,
|
|
27
|
-
r,
|
|
28
|
-
fill: p.color,
|
|
29
|
-
...(opacity !== 1 ? { opacity } : {}),
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
}),
|
|
33
|
-
});
|
package/dist/shapes/Layer.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import * as Schema from "effect/Schema";
|
|
2
|
-
import * as Entity from "../Entity";
|
|
3
|
-
export declare const Layer: Entity.Entity<"shapes/Layer", Schema.Struct<{
|
|
4
|
-
depth: Schema.withConstructorDefault<Schema.Number>;
|
|
5
|
-
children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
|
|
6
|
-
}>, {}, {
|
|
7
|
-
readonly depth?: number;
|
|
8
|
-
readonly children?: readonly string[];
|
|
9
|
-
}>;
|
package/dist/shapes/Layer.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
|
-
import * as Schema from "effect/Schema";
|
|
3
|
-
import * as Entity from "../Entity";
|
|
4
|
-
import * as Shape2D from "./Shape2D";
|
|
5
|
-
// A parallax layer: a container that holds children at a camera `depth`, and
|
|
6
|
-
// nothing else. Unlike Group it carries NO position or opacity of its own —
|
|
7
|
-
// `depth` is the only field, so it can never collide with the transform
|
|
8
|
-
// semantics a Group is expected to grow (position now, rotation/scale later),
|
|
9
|
-
// and a future guard can restrict Layers without touching Group.
|
|
10
|
-
//
|
|
11
|
-
// `depth` is the fraction of the camera this layer feels (pan AND zoom): 1 =
|
|
12
|
-
// full camera (default), 0 = pinned to the screen (a HUD), between = parallax.
|
|
13
|
-
// The sink reads it off each top-level layer (see svg/camera.ts).
|
|
14
|
-
//
|
|
15
|
-
// ponytail: a Layer nested inside another Layer is undefined behavior — the
|
|
16
|
-
// point of a dedicated entity is to leave room for a rule here, but the
|
|
17
|
-
// semantics ("a depth inside a depth"?) aren't decided yet, so no guard ships.
|
|
18
|
-
// Add one in Runner.instantiate (dispatch on entity.name === Layer.name) once
|
|
19
|
-
// nesting semantics are settled.
|
|
20
|
-
export const Layer = Entity.make("shapes/Layer", {
|
|
21
|
-
depth: Shape2D.defaultedNumber(1),
|
|
22
|
-
children: Schema.Array(Schema.String).pipe(Schema.withConstructorDefault(Effect.sync(() => []))),
|
|
23
|
-
});
|
package/dist/shapes/index.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export { Circle } from "./Circle";
|
|
2
|
-
export { Ellipse } from "./Ellipse";
|
|
3
|
-
export * from "./Group";
|
|
4
|
-
export { Layer } from "./Layer";
|
|
5
|
-
export { Line } from "./Line";
|
|
6
|
-
export { Path } from "./Path";
|
|
7
|
-
export { Rect } from "./Rect";
|
|
8
|
-
export * as Shape2D from "./Shape2D";
|
|
9
|
-
export { Square } from "./Square";
|
|
10
|
-
export { Text } from "./Text";
|
package/dist/shapes/index.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export { Circle } from "./Circle";
|
|
2
|
-
export { Ellipse } from "./Ellipse";
|
|
3
|
-
export * from "./Group";
|
|
4
|
-
export { Layer } from "./Layer";
|
|
5
|
-
export { Line } from "./Line";
|
|
6
|
-
export { Path } from "./Path";
|
|
7
|
-
export { Rect } from "./Rect";
|
|
8
|
-
export * as Shape2D from "./Shape2D";
|
|
9
|
-
export { Square } from "./Square";
|
|
10
|
-
export { Text } from "./Text";
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
|
-
import * as Renderer from "../Renderer";
|
|
3
|
-
import { type SvgNode } from "./SvgNode";
|
|
4
|
-
export interface SvgDomConfig {
|
|
5
|
-
readonly target: HTMLElement;
|
|
6
|
-
/** viewport size — defaults to the frame's own width/height metadata */
|
|
7
|
-
readonly width?: number;
|
|
8
|
-
readonly height?: number;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Materializes a frame into `target` as live DOM.
|
|
12
|
-
*
|
|
13
|
-
* ponytail: clear-and-rebuild per frame; upgrade to keyed reconciliation
|
|
14
|
-
* (instance ids are stable keys) inside this sink if it ever gets slow.
|
|
15
|
-
*/
|
|
16
|
-
export declare const SvgDomRenderer: {
|
|
17
|
-
Context: import("effect/Context").Service<{
|
|
18
|
-
render: <const Entities extends import("../Entity").AnyEntity>(frame: import("../Scene").Frame<Entities>, config: SvgDomConfig) => Effect.Effect<void, never, Entities extends import("../Entity").AnyEntity ? { [K in Entities as K["name"]]: Renderer.EntityRenderer<`SvgDomRenderer/${K["name"]}`, SvgNode, K, never, never>; }[Entities["name"]] : never>;
|
|
19
|
-
}, {
|
|
20
|
-
render: <const Entities extends import("../Entity").AnyEntity>(frame: import("../Scene").Frame<Entities>, config: SvgDomConfig) => Effect.Effect<void, never, Entities extends import("../Entity").AnyEntity ? { [K in Entities as K["name"]]: Renderer.EntityRenderer<`SvgDomRenderer/${K["name"]}`, SvgNode, K, never, never>; }[Entities["name"]] : never>;
|
|
21
|
-
}>;
|
|
22
|
-
layer: import("effect/Layer").Layer<{
|
|
23
|
-
render: <const Entities extends import("../Entity").AnyEntity>(frame: import("../Scene").Frame<Entities>, config: SvgDomConfig) => Effect.Effect<void, never, Entities extends import("../Entity").AnyEntity ? { [K in Entities as K["name"]]: Renderer.EntityRenderer<`SvgDomRenderer/${K["name"]}`, SvgNode, K, never, never>; }[Entities["name"]] : never>;
|
|
24
|
-
}, never, never>;
|
|
25
|
-
makeEntityRendererContext: <const Ent extends import("../Entity").AnyEntity, E = never, R = never>(entity: Ent) => import("effect/Context").Service<Renderer.EntityRenderer<`SvgDomRenderer/${Ent["name"]}`, SvgNode, Ent, E, R>, Renderer.EntityRenderer<`SvgDomRenderer/${Ent["name"]}`, SvgNode, Ent, E, R>>;
|
|
26
|
-
makeEntityRendererService: <const Ent extends import("../Entity").AnyEntity, E = never, R = never>(entity: Ent, render: Renderer.RenderFunction<SvgNode, Ent, E, R>) => Renderer.EntityRenderer<`SvgDomRenderer/${Ent["name"]}`, SvgNode, Ent, E, R>;
|
|
27
|
-
makeEntityRendererLayer: <const Ent extends import("../Entity").AnyEntity, E = never, R = never>(entity: Ent, render: Renderer.RenderFunction<SvgNode, Ent, E, R>) => import("effect/Layer").Layer<Renderer.EntityRenderer<`SvgDomRenderer/${Ent["name"]}`, SvgNode, Ent, E, R>, never, never>;
|
|
28
|
-
};
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
|
-
import * as Renderer from "../Renderer";
|
|
3
|
-
import { depthOf, layerTransform, wrapLayer } from "./camera";
|
|
4
|
-
import { SVG_NS } from "./SvgNode";
|
|
5
|
-
const createSvgElement = (doc, node) => {
|
|
6
|
-
// namespace is sink-owned: entity renderers never see it
|
|
7
|
-
const el = doc.createElementNS(SVG_NS, node.tag);
|
|
8
|
-
for (const [key, value] of Object.entries(node.props)) {
|
|
9
|
-
el.setAttribute(key, String(value));
|
|
10
|
-
}
|
|
11
|
-
if (typeof node.children === "string") {
|
|
12
|
-
el.textContent = node.children;
|
|
13
|
-
}
|
|
14
|
-
else if (node.children !== undefined) {
|
|
15
|
-
for (const child of node.children) {
|
|
16
|
-
el.append(createSvgElement(doc, child));
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return el;
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* Materializes a frame into `target` as live DOM.
|
|
23
|
-
*
|
|
24
|
-
* ponytail: clear-and-rebuild per frame; upgrade to keyed reconciliation
|
|
25
|
-
* (instance ids are stable keys) inside this sink if it ever gets slow.
|
|
26
|
-
*/
|
|
27
|
-
export const SvgDomRenderer = Renderer.make()("SvgDomRenderer", {
|
|
28
|
-
render: (entities, config, meta) => Effect.gen(function* () {
|
|
29
|
-
const doc = config.target.ownerDocument;
|
|
30
|
-
const width = config.width ?? meta.width;
|
|
31
|
-
const height = config.height ?? meta.height;
|
|
32
|
-
const root = doc.createElementNS(SVG_NS, "svg");
|
|
33
|
-
root.setAttribute("width", String(width));
|
|
34
|
-
root.setAttribute("height", String(height));
|
|
35
|
-
root.append(createSvgElement(doc, {
|
|
36
|
-
tag: "rect",
|
|
37
|
-
props: {
|
|
38
|
-
width: "100%",
|
|
39
|
-
height: "100%",
|
|
40
|
-
fill: meta.backgroundColor,
|
|
41
|
-
},
|
|
42
|
-
}));
|
|
43
|
-
// each top-level layer gets the camera scaled by its own depth
|
|
44
|
-
for (const { render, entry } of entities) {
|
|
45
|
-
const transform = layerTransform(meta.camera, depthOf(entry.data), width, height);
|
|
46
|
-
root.append(createSvgElement(doc, wrapLayer(yield* render, transform)));
|
|
47
|
-
}
|
|
48
|
-
config.target.replaceChildren(root);
|
|
49
|
-
}),
|
|
50
|
-
});
|
package/dist/svg/SvgNode.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Serialized SVG node — the data contract between entity renderers and
|
|
3
|
-
* sinks. Entity renderers return descriptions; sinks own materialization
|
|
4
|
-
* (string folding, DOM creation, namespaces).
|
|
5
|
-
*/
|
|
6
|
-
export interface SvgNode {
|
|
7
|
-
readonly tag: string;
|
|
8
|
-
readonly props: Record<string, string | number>;
|
|
9
|
-
/** nested nodes, or a string for text content */
|
|
10
|
-
readonly children?: ReadonlyArray<SvgNode> | string;
|
|
11
|
-
}
|
|
12
|
-
export declare const SVG_NS = "http://www.w3.org/2000/svg";
|
|
13
|
-
export declare const vnodeToString: (node: SvgNode) => string;
|
package/dist/svg/SvgNode.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export const SVG_NS = "http://www.w3.org/2000/svg";
|
|
2
|
-
const escapeAttr = (value) => String(value)
|
|
3
|
-
.replaceAll("&", "&")
|
|
4
|
-
.replaceAll('"', """)
|
|
5
|
-
.replaceAll("<", "<");
|
|
6
|
-
const escapeText = (value) => value.replaceAll("&", "&").replaceAll("<", "<");
|
|
7
|
-
export const vnodeToString = (node) => {
|
|
8
|
-
const props = Object.entries(node.props)
|
|
9
|
-
.map(([key, value]) => ` ${key}="${escapeAttr(value)}"`)
|
|
10
|
-
.join("");
|
|
11
|
-
if (node.children === undefined) {
|
|
12
|
-
return `<${node.tag}${props} />`;
|
|
13
|
-
}
|
|
14
|
-
const children = typeof node.children === "string"
|
|
15
|
-
? escapeText(node.children)
|
|
16
|
-
: node.children.map(vnodeToString).join("");
|
|
17
|
-
return `<${node.tag}${props}>${children}</${node.tag}>`;
|
|
18
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
|
-
import * as Renderer from "../Renderer";
|
|
3
|
-
import { type SvgNode } from "./SvgNode";
|
|
4
|
-
export interface SvgConfig {
|
|
5
|
-
/** viewport size — defaults to the frame's own width/height metadata */
|
|
6
|
-
readonly width?: number;
|
|
7
|
-
readonly height?: number;
|
|
8
|
-
}
|
|
9
|
-
/** Folds a frame into a single self-contained SVG document string. */
|
|
10
|
-
export declare const SvgRenderer: {
|
|
11
|
-
Context: import("effect/Context").Service<{
|
|
12
|
-
render: <const Entities extends import("../Entity").AnyEntity>(frame: import("../Scene").Frame<Entities>, config: SvgConfig) => Effect.Effect<string, never, Entities extends import("../Entity").AnyEntity ? { [K in Entities as K["name"]]: Renderer.EntityRenderer<`SvgRenderer/${K["name"]}`, SvgNode, K, never, never>; }[Entities["name"]] : never>;
|
|
13
|
-
}, {
|
|
14
|
-
render: <const Entities extends import("../Entity").AnyEntity>(frame: import("../Scene").Frame<Entities>, config: SvgConfig) => Effect.Effect<string, never, Entities extends import("../Entity").AnyEntity ? { [K in Entities as K["name"]]: Renderer.EntityRenderer<`SvgRenderer/${K["name"]}`, SvgNode, K, never, never>; }[Entities["name"]] : never>;
|
|
15
|
-
}>;
|
|
16
|
-
layer: import("effect/Layer").Layer<{
|
|
17
|
-
render: <const Entities extends import("../Entity").AnyEntity>(frame: import("../Scene").Frame<Entities>, config: SvgConfig) => Effect.Effect<string, never, Entities extends import("../Entity").AnyEntity ? { [K in Entities as K["name"]]: Renderer.EntityRenderer<`SvgRenderer/${K["name"]}`, SvgNode, K, never, never>; }[Entities["name"]] : never>;
|
|
18
|
-
}, never, never>;
|
|
19
|
-
makeEntityRendererContext: <const Ent extends import("../Entity").AnyEntity, E = never, R = never>(entity: Ent) => import("effect/Context").Service<Renderer.EntityRenderer<`SvgRenderer/${Ent["name"]}`, SvgNode, Ent, E, R>, Renderer.EntityRenderer<`SvgRenderer/${Ent["name"]}`, SvgNode, Ent, E, R>>;
|
|
20
|
-
makeEntityRendererService: <const Ent extends import("../Entity").AnyEntity, E = never, R = never>(entity: Ent, render: Renderer.RenderFunction<SvgNode, Ent, E, R>) => Renderer.EntityRenderer<`SvgRenderer/${Ent["name"]}`, SvgNode, Ent, E, R>;
|
|
21
|
-
makeEntityRendererLayer: <const Ent extends import("../Entity").AnyEntity, E = never, R = never>(entity: Ent, render: Renderer.RenderFunction<SvgNode, Ent, E, R>) => import("effect/Layer").Layer<Renderer.EntityRenderer<`SvgRenderer/${Ent["name"]}`, SvgNode, Ent, E, R>, never, never>;
|
|
22
|
-
};
|
package/dist/svg/SvgRenderer.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
|
-
import * as Renderer from "../Renderer";
|
|
3
|
-
import { depthOf, layerTransform, wrapLayer } from "./camera";
|
|
4
|
-
import { SVG_NS, vnodeToString } from "./SvgNode";
|
|
5
|
-
/** Folds a frame into a single self-contained SVG document string. */
|
|
6
|
-
export const SvgRenderer = Renderer.make()("SvgRenderer", {
|
|
7
|
-
render: (entities, config, meta) => Effect.gen(function* () {
|
|
8
|
-
const width = config.width ?? meta.width;
|
|
9
|
-
const height = config.height ?? meta.height;
|
|
10
|
-
let svg = `<svg xmlns="${SVG_NS}" width="${width}" height="${height}">`;
|
|
11
|
-
// a rect, not a style attr — survives rasterizers that ignore CSS
|
|
12
|
-
svg += `<rect width="100%" height="100%" fill="${meta.backgroundColor}"/>`;
|
|
13
|
-
// each top-level layer gets the camera scaled by its own depth
|
|
14
|
-
for (const { render, entry } of entities) {
|
|
15
|
-
const transform = layerTransform(meta.camera, depthOf(entry.data), width, height);
|
|
16
|
-
svg += vnodeToString(wrapLayer(yield* render, transform));
|
|
17
|
-
}
|
|
18
|
-
return `${svg}</svg>`;
|
|
19
|
-
}),
|
|
20
|
-
});
|
package/dist/svg/camera.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { FrameMeta } from "../Renderer";
|
|
2
|
-
import type { SvgNode } from "./SvgNode";
|
|
3
|
-
/**
|
|
4
|
-
* The camera as an SVG transform for one top-level layer, scaled by that
|
|
5
|
-
* layer's `depth` (pan AND zoom together). `depth` lerps the layer's
|
|
6
|
-
* transform from identity toward the full camera:
|
|
7
|
-
*
|
|
8
|
-
* depth 1 → full camera depth 0 → identity (screen-fixed HUD)
|
|
9
|
-
* depth d → 1 + (zoom-1)*d zoom, d*pan translation
|
|
10
|
-
*
|
|
11
|
-
* Zoom is about the viewport center so it reads as "zoom into the middle of
|
|
12
|
-
* the shot", not the world origin. Returns "" when the layer transform is
|
|
13
|
-
* identity, so identity cameras add no attribute (output byte-identical to
|
|
14
|
-
* before the camera existed).
|
|
15
|
-
*/
|
|
16
|
-
export declare const layerTransform: (camera: FrameMeta["camera"], depth: number, width: number, height: number) => string;
|
|
17
|
-
/** `depth` for a top-level layer's data (a Layer carries it; others = 1). */
|
|
18
|
-
export declare const depthOf: (data: unknown) => number;
|
|
19
|
-
/** Wrap a rendered layer node in a camera `<g>`, or return it unwrapped
|
|
20
|
-
* when the transform is identity (keeps the common case flat). */
|
|
21
|
-
export declare const wrapLayer: (node: SvgNode, transform: string) => SvgNode;
|
package/dist/svg/camera.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The camera as an SVG transform for one top-level layer, scaled by that
|
|
3
|
-
* layer's `depth` (pan AND zoom together). `depth` lerps the layer's
|
|
4
|
-
* transform from identity toward the full camera:
|
|
5
|
-
*
|
|
6
|
-
* depth 1 → full camera depth 0 → identity (screen-fixed HUD)
|
|
7
|
-
* depth d → 1 + (zoom-1)*d zoom, d*pan translation
|
|
8
|
-
*
|
|
9
|
-
* Zoom is about the viewport center so it reads as "zoom into the middle of
|
|
10
|
-
* the shot", not the world origin. Returns "" when the layer transform is
|
|
11
|
-
* identity, so identity cameras add no attribute (output byte-identical to
|
|
12
|
-
* before the camera existed).
|
|
13
|
-
*/
|
|
14
|
-
export const layerTransform = (camera, depth, width, height) => {
|
|
15
|
-
const layerZoom = 1 + (camera.zoom - 1) * depth;
|
|
16
|
-
const panX = camera.x * depth;
|
|
17
|
-
const panY = camera.y * depth;
|
|
18
|
-
if (layerZoom === 1 && panX === 0 && panY === 0) {
|
|
19
|
-
return "";
|
|
20
|
-
}
|
|
21
|
-
const cx = width / 2;
|
|
22
|
-
const cy = height / 2;
|
|
23
|
-
// pan first, then zoom about the viewport center: reads right-to-left as
|
|
24
|
-
// translate(-pan) → recenter → scale → uncenter applied to the layer
|
|
25
|
-
const parts = [];
|
|
26
|
-
if (layerZoom !== 1) {
|
|
27
|
-
parts.push(`translate(${cx} ${cy})`, `scale(${layerZoom})`, `translate(${-cx} ${-cy})`);
|
|
28
|
-
}
|
|
29
|
-
if (panX !== 0 || panY !== 0) {
|
|
30
|
-
parts.push(`translate(${-panX} ${-panY})`);
|
|
31
|
-
}
|
|
32
|
-
return parts.join(" ");
|
|
33
|
-
};
|
|
34
|
-
/** `depth` for a top-level layer's data (a Layer carries it; others = 1). */
|
|
35
|
-
export const depthOf = (data) => {
|
|
36
|
-
const depth = data?.depth;
|
|
37
|
-
return typeof depth === "number" ? depth : 1;
|
|
38
|
-
};
|
|
39
|
-
/** Wrap a rendered layer node in a camera `<g>`, or return it unwrapped
|
|
40
|
-
* when the transform is identity (keeps the common case flat). */
|
|
41
|
-
export const wrapLayer = (node, transform) => transform === ""
|
|
42
|
-
? node
|
|
43
|
-
: { tag: "g", props: { transform }, children: [node] };
|
package/dist/svg/index.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { entityRendererLayer, layer } from "./layers";
|
|
2
|
-
export { type SvgDomConfig, SvgDomRenderer } from "./SvgDomRenderer";
|
|
3
|
-
export { SVG_NS, type SvgNode, vnodeToString } from "./SvgNode";
|
|
4
|
-
export { type SvgConfig, SvgRenderer } from "./SvgRenderer";
|
|
5
|
-
export * as shapes from "./shapes";
|
|
6
|
-
export { shapesLayer } from "./shapes";
|
package/dist/svg/index.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { entityRendererLayer, layer } from "./layers";
|
|
2
|
-
export { SvgDomRenderer } from "./SvgDomRenderer";
|
|
3
|
-
export { SVG_NS, vnodeToString } from "./SvgNode";
|
|
4
|
-
export { SvgRenderer } from "./SvgRenderer";
|
|
5
|
-
export * as shapes from "./shapes";
|
|
6
|
-
export { shapesLayer } from "./shapes";
|
package/dist/svg/layers.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Layer } from "effect";
|
|
2
|
-
import type * as Entity from "../Entity";
|
|
3
|
-
import type * as Renderer from "../Renderer";
|
|
4
|
-
import type { SvgNode } from "./SvgNode";
|
|
5
|
-
/**
|
|
6
|
-
* Register one SvgNode render function for an entity with BOTH sinks.
|
|
7
|
-
*
|
|
8
|
-
* Entity renderer context keys are per renderer family
|
|
9
|
-
* (`SvgRenderer/<name>` and `SvgDomRenderer/<name>`), so sharing a render
|
|
10
|
-
* function across sinks means registering it twice — this does both.
|
|
11
|
-
*/
|
|
12
|
-
export declare const entityRendererLayer: <const Ent extends Entity.AnyEntity>(entity: Ent, render: Renderer.RenderFunction<SvgNode, Ent>) => Layer.Layer<Renderer.EntityRenderer<`SvgDomRenderer/${Ent["name"]}`, SvgNode, Ent, never, never> | Renderer.EntityRenderer<`SvgRenderer/${Ent["name"]}`, SvgNode, Ent, never, never>, never, never>;
|
|
13
|
-
/** Both sinks' frame renderers, ready to provide. */
|
|
14
|
-
export declare const layer: Layer.Layer<{
|
|
15
|
-
render: <const Entities extends Entity.AnyEntity>(frame: import("../Scene").Frame<Entities>, config: import("./SvgRenderer").SvgConfig) => import("effect/Effect").Effect<string, never, Entities extends Entity.AnyEntity ? { [K in Entities as K["name"]]: Renderer.EntityRenderer<`SvgRenderer/${K["name"]}`, SvgNode, K, never, never>; }[Entities["name"]] : never>;
|
|
16
|
-
} | {
|
|
17
|
-
render: <const Entities extends Entity.AnyEntity>(frame: import("../Scene").Frame<Entities>, config: import("./SvgDomRenderer").SvgDomConfig) => import("effect/Effect").Effect<void, never, Entities extends Entity.AnyEntity ? { [K in Entities as K["name"]]: Renderer.EntityRenderer<`SvgDomRenderer/${K["name"]}`, SvgNode, K, never, never>; }[Entities["name"]] : never>;
|
|
18
|
-
}, never, never>;
|
package/dist/svg/layers.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Layer } from "effect";
|
|
2
|
-
import { SvgDomRenderer } from "./SvgDomRenderer";
|
|
3
|
-
import { SvgRenderer } from "./SvgRenderer";
|
|
4
|
-
/**
|
|
5
|
-
* Register one SvgNode render function for an entity with BOTH sinks.
|
|
6
|
-
*
|
|
7
|
-
* Entity renderer context keys are per renderer family
|
|
8
|
-
* (`SvgRenderer/<name>` and `SvgDomRenderer/<name>`), so sharing a render
|
|
9
|
-
* function across sinks means registering it twice — this does both.
|
|
10
|
-
*/
|
|
11
|
-
export const entityRendererLayer = (entity, render) => Layer.mergeAll(SvgRenderer.makeEntityRendererLayer(entity, render), SvgDomRenderer.makeEntityRendererLayer(entity, render));
|
|
12
|
-
/** Both sinks' frame renderers, ready to provide. */
|
|
13
|
-
export const layer = Layer.mergeAll(SvgRenderer.layer, SvgDomRenderer.layer);
|