web-annotation-renderer 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +129 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +62 -61
- package/dist/index.js.map +1 -1
- package/dist/index10.cjs +1 -1
- package/dist/index10.cjs.map +1 -1
- package/dist/index10.js +160 -13
- package/dist/index10.js.map +1 -1
- package/dist/index11.cjs +1 -1
- package/dist/index11.cjs.map +1 -1
- package/dist/index11.js +13 -50
- package/dist/index11.js.map +1 -1
- package/dist/index12.cjs +1 -1
- package/dist/index12.cjs.map +1 -1
- package/dist/index12.js +48 -155
- package/dist/index12.js.map +1 -1
- package/dist/index13.cjs +1 -1
- package/dist/index13.cjs.map +1 -1
- package/dist/index13.js +150 -34
- package/dist/index13.js.map +1 -1
- package/dist/index14.cjs +1 -1
- package/dist/index14.cjs.map +1 -1
- package/dist/index14.js +32 -65
- package/dist/index14.js.map +1 -1
- package/dist/index15.cjs +1 -1
- package/dist/index15.cjs.map +1 -1
- package/dist/index15.js +66 -33
- package/dist/index15.js.map +1 -1
- package/dist/index16.cjs +1 -1
- package/dist/index16.cjs.map +1 -1
- package/dist/index16.js +35 -77
- package/dist/index16.js.map +1 -1
- package/dist/index17.cjs +1 -1
- package/dist/index17.cjs.map +1 -1
- package/dist/index17.js +53 -28
- package/dist/index17.js.map +1 -1
- package/dist/index18.cjs +1 -1
- package/dist/index18.cjs.map +1 -1
- package/dist/index18.js +28 -22
- package/dist/index18.js.map +1 -1
- package/dist/index19.cjs +1 -1
- package/dist/index19.cjs.map +1 -1
- package/dist/index19.js +22 -117
- package/dist/index19.js.map +1 -1
- package/dist/index2.cjs +1 -1
- package/dist/index2.cjs.map +1 -1
- package/dist/index2.js +94 -98
- package/dist/index2.js.map +1 -1
- package/dist/index20.cjs +1 -1
- package/dist/index20.cjs.map +1 -1
- package/dist/index20.js +137 -100
- package/dist/index20.js.map +1 -1
- package/dist/index21.cjs +1 -1
- package/dist/index21.cjs.map +1 -1
- package/dist/index21.js +34 -76
- package/dist/index21.js.map +1 -1
- package/dist/index22.cjs +1 -1
- package/dist/index22.cjs.map +1 -1
- package/dist/index22.js +35 -139
- package/dist/index22.js.map +1 -1
- package/dist/index23.cjs +1 -1
- package/dist/index23.cjs.map +1 -1
- package/dist/index23.js +37 -37
- package/dist/index23.js.map +1 -1
- package/dist/index24.cjs +1 -1
- package/dist/index24.cjs.map +1 -1
- package/dist/index24.js +69 -37
- package/dist/index24.js.map +1 -1
- package/dist/index25.cjs +1 -1
- package/dist/index25.cjs.map +1 -1
- package/dist/index25.js +40 -38
- package/dist/index25.js.map +1 -1
- package/dist/index26.cjs +1 -1
- package/dist/index26.cjs.map +1 -1
- package/dist/index26.js +4 -39
- package/dist/index26.js.map +1 -1
- package/dist/index27.cjs +1 -1
- package/dist/index27.js +4 -4
- package/dist/index28.cjs +1 -1
- package/dist/index28.cjs.map +1 -1
- package/dist/index28.js +71 -5
- package/dist/index28.js.map +1 -1
- package/dist/index29.cjs +1 -1
- package/dist/index29.cjs.map +1 -1
- package/dist/index29.js +24 -69
- package/dist/index29.js.map +1 -1
- package/dist/index3.cjs +1 -1
- package/dist/index3.cjs.map +1 -1
- package/dist/index3.js +31 -31
- package/dist/index3.js.map +1 -1
- package/dist/index5.cjs +1 -1
- package/dist/index5.cjs.map +1 -1
- package/dist/index5.js +237 -190
- package/dist/index5.js.map +1 -1
- package/dist/index6.cjs +1 -1
- package/dist/index6.cjs.map +1 -1
- package/dist/index6.js +37 -19
- package/dist/index6.js.map +1 -1
- package/dist/index7.cjs +1 -1
- package/dist/index7.cjs.map +1 -1
- package/dist/index7.js +11 -17
- package/dist/index7.js.map +1 -1
- package/dist/index8.cjs +1 -1
- package/dist/index8.cjs.map +1 -1
- package/dist/index8.js +16 -125
- package/dist/index8.js.map +1 -1
- package/dist/index9.cjs +1 -1
- package/dist/index9.cjs.map +1 -1
- package/dist/index9.js +118 -201
- package/dist/index9.js.map +1 -1
- package/package.json +6 -3
package/dist/index5.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index5.cjs","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 = [\n \"highlight\",\n \"text\",\n \"underline\",\n \"arrow\",\n \"circle\",\n \"ink\",\n];\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: 1.8,\n bowing: 2,\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.5,\n bowing: 1.7,\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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"}
|
|
1
|
+
{"version":3,"file":"index5.cjs","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\";\nimport {\n ANNOTATION_TYPES,\n DEFAULT_CONFIG,\n REFERENCE_HEIGHT,\n} from \"../config/defaults.js\";\nimport { normalizeAnnotationArray } from \"../types/validators.js\";\nimport { pointNormToAbs } from \"../utils/coordinateUtils.js\";\n\n// Re-export so converters / callers can import REFERENCE_HEIGHT either from\n// the config module or from StrokeRenderer (keeps both paths working).\nexport { REFERENCE_HEIGHT };\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 // Per-page strokes cache. Populated lazily in setAnnotations.\n // Keyed by page number; value is an array of stroke commands.\n this._strokeCacheByPage = new Map();\n // Identity reference of the last annotations array we cached. Used to\n // detect when the caller swaps in a new array so we can invalidate.\n this._lastAnnotationsRef = null;\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 // Invalidate per-page strokes cache: cached strokes were converted under\n // the previous style and would silently render stale colors / widths if\n // the next setAnnotations call passes the same array reference. The\n // `_lastAnnotationsRef = null` reset forces the next setAnnotations to\n // re-warm the cache regardless of identity. (BLOCKING-1, T17a)\n if (this._strokeCacheByPage) this._strokeCacheByPage.clear();\n this._lastAnnotationsRef = null;\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 * Behavior:\n * - When `page` is null, all annotations are converted and exposed via\n * `this.strokes`. Per-page cache is still populated.\n * - When `page` is a number, conversion is performed per page (cached) and\n * `this.strokes` is set to the cache entry for the requested page.\n * - When the `annotations` reference changes between calls, the cache is\n * invalidated.\n *\n * @param {Array} annotations - Array of annotation objects\n * @param {number|null} [page=null] - Optional page filter\n * @param {Object} [options]\n * @param {boolean} [options.validate=false] - Run normalizeAnnotationArray\n * first and convert the normalized result instead of the raw input.\n * @param {boolean} [options.noCache=false] - Skip the per-page cache and\n * convert the filtered annotations directly (legacy fast path).\n */\n setAnnotations(annotations, page = null, options = {}) {\n if (!Array.isArray(annotations)) {\n console.warn(\n \"StrokeRenderer.setAnnotations: annotations must be an array\",\n );\n this.strokes = [];\n this._strokeCacheByPage.clear();\n this._lastAnnotationsRef = null;\n return;\n }\n\n const { validate = false, noCache = false } = options;\n\n // Optionally normalize input before conversion.\n let source = annotations;\n if (validate) {\n const result = normalizeAnnotationArray(annotations);\n source = result.normalized;\n }\n\n // Detect identity change → invalidate cache.\n if (source !== this._lastAnnotationsRef) {\n this._strokeCacheByPage.clear();\n this._lastAnnotationsRef = source;\n }\n\n // Legacy / opt-out path: convert filtered annotations without caching.\n if (noCache) {\n const filtered =\n page !== null ? source.filter((a) => a.page === page) : source;\n this.strokes = this._convertAnnotations(filtered);\n return;\n }\n\n // Group annotations by page once, populate cache for each touched page.\n // We always (re)populate the cache for every page present in `source`\n // when the cache is empty so subsequent setPage-style calls hit cache.\n if (this._strokeCacheByPage.size === 0) {\n const byPage = new Map();\n for (const ann of source) {\n const key = ann && ann.page != null ? ann.page : null;\n let bucket = byPage.get(key);\n if (!bucket) {\n bucket = [];\n byPage.set(key, bucket);\n }\n bucket.push(ann);\n }\n for (const [key, bucket] of byPage) {\n this._strokeCacheByPage.set(key, this._convertAnnotations(bucket));\n }\n }\n\n // Resolve `this.strokes` from cache.\n if (page !== null) {\n this.strokes = this._strokeCacheByPage.get(page) || [];\n } else {\n // No page filter — flatten all cached pages.\n const all = [];\n for (const bucket of this._strokeCacheByPage.values()) {\n for (const stroke of bucket) all.push(stroke);\n }\n this.strokes = all;\n }\n }\n\n /**\n * Convert a list of annotations to stroke commands using the registered\n * converters. Unknown types are skipped with a warning.\n *\n * @private\n * @param {Array} annotations\n * @returns {Array} stroke command objects\n */\n _convertAnnotations(annotations) {\n return annotations.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 const style = this._resolveStyle(annotation);\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 if (this._strokeCacheByPage) this._strokeCacheByPage.clear();\n this._strokeCacheByPage = null;\n this._lastAnnotationsRef = null;\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 * Project a normalized stroke point into screen pixel space, honouring\n * the legacy `uniformScale` / `baseX/baseY` flags used by glyph strokes.\n *\n * Three cases:\n * - hasBase : `[coordX, coordY]` is an offset from `(baseX, baseY)` in\n * uniform-scaled space (both axes by viewport.height).\n * - uniform : `[coordX, coordY]` is a full position in uniform-scaled\n * space (legacy text glyph fallback).\n * - default : `[coordX, coordY]` is a normalized 0–1 page coordinate;\n * converted via `pointNormToAbs`.\n *\n * @private\n * @param {number} coordX\n * @param {number} coordY\n * @param {boolean} uniformScale\n * @param {boolean} hasBase\n * @param {number|null} baseX\n * @param {number|null} baseY\n * @returns {[number, number]}\n */\n _projectPoint(coordX, coordY, uniformScale, hasBase, baseX, baseY) {\n const { viewport } = this;\n if (hasBase) {\n return [\n baseX * viewport.width + coordX * viewport.height,\n baseY * viewport.height + coordY * viewport.height,\n ];\n }\n if (uniformScale) {\n return [coordX * viewport.height, coordY * viewport.height];\n }\n const { x, y } = pointNormToAbs({ x: coordX, y: coordY }, viewport);\n return [x, y];\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 // Screen-space wing strokes are recomputed every frame in pixel space so\n // diagonal arrows render symmetric wings regardless of viewport aspect.\n if (stroke.screenSpaceWing) {\n this._drawScreenSpaceWingStroke(stroke, progress);\n return;\n }\n\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 const hasBase = uniformScale && baseX !== null && baseY !== null;\n\n for (let i = 0; i < points.length; i++) {\n const [coordX, coordY] = points[i];\n const [px, py] = this._projectPoint(coordX, coordY, uniformScale, hasBase, baseX, baseY);\n if (i === 0) ctx.moveTo(px, py);\n else ctx.lineTo(px, py);\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 const hasBase = uniformScale && baseX !== null && baseY !== null;\n const scale = viewport.height / REFERENCE_HEIGHT;\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 const [px1, py1] = this._projectPoint(c1x, c1y, uniformScale, hasBase, baseX, baseY);\n const [px2, py2] = this._projectPoint(c2x, c2y, uniformScale, hasBase, baseX, baseY);\n\n // Average pressure between two points; clamp width to a min of 0.5px.\n const avgPressure = ((pressures[i - 1] || 1) + (pressures[i] || 1)) / 2;\n ctx.lineWidth = Math.max(0.5, baseWidth * avgPressure) * scale;\n\n ctx.beginPath();\n ctx.moveTo(px1, py1);\n ctx.lineTo(px2, py2);\n ctx.stroke();\n }\n }\n\n /**\n * Draw an arrow wing whose geometry is recomputed in screen-pixel space at\n * every frame, so diagonal arrows render with symmetric wings regardless of\n * viewport aspect ratio.\n *\n * Stroke shape (`stroke.screenSpaceWing`):\n * - fromX, fromY: tail of the main arrow line (normalized 0–1)\n * - toX, toY: tip of the main arrow line (normalized 0–1)\n * - headAngle: wing offset angle in radians (defaults to π/6)\n * - wingSide: 'left' uses (θ - headAngle); 'right' uses (θ + headAngle)\n *\n * The wing is drawn as a single straight line via the canvas API (no\n * RoughJS), since screen-space recomputation runs every render frame and\n * regenerating a rough path each frame would be too expensive.\n *\n * @private\n * @param {Object} stroke\n * @param {number} progress - 0..1 along the wing's full extent\n */\n _drawScreenSpaceWingStroke(stroke, progress) {\n const { ctx, viewport } = this;\n const wing = stroke.screenSpaceWing;\n if (!wing) return;\n\n const { fromX, fromY, toX, toY, wingSide } = wing;\n const headAngle =\n typeof wing.headAngle === \"number\" ? wing.headAngle : Math.PI / 6;\n\n if (\n typeof fromX !== \"number\" ||\n typeof fromY !== \"number\" ||\n typeof toX !== \"number\" ||\n typeof toY !== \"number\"\n ) {\n return;\n }\n\n // Convert main-line endpoints to screen pixels.\n const tip = pointNormToAbs({ x: toX, y: toY }, viewport);\n const tail = pointNormToAbs({ x: fromX, y: fromY }, viewport);\n const tipX = tip.x;\n const tipY = tip.y;\n const tailX = tail.x;\n const tailY = tail.y;\n\n const dx = tipX - tailX;\n const dy = tipY - tailY;\n const length = Math.sqrt(dx * dx + dy * dy);\n if (length === 0) return;\n\n // Same proportional rule as v0.6.4, but in screen pixels.\n const headSize = Math.min(length * 0.2, 0.03 * viewport.height);\n\n const theta = Math.atan2(dy, dx);\n const wingOffsetAngle =\n wingSide === \"right\" ? theta + headAngle : theta - headAngle;\n\n // Full wing endpoint (in screen pixels).\n const fullWingX = tipX - headSize * Math.cos(wingOffsetAngle);\n const fullWingY = tipY - headSize * Math.sin(wingOffsetAngle);\n\n // Apply progress: interpolate from the tip toward the full wing extent.\n const t = Math.max(0, Math.min(1, progress));\n const wingX = tipX + (fullWingX - tipX) * t;\n const wingY = tipY + (fullWingY - tipY) * t;\n\n const scale = viewport.height / REFERENCE_HEIGHT;\n ctx.strokeStyle = stroke.color || \"rgba(0, 0, 0, 0.5)\";\n ctx.lineCap = stroke.lineCap || \"round\";\n ctx.lineJoin = \"round\";\n ctx.lineWidth = (stroke.width || 2) * scale;\n\n ctx.beginPath();\n ctx.moveTo(tipX, tipY);\n ctx.lineTo(wingX, wingY);\n ctx.stroke();\n }\n}\n\nexport { StrokeRenderer };\nexport default StrokeRenderer;\n"],"names":["StrokeRenderer","canvas","config","type","ANNOTATION_TYPES","DEFAULT_CONFIG","highlightToStrokes","textToStrokes","underlineToStrokes","arrowToStrokes","circleToStrokes","inkToStrokes","globalOptions","typeOptions","key","value","converter","width","height","dpr","annotations","page","options","validate","noCache","source","normalizeAnnotationArray","filtered","a","byPage","ann","bucket","all","stroke","annotation","style","strokes","time","ctx","viewport","duration","elapsed","progress","p1","p2","t","coordX","coordY","uniformScale","hasBase","baseX","baseY","x","y","pointNormToAbs","pressures","lastCompleteIndex","segmentProgress","visiblePressures","points","color","lineCap","totalSegments","progressAlongPath","visiblePoints","scale","REFERENCE_HEIGHT","i","px","py","baseWidth","c1x","c1y","c2x","c2y","px1","py1","px2","py2","avgPressure","wing","fromX","fromY","toX","toY","wingSide","headAngle","tip","tail","tipX","tipY","tailX","tailY","dx","dy","length","headSize","theta","wingOffsetAngle","fullWingX","fullWingY","wingX","wingY"],"mappings":"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"}
|