Skip to content

Commit 9d024cc

Browse files
committed
restore line option; fillOpacity 0.3
1 parent ac22d70 commit 9d024cc

10 files changed

Lines changed: 87 additions & 158 deletions

File tree

docs/data/api.data.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ function getHref(name: string, path: string): string {
5050
return `${path}s`;
5151
case "features/options":
5252
return "features/transforms";
53-
case "marks/area-line":
54-
return "marks/area";
5553
case "marks/axis": {
5654
switch (name) {
5755
case "gridX":

docs/marks/area.md

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Plot.areaY(aapl, {x: "Date", y: "Close"}).plot()
2020

2121
The area mark has three constructors: [areaY](#areaY) for when the baseline and topline share *x* values, as in a time-series area chart where time goes right→ (or ←left); [areaX](#areaX) for when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑ (or down↓); and lastly the rarely-used [area](#area) where the baseline and topline share neither *x* nor *y* values.
2222

23-
The **area line mark** <VersionBadge pr="2407" /> is a variant of the area mark that accentuates the topline. It is often paired with a [rule](./rule.md) mark for the baseline.
23+
The **line** option <VersionBadge pr="2407" /> strokes the topline. It is often paired with a [rule](./rule.md) mark to denote the baseline.
2424

2525
:::plot https://observablehq.com/@observablehq/plot-area-and-line
2626
```js
@@ -29,7 +29,7 @@ Plot.plot({
2929
grid: true
3030
},
3131
marks: [
32-
Plot.areaLineY(aapl, {x: "Date", y: "Close"}),
32+
Plot.areaY(aapl, {x: "Date", y: "Close", line: true}),
3333
Plot.ruleY([0])
3434
]
3535
})
@@ -93,7 +93,7 @@ Plot.plot({
9393
reverse: true
9494
},
9595
marks: [
96-
Plot.areaLineY(aapl, {x: "Date", y: "Close"}),
96+
Plot.areaY(aapl, {x: "Date", y: "Close", line: true}),
9797
Plot.ruleY([0])
9898
]
9999
})
@@ -109,7 +109,7 @@ Plot.plot({
109109
grid: true
110110
},
111111
marks: [
112-
Plot.areaLineX(aapl, {y: "Date", x: "Close"}),
112+
Plot.areaX(aapl, {y: "Date", x: "Close", line: true}),
113113
Plot.ruleX([0])
114114
]
115115
})
@@ -125,7 +125,7 @@ Plot.plot({
125125
grid: true
126126
},
127127
marks: [
128-
Plot.areaLineY(aapl, {x: "Date", y: (d) => d.Date.getUTCMonth() < 3 ? NaN : d.Close}),
128+
Plot.areaY(aapl, {x: "Date", y: (d) => d.Date.getUTCMonth() < 3 ? NaN : d.Close, line: true}),
129129
Plot.ruleY([0])
130130
]
131131
})
@@ -304,7 +304,7 @@ Plot.areaY(observations, {x: "date", y: "temperature", interval: "day"})
304304

305305
The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use "day" as the interval.
306306

307-
The **areaY** mark draws the region between a baseline (*y1*) and a topline (*y2*) as in an area chart. When the baseline is *y* = 0, the *y* channel can be specified instead of *y1* and *y2*.
307+
The **areaY** mark draws the region between a vertically-separated baseline (*y1*) and topline (*y2*) as in an area chart. When the baseline is *y* = 0, the *y* channel can be specified instead of *y1* and *y2*. If the **line** option <VersionBadge pr="2407" /> is true, the **stroke** applies exclusively to the topline, and the **fillOpacity** defaults to 0.3.
308308

309309
## areaX(*data*, *options*) {#areaX}
310310

@@ -322,21 +322,7 @@ Plot.areaX(observations, {y: "date", x: "temperature", interval: "day"})
322322

323323
The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use "day" as the interval.
324324

325-
## areaLineY(*data*, *options*) {#areaLineY}
326-
327-
```js
328-
Plot.areaLineY(aapl, {x: "Date", y: "Close"})
329-
```
330-
331-
A variant of [areaY](#areaY) that accentuates the topline.
332-
333-
## areaLineX(*data*, *options*) {#areaLineX}
334-
335-
```js
336-
Plot.areaLineX(aapl, {y: "Date", x: "Close"})
337-
```
338-
339-
A variant of [areaX](#areaX) that accentuates the topline.
325+
The **areaX** mark draws the region between a horizontally-separated baseline (*x1*) and topline (*x2*) as in a vertical area chart. When the baseline is *x* = 0, the *x* channel can be specified instead of *x1* and *x2*. If the **line** option <VersionBadge pr="2407" /> is true, the **stroke** applies exclusively to the topline, and the **fillOpacity** defaults to 0.3.
340326

341327
## area(*data*, *options*) {#area}
342328

src/index.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export * from "./legends.js";
1010
export * from "./mark.js";
1111
export * from "./marker.js";
1212
export * from "./marks/area.js";
13-
export * from "./marks/areaLine.js";
1413
export * from "./marks/arrow.js";
1514
export * from "./marks/auto.js";
1615
export * from "./marks/axis.js";

src/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Mark.prototype.plot = function ({marks = [], ...options} = {}) {
99
export {plot} from "./plot.js";
1010
export {Mark, marks} from "./mark.js";
1111
export {Area, area, areaX, areaY} from "./marks/area.js";
12-
export {AreaLine, areaLineX, areaLineY} from "./marks/areaLine.js";
1312
export {Arrow, arrow} from "./marks/arrow.js";
1413
export {auto, autoSpec} from "./marks/auto.js";
1514
export {axisX, axisY, axisFx, axisFy, gridX, gridY, gridFx, gridFy} from "./marks/axis.js";

src/marks/area.d.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type {ChannelValue, ChannelValueDenseBinSpec, ChannelValueSpec} from "../channel.js";
22
import type {CurveOptions} from "../curve.js";
3-
import type {Data, MarkOptions, RenderableMark} from "../mark.js";
3+
import type {ColorOptions, Data, MarkOptions, RenderableMark} from "../mark.js";
4+
import {MarkerOptions} from "../marker.js";
45
import type {BinOptions, BinReducer} from "../transforms/bin.js";
56
import type {StackOptions} from "../transforms/stack.js";
67

78
/** Options for the area, areaX, and areaY marks. */
8-
export interface AreaOptions extends MarkOptions, StackOptions, CurveOptions {
9+
export interface AreaOptions extends MarkOptions, StackOptions, ColorOptions, CurveOptions {
910
/**
1011
* The required primary (starting, often left) horizontal position channel,
1112
* representing the area’s baseline, typically bound to the *x* scale. For
@@ -124,6 +125,9 @@ export interface AreaYOptions extends Omit<AreaOptions, "x1" | "x2">, BinOptions
124125
reduce?: BinReducer;
125126
}
126127

128+
/** The area mark’s line option. */
129+
export type AreaLineOptions = {line?: false} | ({line: true} & MarkerOptions);
130+
127131
/**
128132
* Returns a new area mark with the given *data* and *options*. The area mark is
129133
* rarely used directly; it is only needed when the baseline and topline have
@@ -163,7 +167,7 @@ export function area(data?: Data, options?: AreaOptions): Area;
163167
* channels. When any of these channels are used, setting an explicit **z**
164168
* channel (possibly to null) is strongly recommended.
165169
*/
166-
export function areaX(data?: Data, options?: AreaXOptions): Area;
170+
export function areaX(data?: Data, options?: AreaXOptions & AreaLineOptions): Area;
167171

168172
/**
169173
* Returns a new horizontally-oriented area mark for the given *data* and
@@ -195,7 +199,7 @@ export function areaX(data?: Data, options?: AreaXOptions): Area;
195199
* channels. When any of these channels are used, setting an explicit **z**
196200
* channel (possibly to null) is strongly recommended.
197201
*/
198-
export function areaY(data?: Data, options?: AreaYOptions): Area;
202+
export function areaY(data?: Data, options?: AreaYOptions & AreaLineOptions): Area;
199203

200204
/** The area mark. */
201205
export class Area extends RenderableMark {}

src/marks/area.js

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
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";
5+
import {applyGroupedMarkers, markers} from "../marker.js";
56
import {first, maybeZ, second} from "../options.js";
67
import {applyDirectStyles, applyIndirectStyles, applyTransform, applyGroupedChannelStyles} from "../style.js";
7-
import {groupIndex} from "../style.js";
8+
import {groupIndex, offset} from "../style.js";
89
import {maybeDenseIntervalX, maybeDenseIntervalY} from "../transforms/bin.js";
910
import {maybeStackX, maybeStackY} from "../transforms/stack.js";
1011

@@ -16,6 +17,16 @@ const areaDefaults = {
1617
strokeMiterlimit: 1
1718
};
1819

20+
const areaLineDefaults = {
21+
ariaLabel: "area-line",
22+
fillOpacity: 0.3,
23+
stroke: "currentColor",
24+
strokeWidth: 1.5,
25+
strokeLinecap: "round",
26+
strokeLinejoin: "round",
27+
strokeMiterlimit: 1
28+
};
29+
1930
export class Area extends Mark {
2031
constructor(data, options = {}, defaults = areaDefaults) {
2132
const {x1, y1, x2, y2, z, curve, tension} = options;
@@ -65,17 +76,70 @@ export class Area extends Mark {
6576
}
6677
}
6778

79+
class AreaLine extends Area {
80+
constructor(data, options = {}) {
81+
super(data, options, areaLineDefaults);
82+
markers(this, options);
83+
}
84+
render(index, scales, channels, dimensions, context) {
85+
const {x1: X1, y1: Y1, x2: X2 = X1, y2: Y2 = Y1} = channels;
86+
return create("svg:g", context)
87+
.call(applyIndirectStyles, this, dimensions, context)
88+
.call(applyTransform, this, scales, 0, 0)
89+
.call((g) =>
90+
g
91+
.selectAll()
92+
.data(groupIndex(index, [X1, Y1, X2, Y2], this, channels))
93+
.enter()
94+
.append("g")
95+
.call(applyDirectStyles, this)
96+
.call(applyGroupedChannelStyles, this, channels)
97+
.call((e) =>
98+
e
99+
.append("path")
100+
.attr("stroke", "none")
101+
.attr(
102+
"d",
103+
shapeArea()
104+
.curve(this.curve)
105+
.defined((i) => i >= 0)
106+
.x0((i) => X1[i])
107+
.y0((i) => Y1[i])
108+
.x1((i) => X2[i])
109+
.y1((i) => Y2[i])
110+
)
111+
)
112+
.call((e) =>
113+
e
114+
.append("path")
115+
.call(applyGroupedMarkers, this, channels, context)
116+
.attr("fill", "none")
117+
.attr("transform", offset ? `translate(${offset},${offset})` : null)
118+
.attr(
119+
"d",
120+
shapeLine()
121+
.curve(this.curve)
122+
.defined((i) => i >= 0)
123+
.x((i) => X2[i])
124+
.y((i) => Y2[i])
125+
)
126+
)
127+
)
128+
.node();
129+
}
130+
}
131+
68132
export function area(data, options) {
69133
if (options === undefined) return areaY(data, {x: first, y: second});
70134
return new Area(data, options);
71135
}
72136

73137
export function areaX(data, options) {
74-
const {x, y, fill, z = x === fill ? null : undefined, ...rest} = maybeDenseIntervalY(options);
75-
return new Area(data, maybeStackX({...rest, x, y1: y, y2: undefined, z, fill}));
138+
const {x, y, line, color, stroke = color, fill = color, z = x === fill || (line && x === stroke) ? null : undefined, ...rest} = maybeDenseIntervalY(options); // prettier-ignore
139+
return new (line ? AreaLine : Area)(data, maybeStackX({...rest, x, y1: y, y2: undefined, z, stroke, fill}));
76140
}
77141

78142
export function areaY(data, options) {
79-
const {x, y, fill, z = y === fill ? null : undefined, ...rest} = maybeDenseIntervalX(options);
80-
return new Area(data, maybeStackY({...rest, x1: x, x2: undefined, y, z, fill}));
143+
const {x, y, line, color, stroke = color, fill = color, z = y === fill || (line && y === stroke) ? null : undefined, ...rest} = maybeDenseIntervalX(options); // prettier-ignore
144+
return new (line ? AreaLine : Area)(data, maybeStackY({...rest, x1: x, x2: undefined, y, z, stroke, fill}));
81145
}

src/marks/areaLine.d.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/marks/areaLine.js

Lines changed: 0 additions & 81 deletions
This file was deleted.

test/output/aaplClose.svg

Lines changed: 1 addition & 1 deletion
Loading

test/plots/aapl-close.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ test(async function aaplClose() {
66
const aapl = await d3.csv<any>("data/aapl.csv", d3.autoType);
77
return Plot.plot({
88
y: {grid: true},
9-
marks: [Plot.areaLineY(aapl, {x: "Date", y: "Close"}), Plot.ruleY([0])]
9+
marks: [Plot.areaY(aapl, {x: "Date", y: "Close", line: true}), Plot.ruleY([0])]
1010
});
1111
});
1212

0 commit comments

Comments
 (0)