web-annotation-renderer 0.6.0 → 0.6.2

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 (87) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +52 -51
  4. package/dist/index.js.map +1 -1
  5. package/dist/index10.cjs +1 -1
  6. package/dist/index10.cjs.map +1 -1
  7. package/dist/index10.js +13 -210
  8. package/dist/index10.js.map +1 -1
  9. package/dist/index11.cjs +1 -1
  10. package/dist/index11.cjs.map +1 -1
  11. package/dist/index11.js +50 -13
  12. package/dist/index11.js.map +1 -1
  13. package/dist/index12.cjs +1 -1
  14. package/dist/index12.cjs.map +1 -1
  15. package/dist/index12.js +155 -48
  16. package/dist/index12.js.map +1 -1
  17. package/dist/index13.cjs +1 -1
  18. package/dist/index13.cjs.map +1 -1
  19. package/dist/index13.js +34 -169
  20. package/dist/index13.js.map +1 -1
  21. package/dist/index14.cjs +1 -1
  22. package/dist/index14.cjs.map +1 -1
  23. package/dist/index14.js +65 -28
  24. package/dist/index14.js.map +1 -1
  25. package/dist/index15.cjs +1 -1
  26. package/dist/index15.cjs.map +1 -1
  27. package/dist/index15.js +33 -71
  28. package/dist/index15.js.map +1 -1
  29. package/dist/index16.cjs +1 -1
  30. package/dist/index16.cjs.map +1 -1
  31. package/dist/index16.js +77 -22
  32. package/dist/index16.js.map +1 -1
  33. package/dist/index17.cjs +1 -1
  34. package/dist/index17.cjs.map +1 -1
  35. package/dist/index17.js +31 -52
  36. package/dist/index17.js.map +1 -1
  37. package/dist/index18.cjs +1 -1
  38. package/dist/index18.cjs.map +1 -1
  39. package/dist/index18.js +22 -23
  40. package/dist/index18.js.map +1 -1
  41. package/dist/index2.cjs +1 -1
  42. package/dist/index2.cjs.map +1 -1
  43. package/dist/index2.js +5 -6
  44. package/dist/index2.js.map +1 -1
  45. package/dist/index21.cjs +1 -1
  46. package/dist/index21.cjs.map +1 -1
  47. package/dist/index21.js +48 -27
  48. package/dist/index21.js.map +1 -1
  49. package/dist/index24.cjs +1 -1
  50. package/dist/index24.js +1 -1
  51. package/dist/index25.cjs +1 -1
  52. package/dist/index25.js +1 -1
  53. package/dist/index29.cjs +1 -1
  54. package/dist/index29.cjs.map +1 -1
  55. package/dist/index29.js +70 -4
  56. package/dist/index29.js.map +1 -1
  57. package/dist/index3.cjs +1 -1
  58. package/dist/index3.js +1 -1
  59. package/dist/index30.cjs +2 -0
  60. package/dist/index30.cjs.map +1 -0
  61. package/dist/index30.js +8 -0
  62. package/dist/index30.js.map +1 -0
  63. package/dist/index31.cjs +2 -0
  64. package/dist/index31.cjs.map +1 -0
  65. package/dist/index31.js +995 -0
  66. package/dist/index31.js.map +1 -0
  67. package/dist/index5.cjs +1 -1
  68. package/dist/index5.cjs.map +1 -1
  69. package/dist/index5.js +189 -80
  70. package/dist/index5.js.map +1 -1
  71. package/dist/index6.cjs +1 -1
  72. package/dist/index6.cjs.map +1 -1
  73. package/dist/index6.js +18 -60
  74. package/dist/index6.js.map +1 -1
  75. package/dist/index7.cjs +1 -1
  76. package/dist/index7.cjs.map +1 -1
  77. package/dist/index7.js +17 -17
  78. package/dist/index7.js.map +1 -1
  79. package/dist/index8.cjs +1 -1
  80. package/dist/index8.cjs.map +1 -1
  81. package/dist/index8.js +125 -16
  82. package/dist/index8.js.map +1 -1
  83. package/dist/index9.cjs +1 -1
  84. package/dist/index9.cjs.map +1 -1
  85. package/dist/index9.js +201 -118
  86. package/dist/index9.js.map +1 -1
  87. package/package.json +4 -2
