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
|
@@ -0,0 +1,310 @@
|
|
|
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
CHANGED
|
@@ -1,37 +1,32 @@
|
|
|
1
|
-
import * as Entity from "../Entity";
|
|
1
|
+
import * as Entity from "../Entity.js";
|
|
2
2
|
export declare const Circle: Entity.Entity<"shapes/Circle", import("effect/Schema").Struct<{
|
|
3
3
|
x: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
4
4
|
y: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
5
|
+
z: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
5
6
|
opacity: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
6
|
-
fill: import("effect/Schema").withConstructorDefault<import("
|
|
7
|
-
stroke: import("effect/Schema").optionalKey<import("
|
|
7
|
+
fill: import("effect/Schema").withConstructorDefault<typeof import("../Color.js").Color>;
|
|
8
|
+
stroke: import("effect/Schema").optionalKey<typeof import("../Color.js").Color>;
|
|
8
9
|
strokeWidth: import("effect/Schema").optionalKey<import("effect/Schema").Number>;
|
|
9
10
|
radius: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
10
11
|
}>, {
|
|
11
12
|
readonly "~position": Entity.TraitLens<{
|
|
12
13
|
readonly x: number;
|
|
13
14
|
readonly y: number;
|
|
15
|
+
readonly z: number;
|
|
14
16
|
readonly opacity: number;
|
|
15
|
-
readonly fill:
|
|
16
|
-
readonly stroke?:
|
|
17
|
+
readonly fill: import("../Color.js").Color;
|
|
18
|
+
readonly stroke?: import("../Color.js").Color;
|
|
17
19
|
readonly strokeWidth?: number;
|
|
18
20
|
readonly radius: number;
|
|
19
21
|
}, Entity.Position>;
|
|
20
22
|
readonly "~opacity": Entity.TraitLens<{
|
|
21
23
|
readonly x: number;
|
|
22
24
|
readonly y: number;
|
|
25
|
+
readonly z: number;
|
|
23
26
|
readonly opacity: number;
|
|
24
|
-
readonly fill:
|
|
25
|
-
readonly stroke?:
|
|
27
|
+
readonly fill: import("../Color.js").Color;
|
|
28
|
+
readonly stroke?: import("../Color.js").Color;
|
|
26
29
|
readonly strokeWidth?: number;
|
|
27
30
|
readonly radius: number;
|
|
28
31
|
}, number>;
|
|
29
|
-
}, {
|
|
30
|
-
readonly x?: number;
|
|
31
|
-
readonly y?: number;
|
|
32
|
-
readonly opacity?: number;
|
|
33
|
-
readonly fill?: string;
|
|
34
|
-
readonly stroke?: string;
|
|
35
|
-
readonly strokeWidth?: number;
|
|
36
|
-
readonly radius?: number;
|
|
37
32
|
}>;
|
package/dist/shapes/Circle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as Entity from "../Entity";
|
|
2
|
-
import * as Shape2D from "./Shape2D";
|
|
1
|
+
import * as Entity from "../Entity.js";
|
|
2
|
+
import * as Shape2D from "./Shape2D.js";
|
|
3
3
|
export const Circle = Entity.make("shapes/Circle", {
|
|
4
4
|
...Shape2D.filled,
|
|
5
5
|
radius: Shape2D.defaultedNumber(10),
|
package/dist/shapes/Ellipse.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import * as Entity from "../Entity";
|
|
1
|
+
import * as Entity from "../Entity.js";
|
|
2
2
|
export declare const Ellipse: Entity.Entity<"shapes/Ellipse", import("effect/Schema").Struct<{
|
|
3
3
|
x: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
4
4
|
y: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
5
|
+
z: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
5
6
|
opacity: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
6
|
-
fill: import("effect/Schema").withConstructorDefault<import("
|
|
7
|
-
stroke: import("effect/Schema").optionalKey<import("
|
|
7
|
+
fill: import("effect/Schema").withConstructorDefault<typeof import("../Color.js").Color>;
|
|
8
|
+
stroke: import("effect/Schema").optionalKey<typeof import("../Color.js").Color>;
|
|
8
9
|
strokeWidth: import("effect/Schema").optionalKey<import("effect/Schema").Number>;
|
|
9
10
|
rx: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
10
11
|
ry: import("effect/Schema").withConstructorDefault<import("effect/Schema").Number>;
|
|
@@ -12,9 +13,10 @@ export declare const Ellipse: Entity.Entity<"shapes/Ellipse", import("effect/Sch
|
|
|
12
13
|
readonly "~position": Entity.TraitLens<{
|
|
13
14
|
readonly x: number;
|
|
14
15
|
readonly y: number;
|
|
16
|
+
readonly z: number;
|
|
15
17
|
readonly opacity: number;
|
|
16
|
-
readonly fill:
|
|
17
|
-
readonly stroke?:
|
|
18
|
+
readonly fill: import("../Color.js").Color;
|
|
19
|
+
readonly stroke?: import("../Color.js").Color;
|
|
18
20
|
readonly strokeWidth?: number;
|
|
19
21
|
readonly rx: number;
|
|
20
22
|
readonly ry: number;
|
|
@@ -22,20 +24,12 @@ export declare const Ellipse: Entity.Entity<"shapes/Ellipse", import("effect/Sch
|
|
|
22
24
|
readonly "~opacity": Entity.TraitLens<{
|
|
23
25
|
readonly x: number;
|
|
24
26
|
readonly y: number;
|
|
27
|
+
readonly z: number;
|
|
25
28
|
readonly opacity: number;
|
|
26
|
-
readonly fill:
|
|
27
|
-
readonly stroke?:
|
|
29
|
+
readonly fill: import("../Color.js").Color;
|
|
30
|
+
readonly stroke?: import("../Color.js").Color;
|
|
28
31
|
readonly strokeWidth?: number;
|
|
29
32
|
readonly rx: number;
|
|
30
33
|
readonly ry: number;
|
|
31
34
|
}, number>;
|
|
32
|
-
}, {
|
|
33
|
-
readonly x?: number;
|
|
34
|
-
readonly y?: number;
|
|
35
|
-
readonly opacity?: number;
|
|
36
|
-
readonly fill?: string;
|
|
37
|
-
readonly stroke?: string;
|
|
38
|
-
readonly strokeWidth?: number;
|
|
39
|
-
readonly rx?: number;
|
|
40
|
-
readonly ry?: number;
|
|
41
35
|
}>;
|
package/dist/shapes/Ellipse.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as Entity from "../Entity";
|
|
2
|
-
import * as Shape2D from "./Shape2D";
|
|
1
|
+
import * as Entity from "../Entity.js";
|
|
2
|
+
import * as Shape2D from "./Shape2D.js";
|
|
3
3
|
export const Ellipse = Entity.make("shapes/Ellipse", {
|
|
4
4
|
...Shape2D.filled,
|
|
5
5
|
rx: Shape2D.defaultedNumber(20),
|
package/dist/shapes/Group.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
|
-
import * as Entity from "../Entity";
|
|
2
|
+
import * as Entity from "../Entity.js";
|
|
3
3
|
export declare const TransformMatrix: Schema.Struct<{
|
|
4
4
|
readonly a: Schema.Number;
|
|
5
5
|
readonly b: Schema.Number;
|
|
@@ -36,24 +36,6 @@ export declare const TransformOperation: Schema.TaggedUnion<{
|
|
|
36
36
|
readonly y: Schema.Number;
|
|
37
37
|
}>;
|
|
38
38
|
}>;
|
|
39
|
-
declare const TransformOperations: Schema.$Array<Schema.TaggedUnion<{
|
|
40
|
-
readonly "transform/matrix": Schema.TaggedStruct<"transform/matrix", {
|
|
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
|
-
}>;
|
|
48
|
-
readonly "transform/scale": Schema.TaggedStruct<"transform/scale", {
|
|
49
|
-
readonly x: Schema.Number;
|
|
50
|
-
readonly y: Schema.Number;
|
|
51
|
-
}>;
|
|
52
|
-
readonly "transform/translate": Schema.TaggedStruct<"transform/translate", {
|
|
53
|
-
readonly x: Schema.Number;
|
|
54
|
-
readonly y: Schema.Number;
|
|
55
|
-
}>;
|
|
56
|
-
}>>;
|
|
57
39
|
export declare const Transform: Schema.decodeTo<Schema.Struct<{
|
|
58
40
|
readonly a: Schema.Number;
|
|
59
41
|
readonly b: Schema.Number;
|
|
@@ -79,27 +61,10 @@ export declare const Transform: Schema.decodeTo<Schema.Struct<{
|
|
|
79
61
|
readonly y: Schema.Number;
|
|
80
62
|
}>;
|
|
81
63
|
}>>, never, never>;
|
|
82
|
-
declare const fields: {
|
|
83
|
-
x: Schema.withConstructorDefault<Schema.Number>;
|
|
84
|
-
y: Schema.withConstructorDefault<Schema.Number>;
|
|
85
|
-
opacity: Schema.withConstructorDefault<Schema.Number>;
|
|
86
|
-
transform: Schema.withConstructorDefault<Schema.Struct<{
|
|
87
|
-
readonly a: Schema.Number;
|
|
88
|
-
readonly b: Schema.Number;
|
|
89
|
-
readonly c: Schema.Number;
|
|
90
|
-
readonly d: Schema.Number;
|
|
91
|
-
readonly e: Schema.Number;
|
|
92
|
-
readonly f: Schema.Number;
|
|
93
|
-
}>>;
|
|
94
|
-
children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
|
|
95
|
-
};
|
|
96
|
-
type StoredInput = Schema.Struct<typeof fields>["~type.make.in"];
|
|
97
|
-
type GroupInput = Omit<StoredInput, "transform"> & {
|
|
98
|
-
readonly transform?: typeof TransformOperations.Type;
|
|
99
|
-
};
|
|
100
64
|
export declare const Group: Entity.Entity<"shapes/Group", Schema.Struct<{
|
|
101
65
|
x: Schema.withConstructorDefault<Schema.Number>;
|
|
102
66
|
y: Schema.withConstructorDefault<Schema.Number>;
|
|
67
|
+
z: Schema.withConstructorDefault<Schema.Number>;
|
|
103
68
|
opacity: Schema.withConstructorDefault<Schema.Number>;
|
|
104
69
|
transform: Schema.withConstructorDefault<Schema.Struct<{
|
|
105
70
|
readonly a: Schema.Number;
|
|
@@ -114,6 +79,7 @@ export declare const Group: Entity.Entity<"shapes/Group", Schema.Struct<{
|
|
|
114
79
|
"~position": Entity.TraitLens<Schema.Struct.ReadonlySide<{
|
|
115
80
|
x: Schema.withConstructorDefault<Schema.Number>;
|
|
116
81
|
y: Schema.withConstructorDefault<Schema.Number>;
|
|
82
|
+
z: Schema.withConstructorDefault<Schema.Number>;
|
|
117
83
|
opacity: Schema.withConstructorDefault<Schema.Number>;
|
|
118
84
|
transform: Schema.withConstructorDefault<Schema.Struct<{
|
|
119
85
|
readonly a: Schema.Number;
|
|
@@ -128,6 +94,7 @@ export declare const Group: Entity.Entity<"shapes/Group", Schema.Struct<{
|
|
|
128
94
|
"~opacity": Entity.TraitLens<Schema.Struct.ReadonlySide<{
|
|
129
95
|
x: Schema.withConstructorDefault<Schema.Number>;
|
|
130
96
|
y: Schema.withConstructorDefault<Schema.Number>;
|
|
97
|
+
z: Schema.withConstructorDefault<Schema.Number>;
|
|
131
98
|
opacity: Schema.withConstructorDefault<Schema.Number>;
|
|
132
99
|
transform: Schema.withConstructorDefault<Schema.Struct<{
|
|
133
100
|
readonly a: Schema.Number;
|
|
@@ -139,7 +106,7 @@ export declare const Group: Entity.Entity<"shapes/Group", Schema.Struct<{
|
|
|
139
106
|
}>>;
|
|
140
107
|
children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
|
|
141
108
|
}, "Type">, number>;
|
|
142
|
-
}
|
|
109
|
+
}>;
|
|
143
110
|
/** The group's x/y position composed outside its normalized local transform. */
|
|
144
111
|
export declare const resolvedTransform: (data: {
|
|
145
112
|
readonly x: number;
|
|
@@ -147,4 +114,3 @@ export declare const resolvedTransform: (data: {
|
|
|
147
114
|
readonly transform: TransformMatrix;
|
|
148
115
|
}) => TransformMatrix;
|
|
149
116
|
export declare const isIdentityTransform: (matrix: TransformMatrix) => boolean;
|
|
150
|
-
export {};
|
package/dist/shapes/Group.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
3
|
import * as SchemaGetter from "effect/SchemaGetter";
|
|
4
|
-
import * as Entity from "../Entity";
|
|
5
|
-
import * as Shape2D from "./Shape2D";
|
|
4
|
+
import * as Entity from "../Entity.js";
|
|
5
|
+
import * as Shape2D from "./Shape2D.js";
|
|
6
6
|
export const TransformMatrix = Schema.Struct({
|
|
7
7
|
a: Schema.Number,
|
|
8
8
|
b: Schema.Number,
|
|
@@ -60,7 +60,7 @@ const fields = {
|
|
|
60
60
|
children: Schema.Array(Schema.String).pipe(Schema.withConstructorDefault(Effect.sync(() => []))),
|
|
61
61
|
};
|
|
62
62
|
const decodeTransform = Schema.decodeUnknownSync(Transform);
|
|
63
|
-
const
|
|
63
|
+
const _normalizeInput = (input) => Array.isArray(input.transform)
|
|
64
64
|
? { ...input, transform: decodeTransform(input.transform) }
|
|
65
65
|
: input;
|
|
66
66
|
// A container: positions and structures its children, paints nothing.
|
|
@@ -71,7 +71,7 @@ const traits = {
|
|
|
71
71
|
"~position": Shape2D.positionLens(),
|
|
72
72
|
"~opacity": Shape2D.opacityLens(),
|
|
73
73
|
};
|
|
74
|
-
export const Group = Entity.make("shapes/Group", fields, traits
|
|
74
|
+
export const Group = Entity.make("shapes/Group", fields, traits);
|
|
75
75
|
/** The group's x/y position composed outside its normalized local transform. */
|
|
76
76
|
export const resolvedTransform = (data) => multiplyTransforms({ ...identityTransform, e: data.x, f: data.y }, data.transform);
|
|
77
77
|
export const isIdentityTransform = (matrix) => matrix.a === 1 &&
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
import * as Entity from "../Entity.js";
|
|
3
|
+
/**
|
|
4
|
+
* A screen-space container: its subtree is projected through the IDENTITY
|
|
5
|
+
* camera instead of the active one, so HUD content ignores camera movement,
|
|
6
|
+
* zoom, shake — and depth of field (the identity camera's aperture is 0).
|
|
7
|
+
* HUD content always paints on top of world content.
|
|
8
|
+
*
|
|
9
|
+
* `x`/`y` compose into the children in screen coordinates (slide a whole
|
|
10
|
+
* lower-third in with one tween). There is deliberately no `z`: the
|
|
11
|
+
* container has no world depth. Children may still use their own `z` for
|
|
12
|
+
* deliberate in-HUD depth; it defaults to 0 (flat, plain-2D placement).
|
|
13
|
+
*
|
|
14
|
+
* A Hud must be a top-level child of the root — nesting it inside world
|
|
15
|
+
* content would compose world offsets into screen coordinates and is a loud
|
|
16
|
+
* defect. A Hud inside a Hud behaves as a plain group.
|
|
17
|
+
*/
|
|
18
|
+
declare const fields: {
|
|
19
|
+
x: Schema.withConstructorDefault<Schema.Number>;
|
|
20
|
+
y: Schema.withConstructorDefault<Schema.Number>;
|
|
21
|
+
children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
|
|
22
|
+
};
|
|
23
|
+
type HudData = Schema.Struct<typeof fields>["Type"];
|
|
24
|
+
export declare const Hud: Entity.Entity<"shapes/Hud", Schema.Struct<{
|
|
25
|
+
x: Schema.withConstructorDefault<Schema.Number>;
|
|
26
|
+
y: Schema.withConstructorDefault<Schema.Number>;
|
|
27
|
+
children: Schema.withConstructorDefault<Schema.$Array<Schema.String>>;
|
|
28
|
+
}>, {
|
|
29
|
+
readonly "~position": {
|
|
30
|
+
get: (data: HudData) => {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
z: number;
|
|
34
|
+
};
|
|
35
|
+
set: (data: HudData, value: Entity.Position) => HudData;
|
|
36
|
+
};
|
|
37
|
+
}>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import * as Entity from "../Entity.js";
|
|
4
|
+
import * as Shape2D from "./Shape2D.js";
|
|
5
|
+
/**
|
|
6
|
+
* A screen-space container: its subtree is projected through the IDENTITY
|
|
7
|
+
* camera instead of the active one, so HUD content ignores camera movement,
|
|
8
|
+
* zoom, shake — and depth of field (the identity camera's aperture is 0).
|
|
9
|
+
* HUD content always paints on top of world content.
|
|
10
|
+
*
|
|
11
|
+
* `x`/`y` compose into the children in screen coordinates (slide a whole
|
|
12
|
+
* lower-third in with one tween). There is deliberately no `z`: the
|
|
13
|
+
* container has no world depth. Children may still use their own `z` for
|
|
14
|
+
* deliberate in-HUD depth; it defaults to 0 (flat, plain-2D placement).
|
|
15
|
+
*
|
|
16
|
+
* A Hud must be a top-level child of the root — nesting it inside world
|
|
17
|
+
* content would compose world offsets into screen coordinates and is a loud
|
|
18
|
+
* defect. A Hud inside a Hud behaves as a plain group.
|
|
19
|
+
*/
|
|
20
|
+
const fields = {
|
|
21
|
+
x: Shape2D.defaultedNumber(0),
|
|
22
|
+
y: Shape2D.defaultedNumber(0),
|
|
23
|
+
children: Schema.Array(Schema.String).pipe(Schema.withConstructorDefault(Effect.sync(() => []))),
|
|
24
|
+
};
|
|
25
|
+
export const Hud = Entity.make("shapes/Hud", fields, {
|
|
26
|
+
// screen-space position; z is pinned 0 (no world depth on the container)
|
|
27
|
+
"~position": {
|
|
28
|
+
get: (data) => ({ x: data.x, y: data.y, z: 0 }),
|
|
29
|
+
set: (data, value) => ({
|
|
30
|
+
...data,
|
|
31
|
+
x: value.x,
|
|
32
|
+
y: value.y,
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
import * as Entity from "../Entity.js";
|
|
3
|
+
/**
|
|
4
|
+
* A raster/vector image leaf. `image` names an asset declared via the
|
|
5
|
+
* `Images` annotation; the render session decodes it once and every frame
|
|
6
|
+
* reuses the decoded picture. An undeclared or failed asset paints nothing
|
|
7
|
+
* (soft skip — the rest of the frame renders).
|
|
8
|
+
*
|
|
9
|
+
* `width`/`height` are optional and undefaulted: set BOTH to draw at that
|
|
10
|
+
* size (numeric, so they tween); leave both absent to draw at the source's
|
|
11
|
+
* natural size. A lone dimension is ignored (natural size used) — aspect
|
|
12
|
+
* math would need the natural size, which frame data never sees.
|
|
13
|
+
*
|
|
14
|
+
* Billboard only: no orientation fields. ponytail: tilting an image needs a
|
|
15
|
+
* projective setTransform (full 3×3, we hardcode the bottom row) mapping the
|
|
16
|
+
* projected quad; add when a scene needs a tilted image.
|
|
17
|
+
*/
|
|
18
|
+
export declare const Image: Entity.Entity<"shapes/Image", Schema.Struct<{
|
|
19
|
+
x: Schema.withConstructorDefault<Schema.Number>;
|
|
20
|
+
y: Schema.withConstructorDefault<Schema.Number>;
|
|
21
|
+
z: Schema.withConstructorDefault<Schema.Number>;
|
|
22
|
+
opacity: Schema.withConstructorDefault<Schema.Number>;
|
|
23
|
+
image: Schema.String;
|
|
24
|
+
width: Schema.optionalKey<Schema.Number>;
|
|
25
|
+
height: Schema.optionalKey<Schema.Number>;
|
|
26
|
+
}>, {
|
|
27
|
+
readonly "~position": Entity.TraitLens<{
|
|
28
|
+
readonly x: number;
|
|
29
|
+
readonly y: number;
|
|
30
|
+
readonly z: number;
|
|
31
|
+
readonly opacity: number;
|
|
32
|
+
readonly image: string;
|
|
33
|
+
readonly width?: number;
|
|
34
|
+
readonly height?: number;
|
|
35
|
+
}, Entity.Position>;
|
|
36
|
+
readonly "~opacity": Entity.TraitLens<{
|
|
37
|
+
readonly x: number;
|
|
38
|
+
readonly y: number;
|
|
39
|
+
readonly z: number;
|
|
40
|
+
readonly opacity: number;
|
|
41
|
+
readonly image: string;
|
|
42
|
+
readonly width?: number;
|
|
43
|
+
readonly height?: number;
|
|
44
|
+
}, number>;
|
|
45
|
+
}>;
|