effect-motion 0.4.0 → 0.5.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/README.md +2 -2
- package/dist/Camera.d.ts +186 -49
- package/dist/Camera.js +343 -76
- package/dist/Color.d.ts +101 -1
- package/dist/Color.js +101 -1
- package/dist/EffectMotionError.d.ts +17 -0
- package/dist/EffectMotionError.js +17 -0
- package/dist/Entity.d.ts +682 -38
- package/dist/Entity.js +271 -27
- package/dist/Font.d.ts +108 -0
- package/dist/Font.js +95 -0
- package/dist/Image.d.ts +71 -0
- package/dist/Image.js +50 -0
- package/dist/Instance.d.ts +73 -11
- package/dist/Instance.js +44 -11
- package/dist/Motion.d.ts +328 -53
- package/dist/Motion.js +278 -46
- package/dist/Physics.d.ts +154 -20
- package/dist/Physics.js +92 -11
- package/dist/Projection.d.ts +37 -132
- package/dist/Projection.js +33 -292
- package/dist/Resource.d.ts +26 -0
- package/dist/Resource.js +41 -0
- package/dist/Runner.d.ts +639 -350
- package/dist/Runner.js +197 -150
- package/dist/Scene.d.ts +603 -175
- package/dist/Scene.js +596 -112
- package/dist/Timing.d.ts +170 -13
- package/dist/Timing.js +124 -6
- package/dist/Tree.d.ts +39 -0
- package/dist/Tree.js +127 -0
- package/dist/index.d.ts +54 -5
- package/dist/index.js +56 -5
- package/dist/particles/Particle.d.ts +2 -2
- package/dist/particles/ParticleField.d.ts +8 -6
- package/dist/particles/ParticleField.js +8 -9
- package/dist/particles/constructors.d.ts +5 -6
- package/dist/particles/constructors.js +8 -3
- package/dist/particles/legacy.d.ts +58 -0
- package/dist/particles/legacy.js +46 -0
- package/dist/particles/simulate.js +11 -3
- package/dist/particles/step.js +14 -10
- package/dist/types.d.ts +5 -0
- package/dist/types.js +1 -0
- package/package.json +2 -4
- package/dist/CameraHelpers.d.ts +0 -70
- package/dist/CameraHelpers.js +0 -239
- package/dist/CanvasExporter.d.ts +0 -12
- package/dist/CanvasExporter.js +0 -40
- package/dist/Fonts.d.ts +0 -41
- package/dist/Fonts.js +0 -27
- package/dist/Images.d.ts +0 -33
- package/dist/Images.js +0 -24
- package/dist/PngExporter.d.ts +0 -6
- package/dist/PngExporter.js +0 -85
- package/dist/Renderer.d.ts +0 -118
- package/dist/Renderer.js +0 -508
- package/dist/Shapes.d.ts +0 -11
- package/dist/Shapes.js +0 -11
- package/dist/demo.d.ts +0 -3
- package/dist/demo.js +0 -71
- package/dist/render/dof.d.ts +0 -27
- package/dist/render/dof.js +0 -37
- package/dist/render/paint.d.ts +0 -30
- package/dist/render/paint.js +0 -36
- package/dist/render/shapes.d.ts +0 -42
- package/dist/render/shapes.js +0 -310
- package/dist/shapes/Circle.d.ts +0 -32
- package/dist/shapes/Circle.js +0 -9
- package/dist/shapes/Ellipse.d.ts +0 -35
- package/dist/shapes/Ellipse.js +0 -10
- package/dist/shapes/Group.d.ts +0 -126
- package/dist/shapes/Group.js +0 -89
- package/dist/shapes/Hud.d.ts +0 -38
- package/dist/shapes/Hud.js +0 -35
- package/dist/shapes/Image.d.ts +0 -45
- package/dist/shapes/Image.js +0 -28
- package/dist/shapes/Line.d.ts +0 -47
- package/dist/shapes/Line.js +0 -35
- package/dist/shapes/Path.d.ts +0 -107
- package/dist/shapes/Path.js +0 -32
- package/dist/shapes/Rect.d.ts +0 -51
- package/dist/shapes/Rect.js +0 -22
- package/dist/shapes/Shape2D.d.ts +0 -49
- package/dist/shapes/Shape2D.js +0 -52
- package/dist/shapes/Shapes.d.ts +0 -11
- package/dist/shapes/Shapes.js +0 -11
- package/dist/shapes/Square.d.ts +0 -32
- package/dist/shapes/Square.js +0 -11
- package/dist/shapes/Text.d.ts +0 -51
- package/dist/shapes/Text.js +0 -24
package/dist/render/paint.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { OwnedPaint, ThorvgException } from "@effect-motion/thorvg";
|
|
2
|
-
import * as Tvg from "@effect-motion/thorvg";
|
|
3
|
-
import * as Effect from "effect/Effect";
|
|
4
|
-
import type * as Scope from "effect/Scope";
|
|
5
|
-
import * as Color from "../Color.js";
|
|
6
|
-
import type { PaintProjection } from "../Renderer.js";
|
|
7
|
-
/**
|
|
8
|
-
* Shared paint helpers: style application, projection, and the attach step
|
|
9
|
-
* every leaf paint function runs. Keeping the ThorVG call sequence in one
|
|
10
|
-
* place means each shape's paint function is just "make geometry, then
|
|
11
|
-
* `finishPaint`".
|
|
12
|
-
*/
|
|
13
|
-
/** The style fields a filled/stroked shape carries in its data. */
|
|
14
|
-
export interface StyleData {
|
|
15
|
-
readonly fill?: Color.Color;
|
|
16
|
-
readonly stroke?: Color.Color;
|
|
17
|
-
readonly strokeWidth?: number;
|
|
18
|
-
readonly opacity: number;
|
|
19
|
-
}
|
|
20
|
-
/** Apply fill/stroke/opacity from shape data onto a ThorVG shape paint. */
|
|
21
|
-
export declare const applyStyle: (shape: OwnedPaint, data: StyleData) => Effect.Effect<void, ThorvgException, Tvg.ThorvgWasm>;
|
|
22
|
-
/**
|
|
23
|
-
* Apply the billboard projection affine to a shape, then add it to the scene.
|
|
24
|
-
* The identity affine (resting camera, z=0) is skipped so plain-2D scenes
|
|
25
|
-
* issue the minimal draw calls. Ownership transfers to the scene on add.
|
|
26
|
-
*
|
|
27
|
-
* Callers must NOT have applied any translate/rotate/scale to the shape —
|
|
28
|
-
* `setTransform` is the single, final transform (design D3).
|
|
29
|
-
*/
|
|
30
|
-
export declare const finishPaint: (shape: OwnedPaint, scene: OwnedPaint, projection: PaintProjection) => Effect.Effect<void, ThorvgException, Tvg.ThorvgWasm | Scope.Scope>;
|
package/dist/render/paint.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import * as Tvg from "@effect-motion/thorvg";
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
-
import * as Color from "../Color.js";
|
|
4
|
-
/** Apply fill/stroke/opacity from shape data onto a ThorVG shape paint. */
|
|
5
|
-
export const applyStyle = (shape, data) => Effect.gen(function* () {
|
|
6
|
-
if (data.fill !== undefined) {
|
|
7
|
-
const { r, g, b, a } = Color.bytes(data.fill);
|
|
8
|
-
yield* Tvg.Shape.setFillColor(shape, r, g, b, a);
|
|
9
|
-
}
|
|
10
|
-
if (data.stroke !== undefined) {
|
|
11
|
-
const { r, g, b, a } = Color.bytes(data.stroke);
|
|
12
|
-
yield* Tvg.Shape.setStrokeColor(shape, r, g, b, a);
|
|
13
|
-
}
|
|
14
|
-
if (data.strokeWidth !== undefined) {
|
|
15
|
-
yield* Tvg.Shape.setStrokeWidth(shape, data.strokeWidth);
|
|
16
|
-
}
|
|
17
|
-
// opacity 1 is the ThorVG default; only set when it changes something
|
|
18
|
-
if (data.opacity !== 1) {
|
|
19
|
-
yield* Tvg.Paint.setOpacity(shape, Math.round(data.opacity * 255));
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
const isIdentity = (m) => m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0;
|
|
23
|
-
/**
|
|
24
|
-
* Apply the billboard projection affine to a shape, then add it to the scene.
|
|
25
|
-
* The identity affine (resting camera, z=0) is skipped so plain-2D scenes
|
|
26
|
-
* issue the minimal draw calls. Ownership transfers to the scene on add.
|
|
27
|
-
*
|
|
28
|
-
* Callers must NOT have applied any translate/rotate/scale to the shape —
|
|
29
|
-
* `setTransform` is the single, final transform (design D3).
|
|
30
|
-
*/
|
|
31
|
-
export const finishPaint = (shape, scene, projection) => Effect.gen(function* () {
|
|
32
|
-
if (!isIdentity(projection.screen)) {
|
|
33
|
-
yield* Tvg.Paint.setTransform(shape, projection.screen);
|
|
34
|
-
}
|
|
35
|
-
yield* Tvg.Scene.add(scene, shape);
|
|
36
|
-
});
|
package/dist/render/shapes.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { ParticleField } from "../particles/ParticleField.js";
|
|
2
|
-
import type { PaintFunction, PaintFunctions } from "../Renderer.js";
|
|
3
|
-
import * as Circle from "../shapes/Circle.js";
|
|
4
|
-
import * as Ellipse from "../shapes/Ellipse.js";
|
|
5
|
-
import * as Group from "../shapes/Group.js";
|
|
6
|
-
import * as Hud from "../shapes/Hud.js";
|
|
7
|
-
import * as Image from "../shapes/Image.js";
|
|
8
|
-
import * as Line from "../shapes/Line.js";
|
|
9
|
-
import * as Path from "../shapes/Path.js";
|
|
10
|
-
import * as Rect from "../shapes/Rect.js";
|
|
11
|
-
import * as Square from "../shapes/Square.js";
|
|
12
|
-
import * as Text from "../shapes/Text.js";
|
|
13
|
-
/**
|
|
14
|
-
* ThorVG paint functions for the built-in shapes — this file is the coverage
|
|
15
|
-
* manifest: `paints` is typed `PaintFunctions<...>`, so a built-in missing a
|
|
16
|
-
* paint function is a type error, not a runtime surprise.
|
|
17
|
-
*/
|
|
18
|
-
export declare const circle: PaintFunction<typeof Circle.Circle>;
|
|
19
|
-
export declare const rect: PaintFunction<typeof Rect.Rect>;
|
|
20
|
-
export declare const square: PaintFunction<typeof Square.Square>;
|
|
21
|
-
export declare const ellipse: PaintFunction<typeof Ellipse.Ellipse>;
|
|
22
|
-
export declare const line: PaintFunction<typeof Line.Line>;
|
|
23
|
-
export declare const path: PaintFunction<typeof Path.Path>;
|
|
24
|
-
export declare const group: PaintFunction<typeof Group.Group>;
|
|
25
|
-
export declare const hud: PaintFunction<typeof Hud.Hud>;
|
|
26
|
-
export declare const image: PaintFunction<typeof Image.Image>;
|
|
27
|
-
export declare const text: PaintFunction<typeof Text.Text>;
|
|
28
|
-
/**
|
|
29
|
-
* ParticleField: live particles batched into one shape PER (color, opacity)
|
|
30
|
-
* bucket — ThorVG fills a shape uniformly, so particles that share a color and
|
|
31
|
-
* (quantized) over-life opacity are appended into the same shape and filled
|
|
32
|
-
* once. Sized/faded by the over-life curves; dead slots emit nothing.
|
|
33
|
-
* ponytail: one shape per distinct (color, opacity-bucket) — far fewer than one
|
|
34
|
-
* per particle, but a fully instanced path is the upgrade if this is a wall.
|
|
35
|
-
*/
|
|
36
|
-
export declare const particleField: PaintFunction<typeof ParticleField>;
|
|
37
|
-
/**
|
|
38
|
-
* The exhaustive paint-function map for every built-in entity. Typed
|
|
39
|
-
* `PaintFunctions<...>` so a missing built-in fails to type-check (the old
|
|
40
|
-
* "coverage manifest" guarantee, without a Context registry).
|
|
41
|
-
*/
|
|
42
|
-
export declare const builtinPaints: PaintFunctions<typeof Circle.Circle | typeof Rect.Rect | typeof Square.Square | typeof Ellipse.Ellipse | typeof Line.Line | typeof Path.Path | typeof Group.Group | typeof Hud.Hud | typeof Text.Text | typeof Image.Image | typeof ParticleField>;
|
package/dist/render/shapes.js
DELETED
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
import * as Tvg from "@effect-motion/thorvg";
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
-
import * as Color from "../Color.js";
|
|
4
|
-
import { renderOpacity, renderSize } from "../particles/overLife.js";
|
|
5
|
-
import { ParticleField } from "../particles/ParticleField.js";
|
|
6
|
-
import * as Circle from "../shapes/Circle.js";
|
|
7
|
-
import * as Ellipse from "../shapes/Ellipse.js";
|
|
8
|
-
import * as Group from "../shapes/Group.js";
|
|
9
|
-
import * as Hud from "../shapes/Hud.js";
|
|
10
|
-
import * as Image from "../shapes/Image.js";
|
|
11
|
-
import * as Line from "../shapes/Line.js";
|
|
12
|
-
import * as Path from "../shapes/Path.js";
|
|
13
|
-
import * as Rect from "../shapes/Rect.js";
|
|
14
|
-
import * as Square from "../shapes/Square.js";
|
|
15
|
-
import * as Text from "../shapes/Text.js";
|
|
16
|
-
import { applyStyle, finishPaint } from "./paint.js";
|
|
17
|
-
/**
|
|
18
|
-
* ThorVG paint functions for the built-in shapes — this file is the coverage
|
|
19
|
-
* manifest: `paints` is typed `PaintFunctions<...>`, so a built-in missing a
|
|
20
|
-
* paint function is a type error, not a runtime surprise.
|
|
21
|
-
*/
|
|
22
|
-
export const circle = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
23
|
-
const shape = yield* Tvg.Shape.make();
|
|
24
|
-
yield* Tvg.Shape.appendCircle(shape, data.x, data.y, data.radius, data.radius);
|
|
25
|
-
yield* applyStyle(shape, data);
|
|
26
|
-
yield* finishPaint(shape, scene, projection);
|
|
27
|
-
});
|
|
28
|
-
export const rect = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
29
|
-
const shape = yield* Tvg.Shape.make();
|
|
30
|
-
const quad = projection.quad;
|
|
31
|
-
if (quad !== undefined) {
|
|
32
|
-
// a tilted plane: the vertices are already projected to screen
|
|
33
|
-
// (near-plane-clipped, 3–5 points — true perspective foreshortening,
|
|
34
|
-
// not an affine), so paint the exact polygon and skip the billboard
|
|
35
|
-
// transform
|
|
36
|
-
for (const [i, p] of quad.entries()) {
|
|
37
|
-
yield* i === 0
|
|
38
|
-
? Tvg.Shape.moveTo(shape, p.x, p.y)
|
|
39
|
-
: Tvg.Shape.lineTo(shape, p.x, p.y);
|
|
40
|
-
}
|
|
41
|
-
yield* Tvg.Shape.close(shape);
|
|
42
|
-
yield* applyStyle(shape, data);
|
|
43
|
-
yield* Tvg.Scene.add(scene, shape);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
// SVG lone-radius semantics: one set radius applies to both axes
|
|
47
|
-
yield* Tvg.Shape.appendRect(shape, data.x, data.y, data.width, data.height, data.rx ?? data.ry ?? 0, data.ry ?? data.rx ?? 0);
|
|
48
|
-
yield* applyStyle(shape, data);
|
|
49
|
-
yield* finishPaint(shape, scene, projection);
|
|
50
|
-
});
|
|
51
|
-
export const square = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
52
|
-
const shape = yield* Tvg.Shape.make();
|
|
53
|
-
yield* Tvg.Shape.appendRect(shape, data.x, data.y, data.size, data.size);
|
|
54
|
-
yield* applyStyle(shape, data);
|
|
55
|
-
yield* finishPaint(shape, scene, projection);
|
|
56
|
-
});
|
|
57
|
-
export const ellipse = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
58
|
-
const shape = yield* Tvg.Shape.make();
|
|
59
|
-
yield* Tvg.Shape.appendCircle(shape, data.x, data.y, data.rx, data.ry);
|
|
60
|
-
yield* applyStyle(shape, data);
|
|
61
|
-
yield* finishPaint(shape, scene, projection);
|
|
62
|
-
});
|
|
63
|
-
export const line = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
64
|
-
const shape = yield* Tvg.Shape.make();
|
|
65
|
-
const segment = projection.segment;
|
|
66
|
-
if (segment !== undefined) {
|
|
67
|
-
// skeletal path: the endpoints are already projected to screen
|
|
68
|
-
// (near-plane-clipped, per-endpoint perspective), so draw the
|
|
69
|
-
// exact segment and skip the billboard transform — stroke width
|
|
70
|
-
// scales by the segment's midpoint perspective scale instead
|
|
71
|
-
yield* Tvg.Shape.moveTo(shape, segment[0].x, segment[0].y);
|
|
72
|
-
yield* Tvg.Shape.lineTo(shape, segment[1].x, segment[1].y);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
yield* Tvg.Shape.moveTo(shape, data.x, data.y);
|
|
76
|
-
yield* Tvg.Shape.lineTo(shape, data.x2, data.y2);
|
|
77
|
-
}
|
|
78
|
-
// a line has no fill; stroke defaults come from its schema
|
|
79
|
-
if (data.stroke !== undefined) {
|
|
80
|
-
const { r, g, b, a } = Color.bytes(data.stroke);
|
|
81
|
-
yield* Tvg.Shape.setStrokeColor(shape, r, g, b, a);
|
|
82
|
-
}
|
|
83
|
-
yield* Tvg.Shape.setStrokeWidth(shape, segment !== undefined
|
|
84
|
-
? data.strokeWidth * projection.scale
|
|
85
|
-
: data.strokeWidth);
|
|
86
|
-
if (data.opacity !== 1) {
|
|
87
|
-
yield* Tvg.Paint.setOpacity(shape, Math.round(data.opacity * 255));
|
|
88
|
-
}
|
|
89
|
-
if (segment !== undefined) {
|
|
90
|
-
yield* Tvg.Scene.add(scene, shape);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
yield* finishPaint(shape, scene, projection);
|
|
94
|
-
});
|
|
95
|
-
export const path = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
96
|
-
// the flatten step already split, projected, and near-plane-clipped the
|
|
97
|
-
// command points (see Projection.projectPath) — geometry arrives in
|
|
98
|
-
// screen space, so emit it directly and apply no transform. A path with
|
|
99
|
-
// nothing visible was culled before paint, so subpaths is present here.
|
|
100
|
-
const subpaths = projection.subpaths;
|
|
101
|
-
if (subpaths === undefined) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const shape = yield* Tvg.Shape.make();
|
|
105
|
-
for (const subpath of subpaths) {
|
|
106
|
-
for (const [i, p] of subpath.points.entries()) {
|
|
107
|
-
yield* i === 0
|
|
108
|
-
? Tvg.Shape.moveTo(shape, p.x, p.y)
|
|
109
|
-
: Tvg.Shape.lineTo(shape, p.x, p.y);
|
|
110
|
-
}
|
|
111
|
-
// ponytail: a near-clipped closed subpath strokes along the synthetic
|
|
112
|
-
// clip edge too — accepted; split stroke/fill geometry if it matters
|
|
113
|
-
if (subpath.closed) {
|
|
114
|
-
yield* Tvg.Shape.close(shape);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
yield* applyStyle(shape, data);
|
|
118
|
-
// stroke width scales by the path's perspective scale (its mean visible
|
|
119
|
-
// depth), overriding the unscaled width applyStyle set
|
|
120
|
-
if (data.strokeWidth !== undefined) {
|
|
121
|
-
yield* Tvg.Shape.setStrokeWidth(shape, data.strokeWidth * projection.scale);
|
|
122
|
-
}
|
|
123
|
-
yield* Tvg.Scene.add(scene, shape);
|
|
124
|
-
});
|
|
125
|
-
// A group paints nothing itself: its position has already composed into its
|
|
126
|
-
// children's world coordinates during flatten, and each child was emitted as
|
|
127
|
-
// its own paintable. This is a no-op, present only so the coverage map is
|
|
128
|
-
// exhaustive.
|
|
129
|
-
export const group = () => Effect.void;
|
|
130
|
-
// A Hud is a container like Group: it paints nothing itself — its meaning
|
|
131
|
-
// (identity-camera projection, top-tier order) lives in the renderer's
|
|
132
|
-
// flatten/sort, not in a paint.
|
|
133
|
-
export const hud = () => Effect.void;
|
|
134
|
-
export const image = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
135
|
-
// the session decoded the asset once at open; a missing name (never
|
|
136
|
-
// declared, or its fetch/decode failed) is a soft skip — this instance
|
|
137
|
-
// paints nothing and the rest of the frame renders
|
|
138
|
-
const { pictures } = yield* Tvg.RenderSession;
|
|
139
|
-
const source = pictures.get(data.image);
|
|
140
|
-
if (source === undefined) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
// duplicates share the decoded surface (spike-verified ~42µs each), so
|
|
144
|
-
// per-frame cost is a handle copy, not a decode
|
|
145
|
-
const picture = yield* Tvg.Paint.duplicate(source);
|
|
146
|
-
// declared size scales per axis via the transform — NOT Picture.setSize,
|
|
147
|
-
// which preserves the source aspect (uniform min-factor scale, probe-
|
|
148
|
-
// verified: 8×8 sized to 40×20 renders 20×20). A lone dimension is
|
|
149
|
-
// ignored (aspect math would need the natural size, which frame data
|
|
150
|
-
// never sees).
|
|
151
|
-
let sx = 1;
|
|
152
|
-
let sy = 1;
|
|
153
|
-
if (data.width !== undefined && data.height !== undefined) {
|
|
154
|
-
const natural = yield* Tvg.Picture.getSize(picture);
|
|
155
|
-
if (natural.width > 0 && natural.height > 0) {
|
|
156
|
-
sx = data.width / natural.width;
|
|
157
|
-
sy = data.height / natural.height;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (data.opacity !== 1) {
|
|
161
|
-
yield* Tvg.Paint.setOpacity(picture, Math.round(data.opacity * 255));
|
|
162
|
-
}
|
|
163
|
-
// a picture draws from its own origin, unlike shapes which bake
|
|
164
|
-
// data.x/y into geometry — compose scale-to-declared-size and the local
|
|
165
|
-
// anchor translate into the projection affine so (data.x, data.y) is
|
|
166
|
-
// the top-left, like Rect: screen ∘ translate(x, y) ∘ scale(sx, sy)
|
|
167
|
-
const m = projection.screen;
|
|
168
|
-
const composed = {
|
|
169
|
-
a: m.a * sx,
|
|
170
|
-
b: m.b * sx,
|
|
171
|
-
c: m.c * sy,
|
|
172
|
-
d: m.d * sy,
|
|
173
|
-
e: m.a * data.x + m.c * data.y + m.e,
|
|
174
|
-
f: m.b * data.x + m.d * data.y + m.f,
|
|
175
|
-
};
|
|
176
|
-
const isIdentity = composed.a === 1 &&
|
|
177
|
-
composed.b === 0 &&
|
|
178
|
-
composed.c === 0 &&
|
|
179
|
-
composed.d === 1 &&
|
|
180
|
-
composed.e === 0 &&
|
|
181
|
-
composed.f === 0;
|
|
182
|
-
if (!isIdentity) {
|
|
183
|
-
yield* Tvg.Paint.setTransform(picture, composed);
|
|
184
|
-
}
|
|
185
|
-
yield* Tvg.Scene.add(scene, picture);
|
|
186
|
-
// ponytail: billboard only — a tilted image needs a projective
|
|
187
|
-
// setTransform (full 3×3 bottom row) mapping the projected quad; add
|
|
188
|
-
// when a scene needs a tilted image.
|
|
189
|
-
});
|
|
190
|
-
export const text = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
191
|
-
const glyphs = yield* Tvg.Text.make();
|
|
192
|
-
// setFont fails ("insufficient condition") when the family isn't loaded
|
|
193
|
-
// into the engine yet — a missing/not-yet-fetched font. That must NOT
|
|
194
|
-
// abort the frame (it would blank every other paint too): swallow it so
|
|
195
|
-
// this one text simply doesn't draw and the rest of the frame renders.
|
|
196
|
-
yield* Tvg.Text.setFont(glyphs, data.fontFamily).pipe(Effect.ignore);
|
|
197
|
-
yield* Tvg.Text.setText(glyphs, data.text);
|
|
198
|
-
const { r, g, b } = Color.bytes(data.fill);
|
|
199
|
-
yield* Tvg.Text.setColor(glyphs, r, g, b);
|
|
200
|
-
if (data.opacity !== 1) {
|
|
201
|
-
yield* Tvg.Paint.setOpacity(glyphs, Math.round(data.opacity * 255));
|
|
202
|
-
}
|
|
203
|
-
// ThorVG QUIRK (verified against this build): text inside a nested scene
|
|
204
|
-
// renders ONLY when positioned by a plain `translate` — `set_transform`,
|
|
205
|
-
// `scale`, and `text_align` on a scene-child text all produce nothing. So
|
|
206
|
-
// text can't use the projection's full affine or ThorVG's align. Instead:
|
|
207
|
-
// - fold the perspective scale into the FONT SIZE (setTextSize scales the
|
|
208
|
-
// glyphs; scale-transform doesn't work),
|
|
209
|
-
// - apply textAnchor/baseline as a local offset from an ESTIMATED box,
|
|
210
|
-
// - position with translate(screenX, screenY).
|
|
211
|
-
// ponytail: this handles translate+uniform-scale cameras (the common
|
|
212
|
-
// case). A rotated/sheared camera on text isn't expressed (text stays
|
|
213
|
-
// axis-aligned); revisit if a scene tilts text in 3D.
|
|
214
|
-
const scale = projection.scale > 0 ? projection.scale : 1;
|
|
215
|
-
const size = data.fontSize * scale;
|
|
216
|
-
yield* Tvg.Text.setSize(glyphs, size);
|
|
217
|
-
// estimated text box (proportional font, ~0.6 advance/size; ascent/cap
|
|
218
|
-
// factors) — used because ThorVG align doesn't work on scene-child text
|
|
219
|
-
const estWidth = size * data.text.length * AVG_CHAR_WIDTH;
|
|
220
|
-
const dx = data.textAnchor === "middle"
|
|
221
|
-
? -estWidth / 2
|
|
222
|
-
: data.textAnchor === "end"
|
|
223
|
-
? -estWidth
|
|
224
|
-
: 0;
|
|
225
|
-
// text origin y is the baseline (bottom of glyphs); shift UP by ~half cap
|
|
226
|
-
// height to center (middle), or up by the full ascent so the given point
|
|
227
|
-
// is the TOP (hanging); leave the baseline at y (auto)
|
|
228
|
-
const dy = data.baseline === "middle"
|
|
229
|
-
? -size * CAP_CENTER
|
|
230
|
-
: data.baseline === "hanging"
|
|
231
|
-
? -size * ASCENT
|
|
232
|
-
: 0;
|
|
233
|
-
// where the anchor point (data.x, data.y) lands on screen under the camera
|
|
234
|
-
const m = projection.screen;
|
|
235
|
-
const screenX = m.a * data.x + m.c * data.y + m.e;
|
|
236
|
-
const screenY = m.b * data.x + m.d * data.y + m.f;
|
|
237
|
-
yield* Tvg.Paint.translate(glyphs, screenX + dx, screenY + dy);
|
|
238
|
-
yield* Tvg.Scene.add(scene, glyphs);
|
|
239
|
-
});
|
|
240
|
-
// Text-box estimates (see the `text` paint fn): mean advance / cap-center /
|
|
241
|
-
// ascent as fractions of font size, for the default sans. Used because ThorVG's
|
|
242
|
-
// text align/transform don't work on scene-child text in this build.
|
|
243
|
-
const AVG_CHAR_WIDTH = 0.6;
|
|
244
|
-
const CAP_CENTER = 0.35;
|
|
245
|
-
const ASCENT = 0.8;
|
|
246
|
-
/**
|
|
247
|
-
* ParticleField: live particles batched into one shape PER (color, opacity)
|
|
248
|
-
* bucket — ThorVG fills a shape uniformly, so particles that share a color and
|
|
249
|
-
* (quantized) over-life opacity are appended into the same shape and filled
|
|
250
|
-
* once. Sized/faded by the over-life curves; dead slots emit nothing.
|
|
251
|
-
* ponytail: one shape per distinct (color, opacity-bucket) — far fewer than one
|
|
252
|
-
* per particle, but a fully instanced path is the upgrade if this is a wall.
|
|
253
|
-
*/
|
|
254
|
-
export const particleField = ({ data, scene, projection, }) => Effect.gen(function* () {
|
|
255
|
-
// group live particles by fill color + quantized opacity (24 buckets), so
|
|
256
|
-
// each bucket is one uniformly-filled shape. Circles are appended in the
|
|
257
|
-
// field's local coords (data.x/y offsets the whole field).
|
|
258
|
-
const buckets = new Map();
|
|
259
|
-
for (const p of data.buffer) {
|
|
260
|
-
if (!p.alive) {
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
const r = renderSize(p, data.sizeOverLife);
|
|
264
|
-
if (r <= 0) {
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
const o = renderOpacity(p, data.opacityOverLife) *
|
|
268
|
-
data.opacity;
|
|
269
|
-
const alpha = Math.max(0, Math.min(255, Math.round(o * 255)));
|
|
270
|
-
if (alpha === 0) {
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
// quantize alpha to keep the bucket count bounded (~24 steps)
|
|
274
|
-
const qAlpha = Math.round(alpha / 11) * 11;
|
|
275
|
-
const key = `${Color.toHex(p.color)}\0${qAlpha}`;
|
|
276
|
-
let bucket = buckets.get(key);
|
|
277
|
-
if (bucket === undefined) {
|
|
278
|
-
bucket = { color: p.color, alpha: qAlpha, circles: [] };
|
|
279
|
-
buckets.set(key, bucket);
|
|
280
|
-
}
|
|
281
|
-
bucket.circles.push([data.x + p.x, data.y + p.y, r]);
|
|
282
|
-
}
|
|
283
|
-
for (const { color, alpha, circles } of buckets.values()) {
|
|
284
|
-
const shape = yield* Tvg.Shape.make();
|
|
285
|
-
for (const [cx, cy, r] of circles) {
|
|
286
|
-
yield* Tvg.Shape.appendCircle(shape, cx, cy, r, r);
|
|
287
|
-
}
|
|
288
|
-
const { r, g, b } = Color.bytes(color);
|
|
289
|
-
yield* Tvg.Shape.setFillColor(shape, r, g, b, alpha);
|
|
290
|
-
yield* finishPaint(shape, scene, projection);
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
/**
|
|
294
|
-
* The exhaustive paint-function map for every built-in entity. Typed
|
|
295
|
-
* `PaintFunctions<...>` so a missing built-in fails to type-check (the old
|
|
296
|
-
* "coverage manifest" guarantee, without a Context registry).
|
|
297
|
-
*/
|
|
298
|
-
export const builtinPaints = {
|
|
299
|
-
[Circle.Circle.name]: circle,
|
|
300
|
-
[Rect.Rect.name]: rect,
|
|
301
|
-
[Square.Square.name]: square,
|
|
302
|
-
[Ellipse.Ellipse.name]: ellipse,
|
|
303
|
-
[Line.Line.name]: line,
|
|
304
|
-
[Path.Path.name]: path,
|
|
305
|
-
[Group.Group.name]: group,
|
|
306
|
-
[Hud.Hud.name]: hud,
|
|
307
|
-
[Text.Text.name]: text,
|
|
308
|
-
[Image.Image.name]: image,
|
|
309
|
-
[ParticleField.name]: particleField,
|
|
310
|
-
};
|
package/dist/shapes/Circle.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import * as Entity from "../Entity.js";
|
|
2
|
-
export declare const Circle: Entity.Entity<"shapes/Circle", import("effect/Schema").Struct<{
|
|
3
|
-
x: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
4
|
-
y: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
5
|
-
z: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
6
|
-
opacity: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
7
|
-
fill: import("effect/Schema").withConstructorDefault<typeof import("../Color.js").Color>;
|
|
8
|
-
stroke: import("effect/Schema").optionalKey<typeof import("../Color.js").Color>;
|
|
9
|
-
strokeWidth: import("effect/Schema").optionalKey<import("effect/Schema").Number>;
|
|
10
|
-
radius: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
11
|
-
}>, {
|
|
12
|
-
readonly "~position": Entity.TraitLens<{
|
|
13
|
-
readonly x: number;
|
|
14
|
-
readonly y: number;
|
|
15
|
-
readonly z: number;
|
|
16
|
-
readonly opacity: number;
|
|
17
|
-
readonly fill: import("../Color.js").Color;
|
|
18
|
-
readonly stroke?: import("../Color.js").Color;
|
|
19
|
-
readonly strokeWidth?: number;
|
|
20
|
-
readonly radius: number;
|
|
21
|
-
}, Entity.Position>;
|
|
22
|
-
readonly "~opacity": Entity.TraitLens<{
|
|
23
|
-
readonly x: number;
|
|
24
|
-
readonly y: number;
|
|
25
|
-
readonly z: number;
|
|
26
|
-
readonly opacity: number;
|
|
27
|
-
readonly fill: import("../Color.js").Color;
|
|
28
|
-
readonly stroke?: import("../Color.js").Color;
|
|
29
|
-
readonly strokeWidth?: number;
|
|
30
|
-
readonly radius: number;
|
|
31
|
-
}, number>;
|
|
32
|
-
}>;
|
package/dist/shapes/Circle.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import * as Entity from "../Entity.js";
|
|
2
|
-
import * as Shape2D from "./Shape2D.js";
|
|
3
|
-
export const Circle = Entity.make("shapes/Circle", {
|
|
4
|
-
...Shape2D.filled,
|
|
5
|
-
radius: Shape2D.defaultedNumber(10),
|
|
6
|
-
}, {
|
|
7
|
-
"~position": Shape2D.positionLens(),
|
|
8
|
-
"~opacity": Shape2D.opacityLens(),
|
|
9
|
-
});
|
package/dist/shapes/Ellipse.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import * as Entity from "../Entity.js";
|
|
2
|
-
export declare const Ellipse: Entity.Entity<"shapes/Ellipse", import("effect/Schema").Struct<{
|
|
3
|
-
x: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
4
|
-
y: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
5
|
-
z: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
6
|
-
opacity: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
7
|
-
fill: import("effect/Schema").withConstructorDefault<typeof import("../Color.js").Color>;
|
|
8
|
-
stroke: import("effect/Schema").optionalKey<typeof import("../Color.js").Color>;
|
|
9
|
-
strokeWidth: import("effect/Schema").optionalKey<import("effect/Schema").Number>;
|
|
10
|
-
rx: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
11
|
-
ry: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
12
|
-
}>, {
|
|
13
|
-
readonly "~position": Entity.TraitLens<{
|
|
14
|
-
readonly x: number;
|
|
15
|
-
readonly y: number;
|
|
16
|
-
readonly z: number;
|
|
17
|
-
readonly opacity: number;
|
|
18
|
-
readonly fill: import("../Color.js").Color;
|
|
19
|
-
readonly stroke?: import("../Color.js").Color;
|
|
20
|
-
readonly strokeWidth?: number;
|
|
21
|
-
readonly rx: number;
|
|
22
|
-
readonly ry: number;
|
|
23
|
-
}, Entity.Position>;
|
|
24
|
-
readonly "~opacity": Entity.TraitLens<{
|
|
25
|
-
readonly x: number;
|
|
26
|
-
readonly y: number;
|
|
27
|
-
readonly z: number;
|
|
28
|
-
readonly opacity: number;
|
|
29
|
-
readonly fill: import("../Color.js").Color;
|
|
30
|
-
readonly stroke?: import("../Color.js").Color;
|
|
31
|
-
readonly strokeWidth?: number;
|
|
32
|
-
readonly rx: number;
|
|
33
|
-
readonly ry: number;
|
|
34
|
-
}, number>;
|
|
35
|
-
}>;
|
package/dist/shapes/Ellipse.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import * as Entity from "../Entity.js";
|
|
2
|
-
import * as Shape2D from "./Shape2D.js";
|
|
3
|
-
export const Ellipse = Entity.make("shapes/Ellipse", {
|
|
4
|
-
...Shape2D.filled,
|
|
5
|
-
rx: Shape2D.defaultedNumber(20),
|
|
6
|
-
ry: Shape2D.defaultedNumber(10),
|
|
7
|
-
}, {
|
|
8
|
-
"~position": Shape2D.positionLens(),
|
|
9
|
-
"~opacity": Shape2D.opacityLens(),
|
|
10
|
-
});
|
package/dist/shapes/Group.d.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import * as Schema from "effect/Schema";
|
|
2
|
-
import * as Color from "../Color.js";
|
|
3
|
-
import * as Entity from "../Entity.js";
|
|
4
|
-
export declare const TransformMatrix: Schema.Struct<{
|
|
5
|
-
readonly a: Schema.Number;
|
|
6
|
-
readonly b: Schema.Number;
|
|
7
|
-
readonly c: Schema.Number;
|
|
8
|
-
readonly d: Schema.Number;
|
|
9
|
-
readonly e: Schema.Number;
|
|
10
|
-
readonly f: Schema.Number;
|
|
11
|
-
}>;
|
|
12
|
-
export type TransformMatrix = typeof TransformMatrix.Type;
|
|
13
|
-
export declare const identityTransform: {
|
|
14
|
-
a: number;
|
|
15
|
-
b: number;
|
|
16
|
-
c: number;
|
|
17
|
-
d: number;
|
|
18
|
-
e: number;
|
|
19
|
-
f: number;
|
|
20
|
-
};
|
|
21
|
-
export declare const multiplyTransforms: (left: TransformMatrix, right: TransformMatrix) => TransformMatrix;
|
|
22
|
-
export declare const TransformOperation: Schema.TaggedUnion<{
|
|
23
|
-
readonly "transform/matrix": Schema.TaggedStruct<"transform/matrix", {
|
|
24
|
-
readonly a: Schema.Number;
|
|
25
|
-
readonly b: Schema.Number;
|
|
26
|
-
readonly c: Schema.Number;
|
|
27
|
-
readonly d: Schema.Number;
|
|
28
|
-
readonly e: Schema.Number;
|
|
29
|
-
readonly f: Schema.Number;
|
|
30
|
-
}>;
|
|
31
|
-
readonly "transform/scale": Schema.TaggedStruct<"transform/scale", {
|
|
32
|
-
readonly x: Schema.Number;
|
|
33
|
-
readonly y: Schema.Number;
|
|
34
|
-
}>;
|
|
35
|
-
readonly "transform/translate": Schema.TaggedStruct<"transform/translate", {
|
|
36
|
-
readonly x: Schema.Number;
|
|
37
|
-
readonly y: Schema.Number;
|
|
38
|
-
}>;
|
|
39
|
-
}>;
|
|
40
|
-
export declare const Transform: Schema.decodeTo<Schema.Struct<{
|
|
41
|
-
readonly a: Schema.Number;
|
|
42
|
-
readonly b: Schema.Number;
|
|
43
|
-
readonly c: Schema.Number;
|
|
44
|
-
readonly d: Schema.Number;
|
|
45
|
-
readonly e: Schema.Number;
|
|
46
|
-
readonly f: Schema.Number;
|
|
47
|
-
}>, Schema.$Array<Schema.TaggedUnion<{
|
|
48
|
-
readonly "transform/matrix": Schema.TaggedStruct<"transform/matrix", {
|
|
49
|
-
readonly a: Schema.Number;
|
|
50
|
-
readonly b: Schema.Number;
|
|
51
|
-
readonly c: Schema.Number;
|
|
52
|
-
readonly d: Schema.Number;
|
|
53
|
-
readonly e: Schema.Number;
|
|
54
|
-
readonly f: Schema.Number;
|
|
55
|
-
}>;
|
|
56
|
-
readonly "transform/scale": Schema.TaggedStruct<"transform/scale", {
|
|
57
|
-
readonly x: Schema.Number;
|
|
58
|
-
readonly y: Schema.Number;
|
|
59
|
-
}>;
|
|
60
|
-
readonly "transform/translate": Schema.TaggedStruct<"transform/translate", {
|
|
61
|
-
readonly x: Schema.Number;
|
|
62
|
-
readonly y: Schema.Number;
|
|
63
|
-
}>;
|
|
64
|
-
}>>, never, never>;
|
|
65
|
-
export declare const Group: Entity.Entity<"shapes/Group", Schema.Struct<{
|
|
66
|
-
x: Schema.withConstructorDefault<Schema.Number>;
|
|
67
|
-
y: Schema.withConstructorDefault<Schema.Number>;
|
|
68
|
-
z: Schema.withConstructorDefault<Schema.Number>;
|
|
69
|
-
opacity: Schema.withConstructorDefault<Schema.Number>;
|
|
70
|
-
transform: Schema.withConstructorDefault<Schema.Struct<{
|
|
71
|
-
readonly a: Schema.Number;
|
|
72
|
-
readonly b: Schema.Number;
|
|
73
|
-
readonly c: Schema.Number;
|
|
74
|
-
readonly d: Schema.Number;
|
|
75
|
-
readonly e: Schema.Number;
|
|
76
|
-
readonly f: Schema.Number;
|
|
77
|
-
}>>;
|
|
78
|
-
width: Schema.optionalKey<Schema.Number>;
|
|
79
|
-
height: Schema.optionalKey<Schema.Number>;
|
|
80
|
-
backgroundColor: Schema.optionalKey<typeof Color.Color>;
|
|
81
|
-
children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
|
|
82
|
-
}>, {
|
|
83
|
-
"~position": Entity.TraitLens<{
|
|
84
|
-
readonly x: number;
|
|
85
|
-
readonly y: number;
|
|
86
|
-
readonly z: number;
|
|
87
|
-
readonly opacity: number;
|
|
88
|
-
readonly transform: Schema.Struct.ReadonlySide<{
|
|
89
|
-
readonly a: Schema.Number;
|
|
90
|
-
readonly b: Schema.Number;
|
|
91
|
-
readonly c: Schema.Number;
|
|
92
|
-
readonly d: Schema.Number;
|
|
93
|
-
readonly e: Schema.Number;
|
|
94
|
-
readonly f: Schema.Number;
|
|
95
|
-
}, "Type">;
|
|
96
|
-
readonly width?: number;
|
|
97
|
-
readonly height?: number;
|
|
98
|
-
readonly backgroundColor?: Color.Color;
|
|
99
|
-
readonly children: readonly string[];
|
|
100
|
-
}, Entity.Position>;
|
|
101
|
-
"~opacity": Entity.TraitLens<{
|
|
102
|
-
readonly x: number;
|
|
103
|
-
readonly y: number;
|
|
104
|
-
readonly z: number;
|
|
105
|
-
readonly opacity: number;
|
|
106
|
-
readonly transform: Schema.Struct.ReadonlySide<{
|
|
107
|
-
readonly a: Schema.Number;
|
|
108
|
-
readonly b: Schema.Number;
|
|
109
|
-
readonly c: Schema.Number;
|
|
110
|
-
readonly d: Schema.Number;
|
|
111
|
-
readonly e: Schema.Number;
|
|
112
|
-
readonly f: Schema.Number;
|
|
113
|
-
}, "Type">;
|
|
114
|
-
readonly width?: number;
|
|
115
|
-
readonly height?: number;
|
|
116
|
-
readonly backgroundColor?: Color.Color;
|
|
117
|
-
readonly children: readonly string[];
|
|
118
|
-
}, number>;
|
|
119
|
-
}>;
|
|
120
|
-
/** The group's x/y position composed outside its normalized local transform. */
|
|
121
|
-
export declare const resolvedTransform: (data: {
|
|
122
|
-
readonly x: number;
|
|
123
|
-
readonly y: number;
|
|
124
|
-
readonly transform: TransformMatrix;
|
|
125
|
-
}) => TransformMatrix;
|
|
126
|
-
export declare const isIdentityTransform: (matrix: TransformMatrix) => boolean;
|