@opsydyn/effect-three 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 +56 -0
- package/README.md +15 -1
- package/dist/{index-8cf8t6by.js → index-re3rrfwd.js} +2935 -104
- package/dist/index-re3rrfwd.js.map +56 -0
- package/dist/index-ttwy0znv.js +145 -0
- package/dist/index-ttwy0znv.js.map +12 -0
- package/dist/index.js +557 -147
- package/dist/index.js.map +8 -6
- package/dist/kernel/bodies.d.ts +20 -1
- package/dist/kernel/bodies.d.ts.map +1 -1
- package/dist/kernel/boxSelect.d.ts +22 -0
- package/dist/kernel/boxSelect.d.ts.map +1 -0
- package/dist/kernel/build.d.ts +165 -7
- package/dist/kernel/build.d.ts.map +1 -1
- package/dist/kernel/cameraShake.d.ts.map +1 -1
- package/dist/kernel/describeScene.d.ts +50 -0
- package/dist/kernel/describeScene.d.ts.map +1 -0
- package/dist/kernel/description.d.ts +223 -1
- package/dist/kernel/description.d.ts.map +1 -1
- package/dist/kernel/diff.d.ts +15 -0
- package/dist/kernel/diff.d.ts.map +1 -1
- package/dist/kernel/equality.d.ts +11 -1
- package/dist/kernel/equality.d.ts.map +1 -1
- package/dist/kernel/gizmo.d.ts +69 -0
- package/dist/kernel/gizmo.d.ts.map +1 -0
- package/dist/kernel/history.d.ts +61 -0
- package/dist/kernel/history.d.ts.map +1 -0
- package/dist/kernel/index.d.ts +19 -6
- package/dist/kernel/index.d.ts.map +1 -1
- package/dist/kernel/index.js +124 -3
- package/dist/kernel/index.js.map +1 -1
- package/dist/kernel/inspect.d.ts +85 -0
- package/dist/kernel/inspect.d.ts.map +1 -0
- package/dist/kernel/lineGeometry.d.ts +25 -0
- package/dist/kernel/lineGeometry.d.ts.map +1 -0
- package/dist/kernel/lockstep.d.ts +114 -0
- package/dist/kernel/lockstep.d.ts.map +1 -0
- package/dist/kernel/loop.d.ts.map +1 -1
- package/dist/kernel/materials.d.ts.map +1 -1
- package/dist/kernel/paintOrder.d.ts +53 -0
- package/dist/kernel/paintOrder.d.ts.map +1 -0
- package/dist/kernel/physics.d.ts +39 -0
- package/dist/kernel/physics.d.ts.map +1 -1
- package/dist/kernel/pivot.d.ts +54 -0
- package/dist/kernel/pivot.d.ts.map +1 -0
- package/dist/kernel/pointBuffers.d.ts +17 -0
- package/dist/kernel/pointBuffers.d.ts.map +1 -0
- package/dist/kernel/pointer.d.ts +31 -1
- package/dist/kernel/pointer.d.ts.map +1 -1
- package/dist/kernel/preview.d.ts +32 -0
- package/dist/kernel/preview.d.ts.map +1 -0
- package/dist/kernel/renderKit.d.ts +21 -1
- package/dist/kernel/renderKit.d.ts.map +1 -1
- package/dist/kernel/renderer.d.ts +6 -0
- package/dist/kernel/renderer.d.ts.map +1 -1
- package/dist/kernel/sceneChanges.d.ts +87 -0
- package/dist/kernel/sceneChanges.d.ts.map +1 -0
- package/dist/kernel/sceneGraph.d.ts +9 -1
- package/dist/kernel/sceneGraph.d.ts.map +1 -1
- package/dist/kernel/sceneIndex.d.ts +5 -0
- package/dist/kernel/sceneIndex.d.ts.map +1 -1
- package/dist/kernel/seeded.d.ts +9 -0
- package/dist/kernel/seeded.d.ts.map +1 -0
- package/dist/kernel/shots.d.ts +26 -0
- package/dist/kernel/shots.d.ts.map +1 -0
- package/dist/kernel/tape.d.ts +38 -0
- package/dist/kernel/tape.d.ts.map +1 -0
- package/dist/kernel/timeline.d.ts +60 -0
- package/dist/kernel/timeline.d.ts.map +1 -0
- package/dist/kernel/trail.d.ts +31 -0
- package/dist/kernel/trail.d.ts.map +1 -0
- package/dist/kernel/viewportGizmo.d.ts +25 -0
- package/dist/kernel/viewportGizmo.d.ts.map +1 -0
- package/dist/runtime/Inspector.d.ts +20 -0
- package/dist/runtime/Inspector.d.ts.map +1 -0
- package/dist/runtime/SceneFile.d.ts +462 -0
- package/dist/runtime/SceneFile.d.ts.map +1 -0
- package/dist/runtime/SceneSchema.d.ts +404 -0
- package/dist/runtime/SceneSchema.d.ts.map +1 -1
- package/dist/runtime/errors.d.ts +9 -0
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +3 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/webgl/index.d.ts.map +1 -1
- package/dist/webgl/index.js +228 -3
- package/dist/webgl/index.js.map +6 -4
- package/dist/webgl/line.d.ts +4 -0
- package/dist/webgl/line.d.ts.map +1 -0
- package/dist/webgl/points.d.ts +4 -0
- package/dist/webgl/points.d.ts.map +1 -0
- package/dist/webgpu/index.d.ts.map +1 -1
- package/dist/webgpu/index.js +206 -26
- package/dist/webgpu/index.js.map +6 -4
- package/dist/webgpu/line.d.ts +4 -0
- package/dist/webgpu/line.d.ts.map +1 -0
- package/dist/webgpu/points.d.ts +4 -0
- package/dist/webgpu/points.d.ts.map +1 -0
- package/package.json +4 -2
- package/src/kernel/bodies.ts +299 -17
- package/src/kernel/boxSelect.ts +73 -0
- package/src/kernel/build.ts +374 -1
- package/src/kernel/cameraShake.ts +3 -13
- package/src/kernel/describeScene.ts +224 -0
- package/src/kernel/description.ts +243 -0
- package/src/kernel/diff.ts +39 -0
- package/src/kernel/equality.ts +123 -0
- package/src/kernel/gizmo.ts +478 -0
- package/src/kernel/history.ts +118 -0
- package/src/kernel/index.ts +110 -1
- package/src/kernel/inspect.ts +214 -0
- package/src/kernel/lineGeometry.ts +75 -0
- package/src/kernel/lockstep.ts +237 -0
- package/src/kernel/loop.ts +7 -0
- package/src/kernel/materials.ts +3 -0
- package/src/kernel/paintOrder.ts +274 -0
- package/src/kernel/physics.ts +86 -1
- package/src/kernel/pivot.ts +418 -0
- package/src/kernel/pointBuffers.ts +115 -0
- package/src/kernel/pointer.ts +98 -17
- package/src/kernel/preview.ts +467 -0
- package/src/kernel/renderKit.ts +23 -1
- package/src/kernel/renderer.ts +6 -0
- package/src/kernel/sceneChanges.ts +265 -0
- package/src/kernel/sceneGraph.ts +354 -11
- package/src/kernel/sceneIndex.ts +8 -0
- package/src/kernel/seeded.ts +18 -0
- package/src/kernel/shots.ts +167 -0
- package/src/kernel/tape.ts +124 -0
- package/src/kernel/timeline.ts +93 -0
- package/src/kernel/trail.ts +170 -0
- package/src/kernel/viewportGizmo.ts +194 -0
- package/src/runtime/Inspector.ts +43 -0
- package/src/runtime/SceneFile.ts +46 -0
- package/src/runtime/SceneSchema.ts +319 -0
- package/src/runtime/errors.ts +6 -0
- package/src/runtime/index.ts +3 -1
- package/src/webgl/index.ts +4 -0
- package/src/webgl/line.ts +66 -0
- package/src/webgl/points.ts +205 -0
- package/src/webgpu/index.ts +4 -0
- package/src/webgpu/line.ts +67 -0
- package/src/webgpu/points.ts +193 -0
- package/dist/index-8cf8t6by.js.map +0 -41
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scene diffs for review (roadmap, "Beyond parity" 4): what changed between two
|
|
3
|
+
* descriptions, in the kernel's own terms (the operations `diff` would apply, plus the
|
|
4
|
+
* scene-wide settings the graph applies beside them), as typed changes and as lines a
|
|
5
|
+
* person reads in review. `formatChanges` writes one line per change:
|
|
6
|
+
*
|
|
7
|
+
* ```text
|
|
8
|
+
* + Mesh crate in table
|
|
9
|
+
* - Light sun
|
|
10
|
+
* ~ crate: position, rotation
|
|
11
|
+
* > crate to shelf
|
|
12
|
+
* = order of the root: table, crate
|
|
13
|
+
* ⋯ scene: camera, environment
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Changes inside the HUD and render textures are listed after them, each line prefixed
|
|
17
|
+
* with where it is (`hud`, `texture screen`).
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import type { SceneDescription, SceneNode } from './description.ts';
|
|
22
|
+
import { diff, type Field, type Op } from './diff.ts';
|
|
23
|
+
import { type DescriptionError, emptyIndex, indexScene, type ParentKey } from './sceneIndex.ts';
|
|
24
|
+
|
|
25
|
+
/** A scene-wide part of a description, which changes as a whole. */
|
|
26
|
+
export type SceneSetting =
|
|
27
|
+
| 'camera'
|
|
28
|
+
| 'views'
|
|
29
|
+
| 'render'
|
|
30
|
+
| 'resources'
|
|
31
|
+
| 'gestures'
|
|
32
|
+
| 'overlays'
|
|
33
|
+
| 'environment'
|
|
34
|
+
| 'sky'
|
|
35
|
+
| 'contactShadows'
|
|
36
|
+
| 'physics'
|
|
37
|
+
| 'xr'
|
|
38
|
+
| 'measure';
|
|
39
|
+
|
|
40
|
+
/** Where a change is: the scene, its HUD, or one of its render textures. */
|
|
41
|
+
export type ChangePlace = 'scene' | 'hud' | { readonly texture: string };
|
|
42
|
+
|
|
43
|
+
export type SceneChange = { readonly place: ChangePlace } & (
|
|
44
|
+
| {
|
|
45
|
+
readonly _tag: 'Added';
|
|
46
|
+
readonly key: string;
|
|
47
|
+
readonly kind: SceneNode['_tag'];
|
|
48
|
+
readonly parent: ParentKey;
|
|
49
|
+
}
|
|
50
|
+
| { readonly _tag: 'Removed'; readonly key: string }
|
|
51
|
+
| { readonly _tag: 'Replaced'; readonly key: string; readonly kind: SceneNode['_tag'] }
|
|
52
|
+
| { readonly _tag: 'Changed'; readonly key: string; readonly fields: ReadonlyArray<Field> }
|
|
53
|
+
| { readonly _tag: 'Moved'; readonly key: string; readonly parent: ParentKey }
|
|
54
|
+
| { readonly _tag: 'Reordered'; readonly parent: ParentKey; readonly keys: ReadonlyArray<string> }
|
|
55
|
+
| { readonly _tag: 'Settings'; readonly settings: ReadonlyArray<SceneSetting> }
|
|
56
|
+
| { readonly _tag: 'HudAdded' }
|
|
57
|
+
| { readonly _tag: 'HudRemoved' }
|
|
58
|
+
| { readonly _tag: 'TextureAdded'; readonly id: string }
|
|
59
|
+
| { readonly _tag: 'TextureRemoved'; readonly id: string }
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
export type SceneChanges =
|
|
63
|
+
| { readonly _tag: 'Changes'; readonly changes: ReadonlyArray<SceneChange> }
|
|
64
|
+
/** The second description is not a valid scene: why, as `reconcile` would say. */
|
|
65
|
+
| { readonly _tag: 'Invalid'; readonly error: DescriptionError };
|
|
66
|
+
|
|
67
|
+
/** Settings `diff` reports as operations; the rest are compared here. */
|
|
68
|
+
const settingOf: Partial<Record<Op['_tag'], SceneSetting>> = {
|
|
69
|
+
SetCamera: 'camera',
|
|
70
|
+
SetViews: 'views',
|
|
71
|
+
SetRenderSettings: 'render',
|
|
72
|
+
DefineResources: 'resources',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const comparedSettings = [
|
|
76
|
+
'gestures',
|
|
77
|
+
'overlays',
|
|
78
|
+
'environment',
|
|
79
|
+
'sky',
|
|
80
|
+
'contactShadows',
|
|
81
|
+
'physics',
|
|
82
|
+
'xr',
|
|
83
|
+
'measure',
|
|
84
|
+
] as const satisfies ReadonlyArray<SceneSetting>;
|
|
85
|
+
|
|
86
|
+
const entriesOf = (value: object): ReadonlyArray<readonly [string, unknown]> =>
|
|
87
|
+
Object.entries(value);
|
|
88
|
+
|
|
89
|
+
/** Descriptions are plain data: equal when every array item and field is. */
|
|
90
|
+
const same = (a: unknown, b: unknown): boolean => {
|
|
91
|
+
if (a === b) return true;
|
|
92
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;
|
|
93
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
94
|
+
const fieldsA = entriesOf(a);
|
|
95
|
+
const fieldsB = new Map(entriesOf(b));
|
|
96
|
+
return (
|
|
97
|
+
fieldsA.length === fieldsB.size &&
|
|
98
|
+
fieldsA.every(([key, value]) => fieldsB.has(key) && same(value, fieldsB.get(key)))
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const nodeChange = (op: Op, place: ChangePlace): ReadonlyArray<SceneChange> => {
|
|
103
|
+
switch (op._tag) {
|
|
104
|
+
case 'Create':
|
|
105
|
+
return [{ place, _tag: 'Added', key: op.node.key, kind: op.node._tag, parent: op.parent }];
|
|
106
|
+
case 'Remove':
|
|
107
|
+
return [{ place, _tag: 'Removed', key: op.key }];
|
|
108
|
+
case 'Replace':
|
|
109
|
+
return [{ place, _tag: 'Replaced', key: op.node.key, kind: op.node._tag }];
|
|
110
|
+
case 'Update':
|
|
111
|
+
return [{ place, _tag: 'Changed', key: op.node.key, fields: op.fields }];
|
|
112
|
+
case 'Move':
|
|
113
|
+
return [{ place, _tag: 'Moved', key: op.key, parent: op.parent }];
|
|
114
|
+
case 'Order':
|
|
115
|
+
return [{ place, _tag: 'Reordered', parent: op.parent, keys: op.keys }];
|
|
116
|
+
default:
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/** Every behaviour a scene names: a review judges what changed, not what is registered. */
|
|
122
|
+
const behavioursIn = (description: SceneDescription): ReadonlySet<string> => {
|
|
123
|
+
const names = new Set<string>();
|
|
124
|
+
const visit = (node: SceneNode): void => {
|
|
125
|
+
if ('behaviour' in node && node.behaviour !== null) names.add(node.behaviour.name);
|
|
126
|
+
if (node._tag === 'Group') node.children.forEach(visit);
|
|
127
|
+
};
|
|
128
|
+
description.children.forEach(visit);
|
|
129
|
+
return names;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/** Where `before` starts from: its index, or nothing when it is itself invalid. */
|
|
133
|
+
const startOf = (before: SceneDescription) => {
|
|
134
|
+
const indexed = indexScene(before, behavioursIn(before));
|
|
135
|
+
return indexed._tag === 'Indexed' ? indexed.index : emptyIndex;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const isSetting = (setting: SceneSetting | undefined): setting is SceneSetting =>
|
|
139
|
+
setting !== undefined;
|
|
140
|
+
|
|
141
|
+
const settingsOf = (
|
|
142
|
+
before: SceneDescription,
|
|
143
|
+
after: SceneDescription,
|
|
144
|
+
ops: ReadonlyArray<Op>,
|
|
145
|
+
): ReadonlyArray<SceneSetting> => [
|
|
146
|
+
...ops.map((op) => settingOf[op._tag]).filter(isSetting),
|
|
147
|
+
...comparedSettings.filter((setting) => !same(before[setting], after[setting])),
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
/** The changes to one scene's nodes and settings, or why `after` is invalid. */
|
|
151
|
+
const changesIn = (
|
|
152
|
+
before: SceneDescription,
|
|
153
|
+
after: SceneDescription,
|
|
154
|
+
place: ChangePlace,
|
|
155
|
+
): SceneChanges => {
|
|
156
|
+
const result = diff(startOf(before), after, behavioursIn(after));
|
|
157
|
+
if (result._tag === 'Invalid') return result;
|
|
158
|
+
const settings = settingsOf(before, after, result.ops);
|
|
159
|
+
const settingsChange: ReadonlyArray<SceneChange> =
|
|
160
|
+
settings.length === 0 ? [] : [{ place, _tag: 'Settings', settings }];
|
|
161
|
+
return {
|
|
162
|
+
_tag: 'Changes',
|
|
163
|
+
changes: [...settingsChange, ...result.ops.flatMap((op) => nodeChange(op, place))],
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/** A nested scene (a HUD or a render texture) as a description of its own. */
|
|
168
|
+
const nestedScene = (parent: SceneDescription, children: ReadonlyArray<SceneNode>) =>
|
|
169
|
+
({ ...parent, hud: null, renderTextures: [], children }) satisfies SceneDescription;
|
|
170
|
+
|
|
171
|
+
const hudChanges = (before: SceneDescription, after: SceneDescription): SceneChanges => {
|
|
172
|
+
if (before.hud === null && after.hud === null) return { _tag: 'Changes', changes: [] };
|
|
173
|
+
if (after.hud === null)
|
|
174
|
+
return { _tag: 'Changes', changes: [{ place: 'hud', _tag: 'HudRemoved' }] };
|
|
175
|
+
const added: ReadonlyArray<SceneChange> =
|
|
176
|
+
before.hud === null ? [{ place: 'hud', _tag: 'HudAdded' }] : [];
|
|
177
|
+
const inside = changesIn(
|
|
178
|
+
nestedScene(before, before.hud?.children ?? []),
|
|
179
|
+
nestedScene(before, after.hud.children),
|
|
180
|
+
'hud',
|
|
181
|
+
);
|
|
182
|
+
return inside._tag === 'Invalid'
|
|
183
|
+
? inside
|
|
184
|
+
: { _tag: 'Changes', changes: [...added, ...inside.changes] };
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const textureChanges = (before: SceneDescription, after: SceneDescription): SceneChanges => {
|
|
188
|
+
const changes: Array<SceneChange> = [];
|
|
189
|
+
for (const texture of after.renderTextures) {
|
|
190
|
+
const was = before.renderTextures.find((candidate) => candidate.id === texture.id);
|
|
191
|
+
const place = { texture: texture.id };
|
|
192
|
+
if (was === undefined) changes.push({ place, _tag: 'TextureAdded', id: texture.id });
|
|
193
|
+
const inside = changesIn(
|
|
194
|
+
nestedScene(before, was?.children ?? []),
|
|
195
|
+
nestedScene(before, texture.children),
|
|
196
|
+
place,
|
|
197
|
+
);
|
|
198
|
+
if (inside._tag === 'Invalid') return inside;
|
|
199
|
+
changes.push(...inside.changes);
|
|
200
|
+
}
|
|
201
|
+
for (const texture of before.renderTextures) {
|
|
202
|
+
if (!after.renderTextures.some((candidate) => candidate.id === texture.id)) {
|
|
203
|
+
changes.push({ place: { texture: texture.id }, _tag: 'TextureRemoved', id: texture.id });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return { _tag: 'Changes', changes };
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* What changed from `before` to `after`: the scene's settings and nodes, then its HUD's
|
|
211
|
+
* and its render textures'. Invalid when `after` is not a valid scene; a `before` that
|
|
212
|
+
* is not is compared as though it were empty.
|
|
213
|
+
*/
|
|
214
|
+
export const sceneChanges = (before: SceneDescription, after: SceneDescription): SceneChanges => {
|
|
215
|
+
const parts = [
|
|
216
|
+
changesIn(before, after, 'scene'),
|
|
217
|
+
hudChanges(before, after),
|
|
218
|
+
textureChanges(before, after),
|
|
219
|
+
];
|
|
220
|
+
const invalid = parts.find((part) => part._tag === 'Invalid');
|
|
221
|
+
if (invalid !== undefined) return invalid;
|
|
222
|
+
return {
|
|
223
|
+
_tag: 'Changes',
|
|
224
|
+
changes: parts.flatMap((part) => (part._tag === 'Changes' ? part.changes : [])),
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const placeLabel = (place: ChangePlace): string => {
|
|
229
|
+
if (place === 'scene') return '';
|
|
230
|
+
return place === 'hud' ? 'hud ' : `texture ${place.texture} `;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const parentLabel = (parent: ParentKey): string => (parent === null ? 'the root' : parent);
|
|
234
|
+
|
|
235
|
+
const lineOf = (change: SceneChange): string => {
|
|
236
|
+
const where = placeLabel(change.place);
|
|
237
|
+
switch (change._tag) {
|
|
238
|
+
case 'Added':
|
|
239
|
+
return `${where}+ ${change.kind} ${change.key}${change.parent === null ? '' : ` in ${change.parent}`}`;
|
|
240
|
+
case 'Removed':
|
|
241
|
+
return `${where}- ${change.key}`;
|
|
242
|
+
case 'Replaced':
|
|
243
|
+
return `${where}± ${change.key}, now a ${change.kind}`;
|
|
244
|
+
case 'Changed':
|
|
245
|
+
return `${where}~ ${change.key}: ${change.fields.join(', ')}`;
|
|
246
|
+
case 'Moved':
|
|
247
|
+
return `${where}> ${change.key} to ${parentLabel(change.parent)}`;
|
|
248
|
+
case 'Reordered':
|
|
249
|
+
return `${where}= order of ${parentLabel(change.parent)}: ${change.keys.join(', ')}`;
|
|
250
|
+
case 'Settings':
|
|
251
|
+
return `${where}⋯ scene: ${change.settings.join(', ')}`;
|
|
252
|
+
case 'HudAdded':
|
|
253
|
+
return '+ hud';
|
|
254
|
+
case 'HudRemoved':
|
|
255
|
+
return '- hud';
|
|
256
|
+
case 'TextureAdded':
|
|
257
|
+
return `+ texture ${change.id}`;
|
|
258
|
+
case 'TextureRemoved':
|
|
259
|
+
return `- texture ${change.id}`;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/** The changes as lines for review, one per change; `No changes.` when there are none. */
|
|
264
|
+
export const formatChanges = (changes: ReadonlyArray<SceneChange>): string =>
|
|
265
|
+
changes.length === 0 ? 'No changes.' : changes.map(lineOf).join('\n');
|