lecodes-sdk 2.4.1 → 2.4.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/types/core/Aspect.d.ts +34 -5
- package/dist/types/g2/OneWay2D.d.ts +0 -1
- package/dist/types/g2/Physics2D.d.ts +8 -6
- package/dist/types/g2/Scene2D.d.ts +1 -1
- package/dist/types/g2/Shape2D.d.ts +43 -15
- package/dist/types/gl/Model.d.ts +3 -2
- package/dist/types/gl/Scene.d.ts +1 -1
- package/dist/types/gl/animation/DynamicBone.d.ts +99 -35
- package/dist/types/gl/animation/IK.d.ts +0 -1
- package/dist/types/gl/nav/NavAgent.d.ts +3 -2
- package/dist/types/gl/physics/Physics.d.ts +4 -5
- package/dist/types/gl/physics/Shape.d.ts +40 -15
- package/dist/types/gl/vehicle/Vehicle.d.ts +54 -35
- package/dist/types/gl/vehicle/Wheel.d.ts +31 -10
- package/dist/types/plugins/gen/ar/contract.d.ts +152 -0
- package/dist/types/plugins/gen/ar/sdk/ar.d.ts +47 -21
- package/dist/types/plugins/gen/ar/sdk/ar.gen.d.ts +17 -3
- package/dist/types/plugins/gen/audio-recorder/contract.d.ts +64 -0
- package/dist/types/plugins/gen/browser/contract.d.ts +108 -0
- package/dist/types/plugins/gen/camera/contract.d.ts +27 -0
- package/dist/types/plugins/gen/geolocation/contract.d.ts +52 -0
- package/dist/types/plugins/gen/map/contract.d.ts +144 -0
- package/dist/types/plugins/gen/push/contract.d.ts +83 -0
- package/dist/types/plugins/gen/qr-scanner/contract.d.ts +16 -0
- package/dist/types/plugins/gen/speech/contract.d.ts +67 -0
- package/dist/types/plugins/gen/webrtc/contract.d.ts +342 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types.json +1 -1
- package/package.json +1 -1
- package/src/bridges/gl.d.ts +5 -3
- package/src/compile/compileProject.ts +45 -10
- package/src/compile/index.ts +7 -0
- package/src/compile/webHalf.ts +255 -0
- package/src/core/Aspect.ts +118 -35
- package/src/g2/OneWay2D.ts +0 -2
- package/src/g2/Physics2D.ts +8 -16
- package/src/g2/Scene2D.ts +1 -1
- package/src/g2/Shape2D.ts +51 -22
- package/src/gl/Model.ts +3 -2
- package/src/gl/Scene.ts +1 -1
- package/src/gl/animation/DynamicBone.ts +549 -486
- package/src/gl/animation/IK.ts +0 -2
- package/src/gl/nav/NavAgent.ts +14 -12
- package/src/gl/physics/Physics.ts +4 -5
- package/src/gl/physics/Shape.ts +49 -23
- package/src/gl/vehicle/Vehicle.ts +102 -58
- package/src/gl/vehicle/Wheel.ts +45 -11
- package/src/plugins/gen/ar/contract.d.ts +35 -1
- package/src/plugins/gen/ar/sdk/ar.gen.ts +25 -6
- package/src/plugins/gen/ar/sdk/ar.ts +102 -35
- package/src/version.ts +1 -1
|
@@ -131,21 +131,28 @@ export declare class Vehicle extends Aspect<"vehicle", Node> {
|
|
|
131
131
|
* without physics or on an older host. A host that has it can still refuse a car's settings
|
|
132
132
|
* (`id` stays 0, with a warning). */
|
|
133
133
|
static get supported(): boolean;
|
|
134
|
-
|
|
135
|
-
mass:
|
|
134
|
+
private _mass;
|
|
135
|
+
/** Chassis mass in kg (default 1200). Wheels are massless in this model. Structural: a write on a
|
|
136
|
+
* live car warns and keeps the built mass. */
|
|
137
|
+
get mass(): number;
|
|
138
|
+
set mass(v: number);
|
|
136
139
|
/** Centre of mass height in chassis space (metres, negative = lower). Left out, it drops to halfway
|
|
137
|
-
* down the chassis shape — a car with its mass at the shape's centre tips over. */
|
|
140
|
+
* down the chassis shape — a car with its mass at the shape's centre tips over. Structural. */
|
|
138
141
|
centerOfMass?: number;
|
|
139
142
|
/** Which axle the engine drives (default "rear") — the default for every wheel's own `driven`
|
|
140
|
-
* share: 1 on that axle, 0 elsewhere. `none` = no drive at all (a trailer, a pushed cart). */
|
|
143
|
+
* share: 1 on that axle, 0 elsewhere. `none` = no drive at all (a trailer, a pushed cart). Structural. */
|
|
141
144
|
driveAxle: DriveLayout;
|
|
145
|
+
private _differential;
|
|
142
146
|
/** Differential lock across each driven axle. `locked` is the drift setup — both wheels turn
|
|
143
|
-
* together, so the pair lets go as a pair. Live:
|
|
144
|
-
differential: DifferentialMode | number;
|
|
147
|
+
* together, so the pair lets go as a pair. Live: switch it mid-drive. */
|
|
148
|
+
get differential(): DifferentialMode | number;
|
|
149
|
+
set differential(v: DifferentialMode | number);
|
|
150
|
+
private _antiRoll;
|
|
145
151
|
/**
|
|
146
152
|
* Anti-roll bars between each axle's wheels — what stops a car rolling onto its inside wheels in a
|
|
147
153
|
* fast corner. `true` (default) is a bar 0.01 as stiff as the wheel's own spring; a number is that
|
|
148
|
-
* ratio directly, and `0` / `false` removes the bars.
|
|
154
|
+
* ratio directly, and `0` / `false` removes the bars. Live: the stiffness, on axles built with a
|
|
155
|
+
* bar (`false` at build = no bar to stiffen later).
|
|
149
156
|
*
|
|
150
157
|
* **The numbers are far smaller than they look and the gain is steep** — Jolt's bar feeds
|
|
151
158
|
* `lengthDifference × stiffness × dt` as a bias into the suspension's own soft constraint, so 4%
|
|
@@ -155,24 +162,43 @@ export declare class Vehicle extends Aspect<"vehicle", Node> {
|
|
|
155
162
|
* This is the knob for how much a car visibly LEANS, not for wheel lift: bars change how far the
|
|
156
163
|
* body tips, not how much load transfers. For lift, lower `centerOfMass`.
|
|
157
164
|
*/
|
|
158
|
-
antiRoll: boolean | number;
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
get antiRoll(): boolean | number;
|
|
166
|
+
set antiRoll(v: boolean | number);
|
|
167
|
+
private _maxTilt;
|
|
168
|
+
/** Pitch/roll limit in degrees (default 60) before the constraint rights the car. 180 turns it off. Live. */
|
|
169
|
+
get maxTilt(): number;
|
|
170
|
+
set maxTilt(v: number);
|
|
171
|
+
private _friction;
|
|
161
172
|
/** Friction of the CHASSIS body itself — how it slides when it lands on its roof or side. Nothing
|
|
162
|
-
* to do with the tires; those are the `Wheel`'s `tire` × the ground's friction. */
|
|
163
|
-
friction: number;
|
|
173
|
+
* to do with the tires; those are the `Wheel`'s `tire` × the ground's friction. Live, at once. */
|
|
174
|
+
get friction(): number;
|
|
175
|
+
set friction(v: number);
|
|
176
|
+
private _clutchStrength;
|
|
164
177
|
/** Strength of the clutch when fully engaged (default 10). Small values slip; large ones weld the
|
|
165
|
-
* engine to the wheels. `clutch` (the input) scales it 0..1. */
|
|
166
|
-
clutchStrength: number;
|
|
178
|
+
* engine to the wheels. `clutch` (the input) scales it 0..1. Live. */
|
|
179
|
+
get clutchStrength(): number;
|
|
180
|
+
set clutchStrength(v: number);
|
|
181
|
+
private _clutchCapacity;
|
|
167
182
|
/** The clutch's torque CAPACITY, N·m (default 0 = none: Jolt's viscous clutch alone, torque =
|
|
168
183
|
* `clutchStrength` × slip speed, no ceiling — rigid at any real slip). Set it and the clutch
|
|
169
184
|
* SLIPS at this much torque × `clutch` — a dry clutch, emulated a physics step at a time: a
|
|
170
185
|
* launch can be feathered, and a dumped clutch in too high a gear drags the engine down instead
|
|
171
|
-
* of lurching. A road car's is ~1.5× the engine's peak torque. */
|
|
172
|
-
clutchCapacity: number;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
186
|
+
* of lurching. A road car's is ~1.5× the engine's peak torque. Live. */
|
|
187
|
+
get clutchCapacity(): number;
|
|
188
|
+
set clutchCapacity(v: number);
|
|
189
|
+
private _engine;
|
|
190
|
+
/** The engine (`EngineConfig`). Live: `car.vehicle.engine.torque = 800`, or assign a whole config. */
|
|
191
|
+
get engine(): EngineConfig;
|
|
192
|
+
set engine(v: EngineConfig);
|
|
193
|
+
private _steering;
|
|
194
|
+
/** The steering (`SteeringConfig`). Live, one level deep: `feedback` and `driver` are each
|
|
195
|
+
* replaced as a whole (`steering.driver = { maxTorque: 90 }`), not edited inside. */
|
|
196
|
+
get steering(): SteeringConfig;
|
|
197
|
+
set steering(v: SteeringConfig);
|
|
198
|
+
private _aero;
|
|
199
|
+
/** Aerodynamics (`AeroConfig`). Live: a wing that deploys is `car.vehicle.aero.downforce = 0.6`. */
|
|
200
|
+
get aero(): AeroConfig;
|
|
201
|
+
set aero(v: AeroConfig);
|
|
176
202
|
/** Scene-editor inspector: the flat, tunable half. The grouped objects are edited in code. */
|
|
177
203
|
static fields: FieldMeta<Vehicle>;
|
|
178
204
|
static editor: {
|
|
@@ -194,22 +220,14 @@ export declare class Vehicle extends Aspect<"vehicle", Node> {
|
|
|
194
220
|
private _input;
|
|
195
221
|
private _sent;
|
|
196
222
|
private _dirty;
|
|
197
|
-
private
|
|
223
|
+
private _rebuildPending;
|
|
198
224
|
onAttach(): void;
|
|
199
225
|
onDetach(): void;
|
|
200
|
-
/**
|
|
201
|
-
*
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
*
|
|
206
|
-
* Live: `differential`, `engine`, `steering`, `aero`, `maxTilt`, `friction`, `clutchStrength`,
|
|
207
|
-
* `clutchCapacity`, `antiRoll` (its stiffness, on axles that have a bar), and every `Wheel`'s
|
|
208
|
-
* `tire`. Everything else describes the car's STRUCTURE — `mass`, `centerOfMass`, which wheels
|
|
209
|
-
* exist and where, their suspension, the driven shares — and is baked into the constraint at
|
|
210
|
-
* attach; to change one of those, re-attach the aspect.
|
|
211
|
-
*/
|
|
212
|
-
onReconfigure(): void;
|
|
226
|
+
/** A live value changed on the car or a wheel: the tuning half of the blob is re-sent in
|
|
227
|
+
* `flush()`, one send however many values changed this frame. */
|
|
228
|
+
protected flush(): void;
|
|
229
|
+
private _setup;
|
|
230
|
+
private _teardown;
|
|
213
231
|
/** Native vehicle id (0 until attached / no physics support). */
|
|
214
232
|
get id(): number;
|
|
215
233
|
/** The chassis rigid body id (for `physicsApplyImpulse`-style calls). */
|
|
@@ -239,8 +257,9 @@ export declare class Vehicle extends Aspect<"vehicle", Node> {
|
|
|
239
257
|
/**
|
|
240
258
|
* EARLY phase: hand this frame's inputs to the engine, before the step. The vector is sent when
|
|
241
259
|
* anything changed — and every frame while a throttle, steer or brake is non-zero, because that is
|
|
242
|
-
* what keeps a driven car awake (an idle car with zero input sleeps).
|
|
243
|
-
*
|
|
260
|
+
* what keeps a driven car awake (an idle car with zero input sleeps). A pending re-tune (a live
|
|
261
|
+
* value of the car or a wheel changed) went out in `flush()`, before this phase, so the step runs
|
|
262
|
+
* on the new numbers.
|
|
244
263
|
*/
|
|
245
264
|
protected updateBefore(): void;
|
|
246
265
|
/** Teleport upright and clear all motion (velocity, engine rpm, wheel spin). Defaults to the
|
|
@@ -70,12 +70,22 @@ export declare const Tire: {
|
|
|
70
70
|
scale: (t: Tire, k: number) => Tire;
|
|
71
71
|
};
|
|
72
72
|
export declare function resolveTire(tire: Tire): ResolvedTire;
|
|
73
|
+
/**
|
|
74
|
+
* One wheel. LIVE: `radius`, `suspension.stiffness` / `damping` and `tire` — write them and the car
|
|
75
|
+
* retunes before the next step (a tire losing air: `wheel.radius *= 0.9`, `wheel.suspension.stiffness
|
|
76
|
+
* *= 0.7`, `wheel.tire = Tire.scale(wheel.tire, 0.5)`). STRUCTURAL: `width`, `suspension.travel`, `axle`,
|
|
77
|
+
* `steer`, `driven` — baked when the car is built; to change one, build the wheel again
|
|
78
|
+
* (`hub.aspect(Wheel, {…})` — the car rebuilds itself around it, its motion reset).
|
|
79
|
+
*/
|
|
73
80
|
export declare class Wheel extends Aspect<"wheel", Node> {
|
|
74
81
|
static readonly aspect = "wheel";
|
|
75
82
|
/** Wheel radius in metres. Default: measured from the node's rendered bounds, else 0.35. Too small
|
|
76
|
-
* and the car scrapes, too large and it floats.
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
* and the car scrapes, too large and it floats. Live: the ROLLING radius — the suspension hangs
|
|
84
|
+
* where it was built, so a smaller one lowers that corner of the car, the way a tire losing air
|
|
85
|
+
* does; the wheel's inertia keeps the radius it was built with. */
|
|
86
|
+
get radius(): number | undefined;
|
|
87
|
+
set radius(v: number | undefined);
|
|
88
|
+
/** Wheel width in metres. Default: measured, else 0.25. Structural. */
|
|
79
89
|
width?: number;
|
|
80
90
|
/** Axle group, front to back. Default: derived from Z. Set it for a car whose wheels are not in neat
|
|
81
91
|
* left/right pairs — an axle's wheels are paired for the differential and the anti-roll bar. */
|
|
@@ -87,10 +97,19 @@ export declare class Wheel extends Aspect<"wheel", Node> {
|
|
|
87
97
|
* split their ratio, so `0.6` / `0.4` front-to-rear is `driven: 0.3` on each front wheel and
|
|
88
98
|
* `0.2` on each rear. Structural: baked at attach. */
|
|
89
99
|
driven?: number;
|
|
90
|
-
|
|
91
|
-
/** The
|
|
92
|
-
*
|
|
93
|
-
|
|
100
|
+
private _suspension;
|
|
101
|
+
/** The suspension. Live: `stiffness` and `damping` (a softer corner sags and rolls more) — write a
|
|
102
|
+
* field (`wheel.suspension.stiffness = 1`) or assign a whole object (what it leaves out is the
|
|
103
|
+
* default). `travel` is structural. */
|
|
104
|
+
get suspension(): WheelSuspension;
|
|
105
|
+
set suspension(v: WheelSuspension);
|
|
106
|
+
private _tire;
|
|
107
|
+
/** The tire (default `Tire.road()`). Live: assign one (`wheel.tire = Tire.drift()`, or
|
|
108
|
+
* `Tire.scale(wheel.tire, 0.5)` for a grip slider, a tire that heats up) or write a field
|
|
109
|
+
* (`wheel.tire.traction = 1`); a curve is replaced, never edited in place. A tire assigned without
|
|
110
|
+
* `traction` / `circle` gets `road`'s. */
|
|
111
|
+
get tire(): Tire;
|
|
112
|
+
set tire(v: Tire);
|
|
94
113
|
/** INPUT: brake torque on this wheel THIS frame, N·m (default 0). Sticky, like `Vehicle.throttle`:
|
|
95
114
|
* write it every frame from a driver system's `updateBefore` — the foot brake's share, the
|
|
96
115
|
* handbrake's 4500 on a rear wheel, an ABS's pulses — and the same physics step applies it. A
|
|
@@ -102,10 +121,12 @@ export declare class Wheel extends Aspect<"wheel", Node> {
|
|
|
102
121
|
static editor: {
|
|
103
122
|
rebuild: boolean;
|
|
104
123
|
};
|
|
124
|
+
/** A wheel built under a car that already runs (`hub.aspect(Wheel, {…})` on a live car): the car
|
|
125
|
+
* rebuilds itself around the new set of wheels before the next step. */
|
|
126
|
+
onAttach(): void;
|
|
105
127
|
onDetach(): void;
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
onReconfigure(): void;
|
|
128
|
+
/** A live value changed (the tire, the radius, the spring): the car retunes in the same flush. */
|
|
129
|
+
protected flush(): void;
|
|
109
130
|
private _state;
|
|
110
131
|
/** Touching the ground this step. */
|
|
111
132
|
get contact(): boolean;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/ar/contract.d.ts — do not edit here.
|
|
2
|
+
// Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
|
|
3
|
+
// ar — world tracking over the device camera: ARKit on iOS, ARCore on Android, the camera under the
|
|
4
|
+
// scene turned by the sensors on the web. The app's side is ARScene (sdk/ar.ts), a Scene whose
|
|
5
|
+
// camera and background the session drives every frame through the host's gl facade.
|
|
6
|
+
// A session is ONE AR scene: ARScene opens it with the scene and its camera entity as params and
|
|
7
|
+
// closes it with the scene. `@permission camera` on start: the host asks, the halves never do.
|
|
8
|
+
// What a session finds in the camera's picture and drives an entity to: an image target (a printed
|
|
9
|
+
// picture, by its bytes) and, since contract version 2, a QR code (a printed symbol, by its text) —
|
|
10
|
+
// both through the `image` event. Codes are the web's (WebXR) first: the native halves take them and
|
|
11
|
+
// say they track none yet.
|
|
12
|
+
import type { Service, File, Entity, Scene3D, f64 } from "../../../plugin"
|
|
13
|
+
|
|
14
|
+
export type ARMode = "world" | "images"
|
|
15
|
+
export type PlaneDetection = "horizontal" | "vertical" | "both" | "none"
|
|
16
|
+
export type WorldAlignment = "gravity" | "gravityAndHeading"
|
|
17
|
+
export type TrackingState = "notAvailable" | "limited" | "normal"
|
|
18
|
+
export type TrackingReason = "initializing" | "excessiveMotion" | "insufficientFeatures" | "relocalizing"
|
|
19
|
+
export type PlaneType = "horizontal" | "vertical"
|
|
20
|
+
export type PlaneChange = "added" | "updated" | "removed"
|
|
21
|
+
export type HitTarget = "planes" | "estimated" | "any"
|
|
22
|
+
export type ARErrorCode = "unavailable" | "camera" | "failed"
|
|
23
|
+
|
|
24
|
+
export interface Vec3 { x: f64, y: f64, z: f64 }
|
|
25
|
+
export interface Quat { x: f64, y: f64, z: f64, w: f64 }
|
|
26
|
+
/** A pose in the scene's world: metres, Y up. */
|
|
27
|
+
export interface Pose { position: Vec3, rotation: Quat }
|
|
28
|
+
|
|
29
|
+
export interface ImageTarget {
|
|
30
|
+
image: File
|
|
31
|
+
/** The printed width, metres: the size the session starts from (the height by the image's
|
|
32
|
+
* proportions) — the framework refines it (the `image` event's `extent`). */
|
|
33
|
+
physicalWidth: f64
|
|
34
|
+
/** The entity that follows the image (its Anchor node). */
|
|
35
|
+
entity: Entity
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A printed QR code the session tracks: found by its text in the camera's picture; while
|
|
39
|
+
* tracked its entity is driven like an image target's — a rigid pose in metres, no scale — the
|
|
40
|
+
* symbol's centre, Y out of it, X along its top edge, Z towards its bottom. Web (WebXR) first;
|
|
41
|
+
* the native halves later. */
|
|
42
|
+
export interface QRTarget {
|
|
43
|
+
/** The code's text: a symbol with another text is not this target. "" = ANY code (the first
|
|
44
|
+
* one decoded): a test, or a plaque whose text is not known to the app. */
|
|
45
|
+
text: string
|
|
46
|
+
/** The printed side of the symbol (its modules, no quiet zone), metres. */
|
|
47
|
+
size: f64
|
|
48
|
+
/** The entity that follows the code (its Anchor node). */
|
|
49
|
+
entity: Entity
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface StartOptions {
|
|
53
|
+
/** Default "world". "images": image tracking only (no planes, no hit test; iOS runs
|
|
54
|
+
* ARImageTrackingConfiguration, which is lighter). */
|
|
55
|
+
mode?: ARMode
|
|
56
|
+
/** Default "horizontal". Ignored in "images". */
|
|
57
|
+
planes?: PlaneDetection
|
|
58
|
+
images?: ImageTarget[]
|
|
59
|
+
/** The QR codes to track (in "world" and in "images"); a half without them says so and drives
|
|
60
|
+
* none. @since 2 */
|
|
61
|
+
codes?: QRTarget[]
|
|
62
|
+
/** Default "gravity". "gravityAndHeading": -Z points north (the compass). */
|
|
63
|
+
worldAlignment?: WorldAlignment
|
|
64
|
+
/** Deliver `light` events. Default false. */
|
|
65
|
+
lightEstimation?: boolean
|
|
66
|
+
/** Default true. */
|
|
67
|
+
autoFocus?: boolean
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface HitResult {
|
|
71
|
+
pose: Pose
|
|
72
|
+
/** "plane": on a detected plane (`plane` = its id); "estimated": on a point of the feature
|
|
73
|
+
* cloud / an estimated plane. */
|
|
74
|
+
type: "plane" | "estimated"
|
|
75
|
+
plane?: f64
|
|
76
|
+
/** Metres from the camera. */
|
|
77
|
+
distance: f64
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Both are ARScene's, always sent. Optional because a channel's params are (the generator's rule: a
|
|
81
|
+
* param a half cannot read has no one to reject to); a session opened without them rejects `start`
|
|
82
|
+
* with "failed". */
|
|
83
|
+
export interface ARParams {
|
|
84
|
+
scene?: Scene3D
|
|
85
|
+
/** The scene's camera entity: the session sets its matrix and the scene's projection each frame. */
|
|
86
|
+
camera?: Entity
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface AREvents {
|
|
90
|
+
/** The tracking quality; the first event of a session. */
|
|
91
|
+
tracking: { state: TrackingState, reason?: TrackingReason }
|
|
92
|
+
/** A detected plane: added, moved / grown (throttled: a change over 1 cm, at most 10 per second),
|
|
93
|
+
* or gone (`mergedInto` = the plane that absorbed it, ARCore). `pose` = the plane's centre, its
|
|
94
|
+
* Y the normal; `extent` = [width, height] metres; `polygon` = the boundary as x,z pairs in the
|
|
95
|
+
* plane's space. */
|
|
96
|
+
plane: { change: PlaneChange, id: f64, type: PlaneType, pose: Pose, extent: f64[], polygon: f64[], mergedInto?: f64 }
|
|
97
|
+
/** An image target found, lost, or its size refined. While tracked its entity is driven: a rigid
|
|
98
|
+
* pose in metres, no scale — the image's centre, Y out of it, the image in XZ (X along its width,
|
|
99
|
+
* Z towards its bottom). `extent` = [width, height] metres, the framework's estimate (sent again
|
|
100
|
+
* when it changes by a millimetre, at most 10 per second); the last one when lost. A QR target's
|
|
101
|
+
* comes the same way (its frame: X along the symbol's top edge), `extent` = [size, size]. */
|
|
102
|
+
image: { entity: Entity, tracked: boolean, extent: f64[] }
|
|
103
|
+
/** An anchor made by `anchor()` lost or regained after a relocalization. */
|
|
104
|
+
anchor: { entity: Entity, tracked: boolean }
|
|
105
|
+
/** The estimated light (`lightEstimation`): lumens-ish ambient intensity (1000 = ARKit's
|
|
106
|
+
* neutral), colour temperature in kelvin; at most 10 per second. */
|
|
107
|
+
light: { intensity: f64, temperature: f64 }
|
|
108
|
+
/** The session failed and is over: "unavailable" (no ARKit / ARCore support, ARCore not
|
|
109
|
+
* installed and declined), "camera" (the camera could not be used), "failed". */
|
|
110
|
+
error: { code: ARErrorCode, message: string }
|
|
111
|
+
/** The session is over, and not by `stop()`: the user left it ("user" — the browser's back / exit
|
|
112
|
+
* of an immersive WebXR session) or the system took it ("system"). Not an error: ARScene closes
|
|
113
|
+
* the scene. ARKit and ARCore never end a session by themselves (an interruption is `tracking`
|
|
114
|
+
* limited): the native halves do not send it. @since 2 */
|
|
115
|
+
ended: { reason: "user" | "system" }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface AR extends Service<"ar", ARParams, AREvents> {
|
|
119
|
+
/** Start tracking; resolves at the first frame. A second start rejects "busy".
|
|
120
|
+
* @permission camera
|
|
121
|
+
* @rejects busy unavailable failed */
|
|
122
|
+
start(options?: StartOptions): Promise<void>
|
|
123
|
+
/** Stop tracking: the camera is released, the scene's projection and background cleared. Not
|
|
124
|
+
* running is not an error. */
|
|
125
|
+
stop(): Promise<void>
|
|
126
|
+
/** Replace the set of image targets of a RUNNING session without restarting it (the tracking, the
|
|
127
|
+
* planes and the anchors stay); an image no longer in the set stops driving its entity (its
|
|
128
|
+
* `image` event says `tracked: false` once if it was tracked). Before `start`, the images are
|
|
129
|
+
* `StartOptions.images`.
|
|
130
|
+
* @rejects notRunning failed */
|
|
131
|
+
setImages(images: ImageTarget[]): Promise<void>
|
|
132
|
+
/** The twin of `setImages` for QR codes: replace the set of QR targets of a RUNNING session
|
|
133
|
+
* without restarting it; a code no longer in the set stops driving its entity (`tracked: false`
|
|
134
|
+
* once if it was tracked). Before `start`, the codes are `StartOptions.codes`. A half that tracks
|
|
135
|
+
* no codes yet (the native ones) rejects "failed".
|
|
136
|
+
* @since 2
|
|
137
|
+
* @rejects notRunning failed */
|
|
138
|
+
setCodes(codes: QRTarget[]): Promise<void>
|
|
139
|
+
/** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
|
|
140
|
+
* `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
|
|
141
|
+
* a host without surfaces (the web).
|
|
142
|
+
* @rejects notRunning */
|
|
143
|
+
hitTest(x: f64, y: f64, target?: HitTarget): Promise<HitResult[]>
|
|
144
|
+
/** Pin `entity` to `pose`: the host's anchor drives its matrix from now on (and keeps it true
|
|
145
|
+
* as the world map refines). @rejects notRunning */
|
|
146
|
+
anchor(entity: Entity, pose: Pose): Promise<void>
|
|
147
|
+
/** Stop driving `entity` (its matrix stays where it was). */
|
|
148
|
+
release(entity: Entity): Promise<void>
|
|
149
|
+
/** The latest camera frame as a JPEG (the raw image, not the composite — that is
|
|
150
|
+
* `Scene.capture()`). @rejects notRunning failed */
|
|
151
|
+
captureFrame(): Promise<File>
|
|
152
|
+
}
|
|
@@ -41,11 +41,11 @@ export type ARPlaneEvent = {
|
|
|
41
41
|
mergedInto: ARPlane | null;
|
|
42
42
|
};
|
|
43
43
|
export type ARSceneOptions = SceneOptions & {
|
|
44
|
-
/** "world" (default): planes, hit tests, anchors, image targets. "images": image
|
|
45
|
-
* lighter on iOS; no planes, no hit test, no `root`. */
|
|
44
|
+
/** "world" (default): planes, hit tests, anchors, image and QR targets. "images": image and QR
|
|
45
|
+
* targets only — lighter on iOS; no planes, no hit test, no `root`. */
|
|
46
46
|
mode?: ARMode;
|
|
47
47
|
};
|
|
48
|
-
export type AROpenOptions = PresentOptions & Omit<StartOptions, "mode" | "images"> & {
|
|
48
|
+
export type AROpenOptions = PresentOptions & Omit<StartOptions, "mode" | "images" | "codes"> & {
|
|
49
49
|
/** The scene's ambient (its `environmentIntensity`) follows the estimated light; turns
|
|
50
50
|
* `lightEstimation` on. */
|
|
51
51
|
followLight?: boolean;
|
|
@@ -55,6 +55,13 @@ export type ARSceneEvents = {
|
|
|
55
55
|
plane: (event: ARPlaneEvent) => void;
|
|
56
56
|
light: (estimate: ARLightEstimate) => void;
|
|
57
57
|
error: (error: ARError) => void;
|
|
58
|
+
/** The session ended on its own — the user left it (the back gesture of an immersive web session)
|
|
59
|
+
* or the system took it — and the scene leaves: popped when it is the router's top, else closed
|
|
60
|
+
* (`onClose` is where an app opens what comes next, as after `scene.close()`). Not an error. Sent
|
|
61
|
+
* before the scene leaves. (A `Router.pop()` of an AR scene by the app does NOT end its session:
|
|
62
|
+
* a covered scene comes back with it — on the web an immersive session then stays on; `close()`
|
|
63
|
+
* ends it.) */
|
|
64
|
+
end: (reason: "user" | "system") => void;
|
|
58
65
|
};
|
|
59
66
|
export type AnchorEvents = NodeEvents & {
|
|
60
67
|
/** The anchor began tracking: found, placed, or regained after a relocalization. */
|
|
@@ -63,9 +70,10 @@ export type AnchorEvents = NodeEvents & {
|
|
|
63
70
|
untrack: () => void;
|
|
64
71
|
};
|
|
65
72
|
/**
|
|
66
|
-
* A node the AR session drives: it follows a place in the room (`scene.anchor(pose)`, `scene.root`)
|
|
67
|
-
*
|
|
68
|
-
* holds it now; `track` / `untrack` fire when
|
|
73
|
+
* A node the AR session drives: it follows a place in the room (`scene.anchor(pose)`, `scene.root`),
|
|
74
|
+
* an image (`scene.createAnchor(image)`) or a QR code (`scene.createQRAnchor(text, size)`). Put
|
|
75
|
+
* content under it. `tracked` says whether the session holds it now; `track` / `untrack` fire when
|
|
76
|
+
* that changes.
|
|
69
77
|
*/
|
|
70
78
|
export declare class Anchor extends Node {
|
|
71
79
|
/** Whether the session holds the anchor now (it moves with what it follows). */
|
|
@@ -76,16 +84,20 @@ export declare class Anchor extends Node {
|
|
|
76
84
|
remove(): void;
|
|
77
85
|
}
|
|
78
86
|
/**
|
|
79
|
-
* An anchor that follows an image (`scene.createAnchor(image, physicalWidth)`)
|
|
80
|
-
*
|
|
81
|
-
*
|
|
87
|
+
* An anchor that follows an image (`scene.createAnchor(image, physicalWidth)`) or a printed QR code
|
|
88
|
+
* (`scene.createQRAnchor(text, size)`, `code` its text): while tracked it sits on it — a rigid pose
|
|
89
|
+
* in metres, no scale: its centre, Y out of it, the image in X (its width; a code's top edge) and Z
|
|
90
|
+
* (towards its bottom). How big it is, is `extent`.
|
|
82
91
|
*/
|
|
83
92
|
export declare class ImageAnchor extends Anchor {
|
|
84
93
|
constructor(physicalWidth: number);
|
|
85
94
|
/** [width, height] in metres, along the anchor's X and Z: the session's estimate of the printed
|
|
86
95
|
* image, refined while it tracks it. Before the image is first found: [physicalWidth, 0] — the
|
|
87
|
-
* height comes with the image's proportions, which the session reads, at the first `track`.
|
|
96
|
+
* height comes with the image's proportions, which the session reads, at the first `track`. A QR
|
|
97
|
+
* code's is [size, size] from the start (a code is square). */
|
|
88
98
|
get extent(): [number, number];
|
|
99
|
+
/** The text of the QR code the anchor follows (`createQRAnchor`); null for an image's. */
|
|
100
|
+
get code(): string | null;
|
|
89
101
|
}
|
|
90
102
|
/** A surface the session found: a floor, a table, a wall. Its node sits at the plane's centre with Y
|
|
91
103
|
* along its normal; `extent` and `polygon` are in that space. */
|
|
@@ -124,11 +136,15 @@ export declare class ARScene extends Scene {
|
|
|
124
136
|
private _shown;
|
|
125
137
|
/** The open's start while it is out (`_begin`). `closed`: close() ended its session meanwhile — the
|
|
126
138
|
* half refuses a start its close cut short ("failed"), which is the close's doing: the open was
|
|
127
|
-
* superseded, not failed. `images`: the image targets changed meanwhile, which the
|
|
128
|
-
* carry (it read them when it was sent) — the set goes to the session once it runs. */
|
|
139
|
+
* superseded, not failed. `images` / `codes`: the image / QR targets changed meanwhile, which the
|
|
140
|
+
* start did not carry (it read them when it was sent) — the set goes to the session once it runs. */
|
|
129
141
|
private _startOut;
|
|
130
|
-
/** A `setImages` is due at the end of this turn (`
|
|
131
|
-
private
|
|
142
|
+
/** A `setImages` / `setCodes` is due at the end of this turn (`_setTargets`). */
|
|
143
|
+
private readonly _due;
|
|
144
|
+
/** The QR targets (`createQRAnchor`). */
|
|
145
|
+
private readonly _codes;
|
|
146
|
+
/** This host's half is older than the QR targets: said once. */
|
|
147
|
+
private _codesWarned;
|
|
132
148
|
private _options;
|
|
133
149
|
private _root;
|
|
134
150
|
/** The first horizontal plane's pose: where `root` is pinned. */
|
|
@@ -167,6 +183,15 @@ export declare class ARScene extends Scene {
|
|
|
167
183
|
* while not tracked. Before `open()`, or any time: on a running scene the image set is updated in
|
|
168
184
|
* place — the tracking, the planes and the anchors stay. */
|
|
169
185
|
createAnchor(source: FetchResponse | File, physicalWidth?: number): ImageAnchor;
|
|
186
|
+
/** An anchor that follows a printed QR code whose text is `text`: the session finds the symbol in
|
|
187
|
+
* the camera image and keeps the anchor on it, as on an image — a rigid pose in metres (its centre,
|
|
188
|
+
* Y out of the symbol, X along its top edge, Z towards its bottom), `anchor.extent` = [size, size].
|
|
189
|
+
* `size` = the printed side of the symbol in metres (its modules, no quiet zone): how far it is
|
|
190
|
+
* comes from it. Hidden while not tracked. Before `open()`, or any time: on a running scene the set
|
|
191
|
+
* of codes is updated in place. The web finds codes first (WebXR); on a host whose `ar` plugin does
|
|
192
|
+
* not track them the anchor is never tracked, and the console says so. `text` null or "" = ANY
|
|
193
|
+
* code: the first symbol decoded (a test, a plaque whose text the app does not know). */
|
|
194
|
+
createQRAnchor(text: string | null, size: number): ImageAnchor;
|
|
170
195
|
/** An anchor at `pose` (a hit test's, say): the session keeps it where it is in the room as its
|
|
171
196
|
* map of the world improves. `anchor.remove()` lets it go. */
|
|
172
197
|
anchor(pose: ARPoseLike): Anchor;
|
|
@@ -202,18 +227,19 @@ export declare class ARScene extends Scene {
|
|
|
202
227
|
private _emit;
|
|
203
228
|
private _startOptions;
|
|
204
229
|
private _targets;
|
|
230
|
+
private _codeTargets;
|
|
205
231
|
private _begin;
|
|
206
232
|
/** The session is over (closed, failed): every anchor untracked, the planes gone. */
|
|
207
233
|
private _drop;
|
|
208
234
|
private _end;
|
|
209
235
|
private _lost;
|
|
210
|
-
/** The image targets changed (one added or removed): a running session takes the
|
|
211
|
-
* (`setImages` — the tracking, the planes and the anchors stay). Sent
|
|
212
|
-
* targets added in a row are one set. While the open's start is out
|
|
213
|
-
* carried the set it read when it was sent); before a session there is
|
|
214
|
-
* carries the set. Nothing waits for it: a set the half refuses is a
|
|
215
|
-
* whose
|
|
216
|
-
private
|
|
236
|
+
/** The image targets or the QR targets changed (one added or removed): a running session takes the
|
|
237
|
+
* whole set in place (`setImages` / `setCodes` — the tracking, the planes and the anchors stay). Sent
|
|
238
|
+
* at the end of the turn: several targets added in a row are one set. While the open's start is out
|
|
239
|
+
* it waits for it (the start carried the set it read when it was sent); before a session there is
|
|
240
|
+
* nothing to tell — the start carries the set. Nothing waits for it: a set the half refuses is a
|
|
241
|
+
* console line, and an anchor whose target the session did not take never tracks. */
|
|
242
|
+
private _setTargets;
|
|
217
243
|
/** A wire call to the running session; rejects without one. */
|
|
218
244
|
private _wire;
|
|
219
245
|
/** Pin `anchor` at `pose` (where it is until the session holds it) — now, or when a session starts. */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ServiceChannel, type Entity, type EventReaders, type File } from "../../../../plugin";
|
|
2
|
-
import type { AREvents, ARParams, HitResult, HitTarget, ImageTarget, Pose, StartOptions } from "../contract";
|
|
2
|
+
import type { AREvents, ARParams, HitResult, HitTarget, ImageTarget, Pose, QRTarget, StartOptions } from "../contract";
|
|
3
3
|
/** What the halves register under. */
|
|
4
4
|
export declare const NAME = "ar";
|
|
5
5
|
/** The contract's version: the highest `@since` of its members. */
|
|
6
|
-
export declare const VERSION =
|
|
6
|
+
export declare const VERSION = 2;
|
|
7
7
|
/** The events whose payload holds a File: read before a listener sees it. */
|
|
8
8
|
export declare const READERS: EventReaders;
|
|
9
9
|
/** Start tracking; resolves at the first frame. A second start rejects "busy".
|
|
@@ -19,6 +19,13 @@ export declare const stop: (service: ServiceChannel<AREvents>) => Promise<void>;
|
|
|
19
19
|
* `StartOptions.images`.
|
|
20
20
|
* Rejects with "notRunning" / "failed". */
|
|
21
21
|
export declare const setImages: (service: ServiceChannel<AREvents>, images: ImageTarget[]) => Promise<void>;
|
|
22
|
+
/** The twin of `setImages` for QR codes: replace the set of QR targets of a RUNNING session
|
|
23
|
+
* without restarting it; a code no longer in the set stops driving its entity (`tracked: false`
|
|
24
|
+
* once if it was tracked). Before `start`, the codes are `StartOptions.codes`. A half that tracks
|
|
25
|
+
* no codes yet (the native ones) rejects "failed".
|
|
26
|
+
* Contract version 2: an older half rejects the call — ask `supports("setCodes")`.
|
|
27
|
+
* Rejects with "notRunning" / "failed". */
|
|
28
|
+
export declare const setCodes: (service: ServiceChannel<AREvents>, codes: QRTarget[]) => Promise<void>;
|
|
22
29
|
/** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
|
|
23
30
|
* `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
|
|
24
31
|
* a host without surfaces (the web).
|
|
@@ -35,7 +42,7 @@ export declare const release: (service: ServiceChannel<AREvents>, entity: Entity
|
|
|
35
42
|
* Rejects with "notRunning" / "failed". */
|
|
36
43
|
export declare const captureFrame: (service: ServiceChannel<AREvents>) => Promise<File>;
|
|
37
44
|
/** A method of the contract. */
|
|
38
|
-
export type Method = "start" | "stop" | "setImages" | "hitTest" | "anchor" | "release" | "captureFrame";
|
|
45
|
+
export type Method = "start" | "stop" | "setImages" | "setCodes" | "hitTest" | "anchor" | "release" | "captureFrame";
|
|
39
46
|
/** Whether this host has the service "ar" — and, given a method, a half new enough for it
|
|
40
47
|
* (a method added after the first version of the contract is tagged `@since`). */
|
|
41
48
|
export declare const supports: (method?: Method) => boolean;
|
|
@@ -55,6 +62,13 @@ export declare class ARBase extends ServiceChannel<AREvents> {
|
|
|
55
62
|
* `StartOptions.images`.
|
|
56
63
|
* Rejects with "notRunning" / "failed". */
|
|
57
64
|
setImages(images: ImageTarget[]): Promise<void>;
|
|
65
|
+
/** The twin of `setImages` for QR codes: replace the set of QR targets of a RUNNING session
|
|
66
|
+
* without restarting it; a code no longer in the set stops driving its entity (`tracked: false`
|
|
67
|
+
* once if it was tracked). Before `start`, the codes are `StartOptions.codes`. A half that tracks
|
|
68
|
+
* no codes yet (the native ones) rejects "failed".
|
|
69
|
+
* Contract version 2: an older half rejects the call — ask `supports("setCodes")`.
|
|
70
|
+
* Rejects with "notRunning" / "failed". */
|
|
71
|
+
setCodes(codes: QRTarget[]): Promise<void>;
|
|
58
72
|
/** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
|
|
59
73
|
* `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
|
|
60
74
|
* a host without surfaces (the web).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/audio-recorder/contract.d.ts — do not edit here.
|
|
2
|
+
// Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
|
|
3
|
+
// audio-recorder — audio recording to a file: voice messages, voice notes.
|
|
4
|
+
// A session is ONE recording (the app-facing `AudioRecorder` opens one per `start()`): start, pause
|
|
5
|
+
// / resume, then `stop()` for the file or `cancel()` to discard it. The microphone permission is the
|
|
6
|
+
// contract's (`@permission microphone` on start): the host asks, the halves never do.
|
|
7
|
+
//
|
|
8
|
+
// The format is the platform's encoder: AAC in an .m4a on iOS and Android, what the browser's
|
|
9
|
+
// MediaRecorder gives on the web (webm/opus in Chrome and Firefox, mp4 in Safari) — `mimeType`
|
|
10
|
+
// says which, the app's server decides whether to transcode.
|
|
11
|
+
|
|
12
|
+
import type { File, Service, f64 } from "../../../plugin"
|
|
13
|
+
|
|
14
|
+
export interface RecordOptions {
|
|
15
|
+
/** Kilobits per second of the encoded audio. Default 64 — speech; 128 for music. */
|
|
16
|
+
bitrate?: f64
|
|
17
|
+
/** Samples per second. Default 44100; the browser keeps its own. */
|
|
18
|
+
sampleRate?: f64
|
|
19
|
+
/** Stop on its own after this many ms: `ended` fires with "maxDuration", `stop()` then returns
|
|
20
|
+
* the recording. Default: no limit. */
|
|
21
|
+
maxDuration?: f64
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type EndReason = "maxDuration" | "interrupted" | "failed"
|
|
25
|
+
|
|
26
|
+
export interface Recording {
|
|
27
|
+
/** The audio file — `recording.m4a` / `.webm` / `.mp4` by the encoder. */
|
|
28
|
+
file: File
|
|
29
|
+
/** "audio/mp4", "audio/webm", … — the container, without codec parameters. */
|
|
30
|
+
mimeType: string
|
|
31
|
+
/** Seconds of audio (the pauses left out). */
|
|
32
|
+
duration: f64
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AudioRecorderParams {}
|
|
36
|
+
|
|
37
|
+
export interface AudioRecorderEvents {
|
|
38
|
+
/** The input level, 0..1, about ten times a second while recording (not while paused): a meter,
|
|
39
|
+
* a waveform. */
|
|
40
|
+
level: { value: f64 }
|
|
41
|
+
/** The recording stopped on its own: the limit (`maxDuration`), an interruption (a call came in),
|
|
42
|
+
* a failure of the encoder. What was recorded is kept until `stop()` (which returns it) or
|
|
43
|
+
* `cancel()`. */
|
|
44
|
+
ended: { reason: EndReason }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AudioRecorder extends Service<"audioRecorder", AudioRecorderParams, AudioRecorderEvents> {
|
|
48
|
+
/** Start recording. One recording per session: a second start rejects "busy".
|
|
49
|
+
* @permission microphone
|
|
50
|
+
* @rejects busy failed */
|
|
51
|
+
start(options?: RecordOptions): Promise<void>
|
|
52
|
+
/** Pause: the level stops, the duration does not grow. Rejects "notRecording" before a start
|
|
53
|
+
* or after the recording ended.
|
|
54
|
+
* @rejects notRecording failed */
|
|
55
|
+
pause(): Promise<void>
|
|
56
|
+
/** @rejects notRecording failed */
|
|
57
|
+
resume(): Promise<void>
|
|
58
|
+
/** Stop and get the recording (also after `ended`: the file was kept). Rejects "failed" when
|
|
59
|
+
* nothing could be encoded.
|
|
60
|
+
* @rejects notRecording failed */
|
|
61
|
+
stop(): Promise<Recording>
|
|
62
|
+
/** Stop and discard. Nothing to discard is not an error. */
|
|
63
|
+
cancel(): Promise<void>
|
|
64
|
+
}
|