@edv4h/usketch-gpu3d-renderer 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.
@@ -0,0 +1,272 @@
1
+ // The board's shapes in the GPU 3D view: each shape's picture (see `shape-raster`) laid
2
+ // flat as a textured quad at the ground's height under its centre, in z-order. Like the
3
+ // CSS 3D renderer's flat shapes they are drawn over the ground and the terrain (not
4
+ // hidden by a hill: the terrain's drawn surface, water included, isn't known here).
5
+ // Pictures are made a few per frame (nearest the camera first) and kept until the shape
6
+ // changes; until then the shape shows as a pale placeholder.
7
+ import { safeRotation, VIEW3D_DEPTH_GLSL, viewMatrix, } from "@edv4h/usketch-shared";
8
+ import { linkProgram } from "./gpu3d-ground.js";
9
+ import { loadSvgImage, nodePictureSvg, PICTURE_PAD, pictureScale, primitivePicture, } from "./shape-raster.js";
10
+ /** Time (ms) a frame may spend making pictures. */
11
+ const PICTURE_BUDGET_MS = 6;
12
+ /** Pictures being loaded at once, at most. */
13
+ const MAX_PENDING = 6;
14
+ /** Frames a picture is kept after its shape stops being drawn. */
15
+ const KEEP_FRAMES = 120;
16
+ const PLACEHOLDER = [0.55, 0.58, 0.65, 0.25];
17
+ /** How high a flat shape lies: the ground under its centre (as the CSS 3D renderer). Pure. */
18
+ export function flatShapeHeight(box, groundAt) {
19
+ const h = groundAt(box.x + box.width / 2, box.y + box.height / 2);
20
+ return Number.isFinite(h) && h > 0 ? h : 0;
21
+ }
22
+ const VERTEX = `#version 300 es
23
+ layout(location = 0) in vec2 aCorner;
24
+ uniform mat4 uView;
25
+ uniform vec4 uRect;
26
+ uniform vec2 uRot;
27
+ uniform float uHeight;
28
+ out vec2 vUv;
29
+ ${VIEW3D_DEPTH_GLSL}
30
+ void main() {
31
+ vUv = aCorner;
32
+ vec2 halfSize = uRect.zw * 0.5;
33
+ vec2 p = aCorner * uRect.zw - halfSize;
34
+ p = vec2(p.x * uRot.x - p.y * uRot.y, p.x * uRot.y + p.y * uRot.x);
35
+ vec2 world = uRect.xy + halfSize + p;
36
+ gl_Position = view3dPosition(uView * vec4(world, uHeight, 1.0));
37
+ }`;
38
+ const FRAGMENT = `#version 300 es
39
+ precision highp float;
40
+ in vec2 vUv;
41
+ uniform sampler2D uTex;
42
+ uniform float uHasTex;
43
+ uniform vec4 uColor;
44
+ out vec4 outColor;
45
+ void main() {
46
+ vec4 c = uHasTex > 0.5 ? texture(uTex, vUv) : uColor;
47
+ if (c.a < 0.004) discard;
48
+ outColor = c;
49
+ }`;
50
+ export class ShapePainter {
51
+ gl;
52
+ onReady;
53
+ program;
54
+ vao;
55
+ buffer;
56
+ u = {};
57
+ pictures = new Map();
58
+ frameNo = 0;
59
+ /** Anisotropic filtering (sharp pictures seen at a grazing angle), when there is. */
60
+ aniso;
61
+ pending = 0;
62
+ released = false;
63
+ constructor(gl,
64
+ /** Asked for another frame when a picture is ready. */
65
+ onReady) {
66
+ this.gl = gl;
67
+ this.onReady = onReady;
68
+ this.program = linkProgram(gl, VERTEX, FRAGMENT);
69
+ const ext = gl.getExtension("EXT_texture_filter_anisotropic");
70
+ this.aniso = ext
71
+ ? {
72
+ ext: ext.TEXTURE_MAX_ANISOTROPY_EXT,
73
+ max: Math.min(8, gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT)),
74
+ }
75
+ : null;
76
+ for (const name of ["uView", "uRect", "uRot", "uHeight", "uTex", "uHasTex", "uColor"]) {
77
+ this.u[name] = gl.getUniformLocation(this.program, name);
78
+ }
79
+ const vao = gl.createVertexArray();
80
+ const buffer = gl.createBuffer();
81
+ if (!vao || !buffer)
82
+ throw new Error("gl objects");
83
+ this.vao = vao;
84
+ this.buffer = buffer;
85
+ gl.bindVertexArray(vao);
86
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
87
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]), gl.STATIC_DRAW);
88
+ gl.enableVertexAttribArray(0);
89
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 8, 0);
90
+ gl.bindVertexArray(null);
91
+ }
92
+ /** Draw the shapes; `true` when pictures are still to be made (draw again). */
93
+ draw(frame, source) {
94
+ const { gl, u } = this;
95
+ this.frameNo++;
96
+ const { viewport } = frame.view;
97
+ const drawn = [];
98
+ const due = [];
99
+ for (const shape of source.shapes) {
100
+ if (shape.hidden)
101
+ continue;
102
+ if (!(shape.width > 0 && shape.height > 0))
103
+ continue;
104
+ const def = source.registry.get(shape.type);
105
+ if (!def)
106
+ continue;
107
+ const node = source.node(shape.id);
108
+ if (!node && !def.gpuPrimitive)
109
+ continue;
110
+ const scale = pictureScale(viewport.zoom, frame.pixelRatio, shape.width, shape.height);
111
+ const picture = this.pictures.get(shape.id) ?? null;
112
+ if (picture)
113
+ picture.lastFrame = this.frameNo;
114
+ const fresh = picture && picture.shape === shape && picture.scale === scale;
115
+ if (!fresh && !picture?.pending) {
116
+ // Nearer the camera = lower on the flat screen.
117
+ const nearness = (shape.y + shape.height / 2) * viewport.zoom + viewport.y;
118
+ due.push({ shape, scale, nearness });
119
+ }
120
+ const box = { x: shape.x, y: shape.y, width: shape.width, height: shape.height };
121
+ drawn.push({ shape, picture, height: flatShapeHeight(box, source.groundAt) });
122
+ }
123
+ this.makePictures(due, source);
124
+ this.forgetOld();
125
+ if (drawn.length > 0) {
126
+ const origin = gridOrigin(viewport.x, viewport.y, viewport.zoom);
127
+ // biome-ignore lint/correctness/useHookAtTopLevel: WebGL's useProgram, not a React hook
128
+ gl.useProgram(this.program);
129
+ gl.bindVertexArray(this.vao);
130
+ gl.uniformMatrix4fv(u.uView, false, viewMatrix(frame.view, origin));
131
+ gl.uniform1i(u.uTex, 0);
132
+ gl.uniform4fv(u.uColor, PLACEHOLDER);
133
+ gl.activeTexture(gl.TEXTURE0);
134
+ // Over the ground and the terrain, in z-order; leaves the depth to what stands.
135
+ gl.depthFunc(gl.ALWAYS);
136
+ gl.depthMask(false);
137
+ for (const { shape, picture, height } of drawn) {
138
+ const tex = picture && !picture.failed ? picture.texture : null;
139
+ if (picture?.failed)
140
+ continue;
141
+ const rad = (safeRotation(shape.rotation) * Math.PI) / 180;
142
+ gl.uniform4f(u.uRect, shape.x - PICTURE_PAD - origin.x, shape.y - PICTURE_PAD - origin.y, shape.width + 2 * PICTURE_PAD, shape.height + 2 * PICTURE_PAD);
143
+ gl.uniform2f(u.uRot, Math.cos(rad), Math.sin(rad));
144
+ gl.uniform1f(u.uHeight, height);
145
+ gl.uniform1f(u.uHasTex, tex ? 1 : 0);
146
+ gl.bindTexture(gl.TEXTURE_2D, tex);
147
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
148
+ }
149
+ gl.depthFunc(gl.LEQUAL);
150
+ gl.depthMask(true);
151
+ gl.bindTexture(gl.TEXTURE_2D, null);
152
+ gl.bindVertexArray(null);
153
+ }
154
+ return due.length > 0 && this.pending < MAX_PENDING;
155
+ }
156
+ makePictures(due, source) {
157
+ if (due.length === 0)
158
+ return;
159
+ due.sort((a, b) => b.nearness - a.nearness);
160
+ const start = now();
161
+ for (const { shape, scale } of due) {
162
+ if (this.pending >= MAX_PENDING || now() - start > PICTURE_BUDGET_MS)
163
+ break;
164
+ const def = source.registry.get(shape.type);
165
+ if (!def)
166
+ continue;
167
+ const old = this.pictures.get(shape.id);
168
+ const picture = {
169
+ shape,
170
+ scale,
171
+ // Keep showing the old picture until the new one is ready.
172
+ texture: old?.texture ?? null,
173
+ pending: false,
174
+ failed: false,
175
+ lastFrame: this.frameNo,
176
+ };
177
+ this.pictures.set(shape.id, picture);
178
+ const node = source.node(shape.id);
179
+ try {
180
+ if (node) {
181
+ const svg = nodePictureSvg(node, shape.width, shape.height, scale);
182
+ picture.pending = true;
183
+ this.pending++;
184
+ loadSvgImage(svg).then((img) => this.finish(shape.id, picture, img), () => this.finish(shape.id, picture, null));
185
+ }
186
+ else {
187
+ const prim = def.gpuPrimitive?.(shape) ?? null;
188
+ const canvas = prim ? primitivePicture(shape, prim, scale) : null;
189
+ this.upload(picture, canvas);
190
+ }
191
+ }
192
+ catch {
193
+ picture.failed = !picture.texture;
194
+ }
195
+ }
196
+ }
197
+ finish(id, picture, img) {
198
+ this.pending--;
199
+ picture.pending = false;
200
+ if (this.released || this.pictures.get(id) !== picture)
201
+ return;
202
+ this.upload(picture, img);
203
+ this.onReady();
204
+ }
205
+ upload(picture, src) {
206
+ const { gl } = this;
207
+ if (!src) {
208
+ picture.failed = !picture.texture;
209
+ return;
210
+ }
211
+ const tex = picture.texture ?? gl.createTexture();
212
+ if (!tex)
213
+ return;
214
+ gl.bindTexture(gl.TEXTURE_2D, tex);
215
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
216
+ try {
217
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, src);
218
+ gl.generateMipmap(gl.TEXTURE_2D);
219
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
220
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
221
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
222
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
223
+ if (this.aniso)
224
+ gl.texParameterf(gl.TEXTURE_2D, this.aniso.ext, this.aniso.max);
225
+ picture.texture = tex;
226
+ picture.failed = false;
227
+ }
228
+ catch {
229
+ // A tainted picture (a browser that won't let it on the GPU): the placeholder stays.
230
+ if (!picture.texture)
231
+ gl.deleteTexture(tex);
232
+ picture.failed = !picture.texture;
233
+ }
234
+ finally {
235
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
236
+ gl.bindTexture(gl.TEXTURE_2D, null);
237
+ }
238
+ }
239
+ forgetOld() {
240
+ for (const [id, p] of this.pictures) {
241
+ if (this.frameNo - p.lastFrame > KEEP_FRAMES && !p.pending) {
242
+ if (p.texture)
243
+ this.gl.deleteTexture(p.texture);
244
+ this.pictures.delete(id);
245
+ }
246
+ }
247
+ }
248
+ /** Pictures held now (tests, stats). */
249
+ pictureCount() {
250
+ return this.pictures.size;
251
+ }
252
+ release() {
253
+ this.released = true;
254
+ const { gl } = this;
255
+ for (const p of this.pictures.values())
256
+ if (p.texture)
257
+ gl.deleteTexture(p.texture);
258
+ this.pictures.clear();
259
+ gl.deleteBuffer(this.buffer);
260
+ gl.deleteVertexArray(this.vao);
261
+ gl.deleteProgram(this.program);
262
+ }
263
+ }
264
+ /** A grid-aligned world point near the view's top-left (keeps float32 vertices small). */
265
+ function gridOrigin(vx, vy, zoom) {
266
+ const z = zoom > 0 ? zoom : 1;
267
+ return { x: Math.round(-vx / z / 256) * 256, y: Math.round(-vy / z / 256) * 256 };
268
+ }
269
+ function now() {
270
+ return typeof performance !== "undefined" ? performance.now() : Date.now();
271
+ }
272
+ //# sourceMappingURL=gpu3d-shapes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu3d-shapes.js","sourceRoot":"","sources":["../src/gpu3d-shapes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { GROUND_GRID_SIZE, type GroundLook, GroundPainter, groundRegionPx, linkProgram, parseCssColor, type StageRect, stageRectWorld, } from "./gpu3d-ground.js";
2
+ export { createGpu3dRendererPlugin, drawnCamera, GPU_STAGE_ATTR, GPU3D_LAYER_ID, type Gpu3dRendererApi, type Gpu3dRendererOptions, gpu3dRendererService, hiddenLayerRules, setupGpu3dRenderer, } from "./gpu3d-renderer.js";
3
+ export { flatShapeHeight, ShapePainter, type ShapeSource } from "./gpu3d-shapes.js";
4
+ export { loadSvgImage, MAX_PICTURE_PX, nodePictureSvg, PICTURE_PAD, pictureScale, primitivePicture, } from "./shape-raster.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,gBAAgB,EAChB,KAAK,UAAU,EACf,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,KAAK,SAAS,EACd,cAAc,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,yBAAyB,EACzB,WAAW,EACX,cAAc,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EACN,YAAY,EACZ,cAAc,EACd,cAAc,EACd,WAAW,EACX,YAAY,EACZ,gBAAgB,GAChB,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { GROUND_GRID_SIZE, GroundPainter, groundRegionPx, linkProgram, parseCssColor, stageRectWorld, } from "./gpu3d-ground.js";
2
+ export { createGpu3dRendererPlugin, drawnCamera, GPU_STAGE_ATTR, GPU3D_LAYER_ID, gpu3dRendererService, hiddenLayerRules, setupGpu3dRenderer, } from "./gpu3d-renderer.js";
3
+ export { flatShapeHeight, ShapePainter } from "./gpu3d-shapes.js";
4
+ export { loadSvgImage, MAX_PICTURE_PX, nodePictureSvg, PICTURE_PAD, pictureScale, primitivePicture, } from "./shape-raster.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,gBAAgB,EAEhB,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EAEb,cAAc,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,yBAAyB,EACzB,WAAW,EACX,cAAc,EACd,cAAc,EAGd,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAoB,MAAM,mBAAmB,CAAC;AACpF,OAAO,EACN,YAAY,EACZ,cAAc,EACd,cAAc,EACd,WAAW,EACX,YAAY,EACZ,gBAAgB,GAChB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { GpuPrimitive, ShapeData } from "@edv4h/usketch-shared";
2
+ /** Longest side of a shape picture, device px. */
3
+ export declare const MAX_PICTURE_PX = 2048;
4
+ /**
5
+ * World units a picture reaches past the shape's box on each side: what a shape draws
6
+ * just outside it (half of an edge's stroke, a shadow).
7
+ */
8
+ export declare const PICTURE_PAD = 8;
9
+ /**
10
+ * Picture px per world unit for a view zoom and pixel ratio: twice the screen's, as a
11
+ * power of two (so a little zooming re-uses the pictures), between 1/4 and 4, and small enough that the longest side
12
+ * of a `width` × `height` shape stays within {@link MAX_PICTURE_PX}. Pure.
13
+ */
14
+ export declare function pictureScale(zoom: number, pixelRatio: number, width: number, height: number): number;
15
+ /**
16
+ * The SVG markup of a DOM node's drawing at `scale` picture px per CSS px, `width` ×
17
+ * `height` CSS px, with {@link PICTURE_PAD} around it. Needs a document (computed styles).
18
+ */
19
+ export declare function nodePictureSvg(node: Element, width: number, height: number, scale: number): string;
20
+ /** Loads SVG markup as an image (rejects when it can't be decoded). */
21
+ export declare function loadSvgImage(svg: string): Promise<HTMLImageElement>;
22
+ /**
23
+ * A shape's `gpuPrimitive` drawn on a 2D canvas at `scale` px per world unit (in the
24
+ * shape's own unrotated box, with {@link PICTURE_PAD} around it). `null` when there is no 2D canvas or nothing to draw.
25
+ */
26
+ export declare function primitivePicture(shape: ShapeData, prim: GpuPrimitive, scale: number): HTMLCanvasElement | null;
27
+ //# sourceMappingURL=shape-raster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-raster.d.ts","sourceRoot":"","sources":["../src/shape-raster.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAErE,kDAAkD;AAClD,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC;;;GAGG;AACH,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B;;;;GAIG;AACH,wBAAgB,YAAY,CAC3B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACZ,MAAM,CAQR;AAgCD;;;GAGG;AACH,wBAAgB,cAAc,CAC7B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,MAAM,CAsBR;AAED,uEAAuE;AACvE,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAMzE;AAOD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,GACX,iBAAiB,GAAG,IAAI,CAsD1B"}
@@ -0,0 +1,140 @@
1
+ /** Longest side of a shape picture, device px. */
2
+ export const MAX_PICTURE_PX = 2048;
3
+ /**
4
+ * World units a picture reaches past the shape's box on each side: what a shape draws
5
+ * just outside it (half of an edge's stroke, a shadow).
6
+ */
7
+ export const PICTURE_PAD = 8;
8
+ /**
9
+ * Picture px per world unit for a view zoom and pixel ratio: twice the screen's, as a
10
+ * power of two (so a little zooming re-uses the pictures), between 1/4 and 4, and small enough that the longest side
11
+ * of a `width` × `height` shape stays within {@link MAX_PICTURE_PX}. Pure.
12
+ */
13
+ export function pictureScale(zoom, pixelRatio, width, height) {
14
+ // Twice the screen's px: the ground is seen at a slant, which squeezes the picture.
15
+ const want = 2 * (Number.isFinite(zoom) && zoom > 0 ? zoom : 1) * (pixelRatio > 0 ? pixelRatio : 1);
16
+ let s = 2 ** Math.ceil(Math.log2(Math.min(4, Math.max(0.25, want))));
17
+ const side = Math.max(width, height, 1) + 2 * PICTURE_PAD;
18
+ while (s > 1 / 64 && side * s > MAX_PICTURE_PX)
19
+ s /= 2;
20
+ return s;
21
+ }
22
+ /** Computed styles left out of the copy (they place the node on the board, or hide it there). */
23
+ const SKIPPED = new Set(["visibility", "pointer-events", "cursor", "transition", "animation"]);
24
+ /** Styles of the copied root that would move it inside the picture. */
25
+ const ROOT_RESET = {
26
+ position: "relative",
27
+ left: "0px",
28
+ top: "0px",
29
+ right: "auto",
30
+ bottom: "auto",
31
+ margin: "0px",
32
+ transform: "none",
33
+ "z-index": "auto",
34
+ };
35
+ function copyStyles(from, to) {
36
+ const cs = getComputedStyle(from);
37
+ const out = [];
38
+ for (let i = 0; i < cs.length; i++) {
39
+ const name = cs[i];
40
+ if (SKIPPED.has(name) || name.startsWith("transition") || name.startsWith("animation"))
41
+ continue;
42
+ const value = cs.getPropertyValue(name);
43
+ if (value)
44
+ out.push(`${name}:${value}`);
45
+ }
46
+ to.setAttribute("style", out.join(";"));
47
+ const fromKids = from.children;
48
+ const toKids = to.children;
49
+ for (let i = 0; i < fromKids.length && i < toKids.length; i++)
50
+ copyStyles(fromKids[i], toKids[i]);
51
+ }
52
+ /**
53
+ * The SVG markup of a DOM node's drawing at `scale` picture px per CSS px, `width` ×
54
+ * `height` CSS px, with {@link PICTURE_PAD} around it. Needs a document (computed styles).
55
+ */
56
+ export function nodePictureSvg(node, width, height, scale) {
57
+ const clone = node.cloneNode(true);
58
+ copyStyles(node, clone);
59
+ if (clone instanceof HTMLElement || clone instanceof SVGElement) {
60
+ for (const [k, v] of Object.entries(ROOT_RESET))
61
+ clone.style.setProperty(k, v);
62
+ }
63
+ // Images from other URLs can't load inside an SVG image: leave them out.
64
+ for (const img of clone.querySelectorAll("img, image, video, iframe, canvas"))
65
+ img.remove();
66
+ const wrap = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
67
+ wrap.setAttribute("style", `width:${width}px;height:${height}px;padding:${PICTURE_PAD}px;` +
68
+ `transform:scale(${scale});transform-origin:0 0;`);
69
+ wrap.appendChild(clone);
70
+ const xml = new XMLSerializer().serializeToString(wrap);
71
+ const pw = Math.max(1, Math.ceil((width + 2 * PICTURE_PAD) * scale));
72
+ const ph = Math.max(1, Math.ceil((height + 2 * PICTURE_PAD) * scale));
73
+ return (`<svg xmlns="http://www.w3.org/2000/svg" width="${pw}" height="${ph}">` +
74
+ `<foreignObject x="0" y="0" width="${pw}" height="${ph}">${xml}</foreignObject></svg>`);
75
+ }
76
+ /** Loads SVG markup as an image (rejects when it can't be decoded). */
77
+ export async function loadSvgImage(svg) {
78
+ const img = new Image();
79
+ img.decoding = "async";
80
+ img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
81
+ await img.decode();
82
+ return img;
83
+ }
84
+ function css(c, opacity = 1) {
85
+ const [r, g, b, a] = c;
86
+ return `rgba(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)},${(a ?? 1) * opacity})`;
87
+ }
88
+ /**
89
+ * A shape's `gpuPrimitive` drawn on a 2D canvas at `scale` px per world unit (in the
90
+ * shape's own unrotated box, with {@link PICTURE_PAD} around it). `null` when there is no 2D canvas or nothing to draw.
91
+ */
92
+ export function primitivePicture(shape, prim, scale) {
93
+ if (typeof document === "undefined")
94
+ return null;
95
+ const w = Math.max(1, Math.ceil((shape.width + 2 * PICTURE_PAD) * scale));
96
+ const h = Math.max(1, Math.ceil((shape.height + 2 * PICTURE_PAD) * scale));
97
+ const canvas = document.createElement("canvas");
98
+ canvas.width = w;
99
+ canvas.height = h;
100
+ const g = canvas.getContext("2d");
101
+ if (!g)
102
+ return null;
103
+ g.scale(scale, scale);
104
+ g.translate(PICTURE_PAD - shape.x, PICTURE_PAD - shape.y);
105
+ const { bounds } = prim;
106
+ const half = prim.strokeWidth / 2;
107
+ g.beginPath();
108
+ if (prim.kind === "rect") {
109
+ const r = Math.min(prim.cornerRadius ?? 0, bounds.width / 2, bounds.height / 2);
110
+ g.roundRect(bounds.x + half, bounds.y + half, bounds.width - 2 * half, bounds.height - 2 * half, r);
111
+ }
112
+ else if (prim.kind === "ellipse") {
113
+ g.ellipse(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2, Math.max(0, bounds.width / 2 - half), Math.max(0, bounds.height / 2 - half), 0, 0, Math.PI * 2);
114
+ }
115
+ else if (prim.vertices && prim.vertices.length >= 4) {
116
+ const v = prim.vertices;
117
+ g.moveTo(v[0], v[1]);
118
+ for (let i = 2; i + 1 < v.length; i += 2)
119
+ g.lineTo(v[i], v[i + 1]);
120
+ if (prim.kind === "polygon")
121
+ g.closePath();
122
+ }
123
+ else {
124
+ return null;
125
+ }
126
+ g.globalAlpha = Math.min(1, Math.max(0, prim.opacity));
127
+ if (prim.kind !== "polyline" && prim.fill[3] > 0) {
128
+ g.fillStyle = css(prim.fill);
129
+ g.fill();
130
+ }
131
+ if (prim.strokeWidth > 0 && prim.stroke[3] > 0) {
132
+ g.lineWidth = prim.strokeWidth;
133
+ g.lineJoin = "round";
134
+ g.lineCap = "round";
135
+ g.strokeStyle = css(prim.stroke);
136
+ g.stroke();
137
+ }
138
+ return canvas;
139
+ }
140
+ //# sourceMappingURL=shape-raster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape-raster.js","sourceRoot":"","sources":["../src/shape-raster.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "@edv4h/usketch-gpu3d-renderer",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.ts",
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@edv4h/usketch-shared": "4.25.0"
16
+ },
17
+ "peerDependencies": {
18
+ "react": ">=19"
19
+ },
20
+ "devDependencies": {
21
+ "@types/react": "19.3.0",
22
+ "@types/react-dom": "19.3.0",
23
+ "jsdom": "31.0.0",
24
+ "react-dom": "19.3.1",
25
+ "typescript": "7.0.2",
26
+ "vitest": "5.1.0"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md"
34
+ ],
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/EdV4H/usketch.git",
39
+ "directory": "packages/gpu3d-renderer"
40
+ },
41
+ "homepage": "https://github.com/EdV4H/usketch#readme",
42
+ "bugs": {
43
+ "url": "https://github.com/EdV4H/usketch/issues"
44
+ },
45
+ "description": "Experimental GPU (WebGL2) renderer for the board 3D view: draws the ground, the terrain and what stands on it on one canvas with a depth buffer",
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.json",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run --passWithNoTests",
50
+ "clean": "rm -rf dist"
51
+ }
6
52
  }