Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

difference mark and shift transform #1896

Merged
merged 55 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
ecf9138
difference mark
mbostock Oct 17, 2023
64e3f62
fix filtering; opacity options
mbostock Oct 17, 2023
4ac221f
remove unused import
mbostock Oct 17, 2023
6b56c80
withTip; don’t duplicate channels
mbostock Oct 18, 2023
1880bff
difference as a composite mark
Fil Oct 19, 2023
a93cc56
difference tip
Fil Oct 19, 2023
c6c0be5
reuse channels
Fil Oct 23, 2023
a347333
more composite marks
mbostock Oct 23, 2023
b5ccc92
apply clip as render transform
mbostock Oct 23, 2023
43275f8
consolidate code
mbostock Oct 23, 2023
dea1cf3
aria labels
mbostock Oct 23, 2023
cc32a9b
organize imports
mbostock Oct 23, 2023
0326a33
fix differenceY1 test
mbostock Oct 23, 2023
b893638
update tests
mbostock Oct 23, 2023
376c079
better defaults
mbostock Oct 23, 2023
f1cd4ed
handle ChannelValueSpec
mbostock Oct 24, 2023
3544a98
update test
mbostock Oct 24, 2023
0775c7e
memoTuple
mbostock Oct 24, 2023
79ceefe
checkpoint docs
mbostock Oct 24, 2023
9e8a7ff
fix differenceY1 test
mbostock Oct 24, 2023
6952832
tip fixes
mbostock Oct 24, 2023
b18c780
**positiveOpacity**, **negativeOpacity** default to **fillOpacity**; …
Fil Oct 24, 2023
56cbe36
positiveFill
Fil Oct 24, 2023
6aa5488
another test
mbostock Oct 24, 2023
23dd51f
positiveFillOpacity & fix test
Fil Oct 25, 2023
fea7c77
swap [xy][12]; default y1 = 0
mbostock Oct 26, 2023
790b449
Merge branch 'main' into mbostock/difference
mbostock Oct 28, 2023
0b2eea1
shift option
mbostock Oct 28, 2023
6585322
another difference example
mbostock Oct 28, 2023
e170c8c
z
Fil Oct 30, 2023
24531b9
simpler marks (no need for two differences)
Fil Oct 30, 2023
db54a68
inferScaleOrder
mbostock Oct 30, 2023
a6a88da
Merge branch 'mbostock/difference' into fil/difference
Fil Oct 31, 2023
354d22f
simpler chart
Fil Oct 31, 2023
a6ecc1c
enhanced group extent; findX sketch
mbostock Nov 1, 2023
136ffc2
Merge branch 'main' into mbostock/difference
mbostock Nov 3, 2023
9fa447a
shift transform
mbostock Nov 4, 2023
52cc5b1
shift domain hint
mbostock Nov 4, 2023
d10ce7e
promote stroke to z
mbostock Nov 4, 2023
94a48ee
simpler channel domain hint
mbostock Nov 4, 2023
d4b0685
more difference docs
mbostock Nov 5, 2023
67b3e3b
more difference docs
mbostock Nov 5, 2023
54bf3fe
more documentation
mbostock Nov 5, 2023
b7a95ad
Merge branch 'mbostock/difference' into fil/difference
Fil Nov 6, 2023
6452734
call next twice (once for the path, once for the clipPath)
Fil Nov 6, 2023
30db89c
support clip: frame
Fil Nov 6, 2023
60b48f9
document differenceY
Fil Nov 6, 2023
0c58cf0
test ordinal difference
Fil Nov 6, 2023
f1cee3d
adopt Plot.find
Fil Nov 6, 2023
36dd5ea
Merge branch 'main' into mbostock/difference
mbostock Nov 7, 2023
c7afcb8
Merge branch 'fil/difference' into mbostock/difference
mbostock Nov 7, 2023
bb30385
more docs
mbostock Nov 7, 2023
00ab6f0
fix z documentation
mbostock Nov 7, 2023
2426fb1
fix space
mbostock Nov 7, 2023
5b22d28
Merge branch 'main' into mbostock/difference
mbostock Nov 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from "./marks/contour.js";
export * from "./marks/crosshair.js";
export * from "./marks/delaunay.js";
export * from "./marks/density.js";
export * from "./marks/difference.js";
export * from "./marks/dot.js";
export * from "./marks/frame.js";
export * from "./marks/geo.js";
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {Contour, contour} from "./marks/contour.js";
export {crosshair, crosshairX, crosshairY} from "./marks/crosshair.js";
export {delaunayLink, delaunayMesh, hull, voronoi, voronoiMesh} from "./marks/delaunay.js";
export {Density, density} from "./marks/density.js";
export {differenceY} from "./marks/difference.js";
export {Dot, dot, dotX, dotY, circle, hexagon} from "./marks/dot.js";
export {Frame, frame} from "./marks/frame.js";
export {Geo, geo, sphere, graticule} from "./marks/geo.js";
Expand Down
8 changes: 8 additions & 0 deletions src/marks/difference.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type {Data, RenderableMark} from "../mark.js";
import type {AreaYOptions} from "./area.js";

/** TODO */
export function differenceY(data?: Data, options?: AreaYOptions): Difference;

