diff --git a/src/transforms/bin.js b/src/transforms/bin.js
index 02321a3345..e051ac98a1 100644
--- a/src/transforms/bin.js
+++ b/src/transforms/bin.js
@@ -67,22 +67,24 @@ export function bin(outputs = {fill: "count"}, options = {}) {
return binn(x, y, null, null, outputs, maybeInsetX(maybeInsetY(options)));
}
-function maybeDenseInterval(bin, k, options = {}) {
- if (options?.interval == null) return options;
+function maybeDenseInterval(x, options) {
+ options = withTip(options, x);
+ if (options.interval == null) return options;
const {reduce = reduceFirst} = options;
- const outputs = {filter: null};
- if (options[k] != null) outputs[k] = reduce;
- if (options[`${k}1`] != null) outputs[`${k}1`] = reduce;
- if (options[`${k}2`] != null) outputs[`${k}2`] = reduce;
- return bin(outputs, options);
+ const outputs = {filter: null, [x]: `${x}1`};
+ const y = x === "x" ? "y" : "x";
+ if (options[y] != null) outputs[y] = reduce;
+ if (options[`${y}1`] != null) outputs[`${y}1`] = reduce;
+ if (options[`${y}2`] != null) outputs[`${y}2`] = reduce;
+ return (x === "x" ? binX : binY)(outputs, options);
}
export function maybeDenseIntervalX(options = {}) {
- return maybeDenseInterval(binX, "y", withTip(options, "x"));
+ return maybeDenseInterval("x", options);
}
export function maybeDenseIntervalY(options = {}) {
- return maybeDenseInterval(binY, "x", withTip(options, "y"));
+ return maybeDenseInterval("y", options);
}
function binn(
diff --git a/test/output/aaplInterval.svg b/test/output/aaplInterval.svg
index 8287a1e7fd..5e76b5099f 100644
--- a/test/output/aaplInterval.svg
+++ b/test/output/aaplInterval.svg
@@ -14,38 +14,38 @@
}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
- 56
- 58
- 60
- 62
- 64
- 66
- 68
- 70
- 72
+ 56
+ 58
+ 60
+ 62
+ 64
+ 66
+ 68
+ 70
+ 72
-
-
-
-
+
+
+
+
- Jun2013
- Jul
- Aug
- Sep
+ Jun2013
+ Jul
+ Aug
+ Sep
diff --git a/test/output/availability.svg b/test/output/availability.svg
index 2727bef159..28db7454f8 100644
--- a/test/output/availability.svg
+++ b/test/output/availability.svg
@@ -18,7 +18,7 @@
-
+
@@ -26,27 +26,27 @@
1
2
3
- 4
+ 4
5
↑ value
-
-
-
-
-
-
+
+
+
+
+
+
- Jan2020
- Apr
- Jul
- Oct
- Jan2021
- Apr
+ Jan2020
+ Apr
+ Jul
+ Oct
+ Jan2021
+ Apr
diff --git a/test/output/downloads.svg b/test/output/downloads.svg
index 4b0fb6d555..ab1a534643 100644
--- a/test/output/downloads.svg
+++ b/test/output/downloads.svg
@@ -15,50 +15,50 @@
-
+
-
+
-
+
-
+
-
+
0
- 2
+ 2
4
6
- 8
+ 8
10
12
- 14
+ 14
16
18
- 20
+ 20
22
- 24
+ 24
↑ downloads
-
-
-
-
-
+
+
+
+
+
- 2018
- 2019
- 2020
- 2021
- 2022
+ 2018
+ 2019
+ 2020
+ 2021
+ 2022
diff --git a/test/output/integerIntervalArea.html b/test/output/integerIntervalArea.html
index d7073c9069..382bcee5c1 100644
--- a/test/output/integerIntervalArea.html
+++ b/test/output/integerIntervalArea.html
@@ -70,28 +70,30 @@
↑ y
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
x →
diff --git a/test/output/lineInterval.svg b/test/output/lineInterval.svg
new file mode 100644
index 0000000000..c08693f3bc
--- /dev/null
+++ b/test/output/lineInterval.svg
@@ -0,0 +1,127 @@
+
\ No newline at end of file
diff --git a/test/plots/index.ts b/test/plots/index.ts
index 3c019e678f..3bcc3426d4 100644
--- a/test/plots/index.ts
+++ b/test/plots/index.ts
@@ -157,6 +157,7 @@ export * from "./likert-survey.js";
export * from "./linear-regression-cars.js";
export * from "./linear-regression-mtcars.js";
export * from "./linear-regression-penguins.js";
+export * from "./line-interval.js";
export * from "./log-degenerate.js";
export * from "./log-tick-format.js";
export * from "./long-labels.js";
diff --git a/test/plots/line-interval.ts b/test/plots/line-interval.ts
new file mode 100644
index 0000000000..628ac15d0d
--- /dev/null
+++ b/test/plots/line-interval.ts
@@ -0,0 +1,29 @@
+import * as Plot from "@observablehq/plot";
+import * as d3 from "d3";
+
+export async function lineInterval() {
+ const random = d3.randomLcg(42);
+ const logins = ["Alice", "Bob", "Carol", "Dave", "Eve"];
+ const dates = d3.utcDays(new Date("2024-01-01"), new Date("2024-01-15"));
+ const activity = dates.flatMap((date) => logins.filter(() => random() > 0.15).map((login) => ({date, login})));
+ return Plot.plot({
+ height: 200,
+ marks: [
+ Plot.ruleY([0]),
+ Plot.areaY(activity, {
+ x: "date",
+ interval: "day",
+ fill: "login",
+ order: "-sum",
+ fillOpacity: 0.3,
+ y: 1,
+ curve: "catmull-rom"
+ }),
+ Plot.lineY(
+ activity,
+ Plot.stackY({x: "date", interval: "day", stroke: "login", order: "-sum", curve: "catmull-rom"})
+ ),
+ Plot.dot(activity, Plot.stackY({x: "date", interval: "day", z: "login", fill: "login", order: "-sum"}))
+ ]
+ });
+}