@dou-ui/chart 0.0.0-stage → 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +10 -2
- package/dist/chart.js +6 -0
- package/dist/chart.js.map +1 -0
- package/dist/chart.vue_vue_type_script_setup_true_lang.js +4245 -0
- package/dist/chart.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/area.js +6 -0
- package/dist/components/area.js.map +1 -0
- package/dist/components/area.vue_vue_type_script_setup_true_lang.js +55 -0
- package/dist/components/area.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/axis.js +6 -0
- package/dist/components/axis.js.map +1 -0
- package/dist/components/axis.vue_vue_type_script_setup_true_lang.js +46 -0
- package/dist/components/axis.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/bar.js +6 -0
- package/dist/components/bar.js.map +1 -0
- package/dist/components/bar.vue_vue_type_script_setup_true_lang.js +46 -0
- package/dist/components/bar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/boxplot.js +6 -0
- package/dist/components/boxplot.js.map +1 -0
- package/dist/components/boxplot.vue_vue_type_script_setup_true_lang.js +42 -0
- package/dist/components/boxplot.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/brush.js +6 -0
- package/dist/components/brush.js.map +1 -0
- package/dist/components/brush.vue_vue_type_script_setup_true_lang.js +42 -0
- package/dist/components/brush.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/funnel.js +6 -0
- package/dist/components/funnel.js.map +1 -0
- package/dist/components/funnel.vue_vue_type_script_setup_true_lang.js +41 -0
- package/dist/components/funnel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/gauge.js +6 -0
- package/dist/components/gauge.js.map +1 -0
- package/dist/components/gauge.vue_vue_type_script_setup_true_lang.js +91 -0
- package/dist/components/gauge.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/heatmap.js +6 -0
- package/dist/components/heatmap.js.map +1 -0
- package/dist/components/heatmap.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/components/heatmap.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/histogram.js +6 -0
- package/dist/components/histogram.js.map +1 -0
- package/dist/components/histogram.vue_vue_type_script_setup_true_lang.js +61 -0
- package/dist/components/histogram.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/line.js +6 -0
- package/dist/components/line.js.map +1 -0
- package/dist/components/line.vue_vue_type_script_setup_true_lang.js +52 -0
- package/dist/components/line.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/marker.js +6 -0
- package/dist/components/marker.js.map +1 -0
- package/dist/components/marker.vue_vue_type_script_setup_true_lang.js +31 -0
- package/dist/components/marker.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/pie.js +6 -0
- package/dist/components/pie.js.map +1 -0
- package/dist/components/pie.vue_vue_type_script_setup_true_lang.js +42 -0
- package/dist/components/pie.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/radar.js +6 -0
- package/dist/components/radar.js.map +1 -0
- package/dist/components/radar.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/components/radar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/range.js +6 -0
- package/dist/components/range.js.map +1 -0
- package/dist/components/range.vue_vue_type_script_setup_true_lang.js +27 -0
- package/dist/components/range.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/scatter.js +6 -0
- package/dist/components/scatter.js.map +1 -0
- package/dist/components/scatter.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/components/scatter.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/sunburst.js +6 -0
- package/dist/components/sunburst.js.map +1 -0
- package/dist/components/sunburst.vue_vue_type_script_setup_true_lang.js +36 -0
- package/dist/components/sunburst.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/tooltip.js +6 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/components/tooltip.vue_vue_type_script_setup_true_lang.js +31 -0
- package/dist/components/tooltip.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/treemap.js +6 -0
- package/dist/components/treemap.js.map +1 -0
- package/dist/components/treemap.vue_vue_type_script_setup_true_lang.js +33 -0
- package/dist/components/treemap.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/waterfall.js +6 -0
- package/dist/components/waterfall.js.map +1 -0
- package/dist/components/waterfall.vue_vue_type_script_setup_true_lang.js +49 -0
- package/dist/components/waterfall.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/zoom.js +6 -0
- package/dist/components/zoom.js.map +1 -0
- package/dist/components/zoom.vue_vue_type_script_setup_true_lang.js +37 -0
- package/dist/components/zoom.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/context.js +30 -0
- package/dist/context.js.map +1 -0
- package/dist/core/bar.js +223 -0
- package/dist/core/bar.js.map +1 -0
- package/dist/core/box.js +150 -0
- package/dist/core/box.js.map +1 -0
- package/dist/core/colors.js +58 -0
- package/dist/core/colors.js.map +1 -0
- package/dist/core/data.js +257 -0
- package/dist/core/data.js.map +1 -0
- package/dist/core/decimate.js +211 -0
- package/dist/core/decimate.js.map +1 -0
- package/dist/core/format.js +151 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/funnel.js +68 -0
- package/dist/core/funnel.js.map +1 -0
- package/dist/core/grid.js +87 -0
- package/dist/core/grid.js.map +1 -0
- package/dist/core/heatmap.js +187 -0
- package/dist/core/heatmap.js.map +1 -0
- package/dist/core/histogram.js +81 -0
- package/dist/core/histogram.js.map +1 -0
- package/dist/core/hit.js +115 -0
- package/dist/core/hit.js.map +1 -0
- package/dist/core/labels.js +141 -0
- package/dist/core/labels.js.map +1 -0
- package/dist/core/layout.js +1002 -0
- package/dist/core/layout.js.map +1 -0
- package/dist/core/line.js +230 -0
- package/dist/core/line.js.map +1 -0
- package/dist/core/marker.js +205 -0
- package/dist/core/marker.js.map +1 -0
- package/dist/core/model.js +391 -0
- package/dist/core/model.js.map +1 -0
- package/dist/core/painter.js +79 -0
- package/dist/core/painter.js.map +1 -0
- package/dist/core/pattern.js +102 -0
- package/dist/core/pattern.js.map +1 -0
- package/dist/core/pie.js +119 -0
- package/dist/core/pie.js.map +1 -0
- package/dist/core/radar.js +83 -0
- package/dist/core/radar.js.map +1 -0
- package/dist/core/scale.js +149 -0
- package/dist/core/scale.js.map +1 -0
- package/dist/core/scatter.js +263 -0
- package/dist/core/scatter.js.map +1 -0
- package/dist/core/svg.js +209 -0
- package/dist/core/svg.js.map +1 -0
- package/dist/core/sync.js +21 -0
- package/dist/core/sync.js.map +1 -0
- package/dist/core/time.js +217 -0
- package/dist/core/time.js.map +1 -0
- package/dist/core/tree.js +348 -0
- package/dist/core/tree.js.map +1 -0
- package/dist/core/warn.js +11 -0
- package/dist/core/warn.js.map +1 -0
- package/dist/core/waterfall.js +134 -0
- package/dist/core/waterfall.js.map +1 -0
- package/dist/core/window.js +94 -0
- package/dist/core/window.js.map +1 -0
- package/dist/define.js +62 -0
- package/dist/define.js.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/locale.js +170 -0
- package/dist/locale.js.map +1 -0
- package/dist/overlay.js +6 -0
- package/dist/overlay.js.map +1 -0
- package/dist/overlay.vue_vue_type_script_setup_true_lang.js +207 -0
- package/dist/overlay.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/pointer.js +6 -0
- package/dist/pointer.js.map +1 -0
- package/dist/pointer.vue_vue_type_script_setup_true_lang.js +136 -0
- package/dist/pointer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/static.js +6 -0
- package/dist/static.js.map +1 -0
- package/dist/static.vue_vue_type_script_setup_true_lang.js +34 -0
- package/dist/static.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/style.css +1 -0
- package/dist/style.d.ts +1 -0
- package/dist/style.js +3 -0
- package/dist/table.js +6 -0
- package/dist/table.js.map +1 -0
- package/dist/table.vue_vue_type_script_setup_true_lang.js +52 -0
- package/dist/table.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/types/chart.vue.d.ts +24 -0
- package/dist/types/components/area.vue.d.ts +16 -0
- package/dist/types/components/axis.vue.d.ts +8 -0
- package/dist/types/components/bar.vue.d.ts +14 -0
- package/dist/types/components/boxplot.vue.d.ts +14 -0
- package/dist/types/components/brush.vue.d.ts +22 -0
- package/dist/types/components/funnel.vue.d.ts +14 -0
- package/dist/types/components/gauge.vue.d.ts +17 -0
- package/dist/types/components/heatmap.vue.d.ts +14 -0
- package/dist/types/components/histogram.vue.d.ts +14 -0
- package/dist/types/components/line.vue.d.ts +16 -0
- package/dist/types/components/marker.vue.d.ts +6 -0
- package/dist/types/components/pie.vue.d.ts +23 -0
- package/dist/types/components/radar.vue.d.ts +16 -0
- package/dist/types/components/range.vue.d.ts +12 -0
- package/dist/types/components/scatter.vue.d.ts +14 -0
- package/dist/types/components/sunburst.vue.d.ts +17 -0
- package/dist/types/components/tooltip.vue.d.ts +13 -0
- package/dist/types/components/treemap.vue.d.ts +8 -0
- package/dist/types/components/waterfall.vue.d.ts +16 -0
- package/dist/types/components/zoom.vue.d.ts +18 -0
- package/dist/types/context.d.ts +87 -0
- package/dist/types/core/bar.d.ts +38 -0
- package/dist/types/core/box.d.ts +25 -0
- package/dist/types/core/colors.d.ts +16 -0
- package/dist/types/core/data.d.ts +72 -0
- package/dist/types/core/decimate.d.ts +39 -0
- package/dist/types/core/format.d.ts +33 -0
- package/dist/types/core/funnel.d.ts +15 -0
- package/dist/types/core/grid.d.ts +18 -0
- package/dist/types/core/heatmap.d.ts +17 -0
- package/dist/types/core/histogram.d.ts +23 -0
- package/dist/types/core/hit.d.ts +31 -0
- package/dist/types/core/labels.d.ts +45 -0
- package/dist/types/core/layout.d.ts +259 -0
- package/dist/types/core/line.d.ts +36 -0
- package/dist/types/core/marker.d.ts +67 -0
- package/dist/types/core/model.d.ts +86 -0
- package/dist/types/core/painter.d.ts +34 -0
- package/dist/types/core/pattern.d.ts +14 -0
- package/dist/types/core/pie.d.ts +66 -0
- package/dist/types/core/radar.d.ts +12 -0
- package/dist/types/core/scale.d.ts +60 -0
- package/dist/types/core/scatter.d.ts +16 -0
- package/dist/types/core/series.d.ts +228 -0
- package/dist/types/core/svg.d.ts +63 -0
- package/dist/types/core/sync.d.ts +8 -0
- package/dist/types/core/time.d.ts +20 -0
- package/dist/types/core/tree.d.ts +84 -0
- package/dist/types/core/warn.d.ts +4 -0
- package/dist/types/core/waterfall.d.ts +24 -0
- package/dist/types/core/window.d.ts +36 -0
- package/dist/types/define.d.ts +115 -0
- package/dist/types/index.d.ts +363 -0
- package/dist/types/locale.d.ts +100 -0
- package/dist/types/overlay.vue.d.ts +95 -0
- package/dist/types/pointer.vue.d.ts +53 -0
- package/dist/types/static.vue.d.ts +24 -0
- package/dist/types/table.vue.d.ts +19 -0
- package/dist/types/types.d.ts +555 -0
- package/dist/types/zoom-layer.vue.d.ts +41 -0
- package/dist/zoom-layer.js +6 -0
- package/dist/zoom-layer.js.map +1 -0
- package/dist/zoom-layer.vue_vue_type_script_setup_true_lang.js +210 -0
- package/dist/zoom-layer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/package.json +53 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","names":[],"sources":["../../src/core/layout.ts"],"sourcesContent":["import { toTime, type XKind } from './data'\nimport { numberTickFormat, timeTickFormat } from './format'\nimport type { ChartModel } from './model'\nimport type { Rect } from './painter'\nimport {\n categoryScale,\n categoryTicks,\n linearDomain,\n linearScale,\n linearTicks,\n logDomain,\n logScale,\n logTicks,\n nextNiceStep,\n niceStep,\n stepDecimals,\n type Scale,\n} from './scale'\nimport type { ChartValueAxis } from './series'\nimport { timeInterval, timeTicks } from './time'\nimport { unitsOf, type Window } from './window'\n\n/** Where an axis is drawn. */\nexport type ChartAxisPosition = 'bottom' | 'top' | 'left' | 'right'\n/** What an axis measures: x, the values, or a second kind of values. */\nexport type ChartAxisRole = 'x' | ChartValueAxis\nexport type ChartAxisType = 'linear' | 'log' | 'time' | 'category'\n\n/** A tick value as `format` receives it: a number, a `Date` on time axes, the category on category axes. */\nexport type ChartTickValue = number | Date | string\n\n/** An axis as configured, with defaults filled in. `position` is where it is drawn. */\nexport interface AxisConfig {\n position: ChartAxisPosition\n type?: ChartAxisType\n min?: number | string | Date\n max?: number | string | Date\n zero?: boolean\n nice?: boolean\n ticks?: number | Array<number | string | Date>\n format?: (value: any) => string\n grid: boolean\n title?: string\n hidden: boolean\n /** The room for tick labels down the side, in px, whatever they measure. */\n labelWidth?: number\n /** Runs the other way: its scale's range is turned round. */\n reverse?: boolean\n}\n\n/** `range` as the axis runs: turned round when it is reversed. */\nconst runs = (config: AxisConfig | null | undefined, range: [number, number]): [number, number] =>\n config?.reverse ? [range[1], range[0]] : range\n\n/** The axes by what they measure. */\nexport interface ChartAxes {\n x: AxisConfig\n y: AxisConfig | null\n y2: AxisConfig | null\n}\n\nexport interface TickLabel {\n text: string\n /** Where the label's box goes, in px from the chart's top left. */\n left: number\n top: number\n width: number\n /** Whether the text is longer than `width` and is cut short with an ellipsis. */\n clipped?: boolean\n}\n\nexport interface AxisLayout {\n /** Where the axis is drawn. */\n position: ChartAxisPosition\n scale: Scale\n /** Tick values, as numbers on the scale. */\n ticks: number[]\n labels: TickLabel[]\n grid: boolean\n title?: { text: string; left: number; top: number; align: 'start' | 'end' }\n /** The whole domain, when zoomed in to part of it (`scale.domain`). */\n full: [number, number]\n}\n\nexport interface ChartLayout {\n width: number\n height: number\n plot: Rect\n x: AxisLayout\n /** The value axes, by what they measure. */\n y: Partial<Record<ChartValueAxis, AxisLayout>>\n legend: LegendItem[]\n /** The room the legend shows in; its entries scroll in it when they don't all fit. */\n legendBox?: LegendBox\n /** Where the range strip goes: under the chart, or down its right side when horizontal. */\n strip?: Rect\n}\n\n/** Where a chart's legend goes. */\nexport type ChartLegendPlacement = 'top' | 'bottom' | 'right'\n\n/** The room a legend shows in, in px from the chart's top left. */\nexport interface LegendBox extends Rect {\n /** Its entries take more room than it has: they scroll in it. */\n scroll: boolean\n}\n\n/** A legend entry, placed. */\nexport interface LegendItem {\n /** The entry's position in the names given. */\n index: number\n text: string\n left: number\n top: number\n width: number\n /** The name is longer than the room down the side, and is cut short. */\n clipped?: boolean\n /** The swatch's width, when wider than a series' (a heatmap's scale). */\n swatch?: number\n}\n\n/** A legend entry's name, or its name and a wider swatch (a heatmap's scale). */\nexport type LegendName = string | { text: string; swatch: number }\n\nconst nameText = (name: LegendName) => (typeof name === 'string' ? name : name.text)\nconst swatchOf = (name: LegendName) => (typeof name === 'string' ? SWATCH : name.swatch)\n\n/** Height of a line of tick text, and the room between it and the plot. */\nexport const LABEL_HEIGHT = 16\nexport const LABEL_GAP = 8\n/** Room left around the plot so lines and dots at the edge aren't cut. */\nexport const EDGE = 8\n/** Room between text and the chart's sides. */\nexport const INSET = 4\n/** A legend entry: the swatch, the room after it, and the room to the next entry. */\nconst SWATCH = 12\nconst SWATCH_GAP = 6\n/** A heatmap's scale in the legend: wide enough to read from light to dark. */\nexport const HEAT_SWATCH = 40\nconst LEGEND_GAP = 16\nconst LEGEND_ROW_GAP = 4\n/** Rows 40px apart on a touch screen: each entry's hit area reaches 12px past its text, above and below. */\nconst LEGEND_ROW_GAP_COARSE = 24\n/** The most rows a legend above or below the chart shows; more scroll. */\nconst LEGEND_ROWS = 3\n/** The most of the chart's width a legend down the side may take. */\nconst LEGEND_SIDE_SHARE = 0.3\n/** The range strip's height (its width when horizontal), and the room before it. */\nexport const STRIP = 32\nconst STRIP_GAP = 12\n/** Wanted room between ticks, in px. */\nconst X_TICK_SPACE = 64\nconst Y_TICK_SPACE = 48\n/** Room between labels stacked down an axis. */\nconst ROW_GAP = 4\n/** The most of the chart's width that category names down the side may take. */\nconst SIDE_LABEL_SHARE = 0.4\n\ninterface LayoutOptions {\n model: ChartModel\n axes: ChartAxes\n /** x runs down the left side and values along the bottom (and the top). */\n horizontal?: boolean\n width: number\n height: number\n locale: string\n measure: (text: string) => number\n /** Names for a legend, or none. */\n legend?: LegendName[]\n /** Rows of the legend far enough apart for a finger to press each (on a touch screen). */\n coarse?: boolean\n /** Where the legend goes: under the chart (the default), above it, or down its right side. */\n legendPlacement?: ChartLegendPlacement\n /**\n * The stretches shown when zoomed in: of x in the scale's numbers\n * (category indices, ms), of the values in those of `y` (or `y2` when\n * it is alone; `y2` follows `y`).\n */\n zoom?: { x?: Window | null; y?: Window | null }\n /** Leave room for a range strip. */\n strip?: boolean\n /** Room past the plot's end along x, in px, for labels at the ends of lines. */\n trailing?: number\n}\n\nconst asNumber = (value: number | string | Date | undefined, kind: XKind | 'log') =>\n value === undefined ? undefined : kind === 'time' ? toTime(value) : Number(value)\n\nconst clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v))\n\n/** Widens `[lo, hi]` so `inset` px stay free at both ends of an axis `length` px long. */\nfunction widen(lo: number, hi: number, inset: number, length: number): [number, number] {\n if (!(inset > 0) || !(hi > lo)) return [lo, hi]\n const pad = ((hi - lo) * inset) / Math.max(1, length - inset * 2)\n return [lo - pad, hi + pad]\n}\n\n/** Places the axes and works out the plot area, the scales and the tick labels. */\nexport function layoutChart(options: LayoutOptions): ChartLayout {\n const { width, height, measure } = options\n const placed = placeLegendIn(\n options.legend ?? [],\n options.legendPlacement ?? 'bottom',\n width,\n height,\n measure,\n )\n const { legend, box: legendBox } = placed\n const { bottom: legendHeight, top: legendTop, right: legendRight } = placed.room\n const room = options.strip ? STRIP + STRIP_GAP : 0\n if (options.horizontal) {\n // the strip down the right side, as tall as the plot\n const inner = width - room - legendRight\n const layout = layoutAcross({ ...options, width: inner }, legendHeight, legendTop)\n const { plot } = layout\n const strip = room\n ? { x: inner + STRIP_GAP, y: plot.y, width: STRIP, height: plot.height }\n : undefined\n return { ...layout, width, legend, legendBox, strip }\n }\n // the strip under the x axis, above the legend\n const layout = layoutUp(\n { ...options, width: width - legendRight },\n legendHeight + room,\n legendTop,\n )\n const { plot } = layout\n const strip = room\n ? { x: plot.x, y: height - legendHeight - STRIP, width: plot.width, height: STRIP }\n : undefined\n return { ...layout, width, legend, legendBox, strip }\n}\n\n/**\n * Places a legend's entries where it goes: in rows under or above the\n * chart, or one under another down its right side. Gives the room it\n * takes from the chart on each side, and the box it shows in: past three\n * rows (or the chart's height, down the side), its entries scroll.\n */\nexport function placeLegendIn(\n names: LegendName[],\n at: ChartLegendPlacement,\n width: number,\n height: number,\n measure: (text: string) => number,\n coarse = false,\n): { legend: LegendItem[]; box?: LegendBox; room: { top: number; bottom: number; right: number } } {\n const rowGap = coarse ? LEGEND_ROW_GAP_COARSE : LEGEND_ROW_GAP\n const room = { top: 0, bottom: 0, right: 0 }\n if (!names.length) return { legend: [], room }\n if (at === 'right') {\n const widest = Math.max(\n SWATCH + SWATCH_GAP + 24,\n Math.floor(width * LEGEND_SIDE_SHARE) - LEGEND_GAP,\n )\n const step = LABEL_HEIGHT + Math.max(rowGap, LEGEND_ROW_GAP + 4)\n const legend = names.map((name, index) => {\n const text = nameText(name)\n const full = swatchOf(name) + SWATCH_GAP + measure(text)\n const item: LegendItem = {\n index,\n text,\n left: 0,\n top: index * step,\n width: Math.min(full, widest),\n }\n if (full > widest) item.clipped = true\n if (typeof name !== 'string') item.swatch = name.swatch\n return item\n })\n const boxWidth = Math.max(...legend.map((item) => item.width))\n const content = (legend.length - 1) * step + LABEL_HEIGHT\n const boxHeight = Math.min(content, height - EDGE * 2)\n const box: LegendBox = {\n x: width - INSET - boxWidth,\n y: EDGE,\n width: boxWidth,\n height: boxHeight,\n scroll: content > boxHeight + 0.5,\n }\n for (const item of legend) {\n item.left += box.x\n item.top += box.y\n }\n room.right = boxWidth + LEGEND_GAP\n return { legend, box, room }\n }\n const legend = placeLegend(names, width, measure, coarse)\n const rowStep = LABEL_HEIGHT + rowGap\n const content = legend[legend.length - 1]!.top + LABEL_HEIGHT\n const boxHeight = Math.min(content, (LEGEND_ROWS - 1) * rowStep + LABEL_HEIGHT)\n const gap = LABEL_GAP + 4\n const y = at === 'top' ? INSET : height - boxHeight\n for (const item of legend) item.top += y\n const box: LegendBox = { x: 0, y, width, height: boxHeight, scroll: content > boxHeight + 0.5 }\n if (at === 'top') room.top = INSET + boxHeight + gap - EDGE\n else room.bottom = boxHeight + gap\n return { legend, box, room }\n}\n\nconst shown = (config: AxisConfig | null): config is AxisConfig => !!config && !config.hidden\n\n/**\n * The value axes for about `count` ticks; the second one gets as many\n * steps as the first, so both share one set of grid lines.\n */\nfunction valueAxes(\n axes: ChartAxes,\n model: ChartModel,\n count: number,\n locale: string,\n length: number,\n zoom?: Window | null,\n /** The values run across (a horizontal chart). */\n across = false,\n /** The most ticks whose labels fit, even when `ticks` asks for more. */\n most = Infinity,\n): { y: ValueAxis | null; y2: ValueAxis | null } {\n let y = axes.y ? valueAxis('y', axes.y, model, count, locale, length, across, most) : null\n let y2 = axes.y2 ? valueAxis('y2', axes.y2, model, count, locale, length, across, most) : null\n if (y && y2 && !axes.y2!.ticks && !y2.fixed && y.ticks.length > 1) {\n const both = alignZero(y, y2, axes.y!, axes.y2!, model, locale, length)\n if (both) ({ y, y2 } = both)\n else y2 = alignTo(y2, y.ticks.length - 1, axes.y2!, model, locale, length)\n }\n const primary = y ?? y2\n if (!zoom || !primary) return { y, y2 }\n // the same part of each axis: y2 follows y\n const units = unitsOf({ kind: primary.kind })\n const [d0, d1] = primary.domain.map(units.to) as Window\n const part: Window = [\n (units.to(zoom[0]) - d0) / (d1 - d0 || 1),\n (units.to(zoom[1]) - d0) / (d1 - d0 || 1),\n ]\n const zoomed = (axis: ValueAxis | null, config: AxisConfig | null) =>\n axis && config ? zoomAxis(axis, part, count, config, locale, most) : null\n return { y: zoomed(y, axes.y), y2: zoomed(y2, axes.y2) }\n}\n\n/** `axis` zoomed in to `part` of it (0 to 1 from its start), with ticks of its own. */\nfunction zoomAxis(\n axis: ValueAxis,\n part: Window,\n count: number,\n config: AxisConfig,\n locale: string,\n most = Infinity,\n): ValueAxis {\n const units = unitsOf({ kind: axis.kind })\n const [d0, d1] = axis.domain.map(units.to) as Window\n const domain: Window = [\n units.from(d0 + (d1 - d0) * part[0]),\n units.from(d0 + (d1 - d0) * part[1]),\n ]\n count = tickCount(config, count, most)\n const given = explicitTicks(config, axis.kind)\n const ticks = given\n ? given.filter((v) => v >= domain[0] && v <= domain[1])\n : axis.kind === 'log'\n ? logTicks(domain[0], domain[1], count)\n : linearTicks(domain[0], domain[1], count)\n return { ...axis, full: axis.domain, domain, ticks, texts: valueTexts(ticks, config, locale) }\n}\n\n/** The data span an axis has to show, with 0 when it starts at 0. */\nfunction extent(\n role: ChartValueAxis,\n config: AxisConfig,\n model: ChartModel,\n length: number,\n): [number, number] {\n const data = model.y[role]\n let [lo, hi] = data ? widen(data[0], data[1], model.inset, length) : [0, 1]\n if (config.zero ?? model.zero[role]) {\n lo = Math.min(lo, 0)\n hi = Math.max(hi, 0)\n }\n return [lo, hi]\n}\n\n/**\n * Two linear axes that both reach below 0: 0 on one line, so bars on\n * both start from the same baseline. Each gets nice steps of its own, the\n * same number of them below 0 and above, as few as cover both. `null`\n * when it doesn't apply (an end or the ticks given, a log axis, all data\n * on one side of 0, where aligning the steps already lines up 0).\n */\nfunction alignZero(\n y: ValueAxis,\n y2: ValueAxis,\n yConfig: AxisConfig,\n y2Config: AxisConfig,\n model: ChartModel,\n locale: string,\n length: number,\n): { y: ValueAxis; y2: ValueAxis } | null {\n if (y.kind === 'log' || y2.kind === 'log') return null\n const given = (c: AxisConfig) => c.min !== undefined || c.max !== undefined || !!c.ticks\n if (given(yConfig) || given(y2Config)) return null\n const spans = [extent('y', yConfig, model, length), extent('y2', y2Config, model, length)]\n if (!spans.some(([lo, hi]) => lo < 0 && hi > 0) || spans.some(([lo, hi]) => lo > 0 || hi < 0))\n return null\n const intervals = y.ticks.length - 1\n const steps = spans.map(([lo, hi]) => {\n const choices: number[] = []\n let step = niceStep(hi - lo || 1, intervals * 2)\n for (let i = 0; i < 6; i++, step = nextNiceStep(step)) choices.push(step)\n return choices\n })\n let best: { s: [number, number]; below: number; above: number; fill: number } | null = null\n for (const s1 of steps[0]!)\n for (const s2 of steps[1]!) {\n const s: [number, number] = [s1, s2]\n const below = Math.max(...spans.map(([lo], i) => Math.ceil(-lo / s[i]! - 1e-9)))\n const above = Math.max(...spans.map(([, hi], i) => Math.ceil(hi / s[i]! - 1e-9)))\n const total = below + above\n if (total < Math.max(2, intervals - 1) || total > intervals + 1) continue\n // how much of each axis the data fills; the emptier one counts\n const fill = Math.min(...spans.map(([lo, hi], i) => (hi - lo) / (s[i]! * total)))\n if (!best || fill > best.fill + 1e-9) best = { s, below, above, fill }\n }\n if (!best) return null\n const { s, below, above } = best\n const make = (axis: ValueAxis, config: AxisConfig, step: number): ValueAxis => {\n const decimals = stepDecimals(step)\n const ticks = Array.from({ length: below + above + 1 }, (_, i) =>\n Number(((i - below) * step).toFixed(decimals)),\n )\n return {\n ...axis,\n domain: [ticks[0]!, ticks[ticks.length - 1]!],\n ticks,\n texts: valueTexts(ticks, config, locale),\n }\n }\n return { y: make(y, yConfig, s[0]), y2: make(y2, y2Config, s[1]) }\n}\n\n/** x along the bottom, values up the left side (and the right). */\nfunction layoutUp(\n { model, axes, width, height, locale, measure, zoom, trailing = 0 }: LayoutOptions,\n legendHeight: number,\n /** Room taken at the top: a legend above the chart. */\n topRoom = 0,\n): Omit<ChartLayout, 'legend'> {\n const xAxis = axes.x\n const xShown = !xAxis.hidden\n const titleRoom = (shown(axes.y) && axes.y.title) || (shown(axes.y2) && axes.y2.title)\n // a title sits above the top tick label, which is centered on the plot's top edge\n const top = (titleRoom ? LABEL_HEIGHT + LABEL_GAP + LABEL_HEIGHT / 2 : EDGE) + topRoom\n const bottom =\n (xShown ? LABEL_HEIGHT + LABEL_GAP + (xAxis.title ? LABEL_HEIGHT + 4 : 0) : EDGE) + legendHeight\n const plotTop = top\n const plotBottom = Math.max(plotTop + 1, height - bottom)\n const plotHeight = plotBottom - plotTop\n\n // value axes first: their labels decide how wide the plot is\n const yCount = Math.max(2, Math.floor(plotHeight / Y_TICK_SPACE))\n // fewer and fewer ticks until their labels have a row each, whatever `ticks` asks\n const roomy = (axis: ValueAxis | null) => {\n // a heatmap's rows are named one each, as before: fewer ticks can't thin them\n if (!axis || axis.ticks.length < 2 || model.yCategories?.[axis.role]) return true\n const scale = valueScale(axis, [plotHeight, 0])\n // ticks run in order, so neighbors on the axis are neighbors in the list\n const at = axis.ticks.map((t) => scale.map(t))\n return at.every((v, i) => i === 0 || Math.abs(v - at[i - 1]!) >= LABEL_HEIGHT + ROW_GAP - 0.5)\n }\n const asked = [axes.y?.ticks, axes.y2?.ticks].filter((t): t is number => typeof t === 'number')\n // never more than there are rows for, however many are asked\n const rows = Math.max(2, Math.floor(plotHeight / (LABEL_HEIGHT + ROW_GAP)) + 1)\n let values = valueAxes(axes, model, yCount, locale, plotHeight, zoom?.y, false, rows)\n for (let most = Math.min(Math.max(yCount, ...asked), rows); most >= 1; most--) {\n values = valueAxes(axes, model, yCount, locale, plotHeight, zoom?.y, false, most)\n if (roomy(values.y) && roomy(values.y2)) break\n }\n // too short for even two: only the top one\n const topOnly = (axis: ValueAxis | null) => {\n if (!axis || roomy(axis)) return axis\n const last = axis.ticks.length - 1\n return { ...axis, ticks: [axis.ticks[last]!], texts: [axis.texts[last]!] }\n }\n const y = topOnly(values.y)\n const y2 = topOnly(values.y2)\n const side = (axis: ValueAxis | null, config: AxisConfig | null) =>\n axis && shown(config)\n ? INSET + (config.labelWidth ?? labelWidth(axis.texts, measure)) + LABEL_GAP\n : EDGE\n const leftWidth = side(y, axes.y)\n const rightWidth = side(y2, axes.y2) + trailing\n const plot: Rect = {\n x: leftWidth,\n y: plotTop,\n width: Math.max(1, width - leftWidth - rightWidth),\n height: plotHeight,\n }\n\n const yLayout: ChartLayout['y'] = {}\n for (const axis of [y, y2]) {\n if (!axis) continue\n const config = axes[axis.role]!\n const scale = valueScale(axis, runs(config, [plot.y + plot.height, plot.y]))\n const labels = config.hidden ? [] : placeDown(axis.ticks, axis.texts, scale, height, measure)\n for (const label of labels)\n if (axis.role === 'y') alignEnd(label, plot.x)\n else label.left = plot.x + plot.width + trailing + LABEL_GAP\n yLayout[axis.role] = {\n position: config.position,\n scale,\n ticks: axis.ticks,\n labels,\n grid: config.grid,\n full: axis.full ?? axis.domain,\n title:\n config.title && !config.hidden\n ? topTitle(config.title, axis.role === 'y2', width, topRoom)\n : undefined,\n }\n }\n\n const along: [number, number] = [plot.x, plot.x + plot.width]\n const count = Math.max(2, Math.floor(plot.width / X_TICK_SPACE))\n const { scale, full } = xScale(xAxis, model, runs(xAxis, along), count, zoom?.x)\n const labelTop = plot.y + plot.height + LABEL_GAP\n let x: AxisLayout | undefined\n for (const { ticks, texts } of xTickChoices(\n xAxis,\n model,\n scale,\n plot.width,\n X_TICK_SPACE,\n locale,\n )) {\n const labels = placeAcross(ticks, texts, scale, labelTop, width, measure)\n x = { position: xAxis.position, scale, ticks, labels, grid: xAxis.grid, full }\n if (fitAcross(labels)) break\n }\n x ??= { position: xAxis.position, scale, ticks: [], labels: [], grid: xAxis.grid, full }\n if (!xShown) x.labels = []\n else if (xAxis.title) x.title = bottomTitle(xAxis.title, width, height, legendHeight)\n\n return { width, height, plot, x, y: yLayout }\n}\n\n/**\n * x down the left side, values along the bottom (and a second kind along\n * the top): bars with long category names.\n */\nfunction layoutAcross(\n { model, axes, width, height, locale, measure, zoom }: LayoutOptions,\n legendHeight: number,\n /** Room taken at the top: a legend above the chart. */\n topRoom = 0,\n): Omit<ChartLayout, 'legend'> {\n const xAxis = axes.x\n const xShown = !xAxis.hidden\n const yShown = shown(axes.y)\n const y2Shown = shown(axes.y2)\n // at the top: a row of titles (x on the left, y2 on the right), then y2's labels\n const titles = (xShown && xAxis.title) || (y2Shown && axes.y2!.title)\n const titleRow = titles ? LABEL_HEIGHT + LABEL_GAP : 0\n const topLabels = y2Shown ? LABEL_HEIGHT + LABEL_GAP : 0\n const top = Math.max(EDGE, titleRow + topLabels) + topRoom\n const bottom =\n (yShown ? LABEL_HEIGHT + LABEL_GAP + (axes.y!.title ? LABEL_HEIGHT + 4 : 0) : EDGE) +\n legendHeight\n const plotTop = top\n const plotBottom = Math.max(plotTop + 1, height - bottom)\n const plotHeight = plotBottom - plotTop\n\n // x first: its labels decide how far in the plot starts\n const down: [number, number] = [plotTop, plotBottom]\n const { scale, full } = xScale(\n xAxis,\n model,\n runs(xAxis, down),\n Math.max(2, Math.floor(plotHeight / Y_TICK_SPACE)),\n zoom?.x,\n )\n let x: AxisLayout | undefined\n for (const { ticks, texts } of xTickChoices(\n xAxis,\n model,\n scale,\n plotHeight,\n Y_TICK_SPACE,\n locale,\n )) {\n const labels = placeDown(ticks, texts, scale, height, measure)\n x = { position: xAxis.position, scale, ticks, labels, grid: xAxis.grid, full }\n if (fitDown(labels)) break\n }\n x ??= { position: xAxis.position, scale, ticks: [], labels: [], grid: xAxis.grid, full }\n if (!xShown) x.labels = []\n // long names are cut short, so they can't squeeze the plot away\n const widest = xAxis.labelWidth ?? Math.floor(width * SIDE_LABEL_SHARE)\n for (const label of x.labels)\n if (label.width > widest) {\n label.width = widest\n label.clipped = true\n }\n const leftWidth = !xShown\n ? EDGE\n : xAxis.labelWidth !== undefined\n ? INSET + xAxis.labelWidth + LABEL_GAP\n : x.labels.length\n ? INSET + labelWidth(x.labels.map((l) => l.width)) + LABEL_GAP\n : EDGE\n\n let rightWidth = EDGE\n const plot: Rect = { x: leftWidth, y: plotTop, width: 1, height: plotHeight }\n const across = width - leftWidth - EDGE\n const place = (axis: ValueAxis | null, labelTop: number, visible: boolean) => {\n if (!axis) return null\n const valueScaleAcross = valueScale(axis, runs(axes[axis.role], [plot.x, plot.x + plot.width]))\n const labels = visible\n ? placeAcross(axis.ticks, axis.texts, valueScaleAcross, labelTop, width, measure)\n : []\n return { axis, scale: valueScaleAcross, labels }\n }\n const placeAll = (axes2: { y: ValueAxis | null; y2: ValueAxis | null }) => {\n plot.width = Math.max(1, width - leftWidth - rightWidth)\n return {\n y: place(axes2.y, plotBottom + LABEL_GAP, yShown),\n y2: place(axes2.y2, plotTop - LABEL_GAP - LABEL_HEIGHT, y2Shown),\n }\n }\n let values = valueAxes(axes, model, 2, locale, across, zoom?.y, true, 2)\n let placed = placeAll(values)\n // fewer and fewer ticks until their labels fit side by side, `ticks` given or not\n const asked = [axes.y?.ticks, axes.y2?.ticks].filter((t): t is number => typeof t === 'number')\n // never more than a label a line high could fit, however many are asked\n const start = Math.min(\n Math.max(2, Math.floor(across / X_TICK_SPACE), ...asked),\n Math.max(2, Math.floor(across / LABEL_HEIGHT)),\n )\n // the last labels are centered on the plot's right edge: leave them room\n const placeWithRoom = () => {\n rightWidth = EDGE\n let out = placeAll(values)\n let excess = 0\n for (const p of [out.y, out.y2]) {\n const last = p?.labels[p.labels.length - 1]\n if (p && last)\n excess = Math.max(\n excess,\n p.scale.map(p.axis.ticks[p.axis.ticks.length - 1]!) + last.width / 2 + INSET - width,\n )\n }\n if (excess > 0) {\n rightWidth += Math.ceil(excess)\n out = placeAll(values)\n }\n return out\n }\n for (let count = start; count >= 2; count--) {\n values = valueAxes(axes, model, count, locale, across, zoom?.y, true, count)\n placed = placeWithRoom()\n if ((!placed.y || fitAcross(placed.y.labels)) && (!placed.y2 || fitAcross(placed.y2.labels)))\n break\n }\n\n for (const label of x.labels) alignEnd(label, plot.x)\n x.title =\n xShown && xAxis.title\n ? { text: xAxis.title, left: INSET, top: topRoom, align: 'start' }\n : undefined\n const yLayout: ChartLayout['y'] = {}\n for (const role of ['y', 'y2'] as const) {\n const p = placed[role]\n const config = axes[role]\n if (!p || !config) continue\n yLayout[role] = {\n position: config.position,\n scale: p.scale,\n ticks: p.axis.ticks,\n labels: p.labels,\n grid: config.grid,\n full: p.axis.full ?? p.axis.domain,\n title:\n config.title && !config.hidden\n ? role === 'y'\n ? bottomTitle(config.title, width, height, legendHeight)\n : topTitle(config.title, true, width, topRoom)\n : undefined,\n }\n }\n return { width, height, plot, x, y: yLayout }\n}\n\n/** Puts a label's right edge a gap before `edge`. */\nfunction alignEnd(label: TickLabel, edge: number) {\n label.left = edge - LABEL_GAP - label.width\n}\n\n/** A title at the top: on the left, or (`end`) on the right. */\nfunction topTitle(text: string, end: boolean, width: number, top = 0): AxisLayout['title'] {\n return { text, top, left: end ? width - INSET : INSET, align: end ? 'end' : 'start' }\n}\n\nfunction bottomTitle(\n text: string,\n width: number,\n height: number,\n legendHeight: number,\n): AxisLayout['title'] {\n return { text, top: height - legendHeight - LABEL_HEIGHT, left: width - INSET, align: 'end' }\n}\n\n/** Labels centered under their ticks, kept inside the chart. */\nfunction placeAcross(\n ticks: number[],\n texts: string[],\n scale: Scale,\n top: number,\n width: number,\n measure: (text: string) => number,\n): TickLabel[] {\n return ticks.map((value, i) => {\n const text = texts[i]!\n const w = measure(text)\n return { text, width: w, left: clamp(scale.map(value) - w / 2, INSET, width - INSET - w), top }\n })\n}\n\n/** Labels centered beside their ticks, kept inside the chart; `left` is set by the caller. */\nfunction placeDown(\n ticks: number[],\n texts: string[],\n scale: Scale,\n height: number,\n measure: (text: string) => number,\n): TickLabel[] {\n return ticks.map((value, i) => {\n const text = texts[i]!\n return {\n text,\n width: measure(text),\n left: 0,\n top: clamp(scale.map(value) - LABEL_HEIGHT / 2, 0, height - LABEL_HEIGHT),\n }\n })\n}\n\nconst fitAcross = (labels: TickLabel[]) => {\n // left to right, whichever way the axis runs (a reversed one runs right to left)\n const list = [...labels]\n list.sort((a, b) => a.left - b.left)\n for (let i = 1; i < list.length; i++)\n if (list[i]!.left < list[i - 1]!.left + list[i - 1]!.width + LABEL_GAP) return false\n return true\n}\n\nconst fitDown = (labels: TickLabel[]) => {\n for (let i = 1; i < labels.length; i++)\n if (Math.abs(labels[i]!.top - labels[i - 1]!.top) < LABEL_HEIGHT + ROW_GAP) return false\n return true\n}\n\n/** Entries in rows that fit `width`, each row centered; `top` counts from the first row. */\nexport function placeLegend(\n names: LegendName[],\n width: number,\n measure: (text: string) => number,\n coarse = false,\n): LegendItem[] {\n const items: LegendItem[] = []\n let row: LegendItem[] = []\n let rowWidth = 0\n let top = 0\n const finish = () => {\n let left = Math.max(INSET, (width - rowWidth) / 2)\n for (const item of row) {\n item.left = left\n left += item.width + LEGEND_GAP\n }\n row = []\n rowWidth = 0\n }\n names.forEach((name, index) => {\n const text = nameText(name)\n const w = swatchOf(name) + SWATCH_GAP + measure(text)\n if (row.length && rowWidth + LEGEND_GAP + w > width - INSET * 2) {\n finish()\n top += LABEL_HEIGHT + (coarse ? LEGEND_ROW_GAP_COARSE : LEGEND_ROW_GAP)\n }\n const item: LegendItem = { index, text, left: 0, top, width: w }\n if (typeof name !== 'string') item.swatch = name.swatch\n rowWidth += (row.length ? LEGEND_GAP : 0) + w\n row.push(item)\n items.push(item)\n })\n if (row.length) finish()\n return items\n}\n\n/** The widest of `texts` (measured), or of `widths`. */\nfunction labelWidth(texts: string[], measure: (text: string) => number): number\nfunction labelWidth(widths: number[]): number\nfunction labelWidth(items: Array<string | number>, measure?: (text: string) => number): number {\n let widest = 0\n for (const item of items)\n widest = Math.max(widest, typeof item === 'number' ? item : measure!(item))\n return Math.ceil(widest)\n}\n\n/* value axes */\n\ninterface ValueAxis {\n /** What it measures. */\n role: ChartValueAxis\n kind: 'linear' | 'log'\n domain: [number, number]\n ticks: number[]\n texts: string[]\n /** Whether both ends were given, so it can't be aligned to another axis. */\n fixed: boolean\n /** The whole domain, when zoomed in. */\n full?: [number, number]\n}\n\nfunction valueScale(axis: ValueAxis, range: [number, number]): Scale {\n return axis.kind === 'log' ? logScale(axis.domain, range) : linearScale(axis.domain, range)\n}\n\nfunction valueAxis(\n role: ChartValueAxis,\n config: AxisConfig,\n model: ChartModel,\n count: number,\n locale: string,\n length: number,\n across = false,\n most = Infinity,\n): ValueAxis {\n const listed = model.yCategories?.[role]\n if (listed) {\n // categories along the axis (a heatmap's rows): one band each, the first\n // at the top (at the left, across)\n const ticks = listed.map((_, i) => i)\n return {\n role,\n kind: 'linear',\n domain: across ? [-0.5, listed.length - 0.5] : [listed.length - 0.5, -0.5],\n ticks,\n texts: listed.map((label) => (config.format ? config.format(label) : label)),\n fixed: true,\n }\n }\n count = tickCount(config, count, most)\n const kind = config.type === 'log' ? 'log' : 'linear'\n const data = model.y[role]\n const zero = config.zero ?? model.zero[role]\n let [lo, hi] = data\n ? kind === 'linear'\n ? widen(data[0], data[1], model.inset, length)\n : data\n : [0, 1]\n if (zero && kind === 'linear') {\n lo = Math.min(lo, 0)\n hi = Math.max(hi, 0)\n }\n const min = asNumber(config.min, kind)\n const max = asNumber(config.max, kind)\n if (min !== undefined) lo = min\n if (max !== undefined) hi = max\n let domain: [number, number]\n if (kind === 'log') domain = min !== undefined && max !== undefined ? [lo, hi] : logDomain(lo, hi)\n else {\n domain = linearDomain(lo, hi, { count, nice: config.nice ?? true })\n if (min !== undefined) domain[0] = min\n if (max !== undefined) domain[1] = max\n }\n const given = explicitTicks(config, kind)\n let ticks =\n given ??\n (kind === 'log'\n ? logTicks(domain[0], domain[1], count)\n : linearTicks(domain[0], domain[1], count))\n // nice ends are ticks: an end the ticks' step falls short of reaches the next one\n if (!given && kind === 'linear' && (config.nice ?? true) && ticks.length > 1) {\n const step = ticks[1]! - ticks[0]!\n const decimals = stepDecimals(step)\n const from = min ?? Number((Math.floor(domain[0] / step + 1e-9) * step).toFixed(decimals))\n const to = max ?? Number((Math.ceil(domain[1] / step - 1e-9) * step).toFixed(decimals))\n if (from !== domain[0] || to !== domain[1]) {\n domain = [from, to]\n ticks = linearTicks(from, to, count)\n }\n }\n return {\n role,\n kind,\n domain,\n ticks,\n texts: valueTexts(ticks, config, locale),\n fixed: min !== undefined && max !== undefined,\n }\n}\n\n/** Gives `axis` exactly `intervals` steps of a nice size, still covering its data. */\nfunction alignTo(\n axis: ValueAxis,\n intervals: number,\n config: AxisConfig,\n model: ChartModel,\n locale: string,\n length: number,\n): ValueAxis {\n if (axis.kind === 'log') return axis\n let [lo, hi] = extent(axis.role, config, model, length)\n if (config.min !== undefined) lo = Number(config.min)\n if (lo === hi) hi = lo + 1\n let step = niceStep(hi - lo, intervals)\n for (let guard = 0; guard < 8; guard++) {\n const start = config.min !== undefined ? lo : Math.floor(lo / step) * step\n if (start + step * intervals >= hi - 1e-9) {\n const decimals = stepDecimals(step)\n const ticks = Array.from({ length: intervals + 1 }, (_, i) =>\n Number((start + step * i).toFixed(decimals)),\n )\n return {\n ...axis,\n domain: [ticks[0]!, ticks[intervals]!],\n ticks,\n texts: valueTexts(ticks, config, locale),\n }\n }\n step = nextNiceStep(step)\n }\n return axis\n}\n\n/**\n * How many ticks to aim for: the number given as `ticks`, else `count`\n * (what fits); never more than `most`, so asking for many on a narrow\n * chart doesn't pile the labels on one another.\n */\nfunction tickCount(config: AxisConfig, count: number, most = Infinity): number {\n const asked =\n typeof config.ticks === 'number' && config.ticks >= 1 ? Math.round(config.ticks) : count\n return Math.max(1, Math.min(asked, most))\n}\n\nfunction explicitTicks(config: AxisConfig, kind: XKind | 'log'): number[] | null {\n if (!Array.isArray(config.ticks)) return null\n return config.ticks.map((value) => asNumber(value, kind)!).filter((v) => Number.isFinite(v))\n}\n\nfunction valueTexts(ticks: number[], config: AxisConfig, locale: string): string[] {\n if (config.format) return ticks.map((value) => config.format!(value))\n const format = numberTickFormat(locale, ticks)\n return ticks.map((value) => format(value))\n}\n\n/* the x axis */\n\n/** The x scale over `range`; `count` is about how many ticks a nice number domain is rounded to. */\nfunction xScale(\n config: AxisConfig,\n model: ChartModel,\n range: [number, number],\n count: number,\n zoom?: Window | null,\n): { scale: Scale; full: [number, number] } {\n const scale = wholeXScale(config, model, range, count)\n const full = scale.domain\n if (!zoom) return { scale, full }\n // only the part zoomed in to, kept inside the whole\n const lo = Math.max(full[0], Math.min(zoom[0], zoom[1]))\n const hi = Math.min(full[1], Math.max(zoom[0], zoom[1]))\n if (!(hi > lo)) return { scale, full }\n const part = linearScale([lo, hi], range, scale.kind === 'time' ? 'time' : 'linear')\n if (scale.kind !== 'category') return { scale: part, full }\n const step = Math.abs(range[1] - range[0]) / (hi - lo)\n return { scale: { ...part, kind: 'category', step, categories: scale.categories }, full }\n}\n\nfunction wholeXScale(\n config: AxisConfig,\n model: ChartModel,\n range: [number, number],\n count: number,\n): Scale {\n const kind = model.xKind\n if (kind === 'category') return categoryScale(model.categories, range)\n const data = model.x ?? [0, 1]\n // bars sit centered on their x: leave half a band at both ends\n const pad = model.xStep > 0 ? model.xStep / 2 : 0\n const [from, to] = widen(data[0] - pad, data[1] + pad, model.inset, Math.abs(range[1] - range[0]))\n let lo = asNumber(config.min, kind) ?? from\n let hi = asNumber(config.max, kind) ?? to\n if (kind === 'time') {\n if (lo === hi) {\n lo -= 12 * 3600e3\n hi += 12 * 3600e3\n }\n return linearScale([lo, hi], range, 'time')\n }\n const domain = linearDomain(lo, hi, {\n count: tickCount(config, count),\n nice: config.nice ?? false,\n })\n if (config.min !== undefined) domain[0] = lo\n if (config.max !== undefined) domain[1] = hi\n return linearScale(domain, range)\n}\n\n/**\n * The ticks to try on the x axis with their texts, most first: the\n * caller takes the first whose labels fit. `length` is the axis length\n * in px and `space` the wanted room per tick.\n */\nfunction* xTickChoices(\n config: AxisConfig,\n model: ChartModel,\n scale: Scale,\n length: number,\n space: number,\n locale: string,\n): Generator<{ ticks: number[]; texts: string[] }> {\n const kind = model.xKind\n if (kind === 'category') {\n const text = (value: number) =>\n config.format ? config.format(model.categories[value]) : (model.categories[value] ?? '')\n // every category when they fit, else every second, third…\n const count = model.categories.length\n // zoomed in: the categories whose middle is shown, still every nth of all, so they don't jump while panning\n const [lo, hi] = scale.domain\n const shownTicks = (ticks: number[]) =>\n lo <= -0.5 && hi >= count - 0.5 ? ticks : ticks.filter((i) => i >= lo && i <= hi)\n for (let every = 1; every <= Math.max(1, count); every++) {\n const ticks = shownTicks(categoryTicks(count, every))\n yield { ticks, texts: ticks.map(text) }\n }\n return\n }\n const explicit = explicitTicks(config, kind)\n const [lo, hi] = scale.domain\n const wanted = tickCount(config, Math.max(2, Math.floor(length / space)))\n if (kind === 'time') {\n for (let count = wanted; count >= 1; count--) {\n const interval = timeInterval(hi - lo, count)\n const ticks = explicit ?? timeTicks(lo, hi, interval)\n const format = timeTickFormat(locale, interval)\n yield {\n ticks,\n texts: ticks.map((value, i) =>\n config.format ? config.format(new Date(value)) : format(value, ticks[i - 1]),\n ),\n }\n if (explicit) return\n }\n return\n }\n // whole-number data (years): no ticks between them\n const most = model.xIntegers ? Math.max(1, Math.floor(hi - lo)) : wanted\n for (let count = Math.min(wanted, most); count >= 1; count--) {\n const ticks = explicit ?? linearTicks(lo, hi, count)\n const format = numberTickFormat(locale, ticks, { years: true })\n yield {\n ticks,\n texts: ticks.map((value) => (config.format ? config.format(value) : format(value))),\n }\n if (explicit) return\n }\n}\n\n/* charts without axes */\n\nexport interface PieLayout {\n width: number\n height: number\n /** The center and the outer radius, in px. */\n cx: number\n cy: number\n radius: number\n /** The lowest the pie's labels may go: above the legend. */\n bottom: number\n legend: LegendItem[]\n legendBox?: LegendBox\n}\n\n/**\n * The box round the part of a circle of radius 1 from `start` to `end`\n * (radians clockwise from 12 o'clock), its center included, in units of\n * the radius from the center: a half circle is half as tall as a whole.\n */\nexport function arcBox(start: number, end: number) {\n let [left, right, top, bottom] = [0, 0, 0, 0]\n const take = (angle: number) => {\n const x = Math.sin(angle)\n const y = -Math.cos(angle)\n left = Math.min(left, x)\n right = Math.max(right, x)\n top = Math.min(top, y)\n bottom = Math.max(bottom, y)\n }\n take(start)\n take(end)\n // the quarters it passes: the top, the right, the bottom, the left\n for (let k = Math.ceil(start / (Math.PI / 2)); k * (Math.PI / 2) <= end; k++)\n take(k * (Math.PI / 2))\n return { left, right, top, bottom }\n}\n\n/** Room from a pie's edge to the labels outside it: the line out, and the gap before the text. */\nexport const PIE_LABEL_ROOM = 24\n\n/**\n * A pie as large as fits above its legend; with labels outside, as large\n * as leaves room for the widest of them on each side, moved over towards\n * the side with the narrower labels.\n */\nexport function layoutPie({\n width,\n height,\n measure,\n legend: names = [],\n coarse,\n legendPlacement = 'bottom',\n sideLabels = { left: 0, right: 0 },\n arc = [0, Math.PI * 2],\n}: Pick<LayoutOptions, 'width' | 'height' | 'measure' | 'legend' | 'coarse' | 'legendPlacement'> & {\n /** The widest label outside the pie on each side, in px; 0 for none. */\n sideLabels?: { left: number; right: number }\n /** The part of the turn the pie takes (a half donut's), in radians from 12 o'clock. */\n arc?: [number, number]\n}): PieLayout {\n const placed = placeLegendIn(names, legendPlacement, width, height, measure, coarse)\n const { top, bottom, right: legendRight } = placed.room\n const room = height - top - bottom - EDGE * 2\n const inner = width - legendRight\n // labels outside: room for them on their side, and for the lines out above and below\n const left = sideLabels.left > 0 ? sideLabels.left + PIE_LABEL_ROOM : 0\n const right = sideLabels.right > 0 ? sideLabels.right + PIE_LABEL_ROOM : 0\n const across = inner - left - right\n const down = left || right ? room - LABEL_HEIGHT : room\n // a part of a circle (a half donut) takes only its own box: it grows to fill the room\n const box = arcBox(arc[0], arc[1])\n const radius = Math.max(\n 1,\n Math.min((across - EDGE * 2) / (box.right - box.left || 1), down / (box.bottom - box.top || 1)),\n )\n return {\n width,\n height,\n cx: left + across / 2 - ((box.left + box.right) / 2) * radius,\n cy: top + EDGE + room / 2 - ((box.top + box.bottom) / 2) * radius,\n radius,\n bottom: height - bottom - EDGE,\n legend: placed.legend,\n legendBox: placed.box,\n }\n}\n\n/** A radar laid out: its center and radius, its rings and the names round it. */\nexport interface RadarLayout {\n width: number\n height: number\n cx: number\n cy: number\n radius: number\n /** How many spokes: one per category. */\n count: number\n /** From a value to its distance from the center, in px. */\n scale: Scale\n /** The values of the rings. */\n ticks: number[]\n /** The spokes' names round the rim. */\n labels: TickLabel[]\n /** The rings' values, between the first two spokes. */\n values: TickLabel[]\n legend: LegendItem[]\n legendBox?: LegendBox\n}\n\n/** Room between the rim and a spoke's name. */\nconst SPOKE_GAP = 8\n/** The most of the chart's width a spoke's name may take, each side. */\nconst SPOKE_SHARE = 0.22\n/** Wanted room between rings, in px. */\nconst RING_SPACE = 24\n\n/**\n * Lays out a radar: the legend first, then the largest circle that leaves\n * room round it for the spokes' names (the categories), with rings at\n * nice values from the center (0, or the smallest value below it) out.\n */\nexport function layoutRadar({\n model,\n axes,\n width,\n height,\n locale,\n measure,\n legend: names = [],\n coarse,\n legendPlacement = 'bottom',\n spokes,\n}: Omit<LayoutOptions, 'horizontal' | 'zoom' | 'strip' | 'trailing'> & {\n /** The spokes' names, in order from 12 o'clock. */\n spokes: string[]\n}): RadarLayout {\n const placed = placeLegendIn(names, legendPlacement, width, height, measure, coarse)\n const { top, bottom, right: legendRight } = placed.room\n const across = width - legendRight\n const down = height - top - bottom\n const count = spokes.length\n const most = Math.max(24, Math.floor(across * SPOKE_SHARE))\n const widths = spokes.map((text) => measure(text))\n const angleOf = (i: number) => (i / Math.max(1, count)) * Math.PI * 2\n // the names beside the circle take room across; those at the top and bottom, down\n let side = 0\n widths.forEach((w, i) => {\n if (Math.abs(Math.sin(angleOf(i))) >= 0.1) side = Math.max(side, w)\n })\n side = Math.min(side, most)\n const radius = Math.max(\n 1,\n Math.min(\n (across - (side + SPOKE_GAP) * 2 - EDGE * 2) / 2,\n (down - (LABEL_HEIGHT + SPOKE_GAP) * 2 - EDGE * 2) / 2,\n ),\n )\n const cx = across / 2\n const cy = top + down / 2\n const config = axes.y ?? { position: 'left' as const, grid: true, hidden: false }\n const axis = valueAxis(\n 'y',\n config,\n model,\n Math.max(2, Math.floor(radius / RING_SPACE)),\n locale,\n radius,\n )\n const scale = linearScale(axis.domain, [0, radius])\n const labels: TickLabel[] = []\n if (!axes.x.hidden)\n spokes.forEach((text, i) => {\n const room = Math.min(widths[i]!, most)\n const sin = Math.sin(angleOf(i))\n const cos = Math.cos(angleOf(i))\n const x = cx + sin * (radius + SPOKE_GAP)\n const y = cy - cos * (radius + SPOKE_GAP)\n const where = Math.abs(sin) < 0.1 ? 0 : sin > 0 ? 1 : -1\n // the names at the top and bottom sit past the rim; those beside it, centered on their spoke\n const lift = where === 0 ? (cos > 0 ? LABEL_HEIGHT / 2 : -LABEL_HEIGHT / 2) : 0\n const label: TickLabel = {\n text,\n left: where === 0 ? x - room / 2 : where > 0 ? x : x - room,\n top: y - LABEL_HEIGHT / 2 - lift,\n width: room,\n }\n if (widths[i]! > most) label.clipped = true\n labels.push(label)\n })\n const values: TickLabel[] = []\n if (!config.hidden)\n axis.ticks.forEach((tick, i) => {\n const r = scale.map(tick)\n // the center needs no number: it is where the rings start\n if (r < 1) return\n const text = axis.texts[i]!\n // halfway between the first two spokes, just outside its ring: clear of the spokes and the dots on them\n const half = Math.PI / Math.max(2, count)\n const out = r * Math.cos(half)\n const x = cx + Math.sin(half) * out\n const y = cy - Math.cos(half) * out\n const label = { text, left: x + 2, top: y - LABEL_HEIGHT, width: measure(text) }\n // a spoke's name past the rim: the ring's value gives way to it\n const hits = labels.some(\n (other) =>\n label.left < other.left + other.width &&\n other.left < label.left + label.width &&\n label.top < other.top + LABEL_HEIGHT &&\n other.top < label.top + LABEL_HEIGHT,\n )\n if (!hits) values.push(label)\n })\n return {\n width,\n height,\n cx,\n cy,\n radius,\n count,\n scale,\n ticks: axis.ticks,\n labels,\n values,\n legend: placed.legend,\n legendBox: placed.box,\n }\n}\n\n/** A funnel laid out: where its stages go, and their names and values beside them. */\nexport interface FunnelLayout {\n width: number\n height: number\n /** Where the bars go: the widest takes its whole width. */\n plot: Rect\n /** How many stages, and the height of each one's row. */\n count: number\n step: number\n /** From a value to half its bar's width, in px. */\n scale: Scale\n /** The stages' names, on the left. */\n names: TickLabel[]\n /** Their values, on the right, and the share each kept of the one before it. */\n values: TickLabel[]\n rates: TickLabel[]\n legend: LegendItem[]\n legendBox?: LegendBox\n}\n\n/** Room between a funnel's bars and the text beside them, and between a value and its rate. */\nconst FUNNEL_GAP = 12\nconst RATE_GAP = 8\n/** The most of the chart's width the names may take. */\nconst FUNNEL_NAME_SHARE = 0.3\n/** The tallest a stage's row grows, in px: a few stages don't fill a tall chart with bars. */\nconst FUNNEL_STEP = 64\n\n/**\n * Lays out a funnel: the legend, then the names in a column on the left\n * and the values (with rates) on the right, and the bars between them.\n */\nexport function layoutFunnel({\n width,\n height,\n measure,\n legend: names = [],\n coarse,\n legendPlacement = 'bottom',\n stages,\n values: written,\n rates: rateTexts,\n max,\n}: Pick<LayoutOptions, 'width' | 'height' | 'measure' | 'legend' | 'coarse' | 'legendPlacement'> & {\n stages: string[]\n values: string[]\n /** The share each stage kept of the one before, written; `null` for the first. */\n rates: Array<string | null>\n /** The largest value: its bar is the widest. */\n max: number\n}): FunnelLayout {\n const placed = placeLegendIn(names, legendPlacement, width, height, measure, coarse)\n const { top, bottom, right: legendRight } = placed.room\n const across = width - legendRight\n const count = stages.length\n const most = Math.floor(across * FUNNEL_NAME_SHARE)\n const nameWidths = stages.map((text) => Math.min(measure(text), most))\n const valueWidths = written.map((text) => measure(text))\n const rateWidths = rateTexts.map((text) => (text ? measure(text) : 0))\n const valueColumn = Math.max(0, ...valueWidths)\n const rateColumn = Math.max(0, ...rateWidths)\n const left = INSET + Math.max(0, ...nameWidths) + FUNNEL_GAP\n const right = FUNNEL_GAP + valueColumn + (rateColumn ? RATE_GAP + rateColumn : 0) + INSET\n const room = Math.max(1, height - top - bottom - EDGE * 2)\n const step = count ? Math.min(FUNNEL_STEP, room / count) : room\n const plotHeight = step * count\n const plot: Rect = {\n x: left,\n y: top + EDGE + (room - plotHeight) / 2,\n width: Math.max(1, across - left - right),\n height: plotHeight,\n }\n const scale = linearScale([0, max > 0 ? max : 1], [0, plot.width / 2])\n const middle = (i: number) => plot.y + step * i + step / 2 - LABEL_HEIGHT / 2\n const labels = stages.map((text, i) => {\n const label: TickLabel = {\n text,\n left: plot.x - FUNNEL_GAP - nameWidths[i]!,\n top: middle(i),\n width: nameWidths[i]!,\n }\n if (measure(text) > most) label.clipped = true\n return label\n })\n const valueLeft = plot.x + plot.width + FUNNEL_GAP\n const values = written.map((text, i) => ({\n text,\n // right-aligned in their column, so the digits line up\n left: valueLeft + valueColumn - valueWidths[i]!,\n top: middle(i),\n width: valueWidths[i]!,\n }))\n const rates: TickLabel[] = []\n rateTexts.forEach((text, i) => {\n if (!text) return\n rates.push({\n text,\n left: valueLeft + valueColumn + RATE_GAP + rateColumn - rateWidths[i]!,\n top: middle(i),\n width: rateWidths[i]!,\n })\n })\n return {\n width,\n height,\n plot,\n count,\n step,\n scale,\n names: labels,\n values,\n rates,\n legend: placed.legend,\n legendBox: placed.box,\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { decimate, summarize, visibleRange } from "./decimate.js";
|
|
2
|
+
import { tracePoints } from "./painter.js";
|
|
3
|
+
import { fillPattern } from "./pattern.js";
|
|
4
|
+
import { baseline } from "./scale.js";
|
|
5
|
+
// src/core/line.ts
|
|
6
|
+
/** Room a point needs before dots are shown on their own (`dots: 'auto'`). */
|
|
7
|
+
var DOT_SPACE = 16;
|
|
8
|
+
/** Room a point needs before a line may curve: denser data is drawn straight. */
|
|
9
|
+
var SMOOTH_SPACE = 3;
|
|
10
|
+
/** Room a point needs before each gets a label. */
|
|
11
|
+
var LABEL_SPACE = 12;
|
|
12
|
+
/**
|
|
13
|
+
* The corners of a line that steps through `count` points: `after` holds
|
|
14
|
+
* each value until the next x, `before` takes the next value at once,
|
|
15
|
+
* `middle` changes halfway. Gives the corners' x, the values (`ys`, and
|
|
16
|
+
* `bs` for an area's bottom, stepped the same way) and how many there are.
|
|
17
|
+
*/
|
|
18
|
+
function stepPoints(px, ys, bs, count, mode) {
|
|
19
|
+
const per = mode === "middle" ? 3 : 2;
|
|
20
|
+
const n = count > 0 ? (count - 1) * per + 1 : 0;
|
|
21
|
+
const x = new Float64Array(n);
|
|
22
|
+
const y = new Float64Array(n);
|
|
23
|
+
const b = new Float64Array(n);
|
|
24
|
+
let k = 0;
|
|
25
|
+
const put = (u, i) => {
|
|
26
|
+
x[k] = u;
|
|
27
|
+
y[k] = ys[i];
|
|
28
|
+
b[k] = bs[i];
|
|
29
|
+
k++;
|
|
30
|
+
};
|
|
31
|
+
for (let i = 0; i < count; i++) {
|
|
32
|
+
if (i > 0) {
|
|
33
|
+
const u0 = px[i - 1];
|
|
34
|
+
const u1 = px[i];
|
|
35
|
+
if (mode === "after") put(u1, i - 1);
|
|
36
|
+
else if (mode === "before") put(u0, i);
|
|
37
|
+
else {
|
|
38
|
+
const m = (u0 + u1) / 2;
|
|
39
|
+
put(m, i - 1);
|
|
40
|
+
put(m, i);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
put(px[i], i);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
x,
|
|
47
|
+
y,
|
|
48
|
+
b,
|
|
49
|
+
count: k
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
var bufferX = /* @__PURE__ */ new Float64Array(1024);
|
|
53
|
+
var bufferY = /* @__PURE__ */ new Float64Array(1024);
|
|
54
|
+
var bufferB = /* @__PURE__ */ new Float64Array(1024);
|
|
55
|
+
function ensure(length) {
|
|
56
|
+
if (bufferX.length >= length) return;
|
|
57
|
+
const size = 2 ** Math.ceil(Math.log2(length));
|
|
58
|
+
bufferX = new Float64Array(size);
|
|
59
|
+
bufferY = new Float64Array(size);
|
|
60
|
+
bufferB = new Float64Array(size);
|
|
61
|
+
}
|
|
62
|
+
/** Draws a line, and with `area` the area under it (or under its slice of a stack). */
|
|
63
|
+
function drawLine(painter, series, view, area) {
|
|
64
|
+
const style = series.props;
|
|
65
|
+
const { x: sx, y: sy, plot, horizontal: swap } = view;
|
|
66
|
+
const along = swap ? plot.height : plot.width;
|
|
67
|
+
const xs = series.x.values;
|
|
68
|
+
const [from, to] = visibleRange(xs, series.x.sorted, sx.domain[0], sx.domain[1]);
|
|
69
|
+
const { indices, length } = decimate(xs, series.top, from, to, sx.k, sx.b, style.connectNulls, series.x.sorted);
|
|
70
|
+
if (length === 0) return;
|
|
71
|
+
const shown = to - from;
|
|
72
|
+
const step = style.step === true ? "after" : style.step ? style.step : null;
|
|
73
|
+
const smooth = !step && !!style.smooth && shown * SMOOTH_SPACE <= along;
|
|
74
|
+
const lineWidth = style.lineWidth ?? 2;
|
|
75
|
+
const band = area && !!series.base && !series.stackEnds;
|
|
76
|
+
const floorY = baseline(sy);
|
|
77
|
+
ensure(length);
|
|
78
|
+
const runs = [];
|
|
79
|
+
let count = 0;
|
|
80
|
+
let start = 0;
|
|
81
|
+
for (let k = 0; k <= length; k++) {
|
|
82
|
+
const i = k < length ? indices[k] : -1;
|
|
83
|
+
if (i === -1) {
|
|
84
|
+
if (count > start) runs.push([start, count]);
|
|
85
|
+
start = count;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
bufferX[count] = sx.map(xs[i]);
|
|
89
|
+
bufferY[count] = sy.map(series.top[i]);
|
|
90
|
+
bufferB[count] = series.base ? sy.map(series.base[i]) : floorY;
|
|
91
|
+
count++;
|
|
92
|
+
}
|
|
93
|
+
const drawn = runs.map(([a, b]) => {
|
|
94
|
+
const xsRun = bufferX.subarray(a, b);
|
|
95
|
+
const ysRun = bufferY.subarray(a, b);
|
|
96
|
+
const bsRun = bufferB.subarray(a, b);
|
|
97
|
+
if (!step) return {
|
|
98
|
+
x: xsRun,
|
|
99
|
+
y: ysRun,
|
|
100
|
+
b: bsRun,
|
|
101
|
+
count: b - a
|
|
102
|
+
};
|
|
103
|
+
return stepPoints(xsRun, ysRun, bsRun, b - a, step);
|
|
104
|
+
});
|
|
105
|
+
if (area) {
|
|
106
|
+
painter.save();
|
|
107
|
+
painter.fillStyle = series.color;
|
|
108
|
+
painter.globalAlpha *= series.base ? .18 : .1;
|
|
109
|
+
painter.beginPath();
|
|
110
|
+
for (const run of drawn) {
|
|
111
|
+
tracePoints(painter, run.x, run.y, run.count, {
|
|
112
|
+
smooth,
|
|
113
|
+
swap
|
|
114
|
+
});
|
|
115
|
+
tracePoints(painter, run.x, run.b, run.count, {
|
|
116
|
+
smooth,
|
|
117
|
+
swap,
|
|
118
|
+
reverse: true,
|
|
119
|
+
join: true
|
|
120
|
+
});
|
|
121
|
+
painter.closePath();
|
|
122
|
+
}
|
|
123
|
+
painter.fill();
|
|
124
|
+
painter.restore();
|
|
125
|
+
if (series.pattern) fillPattern(painter, series.pattern, view.plot, series.color, .5);
|
|
126
|
+
}
|
|
127
|
+
if (lineWidth > 0) {
|
|
128
|
+
painter.save();
|
|
129
|
+
painter.strokeStyle = series.color;
|
|
130
|
+
painter.lineWidth = lineWidth;
|
|
131
|
+
painter.lineJoin = "round";
|
|
132
|
+
painter.lineCap = "round";
|
|
133
|
+
if (style.dashed) painter.setLineDash([lineWidth * 3, lineWidth * 2.5]);
|
|
134
|
+
painter.beginPath();
|
|
135
|
+
for (const run of drawn) {
|
|
136
|
+
tracePoints(painter, run.x, run.y, run.count, {
|
|
137
|
+
smooth,
|
|
138
|
+
swap
|
|
139
|
+
});
|
|
140
|
+
if (band) tracePoints(painter, run.x, run.b, run.count, {
|
|
141
|
+
smooth,
|
|
142
|
+
swap
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
painter.stroke();
|
|
146
|
+
painter.restore();
|
|
147
|
+
}
|
|
148
|
+
const dots = style.dots ?? (band ? false : "auto");
|
|
149
|
+
const every = dots === true || dots === "auto" && !band && shown * DOT_SPACE <= along;
|
|
150
|
+
const lonely = runs.some(([a, b]) => b - a === 1);
|
|
151
|
+
if (every || lonely) {
|
|
152
|
+
const r = Math.max(2.5, lineWidth + .5);
|
|
153
|
+
painter.save();
|
|
154
|
+
painter.fillStyle = series.color;
|
|
155
|
+
painter.strokeStyle = view.colors.surface;
|
|
156
|
+
painter.lineWidth = 1.5;
|
|
157
|
+
painter.beginPath();
|
|
158
|
+
for (const [a, b] of runs) {
|
|
159
|
+
if (!every && b - a > 1) continue;
|
|
160
|
+
for (let k = a; k < b; k++) {
|
|
161
|
+
const [cx, cy] = swap ? [bufferY[k], bufferX[k]] : [bufferX[k], bufferY[k]];
|
|
162
|
+
painter.moveTo(cx + r, cy);
|
|
163
|
+
painter.arc(cx, cy, r, 0, Math.PI * 2);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
painter.stroke();
|
|
167
|
+
painter.fill();
|
|
168
|
+
painter.restore();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Where a line's labels go: above each point when there is room for them
|
|
173
|
+
* all, or beside the last point shown (`end`).
|
|
174
|
+
*/
|
|
175
|
+
function lineLabels(series, view) {
|
|
176
|
+
const { x: sx, plot, horizontal } = view;
|
|
177
|
+
const xs = series.x.values;
|
|
178
|
+
const top = series.top;
|
|
179
|
+
const [from, to] = visibleRange(xs, series.x.sorted, sx.domain[0], sx.domain[1]);
|
|
180
|
+
const at = (i) => view.point(xs[i], top[i]);
|
|
181
|
+
const valued = (i) => xs[i] === xs[i] && top[i] === top[i];
|
|
182
|
+
if (series.props.labelPosition === "end") {
|
|
183
|
+
let last = -1;
|
|
184
|
+
for (let i = to - 1; i >= from; i--) if (valued(i) && (!series.x.sorted || last < 0 || xs[i] > xs[last])) {
|
|
185
|
+
last = i;
|
|
186
|
+
if (series.x.sorted) break;
|
|
187
|
+
}
|
|
188
|
+
return last < 0 ? [] : [{
|
|
189
|
+
index: last,
|
|
190
|
+
at: at(last),
|
|
191
|
+
side: horizontal ? "bottom" : "right"
|
|
192
|
+
}];
|
|
193
|
+
}
|
|
194
|
+
const along = horizontal ? plot.height : plot.width;
|
|
195
|
+
if ((to - from) * LABEL_SPACE > along) return [];
|
|
196
|
+
const [r0, r1] = view.y.range;
|
|
197
|
+
const flipped = horizontal ? r0 > r1 : r0 < r1;
|
|
198
|
+
const out = [];
|
|
199
|
+
for (let i = from; i < to; i++) {
|
|
200
|
+
if (!valued(i)) continue;
|
|
201
|
+
const down = (series.y[i] < 0 && !series.stackEnds) !== flipped;
|
|
202
|
+
out.push({
|
|
203
|
+
index: i,
|
|
204
|
+
at: at(i),
|
|
205
|
+
side: horizontal ? down ? "left" : "right" : down ? "bottom" : "top"
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
/** For a long line: what lets zooming and panning skip over most of it. */
|
|
211
|
+
function prepare(series) {
|
|
212
|
+
return series.x.sorted ? summarize(series.x.values, series.top) : null;
|
|
213
|
+
}
|
|
214
|
+
var lineKind = {
|
|
215
|
+
type: "line",
|
|
216
|
+
draw: (painter, series, view) => drawLine(painter, series, view, false),
|
|
217
|
+
prepare,
|
|
218
|
+
labels: lineLabels
|
|
219
|
+
};
|
|
220
|
+
var areaKind = {
|
|
221
|
+
type: "area",
|
|
222
|
+
zero: true,
|
|
223
|
+
stackable: true,
|
|
224
|
+
draw: (painter, series, view) => drawLine(painter, series, view, true),
|
|
225
|
+
prepare,
|
|
226
|
+
labels: lineLabels
|
|
227
|
+
};
|
|
228
|
+
export { areaKind, drawLine, lineKind, lineLabels, stepPoints };
|
|
229
|
+
|
|
230
|
+
//# sourceMappingURL=line.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line.js","names":[],"sources":["../../src/core/line.ts"],"sourcesContent":["import { BREAK, decimate, summarize, visibleRange } from './decimate'\nimport { tracePoints, type Painter } from './painter'\nimport { fillPattern } from './pattern'\nimport { baseline } from './scale'\nimport type { ChartLabelAnchor, ChartSeries, ChartSeriesKind, ChartView } from './series'\n\n/** Room a point needs before dots are shown on their own (`dots: 'auto'`). */\nconst DOT_SPACE = 16\n/** Room a point needs before a line may curve: denser data is drawn straight. */\nconst SMOOTH_SPACE = 3\n\nexport interface LineStyle {\n smooth?: boolean\n dots?: boolean | 'auto'\n dashed?: boolean\n lineWidth?: number\n connectNulls?: boolean\n step?: boolean | 'before' | 'after' | 'middle'\n labelPosition?: 'point' | 'end'\n /** An area's bottom of its own (a band); the model reads it into `base`. */\n y0?: unknown\n}\n\n/** Room a point needs before each gets a label. */\nconst LABEL_SPACE = 12\n\ntype StepMode = 'before' | 'after' | 'middle'\n\n/**\n * The corners of a line that steps through `count` points: `after` holds\n * each value until the next x, `before` takes the next value at once,\n * `middle` changes halfway. Gives the corners' x, the values (`ys`, and\n * `bs` for an area's bottom, stepped the same way) and how many there are.\n */\nexport function stepPoints(\n px: ArrayLike<number>,\n ys: ArrayLike<number>,\n bs: ArrayLike<number>,\n count: number,\n mode: StepMode,\n): { x: Float64Array; y: Float64Array; b: Float64Array; count: number } {\n const per = mode === 'middle' ? 3 : 2\n const n = count > 0 ? (count - 1) * per + 1 : 0\n const x = new Float64Array(n)\n const y = new Float64Array(n)\n const b = new Float64Array(n)\n let k = 0\n const put = (u: number, i: number) => {\n x[k] = u\n y[k] = ys[i]!\n b[k] = bs[i]!\n k++\n }\n for (let i = 0; i < count; i++) {\n if (i > 0) {\n const u0 = px[i - 1]!\n const u1 = px[i]!\n if (mode === 'after') put(u1, i - 1)\n else if (mode === 'before') put(u0, i)\n else {\n const m = (u0 + u1) / 2\n put(m, i - 1)\n put(m, i)\n }\n }\n put(px[i]!, i)\n }\n return { x, y, b, count: k }\n}\n\nlet bufferX = new Float64Array(1024)\nlet bufferY = new Float64Array(1024)\nlet bufferB = new Float64Array(1024)\n\nfunction ensure(length: number) {\n if (bufferX.length >= length) return\n const size = 2 ** Math.ceil(Math.log2(length))\n bufferX = new Float64Array(size)\n bufferY = new Float64Array(size)\n bufferB = new Float64Array(size)\n}\n\n/** Draws a line, and with `area` the area under it (or under its slice of a stack). */\nexport function drawLine(\n painter: Painter,\n series: ChartSeries<LineStyle>,\n view: ChartView,\n area: boolean,\n) {\n const style = series.props\n const { x: sx, y: sy, plot, horizontal: swap } = view\n const along = swap ? plot.height : plot.width\n const xs = series.x.values\n const [from, to] = visibleRange(xs, series.x.sorted, sx.domain[0], sx.domain[1])\n const { indices, length } = decimate(\n xs,\n series.top,\n from,\n to,\n sx.k,\n sx.b,\n style.connectNulls,\n series.x.sorted,\n )\n if (length === 0) return\n\n const shown = to - from\n const step: StepMode | null =\n style.step === true ? 'after' : style.step ? (style.step as StepMode) : null\n const smooth = !step && !!style.smooth && shown * SMOOTH_SPACE <= along\n const lineWidth = style.lineWidth ?? 2\n // an area with a bottom of its own: a band, with a line along both edges\n const band = area && !!series.base && !series.stackEnds\n\n // where an area without a stack grows from\n const floorY = baseline(sy)\n\n ensure(length)\n const runs: Array<[number, number]> = []\n let count = 0\n let start = 0\n for (let k = 0; k <= length; k++) {\n const i = k < length ? indices[k]! : BREAK\n if (i === BREAK) {\n if (count > start) runs.push([start, count])\n start = count\n continue\n }\n bufferX[count] = sx.map(xs[i]!)\n bufferY[count] = sy.map(series.top[i]!)\n bufferB[count] = series.base ? sy.map(series.base[i]!) : floorY\n count++\n }\n\n // the runs as drawn: their corners, when the line steps\n const drawn = runs.map(([a, b]) => {\n const xsRun = bufferX.subarray(a, b)\n const ysRun = bufferY.subarray(a, b)\n const bsRun = bufferB.subarray(a, b)\n if (!step) return { x: xsRun, y: ysRun, b: bsRun, count: b - a }\n return stepPoints(xsRun, ysRun, bsRun, b - a, step)\n })\n\n if (area) {\n painter.save()\n painter.fillStyle = series.color\n painter.globalAlpha *= series.base ? 0.18 : 0.1\n painter.beginPath()\n for (const run of drawn) {\n tracePoints(painter, run.x, run.y, run.count, { smooth, swap })\n tracePoints(painter, run.x, run.b, run.count, { smooth, swap, reverse: true, join: true })\n painter.closePath()\n }\n painter.fill()\n painter.restore()\n // on a light fill, the pattern is in the series' color (the path is still the area's)\n if (series.pattern) fillPattern(painter, series.pattern, view.plot, series.color, 0.5)\n }\n\n if (lineWidth > 0) {\n painter.save()\n painter.strokeStyle = series.color\n painter.lineWidth = lineWidth\n painter.lineJoin = 'round'\n painter.lineCap = 'round'\n if (style.dashed) painter.setLineDash([lineWidth * 3, lineWidth * 2.5])\n painter.beginPath()\n for (const run of drawn) {\n tracePoints(painter, run.x, run.y, run.count, { smooth, swap })\n if (band) tracePoints(painter, run.x, run.b, run.count, { smooth, swap })\n }\n painter.stroke()\n painter.restore()\n }\n\n // a band's edges are a range, not points: dots only when asked for\n const dots = style.dots ?? (band ? false : 'auto')\n // a point standing alone (a gap on both sides) is invisible without its dot\n const every = dots === true || (dots === 'auto' && !band && shown * DOT_SPACE <= along)\n const lonely = runs.some(([a, b]) => b - a === 1)\n if (every || lonely) {\n const r = Math.max(2.5, lineWidth + 0.5)\n painter.save()\n painter.fillStyle = series.color\n painter.strokeStyle = view.colors.surface\n painter.lineWidth = 1.5\n painter.beginPath()\n for (const [a, b] of runs) {\n if (!every && b - a > 1) continue\n for (let k = a; k < b; k++) {\n const [cx, cy] = swap ? [bufferY[k]!, bufferX[k]!] : [bufferX[k]!, bufferY[k]!]\n painter.moveTo(cx + r, cy)\n painter.arc(cx, cy, r, 0, Math.PI * 2)\n }\n }\n painter.stroke()\n painter.fill()\n painter.restore()\n }\n}\n\n/**\n * Where a line's labels go: above each point when there is room for them\n * all, or beside the last point shown (`end`).\n */\nexport function lineLabels(series: ChartSeries<LineStyle>, view: ChartView): ChartLabelAnchor[] {\n const { x: sx, plot, horizontal } = view\n const xs = series.x.values\n const top = series.top\n const [from, to] = visibleRange(xs, series.x.sorted, sx.domain[0], sx.domain[1])\n const at = (i: number) => view.point(xs[i]!, top[i]!)\n const valued = (i: number) => xs[i] === xs[i] && top[i] === top[i]\n if (series.props.labelPosition === 'end') {\n let last = -1\n for (let i = to - 1; i >= from; i--)\n if (valued(i) && (!series.x.sorted || last < 0 || xs[i]! > xs[last]!)) {\n last = i\n if (series.x.sorted) break\n }\n return last < 0 ? [] : [{ index: last, at: at(last), side: horizontal ? 'bottom' : 'right' }]\n }\n const along = horizontal ? plot.height : plot.width\n if ((to - from) * LABEL_SPACE > along) return []\n // a reversed value axis: what is above in value is below on screen\n const [r0, r1] = view.y.range\n const flipped = horizontal ? r0 > r1 : r0 < r1\n const out: ChartLabelAnchor[] = []\n for (let i = from; i < to; i++) {\n if (!valued(i)) continue\n // below a point that dips under 0, above the others\n const down = (series.y[i]! < 0 && !series.stackEnds) !== flipped\n out.push({\n index: i,\n at: at(i),\n side: horizontal ? (down ? 'left' : 'right') : down ? 'bottom' : 'top',\n })\n }\n return out\n}\n\n/** For a long line: what lets zooming and panning skip over most of it. */\nfunction prepare(series: ChartSeries<LineStyle>) {\n return series.x.sorted ? summarize(series.x.values, series.top) : null\n}\n\nexport const lineKind: ChartSeriesKind<LineStyle> = {\n type: 'line',\n draw: (painter, series, view) => drawLine(painter, series, view, false),\n prepare,\n labels: lineLabels,\n}\n\nexport const areaKind: ChartSeriesKind<LineStyle> = {\n type: 'area',\n zero: true,\n stackable: true,\n draw: (painter, series, view) => drawLine(painter, series, view, true),\n prepare,\n labels: lineLabels,\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { crisp } from "./painter.js";
|
|
2
|
+
// src/core/marker.ts
|
|
3
|
+
/** Height of a label's box, as tick labels. */
|
|
4
|
+
var LABEL = 16;
|
|
5
|
+
/** Room between a label and its line or point, and the plot's edge. */
|
|
6
|
+
var GAP = 4;
|
|
7
|
+
/** Whether `v` is between `from` and `to`, give or take half a pixel. */
|
|
8
|
+
var inside = (v, from, to) => v >= from - .5 && v <= to + .5;
|
|
9
|
+
/** A value (or the ends of a stretch) in px. */
|
|
10
|
+
var along = (scale, value) => typeof value === "number" ? scale.map(value) : [scale.map(value[0]), scale.map(value[1])];
|
|
11
|
+
/** Where each marker is drawn, and its label; markers wholly off the plot are left out. */
|
|
12
|
+
function placeMarkers(markers, options) {
|
|
13
|
+
const { plot, horizontal, measure } = options;
|
|
14
|
+
const right = plot.x + plot.width;
|
|
15
|
+
const bottom = plot.y + plot.height;
|
|
16
|
+
const out = [];
|
|
17
|
+
const taken = [];
|
|
18
|
+
const placeLabel = (places, width) => {
|
|
19
|
+
const label = places.find((place) => {
|
|
20
|
+
const [x0, y0, x1, y1] = boxOf(place, width);
|
|
21
|
+
return taken.every(([a0, b0, a1, b1]) => x1 <= a0 || x0 >= a1 || y1 <= b0 || y0 >= b1);
|
|
22
|
+
}) ?? places[0];
|
|
23
|
+
taken.push(boxOf(label, width));
|
|
24
|
+
return label;
|
|
25
|
+
};
|
|
26
|
+
for (const marker of markers) {
|
|
27
|
+
const yScale = options.y(marker.axis);
|
|
28
|
+
const xs = marker.x === null ? null : along(options.x, marker.x);
|
|
29
|
+
const ys = marker.y === null || !yScale ? null : along(yScale, marker.y);
|
|
30
|
+
if (xs === null && ys === null) continue;
|
|
31
|
+
const u = horizontal ? ys : xs;
|
|
32
|
+
const v = horizontal ? xs : ys;
|
|
33
|
+
let shape = null;
|
|
34
|
+
if (typeof u === "number" && typeof v === "number") {
|
|
35
|
+
if (inside(u, plot.x, right) && inside(v, plot.y, bottom)) shape = {
|
|
36
|
+
kind: "point",
|
|
37
|
+
x: u,
|
|
38
|
+
y: v
|
|
39
|
+
};
|
|
40
|
+
} else if (typeof u === "number" && v === null) {
|
|
41
|
+
if (inside(u, plot.x, right)) shape = {
|
|
42
|
+
kind: "line",
|
|
43
|
+
vertical: true,
|
|
44
|
+
at: u,
|
|
45
|
+
from: plot.y,
|
|
46
|
+
to: bottom
|
|
47
|
+
};
|
|
48
|
+
} else if (typeof v === "number" && u === null) {
|
|
49
|
+
if (inside(v, plot.y, bottom)) shape = {
|
|
50
|
+
kind: "line",
|
|
51
|
+
vertical: false,
|
|
52
|
+
at: v,
|
|
53
|
+
from: plot.x,
|
|
54
|
+
to: right
|
|
55
|
+
};
|
|
56
|
+
} else {
|
|
57
|
+
const [u0, u1] = Array.isArray(u) ? [Math.min(...u), Math.max(...u)] : [plot.x, right];
|
|
58
|
+
const [v0, v1] = Array.isArray(v) ? [Math.min(...v), Math.max(...v)] : [plot.y, bottom];
|
|
59
|
+
const x = Math.max(plot.x, u0);
|
|
60
|
+
const y = Math.max(plot.y, v0);
|
|
61
|
+
const width = Math.min(right, u1) - x;
|
|
62
|
+
const height = Math.min(bottom, v1) - y;
|
|
63
|
+
if (width > 0 && height > 0) shape = {
|
|
64
|
+
kind: "area",
|
|
65
|
+
x,
|
|
66
|
+
y,
|
|
67
|
+
width,
|
|
68
|
+
height
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (!shape) continue;
|
|
72
|
+
const text = marker.label;
|
|
73
|
+
out.push({
|
|
74
|
+
shape,
|
|
75
|
+
tone: marker.tone,
|
|
76
|
+
solid: marker.solid,
|
|
77
|
+
label: text ? placeLabel(placesFor(shape, text, measure(text), plot), measure(text)) : null
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/** Where a marker's label may go, best first: by the line's end, at the band's corner, over the point. */
|
|
83
|
+
function placesFor(shape, text, width, plot) {
|
|
84
|
+
const right = plot.x + plot.width;
|
|
85
|
+
const bottom = plot.y + plot.height;
|
|
86
|
+
/** Further down, one label's height at a time, while in the plot. */
|
|
87
|
+
const down = (first) => [
|
|
88
|
+
0,
|
|
89
|
+
1,
|
|
90
|
+
2,
|
|
91
|
+
3
|
|
92
|
+
].map((k) => ({
|
|
93
|
+
text,
|
|
94
|
+
...first,
|
|
95
|
+
top: first.top + k * 18
|
|
96
|
+
})).filter((place, k) => k === 0 || place.top + LABEL <= bottom);
|
|
97
|
+
if (shape.kind === "line" && shape.vertical) {
|
|
98
|
+
const after = shape.at + GAP + width <= right;
|
|
99
|
+
return down({
|
|
100
|
+
left: after ? shape.at + GAP : shape.at - GAP,
|
|
101
|
+
top: plot.y,
|
|
102
|
+
align: after ? "start" : "end"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (shape.kind === "line") {
|
|
106
|
+
const above = {
|
|
107
|
+
text,
|
|
108
|
+
left: right - GAP,
|
|
109
|
+
top: shape.at - LABEL - 2,
|
|
110
|
+
align: "end"
|
|
111
|
+
};
|
|
112
|
+
const under = {
|
|
113
|
+
...above,
|
|
114
|
+
top: shape.at + 2
|
|
115
|
+
};
|
|
116
|
+
return above.top >= plot.y - GAP ? [above, under] : [under, above];
|
|
117
|
+
}
|
|
118
|
+
if (shape.kind === "area") return down({
|
|
119
|
+
left: shape.x + GAP,
|
|
120
|
+
top: shape.y + 2,
|
|
121
|
+
align: "start"
|
|
122
|
+
});
|
|
123
|
+
const reach = 8;
|
|
124
|
+
const half = width / 2;
|
|
125
|
+
const over = {
|
|
126
|
+
text,
|
|
127
|
+
left: Math.min(Math.max(shape.x, plot.x + half), Math.max(plot.x + half, right - half)),
|
|
128
|
+
top: shape.y - reach - LABEL,
|
|
129
|
+
align: "middle"
|
|
130
|
+
};
|
|
131
|
+
const after = {
|
|
132
|
+
text,
|
|
133
|
+
left: shape.x + reach,
|
|
134
|
+
top: shape.y - LABEL / 2,
|
|
135
|
+
align: "start"
|
|
136
|
+
};
|
|
137
|
+
const before = {
|
|
138
|
+
...after,
|
|
139
|
+
left: shape.x - reach,
|
|
140
|
+
align: "end"
|
|
141
|
+
};
|
|
142
|
+
const under = {
|
|
143
|
+
...over,
|
|
144
|
+
top: shape.y + reach
|
|
145
|
+
};
|
|
146
|
+
const sides = shape.x + reach + width <= right ? [after, before] : [before, after];
|
|
147
|
+
return over.top >= plot.y - GAP ? [
|
|
148
|
+
over,
|
|
149
|
+
...sides,
|
|
150
|
+
under
|
|
151
|
+
] : [
|
|
152
|
+
...sides,
|
|
153
|
+
under,
|
|
154
|
+
over
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
/** The box a label takes, with its backdrop. */
|
|
158
|
+
function boxOf(label, width) {
|
|
159
|
+
const start = label.align === "start" ? label.left : label.left - (label.align === "middle" ? width / 2 : width);
|
|
160
|
+
return [
|
|
161
|
+
start - GAP,
|
|
162
|
+
label.top,
|
|
163
|
+
start + width + GAP,
|
|
164
|
+
label.top + LABEL
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
/** Draws the bands (`under` the series) or the lines and points (over them). */
|
|
168
|
+
function drawMarkers(painter, markers, colors, dpr, under) {
|
|
169
|
+
for (const marker of markers) {
|
|
170
|
+
const { shape } = marker;
|
|
171
|
+
if (shape.kind === "area" !== under) continue;
|
|
172
|
+
const c = colors(marker.tone);
|
|
173
|
+
painter.save();
|
|
174
|
+
painter.beginPath();
|
|
175
|
+
if (shape.kind === "area") {
|
|
176
|
+
painter.fillStyle = c.fill;
|
|
177
|
+
painter.rect(shape.x, shape.y, shape.width, shape.height);
|
|
178
|
+
painter.fill();
|
|
179
|
+
} else if (shape.kind === "line") {
|
|
180
|
+
const at = crisp(shape.at, dpr);
|
|
181
|
+
if (shape.vertical) {
|
|
182
|
+
painter.moveTo(at, shape.from);
|
|
183
|
+
painter.lineTo(at, shape.to);
|
|
184
|
+
} else {
|
|
185
|
+
painter.moveTo(shape.from, at);
|
|
186
|
+
painter.lineTo(shape.to, at);
|
|
187
|
+
}
|
|
188
|
+
painter.lineWidth = 1;
|
|
189
|
+
painter.strokeStyle = c.line;
|
|
190
|
+
if (!marker.solid) painter.setLineDash([4, 3]);
|
|
191
|
+
painter.stroke();
|
|
192
|
+
} else {
|
|
193
|
+
painter.arc(shape.x, shape.y, 4, 0, Math.PI * 2);
|
|
194
|
+
painter.fillStyle = c.dot;
|
|
195
|
+
painter.strokeStyle = c.surface;
|
|
196
|
+
painter.lineWidth = 2;
|
|
197
|
+
painter.stroke();
|
|
198
|
+
painter.fill();
|
|
199
|
+
}
|
|
200
|
+
painter.restore();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export { drawMarkers, placeMarkers };
|
|
204
|
+
|
|
205
|
+
//# sourceMappingURL=marker.js.map
|