Skip to content

Commit 7abc115

Browse files
committed
area + line
1 parent 1d01e25 commit 7abc115

5 files changed

Lines changed: 127 additions & 8 deletions

File tree

docs/marks/area.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ Plot.plot((() => {
265265
```
266266
:::
267267

268+
The **line** option draws a line connecting the points with coordinates **x2** and **y2** (the “top of the area”).
269+
270+
Example TK.
271+
268272
See also the [ridgeline chart](https://observablehq.com/@observablehq/plot-ridgeline) example.
269273

270274
Interpolation is controlled by the [**curve** option](../features/curves.md). The default curve is *linear*, which draws straight line segments between pairs of adjacent points. A *step* curve is nice for emphasizing when the value changes, while *basis* and *catmull–rom* are nice for smoothing.
@@ -292,6 +296,8 @@ Points along the baseline and topline are connected in input order. Likewise, if
292296

293297
The area mark supports [curve options](../features/curves.md) to control interpolation between points. If any of the **x1**, **y1**, **x2**, or **y2** values are invalid (undefined, null, or NaN), the baseline and topline will be interrupted, resulting in a break that divides the area shape into multiple segments. (See [d3-shape’s *area*.defined](https://d3js.org/d3-shape/area#area_defined) for more.) If an area segment consists of only a single point, it may appear invisible unless rendered with rounded or square line caps. In addition, some curves such as *cardinal-open* only render a visible segment if it contains multiple points.
294298

299+
The **line** option (boolean, defaults to false) indicates whether the mark should draw a line connecting the points with coordinates **x2** and **y2** (the “top of the area”). In that case, the **stroke** attribute defaults to *currentColor* and is applied to the line only, as well as the stroke opacity. The line uses the same **curve** as the area.
300+
295301
## areaY(*data*, *options*) {#areaY}
296302

297303
```js

src/marks/area.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ export interface AreaOptions extends MarkOptions, StackOptions, CurveOptions {
4242
* **stroke** if a channel.
4343
*/
4444
z?: ChannelValue;
45+
46+
/**
47+
* Whether a line should be drawn connecting the points with coordinates *x2*,
48+
* *y2*; the **stroke** then applies to that line and defaults to *currentColor*.
49+
*/
50+
line?: boolean;
4551
}
4652

4753
/** Options for the areaX mark. */

src/marks/area.js

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {area as shapeArea} from "d3";
1+
import {area as shapeArea, line as shapeLine} from "d3";
22
import {create} from "../context.js";
33
import {maybeCurve} from "../curve.js";
44
import {Mark} from "../mark.js";
@@ -24,7 +24,7 @@ const defaults = {
2424

2525
export class Area extends Mark {
2626
constructor(data, options = {}) {
27-
const {x1, y1, x2, y2, z, curve, tension} = options;
27+
const {x1, y1, x2, y2, z, curve, tension, line} = options;
2828
super(
2929
data,
3030
{
@@ -35,10 +35,11 @@ export class Area extends Mark {
3535
z: {value: maybeZ(options), optional: true}
3636
},
3737
options,
38-
defaults
38+
line ? {...defaults, stroke: "currentColor"} : defaults
3939
);
4040
this.z = z;
4141
this.curve = maybeCurve(curve, tension);
42+
this.line = !!line;
4243
}
4344
filter(index) {
4445
return index;
@@ -48,11 +49,14 @@ export class Area extends Mark {
4849
return create("svg:g", context)
4950
.call(applyIndirectStyles, this, dimensions, context)
5051
.call(applyTransform, this, scales, 0, 0)
51-
.call((g) =>
52-
g
52+
.call((g) => {
53+
g = g
5354
.selectAll()
5455
.data(groupIndex(index, [X1, Y1, X2, Y2], this, channels))
55-
.enter()
56+
.enter();
57+
58+
if (this.line) g = g.append("g");
59+
const area = g
5660
.append("path")
5761
.call(applyDirectStyles, this)
5862
.call(applyGroupedChannelStyles, this, channels)
@@ -65,8 +69,23 @@ export class Area extends Mark {
6569
.y0((i) => Y1[i])
6670
.x1((i) => X2[i])
6771
.y1((i) => Y2[i])
68-
)
69-
)
72+
);
73+
if (this.line) {
74+
area.attr("stroke", "none");
75+
g.append("path")
76+
.call(applyDirectStyles, this)
77+
.call(applyGroupedChannelStyles, this, channels)
78+
.attr(
79+
"d",
80+
shapeLine()
81+
.curve(this.curve)
82+
.defined((i) => i >= 0)
83+
.x((i) => X2[i])
84+
.y((i) => Y2[i])
85+
)
86+
.attr("fill", "none");
87+
}
88+
})
7089
.node();
7190
}
7291
}

test/output/aaplCloseLine.svg

Lines changed: 80 additions & 0 deletions
Loading

test/plots/aapl-close.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ export async function aaplClose() {
1313
});
1414
}
1515

16+
export async function aaplCloseLine() {
17+
const aapl = (await d3.csv<any>("data/aapl.csv", d3.autoType)).slice(-120);
18+
return Plot.plot({
19+
y: {grid: true},
20+
marks: [Plot.areaY(aapl, {x: "Date", y: "Close", fillOpacity: 0.1, line: true, stroke: "red"}), Plot.ruleY([0])]
21+
});
22+
}
23+
1624
export async function aaplCloseClip() {
1725
const aapl = await d3.csv<any>("data/aapl.csv", d3.autoType);
1826
return Plot.plot({

0 commit comments

Comments
 (0)