goro-charts 1.0.0 → 1.3.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 +113 -0
- package/README.md +46 -19
- package/dist/charts/chart-base.d.ts +80 -17
- package/dist/data/series-store.d.ts +25 -2
- package/dist/defaults.d.ts +2 -2
- package/dist/goro-charts.js +229 -121
- package/dist/goro-charts.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +23 -4
- package/package.json +2 -1
package/dist/goro-charts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"goro-charts.js","names":[],"sources":["../src/defaults.ts","../src/data/monotonic-extent.ts","../src/data/ring-buffer.ts","../src/data/series-store.ts","../src/render/surface.ts","../src/math/ticks.ts","../src/math/format.ts","../src/math/scale.ts","../src/render/axes.ts","../src/render/shape.ts","../src/render/crosshair.ts","../src/render/legend.ts","../src/render/stacked-band.ts","../src/charts/chart-base.ts","../src/render/line.ts","../src/charts/line-chart.ts","../src/render/area.ts","../src/charts/area-chart.ts","../src/render/scatter.ts","../src/charts/scatter-chart.ts","../src/presets.ts"],"sourcesContent":["/**\n * @file Default options merged over any user-supplied {@link ChartOpts}.\n *\n * The top-level colour / width fields are per-series fallbacks — each series\n * config entry provides its own colour and optional overrides. `maxPoints: 0`\n * is a sentinel meaning \"snapshot mode\" (no ring); the constructor activates\n * ring mode only when the user passes a positive value.\n *\n * `yMin: 0 / yMax: 0` are sentinels meaning \"auto\" — the grid domain\n * expands from data unless the user sets non-zero values.\n */\n\nimport type { ResolvedOpts } from './types.ts';\n\nexport const CHART_DEFAULTS: ResolvedOpts = {\n series: [{ name: 'Series 0', color: '#4ea8ff', lineWidth: 1.5 }],\n padding: [16, 16, 32, 56],\n lineColor: '#4ea8ff',\n lineWidth: 1.5,\n fillColor: '#4ea8ff',\n fillOpacity: 0.15,\n pointColor: '#4ea8ff',\n gridColor: 'rgba(255,255,255,0.08)',\n axisColor: 'rgba(255,255,255,0.25)',\n textColor: 'rgba(255,255,255,0.5)',\n fontSize: 11,\n fontFamily: 'system-ui, -apple-system, sans-serif',\n crosshairColor: 'rgba(255,255,255,0.3)',\n crosshairWidth: 1,\n pointRadius: 4,\n bgColor: '#111',\n xTicks: 8,\n yTicks: 6,\n maxPoints: 0,\n autoDraw: false,\n yMin: 0,\n yMax: 0,\n maxDots: 2000,\n};\n","/**\n * @file Sliding-window min & max in O(1) amortized per push.\n *\n * Maintains two monotonic deques backed by circular Float64Arrays (no object\n * allocation): the min-deque is strictly increasing front→back, the max-deque\n * strictly decreasing, so each window extreme sits at its deque's front.\n *\n * Each push runs three steps in the canonical order — evict expired from\n * front, pop dominated from back, append — which is what guarantees a deque\n * never exceeds capacity. `seq` is a globally increasing sample id and\n * `windowStart` is the oldest seq still inside the window; an entry expires\n * once its seq < windowStart.\n */\n\n/** Incremental sliding-window extent tracker. */\nexport class MonotonicExtent {\n private cap: number;\n private vMin: Float64Array;\n private sMin: Float64Array;\n private hMin = 0;\n private nMin = 0;\n private vMax: Float64Array;\n private sMax: Float64Array;\n private hMax = 0;\n private nMax = 0;\n\n constructor(cap: number) {\n this.cap = cap;\n this.vMin = new Float64Array(cap);\n this.sMin = new Float64Array(cap);\n this.vMax = new Float64Array(cap);\n this.sMax = new Float64Array(cap);\n }\n\n /** Reset to empty (keeps allocated buffers). */\n clear(): void {\n this.hMin = this.nMin = this.hMax = this.nMax = 0;\n }\n\n /**\n * Record a new sample and slide the window forward.\n * @param value the sample value\n * @param seq globally increasing sample id\n * @param windowStart oldest seq still inside the window\n */\n push(value: number, seq: number, windowStart: number): void {\n const cap = this.cap;\n\n // Canonical order (evict → pop → append) keeps each deque length ≤ cap.\n const vMin = this.vMin;\n const sMin = this.sMin;\n while (this.nMin > 0 && sMin[this.hMin] < windowStart) {\n this.hMin = this.hMin + 1 === cap ? 0 : this.hMin + 1;\n this.nMin--;\n }\n while (this.nMin > 0 && vMin[(this.hMin + this.nMin - 1) % cap] >= value) {\n this.nMin--;\n }\n let ib = (this.hMin + this.nMin) % cap;\n vMin[ib] = value;\n sMin[ib] = seq;\n this.nMin++;\n\n const vMax = this.vMax;\n const sMax = this.sMax;\n while (this.nMax > 0 && sMax[this.hMax] < windowStart) {\n this.hMax = this.hMax + 1 === cap ? 0 : this.hMax + 1;\n this.nMax--;\n }\n while (this.nMax > 0 && vMax[(this.hMax + this.nMax - 1) % cap] <= value) {\n this.nMax--;\n }\n ib = (this.hMax + this.nMax) % cap;\n vMax[ib] = value;\n sMax[ib] = seq;\n this.nMax++;\n }\n\n /** Current window minimum. */\n get min(): number {\n return this.vMin[this.hMin];\n }\n /** Current window maximum. */\n get max(): number {\n return this.vMax[this.hMax];\n }\n}\n","/**\n * @file Fixed-capacity ring buffer of parallel (x, y) Float64Arrays.\n *\n * Append is O(1) with no memmove: once the window is full the oldest sample is\n * overwritten in place and `head` advances. y-extent is maintained\n * incrementally by a {@link MonotonicExtent}, so min/max are O(1) too.\n *\n * Logical order (oldest→newest) is `head, head+1, … head+count-1` (mod cap).\n * x is assumed monotonically increasing in push order, so it stays sorted in\n * logical order — which the crosshair's binary search relies on.\n */\n\nimport { MonotonicExtent } from './monotonic-extent.ts';\n\n/** Sliding-window columnar store for streaming (ring) mode. */\nexport class RingBuffer {\n cap: number;\n x: Float64Array;\n y: Float64Array;\n head = 0;\n count = 0;\n private seq = 0;\n private ext: MonotonicExtent;\n\n constructor(cap: number) {\n this.cap = cap;\n this.x = new Float64Array(cap);\n this.y = new Float64Array(cap);\n this.ext = new MonotonicExtent(cap);\n }\n\n /** Current window y minimum (O(1)). */\n get yMin(): number {\n return this.ext.min;\n }\n /** Current window y maximum (O(1)). */\n get yMax(): number {\n return this.ext.max;\n }\n /** x of the oldest sample. */\n get xFirst(): number {\n return this.x[this.head];\n }\n /** x of the newest sample. */\n get xLast(): number {\n const p = this.head + this.count - 1;\n return this.x[p >= this.cap ? p - this.cap : p];\n }\n /** y of the newest sample. */\n get lastY(): number {\n const p = this.head + this.count - 1;\n return this.y[p >= this.cap ? p - this.cap : p];\n }\n\n /** Map a logical index [0, count) to its physical slot. */\n physOf(logical: number): number {\n const p = this.head + logical;\n return p >= this.cap ? p - this.cap : p;\n }\n\n /** Append one sample. x must be ≥ the previous x (monotonic). */\n push(xv: number, yv: number): void {\n let phys: number;\n if (this.count < this.cap) {\n phys = this.head + this.count;\n if (phys >= this.cap) phys -= this.cap;\n this.count++;\n } else {\n phys = this.head;\n this.head = this.head + 1 === this.cap ? 0 : this.head + 1;\n }\n this.x[phys] = xv;\n this.y[phys] = yv;\n\n const s = this.seq++;\n this.ext.push(yv, s, s - this.count + 1);\n }\n\n /** Reset to empty (keeps allocated buffers). */\n clear(): void {\n this.head = 0;\n this.count = 0;\n this.seq = 0;\n this.ext.clear();\n }\n\n /**\n * Resize the window, preserving the most recent samples. Rare and O(keep):\n * the retained tail is copied out, fresh buffers allocated, then re-pushed so\n * the extent deque rebuilds correctly.\n */\n resize(newCap: number): void {\n if (newCap === this.cap || newCap < 1) return;\n\n const keep = Math.min(this.count, newCap);\n const startLogical = this.count - keep;\n const tx = new Float64Array(keep);\n const ty = new Float64Array(keep);\n for (let i = 0; i < keep; i++) {\n const p = this.physOf(startLogical + i);\n tx[i] = this.x[p];\n ty[i] = this.y[p];\n }\n\n this.cap = newCap;\n this.x = new Float64Array(newCap);\n this.y = new Float64Array(newCap);\n this.ext = new MonotonicExtent(newCap);\n this.head = 0;\n this.count = 0;\n this.seq = 0;\n for (let i = 0; i < keep; i++) this.push(tx[i], ty[i]);\n }\n}\n","/**\n * @file Unified data model behind both chart modes, implementing SeriesView.\n *\n * Snapshot mode (setData) holds caller-owned arrays directly with head=0.\n * Ring mode (append/appendBatch) delegates storage to a {@link RingBuffer} and\n * mirrors its O(1) cached geometry into local fields each tick. Either way the\n * store exposes the same logical window `[0, count)` plus `physOf` /\n * `bracketLogical` addressing, so renderers consume one stable contract.\n *\n * The store is purely about data: it never touches the canvas or scheduling.\n */\n\nimport { RingBuffer } from './ring-buffer.ts';\nimport type { SeriesView } from '../types.ts';\n\n/** Owns the series data and computes/caches its extents. */\nexport class SeriesStore implements SeriesView {\n xArr: Float64Array<ArrayBufferLike> = new Float64Array();\n yArr: Float64Array<ArrayBufferLike> = new Float64Array();\n head = 0;\n count = 0;\n cap = 0;\n xMin = 0;\n xMax = 0;\n yMin = 0;\n yMax = 0;\n\n private ring: RingBuffer | null = null;\n\n /** Whether ring (streaming) mode is active. */\n get isRing(): boolean {\n return this.ring !== null;\n }\n\n /**\n * Create the ring up front (used when constructed with maxPoints).\n * @throws {Error} if maxPoints < 1\n */\n initRing(maxPoints: number): void {\n if (maxPoints < 1) throw new Error('maxPoints must be >= 1');\n this.ring = new RingBuffer(maxPoints);\n this.bindRing();\n }\n\n /**\n * Snapshot mode: replace the whole series. Extents computed once in O(n).\n * Disables any active ring mode.\n * @throws {Error} if x and y have different lengths\n * @throws {Error} if x is empty\n */\n setData(x: Float64Array<ArrayBufferLike>, y: Float64Array<ArrayBufferLike>): void {\n if (x.length !== y.length) throw new Error('x and y must have same length');\n if (x.length === 0) throw new Error('data arrays must not be empty');\n\n this.ring = null;\n this.xArr = x;\n this.yArr = y;\n this.head = 0;\n this.count = x.length;\n this.cap = x.length;\n\n this.xMin = x[0];\n this.xMax = x[x.length - 1];\n\n let yMin = Infinity;\n let yMax = -Infinity;\n for (let i = 0; i < y.length; i++) {\n const v = y[i];\n if (v < yMin) yMin = v;\n if (v > yMax) yMax = v;\n }\n this.applyExtent(yMin, yMax);\n }\n\n /**\n * Ring mode: append one sample (x must be monotonically increasing).\n * @throws {Error} if ring mode is not active (maxPoints not set)\n */\n append(x: number, y: number): void {\n const r = this.requireRing('append');\n if (r.count > 0 && x < r.xLast) {\n console.warn(`[goro-charts] append x=${x} is < last x=${r.xLast}; x values must be monotonically increasing`);\n }\n r.push(x, y);\n this.syncFromRing();\n }\n\n /**\n * Ring mode: append a batch of parallel samples. O(k).\n * @throws {Error} if ring mode is not active\n * @throws {Error} if xs and ys have different lengths\n */\n appendBatch(xs: ArrayLike<number>, ys: ArrayLike<number>): void {\n const r = this.requireRing('appendBatch');\n if (xs.length !== ys.length) throw new Error('xs and ys must have same length');\n for (let i = 0; i < xs.length; i++) r.push(xs[i], ys[i]);\n this.syncFromRing();\n }\n\n /** Resize the streaming window, keeping the most recent samples. */\n setMaxPoints(maxPoints: number): void {\n if (maxPoints < 1) throw new Error('maxPoints must be >= 1');\n if (!this.ring) this.ring = new RingBuffer(maxPoints);\n else this.ring.resize(maxPoints);\n this.bindRing();\n this.syncFromRing();\n }\n\n /** Empty the current data (works in both modes). */\n clear(): void {\n if (this.ring) {\n this.ring.clear();\n this.syncFromRing();\n } else {\n this.head = this.count = this.cap = 0;\n this.xArr = new Float64Array();\n this.yArr = new Float64Array();\n }\n }\n\n /** Most recent y value, or NaN if empty. */\n get lastValue(): number {\n if (this.count === 0) return NaN;\n return this.yArr[this.physOf(this.count - 1)];\n }\n\n physOf(logical: number): number {\n const p = this.head + logical;\n return p >= this.cap ? p - this.cap : p;\n }\n\n bracketLogical(target: number): number {\n const n = this.count;\n let lo = 0;\n let hi = n - 1;\n if (target <= this.xArr[this.physOf(0)]) return 0;\n if (target >= this.xArr[this.physOf(hi)]) return hi;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (this.xArr[this.physOf(mid)] <= target) lo = mid;\n else hi = mid - 1;\n }\n return lo;\n }\n\n private requireRing(method: string): RingBuffer {\n if (!this.ring) {\n throw new Error(`${method}() requires the chart to be created with { maxPoints }`);\n }\n return this.ring;\n }\n\n /** Point the store's logical window at the ring's physical storage. */\n private bindRing(): void {\n const r = this.ring!;\n this.xArr = r.x;\n this.yArr = r.y;\n }\n\n /** Mirror the ring's O(1) cached geometry into the store's fields. */\n private syncFromRing(): void {\n const r = this.ring!;\n if (this.xArr !== r.x) this.bindRing();\n this.head = r.head;\n this.count = r.count;\n this.cap = r.cap;\n if (r.count > 0) {\n this.xMin = r.xFirst;\n this.xMax = r.xLast;\n this.applyExtent(r.yMin, r.yMax);\n }\n }\n\n /** Store y-extent, expanding a degenerate (flat) range so the line shows. */\n private applyExtent(yMin: number, yMax: number): void {\n if (yMax - yMin === 0) {\n this.yMin = yMin - 1;\n this.yMax = yMax + 1;\n } else {\n this.yMin = yMin;\n this.yMax = yMax;\n }\n }\n}\n","/**\n * @file Canvas surface: DPR handling, sizing, and the offscreen blit buffer.\n *\n * Owns the visible canvas plus an offscreen canvas that holds the static\n * content (grid, axes, and the series). Both contexts carry a devicePixelRatio transform\n * so all drawing happens in CSS-pixel coordinates. The static layer is painted\n * once to the offscreen buffer; `blit` copies it to the visible canvas 1:1 in\n * device pixels, leaving the crosshair to be overlaid on top — so cursor\n * movement never repaints the series.\n */\n\n/** Manages the visible canvas, its DPR transform, and the offscreen buffer. */\nexport class Surface {\n readonly canvas: HTMLCanvasElement;\n readonly ctx: CanvasRenderingContext2D;\n readonly dpr: number;\n\n cssW = 0;\n cssH = 0;\n\n private offCanvas: HTMLCanvasElement | null = null;\n private offCtx: CanvasRenderingContext2D | null = null;\n\n constructor(canvas: HTMLCanvasElement) {\n this.canvas = canvas;\n // Accessibility\n canvas.setAttribute('role', 'img');\n canvas.tabIndex = 0;\n\n this.dpr = window.devicePixelRatio || 1;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context not available');\n this.ctx = ctx;\n this.measure();\n }\n\n /**\n * Re-measure the canvas against its CSS box and resize the backing store.\n * @returns true if the size actually changed (caller should redraw)\n */\n measure(): boolean {\n const rect = this.canvas.getBoundingClientRect();\n const w = Math.floor(rect.width);\n const h = Math.floor(rect.height);\n\n // Ignore zero/negative sizes (display:none, detached, pre-layout).\n if (w <= 0 || h <= 0) return false;\n\n // Skip redundant ResizeObserver fires where nothing changed.\n const bw = w * this.dpr;\n const bh = h * this.dpr;\n if (this.cssW === w && this.cssH === h && this.canvas.width === bw) return false;\n\n this.cssW = w;\n this.cssH = h;\n this.canvas.width = bw;\n this.canvas.height = bh;\n this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.offCanvas = null;\n this.offCtx = null;\n return true;\n }\n\n /** Get the offscreen context (lazily created), ready in CSS-pixel coords. */\n offscreenCtx(): CanvasRenderingContext2D {\n const bw = this.canvas.width;\n const bh = this.canvas.height;\n if (this.offCanvas && this.offCanvas.width === bw && this.offCanvas.height === bh) {\n return this.offCtx!;\n }\n const off = document.createElement('canvas');\n off.width = bw;\n off.height = bh;\n const ctx = off.getContext('2d');\n if (!ctx) throw new Error('Offscreen 2D context not available');\n ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.offCanvas = off;\n this.offCtx = ctx;\n return ctx;\n }\n\n /** Copy the offscreen buffer onto the visible canvas, 1:1 in device pixels. */\n blit(): void {\n if (!this.offCanvas) return;\n this.ctx.setTransform(1, 0, 0, 1, 0, 0);\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.ctx.drawImage(this.offCanvas, 0, 0);\n this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n }\n\n /** Release the offscreen buffer references. */\n dispose(): void {\n this.offCanvas = null;\n this.offCtx = null;\n }\n}\n","/**\n * @file Axis tick generation.\n *\n * Produces \"nice\" round tick values for an axis given a data range and a\n * target tick count. Spacing is snapped to 1, 2, or 5 times a power of ten so\n * labels read cleanly (…, 10, 20, 50, 100, …) instead of arbitrary fractions.\n */\n\n/** Range guard: a zero-width range collapses to 1 so tick math stays finite. */\nfunction niceRange(min: number, max: number): number {\n const r = max - min;\n return r === 0 ? 1 : r;\n}\n\n/**\n * Snap a raw per-tick spacing to the nearest \"nice\" value (1, 2, 5 × 10^exp).\n * @param range total data range\n * @param maxTicks approximate desired number of ticks\n */\nfunction niceSpacing(range: number, maxTicks: number): number {\n const exp = Math.floor(Math.log10(range / maxTicks));\n const frac = range / maxTicks / Math.pow(10, exp);\n let nice: number;\n if (frac <= 1.5) nice = 1;\n else if (frac <= 3.5) nice = 2;\n else if (frac <= 7.5) nice = 5;\n else nice = 10;\n return nice * Math.pow(10, exp);\n}\n\n/**\n * Generate the list of tick values spanning [min, max] at a nice spacing.\n * Spacing is snapped to 1, 2, or 5 × 10ⁿ so labels read cleanly.\n * @param min - Lower bound of the data range\n * @param max - Upper bound of the data range\n * @param maxTicks - Approximate desired number of ticks (actual count varies)\n * @returns Array of tick values sorted ascending\n */\nexport function generateTicks(min: number, max: number, maxTicks: number): number[] {\n const spacing = niceSpacing(niceRange(min, max), maxTicks);\n const start = Math.ceil(min / spacing) * spacing;\n const end = Math.floor(max / spacing) * spacing;\n const ticks: number[] = [];\n for (let v = start; v <= end + spacing * 0.5; v += spacing) {\n ticks.push(v);\n }\n return ticks;\n}\n","/**\n * @file Numeric label formatting for axis ticks and the crosshair tooltip.\n *\n * Chooses a compact representation by magnitude: integers print without a\n * decimal, very large/small magnitudes switch to exponential, and the middle\n * band keeps a fixed two decimals (or three significant digits below 1).\n */\n\n/**\n * Format a number for display on an axis or in the tooltip.\n * Integers print without decimal; very large/small magnitudes switch to\n * exponential notation; values between 1e-4 and 1 use toPrecision(3);\n * everything else uses toFixed(2).\n * @param n - Number to format\n * @returns Formatted string suitable for axis labels and tooltips\n */\nexport function formatNumber(n: number): string {\n if (Number.isInteger(n)) return n.toFixed(0);\n const abs = Math.abs(n);\n if ((abs >= 1e6 || (abs <= 1e-4 && abs > 0)) && isFinite(n)) return n.toExponential(2);\n if (abs >= 1) return n.toFixed(2);\n return n.toPrecision(3);\n}\n","/**\n * @file Linear data-space ↔ pixel-space coordinate transforms.\n *\n * Maps a data value onto the plot rectangle and back. Used outside the line\n * hot loop (ticks, crosshair); the line renderer inlines the same arithmetic\n * for per-point speed. A zero-width range degenerates to the plot origin\n * rather than dividing by zero.\n */\n\nimport type { Domain, PlotRect } from '../types.ts';\n\n/**\n * Map a data x value to a horizontal pixel coordinate.\n * @param x - Data-space x value\n * @param d - Data domain (xMin, xMax)\n * @param plot - Plot rectangle in CSS pixels\n * @returns Pixel x-coordinate (CSS pixels)\n */\nexport function xToPx(x: number, d: Domain, plot: PlotRect): number {\n const range = d.xMax - d.xMin;\n if (range <= 0) return plot.x;\n return plot.x + ((x - d.xMin) / range) * plot.w;\n}\n\n/**\n * Map a data y value to a vertical pixel coordinate (y grows downward).\n * @param y - Data-space y value\n * @param d - Data domain (yMin, yMax)\n * @param plot - Plot rectangle in CSS pixels\n * @returns Pixel y-coordinate (CSS pixels)\n */\nexport function yToPx(y: number, d: Domain, plot: PlotRect): number {\n const range = d.yMax - d.yMin;\n if (range <= 0) return plot.y;\n return plot.y + (1 - (y - d.yMin) / range) * plot.h;\n}\n\n/**\n * Map a horizontal pixel coordinate back to a data x value.\n * @param px - Pixel x-coordinate (CSS pixels)\n * @param d - Data domain (xMin, xMax)\n * @param plot - Plot rectangle in CSS pixels\n * @returns Data-space x value\n */\nexport function pxToX(px: number, d: Domain, plot: PlotRect): number {\n const range = d.xMax - d.xMin;\n if (range <= 0) return d.xMin;\n return d.xMin + ((px - plot.x) / plot.w) * range;\n}\n","/**\n * @file Grid and tick labels.\n *\n * Pure renderers over a {@link Domain} and a {@link PlotRect}. The grid is a\n * dashed internal lattice plus an explicit rectangular frame around the plot —\n * the frame is an explicit `strokeRect` so it always closes into a box,\n * independent of tick placement. Tick labels sit outside the frame with no\n * extra axis strokes (the frame itself is the axis boundary).\n */\n\nimport type { Domain, PlotRect, ResolvedOpts } from '../types.ts';\nimport { generateTicks } from '../math/ticks.ts';\nimport { formatNumber } from '../math/format.ts';\nimport { xToPx, yToPx } from '../math/scale.ts';\n\n/** Draw the background grid (dashed internal lines + closed frame). */\nexport function renderGrid(ctx: CanvasRenderingContext2D, d: Domain, plot: PlotRect, opts: ResolvedOpts): void {\n ctx.setLineDash([6, 4]);\n\n const bottom = plot.y + plot.h;\n const right = plot.x + plot.w;\n\n ctx.strokeStyle = opts.gridColor;\n ctx.lineWidth = 0.5;\n\n // Dashed horizontal lines — skip if they land exactly on the top/bottom\n // boundary (the frame handles those edges).\n const yTicks = generateTicks(d.yMin, d.yMax, opts.yTicks);\n ctx.beginPath();\n for (const y of yTicks) {\n const py = yToPx(y, d, plot);\n if (py <= plot.y || py >= bottom) continue;\n ctx.moveTo(plot.x, py);\n ctx.lineTo(right, py);\n }\n ctx.stroke();\n\n // Dashed vertical lines — skip if they land on left/right boundary.\n const xTicks = generateTicks(d.xMin, d.xMax, opts.xTicks);\n ctx.beginPath();\n for (const x of xTicks) {\n const px = xToPx(x, d, plot);\n if (px <= plot.x || px >= right) continue;\n ctx.moveTo(px, plot.y);\n ctx.lineTo(px, bottom);\n }\n ctx.stroke();\n\n // The frame is a single explicit rectangle — it always closes, regardless\n // of where the ticks land. Slightly stronger than internal grid lines.\n ctx.strokeStyle = opts.axisColor;\n ctx.lineWidth = 0.8;\n ctx.strokeRect(plot.x, plot.y, plot.w, plot.h);\n\n ctx.setLineDash([]);\n}\n\n/** Draw axis tick labels (Y on the left or right, X below). No axis strokes — the grid frame is the boundary. */\nexport function renderAxes(\n ctx: CanvasRenderingContext2D,\n d: Domain,\n plot: PlotRect,\n opts: ResolvedOpts,\n side: 'left' | 'right' = 'left',\n): void {\n ctx.font = `${opts.fontSize}px ${opts.fontFamily}`;\n ctx.fillStyle = opts.textColor;\n\n const yTicks = generateTicks(d.yMin, d.yMax, opts.yTicks);\n ctx.textAlign = side === 'right' ? 'left' : 'right';\n ctx.textBaseline = 'middle';\n const ypx = side === 'right' ? plot.x + plot.w + 6 : plot.x - 6;\n for (const y of yTicks) {\n ctx.fillText(formatNumber(y), ypx, yToPx(y, d, plot));\n }\n\n // X labels only on the left side (they share the same x domain)\n if (side === 'left') {\n const xTicks = generateTicks(d.xMin, d.xMax, opts.xTicks);\n ctx.textAlign = 'center';\n ctx.textBaseline = 'top';\n for (const x of xTicks) {\n ctx.fillText(formatNumber(x), xToPx(x, d, plot), plot.y + plot.h + 6);\n }\n }\n}\n","/**\n * @file Small Canvas path helpers used by the renderers.\n */\n\n/** Trace a rounded rectangle path (clockwise). Does NOT call beginPath/stroke/fill. */\nexport function roundedRect(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number,\n r: number,\n): void {\n if (w <= 0 || h <= 0) return;\n const rr = Math.min(r, w / 2, h / 2);\n ctx.moveTo(x + rr, y);\n ctx.arcTo(x + w, y, x + w, y + h, rr);\n ctx.arcTo(x + w, y + h, x, y + h, rr);\n ctx.arcTo(x, y + h, x, y, rr);\n ctx.arcTo(x, y, x + w, y, rr);\n ctx.closePath();\n}\n","/**\n * @file Interactive crosshair with an interpolated, multi-series tooltip card.\n *\n * The cursor x maps to an exact data value. For each non-empty series, y is\n * linearly interpolated between the two samples bracketing the cursor, so the\n * readout slides smoothly and never jumps by whole samples even where many\n * points share a pixel column. The bracket search runs on logical indices (x\n * is sorted there) and translates to physical slots, correct under ring\n * wraparound.\n *\n * `computeHits` is exported so external code can build custom tooltips via the\n * {@link ChartBase.onHover} callback without re-implementing the interpolation\n * math. `renderCrosshair` uses it internally.\n */\n\nimport type { SeriesView, SeriesConfig, PlotRect, ResolvedOpts } from '../types.ts';\nimport { formatNumber } from '../math/format.ts';\nimport { xToPx, yToPx, pxToX } from '../math/scale.ts';\nimport { roundedRect } from './shape.ts';\n\n/** Interpolated sample for one series at the cursor position. */\nexport interface SeriesHit {\n px: number;\n py: number;\n xVal: number;\n yVal: number;\n color: string;\n /** Series display name (from {@link SeriesConfig.name}). */\n label: string;\n}\n\n/**\n * Compute interpolated hit data for every non-empty series at `cursorX`.\n * Called internally by {@link renderCrosshair} and available for the\n * `onHover` callback so consumers can build custom tooltips.\n */\nexport function computeHits(\n views: readonly SeriesView[],\n configs: readonly SeriesConfig[],\n plot: PlotRect,\n cursorX: number,\n): SeriesHit[] {\n const hits: SeriesHit[] = [];\n\n for (let s = 0; s < views.length; s++) {\n const view = views[s];\n const n = view.count;\n if (n === 0) continue;\n\n const cursorVal = pxToX(cursorX, view, plot);\n const loLogical = view.bracketLogical(cursorVal);\n const hiLogical = loLogical + 1 < n ? loLogical + 1 : n - 1;\n const lo = view.physOf(loLogical);\n const hi = view.physOf(hiLogical);\n\n const x0 = view.xArr[lo];\n const x1 = view.xArr[hi];\n const y0 = view.yArr[lo];\n const y1 = view.yArr[hi];\n let t = x1 > x0 ? (cursorVal - x0) / (x1 - x0) : 0;\n if (t < 0) t = 0;\n else if (t > 1) t = 1;\n const xVal = x0 + (x1 - x0) * t;\n const yVal = y0 + (y1 - y0) * t;\n\n hits.push({\n px: xToPx(xVal, view, plot),\n py: yToPx(yVal, view, plot),\n xVal,\n yVal,\n color: configs[s].color,\n label: configs[s].name,\n });\n }\n return hits;\n}\n\n/** Draw the crosshair lines, marker dots, and multi-series tooltip card. */\nexport function renderCrosshair(\n ctx: CanvasRenderingContext2D,\n views: readonly SeriesView[],\n configs: readonly SeriesConfig[],\n plot: PlotRect,\n opts: ResolvedOpts,\n cursor: { x: number; y: number },\n cssW: number,\n): void {\n if (cursor.x < plot.x || cursor.x > plot.x + plot.w) return;\n if (cursor.y < plot.y || cursor.y > plot.y + plot.h) return;\n\n const hits = computeHits(views, configs, plot, cursor.x);\n if (hits.length === 0) return;\n\n const guidePx = hits[0].px;\n const cursorX = hits[0].xVal;\n\n // Dashed vertical guide\n ctx.strokeStyle = opts.crosshairColor;\n ctx.lineWidth = opts.crosshairWidth;\n ctx.setLineDash([4, 3]);\n ctx.beginPath();\n ctx.moveTo(guidePx, plot.y);\n ctx.lineTo(guidePx, plot.y + plot.h);\n ctx.stroke();\n ctx.setLineDash([]);\n\n // Dashed horizontal guide — only with a single visible series\n if (hits.length === 1) {\n const h = hits[0];\n ctx.strokeStyle = opts.crosshairColor;\n ctx.lineWidth = opts.crosshairWidth;\n ctx.setLineDash([4, 3]);\n ctx.beginPath();\n ctx.moveTo(plot.x, h.py);\n ctx.lineTo(plot.x + plot.w, h.py);\n ctx.stroke();\n ctx.setLineDash([]);\n }\n\n // Marker dots with a dark halo behind so they read over filled areas\n for (const h of hits) {\n ctx.fillStyle = 'rgba(0,0,0,0.65)';\n ctx.beginPath();\n ctx.arc(h.px, h.py, opts.pointRadius + 1.5, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.fillStyle = h.color;\n ctx.beginPath();\n ctx.arc(h.px, h.py, opts.pointRadius, 0, Math.PI * 2);\n ctx.fill();\n }\n\n // ---- Tooltip card ----------------------------------------------------\n const fx = (n: number) => formatNumber(n);\n const nameFont = `${opts.fontSize}px ${opts.fontFamily}`;\n const valueFont = `600 ${opts.fontSize + 1}px ${opts.fontFamily}`;\n const pad = 10;\n const dotR = 4;\n const colGap = 8;\n const rowH = opts.fontSize + 6;\n const cardR = 6;\n\n ctx.font = nameFont;\n const nameW = Math.max(...hits.map((h) => ctx.measureText(h.label).width));\n\n ctx.font = valueFont;\n const valueW = Math.max(...hits.map((h) => ctx.measureText(fx(h.yVal)).width));\n\n ctx.font = nameFont;\n const xLabelW = ctx.measureText('x').width;\n ctx.font = valueFont;\n const xValW = ctx.measureText(fx(cursorX)).width;\n\n const col2W = Math.max(0, nameW, xLabelW);\n const col3W = Math.max(valueW, xValW);\n\n const cardW = pad * 2 + dotR * 2 + colGap + col2W + colGap + col3W;\n const headerH = rowH + 4;\n const dividerH = 2;\n const cardH = pad * 2 + headerH + dividerH + hits.length * rowH;\n\n let tx = guidePx + 14;\n if (tx + cardW > cssW) tx = Math.max(2, guidePx - cardW - 14);\n let ty = cursor.y - cardH - 10;\n if (ty < 2) ty = cursor.y + 10;\n // Avoid overflowing the plot area vertically\n if (ty + cardH > plot.y + plot.h) {\n ty = Math.max(2, cursor.y - cardH - 10);\n }\n\n ctx.fillStyle = 'rgba(10,12,14,0.70)';\n ctx.beginPath();\n roundedRect(ctx, tx, ty, cardW, cardH, cardR);\n ctx.fill();\n\n ctx.strokeStyle = 'rgba(255,255,255,0.08)';\n ctx.lineWidth = 0.8;\n ctx.beginPath();\n roundedRect(ctx, tx, ty, cardW, cardH, cardR);\n ctx.stroke();\n\n const xRowY = ty + pad;\n ctx.fillStyle = opts.textColor;\n ctx.globalAlpha = 0.5;\n ctx.font = nameFont;\n ctx.fillText('x', tx + pad + dotR * 2 + colGap, xRowY + rowH - 4);\n ctx.globalAlpha = 1;\n\n ctx.font = valueFont;\n ctx.textAlign = 'right';\n ctx.fillStyle = 'rgba(255,255,255,0.85)';\n ctx.fillText(fx(cursorX), tx + cardW - pad, xRowY + rowH - 4);\n\n const divY = ty + pad + headerH + 1;\n ctx.strokeStyle = 'rgba(255,255,255,0.07)';\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(tx + pad, divY);\n ctx.lineTo(tx + cardW - pad, divY);\n ctx.stroke();\n\n for (let i = 0; i < hits.length; i++) {\n const h = hits[i];\n const ry = ty + pad + headerH + dividerH + i * rowH;\n\n ctx.fillStyle = h.color;\n ctx.beginPath();\n ctx.arc(tx + pad + dotR, ry + rowH / 2, dotR, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.font = nameFont;\n ctx.textAlign = 'left';\n ctx.fillStyle = 'rgba(255,255,255,0.55)';\n ctx.fillText(h.label, tx + pad + dotR * 2 + colGap, ry + rowH - 4);\n\n ctx.font = valueFont;\n ctx.textAlign = 'right';\n ctx.fillStyle = 'rgba(255,255,255,0.90)';\n ctx.fillText(fx(h.yVal), tx + cardW - pad, ry + rowH - 4);\n }\n\n ctx.textAlign = 'left';\n ctx.globalAlpha = 1;\n}\n","/**\n * @file Legend drawn inside the plot area (top-right corner).\n *\n * Laid out as a single horizontal row of colour dots + series names inside a\n * rounded, semi-transparent pill. Falls back to vertical layout only when\n * the horizontal row would overflow the plot width. Rendered only with two or\n * more series — one series doesn't need a legend.\n */\n\nimport type { SeriesConfig, PlotRect, ResolvedOpts } from '../types.ts';\nimport { roundedRect } from './shape.ts';\n\nexport function renderLegend(\n ctx: CanvasRenderingContext2D,\n configs: readonly SeriesConfig[],\n plot: PlotRect,\n opts: ResolvedOpts,\n): void {\n if (configs.length < 2) return;\n\n const dotR = 4;\n const dotD = dotR * 2;\n const gap = 6;\n const itemGap = 16;\n const padX = 10;\n const padY = 7;\n const cardR = 6;\n\n ctx.font = `${opts.fontSize}px ${opts.fontFamily}`;\n\n // Measure each item width: dot + gap + text\n const items: { width: number; config: SeriesConfig }[] = configs.map((c) => ({\n config: c,\n width: dotD + gap + ctx.measureText(c.name).width,\n }));\n const totalW = items.reduce((s, it) => s + it.width + (s > 0 ? itemGap : 0), 0);\n const maxItemW = Math.max(...items.map((it) => it.width));\n\n // Decide horizontal vs vertical based on available plot width\n const availableW = plot.w - 16; // margin from edges\n const horizontal = totalW <= availableW;\n\n let cardW: number;\n let cardH: number;\n\n if (horizontal) {\n cardW = totalW + padX * 2;\n cardH = opts.fontSize + padY * 2;\n } else {\n cardW = maxItemW + padX * 2;\n cardH = items.length * (opts.fontSize + 6) + padY * 2;\n }\n\n const x = plot.x + plot.w - cardW - 8;\n const y = plot.y + 8;\n\n // Background\n ctx.fillStyle = 'rgba(10,12,14,0.70)';\n ctx.beginPath();\n roundedRect(ctx, x, y, cardW, cardH, cardR);\n ctx.fill();\n\n // Border\n ctx.strokeStyle = 'rgba(255,255,255,0.08)';\n ctx.lineWidth = 0.7;\n ctx.beginPath();\n roundedRect(ctx, x, y, cardW, cardH, cardR);\n ctx.stroke();\n\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n\n if (horizontal) {\n let cx = x + padX;\n for (let i = 0; i < items.length; i++) {\n if (i > 0) cx += itemGap;\n const it = items[i];\n const cy = y + cardH / 2;\n\n // Dot\n ctx.fillStyle = it.config.color;\n ctx.beginPath();\n ctx.arc(cx + dotR, cy, dotR, 0, Math.PI * 2);\n ctx.fill();\n\n // Name\n ctx.fillStyle = 'rgba(255,255,255,0.65)';\n ctx.fillText(it.config.name, cx + dotD + gap, cy);\n\n cx += it.width;\n }\n } else {\n ctx.textBaseline = 'top';\n for (let i = 0; i < items.length; i++) {\n const it = items[i];\n const iy = y + padY + i * (opts.fontSize + 6);\n\n ctx.fillStyle = it.config.color;\n ctx.beginPath();\n ctx.arc(x + padX + dotR, iy + opts.fontSize / 2, dotR, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.fillStyle = 'rgba(255,255,255,0.65)';\n ctx.fillText(it.config.name, x + padX + dotD + gap, iy);\n }\n }\n\n ctx.textAlign = 'left';\n ctx.textBaseline = 'top';\n}\n","/**\n * @file Stacked area bands for a group of series with the same `stack` id.\n *\n * Each layer renders as a polyline band between its own cumulative Y and the\n * previous layer's cumulative Y (or the plot bottom for the first layer). Fill\n * covers the band; the top edge is stroked separately.\n *\n * Two regimes, mirroring {@link renderLine} / {@link renderArea}:\n *\n * - **Dense** (n > 2·plotW): per-pixel-column decimation. Each pixel column\n * collapses to its min/max cumulative Y, so a full window of N points per\n * layer becomes ~2·plotW column samples — the visual envelope of the band —\n * instead of N line segments. This is what keeps a 10k-point stacked chart\n * at the same cost as a decimated area chart.\n *\n * - **Sparse** (n ≤ 2·plotW): the real cumulative polyline, point for point.\n *\n * Y values are clamped to the plot rect so a band never spills past the frame\n * even if the domain lags the accumulated values.\n *\n * The crosshair uses the (undecimated) cumulative arrays via proxy views built\n * by the orchestrator, so dots line up with band edges.\n */\n\nimport type { SeriesView, PlotRect, Domain } from '../types.ts';\n\ninterface LayerStyle {\n lineColor: string;\n lineWidth: number;\n fillColor: string;\n fillOpacity: number;\n}\n\n/**\n * Decimated column samples for one layer's cumulative edge. Parallel arrays:\n * `cx[k]` is the column centre x; `top[k]` / `bot[k]` are the min/max cumulative\n * Y pixels in that column (top = smaller y = higher value). Length ≤ ~plotW.\n */\ninterface Decimated {\n cx: number[];\n top: number[];\n bot: number[];\n}\n\n/**\n * Render stacked area bands for a group of series sharing one `stack` key.\n *\n * @param stores one SeriesView per layer, bottom to top (index 0 = first)\n * @param styles per-series fill/stroke configs\n * @param plot the plot rectangle in CSS pixels\n * @param domain shared data-space extents (left or right grid domain)\n */\nexport function renderStackedBands(\n ctx: CanvasRenderingContext2D,\n stores: readonly SeriesView[],\n styles: readonly LayerStyle[],\n plot: PlotRect,\n domain: Domain,\n): void {\n const n = stores[0].count;\n if (n === 0) return;\n\n const first = stores[0];\n const { xArr, cap } = first;\n\n const xRange = domain.xMax - domain.xMin;\n const yRange = domain.yMax - domain.yMin;\n const xScale = xRange > 0 ? plot.w / xRange : 0;\n const xOff = plot.x - domain.xMin * xScale;\n const yScale = yRange > 0 ? plot.h / yRange : 0;\n const yOff = plot.y + plot.h + domain.yMin * yScale;\n const bottomY = plot.y + plot.h;\n const topY = plot.y;\n\n // Clamp a data-space cumulative Y to a pixel inside the plot rect.\n const clampY = (yPx: number): number => (yPx < topY ? topY : yPx > bottomY ? bottomY : yPx);\n const py = (cumY: number): number => clampY(yOff - cumY * yScale);\n\n // Pre-compute cumulative Y for each layer (logical order).\n const cumYArr: Float64Array[] = [];\n const running = new Float64Array(n);\n for (let li = 0; li < stores.length; li++) {\n const s = stores[li];\n let p = s.head;\n let toWrap = s.cap - s.head;\n for (let j = 0; j < n; j++) {\n running[j] += s.yArr[p];\n if (--toWrap === 0) {\n p = 0;\n toWrap = s.cap;\n } else p++;\n }\n cumYArr.push(new Float64Array(running));\n }\n\n ctx.lineJoin = 'round';\n\n if (n > plot.w * 2) {\n renderDecimated(ctx, cumYArr, styles, xArr, first.head, cap, xOff, xScale, py, bottomY);\n } else {\n renderSparse(ctx, cumYArr, styles, xArr, first.head, cap, n, xOff, xScale, py, bottomY);\n }\n}\n\n/** Sparse regime: draw the real cumulative polyline for each layer. */\nfunction renderSparse(\n ctx: CanvasRenderingContext2D,\n cumYArr: Float64Array[],\n styles: readonly LayerStyle[],\n xArr: Float64Array<ArrayBufferLike>,\n head: number,\n cap: number,\n n: number,\n xOff: number,\n xScale: number,\n py: (cumY: number) => number,\n bottomY: number,\n): void {\n // Walk x in logical order once (shared across all layers).\n const xs = new Float64Array(n);\n let p = head;\n let toWrap = cap - head;\n for (let j = 0; j < n; j++) {\n xs[j] = xOff + xArr[p] * xScale;\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n\n for (let li = 0; li < cumYArr.length; li++) {\n const cumCurr = cumYArr[li];\n const style = styles[li];\n\n ctx.beginPath();\n ctx.fillStyle = style.fillColor;\n if (style.fillOpacity < 1) ctx.globalAlpha = style.fillOpacity;\n\n ctx.moveTo(xs[0], py(cumCurr[0]));\n for (let j = 1; j < n; j++) ctx.lineTo(xs[j], py(cumCurr[j]));\n\n if (li === 0) {\n ctx.lineTo(xs[n - 1], bottomY);\n ctx.lineTo(xs[0], bottomY);\n } else {\n const cumPrev = cumYArr[li - 1];\n for (let j = n - 1; j >= 0; j--) ctx.lineTo(xs[j], py(cumPrev[j]));\n }\n ctx.closePath();\n ctx.fill();\n ctx.globalAlpha = 1;\n\n ctx.beginPath();\n ctx.strokeStyle = style.lineColor;\n ctx.lineWidth = style.lineWidth;\n ctx.lineCap = 'round';\n ctx.moveTo(xs[0], py(cumCurr[0]));\n for (let j = 1; j < n; j++) ctx.lineTo(xs[j], py(cumCurr[j]));\n ctx.stroke();\n }\n}\n\n/**\n * Dense regime: collapse each layer's cumulative edge to per-pixel-column\n * min/max, then fill each band between its decimated edge and the previous\n * layer's decimated edge (or the plot bottom).\n */\nfunction renderDecimated(\n ctx: CanvasRenderingContext2D,\n cumYArr: Float64Array[],\n styles: readonly LayerStyle[],\n xArr: Float64Array<ArrayBufferLike>,\n head: number,\n cap: number,\n xOff: number,\n xScale: number,\n py: (cumY: number) => number,\n bottomY: number,\n): void {\n const n = cumYArr[0].length;\n\n // Pre-compute the column index for each logical sample once (shared x).\n const colOf = new Int32Array(n);\n {\n let p = head;\n let toWrap = cap - head;\n for (let j = 0; j < n; j++) {\n colOf[j] = (xOff + xArr[p] * xScale) | 0;\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n }\n\n // Decimate each layer into per-column min/max cumulative Y pixels.\n const layers: Decimated[] = cumYArr.map((cum) => {\n const cx: number[] = [];\n const top: number[] = [];\n const bot: number[] = [];\n let col = -1;\n let minY = 0;\n let maxY = 0;\n for (let j = 0; j < n; j++) {\n const c = colOf[j];\n const yPx = py(cum[j]);\n if (c !== col) {\n if (col >= 0) {\n cx.push(col + 0.5);\n top.push(minY);\n bot.push(maxY);\n }\n col = c;\n minY = maxY = yPx;\n } else {\n if (yPx < minY) minY = yPx;\n if (yPx > maxY) maxY = yPx;\n }\n }\n if (col >= 0) {\n cx.push(col + 0.5);\n top.push(minY);\n bot.push(maxY);\n }\n return { cx, top, bot };\n });\n\n for (let li = 0; li < layers.length; li++) {\n const cur = layers[li];\n const m = cur.cx.length;\n if (m === 0) continue;\n const style = styles[li];\n\n // Fill: forward along current edge (use bot = lower value edge for a solid\n // envelope), then close along previous layer's edge (or the plot bottom).\n ctx.beginPath();\n ctx.fillStyle = style.fillColor;\n if (style.fillOpacity < 1) ctx.globalAlpha = style.fillOpacity;\n\n ctx.moveTo(cur.cx[0], cur.top[0]);\n for (let k = 0; k < m; k++) {\n ctx.lineTo(cur.cx[k], cur.top[k]);\n ctx.lineTo(cur.cx[k], cur.bot[k]);\n }\n\n if (li === 0) {\n ctx.lineTo(cur.cx[m - 1], bottomY);\n ctx.lineTo(cur.cx[0], bottomY);\n } else {\n const prev = layers[li - 1];\n for (let k = prev.cx.length - 1; k >= 0; k--) {\n ctx.lineTo(prev.cx[k], prev.bot[k]);\n ctx.lineTo(prev.cx[k], prev.top[k]);\n }\n }\n ctx.closePath();\n ctx.fill();\n ctx.globalAlpha = 1;\n\n // Stroke: the top envelope of this layer only.\n ctx.beginPath();\n ctx.strokeStyle = style.lineColor;\n ctx.lineWidth = style.lineWidth;\n ctx.lineCap = 'round';\n ctx.moveTo(cur.cx[0], cur.top[0]);\n for (let k = 0; k < m; k++) {\n ctx.lineTo(cur.cx[k], cur.top[k]);\n ctx.lineTo(cur.cx[k], cur.bot[k]);\n }\n ctx.stroke();\n }\n}\n","/**\n * @file Abstract chart orchestrator shared by every chart type.\n *\n * Owns cross-cutting concerns: the data stores (one per series), the canvas\n * surface, the dirty flag and rAF coalescing, pointer interaction state, the\n * ResizeObserver, and the draw sequence (static layer → offscreen, blit,\n * crosshair overlay). All data lives in {@link SeriesStore} instances; all\n * pixels are produced by the `render/` functions.\n *\n * Multi-series: a {@link SeriesStore} is created for each\n * {@link SeriesConfig} entry. The series extent is computed as a union over\n * all stores so grid ticks span every visible series. Each store is rendered\n * independently by the subclass's {@link renderSeries}, receiving per-series\n * colour/style overrides merged from its config entry.\n *\n * Dual-Y: when any series opts into `yAxis: 'right'` the chart maintains\n * left and right grid domains independently. Left-axis series share the left\n * domain; right-axis series share the right. Tick labels appear on both sides\n * and the crosshair reads the correct axis per series.\n */\n\nimport { CHART_DEFAULTS } from '../defaults.ts';\nimport { SeriesStore } from '../data/series-store.ts';\nimport { Surface } from '../render/surface.ts';\nimport { renderGrid, renderAxes } from '../render/axes.ts';\nimport { renderCrosshair, computeHits } from '../render/crosshair.ts';\nimport { renderLegend } from '../render/legend.ts';\nimport { renderStackedBands } from '../render/stacked-band.ts';\nimport { formatNumber } from '../math/format.ts';\nimport type { ChartOpts, ResolvedOpts, SeriesConfig, SeriesView, PlotRect, Domain } from '../types.ts';\nimport type { SeriesHit } from '../render/crosshair.ts';\n\n/** Abstract base for all chart types. */\nexport abstract class ChartBase {\n protected opts: ResolvedOpts;\n protected surface: Surface;\n protected stores: SeriesStore[];\n protected seriesConfigs: SeriesConfig[];\n\n private gridDomainLeft: Domain = { xMin: 0, xMax: 0, yMin: 0, yMax: 0 };\n private gridDomainRight: Domain = { xMin: 0, xMax: 0, yMin: 0, yMax: 0 };\n private gridPinned = false;\n private hasRightAxis = false;\n\n /**\n * Cached stack-group detection. `seriesConfigs` is immutable after the\n * constructor (validateOpts is the last writer), so groups are computed\n * once and reused every draw instead of rebuilt each frame.\n */\n private stackGroupsAll!: { groups: Map<string, number[]>; stacked: Set<number> };\n private stackGroupsByAxis!: Record<'left' | 'right', { groups: Map<string, number[]>; stacked: Set<number> }>;\n\n private dirty = false;\n private cursorX = -1;\n private cursorY = -1;\n private showCrosshair = false;\n\n private autoDraw: boolean;\n private rafScheduled = 0;\n private suspendCount = 0;\n private syncTargets = new Set<ChartBase>();\n\n private resizeObserver: ResizeObserver | null = null;\n protected destroyed = false;\n private liveRegion: HTMLElement | null = null;\n private readonly handleResize = () => this.onResize();\n private readonly handleMouseMove = (e: MouseEvent) => this.onMouseMove(e);\n private readonly handleMouseLeave = () => this.onMouseLeave();\n private readonly handleKeyDown = (e: KeyboardEvent) => this.onKeyDown(e);\n\n constructor(canvas: HTMLCanvasElement, opts?: ChartOpts) {\n this.opts = { ...CHART_DEFAULTS, ...opts };\n this.autoDraw = this.opts.autoDraw;\n\n this.seriesConfigs =\n this.opts.series.length > 0 ? this.opts.series : [{ name: 'Series 0', color: this.opts.lineColor }];\n\n this.validateOpts();\n\n this.surface = new Surface(canvas);\n\n this.stores = this.seriesConfigs.map(() => new SeriesStore());\n this.hasRightAxis = this.seriesConfigs.some((c) => c.yAxis === 'right');\n\n // Detect stack groups once — seriesConfigs is frozen from here on.\n this.stackGroupsAll = this.computeAllStackGroups();\n this.stackGroupsByAxis = {\n left: this.computeStackGroupsOnAxis('left'),\n right: this.computeStackGroupsOnAxis('right'),\n };\n\n if (opts?.maxPoints != null && opts.maxPoints > 0) {\n for (const s of this.stores) s.initRing(opts.maxPoints);\n }\n\n if (this.opts.yMin !== 0 || this.opts.yMax !== 0) {\n this.gridDomainLeft.yMin = this.opts.yMin;\n this.gridDomainLeft.yMax = this.opts.yMax;\n this.gridDomainRight.yMin = this.opts.yMin;\n this.gridDomainRight.yMax = this.opts.yMax;\n this.gridPinned = true;\n }\n\n this.dirty = true;\n this.attachEvents();\n this.applySystemTheme();\n this.ensureLiveRegion();\n }\n\n /**\n * Validate and normalize constructor options.\n * Warns on suspicious values and clamps/repairs where possible.\n */\n private validateOpts(): void {\n if (this.opts.maxPoints < 0) {\n console.warn(`[goro-charts] maxPoints must be >= 0, got ${this.opts.maxPoints}. Using 0.`);\n this.opts.maxPoints = 0;\n }\n if (this.opts.fontSize < 6) {\n console.warn(`[goro-charts] fontSize ${this.opts.fontSize} is very small. Minimum recommended: 6.`);\n }\n const pad = this.opts.padding;\n if (pad.some((p) => p < 0)) {\n console.warn(`[goro-charts] padding values must be >= 0, got [${pad}]. Clamping to 0.`);\n this.opts.padding = pad.map((p) => Math.max(0, p)) as [number, number, number, number];\n }\n if (this.opts.yMin > 0 && this.opts.yMax > 0 && this.opts.yMin >= this.opts.yMax) {\n console.warn(`[goro-charts] yMin (${this.opts.yMin}) must be < yMax (${this.opts.yMax}). Swapping.`);\n [this.opts.yMin, this.opts.yMax] = [this.opts.yMax, this.opts.yMin];\n }\n // Validate series configs\n for (let i = 0; i < this.seriesConfigs.length; i++) {\n const s = this.seriesConfigs[i];\n if (!s.name || s.name.trim() === '') {\n console.warn(`[goro-charts] series[${i}] name is empty. Using \"Series ${i}\".`);\n this.seriesConfigs[i] = { ...s, name: `Series ${i}` };\n }\n if (s.yAxis && s.yAxis !== 'left' && s.yAxis !== 'right') {\n console.warn(`[goro-charts] series[${i}] has invalid yAxis \"${s.yAxis}\". Using \"left\".`);\n this.seriesConfigs[i] = { ...s, yAxis: 'left' };\n }\n }\n }\n\n /**\n * Detect system accessibility preferences and adjust visual styles.\n * - prefers-reduced-motion → disable rAF coalescing (synchronous draws)\n * - prefers-contrast: more → increase colour opacity\n * - forced-colors: active → use system CSS system colours\n */\n private applySystemTheme(): void {\n try {\n const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');\n if (reducedMotion.matches) this.autoDraw = false;\n\n const highContrast = window.matchMedia('(prefers-contrast: more)');\n if (highContrast.matches) {\n if (this.opts.gridColor === 'rgba(255,255,255,0.08)') this.opts.gridColor = 'rgba(255,255,255,0.25)';\n if (this.opts.textColor === 'rgba(255,255,255,0.5)') this.opts.textColor = 'rgba(255,255,255,0.8)';\n }\n\n const forcedColors = window.matchMedia('(forced-colors: active)');\n if (forcedColors.matches) {\n this.opts.textColor = 'CanvasText';\n this.opts.bgColor = 'Canvas';\n this.opts.gridColor = 'GrayText';\n this.opts.axisColor = 'GrayText';\n this.opts.crosshairColor = 'GrayText';\n }\n } catch {\n // matchMedia may not be available in all environments (SSR, jsdom)\n }\n }\n\n /**\n * Ensure an aria-live region exists for screen-reader announcements\n * of crosshair position values.\n */\n private ensureLiveRegion(): void {\n if (this.liveRegion) return;\n try {\n this.liveRegion = document.createElement('div');\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n this.liveRegion.style.cssText = 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;';\n this.surface.canvas.parentElement?.appendChild(this.liveRegion);\n } catch {\n // DOM may not be available in test environments\n }\n }\n\n /**\n * Update the canvas aria-label with a summary of visible data.\n * Called once per static redraw.\n */\n private updateAriaLabel(): void {\n const nonEmpty = this.stores\n .map((s, i) => ({ config: this.seriesConfigs[i], count: s.count, last: s.lastValue }))\n .filter((s) => s.count > 0);\n\n let label: string;\n if (nonEmpty.length === 0) {\n label = 'Chart: no data';\n } else {\n label = `Chart: ${nonEmpty\n .map((s) => `${s.config.name} ${formatNumber(s.last)}`)\n .join(', ')}`;\n }\n this.surface.canvas.setAttribute('aria-label', label);\n }\n\n /**\n * Handle keyboard navigation for the crosshair.\n * - ArrowLeft / ArrowRight: move crosshair by 1 point (Shift: 10 points)\n * - Escape: hide crosshair\n */\n private onKeyDown(e: KeyboardEvent): void {\n if (document.activeElement !== this.surface.canvas) return;\n\n const step = e.shiftKey ? 10 : 1;\n const plot = this.plotRect();\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.cursorX = Math.max(plot.x, this.cursorX - step);\n this.showCrosshair = true;\n this.draw();\n this.notifySyncCrosshair();\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.cursorX = Math.min(plot.x + plot.w, this.cursorX + step);\n this.showCrosshair = true;\n this.draw();\n this.notifySyncCrosshair();\n break;\n case 'Escape':\n e.preventDefault();\n this.showCrosshair = false;\n this.draw();\n this.notifySyncCrosshairLeave();\n break;\n }\n }\n\n /** Reusable crosshair-sync helpers used by both mouse and keyboard handlers. */\n private notifySyncCrosshair(): void {\n const rect = this.surface.canvas.getBoundingClientRect();\n const clientX = rect.left + this.cursorX;\n for (const target of this.syncTargets) target.injectCursor(clientX);\n }\n\n private notifySyncCrosshairLeave(): void {\n for (const target of this.syncTargets) target.injectCursorLeave();\n }\n\n /** Draw the series shape. Implemented by {@link LineChart} / {@link AreaChart}. */\n protected abstract renderSeries(\n ctx: CanvasRenderingContext2D,\n view: SeriesView,\n plot: PlotRect,\n opts: ResolvedOpts,\n ): void;\n\n // ---- Public data API -----------------------------------------------------\n\n /**\n * Snapshot mode: replace the series at `index` (O(n) extent).\n * @param index - Series index (0-based)\n * @param x - X values, must be monotonically increasing\n * @param y - Y values, must have the same length as x\n * @throws {Error} if `index` is out of range, or x/y length mismatch\n */\n setData(index: number, x: Float64Array<ArrayBufferLike>, y: Float64Array<ArrayBufferLike>): void {\n if (this.destroyed) return;\n this.storeAt(index).setData(x, y);\n this.gridPinned = false;\n this.invalidate();\n }\n\n /**\n * Ring mode: append one sample to series `index`.\n * @param index - Series index (0-based)\n * @param x - X value (must be monotonically increasing)\n * @param y - Y value\n * @throws {Error} if ring mode is not active (maxPoints not set)\n * @throws {Error} if `index` is out of range\n */\n append(index: number, x: number, y: number): void {\n if (this.destroyed) return;\n this.storeAt(index).append(x, y);\n this.invalidate();\n }\n\n /**\n * Ring mode: append a batch of samples to series `index`.\n * @param index - Series index (0-based)\n * @param xs - X values (must be monotonically increasing)\n * @param ys - Y values, must have the same length as xs\n * @throws {Error} if ring mode is not active\n * @throws {Error} if `index` is out of range or xs/ys length mismatch\n */\n appendBatch(index: number, xs: ArrayLike<number>, ys: ArrayLike<number>): void {\n if (this.destroyed) return;\n this.storeAt(index).appendBatch(xs, ys);\n this.invalidate();\n }\n\n /**\n * Resize the streaming window (applies to all series).\n * Preserves the most recent samples in each series.\n * @param maxPoints - New window size (must be >= 1)\n */\n setMaxPoints(maxPoints: number): void {\n if (this.destroyed) return;\n for (const s of this.stores) s.setMaxPoints(maxPoints);\n this.invalidate();\n }\n\n /** Empty all series and reset grid domain. */\n clear(): void {\n if (this.destroyed) return;\n for (const s of this.stores) s.clear();\n this.gridPinned = false;\n this.invalidate();\n }\n\n /** Number of series configured. */\n get seriesCount(): number {\n if (this.destroyed) return 0;\n return this.stores.length;\n }\n\n /**\n * Total points rendered across all series in the last draw.\n * Useful for debug/performance monitoring (e.g. to verify decimation is active).\n */\n get renderedPointCount(): number {\n if (this.destroyed) return 0;\n return this.stores.reduce((sum, s) => sum + s.count, 0);\n }\n\n /**\n * Number of points currently in the window for series `index`.\n * @param index - Series index (0-based)\n */\n pointCount(index: number): number {\n if (this.destroyed) return 0;\n return this.stores[index].count;\n }\n /**\n * Current window y minimum for series `index` (O(1)).\n * @param index - Series index (0-based)\n */\n extentMin(index: number): number {\n if (this.destroyed) return NaN;\n return this.stores[index].yMin;\n }\n /**\n * Current window y maximum for series `index` (O(1)).\n * @param index - Series index (0-based)\n */\n extentMax(index: number): number {\n if (this.destroyed) return NaN;\n return this.stores[index].yMax;\n }\n /**\n * Most recent y value for series `index`, or NaN if empty.\n * @param index - Series index (0-based)\n */\n lastValue(index: number): number {\n if (this.destroyed) return NaN;\n return this.stores[index].lastValue;\n }\n\n /**\n * Pause rAF-coalesced drawing. Nestable — call {@link resumeDraw} the\n * same number of times to re-enable. Useful for bulk-loading data without\n * intermediate paints.\n */\n suspendDraw(): void {\n if (this.destroyed) return;\n this.suspendCount++;\n }\n /** Resume drawing after a matching {@link suspendDraw}. Draws immediately if dirty. */\n resumeDraw(): void {\n if (this.destroyed) return;\n if (this.suspendCount > 0) this.suspendCount--;\n if (this.suspendCount === 0 && this.dirty) this.invalidate();\n }\n\n /**\n * Export the current canvas as a PNG data URL.\n * @returns A `data:image/png` URL string\n */\n toImage(): string {\n if (this.destroyed) return '';\n return this.surface.canvas.toDataURL('image/png');\n }\n\n /**\n * Bidirectionally sync crosshair position with `other`. When the mouse\n * moves on one chart, crosshair overlay is injected on all synced charts\n * at the matching x coordinate.\n * @param other - Another chart instance to sync with\n */\n sync(other: ChartBase): void {\n if (this.destroyed) return;\n this.syncTargets.add(other);\n other.syncTargets.add(this);\n }\n\n /**\n * External callback for hover events. Called on `mousemove` with the\n * interpolated data for every visible series. Use to build custom DOM\n * tooltips or bind to framework state — the Canvas tooltip still draws\n * unless suppressed externally.\n */\n onHover?: (hits: SeriesHit[]) => void;\n\n // ---- Rendering -----------------------------------------------------------\n\n /** Paint the chart. Cheap to call repeatedly: returns early when clean. */\n draw(): void {\n if (this.destroyed) return;\n if (!this.dirty && !this.showCrosshair) return;\n const { cssW, cssH } = this.surface;\n if (cssW <= 0 || cssH <= 0) return;\n\n const plot = this.plotRect();\n if (plot.w <= 0 || plot.h <= 0) return;\n\n if (this.dirty) {\n this.renderStatic(plot);\n this.updateAriaLabel();\n }\n\n this.surface.blit();\n\n if (this.showCrosshair) {\n // Detect stacked groups so we can use accumulated Y (matching the band\n // edges) for crosshair dot positions.\n const { groups: stackGroups } = this.detectAllStackGroups();\n\n // Pre-compute accumulated crosshair views — for stacked series we\n // replace yArr with the cumulative Y so dots sit on band edges.\n const stackedSurrogates = new Map<number, Float64Array>();\n for (const [, grp] of stackGroups) {\n if (grp.length < 2) continue;\n const cum = this.accumulateStackGroup(grp);\n if (!cum) continue;\n for (const idx of grp) {\n stackedSurrogates.set(idx, new Float64Array(cum));\n }\n }\n\n const crosshairViews: SeriesView[] = this.stores.map((store, i) => {\n const cfg = this.seriesConfigs[i];\n const dom = (cfg.yAxis ?? 'left') === 'right' ? this.gridDomainRight : this.gridDomainLeft;\n let viewYMin = dom.yMin;\n let viewYMax = dom.yMax;\n if (cfg.yMin != null) viewYMin = cfg.yMin;\n if (cfg.yMax != null) viewYMax = cfg.yMax;\n const accY = stackedSurrogates.get(i);\n return Object.assign(Object.create(Object.getPrototypeOf(store)), store, {\n xMin: dom.xMin,\n xMax: dom.xMax,\n yMin: viewYMin,\n yMax: viewYMax,\n ...(accY ? { yArr: accY } : {}),\n });\n });\n\n if (this.onHover || this.liveRegion) {\n const hits = computeHits(crosshairViews, this.seriesConfigs, plot, this.cursorX);\n if (this.onHover && hits.length > 0) this.onHover(hits);\n if (this.liveRegion) {\n this.liveRegion.textContent = hits.length > 0\n ? hits.map((h) => `${h.label}: ${formatNumber(h.yVal)}`).join(', ')\n : '';\n }\n }\n\n renderCrosshair(\n this.surface.ctx,\n crosshairViews,\n this.seriesConfigs,\n plot,\n this.opts,\n { x: this.cursorX, y: this.cursorY },\n cssW,\n );\n }\n\n if (!this.showCrosshair && this.liveRegion) {\n this.liveRegion.textContent = '';\n }\n\n this.dirty = false;\n }\n\n /** Detach observers/listeners and release buffers. Idempotent. */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n if (this.rafScheduled) {\n cancelAnimationFrame(this.rafScheduled);\n this.rafScheduled = 0;\n }\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.surface.canvas.removeEventListener('mousemove', this.handleMouseMove);\n this.surface.canvas.removeEventListener('mouseleave', this.handleMouseLeave);\n this.surface.canvas.removeEventListener('keydown', this.handleKeyDown);\n this.liveRegion?.remove();\n this.liveRegion = null;\n this.surface.dispose();\n this.stores = [];\n }\n\n // ---- Internals -----------------------------------------------------------\n\n /** Render the static layer (background, grid, axes, series, legend) to offscreen. */\n private renderStatic(plot: PlotRect): void {\n const ctx = this.surface.offscreenCtx();\n const { cssW, cssH } = this.surface;\n ctx.clearRect(0, 0, cssW, cssH);\n ctx.fillStyle = this.opts.bgColor;\n ctx.fillRect(0, 0, cssW, cssH);\n\n if (this.stores.every((s) => s.count === 0)) return;\n\n this.updateGridDomain();\n\n renderGrid(ctx, this.gridDomainLeft, plot, this.opts);\n renderAxes(ctx, this.gridDomainLeft, plot, this.opts);\n if (this.hasRightAxis) {\n renderAxes(ctx, this.gridDomainRight, plot, this.opts, 'right');\n }\n\n // Detect stack groups.\n const { groups: stackGroups, stacked: stackedIndices } = this.detectAllStackGroups();\n\n // Non-stacked series.\n for (let i = 0; i < this.stores.length; i++) {\n if (stackedIndices.has(i)) continue;\n const store = this.stores[i];\n if (store.count === 0) continue;\n this.renderOne(ctx, i, store, store.yArr, plot);\n }\n\n // Stacked groups: build stores + styles arrays, delegate to band renderer.\n for (const [, grp] of stackGroups) {\n if (grp.length < 2) {\n for (const idx of grp) {\n const s = this.stores[idx];\n if (s.count > 0) this.renderOne(ctx, idx, s, s.yArr, plot);\n }\n continue;\n }\n const bandStores: SeriesView[] = [];\n const bandStyles: { lineColor: string; lineWidth: number; fillColor: string; fillOpacity: number }[] = [];\n for (const idx of grp) {\n const s = this.stores[idx];\n if (s.count === 0) continue;\n const cfg = this.seriesConfigs[idx];\n bandStores.push(s);\n bandStyles.push({\n lineColor: cfg.color,\n lineWidth: cfg.lineWidth ?? this.opts.lineWidth,\n fillColor: cfg.fillColor ?? this.opts.fillColor,\n fillOpacity: cfg.fillOpacity ?? this.opts.fillOpacity,\n });\n }\n if (bandStores.length < 2) {\n for (let i = 0; i < bandStores.length; i++) {\n this.renderOne(ctx, grp[i], bandStores[i], bandStores[i].yArr, plot);\n }\n continue;\n }\n const dom = (this.seriesConfigs[grp[0]].yAxis ?? 'left') === 'right' ? this.gridDomainRight : this.gridDomainLeft;\n renderStackedBands(ctx, bandStores, bandStyles, plot, dom);\n }\n\n renderLegend(ctx, this.seriesConfigs, plot, this.opts);\n }\n\n /** Render a single series. */\n private renderOne(\n ctx: CanvasRenderingContext2D,\n i: number,\n store: SeriesView,\n yArr: Float64Array<ArrayBufferLike>,\n plot: PlotRect,\n ): void {\n const cfg = this.seriesConfigs[i];\n const sOpts: ResolvedOpts = {\n ...this.opts,\n lineColor: cfg.color,\n lineWidth: cfg.lineWidth ?? this.opts.lineWidth,\n fillColor: cfg.fillColor ?? this.opts.fillColor,\n fillOpacity: cfg.fillOpacity ?? this.opts.fillOpacity,\n };\n\n if (cfg.dash) ctx.setLineDash(cfg.dash);\n\n const dom = (cfg.yAxis ?? 'left') === 'right' ? this.gridDomainRight : this.gridDomainLeft;\n let viewYMin = dom.yMin;\n let viewYMax = dom.yMax;\n if (cfg.yMin != null) viewYMin = cfg.yMin;\n if (cfg.yMax != null) viewYMax = cfg.yMax;\n\n const proxyView: SeriesView = Object.assign(Object.create(Object.getPrototypeOf(store)), store, {\n xMin: dom.xMin,\n xMax: dom.xMax,\n yArr,\n yMin: viewYMin,\n yMax: viewYMax,\n });\n\n this.renderSeries(ctx, proxyView, plot, sOpts);\n\n if (cfg.dash) ctx.setLineDash([]);\n }\n\n /** Cached per-axis stack groups (computed once in the constructor). */\n private detectStackGroupsOnAxis(\n axis: 'left' | 'right',\n ): { groups: Map<string, number[]>; stacked: Set<number> } {\n return this.stackGroupsByAxis[axis];\n }\n\n /**\n * Compute stack groups for a given axis. Returns the group map and a set\n * of series indices that belong to groups with 2+ members.\n */\n private computeStackGroupsOnAxis(\n axis: 'left' | 'right',\n ): { groups: Map<string, number[]>; stacked: Set<number> } {\n const groups = new Map<string, number[]>();\n const stacked = new Set<number>();\n for (let i = 0; i < this.stores.length; i++) {\n if ((this.seriesConfigs[i].yAxis ?? 'left') !== axis) continue;\n const g = this.seriesConfigs[i].stack;\n if (g) {\n let grp = groups.get(g);\n if (!grp) { grp = []; groups.set(g, grp); }\n grp.push(i);\n }\n }\n for (const grp of groups.values()) {\n if (grp.length >= 2) for (const idx of grp) stacked.add(idx);\n }\n return { groups, stacked };\n }\n\n /** Cached all-axis stack groups (computed once in the constructor). */\n private detectAllStackGroups(): {\n groups: Map<string, number[]>;\n stacked: Set<number>;\n } {\n return this.stackGroupsAll;\n }\n\n /**\n * Compute all stack groups across all axes.\n * Unlike {@link computeStackGroupsOnAxis}, this ignores the yAxis setting\n * and returns every group that has 2+ members.\n */\n private computeAllStackGroups(): {\n groups: Map<string, number[]>;\n stacked: Set<number>;\n } {\n const groups = new Map<string, number[]>();\n const stacked = new Set<number>();\n for (let i = 0; i < this.stores.length; i++) {\n const g = this.seriesConfigs[i].stack;\n if (g) {\n let grp = groups.get(g);\n if (!grp) { grp = []; groups.set(g, grp); }\n grp.push(i);\n }\n }\n for (const grp of groups.values()) {\n if (grp.length >= 2) for (const idx of grp) stacked.add(idx);\n }\n return { groups, stacked };\n }\n\n /**\n * Compute accumulated Y values across a stack group.\n * Returns the cumulative Y array (length = n), or null if all stores are empty.\n */\n private accumulateStackGroup(indices: number[]): Float64Array | null {\n const first = this.stores[indices[0]];\n const n = first.count;\n if (n === 0) return null;\n const running = new Float64Array(n);\n for (const idx of indices) {\n const s = this.stores[idx];\n let p = s.head;\n let toWrap = s.cap - s.head;\n for (let j = 0; j < n; j++) {\n running[j] += s.yArr[p];\n if (--toWrap === 0) { p = 0; toWrap = s.cap; }\n else p++;\n }\n }\n return running;\n }\n\n /**\n * Update the grid Y domain.\n *\n * Two regimes:\n * - **Snapshot mode:** anchor the grid so it only expands, never shrinks.\n * The domain snaps to the union extent on the first draw and stays frozen\n * until a series exceeds it, then expands with a 10 % margin — a stable\n * visual anchor for static data.\n * - **Ring (streaming) mode:** the window slides, so the true extent both\n * grows and shrinks as samples enter and leave. Here we recompute the\n * domain from the current window every tick (via {@link initDomain}) so\n * the grid tracks the visible data instead of drifting or letting stacked\n * bands overflow the frame.\n *\n * If the user supplied `yMin` / `yMax` those are hard bounds that override\n * the auto logic in either regime.\n */\n private updateGridDomain(): void {\n if (this.stores.every((s) => s.count === 0)) return;\n\n const userYMin = this.opts.yMin;\n const userYMax = this.opts.yMax;\n const fixedY = userYMin !== 0 || userYMax !== 0;\n const streaming = this.stores.some((s) => s.isRing);\n\n const applyUserBounds = () => {\n if (!fixedY) return;\n if (userYMin !== 0) {\n this.gridDomainLeft.yMin = userYMin;\n this.gridDomainRight.yMin = userYMin;\n }\n if (userYMax !== 0) {\n this.gridDomainLeft.yMax = userYMax;\n this.gridDomainRight.yMax = userYMax;\n }\n };\n\n // Streaming: recompute the window extent every tick so the grid tracks the\n // sliding window (shrinks as well as grows). Fixed bounds still win. A\n // small margin keeps peaks/troughs off the frame edge.\n if (streaming && !fixedY) {\n this.initDomain(this.gridDomainLeft, 'left', 0.05);\n if (this.hasRightAxis) this.initDomain(this.gridDomainRight, 'right', 0.05);\n this.refreshXDomain();\n this.gridPinned = true;\n return;\n }\n\n if (!this.gridPinned) {\n // First draw: compute union per axis from data, respecting any user bounds.\n this.initDomain(this.gridDomainLeft, 'left');\n if (this.hasRightAxis) this.initDomain(this.gridDomainRight, 'right');\n applyUserBounds();\n this.refreshXDomain();\n this.gridPinned = true;\n return;\n }\n\n // Refresh X every tick; Y expands only when data exceeds bounds.\n this.refreshXDomain();\n if (fixedY) return; // hard-locked — no expansion\n this.expandDomain(this.gridDomainLeft, 'left');\n if (this.hasRightAxis) this.expandDomain(this.gridDomainRight, 'right');\n }\n\n /**\n * Compute the domain for one axis from the current window.\n * @param margin optional fraction of the Y range to pad on each side\n * (used in streaming mode so peaks/troughs don't touch the frame).\n */\n private initDomain(d: Domain, axis: 'left' | 'right', margin = 0): void {\n d.xMin = Infinity;\n d.xMax = -Infinity;\n d.yMin = Infinity;\n d.yMax = -Infinity;\n\n const { groups, stacked } = this.detectStackGroupsOnAxis(axis);\n\n // Stacked groups: compute extent from accumulated Y.\n for (const [, grp] of groups) {\n if (grp.length < 2) continue;\n const cum = this.accumulateStackGroup(grp);\n if (!cum) continue;\n let yMin = Infinity;\n let yMax = -Infinity;\n // Also collect x extents from the group\n for (const idx of grp) {\n const s = this.stores[idx];\n if (s.xMin < d.xMin) d.xMin = s.xMin;\n if (s.xMax > d.xMax) d.xMax = s.xMax;\n }\n for (let j = 0; j < cum.length; j++) {\n if (cum[j] < yMin) yMin = cum[j];\n if (cum[j] > yMax) yMax = cum[j];\n }\n if (yMin < d.yMin) d.yMin = yMin;\n if (yMax > d.yMax) d.yMax = yMax;\n }\n\n // Non-stacked series on this axis.\n for (let i = 0; i < this.stores.length; i++) {\n if (stacked.has(i)) continue;\n const s = this.stores[i];\n if (s.count === 0) continue;\n if ((this.seriesConfigs[i].yAxis ?? 'left') !== axis) continue;\n if (s.xMin < d.xMin) d.xMin = s.xMin;\n if (s.xMax > d.xMax) d.xMax = s.xMax;\n if (s.yMin < d.yMin) d.yMin = s.yMin;\n if (s.yMax > d.yMax) d.yMax = s.yMax;\n }\n\n // Optional breathing room so extremes don't sit on the frame edge.\n if (margin > 0 && d.yMax > d.yMin) {\n const pad = (d.yMax - d.yMin) * margin;\n d.yMin -= pad;\n d.yMax += pad;\n }\n }\n\n private expandDomain(d: Domain, axis: 'left' | 'right'): void {\n const yr = d.yMax - d.yMin || 1;\n const { groups, stacked } = this.detectStackGroupsOnAxis(axis);\n let changed = false;\n\n // Stacked groups: expand if accumulated Y exceeds domain.\n for (const [, grp] of groups) {\n if (grp.length < 2) continue;\n const cum = this.accumulateStackGroup(grp);\n if (!cum) continue;\n let accMin = Infinity;\n let accMax = -Infinity;\n for (let j = 0; j < cum.length; j++) {\n if (cum[j] < accMin) accMin = cum[j];\n if (cum[j] > accMax) accMax = cum[j];\n }\n if (accMin < d.yMin) { d.yMin = accMin - yr * 0.1; changed = true; }\n if (accMax > d.yMax) { d.yMax = accMax + yr * 0.1; changed = true; }\n }\n\n // Non-stacked series on this axis.\n for (let i = 0; i < this.stores.length; i++) {\n if (stacked.has(i)) continue;\n const s = this.stores[i];\n if (s.count === 0) continue;\n if ((this.seriesConfigs[i].yAxis ?? 'left') !== axis) continue;\n if (s.yMin < d.yMin) { d.yMin = s.yMin - yr * 0.1; changed = true; }\n if (s.yMax > d.yMax) { d.yMax = s.yMax + yr * 0.1; changed = true; }\n }\n\n if (changed) {\n d.xMin = Infinity;\n d.xMax = -Infinity;\n for (let i = 0; i < this.stores.length; i++) {\n const s = this.stores[i];\n if (s.count === 0) continue;\n if ((this.seriesConfigs[i].yAxis ?? 'left') !== axis) continue;\n if (s.xMin < d.xMin) d.xMin = s.xMin;\n if (s.xMax > d.xMax) d.xMax = s.xMax;\n }\n }\n }\n\n /**\n * Recompute the shared X domain from all non-empty series so grid ticks\n * and renderers always map fresh X positions. Called every draw.\n */\n private refreshXDomain(): void {\n let xMin = Infinity;\n let xMax = -Infinity;\n for (const s of this.stores) {\n if (s.count === 0) continue;\n if (s.xMin < xMin) xMin = s.xMin;\n if (s.xMax > xMax) xMax = s.xMax;\n }\n if (xMin <= xMax) {\n this.gridDomainLeft.xMin = xMin;\n this.gridDomainLeft.xMax = xMax;\n this.gridDomainRight.xMin = xMin;\n this.gridDomainRight.xMax = xMax;\n }\n }\n\n /** Compute the plot rectangle from canvas size minus padding. */\n private plotRect(): PlotRect {\n const [pt, pr, pb, pl] = this.opts.padding;\n return {\n x: pl,\n y: pt,\n w: this.surface.cssW - pl - pr,\n h: this.surface.cssH - pt - pb,\n };\n }\n\n /** Mark dirty and schedule/perform a draw per the autoDraw policy. */\n private invalidate(): void {\n this.dirty = true;\n if (!this.autoDraw || this.suspendCount > 0) return;\n if (this.rafScheduled) return;\n this.rafScheduled = requestAnimationFrame(() => {\n this.rafScheduled = 0;\n this.draw();\n });\n }\n\n private storeAt(index: number): SeriesStore {\n const s = this.stores[index];\n if (!s) throw new Error(`series index ${index} out of range (${this.stores.length} series)`);\n return s;\n }\n\n private injectCursor(clientX: number): void {\n const rect = this.surface.canvas.getBoundingClientRect();\n this.cursorX = clientX - rect.left;\n this.showCrosshair = true;\n this.draw();\n }\n\n private injectCursorLeave(): void {\n this.showCrosshair = false;\n this.draw();\n }\n\n private attachEvents(): void {\n this.resizeObserver = new ResizeObserver(this.handleResize);\n this.resizeObserver.observe(this.surface.canvas);\n this.surface.canvas.addEventListener('mousemove', this.handleMouseMove);\n this.surface.canvas.addEventListener('mouseleave', this.handleMouseLeave);\n this.surface.canvas.addEventListener('keydown', this.handleKeyDown);\n }\n\n private onResize(): void {\n if (this.surface.measure()) {\n this.dirty = true;\n this.draw();\n }\n }\n\n private onMouseMove(e: MouseEvent): void {\n const rect = this.surface.canvas.getBoundingClientRect();\n this.cursorX = e.clientX - rect.left;\n this.cursorY = e.clientY - rect.top;\n this.showCrosshair = true;\n this.draw();\n this.notifySyncCrosshair();\n }\n\n private onMouseLeave(): void {\n this.showCrosshair = false;\n this.draw();\n this.notifySyncCrosshairLeave();\n }\n}\n","/**\n * @file The series line, drawn as a single batched stroke.\n *\n * Two regimes share one path:\n *\n * - Dense (count > 2·plotW): per-pixel-column min/max decimation. Drawing\n * every point would smear into a solid band and alias badly; instead each\n * pixel column collapses to first→min→max→last, joined so adjacent columns\n * form one continuous ribbon (the signal's visual envelope). Collapses\n * 500k points to ~2·width segments.\n *\n * - Sparse: the real polyline, point for point.\n *\n * Both walk logical order over physical storage using a `toWrap` countdown\n * instead of a modulo per point (snapshot mode never wraps; ring mode wraps\n * once). The scale arithmetic is inlined for hot-loop speed.\n */\n\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** Render the series line into `ctx` for the given view and plot rect. */\nexport function renderLine(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n const { xArr, yArr, count: n, cap } = view;\n if (n === 0) return;\n\n ctx.strokeStyle = opts.lineColor;\n ctx.lineWidth = opts.lineWidth;\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n ctx.beginPath();\n\n const xRange = view.xMax - view.xMin;\n const yRange = view.yMax - view.yMin;\n const xScale = xRange > 0 ? plot.w / xRange : 0;\n const xOff = plot.x - view.xMin * xScale;\n const yScale = yRange > 0 ? plot.h / yRange : 0;\n const yOff = plot.y + plot.h + view.yMin * yScale;\n\n let p = view.head;\n let toWrap = cap - view.head;\n\n if (n > plot.w * 2) {\n let col = -1;\n let colMinY = 0;\n let colMaxY = 0;\n let colFirstY = 0;\n let colLastY = 0;\n let started = false;\n\n const flush = (cx: number) => {\n ctx.lineTo(cx, colFirstY);\n ctx.lineTo(cx, colMinY);\n ctx.lineTo(cx, colMaxY);\n ctx.lineTo(cx, colLastY);\n };\n\n for (let i = 0; i < n; i++) {\n const px = xOff + xArr[p] * xScale;\n const c = px | 0;\n const py = yOff - yArr[p] * yScale;\n\n if (c !== col) {\n if (started) flush(col + 0.5);\n else {\n ctx.moveTo(c + 0.5, py);\n started = true;\n }\n col = c;\n colMinY = py;\n colMaxY = py;\n colFirstY = py;\n colLastY = py;\n } else {\n if (py < colMinY) colMinY = py;\n if (py > colMaxY) colMaxY = py;\n colLastY = py;\n }\n\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n if (started) flush(col + 0.5);\n } else {\n ctx.moveTo(xOff + xArr[p] * xScale, yOff - yArr[p] * yScale);\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n for (let i = 1; i < n; i++) {\n ctx.lineTo(xOff + xArr[p] * xScale, yOff - yArr[p] * yScale);\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n }\n\n ctx.stroke();\n}\n","/**\n * @file Line chart — the default series drawn as a single batched polyline.\n *\n * Supports the decimation auto-switch: dense data collapses to a per-pixel\n * min/max envelope ribbon; sparse data draws the real polyline. For the\n * full algorithm details see {@link renderLine}.\n */\n\nimport { ChartBase } from './chart-base.ts';\nimport { renderLine } from '../render/line.ts';\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** High-performance Canvas 2D line chart. */\nexport class LineChart extends ChartBase {\n protected renderSeries(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n renderLine(ctx, view, plot, opts);\n }\n}\n","/**\n * @file The area fill + top-line stroke, drawn as separate batched paths.\n *\n * Shares the same per-pixel-column min/max decimation as {@link renderLine}.\n * Two paths are built from the same data so the fill covers the region below\n * the envelope while the stroke only traces the visible top line — the bottom\n * and side closure edges are never stroked.\n *\n * In the decimated regime each column's first→min→max→last edge pair is\n * recorded during the single pass (~plot-width entries, bounded); the filled\n * closure drops to the plot bottom, sweeps left, and rises to the start.\n */\n\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/**\n * One decimated pixel column. 5 numbers per column: centre-x and the four Y\n * values that trace the envelope in order (first → min → max → last).\n * Maximum ~plot.w entries — a few hundred, not half a million.\n */\ntype ColData = [number, number, number, number, number];\n\n/** Render the filled area into `ctx` for the given view and plot rect. */\nexport function renderArea(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n const { xArr, yArr, count: n, cap } = view;\n if (n === 0) return;\n\n const xRange = view.xMax - view.xMin;\n const yRange = view.yMax - view.yMin;\n const xScale = xRange > 0 ? plot.w / xRange : 0;\n const xOff = plot.x - view.xMin * xScale;\n const yScale = yRange > 0 ? plot.h / yRange : 0;\n const yOff = plot.y + plot.h + view.yMin * yScale;\n const bottomY = plot.y + plot.h;\n\n let p = view.head;\n let toWrap = cap - view.head;\n\n ctx.lineJoin = 'round';\n\n const N_DENSE = plot.w * 2;\n\n if (n > N_DENSE) {\n // ---- Decimated: one pass collecting column data, then fill + stroke separately\n const cols: ColData[] = [];\n let ci = -1;\n let cMinY = 0;\n let cMaxY = 0;\n let cFirstY = 0;\n let cLastY = 0;\n\n for (let i = 0; i < n; i++) {\n const px = xOff + xArr[p] * xScale;\n const c = px | 0;\n const py = yOff - yArr[p] * yScale;\n\n if (c !== ci) {\n if (ci >= 0) cols.push([ci + 0.5, cFirstY, cMinY, cMaxY, cLastY]);\n ci = c;\n cMinY = cMaxY = cFirstY = cLastY = py;\n } else {\n if (py < cMinY) cMinY = py;\n if (py > cMaxY) cMaxY = py;\n cLastY = py;\n }\n\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n if (ci >= 0) cols.push([ci + 0.5, cFirstY, cMinY, cMaxY, cLastY]);\n\n if (cols.length === 0) return;\n\n const firstCol = cols[0];\n const lastCol = cols[cols.length - 1];\n\n // Fill: closed path (envelope + bottom edges)\n ctx.beginPath();\n ctx.moveTo(firstCol[0], firstCol[1]);\n for (let i = 0; i < cols.length; i++) {\n const [, fy, minY, maxY, ly] = cols[i];\n ctx.lineTo(cols[i][0], fy);\n ctx.lineTo(cols[i][0], minY);\n ctx.lineTo(cols[i][0], maxY);\n ctx.lineTo(cols[i][0], ly);\n }\n ctx.lineTo(lastCol[0], bottomY);\n ctx.lineTo(firstCol[0], bottomY);\n ctx.closePath();\n\n ctx.fillStyle = opts.fillColor;\n if (opts.fillOpacity < 1) ctx.globalAlpha = opts.fillOpacity;\n ctx.fill();\n ctx.globalAlpha = 1;\n\n // Stroke: open path (top envelope only, no bottom edges)\n ctx.beginPath();\n ctx.strokeStyle = opts.lineColor;\n ctx.lineWidth = opts.lineWidth;\n ctx.lineCap = 'round';\n ctx.moveTo(firstCol[0], firstCol[1]);\n for (let i = 0; i < cols.length; i++) {\n const [, fy, minY, maxY, ly] = cols[i];\n ctx.lineTo(cols[i][0], fy);\n ctx.lineTo(cols[i][0], minY);\n ctx.lineTo(cols[i][0], maxY);\n ctx.lineTo(cols[i][0], ly);\n }\n ctx.stroke();\n } else {\n // ---- Sparse: two passes (n is small, ≤ 2×plot.w)\n // First pass: collect the polyline\n type Pt = [number, number];\n const pts: Pt[] = Array(n);\n p = view.head;\n toWrap = cap - view.head;\n for (let i = 0; i < n; i++) {\n pts[i] = [xOff + xArr[p] * xScale, yOff - yArr[p] * yScale];\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n\n const firstX = pts[0][0];\n const lastX = pts[n - 1][0];\n\n // Fill: closed path\n ctx.beginPath();\n ctx.moveTo(pts[0][0], pts[0][1]);\n for (let i = 1; i < n; i++) ctx.lineTo(pts[i][0], pts[i][1]);\n ctx.lineTo(lastX, bottomY);\n ctx.lineTo(firstX, bottomY);\n ctx.closePath();\n\n ctx.fillStyle = opts.fillColor;\n if (opts.fillOpacity < 1) ctx.globalAlpha = opts.fillOpacity;\n ctx.fill();\n ctx.globalAlpha = 1;\n\n // Stroke: open path (top line only)\n ctx.beginPath();\n ctx.strokeStyle = opts.lineColor;\n ctx.lineWidth = opts.lineWidth;\n ctx.lineCap = 'round';\n ctx.moveTo(pts[0][0], pts[0][1]);\n for (let i = 1; i < n; i++) ctx.lineTo(pts[i][0], pts[i][1]);\n ctx.stroke();\n }\n}\n","/**\n * @file Area chart — filled region below the series line.\n *\n * Same batched path + decimation as {@link LineChart}, but the path is closed\n * to the plot bottom so a single `fill()` paints the area, with the stroke\n * line drawn on top for readability even at low fill opacity.\n */\n\nimport { ChartBase } from './chart-base.ts';\nimport { renderArea } from '../render/area.ts';\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** High-performance Canvas 2D area chart. */\nexport class AreaChart extends ChartBase {\n protected renderSeries(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n renderArea(ctx, view, plot, opts);\n }\n}\n","/**\n * @file Scatter plot — one circle per (sampled) point.\n *\n * When the dataset is far denser than `maxDots` every `floor(n / maxDots)`-th\n * point is drawn (stride thinning) so the chart stays responsive. Thinning\n * skips the binary-search overhead of true polyline decimation; scatter\n * doesn't benefit from an envelope anyway because each point carries its own\n * visual weight.\n */\n\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** Advance a physical index by `k` positions, modulo `cap`. */\nfunction advance(p: number, k: number, cap: number): number {\n const next = p + k;\n return next >= cap ? next - cap : next;\n}\n\nexport function renderScatter(\n ctx: CanvasRenderingContext2D,\n view: SeriesView,\n plot: PlotRect,\n opts: ResolvedOpts,\n): void {\n const { xArr, yArr, count: n, cap } = view;\n if (n === 0) return;\n\n const xRange = view.xMax - view.xMin;\n const yRange = view.yMax - view.yMin;\n const xScale = xRange > 0 ? plot.w / xRange : 0;\n const xOff = plot.x - view.xMin * xScale;\n const yScale = yRange > 0 ? plot.h / yRange : 0;\n const yOff = plot.y + plot.h + view.yMin * yScale;\n\n const maxDots = opts.maxDots;\n const step = n > maxDots ? Math.max(1, Math.floor(n / maxDots)) : 1;\n const r = opts.pointRadius;\n\n let p = view.head;\n\n ctx.fillStyle = opts.lineColor;\n ctx.beginPath();\n\n for (let i = 0; i < n; i += step) {\n const px = xOff + xArr[p] * xScale;\n const py = yOff - yArr[p] * yScale;\n ctx.moveTo(px + r, py);\n ctx.arc(px, py, r, 0, Math.PI * 2);\n p = advance(p, step, cap);\n }\n ctx.fill();\n}\n","/**\n * @file Scatter chart — one filled circle per (sampled) point.\n *\n * Uses stride thinning via {@link renderScatter}: when the dataset exceeds\n * `maxDots` every Nth point is drawn so the chart stays responsive. The\n * thinning step is `floor(n / maxDots)`.\n */\n\nimport { ChartBase } from './chart-base.ts';\nimport { renderScatter } from '../render/scatter.ts';\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** High-performance Canvas 2D scatter chart. */\nexport class ScatterChart extends ChartBase {\n protected renderSeries(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n renderScatter(ctx, view, plot, opts);\n }\n}\n","/**\n * @file Ready-to-use colour presets for quick theming.\n *\n * Spread over any chart's constructor options to override colours without\n * assembling every value manually.\n *\n * Usage:\n * ```ts\n * import { LineChart, DARK } from 'goro-charts'\n * new LineChart(canvas, { ...DARK, series: [...] })\n * ```\n */\n\nimport type { ChartOpts } from './types.ts';\n\n/** Dark theme preset. */\nexport const DARK: ChartOpts = {\n gridColor: 'rgba(255,255,255,0.08)',\n axisColor: 'rgba(255,255,255,0.25)',\n textColor: 'rgba(255,255,255,0.5)',\n crosshairColor: 'rgba(255,255,255,0.3)',\n pointColor: '#4ea8ff',\n bgColor: '#111',\n};\n\n/** Light theme preset. */\nexport const LIGHT: ChartOpts = {\n gridColor: 'rgba(0,0,0,0.08)',\n axisColor: 'rgba(0,0,0,0.18)',\n textColor: 'rgba(0,0,0,0.55)',\n crosshairColor: 'rgba(0,0,0,0.18)',\n pointColor: '#2563eb',\n bgColor: '#fff',\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"goro-charts.js","names":[],"sources":["../src/defaults.ts","../src/data/monotonic-extent.ts","../src/data/ring-buffer.ts","../src/data/series-store.ts","../src/render/surface.ts","../src/math/ticks.ts","../src/math/format.ts","../src/math/scale.ts","../src/render/axes.ts","../src/render/shape.ts","../src/render/crosshair.ts","../src/render/legend.ts","../src/render/stacked-band.ts","../src/charts/chart-base.ts","../src/render/line.ts","../src/charts/line-chart.ts","../src/render/area.ts","../src/charts/area-chart.ts","../src/render/scatter.ts","../src/charts/scatter-chart.ts","../src/presets.ts"],"sourcesContent":["/**\n * @file Default options merged over any user-supplied {@link ChartOpts}.\n *\n * The top-level colour / width fields are per-series fallbacks — each series\n * config entry provides its own colour and optional overrides. `maxPoints: 0`\n * is a sentinel meaning \"snapshot mode\" (no ring); the constructor activates\n * ring mode only when the user passes a positive value.\n *\n * `yMin: undefined / yMax: undefined` — the grid domain expands from data\n * automatically. Set a number to pin the bound; `0` is a legitimate bound.\n */\n\nimport type { ResolvedOpts } from './types.ts';\n\nexport const CHART_DEFAULTS: ResolvedOpts = {\n series: [{ name: 'Series 0', color: '#4ea8ff', lineWidth: 1.5 }],\n padding: [16, 16, 32, 56],\n lineColor: '#4ea8ff',\n lineWidth: 1.5,\n fillColor: '#4ea8ff',\n fillOpacity: 0.15,\n pointColor: '#4ea8ff',\n gridColor: 'rgba(255,255,255,0.08)',\n axisColor: 'rgba(255,255,255,0.25)',\n textColor: 'rgba(255,255,255,0.5)',\n fontSize: 11,\n fontFamily: 'system-ui, -apple-system, sans-serif',\n crosshairColor: 'rgba(255,255,255,0.3)',\n crosshairWidth: 1,\n pointRadius: 4,\n bgColor: '#111',\n xTicks: 8,\n yTicks: 6,\n maxPoints: 0,\n autoDraw: false,\n yMin: undefined,\n yMax: undefined,\n maxDots: 2000,\n};\n","/**\n * @file Sliding-window min & max in O(1) amortized per push.\n *\n * Maintains two monotonic deques backed by circular Float64Arrays (no object\n * allocation): the min-deque is strictly increasing front→back, the max-deque\n * strictly decreasing, so each window extreme sits at its deque's front.\n *\n * Each push runs three steps in the canonical order — evict expired from\n * front, pop dominated from back, append — which is what guarantees a deque\n * never exceeds capacity. `seq` is a globally increasing sample id and\n * `windowStart` is the oldest seq still inside the window; an entry expires\n * once its seq < windowStart.\n */\n\n/** Incremental sliding-window extent tracker. */\nexport class MonotonicExtent {\n private cap: number;\n private vMin: Float64Array;\n private sMin: Float64Array;\n private hMin = 0;\n private nMin = 0;\n private vMax: Float64Array;\n private sMax: Float64Array;\n private hMax = 0;\n private nMax = 0;\n\n constructor(cap: number) {\n this.cap = cap;\n this.vMin = new Float64Array(cap);\n this.sMin = new Float64Array(cap);\n this.vMax = new Float64Array(cap);\n this.sMax = new Float64Array(cap);\n }\n\n /** Reset to empty (keeps allocated buffers). */\n clear(): void {\n this.hMin = this.nMin = this.hMax = this.nMax = 0;\n }\n\n /**\n * Record a new sample and slide the window forward.\n * @param value the sample value\n * @param seq globally increasing sample id\n * @param windowStart oldest seq still inside the window\n */\n push(value: number, seq: number, windowStart: number): void {\n const cap = this.cap;\n\n // Canonical order (evict → pop → append) keeps each deque length ≤ cap.\n const vMin = this.vMin;\n const sMin = this.sMin;\n while (this.nMin > 0 && sMin[this.hMin] < windowStart) {\n this.hMin = this.hMin + 1 === cap ? 0 : this.hMin + 1;\n this.nMin--;\n }\n while (this.nMin > 0 && vMin[(this.hMin + this.nMin - 1) % cap] >= value) {\n this.nMin--;\n }\n let ib = (this.hMin + this.nMin) % cap;\n vMin[ib] = value;\n sMin[ib] = seq;\n this.nMin++;\n\n const vMax = this.vMax;\n const sMax = this.sMax;\n while (this.nMax > 0 && sMax[this.hMax] < windowStart) {\n this.hMax = this.hMax + 1 === cap ? 0 : this.hMax + 1;\n this.nMax--;\n }\n while (this.nMax > 0 && vMax[(this.hMax + this.nMax - 1) % cap] <= value) {\n this.nMax--;\n }\n ib = (this.hMax + this.nMax) % cap;\n vMax[ib] = value;\n sMax[ib] = seq;\n this.nMax++;\n }\n\n /** Current window minimum. */\n get min(): number {\n return this.vMin[this.hMin];\n }\n /** Current window maximum. */\n get max(): number {\n return this.vMax[this.hMax];\n }\n}\n","/**\n * @file Fixed-capacity ring buffer of parallel (x, y) Float64Arrays.\n *\n * Append is O(1) with no memmove: once the window is full the oldest sample is\n * overwritten in place and `head` advances. y-extent is maintained\n * incrementally by a {@link MonotonicExtent}, so min/max are O(1) too.\n *\n * Logical order (oldest→newest) is `head, head+1, … head+count-1` (mod cap).\n * x is assumed monotonically increasing in push order, so it stays sorted in\n * logical order — which the crosshair's binary search relies on.\n */\n\nimport { MonotonicExtent } from './monotonic-extent.ts';\n\n/** Sliding-window columnar store for streaming (ring) mode. */\nexport class RingBuffer {\n cap: number;\n x: Float64Array;\n y: Float64Array;\n head = 0;\n count = 0;\n private seq = 0;\n private ext: MonotonicExtent;\n\n constructor(cap: number) {\n this.cap = cap;\n this.x = new Float64Array(cap);\n this.y = new Float64Array(cap);\n this.ext = new MonotonicExtent(cap);\n }\n\n /** Current window y minimum (O(1)). */\n get yMin(): number {\n return this.ext.min;\n }\n /** Current window y maximum (O(1)). */\n get yMax(): number {\n return this.ext.max;\n }\n /** x of the oldest sample. */\n get xFirst(): number {\n return this.x[this.head];\n }\n /** x of the newest sample. */\n get xLast(): number {\n const p = this.head + this.count - 1;\n return this.x[p >= this.cap ? p - this.cap : p];\n }\n /** y of the newest sample. */\n get lastY(): number {\n const p = this.head + this.count - 1;\n return this.y[p >= this.cap ? p - this.cap : p];\n }\n\n /** Map a logical index [0, count) to its physical slot. */\n physOf(logical: number): number {\n const p = this.head + logical;\n return p >= this.cap ? p - this.cap : p;\n }\n\n /** Append one sample. x must be ≥ the previous x (monotonic). */\n push(xv: number, yv: number): void {\n let phys: number;\n if (this.count < this.cap) {\n phys = this.head + this.count;\n if (phys >= this.cap) phys -= this.cap;\n this.count++;\n } else {\n phys = this.head;\n this.head = this.head + 1 === this.cap ? 0 : this.head + 1;\n }\n this.x[phys] = xv;\n this.y[phys] = yv;\n\n const s = this.seq++;\n const windowStart = s - this.count + 1;\n // NaN in Y is reserved for future gap rendering (v1.6.0) — exclude it\n // from the sliding-window extent so it doesn't corrupt min/max.\n if (!Number.isNaN(yv)) {\n this.ext.push(yv, s, windowStart);\n }\n }\n\n /** Reset to empty (keeps allocated buffers). */\n clear(): void {\n this.head = 0;\n this.count = 0;\n this.seq = 0;\n this.ext.clear();\n }\n\n /**\n * Resize the window, preserving the most recent samples. Rare and O(keep):\n * the retained tail is copied out, fresh buffers allocated, then re-pushed so\n * the extent deque rebuilds correctly.\n */\n resize(newCap: number): void {\n if (newCap === this.cap || newCap < 1) return;\n\n const keep = Math.min(this.count, newCap);\n const startLogical = this.count - keep;\n const tx = new Float64Array(keep);\n const ty = new Float64Array(keep);\n for (let i = 0; i < keep; i++) {\n const p = this.physOf(startLogical + i);\n tx[i] = this.x[p];\n ty[i] = this.y[p];\n }\n\n this.cap = newCap;\n this.x = new Float64Array(newCap);\n this.y = new Float64Array(newCap);\n this.ext = new MonotonicExtent(newCap);\n this.head = 0;\n this.count = 0;\n this.seq = 0;\n for (let i = 0; i < keep; i++) this.push(tx[i], ty[i]);\n }\n}\n","/**\n * @file Unified data model behind both chart modes, implementing SeriesView.\n *\n * Snapshot mode (setData) holds caller-owned arrays directly with head=0.\n * Ring mode (append/appendBatch) delegates storage to a {@link RingBuffer} and\n * mirrors its O(1) cached geometry into local fields each tick. Either way the\n * store exposes the same logical window `[0, count)` plus `physOf` /\n * `bracketLogical` addressing, so renderers consume one stable contract.\n *\n * The store is purely about data: it never touches the canvas or scheduling.\n */\n\nimport { RingBuffer } from './ring-buffer.ts';\nimport type { SeriesView, DataOwnership } from '../types.ts';\n\n/** Owns the series data and computes/caches its extents. */\nexport class SeriesStore implements SeriesView {\n xArr: Float64Array<ArrayBufferLike> = new Float64Array();\n yArr: Float64Array<ArrayBufferLike> = new Float64Array();\n head = 0;\n count = 0;\n cap = 0;\n xMin = 0;\n xMax = 0;\n yMin = 0;\n yMax = 0;\n\n private ring: RingBuffer | null = null;\n\n /** Whether ring (streaming) mode is active. */\n get isRing(): boolean {\n return this.ring !== null;\n }\n\n /**\n * Create the ring up front (used when constructed with maxPoints).\n * @throws {Error} if maxPoints < 1\n */\n initRing(maxPoints: number): void {\n if (maxPoints < 1) throw new Error('maxPoints must be >= 1');\n this.ring = new RingBuffer(maxPoints);\n this.bindRing();\n }\n\n /**\n * Snapshot mode: replace the whole series. Extents computed once in O(n).\n * Disables any active ring mode.\n *\n * By default (`ownership: 'copy'`) the store copies the caller's arrays into\n * fresh buffers — the caller may mutate the originals freely after the call.\n * Pass `'borrowed'` to keep the caller's arrays by reference (must be treated\n * as immutable for as long as the chart holds them).\n *\n * @throws {Error} if x and y have different lengths\n * @throws {Error} if x is empty\n * @throws {Error} if any x value is not finite, or x is not monotonically increasing\n * @throws {Error} if any y value is not finite (NaN in Y is allowed — reserved for gaps v1.6.0)\n */\n setData(x: Float64Array<ArrayBufferLike>, y: Float64Array<ArrayBufferLike>, ownership: DataOwnership = 'copy'): void {\n this.validateSnapshot(x, y);\n\n this.ring = null;\n this.xArr = ownership === 'copy' ? new Float64Array(x) : x;\n this.yArr = ownership === 'copy' ? new Float64Array(y) : y;\n this.head = 0;\n this.count = x.length;\n this.cap = x.length;\n\n this.xMin = x[0];\n this.xMax = x[x.length - 1];\n\n let yMin = Infinity;\n let yMax = -Infinity;\n for (let i = 0; i < y.length; i++) {\n const v = y[i];\n if (Number.isNaN(v)) continue; // reserved for gap rendering (v1.6.0)\n if (v < yMin) yMin = v;\n if (v > yMax) yMax = v;\n }\n if (yMin === Infinity) {\n // all Y values were NaN — degenerate safe range\n yMin = 0;\n yMax = 0;\n }\n this.applyExtent(yMin, yMax);\n }\n\n /**\n * Ring mode: append one sample (x must be monotonically increasing).\n * @throws {Error} if ring mode is not active (maxPoints not set)\n * @throws {Error} if x is not finite\n * @throws {Error} if x is < last x (not monotonically increasing)\n * @throws {Error} if y is not finite (NaN in Y is allowed — reserved for gaps v1.6.0)\n */\n append(x: number, y: number): void {\n const r = this.requireRing('append');\n if (!Number.isFinite(x)) {\n throw new Error(`append x=${x} is not finite`);\n }\n if (r.count > 0 && x < r.xLast) {\n throw new Error(`append x=${x} is < last x=${r.xLast}; x must be monotonically increasing`);\n }\n if (!isFiniteOrNaN(y)) {\n throw new Error(`append y=${y} is not finite`);\n }\n r.push(x, y);\n this.syncFromRing();\n }\n\n /**\n * Ring mode: append a batch of parallel samples. O(k).\n *\n * The entire batch is validated before any sample is pushed, so a partial\n * batch never corrupts the ring.\n *\n * @throws {Error} if ring mode is not active\n * @throws {Error} if xs and ys have different lengths\n * @throws {Error} if any x is not finite, or xs are not monotonically increasing\n * @throws {Error} if any y is not finite (NaN in Y is allowed — reserved for gaps v1.6.0)\n */\n appendBatch(xs: ArrayLike<number>, ys: ArrayLike<number>): void {\n const r = this.requireRing('appendBatch');\n if (xs.length !== ys.length) throw new Error('xs and ys must have same length');\n\n // Validate the entire batch before touching ring state, so a rejected\n // batch never leaves the ring half-updated.\n const k = xs.length;\n if (k === 0) return;\n for (let i = 0; i < k; i++) {\n if (!Number.isFinite(xs[i])) {\n throw new Error(`xs[${i}]=${xs[i]} is not finite`);\n }\n if (i > 0 && xs[i] < xs[i - 1]) {\n throw new Error(\n `xs not monotonically increasing at batch index ${i}: xs[${i}]=${xs[i]} < xs[${i - 1}]=${xs[i - 1]}`,\n );\n }\n if (!isFiniteOrNaN(ys[i])) {\n throw new Error(`ys[${i}]=${ys[i]} is not finite`);\n }\n }\n\n for (let i = 0; i < k; i++) r.push(xs[i], ys[i]);\n this.syncFromRing();\n }\n\n /** Resize the streaming window, keeping the most recent samples. */\n setMaxPoints(maxPoints: number): void {\n if (maxPoints < 1) throw new Error('maxPoints must be >= 1');\n if (!this.ring) this.ring = new RingBuffer(maxPoints);\n else this.ring.resize(maxPoints);\n this.bindRing();\n this.syncFromRing();\n }\n\n /** Empty the current data (works in both modes). */\n clear(): void {\n if (this.ring) {\n this.ring.clear();\n this.syncFromRing();\n } else {\n this.head = this.count = this.cap = 0;\n this.xArr = new Float64Array();\n this.yArr = new Float64Array();\n }\n }\n\n /** Most recent y value, or NaN if empty. */\n get lastValue(): number {\n if (this.count === 0) return NaN;\n return this.yArr[this.physOf(this.count - 1)];\n }\n\n physOf(logical: number): number {\n const p = this.head + logical;\n return p >= this.cap ? p - this.cap : p;\n }\n\n bracketLogical(target: number): number {\n const n = this.count;\n let lo = 0;\n let hi = n - 1;\n if (target <= this.xArr[this.physOf(0)]) return 0;\n if (target >= this.xArr[this.physOf(hi)]) return hi;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (this.xArr[this.physOf(mid)] <= target) lo = mid;\n else hi = mid - 1;\n }\n return lo;\n }\n\n /**\n * Validate snapshot data before accepting it into the store.\n * Checks length, non-empty, finite + monotonic X, and finite Y.\n * @throws {Error} with position-aware message on any violation.\n */\n private validateSnapshot(x: Float64Array<ArrayBufferLike>, y: Float64Array<ArrayBufferLike>): void {\n if (x.length !== y.length) {\n throw new Error(`x and y length mismatch: x.length=${x.length}, y.length=${y.length}`);\n }\n if (x.length === 0) throw new Error('data arrays must not be empty');\n\n if (!Number.isFinite(x[0])) {\n throw new Error(`x[0]=${x[0]} is not finite`);\n }\n for (let i = 1; i < x.length; i++) {\n if (!Number.isFinite(x[i])) {\n throw new Error(`x[${i}]=${x[i]} is not finite`);\n }\n if (x[i] < x[i - 1]) {\n throw new Error(`x not monotonically increasing at index ${i}: x[${i}]=${x[i]} < x[${i - 1}]=${x[i - 1]}`);\n }\n }\n\n for (let i = 0; i < y.length; i++) {\n if (!isFiniteOrNaN(y[i])) {\n throw new Error(`y[${i}]=${y[i]} is not finite`);\n }\n }\n }\n\n private requireRing(method: string): RingBuffer {\n if (!this.ring) {\n throw new Error(`${method}() requires the chart to be created with { maxPoints }`);\n }\n return this.ring;\n }\n\n /** Point the store's logical window at the ring's physical storage. */\n private bindRing(): void {\n const r = this.ring!;\n this.xArr = r.x;\n this.yArr = r.y;\n }\n\n /** Mirror the ring's O(1) cached geometry into the store's fields. */\n private syncFromRing(): void {\n const r = this.ring!;\n if (this.xArr !== r.x) this.bindRing();\n this.head = r.head;\n this.count = r.count;\n this.cap = r.cap;\n if (r.count > 0) {\n this.xMin = r.xFirst;\n this.xMax = r.xLast;\n this.applyExtent(r.yMin, r.yMax);\n }\n }\n\n /** Store y-extent, expanding a degenerate (flat) range so the line shows. */\n private applyExtent(yMin: number, yMax: number): void {\n if (yMax - yMin === 0) {\n this.yMin = yMin - 1;\n this.yMax = yMax + 1;\n } else {\n this.yMin = yMin;\n this.yMax = yMax;\n }\n }\n}\n\n/**\n * The Y contract, shared by snapshot and ring paths: a Y value is accepted iff\n * it is finite or `NaN`. `NaN` is a deliberate sentinel reserved for gap\n * rendering (v1.6.0) and is excluded from the extent; `±Infinity` is rejected\n * because it would silently corrupt the min/max scale.\n */\nfunction isFiniteOrNaN(v: number): boolean {\n return Number.isFinite(v) || Number.isNaN(v);\n}\n","/**\n * @file Canvas surface: DPR handling, sizing, and the offscreen blit buffer.\n *\n * Owns the visible canvas plus an offscreen canvas that holds the static\n * content (grid, axes, and the series). Both contexts carry a devicePixelRatio transform\n * so all drawing happens in CSS-pixel coordinates. The static layer is painted\n * once to the offscreen buffer; `blit` copies it to the visible canvas 1:1 in\n * device pixels, leaving the crosshair to be overlaid on top — so cursor\n * movement never repaints the series.\n */\n\n/** Manages the visible canvas, its DPR transform, and the offscreen buffer. */\nexport class Surface {\n readonly canvas: HTMLCanvasElement;\n readonly ctx: CanvasRenderingContext2D;\n readonly dpr: number;\n\n cssW = 0;\n cssH = 0;\n\n private offCanvas: HTMLCanvasElement | null = null;\n private offCtx: CanvasRenderingContext2D | null = null;\n\n constructor(canvas: HTMLCanvasElement) {\n this.canvas = canvas;\n // Accessibility\n canvas.setAttribute('role', 'img');\n canvas.tabIndex = 0;\n\n this.dpr = window.devicePixelRatio || 1;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context not available');\n this.ctx = ctx;\n this.measure();\n }\n\n /**\n * Re-measure the canvas against its CSS box and resize the backing store.\n * @returns true if the size actually changed (caller should redraw)\n */\n measure(): boolean {\n const rect = this.canvas.getBoundingClientRect();\n const w = Math.floor(rect.width);\n const h = Math.floor(rect.height);\n\n // Ignore zero/negative sizes (display:none, detached, pre-layout).\n if (w <= 0 || h <= 0) return false;\n\n // Skip redundant ResizeObserver fires where nothing changed.\n const bw = w * this.dpr;\n const bh = h * this.dpr;\n if (this.cssW === w && this.cssH === h && this.canvas.width === bw) return false;\n\n this.cssW = w;\n this.cssH = h;\n this.canvas.width = bw;\n this.canvas.height = bh;\n this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.offCanvas = null;\n this.offCtx = null;\n return true;\n }\n\n /** Get the offscreen context (lazily created), ready in CSS-pixel coords. */\n offscreenCtx(): CanvasRenderingContext2D {\n const bw = this.canvas.width;\n const bh = this.canvas.height;\n if (this.offCanvas && this.offCanvas.width === bw && this.offCanvas.height === bh) {\n return this.offCtx!;\n }\n const off = document.createElement('canvas');\n off.width = bw;\n off.height = bh;\n const ctx = off.getContext('2d');\n if (!ctx) throw new Error('Offscreen 2D context not available');\n ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.offCanvas = off;\n this.offCtx = ctx;\n return ctx;\n }\n\n /** Copy the offscreen buffer onto the visible canvas, 1:1 in device pixels. */\n blit(): void {\n if (!this.offCanvas) return;\n this.ctx.setTransform(1, 0, 0, 1, 0, 0);\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.ctx.drawImage(this.offCanvas, 0, 0);\n this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n }\n\n /** Release the offscreen buffer references. */\n dispose(): void {\n this.offCanvas = null;\n this.offCtx = null;\n }\n}\n","/**\n * @file Axis tick generation.\n *\n * Produces \"nice\" round tick values for an axis given a data range and a\n * target tick count. Spacing is snapped to 1, 2, or 5 times a power of ten so\n * labels read cleanly (…, 10, 20, 50, 100, …) instead of arbitrary fractions.\n */\n\n/** Range guard: a zero-width range collapses to 1 so tick math stays finite. */\nfunction niceRange(min: number, max: number): number {\n const r = max - min;\n return r === 0 ? 1 : r;\n}\n\n/**\n * Snap a raw per-tick spacing to the nearest \"nice\" value (1, 2, 5 × 10^exp).\n * @param range total data range\n * @param maxTicks approximate desired number of ticks\n */\nfunction niceSpacing(range: number, maxTicks: number): number {\n const exp = Math.floor(Math.log10(range / maxTicks));\n const frac = range / maxTicks / Math.pow(10, exp);\n let nice: number;\n if (frac <= 1.5) nice = 1;\n else if (frac <= 3.5) nice = 2;\n else if (frac <= 7.5) nice = 5;\n else nice = 10;\n return nice * Math.pow(10, exp);\n}\n\n/**\n * Generate the list of tick values spanning [min, max] at a nice spacing.\n * Spacing is snapped to 1, 2, or 5 × 10ⁿ so labels read cleanly.\n * @param min - Lower bound of the data range\n * @param max - Upper bound of the data range\n * @param maxTicks - Approximate desired number of ticks (actual count varies)\n * @returns Array of tick values sorted ascending\n */\nexport function generateTicks(min: number, max: number, maxTicks: number): number[] {\n const spacing = niceSpacing(niceRange(min, max), maxTicks);\n const start = Math.ceil(min / spacing) * spacing;\n const end = Math.floor(max / spacing) * spacing;\n const ticks: number[] = [];\n for (let v = start; v <= end + spacing * 0.5; v += spacing) {\n ticks.push(v);\n }\n return ticks;\n}\n","/**\n * @file Numeric label formatting for axis ticks and the crosshair tooltip.\n *\n * Chooses a compact representation by magnitude: integers print without a\n * decimal, very large/small magnitudes switch to exponential, and the middle\n * band keeps a fixed two decimals (or three significant digits below 1).\n */\n\n/**\n * Format a number for display on an axis or in the tooltip.\n * Integers print without decimal; very large/small magnitudes switch to\n * exponential notation; values between 1e-4 and 1 use toPrecision(3);\n * everything else uses toFixed(2).\n * @param n - Number to format\n * @returns Formatted string suitable for axis labels and tooltips\n */\nexport function formatNumber(n: number): string {\n if (Number.isInteger(n)) return n.toFixed(0);\n const abs = Math.abs(n);\n if ((abs >= 1e6 || (abs <= 1e-4 && abs > 0)) && isFinite(n)) return n.toExponential(2);\n if (abs >= 1) return n.toFixed(2);\n return n.toPrecision(3);\n}\n","/**\n * @file Linear data-space ↔ pixel-space coordinate transforms.\n *\n * Maps a data value onto the plot rectangle and back. Used outside the line\n * hot loop (ticks, crosshair); the line renderer inlines the same arithmetic\n * for per-point speed. A zero-width range degenerates to the plot origin\n * rather than dividing by zero.\n */\n\nimport type { Domain, PlotRect } from '../types.ts';\n\n/**\n * Map a data x value to a horizontal pixel coordinate.\n * @param x - Data-space x value\n * @param d - Data domain (xMin, xMax)\n * @param plot - Plot rectangle in CSS pixels\n * @returns Pixel x-coordinate (CSS pixels)\n */\nexport function xToPx(x: number, d: Domain, plot: PlotRect): number {\n const range = d.xMax - d.xMin;\n if (range <= 0) return plot.x;\n return plot.x + ((x - d.xMin) / range) * plot.w;\n}\n\n/**\n * Map a data y value to a vertical pixel coordinate (y grows downward).\n * @param y - Data-space y value\n * @param d - Data domain (yMin, yMax)\n * @param plot - Plot rectangle in CSS pixels\n * @returns Pixel y-coordinate (CSS pixels)\n */\nexport function yToPx(y: number, d: Domain, plot: PlotRect): number {\n const range = d.yMax - d.yMin;\n if (range <= 0) return plot.y;\n return plot.y + (1 - (y - d.yMin) / range) * plot.h;\n}\n\n/**\n * Map a horizontal pixel coordinate back to a data x value.\n * @param px - Pixel x-coordinate (CSS pixels)\n * @param d - Data domain (xMin, xMax)\n * @param plot - Plot rectangle in CSS pixels\n * @returns Data-space x value\n */\nexport function pxToX(px: number, d: Domain, plot: PlotRect): number {\n const range = d.xMax - d.xMin;\n if (range <= 0) return d.xMin;\n return d.xMin + ((px - plot.x) / plot.w) * range;\n}\n","/**\n * @file Grid and tick labels.\n *\n * Pure renderers over a {@link Domain} and a {@link PlotRect}. The grid is a\n * dashed internal lattice plus an explicit rectangular frame around the plot —\n * the frame is an explicit `strokeRect` so it always closes into a box,\n * independent of tick placement. Tick labels sit outside the frame with no\n * extra axis strokes (the frame itself is the axis boundary).\n */\n\nimport type { Domain, PlotRect, ResolvedOpts } from '../types.ts';\nimport { generateTicks } from '../math/ticks.ts';\nimport { formatNumber } from '../math/format.ts';\nimport { xToPx, yToPx } from '../math/scale.ts';\n\n/** Draw the background grid (dashed internal lines + closed frame). */\nexport function renderGrid(ctx: CanvasRenderingContext2D, d: Domain, plot: PlotRect, opts: ResolvedOpts): void {\n ctx.setLineDash([6, 4]);\n\n const bottom = plot.y + plot.h;\n const right = plot.x + plot.w;\n\n ctx.strokeStyle = opts.gridColor;\n ctx.lineWidth = 0.5;\n\n // Dashed horizontal lines — skip if they land exactly on the top/bottom\n // boundary (the frame handles those edges).\n const yTicks = generateTicks(d.yMin, d.yMax, opts.yTicks);\n ctx.beginPath();\n for (const y of yTicks) {\n const py = yToPx(y, d, plot);\n if (py <= plot.y || py >= bottom) continue;\n ctx.moveTo(plot.x, py);\n ctx.lineTo(right, py);\n }\n ctx.stroke();\n\n // Dashed vertical lines — skip if they land on left/right boundary.\n const xTicks = generateTicks(d.xMin, d.xMax, opts.xTicks);\n ctx.beginPath();\n for (const x of xTicks) {\n const px = xToPx(x, d, plot);\n if (px <= plot.x || px >= right) continue;\n ctx.moveTo(px, plot.y);\n ctx.lineTo(px, bottom);\n }\n ctx.stroke();\n\n // The frame is a single explicit rectangle — it always closes, regardless\n // of where the ticks land. Slightly stronger than internal grid lines.\n ctx.strokeStyle = opts.axisColor;\n ctx.lineWidth = 0.8;\n ctx.strokeRect(plot.x, plot.y, plot.w, plot.h);\n\n ctx.setLineDash([]);\n}\n\n/** Draw axis tick labels (Y on the left or right, X below). No axis strokes — the grid frame is the boundary. */\nexport function renderAxes(\n ctx: CanvasRenderingContext2D,\n d: Domain,\n plot: PlotRect,\n opts: ResolvedOpts,\n side: 'left' | 'right' = 'left',\n): void {\n ctx.font = `${opts.fontSize}px ${opts.fontFamily}`;\n ctx.fillStyle = opts.textColor;\n\n const yTicks = generateTicks(d.yMin, d.yMax, opts.yTicks);\n ctx.textAlign = side === 'right' ? 'left' : 'right';\n ctx.textBaseline = 'middle';\n const ypx = side === 'right' ? plot.x + plot.w + 6 : plot.x - 6;\n for (const y of yTicks) {\n ctx.fillText(formatNumber(y), ypx, yToPx(y, d, plot));\n }\n\n // X labels only on the left side (they share the same x domain)\n if (side === 'left') {\n const xTicks = generateTicks(d.xMin, d.xMax, opts.xTicks);\n ctx.textAlign = 'center';\n ctx.textBaseline = 'top';\n for (const x of xTicks) {\n ctx.fillText(formatNumber(x), xToPx(x, d, plot), plot.y + plot.h + 6);\n }\n }\n}\n","/**\n * @file Small Canvas path helpers used by the renderers.\n */\n\n/** Trace a rounded rectangle path (clockwise). Does NOT call beginPath/stroke/fill. */\nexport function roundedRect(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number,\n r: number,\n): void {\n if (w <= 0 || h <= 0) return;\n const rr = Math.min(r, w / 2, h / 2);\n ctx.moveTo(x + rr, y);\n ctx.arcTo(x + w, y, x + w, y + h, rr);\n ctx.arcTo(x + w, y + h, x, y + h, rr);\n ctx.arcTo(x, y + h, x, y, rr);\n ctx.arcTo(x, y, x + w, y, rr);\n ctx.closePath();\n}\n","/**\n * @file Interactive crosshair with an interpolated, multi-series tooltip card.\n *\n * The cursor x maps to an exact data value. For each non-empty series, y is\n * linearly interpolated between the two samples bracketing the cursor, so the\n * readout slides smoothly and never jumps by whole samples even where many\n * points share a pixel column. The bracket search runs on logical indices (x\n * is sorted there) and translates to physical slots, correct under ring\n * wraparound.\n *\n * `computeHits` is exported so external code can build custom tooltips via the\n * {@link ChartBase.onHover} callback without re-implementing the interpolation\n * math. `renderCrosshair` uses it internally.\n */\n\nimport type { SeriesView, SeriesConfig, PlotRect, ResolvedOpts } from '../types.ts';\nimport { formatNumber } from '../math/format.ts';\nimport { xToPx, yToPx, pxToX } from '../math/scale.ts';\nimport { roundedRect } from './shape.ts';\n\n/** Interpolated sample for one series at the cursor position. */\nexport interface SeriesHit {\n px: number;\n py: number;\n xVal: number;\n yVal: number;\n color: string;\n /** Series display name (from {@link SeriesConfig.name}). */\n label: string;\n}\n\n/**\n * Compute interpolated hit data for every non-empty series at `cursorX`.\n * Called internally by {@link renderCrosshair} and available for the\n * `onHover` callback so consumers can build custom tooltips.\n */\nexport function computeHits(\n views: readonly SeriesView[],\n configs: readonly SeriesConfig[],\n plot: PlotRect,\n cursorX: number,\n): SeriesHit[] {\n const hits: SeriesHit[] = [];\n\n for (let s = 0; s < views.length; s++) {\n const view = views[s];\n const n = view.count;\n if (n === 0) continue;\n\n const cursorVal = pxToX(cursorX, view, plot);\n const loLogical = view.bracketLogical(cursorVal);\n const hiLogical = loLogical + 1 < n ? loLogical + 1 : n - 1;\n const lo = view.physOf(loLogical);\n const hi = view.physOf(hiLogical);\n\n const x0 = view.xArr[lo];\n const x1 = view.xArr[hi];\n const y0 = view.yArr[lo];\n const y1 = view.yArr[hi];\n let t = x1 > x0 ? (cursorVal - x0) / (x1 - x0) : 0;\n if (t < 0) t = 0;\n else if (t > 1) t = 1;\n const xVal = x0 + (x1 - x0) * t;\n const yVal = y0 + (y1 - y0) * t;\n\n hits.push({\n px: xToPx(xVal, view, plot),\n py: yToPx(yVal, view, plot),\n xVal,\n yVal,\n color: configs[s].color,\n label: configs[s].name,\n });\n }\n return hits;\n}\n\n/** Draw the crosshair lines, marker dots, and multi-series tooltip card. */\nexport function renderCrosshair(\n ctx: CanvasRenderingContext2D,\n views: readonly SeriesView[],\n configs: readonly SeriesConfig[],\n plot: PlotRect,\n opts: ResolvedOpts,\n cursor: { x: number; y: number },\n cssW: number,\n): void {\n if (cursor.x < plot.x || cursor.x > plot.x + plot.w) return;\n if (cursor.y < plot.y || cursor.y > plot.y + plot.h) return;\n\n const hits = computeHits(views, configs, plot, cursor.x);\n if (hits.length === 0) return;\n\n const guidePx = hits[0].px;\n const cursorX = hits[0].xVal;\n\n // Dashed vertical guide\n ctx.strokeStyle = opts.crosshairColor;\n ctx.lineWidth = opts.crosshairWidth;\n ctx.setLineDash([4, 3]);\n ctx.beginPath();\n ctx.moveTo(guidePx, plot.y);\n ctx.lineTo(guidePx, plot.y + plot.h);\n ctx.stroke();\n ctx.setLineDash([]);\n\n // Dashed horizontal guide — only with a single visible series\n if (hits.length === 1) {\n const h = hits[0];\n ctx.strokeStyle = opts.crosshairColor;\n ctx.lineWidth = opts.crosshairWidth;\n ctx.setLineDash([4, 3]);\n ctx.beginPath();\n ctx.moveTo(plot.x, h.py);\n ctx.lineTo(plot.x + plot.w, h.py);\n ctx.stroke();\n ctx.setLineDash([]);\n }\n\n // Marker dots with a dark halo behind so they read over filled areas\n for (const h of hits) {\n ctx.fillStyle = 'rgba(0,0,0,0.65)';\n ctx.beginPath();\n ctx.arc(h.px, h.py, opts.pointRadius + 1.5, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.fillStyle = h.color;\n ctx.beginPath();\n ctx.arc(h.px, h.py, opts.pointRadius, 0, Math.PI * 2);\n ctx.fill();\n }\n\n // ---- Tooltip card ----------------------------------------------------\n const fx = (n: number) => formatNumber(n);\n const nameFont = `${opts.fontSize}px ${opts.fontFamily}`;\n const valueFont = `600 ${opts.fontSize + 1}px ${opts.fontFamily}`;\n const pad = 10;\n const dotR = 4;\n const colGap = 8;\n const rowH = opts.fontSize + 6;\n const cardR = 6;\n\n ctx.font = nameFont;\n const nameW = Math.max(...hits.map((h) => ctx.measureText(h.label).width));\n\n ctx.font = valueFont;\n const valueW = Math.max(...hits.map((h) => ctx.measureText(fx(h.yVal)).width));\n\n ctx.font = nameFont;\n const xLabelW = ctx.measureText('x').width;\n ctx.font = valueFont;\n const xValW = ctx.measureText(fx(cursorX)).width;\n\n const col2W = Math.max(0, nameW, xLabelW);\n const col3W = Math.max(valueW, xValW);\n\n const cardW = pad * 2 + dotR * 2 + colGap + col2W + colGap + col3W;\n const headerH = rowH + 4;\n const dividerH = 2;\n const cardH = pad * 2 + headerH + dividerH + hits.length * rowH;\n\n let tx = guidePx + 14;\n if (tx + cardW > cssW) tx = Math.max(2, guidePx - cardW - 14);\n let ty = cursor.y - cardH - 10;\n if (ty < 2) ty = cursor.y + 10;\n // Avoid overflowing the plot area vertically\n if (ty + cardH > plot.y + plot.h) {\n ty = Math.max(2, cursor.y - cardH - 10);\n }\n\n ctx.fillStyle = 'rgba(10,12,14,0.70)';\n ctx.beginPath();\n roundedRect(ctx, tx, ty, cardW, cardH, cardR);\n ctx.fill();\n\n ctx.strokeStyle = 'rgba(255,255,255,0.08)';\n ctx.lineWidth = 0.8;\n ctx.beginPath();\n roundedRect(ctx, tx, ty, cardW, cardH, cardR);\n ctx.stroke();\n\n const xRowY = ty + pad;\n ctx.fillStyle = opts.textColor;\n ctx.globalAlpha = 0.5;\n ctx.font = nameFont;\n ctx.fillText('x', tx + pad + dotR * 2 + colGap, xRowY + rowH - 4);\n ctx.globalAlpha = 1;\n\n ctx.font = valueFont;\n ctx.textAlign = 'right';\n ctx.fillStyle = 'rgba(255,255,255,0.85)';\n ctx.fillText(fx(cursorX), tx + cardW - pad, xRowY + rowH - 4);\n\n const divY = ty + pad + headerH + 1;\n ctx.strokeStyle = 'rgba(255,255,255,0.07)';\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(tx + pad, divY);\n ctx.lineTo(tx + cardW - pad, divY);\n ctx.stroke();\n\n for (let i = 0; i < hits.length; i++) {\n const h = hits[i];\n const ry = ty + pad + headerH + dividerH + i * rowH;\n\n ctx.fillStyle = h.color;\n ctx.beginPath();\n ctx.arc(tx + pad + dotR, ry + rowH / 2, dotR, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.font = nameFont;\n ctx.textAlign = 'left';\n ctx.fillStyle = 'rgba(255,255,255,0.55)';\n ctx.fillText(h.label, tx + pad + dotR * 2 + colGap, ry + rowH - 4);\n\n ctx.font = valueFont;\n ctx.textAlign = 'right';\n ctx.fillStyle = 'rgba(255,255,255,0.90)';\n ctx.fillText(fx(h.yVal), tx + cardW - pad, ry + rowH - 4);\n }\n\n ctx.textAlign = 'left';\n ctx.globalAlpha = 1;\n}\n","/**\n * @file Legend drawn inside the plot area (top-right corner).\n *\n * Laid out as a single horizontal row of colour dots + series names inside a\n * rounded, semi-transparent pill. Falls back to vertical layout only when\n * the horizontal row would overflow the plot width. Rendered only with two or\n * more series — one series doesn't need a legend.\n */\n\nimport type { SeriesConfig, PlotRect, ResolvedOpts } from '../types.ts';\nimport { roundedRect } from './shape.ts';\n\nexport function renderLegend(\n ctx: CanvasRenderingContext2D,\n configs: readonly SeriesConfig[],\n plot: PlotRect,\n opts: ResolvedOpts,\n): void {\n if (configs.length < 2) return;\n\n const dotR = 4;\n const dotD = dotR * 2;\n const gap = 6;\n const itemGap = 16;\n const padX = 10;\n const padY = 7;\n const cardR = 6;\n\n ctx.font = `${opts.fontSize}px ${opts.fontFamily}`;\n\n // Measure each item width: dot + gap + text\n const items: { width: number; config: SeriesConfig }[] = configs.map((c) => ({\n config: c,\n width: dotD + gap + ctx.measureText(c.name).width,\n }));\n const totalW = items.reduce((s, it) => s + it.width + (s > 0 ? itemGap : 0), 0);\n const maxItemW = Math.max(...items.map((it) => it.width));\n\n // Decide horizontal vs vertical based on available plot width\n const availableW = plot.w - 16; // margin from edges\n const horizontal = totalW <= availableW;\n\n let cardW: number;\n let cardH: number;\n\n if (horizontal) {\n cardW = totalW + padX * 2;\n cardH = opts.fontSize + padY * 2;\n } else {\n cardW = maxItemW + padX * 2;\n cardH = items.length * (opts.fontSize + 6) + padY * 2;\n }\n\n const x = plot.x + plot.w - cardW - 8;\n const y = plot.y + 8;\n\n // Background\n ctx.fillStyle = 'rgba(10,12,14,0.70)';\n ctx.beginPath();\n roundedRect(ctx, x, y, cardW, cardH, cardR);\n ctx.fill();\n\n // Border\n ctx.strokeStyle = 'rgba(255,255,255,0.08)';\n ctx.lineWidth = 0.7;\n ctx.beginPath();\n roundedRect(ctx, x, y, cardW, cardH, cardR);\n ctx.stroke();\n\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n\n if (horizontal) {\n let cx = x + padX;\n for (let i = 0; i < items.length; i++) {\n if (i > 0) cx += itemGap;\n const it = items[i];\n const cy = y + cardH / 2;\n\n // Dot\n ctx.fillStyle = it.config.color;\n ctx.beginPath();\n ctx.arc(cx + dotR, cy, dotR, 0, Math.PI * 2);\n ctx.fill();\n\n // Name\n ctx.fillStyle = 'rgba(255,255,255,0.65)';\n ctx.fillText(it.config.name, cx + dotD + gap, cy);\n\n cx += it.width;\n }\n } else {\n ctx.textBaseline = 'top';\n for (let i = 0; i < items.length; i++) {\n const it = items[i];\n const iy = y + padY + i * (opts.fontSize + 6);\n\n ctx.fillStyle = it.config.color;\n ctx.beginPath();\n ctx.arc(x + padX + dotR, iy + opts.fontSize / 2, dotR, 0, Math.PI * 2);\n ctx.fill();\n\n ctx.fillStyle = 'rgba(255,255,255,0.65)';\n ctx.fillText(it.config.name, x + padX + dotD + gap, iy);\n }\n }\n\n ctx.textAlign = 'left';\n ctx.textBaseline = 'top';\n}\n","/**\n * @file Stacked area bands for a group of series with the same `stack` id.\n *\n * Each layer renders as a polyline band between its own cumulative Y and the\n * previous layer's cumulative Y (or the plot bottom for the first layer). Fill\n * covers the band; the top edge is stroked separately.\n *\n * Two regimes, mirroring {@link renderLine} / {@link renderArea}:\n *\n * - **Dense** (n > 2·plotW): per-pixel-column decimation. Each pixel column\n * collapses to its min/max cumulative Y, so a full window of N points per\n * layer becomes ~2·plotW column samples — the visual envelope of the band —\n * instead of N line segments. This is what keeps a 10k-point stacked chart\n * at the same cost as a decimated area chart.\n *\n * - **Sparse** (n ≤ 2·plotW): the real cumulative polyline, point for point.\n *\n * Y values are clamped to the plot rect so a band never spills past the frame\n * even if the domain lags the accumulated values.\n *\n * The crosshair uses the (undecimated) cumulative arrays via proxy views built\n * by the orchestrator, so dots line up with band edges.\n */\n\nimport type { SeriesView, PlotRect, Domain } from '../types.ts';\n\ninterface LayerStyle {\n lineColor: string;\n lineWidth: number;\n fillColor: string;\n fillOpacity: number;\n}\n\n/**\n * Decimated column samples for one layer's cumulative edge. Parallel arrays:\n * `cx[k]` is the column centre x; `top[k]` / `bot[k]` are the min/max cumulative\n * Y pixels in that column (top = smaller y = higher value). Length ≤ ~plotW.\n */\ninterface Decimated {\n cx: number[];\n top: number[];\n bot: number[];\n}\n\n/**\n * Render stacked area bands for a group of series sharing one `stack` key.\n *\n * @param stores one SeriesView per layer, bottom to top (index 0 = first)\n * @param styles per-series fill/stroke configs\n * @param plot the plot rectangle in CSS pixels\n * @param domain shared data-space extents (left or right grid domain)\n */\nexport function renderStackedBands(\n ctx: CanvasRenderingContext2D,\n stores: readonly SeriesView[],\n styles: readonly LayerStyle[],\n plot: PlotRect,\n domain: Domain,\n): void {\n const n = stores[0].count;\n if (n === 0) return;\n\n const first = stores[0];\n const { xArr, cap } = first;\n\n const xRange = domain.xMax - domain.xMin;\n const yRange = domain.yMax - domain.yMin;\n const xScale = xRange > 0 ? plot.w / xRange : 0;\n const xOff = plot.x - domain.xMin * xScale;\n const yScale = yRange > 0 ? plot.h / yRange : 0;\n const yOff = plot.y + plot.h + domain.yMin * yScale;\n const bottomY = plot.y + plot.h;\n const topY = plot.y;\n\n // Clamp a data-space cumulative Y to a pixel inside the plot rect.\n const clampY = (yPx: number): number => (yPx < topY ? topY : yPx > bottomY ? bottomY : yPx);\n const py = (cumY: number): number => clampY(yOff - cumY * yScale);\n\n // Pre-compute cumulative Y for each layer (logical order).\n const cumYArr: Float64Array[] = [];\n const running = new Float64Array(n);\n for (let li = 0; li < stores.length; li++) {\n const s = stores[li];\n let p = s.head;\n let toWrap = s.cap - s.head;\n for (let j = 0; j < n; j++) {\n running[j] += s.yArr[p];\n if (--toWrap === 0) {\n p = 0;\n toWrap = s.cap;\n } else p++;\n }\n cumYArr.push(new Float64Array(running));\n }\n\n ctx.lineJoin = 'round';\n\n if (n > plot.w * 2) {\n renderDecimated(ctx, cumYArr, styles, xArr, first.head, cap, xOff, xScale, py, bottomY);\n } else {\n renderSparse(ctx, cumYArr, styles, xArr, first.head, cap, n, xOff, xScale, py, bottomY);\n }\n}\n\n/** Sparse regime: draw the real cumulative polyline for each layer. */\nfunction renderSparse(\n ctx: CanvasRenderingContext2D,\n cumYArr: Float64Array[],\n styles: readonly LayerStyle[],\n xArr: Float64Array<ArrayBufferLike>,\n head: number,\n cap: number,\n n: number,\n xOff: number,\n xScale: number,\n py: (cumY: number) => number,\n bottomY: number,\n): void {\n // Walk x in logical order once (shared across all layers).\n const xs = new Float64Array(n);\n let p = head;\n let toWrap = cap - head;\n for (let j = 0; j < n; j++) {\n xs[j] = xOff + xArr[p] * xScale;\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n\n for (let li = 0; li < cumYArr.length; li++) {\n const cumCurr = cumYArr[li];\n const style = styles[li];\n\n ctx.beginPath();\n ctx.fillStyle = style.fillColor;\n if (style.fillOpacity < 1) ctx.globalAlpha = style.fillOpacity;\n\n ctx.moveTo(xs[0], py(cumCurr[0]));\n for (let j = 1; j < n; j++) ctx.lineTo(xs[j], py(cumCurr[j]));\n\n if (li === 0) {\n ctx.lineTo(xs[n - 1], bottomY);\n ctx.lineTo(xs[0], bottomY);\n } else {\n const cumPrev = cumYArr[li - 1];\n for (let j = n - 1; j >= 0; j--) ctx.lineTo(xs[j], py(cumPrev[j]));\n }\n ctx.closePath();\n ctx.fill();\n ctx.globalAlpha = 1;\n\n ctx.beginPath();\n ctx.strokeStyle = style.lineColor;\n ctx.lineWidth = style.lineWidth;\n ctx.lineCap = 'round';\n ctx.moveTo(xs[0], py(cumCurr[0]));\n for (let j = 1; j < n; j++) ctx.lineTo(xs[j], py(cumCurr[j]));\n ctx.stroke();\n }\n}\n\n/**\n * Dense regime: collapse each layer's cumulative edge to per-pixel-column\n * min/max, then fill each band between its decimated edge and the previous\n * layer's decimated edge (or the plot bottom).\n */\nfunction renderDecimated(\n ctx: CanvasRenderingContext2D,\n cumYArr: Float64Array[],\n styles: readonly LayerStyle[],\n xArr: Float64Array<ArrayBufferLike>,\n head: number,\n cap: number,\n xOff: number,\n xScale: number,\n py: (cumY: number) => number,\n bottomY: number,\n): void {\n const n = cumYArr[0].length;\n\n // Pre-compute the column index for each logical sample once (shared x).\n const colOf = new Int32Array(n);\n {\n let p = head;\n let toWrap = cap - head;\n for (let j = 0; j < n; j++) {\n colOf[j] = (xOff + xArr[p] * xScale) | 0;\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n }\n\n // Decimate each layer into per-column min/max cumulative Y pixels.\n const layers: Decimated[] = cumYArr.map((cum) => {\n const cx: number[] = [];\n const top: number[] = [];\n const bot: number[] = [];\n let col = -1;\n let minY = 0;\n let maxY = 0;\n for (let j = 0; j < n; j++) {\n const c = colOf[j];\n const yPx = py(cum[j]);\n if (c !== col) {\n if (col >= 0) {\n cx.push(col + 0.5);\n top.push(minY);\n bot.push(maxY);\n }\n col = c;\n minY = maxY = yPx;\n } else {\n if (yPx < minY) minY = yPx;\n if (yPx > maxY) maxY = yPx;\n }\n }\n if (col >= 0) {\n cx.push(col + 0.5);\n top.push(minY);\n bot.push(maxY);\n }\n return { cx, top, bot };\n });\n\n for (let li = 0; li < layers.length; li++) {\n const cur = layers[li];\n const m = cur.cx.length;\n if (m === 0) continue;\n const style = styles[li];\n\n // Fill: forward along current edge (use bot = lower value edge for a solid\n // envelope), then close along previous layer's edge (or the plot bottom).\n ctx.beginPath();\n ctx.fillStyle = style.fillColor;\n if (style.fillOpacity < 1) ctx.globalAlpha = style.fillOpacity;\n\n ctx.moveTo(cur.cx[0], cur.top[0]);\n for (let k = 0; k < m; k++) {\n ctx.lineTo(cur.cx[k], cur.top[k]);\n ctx.lineTo(cur.cx[k], cur.bot[k]);\n }\n\n if (li === 0) {\n ctx.lineTo(cur.cx[m - 1], bottomY);\n ctx.lineTo(cur.cx[0], bottomY);\n } else {\n const prev = layers[li - 1];\n for (let k = prev.cx.length - 1; k >= 0; k--) {\n ctx.lineTo(prev.cx[k], prev.bot[k]);\n ctx.lineTo(prev.cx[k], prev.top[k]);\n }\n }\n ctx.closePath();\n ctx.fill();\n ctx.globalAlpha = 1;\n\n // Stroke: the top envelope of this layer only.\n ctx.beginPath();\n ctx.strokeStyle = style.lineColor;\n ctx.lineWidth = style.lineWidth;\n ctx.lineCap = 'round';\n ctx.moveTo(cur.cx[0], cur.top[0]);\n for (let k = 0; k < m; k++) {\n ctx.lineTo(cur.cx[k], cur.top[k]);\n ctx.lineTo(cur.cx[k], cur.bot[k]);\n }\n ctx.stroke();\n }\n}\n","/**\n * @file Abstract chart orchestrator shared by every chart type.\n *\n * Owns cross-cutting concerns: the data stores (one per series), the canvas\n * surface, the dirty flag and rAF coalescing, pointer interaction state, the\n * ResizeObserver, and the draw sequence (static layer → offscreen, blit,\n * crosshair overlay). All data lives in {@link SeriesStore} instances; all\n * pixels are produced by the `render/` functions.\n *\n * Multi-series: a {@link SeriesStore} is created for each\n * {@link SeriesConfig} entry. The series extent is computed as a union over\n * all stores so grid ticks span every visible series. Each store is rendered\n * independently by the subclass's {@link renderSeries}, receiving per-series\n * colour/style overrides merged from its config entry.\n *\n * Dual-Y: when any series opts into `yAxis: 'right'` the chart maintains\n * left and right grid domains independently. Left-axis series share the left\n * domain; right-axis series share the right. Tick labels appear on both sides\n * and the crosshair reads the correct axis per series.\n */\n\nimport { CHART_DEFAULTS } from '../defaults.ts';\nimport { SeriesStore } from '../data/series-store.ts';\nimport { Surface } from '../render/surface.ts';\nimport { renderGrid, renderAxes } from '../render/axes.ts';\nimport { renderCrosshair, computeHits } from '../render/crosshair.ts';\nimport { renderLegend } from '../render/legend.ts';\nimport { renderStackedBands } from '../render/stacked-band.ts';\nimport { formatNumber } from '../math/format.ts';\nimport { pxToX, xToPx } from '../math/scale.ts';\nimport type { ChartOpts, ResolvedOpts, SeriesConfig, SeriesView, PlotRect, Domain, DataOwnership } from '../types.ts';\nimport type { SeriesHit } from '../render/crosshair.ts';\n\n/** Abstract base for all chart types. */\nexport abstract class ChartBase {\n protected opts: ResolvedOpts;\n protected surface: Surface;\n protected stores: SeriesStore[];\n protected seriesConfigs: SeriesConfig[];\n\n private gridDomainLeft: Domain = { xMin: 0, xMax: 0, yMin: 0, yMax: 0 };\n private gridDomainRight: Domain = { xMin: 0, xMax: 0, yMin: 0, yMax: 0 };\n private gridPinned = false;\n private hasRightAxis = false;\n\n /**\n * Cached stack-group detection. `seriesConfigs` is immutable after the\n * constructor (validateOpts is the last writer), so groups are computed\n * once and reused every draw instead of rebuilt each frame.\n */\n private stackGroupsAll!: { groups: Map<string, number[]>; stacked: Set<number> };\n private stackGroupsByAxis!: Record<'left' | 'right', { groups: Map<string, number[]>; stacked: Set<number> }>;\n\n /**\n * Tracks stack-misalignment warnings already emitted, so a misaligned group\n * warns once instead of on every frame (accumulateStackGroup runs per draw).\n */\n private stackWarned = new Set<string>();\n\n private dirty = false;\n private cursorX = -1;\n private cursorY = -1;\n private showCrosshair = false;\n\n /** Logical index of the keyboard-cursor point in the reference series. */\n private cursorLogical = -1;\n\n private autoDraw: boolean;\n private rafScheduled = 0;\n private suspendCount = 0;\n private syncTargets = new Set<ChartBase>();\n\n private resizeObserver: ResizeObserver | null = null;\n protected destroyed = false;\n private liveRegion: HTMLElement | null = null;\n /**\n * Reference to the `prefers-reduced-motion` MQL plus its change listener.\n * Check `reducedMotionMql.matches` to test the preference; the listener\n * triggers a re-draw when the preference changes at runtime.\n */\n private reducedMotionMql: MediaQueryList | null = null;\n private readonly handleReducedMotionChange = () => {\n // The flag is live via reducedMotionMql.matches — just redraw.\n this.draw();\n };\n private readonly handleResize = () => this.onResize();\n private readonly handleMouseMove = (e: MouseEvent) => this.onMouseMove(e);\n private readonly handleMouseLeave = () => this.onMouseLeave();\n private readonly handleKeyDown = (e: KeyboardEvent) => this.onKeyDown(e);\n\n constructor(canvas: HTMLCanvasElement, opts?: ChartOpts) {\n this.opts = { ...CHART_DEFAULTS, ...opts };\n this.autoDraw = this.opts.autoDraw;\n\n this.seriesConfigs =\n this.opts.series.length > 0 ? this.opts.series : [{ name: 'Series 0', color: this.opts.lineColor }];\n\n this.validateOpts();\n\n this.surface = new Surface(canvas);\n\n this.stores = this.seriesConfigs.map(() => new SeriesStore());\n this.hasRightAxis = this.seriesConfigs.some((c) => c.yAxis === 'right');\n\n // Detect stack groups once — seriesConfigs is frozen from here on.\n this.stackGroupsAll = this.computeAllStackGroups();\n this.stackGroupsByAxis = {\n left: this.computeStackGroupsOnAxis('left'),\n right: this.computeStackGroupsOnAxis('right'),\n };\n this.validateStackGroups();\n\n if (opts?.maxPoints != null && opts.maxPoints > 0) {\n for (const s of this.stores) s.initRing(opts.maxPoints);\n }\n\n if (this.opts.yMin !== undefined || this.opts.yMax !== undefined) {\n if (this.opts.yMin !== undefined) this.gridDomainLeft.yMin = this.opts.yMin;\n if (this.opts.yMax !== undefined) this.gridDomainLeft.yMax = this.opts.yMax;\n if (this.opts.yMin !== undefined) this.gridDomainRight.yMin = this.opts.yMin;\n if (this.opts.yMax !== undefined) this.gridDomainRight.yMax = this.opts.yMax;\n this.gridPinned = true;\n }\n\n this.dirty = true;\n this.attachEvents();\n this.applySystemTheme();\n this.ensureLiveRegion();\n }\n\n /**\n * Validate and normalize constructor options.\n * Warns on suspicious values and clamps/repairs where possible.\n */\n private validateOpts(): void {\n if (this.opts.maxPoints < 0) {\n console.warn(`[goro-charts] maxPoints must be >= 0, got ${this.opts.maxPoints}. Using 0.`);\n this.opts.maxPoints = 0;\n }\n if (this.opts.fontSize < 6) {\n console.warn(`[goro-charts] fontSize ${this.opts.fontSize} is very small. Minimum recommended: 6.`);\n }\n const pad = this.opts.padding;\n if (pad.some((p) => p < 0)) {\n console.warn(`[goro-charts] padding values must be >= 0, got [${pad}]. Clamping to 0.`);\n this.opts.padding = pad.map((p) => Math.max(0, p)) as [number, number, number, number];\n }\n if (this.opts.yMin !== undefined && this.opts.yMax !== undefined && this.opts.yMin >= this.opts.yMax) {\n console.warn(`[goro-charts] yMin (${this.opts.yMin}) must be < yMax (${this.opts.yMax}). Swapping.`);\n [this.opts.yMin, this.opts.yMax] = [this.opts.yMax, this.opts.yMin];\n }\n // Validate series configs\n for (let i = 0; i < this.seriesConfigs.length; i++) {\n const s = this.seriesConfigs[i];\n if (!s.name || s.name.trim() === '') {\n console.warn(`[goro-charts] series[${i}] name is empty. Using \"Series ${i}\".`);\n this.seriesConfigs[i] = { ...s, name: `Series ${i}` };\n }\n if (s.yAxis && s.yAxis !== 'left' && s.yAxis !== 'right') {\n console.warn(`[goro-charts] series[${i}] has invalid yAxis \"${s.yAxis}\". Using \"left\".`);\n this.seriesConfigs[i] = { ...s, yAxis: 'left' };\n }\n }\n }\n\n /**\n * Detect system accessibility preferences and adjust visual styles.\n * - prefers-reduced-motion → tracked via `reducedMotionMql.matches`\n * (streaming/rAF continue normally; read the flag to suppress future\n * transitions/animations)\n * - prefers-contrast: more → increase colour opacity\n * - forced-colors: active → use system CSS system colours\n *\n * Also registers a `change` listener on the reduced-motion MQL so the\n * preference is re-evaluated at runtime. The listener is removed in\n * {@link destroy}.\n */\n private applySystemTheme(): void {\n try {\n const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');\n this.reducedMotionMql = reducedMotion;\n reducedMotion.addEventListener('change', this.handleReducedMotionChange);\n\n const highContrast = window.matchMedia('(prefers-contrast: more)');\n if (highContrast.matches) {\n if (this.opts.gridColor === 'rgba(255,255,255,0.08)') this.opts.gridColor = 'rgba(255,255,255,0.25)';\n if (this.opts.textColor === 'rgba(255,255,255,0.5)') this.opts.textColor = 'rgba(255,255,255,0.8)';\n }\n\n const forcedColors = window.matchMedia('(forced-colors: active)');\n if (forcedColors.matches) {\n this.opts.textColor = 'CanvasText';\n this.opts.bgColor = 'Canvas';\n this.opts.gridColor = 'GrayText';\n this.opts.axisColor = 'GrayText';\n this.opts.crosshairColor = 'GrayText';\n }\n } catch {\n // matchMedia may not be available in all environments (SSR, jsdom)\n }\n }\n\n /**\n * Ensure an aria-live region exists for screen-reader announcements\n * of crosshair position values.\n */\n private ensureLiveRegion(): void {\n if (this.liveRegion) return;\n try {\n this.liveRegion = document.createElement('div');\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n this.liveRegion.style.cssText =\n 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;';\n this.surface.canvas.parentElement?.appendChild(this.liveRegion);\n } catch {\n // DOM may not be available in test environments\n }\n }\n\n /**\n * Update the canvas aria-label with a summary of visible data.\n * Called once per static redraw.\n */\n private updateAriaLabel(): void {\n const nonEmpty = this.stores\n .map((s, i) => ({ config: this.seriesConfigs[i], count: s.count, last: s.lastValue }))\n .filter((s) => s.count > 0);\n\n let label: string;\n if (nonEmpty.length === 0) {\n label = 'Chart: no data';\n } else {\n label = `Chart: ${nonEmpty.map((s) => `${s.config.name} ${formatNumber(s.last)}`).join(', ')}`;\n }\n this.surface.canvas.setAttribute('aria-label', label);\n }\n\n /**\n * Return the first non-empty series index, or -1 when all stores are empty.\n * Used as the reference for keyboard navigation — the cursor advances by\n * logical point indices of this series and the crosshair position is derived\n * from its X value at the current logical index.\n */\n private referenceSeriesIndex(): number {\n for (let i = 0; i < this.stores.length; i++) {\n if (this.stores[i].count > 0) return i;\n }\n return -1;\n }\n\n /**\n * Handle keyboard navigation for the crosshair.\n * - ArrowLeft / ArrowRight: move crosshair by 1 point (Shift: 10 points)\n * - Escape: hide crosshair\n *\n * Navigation is anchored to the logical index of the first non-empty series\n * (see {@link referenceSeriesIndex}). The pixel position is derived from the\n * point's X value via the grid domain so the cursor tracks data, not pixels.\n */\n private onKeyDown(e: KeyboardEvent): void {\n if (document.activeElement !== this.surface.canvas) return;\n\n const step = e.shiftKey ? 10 : 1;\n const ref = this.referenceSeriesIndex();\n if (ref < 0) return;\n\n const count = this.stores[ref].count;\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n if (this.cursorLogical < 0 || this.cursorLogical >= count) {\n this.cursorLogical = count - 1;\n } else {\n this.cursorLogical = Math.max(0, this.cursorLogical - step);\n }\n break;\n case 'ArrowRight':\n e.preventDefault();\n if (this.cursorLogical < 0 || this.cursorLogical >= count) {\n this.cursorLogical = 0;\n } else {\n this.cursorLogical = Math.min(count - 1, this.cursorLogical + step);\n }\n break;\n case 'Escape':\n e.preventDefault();\n this.showCrosshair = false;\n this.draw();\n this.notifySyncCrosshairLeave();\n return;\n default:\n return;\n }\n\n // Convert the logical point index to a pixel X coordinate.\n const store = this.stores[ref];\n const xVal = store.xArr[store.physOf(this.cursorLogical)];\n const plot = this.plotRect();\n // Use the grid domain for the reference series axis.\n const axis = (this.seriesConfigs[ref].yAxis ?? 'left') === 'right' ? this.gridDomainRight : this.gridDomainLeft;\n this.cursorX = xToPx(xVal, axis, plot);\n this.showCrosshair = true;\n this.draw();\n this.notifySyncCrosshair();\n }\n\n /** Reusable crosshair-sync helpers used by both mouse and keyboard handlers. */\n private notifySyncCrosshair(): void {\n const xVal = pxToX(this.cursorX, this.gridDomainLeft, this.plotRect());\n for (const target of this.syncTargets) target.injectCursor(xVal);\n }\n\n private notifySyncCrosshairLeave(): void {\n for (const target of this.syncTargets) target.injectCursorLeave();\n }\n\n /** Draw the series shape. Implemented by {@link LineChart} / {@link AreaChart}. */\n protected abstract renderSeries(\n ctx: CanvasRenderingContext2D,\n view: SeriesView,\n plot: PlotRect,\n opts: ResolvedOpts,\n ): void;\n\n // ---- Public data API -----------------------------------------------------\n\n /**\n * Snapshot mode: replace the series at `index` (O(n) extent).\n * @param index - Series index (0-based)\n * @param x - X values, must be finite and monotonically increasing\n * @param y - Y values, may contain NaN (reserved for gaps v1.6.0); non-finite other than NaN is rejected\n * @param ownership - `'copy'` (default): store copies the arrays; `'borrowed'`: store keeps caller's arrays by reference (must be treated as immutable)\n * @throws {Error} if `index` is out of range, length mismatch, empty, non-finite X, or non-monotonic X\n */\n setData(\n index: number,\n x: Float64Array<ArrayBufferLike>,\n y: Float64Array<ArrayBufferLike>,\n ownership?: DataOwnership,\n ): void {\n if (this.destroyed) return;\n try {\n this.storeAt(index).setData(x, y, ownership);\n } catch (e) {\n throw new Error(`series ${index}: ${(e as Error).message}`, { cause: e });\n }\n this.gridPinned = false;\n this.invalidate();\n }\n\n /**\n * Ring mode: append one sample to series `index`.\n * @param index - Series index (0-based)\n * @param x - X value (must be finite and monotonically increasing)\n * @param y - Y value (NaN is allowed — reserved for gaps v1.6.0)\n * @throws {Error} if ring mode is not active (maxPoints not set)\n * @throws {Error} if `index` is out of range, x is not finite, x is non-monotonic, or y is non-finite (NaN allowed)\n */\n append(index: number, x: number, y: number): void {\n if (this.destroyed) return;\n try {\n this.storeAt(index).append(x, y);\n } catch (e) {\n throw new Error(`series ${index}: ${(e as Error).message}`, { cause: e });\n }\n this.invalidate();\n }\n\n /**\n * Ring mode: append a batch of samples to series `index`.\n * @param index - Series index (0-based)\n * @param xs - X values (must be finite and monotonically increasing)\n * @param ys - Y values (NaN is allowed — reserved for gaps v1.6.0)\n * @throws {Error} if ring mode is not active\n * @throws {Error} if `index` is out of range, length mismatch, non-finite X, non-monotonic X, or non-finite Y (NaN allowed)\n */\n appendBatch(index: number, xs: ArrayLike<number>, ys: ArrayLike<number>): void {\n if (this.destroyed) return;\n try {\n this.storeAt(index).appendBatch(xs, ys);\n } catch (e) {\n throw new Error(`series ${index}: ${(e as Error).message}`, { cause: e });\n }\n this.invalidate();\n }\n\n /**\n * Resize the streaming window (applies to all series).\n * Preserves the most recent samples in each series.\n * @param maxPoints - New window size (must be >= 1)\n */\n setMaxPoints(maxPoints: number): void {\n if (this.destroyed) return;\n for (const s of this.stores) s.setMaxPoints(maxPoints);\n this.invalidate();\n }\n\n /** Empty all series and reset grid domain. */\n clear(): void {\n if (this.destroyed) return;\n for (const s of this.stores) s.clear();\n this.gridPinned = false;\n this.stackWarned.clear();\n this.invalidate();\n }\n\n /** Number of series configured. */\n get seriesCount(): number {\n if (this.destroyed) return 0;\n return this.stores.length;\n }\n\n /**\n * Total points currently in the window across all series (before decimation).\n * Useful as a high-level data-volume indicator.\n */\n get windowPointCount(): number {\n if (this.destroyed) return 0;\n return this.stores.reduce((sum, s) => sum + s.count, 0);\n }\n\n /**\n * Estimated number of line segments actually drawn in the last render.\n * When a series has more than 2×plotW points, the renderer decimates to\n * ~2·plotW per-pixel-column segments (the `dense` regime). This property\n * mirrors that rule so you can verify decimation is active.\n *\n * Returns an upper bound (the real hardware draw count may be slightly\n * lower due to degenerate columns) — sufficient for debug/monitoring.\n */\n get drawnPointCount(): number {\n if (this.destroyed) return 0;\n const plotW = this.plotRect().w;\n if (plotW <= 0) return this.windowPointCount;\n return this.stores.reduce((sum, s) => {\n if (s.count === 0) return sum;\n return sum + (s.count > plotW * 2 ? Math.min(s.count, Math.ceil(plotW * 2)) : s.count);\n }, 0);\n }\n\n /**\n * Number of points currently in the window for series `index`.\n * @param index - Series index (0-based)\n */\n pointCount(index: number): number {\n if (this.destroyed) return 0;\n return this.stores[index].count;\n }\n /**\n * Current window y minimum for series `index` (O(1)).\n * @param index - Series index (0-based)\n */\n extentMin(index: number): number {\n if (this.destroyed) return NaN;\n return this.stores[index].yMin;\n }\n /**\n * Current window y maximum for series `index` (O(1)).\n * @param index - Series index (0-based)\n */\n extentMax(index: number): number {\n if (this.destroyed) return NaN;\n return this.stores[index].yMax;\n }\n /**\n * Most recent y value for series `index`, or NaN if empty.\n * @param index - Series index (0-based)\n */\n lastValue(index: number): number {\n if (this.destroyed) return NaN;\n return this.stores[index].lastValue;\n }\n\n /**\n * Pause rAF-coalesced drawing. Nestable — call {@link resumeDraw} the\n * same number of times to re-enable. Useful for bulk-loading data without\n * intermediate paints.\n */\n suspendDraw(): void {\n if (this.destroyed) return;\n this.suspendCount++;\n }\n /** Resume drawing after a matching {@link suspendDraw}. Draws immediately if dirty. */\n resumeDraw(): void {\n if (this.destroyed) return;\n if (this.suspendCount > 0) this.suspendCount--;\n if (this.suspendCount === 0 && this.dirty) this.invalidate();\n }\n\n /**\n * Export the current canvas as a PNG data URL.\n * @returns A `data:image/png` URL string\n */\n toImage(): string {\n if (this.destroyed) return '';\n return this.surface.canvas.toDataURL('image/png');\n }\n\n /**\n * Bidirectionally sync crosshair position with `other`. When the mouse\n * moves on one chart, crosshair overlay is injected on all synced charts\n * at the matching x coordinate.\n * @param other - Another chart instance to sync with\n */\n sync(other: ChartBase): void {\n if (this.destroyed) return;\n this.syncTargets.add(other);\n other.syncTargets.add(this);\n }\n\n /**\n * Remove bidirectional crosshair sync previously established with `other`.\n * No-op if the two charts were not synced.\n * @param other - Another chart instance to unsync from\n */\n unsync(other: ChartBase): void {\n if (this.destroyed) return;\n this.syncTargets.delete(other);\n other.syncTargets.delete(this);\n }\n\n /**\n * External callback for hover events. Called on `mousemove` with the\n * interpolated data for every visible series. Use to build custom DOM\n * tooltips or bind to framework state — the Canvas tooltip still draws\n * unless suppressed externally.\n */\n onHover?: (hits: SeriesHit[]) => void;\n\n // ---- Rendering -----------------------------------------------------------\n\n /** Paint the chart. Cheap to call repeatedly: returns early when clean. */\n draw(): void {\n if (this.destroyed) return;\n if (!this.dirty && !this.showCrosshair) return;\n const { cssW, cssH } = this.surface;\n if (cssW <= 0 || cssH <= 0) return;\n\n const plot = this.plotRect();\n if (plot.w <= 0 || plot.h <= 0) return;\n\n if (this.dirty) {\n this.renderStatic(plot);\n this.updateAriaLabel();\n }\n\n this.surface.blit();\n\n if (this.showCrosshair) {\n // Detect stacked groups so we can use accumulated Y (matching the band\n // edges) for crosshair dot positions.\n const { groups: stackGroups } = this.detectAllStackGroups();\n\n // Pre-compute accumulated crosshair views — for stacked series we\n // replace yArr with the cumulative Y so dots sit on band edges.\n const stackedSurrogates = new Map<number, Float64Array>();\n for (const [, grp] of stackGroups) {\n if (grp.length < 2) continue;\n const { posCum, negCum } = this.accumulateStackGroup(grp);\n if (!posCum && !negCum) continue;\n // Combine positive and negative tracks so the crosshair dot sits on\n // the net band edge (top of positive envelope plus negative dip).\n const combined = posCum && negCum ? posCum.map((v, i) => v + negCum[i]) : (posCum ?? negCum)!;\n for (const idx of grp) {\n stackedSurrogates.set(idx, new Float64Array(combined));\n }\n }\n\n const crosshairViews: SeriesView[] = this.stores.map((store, i) => {\n const cfg = this.seriesConfigs[i];\n const dom = (cfg.yAxis ?? 'left') === 'right' ? this.gridDomainRight : this.gridDomainLeft;\n let viewYMin = dom.yMin;\n let viewYMax = dom.yMax;\n if (cfg.yMin != null) viewYMin = cfg.yMin;\n if (cfg.yMax != null) viewYMax = cfg.yMax;\n const accY = stackedSurrogates.get(i);\n return Object.assign(Object.create(Object.getPrototypeOf(store)), store, {\n xMin: dom.xMin,\n xMax: dom.xMax,\n yMin: viewYMin,\n yMax: viewYMax,\n ...(accY ? { yArr: accY } : {}),\n });\n });\n\n if (this.onHover || this.liveRegion) {\n const hits = computeHits(crosshairViews, this.seriesConfigs, plot, this.cursorX);\n if (this.onHover && hits.length > 0) this.onHover(hits);\n if (this.liveRegion) {\n this.liveRegion.textContent =\n hits.length > 0 ? hits.map((h) => `${h.label}: ${formatNumber(h.yVal)}`).join(', ') : '';\n }\n }\n\n renderCrosshair(\n this.surface.ctx,\n crosshairViews,\n this.seriesConfigs,\n plot,\n this.opts,\n { x: this.cursorX, y: this.cursorY },\n cssW,\n );\n }\n\n if (!this.showCrosshair && this.liveRegion) {\n this.liveRegion.textContent = '';\n }\n\n this.dirty = false;\n }\n\n /** Detach observers/listeners and release buffers. Idempotent. */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n if (this.rafScheduled) {\n cancelAnimationFrame(this.rafScheduled);\n this.rafScheduled = 0;\n }\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.reducedMotionMql?.removeEventListener('change', this.handleReducedMotionChange);\n this.reducedMotionMql = null;\n this.surface.canvas.removeEventListener('mousemove', this.handleMouseMove);\n this.surface.canvas.removeEventListener('mouseleave', this.handleMouseLeave);\n this.surface.canvas.removeEventListener('keydown', this.handleKeyDown);\n this.liveRegion?.remove();\n this.liveRegion = null;\n // Remove this chart from every peer's sync set to avoid dangling refs.\n for (const target of this.syncTargets) target.syncTargets.delete(this);\n this.syncTargets.clear();\n this.surface.dispose();\n this.stores = [];\n }\n\n // ---- Internals -----------------------------------------------------------\n\n /** Render the static layer (background, grid, axes, series, legend) to offscreen. */\n private renderStatic(plot: PlotRect): void {\n const ctx = this.surface.offscreenCtx();\n const { cssW, cssH } = this.surface;\n ctx.clearRect(0, 0, cssW, cssH);\n ctx.fillStyle = this.opts.bgColor;\n ctx.fillRect(0, 0, cssW, cssH);\n\n if (this.stores.every((s) => s.count === 0)) return;\n\n this.updateGridDomain();\n\n renderGrid(ctx, this.gridDomainLeft, plot, this.opts);\n renderAxes(ctx, this.gridDomainLeft, plot, this.opts);\n if (this.hasRightAxis) {\n renderAxes(ctx, this.gridDomainRight, plot, this.opts, 'right');\n }\n\n // Detect stack groups.\n const { groups: stackGroups, stacked: stackedIndices } = this.detectAllStackGroups();\n\n // Non-stacked series.\n for (let i = 0; i < this.stores.length; i++) {\n if (stackedIndices.has(i)) continue;\n const store = this.stores[i];\n if (store.count === 0) continue;\n this.renderOne(ctx, i, store, store.yArr, plot);\n }\n\n // Stacked groups: build stores + styles arrays, delegate to band renderer.\n for (const [, grp] of stackGroups) {\n if (grp.length < 2) {\n for (const idx of grp) {\n const s = this.stores[idx];\n if (s.count > 0) this.renderOne(ctx, idx, s, s.yArr, plot);\n }\n continue;\n }\n const bandStores: SeriesView[] = [];\n const bandStyles: { lineColor: string; lineWidth: number; fillColor: string; fillOpacity: number }[] = [];\n for (const idx of grp) {\n const s = this.stores[idx];\n if (s.count === 0) continue;\n const cfg = this.seriesConfigs[idx];\n bandStores.push(s);\n bandStyles.push({\n lineColor: cfg.color,\n lineWidth: cfg.lineWidth ?? this.opts.lineWidth,\n fillColor: cfg.fillColor ?? this.opts.fillColor,\n fillOpacity: cfg.fillOpacity ?? this.opts.fillOpacity,\n });\n }\n if (bandStores.length < 2) {\n for (let i = 0; i < bandStores.length; i++) {\n this.renderOne(ctx, grp[i], bandStores[i], bandStores[i].yArr, plot);\n }\n continue;\n }\n const dom = (this.seriesConfigs[grp[0]].yAxis ?? 'left') === 'right' ? this.gridDomainRight : this.gridDomainLeft;\n renderStackedBands(ctx, bandStores, bandStyles, plot, dom);\n }\n\n renderLegend(ctx, this.seriesConfigs, plot, this.opts);\n }\n\n /** Render a single series. */\n private renderOne(\n ctx: CanvasRenderingContext2D,\n i: number,\n store: SeriesView,\n yArr: Float64Array<ArrayBufferLike>,\n plot: PlotRect,\n ): void {\n const cfg = this.seriesConfigs[i];\n const sOpts: ResolvedOpts = {\n ...this.opts,\n lineColor: cfg.color,\n lineWidth: cfg.lineWidth ?? this.opts.lineWidth,\n fillColor: cfg.fillColor ?? this.opts.fillColor,\n fillOpacity: cfg.fillOpacity ?? this.opts.fillOpacity,\n };\n\n if (cfg.dash) ctx.setLineDash(cfg.dash);\n\n const dom = (cfg.yAxis ?? 'left') === 'right' ? this.gridDomainRight : this.gridDomainLeft;\n let viewYMin = dom.yMin;\n let viewYMax = dom.yMax;\n if (cfg.yMin != null) viewYMin = cfg.yMin;\n if (cfg.yMax != null) viewYMax = cfg.yMax;\n\n const proxyView: SeriesView = Object.assign(Object.create(Object.getPrototypeOf(store)), store, {\n xMin: dom.xMin,\n xMax: dom.xMax,\n yArr,\n yMin: viewYMin,\n yMax: viewYMax,\n });\n\n this.renderSeries(ctx, proxyView, plot, sOpts);\n\n if (cfg.dash) ctx.setLineDash([]);\n }\n\n /** Cached per-axis stack groups (computed once in the constructor). */\n private detectStackGroupsOnAxis(axis: 'left' | 'right'): { groups: Map<string, number[]>; stacked: Set<number> } {\n return this.stackGroupsByAxis[axis];\n }\n\n /**\n * Compute stack groups for a given axis. Returns the group map and a set\n * of series indices that belong to groups with 2+ members.\n */\n private computeStackGroupsOnAxis(axis: 'left' | 'right'): { groups: Map<string, number[]>; stacked: Set<number> } {\n const groups = new Map<string, number[]>();\n const stacked = new Set<number>();\n for (let i = 0; i < this.stores.length; i++) {\n if ((this.seriesConfigs[i].yAxis ?? 'left') !== axis) continue;\n const g = this.seriesConfigs[i].stack;\n if (g) {\n let grp = groups.get(g);\n if (!grp) {\n grp = [];\n groups.set(g, grp);\n }\n grp.push(i);\n }\n }\n for (const grp of groups.values()) {\n if (grp.length >= 2) for (const idx of grp) stacked.add(idx);\n }\n return { groups, stacked };\n }\n\n /** Cached all-axis stack groups (computed once in the constructor). */\n private detectAllStackGroups(): {\n groups: Map<string, number[]>;\n stacked: Set<number>;\n } {\n return this.stackGroupsAll;\n }\n\n /**\n * Compute all stack groups across all axes.\n * Unlike {@link computeStackGroupsOnAxis}, this ignores the yAxis setting\n * and returns every group that has 2+ members.\n */\n private computeAllStackGroups(): {\n groups: Map<string, number[]>;\n stacked: Set<number>;\n } {\n const groups = new Map<string, number[]>();\n const stacked = new Set<number>();\n for (let i = 0; i < this.stores.length; i++) {\n const g = this.seriesConfigs[i].stack;\n if (g) {\n let grp = groups.get(g);\n if (!grp) {\n grp = [];\n groups.set(g, grp);\n }\n grp.push(i);\n }\n }\n for (const grp of groups.values()) {\n if (grp.length >= 2) for (const idx of grp) stacked.add(idx);\n }\n return { groups, stacked };\n }\n\n /**\n * Validate that all series within each stack group share the same axis.\n * Runs once in the constructor — axis assignment is immutable after that.\n * Data-length alignment is validated separately at draw time in\n * {@link accumulateStackGroup}, since counts change as data arrives.\n * Warns in development — mixed-axis stacking produces incorrect output.\n */\n private validateStackGroups(): void {\n for (let i = 0; i < this.stores.length; i++) {\n const g = this.seriesConfigs[i].stack;\n if (!g) continue;\n for (let j = i + 1; j < this.stores.length; j++) {\n if (this.seriesConfigs[j].stack !== g) continue;\n const axisI = this.seriesConfigs[i].yAxis ?? 'left';\n const axisJ = this.seriesConfigs[j].yAxis ?? 'left';\n if (axisI !== axisJ) {\n console.warn(\n `[goro-charts] stack group \"${g}\" mixes axis ${axisI} (series ${i}) and ${axisJ} (series ${j}). ` +\n `All series in a stack group must share the same yAxis.`,\n );\n }\n }\n }\n }\n\n /**\n * Compute accumulated Y values across a stack group, separating positive\n * and negative contributions.\n *\n * Positive values accumulate upward from 0 (`posCum`); negative values\n * accumulate downward from 0 (`negCum`). This prevents positive and negative\n * series from cancelling each other in the same accumulation track.\n *\n * Returns `{ posCum, negCum }` where each is `null` when no value of that\n * sign exists, or the cumulative `Float64Array` (length = n).\n */\n private accumulateStackGroup(indices: number[]): { posCum: Float64Array | null; negCum: Float64Array | null } {\n const first = this.stores[indices[0]];\n const n = first.count;\n if (n === 0) return { posCum: null, negCum: null };\n\n // Validate data-length alignment at runtime. accumulateStackGroup runs\n // multiple times per draw, so warn once per (group, series) pair.\n for (const idx of indices) {\n if (this.stores[idx].count !== n && this.stores[idx].count > 0) {\n const key = `len:${indices.join(',')}:${idx}:${this.stores[idx].count}:${n}`;\n if (!this.stackWarned.has(key)) {\n this.stackWarned.add(key);\n console.warn(\n `[goro-charts] stack accumulation skipped series ${idx}: count=${this.stores[idx].count} ` +\n `doesn't match group length ${n}.`,\n );\n }\n }\n }\n\n const runningPos = new Float64Array(n);\n const runningNeg = new Float64Array(n);\n let hasPos = false;\n let hasNeg = false;\n\n for (const idx of indices) {\n const s = this.stores[idx];\n if (s.count !== n) continue; // skip misaligned series\n let p = s.head;\n let toWrap = s.cap - s.head;\n for (let j = 0; j < n; j++) {\n const v = s.yArr[p];\n if (v > 0) {\n runningPos[j] += v;\n hasPos = true;\n } else if (v < 0) {\n runningNeg[j] += v;\n hasNeg = true;\n }\n if (--toWrap === 0) {\n p = 0;\n toWrap = s.cap;\n } else p++;\n }\n }\n return {\n posCum: hasPos ? runningPos : null,\n negCum: hasNeg ? runningNeg : null,\n };\n }\n\n /**\n * Update the grid Y domain.\n *\n * Two regimes:\n * - **Snapshot mode:** anchor the grid so it only expands, never shrinks.\n * The domain snaps to the union extent on the first draw and stays frozen\n * until a series exceeds it, then expands with a 10 % margin — a stable\n * visual anchor for static data.\n * - **Ring (streaming) mode:** the window slides, so the true extent both\n * grows and shrinks as samples enter and leave. Here we recompute the\n * domain from the current window every tick (via {@link initDomain}) so\n * the grid tracks the visible data instead of drifting or letting stacked\n * bands overflow the frame.\n *\n * If the user supplied `yMin` / `yMax` those are hard bounds that override\n * the auto logic in either regime.\n */\n private updateGridDomain(): void {\n if (this.stores.every((s) => s.count === 0)) return;\n\n const userYMin = this.opts.yMin;\n const userYMax = this.opts.yMax;\n const fixedY = userYMin !== undefined || userYMax !== undefined;\n const streaming = this.stores.some((s) => s.isRing);\n\n const applyUserBounds = () => {\n if (!fixedY) return;\n if (userYMin !== undefined) {\n this.gridDomainLeft.yMin = userYMin;\n this.gridDomainRight.yMin = userYMin;\n }\n if (userYMax !== undefined) {\n this.gridDomainLeft.yMax = userYMax;\n this.gridDomainRight.yMax = userYMax;\n }\n };\n\n // Streaming: recompute the window extent every tick so the grid tracks the\n // sliding window (shrinks as well as grows). Fixed bounds still win. A\n // small margin keeps peaks/troughs off the frame edge.\n if (streaming && !fixedY) {\n this.initDomain(this.gridDomainLeft, 'left', 0.05);\n if (this.hasRightAxis) this.initDomain(this.gridDomainRight, 'right', 0.05);\n this.refreshXDomain();\n this.gridPinned = true;\n return;\n }\n\n if (!this.gridPinned) {\n // First draw: compute union per axis from data, respecting any user bounds.\n this.initDomain(this.gridDomainLeft, 'left');\n if (this.hasRightAxis) this.initDomain(this.gridDomainRight, 'right');\n applyUserBounds();\n this.refreshXDomain();\n this.gridPinned = true;\n return;\n }\n\n // Refresh X every tick; Y expands only when data exceeds bounds.\n this.refreshXDomain();\n if (fixedY) return; // hard-locked — no expansion\n this.expandDomain(this.gridDomainLeft, 'left');\n if (this.hasRightAxis) this.expandDomain(this.gridDomainRight, 'right');\n }\n\n /**\n * Compute the domain for one axis from the current window.\n * @param margin optional fraction of the Y range to pad on each side\n * (used in streaming mode so peaks/troughs don't touch the frame).\n */\n private initDomain(d: Domain, axis: 'left' | 'right', margin = 0): void {\n d.xMin = Infinity;\n d.xMax = -Infinity;\n d.yMin = Infinity;\n d.yMax = -Infinity;\n\n const { groups, stacked } = this.detectStackGroupsOnAxis(axis);\n\n // Stacked groups: compute extent from accumulated Y.\n for (const [, grp] of groups) {\n if (grp.length < 2) continue;\n const { posCum, negCum } = this.accumulateStackGroup(grp);\n if (!posCum && !negCum) continue;\n let yMin = Infinity;\n let yMax = -Infinity;\n // Also collect x extents from the group\n for (const idx of grp) {\n const s = this.stores[idx];\n if (s.xMin < d.xMin) d.xMin = s.xMin;\n if (s.xMax > d.xMax) d.xMax = s.xMax;\n }\n const scan = (arr: Float64Array) => {\n for (let j = 0; j < arr.length; j++) {\n if (arr[j] < yMin) yMin = arr[j];\n if (arr[j] > yMax) yMax = arr[j];\n }\n };\n if (posCum) scan(posCum);\n if (negCum) scan(negCum);\n if (yMin < d.yMin) d.yMin = yMin;\n if (yMax > d.yMax) d.yMax = yMax;\n }\n\n // Non-stacked series on this axis.\n for (let i = 0; i < this.stores.length; i++) {\n if (stacked.has(i)) continue;\n const s = this.stores[i];\n if (s.count === 0) continue;\n if ((this.seriesConfigs[i].yAxis ?? 'left') !== axis) continue;\n if (s.xMin < d.xMin) d.xMin = s.xMin;\n if (s.xMax > d.xMax) d.xMax = s.xMax;\n if (s.yMin < d.yMin) d.yMin = s.yMin;\n if (s.yMax > d.yMax) d.yMax = s.yMax;\n }\n\n // Optional breathing room so extremes don't sit on the frame edge.\n if (margin > 0 && d.yMax > d.yMin) {\n const pad = (d.yMax - d.yMin) * margin;\n d.yMin -= pad;\n d.yMax += pad;\n }\n }\n\n private expandDomain(d: Domain, axis: 'left' | 'right'): void {\n const yr = d.yMax - d.yMin || 1;\n const { groups, stacked } = this.detectStackGroupsOnAxis(axis);\n let changed = false;\n\n // Stacked groups: expand if accumulated Y exceeds domain.\n for (const [, grp] of groups) {\n if (grp.length < 2) continue;\n const { posCum, negCum } = this.accumulateStackGroup(grp);\n if (!posCum && !negCum) continue;\n let accMin = Infinity;\n let accMax = -Infinity;\n const scan = (arr: Float64Array) => {\n for (let j = 0; j < arr.length; j++) {\n if (arr[j] < accMin) accMin = arr[j];\n if (arr[j] > accMax) accMax = arr[j];\n }\n };\n if (posCum) scan(posCum);\n if (negCum) scan(negCum);\n if (accMin < d.yMin) {\n d.yMin = accMin - yr * 0.1;\n changed = true;\n }\n if (accMax > d.yMax) {\n d.yMax = accMax + yr * 0.1;\n changed = true;\n }\n }\n\n // Non-stacked series on this axis.\n for (let i = 0; i < this.stores.length; i++) {\n if (stacked.has(i)) continue;\n const s = this.stores[i];\n if (s.count === 0) continue;\n if ((this.seriesConfigs[i].yAxis ?? 'left') !== axis) continue;\n if (s.yMin < d.yMin) {\n d.yMin = s.yMin - yr * 0.1;\n changed = true;\n }\n if (s.yMax > d.yMax) {\n d.yMax = s.yMax + yr * 0.1;\n changed = true;\n }\n }\n\n if (changed) {\n d.xMin = Infinity;\n d.xMax = -Infinity;\n for (let i = 0; i < this.stores.length; i++) {\n const s = this.stores[i];\n if (s.count === 0) continue;\n if ((this.seriesConfigs[i].yAxis ?? 'left') !== axis) continue;\n if (s.xMin < d.xMin) d.xMin = s.xMin;\n if (s.xMax > d.xMax) d.xMax = s.xMax;\n }\n }\n }\n\n /**\n * Recompute the shared X domain from all non-empty series so grid ticks\n * and renderers always map fresh X positions. Called every draw.\n */\n private refreshXDomain(): void {\n let xMin = Infinity;\n let xMax = -Infinity;\n for (const s of this.stores) {\n if (s.count === 0) continue;\n if (s.xMin < xMin) xMin = s.xMin;\n if (s.xMax > xMax) xMax = s.xMax;\n }\n if (xMin <= xMax) {\n this.gridDomainLeft.xMin = xMin;\n this.gridDomainLeft.xMax = xMax;\n this.gridDomainRight.xMin = xMin;\n this.gridDomainRight.xMax = xMax;\n }\n }\n\n /** Compute the plot rectangle from canvas size minus padding. */\n private plotRect(): PlotRect {\n const [pt, pr, pb, pl] = this.opts.padding;\n return {\n x: pl,\n y: pt,\n w: this.surface.cssW - pl - pr,\n h: this.surface.cssH - pt - pb,\n };\n }\n\n /** Mark dirty and schedule/perform a draw per the autoDraw policy. */\n private invalidate(): void {\n this.dirty = true;\n if (!this.autoDraw || this.suspendCount > 0) return;\n if (this.rafScheduled) return;\n this.rafScheduled = requestAnimationFrame(() => {\n this.rafScheduled = 0;\n this.draw();\n });\n }\n\n private storeAt(index: number): SeriesStore {\n const s = this.stores[index];\n if (!s) throw new Error(`series index ${index} out of range (${this.stores.length} series)`);\n return s;\n }\n\n private injectCursor(xVal: number): void {\n const dom = this.gridDomainLeft;\n if (xVal < dom.xMin || xVal > dom.xMax) {\n this.injectCursorLeave();\n return;\n }\n this.cursorX = xToPx(xVal, dom, this.plotRect());\n this.showCrosshair = true;\n this.draw();\n }\n\n private injectCursorLeave(): void {\n this.showCrosshair = false;\n this.draw();\n }\n\n private attachEvents(): void {\n this.resizeObserver = new ResizeObserver(this.handleResize);\n this.resizeObserver.observe(this.surface.canvas);\n this.surface.canvas.addEventListener('mousemove', this.handleMouseMove);\n this.surface.canvas.addEventListener('mouseleave', this.handleMouseLeave);\n this.surface.canvas.addEventListener('keydown', this.handleKeyDown);\n }\n\n private onResize(): void {\n if (this.surface.measure()) {\n this.dirty = true;\n this.draw();\n }\n }\n\n private onMouseMove(e: MouseEvent): void {\n const rect = this.surface.canvas.getBoundingClientRect();\n this.cursorX = e.clientX - rect.left;\n this.cursorY = e.clientY - rect.top;\n this.showCrosshair = true;\n this.draw();\n this.notifySyncCrosshair();\n }\n\n private onMouseLeave(): void {\n this.showCrosshair = false;\n this.draw();\n this.notifySyncCrosshairLeave();\n }\n}\n","/**\n * @file The series line, drawn as a single batched stroke.\n *\n * Two regimes share one path:\n *\n * - Dense (count > 2·plotW): per-pixel-column min/max decimation. Drawing\n * every point would smear into a solid band and alias badly; instead each\n * pixel column collapses to first→min→max→last, joined so adjacent columns\n * form one continuous ribbon (the signal's visual envelope). Collapses\n * 500k points to ~2·width segments.\n *\n * - Sparse: the real polyline, point for point.\n *\n * Both walk logical order over physical storage using a `toWrap` countdown\n * instead of a modulo per point (snapshot mode never wraps; ring mode wraps\n * once). The scale arithmetic is inlined for hot-loop speed.\n */\n\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** Render the series line into `ctx` for the given view and plot rect. */\nexport function renderLine(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n const { xArr, yArr, count: n, cap } = view;\n if (n === 0) return;\n\n ctx.strokeStyle = opts.lineColor;\n ctx.lineWidth = opts.lineWidth;\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n ctx.beginPath();\n\n const xRange = view.xMax - view.xMin;\n const yRange = view.yMax - view.yMin;\n const xScale = xRange > 0 ? plot.w / xRange : 0;\n const xOff = plot.x - view.xMin * xScale;\n const yScale = yRange > 0 ? plot.h / yRange : 0;\n const yOff = plot.y + plot.h + view.yMin * yScale;\n\n let p = view.head;\n let toWrap = cap - view.head;\n\n if (n > plot.w * 2) {\n let col = -1;\n let colMinY = 0;\n let colMaxY = 0;\n let colFirstY = 0;\n let colLastY = 0;\n let started = false;\n\n const flush = (cx: number) => {\n ctx.lineTo(cx, colFirstY);\n ctx.lineTo(cx, colMinY);\n ctx.lineTo(cx, colMaxY);\n ctx.lineTo(cx, colLastY);\n };\n\n for (let i = 0; i < n; i++) {\n const px = xOff + xArr[p] * xScale;\n const c = px | 0;\n const py = yOff - yArr[p] * yScale;\n\n if (c !== col) {\n if (started) flush(col + 0.5);\n else {\n ctx.moveTo(c + 0.5, py);\n started = true;\n }\n col = c;\n colMinY = py;\n colMaxY = py;\n colFirstY = py;\n colLastY = py;\n } else {\n if (py < colMinY) colMinY = py;\n if (py > colMaxY) colMaxY = py;\n colLastY = py;\n }\n\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n if (started) flush(col + 0.5);\n } else {\n ctx.moveTo(xOff + xArr[p] * xScale, yOff - yArr[p] * yScale);\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n for (let i = 1; i < n; i++) {\n ctx.lineTo(xOff + xArr[p] * xScale, yOff - yArr[p] * yScale);\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n }\n\n ctx.stroke();\n}\n","/**\n * @file Line chart — the default series drawn as a single batched polyline.\n *\n * Supports the decimation auto-switch: dense data collapses to a per-pixel\n * min/max envelope ribbon; sparse data draws the real polyline. For the\n * full algorithm details see {@link renderLine}.\n */\n\nimport { ChartBase } from './chart-base.ts';\nimport { renderLine } from '../render/line.ts';\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** High-performance Canvas 2D line chart. */\nexport class LineChart extends ChartBase {\n protected renderSeries(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n renderLine(ctx, view, plot, opts);\n }\n}\n","/**\n * @file The area fill + top-line stroke, drawn as separate batched paths.\n *\n * Shares the same per-pixel-column min/max decimation as {@link renderLine}.\n * Two paths are built from the same data so the fill covers the region below\n * the envelope while the stroke only traces the visible top line — the bottom\n * and side closure edges are never stroked.\n *\n * In the decimated regime each column's first→min→max→last edge pair is\n * recorded during the single pass (~plot-width entries, bounded); the filled\n * closure drops to the plot bottom, sweeps left, and rises to the start.\n */\n\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/**\n * One decimated pixel column. 5 numbers per column: centre-x and the four Y\n * values that trace the envelope in order (first → min → max → last).\n * Maximum ~plot.w entries — a few hundred, not half a million.\n */\ntype ColData = [number, number, number, number, number];\n\n/** Render the filled area into `ctx` for the given view and plot rect. */\nexport function renderArea(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n const { xArr, yArr, count: n, cap } = view;\n if (n === 0) return;\n\n const xRange = view.xMax - view.xMin;\n const yRange = view.yMax - view.yMin;\n const xScale = xRange > 0 ? plot.w / xRange : 0;\n const xOff = plot.x - view.xMin * xScale;\n const yScale = yRange > 0 ? plot.h / yRange : 0;\n const yOff = plot.y + plot.h + view.yMin * yScale;\n const bottomY = plot.y + plot.h;\n\n let p = view.head;\n let toWrap = cap - view.head;\n\n ctx.lineJoin = 'round';\n\n const N_DENSE = plot.w * 2;\n\n if (n > N_DENSE) {\n // ---- Decimated: one pass collecting column data, then fill + stroke separately\n const cols: ColData[] = [];\n let ci = -1;\n let cMinY = 0;\n let cMaxY = 0;\n let cFirstY = 0;\n let cLastY = 0;\n\n for (let i = 0; i < n; i++) {\n const px = xOff + xArr[p] * xScale;\n const c = px | 0;\n const py = yOff - yArr[p] * yScale;\n\n if (c !== ci) {\n if (ci >= 0) cols.push([ci + 0.5, cFirstY, cMinY, cMaxY, cLastY]);\n ci = c;\n cMinY = cMaxY = cFirstY = cLastY = py;\n } else {\n if (py < cMinY) cMinY = py;\n if (py > cMaxY) cMaxY = py;\n cLastY = py;\n }\n\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n if (ci >= 0) cols.push([ci + 0.5, cFirstY, cMinY, cMaxY, cLastY]);\n\n if (cols.length === 0) return;\n\n const firstCol = cols[0];\n const lastCol = cols[cols.length - 1];\n\n // Fill: closed path (envelope + bottom edges)\n ctx.beginPath();\n ctx.moveTo(firstCol[0], firstCol[1]);\n for (let i = 0; i < cols.length; i++) {\n const [, fy, minY, maxY, ly] = cols[i];\n ctx.lineTo(cols[i][0], fy);\n ctx.lineTo(cols[i][0], minY);\n ctx.lineTo(cols[i][0], maxY);\n ctx.lineTo(cols[i][0], ly);\n }\n ctx.lineTo(lastCol[0], bottomY);\n ctx.lineTo(firstCol[0], bottomY);\n ctx.closePath();\n\n ctx.fillStyle = opts.fillColor;\n if (opts.fillOpacity < 1) ctx.globalAlpha = opts.fillOpacity;\n ctx.fill();\n ctx.globalAlpha = 1;\n\n // Stroke: open path (top envelope only, no bottom edges)\n ctx.beginPath();\n ctx.strokeStyle = opts.lineColor;\n ctx.lineWidth = opts.lineWidth;\n ctx.lineCap = 'round';\n ctx.moveTo(firstCol[0], firstCol[1]);\n for (let i = 0; i < cols.length; i++) {\n const [, fy, minY, maxY, ly] = cols[i];\n ctx.lineTo(cols[i][0], fy);\n ctx.lineTo(cols[i][0], minY);\n ctx.lineTo(cols[i][0], maxY);\n ctx.lineTo(cols[i][0], ly);\n }\n ctx.stroke();\n } else {\n // ---- Sparse: two passes (n is small, ≤ 2×plot.w)\n // First pass: collect the polyline\n type Pt = [number, number];\n const pts: Pt[] = Array(n);\n p = view.head;\n toWrap = cap - view.head;\n for (let i = 0; i < n; i++) {\n pts[i] = [xOff + xArr[p] * xScale, yOff - yArr[p] * yScale];\n if (--toWrap === 0) {\n p = 0;\n toWrap = cap;\n } else p++;\n }\n\n const firstX = pts[0][0];\n const lastX = pts[n - 1][0];\n\n // Fill: closed path\n ctx.beginPath();\n ctx.moveTo(pts[0][0], pts[0][1]);\n for (let i = 1; i < n; i++) ctx.lineTo(pts[i][0], pts[i][1]);\n ctx.lineTo(lastX, bottomY);\n ctx.lineTo(firstX, bottomY);\n ctx.closePath();\n\n ctx.fillStyle = opts.fillColor;\n if (opts.fillOpacity < 1) ctx.globalAlpha = opts.fillOpacity;\n ctx.fill();\n ctx.globalAlpha = 1;\n\n // Stroke: open path (top line only)\n ctx.beginPath();\n ctx.strokeStyle = opts.lineColor;\n ctx.lineWidth = opts.lineWidth;\n ctx.lineCap = 'round';\n ctx.moveTo(pts[0][0], pts[0][1]);\n for (let i = 1; i < n; i++) ctx.lineTo(pts[i][0], pts[i][1]);\n ctx.stroke();\n }\n}\n","/**\n * @file Area chart — filled region below the series line.\n *\n * Same batched path + decimation as {@link LineChart}, but the path is closed\n * to the plot bottom so a single `fill()` paints the area, with the stroke\n * line drawn on top for readability even at low fill opacity.\n */\n\nimport { ChartBase } from './chart-base.ts';\nimport { renderArea } from '../render/area.ts';\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** High-performance Canvas 2D area chart. */\nexport class AreaChart extends ChartBase {\n protected renderSeries(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n renderArea(ctx, view, plot, opts);\n }\n}\n","/**\n * @file Scatter plot — one circle per (sampled) point.\n *\n * When the dataset is far denser than `maxDots` every `floor(n / maxDots)`-th\n * point is drawn (stride thinning) so the chart stays responsive. Thinning\n * skips the binary-search overhead of true polyline decimation; scatter\n * doesn't benefit from an envelope anyway because each point carries its own\n * visual weight.\n */\n\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** Advance a physical index by `k` positions, modulo `cap`. */\nfunction advance(p: number, k: number, cap: number): number {\n const next = p + k;\n return next >= cap ? next - cap : next;\n}\n\nexport function renderScatter(\n ctx: CanvasRenderingContext2D,\n view: SeriesView,\n plot: PlotRect,\n opts: ResolvedOpts,\n): void {\n const { xArr, yArr, count: n, cap } = view;\n if (n === 0) return;\n\n const xRange = view.xMax - view.xMin;\n const yRange = view.yMax - view.yMin;\n const xScale = xRange > 0 ? plot.w / xRange : 0;\n const xOff = plot.x - view.xMin * xScale;\n const yScale = yRange > 0 ? plot.h / yRange : 0;\n const yOff = plot.y + plot.h + view.yMin * yScale;\n\n const maxDots = opts.maxDots;\n const step = n > maxDots ? Math.max(1, Math.floor(n / maxDots)) : 1;\n const r = opts.pointRadius;\n\n let p = view.head;\n\n ctx.fillStyle = opts.lineColor;\n ctx.beginPath();\n\n for (let i = 0; i < n; i += step) {\n const px = xOff + xArr[p] * xScale;\n const py = yOff - yArr[p] * yScale;\n ctx.moveTo(px + r, py);\n ctx.arc(px, py, r, 0, Math.PI * 2);\n p = advance(p, step, cap);\n }\n ctx.fill();\n}\n","/**\n * @file Scatter chart — one filled circle per (sampled) point.\n *\n * Uses stride thinning via {@link renderScatter}: when the dataset exceeds\n * `maxDots` every Nth point is drawn so the chart stays responsive. The\n * thinning step is `floor(n / maxDots)`.\n */\n\nimport { ChartBase } from './chart-base.ts';\nimport { renderScatter } from '../render/scatter.ts';\nimport type { SeriesView, PlotRect, ResolvedOpts } from '../types.ts';\n\n/** High-performance Canvas 2D scatter chart. */\nexport class ScatterChart extends ChartBase {\n protected renderSeries(ctx: CanvasRenderingContext2D, view: SeriesView, plot: PlotRect, opts: ResolvedOpts): void {\n renderScatter(ctx, view, plot, opts);\n }\n}\n","/**\n * @file Ready-to-use colour presets for quick theming.\n *\n * Spread over any chart's constructor options to override colours without\n * assembling every value manually.\n *\n * Usage:\n * ```ts\n * import { LineChart, DARK } from 'goro-charts'\n * new LineChart(canvas, { ...DARK, series: [...] })\n * ```\n */\n\nimport type { ChartOpts } from './types.ts';\n\n/** Dark theme preset. */\nexport const DARK: ChartOpts = {\n gridColor: 'rgba(255,255,255,0.08)',\n axisColor: 'rgba(255,255,255,0.25)',\n textColor: 'rgba(255,255,255,0.5)',\n crosshairColor: 'rgba(255,255,255,0.3)',\n pointColor: '#4ea8ff',\n bgColor: '#111',\n};\n\n/** Light theme preset. */\nexport const LIGHT: ChartOpts = {\n gridColor: 'rgba(0,0,0,0.08)',\n axisColor: 'rgba(0,0,0,0.18)',\n textColor: 'rgba(0,0,0,0.55)',\n crosshairColor: 'rgba(0,0,0,0.18)',\n pointColor: '#2563eb',\n bgColor: '#fff',\n};\n"],"mappings":"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"}
|