lecodes-sdk 2.2.8 → 2.2.10
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 +42 -1
- package/dist/types/gl/Camera.d.ts +5 -2
- package/dist/types/gl/Node.d.ts +0 -7
- package/dist/types/gl/Scene.d.ts +6 -24
- package/dist/types/inject.d.ts +5 -1
- package/dist/types/plugin.d.ts +27 -0
- package/dist/types/plugins/gen/ar/sdk/ar.d.ts +228 -0
- package/dist/types/plugins/gen/ar/sdk/ar.gen.d.ts +73 -0
- package/dist/types/plugins/gen/ar/sdk/controls.d.ts +28 -0
- package/dist/types/plugins/gen/audio-recorder/sdk/audio-recorder.d.ts +43 -0
- package/dist/types/plugins/gen/audio-recorder/sdk/audio-recorder.gen.d.ts +47 -0
- package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +6 -2
- package/dist/types/plugins/gen/speech/sdk/speech.d.ts +44 -0
- package/dist/types/plugins/gen/speech/sdk/speech.gen.d.ts +39 -0
- package/dist/types/plugins/gen/webrtc/sdk/webrtc.d.ts +3 -0
- package/dist/types/runtime/permissions.d.ts +16 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types.json +1 -1
- package/package.json +1 -1
- package/prompts/ar.md +81 -48
- package/prompts/dist/2d-game.md +1567 -0
- package/prompts/dist/3d-app.md +1712 -0
- package/prompts/dist/ar-app.md +1585 -0
- package/prompts/dist/concept.md +49 -0
- package/prompts/dist/design.md +521 -0
- package/prompts/dist/ui-app.md +1175 -0
- package/prompts/index.md +3 -1
- package/src/animate/tween/Timeline.ts +175 -175
- package/src/bridges/device.d.ts +20 -6
- package/src/bridges/gl.d.ts +33 -19
- package/src/compile/header.ts +11 -4
- package/src/gl/Camera.ts +5 -2
- package/src/gl/CameraPlace.ts +52 -52
- package/src/gl/Light.ts +257 -257
- package/src/gl/Mesh.ts +120 -120
- package/src/gl/Node.ts +0 -8
- package/src/gl/Scene.ts +14 -81
- package/src/gl/animation/DynamicBone.ts +482 -482
- package/src/gl/scenarios.ts +291 -291
- package/src/inject.ts +13 -1
- package/src/plugin.ts +45 -2
- package/src/plugins/gen/ar/contract.d.ts +118 -0
- package/src/plugins/gen/ar/sdk/ar.gen.ts +112 -0
- package/src/plugins/gen/ar/sdk/ar.ts +699 -0
- package/src/plugins/gen/ar/sdk/controls.ts +148 -0
- package/src/plugins/gen/audio-recorder/contract.d.ts +64 -0
- package/src/plugins/gen/audio-recorder/sdk/audio-recorder.gen.ts +76 -0
- package/src/plugins/gen/audio-recorder/sdk/audio-recorder.ts +113 -0
- package/src/plugins/gen/geolocation/contract.d.ts +7 -5
- package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +6 -2
- package/src/plugins/gen/speech/contract.d.ts +67 -0
- package/src/plugins/gen/speech/sdk/speech.gen.ts +64 -0
- package/src/plugins/gen/speech/sdk/speech.ts +104 -0
- package/src/plugins/gen/webrtc/sdk/webrtc.ts +19 -14
- package/src/runtime/permissions.ts +40 -0
- package/src/server/auth/host.ts +1 -1
- package/src/ui/presentable.ts +5 -0
- package/src/ui/router.ts +4 -3
- package/src/version.ts +1 -1
- package/dist/types/gl/controls.d.ts +0 -19
- package/dist/types/plugins/permission.d.ts +0 -1
- package/src/gl/controls.ts +0 -95
- package/src/plugins/permission.ts +0 -8
package/src/plugin.ts
CHANGED
|
@@ -12,16 +12,41 @@ import { File as HostFile } from "./runtime/fetch"
|
|
|
12
12
|
import { ServiceClient } from "./runtime/service"
|
|
13
13
|
import { NativeView, NativeViewElement } from "./ui/NativeView"
|
|
14
14
|
import { _navEpoch } from "./ui/presentable"
|
|
15
|
-
import {
|
|
15
|
+
import { _ensurePermission } from "./runtime/permissions"
|
|
16
|
+
export { permissions, type PermissionKind, type PermissionState } from "./runtime/permissions"
|
|
16
17
|
import { appEventsOff, appEventsOn } from "./runtime/appEvents"
|
|
18
|
+
import { nodeOf, type Node } from "./gl/Node"
|
|
19
|
+
import type { Scene } from "./gl/Scene"
|
|
20
|
+
/** @internal Ask for an OS permission and reject unless it is granted: a wrapper's preparation, while
|
|
21
|
+
* the current destination stays on screen (ARScene's camera). */
|
|
22
|
+
export { _ensurePermission }
|
|
17
23
|
|
|
18
24
|
export type { PresentOptions } from "./ui/presentable"
|
|
19
25
|
// The SDK UI a wrapper builds its own chrome with (the browser plugin's bar over an embedded page) and
|
|
20
26
|
// the navigation it presents through. Re-exported, never copied: chisel keys on these names.
|
|
21
27
|
export { Presentable, Router, UIBox, UIButton, UIColumn, UIRow, UIScreen, UIText } from "./ui/UI"
|
|
22
28
|
export { theme } from "./ui/theme"
|
|
29
|
+
export { Emitter } from "./core/events"
|
|
23
30
|
export type { NativeView } from "./ui/NativeView"
|
|
24
31
|
export type { FetchResponse } from "./runtime/fetch"
|
|
32
|
+
// The 3D a wrapper builds on (the "ar" plugin's ARScene — docs/plans/ar-plugin-plan.md): the scene it
|
|
33
|
+
// extends, the node its anchors are, the mesh it draws a plane's grid with, the math of a pose, the
|
|
34
|
+
// touch a placement gesture follows, the System its per-frame work rides on (the scene's first frame).
|
|
35
|
+
// Re-exported, never copied, as the UI above.
|
|
36
|
+
export { Scene, type SceneOptions } from "./gl/Scene"
|
|
37
|
+
export { System } from "./core/Aspect"
|
|
38
|
+
export { Node, type NodeEvents } from "./gl/Node"
|
|
39
|
+
export { Mesh } from "./gl/Mesh"
|
|
40
|
+
export { Geometry } from "./gl/Geometry"
|
|
41
|
+
export { Material } from "./gl/Material"
|
|
42
|
+
export { Vec2, Vec3, type Vec3Like } from "./math/vec"
|
|
43
|
+
export { Quat, type QuatLike } from "./math/quat"
|
|
44
|
+
export { Mat4 } from "./math/mat4"
|
|
45
|
+
export type { ClickEvent, TouchStartEvent } from "./runtime/touch"
|
|
46
|
+
export type { DismissOptions } from "./ui/presentable"
|
|
47
|
+
/** @internal The navigation epoch: a destination that prepares before it presents (ARScene) reads it
|
|
48
|
+
* before its awaits and gives up when another navigation moved it meanwhile. */
|
|
49
|
+
export { _navEpoch }
|
|
25
50
|
|
|
26
51
|
// ---- the contract's vocabulary ---------------------------------------------------------------------
|
|
27
52
|
|
|
@@ -35,6 +60,17 @@ export type f64 = number
|
|
|
35
60
|
* never enter the JS heap. For bytes the app must read itself, a contract says `Uint8Array`. */
|
|
36
61
|
export type File = HostFile
|
|
37
62
|
|
|
63
|
+
/** A 3D node of the app (`Node`) a plugin drives — an AR anchor, the camera: its entity id on the
|
|
64
|
+
* wire (u32, `node._h.id`); the native half receives `Entity` (a struct over the id) and moves it
|
|
65
|
+
* through the host's gl facade (`engine.gl` / `host.gl`), never creating or freeing it. One the half
|
|
66
|
+
* names back (an event's) is the app's own Node again. */
|
|
67
|
+
export type Entity = Node
|
|
68
|
+
|
|
69
|
+
/** A 3D scene of the app (`Scene`) a plugin drives — its camera's projection, its background: the
|
|
70
|
+
* scene id on the wire (`scene._id`); the native half receives `Scene3D`. From the app to the host
|
|
71
|
+
* only: a half never names a scene back. */
|
|
72
|
+
export type Scene3D = Scene
|
|
73
|
+
|
|
38
74
|
/** Any JSON value, opaque to the generator (GeoJSON, a developer's own payload): it crosses as is
|
|
39
75
|
* and the native half receives the platform's untyped value. */
|
|
40
76
|
export type Json = null | boolean | number | string | Json[] | { [key: string]: Json }
|
|
@@ -175,6 +211,13 @@ export const _toFile = (wire: { systemId: number, name: string, size: number }):
|
|
|
175
211
|
export const _fromFile = (file: File): { systemId: number, name: string, size: number } =>
|
|
176
212
|
({ systemId: file._h.id, name: file.name, size: file.size })
|
|
177
213
|
|
|
214
|
+
/** @internal The generated wrapper's: an Entity / a Scene3D the app hands a half crosses as its id… */
|
|
215
|
+
export const _fromEntity = (node: Entity): number => node._h.id
|
|
216
|
+
export const _fromScene3D = (scene: Scene3D): number => scene._id
|
|
217
|
+
/** @internal …and an Entity a half names back is the app's own Node (the pinned one, the app's live
|
|
218
|
+
* handle, else a wrapper over the engine's id). */
|
|
219
|
+
export const _toEntity = (id: number): Entity => nodeOf(id)
|
|
220
|
+
|
|
178
221
|
// ---- for the hand-written part ---------------------------------------------------------------------
|
|
179
222
|
|
|
180
223
|
/** Hear the app's "url" events (a deep link while the app runs — an OAuth redirect, a checkout's
|
|
@@ -197,7 +240,7 @@ export const prepareCamera = async (view: ViewChannel<any>): Promise<void> => {
|
|
|
197
240
|
throw new Error(`${view._iface} isn't available on this host — it needs a registered "${view.viewName}" view.`)
|
|
198
241
|
}
|
|
199
242
|
const epoch = _navEpoch()
|
|
200
|
-
await
|
|
243
|
+
await _ensurePermission("camera")
|
|
201
244
|
if (_navEpoch() !== epoch) {
|
|
202
245
|
throw new Error(`${view._iface}.open() superseded: another destination was opened while preparing`)
|
|
203
246
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
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
|
+
import type { Service, File, Entity, Scene3D, f64 } from "../../../plugin"
|
|
9
|
+
|
|
10
|
+
export type ARMode = "world" | "images"
|
|
11
|
+
export type PlaneDetection = "horizontal" | "vertical" | "both" | "none"
|
|
12
|
+
export type WorldAlignment = "gravity" | "gravityAndHeading"
|
|
13
|
+
export type TrackingState = "notAvailable" | "limited" | "normal"
|
|
14
|
+
export type TrackingReason = "initializing" | "excessiveMotion" | "insufficientFeatures" | "relocalizing"
|
|
15
|
+
export type PlaneType = "horizontal" | "vertical"
|
|
16
|
+
export type PlaneChange = "added" | "updated" | "removed"
|
|
17
|
+
export type HitTarget = "planes" | "estimated" | "any"
|
|
18
|
+
export type ARErrorCode = "unavailable" | "camera" | "failed"
|
|
19
|
+
|
|
20
|
+
export interface Vec3 { x: f64, y: f64, z: f64 }
|
|
21
|
+
export interface Quat { x: f64, y: f64, z: f64, w: f64 }
|
|
22
|
+
/** A pose in the scene's world: metres, Y up. */
|
|
23
|
+
export interface Pose { position: Vec3, rotation: Quat }
|
|
24
|
+
|
|
25
|
+
export interface ImageTarget {
|
|
26
|
+
image: File
|
|
27
|
+
/** The printed width, metres: the size the session starts from (the height by the image's
|
|
28
|
+
* proportions) — the framework refines it (the `image` event's `extent`). */
|
|
29
|
+
physicalWidth: f64
|
|
30
|
+
/** The entity that follows the image (its Anchor node). */
|
|
31
|
+
entity: Entity
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface StartOptions {
|
|
35
|
+
/** Default "world". "images": image tracking only (no planes, no hit test; iOS runs
|
|
36
|
+
* ARImageTrackingConfiguration, which is lighter). */
|
|
37
|
+
mode?: ARMode
|
|
38
|
+
/** Default "horizontal". Ignored in "images". */
|
|
39
|
+
planes?: PlaneDetection
|
|
40
|
+
images?: ImageTarget[]
|
|
41
|
+
/** Default "gravity". "gravityAndHeading": -Z points north (the compass). */
|
|
42
|
+
worldAlignment?: WorldAlignment
|
|
43
|
+
/** Deliver `light` events. Default false. */
|
|
44
|
+
lightEstimation?: boolean
|
|
45
|
+
/** Default true. */
|
|
46
|
+
autoFocus?: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface HitResult {
|
|
50
|
+
pose: Pose
|
|
51
|
+
/** "plane": on a detected plane (`plane` = its id); "estimated": on a point of the feature
|
|
52
|
+
* cloud / an estimated plane. */
|
|
53
|
+
type: "plane" | "estimated"
|
|
54
|
+
plane?: f64
|
|
55
|
+
/** Metres from the camera. */
|
|
56
|
+
distance: f64
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Both are ARScene's, always sent. Optional because a channel's params are (the generator's rule: a
|
|
60
|
+
* param a half cannot read has no one to reject to); a session opened without them rejects `start`
|
|
61
|
+
* with "failed". */
|
|
62
|
+
export interface ARParams {
|
|
63
|
+
scene?: Scene3D
|
|
64
|
+
/** The scene's camera entity: the session sets its matrix and the scene's projection each frame. */
|
|
65
|
+
camera?: Entity
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AREvents {
|
|
69
|
+
/** The tracking quality; the first event of a session. */
|
|
70
|
+
tracking: { state: TrackingState, reason?: TrackingReason }
|
|
71
|
+
/** A detected plane: added, moved / grown (throttled: a change over 1 cm, at most 10 per second),
|
|
72
|
+
* or gone (`mergedInto` = the plane that absorbed it, ARCore). `pose` = the plane's centre, its
|
|
73
|
+
* Y the normal; `extent` = [width, height] metres; `polygon` = the boundary as x,z pairs in the
|
|
74
|
+
* plane's space. */
|
|
75
|
+
plane: { change: PlaneChange, id: f64, type: PlaneType, pose: Pose, extent: f64[], polygon: f64[], mergedInto?: f64 }
|
|
76
|
+
/** An image target found, lost, or its size refined. While tracked its entity is driven: a rigid
|
|
77
|
+
* pose in metres, no scale — the image's centre, Y out of it, the image in XZ (X along its width,
|
|
78
|
+
* Z towards its bottom). `extent` = [width, height] metres, the framework's estimate (sent again
|
|
79
|
+
* when it changes by a millimetre, at most 10 per second); the last one when lost. */
|
|
80
|
+
image: { entity: Entity, tracked: boolean, extent: f64[] }
|
|
81
|
+
/** An anchor made by `anchor()` lost or regained after a relocalization. */
|
|
82
|
+
anchor: { entity: Entity, tracked: boolean }
|
|
83
|
+
/** The estimated light (`lightEstimation`): lumens-ish ambient intensity (1000 = ARKit's
|
|
84
|
+
* neutral), colour temperature in kelvin; at most 10 per second. */
|
|
85
|
+
light: { intensity: f64, temperature: f64 }
|
|
86
|
+
/** The session failed and is over: "unavailable" (no ARKit / ARCore support, ARCore not
|
|
87
|
+
* installed and declined), "camera" (the camera could not be used), "failed". */
|
|
88
|
+
error: { code: ARErrorCode, message: string }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface AR extends Service<"ar", ARParams, AREvents> {
|
|
92
|
+
/** Start tracking; resolves at the first frame. A second start rejects "busy".
|
|
93
|
+
* @permission camera
|
|
94
|
+
* @rejects busy unavailable failed */
|
|
95
|
+
start(options?: StartOptions): Promise<void>
|
|
96
|
+
/** Stop tracking: the camera is released, the scene's projection and background cleared. Not
|
|
97
|
+
* running is not an error. */
|
|
98
|
+
stop(): Promise<void>
|
|
99
|
+
/** Replace the set of image targets of a RUNNING session without restarting it (the tracking, the
|
|
100
|
+
* planes and the anchors stay); an image no longer in the set stops driving its entity (its
|
|
101
|
+
* `image` event says `tracked: false` once if it was tracked). Before `start`, the images are
|
|
102
|
+
* `StartOptions.images`.
|
|
103
|
+
* @rejects notRunning failed */
|
|
104
|
+
setImages(images: ImageTarget[]): Promise<void>
|
|
105
|
+
/** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
|
|
106
|
+
* `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
|
|
107
|
+
* a host without surfaces (the web).
|
|
108
|
+
* @rejects notRunning */
|
|
109
|
+
hitTest(x: f64, y: f64, target?: HitTarget): Promise<HitResult[]>
|
|
110
|
+
/** Pin `entity` to `pose`: the host's anchor drives its matrix from now on (and keeps it true
|
|
111
|
+
* as the world map refines). @rejects notRunning */
|
|
112
|
+
anchor(entity: Entity, pose: Pose): Promise<void>
|
|
113
|
+
/** Stop driving `entity` (its matrix stays where it was). */
|
|
114
|
+
release(entity: Entity): Promise<void>
|
|
115
|
+
/** The latest camera frame as a JPEG (the raw image, not the composite — that is
|
|
116
|
+
* `Scene.capture()`). @rejects notRunning failed */
|
|
117
|
+
captureFrame(): Promise<File>
|
|
118
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/ar/sdk/ar.gen.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
|
+
// GENERATED by `lecodes plugin gen` from contract.d.ts — do not edit.
|
|
4
|
+
// ar: the service "ar", contract version 1.
|
|
5
|
+
|
|
6
|
+
import { ServiceChannel, _fromEntity, _fromFile, _fromScene3D, _toEntity, _toFile, type Entity, type EventReaders, type File } from "../../../../plugin"
|
|
7
|
+
import type { AREvents, ARParams, HitResult, HitTarget, ImageTarget, Pose, StartOptions } from "../contract"
|
|
8
|
+
|
|
9
|
+
/** What the halves register under. */
|
|
10
|
+
export const NAME = "ar"
|
|
11
|
+
/** The contract's version: the highest `@since` of its members. */
|
|
12
|
+
export const VERSION = 1
|
|
13
|
+
|
|
14
|
+
const _fromStartOptions = (v: StartOptions): any => ({ ...v, images: v.images === undefined ? undefined : v.images.map((x: any) => _fromImageTarget(x)) })
|
|
15
|
+
const _fromImageTarget = (v: ImageTarget): any => ({ ...v, image: _fromFile(v.image), entity: _fromEntity(v.entity) })
|
|
16
|
+
const _fromARParams = (v: ARParams): any => ({ ...v, scene: v.scene === undefined ? undefined : _fromScene3D(v.scene), camera: v.camera === undefined ? undefined : _fromEntity(v.camera) })
|
|
17
|
+
|
|
18
|
+
/** The events whose payload holds a File: read before a listener sees it. */
|
|
19
|
+
export const READERS: EventReaders = {
|
|
20
|
+
image: (w: any) => ({ ...w, entity: _toEntity(w.entity) }),
|
|
21
|
+
anchor: (w: any) => ({ ...w, entity: _toEntity(w.entity) }),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ---- the wire, call by call ----------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
/** Start tracking; resolves at the first frame. A second start rejects "busy".
|
|
27
|
+
* Needs the "camera" permission: the host asks before the call, "denied" when refused.
|
|
28
|
+
* Rejects with "denied" / "busy" / "unavailable" / "failed". */
|
|
29
|
+
export const start = (service: ServiceChannel<AREvents>, options?: StartOptions): Promise<void> =>
|
|
30
|
+
service._call("start", [options === undefined ? undefined : _fromStartOptions(options)])
|
|
31
|
+
|
|
32
|
+
/** Stop tracking: the camera is released, the scene's projection and background cleared. Not
|
|
33
|
+
* running is not an error. */
|
|
34
|
+
export const stop = (service: ServiceChannel<AREvents>): Promise<void> =>
|
|
35
|
+
service._call("stop", [])
|
|
36
|
+
|
|
37
|
+
/** Replace the set of image targets of a RUNNING session without restarting it (the tracking, the
|
|
38
|
+
* planes and the anchors stay); an image no longer in the set stops driving its entity (its
|
|
39
|
+
* `image` event says `tracked: false` once if it was tracked). Before `start`, the images are
|
|
40
|
+
* `StartOptions.images`.
|
|
41
|
+
* Rejects with "notRunning" / "failed". */
|
|
42
|
+
export const setImages = (service: ServiceChannel<AREvents>, images: ImageTarget[]): Promise<void> =>
|
|
43
|
+
service._call("setImages", [images.map((x: any) => _fromImageTarget(x))])
|
|
44
|
+
|
|
45
|
+
/** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
|
|
46
|
+
* `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
|
|
47
|
+
* a host without surfaces (the web).
|
|
48
|
+
* Rejects with "notRunning". */
|
|
49
|
+
export const hitTest = (service: ServiceChannel<AREvents>, x: number, y: number, target?: HitTarget): Promise<HitResult[]> =>
|
|
50
|
+
service._call("hitTest", [x, y, target])
|
|
51
|
+
|
|
52
|
+
/** Pin `entity` to `pose`: the host's anchor drives its matrix from now on (and keeps it true
|
|
53
|
+
* as the world map refines).
|
|
54
|
+
* Rejects with "notRunning". */
|
|
55
|
+
export const anchor = (service: ServiceChannel<AREvents>, entity: Entity, pose: Pose): Promise<void> =>
|
|
56
|
+
service._call("anchor", [_fromEntity(entity), pose])
|
|
57
|
+
|
|
58
|
+
/** Stop driving `entity` (its matrix stays where it was). */
|
|
59
|
+
export const release = (service: ServiceChannel<AREvents>, entity: Entity): Promise<void> =>
|
|
60
|
+
service._call("release", [_fromEntity(entity)])
|
|
61
|
+
|
|
62
|
+
/** The latest camera frame as a JPEG (the raw image, not the composite — that is
|
|
63
|
+
* `Scene.capture()`).
|
|
64
|
+
* Rejects with "notRunning" / "failed". */
|
|
65
|
+
export const captureFrame = (service: ServiceChannel<AREvents>): Promise<File> =>
|
|
66
|
+
service._call("captureFrame", []).then((w: any) => _toFile(w))
|
|
67
|
+
|
|
68
|
+
// ---- what the host's half has --------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
/** A method of the contract. */
|
|
71
|
+
export type Method = "start" | "stop" | "setImages" | "hitTest" | "anchor" | "release" | "captureFrame"
|
|
72
|
+
|
|
73
|
+
/** Whether this host has the service "ar" — and, given a method, a half new enough for it
|
|
74
|
+
* (a method added after the first version of the contract is tagged `@since`). */
|
|
75
|
+
export const supports = (method?: Method): boolean =>
|
|
76
|
+
ServiceChannel.supports(NAME) // every method is of version 1
|
|
77
|
+
|
|
78
|
+
// ---- the client -----------------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
/** The service "ar" with the wire's methods as its own. */
|
|
81
|
+
export class ARBase extends ServiceChannel<AREvents> {
|
|
82
|
+
constructor(params: ARParams = {}) { super("AR", NAME, _fromARParams(params), READERS) }
|
|
83
|
+
|
|
84
|
+
/** Start tracking; resolves at the first frame. A second start rejects "busy".
|
|
85
|
+
* Needs the "camera" permission: the host asks before the call, "denied" when refused.
|
|
86
|
+
* Rejects with "denied" / "busy" / "unavailable" / "failed". */
|
|
87
|
+
start(options?: StartOptions): Promise<void> { return start(this, options) }
|
|
88
|
+
/** Stop tracking: the camera is released, the scene's projection and background cleared. Not
|
|
89
|
+
* running is not an error. */
|
|
90
|
+
stop(): Promise<void> { return stop(this) }
|
|
91
|
+
/** Replace the set of image targets of a RUNNING session without restarting it (the tracking, the
|
|
92
|
+
* planes and the anchors stay); an image no longer in the set stops driving its entity (its
|
|
93
|
+
* `image` event says `tracked: false` once if it was tracked). Before `start`, the images are
|
|
94
|
+
* `StartOptions.images`.
|
|
95
|
+
* Rejects with "notRunning" / "failed". */
|
|
96
|
+
setImages(images: ImageTarget[]): Promise<void> { return setImages(this, images) }
|
|
97
|
+
/** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
|
|
98
|
+
* `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
|
|
99
|
+
* a host without surfaces (the web).
|
|
100
|
+
* Rejects with "notRunning". */
|
|
101
|
+
hitTest(x: number, y: number, target?: HitTarget): Promise<HitResult[]> { return hitTest(this, x, y, target) }
|
|
102
|
+
/** Pin `entity` to `pose`: the host's anchor drives its matrix from now on (and keeps it true
|
|
103
|
+
* as the world map refines).
|
|
104
|
+
* Rejects with "notRunning". */
|
|
105
|
+
anchor(entity: Entity, pose: Pose): Promise<void> { return anchor(this, entity, pose) }
|
|
106
|
+
/** Stop driving `entity` (its matrix stays where it was). */
|
|
107
|
+
release(entity: Entity): Promise<void> { return release(this, entity) }
|
|
108
|
+
/** The latest camera frame as a JPEG (the raw image, not the composite — that is
|
|
109
|
+
* `Scene.capture()`).
|
|
110
|
+
* Rejects with "notRunning" / "failed". */
|
|
111
|
+
captureFrame(): Promise<File> { return captureFrame(this) }
|
|
112
|
+
}
|