Skip to content

Commit 32462b1

Browse files
authored
tick marker (#1892)
* tick marker * tick marker documentation
1 parent 51d85c4 commit 32462b1

File tree

8 files changed

+423
-5
lines changed

8 files changed

+423
-5
lines changed

docs/features/markers.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ A **marker** defines a graphic drawn on vertices of a [line](../marks/line.md) o
2323
<option>dot</option>
2424
<option>circle</option>
2525
<option>circle-stroke</option>
26+
<option>tick</option>
27+
<option>tick-x</option>
28+
<option>tick-y</option>
2629
</select>
2730
</label>
2831
</p>
@@ -53,6 +56,9 @@ The following named markers are supported:
5356
* *dot* - a filled *circle* without a stroke and 2.5px radius
5457
* *circle*, equivalent to *circle-fill* - a filled circle with a white stroke and 3px radius
5558
* *circle-stroke* - a hollow circle with a colored stroke and a white fill and 3px radius
59+
* *tick* - a small opposing line
60+
* *tick-x* - a small horizontal line
61+
* *tick-y* - a small vertical line
5662

5763
If **marker** is true, it defaults to *circle*. If **marker** is a function, it will be called with a given *color* and must return an [SVG marker element](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/marker).
5864

docs/marks/rule.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,31 @@ Plot.plot({
120120
```
121121
:::
122122

123-
Rules can also be a stylistic choice, as in the lollipop 🍭 chart below, serving the role of a skinny [bar](./bar.md) topped with a [dot](./dot.md).
123+
Rules can indicate uncertainty or error by setting the [**marker** option](../features/markers.md) to *tick*; this draws a small perpendicular line at the start and end of the rule. For example, to simulate ±10% error:
124+
125+
:::plot
126+
```js
127+
Plot.plot({
128+
x: {label: null},
129+
y: {percent: true},
130+
marks: [
131+
Plot.barY(alphabet, {x: "letter", y: "frequency", fill: "blue"}),
132+
Plot.ruleX(alphabet, {x: "letter", y1: (d) => d.frequency * 0.9, y2: (d) => d.frequency * 1.1, marker: "tick"}),
133+
Plot.ruleY([0])
134+
]
135+
})
136+
```
137+
:::
138+
139+
Rules can also be a stylistic choice, as in the lollipop 🍭 chart below, serving the role of a skinny [bar](./bar.md) topped with a [*dot* marker](../features/markers.md).
124140

125141
:::plot https://observablehq.com/@observablehq/plot-lollipop
126142
```js
127143
Plot.plot({
128144
x: {label: null, tickPadding: 6, tickSize: 0},
129145
y: {percent: true},
130146
marks: [
131-
Plot.ruleX(alphabet, {x: "letter", y: "frequency", strokeWidth: 2}),
132-
Plot.dot(alphabet, {x: "letter", y: "frequency", fill: "currentColor", r: 4})
147+
Plot.ruleX(alphabet, {x: "letter", y: "frequency", strokeWidth: 2, markerEnd: "dot"})
133148
]
134149
})
135150
```

src/marker.d.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,20 @@
77
* - *circle-fill* - a filled circle with a white stroke and 3px radius
88
* - *circle-stroke* - a stroked circle with a white fill and 3px radius
99
* - *circle* - alias for *circle-fill*
10+
* - *tick* - a small opposing line
11+
* - *tick-x* - a small horizontal line
12+
* - *tick-y* - a small vertical line
1013
*/
11-
export type MarkerName = "arrow" | "arrow-reverse" | "dot" | "circle" | "circle-fill" | "circle-stroke";
14+
export type MarkerName =
15+
| "arrow"
16+
| "arrow-reverse"
17+
| "dot"
18+
| "circle"
19+
| "circle-fill"
20+
| "circle-stroke"
21+
| "tick"
22+
| "tick-x"
23+
| "tick-y";
1224

1325
/** A custom marker implementation. */
1426
export type MarkerFunction = (color: string, context: {document: Document}) => SVGMarkerElement;

src/marker.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ function maybeMarker(marker) {
2424
return markerCircleFill;
2525
case "circle-stroke":
2626
return markerCircleStroke;
27+
case "tick":
28+
return markerTick("auto");
29+
case "tick-x":
30+
return markerTick(90);
31+
case "tick-y":
32+
return markerTick(0);
2733
}
2834
throw new Error(`invalid marker: ${marker}`);
2935
}
@@ -79,6 +85,18 @@ function markerCircleStroke(color, context) {
7985
.node();
8086
}
8187

88+
function markerTick(orient) {
89+
return (color, context) =>
90+
create("svg:marker", context)
91+
.attr("viewBox", "-3 -3 6 6")
92+
.attr("markerWidth", 6)
93+
.attr("markerHeight", 6)
94+
.attr("orient", orient)
95+
.attr("stroke", color)
96+
.call((marker) => marker.append("path").attr("d", "M0,-3v6"))
97+
.node();
98+
}
99+
82100
let nextMarkerId = 0;
83101

84102
export function applyMarkers(path, mark, {stroke: S}, context) {

test/output/errorBarX.svg

Lines changed: 164 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)