package/dist/index5.js CHANGED
@@ -1,66 +1,137 @@
1
- import { deepMerge as v } from "./index6.js";
2
- import { circleToStrokes as b } from "./index18.js";
3
- import { arrowToStrokes as _ } from "./index17.js";
4
- import { underlineToStrokes as C } from "./index16.js";
5
- import { textToStrokes as P } from "./index15.js";
6
- import { highlightToStrokes as T } from "./index14.js";
7
- const R = {
8
- pen: {
9
- jitter: { amplitude: 1, frequency: 0.08 },
10
- pressure: { taperIn: 0.15, taperOut: 0.2 },
11
- wobble: { amplitude: 1.5, frequency: 0.05 }
12
- },
1
+ import { inkToStrokes as T } from "./index18.js";
2
+ import { circleToStrokes as _ } from "./index17.js";
3
+ import { arrowToStrokes as P } from "./index16.js";
4
+ import { underlineToStrokes as R } from "./index15.js";
5
+ import { textToStrokes as E } from "./index14.js";
6
+ import { highlightToStrokes as O } from "./index13.js";
7
+ const v = ["highlight", "text", "underline", "arrow", "circle", "ink"], S = 500, A = {
13
8
  highlight: {
14
9
  color: "rgba(255, 255, 0, 0.3)",
15
10
  width: 24,
16
- lineCap: "butt",
17
- jitter: { amplitude: 0 }
11
+ lineCap: "square",
12
+ roughness: 1.2,
13
+ bowing: 1.2,
14
+ curveFitting: 0.95,
15
+ curveStepCount: 9,
16
+ maxRandomnessOffset: 2,
17
+ disableMultiStroke: !1
18
18
  },
19
19
  text: {
20
20
  color: "rgba(220, 20, 60, 1.0)",
21
21
  width: 2,
22
22
  fontSize: 16,
23
23
  lineCap: "round",
24
- jitter: { amplitude: 0 },
25
- pressure: { taperIn: 0, taperOut: 0 }
24
+ roughness: 0.8
26
25
  },
27
26
  underline: {
28
27
  color: "rgba(0, 0, 255, 0.8)",
29
28
  width: 2,
30
- lineCap: "round"
29
+ lineCap: "round",
30
+ roughness: 2,
31
+ bowing: 2.4,
32
+ curveFitting: 0.85,
33
+ curveStepCount: 9,
34
+ maxRandomnessOffset: 3,
35
+ disableMultiStroke: !1
31
36
  },
32
37
  arrow: {
33
38
  color: "rgba(255, 0, 0, 0.8)",
34
39
  width: 2,
35
- lineCap: "round"
40
+ lineCap: "round",
41
+ roughness: 1.8,
42
+ bowing: 1.8,
43
+ curveFitting: 0.9,
44
+ curveStepCount: 9,
45
+ maxRandomnessOffset: 2,
46
+ disableMultiStroke: !1
36
47
  },
37
48
  circle: {
38
49
  color: "rgba(255, 165, 0, 0.8)",
39
50
  width: 3,
51
+ lineCap: "round",
52
+ roughness: 1.7,
53
+ bowing: 2,
54
+ curveFitting: 0.8,
55
+ curveStepCount: 12,
56
+ maxRandomnessOffset: 3,
57
+ disableMultiStroke: !1
58
+ },
59
+ ink: {
60
+ color: "#DC143C",
61
+ width: 2,
40
62
  lineCap: "round"
41
63
  }
42
64
  };
43
- class O {
65
+ class $ {
44
66
  /**
45
67
  * Create StrokeRenderer instance
46
68
  *
47
69
  * @param {HTMLCanvasElement} canvas - Canvas element for rendering
48
- * @param {Object} [config={}] - Configuration overrides
49
- * @param {Object} [config.pen] - Global pen settings
70
+ * @param {Object} [config={}] - Configuration overrides per annotation type
50
71
  * @param {Object} [config.highlight] - Highlight type settings
51
72
  * @param {Object} [config.text] - Text type settings
73
+ * @param {Object} [config.underline] - Underline type settings
74
+ * @param {Object} [config.arrow] - Arrow type settings
75
+ * @param {Object} [config.circle] - Circle type settings
52
76
  */
53
77
  constructor(t, r = {}) {
54
78
  if (!t || !(t instanceof HTMLCanvasElement))
55
- throw new Error("StrokeRenderer: canvas must be a valid HTMLCanvasElement");
56
- this.canvas = t, this.ctx = t.getContext("2d"), this.config = v(R, r), this.strokes = [], this.viewport = { width: 0, height: 0 }, this.converters = {
57
- highlight: T,
58
- text: P,
59
- underline: C,
60
- arrow: _,
61
- circle: b
79
+ throw new Error(
80
+ "StrokeRenderer: canvas must be a valid HTMLCanvasElement"
81
+ );
82
+ this.canvas = t, this.ctx = t.getContext("2d"), this.strokes = [], this.viewport = { width: 0, height: 0 }, this.config = {};
83
+ for (const i of v)
84
+ this.config[i] = { ...A[i] };
85
+ Object.keys(r).length > 0 && this.setConfig(r), this.converters = {
86
+ highlight: O,
87
+ text: E,
88
+ underline: R,
89
+ arrow: P,
90
+ circle: _,
91
+ ink: T
62
92
  };
63
93
  }
94
+ /**
95
+ * Set configuration options
96
+ *
97
+ * Supports global options (applied to all types) and type-specific options.
98
+ *
99
+ * @param {Object} config - Configuration object
100
+ * @param {number} [config.roughness] - Global roughness for all types
101
+ * @param {string} [config.color] - Global color for all types
102
+ * @param {number} [config.width] - Global width for all types
103
+ * @param {Object} [config.highlight] - Highlight-specific options
104
+ * @param {Object} [config.text] - Text-specific options
105
+ * @param {Object} [config.underline] - Underline-specific options
106
+ * @param {Object} [config.arrow] - Arrow-specific options
107
+ * @param {Object} [config.circle] - Circle-specific options
108
+ * @returns {StrokeRenderer} Returns this for chaining
109
+ *
110
+ * @example
111
+ * // Global options
112
+ * renderer.setConfig({ roughness: 2, color: "blue" });
113
+ *
114
+ * // Type-specific options
115
+ * renderer.setConfig({ highlight: { color: "yellow", width: 30 } });
116
+ *
117
+ * // Combined
118
+ * renderer.setConfig({
119
+ * roughness: 2,
120
+ * highlight: { color: "yellow" }
121
+ * });
122
+ */
123
+ setConfig(t) {
124
+ const r = {}, i = {};
125
+ for (const [e, s] of Object.entries(t))
126
+ v.includes(e) ? i[e] = s : r[e] = s;
127
+ for (const e of v)
128
+ this.config[e] = {
129
+ ...this.config[e],
130
+ ...r,
131
+ ...i[e] || {}
132
+ };
133
+ return this;
134
+ }
64
135
  /**
65
136
  * Register a custom converter for a new annotation type
66
137
  *
@@ -69,7 +140,9 @@ class O {
69
140
  */
70
141
  registerConverter(t, r) {
71
142
  if (typeof r != "function")
72
- throw new Error("StrokeRenderer.registerConverter: converter must be a function");
143
+ throw new Error(
144
+ "StrokeRenderer.registerConverter: converter must be a function"
145
+ );
73
146
  this.converters[t] = r;
74
147
  }
75
148
  /**
@@ -95,16 +168,20 @@ class O {
95
168
  */
96
169
  setAnnotations(t, r = null) {
97
170
  if (!Array.isArray(t)) {
98
- console.warn("StrokeRenderer.setAnnotations: annotations must be an array"), this.strokes = [];
171
+ console.warn(
172
+ "StrokeRenderer.setAnnotations: annotations must be an array"
173
+ ), this.strokes = [];
99
174
  return;
100
175
  }
101
176
  const i = r !== null ? t.filter((e) => e.page === r) : t;
102
177
  this.strokes = i.flatMap((e) => {
103
- const n = this.converters[e.type];
104
- if (!n)
105
- return console.warn(`StrokeRenderer: Unknown annotation type "${e.type}"`), [];
106
- const s = this._resolveStyle(e);
107
- return n(e, s);
178
+ const s = this.converters[e.type];
179
+ if (!s)
180
+ return console.warn(
181
+ `StrokeRenderer: Unknown annotation type "${e.type}"`
182
+ ), [];
183
+ const n = this._resolveStyle(e);
184
+ return s(e, n);
108
185
  });
109
186
  }
110
187
  /**
@@ -131,11 +208,11 @@ class O {
131
208
  render(t) {
132
209
  const { ctx: r, viewport: i, strokes: e } = this;
133
210
  r.clearRect(0, 0, i.width, i.height);
134
- for (const n of e) {
135
- if (t < n.start)
211
+ for (const s of e) {
212
+ if (t < s.start)
136
213
  continue;
137
- const s = n.end - n.start, h = t - n.start, o = s > 0 ? Math.min(1, h / s) : 1;
138
- this._drawStroke(n, o);
214
+ const n = s.end - s.start, h = t - s.start, o = n > 0 ? Math.min(1, h / n) : 1;
215
+ this._drawStroke(s, o);
139
216
  }
140
217
  }
141
218
  /**
@@ -153,16 +230,16 @@ class O {
153
230
  /**
154
231
  * Resolve style for an annotation
155
232
  *
156
- * Merges: pen config type config → annotation.style
233
+ * Returns the config for the annotation type.
234
+ * Style is managed via setConfig(), not per-annotation.
157
235
  *
158
236
  * @private
159
237
  * @param {Object} annotation - Annotation object
160
238
  * @returns {Object} Resolved style object
161
239
  */
162
240
  _resolveStyle(t) {
163
- const { type: r, style: i } = t;
164
- let e = { ...this.config.pen };
165
- return this.config[r] && (e = v(e, this.config[r])), i && (e = v(e, i)), e;
241
+ const { type: r } = t;
242
+ return this.config[r] || {};
166
243
  }
167
244
  /**
168
245
  * Linearly interpolate between two points
@@ -174,10 +251,7 @@ class O {
174
251
  * @returns {Array} Interpolated point [x, y]
175
252
  */
176
253
  _interpolatePoint(t, r, i) {
177
- return [
178
- t[0] + (r[0] - t[0]) * i,
179
- t[1] + (r[1] - t[1]) * i
180
- ];
254
+ return [t[0] + (r[0] - t[0]) * i, t[1] + (r[1] - t[1]) * i];
181
255
  }
182
256
  /**
183
257
  * Get pressure values for visible points including interpolated final
@@ -191,8 +265,8 @@ class O {
191
265
  _interpolatePressures(t, r, i) {
192
266
  const e = t.slice(0, r + 1);
193
267
  if (r < t.length - 1 && i > 0) {
194
- const n = t[r], s = t[r + 1];
195
- e.push(n + (s - n) * i);
268
+ const s = t[r], n = t[r + 1];
269
+ e.push(s + (n - s) * i);
196
270
  }
197
271
  return e;
198
272
  }
@@ -206,27 +280,62 @@ class O {
206
280
  * @param {number} progress - Progress from 0 to 1
207
281
  */
208
282
  _drawStroke(t, r) {
209
- const { ctx: i } = this, { points: e, color: n, width: s, lineCap: h, pressures: o, uniformScale: g, baseX: l, baseY: a } = t;
283
+ const { ctx: i } = this, {
284
+ points: e,
285
+ color: s,
286
+ width: n,
287
+ lineCap: h,
288
+ pressures: o,
289
+ uniformScale: f,
290
+ baseX: l,
291
+ baseY: a
292
+ } = t;
210
293
  if (!e || e.length < 2) return;
211
- if (i.strokeStyle = n || "rgba(0, 0, 0, 0.5)", i.lineCap = h || "round", i.lineJoin = "round", r >= 1) {
212
- o && o.length >= e.length ? this._drawVariableWidthStroke(e, s, o, g, l, a) : this._drawConstantWidthStroke(e, s, g, l, a);
294
+ if (i.strokeStyle = s || "rgba(0, 0, 0, 0.5)", i.lineCap = h || "round", i.lineJoin = "round", r >= 1) {
295
+ o && o.length >= e.length ? this._drawVariableWidthStroke(
296
+ e,
297
+ n,
298
+ o,
299
+ f,
300
+ l,
301
+ a
302
+ ) : this._drawConstantWidthStroke(
303
+ e,
304
+ n,
305
+ f,
306
+ l,
307
+ a
308
+ );
213
309
  return;
214
310
  }
215
- const c = e.length - 1, d = r * c, u = Math.floor(d), f = d - u, p = e.slice(0, u + 1);
216
- if (u < c && f > 0) {
217
- const w = e[u], k = e[u + 1];
218
- p.push(this._interpolatePoint(w, k, f));
311
+ const u = e.length - 1, g = r * u, c = Math.floor(g), w = g - c, d = e.slice(0, c + 1);
312
+ if (c < u && w > 0) {
313
+ const p = e[c], k = e[c + 1];
314
+ d.push(this._interpolatePoint(p, k, w));
219
315
  }
220
- if (!(p.length < 2))
316
+ if (!(d.length < 2))
221
317
  if (o && o.length >= e.length) {
222
- const w = this._interpolatePressures(
318
+ const p = this._interpolatePressures(
223
319
  o,
224
- u,
225
- f
320
+ c,
321
+ w
322
+ );
323
+ this._drawVariableWidthStroke(
324
+ d,
325
+ n,
326
+ p,
327
+ f,
328
+ l,
329
+ a
226
330
  );
227
- this._drawVariableWidthStroke(p, s, w, g, l, a);
228
331
  } else
229
- this._drawConstantWidthStroke(p, s, g, l, a);
332
+ this._drawConstantWidthStroke(
333
+ d,
334
+ n,
335
+ f,
336
+ l,
337
+ a
338
+ );
230
339
  }
231
340
  /**
232
341
  * Draw stroke with constant width
@@ -238,16 +347,16 @@ class O {
238
347
  * @param {number} [baseX] - Base X position in width-normalized coords (for uniformScale with offset points)
239
348
  * @param {number} [baseY] - Base Y position in height-normalized coords (for uniformScale with offset points)
240
349
  */
241
- _drawConstantWidthStroke(t, r, i = !1, e = null, n = null) {
242
- const { ctx: s, viewport: h } = this;
243
- s.lineWidth = r || 2, s.beginPath();
244
- const o = i && e !== null && n !== null;
245
- for (let g = 0; g < t.length; g++) {
246
- const [l, a] = t[g];
247
- let c, d;
248
- o ? (c = e * h.width + l * h.height, d = n * h.height + a * h.height) : i ? (c = l * h.height, d = a * h.height) : (c = l * h.width, d = a * h.height), g === 0 ? s.moveTo(c, d) : s.lineTo(c, d);
350
+ _drawConstantWidthStroke(t, r, i = !1, e = null, s = null) {
351
+ const { ctx: n, viewport: h } = this, o = h.height / S;
352
+ n.lineWidth = (r || 2) * o, n.beginPath();
353
+ const f = i && e !== null && s !== null;
354
+ for (let l = 0; l < t.length; l++) {
355
+ const [a, u] = t[l];
356
+ let g, c;
357
+ f ? (g = e * h.width + a * h.height, c = s * h.height + u * h.height) : i ? (g = a * h.height, c = u * h.height) : (g = a * h.width, c = u * h.height), l === 0 ? n.moveTo(g, c) : n.lineTo(g, c);
249
358
  }
250
- s.stroke();
359
+ n.stroke();
251
360
  }
252
361
  /**
253
362
  * Draw stroke with variable width based on pressure
@@ -260,19 +369,19 @@ class O {
260
369
  * @param {number} [baseX] - Base X position in width-normalized coords (for uniformScale with offset points)
261
370
  * @param {number} [baseY] - Base Y position in height-normalized coords (for uniformScale with offset points)
262
371
  */
263
- _drawVariableWidthStroke(t, r, i, e = !1, n = null, s = null) {
264
- const { ctx: h, viewport: o } = this, g = e && n !== null && s !== null;
372
+ _drawVariableWidthStroke(t, r, i, e = !1, s = null, n = null) {
373
+ const { ctx: h, viewport: o } = this, f = e && s !== null && n !== null;
265
374
  for (let l = 1; l < t.length; l++) {
266
- const [a, c] = t[l - 1], [d, u] = t[l];
267
- let f, p, w, k;
268
- g ? (f = n * o.width + a * o.height, p = s * o.height + c * o.height, w = n * o.width + d * o.height, k = s * o.height + u * o.height) : e ? (f = a * o.height, p = c * o.height, w = d * o.height, k = u * o.height) : (f = a * o.width, p = c * o.height, w = d * o.width, k = u * o.height);
269
- const y = i[l - 1] || 1, S = i[l] || 1, m = (y + S) / 2, x = Math.max(0.5, r * m);
270
- h.lineWidth = x, h.beginPath(), h.moveTo(f, p), h.lineTo(w, k), h.stroke();
375
+ const [a, u] = t[l - 1], [g, c] = t[l];
376
+ let w, d, p, k;
377
+ f ? (w = s * o.width + a * o.height, d = n * o.height + u * o.height, p = s * o.width + g * o.height, k = n * o.height + c * o.height) : e ? (w = a * o.height, d = u * o.height, p = g * o.height, k = c * o.height) : (w = a * o.width, d = u * o.height, p = g * o.width, k = c * o.height);
378
+ const m = i[l - 1] || 1, y = i[l] || 1, b = (m + y) / 2, x = o.height / S, C = Math.max(0.5, r * b) * x;
379
+ h.lineWidth = C, h.beginPath(), h.moveTo(w, d), h.lineTo(p, k), h.stroke();
271
380
  }
272
381
  }
273
382
  }
274
383
  export {
275
- O as StrokeRenderer,
276
- O as default
384
+ $ as StrokeRenderer,
385
+ $ as default
277
386
  };
278
387
  //# sourceMappingURL=index5.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index5.js","sources":["../src/renderer/StrokeRenderer.js"],"sourcesContent":["/**\n * StrokeRenderer - Unified canvas-based annotation renderer\n *\n * Single entry point for rendering annotations as strokes on canvas.\n * Handles conversion from annotations to strokes and progressive rendering.\n *\n * @module renderer/StrokeRenderer\n */\n\nimport {\n highlightToStrokes,\n textToStrokes,\n underlineToStrokes,\n arrowToStrokes,\n circleToStrokes\n} from '../converters/index.js';\nimport { deepMerge } from '../pen/effects.js';\n\n/**\n * Default configuration\n */\nconst DEFAULT_CONFIG = {\n pen: {\n jitter: { amplitude: 1.0, frequency: 0.08 },\n pressure: { taperIn: 0.15, taperOut: 0.20 },\n wobble: { amplitude: 1.5, frequency: 0.05 }\n },\n highlight: {\n color: 'rgba(255, 255, 0, 0.3)',\n width: 24,\n lineCap: 'butt',\n jitter: { amplitude: 0 }\n },\n text: {\n color: 'rgba(220, 20, 60, 1.0)',\n width: 2,\n fontSize: 16,\n lineCap: 'round',\n jitter: { amplitude: 0 },\n pressure: { taperIn: 0, taperOut: 0 }\n },\n underline: {\n color: 'rgba(0, 0, 255, 0.8)',\n width: 2,\n lineCap: 'round'\n },\n arrow: {\n color: 'rgba(255, 0, 0, 0.8)',\n width: 2,\n lineCap: 'round'\n },\n circle: {\n color: 'rgba(255, 165, 0, 0.8)',\n width: 3,\n lineCap: 'round'\n }\n};\n\n/**\n * StrokeRenderer class\n *\n * Converts annotations to strokes and renders them progressively on canvas.\n *\n * @class\n * @example\n * const renderer = new StrokeRenderer(canvas, {\n * highlight: { color: 'rgba(0, 255, 200, 0.4)' }\n * });\n * renderer.setViewport(800, 600);\n * renderer.setAnnotations(annotations);\n * renderer.render(1.5); // Render at t=1.5 seconds\n */\nclass StrokeRenderer {\n /**\n * Create StrokeRenderer instance\n *\n * @param {HTMLCanvasElement} canvas - Canvas element for rendering\n * @param {Object} [config={}] - Configuration overrides\n * @param {Object} [config.pen] - Global pen settings\n * @param {Object} [config.highlight] - Highlight type settings\n * @param {Object} [config.text] - Text type settings\n */\n constructor(canvas, config = {}) {\n if (!canvas || !(canvas instanceof HTMLCanvasElement)) {\n throw new Error('StrokeRenderer: canvas must be a valid HTMLCanvasElement');\n }\n\n this.canvas = canvas;\n this.ctx = canvas.getContext('2d');\n this.config = deepMerge(DEFAULT_CONFIG, config);\n this.strokes = [];\n this.viewport = { width: 0, height: 0 };\n\n // Register built-in converters\n this.converters = {\n highlight: highlightToStrokes,\n text: textToStrokes,\n underline: underlineToStrokes,\n arrow: arrowToStrokes,\n circle: circleToStrokes\n };\n }\n\n /**\n * Register a custom converter for a new annotation type\n *\n * @param {string} type - Annotation type name\n * @param {Function} converter - Converter function (annotation, style) => strokes[]\n */\n registerConverter(type, converter) {\n if (typeof converter !== 'function') {\n throw new Error('StrokeRenderer.registerConverter: converter must be a function');\n }\n this.converters[type] = converter;\n }\n\n /**\n * Set viewport dimensions and configure canvas\n *\n * Handles high-DPI displays by scaling the canvas buffer.\n *\n * @param {number} width - Viewport width in CSS pixels\n * @param {number} height - Viewport height in CSS pixels\n */\n setViewport(width, height) {\n this.viewport = { width, height };\n\n const dpr = window.devicePixelRatio || 1;\n\n // Set canvas buffer size (high-res)\n this.canvas.width = width * dpr;\n this.canvas.height = height * dpr;\n\n // Set canvas display size (CSS)\n this.canvas.style.width = `${width}px`;\n this.canvas.style.height = `${height}px`;\n\n // Scale context for high-DPI\n this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n }\n\n /**\n * Set annotations and convert them to strokes\n *\n * Clears existing strokes and converts all annotations.\n *\n * @param {Array} annotations - Array of annotation objects\n * @param {number} [page] - Optional page filter (only convert annotations for this page)\n */\n setAnnotations(annotations, page = null) {\n if (!Array.isArray(annotations)) {\n console.warn('StrokeRenderer.setAnnotations: annotations must be an array');\n this.strokes = [];\n return;\n }\n\n // Filter by page if specified\n const filtered = page !== null\n ? annotations.filter(a => a.page === page)\n : annotations;\n\n // Convert each annotation to strokes\n this.strokes = filtered.flatMap(annotation => {\n const converter = this.converters[annotation.type];\n\n if (!converter) {\n console.warn(`StrokeRenderer: Unknown annotation type \"${annotation.type}\"`);\n return [];\n }\n\n // Resolve style: pen → type → annotation.style\n const style = this._resolveStyle(annotation);\n\n return converter(annotation, style);\n });\n }\n\n /**\n * Set pre-converted strokes directly\n *\n * Bypasses conversion, useful when strokes come from external source.\n *\n * @param {Array} strokes - Array of stroke command objects\n */\n setStrokes(strokes) {\n if (!Array.isArray(strokes)) {\n console.warn('StrokeRenderer.setStrokes: strokes must be an array');\n this.strokes = [];\n return;\n }\n this.strokes = strokes;\n }\n\n /**\n * Render strokes at the given time\n *\n * Clears canvas and draws all visible strokes with appropriate progress.\n *\n * @param {number} time - Current time in seconds\n */\n render(time) {\n const { ctx, viewport, strokes } = this;\n\n // Clear canvas\n ctx.clearRect(0, 0, viewport.width, viewport.height);\n\n // Draw each stroke\n for (const stroke of strokes) {\n // Skip if not started yet\n if (time < stroke.start) {\n continue;\n }\n\n // Calculate progress\n const duration = stroke.end - stroke.start;\n const elapsed = time - stroke.start;\n const progress = duration > 0 ? Math.min(1, elapsed / duration) : 1;\n\n this._drawStroke(stroke, progress);\n }\n }\n\n /**\n * Clear the canvas\n */\n clear() {\n this.ctx.clearRect(0, 0, this.viewport.width, this.viewport.height);\n }\n\n /**\n * Destroy the renderer and release resources\n */\n destroy() {\n this.strokes = [];\n this.ctx = null;\n this.canvas = null;\n this.config = null;\n this.converters = null;\n }\n\n /**\n * Resolve style for an annotation\n *\n * Merges: pen config → type config → annotation.style\n *\n * @private\n * @param {Object} annotation - Annotation object\n * @returns {Object} Resolved style object\n */\n _resolveStyle(annotation) {\n const { type, style: annotationStyle } = annotation;\n\n // Start with pen config (global)\n let resolved = { ...this.config.pen };\n\n // Merge type config\n if (this.config[type]) {\n resolved = deepMerge(resolved, this.config[type]);\n }\n\n // Merge annotation-level style\n if (annotationStyle) {\n resolved = deepMerge(resolved, annotationStyle);\n }\n\n return resolved;\n }\n\n /**\n * Linearly interpolate between two points\n *\n * @private\n * @param {Array} p1 - Start point [x, y]\n * @param {Array} p2 - End point [x, y]\n * @param {number} t - Interpolation factor (0-1)\n * @returns {Array} Interpolated point [x, y]\n */\n _interpolatePoint(p1, p2, t) {\n return [\n p1[0] + (p2[0] - p1[0]) * t,\n p1[1] + (p2[1] - p1[1]) * t\n ];\n }\n\n /**\n * Get pressure values for visible points including interpolated final\n *\n * @private\n * @param {Array} pressures - Full pressure array\n * @param {number} lastCompleteIndex - Index of last complete point\n * @param {number} segmentProgress - Progress within current segment (0-1)\n * @returns {Array} Pressure values for visible points\n */\n _interpolatePressures(pressures, lastCompleteIndex, segmentProgress) {\n const visiblePressures = pressures.slice(0, lastCompleteIndex + 1);\n\n if (lastCompleteIndex < pressures.length - 1 && segmentProgress > 0) {\n const p1 = pressures[lastCompleteIndex];\n const p2 = pressures[lastCompleteIndex + 1];\n visiblePressures.push(p1 + (p2 - p1) * segmentProgress);\n }\n\n return visiblePressures;\n }\n\n /**\n * Draw a single stroke with progress\n *\n * Uses endpoint interpolation for smooth progressive rendering.\n *\n * @private\n * @param {Object} stroke - Stroke command object\n * @param {number} progress - Progress from 0 to 1\n */\n _drawStroke(stroke, progress) {\n const { ctx } = this;\n const { points, color, width, lineCap, pressures, uniformScale, baseX, baseY } = stroke;\n\n if (!points || points.length < 2) return;\n\n // Configure stroke style\n ctx.strokeStyle = color || 'rgba(0, 0, 0, 0.5)';\n ctx.lineCap = lineCap || 'round';\n ctx.lineJoin = 'round';\n\n // Fast path: full stroke, no interpolation needed\n if (progress >= 1) {\n if (pressures && pressures.length >= points.length) {\n this._drawVariableWidthStroke(points, width, pressures, uniformScale, baseX, baseY);\n } else {\n this._drawConstantWidthStroke(points, width, uniformScale, baseX, baseY);\n }\n return;\n }\n\n // Calculate segment position for interpolation\n const totalSegments = points.length - 1;\n const progressAlongPath = progress * totalSegments;\n const lastCompleteIndex = Math.floor(progressAlongPath);\n const segmentProgress = progressAlongPath - lastCompleteIndex;\n\n // Build visible points array with complete points\n const visiblePoints = points.slice(0, lastCompleteIndex + 1);\n\n // Interpolate final point if mid-segment\n if (lastCompleteIndex < totalSegments && segmentProgress > 0) {\n const p1 = points[lastCompleteIndex];\n const p2 = points[lastCompleteIndex + 1];\n visiblePoints.push(this._interpolatePoint(p1, p2, segmentProgress));\n }\n\n // Need at least 2 points to draw a line\n if (visiblePoints.length < 2) return;\n\n // Draw with variable width if pressures provided\n if (pressures && pressures.length >= points.length) {\n const visiblePressures = this._interpolatePressures(\n pressures, lastCompleteIndex, segmentProgress\n );\n this._drawVariableWidthStroke(visiblePoints, width, visiblePressures, uniformScale, baseX, baseY);\n } else {\n this._drawConstantWidthStroke(visiblePoints, width, uniformScale, baseX, baseY);\n }\n }\n\n /**\n * Draw stroke with constant width\n *\n * @private\n * @param {Array} points - Array of [x, y] coordinates (normalized or offset)\n * @param {number} width - Stroke width in pixels\n * @param {boolean} [uniformScale=false] - Use uniform scaling to preserve aspect ratio\n * @param {number} [baseX] - Base X position in width-normalized coords (for uniformScale with offset points)\n * @param {number} [baseY] - Base Y position in height-normalized coords (for uniformScale with offset points)\n */\n _drawConstantWidthStroke(points, width, uniformScale = false, baseX = null, baseY = null) {\n const { ctx, viewport } = this;\n\n ctx.lineWidth = width || 2;\n ctx.beginPath();\n\n // When uniformScale is true and baseX/baseY provided, points are offsets from base position\n // Base position is in mixed coords (X: width-normalized, Y: height-normalized)\n // Offsets are in uniform space (both scaled by viewport.height)\n const hasBasePosition = uniformScale && baseX !== null && baseY !== null;\n\n for (let i = 0; i < points.length; i++) {\n const [coordX, coordY] = points[i];\n let px, py;\n\n if (hasBasePosition) {\n // Base position in pixels + uniform-scaled offset\n px = baseX * viewport.width + coordX * viewport.height;\n py = baseY * viewport.height + coordY * viewport.height;\n } else if (uniformScale) {\n // Legacy: full coordinates in uniform space\n px = coordX * viewport.height;\n py = coordY * viewport.height;\n } else {\n // Standard: X scaled by width, Y by height\n px = coordX * viewport.width;\n py = coordY * viewport.height;\n }\n\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n ctx.stroke();\n }\n\n /**\n * Draw stroke with variable width based on pressure\n *\n * @private\n * @param {Array} points - Array of [x, y] coordinates (normalized or offset)\n * @param {number} baseWidth - Base stroke width in pixels\n * @param {Array} pressures - Pressure values (0-1) per point\n * @param {boolean} [uniformScale=false] - Use uniform scaling to preserve aspect ratio\n * @param {number} [baseX] - Base X position in width-normalized coords (for uniformScale with offset points)\n * @param {number} [baseY] - Base Y position in height-normalized coords (for uniformScale with offset points)\n */\n _drawVariableWidthStroke(points, baseWidth, pressures, uniformScale = false, baseX = null, baseY = null) {\n const { ctx, viewport } = this;\n\n // When uniformScale is true and baseX/baseY provided, points are offsets from base position\n const hasBasePosition = uniformScale && baseX !== null && baseY !== null;\n\n for (let i = 1; i < points.length; i++) {\n const [c1x, c1y] = points[i - 1];\n const [c2x, c2y] = points[i];\n\n let px1, py1, px2, py2;\n\n if (hasBasePosition) {\n // Base position in pixels + uniform-scaled offset\n px1 = baseX * viewport.width + c1x * viewport.height;\n py1 = baseY * viewport.height + c1y * viewport.height;\n px2 = baseX * viewport.width + c2x * viewport.height;\n py2 = baseY * viewport.height + c2y * viewport.height;\n } else if (uniformScale) {\n // Legacy: full coordinates in uniform space\n px1 = c1x * viewport.height;\n py1 = c1y * viewport.height;\n px2 = c2x * viewport.height;\n py2 = c2y * viewport.height;\n } else {\n // Standard: X scaled by width, Y by height\n px1 = c1x * viewport.width;\n py1 = c1y * viewport.height;\n px2 = c2x * viewport.width;\n py2 = c2y * viewport.height;\n }\n\n // Average pressure between two points\n const p1 = pressures[i - 1] || 1;\n const p2 = pressures[i] || 1;\n const avgPressure = (p1 + p2) / 2;\n\n // Apply pressure to width (min 0.5)\n const width = Math.max(0.5, baseWidth * avgPressure);\n\n ctx.lineWidth = width;\n ctx.beginPath();\n ctx.moveTo(px1, py1);\n ctx.lineTo(px2, py2);\n ctx.stroke();\n }\n }\n}\n\nexport { StrokeRenderer };\nexport default StrokeRenderer;\n"],"names":["DEFAULT_CONFIG","StrokeRenderer","canvas","config","deepMerge","highlightToStrokes","textToStrokes","underlineToStrokes","arrowToStrokes","circleToStrokes","type","converter","width","height","dpr","annotations","page","filtered","a","annotation","style","strokes","time","ctx","viewport","stroke","duration","elapsed","progress","annotationStyle","resolved","p1","p2","t","pressures","lastCompleteIndex","segmentProgress","visiblePressures","points","color","lineCap","uniformScale","baseX","baseY","totalSegments","progressAlongPath","visiblePoints","hasBasePosition","i","coordX","coordY","px","py","baseWidth","c1x","c1y","c2x","c2y","px1","py1","px2","py2","avgPressure"],"mappings":"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"}
1
+ {"version":3,"file":"index5.js","sources":["../src/renderer/StrokeRenderer.js"],"sourcesContent":["/**\n * StrokeRenderer - Unified canvas-based annotation renderer\n *\n * Single entry point for rendering annotations as strokes on canvas.\n * Handles conversion from annotations to strokes and progressive rendering.\n *\n * @module renderer/StrokeRenderer\n */\n\nimport {\n highlightToStrokes,\n textToStrokes,\n underlineToStrokes,\n arrowToStrokes,\n circleToStrokes,\n inkToStrokes,\n} from \"../converters/index.js\";\n\n/**\n * Supported annotation types\n */\nconst ANNOTATION_TYPES = [\"highlight\", \"text\", \"underline\", \"arrow\", \"circle\", \"ink\"];\n\n/**\n * Reference viewport height for width scaling\n * Stroke widths are defined relative to this height (500px = roughjs-tester default)\n */\nconst REFERENCE_HEIGHT = 500;\n\n/**\n * Default configuration\n *\n * Each annotation type has:\n * - Rendering options: color, width, lineCap\n * - RoughJS options: roughness, bowing, curveFitting, curveStepCount, maxRandomnessOffset, disableMultiStroke\n */\nconst DEFAULT_CONFIG = {\n highlight: {\n color: \"rgba(255, 255, 0, 0.3)\",\n width: 24,\n lineCap: \"square\",\n roughness: 1.2,\n bowing: 1.2,\n curveFitting: 0.95,\n curveStepCount: 9,\n maxRandomnessOffset: 2,\n disableMultiStroke: false,\n },\n text: {\n color: \"rgba(220, 20, 60, 1.0)\",\n width: 2,\n fontSize: 16,\n lineCap: \"round\",\n roughness: 0.8,\n },\n underline: {\n color: \"rgba(0, 0, 255, 0.8)\",\n width: 2,\n lineCap: \"round\",\n roughness: 2.0,\n bowing: 2.4,\n curveFitting: 0.85,\n curveStepCount: 9,\n maxRandomnessOffset: 3,\n disableMultiStroke: false,\n },\n arrow: {\n color: \"rgba(255, 0, 0, 0.8)\",\n width: 2,\n lineCap: \"round\",\n roughness: 1.8,\n bowing: 1.8,\n curveFitting: 0.9,\n curveStepCount: 9,\n maxRandomnessOffset: 2,\n disableMultiStroke: false,\n },\n circle: {\n color: \"rgba(255, 165, 0, 0.8)\",\n width: 3,\n lineCap: \"round\",\n roughness: 1.7,\n bowing: 2.0,\n curveFitting: 0.8,\n curveStepCount: 12,\n maxRandomnessOffset: 3,\n disableMultiStroke: false,\n },\n ink: {\n color: \"#DC143C\",\n width: 2,\n lineCap: \"round\",\n },\n};\n\n/**\n * StrokeRenderer class\n *\n * Converts annotations to strokes and renders them progressively on canvas.\n *\n * @class\n * @example\n * const renderer = new StrokeRenderer(canvas, {\n * highlight: { color: 'rgba(0, 255, 200, 0.4)' }\n * });\n * renderer.setViewport(800, 600);\n * renderer.setAnnotations(annotations);\n * renderer.render(1.5); // Render at t=1.5 seconds\n */\nclass StrokeRenderer {\n /**\n * Create StrokeRenderer instance\n *\n * @param {HTMLCanvasElement} canvas - Canvas element for rendering\n * @param {Object} [config={}] - Configuration overrides per annotation type\n * @param {Object} [config.highlight] - Highlight type settings\n * @param {Object} [config.text] - Text type settings\n * @param {Object} [config.underline] - Underline type settings\n * @param {Object} [config.arrow] - Arrow type settings\n * @param {Object} [config.circle] - Circle type settings\n */\n constructor(canvas, config = {}) {\n if (!canvas || !(canvas instanceof HTMLCanvasElement)) {\n throw new Error(\n \"StrokeRenderer: canvas must be a valid HTMLCanvasElement\"\n );\n }\n\n this.canvas = canvas;\n this.ctx = canvas.getContext(\"2d\");\n this.strokes = [];\n this.viewport = { width: 0, height: 0 };\n\n // Initialize config with defaults\n this.config = {};\n for (const type of ANNOTATION_TYPES) {\n this.config[type] = { ...DEFAULT_CONFIG[type] };\n }\n\n // Apply user config if provided\n if (Object.keys(config).length > 0) {\n this.setConfig(config);\n }\n\n // Register built-in converters\n this.converters = {\n highlight: highlightToStrokes,\n text: textToStrokes,\n underline: underlineToStrokes,\n arrow: arrowToStrokes,\n circle: circleToStrokes,\n ink: inkToStrokes,\n };\n }\n\n /**\n * Set configuration options\n *\n * Supports global options (applied to all types) and type-specific options.\n *\n * @param {Object} config - Configuration object\n * @param {number} [config.roughness] - Global roughness for all types\n * @param {string} [config.color] - Global color for all types\n * @param {number} [config.width] - Global width for all types\n * @param {Object} [config.highlight] - Highlight-specific options\n * @param {Object} [config.text] - Text-specific options\n * @param {Object} [config.underline] - Underline-specific options\n * @param {Object} [config.arrow] - Arrow-specific options\n * @param {Object} [config.circle] - Circle-specific options\n * @returns {StrokeRenderer} Returns this for chaining\n *\n * @example\n * // Global options\n * renderer.setConfig({ roughness: 2, color: \"blue\" });\n *\n * // Type-specific options\n * renderer.setConfig({ highlight: { color: \"yellow\", width: 30 } });\n *\n * // Combined\n * renderer.setConfig({\n * roughness: 2,\n * highlight: { color: \"yellow\" }\n * });\n */\n setConfig(config) {\n const globalOptions = {};\n const typeOptions = {};\n\n // Separate global options from type-specific options\n for (const [key, value] of Object.entries(config)) {\n if (ANNOTATION_TYPES.includes(key)) {\n typeOptions[key] = value;\n } else {\n globalOptions[key] = value;\n }\n }\n\n // Apply to each type: current config + global + type-specific\n for (const type of ANNOTATION_TYPES) {\n this.config[type] = {\n ...this.config[type],\n ...globalOptions,\n ...(typeOptions[type] || {}),\n };\n }\n\n return this;\n }\n\n /**\n * Register a custom converter for a new annotation type\n *\n * @param {string} type - Annotation type name\n * @param {Function} converter - Converter function (annotation, style) => strokes[]\n */\n registerConverter(type, converter) {\n if (typeof converter !== \"function\") {\n throw new Error(\n \"StrokeRenderer.registerConverter: converter must be a function\"\n );\n }\n this.converters[type] = converter;\n }\n\n /**\n * Set viewport dimensions and configure canvas\n *\n * Handles high-DPI displays by scaling the canvas buffer.\n *\n * @param {number} width - Viewport width in CSS pixels\n * @param {number} height - Viewport height in CSS pixels\n */\n setViewport(width, height) {\n this.viewport = { width, height };\n\n const dpr = window.devicePixelRatio || 1;\n\n // Set canvas buffer size (high-res)\n this.canvas.width = width * dpr;\n this.canvas.height = height * dpr;\n\n // Set canvas display size (CSS)\n this.canvas.style.width = `${width}px`;\n this.canvas.style.height = `${height}px`;\n\n // Scale context for high-DPI\n this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n }\n\n /**\n * Set annotations and convert them to strokes\n *\n * Clears existing strokes and converts all annotations.\n *\n * @param {Array} annotations - Array of annotation objects\n * @param {number} [page] - Optional page filter (only convert annotations for this page)\n */\n setAnnotations(annotations, page = null) {\n if (!Array.isArray(annotations)) {\n console.warn(\n \"StrokeRenderer.setAnnotations: annotations must be an array\"\n );\n this.strokes = [];\n return;\n }\n\n // Filter by page if specified\n const filtered =\n page !== null ? annotations.filter((a) => a.page === page) : annotations;\n\n // Convert each annotation to strokes\n this.strokes = filtered.flatMap((annotation) => {\n const converter = this.converters[annotation.type];\n\n if (!converter) {\n console.warn(\n `StrokeRenderer: Unknown annotation type \"${annotation.type}\"`\n );\n return [];\n }\n\n // Resolve style: pen → type → annotation.style\n const style = this._resolveStyle(annotation);\n\n return converter(annotation, style);\n });\n }\n\n /**\n * Set pre-converted strokes directly\n *\n * Bypasses conversion, useful when strokes come from external source.\n *\n * @param {Array} strokes - Array of stroke command objects\n */\n setStrokes(strokes) {\n if (!Array.isArray(strokes)) {\n console.warn(\"StrokeRenderer.setStrokes: strokes must be an array\");\n this.strokes = [];\n return;\n }\n this.strokes = strokes;\n }\n\n /**\n * Render strokes at the given time\n *\n * Clears canvas and draws all visible strokes with appropriate progress.\n *\n * @param {number} time - Current time in seconds\n */\n render(time) {\n const { ctx, viewport, strokes } = this;\n\n // Clear canvas\n ctx.clearRect(0, 0, viewport.width, viewport.height);\n\n // Draw each stroke\n for (const stroke of strokes) {\n // Skip if not started yet\n if (time < stroke.start) {\n continue;\n }\n\n // Calculate progress\n const duration = stroke.end - stroke.start;\n const elapsed = time - stroke.start;\n const progress = duration > 0 ? Math.min(1, elapsed / duration) : 1;\n\n this._drawStroke(stroke, progress);\n }\n }\n\n /**\n * Clear the canvas\n */\n clear() {\n this.ctx.clearRect(0, 0, this.viewport.width, this.viewport.height);\n }\n\n /**\n * Destroy the renderer and release resources\n */\n destroy() {\n this.strokes = [];\n this.ctx = null;\n this.canvas = null;\n this.config = null;\n this.converters = null;\n }\n\n /**\n * Resolve style for an annotation\n *\n * Returns the config for the annotation type.\n * Style is managed via setConfig(), not per-annotation.\n *\n * @private\n * @param {Object} annotation - Annotation object\n * @returns {Object} Resolved style object\n */\n _resolveStyle(annotation) {\n const { type } = annotation;\n return this.config[type] || {};\n }\n\n /**\n * Linearly interpolate between two points\n *\n * @private\n * @param {Array} p1 - Start point [x, y]\n * @param {Array} p2 - End point [x, y]\n * @param {number} t - Interpolation factor (0-1)\n * @returns {Array} Interpolated point [x, y]\n */\n _interpolatePoint(p1, p2, t) {\n return [p1[0] + (p2[0] - p1[0]) * t, p1[1] + (p2[1] - p1[1]) * t];\n }\n\n /**\n * Get pressure values for visible points including interpolated final\n *\n * @private\n * @param {Array} pressures - Full pressure array\n * @param {number} lastCompleteIndex - Index of last complete point\n * @param {number} segmentProgress - Progress within current segment (0-1)\n * @returns {Array} Pressure values for visible points\n */\n _interpolatePressures(pressures, lastCompleteIndex, segmentProgress) {\n const visiblePressures = pressures.slice(0, lastCompleteIndex + 1);\n\n if (lastCompleteIndex < pressures.length - 1 && segmentProgress > 0) {\n const p1 = pressures[lastCompleteIndex];\n const p2 = pressures[lastCompleteIndex + 1];\n visiblePressures.push(p1 + (p2 - p1) * segmentProgress);\n }\n\n return visiblePressures;\n }\n\n /**\n * Draw a single stroke with progress\n *\n * Uses endpoint interpolation for smooth progressive rendering.\n *\n * @private\n * @param {Object} stroke - Stroke command object\n * @param {number} progress - Progress from 0 to 1\n */\n _drawStroke(stroke, progress) {\n const { ctx } = this;\n const {\n points,\n color,\n width,\n lineCap,\n pressures,\n uniformScale,\n baseX,\n baseY,\n } = stroke;\n\n if (!points || points.length < 2) return;\n\n // Configure stroke style\n ctx.strokeStyle = color || \"rgba(0, 0, 0, 0.5)\";\n ctx.lineCap = lineCap || \"round\";\n ctx.lineJoin = \"round\";\n\n // Fast path: full stroke, no interpolation needed\n if (progress >= 1) {\n if (pressures && pressures.length >= points.length) {\n this._drawVariableWidthStroke(\n points,\n width,\n pressures,\n uniformScale,\n baseX,\n baseY\n );\n } else {\n this._drawConstantWidthStroke(\n points,\n width,\n uniformScale,\n baseX,\n baseY\n );\n }\n return;\n }\n\n // Calculate segment position for interpolation\n const totalSegments = points.length - 1;\n const progressAlongPath = progress * totalSegments;\n const lastCompleteIndex = Math.floor(progressAlongPath);\n const segmentProgress = progressAlongPath - lastCompleteIndex;\n\n // Build visible points array with complete points\n const visiblePoints = points.slice(0, lastCompleteIndex + 1);\n\n // Interpolate final point if mid-segment\n if (lastCompleteIndex < totalSegments && segmentProgress > 0) {\n const p1 = points[lastCompleteIndex];\n const p2 = points[lastCompleteIndex + 1];\n visiblePoints.push(this._interpolatePoint(p1, p2, segmentProgress));\n }\n\n // Need at least 2 points to draw a line\n if (visiblePoints.length < 2) return;\n\n // Draw with variable width if pressures provided\n if (pressures && pressures.length >= points.length) {\n const visiblePressures = this._interpolatePressures(\n pressures,\n lastCompleteIndex,\n segmentProgress\n );\n this._drawVariableWidthStroke(\n visiblePoints,\n width,\n visiblePressures,\n uniformScale,\n baseX,\n baseY\n );\n } else {\n this._drawConstantWidthStroke(\n visiblePoints,\n width,\n uniformScale,\n baseX,\n baseY\n );\n }\n }\n\n /**\n * Draw stroke with constant width\n *\n * @private\n * @param {Array} points - Array of [x, y] coordinates (normalized or offset)\n * @param {number} width - Stroke width in pixels\n * @param {boolean} [uniformScale=false] - Use uniform scaling to preserve aspect ratio\n * @param {number} [baseX] - Base X position in width-normalized coords (for uniformScale with offset points)\n * @param {number} [baseY] - Base Y position in height-normalized coords (for uniformScale with offset points)\n */\n _drawConstantWidthStroke(\n points,\n width,\n uniformScale = false,\n baseX = null,\n baseY = null\n ) {\n const { ctx, viewport } = this;\n\n // Scale width based on viewport height for consistent appearance\n const scale = viewport.height / REFERENCE_HEIGHT;\n ctx.lineWidth = (width || 2) * scale;\n ctx.beginPath();\n\n // When uniformScale is true and baseX/baseY provided, points are offsets from base position\n // Base position is in mixed coords (X: width-normalized, Y: height-normalized)\n // Offsets are in uniform space (both scaled by viewport.height)\n const hasBasePosition = uniformScale && baseX !== null && baseY !== null;\n\n for (let i = 0; i < points.length; i++) {\n const [coordX, coordY] = points[i];\n let px, py;\n\n if (hasBasePosition) {\n // Base position in pixels + uniform-scaled offset\n px = baseX * viewport.width + coordX * viewport.height;\n py = baseY * viewport.height + coordY * viewport.height;\n } else if (uniformScale) {\n // Legacy: full coordinates in uniform space\n px = coordX * viewport.height;\n py = coordY * viewport.height;\n } else {\n // Standard: X scaled by width, Y by height\n px = coordX * viewport.width;\n py = coordY * viewport.height;\n }\n\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n ctx.stroke();\n }\n\n /**\n * Draw stroke with variable width based on pressure\n *\n * @private\n * @param {Array} points - Array of [x, y] coordinates (normalized or offset)\n * @param {number} baseWidth - Base stroke width in pixels\n * @param {Array} pressures - Pressure values (0-1) per point\n * @param {boolean} [uniformScale=false] - Use uniform scaling to preserve aspect ratio\n * @param {number} [baseX] - Base X position in width-normalized coords (for uniformScale with offset points)\n * @param {number} [baseY] - Base Y position in height-normalized coords (for uniformScale with offset points)\n */\n _drawVariableWidthStroke(\n points,\n baseWidth,\n pressures,\n uniformScale = false,\n baseX = null,\n baseY = null\n ) {\n const { ctx, viewport } = this;\n\n // When uniformScale is true and baseX/baseY provided, points are offsets from base position\n const hasBasePosition = uniformScale && baseX !== null && baseY !== null;\n\n for (let i = 1; i < points.length; i++) {\n const [c1x, c1y] = points[i - 1];\n const [c2x, c2y] = points[i];\n\n let px1, py1, px2, py2;\n\n if (hasBasePosition) {\n // Base position in pixels + uniform-scaled offset\n px1 = baseX * viewport.width + c1x * viewport.height;\n py1 = baseY * viewport.height + c1y * viewport.height;\n px2 = baseX * viewport.width + c2x * viewport.height;\n py2 = baseY * viewport.height + c2y * viewport.height;\n } else if (uniformScale) {\n // Legacy: full coordinates in uniform space\n px1 = c1x * viewport.height;\n py1 = c1y * viewport.height;\n px2 = c2x * viewport.height;\n py2 = c2y * viewport.height;\n } else {\n // Standard: X scaled by width, Y by height\n px1 = c1x * viewport.width;\n py1 = c1y * viewport.height;\n px2 = c2x * viewport.width;\n py2 = c2y * viewport.height;\n }\n\n // Average pressure between two points\n const p1 = pressures[i - 1] || 1;\n const p2 = pressures[i] || 1;\n const avgPressure = (p1 + p2) / 2;\n\n // Apply pressure to width (min 0.5) and scale for viewport\n const scale = viewport.height / REFERENCE_HEIGHT;\n const width = Math.max(0.5, baseWidth * avgPressure) * scale;\n\n ctx.lineWidth = width;\n ctx.beginPath();\n ctx.moveTo(px1, py1);\n ctx.lineTo(px2, py2);\n ctx.stroke();\n }\n }\n}\n\nexport { StrokeRenderer };\nexport default StrokeRenderer;\n"],"names":["ANNOTATION_TYPES","REFERENCE_HEIGHT","DEFAULT_CONFIG","StrokeRenderer","canvas","config","type","highlightToStrokes","textToStrokes","underlineToStrokes","arrowToStrokes","circleToStrokes","inkToStrokes","globalOptions","typeOptions","key","value","converter","width","height","dpr","annotations","page","filtered","a","annotation","style","strokes","time","ctx","viewport","stroke","duration","elapsed","progress","p1","p2","t","pressures","lastCompleteIndex","segmentProgress","visiblePressures","points","color","lineCap","uniformScale","baseX","baseY","totalSegments","progressAlongPath","visiblePoints","scale","hasBasePosition","i","coordX","coordY","px","py","baseWidth","c1x","c1y","c2x","c2y","px1","py1","px2","py2","avgPressure"],"mappings":"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"}
package/dist/index6.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function h(e){if(e==null)return Math.random;let r=typeof e=="string"?e.split("").reduce((t,n)=>t+n.charCodeAt(0),0):e;return function(){r|=0,r=r+1831565813|0;let t=Math.imul(r^r>>>15,1|r);return t=t+Math.imul(t^t>>>7,61|t)^t,((t^t>>>14)>>>0)/4294967296}}function b(e,r,t=null){const{amplitude:n=0}=r||{};if(n===0||!e||e.length===0)return e;const l=h(t);return e.map(([u,a])=>[u+(l()-.5)*n,a+(l()-.5)*n])}function M(e,r){const{taperIn:t=0,taperOut:n=0}=r||{};if(!e||e.length===0)return[];if(t===0&&n===0)return e.map(()=>1);const l=e.length;return e.map((u,a)=>{const f=l>1?a/(l-1):0;return t>0&&f<t?f/t:n>0&&f>1-n?(1-f)/n:1})}function A(e,r){const{amplitude:t=0,frequency:n=.05}=r||{};return t===0||!e||e.length<2?e:e.map(([l,u],a)=>{const f=a/(e.length-1),y=Math.sin(f*Math.PI*2*n*e.length)*t;let d=0,p=1;if(a<e.length-1){const[m,g]=e[a+1],c=m-l,o=g-u,i=Math.sqrt(c*c+o*o);i>1e-4&&(d=-o/i,p=c/i)}return[l+d*y,u+p*y]})}function s(e,r){if(!r)return{...e};if(!e)return{...r};const t={...e};for(const n of Object.keys(r)){const l=r[n],u=e[n];l!==null&&typeof l=="object"&&!Array.isArray(l)&&u!==null&&typeof u=="object"&&!Array.isArray(u)?t[n]=s(u,l):l!==void 0&&(t[n]=l)}return t}exports.applyJitter=b;exports.applyPressure=M;exports.applyWobble=A;exports.deepMerge=s;exports.seededRandom=h;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=(t,h)=>({left:t.x*h.width,top:t.y*h.height,width:(t.w??0)*h.width,height:(t.h??0)*h.height}),i=(t,h)=>({left:t.x*h.width,top:t.y*h.height,width:(t.w??0)*h.width,height:(t.h??0)*h.height});function o(t,h){return{x:t.x*h.width,y:t.y*h.height}}exports.NormSizeToPixel=i;exports.pointNormToAbs=o;exports.rectNormToAbs=e;
2
2
  //# sourceMappingURL=index6.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index6.cjs","sources":["../src/pen/effects.js"],"sourcesContent":["/**\n * Pen Effects - Utility functions for hand-drawn feel\n *\n * Provides jitter, pressure, and wobble effects for stroke paths.\n * Pure functions that transform point arrays without side effects.\n *\n * @module pen/effects\n */\n\n/**\n * Creates a seeded random number generator\n *\n * Uses mulberry32 algorithm for deterministic randomness.\n * Same seed produces same sequence of random numbers.\n *\n * @param {string|number|null} seed - Seed value (null uses Math.random)\n * @returns {Function} Function that returns random numbers 0-1\n */\nexport function seededRandom(seed) {\n if (seed === null || seed === undefined) {\n return Math.random;\n }\n\n // Convert string seed to number\n let numSeed = typeof seed === 'string'\n ? seed.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)\n : seed;\n\n // Mulberry32 algorithm\n return function() {\n numSeed |= 0;\n numSeed = (numSeed + 0x6D2B79F5) | 0;\n let t = Math.imul(numSeed ^ (numSeed >>> 15), 1 | numSeed);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Applies random jitter offset to points\n *\n * Adds small random displacement to each point for hand-drawn imperfection.\n * Returns new array, does not mutate input.\n *\n * @param {Array<[number, number]>} points - Array of [x, y] coordinates\n * @param {Object} config - Jitter configuration\n * @param {number} config.amplitude - Max offset in normalized units (0 = disabled)\n * @param {number} [config.frequency=1] - Not currently used, reserved for future\n * @param {string|number|null} [seed=null] - Seed for reproducible randomness\n * @returns {Array<[number, number]>} New array with jittered points\n */\nexport function applyJitter(points, config, seed = null) {\n const { amplitude = 0 } = config || {};\n\n if (amplitude === 0 || !points || points.length === 0) {\n return points;\n }\n\n const rng = seededRandom(seed);\n\n return points.map(([x, y]) => [\n x + (rng() - 0.5) * amplitude,\n y + (rng() - 0.5) * amplitude\n ]);\n}\n\n/**\n * Calculates pressure values for variable stroke width\n *\n * Simulates pen pressure with taper at start and end of stroke.\n * Returns pressure multipliers (0-1) for each point.\n *\n * @param {Array<[number, number]>} points - Array of [x, y] coordinates\n * @param {Object} config - Pressure configuration\n * @param {number} [config.taperIn=0] - Start taper length (0-1, fraction of stroke)\n * @param {number} [config.taperOut=0] - End taper length (0-1, fraction of stroke)\n * @returns {Array<number>} Pressure values (0-1) per point\n */\nexport function applyPressure(points, config) {\n const { taperIn = 0, taperOut = 0 } = config || {};\n\n if (!points || points.length === 0) {\n return [];\n }\n\n if (taperIn === 0 && taperOut === 0) {\n return points.map(() => 1.0);\n }\n\n const len = points.length;\n\n return points.map((_, i) => {\n const t = len > 1 ? i / (len - 1) : 0;\n\n // Taper in at start\n if (taperIn > 0 && t < taperIn) {\n return t / taperIn;\n }\n\n // Taper out at end\n if (taperOut > 0 && t > 1 - taperOut) {\n return (1 - t) / taperOut;\n }\n\n // Full pressure in middle\n return 1.0;\n });\n}\n\n/**\n * Applies low-frequency wobble perpendicular to stroke direction\n *\n * Adds gentle wave effect to simulate natural hand movement.\n * Returns new array, does not mutate input.\n *\n * @param {Array<[number, number]>} points - Array of [x, y] coordinates\n * @param {Object} config - Wobble configuration\n * @param {number} config.amplitude - Wave height in normalized units (0 = disabled)\n * @param {number} [config.frequency=0.05] - Wave frequency\n * @returns {Array<[number, number]>} New array with wobbled points\n */\nexport function applyWobble(points, config) {\n const { amplitude = 0, frequency = 0.05 } = config || {};\n\n if (amplitude === 0 || !points || points.length < 2) {\n return points;\n }\n\n return points.map(([x, y], i) => {\n // Calculate perpendicular direction based on position in stroke\n // Use simple sine wave for now\n const t = i / (points.length - 1);\n const offset = Math.sin(t * Math.PI * 2 * frequency * points.length) * amplitude;\n\n // Calculate perpendicular direction from neighbors\n let perpX = 0;\n let perpY = 1;\n\n if (i < points.length - 1) {\n const [nx, ny] = points[i + 1];\n const dx = nx - x;\n const dy = ny - y;\n const len = Math.sqrt(dx * dx + dy * dy);\n\n if (len > 0.0001) {\n // Perpendicular is (-dy, dx) normalized\n perpX = -dy / len;\n perpY = dx / len;\n }\n }\n\n return [\n x + perpX * offset,\n y + perpY * offset\n ];\n });\n}\n\n/**\n * Deep merges configuration objects\n *\n * Merges source into target, handling nested objects.\n * Returns new object, does not mutate inputs.\n *\n * @param {Object} target - Target object\n * @param {Object} source - Source object to merge\n * @returns {Object} Merged object\n */\nexport function deepMerge(target, source) {\n if (!source) return { ...target };\n if (!target) return { ...source };\n\n const result = { ...target };\n\n for (const key of Object.keys(source)) {\n const sourceVal = source[key];\n const targetVal = target[key];\n\n if (\n sourceVal !== null &&\n typeof sourceVal === 'object' &&\n !Array.isArray(sourceVal) &&\n targetVal !== null &&\n typeof targetVal === 'object' &&\n !Array.isArray(targetVal)\n ) {\n result[key] = deepMerge(targetVal, sourceVal);\n } else if (sourceVal !== undefined) {\n result[key] = sourceVal;\n }\n }\n\n return result;\n}\n"],"names":["seededRandom","seed","numSeed","acc","char","applyJitter","points","config","amplitude","rng","x","y","applyPressure","taperIn","taperOut","len","_","i","t","applyWobble","frequency","offset","perpX","perpY","nx","ny","dx","dy","deepMerge","target","source","result","key","sourceVal","targetVal"],"mappings":"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"}
1
+ {"version":3,"file":"index6.cjs","sources":["../src/utils/coordinateUtils.js"],"sourcesContent":["/**\n * Coordinate Utility Functions\n *\n * This module provides utility functions for coordinate transformations\n * between normalized (0-1) coordinates and absolute pixel coordinates.\n * Used by annotation layers to position elements on the PDF canvas.\n */\n\n/**\n * Convert normalized rectangle to absolute pixel coordinates\n *\n * Transforms a rectangle with normalized coordinates (0-1 range) to\n * absolute pixel coordinates based on viewport dimensions.\n *\n * @param {{x: number, y: number, w: number, h: number}} rect - Normalized rectangle (0-1)\n * @param {{width: number, height: number}} viewport - Viewport dimensions in pixels\n * @returns {{left: number, top: number, width: number, height: number}} Absolute coordinates in pixels\n *\n * @example\n * const rect = { x: 0.1, y: 0.2, w: 0.5, h: 0.3 };\n * const viewport = { width: 1000, height: 1400 };\n * const absolute = rectNormToAbs(rect, viewport);\n * // Returns: { left: 100, top: 280, width: 500, height: 420 }\n */\nexport const rectNormToAbs = (r, vp) => ({\n left: r.x * vp.width,\n top: r.y * vp.height,\n width: (r.w ?? 0) * vp.width,\n height: (r.h ?? 0) * vp.height,\n});\n\n/**\n * Convert normalized size to pixel dimensions (Legacy)\n *\n * @deprecated Use rectNormToAbs instead. This function is kept for backward compatibility.\n *\n * Transforms normalized rectangle coordinates to absolute pixel coordinates.\n * This is an alias for rectNormToAbs maintained for backward compatibility.\n *\n * @param {{x: number, y: number, w: number, h: number}} r - Normalized rectangle (0-1)\n * @param {{width: number, height: number}} vp - Viewport dimensions in pixels\n * @returns {{left: number, top: number, width: number, height: number}} Absolute coordinates in pixels\n */\nexport const NormSizeToPixel = (r, vp) => ({\n left: r.x * vp.width,\n top: r.y * vp.height,\n width: (r.w ?? 0) * vp.width,\n height: (r.h ?? 0) * vp.height,\n});\n\n/**\n * Convert normalized point to absolute pixel coordinates\n *\n * Transforms a point with normalized coordinates (0-1 range) to\n * absolute pixel coordinates based on viewport dimensions.\n *\n * @param {{x: number, y: number}} point - Normalized point (0-1)\n * @param {{width: number, height: number}} viewport - Viewport dimensions in pixels\n * @returns {{x: number, y: number}} Absolute coordinates in pixels\n *\n * @example\n * const point = { x: 0.5, y: 0.5 };\n * const viewport = { width: 1000, height: 1400 };\n * const absolute = pointNormToAbs(point, viewport);\n * // Returns: { x: 500, y: 700 }\n */\nexport function pointNormToAbs(point, viewport) {\n return {\n x: point.x * viewport.width,\n y: point.y * viewport.height\n };\n}\n"],"names":["rectNormToAbs","r","vp","NormSizeToPixel","pointNormToAbs","point","viewport"],"mappings":"gFAwBY,MAACA,EAAgB,CAACC,EAAGC,KAAQ,CACvC,KAAMD,EAAE,EAAIC,EAAG,MACf,IAAKD,EAAE,EAAIC,EAAG,OACd,OAAQD,EAAE,GAAK,GAAKC,EAAG,MACvB,QAASD,EAAE,GAAK,GAAKC,EAAG,MAC1B,GAcaC,EAAkB,CAACF,EAAGC,KAAQ,CACzC,KAAMD,EAAE,EAAIC,EAAG,MACf,IAAKD,EAAE,EAAIC,EAAG,OACd,OAAQD,EAAE,GAAK,GAAKC,EAAG,MACvB,QAASD,EAAE,GAAK,GAAKC,EAAG,MAC1B,GAkBO,SAASE,EAAeC,EAAOC,EAAU,CAC9C,MAAO,CACL,EAAGD,EAAM,EAAIC,EAAS,MACtB,EAAGD,EAAM,EAAIC,EAAS,MAC1B,CACA"}