inkplot-scratch 0.0.0-stage → 1.0.0-beta.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 +60 -2
- package/bin.mjs +312 -0
- package/fonts/Inter-Bold.otf +0 -0
- package/fonts/Inter-Regular.otf +0 -0
- package/fonts/LICENSE.txt +133 -0
- package/package.json +14 -4
- package/skill/SKILL.md +61 -0
- package/skill/inkplot/SKILL.md +309 -0
- package/skill/inkplot/reference/exports.md +104 -0
- package/skill/inkplot/topics/axes.md +125 -0
- package/skill/inkplot/topics/data.md +141 -0
- package/skill/inkplot/topics/furniture.md +137 -0
- package/skill/inkplot/topics/interaction.md +169 -0
- package/skill/inkplot/topics/lines-bars.md +201 -0
- package/skill/inkplot/topics/markers-color.md +274 -0
- package/skill/inkplot/topics/planes.md +139 -0
- package/skill/inkplot/topics/statistics.md +365 -0
- package/skill/inkplot/topics/styling-output.md +75 -0
- package/skill/inkplot/topics/troubleshooting.md +24 -0
- package/src/app.ts +100 -0
- package/src/figures.ts +105 -0
- package/src/interact.ts +39 -0
- package/src/template.html +38 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# inkplot topic: data sources and big data
|
|
2
|
+
|
|
3
|
+
For: a million points (decimation), data as columns or from a function, updating live data, reading a chart's data back as a table.
|
|
4
|
+
|
|
5
|
+
Read `SKILL.md` first (rules, template, and the attributes of `Chart`, `CartesianPlane`, `Axis`, `LinearScale`, `DomainSeries`, `FlatDataSource`, `ObjectDataSource`, `LinePath`, `LinearCurve`). This file has the rest of what this topic needs: examples, notes, and the attributes of its elements. Everything here is exported from `'inkplot'`.
|
|
6
|
+
|
|
7
|
+
## Large data, and a live update
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
`Decimator` takes a **kernel first, then the source**, and goes where the source would. It cannot carry channels or sit on an `XYSeries`.
|
|
11
|
+
|
|
12
|
+
```ts check title="A million points, then updates"
|
|
13
|
+
import {
|
|
14
|
+
Axis,
|
|
15
|
+
CartesianPlane,
|
|
16
|
+
Chart,
|
|
17
|
+
Decimator,
|
|
18
|
+
DomainSeries,
|
|
19
|
+
FlatDataSource,
|
|
20
|
+
LinePath,
|
|
21
|
+
LinearCurve,
|
|
22
|
+
LinearScale,
|
|
23
|
+
MinMaxKernel,
|
|
24
|
+
PanZoom,
|
|
25
|
+
lightTheme,
|
|
26
|
+
} from 'inkplot';
|
|
27
|
+
|
|
28
|
+
const values = new Float64Array(1_000_000);
|
|
29
|
+
for (let i = 0; i < values.length; i++) values[i] = Math.sin(i / 5000) + Math.sin(i / 37) * 0.1;
|
|
30
|
+
const source = new FlatDataSource({ data: values, regularDomain: { start: 0, step: 1 }, isSorted: true });
|
|
31
|
+
|
|
32
|
+
export const chart = new Chart(
|
|
33
|
+
{ theme: lightTheme },
|
|
34
|
+
new CartesianPlane(
|
|
35
|
+
{},
|
|
36
|
+
new Axis({ orientation: 'bottom' }, new LinearScale()),
|
|
37
|
+
new Axis({ orientation: 'left' }, new LinearScale()),
|
|
38
|
+
new DomainSeries(
|
|
39
|
+
{ name: 'Signal' },
|
|
40
|
+
new Decimator({}, new MinMaxKernel(), source),
|
|
41
|
+
new LinePath({}, new LinearCurve()),
|
|
42
|
+
),
|
|
43
|
+
new PanZoom(),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// New data: set the attribute. Edited in place: tell the source.
|
|
48
|
+
|
|
49
|
+
export function replace(next: Float64Array) {
|
|
50
|
+
source.setAttribute('data', next);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function bump(i: number, v: number) {
|
|
54
|
+
values[i] = v;
|
|
55
|
+
source.invalidate();
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Notes
|
|
60
|
+
|
|
61
|
+
| To… | Use |
|
|
62
|
+
|---|---|
|
|
63
|
+
| Chart data as a table | `readTable(node)` (a source, transform, trendline or `series.data`): typed-array columns and `rows()` |
|
|
64
|
+
|
|
65
|
+
## Attributes of this topic's elements
|
|
66
|
+
|
|
67
|
+
An attribute followed by `(required)` must be given. Every element also takes `id`, `meta`, `visible` and `style`.
|
|
68
|
+
|
|
69
|
+
<!-- api:start -->
|
|
70
|
+
### CompositeDataSource
|
|
71
|
+
|
|
72
|
+
Separate columns.
|
|
73
|
+
|
|
74
|
+
**In** a series · **Holds** none
|
|
75
|
+
|
|
76
|
+
| Attribute | Type | |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `count` (required) | `number` | |
|
|
79
|
+
| `domain` (required) | `Column` | Where each row is along the domain: for a made-up domain, `(i) => start + step * i`. |
|
|
80
|
+
| `value` (required) | `Column` | |
|
|
81
|
+
| `channels` | `Readonly<Record<Channel, Column>>` | Extra columns that bindings and tooltips can address by name. |
|
|
82
|
+
| `isSorted` | `boolean` | The stored points already ascend in the domain. Default false. |
|
|
83
|
+
| `wrap` | `number` | The domain repeats with this period, and the source delivers one more point: the first, again, one period on. |
|
|
84
|
+
|
|
85
|
+
### SyntheticDataSource
|
|
86
|
+
|
|
87
|
+
A function sampled when drawn.
|
|
88
|
+
|
|
89
|
+
**In** a series · **Holds** none
|
|
90
|
+
|
|
91
|
+
| Attribute | Type | |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `fn` (required) | `(x: number) => number` | Value at a domain position. |
|
|
94
|
+
| `domain` (required) | `Extent` | The span the function is defined over: the data's whole domain. |
|
|
95
|
+
| `count` | `number` | Number of samples, evenly spaced across the domain, always. |
|
|
96
|
+
| `isSorted` | `boolean` | The stored points already ascend in the domain. Default false. |
|
|
97
|
+
| `wrap` | `number` | The domain repeats with this period, and the source delivers one more point: the first, again, one period on. |
|
|
98
|
+
|
|
99
|
+
### Decimator
|
|
100
|
+
|
|
101
|
+
Reduces large data to what the plot can show.
|
|
102
|
+
|
|
103
|
+
**In** a series · **Holds** 1 kernel, then 1 source
|
|
104
|
+
|
|
105
|
+
| Attribute | Type | |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `resolution` | `number` | Pixel columns reduced to per pixel of plot width. Default 1; raise it for high-DPI output. |
|
|
108
|
+
| `enabled` | `boolean` | Turn it off without removing it. Default true. |
|
|
109
|
+
|
|
110
|
+
### MinMaxKernel
|
|
111
|
+
|
|
112
|
+
Keeps the lowest and highest of each pixel column.
|
|
113
|
+
|
|
114
|
+
**In** Decimator · **Holds** none
|
|
115
|
+
|
|
116
|
+
No attributes of its own.
|
|
117
|
+
|
|
118
|
+
### M4Kernel
|
|
119
|
+
|
|
120
|
+
First, last, lowest and highest of each column.
|
|
121
|
+
|
|
122
|
+
**In** Decimator · **Holds** none
|
|
123
|
+
|
|
124
|
+
No attributes of its own.
|
|
125
|
+
|
|
126
|
+
### LttbKernel
|
|
127
|
+
|
|
128
|
+
Largest-triangle-three-buckets: keeps the shape.
|
|
129
|
+
|
|
130
|
+
**In** Decimator · **Holds** none
|
|
131
|
+
|
|
132
|
+
No attributes of its own.
|
|
133
|
+
|
|
134
|
+
### StrideKernel
|
|
135
|
+
|
|
136
|
+
Every nth point.
|
|
137
|
+
|
|
138
|
+
**In** Decimator · **Holds** none
|
|
139
|
+
|
|
140
|
+
No attributes of its own.
|
|
141
|
+
<!-- api:end -->
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# inkplot topic: title, legend, annotations, accessibility
|
|
2
|
+
|
|
3
|
+
For: a chart title or caption, a legend, reference lines and callouts, an accessible name and summary.
|
|
4
|
+
|
|
5
|
+
Read `SKILL.md` first (rules, template, and the attributes of `Chart`, `CartesianPlane`, `Axis`, `LinearScale`, `DomainSeries`, `FlatDataSource`, `ObjectDataSource`, `LinePath`, `LinearCurve`). This file has the rest of what this topic needs: examples, notes, and the attributes of its elements. Everything here is exported from `'inkplot'`.
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts check title="Title, legend, reference line and callout"
|
|
10
|
+
import {
|
|
11
|
+
Axis,
|
|
12
|
+
CalloutAnnotation,
|
|
13
|
+
CartesianPlane,
|
|
14
|
+
Chart,
|
|
15
|
+
ChartTitle,
|
|
16
|
+
DomainSeries,
|
|
17
|
+
FlatDataSource,
|
|
18
|
+
Legend,
|
|
19
|
+
LineAnnotation,
|
|
20
|
+
LinePath,
|
|
21
|
+
LinearCurve,
|
|
22
|
+
LinearScale,
|
|
23
|
+
lightTheme,
|
|
24
|
+
} from 'inkplot';
|
|
25
|
+
|
|
26
|
+
const line = (name: string, data: number[]) =>
|
|
27
|
+
new DomainSeries(
|
|
28
|
+
{ name: name },
|
|
29
|
+
new FlatDataSource({ data: data, regularDomain: { start: 1, step: 1 } }),
|
|
30
|
+
new LinePath({}, new LinearCurve()),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export const chart = new Chart(
|
|
34
|
+
{
|
|
35
|
+
theme: lightTheme,
|
|
36
|
+
meta: { title: 'Sales by month', description: 'Two years of monthly sales, 2024 above 2023.' },
|
|
37
|
+
},
|
|
38
|
+
new ChartTitle({ text: 'Monthly sales' }),
|
|
39
|
+
new ChartTitle({ text: 'Source: internal ledger', role: 'caption', side: 'bottom' }),
|
|
40
|
+
new CartesianPlane(
|
|
41
|
+
{},
|
|
42
|
+
new Axis({ orientation: 'bottom', title: 'Month' }, new LinearScale()),
|
|
43
|
+
new Axis({ orientation: 'left', title: 'Sales (k)' }, new LinearScale()),
|
|
44
|
+
line('2023', [12, 15, 14, 18, 22, 25]),
|
|
45
|
+
line('2024', [14, 18, 17, 21, 26, 30]),
|
|
46
|
+
new LineAnnotation({ value: 20, label: 'Target' }),
|
|
47
|
+
new CalloutAnnotation({ domain: 6, value: 30, text: 'Best month' }),
|
|
48
|
+
),
|
|
49
|
+
new Legend({ side: 'bottom' }),
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`ChartTitle` and `Legend` are children of the **chart**; the annotations are children of the **plane**. `role: 'caption'` makes a smaller title; `side` puts it on another edge. Series appear in the legend by `name`; `toggle` lets a click hide one. `meta: { title }` and `meta: { description }` on the `Chart` are its accessible name and description; `new ChartSummary()` makes a spoken summary from the data.
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
- `Legend` and the keys: `side` is `'top'`, `'right'`, `'bottom'` or `'left'`; `orientation` is `'horizontal'` or `'vertical'`.
|
|
57
|
+
|
|
58
|
+
| To… | Use |
|
|
59
|
+
|---|---|
|
|
60
|
+
| Title / caption | `new ChartTitle({ text: '…' })`, `new ChartTitle({ text: '…', role: 'caption' })` |
|
|
61
|
+
| Accessible name | `meta: { title }`, `meta: { description }` on the `Chart`; `new ChartSummary()` for an automatic one |
|
|
62
|
+
|
|
63
|
+
## Attributes of this topic's elements
|
|
64
|
+
|
|
65
|
+
An attribute followed by `(required)` must be given. Every element also takes `id`, `meta`, `visible` and `style`.
|
|
66
|
+
|
|
67
|
+
<!-- api:start -->
|
|
68
|
+
### ChartTitle
|
|
69
|
+
|
|
70
|
+
Text across the top or bottom: a title, subtitle or caption.
|
|
71
|
+
|
|
72
|
+
**In** Chart · **Holds** none
|
|
73
|
+
|
|
74
|
+
| Attribute | Type | |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `text` (required) | `string` | The words. |
|
|
77
|
+
| `role` | `TitleRole` | What it is, which decides its look and (unless `side` says) where it goes. Default 'title'. |
|
|
78
|
+
| `side` | `'top' \| 'bottom'` | The edge it spans. Default: the top, except for a caption, which goes at the bottom. |
|
|
79
|
+
|
|
80
|
+
### Legend
|
|
81
|
+
|
|
82
|
+
Lists the named series; click to hide or show.
|
|
83
|
+
|
|
84
|
+
**In** Chart · **Holds** none
|
|
85
|
+
|
|
86
|
+
| Attribute | Type | |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `side` | `Side` | The edge of the chart it sits along. Default 'right'. |
|
|
89
|
+
| `orientation` | `'vertical' \| 'horizontal'` | Entries in a column or a row. Default: along its side, so a column on the left and right, a row on the top and bottom. |
|
|
90
|
+
| `title` | `string` | A heading above the entries. |
|
|
91
|
+
| `toggle` | `boolean` | Clicking an entry hides or shows its series. Default true. |
|
|
92
|
+
|
|
93
|
+
### ChartSummary
|
|
94
|
+
|
|
95
|
+
A spoken summary of the chart, for screen readers (English, replaceable).
|
|
96
|
+
|
|
97
|
+
**In** Chart · **Holds** none
|
|
98
|
+
|
|
99
|
+
| Attribute | Type | |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `text` | `(facts: ChartFacts) => string` | Words the facts. Default: English sentences. |
|
|
102
|
+
| `roleDescription` | `string` | What the chart is, as a screen reader announces it. Default 'chart'. |
|
|
103
|
+
|
|
104
|
+
### LineAnnotation
|
|
105
|
+
|
|
106
|
+
A reference line in data terms.
|
|
107
|
+
|
|
108
|
+
**In** a plane · **Holds** none
|
|
109
|
+
|
|
110
|
+
| Attribute | Type | |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `value` | `number` | A horizontal line at this value (give one of `value`, `domain`, or `from` and `to`). |
|
|
113
|
+
| `domain` | `number` | A vertical line at this domain (give one of `value`, `domain`, or `from` and `to`). |
|
|
114
|
+
| `from` | `readonly [domain: number, value: number]` | One end of a diagonal line, as `[domain, value]` (with `to`). |
|
|
115
|
+
| `to` | `readonly [domain: number, value: number]` | The other end of a diagonal line, as `[domain, value]` (with `from`). |
|
|
116
|
+
| `domainAxis` | `ElementRef<Axis>` | Default: the plane's first axis, as for a series. |
|
|
117
|
+
| `valueAxis` | `ElementRef<Axis>` | Default: the plane's second axis. |
|
|
118
|
+
| `label` | `string` | Text beside the line. |
|
|
119
|
+
| `stroke` | `string` | Line colour: the same as `style: { annotationLine: { color } }`, set directly. |
|
|
120
|
+
| `strokeWidth` | `number` | Line width in pixels: the same as `style: { annotationLine: { width } }`, set directly. |
|
|
121
|
+
|
|
122
|
+
### CalloutAnnotation
|
|
123
|
+
|
|
124
|
+
A note attached to a data point.
|
|
125
|
+
|
|
126
|
+
**In** a plane · **Holds** none
|
|
127
|
+
|
|
128
|
+
| Attribute | Type | |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `domainAxis` | `ElementRef<Axis>` | Default: the plane's first axis. |
|
|
131
|
+
| `valueAxis` | `ElementRef<Axis>` | Default: the plane's second axis. |
|
|
132
|
+
| `domain` (required) | `number` | The point being called out, in data space. |
|
|
133
|
+
| `value` (required) | `number` | The value of the point being called out. |
|
|
134
|
+
| `text` (required) | `string` | What it says. |
|
|
135
|
+
| `dx` | `number` | Where the middle of the box is, in pixels from the point. |
|
|
136
|
+
| `dy` | `number` | Pixels down from the point to the text box (negative is up). |
|
|
137
|
+
<!-- api:end -->
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# inkplot topic: interaction
|
|
2
|
+
|
|
3
|
+
For: tooltips, a crosshair, pan and zoom, drag-to-zoom, brushing a range, clicks and hovers, touch.
|
|
4
|
+
|
|
5
|
+
Read `SKILL.md` first (rules, template, and the attributes of `Chart`, `CartesianPlane`, `Axis`, `LinearScale`, `DomainSeries`, `FlatDataSource`, `ObjectDataSource`, `LinePath`, `LinearCurve`). This file has the rest of what this topic needs: examples, notes, and the attributes of its elements. Everything here is exported from `'inkplot'`.
|
|
6
|
+
|
|
7
|
+
## Tooltip, crosshair, pan and zoom, reference line
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
`Cursor` is a child of the **chart**; `PanZoom` and annotations are children of the **plane**.
|
|
11
|
+
|
|
12
|
+
```ts check title="Interactive"
|
|
13
|
+
import {
|
|
14
|
+
Axis,
|
|
15
|
+
CartesianPlane,
|
|
16
|
+
Chart,
|
|
17
|
+
ChartTooltip,
|
|
18
|
+
Crosshair,
|
|
19
|
+
Cursor,
|
|
20
|
+
DomainSeries,
|
|
21
|
+
FlatDataSource,
|
|
22
|
+
LineAnnotation,
|
|
23
|
+
LinePath,
|
|
24
|
+
LinearCurve,
|
|
25
|
+
LinearScale,
|
|
26
|
+
PanZoom,
|
|
27
|
+
lightTheme,
|
|
28
|
+
} from 'inkplot';
|
|
29
|
+
|
|
30
|
+
export const chart = new Chart(
|
|
31
|
+
{ theme: lightTheme },
|
|
32
|
+
new CartesianPlane(
|
|
33
|
+
{ meta: { title: 'Latency' } },
|
|
34
|
+
new Axis({ orientation: 'bottom' }, new LinearScale()),
|
|
35
|
+
new Axis({ orientation: 'left', title: 'ms' }, new LinearScale()),
|
|
36
|
+
new DomainSeries(
|
|
37
|
+
{ name: 'p95', hit: 'line' },
|
|
38
|
+
new FlatDataSource({ data: [120, 135, 128, 160, 142, 150, 138], regularDomain: { start: 0, step: 1 } }),
|
|
39
|
+
new LinePath({}, new LinearCurve()),
|
|
40
|
+
),
|
|
41
|
+
new LineAnnotation({ value: 150, label: 'Target', stroke: '#c0392b' }),
|
|
42
|
+
new PanZoom(),
|
|
43
|
+
),
|
|
44
|
+
new Cursor({ mode: 'shared' }, new Crosshair(), new ChartTooltip()),
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Notes
|
|
49
|
+
|
|
50
|
+
- A tooltip's `format` gets a `Hit`: `hit.series` (its `attrs.name`), `hit.first` and `hit.second` (the point's two coordinates in data terms: for a line chart the domain, e.g. a date in milliseconds, and the value), `hit.index`, `hit.channels` (bound channels by name). Hover is live in a browser only; `toSvgString` draws no tooltip, so test the text by calling your `format` on a sample.
|
|
51
|
+
|
|
52
|
+
| To… | Use |
|
|
53
|
+
|---|---|
|
|
54
|
+
| Click a point | `new PointerListener({ onClick: (info) => info.hit && … })` in the plane (before `PanZoom`) |
|
|
55
|
+
| Select a range and report it (no zoom) | `new Brush({ mode: 'x', onBrush: ... })` before `new PanZoom()`; `brush.select()` / `clear()` from code |
|
|
56
|
+
| Zoom to a dragged box | `new BoxZoom({ mode: 'auto' })` in the plane, **before** `new PanZoom()` (Shift-drag; `activate: 'always'` for every drag) |
|
|
57
|
+
| Reset zoom (button) | `chart.resetViews()`; know when it is zoomed with `onViewChange` on an `Axis` |
|
|
58
|
+
| Touch | Default: one finger scrolls the page, two pinch the chart; `new PanZoom({ touch: 'always' })` for one-finger pan |
|
|
59
|
+
| Tooltip text | ``new ChartTooltip({ format: (hit) => `${hit.series.attrs.name}: ${hit.second}` })`` |
|
|
60
|
+
|
|
61
|
+
## Attributes of this topic's elements
|
|
62
|
+
|
|
63
|
+
An attribute followed by `(required)` must be given. Every element also takes `id`, `meta`, `visible` and `style`.
|
|
64
|
+
|
|
65
|
+
<!-- api:start -->
|
|
66
|
+
### ChartTooltip
|
|
67
|
+
|
|
68
|
+
A box of text at the cursor.
|
|
69
|
+
|
|
70
|
+
**In** Cursor · **Holds** none
|
|
71
|
+
|
|
72
|
+
| Attribute | Type | |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `follow` | `boolean` | Whether the box moves with the cursor, with no arm, or is set beside what is read, with an arm to it. Default: moves with the cursor when the cursor is 'shared' (there is no one point to be beside), else set beside the point. |
|
|
75
|
+
| `swatch` | `boolean` | A sample of the series (its line, box or marker, as drawn on the plot) before its name, or without one before its first line. Default false. |
|
|
76
|
+
| `format` | `(hit: Hit) => string` | The text for one hit. Default: the series name, then each coordinate (with its axis' title if it has one) and the channels. |
|
|
77
|
+
| `announce` | `boolean` | Tell a screen reader what the box says when the cursor is moved by keyboard (see `Platform.announce`), so it can be read without seeing it. Default true. |
|
|
78
|
+
|
|
79
|
+
### Cursor
|
|
80
|
+
|
|
81
|
+
Finds what the pointer or keyboard is on; its children draw it.
|
|
82
|
+
|
|
83
|
+
**In** Chart · **Holds** Crosshair, ChartTooltip
|
|
84
|
+
|
|
85
|
+
| Attribute | Type | |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `mode` | `'nearest' \| 'shared'` | What it finds: the 'nearest' point, or every series 'shared' at its place along the first axis. Default 'nearest'. |
|
|
88
|
+
| `interpolate` | `boolean` | Read the line between its two points at the cursor instead of reporting a point. Default false. |
|
|
89
|
+
| `plane` | `ElementRef<Plane>` | The plane it works in. Default: the plane the pointer is over, and the first plane for the keyboard. |
|
|
90
|
+
| `keys` | `CursorKeys \| false` | Keys for moving it from the keyboard, or `false` for none (and no tab stop). |
|
|
91
|
+
|
|
92
|
+
### Crosshair
|
|
93
|
+
|
|
94
|
+
Guide lines through the cursor.
|
|
95
|
+
|
|
96
|
+
**In** Cursor · **Holds** none
|
|
97
|
+
|
|
98
|
+
| Attribute | Type | |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `lines` | `'first' \| 'second' \| 'both'` | Which guide: the line of constant first coordinate (across a line chart: vertical), of the second, or both. Default 'both'. |
|
|
101
|
+
| `dots` | `boolean` | A dot in the series' colour on each reading under the cursor. Default false. |
|
|
102
|
+
| `dotSize` | `number` | Radius of those dots, in pixels. Default 4. |
|
|
103
|
+
| `snap` | `boolean` | Put the lines through the first reading (a point) rather than through the cursor itself. Default false. |
|
|
104
|
+
|
|
105
|
+
### PanZoom
|
|
106
|
+
|
|
107
|
+
Drag, wheel, pinch, touch and keys change the view.
|
|
108
|
+
|
|
109
|
+
**In** a plane · **Holds** none
|
|
110
|
+
|
|
111
|
+
| Attribute | Type | |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `axes` | `readonly ElementRef<Axis>[]` | The axes it may move, as elements or ids. Default: all of the plane's that can be dragged along: not the rim of a polar or radar plane. |
|
|
114
|
+
| `direction` | `'both' \| 'horizontal' \| 'vertical'` | Which way things may move. Default 'both'. |
|
|
115
|
+
| `pan` | `boolean` | Drag with the primary button to pan. Default true. |
|
|
116
|
+
| `zoom` | `boolean` | Turn the wheel to zoom about the pointer. Default true. |
|
|
117
|
+
| `limit` | `boolean` | Keep the view inside the natural range of each axis, so there is no panning off the data or zooming out past it. Default true. |
|
|
118
|
+
| `zoomSpeed` | `number` | Zoom per wheel pixel, as a natural-log factor. Default 0.002, which is about 20% for one notch of a typical wheel. |
|
|
119
|
+
| `axisZoom` | `boolean` | With the pointer (or both fingers) over an axis, a wheel turn, a pinch or a drag moves that axis alone. Default true. |
|
|
120
|
+
| `pinchSpeed` | `number` | Zoom per pixel of a pinch on a trackpad, which browsers report as small wheel turns with Ctrl held. Default 0.02, ten times the wheel's step per pixel. |
|
|
121
|
+
| `touch` | `'two-finger' \| 'always'` | What a touch does. 'two-finger' (the default): one finger scrolls the page as usual, and two pinch and pan the chart. 'always': one finger pans the chart too, and the page cannot be scrolled from it. |
|
|
122
|
+
| `inertia` | `boolean` | A finger that lets go while still moving carries the view on, slowing to a stop (a flick). Default true. |
|
|
123
|
+
| `resetOnDoubleClick` | `boolean` | Two quick presses put every axis back to its natural range. Default true. |
|
|
124
|
+
| `keys` | `PanZoomKeys \| false` | The keys, once the plot has keyboard focus. |
|
|
125
|
+
| `keyStep` | `number` | How far an arrow moves the view, as a fraction of it. Default 0.1. |
|
|
126
|
+
| `keyZoom` | `number` | The factor of one press of a zoom key. Default 1.25. |
|
|
127
|
+
|
|
128
|
+
### BoxZoom
|
|
129
|
+
|
|
130
|
+
Drag a box (Shift by default) to zoom to it. Before PanZoom.
|
|
131
|
+
|
|
132
|
+
**In** a plane · **Holds** none
|
|
133
|
+
|
|
134
|
+
| Attribute | Type | |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `axes` | `readonly ElementRef<Axis>[]` | The axes it may zoom, as elements or ids. Default: all of the plane's that can be followed along (not the rim of a polar plane). |
|
|
137
|
+
| `activate` | `'shift' \| 'always'` | When a drag makes a box: with Shift held (the default), so a plain drag can pan, or always. |
|
|
138
|
+
| `mode` | `'xy' \| 'x' \| 'y' \| 'auto'` | Which axes the box zooms. Default 'xy'. |
|
|
139
|
+
| `minSize` | `number` | Smallest side of a box, in pixels, along an axis it zooms; below that the box does nothing. Default 8. |
|
|
140
|
+
| `onZoom` | `(box: { x: number; y: number; width: number; height: …` | Called after a zoom, with the box in plot pixels. |
|
|
141
|
+
|
|
142
|
+
### Brush
|
|
143
|
+
|
|
144
|
+
Drag a range to select it and report it with onBrush; the selection stays drawn. Before PanZoom.
|
|
145
|
+
|
|
146
|
+
**In** a plane · **Holds** none
|
|
147
|
+
|
|
148
|
+
| Attribute | Type | |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `axes` | `readonly ElementRef<Axis>[]` | The axes it may cover, as elements or ids. Default: all of the plane's that can be followed along. |
|
|
151
|
+
| `activate` | `'shift' \| 'always'` | When a drag brushes: always (the default) or with Shift held, to leave a plain drag to `PanZoom`. |
|
|
152
|
+
| `mode` | `'xy' \| 'x' \| 'y'` | Which axes it covers. Default 'xy'. |
|
|
153
|
+
| `minSize` | `number` | Smallest side of a brush, in pixels, along an axis it covers; below that a drag only clears. Default 4. |
|
|
154
|
+
| `live` | `boolean` | Call `onBrush` while dragging too. Default false. |
|
|
155
|
+
| `onBrush` | `(selection: BrushSelection \| null) => void` | Called with the selection, or null when it is cleared. |
|
|
156
|
+
|
|
157
|
+
### PointerListener
|
|
158
|
+
|
|
159
|
+
Calls your functions on hover, click and drag.
|
|
160
|
+
|
|
161
|
+
**In** a plane · **Holds** none
|
|
162
|
+
|
|
163
|
+
| Attribute | Type | |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `onHover` | `(info: PointerInfo) => void` | The pointer came onto a point, left it, or moved to another: `info.hit` is the new one, null for none. |
|
|
166
|
+
| `onClick` | `(info: PointerInfo) => void` | A press and release in the same place: on a point, or `info.hit` null on empty plot. |
|
|
167
|
+
| `onDrag` | `(info: DragInfo) => void` | A drag that began on a point. |
|
|
168
|
+
| `dragThreshold` | `number` | How far in pixels the pointer must travel before a press is a drag and no longer a click. Default 3. |
|
|
169
|
+
<!-- api:end -->
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# inkplot topic: lines, areas and bars
|
|
2
|
+
|
|
3
|
+
For: bars (grouped, stacked), areas, smooth and stepped lines, curves, gaps in the data, several series sharing a plane.
|
|
4
|
+
|
|
5
|
+
Read `SKILL.md` first (rules, template, and the attributes of `Chart`, `CartesianPlane`, `Axis`, `LinearScale`, `DomainSeries`, `FlatDataSource`, `ObjectDataSource`, `LinePath`, `LinearCurve`). This file has the rest of what this topic needs: examples, notes, and the attributes of its elements. Everything here is exported from `'inkplot'`.
|
|
6
|
+
|
|
7
|
+
## Bars on categories
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
```ts check title="Bars"
|
|
11
|
+
import {
|
|
12
|
+
Axis,
|
|
13
|
+
BandScale,
|
|
14
|
+
BarPath,
|
|
15
|
+
CartesianPlane,
|
|
16
|
+
Chart,
|
|
17
|
+
DomainSeries,
|
|
18
|
+
FlatDataSource,
|
|
19
|
+
LinearScale,
|
|
20
|
+
lightTheme,
|
|
21
|
+
} from 'inkplot';
|
|
22
|
+
|
|
23
|
+
export const chart = new Chart(
|
|
24
|
+
{ theme: lightTheme },
|
|
25
|
+
new CartesianPlane(
|
|
26
|
+
{},
|
|
27
|
+
new Axis({ orientation: 'bottom' }, new BandScale({ categories: ['Q1', 'Q2', 'Q3', 'Q4'] })),
|
|
28
|
+
new Axis({ orientation: 'left', title: 'Revenue' }, new LinearScale()),
|
|
29
|
+
new DomainSeries(
|
|
30
|
+
{ name: '2024' },
|
|
31
|
+
new FlatDataSource({ data: [42, 58, 51, 73], regularDomain: { start: 0, step: 1 } }),
|
|
32
|
+
new BarPath(),
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Stacked or side by side: wrap several `DomainSeries` in `new Stack({ mode: 'stack' }, …)` (or `"group"`), add `new Legend({ side: 'bottom' })` to the `Chart`.
|
|
39
|
+
|
|
40
|
+
## Notes
|
|
41
|
+
|
|
42
|
+
- `Stack` `mode`: `'stack'`, `'group'`, `'normalize'`, `'center'`.
|
|
43
|
+
- `BarPath` bars start at `baseline` (default 0, and the axis includes it); the tallest bar touches the top of the plot, so add `padding: 0.05` (a fraction of the span) to the value `Axis` for headroom.
|
|
44
|
+
- Curves: `LinearCurve`, `MonotoneCurve`, `StepCurve`, `NaturalCurve`, `CardinalCurve`, `CatmullRomCurve`, `BasisCurve`, `BumpCurve`, `QuadraticCurve`, `ArcCurve`.
|
|
45
|
+
- `hit` on a series: `'point'`, `'line'`, `'area'`, `'shape'` or `false`. `gaps`: `'break'` (default), `'connect'`, `'zero'`.
|
|
46
|
+
|
|
47
|
+
| To… | Use |
|
|
48
|
+
|---|---|
|
|
49
|
+
| Smooth lines | `new LinePath({}, new MonotoneCurve())`; steps: `new StepCurve({ mode: 'after' })` |
|
|
50
|
+
| Series gaps | `NaN` values are gaps; `gaps: 'connect'` on the series to cross them |
|
|
51
|
+
| Hide a series | `series.setAttribute('visible', false)` |
|
|
52
|
+
|
|
53
|
+
## Attributes of this topic's elements
|
|
54
|
+
|
|
55
|
+
An attribute followed by `(required)` must be given. Every element also takes `id`, `meta`, `visible` and `style`.
|
|
56
|
+
|
|
57
|
+
<!-- api:start -->
|
|
58
|
+
### Stack
|
|
59
|
+
|
|
60
|
+
Series that share axes and are laid on each other or side by side.
|
|
61
|
+
|
|
62
|
+
**In** a plane · **Holds** DomainSeries, any number
|
|
63
|
+
|
|
64
|
+
| Attribute | Type | |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `mode` | `StackMode` | How the members' values are laid out. Default 'stack'. |
|
|
67
|
+
| `align` | `StackAlign` | How rows are matched up when the members' domains differ. Default 'strict': they must not. |
|
|
68
|
+
|
|
69
|
+
### AreaPath
|
|
70
|
+
|
|
71
|
+
The region between a line and a baseline.
|
|
72
|
+
|
|
73
|
+
**In** a series · **Holds** 1 curve (required)
|
|
74
|
+
|
|
75
|
+
| Attribute | Type | |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `baseline` | `number` | The value the area is filled from, kept inside the value axis' range. Default 0. |
|
|
78
|
+
| `stroke` | `string` | Outline colour (a CSS colour). Default: from the theme. |
|
|
79
|
+
| `strokeWidth` | `number` | Outline width in pixels. Default: from the theme. |
|
|
80
|
+
| `fill` | `string` | Fill colour (a CSS colour; 'none' for no fill). Default: from the theme. |
|
|
81
|
+
| `opacity` | `number` | Opacity from 0 to 1. Default 1. |
|
|
82
|
+
| `bind` | `Bindings<Bindable>` | Attributes fed from data instead of fixed. |
|
|
83
|
+
| `maxNodes` | `number` | Most paths this renderer may create. Default 1000. |
|
|
84
|
+
|
|
85
|
+
### MonotoneCurve
|
|
86
|
+
|
|
87
|
+
A smooth curve that never overshoots.
|
|
88
|
+
|
|
89
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
90
|
+
|
|
91
|
+
No attributes of its own.
|
|
92
|
+
|
|
93
|
+
### NaturalCurve
|
|
94
|
+
|
|
95
|
+
A natural cubic spline.
|
|
96
|
+
|
|
97
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
98
|
+
|
|
99
|
+
No attributes of its own.
|
|
100
|
+
|
|
101
|
+
### CardinalCurve
|
|
102
|
+
|
|
103
|
+
A cardinal spline.
|
|
104
|
+
|
|
105
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
106
|
+
|
|
107
|
+
| Attribute | Type | |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `tension` | `number` | How far the curve is tightened towards straight lines, 0 (the Catmull-Rom spline) to 1. Default 0. |
|
|
110
|
+
|
|
111
|
+
### CatmullRomCurve
|
|
112
|
+
|
|
113
|
+
A Catmull-Rom spline.
|
|
114
|
+
|
|
115
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
116
|
+
|
|
117
|
+
| Attribute | Type | |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `alpha` | `number` | The spacing of the knots: 0 even, 0.5 centripetal, 1 chordal. Default 0.5. |
|
|
120
|
+
|
|
121
|
+
### BasisCurve
|
|
122
|
+
|
|
123
|
+
A B-spline that smooths past the points.
|
|
124
|
+
|
|
125
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
126
|
+
|
|
127
|
+
No attributes of its own.
|
|
128
|
+
|
|
129
|
+
### BumpCurve
|
|
130
|
+
|
|
131
|
+
S-shaped joins.
|
|
132
|
+
|
|
133
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
134
|
+
|
|
135
|
+
| Attribute | Type | |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `along` | `'x' \| 'y'` | Whether the level ends are along x (a line level at each point) or y. Default: along the series' first axis. |
|
|
138
|
+
|
|
139
|
+
### QuadraticCurve
|
|
140
|
+
|
|
141
|
+
Quadratic joins.
|
|
142
|
+
|
|
143
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
144
|
+
|
|
145
|
+
No attributes of its own.
|
|
146
|
+
|
|
147
|
+
### ArcCurve
|
|
148
|
+
|
|
149
|
+
Circular arcs.
|
|
150
|
+
|
|
151
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
152
|
+
|
|
153
|
+
| Attribute | Type | |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `bend` | `number` | The rise of each arc as a fraction of its chord, positive bulging to the left of the way along. Default 0.3. |
|
|
156
|
+
|
|
157
|
+
### StepCurve
|
|
158
|
+
|
|
159
|
+
Levels and jumps.
|
|
160
|
+
|
|
161
|
+
**In** LinePath or AreaPath · **Holds** none
|
|
162
|
+
|
|
163
|
+
| Attribute | Type | |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `mode` | `StepMode` | Where the value changes between one point and the next. Default 'after'. |
|
|
166
|
+
|
|
167
|
+
### BarPath
|
|
168
|
+
|
|
169
|
+
Bars, in one path.
|
|
170
|
+
|
|
171
|
+
**In** a series · **Holds** none
|
|
172
|
+
|
|
173
|
+
| Attribute | Type | |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `stroke` | `string` | Outline colour (a CSS colour). Default: from the theme. |
|
|
176
|
+
| `strokeWidth` | `number` | Outline width in pixels. Default: from the theme. |
|
|
177
|
+
| `fill` | `string` | Fill colour (a CSS colour; 'none' for no fill). Default: from the theme. |
|
|
178
|
+
| `opacity` | `number` | Opacity from 0 to 1. Default 1. |
|
|
179
|
+
| `bind` | `Bindings<Bindable>` | Attributes fed from data instead of fixed. |
|
|
180
|
+
| `maxNodes` | `number` | Most paths this renderer may create. Default 1000. |
|
|
181
|
+
| `width` | `number` | Fraction of the room each bar fills. Default: a band's own `bandwidth`, else 0.8. |
|
|
182
|
+
| `baseline` | `number` | The value bars grow from when there is no stack to say. Default 0. |
|
|
183
|
+
|
|
184
|
+
### BarLinePath
|
|
185
|
+
|
|
186
|
+
Bars drawn as stems.
|
|
187
|
+
|
|
188
|
+
**In** a series · **Holds** none
|
|
189
|
+
|
|
190
|
+
| Attribute | Type | |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| `cap` | `'butt' \| 'round' \| 'square'` | How the end of a stem is finished: 'butt' (flat, at the value), 'round' or 'square' (both reach half a width past). Default 'butt'. |
|
|
193
|
+
| `stroke` | `string` | Outline colour (a CSS colour). Default: from the theme. |
|
|
194
|
+
| `strokeWidth` | `number` | Outline width in pixels. Default: from the theme. |
|
|
195
|
+
| `fill` | `string` | Fill colour (a CSS colour; 'none' for no fill). Default: from the theme. |
|
|
196
|
+
| `opacity` | `number` | Opacity from 0 to 1. Default 1. |
|
|
197
|
+
| `bind` | `Bindings<Bindable>` | Attributes fed from data instead of fixed. |
|
|
198
|
+
| `maxNodes` | `number` | Most paths this renderer may create. Default 1000. |
|
|
199
|
+
| `width` | `number` | Fraction of the room each bar fills. Default: a band's own `bandwidth`, else 0.8. |
|
|
200
|
+
| `baseline` | `number` | The value bars grow from when there is no stack to say. Default 0. |
|
|
201
|
+
<!-- api:end -->
|