lecodes-sdk 0.20.0 → 0.20.2
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/global.d.ts +31 -0
- package/dist/inject.js +361 -260
- package/dist/types/audio/Bus.d.ts +45 -0
- package/dist/types/audio/Sound.d.ts +28 -0
- package/dist/types/audio/Voice.d.ts +27 -0
- package/dist/types/audio/audio.d.ts +83 -0
- package/dist/types/audio/support.d.ts +1 -0
- package/dist/types/gl/AudioSource.d.ts +60 -0
- package/dist/types/gl/AudioZone.d.ts +32 -0
- package/dist/types/gl/DecalSet.d.ts +103 -0
- package/dist/types/gl/Geometry.d.ts +5 -0
- package/dist/types/gl/Light.d.ts +7 -0
- package/dist/types/gl/Material.d.ts +86 -2
- package/dist/types/gl/Mesh.d.ts +11 -0
- package/dist/types/gl/Scene.d.ts +23 -0
- package/dist/types/gl/SceneAudio.d.ts +11 -0
- package/dist/types/gl/Texture.d.ts +29 -1
- package/dist/types/gl/animation/AnimationClip.d.ts +25 -12
- package/dist/types/gl/animation/core.d.ts +15 -10
- package/dist/types/gl/state.d.ts +0 -1
- package/dist/types/inject.d.ts +10 -0
- package/dist/types/runtime/input.d.ts +11 -0
- package/dist/types/ui/UIImage.d.ts +15 -5
- package/dist/types.json +1 -1
- package/package.json +1 -1
- package/prompts/dist/2d-game.md +408 -197
- package/prompts/dist/3d-app.md +491 -166
- package/prompts/dist/ar-app.md +373 -163
- package/prompts/dist/design.md +83 -87
- package/prompts/dist/ui-app.md +325 -136
- package/src/audio/Bus.ts +102 -0
- package/src/audio/Sound.ts +96 -0
- package/src/audio/Voice.ts +102 -0
- package/src/audio/audio.ts +161 -0
- package/src/audio/support.ts +6 -0
- package/src/bridges.d.ts +1481 -1352
- package/src/compile/compileProject.ts +30 -15
- package/src/compile/index.ts +3 -0
- package/src/core/Aspect.ts +33 -8
- package/src/g2/Scene2D.ts +7 -0
- package/src/gl/AudioSource.ts +113 -0
- package/src/gl/AudioZone.ts +75 -0
- package/src/gl/CameraPlace.ts +52 -52
- package/src/gl/DecalSet.ts +233 -0
- package/src/gl/Geometry.ts +5 -0
- package/src/gl/Light.ts +16 -0
- package/src/gl/Lightmap.ts +3 -2
- package/src/gl/Material.ts +152 -4
- package/src/gl/Mesh.ts +20 -1
- package/src/gl/Ragdoll.ts +270 -270
- package/src/gl/Scene.ts +41 -7
- package/src/gl/SceneAudio.ts +26 -0
- package/src/gl/Texture.ts +43 -3
- package/src/gl/Trigger.ts +45 -45
- package/src/gl/Vehicle.ts +5 -5
- package/src/gl/animation/AnimationClip.ts +43 -20
- package/src/gl/animation/Animator.ts +4 -3
- package/src/gl/animation/core.ts +20 -15
- package/src/gl/scenarios.ts +291 -291
- package/src/gl/state.ts +1 -1
- package/src/inject.ts +12 -0
- package/src/runtime/input.ts +6 -1
- package/src/scene/gizmos.ts +148 -148
- package/src/ui/UIImage.ts +21 -7
package/src/scene/gizmos.ts
CHANGED
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
// Editor gizmos: immediate-mode line drawing for code that runs while a scene is EDITED — a
|
|
2
|
-
// generator aspect's `rebuild()`, a `make()` factory, an editor tool's hooks. Nothing here touches
|
|
3
|
-
// the scene: calls accumulate WORLD-space line segments into the buffer of the run that is
|
|
4
|
-
// currently executing (an ambient scope the scene loader opens around each synchronous call), and
|
|
5
|
-
// the scene editor pushes those buffers to the lite engine's overlay layer, which draws them over
|
|
6
|
-
// the picture — never outlined, never picked, never rendered by Filament, absent in play mode.
|
|
7
|
-
//
|
|
8
|
-
// rebuild() {
|
|
9
|
-
// Gizmos.polyline(points, { color: "#5b8ef0", closed: true })
|
|
10
|
-
// for (const p of points) Gizmos.cross(p, 0.07)
|
|
11
|
-
// }
|
|
12
|
-
//
|
|
13
|
-
// Every run starts with an EMPTY buffer — what a call draws is the whole picture (like
|
|
14
|
-
// `generated.clear()`, only implicit). Outside any scope (play mode, hand-attached aspects, a call
|
|
15
|
-
// after an `await` inside rebuild) the calls are no-ops: rebuild is synchronous by contract.
|
|
16
|
-
//
|
|
17
|
-
// ANCHORED gizmos (`{ node }` in the style): points are in that node's LOCAL frame and the engine
|
|
18
|
-
// follows the node live — through a gizmo drag, not only after it — with the node's scale
|
|
19
|
-
// stripped (an empty scaled ×10 for its children keeps a normal-sized cross). An anchored picture
|
|
20
|
-
// is the node's own: clicking it in the viewport selects the node, and it draws in the selection
|
|
21
|
-
// accent while selected. World-space calls (path lines) are never pickable.
|
|
22
|
-
|
|
23
|
-
import { Mat4, type Mat4Like } from "../math/mat4"
|
|
24
|
-
import type { Vec3Like } from "../math/vec"
|
|
25
|
-
|
|
26
|
-
/** The anchor of a gizmo call: any object with an engine entity id (a `Node`). */
|
|
27
|
-
export type GizmoAnchor = { readonly id: number }
|
|
28
|
-
|
|
29
|
-
export type GizmoStyle = {
|
|
30
|
-
/** CSS hex color (`#rgb` / `#rrggbb`); default a neutral light gray. */
|
|
31
|
-
color?: string
|
|
32
|
-
/** 0..1, default 1. */
|
|
33
|
-
alpha?: number
|
|
34
|
-
/** Anchor: points are in this node's LOCAL frame (scale ignored); the lines follow the node
|
|
35
|
-
* live, are pickable (a click selects the node) and turn the selection accent when it is
|
|
36
|
-
* selected. Omit for world-space lines (not pickable). */
|
|
37
|
-
node?: GizmoAnchor | null
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** One color batch of LINES (flat `[x,y,z, x,y,z]` per segment) — what a host hands the engine.
|
|
41
|
-
* With `entityId` the segments are in that entity's local frame (see GizmoStyle.node). */
|
|
42
|
-
export type GizmoBatch = { color: string, alpha: number, segments: number[], entityId?: number }
|
|
43
|
-
|
|
44
|
-
const DEFAULT_COLOR = "#cfd4dd"
|
|
45
|
-
|
|
46
|
-
/** The per-run collector (owned by the scene loader's EditorRun). Batches are keyed by style so a
|
|
47
|
-
* hundred same-colored segments cost one draw. */
|
|
48
|
-
export class GizmoBuffer {
|
|
49
|
-
batches: GizmoBatch[] = []
|
|
50
|
-
private _byStyle = new Map<string, GizmoBatch>()
|
|
51
|
-
|
|
52
|
-
clear(): void {
|
|
53
|
-
this.batches = []
|
|
54
|
-
this._byStyle.clear()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
segments(style: GizmoStyle | undefined): number[] {
|
|
58
|
-
const color = style?.color ?? DEFAULT_COLOR
|
|
59
|
-
const alpha = Math.max(0, Math.min(1, style?.alpha ?? 1))
|
|
60
|
-
const entityId = style?.node?.id
|
|
61
|
-
const key = `${color}@${alpha}@${entityId ?? ""}`
|
|
62
|
-
let b = this._byStyle.get(key)
|
|
63
|
-
if (!b) {
|
|
64
|
-
b = entityId ? { color, alpha, segments: [], entityId } : { color, alpha, segments: [] }
|
|
65
|
-
this._byStyle.set(key, b)
|
|
66
|
-
this.batches.push(b)
|
|
67
|
-
}
|
|
68
|
-
return b.segments
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let current: GizmoBuffer | null = null
|
|
73
|
-
let warnedOutOfScope = false
|
|
74
|
-
|
|
75
|
-
const xyz = (p: Vec3Like): [number, number, number] =>
|
|
76
|
-
Array.isArray(p) ? [ p[0] ?? 0, p[1] ?? 0, p[2] ?? 0 ] : [ (p as { x: number }).x, (p as { y: number }).y, (p as { z: number }).z ]
|
|
77
|
-
|
|
78
|
-
const target = (style: GizmoStyle | undefined): number[] | null => {
|
|
79
|
-
if (current) return current.segments(style)
|
|
80
|
-
if (!warnedOutOfScope && (globalThis as { __lecodesSceneEdit?: boolean }).__lecodesSceneEdit) {
|
|
81
|
-
warnedOutOfScope = true
|
|
82
|
-
console.warn("[Gizmos] draw call outside an editor run — gizmos must be drawn synchronously inside rebuild() / a make() factory / a tool hook")
|
|
83
|
-
}
|
|
84
|
-
return null
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** @internal Run `fn` with `buffer` as the ambient gizmo target (cleared first). Nested scopes
|
|
88
|
-
* restore the outer one. */
|
|
89
|
-
export const withGizmoScope = <T>(buffer: GizmoBuffer, fn: () => T): T => {
|
|
90
|
-
const prev = current
|
|
91
|
-
buffer.clear()
|
|
92
|
-
current = buffer
|
|
93
|
-
try { return fn() } finally { current = prev }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Editor-only line drawing, available inside generator `rebuild()`, `make()` factories and editor
|
|
98
|
-
* tool hooks. Points are WORLD space. Drawn by the scene editor's viewport overlay (depth-test
|
|
99
|
-
* off — a path through a wall is still a path); invisible everywhere else.
|
|
100
|
-
*/
|
|
101
|
-
export const Gizmos = {
|
|
102
|
-
/** One segment from `a` to `b`. */
|
|
103
|
-
line(a: Vec3Like, b: Vec3Like, style?: GizmoStyle): void {
|
|
104
|
-
const out = target(style)
|
|
105
|
-
if (!out) return
|
|
106
|
-
out.push(...xyz(a), ...xyz(b))
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
/** Consecutive segments through `points`; `closed` joins the last point back to the first. */
|
|
110
|
-
polyline(points: readonly Vec3Like[], style?: GizmoStyle & { closed?: boolean }): void {
|
|
111
|
-
if (points.length < 2) return
|
|
112
|
-
const out = target(style)
|
|
113
|
-
if (!out) return
|
|
114
|
-
const n = points.length
|
|
115
|
-
const last = style?.closed ? n : n - 1
|
|
116
|
-
for (let i = 0; i < last; i++) out.push(...xyz(points[i]!), ...xyz(points[(i + 1) % n]!))
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
/** A wireframe camera frustum looking down −Z: the four edges from the origin to a rect
|
|
120
|
-
* `length` metres ahead sized by `fov` (vertical, degrees) × `aspect` (default 16:9), plus an
|
|
121
|
-
* "up" fin above the rect. Anchor it (`{ node }`) for a camera node's marker — the frustum
|
|
122
|
-
* then follows the node's pose; `matrix` places an unanchored one in world space. */
|
|
123
|
-
frustum(fov: number, style?: GizmoStyle & { aspect?: number, length?: number, matrix?: Mat4Like }): void {
|
|
124
|
-
const out = target(style)
|
|
125
|
-
if (!out) return
|
|
126
|
-
const aspect = style?.aspect ?? 16 / 9, length = style?.length ?? 0.6
|
|
127
|
-
const m = style?.matrix ? new Mat4(style.matrix) : null
|
|
128
|
-
const h = Math.tan((fov * Math.PI) / 360) * length, w = h * aspect, z = -length
|
|
129
|
-
const P = (x: number, y: number, zz: number): [number, number, number] => {
|
|
130
|
-
if (!m) return [ x, y, zz ]
|
|
131
|
-
const v = m.transformPoint([ x, y, zz ])
|
|
132
|
-
return [ v.x, v.y, v.z ]
|
|
133
|
-
}
|
|
134
|
-
const o = P(0, 0, 0)
|
|
135
|
-
const c = [ P(-w, -h, z), P(w, -h, z), P(w, h, z), P(-w, h, z) ]
|
|
136
|
-
for (const q of c) out.push(...o, ...q)
|
|
137
|
-
for (let i = 0; i < 4; i++) out.push(...c[i]!, ...c[(i + 1) % 4]!)
|
|
138
|
-
out.push(...P(-w * 0.4, h, z), ...P(0, h * 1.6, z), ...P(0, h * 1.6, z), ...P(w * 0.4, h, z))
|
|
139
|
-
},
|
|
140
|
-
|
|
141
|
-
/** A three-axis cross centred on `p` (a point marker); `size` = half extent, default 0.1. */
|
|
142
|
-
cross(p: Vec3Like, size = 0.1, style?: GizmoStyle): void {
|
|
143
|
-
const out = target(style)
|
|
144
|
-
if (!out) return
|
|
145
|
-
const [ x, y, z ] = xyz(p)
|
|
146
|
-
out.push(x - size, y, z, x + size, y, z, x, y - size, z, x, y + size, z, x, y, z - size, x, y, z + size)
|
|
147
|
-
},
|
|
148
|
-
}
|
|
1
|
+
// Editor gizmos: immediate-mode line drawing for code that runs while a scene is EDITED — a
|
|
2
|
+
// generator aspect's `rebuild()`, a `make()` factory, an editor tool's hooks. Nothing here touches
|
|
3
|
+
// the scene: calls accumulate WORLD-space line segments into the buffer of the run that is
|
|
4
|
+
// currently executing (an ambient scope the scene loader opens around each synchronous call), and
|
|
5
|
+
// the scene editor pushes those buffers to the lite engine's overlay layer, which draws them over
|
|
6
|
+
// the picture — never outlined, never picked, never rendered by Filament, absent in play mode.
|
|
7
|
+
//
|
|
8
|
+
// rebuild() {
|
|
9
|
+
// Gizmos.polyline(points, { color: "#5b8ef0", closed: true })
|
|
10
|
+
// for (const p of points) Gizmos.cross(p, 0.07)
|
|
11
|
+
// }
|
|
12
|
+
//
|
|
13
|
+
// Every run starts with an EMPTY buffer — what a call draws is the whole picture (like
|
|
14
|
+
// `generated.clear()`, only implicit). Outside any scope (play mode, hand-attached aspects, a call
|
|
15
|
+
// after an `await` inside rebuild) the calls are no-ops: rebuild is synchronous by contract.
|
|
16
|
+
//
|
|
17
|
+
// ANCHORED gizmos (`{ node }` in the style): points are in that node's LOCAL frame and the engine
|
|
18
|
+
// follows the node live — through a gizmo drag, not only after it — with the node's scale
|
|
19
|
+
// stripped (an empty scaled ×10 for its children keeps a normal-sized cross). An anchored picture
|
|
20
|
+
// is the node's own: clicking it in the viewport selects the node, and it draws in the selection
|
|
21
|
+
// accent while selected. World-space calls (path lines) are never pickable.
|
|
22
|
+
|
|
23
|
+
import { Mat4, type Mat4Like } from "../math/mat4"
|
|
24
|
+
import type { Vec3Like } from "../math/vec"
|
|
25
|
+
|
|
26
|
+
/** The anchor of a gizmo call: any object with an engine entity id (a `Node`). */
|
|
27
|
+
export type GizmoAnchor = { readonly id: number }
|
|
28
|
+
|
|
29
|
+
export type GizmoStyle = {
|
|
30
|
+
/** CSS hex color (`#rgb` / `#rrggbb`); default a neutral light gray. */
|
|
31
|
+
color?: string
|
|
32
|
+
/** 0..1, default 1. */
|
|
33
|
+
alpha?: number
|
|
34
|
+
/** Anchor: points are in this node's LOCAL frame (scale ignored); the lines follow the node
|
|
35
|
+
* live, are pickable (a click selects the node) and turn the selection accent when it is
|
|
36
|
+
* selected. Omit for world-space lines (not pickable). */
|
|
37
|
+
node?: GizmoAnchor | null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** One color batch of LINES (flat `[x,y,z, x,y,z]` per segment) — what a host hands the engine.
|
|
41
|
+
* With `entityId` the segments are in that entity's local frame (see GizmoStyle.node). */
|
|
42
|
+
export type GizmoBatch = { color: string, alpha: number, segments: number[], entityId?: number }
|
|
43
|
+
|
|
44
|
+
const DEFAULT_COLOR = "#cfd4dd"
|
|
45
|
+
|
|
46
|
+
/** The per-run collector (owned by the scene loader's EditorRun). Batches are keyed by style so a
|
|
47
|
+
* hundred same-colored segments cost one draw. */
|
|
48
|
+
export class GizmoBuffer {
|
|
49
|
+
batches: GizmoBatch[] = []
|
|
50
|
+
private _byStyle = new Map<string, GizmoBatch>()
|
|
51
|
+
|
|
52
|
+
clear(): void {
|
|
53
|
+
this.batches = []
|
|
54
|
+
this._byStyle.clear()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
segments(style: GizmoStyle | undefined): number[] {
|
|
58
|
+
const color = style?.color ?? DEFAULT_COLOR
|
|
59
|
+
const alpha = Math.max(0, Math.min(1, style?.alpha ?? 1))
|
|
60
|
+
const entityId = style?.node?.id
|
|
61
|
+
const key = `${color}@${alpha}@${entityId ?? ""}`
|
|
62
|
+
let b = this._byStyle.get(key)
|
|
63
|
+
if (!b) {
|
|
64
|
+
b = entityId ? { color, alpha, segments: [], entityId } : { color, alpha, segments: [] }
|
|
65
|
+
this._byStyle.set(key, b)
|
|
66
|
+
this.batches.push(b)
|
|
67
|
+
}
|
|
68
|
+
return b.segments
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let current: GizmoBuffer | null = null
|
|
73
|
+
let warnedOutOfScope = false
|
|
74
|
+
|
|
75
|
+
const xyz = (p: Vec3Like): [number, number, number] =>
|
|
76
|
+
Array.isArray(p) ? [ p[0] ?? 0, p[1] ?? 0, p[2] ?? 0 ] : [ (p as { x: number }).x, (p as { y: number }).y, (p as { z: number }).z ]
|
|
77
|
+
|
|
78
|
+
const target = (style: GizmoStyle | undefined): number[] | null => {
|
|
79
|
+
if (current) return current.segments(style)
|
|
80
|
+
if (!warnedOutOfScope && (globalThis as { __lecodesSceneEdit?: boolean }).__lecodesSceneEdit) {
|
|
81
|
+
warnedOutOfScope = true
|
|
82
|
+
console.warn("[Gizmos] draw call outside an editor run — gizmos must be drawn synchronously inside rebuild() / a make() factory / a tool hook")
|
|
83
|
+
}
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** @internal Run `fn` with `buffer` as the ambient gizmo target (cleared first). Nested scopes
|
|
88
|
+
* restore the outer one. */
|
|
89
|
+
export const withGizmoScope = <T>(buffer: GizmoBuffer, fn: () => T): T => {
|
|
90
|
+
const prev = current
|
|
91
|
+
buffer.clear()
|
|
92
|
+
current = buffer
|
|
93
|
+
try { return fn() } finally { current = prev }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Editor-only line drawing, available inside generator `rebuild()`, `make()` factories and editor
|
|
98
|
+
* tool hooks. Points are WORLD space. Drawn by the scene editor's viewport overlay (depth-test
|
|
99
|
+
* off — a path through a wall is still a path); invisible everywhere else.
|
|
100
|
+
*/
|
|
101
|
+
export const Gizmos = {
|
|
102
|
+
/** One segment from `a` to `b`. */
|
|
103
|
+
line(a: Vec3Like, b: Vec3Like, style?: GizmoStyle): void {
|
|
104
|
+
const out = target(style)
|
|
105
|
+
if (!out) return
|
|
106
|
+
out.push(...xyz(a), ...xyz(b))
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/** Consecutive segments through `points`; `closed` joins the last point back to the first. */
|
|
110
|
+
polyline(points: readonly Vec3Like[], style?: GizmoStyle & { closed?: boolean }): void {
|
|
111
|
+
if (points.length < 2) return
|
|
112
|
+
const out = target(style)
|
|
113
|
+
if (!out) return
|
|
114
|
+
const n = points.length
|
|
115
|
+
const last = style?.closed ? n : n - 1
|
|
116
|
+
for (let i = 0; i < last; i++) out.push(...xyz(points[i]!), ...xyz(points[(i + 1) % n]!))
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/** A wireframe camera frustum looking down −Z: the four edges from the origin to a rect
|
|
120
|
+
* `length` metres ahead sized by `fov` (vertical, degrees) × `aspect` (default 16:9), plus an
|
|
121
|
+
* "up" fin above the rect. Anchor it (`{ node }`) for a camera node's marker — the frustum
|
|
122
|
+
* then follows the node's pose; `matrix` places an unanchored one in world space. */
|
|
123
|
+
frustum(fov: number, style?: GizmoStyle & { aspect?: number, length?: number, matrix?: Mat4Like }): void {
|
|
124
|
+
const out = target(style)
|
|
125
|
+
if (!out) return
|
|
126
|
+
const aspect = style?.aspect ?? 16 / 9, length = style?.length ?? 0.6
|
|
127
|
+
const m = style?.matrix ? new Mat4(style.matrix) : null
|
|
128
|
+
const h = Math.tan((fov * Math.PI) / 360) * length, w = h * aspect, z = -length
|
|
129
|
+
const P = (x: number, y: number, zz: number): [number, number, number] => {
|
|
130
|
+
if (!m) return [ x, y, zz ]
|
|
131
|
+
const v = m.transformPoint([ x, y, zz ])
|
|
132
|
+
return [ v.x, v.y, v.z ]
|
|
133
|
+
}
|
|
134
|
+
const o = P(0, 0, 0)
|
|
135
|
+
const c = [ P(-w, -h, z), P(w, -h, z), P(w, h, z), P(-w, h, z) ]
|
|
136
|
+
for (const q of c) out.push(...o, ...q)
|
|
137
|
+
for (let i = 0; i < 4; i++) out.push(...c[i]!, ...c[(i + 1) % 4]!)
|
|
138
|
+
out.push(...P(-w * 0.4, h, z), ...P(0, h * 1.6, z), ...P(0, h * 1.6, z), ...P(w * 0.4, h, z))
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/** A three-axis cross centred on `p` (a point marker); `size` = half extent, default 0.1. */
|
|
142
|
+
cross(p: Vec3Like, size = 0.1, style?: GizmoStyle): void {
|
|
143
|
+
const out = target(style)
|
|
144
|
+
if (!out) return
|
|
145
|
+
const [ x, y, z ] = xyz(p)
|
|
146
|
+
out.push(x - size, y, z, x + size, y, z, x, y - size, z, x, y + size, z, x, y, z - size, x, y, z + size)
|
|
147
|
+
},
|
|
148
|
+
}
|
package/src/ui/UIImage.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { FetchResponse } from "../runtime/fetch"
|
|
|
2
2
|
import type { SvgSourceValue as SvgSource } from "../runtime/misc"
|
|
3
3
|
import type { File } from "../runtime/fetch"
|
|
4
4
|
import { Canvas, isCanvas } from "../canvas/Canvas"
|
|
5
|
+
import type { Scene2D } from "../g2/Scene2D"
|
|
5
6
|
import { Element, type Color, type ElementStyle, type UIElementBase } from "./UINode"
|
|
6
7
|
|
|
7
8
|
export type UIImageStyle = ElementStyle & { borderRadius?: number, objectFit?: "cover" | "contain" | "fill", tintColor?: Color }
|
|
@@ -10,19 +11,32 @@ export type UIImageStyle = ElementStyle & { borderRadius?: number, objectFit?: "
|
|
|
10
11
|
* docs/canvas-contract.md ("Pending"). */
|
|
11
12
|
export type CanvasImageSource = { canvasSurface: number }
|
|
12
13
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
14
|
+
/** A live 2D scene as an image source: the host draws the scene into the node's box every frame
|
|
15
|
+
* (the scene's camera, projected into the box). No intrinsic size — give the node a width and
|
|
16
|
+
* height. */
|
|
17
|
+
export type Scene2DImageSource = { scene2d: number }
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
/** Accepted image sources. A Canvas is baked to a surface marker on assign; a Scene2D becomes a
|
|
20
|
+
* live-scene marker. */
|
|
21
|
+
export type ImageSource = string | FetchResponse | File | SvgSource | Canvas | Scene2D
|
|
22
|
+
|
|
23
|
+
// An engine object that knows how to present itself as an image source (Scene2D._imageSrc). Duck-typed
|
|
24
|
+
// so the UI layer never imports an engine class — a UI-only bundle carries no engine bridge.
|
|
25
|
+
const hasImageSrc = (v: unknown): v is { _imageSrc(): Scene2DImageSource } =>
|
|
26
|
+
typeof v === "object" && v !== null && typeof (v as { _imageSrc?: unknown })._imageSrc === "function"
|
|
27
|
+
|
|
28
|
+
const toSrc = (src: ImageSource): ResolvedImageSource =>
|
|
17
29
|
// _bakeSurfaceForUI (not _bakeSurface) also flags the canvas UI-bound, so canvas.update() later
|
|
18
30
|
// refreshes this image node in place — that's what makes an animated UIImage(canvas) update.
|
|
19
|
-
isCanvas(src) ? { canvasSurface: src._bakeSurfaceForUI() } : src
|
|
31
|
+
isCanvas(src) ? { canvasSurface: src._bakeSurfaceForUI() } : hasImageSrc(src) ? src._imageSrc() : src
|
|
20
32
|
|
|
21
|
-
/** The stored/resolved image source (a Canvas is resolved to a surface marker
|
|
22
|
-
|
|
33
|
+
/** The stored/resolved image source (a Canvas is resolved to a surface marker, a Scene2D to a
|
|
34
|
+
* live-scene marker). */
|
|
35
|
+
export type ResolvedImageSource = string | FetchResponse | File | SvgSource | CanvasImageSource | Scene2DImageSource
|
|
23
36
|
|
|
24
37
|
/** An image element. Sources: URL / `asset(...)`, fetch response / File, an SVG source
|
|
25
|
-
* (`assetIcon`),
|
|
38
|
+
* (`assetIcon`), a live Canvas (`canvas.update()` refreshes the node in place), or a live Scene2D
|
|
39
|
+
* (drawn into the box every frame — a minimap). */
|
|
26
40
|
export interface UIImage extends UIElementBase<UIImageStyle> {
|
|
27
41
|
readonly type: "image",
|
|
28
42
|
/** The image source — writable to swap the picture in place. */
|