@graview/render 0.0.0-stage → 0.1.0
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/LICENSE +96 -0
- package/README.md +43 -2
- package/dist/gpu.d.ts +6 -0
- package/dist/gpu.d.ts.map +1 -0
- package/dist/gpu.js +23 -0
- package/dist/gpu.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction/pointer-router.d.ts +94 -0
- package/dist/interaction/pointer-router.d.ts.map +1 -0
- package/dist/interaction/pointer-router.js +148 -0
- package/dist/interaction/pointer-router.js.map +1 -0
- package/dist/platform/html-in-canvas.d.ts +100 -0
- package/dist/platform/html-in-canvas.d.ts.map +1 -0
- package/dist/platform/html-in-canvas.js +172 -0
- package/dist/platform/html-in-canvas.js.map +1 -0
- package/dist/platform/matrix.d.ts +53 -0
- package/dist/platform/matrix.d.ts.map +1 -0
- package/dist/platform/matrix.js +61 -0
- package/dist/platform/matrix.js.map +1 -0
- package/dist/scene/compositor.d.ts +112 -0
- package/dist/scene/compositor.d.ts.map +1 -0
- package/dist/scene/compositor.js +340 -0
- package/dist/scene/compositor.js.map +1 -0
- package/dist/scene/compositor.wgsl.d.ts +15 -0
- package/dist/scene/compositor.wgsl.d.ts.map +1 -0
- package/dist/scene/compositor.wgsl.js +106 -0
- package/dist/scene/compositor.wgsl.js.map +1 -0
- package/dist/scene/connectors.d.ts +60 -0
- package/dist/scene/connectors.d.ts.map +1 -0
- package/dist/scene/connectors.js +95 -0
- package/dist/scene/connectors.js.map +1 -0
- package/dist/scene/frame-plan.d.ts +83 -0
- package/dist/scene/frame-plan.d.ts.map +1 -0
- package/dist/scene/frame-plan.js +119 -0
- package/dist/scene/frame-plan.js.map +1 -0
- package/dist/scene/from-layout.d.ts +14 -0
- package/dist/scene/from-layout.d.ts.map +1 -0
- package/dist/scene/from-layout.js +35 -0
- package/dist/scene/from-layout.js.map +1 -0
- package/dist/scene/plane.d.ts +68 -0
- package/dist/scene/plane.d.ts.map +1 -0
- package/dist/scene/plane.js +94 -0
- package/dist/scene/plane.js.map +1 -0
- package/package.json +64 -3
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/// <reference types="@webgpu/types" />
|
|
2
|
+
//
|
|
3
|
+
// The WebGPU globals are part of this file's PUBLIC declarations, so the
|
|
4
|
+
// reference has to live HERE rather than on the package entry: TypeScript
|
|
5
|
+
// drops a type reference from a `.d.ts` that does not itself name one of its
|
|
6
|
+
// types, so a reference on `index.ts` was emitted nowhere and an installed
|
|
7
|
+
// consumer got "cannot find name GPUDevice" from a file they cannot change.
|
|
8
|
+
// Found by typechecking a scratch project against the packed tarballs.
|
|
9
|
+
//
|
|
10
|
+
// It also has to be the ONLY route in: listing `@webgpu/types` under
|
|
11
|
+
// `compilerOptions.types` makes the globals ambient for this build, at which
|
|
12
|
+
// point TypeScript considers the reference satisfied and drops it from the
|
|
13
|
+
// emit. The build sees no difference; a stranger does.
|
|
14
|
+
import { PointerRouter } from "../interaction/pointer-router.js";
|
|
15
|
+
import { captureElement, detectCapabilities, requestPaint, setLayoutSubtree, toDOMMatrix, updateElementGeometry, } from "../platform/html-in-canvas.js";
|
|
16
|
+
import { COMPOSITOR_WGSL } from "./compositor.wgsl.js";
|
|
17
|
+
import { planFrame, } from "./frame-plan.js";
|
|
18
|
+
const UNIFORM_BYTES = 64; // 4 x vec4f
|
|
19
|
+
/**
|
|
20
|
+
* Captures views into GPU textures and composites them as quads at plane
|
|
21
|
+
* depth, then reports each drawn position back to the browser.
|
|
22
|
+
*
|
|
23
|
+
* The frame is decided by `planFrame` — which fidelity captures, what order
|
|
24
|
+
* things draw in, where each view lands — and this class only submits it.
|
|
25
|
+
* Keeping the decisions out of here is what lets them be tested in CI with
|
|
26
|
+
* no GPU and no DOM.
|
|
27
|
+
*/
|
|
28
|
+
export class Compositor {
|
|
29
|
+
canvas;
|
|
30
|
+
deps;
|
|
31
|
+
options;
|
|
32
|
+
views = new Map();
|
|
33
|
+
resources = new Map();
|
|
34
|
+
capturedAt = {};
|
|
35
|
+
router;
|
|
36
|
+
pipeline = null;
|
|
37
|
+
sampler = null;
|
|
38
|
+
frame = 0;
|
|
39
|
+
detachRouter = null;
|
|
40
|
+
connectors = [];
|
|
41
|
+
lastPlan = null;
|
|
42
|
+
lastPlacements = [];
|
|
43
|
+
capabilities;
|
|
44
|
+
constructor(canvas, deps, options = {}) {
|
|
45
|
+
this.canvas = canvas;
|
|
46
|
+
this.deps = deps;
|
|
47
|
+
this.options = options;
|
|
48
|
+
setLayoutSubtree(canvas, true);
|
|
49
|
+
this.capabilities = detectCapabilities(deps.gpu.device.gpu.queue, canvas);
|
|
50
|
+
this.router = new PointerRouter(canvas, {
|
|
51
|
+
...(options.platformHandlesHitTesting === undefined
|
|
52
|
+
? {}
|
|
53
|
+
: { platformHandlesHitTesting: options.platformHandlesHitTesting }),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
attach() {
|
|
57
|
+
this.detachRouter = this.router.attach();
|
|
58
|
+
return () => this.detach();
|
|
59
|
+
}
|
|
60
|
+
detach() {
|
|
61
|
+
this.detachRouter?.();
|
|
62
|
+
this.detachRouter = null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Brings the scene in line with a new set of views, keeping the GPU
|
|
66
|
+
* resources of everything that survived.
|
|
67
|
+
*
|
|
68
|
+
* Removing and re-adding every view on each edit destroys and reallocates
|
|
69
|
+
* every texture, which is both wasteful and — at this scene's size — enough
|
|
70
|
+
* to bring the GPU process down. Reconciling touches only what changed.
|
|
71
|
+
*/
|
|
72
|
+
reconcile(views) {
|
|
73
|
+
const next = new Map(views.map((view) => [view.id, view]));
|
|
74
|
+
for (const id of [...this.views.keys()]) {
|
|
75
|
+
if (!next.has(id))
|
|
76
|
+
this.remove(id);
|
|
77
|
+
}
|
|
78
|
+
for (const view of views) {
|
|
79
|
+
const existing = this.views.get(view.id);
|
|
80
|
+
if (!existing) {
|
|
81
|
+
this.add(view);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// What actually changes the PIXELS: a different element, a different
|
|
85
|
+
// box, or a different opacity. Moving a view does not — and marking a
|
|
86
|
+
// move dirty put every plane-1 view back into the per-frame capture
|
|
87
|
+
// budget for the whole of every transition.
|
|
88
|
+
const resized = existing.width !== view.width || existing.height !== view.height;
|
|
89
|
+
const replaced = existing.element !== view.element;
|
|
90
|
+
const faded = (existing.opacity ?? 1) !== (view.opacity ?? 1);
|
|
91
|
+
const dirty = existing.dirty || resized || replaced || faded;
|
|
92
|
+
this.views.set(view.id, { ...view, dirty });
|
|
93
|
+
if (replaced || (dirty && !this.capturedAtIsFresh(view.id))) {
|
|
94
|
+
// Force a first-class recapture rather than relying on the fidelity
|
|
95
|
+
// rule, which for `glyph` never recaptures at all.
|
|
96
|
+
delete this.capturedAt[view.id];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Whether this view's cached texture still reflects what it looks like. */
|
|
101
|
+
capturedAtIsFresh(id) {
|
|
102
|
+
return this.capturedAt[id] !== undefined && this.views.get(id)?.dirty !== true;
|
|
103
|
+
}
|
|
104
|
+
add(view) {
|
|
105
|
+
if (view.element.parentElement !== this.canvas) {
|
|
106
|
+
// The platform's own message for this arrives from deep inside a
|
|
107
|
+
// capture call, a frame later, naming neither the view nor the fix.
|
|
108
|
+
throw new Error(`View "${view.id}" must be an immediate child of the scene canvas — ` +
|
|
109
|
+
"the capture API rejects deeper descendants. Nest inside a view, " +
|
|
110
|
+
"never between views.");
|
|
111
|
+
}
|
|
112
|
+
this.views.set(view.id, { ...view, dirty: true });
|
|
113
|
+
}
|
|
114
|
+
remove(id) {
|
|
115
|
+
const resource = this.resources.get(id);
|
|
116
|
+
resource?.texture.destroy();
|
|
117
|
+
resource?.uniform.destroy();
|
|
118
|
+
this.resources.delete(id);
|
|
119
|
+
this.views.delete(id);
|
|
120
|
+
delete this.capturedAt[id];
|
|
121
|
+
}
|
|
122
|
+
/** Replaces the connector set, e.g. after a layout change. */
|
|
123
|
+
setConnectors(connectors) {
|
|
124
|
+
this.connectors = connectors;
|
|
125
|
+
}
|
|
126
|
+
/** Marks a view's texture stale, so the next frame recaptures it. */
|
|
127
|
+
invalidate(id) {
|
|
128
|
+
const view = this.views.get(id);
|
|
129
|
+
if (view)
|
|
130
|
+
this.views.set(id, { ...view, dirty: true });
|
|
131
|
+
}
|
|
132
|
+
all() {
|
|
133
|
+
return [...this.views.values()];
|
|
134
|
+
}
|
|
135
|
+
/** Last frame's plan. Measurement harnesses and tests only. */
|
|
136
|
+
plan() {
|
|
137
|
+
return this.lastPlan;
|
|
138
|
+
}
|
|
139
|
+
/** Where each view was drawn last frame, with the element it came from. */
|
|
140
|
+
placements() {
|
|
141
|
+
return this.lastPlacements;
|
|
142
|
+
}
|
|
143
|
+
/** Raw device handle. Measurement harnesses only. */
|
|
144
|
+
deviceForProbe() {
|
|
145
|
+
return this.deps.gpu.device.gpu;
|
|
146
|
+
}
|
|
147
|
+
/** Asks the browser for a `paint` event — the only moment capture is legal. */
|
|
148
|
+
scheduleFrame() {
|
|
149
|
+
requestPaint(this.canvas);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* One frame: capture what is stale, draw every view back to front, and tell
|
|
153
|
+
* the browser where each one landed.
|
|
154
|
+
*
|
|
155
|
+
* Call this from a `paint` listener. Outside one the browser has no paint
|
|
156
|
+
* record and capture throws.
|
|
157
|
+
*/
|
|
158
|
+
render() {
|
|
159
|
+
const device = this.deps.gpu.device.gpu;
|
|
160
|
+
const queue = device.queue;
|
|
161
|
+
this.frame += 1;
|
|
162
|
+
// Read the canvas's CURRENT backing size every frame.
|
|
163
|
+
//
|
|
164
|
+
// This used to be a tuple captured when the compositor was built. Once
|
|
165
|
+
// the scene became responsive the canvas resized underneath it, the
|
|
166
|
+
// shader kept mapping canvas pixels to NDC with the old dimensions, and
|
|
167
|
+
// every quad landed somewhere it should not — barely at the origin,
|
|
168
|
+
// badly further down. The canvas is the surface; there is no second
|
|
169
|
+
// source of truth to go stale.
|
|
170
|
+
const canvasWidth = this.canvas.width;
|
|
171
|
+
const canvasHeight = this.canvas.height;
|
|
172
|
+
const plan = planFrame([...this.views.values()], this.connectors, {
|
|
173
|
+
canvasWidth,
|
|
174
|
+
canvasHeight,
|
|
175
|
+
capturedAt: this.capturedAt,
|
|
176
|
+
...(this.options.connectorOverrides
|
|
177
|
+
? { connectorOverrides: this.options.connectorOverrides }
|
|
178
|
+
: {}),
|
|
179
|
+
});
|
|
180
|
+
this.lastPlan = plan;
|
|
181
|
+
for (const capture of plan.captures) {
|
|
182
|
+
const view = this.views.get(capture.viewId);
|
|
183
|
+
if (!view)
|
|
184
|
+
continue;
|
|
185
|
+
const resource = this.ensureResources(device, view);
|
|
186
|
+
captureElement(queue, view.element, resource.texture);
|
|
187
|
+
this.capturedAt[view.id] = this.frame;
|
|
188
|
+
this.views.set(view.id, { ...view, dirty: false });
|
|
189
|
+
}
|
|
190
|
+
const encoder = device.createCommandEncoder({ label: "graview-compositor" });
|
|
191
|
+
const pass = encoder.beginRenderPass({
|
|
192
|
+
colorAttachments: [
|
|
193
|
+
{
|
|
194
|
+
view: this.deps.surface.context.getCurrentTexture().createView(),
|
|
195
|
+
clearValue: this.groundColor(),
|
|
196
|
+
loadOp: "clear",
|
|
197
|
+
storeOp: "store",
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
pass.setPipeline(this.ensurePipeline(device));
|
|
202
|
+
const placements = [];
|
|
203
|
+
for (const draw of plan.draws) {
|
|
204
|
+
const view = this.views.get(draw.viewId);
|
|
205
|
+
const resource = this.resources.get(draw.viewId);
|
|
206
|
+
if (!view || !resource)
|
|
207
|
+
continue;
|
|
208
|
+
queue.writeBuffer(resource.uniform, 0, packUniform(draw, canvasWidth, canvasHeight, this.options.ground ?? [0.957, 0.949, 0.933]));
|
|
209
|
+
pass.setBindGroup(0, this.ensureBindGroup(device, resource));
|
|
210
|
+
pass.draw(6);
|
|
211
|
+
placements.push({
|
|
212
|
+
element: view.element,
|
|
213
|
+
transform: draw.transform,
|
|
214
|
+
width: view.width,
|
|
215
|
+
height: view.height,
|
|
216
|
+
depth: view.plane,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
pass.end();
|
|
220
|
+
queue.submit([encoder.finish()]);
|
|
221
|
+
// Report the drawn positions even on builds that ignore them: the call is
|
|
222
|
+
// free, and it is what makes the scene correct the day it lands.
|
|
223
|
+
if (this.capabilities.geometrySync) {
|
|
224
|
+
for (const report of plan.geometry) {
|
|
225
|
+
const view = this.views.get(report.viewId);
|
|
226
|
+
if (!view)
|
|
227
|
+
continue;
|
|
228
|
+
updateElementGeometry(this.canvas, view.element, {
|
|
229
|
+
canvasTransform: toDOMMatrix(report.transform),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
this.lastPlacements = placements;
|
|
234
|
+
this.router.setPlacements(placements);
|
|
235
|
+
return plan;
|
|
236
|
+
}
|
|
237
|
+
groundColor() {
|
|
238
|
+
const [r, g, b] = this.options.ground ?? [0.957, 0.949, 0.933];
|
|
239
|
+
return { r, g, b, a: 1 };
|
|
240
|
+
}
|
|
241
|
+
ensurePipeline(device) {
|
|
242
|
+
if (this.pipeline)
|
|
243
|
+
return this.pipeline;
|
|
244
|
+
const module = device.createShaderModule({
|
|
245
|
+
code: COMPOSITOR_WGSL,
|
|
246
|
+
label: "graview-compositor",
|
|
247
|
+
});
|
|
248
|
+
this.pipeline = device.createRenderPipeline({
|
|
249
|
+
layout: "auto",
|
|
250
|
+
vertex: { module, entryPoint: "vs_main" },
|
|
251
|
+
fragment: {
|
|
252
|
+
module,
|
|
253
|
+
entryPoint: "fs_main",
|
|
254
|
+
targets: [
|
|
255
|
+
{
|
|
256
|
+
format: navigator.gpu.getPreferredCanvasFormat(),
|
|
257
|
+
blend: {
|
|
258
|
+
color: { srcFactor: "src-alpha", dstFactor: "one-minus-src-alpha" },
|
|
259
|
+
alpha: { srcFactor: "one", dstFactor: "one-minus-src-alpha" },
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
primitive: { topology: "triangle-list" },
|
|
265
|
+
});
|
|
266
|
+
return this.pipeline;
|
|
267
|
+
}
|
|
268
|
+
ensureResources(device, view) {
|
|
269
|
+
// CEIL, not round. The browser rasterises an element of height 399.4 into
|
|
270
|
+
// 400 rows, and a 399-row texture makes the copy fail validation — which
|
|
271
|
+
// it does silently, leaving the view with whatever was in the texture
|
|
272
|
+
// before, or nothing at all. Allocate at least what can arrive.
|
|
273
|
+
const width = Math.max(1, Math.ceil(view.width));
|
|
274
|
+
const height = Math.max(1, Math.ceil(view.height));
|
|
275
|
+
const existing = this.resources.get(view.id);
|
|
276
|
+
if (existing && existing.width === width && existing.height === height) {
|
|
277
|
+
return existing;
|
|
278
|
+
}
|
|
279
|
+
existing?.texture.destroy();
|
|
280
|
+
const texture = device.createTexture({
|
|
281
|
+
size: [width, height],
|
|
282
|
+
format: "rgba8unorm",
|
|
283
|
+
usage: GPUTextureUsage.COPY_DST |
|
|
284
|
+
GPUTextureUsage.TEXTURE_BINDING |
|
|
285
|
+
GPUTextureUsage.RENDER_ATTACHMENT,
|
|
286
|
+
label: `graview-view-${view.id}`,
|
|
287
|
+
});
|
|
288
|
+
const resource = {
|
|
289
|
+
texture,
|
|
290
|
+
width,
|
|
291
|
+
height,
|
|
292
|
+
bindGroup: null,
|
|
293
|
+
uniform: existing?.uniform ??
|
|
294
|
+
device.createBuffer({
|
|
295
|
+
size: UNIFORM_BYTES,
|
|
296
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
297
|
+
label: `graview-plane-${view.id}`,
|
|
298
|
+
}),
|
|
299
|
+
};
|
|
300
|
+
this.resources.set(view.id, resource);
|
|
301
|
+
return resource;
|
|
302
|
+
}
|
|
303
|
+
ensureBindGroup(device, resource) {
|
|
304
|
+
if (resource.bindGroup)
|
|
305
|
+
return resource.bindGroup;
|
|
306
|
+
this.sampler ??= device.createSampler({
|
|
307
|
+
magFilter: "linear",
|
|
308
|
+
minFilter: "linear",
|
|
309
|
+
addressModeU: "clamp-to-edge",
|
|
310
|
+
addressModeV: "clamp-to-edge",
|
|
311
|
+
});
|
|
312
|
+
resource.bindGroup = device.createBindGroup({
|
|
313
|
+
layout: this.ensurePipeline(device).getBindGroupLayout(0),
|
|
314
|
+
entries: [
|
|
315
|
+
{ binding: 0, resource: { buffer: resource.uniform } },
|
|
316
|
+
{ binding: 1, resource: resource.texture.createView() },
|
|
317
|
+
{ binding: 2, resource: this.sampler },
|
|
318
|
+
],
|
|
319
|
+
});
|
|
320
|
+
return resource.bindGroup;
|
|
321
|
+
}
|
|
322
|
+
dispose() {
|
|
323
|
+
this.detach();
|
|
324
|
+
for (const id of [...this.resources.keys()])
|
|
325
|
+
this.remove(id);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The uniform layout the compositing shader reads, packed as data so a test
|
|
330
|
+
* can assert on it with no GPU present.
|
|
331
|
+
*/
|
|
332
|
+
export function packUniform(draw, canvasWidth, canvasHeight, ground) {
|
|
333
|
+
return new Float32Array([
|
|
334
|
+
draw.transform[12], draw.transform[13], draw.width, draw.height,
|
|
335
|
+
draw.style.blur, draw.style.falloff, draw.style.shadow, draw.opacity,
|
|
336
|
+
canvasWidth, canvasHeight, 0, 0,
|
|
337
|
+
ground[0], ground[1], ground[2], 1,
|
|
338
|
+
]);
|
|
339
|
+
}
|
|
340
|
+
//# sourceMappingURL=compositor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositor.js","sourceRoot":"","sources":["../../src/scene/compositor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plane compositing shader.
|
|
3
|
+
*
|
|
4
|
+
* One textured quad per drawn view. The vertex stage places it from a plane
|
|
5
|
+
* transform expressed in canvas pixels; the fragment stage applies the
|
|
6
|
+
* atmospheric-depth treatment: a separable-ish box blur, contrast falloff
|
|
7
|
+
* toward the ground colour, and a soft drop shadow that separates one plane
|
|
8
|
+
* from the one behind it.
|
|
9
|
+
*
|
|
10
|
+
* Blur and falloff live here rather than in CSS because per-plane blur is
|
|
11
|
+
* near-free on the GPU and expensive in the compositor — that difference is
|
|
12
|
+
* the whole justification for the GPU pipeline.
|
|
13
|
+
*/
|
|
14
|
+
export declare const COMPOSITOR_WGSL = "\nstruct Plane {\n // x, y, width, height in canvas pixels, after the plane transform.\n rect: vec4f,\n // blur radius in texels, contrast falloff 0..1, shadow strength, and the\n // view's own opacity \u2014 which is how a node entering or leaving the scene\n // fades rather than popping.\n style: vec4f,\n // canvas size in pixels, and the ground colour's rgb packed as xyz.\n canvas: vec4f,\n ground: vec4f,\n}\n\n@group(0) @binding(0) var<uniform> plane: Plane;\n@group(0) @binding(1) var view_texture: texture_2d<f32>;\n@group(0) @binding(2) var view_sampler: sampler;\n\nstruct VertexOut {\n @builtin(position) position: vec4f,\n @location(0) uv: vec2f,\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) index: u32) -> VertexOut {\n // Two triangles, no vertex buffer: the quad is entirely a function of the\n // plane rect, so nothing has to be uploaded per frame except the uniform.\n var corners = array<vec2f, 6>(\n vec2f(0.0, 0.0), vec2f(1.0, 0.0), vec2f(0.0, 1.0),\n vec2f(0.0, 1.0), vec2f(1.0, 0.0), vec2f(1.0, 1.0),\n );\n let corner = corners[index];\n let pixel = plane.rect.xy + corner * plane.rect.zw;\n // Canvas pixels to clip space, y down.\n let ndc = vec2f(\n pixel.x / plane.canvas.x * 2.0 - 1.0,\n 1.0 - pixel.y / plane.canvas.y * 2.0,\n );\n var out: VertexOut;\n out.position = vec4f(ndc, 0.0, 1.0);\n out.uv = corner;\n return out;\n}\n\nfn blurred(uv: vec2f, radius: f32) -> vec4f {\n if (radius <= 0.0) {\n return textureSample(view_texture, view_sampler, uv);\n }\n let size = vec2f(textureDimensions(view_texture, 0));\n let step = radius / size;\n var sum = vec4f(0.0);\n var weight = 0.0;\n // A 9-tap kernel: enough to read as depth-of-field at plane scale, cheap\n // enough to run on every capture every frame.\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let offset = vec2f(f32(x), f32(y)) * step;\n let w = select(1.0, 2.0, x == 0 && y == 0);\n sum = sum + textureSample(view_texture, view_sampler, uv + offset) * w;\n weight = weight + w;\n }\n }\n return sum / weight;\n}\n\n@fragment\nfn fs_main(in: VertexOut) -> @location(0) vec4f {\n let radius = plane.style.x;\n let falloff = plane.style.y;\n let shadow = plane.style.z;\n let opacity = plane.style.w;\n\n var color = blurred(in.uv, radius);\n\n // Recede toward the GROUND colour, not toward grey. On a dark ground that\n // reads as dimming and on a light one as haze \u2014 which is what distance\n // actually does to a surface, rather than a fixed \"faded\" look that only\n // works in one scheme.\n color = vec4f(mix(color.rgb, plane.ground.rgb, falloff), color.a);\n\n // Contact shadow around the quad's own border, drawn into its alpha so no\n // second pass is needed. It is what separates one plane from the one\n // behind it when both are dark.\n let edge = min(min(in.uv.x, in.uv.y), min(1.0 - in.uv.x, 1.0 - in.uv.y));\n let lift = 1.0 - smoothstep(0.0, 0.055, edge);\n color = vec4f(color.rgb * (1.0 - lift * shadow), color.a);\n\n // Premultiplied: the target blends with one-minus-src-alpha, so scaling\n // colour and alpha together is what keeps a half-faded view from darkening\n // instead of dissolving.\n return vec4f(color.rgb * opacity, color.a * opacity);\n}\n";
|
|
15
|
+
//# sourceMappingURL=compositor.wgsl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositor.wgsl.d.ts","sourceRoot":"","sources":["../../src/scene/compositor.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,2vGA2F3B,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plane compositing shader.
|
|
3
|
+
*
|
|
4
|
+
* One textured quad per drawn view. The vertex stage places it from a plane
|
|
5
|
+
* transform expressed in canvas pixels; the fragment stage applies the
|
|
6
|
+
* atmospheric-depth treatment: a separable-ish box blur, contrast falloff
|
|
7
|
+
* toward the ground colour, and a soft drop shadow that separates one plane
|
|
8
|
+
* from the one behind it.
|
|
9
|
+
*
|
|
10
|
+
* Blur and falloff live here rather than in CSS because per-plane blur is
|
|
11
|
+
* near-free on the GPU and expensive in the compositor — that difference is
|
|
12
|
+
* the whole justification for the GPU pipeline.
|
|
13
|
+
*/
|
|
14
|
+
export const COMPOSITOR_WGSL = /* wgsl */ `
|
|
15
|
+
struct Plane {
|
|
16
|
+
// x, y, width, height in canvas pixels, after the plane transform.
|
|
17
|
+
rect: vec4f,
|
|
18
|
+
// blur radius in texels, contrast falloff 0..1, shadow strength, and the
|
|
19
|
+
// view's own opacity — which is how a node entering or leaving the scene
|
|
20
|
+
// fades rather than popping.
|
|
21
|
+
style: vec4f,
|
|
22
|
+
// canvas size in pixels, and the ground colour's rgb packed as xyz.
|
|
23
|
+
canvas: vec4f,
|
|
24
|
+
ground: vec4f,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@group(0) @binding(0) var<uniform> plane: Plane;
|
|
28
|
+
@group(0) @binding(1) var view_texture: texture_2d<f32>;
|
|
29
|
+
@group(0) @binding(2) var view_sampler: sampler;
|
|
30
|
+
|
|
31
|
+
struct VertexOut {
|
|
32
|
+
@builtin(position) position: vec4f,
|
|
33
|
+
@location(0) uv: vec2f,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@vertex
|
|
37
|
+
fn vs_main(@builtin(vertex_index) index: u32) -> VertexOut {
|
|
38
|
+
// Two triangles, no vertex buffer: the quad is entirely a function of the
|
|
39
|
+
// plane rect, so nothing has to be uploaded per frame except the uniform.
|
|
40
|
+
var corners = array<vec2f, 6>(
|
|
41
|
+
vec2f(0.0, 0.0), vec2f(1.0, 0.0), vec2f(0.0, 1.0),
|
|
42
|
+
vec2f(0.0, 1.0), vec2f(1.0, 0.0), vec2f(1.0, 1.0),
|
|
43
|
+
);
|
|
44
|
+
let corner = corners[index];
|
|
45
|
+
let pixel = plane.rect.xy + corner * plane.rect.zw;
|
|
46
|
+
// Canvas pixels to clip space, y down.
|
|
47
|
+
let ndc = vec2f(
|
|
48
|
+
pixel.x / plane.canvas.x * 2.0 - 1.0,
|
|
49
|
+
1.0 - pixel.y / plane.canvas.y * 2.0,
|
|
50
|
+
);
|
|
51
|
+
var out: VertexOut;
|
|
52
|
+
out.position = vec4f(ndc, 0.0, 1.0);
|
|
53
|
+
out.uv = corner;
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn blurred(uv: vec2f, radius: f32) -> vec4f {
|
|
58
|
+
if (radius <= 0.0) {
|
|
59
|
+
return textureSample(view_texture, view_sampler, uv);
|
|
60
|
+
}
|
|
61
|
+
let size = vec2f(textureDimensions(view_texture, 0));
|
|
62
|
+
let step = radius / size;
|
|
63
|
+
var sum = vec4f(0.0);
|
|
64
|
+
var weight = 0.0;
|
|
65
|
+
// A 9-tap kernel: enough to read as depth-of-field at plane scale, cheap
|
|
66
|
+
// enough to run on every capture every frame.
|
|
67
|
+
for (var y = -1; y <= 1; y = y + 1) {
|
|
68
|
+
for (var x = -1; x <= 1; x = x + 1) {
|
|
69
|
+
let offset = vec2f(f32(x), f32(y)) * step;
|
|
70
|
+
let w = select(1.0, 2.0, x == 0 && y == 0);
|
|
71
|
+
sum = sum + textureSample(view_texture, view_sampler, uv + offset) * w;
|
|
72
|
+
weight = weight + w;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return sum / weight;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@fragment
|
|
79
|
+
fn fs_main(in: VertexOut) -> @location(0) vec4f {
|
|
80
|
+
let radius = plane.style.x;
|
|
81
|
+
let falloff = plane.style.y;
|
|
82
|
+
let shadow = plane.style.z;
|
|
83
|
+
let opacity = plane.style.w;
|
|
84
|
+
|
|
85
|
+
var color = blurred(in.uv, radius);
|
|
86
|
+
|
|
87
|
+
// Recede toward the GROUND colour, not toward grey. On a dark ground that
|
|
88
|
+
// reads as dimming and on a light one as haze — which is what distance
|
|
89
|
+
// actually does to a surface, rather than a fixed "faded" look that only
|
|
90
|
+
// works in one scheme.
|
|
91
|
+
color = vec4f(mix(color.rgb, plane.ground.rgb, falloff), color.a);
|
|
92
|
+
|
|
93
|
+
// Contact shadow around the quad's own border, drawn into its alpha so no
|
|
94
|
+
// second pass is needed. It is what separates one plane from the one
|
|
95
|
+
// behind it when both are dark.
|
|
96
|
+
let edge = min(min(in.uv.x, in.uv.y), min(1.0 - in.uv.x, 1.0 - in.uv.y));
|
|
97
|
+
let lift = 1.0 - smoothstep(0.0, 0.055, edge);
|
|
98
|
+
color = vec4f(color.rgb * (1.0 - lift * shadow), color.a);
|
|
99
|
+
|
|
100
|
+
// Premultiplied: the target blends with one-minus-src-alpha, so scaling
|
|
101
|
+
// colour and alpha together is what keeps a half-faded view from darkening
|
|
102
|
+
// instead of dissolving.
|
|
103
|
+
return vec4f(color.rgb * opacity, color.a * opacity);
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
//# sourceMappingURL=compositor.wgsl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositor.wgsl.js","sourceRoot":"","sources":["../../src/scene/compositor.wgsl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2FzC,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector styling. Edge kind determines stroke treatment, because the line
|
|
3
|
+
* between two nodes carries meaning: `protects` must never look like
|
|
4
|
+
* `assigned-to`, or the picture is lying about the graph.
|
|
5
|
+
*/
|
|
6
|
+
export type StrokePattern = "solid" | "dashed" | "dotted" | "double" | "tapered";
|
|
7
|
+
export type EndCap = "none" | "arrow" | "dot" | "bar";
|
|
8
|
+
export interface ConnectorStyle {
|
|
9
|
+
readonly pattern: StrokePattern;
|
|
10
|
+
readonly width: number;
|
|
11
|
+
readonly cap: EndCap;
|
|
12
|
+
/** Degrees around the colour wheel; the renderer maps it into its own palette. */
|
|
13
|
+
readonly hue: number;
|
|
14
|
+
readonly opacity: number;
|
|
15
|
+
/** An explicit CSS colour from the brand's kit; absent, the hue paints it. */
|
|
16
|
+
readonly colour?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A distinct treatment for an edge kind, derived rather than authored.
|
|
20
|
+
*
|
|
21
|
+
* A new edge kind gets a stroke nobody had to choose — which is the same
|
|
22
|
+
* promise the rest of the framework makes: declare the kind, and the visual
|
|
23
|
+
* language follows. Apps override anything they care about.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The hue thread lives in core, beside the brand that overrides it; this is
|
|
27
|
+
* the same function every surface reads, re-exported so nothing that reached
|
|
28
|
+
* it here has to move.
|
|
29
|
+
*/
|
|
30
|
+
export { hueFor } from "@graview/core";
|
|
31
|
+
export declare function connectorStyle(kind: string, overrides?: Readonly<Record<string, Partial<ConnectorStyle>>>): ConnectorStyle;
|
|
32
|
+
/**
|
|
33
|
+
* Checks that a set of edge kinds are actually distinguishable from one
|
|
34
|
+
* another. Two relationships that render identically are a bug in the visual
|
|
35
|
+
* language, not a cosmetic issue — so this is worth asserting in a test
|
|
36
|
+
* rather than noticing in a screenshot.
|
|
37
|
+
*/
|
|
38
|
+
export declare function distinguishable(kinds: readonly string[], overrides?: Readonly<Record<string, Partial<ConnectorStyle>>>): {
|
|
39
|
+
ok: boolean;
|
|
40
|
+
collisions: readonly [string, string][];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The dash array for a stroke pattern, in one place.
|
|
44
|
+
*
|
|
45
|
+
* Exported because the scene is not the only thing that draws a connector: a
|
|
46
|
+
* legend has to draw the SAME line, and a legend whose swatch is an
|
|
47
|
+
* approximation is a legend you cannot trust. Copying this map was the exact
|
|
48
|
+
* failure the legend's own docstring promised against.
|
|
49
|
+
*/
|
|
50
|
+
export declare const CONNECTOR_DASH: Readonly<Record<StrokePattern, string | undefined>>;
|
|
51
|
+
/** The colour a connector of this kind is stroked in, on either surface. */
|
|
52
|
+
export declare function connectorStroke(style: ConnectorStyle): string;
|
|
53
|
+
/**
|
|
54
|
+
* How thick, given where it is drawn.
|
|
55
|
+
*
|
|
56
|
+
* Inside the scene a connector is an aside and stays thin; above the stack the
|
|
57
|
+
* lines are the content and earn their weight.
|
|
58
|
+
*/
|
|
59
|
+
export declare function connectorWidth(style: ConnectorStyle, overview: boolean): number;
|
|
60
|
+
//# sourceMappingURL=connectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectors.d.ts","sourceRoot":"","sources":["../../src/scene/connectors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AACjF,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAoBD;;;;;;GAMG;AACH;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAM,GAChE,cAAc,CAUhB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,GAC5D;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;CAAE,CAa1D;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC,CAM9E,CAAC;AAEF,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAE/E"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector styling. Edge kind determines stroke treatment, because the line
|
|
3
|
+
* between two nodes carries meaning: `protects` must never look like
|
|
4
|
+
* `assigned-to`, or the picture is lying about the graph.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The treatments a derived style may take. Deliberately distinguishable by
|
|
8
|
+
* shape as well as by hue: colour alone is not a way to tell two relationships
|
|
9
|
+
* apart.
|
|
10
|
+
*/
|
|
11
|
+
const PATTERNS = ["solid", "dashed", "dotted", "double", "tapered"];
|
|
12
|
+
const CAPS = ["arrow", "none", "dot", "bar"];
|
|
13
|
+
/** Stable string hash — the same edge kind always gets the same treatment. */
|
|
14
|
+
function hash(value) {
|
|
15
|
+
let h = 2166136261;
|
|
16
|
+
for (let i = 0; i < value.length; i++) {
|
|
17
|
+
h ^= value.charCodeAt(i);
|
|
18
|
+
h = Math.imul(h, 16777619);
|
|
19
|
+
}
|
|
20
|
+
return h >>> 0;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A distinct treatment for an edge kind, derived rather than authored.
|
|
24
|
+
*
|
|
25
|
+
* A new edge kind gets a stroke nobody had to choose — which is the same
|
|
26
|
+
* promise the rest of the framework makes: declare the kind, and the visual
|
|
27
|
+
* language follows. Apps override anything they care about.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* The hue thread lives in core, beside the brand that overrides it; this is
|
|
31
|
+
* the same function every surface reads, re-exported so nothing that reached
|
|
32
|
+
* it here has to move.
|
|
33
|
+
*/
|
|
34
|
+
export { hueFor } from "@graview/core";
|
|
35
|
+
export function connectorStyle(kind, overrides = {}) {
|
|
36
|
+
const h = hash(kind);
|
|
37
|
+
const derived = {
|
|
38
|
+
pattern: PATTERNS[h % PATTERNS.length],
|
|
39
|
+
width: 1.5 + ((h >>> 3) % 3) * 0.75,
|
|
40
|
+
cap: CAPS[(h >>> 7) % CAPS.length],
|
|
41
|
+
hue: (h >>> 11) % 360,
|
|
42
|
+
opacity: 0.85,
|
|
43
|
+
};
|
|
44
|
+
return { ...derived, ...overrides[kind] };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Checks that a set of edge kinds are actually distinguishable from one
|
|
48
|
+
* another. Two relationships that render identically are a bug in the visual
|
|
49
|
+
* language, not a cosmetic issue — so this is worth asserting in a test
|
|
50
|
+
* rather than noticing in a screenshot.
|
|
51
|
+
*/
|
|
52
|
+
export function distinguishable(kinds, overrides) {
|
|
53
|
+
const styles = kinds.map((kind) => [kind, connectorStyle(kind, overrides)]);
|
|
54
|
+
const collisions = [];
|
|
55
|
+
for (let i = 0; i < styles.length; i++) {
|
|
56
|
+
for (let j = i + 1; j < styles.length; j++) {
|
|
57
|
+
const [aKind, a] = styles[i];
|
|
58
|
+
const [bKind, b] = styles[j];
|
|
59
|
+
const sameShape = a.pattern === b.pattern && a.cap === b.cap && a.width === b.width;
|
|
60
|
+
const sameHue = Math.abs(a.hue - b.hue) < 0.04;
|
|
61
|
+
if (sameShape && sameHue)
|
|
62
|
+
collisions.push([aKind, bKind]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { ok: collisions.length === 0, collisions };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The dash array for a stroke pattern, in one place.
|
|
69
|
+
*
|
|
70
|
+
* Exported because the scene is not the only thing that draws a connector: a
|
|
71
|
+
* legend has to draw the SAME line, and a legend whose swatch is an
|
|
72
|
+
* approximation is a legend you cannot trust. Copying this map was the exact
|
|
73
|
+
* failure the legend's own docstring promised against.
|
|
74
|
+
*/
|
|
75
|
+
export const CONNECTOR_DASH = {
|
|
76
|
+
solid: undefined,
|
|
77
|
+
dashed: "7 5",
|
|
78
|
+
dotted: "1 5",
|
|
79
|
+
double: "12 3",
|
|
80
|
+
tapered: "10 3 3 3",
|
|
81
|
+
};
|
|
82
|
+
/** The colour a connector of this kind is stroked in, on either surface. */
|
|
83
|
+
export function connectorStroke(style) {
|
|
84
|
+
return style.colour ?? `hsl(${Math.round(style.hue)} 55% 62%)`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* How thick, given where it is drawn.
|
|
88
|
+
*
|
|
89
|
+
* Inside the scene a connector is an aside and stays thin; above the stack the
|
|
90
|
+
* lines are the content and earn their weight.
|
|
91
|
+
*/
|
|
92
|
+
export function connectorWidth(style, overview) {
|
|
93
|
+
return overview ? Math.max(1.6, style.width) : Math.min(1.4, style.width);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=connectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectors.js","sourceRoot":"","sources":["../../src/scene/connectors.ts"],"names":[],"mappings":"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"}
|