bookcover-3d-web 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.
Files changed (51) hide show
  1. package/README.md +159 -0
  2. package/README_mockup.webp +0 -0
  3. package/README_sample.webp +0 -0
  4. package/dist/geometry.d.ts +12 -0
  5. package/dist/geometry.d.ts.map +1 -0
  6. package/dist/geometry.js +390 -0
  7. package/dist/geometry.js.map +1 -0
  8. package/dist/index.d.ts +44 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +404 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/math.d.ts +15 -0
  13. package/dist/math.d.ts.map +1 -0
  14. package/dist/math.js +92 -0
  15. package/dist/math.js.map +1 -0
  16. package/dist/photo.d.ts +6 -0
  17. package/dist/photo.d.ts.map +1 -0
  18. package/dist/photo.js +72 -0
  19. package/dist/photo.js.map +1 -0
  20. package/dist/svg.d.ts +12 -0
  21. package/dist/svg.d.ts.map +1 -0
  22. package/dist/svg.js +40 -0
  23. package/dist/svg.js.map +1 -0
  24. package/dist/types.d.ts +33 -0
  25. package/dist/types.d.ts.map +1 -0
  26. package/dist/types.js +3 -0
  27. package/dist/types.js.map +1 -0
  28. package/dist/webgl.d.ts +15 -0
  29. package/dist/webgl.d.ts.map +1 -0
  30. package/dist/webgl.js +119 -0
  31. package/dist/webgl.js.map +1 -0
  32. package/package.json +18 -0
  33. package/src/backgrounds/coffee_table.jpg +0 -0
  34. package/src/backgrounds/table_side.jpg +0 -0
  35. package/src/backgrounds/table_with_book.jpg +0 -0
  36. package/src/backgrounds/table_with_laptop.jpg +0 -0
  37. package/src/backgrounds/wood.jpg +0 -0
  38. package/src/backgrounds_originals/coffee_table.jpg +0 -0
  39. package/src/backgrounds_originals/table_side.jpg +0 -0
  40. package/src/backgrounds_originals/table_with_book.jpg +0 -0
  41. package/src/backgrounds_originals/table_with_laptop.jpg +0 -0
  42. package/src/backgrounds_originals/wood.jpg +0 -0
  43. package/src/geometry.ts +513 -0
  44. package/src/index.ts +507 -0
  45. package/src/math.ts +84 -0
  46. package/src/photo.ts +77 -0
  47. package/src/svg.ts +46 -0
  48. package/src/types.ts +49 -0
  49. package/src/webgl.ts +131 -0
  50. package/tsconfig.json +19 -0
  51. package/tsconfig.tsbuildinfo +1 -0
