From b722ed2adbf102b575ed965f86ef3d9f21f2f440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Thu, 31 Dec 2020 17:35:08 +0100 Subject: [PATCH 1/6] arc mark and pie transform ref. #80 --- src/index.js | 1 + src/marks/arc.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ test/arc-test.js | 13 +++++++ 3 files changed, 111 insertions(+) create mode 100644 src/marks/arc.js create mode 100644 test/arc-test.js diff --git a/src/index.js b/src/index.js index 963d508869..ceb7d8bb9f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ export {plot} from "./plot.js"; export {Mark} from "./mark.js"; +export {Arc, arc} from "./marks/arc.js"; export {Area, area, areaX, areaY} from "./marks/area.js"; export {AxisX, AxisY} from "./marks/axis.js"; export {BarX, BarY, barX, barY} from "./marks/bar.js"; diff --git a/src/marks/arc.js b/src/marks/arc.js new file mode 100644 index 0000000000..6994d0854e --- /dev/null +++ b/src/marks/arc.js @@ -0,0 +1,97 @@ +import {ascending} from "d3-array"; +import {create} from "d3-selection"; +import {arc as shapeArc} from "d3-shape"; +import {filter} from "../defined.js"; +import {Mark, number, maybeColor, maybeNumber, title} from "../mark.js"; +import {Style, applyDirectStyles, applyIndirectStyles, applyTransform} from "../style.js"; + +const constant = x => () => x; + +export class Arc extends Mark { + constructor( + data, + { + startAngle = d => d.startAngle, + endAngle = d => d.endAngle, + x = constant(0), + y = constant(0), + innerRadius = 0, + outerRadius = 140, + z, + title, + fill, + stroke, + insetTop = 0, + insetRight = 0, + insetBottom = 0, + insetLeft = 0, + transform, + ...style + } = {} + ) { + const [vfill, cfill] = maybeColor(fill); + const [vstroke, cstroke] = maybeColor(stroke); + const [vx, cx = vx == null ? 0 : undefined] = maybeNumber(x); + const [vy, cy = vy == null ? 0 : undefined] = maybeNumber(y); + const [vri, cri = vri == null ? 0 : undefined] = maybeNumber(innerRadius); + const [vro, cro = vro == null ? 0 : undefined] = maybeNumber(outerRadius); + + super( + data, + [ + {name: "x", value: vx, scale: "x"}, + {name: "y", value: vy, scale: "y"}, + {name: "startAngle", value: startAngle}, + {name: "endAngle", value: endAngle}, + {name: "innerRadius", value: innerRadius, optional: true}, + {name: "outerRadius", value: outerRadius, optional: true}, + {name: "z", value: z, optional: true}, + {name: "title", value: title, optional: true}, + {name: "fill", value: vfill, scale: "color", optional: true}, + {name: "stroke", value: vstroke, scale: "color", optional: true} + ], + transform + ); + Style(this, {fill: cfill, stroke: cstroke, ...style}); + this.x = cx; + this.y = cy; + this.ri = cri; + this.ro = cro; + this.insetTop = number(insetTop); + this.insetRight = number(insetRight); + this.insetBottom = number(insetBottom); + this.insetLeft = number(insetLeft); + } + render( + I, + {x, y, color}, + {startAngle: A, endAngle: B, innerRadius: RI, outerRadius: RO, x: X, y: Y, z: Z, title: L, fill: F, stroke: S} + ) { + const index = filter(I, A, B, F, S); + if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); + + const arc = shapeArc() + .startAngle(i => A[i]) + .endAngle(i => B[i]) + .innerRadius(i => RI[i] || this.ri) + .outerRadius(i => RO[i] || this.ro); + + return create("svg:g") + .call(applyIndirectStyles, this) + .call(applyTransform, x, y) + .call(g => g.selectAll() + .data(index) + .join("path") + .call(applyDirectStyles, this) + .attr("d", arc) + .attr("transform", i => `translate(${x(X[i])},${y(Y[i])})`) + .attr("fill", F && (i => color(F[i]))) + .attr("stroke", S && (i => color(S[i]))) + .call(title(L))) + .node(); + } +} + +export function arc(data, options) { + return new Arc(data, options); +} diff --git a/test/arc-test.js b/test/arc-test.js new file mode 100644 index 0000000000..8008af3189 --- /dev/null +++ b/test/arc-test.js @@ -0,0 +1,13 @@ +const Plot = require("../"); +//const d3 = require("d3"); +//const tape = require("tape-await"); +const {JSDOM} = require("jsdom"); +global.document = new JSDOM("").window.document; + +const array = [[0,0], [1,1], [2,-1], [3,2]]; +const xy = array.map(([x,y]) => ({x,y})); + +const A = Plot.plot({marks: [Plot.arc(xy, {startAngle: "x", endAngle: "y" })]}).outerHTML; + +console.warn(A); + From ddf387cd751cabf2e4b52fb92cee68891b45354f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 1 Jan 2021 17:29:37 +0100 Subject: [PATCH 2/6] if functions, arc innerRadius and outerRadius are interpreted as percentages of the available radius arc tests --- src/marks/arc.js | 59 ++++++++-------- test/arc-test.js | 13 ---- test/marks/arc-test.js | 151 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 43 deletions(-) delete mode 100644 test/arc-test.js create mode 100644 test/marks/arc-test.js diff --git a/src/marks/arc.js b/src/marks/arc.js index 6994d0854e..6e586e34b4 100644 --- a/src/marks/arc.js +++ b/src/marks/arc.js @@ -2,7 +2,7 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; import {arc as shapeArc} from "d3-shape"; import {filter} from "../defined.js"; -import {Mark, number, maybeColor, maybeNumber, title} from "../mark.js"; +import {Mark, maybeColor, maybeNumber, title} from "../mark.js"; import {Style, applyDirectStyles, applyIndirectStyles, applyTransform} from "../style.js"; const constant = x => () => x; @@ -15,36 +15,34 @@ export class Arc extends Mark { endAngle = d => d.endAngle, x = constant(0), y = constant(0), - innerRadius = 0, - outerRadius = 140, + innerRadius, + outerRadius, z, title, fill, stroke, - insetTop = 0, - insetRight = 0, - insetBottom = 0, - insetLeft = 0, transform, ...style } = {} ) { - const [vfill, cfill] = maybeColor(fill); - const [vstroke, cstroke] = maybeColor(stroke); - const [vx, cx = vx == null ? 0 : undefined] = maybeNumber(x); - const [vy, cy = vy == null ? 0 : undefined] = maybeNumber(y); - const [vri, cri = vri == null ? 0 : undefined] = maybeNumber(innerRadius); - const [vro, cro = vro == null ? 0 : undefined] = maybeNumber(outerRadius); + const [vfill, cfill] = maybeColor(fill, "currentColor"); + const [vstroke, cstroke] = maybeColor(stroke, cfill === "none" ? "currentColor" : cfill === "currentColor" ? "white" : "none"); + const [vsa, csa] = maybeNumber(startAngle, 0); + const [vea, cea] = maybeNumber(endAngle, 2 * Math.PI); + const [vx, cx] = maybeNumber(x, 0); + const [vy, cy] = maybeNumber(y, 0); + const [vri, cri] = maybeNumber(innerRadius, 0); + const [vro, cro] = maybeNumber(outerRadius, 100); super( data, [ - {name: "x", value: vx, scale: "x"}, - {name: "y", value: vy, scale: "y"}, - {name: "startAngle", value: startAngle}, - {name: "endAngle", value: endAngle}, - {name: "innerRadius", value: innerRadius, optional: true}, - {name: "outerRadius", value: outerRadius, optional: true}, + {name: "x", value: vx, scale: "x", optional: true}, + {name: "y", value: vy, scale: "y", optional: true}, + {name: "startAngle", value: vsa, optional: true}, + {name: "endAngle", value: vea, optional: true}, + {name: "innerRadius", value: vri, optional: true}, + {name: "outerRadius", value: vro, optional: true}, {name: "z", value: z, optional: true}, {name: "title", value: title, optional: true}, {name: "fill", value: vfill, scale: "color", optional: true}, @@ -55,27 +53,28 @@ export class Arc extends Mark { Style(this, {fill: cfill, stroke: cstroke, ...style}); this.x = cx; this.y = cy; + this.sa = csa; + this.ea = cea; this.ri = cri; this.ro = cro; - this.insetTop = number(insetTop); - this.insetRight = number(insetRight); - this.insetBottom = number(insetBottom); - this.insetLeft = number(insetLeft); } render( I, {x, y, color}, - {startAngle: A, endAngle: B, innerRadius: RI, outerRadius: RO, x: X, y: Y, z: Z, title: L, fill: F, stroke: S} + {startAngle: SA, endAngle: EA, innerRadius: RI, outerRadius: RO, x: X, y: Y, z: Z, title: L, fill: F, stroke: S}, + {marginTop, marginRight, marginBottom, marginLeft, width, height} ) { - const index = filter(I, A, B, F, S); + const index = filter(I, SA, EA, F, S); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); + const r0 = Math.min(width - marginLeft - marginRight, height - marginTop - marginBottom) / 200; + const arc = shapeArc() - .startAngle(i => A[i]) - .endAngle(i => B[i]) - .innerRadius(i => RI[i] || this.ri) - .outerRadius(i => RO[i] || this.ro); - + .startAngle(SA ? (i => SA[i]) : this.sa) + .endAngle(EA ? (i => EA[i]) : this.ea) + .innerRadius(RI ? (i => r0 * RI[i]) : r0 * this.ri) + .outerRadius(RO ? (i => r0 * RO[i]) : r0 * this.ro); + return create("svg:g") .call(applyIndirectStyles, this) .call(applyTransform, x, y) diff --git a/test/arc-test.js b/test/arc-test.js deleted file mode 100644 index 8008af3189..0000000000 --- a/test/arc-test.js +++ /dev/null @@ -1,13 +0,0 @@ -const Plot = require("../"); -//const d3 = require("d3"); -//const tape = require("tape-await"); -const {JSDOM} = require("jsdom"); -global.document = new JSDOM("").window.document; - -const array = [[0,0], [1,1], [2,-1], [3,2]]; -const xy = array.map(([x,y]) => ({x,y})); - -const A = Plot.plot({marks: [Plot.arc(xy, {startAngle: "x", endAngle: "y" })]}).outerHTML; - -console.warn(A); - diff --git a/test/marks/arc-test.js b/test/marks/arc-test.js new file mode 100644 index 0000000000..0f9c9bd764 --- /dev/null +++ b/test/marks/arc-test.js @@ -0,0 +1,151 @@ +import * as Plot from "@observablehq/plot"; +import tape from "tape-await"; + +tape("arc(data) has the expected defaults", test => { + const arc = Plot.arc(undefined); + test.strictEqual(arc.data, undefined); + test.strictEqual(arc.transform("foo"), "foo"); + test.deepEqual(arc.channels.map(c => c.name), ["x", "y", "startAngle", "endAngle"]); + test.deepEqual(arc.channels.map(c => c.scale), ["x", "y", undefined, undefined]); + test.strictEqual(arc.fill, undefined); + test.strictEqual(arc.fillOpacity, undefined); + test.strictEqual(arc.stroke, "white"); + test.strictEqual(arc.strokeWidth, undefined); + test.strictEqual(arc.strokeOpacity, undefined); + test.strictEqual(arc.strokeLinejoin, undefined); + test.strictEqual(arc.strokeLinecap, undefined); + test.strictEqual(arc.strokeMiterlimit, undefined); + test.strictEqual(arc.strokeDasharray, undefined); + test.strictEqual(arc.mixBlendMode, undefined); +}); + +tape("arc(data) specifies an optional x channel", test => { + const arc = Plot.arc(undefined); + const x = arc.channels.find(c => c.name === "x"); + test.strictEqual(x.value({}), 0); + test.strictEqual(x.scale, "x"); +}); + +tape("arc(data) specifies an optional y channel", test => { + const arc = Plot.arc(undefined); + const y = arc.channels.find(c => c.name === "y"); + test.strictEqual(y.value({}), 0); + test.strictEqual(y.scale, "y"); +}); + +tape("arc(data) specifies an optional startAngle channel", test => { + const arc = Plot.arc(undefined); + const startAngle = arc.channels.find(c => c.name === "startAngle"); + test.strictEqual(startAngle.value({startAngle: 12}), 12); + test.strictEqual(startAngle.scale, undefined); +}); + +tape("arc(data) specifies an optional endAngle channel", test => { + const arc = Plot.arc(undefined); + const endAngle = arc.channels.find(c => c.name === "endAngle"); + test.strictEqual(endAngle.value({endAngle: 42}), 42); + test.strictEqual(endAngle.scale, undefined); +}); + +tape("arc(data, {z}) specifies an optional z channel", test => { + const arc = Plot.arc(undefined, {z: "x"}); + const z = arc.channels.find(c => c.name === "z"); + test.strictEqual(z.value.label, "x"); + test.strictEqual(z.scale, undefined); +}); + +tape("arc(data, {startAngle}) allows startAngle to be a constant", test => { + const arc = Plot.arc(undefined, {startAngle: 42}); + test.strictEqual(arc.sa, 42); +}); + +tape("arc(data, {startAngle}) allows startAngle to be a variable", test => { + const arc = Plot.arc(undefined, {startAngle: "x"}); + test.strictEqual(arc.sa, undefined); + const r = arc.channels.find(c => c.name === "startAngle"); + test.strictEqual(r.value.label, "x"); + test.strictEqual(r.scale, undefined); +}); + +tape("arc(data, {endAngle}) allows endAngle to be a constant", test => { + const arc = Plot.arc(undefined, {endAngle: 42}); + test.strictEqual(arc.ea, 42); +}); + +tape("arc(data, {endAngle}) allows endAngle to be a variable", test => { + const arc = Plot.arc(undefined, {endAngle: "x"}); + test.strictEqual(arc.ea, undefined); + const r = arc.channels.find(c => c.name === "endAngle"); + test.strictEqual(r.value.label, "x"); + test.strictEqual(r.scale, undefined); +}); + +tape("arc(data, {innerRadius}) allows innerRadius to be a constant", test => { + const arc = Plot.arc(undefined, {innerRadius: 42}); + test.strictEqual(arc.ri, 42); +}); + +tape("arc(data, {innerRadius}) allows innerRadius to be a variable", test => { + const arc = Plot.arc(undefined, {innerRadius: "x"}); + test.strictEqual(arc.ri, undefined); + const r = arc.channels.find(c => c.name === "innerRadius"); + test.strictEqual(r.value.label, "x"); + test.strictEqual(r.scale, undefined); +}); + +tape("arc(data, {outerRadius}) allows outerRadius to be a constant", test => { + const arc = Plot.arc(undefined, {outerRadius: 42}); + test.strictEqual(arc.ro, 42); +}); + +tape("arc(data, {outerRadius}) allows outerRadius to be a variable", test => { + const arc = Plot.arc(undefined, {outerRadius: "x"}); + test.strictEqual(arc.ro, undefined); + const r = arc.channels.find(c => c.name === "outerRadius"); + test.strictEqual(r.value.label, "x"); + test.strictEqual(r.scale, undefined); +}); + +tape("arc(data, {title}) specifies an optional title channel", test => { + const arc = Plot.arc(undefined, {x1: "0", y1: "1", title: "2"}); + const title = arc.channels.find(c => c.name === "title"); + test.strictEqual(title.value.label, "2"); + test.strictEqual(title.scale, undefined); +}); + +tape("arc(data, {fill}) allows fill to be a constant color", test => { + const arc = Plot.arc(undefined, {x1: "0", y1: "1", fill: "red"}); + test.strictEqual(arc.fill, "red"); +}); + +tape("arc(data, {fill}) allows fill to be null", test => { + const arc = Plot.arc(undefined, {x1: "0", y1: "1", fill: null}); + test.strictEqual(arc.fill, "none"); +}); + +tape("arc(data, {fill}) allows fill to be a variable color", test => { + const arc = Plot.arc(undefined, {x1: "0", y1: "1", fill: "x"}); + test.strictEqual(arc.fill, undefined); + const fill = arc.channels.find(c => c.name === "fill"); + test.strictEqual(fill.value.label, "x"); + test.strictEqual(fill.scale, "color"); +}); + +tape("arc(data, {stroke}) allows stroke to be a constant color", test => { + const arc = Plot.arc(undefined, {x1: "0", y1: "1", stroke: "red"}); + test.strictEqual(arc.stroke, "red"); +}); + +tape("arc(data, {stroke}) allows stroke to be null", test => { + const arc = Plot.arc(undefined, {x1: "0", y1: "1", stroke: null}); + test.strictEqual(arc.stroke, undefined); +}); + +tape("arc(data, {stroke}) allows stroke to be a variable color", test => { + const arc = Plot.arc(undefined, {x1: "0", y1: "1", stroke: "x"}); + test.strictEqual(arc.stroke, undefined); + const stroke = arc.channels.find(c => c.name === "stroke"); + test.strictEqual(stroke.value.label, "x"); + test.strictEqual(stroke.scale, "color"); +}); + From caa37237de0bf0de7c8ca2281e250232669602a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 1 Jan 2021 18:11:54 +0100 Subject: [PATCH 3/6] Plot.pie --- src/index.js | 1 + src/marks/arc.js | 9 +++++++-- src/marks/pie.js | 29 +++++++++++++++++++++++++++++ src/transforms/pie.js | 3 +++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/marks/pie.js create mode 100644 src/transforms/pie.js diff --git a/src/index.js b/src/index.js index ceb7d8bb9f..5aa81ba780 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,7 @@ export {Dot, dot, dotX, dotY} from "./marks/dot.js"; export {group, groupX, groupY} from "./marks/group.js"; export {Line, line, lineX, lineY} from "./marks/line.js"; export {Link, link} from "./marks/link.js"; +export {pie} from "./marks/pie.js"; export {Rect, rect, rectX, rectY} from "./marks/rect.js"; export {RuleX, RuleY, ruleX, ruleY} from "./marks/rule.js"; export {Text, text, textX, textY} from "./marks/text.js"; diff --git a/src/marks/arc.js b/src/marks/arc.js index 6e586e34b4..005629155d 100644 --- a/src/marks/arc.js +++ b/src/marks/arc.js @@ -17,6 +17,7 @@ export class Arc extends Mark { y = constant(0), innerRadius, outerRadius, + padAngle, z, title, fill, @@ -29,6 +30,7 @@ export class Arc extends Mark { const [vstroke, cstroke] = maybeColor(stroke, cfill === "none" ? "currentColor" : cfill === "currentColor" ? "white" : "none"); const [vsa, csa] = maybeNumber(startAngle, 0); const [vea, cea] = maybeNumber(endAngle, 2 * Math.PI); + const [vpa, cpa] = maybeNumber(padAngle, 0); const [vx, cx] = maybeNumber(x, 0); const [vy, cy] = maybeNumber(y, 0); const [vri, cri] = maybeNumber(innerRadius, 0); @@ -43,6 +45,7 @@ export class Arc extends Mark { {name: "endAngle", value: vea, optional: true}, {name: "innerRadius", value: vri, optional: true}, {name: "outerRadius", value: vro, optional: true}, + {name: "padAngle", value: vpa, optional: true}, {name: "z", value: z, optional: true}, {name: "title", value: title, optional: true}, {name: "fill", value: vfill, scale: "color", optional: true}, @@ -57,11 +60,12 @@ export class Arc extends Mark { this.ea = cea; this.ri = cri; this.ro = cro; + this.pa = cpa; } render( I, {x, y, color}, - {startAngle: SA, endAngle: EA, innerRadius: RI, outerRadius: RO, x: X, y: Y, z: Z, title: L, fill: F, stroke: S}, + {startAngle: SA, endAngle: EA, innerRadius: RI, outerRadius: RO, padAngle: PA, x: X, y: Y, z: Z, title: L, fill: F, stroke: S}, {marginTop, marginRight, marginBottom, marginLeft, width, height} ) { const index = filter(I, SA, EA, F, S); @@ -73,7 +77,8 @@ export class Arc extends Mark { .startAngle(SA ? (i => SA[i]) : this.sa) .endAngle(EA ? (i => EA[i]) : this.ea) .innerRadius(RI ? (i => r0 * RI[i]) : r0 * this.ri) - .outerRadius(RO ? (i => r0 * RO[i]) : r0 * this.ro); + .outerRadius(RO ? (i => r0 * RO[i]) : r0 * this.ro) + .padAngle(PA ? (i => PA[i]) : this.pa); return create("svg:g") .call(applyIndirectStyles, this) diff --git a/src/marks/pie.js b/src/marks/pie.js new file mode 100644 index 0000000000..b66c612ad3 --- /dev/null +++ b/src/marks/pie.js @@ -0,0 +1,29 @@ +import {arc} from "./arc.js"; +import {pie as transformPie} from "../transforms/pie.js"; + +export function pie(data, { + value, + sort, + sortValues, + startAngle, + endAngle, + padAngle, + ...options +} = {}) { + const transform = transformPie(); + if (value !== undefined) transform.value(value); + if (sort !== undefined) transform.sort(sort); + if (sortValues !== undefined) transform.sortValues(sortValues); + if (startAngle !== undefined) transform.startAngle(startAngle); + if (endAngle !== undefined) transform.endAngle(endAngle); + if (padAngle !== undefined) transform.padAngle(padAngle); + + return arc( + data, + { + ...options, + padAngle, + transform + } + ); +} diff --git a/src/transforms/pie.js b/src/transforms/pie.js new file mode 100644 index 0000000000..6dfd744990 --- /dev/null +++ b/src/transforms/pie.js @@ -0,0 +1,3 @@ +import {pie as shapePie} from "d3-shape"; + +export const pie = shapePie; \ No newline at end of file From a7dfa9c446df07bfb89fec50e78397f78a370dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 5 Jan 2021 16:59:12 +0100 Subject: [PATCH 4/6] allow transforms on pie --- src/marks/pie.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/marks/pie.js b/src/marks/pie.js index b66c612ad3..6279af2873 100644 --- a/src/marks/pie.js +++ b/src/marks/pie.js @@ -6,24 +6,25 @@ export function pie(data, { sort, sortValues, startAngle, + transform, endAngle, padAngle, ...options } = {}) { - const transform = transformPie(); - if (value !== undefined) transform.value(value); - if (sort !== undefined) transform.sort(sort); - if (sortValues !== undefined) transform.sortValues(sortValues); - if (startAngle !== undefined) transform.startAngle(startAngle); - if (endAngle !== undefined) transform.endAngle(endAngle); - if (padAngle !== undefined) transform.padAngle(padAngle); + const pie = transformPie(); + if (value !== undefined) pie.value(value); + if (sort !== undefined) pie.sort(sort); + if (sortValues !== undefined) pie.sortValues(sortValues); + if (startAngle !== undefined) pie.startAngle(startAngle); + if (endAngle !== undefined) pie.endAngle(endAngle); + if (padAngle !== undefined) pie.padAngle(padAngle); return arc( data, { ...options, padAngle, - transform + transform: (!transform ? pie : data => pie(transform(data))) } ); } From f0625a5796dea89e52c72fc84361c684c580d443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 5 Jan 2021 18:11:16 +0100 Subject: [PATCH 5/6] pie options: - sort: false; sortValues: false; - value: "value" --- src/marks/pie.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/marks/pie.js b/src/marks/pie.js index 6279af2873..a39794595f 100644 --- a/src/marks/pie.js +++ b/src/marks/pie.js @@ -1,5 +1,8 @@ import {arc} from "./arc.js"; import {pie as transformPie} from "../transforms/pie.js"; +import {field} from "../mark.js"; + +function noSort() {} export function pie(data, { value, @@ -12,9 +15,9 @@ export function pie(data, { ...options } = {}) { const pie = transformPie(); - if (value !== undefined) pie.value(value); - if (sort !== undefined) pie.sort(sort); - if (sortValues !== undefined) pie.sortValues(sortValues); + if (value !== undefined) pie.value(typeof value === "string" ? field(value) : value); + if (sort !== undefined) pie.sort(sort || noSort); + if (sortValues !== undefined) pie.sortValues(sortValues || noSort); if (startAngle !== undefined) pie.startAngle(startAngle); if (endAngle !== undefined) pie.endAngle(endAngle); if (padAngle !== undefined) pie.padAngle(padAngle); From 6a338e01d9c1745cb8ddc95585d2a70e57eaecaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 5 Jan 2021 18:49:38 +0100 Subject: [PATCH 6/6] arc and pie labels --- src/marks/arc.js | 32 ++++++++++++++++++++++++++------ src/marks/pie.js | 4 ++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/marks/arc.js b/src/marks/arc.js index 005629155d..4993f6828a 100644 --- a/src/marks/arc.js +++ b/src/marks/arc.js @@ -1,7 +1,7 @@ import {ascending} from "d3-array"; import {create} from "d3-selection"; import {arc as shapeArc} from "d3-shape"; -import {filter} from "../defined.js"; +import {filter, nonempty} from "../defined.js"; import {Mark, maybeColor, maybeNumber, title} from "../mark.js"; import {Style, applyDirectStyles, applyIndirectStyles, applyTransform} from "../style.js"; @@ -20,6 +20,7 @@ export class Arc extends Mark { padAngle, z, title, + label, fill, stroke, transform, @@ -48,6 +49,7 @@ export class Arc extends Mark { {name: "padAngle", value: vpa, optional: true}, {name: "z", value: z, optional: true}, {name: "title", value: title, optional: true}, + {name: "label", value: label, optional: true}, {name: "fill", value: vfill, scale: "color", optional: true}, {name: "stroke", value: vstroke, scale: "color", optional: true} ], @@ -65,7 +67,7 @@ export class Arc extends Mark { render( I, {x, y, color}, - {startAngle: SA, endAngle: EA, innerRadius: RI, outerRadius: RO, padAngle: PA, x: X, y: Y, z: Z, title: L, fill: F, stroke: S}, + {startAngle: SA, endAngle: EA, innerRadius: RI, outerRadius: RO, padAngle: PA, x: X, y: Y, z: Z, title: T, label: L, fill: F, stroke: S}, {marginTop, marginRight, marginBottom, marginLeft, width, height} ) { const index = filter(I, SA, EA, F, S); @@ -80,9 +82,11 @@ export class Arc extends Mark { .outerRadius(RO ? (i => r0 * RO[i]) : r0 * this.ro) .padAngle(PA ? (i => PA[i]) : this.pa); - return create("svg:g") + const wrapper = create("svg:g") + .call(applyTransform, x, y); + wrapper + .append("g") .call(applyIndirectStyles, this) - .call(applyTransform, x, y) .call(g => g.selectAll() .data(index) .join("path") @@ -91,8 +95,24 @@ export class Arc extends Mark { .attr("transform", i => `translate(${x(X[i])},${y(Y[i])})`) .attr("fill", F && (i => color(F[i]))) .attr("stroke", S && (i => color(S[i]))) - .call(title(L))) - .node(); + .call(title(T))); + if (L) wrapper.append("g").call(_label(L, index, arc)); + return wrapper.node(); + + function _label(L, index, arc) { + return L ? g => { + g.append("g") + .selectAll("text") + .data(index.filter(i => nonempty(L[i]))) + .join("g") + .attr("transform", i => `translate(${x(X[i])},${y(Y[i])})`) + .append("text") + .text(i => L[i]) + .attr("transform", i => `translate(${arc.centroid(i)})`) + .attr("text-anchor", "center") + .style("fill", "black"); + } : () => {}; + } } } diff --git a/src/marks/pie.js b/src/marks/pie.js index a39794595f..f7e911377c 100644 --- a/src/marks/pie.js +++ b/src/marks/pie.js @@ -2,8 +2,6 @@ import {arc} from "./arc.js"; import {pie as transformPie} from "../transforms/pie.js"; import {field} from "../mark.js"; -function noSort() {} - export function pie(data, { value, sort, @@ -31,3 +29,5 @@ export function pie(data, { } ); } + +function noSort() {}