/** The difference mark. */
export class Difference extends RenderableMark {}
109 changes: 109 additions & 0 deletions src/marks/difference.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {area as shapeArea, line as shapeLine} from "d3";
import {create} from "../context.js";
import {maybeCurve} from "../curve.js";
import {Mark} from "../mark.js";
import {identity, indexOf, isColor} from "../options.js";
import {applyIndirectStyles, applyTransform, getClipId} from "../style.js";
import {maybeDenseIntervalX} from "../transforms/bin.js";

const defaults = {
ariaLabel: "difference",
fill: "none",
stroke: "currentColor",
strokeWidth: 1.5,
strokeLinecap: "round",
strokeLinejoin: "round",
strokeMiterlimit: 1
};

function maybeColor(value) {
if (value == null) return "none";
if (!isColor(value)) throw new Error(`invalid color: ${value}`);
return value;
}

class Difference extends Mark {
constructor(data, options = {}) {
const {x1, y1, x2, y2, curve, tension, positiveColor = "green", negativeColor = "blue"} = options;
mbostock marked this conversation as resolved.
Show resolved Hide resolved
super(
data,
{
x1: {value: x1, scale: "x"},
y1: {value: y1, scale: "y"},
x2: {value: x2, scale: "x", optional: true},
y2: {value: y2, scale: "y", optional: true}
},
options,
defaults
);
this.curve = maybeCurve(curve, tension);
this.positiveColor = maybeColor(positiveColor);
this.negativeColor = maybeColor(negativeColor);
}
filter(index) {
return index;
}
mbostock marked this conversation as resolved.
Show resolved Hide resolved
render(index, scales, channels, dimensions, context) {
const {x1: X1, y1: Y1, x2: X2 = X1, y2: Y2 = Y1} = channels;
const {negativeColor, positiveColor} = this;
const {height} = dimensions;
const clipPositive = getClipId();
const clipNegative = getClipId();
return create("svg:g", context)
.call(applyIndirectStyles, this, dimensions, context)
.call(applyTransform, this, scales, 0, 0)
.call((g) =>
g
.append("clipPath")
.attr("id", clipPositive)
.append("path")
.attr("d", renderArea(X1, Y1, height, this, index))
)
.call((g) =>
g
.append("clipPath")
.attr("id", clipNegative)
.append("path")
.attr("d", renderArea(X1, Y1, 0, this, index))
)
.call((g) =>
g
.append("path")
.attr("fill", positiveColor)
.attr("stroke", "none")
.attr("clip-path", `url(#${clipPositive})`)
.attr("d", renderArea(X2, Y2, 0, this, index))
)
.call((g) =>
g
.append("path")
.attr("fill", negativeColor)
.attr("stroke", "none")
.attr("clip-path", `url(#${clipNegative})`)
.attr("d", renderArea(X2, Y2, height, this, index))
)
.call((g) => g.append("path").attr("d", renderLine(X1, Y1, this, index)))
.node();
}
}

function renderArea(X, Y, y0, {curve}, index) {
return shapeArea()
.curve(curve)
.defined((i) => i >= 0)
mbostock marked this conversation as resolved.
Show resolved Hide resolved
.x((i) => X[i])
.y1((i) => Y[i])
.y0(y0)(index);
}

function renderLine(X, Y, {curve}, index) {
return shapeLine()
.curve(curve)
.defined((i) => i >= 0)
mbostock marked this conversation as resolved.
Show resolved Hide resolved
.x((i) => X[i])
.y((i) => Y[i])(index);
}

export function differenceY(data, {x = indexOf, x1 = x, x2 = x, y = identity, y1 = y, y2 = y, ...options} = {}) {
return new Difference(data, maybeDenseIntervalX({...options, x1, x2, y1, y2}));
}
2 changes: 1 addition & 1 deletion src/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const offset = (typeof window !== "undefined" ? window.devicePixelRatio >

let nextClipId = 0;

function getClipId() {
export function getClipId() {
return `plot-clip-${++nextClipId}`;
}

Expand Down
65 changes: 65 additions & 0 deletions test/output/differenceY.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions test/plots/difference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Plot from "@observablehq/plot";
import * as d3 from "d3";

export async function differenceY() {
const aapl = await d3.csv<any>("data/aapl.csv", d3.autoType);
const goog = await d3.csv<any>("data/goog.csv", d3.autoType);
const x = aapl.map((d) => d.Date);
const y1 = aapl.map((d, i, data) => d.Close / data[0].Close);
const y2 = goog.map((d, i, data) => d.Close / data[0].Close);
mbostock marked this conversation as resolved.
Show resolved Hide resolved
return Plot.plot({
marks: [Plot.differenceY(aapl, {x, y1, y2})]
});
}
1 change: 1 addition & 0 deletions test/plots/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export * from "./diamonds-boxplot.js";
export * from "./diamonds-carat-price-dots.js";
export * from "./diamonds-carat-price.js";
export * from "./diamonds-carat-sampling.js";
export * from "./difference.js";
export * from "./documentation-links.js";
export * from "./dodge-rule.js";
export * from "./dodge-text-radius.js";
Expand Down