inkplot-scratch 0.0.1-placeholder.0 → 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.
@@ -0,0 +1,274 @@
1
+ # inkplot topic: markers, colour and size from data
2
+
3
+ For: scatter and bubble charts, dots and crosses, colour or size taken from the data, colour and size keys.
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
+ ## Scatter, coloured by a value, with a key
8
+
9
+
10
+ `XYSeries` takes x from a channel (`x: 'x'` names the channel; the source's `domain` is not used and can be left out), and y from the source's `value`. Colour comes from a `ColorAxis` (a child of the **chart**, beside the plane) that the marker binds to by id. A `ColorKey` shows it.
11
+
12
+ ```ts check title="Coloured scatter"
13
+ import {
14
+ Axis,
15
+ CartesianPlane,
16
+ Chart,
17
+ CircleMarker,
18
+ ColorAxis,
19
+ ColorKey,
20
+ LinearScale,
21
+ ObjectDataSource,
22
+ XYSeries,
23
+ lightTheme,
24
+ } from 'inkplot';
25
+
26
+ const rows = Array.from({ length: 30 }, (_, i) => ({ x: i, y: Math.sin(i / 4) * 10, temp: i * 1.5 }));
27
+
28
+ export const chart = new Chart(
29
+ { theme: lightTheme },
30
+ new CartesianPlane(
31
+ {},
32
+ new Axis({ orientation: 'bottom' }, new LinearScale()),
33
+ new Axis({ orientation: 'left' }, new LinearScale()),
34
+ new XYSeries(
35
+ { name: 'Readings', x: 'x' },
36
+ new ObjectDataSource({ source: rows, value: 'y', channels: { x: 'x', temp: 'temp' } }),
37
+ new CircleMarker({ size: 5, bind: { fill: { axis: 'heat', channel: 'temp' } } }),
38
+ ),
39
+ ),
40
+ new ColorAxis({ id: 'heat', title: 'Temperature', ramp: 'magma' }, new LinearScale()),
41
+ new ColorKey({ dimension: 'heat', side: 'right' }),
42
+ );
43
+ ```
44
+
45
+ Only **markers** (and `HeatMap`) can `bind` for now. A `bind`'s `channel` is the name on the left of the source's `channels` map, not the field name in the rows. Sizes work the same way:
46
+
47
+ ```ts check title="Bubble chart with a size key"
48
+ import {
49
+ Axis,
50
+ CartesianPlane,
51
+ Chart,
52
+ CircleMarker,
53
+ LinearScale,
54
+ ObjectDataSource,
55
+ SizeAxis,
56
+ SizeKey,
57
+ XYSeries,
58
+ lightTheme,
59
+ } from 'inkplot';
60
+
61
+ const rows = Array.from({ length: 20 }, (_, i) => ({ x: i, y: Math.sin(i / 3) * 10, weight: 1 + (i % 7) }));
62
+
63
+ export const chart = new Chart(
64
+ { theme: lightTheme },
65
+ new CartesianPlane(
66
+ {},
67
+ new Axis({ orientation: 'bottom' }, new LinearScale()),
68
+ new Axis({ orientation: 'left' }, new LinearScale()),
69
+ new XYSeries(
70
+ { name: 'Items', x: 'x' },
71
+ new ObjectDataSource({ source: rows, value: 'y', channels: { x: 'x', mass: 'weight' } }),
72
+ new CircleMarker({ bind: { size: { axis: 'size', channel: 'mass' } } }),
73
+ ),
74
+ ),
75
+ new SizeAxis({ id: 'size', title: 'Weight', output: [3, 12] }, new LinearScale()),
76
+ new SizeKey({ dimension: 'size', side: 'right' }),
77
+ );
78
+ ```
79
+
80
+ `SizeAxis` `output` is the marker size range in pixels (default `[2, 12]`). A bound `size` replaces the marker's own `size`. Both keys can share a side.
81
+
82
+ ## Notes
83
+
84
+ - A `bind` can carry several properties at once: `bind: { fill: { axis: 'heat', channel: 'temp' }, size: { axis: 'size', channel: 'mass' } }`. Markers come out as merged `<path>` elements (a dot is a zero-length stroke with a round cap), not `<circle>`s.
85
+ - Markers: `CircleMarker`, `SquareMarker`, `DotMarker`, `CrossMarker`, `PlusMarker`. A series may have a marker *and* a line.
86
+ - `bind` (markers and `HeatMap` only) takes `fill`, `stroke`, `opacity`, and for markers `size`. A `ColorAxis` `ramp` is `'viridis'`, `'magma'`, `'turbo'`, `'blues'`, `'greys'` or `'diverging'`. A `ColorKey` shows its `title`, else the axis's `title`.
87
+ - `Legend` and the keys: `side` is `'top'`, `'right'`, `'bottom'` or `'left'`; `orientation` is `'horizontal'` or `'vertical'`.
88
+
89
+ ## Attributes of this topic's elements
90
+
91
+ An attribute followed by `(required)` must be given. Every element also takes `id`, `meta`, `visible` and `style`.
92
+
93
+ <!-- api:start -->
94
+ ### XYSeries
95
+
96
+ Points with x and y both from the data.
97
+
98
+ **In** a plane · **Holds** 1 data, then any renderers
99
+
100
+ | Attribute | Type | |
101
+ |---|---|---|
102
+ | `x` (required) | `Channel` | The channel of the source that is x. |
103
+ | `xAxis` | `ElementRef<Axis>` | Default: the plane's first axis (a Cartesian plane's horizontal one). |
104
+ | `yAxis` | `ElementRef<Axis>` | Default: the plane's second axis (the vertical one). |
105
+ | `name` | `string` | What the series is called where it is named: a tooltip's heading. |
106
+ | `hit` | `false \| HitMode \| HitOptions` | How pointer hits find this series: a mode name, options, or `false` for a series that should never be hit (a selection overlay, a reference line). |
107
+ | `gaps` | `'break' \| 'connect' \| 'zero'` | What a missing value (NaN, or infinite) means. 'break' (default): nothing is there, so a line stops and starts again and an area splits. 'connect': the line goes straight across to the next value there is. 'zero': the value is 0 (and the axis reaches it). |
108
+
109
+ ### DotMarker
110
+
111
+ A dot at each point: the cheapest marker.
112
+
113
+ **In** a series · **Holds** none
114
+
115
+ | Attribute | Type | |
116
+ |---|---|---|
117
+ | `cap` | `'round' \| 'square'` | Round caps make circles, square caps make squares. Default 'round'. |
118
+ | `size` | `number` | Half the width of a marker in pixels: a radius, or the distance from the centre to an edge. Default 3. |
119
+ | `merge` | `boolean` | Draw all the markers that look alike as one path. Default true. |
120
+ | `stroke` | `string` | Outline colour (a CSS colour). Default: from the theme. |
121
+ | `strokeWidth` | `number` | Outline width in pixels. Default: from the theme. |
122
+ | `fill` | `string` | Fill colour (a CSS colour; 'none' for no fill). Default: from the theme. |
123
+ | `opacity` | `number` | Opacity from 0 to 1. Default 1. |
124
+ | `bind` | `Bindings<Bindable>` | Attributes fed from data instead of fixed. |
125
+ | `maxNodes` | `number` | Most paths this renderer may create. Default 1000. |
126
+
127
+ ### CrossMarker
128
+
129
+ × at each point.
130
+
131
+ **In** a series · **Holds** none
132
+
133
+ | Attribute | Type | |
134
+ |---|---|---|
135
+ | `size` | `number` | Half the width of a marker in pixels: a radius, or the distance from the centre to an edge. Default 3. |
136
+ | `merge` | `boolean` | Draw all the markers that look alike as one path. Default true. |
137
+ | `stroke` | `string` | Outline colour (a CSS colour). Default: from the theme. |
138
+ | `strokeWidth` | `number` | Outline width in pixels. Default: from the theme. |
139
+ | `fill` | `string` | Fill colour (a CSS colour; 'none' for no fill). Default: from the theme. |
140
+ | `opacity` | `number` | Opacity from 0 to 1. Default 1. |
141
+ | `bind` | `Bindings<Bindable>` | Attributes fed from data instead of fixed. |
142
+ | `maxNodes` | `number` | Most paths this renderer may create. Default 1000. |
143
+
144
+ ### PlusMarker
145
+
146
+ + at each point.
147
+
148
+ **In** a series · **Holds** none
149
+
150
+ | Attribute | Type | |
151
+ |---|---|---|
152
+ | `size` | `number` | Half the width of a marker in pixels: a radius, or the distance from the centre to an edge. Default 3. |
153
+ | `merge` | `boolean` | Draw all the markers that look alike as one path. Default true. |
154
+ | `stroke` | `string` | Outline colour (a CSS colour). Default: from the theme. |
155
+ | `strokeWidth` | `number` | Outline width in pixels. Default: from the theme. |
156
+ | `fill` | `string` | Fill colour (a CSS colour; 'none' for no fill). Default: from the theme. |
157
+ | `opacity` | `number` | Opacity from 0 to 1. Default 1. |
158
+ | `bind` | `Bindings<Bindable>` | Attributes fed from data instead of fixed. |
159
+ | `maxNodes` | `number` | Most paths this renderer may create. Default 1000. |
160
+
161
+ ### CircleMarker
162
+
163
+ A filled circle at each point.
164
+
165
+ **In** a series · **Holds** none
166
+
167
+ | Attribute | Type | |
168
+ |---|---|---|
169
+ | `size` | `number` | Half the width of a marker in pixels: a radius, or the distance from the centre to an edge. Default 3. |
170
+ | `merge` | `boolean` | Draw all the markers that look alike as one path. Default true. |
171
+ | `stroke` | `string` | Outline colour (a CSS colour). Default: from the theme. |
172
+ | `strokeWidth` | `number` | Outline width in pixels. Default: from the theme. |
173
+ | `fill` | `string` | Fill colour (a CSS colour; 'none' for no fill). Default: from the theme. |
174
+ | `opacity` | `number` | Opacity from 0 to 1. Default 1. |
175
+ | `bind` | `Bindings<Bindable>` | Attributes fed from data instead of fixed. |
176
+ | `maxNodes` | `number` | Most paths this renderer may create. Default 1000. |
177
+
178
+ ### SquareMarker
179
+
180
+ A square at each point.
181
+
182
+ **In** a series · **Holds** none
183
+
184
+ | Attribute | Type | |
185
+ |---|---|---|
186
+ | `size` | `number` | Half the width of a marker in pixels: a radius, or the distance from the centre to an edge. Default 3. |
187
+ | `merge` | `boolean` | Draw all the markers that look alike as one path. Default true. |
188
+ | `stroke` | `string` | Outline colour (a CSS colour). Default: from the theme. |
189
+ | `strokeWidth` | `number` | Outline width in pixels. Default: from the theme. |
190
+ | `fill` | `string` | Fill colour (a CSS colour; 'none' for no fill). Default: from the theme. |
191
+ | `opacity` | `number` | Opacity from 0 to 1. Default 1. |
192
+ | `bind` | `Bindings<Bindable>` | Attributes fed from data instead of fixed. |
193
+ | `maxNodes` | `number` | Most paths this renderer may create. Default 1000. |
194
+
195
+ ### ColorAxis
196
+
197
+ Maps data to colours.
198
+
199
+ **In** Chart · **Holds** 1 scale (required)
200
+
201
+ | Attribute | Type | |
202
+ |---|---|---|
203
+ | `ramp` | `ColorRampName \| readonly string[]` | A named ramp, or a list of colours spread evenly. Default 'viridis'. |
204
+ | `interpolation` | `'linear' \| 'step'` | Blend between the stops or pick the nearest band. Default 'linear'. |
205
+ | `missing` | `string` | Colour for data with no value or outside the range. Default '#bbb'. |
206
+ | `min` | `number` | Fix an end of the range. |
207
+ | `max` | `number` | Fix the top end of the range. |
208
+ | `include` | `readonly number[]` | Values the range must cover, e.g. `[0]` to anchor a bar-like axis at zero. |
209
+ | `padding` | `number` | Extra room at both ends as a fraction of the data's span. |
210
+ | `nice` | `boolean` | Round the ends outwards to tidy tick values. |
211
+ | `inverted` | `boolean` | Run from max to min along the axis. |
212
+ | `title` | `string` | The axis title (or, on a `ColorAxis` or `SizeAxis`, the default title of its key). Default: none. |
213
+ | `tickCount` | `number` | Ask for about this many ticks, whatever the length. |
214
+ | `ticks` | `readonly number[] \| ((range: Readonly<Extent>) => readonly …` | Put ticks exactly here (those inside the range are drawn), or at whatever a function of the range returns. |
215
+ | `tickStep` | `number` | Put ticks at every multiple of this step. |
216
+ | `format` | `(value: number) => string` | Turns a tick's number into its label, e.g. (v) => `${v}%`. Default: plain digits, grouped by `locale` if one is set. |
217
+ | `locale` | `string` | A BCP 47 tag for how this writes its numbers (grouping and decimal mark). Default: the chart's `locale`, else plain digits. |
218
+ | `onViewChange` | `(view: Readonly<Extent> \| null) => void` | Called when the view (what pan, zoom or a reset has set) changes: the new range, or null when it is back to the natural one. |
219
+
220
+ ### SizeAxis
221
+
222
+ Maps data to sizes or other numbers.
223
+
224
+ **In** Chart · **Holds** 1 scale (required)
225
+
226
+ | Attribute | Type | |
227
+ |---|---|---|
228
+ | `output` | `readonly [min: number, max: number]` | The output range. |
229
+ | `missing` | `number` | Output for data with no value or outside the range. Default: the low end of `output`. |
230
+ | `min` | `number` | Fix an end of the range. |
231
+ | `max` | `number` | Fix the top end of the range. |
232
+ | `include` | `readonly number[]` | Values the range must cover, e.g. `[0]` to anchor a bar-like axis at zero. |
233
+ | `padding` | `number` | Extra room at both ends as a fraction of the data's span. |
234
+ | `nice` | `boolean` | Round the ends outwards to tidy tick values. |
235
+ | `inverted` | `boolean` | Run from max to min along the axis. |
236
+ | `title` | `string` | The axis title (or, on a `ColorAxis` or `SizeAxis`, the default title of its key). Default: none. |
237
+ | `tickCount` | `number` | Ask for about this many ticks, whatever the length. |
238
+ | `ticks` | `readonly number[] \| ((range: Readonly<Extent>) => readonly …` | Put ticks exactly here (those inside the range are drawn), or at whatever a function of the range returns. |
239
+ | `tickStep` | `number` | Put ticks at every multiple of this step. |
240
+ | `format` | `(value: number) => string` | Turns a tick's number into its label, e.g. (v) => `${v}%`. Default: plain digits, grouped by `locale` if one is set. |
241
+ | `locale` | `string` | A BCP 47 tag for how this writes its numbers (grouping and decimal mark). Default: the chart's `locale`, else plain digits. |
242
+ | `onViewChange` | `(view: Readonly<Extent> \| null) => void` | Called when the view (what pan, zoom or a reset has set) changes: the new range, or null when it is back to the natural one. |
243
+
244
+ ### ColorKey
245
+
246
+ Shows what a ColorAxis means.
247
+
248
+ **In** Chart · **Holds** none
249
+
250
+ | Attribute | Type | |
251
+ |---|---|---|
252
+ | `thickness` | `number` | Thickness of the colour bar in pixels. Default 14. |
253
+ | `dimension` (required) | `ElementRef<Dimension>` | The dimension this key shows, or its id. |
254
+ | `side` | `Side` | The edge of the chart it sits along. Default 'right'. |
255
+ | `orientation` | `KeyOrientation` | Which way its scale runs. Default: along its side, so vertical on the left and right, horizontal on the top and bottom. |
256
+ | `title` | `string` | Default: the dimension's own title. |
257
+ | `length` | `number` | Length of the scale in pixels. Default 140. |
258
+ | `breadth` | `number` | Width of a vertical key: the scale and its labels. Default 72. |
259
+
260
+ ### SizeKey
261
+
262
+ Shows what a SizeAxis means.
263
+
264
+ **In** Chart · **Holds** none
265
+
266
+ | Attribute | Type | |
267
+ |---|---|---|
268
+ | `dimension` (required) | `ElementRef<Dimension>` | The dimension this key shows, or its id. |
269
+ | `side` | `Side` | The edge of the chart it sits along. Default 'right'. |
270
+ | `orientation` | `KeyOrientation` | Which way its scale runs. Default: along its side, so vertical on the left and right, horizontal on the top and bottom. |
271
+ | `title` | `string` | Default: the dimension's own title. |
272
+ | `length` | `number` | Length of the scale in pixels. Default 140. |
273
+ | `breadth` | `number` | Width of a vertical key: the scale and its labels. Default 72. |
274
+ <!-- api:end -->
@@ -0,0 +1,139 @@
1
+ # inkplot topic: polar and radar charts
2
+
3
+ For: polar and radar charts.
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
+ ## Polar and radar
8
+
9
+
10
+ `RadarPlane` and `PolarPlane` take an `angle` axis and a `radius` axis; the series and renderers are the same. For named spokes use a `LinearScale` with `min: 0`, `max` the number of categories, `tickCount` one more, and a `format` that turns the index into a name. `wrap` repeats the first point so the shape closes.
11
+
12
+ ```ts check title="Radar" height=380
13
+ import {
14
+ AreaPath,
15
+ Axis,
16
+ Chart,
17
+ DomainSeries,
18
+ FlatDataSource,
19
+ LinePath,
20
+ LinearCurve,
21
+ LinearScale,
22
+ RadarPlane,
23
+ lightTheme,
24
+ } from 'inkplot';
25
+
26
+ const skills = ['Speed', 'Power', 'Range', 'Armour', 'Stealth', 'Control'];
27
+
28
+ export const chart = new Chart(
29
+ { theme: lightTheme },
30
+ new RadarPlane(
31
+ {},
32
+ new Axis(
33
+ {
34
+ orientation: 'angle',
35
+ min: 0,
36
+ max: skills.length,
37
+ tickCount: skills.length + 1,
38
+ format: (v: number) => skills[v] ?? '',
39
+ },
40
+ new LinearScale(),
41
+ ),
42
+ new Axis({ orientation: 'radius', min: 0, max: 10, tickCount: 6 }, new LinearScale()),
43
+ new DomainSeries(
44
+ { name: 'Profile A' },
45
+ new FlatDataSource({ data: [8, 5, 7, 4, 9, 6], regularDomain: { start: 0, step: 1 }, wrap: skills.length }),
46
+ new AreaPath({ fill: '#2a6fdb', opacity: 0.25 }, new LinearCurve()),
47
+ new LinePath({ stroke: '#2a6fdb' }, new LinearCurve()),
48
+ ),
49
+ ),
50
+ );
51
+ ```
52
+
53
+
54
+ Raw readings on a polar plane are a scatter: an `XYSeries` whose `x` is the channel that carries the angle, the data value as the radius. The angle axis spans whatever range you give it round the full turn, so angles in degrees need no conversion: `min: 0, max: 360`. `bind` colours the points by another channel, here the time, through a `ColorAxis`.
55
+
56
+ ```ts check title="Polar scatter, angle in degrees, coloured by time" height=420
57
+ import {
58
+ Axis,
59
+ Chart,
60
+ CircleMarker,
61
+ ColorAxis,
62
+ ColorKey,
63
+ LinearScale,
64
+ ObjectDataSource,
65
+ PolarPlane,
66
+ XYSeries,
67
+ lightTheme,
68
+ } from 'inkplot';
69
+
70
+ const rows = Array.from({ length: 400 }, (_, i) => ({
71
+ t: i / 10,
72
+ deg: (i * 9) % 360,
73
+ volts: 1 + 3 * Math.abs(Math.sin(i / 40)),
74
+ }));
75
+
76
+ export const chart = new Chart(
77
+ { theme: lightTheme, width: 520, height: 420 },
78
+ new PolarPlane(
79
+ {},
80
+ new Axis(
81
+ {
82
+ orientation: 'angle',
83
+ min: 0,
84
+ max: 360,
85
+ ticks: [0, 45, 90, 135, 180, 225, 270, 315],
86
+ format: (v: number) => `${v}°`,
87
+ },
88
+ new LinearScale(),
89
+ ),
90
+ new Axis({ orientation: 'radius', min: 0 }, new LinearScale()),
91
+ new XYSeries(
92
+ { name: 'Readings', x: 'angle' },
93
+ new ObjectDataSource({ source: rows, value: 'volts', channels: { angle: 'deg', time: 't' } }),
94
+ new CircleMarker({ size: 3, bind: { fill: { axis: 'time', channel: 'time' } } }),
95
+ ),
96
+ ),
97
+ new ColorAxis({ id: 'time', ramp: 'viridis', title: 'Time (s)' }, new LinearScale()),
98
+ new ColorKey({ dimension: 'time', side: 'right' }),
99
+ );
100
+ ```
101
+
102
+ A polar plane is a circle fitted into the room that is left, so a square chart with a colour key at the side leaves blank space below the circle: make the chart wider than it is tall (as above). Give a full turn's angle axis `ticks` that stop short of 360, as above: 0° and 360° are the same spot, and their labels would print over each other. Leave the `radius` axis without a `title`: it is drawn over the 0° spoke and the tick labels. Put the unit in the chart's title or caption instead ("radius = magnitude (V)").
103
+
104
+
105
+ ## Attributes of this topic's elements
106
+
107
+ An attribute followed by `(required)` must be given. Every element also takes `id`, `meta`, `visible` and `style`.
108
+
109
+ <!-- api:start -->
110
+ ### PolarPlane
111
+
112
+ A disc: angle and radius.
113
+
114
+ **In** Chart · **Holds** as CartesianPlane
115
+
116
+ | Attribute | Type | |
117
+ |---|---|---|
118
+ | `startAngle` | `number` | Radians clockwise from twelve o'clock. Default 0. |
119
+ | `sweep` | `number` | Radians swept by the angle axis, clockwise; negative runs the other way. Default 2π. |
120
+ | `innerRadius` | `number` | Fraction of the radius left empty in the middle. Default 0. |
121
+ | `gridlines` | `boolean` | Rings at the radius axis' ticks and spokes at the angle axis'. Default true. |
122
+ | `clip` | `boolean` | Clip series to the plot's outline. Default true. |
123
+ | `insets` | `Partial<Insets>` | Fixed margins round the plot (room for axes). |
124
+
125
+ ### RadarPlane
126
+
127
+ A polygon with a spoke per category.
128
+
129
+ **In** Chart · **Holds** as CartesianPlane
130
+
131
+ | Attribute | Type | |
132
+ |---|---|---|
133
+ | `startAngle` | `number` | Radians clockwise from twelve o'clock. Default 0. |
134
+ | `sweep` | `number` | Radians swept by the angle axis, clockwise; negative runs the other way. Default 2π. |
135
+ | `innerRadius` | `number` | Fraction of the radius left empty in the middle. Default 0. |
136
+ | `gridlines` | `boolean` | Rings at the radius axis' ticks and spokes at the angle axis'. Default true. |
137
+ | `clip` | `boolean` | Clip series to the plot's outline. Default true. |
138
+ | `insets` | `Partial<Insets>` | Fixed margins round the plot (room for axes). |
139
+ <!-- api:end -->