package/dist/index.js ADDED
@@ -0,0 +1,404 @@
1
+ // 3D book renderer — public API
2
+ export { BACKGROUNDS } from './photo.js';
3
+ import { PHOTO_AZIMUTH, PHOTO_ELEVATION, PHOTO_BOOK_SCALE } from './photo.js';
4
+ import { mat4_perspective, mat4_look_at, mat4_mul, mat4_identity, mat4_rotate_z, normal_matrix } from './math.js';
5
+ import { parse_svg_size, svg_to_bitmap } from './svg.js';
6
+ import { VERT_SRC, FRAG_SRC, DEFAULT_LIGHT_AZ, DEFAULT_LIGHT_EL, DEFAULT_AMBIENT, build_program, upload_texture, create_page_edge_texture } from './webgl.js';
7
+ import { build_faces } from './geometry.js';
8
+ // -- Defaults --
9
+ const DEFAULT_AZIMUTH = -30;
10
+ const DEFAULT_ELEVATION = 20;
11
+ const DEFAULT_WIDTH = 800;
12
+ const DEFAULT_HEIGHT = 600;
13
+ // Camera projection constants — shared between render() and shadow projection in composite_photo()
14
+ const RENDER_FOV = 40; // field of view in degrees
15
+ const RENDER_DIST_FACTOR = 3; // camera distance = RENDER_DIST_FACTOR * book_height * zoom
16
+ /** Convert light azimuth/elevation degrees to a normalised world-space direction vector.
17
+ * Convention matches camera spherical coords: az=0 = from front, el=0 = horizontal. */
18
+ function light_dir_vec3(az_deg, el_deg) {
19
+ const az = az_deg * Math.PI / 180;
20
+ const el = el_deg * Math.PI / 180;
21
+ const x = Math.sin(az) * Math.cos(el);
22
+ const y = Math.sin(el);
23
+ const z = Math.cos(az) * Math.cos(el);
24
+ const len = Math.sqrt(x * x + y * y + z * z);
25
+ return [x / len, y / len, z / len];
26
+ }
27
+ // -- Renderer --
28
+ /** Persistent WebGL book renderer. Create once, call load() when covers change, render() cheaply. */
29
+ export class Book3DRenderer {
30
+ canvas;
31
+ _gl_state = null;
32
+ _faces = [];
33
+ _w = 1;
34
+ _h = 1;
35
+ _d = 0.1;
36
+ // Physical cover height in pt (Typst SVG output), used for photo composite scaling
37
+ _cover_height = 1;
38
+ _textures = [];
39
+ _page_tex = null;
40
+ // Prefer OffscreenCanvas for performance; fall back to DOM canvas for WebGL compatibility
41
+ constructor(width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT) {
42
+ const offscreen = Book3DRenderer._try_offscreen_webgl(width, height);
43
+ if (offscreen) {
44
+ this.canvas = offscreen;
45
+ }
46
+ else {
47
+ const el = document.createElement('canvas');
48
+ el.width = width;
49
+ el.height = height;
50
+ el.style.display = 'none';
51
+ this.canvas = el;
52
+ }
53
+ }
54
+ /** Resize the render canvas without recreating the WebGL context or re-uploading textures */
55
+ resize(width, height) {
56
+ this.canvas.width = width;
57
+ this.canvas.height = height;
58
+ }
59
+ /** Projected aspect ratio (width/height) of the book at the default viewing angle */
60
+ get_projected_aspect() {
61
+ const az_rad = Math.abs(DEFAULT_AZIMUTH) * Math.PI / 180;
62
+ const el_rad = Math.abs(DEFAULT_ELEVATION) * Math.PI / 180;
63
+ const proj_w = this._w * Math.cos(az_rad) + this._d * Math.sin(az_rad);
64
+ const proj_h = this._h * Math.cos(el_rad);
65
+ return proj_w / proj_h;
66
+ }
67
+ // Test whether OffscreenCanvas supports WebGL in this browser
68
+ static _try_offscreen_webgl(w, h) {
69
+ try {
70
+ const test = new OffscreenCanvas(1, 1);
71
+ const gl = test.getContext('webgl', { alpha: true, premultipliedAlpha: false, antialias: true });
72
+ if (!gl)
73
+ return null;
74
+ gl.getExtension('WEBGL_lose_context')?.loseContext();
75
+ return new OffscreenCanvas(w, h);
76
+ }
77
+ catch { /* OffscreenCanvas not supported */ }
78
+ return null;
79
+ }
80
+ /** Initialise WebGL on first use */
81
+ _init_gl() {
82
+ if (this._gl_state)
83
+ return this._gl_state;
84
+ const gl = this.canvas.getContext('webgl', {
85
+ alpha: true, premultipliedAlpha: false, antialias: true,
86
+ });
87
+ if (!gl)
88
+ throw new Error('[cover-3d] WebGL not available in this environment');
89
+ const prog = build_program(gl, VERT_SRC, FRAG_SRC);
90
+ this._gl_state = {
91
+ gl, prog,
92
+ a_pos: gl.getAttribLocation(prog, 'a_pos'),
93
+ a_uv: gl.getAttribLocation(prog, 'a_uv'),
94
+ a_normal: gl.getAttribLocation(prog, 'a_normal'),
95
+ u_mvp: gl.getUniformLocation(prog, 'u_mvp'),
96
+ u_mv: gl.getUniformLocation(prog, 'u_mv'),
97
+ u_norm_mat: gl.getUniformLocation(prog, 'u_norm_mat'),
98
+ u_light_dir: gl.getUniformLocation(prog, 'u_light_dir'),
99
+ u_ambient: gl.getUniformLocation(prog, 'u_ambient'),
100
+ u_exposure: gl.getUniformLocation(prog, 'u_exposure'),
101
+ u_tex: gl.getUniformLocation(prog, 'u_tex'),
102
+ u_use_tex: gl.getUniformLocation(prog, 'u_use_tex'),
103
+ u_color: gl.getUniformLocation(prog, 'u_color'),
104
+ u_cam_z: gl.getUniformLocation(prog, 'u_cam_z'),
105
+ };
106
+ return this._gl_state;
107
+ }
108
+ /** Load new cover SVGs and re-upload geometry / textures.
109
+ * depth_mm: book thickness in mm, used when no spine SVG is present (e.g. coil/wire bindings).
110
+ * Pass 0 for bindings where the cover faces meet (e.g. saddle stitch). */
111
+ async load(svgs, cover_type = 'paperback', depth_mm) {
112
+ const { gl } = this._init_gl();
113
+ // Free previous GPU resources
114
+ if (this._page_tex) {
115
+ gl.deleteTexture(this._page_tex);
116
+ this._page_tex = null;
117
+ }
118
+ for (const t of this._textures)
119
+ gl.deleteTexture(t);
120
+ this._textures = [];
121
+ for (const f of this._faces) {
122
+ gl.deleteBuffer(f.vbo);
123
+ gl.deleteBuffer(f.ibo);
124
+ }
125
+ this._faces = [];
126
+ // Parse SVG dimensions to derive normalised 3D aspect ratios
127
+ const front_size = parse_svg_size(svgs.front);
128
+ this._cover_height = front_size.height;
129
+ this._h = 1.0;
130
+ this._w = front_size.width / front_size.height;
131
+ const spine_size = svgs.spine ? parse_svg_size(svgs.spine) : null;
132
+ // No spine SVG → convert depth_mm to the same pt-ratio as spine_size.width/front_size.height
133
+ // 1pt = 25.4/72 mm, so depth_pt = depth_mm * 72/25.4
134
+ const PT_TO_MM = 25.4 / 72;
135
+ this._d = spine_size ? spine_size.width / front_size.height
136
+ : (depth_mm != null ? depth_mm / (front_size.height * PT_TO_MM) : 0);
137
+ // Recreate page-edge texture scaled to this book's spine thickness
138
+ this._page_tex = create_page_edge_texture(gl, this._d);
139
+ // Rasterise each SVG face into an ImageBitmap for GPU upload (4x resolution for quality)
140
+ const [front_bmp, back_bmp, spine_bmp] = await Promise.all([
141
+ svg_to_bitmap(svgs.front, front_size.width * 4, front_size.height * 4),
142
+ svg_to_bitmap(svgs.back, front_size.width * 4, front_size.height * 4),
143
+ spine_size
144
+ ? svg_to_bitmap(svgs.spine, spine_size.width * 4, spine_size.height * 4)
145
+ : Promise.resolve(null),
146
+ ]);
147
+ const front_tex = upload_texture(gl, front_bmp);
148
+ const back_tex = upload_texture(gl, back_bmp);
149
+ const spine_tex = spine_bmp ? upload_texture(gl, spine_bmp) : null;
150
+ front_bmp.close();
151
+ back_bmp.close();
152
+ if (spine_bmp)
153
+ spine_bmp.close();
154
+ this._textures = [front_tex, back_tex, ...(spine_tex ? [spine_tex] : [])];
155
+ // Build face geometry and upload vertex/index buffers
156
+ const face_data = build_faces(this._w, this._h, this._d, front_tex, back_tex, spine_tex, cover_type, this._page_tex);
157
+ for (const fd of face_data) {
158
+ const vbo = gl.createBuffer();
159
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
160
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(fd.vertices), gl.STATIC_DRAW);
161
+ const ibo = gl.createBuffer();
162
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
163
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(fd.indices), gl.STATIC_DRAW);
164
+ this._faces.push({ vbo, ibo, tex: fd.texture, color: fd.color, index_count: fd.indices.length });
165
+ }
166
+ }
167
+ /** Render the book at the given camera angles (degrees), zoom multiplier, and clockwise roll */
168
+ render(azimuth = DEFAULT_AZIMUTH, elevation = DEFAULT_ELEVATION, zoom = 1.0, roll = 0, light_az = DEFAULT_LIGHT_AZ, light_el = DEFAULT_LIGHT_EL, ambient = DEFAULT_AMBIENT, exposure = 1.0) {
169
+ if (!this._gl_state || this._faces.length === 0)
170
+ return;
171
+ const { gl, prog, a_pos, a_uv, a_normal, u_mvp, u_mv, u_norm_mat, u_light_dir, u_ambient, u_exposure, u_tex, u_use_tex, u_color, u_cam_z } = this._gl_state;
172
+ const w = this.canvas.width;
173
+ const h = this.canvas.height;
174
+ gl.viewport(0, 0, w, h);
175
+ gl.clearColor(0, 0, 0, 0);
176
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
177
+ gl.enable(gl.DEPTH_TEST);
178
+ gl.disable(gl.CULL_FACE);
179
+ gl.useProgram(prog);
180
+ // Camera in spherical coordinates orbiting the origin
181
+ const az = azimuth * Math.PI / 180;
182
+ const el = elevation * Math.PI / 180;
183
+ const dist = RENDER_DIST_FACTOR * this._h * zoom;
184
+ const cam = [
185
+ dist * Math.sin(az) * Math.cos(el),
186
+ dist * Math.sin(el),
187
+ dist * Math.cos(az) * Math.cos(el),
188
+ ];
189
+ // MVP = projection × view × model (model rotates book clockwise by roll degrees)
190
+ const proj = mat4_perspective(RENDER_FOV, w / h, 0.01, 100);
191
+ const view = mat4_look_at(cam, [0, 0, 0]);
192
+ const model = roll !== 0 ? mat4_rotate_z(-roll) : mat4_identity();
193
+ const mv = mat4_mul(view, model);
194
+ const mvp = mat4_mul(proj, mv);
195
+ const norm = normal_matrix(mv);
196
+ gl.uniformMatrix4fv(u_mvp, false, mvp);
197
+ gl.uniformMatrix4fv(u_mv, false, mv);
198
+ gl.uniformMatrix3fv(u_norm_mat, false, norm);
199
+ gl.uniform3fv(u_light_dir, light_dir_vec3(light_az, light_el));
200
+ gl.uniform1f(u_ambient, ambient);
201
+ gl.uniform1f(u_exposure, exposure);
202
+ // The world origin (book centre) maps to z = -dist in view space;
203
+ // u_cam_z anchors the gradient so the face centre is always the midpoint.
204
+ gl.uniform1f(u_cam_z, -dist);
205
+ // Stride: 8 floats per vertex (xyz + uv + normal)
206
+ const stride = 8 * 4;
207
+ for (const face of this._faces) {
208
+ gl.bindBuffer(gl.ARRAY_BUFFER, face.vbo);
209
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, face.ibo);
210
+ gl.enableVertexAttribArray(a_pos);
211
+ gl.enableVertexAttribArray(a_uv);
212
+ gl.enableVertexAttribArray(a_normal);
213
+ gl.vertexAttribPointer(a_pos, 3, gl.FLOAT, false, stride, 0);
214
+ gl.vertexAttribPointer(a_uv, 2, gl.FLOAT, false, stride, 3 * 4);
215
+ gl.vertexAttribPointer(a_normal, 3, gl.FLOAT, false, stride, 5 * 4);
216
+ if (face.tex) {
217
+ gl.activeTexture(gl.TEXTURE0);
218
+ gl.bindTexture(gl.TEXTURE_2D, face.tex);
219
+ gl.uniform1i(u_tex, 0);
220
+ gl.uniform1i(u_use_tex, 1);
221
+ }
222
+ else {
223
+ gl.uniform1i(u_use_tex, 0);
224
+ gl.uniform3fv(u_color, face.color);
225
+ }
226
+ gl.drawElements(gl.TRIANGLES, face.index_count, gl.UNSIGNED_SHORT, 0);
227
+ }
228
+ }
229
+ /** Return the current frame as an ImageBitmap */
230
+ async snapshot() {
231
+ return createImageBitmap(this.canvas);
232
+ }
233
+ /** Render and return the current frame as PNG bytes */
234
+ async to_png() {
235
+ let blob;
236
+ if (this.canvas instanceof OffscreenCanvas) {
237
+ blob = await this.canvas.convertToBlob({ type: 'image/png' });
238
+ }
239
+ else {
240
+ blob = await new Promise((resolve, reject) => {
241
+ this.canvas.toBlob(b => b ? resolve(b) : reject(new Error('toBlob failed')), 'image/png');
242
+ });
243
+ }
244
+ return new Uint8Array(await blob.arrayBuffer());
245
+ }
246
+ /** Composite the book onto a background ImageBitmap at a flat-on-table angle.
247
+ * Renders at the given angles (defaulting to a flat overhead perspective),
248
+ * then draws the result centred over the background. */
249
+ async composite_photo(background, options = {}) {
250
+ const az = options.azimuth ?? PHOTO_AZIMUTH;
251
+ const el = options.elevation ?? PHOTO_ELEVATION;
252
+ const zoom = options.zoom ?? 1.0;
253
+ const roll = options.roll ?? 0;
254
+ const book_scale = options.book_scale ?? PHOTO_BOOK_SCALE;
255
+ const offset_x = options.offset_x ?? 0;
256
+ const offset_y = options.offset_y ?? 0;
257
+ const light_az = options.light_az ?? DEFAULT_LIGHT_AZ;
258
+ const light_el = options.light_el ?? DEFAULT_LIGHT_EL;
259
+ const ambient = options.ambient ?? DEFAULT_AMBIENT;
260
+ // Render at a fixed square size so the result is independent of the interactive canvas
261
+ const saved_w = this.canvas.width;
262
+ const saved_h = this.canvas.height;
263
+ const photo_size = 2048;
264
+ this.canvas.width = photo_size;
265
+ this.canvas.height = photo_size;
266
+ this.render(az, el, zoom, roll, light_az, light_el, ambient * 0.7, 1);
267
+ const book_bmp = await this.snapshot();
268
+ this.canvas.width = saved_w;
269
+ this.canvas.height = saved_h;
270
+ // Build composite at background's native resolution
271
+ const cw = background.width;
272
+ const ch = background.height;
273
+ const comp = new OffscreenCanvas(cw, ch);
274
+ const ctx = comp.getContext('2d');
275
+ // Draw background, then scale and centre the book render on top
276
+ ctx.drawImage(background, 0, 0);
277
+ // Scale so physical book height maps to a consistent fraction of the background.
278
+ // Divide by book_bmp.height (constant across all aspect ratios) so that landscape
279
+ // books are not made smaller than portrait books of the same physical height.
280
+ // 648pt = 9in, the most common trim height — used as a baseline so composites look
281
+ // correctly sized regardless of the actual book dimensions
282
+ const REFERENCE_HEIGHT = 648;
283
+ const scale = cw * book_scale * (this._cover_height / REFERENCE_HEIGHT) / book_bmp.height;
284
+ const bw = book_bmp.width * scale;
285
+ const bh = book_bmp.height * scale;
286
+ // Offsets are fractions of the background dimensions — independent of book size,
287
+ // so the book scales toward its fixed center position rather than the corner
288
+ const bx = (cw - bw) / 2 + offset_x * cw;
289
+ const by = (ch - bh) / 2 + offset_y * ch;
290
+ // Derive shadow direction from light properties projected into screen space.
291
+ // Shadow extends opposite to the light's horizontal direction, mapped onto the
292
+ // camera's right and down axes so it matches what the 3D shading already shows.
293
+ const az_rad = az * Math.PI / 180;
294
+ const el_rad = el * Math.PI / 180;
295
+ const laz_rad = light_az * Math.PI / 180;
296
+ const lel_rad = light_el * Math.PI / 180;
297
+ // Screen-space X: cross-camera component of the opposite-light direction
298
+ const shadow_sx = -Math.sin(laz_rad - az_rad);
299
+ // Screen-space Y (canvas +Y = down): camera elevation maps depth onto vertical
300
+ const shadow_sy = -Math.sin(el_rad) * Math.cos(laz_rad - az_rad);
301
+ // Shadow opacity: brighter ambient scenes cast softer shadows
302
+ const shadow_alpha = Math.max(0.1, 0.45 - ambient * 0.3);
303
+ // Shadow length: longer for low-angle light, shorter for overhead light
304
+ const shadow_len = bh * 0.09 * Math.max(0.15, Math.cos(lel_rad));
305
+ // Draw shadow pass: the book masked by a vertical gradient so the shadow is
306
+ // heavy at the base (ground contact) and fades to nothing at the top.
307
+ // This gives the silhouette shape but avoids darkening the air beside the book.
308
+ const smask = new OffscreenCanvas(book_bmp.width, book_bmp.height);
309
+ const sctx = smask.getContext('2d');
310
+ sctx.drawImage(book_bmp, 0, 0);
311
+ const vgrad = sctx.createLinearGradient(0, 0, 0, book_bmp.height);
312
+ vgrad.addColorStop(0, 'rgba(0,0,0,0)');
313
+ vgrad.addColorStop(1, 'rgba(0,0,0,1)');
314
+ sctx.globalCompositeOperation = 'destination-in';
315
+ sctx.fillStyle = vgrad;
316
+ sctx.fillRect(0, 0, book_bmp.width, book_bmp.height);
317
+ ctx.shadowColor = `rgba(0,0,0,${shadow_alpha.toFixed(2)})`;
318
+ ctx.shadowBlur = bh * 0.015 + shadow_len * 0.4;
319
+ ctx.shadowOffsetX = shadow_sx * shadow_len;
320
+ ctx.shadowOffsetY = shadow_sy * shadow_len;
321
+ ctx.filter = 'blur(1.5px)';
322
+ ctx.drawImage(smask, bx, by, bw, bh);
323
+ ctx.filter = 'none';
324
+ ctx.shadowColor = 'transparent';
325
+ // Draw the real book on top
326
+ ctx.filter = 'blur(1.5px)';
327
+ ctx.drawImage(book_bmp, bx, by, bw, bh);
328
+ ctx.filter = 'none';
329
+ // Draw sharp version on top, masked to centre only via radial gradient
330
+ const mask = new OffscreenCanvas(cw, ch);
331
+ const mctx = mask.getContext('2d');
332
+ mctx.drawImage(book_bmp, bx, by, bw, bh);
333
+ const r = Math.min(bw, bh);
334
+ const grad = mctx.createRadialGradient(bx + bw / 2, by + bh / 2, r * 0.25, bx + bw / 2, by + bh / 2, r * 0.6);
335
+ grad.addColorStop(0, 'black');
336
+ grad.addColorStop(1, 'transparent');
337
+ mctx.globalCompositeOperation = 'destination-in';
338
+ mctx.fillStyle = grad;
339
+ mctx.fillRect(0, 0, cw, ch);
340
+ ctx.drawImage(mask, 0, 0);
341
+ book_bmp.close();
342
+ // NOTE Don't remove comment block below and will manually restore when want to test
343
+ // DEBUG: draw light direction line from book centre toward projected light source
344
+ // {
345
+ // const book_cx = bx + bw / 2
346
+ // const book_cy = by + bh / 2
347
+ // // Full 3D projection of light direction onto screen axes
348
+ // const lsx = Math.cos(lel_rad) * Math.sin(laz_rad - az_rad)
349
+ // const lsy = Math.cos(lel_rad) * Math.sin(el_rad) * Math.cos(laz_rad - az_rad)
350
+ // - Math.sin(lel_rad) * Math.cos(el_rad)
351
+ // const line_len = bh * 0.55
352
+ // const end_x = book_cx + lsx * line_len
353
+ // const end_y = book_cy + lsy * line_len
354
+ // ctx.save()
355
+ // ctx.strokeStyle = 'rgba(255,200,0,0.95)'
356
+ // ctx.lineWidth = 3
357
+ // ctx.setLineDash([8, 4])
358
+ // ctx.beginPath()
359
+ // ctx.moveTo(book_cx, book_cy)
360
+ // ctx.lineTo(end_x, end_y)
361
+ // ctx.stroke()
362
+ // ctx.setLineDash([])
363
+ // ctx.fillStyle = 'rgba(255,200,0,0.95)'
364
+ // ctx.beginPath()
365
+ // ctx.arc(end_x, end_y, 10, 0, Math.PI * 2)
366
+ // ctx.fill()
367
+ // ctx.restore()
368
+ // }
369
+ return createImageBitmap(comp);
370
+ }
371
+ /** Free all WebGL resources */
372
+ destroy() {
373
+ if (!this._gl_state)
374
+ return;
375
+ const { gl } = this._gl_state;
376
+ for (const t of this._textures)
377
+ gl.deleteTexture(t);
378
+ if (this._page_tex)
379
+ gl.deleteTexture(this._page_tex);
380
+ for (const f of this._faces) {
381
+ gl.deleteBuffer(f.vbo);
382
+ gl.deleteBuffer(f.ibo);
383
+ }
384
+ gl.deleteProgram(this._gl_state.prog);
385
+ this._gl_state = null;
386
+ this._faces = [];
387
+ this._textures = [];
388
+ }
389
+ }
390
+ /**
391
+ * One-shot render: load the SVGs, render at the given angle, return PNG bytes.
392
+ */
393
+ export async function generate(svgs, options = {}) {
394
+ const renderer = new Book3DRenderer(options.width ?? DEFAULT_WIDTH, options.height ?? DEFAULT_HEIGHT);
395
+ try {
396
+ await renderer.load(svgs, options.cover_type ?? 'paperback');
397
+ renderer.render(options.azimuth ?? DEFAULT_AZIMUTH, options.elevation ?? DEFAULT_ELEVATION, 1.0, options.roll ?? 0);
398
+ return await renderer.to_png();
399
+ }
400
+ finally {
401
+ renderer.destroy();
402
+ }
403
+ }
404
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/math.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ export type Mat4 = Float32Array;
2
+ export type Mat3 = Float32Array;
3
+ /** Create a 4×4 identity matrix */
4
+ export declare function mat4_identity(): Mat4;
5
+ /** Multiply two 4×4 matrices: result = a * b */
6
+ export declare function mat4_mul(a: Mat4, b: Mat4): Mat4;
7
+ /** Perspective projection matrix (column-major, right-hand) */
8
+ export declare function mat4_perspective(fov_deg: number, aspect: number, near: number, far: number): Mat4;
9
+ /** LookAt view matrix — up is always +Y */
10
+ export declare function mat4_look_at(eye: [number, number, number], target: [number, number, number]): Mat4;
11
+ /** Rotation matrix around the Z-axis (column-major); positive angle = counter-clockwise from +Z */
12
+ export declare function mat4_rotate_z(angle_deg: number): Mat4;
13
+ /** Extract the 3×3 normal matrix (upper-left of the view matrix) for rotation-only views */
14
+ export declare function normal_matrix(mv: Mat4): Mat3;
15
+ //# sourceMappingURL=math.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,IAAI,GAAG,YAAY,CAAA;AAC/B,MAAM,MAAM,IAAI,GAAG,YAAY,CAAA;AAE/B,mCAAmC;AACnC,wBAAgB,aAAa,IAAG,IAAI,CAInC;AAED,gDAAgD;AAChD,wBAAgB,QAAQ,CAAC,CAAC,EAAC,IAAI,EAAE,CAAC,EAAC,IAAI,GAAE,IAAI,CAW5C;AAED,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,OAAO,EAAC,MAAM,EAAE,MAAM,EAAC,MAAM,EAAE,IAAI,EAAC,MAAM,EAAE,GAAG,EAAC,MAAM,GAAE,IAAI,CAS5F;AAED,2CAA2C;AAC3C,wBAAgB,YAAY,CAAC,GAAG,EAAC,CAAC,MAAM,EAAC,MAAM,EAAC,MAAM,CAAC,EAAE,MAAM,EAAC,CAAC,MAAM,EAAC,MAAM,EAAC,MAAM,CAAC,GAAE,IAAI,CAsB3F;AAED,mGAAmG;AACnG,wBAAgB,aAAa,CAAC,SAAS,EAAC,MAAM,GAAE,IAAI,CASnD;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,EAAE,EAAC,IAAI,GAAE,IAAI,CAM1C"}
package/dist/math.js ADDED
@@ -0,0 +1,92 @@
1
+ // Column-major Mat4 / Mat3 math — no external dependencies
2
+ /** Create a 4×4 identity matrix */
3
+ export function mat4_identity() {
4
+ const m = new Float32Array(16);
5
+ m[0] = m[5] = m[10] = m[15] = 1;
6
+ return m;
7
+ }
8
+ /** Multiply two 4×4 matrices: result = a * b */
9
+ export function mat4_mul(a, b) {
10
+ const out = new Float32Array(16);
11
+ for (let row = 0; row < 4; row++) {
12
+ for (let col = 0; col < 4; col++) {
13
+ let sum = 0;
14
+ for (let k = 0; k < 4; k++)
15
+ sum += a[row + k * 4] * b[k + col * 4];
16
+ out[row + col * 4] = sum;
17
+ }
18
+ }
19
+ return out;
20
+ }
21
+ /** Perspective projection matrix (column-major, right-hand) */
22
+ export function mat4_perspective(fov_deg, aspect, near, far) {
23
+ const m = new Float32Array(16);
24
+ const f = 1.0 / Math.tan((fov_deg * Math.PI / 180) / 2);
25
+ m[0] = f / aspect;
26
+ m[5] = f;
27
+ m[10] = (far + near) / (near - far);
28
+ m[11] = -1;
29
+ m[14] = (2 * far * near) / (near - far);
30
+ return m;
31
+ }
32
+ /** LookAt view matrix — up is always +Y */
33
+ export function mat4_look_at(eye, target) {
34
+ const up = [0, 1, 0];
35
+ // Forward = normalize(eye - target)
36
+ let fx = eye[0] - target[0], fy = eye[1] - target[1], fz = eye[2] - target[2];
37
+ const fl = Math.sqrt(fx * fx + fy * fy + fz * fz);
38
+ fx /= fl;
39
+ fy /= fl;
40
+ fz /= fl;
41
+ // Right = normalize(up × forward)
42
+ let rx = up[1] * fz - up[2] * fy, ry = up[2] * fx - up[0] * fz, rz = up[0] * fy - up[1] * fx;
43
+ const rl = Math.sqrt(rx * rx + ry * ry + rz * rz);
44
+ rx /= rl;
45
+ ry /= rl;
46
+ rz /= rl;
47
+ // True up = forward × right
48
+ const ux = fy * rz - fz * ry, uy = fz * rx - fx * rz, uz = fx * ry - fy * rx;
49
+ const m = new Float32Array(16);
50
+ m[0] = rx;
51
+ m[4] = ry;
52
+ m[8] = rz;
53
+ m[12] = -(rx * eye[0] + ry * eye[1] + rz * eye[2]);
54
+ m[1] = ux;
55
+ m[5] = uy;
56
+ m[9] = uz;
57
+ m[13] = -(ux * eye[0] + uy * eye[1] + uz * eye[2]);
58
+ m[2] = fx;
59
+ m[6] = fy;
60
+ m[10] = fz;
61
+ m[14] = -(fx * eye[0] + fy * eye[1] + fz * eye[2]);
62
+ m[15] = 1;
63
+ return m;
64
+ }
65
+ /** Rotation matrix around the Z-axis (column-major); positive angle = counter-clockwise from +Z */
66
+ export function mat4_rotate_z(angle_deg) {
67
+ const m = mat4_identity();
68
+ const a = angle_deg * Math.PI / 180;
69
+ const c = Math.cos(a);
70
+ const s = Math.sin(a);
71
+ // Column 0: [c, s, 0, 0], Column 1: [-s, c, 0, 0]
72
+ m[0] = c;
73
+ m[1] = s;
74
+ m[4] = -s;
75
+ m[5] = c;
76
+ return m;
77
+ }
78
+ /** Extract the 3×3 normal matrix (upper-left of the view matrix) for rotation-only views */
79
+ export function normal_matrix(mv) {
80
+ const n = new Float32Array(9);
81
+ n[0] = mv[0];
82
+ n[1] = mv[1];
83
+ n[2] = mv[2];
84
+ n[3] = mv[4];
85
+ n[4] = mv[5];
86
+ n[5] = mv[6];
87
+ n[6] = mv[8];
88
+ n[7] = mv[9];
89
+ n[8] = mv[10];
90
+ return n;
91
+ }
92
+ //# sourceMappingURL=math.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math.js","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Background } from './types.js';
2
+ export declare const PHOTO_AZIMUTH = -15;
3
+ export declare const PHOTO_ELEVATION = 35;
4
+ export declare const PHOTO_BOOK_SCALE = 0.55;
5
+ export declare const BACKGROUNDS: Background[];
6
+ //# sourceMappingURL=photo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"photo.d.ts","sourceRoot":"","sources":["../src/photo.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,YAAY,CAAA;AAI1C,eAAO,MAAM,aAAa,MAAM,CAAA;AAChC,eAAO,MAAM,eAAe,KAAK,CAAA;AAGjC,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAIpC,eAAO,MAAM,WAAW,EAAE,UAAU,EA6DnC,CAAA"}
package/dist/photo.js ADDED
@@ -0,0 +1,72 @@
1
+ // Photo composite mode — background image metadata and default camera angles
2
+ // Default flat-on-table camera angles: moderate elevation to look down at the cover,
3
+ // slight azimuth to show depth without fully revealing the back
4
+ export const PHOTO_AZIMUTH = -15;
5
+ export const PHOTO_ELEVATION = 35;
6
+ // Book render width as a fraction of the background image width
7
+ export const PHOTO_BOOK_SCALE = 0.55;
8
+ // Available background photos (images live in src/backgrounds/).
9
+ // Each entry can override azimuth, elevation, and book_scale for a natural fit.
10
+ export const BACKGROUNDS = [
11
+ {
12
+ id: 'table_with_book',
13
+ azimuth: 0,
14
+ elevation: -30,
15
+ book_scale: 1,
16
+ offset_x: -0.1,
17
+ offset_y: 0.08,
18
+ roll: -30,
19
+ light_az: 10,
20
+ light_el: 60,
21
+ ambient: 1,
22
+ },
23
+ {
24
+ id: 'wood',
25
+ azimuth: 0,
26
+ elevation: 0,
27
+ book_scale: 0.75,
28
+ offset_x: 0,
29
+ offset_y: 0,
30
+ roll: 0,
31
+ light_az: 20,
32
+ light_el: 10,
33
+ ambient: 1,
34
+ },
35
+ {
36
+ id: 'coffee_table',
37
+ azimuth: 3,
38
+ elevation: -50,
39
+ book_scale: 0.5,
40
+ offset_x: 0,
41
+ offset_y: 0.06,
42
+ roll: -30,
43
+ light_az: 50,
44
+ light_el: 50,
45
+ ambient: 0.85,
46
+ },
47
+ {
48
+ id: 'table_with_laptop',
49
+ azimuth: 0,
50
+ elevation: -65,
51
+ book_scale: 0.65,
52
+ offset_x: 0.15,
53
+ offset_y: 0.13,
54
+ roll: 25,
55
+ light_az: -30,
56
+ light_el: 40,
57
+ ambient: 0.8,
58
+ },
59
+ {
60
+ id: 'table_side',
61
+ azimuth: -30,
62
+ elevation: 5,
63
+ book_scale: 0.55,
64
+ offset_x: -0.025,
65
+ offset_y: 0.06,
66
+ roll: 0,
67
+ light_az: 20,
68
+ light_el: 20,
69
+ ambient: 1,
70
+ },
71
+ ];
72
+ //# sourceMappingURL=photo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"photo.js","sourceRoot":"","sources":["../src/photo.ts"],"names":[],"mappings":"AACA,6EAA6E;AAI7E,qFAAqF;AACrF,gEAAgE;AAChE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAE,CAAA;AAChC,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAA;AAEjC,gEAAgE;AAChE,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAA;AAEpC,iEAAiE;AACjE,gFAAgF;AAChF,MAAM,CAAC,MAAM,WAAW,GAAiB;IACrC;QACI,EAAE,EAAE,iBAAiB;QACrB,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,CAAC,EAAE;QACd,UAAU,EAAE,CAAC;QACb,QAAQ,EAAE,CAAC,GAAG;QACd,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,CAAC,EAAE;QACT,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,CAAC;KACb;IACD;QACI,EAAE,EAAE,MAAM;QACV,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,CAAC;QACZ,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,CAAC;KACb;IACD;QACI,EAAE,EAAE,cAAc;QAClB,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,CAAC,EAAE;QACd,UAAU,EAAE,GAAG;QACf,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,CAAC,EAAE;QACT,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,IAAI;KAChB;IACD;QACI,EAAE,EAAE,mBAAmB;QACvB,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,CAAC,EAAE;QACd,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,EAAE;QACR,QAAQ,EAAE,CAAC,EAAE;QACb,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,GAAG;KACf;IACD;QACI,EAAE,EAAE,YAAY;QAChB,OAAO,EAAE,CAAC,EAAE;QACZ,SAAS,EAAE,CAAC;QACZ,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,CAAC,KAAK;QAChB,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,CAAC;QACP,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,CAAC;KACb;CACJ,CAAA"}