@opsydyn/effect-three-foldkit 0.3.0 → 0.6.4
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/CHANGELOG.md +51 -0
- package/README.md +4 -2
- package/dist/assetRegistry.d.ts +49 -0
- package/dist/assetRegistry.d.ts.map +1 -0
- package/dist/assets.d.ts +18 -5
- package/dist/assets.d.ts.map +1 -1
- package/dist/boxSelect.d.ts +150 -0
- package/dist/boxSelect.d.ts.map +1 -0
- package/dist/dom/canvas.d.ts +7 -0
- package/dist/dom/canvas.d.ts.map +1 -1
- package/dist/dom/pointer.d.ts.map +1 -1
- package/dist/dom/webgl.d.ts.map +1 -1
- package/dist/dom/webgpu.d.ts.map +1 -1
- package/dist/element.d.ts +60 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/environment.d.ts +6 -0
- package/dist/environment.d.ts.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +745 -60
- package/dist/index.js.map +19 -12
- package/dist/inspector.d.ts +24 -0
- package/dist/inspector.d.ts.map +1 -0
- package/dist/measuringTape.d.ts +76 -0
- package/dist/measuringTape.d.ts.map +1 -0
- package/dist/mount.d.ts.map +1 -1
- package/dist/pivotControls.d.ts +106 -0
- package/dist/pivotControls.d.ts.map +1 -0
- package/dist/transformGizmo.d.ts +158 -0
- package/dist/transformGizmo.d.ts.map +1 -0
- package/dist/view.d.ts +23 -0
- package/dist/view.d.ts.map +1 -1
- package/dist/viewportGizmo.d.ts +146 -0
- package/dist/viewportGizmo.d.ts.map +1 -0
- package/package.json +8 -8
- package/src/assetRegistry.ts +218 -0
- package/src/assets.ts +96 -39
- package/src/boxSelect.ts +158 -0
- package/src/dom/canvas.ts +12 -0
- package/src/dom/pointer.ts +14 -1
- package/src/dom/webgl.ts +15 -0
- package/src/dom/webgpu.ts +15 -0
- package/src/element.ts +6 -0
- package/src/environment.ts +6 -1
- package/src/index.ts +10 -0
- package/src/inspector.ts +112 -0
- package/src/measuringTape.ts +124 -0
- package/src/mount.ts +24 -1
- package/src/pivotControls.ts +118 -0
- package/src/transformGizmo.ts +131 -0
- package/src/view.ts +28 -0
- package/src/viewportGizmo.ts +119 -0
package/src/view.ts
CHANGED
|
@@ -44,11 +44,17 @@ export type DragDetail = typeof SceneSchema.DragDetail.Type;
|
|
|
44
44
|
*/
|
|
45
45
|
export type Gesture = typeof SceneSchema.Gesture.Type;
|
|
46
46
|
|
|
47
|
+
/** Detail of the end of a gesture that moved: the press's button, modifiers, mesh and view. */
|
|
48
|
+
export type GestureEndDetail = typeof SceneSchema.GestureEndDetail.Type;
|
|
49
|
+
|
|
47
50
|
/** Detail of collision events: the two bodies' keys, and whether they began touching. */
|
|
48
51
|
export type CollisionDetail = typeof SceneSchema.CollisionDetail.Type;
|
|
49
52
|
|
|
50
53
|
/** Detail of contact force events: the two bodies, and how hard they pushed. */
|
|
51
54
|
export type ContactForceDetail = typeof SceneSchema.ContactForceDetail.Type;
|
|
55
|
+
export type PhysicsReachedDetail = typeof SceneSchema.PhysicsReachedDetail.Type;
|
|
56
|
+
export type AssetLoadedDetail = typeof SceneSchema.AssetLoadedDetail.Type;
|
|
57
|
+
export type AssetFailedDetail = typeof SceneSchema.AssetFailedDetail.Type;
|
|
52
58
|
|
|
53
59
|
/** Detail of measured events: a node's world bounding box. */
|
|
54
60
|
export type MeasuredDetail = typeof SceneSchema.MeasuredDetail.Type;
|
|
@@ -119,6 +125,11 @@ export type ViewConfig<Message> = {
|
|
|
119
125
|
* the camera stays in the Model.
|
|
120
126
|
*/
|
|
121
127
|
readonly onGesture?: (gesture: Gesture) => Message;
|
|
128
|
+
/**
|
|
129
|
+
* A gesture that moved has ended (released, or the pointer left): finish what it drew,
|
|
130
|
+
* such as a `BoxSelect` box. Not after a press that never moved.
|
|
131
|
+
*/
|
|
132
|
+
readonly onGestureEnd?: (detail: GestureEndDetail) => Message;
|
|
122
133
|
/**
|
|
123
134
|
* A gesture is about to begin on a camera turning by itself (`autoRotate`): keep where
|
|
124
135
|
* it got to with `autoRotated(model.camera, detail)` (a view's camera when `view` names
|
|
@@ -136,6 +147,12 @@ export type ViewConfig<Message> = {
|
|
|
136
147
|
* set the threshold high.
|
|
137
148
|
*/
|
|
138
149
|
readonly onContactForce?: (detail: ContactForceDetail) => Message;
|
|
150
|
+
/**
|
|
151
|
+
* A world on `physics({ until })` reached that step and waits there, with its run and a
|
|
152
|
+
* checksum of its bodies' poses (ADR-014): the moment to apply the inputs due at that
|
|
153
|
+
* step and move `until` on. It follows the collisions of the steps that led there.
|
|
154
|
+
*/
|
|
155
|
+
readonly onPhysicsReached?: (detail: PhysicsReachedDetail) => Message;
|
|
139
156
|
/**
|
|
140
157
|
* A node listed in the scene's `measure` has a new world bounding box (Drei's `Bounds`
|
|
141
158
|
* measuring, as a Message): fit the camera to it with `fitCamera`.
|
|
@@ -189,6 +206,13 @@ export type ViewConfig<Message> = {
|
|
|
189
206
|
readonly onPerformanceChange?: (detail: PerformanceDetail) => Message;
|
|
190
207
|
/** A scene update was rejected; the scene keeps its last good state. */
|
|
191
208
|
readonly onError?: (detail: { readonly error: SceneError }) => Message;
|
|
209
|
+
/**
|
|
210
|
+
* An asset the scene asks for (`scene({ assets })`) has loaded and is lent under its id:
|
|
211
|
+
* at once, for one another scene loaded already. A model's parts and clips come with it.
|
|
212
|
+
*/
|
|
213
|
+
readonly onAssetLoaded?: (detail: AssetLoadedDetail) => Message;
|
|
214
|
+
/** An asset the scene asks for could not be loaded, and why. */
|
|
215
|
+
readonly onAssetFailed?: (detail: AssetFailedDetail) => Message;
|
|
192
216
|
/**
|
|
193
217
|
* What each scene update did (operations by kind) and the GPU resources held afterwards.
|
|
194
218
|
* Fires at startup and after every update that changed something, never for no-ops.
|
|
@@ -226,6 +250,7 @@ export const view = <Message>(
|
|
|
226
250
|
Option.map(given(config.onDragEnd), element.OnSceneDragEnd),
|
|
227
251
|
Option.map(given(config.onCollision), element.OnSceneCollision),
|
|
228
252
|
Option.map(given(config.onContactForce), element.OnSceneContactForce),
|
|
253
|
+
Option.map(given(config.onPhysicsReached), element.OnScenePhysicsReached),
|
|
229
254
|
Option.map(given(config.onMeasured), element.OnSceneMeasured),
|
|
230
255
|
Option.map(given(config.onLabelMeasured), element.OnSceneLabelMeasured),
|
|
231
256
|
Option.map(given(config.onXRSession), element.OnSceneXr),
|
|
@@ -239,8 +264,11 @@ export const view = <Message>(
|
|
|
239
264
|
Option.map(given(config.onGesture), (toMessage) =>
|
|
240
265
|
element.OnSceneGesture((detail) => toMessage(detail.gesture)),
|
|
241
266
|
),
|
|
267
|
+
Option.map(given(config.onGestureEnd), element.OnSceneGestureEnd),
|
|
242
268
|
Option.map(given(config.onPerformanceChange), element.OnScenePerformance),
|
|
243
269
|
Option.map(given(config.onError), element.OnSceneError),
|
|
270
|
+
Option.map(given(config.onAssetLoaded), element.OnSceneAssetLoaded),
|
|
271
|
+
Option.map(given(config.onAssetFailed), element.OnSceneAssetFailed),
|
|
244
272
|
Option.map(given(config.onReconciled), element.OnSceneReconciled),
|
|
245
273
|
Option.map(given(config.className), h.Class),
|
|
246
274
|
]);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A viewport gizmo as a Foldkit submodel (roadmap, "Beyond parity" 5), after Drei's
|
|
3
|
+
* `GizmoHelper` with `GizmoViewport`: the core's `viewportGizmoNodes` in a scene's
|
|
4
|
+
* `hud`, its heads highlighted under the pointer, and a press on one turning the camera
|
|
5
|
+
* to look from that axis. The parent owns the camera; the submodel owns the highlight:
|
|
6
|
+
*
|
|
7
|
+
* - `Entered` and `Left` (the scene's pointer enter and leave, with the hit key)
|
|
8
|
+
* highlight a head, and clear it;
|
|
9
|
+
* - `Pressed` (a pointer down, with the hit key and the camera) reports
|
|
10
|
+
* `Turned { camera }`, the camera looking from that head's axis, for the parent to
|
|
11
|
+
* keep. A camera with `damping` eases round to it, as Drei's tween does.
|
|
12
|
+
*
|
|
13
|
+
* `hud(model, camera)` is the scene's HUD with the gizmo in it; `nodes` the gizmo alone,
|
|
14
|
+
* for a HUD with more in it.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import {
|
|
19
|
+
type Camera,
|
|
20
|
+
cameraAlong,
|
|
21
|
+
hud as describeHud,
|
|
22
|
+
type Hud,
|
|
23
|
+
type HudAlignment,
|
|
24
|
+
SceneSchema,
|
|
25
|
+
type SceneNode,
|
|
26
|
+
type ViewAxis,
|
|
27
|
+
viewportAxisOf,
|
|
28
|
+
viewportGizmoNodes,
|
|
29
|
+
} from '@opsydyn/effect-three';
|
|
30
|
+
import { Option, Schema } from 'effect';
|
|
31
|
+
import { defineMessageUnion } from 'foldkit/message';
|
|
32
|
+
import type { ReturnWithOutMessage } from 'foldkit/update';
|
|
33
|
+
|
|
34
|
+
const Axis = Schema.Literals(['x', 'y', 'z', '-x', '-y', '-z']);
|
|
35
|
+
|
|
36
|
+
export const Model = Schema.Struct({
|
|
37
|
+
/** The key its nodes are drawn under: `${key}:x`, `${key}:-y` and so on. */
|
|
38
|
+
key: Schema.String,
|
|
39
|
+
/** The head under the pointer. */
|
|
40
|
+
hovered: Schema.NullOr(Axis),
|
|
41
|
+
/** Leaves out the negative heads. */
|
|
42
|
+
hideNegativeAxes: Schema.Boolean,
|
|
43
|
+
});
|
|
44
|
+
export type Model = typeof Model.Type;
|
|
45
|
+
|
|
46
|
+
export const Message = defineMessageUnion({
|
|
47
|
+
Entered: { key: Schema.String },
|
|
48
|
+
Left: { key: Schema.String },
|
|
49
|
+
Pressed: { key: Schema.String, camera: SceneSchema.Camera },
|
|
50
|
+
});
|
|
51
|
+
export type Message = typeof Message.Type;
|
|
52
|
+
|
|
53
|
+
export const OutMessage = defineMessageUnion({
|
|
54
|
+
/** The camera, looking from the axis of the head pressed. */
|
|
55
|
+
Turned: { camera: SceneSchema.Camera },
|
|
56
|
+
});
|
|
57
|
+
export type OutMessage = typeof OutMessage.Type;
|
|
58
|
+
|
|
59
|
+
/** A gizmo drawn under `key`, every head shown unless `hideNegativeAxes`. */
|
|
60
|
+
export const init = (
|
|
61
|
+
key: string,
|
|
62
|
+
options: { readonly hideNegativeAxes?: boolean } = {},
|
|
63
|
+
): Model => ({
|
|
64
|
+
key,
|
|
65
|
+
hovered: null,
|
|
66
|
+
hideNegativeAxes: options.hideNegativeAxes ?? false,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
type Return = ReturnWithOutMessage<Model, Message, OutMessage>;
|
|
70
|
+
|
|
71
|
+
/** Which head a hit `key` is on, if it is one of this gizmo's. */
|
|
72
|
+
export const axisOf = (model: Model, key: string): Option.Option<ViewAxis> =>
|
|
73
|
+
Option.fromNullishOr(viewportAxisOf(model.key, key));
|
|
74
|
+
|
|
75
|
+
const entered = (model: Model, key: string): Model =>
|
|
76
|
+
Option.match(axisOf(model, key), {
|
|
77
|
+
onNone: () => model,
|
|
78
|
+
onSome: (axis) => ({ ...model, hovered: axis }),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/** Leaving a head clears the highlight; leaving anything else changes nothing. */
|
|
82
|
+
const left = (model: Model, key: string): Model =>
|
|
83
|
+
Option.match(axisOf(model, key), {
|
|
84
|
+
onNone: () => model,
|
|
85
|
+
onSome: () => ({ ...model, hovered: null }),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const pressed = (model: Model, key: string, camera: Camera): Return =>
|
|
89
|
+
Option.match(axisOf(model, key), {
|
|
90
|
+
onNone: () => ({ model }),
|
|
91
|
+
onSome: (axis) => ({
|
|
92
|
+
model,
|
|
93
|
+
outMessage: OutMessage.Turned({ camera: cameraAlong(camera, axis) }),
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export const update = (model: Model, message: Message): Return =>
|
|
98
|
+
Message.match<Return>(message, {
|
|
99
|
+
Entered: ({ key }) => ({ model: entered(model, key) }),
|
|
100
|
+
Left: ({ key }) => ({ model: left(model, key) }),
|
|
101
|
+
Pressed: ({ key, camera }) => pressed(model, key, camera),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/** The gizmo for `camera`, for a HUD. */
|
|
105
|
+
export const nodes = (model: Model, camera: Camera): SceneNode =>
|
|
106
|
+
viewportGizmoNodes(model.key, camera, {
|
|
107
|
+
hovered: model.hovered,
|
|
108
|
+
hideNegativeAxes: model.hideNegativeAxes,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/** A HUD holding only the gizmo, 80 pixels in from the bottom right unless placed. */
|
|
112
|
+
export const hud = (
|
|
113
|
+
model: Model,
|
|
114
|
+
camera: Camera,
|
|
115
|
+
placement: {
|
|
116
|
+
readonly alignment?: HudAlignment;
|
|
117
|
+
readonly margin?: readonly [number, number];
|
|
118
|
+
} = {},
|
|
119
|
+
): Hud => describeHud({ ...placement, children: [nodes(model, camera)] });
|