From 727632e70845bf3bface0ee6a6d2a6ff8cb6ede5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 5 Apr 2021 17:10:00 +0200 Subject: [PATCH 1/3] text font-size as a channel, bound to the scale *r* Interestingly, the result is quite smaller than a circle defined with the same accessor. So it seems that we need to introduce fontSizeAdjust (which isn't much supported https://caniuse.com/font-size-adjust), and rescale the resulting channel values in the font-size attribute. See for example https://observablehq.com/d/f5698ebfc65d2b5c#FSA closes https://github.com/observablehq/plot/issues/296 --- src/marks/text.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/marks/text.js b/src/marks/text.js index d483fb2d80..f32cd2a228 100644 --- a/src/marks/text.js +++ b/src/marks/text.js @@ -18,6 +18,7 @@ export class Text extends Mark { textAnchor, fontFamily, fontSize, + fontSizeAdjust = 1, fontStyle, fontVariant, fontWeight, @@ -30,12 +31,14 @@ export class Text extends Mark { const [vfill, cfill] = maybeColor(fill, "currentColor"); const [vfillOpacity, cfillOpacity] = maybeNumber(fillOpacity); const [vrotate, crotate] = maybeNumber(rotate, 0); + const [vfontSize, cfontSize] = maybeNumber(fontSize); super( data, [ {name: "x", value: x, scale: "x", optional: true}, {name: "y", value: y, scale: "y", optional: true}, {name: "z", value: z, optional: true}, + {name: "fontSize", value: vfontSize, scale: "r", optional: true}, {name: "rotate", value: numberChannel(vrotate), optional: true}, {name: "text", value: text}, {name: "title", value: title, optional: true}, @@ -48,7 +51,8 @@ export class Text extends Mark { this.rotate = crotate; this.textAnchor = string(textAnchor); this.fontFamily = string(fontFamily); - this.fontSize = string(fontSize); + this.fontSize = string(cfontSize / fontSizeAdjust); + this.fontSizeAdjust = fontSizeAdjust; this.fontStyle = string(fontStyle); this.fontVariant = string(fontVariant); this.fontWeight = string(fontWeight); @@ -58,10 +62,10 @@ export class Text extends Mark { render( I, {x, y}, - {x: X, y: Y, z: Z, rotate: R, text: T, title: L, fill: F, fillOpacity: FO}, + {x: X, y: Y, z: Z, rotate: R, text: T, title: L, fill: F, fillOpacity: FO, fontSize: FS}, {width, height, marginTop, marginRight, marginBottom, marginLeft} ) { - const {rotate} = this; + const {fontSizeAdjust, rotate} = this; const index = filter(I, X, Y, F, FO, R).filter(i => nonempty(T[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); const cx = (marginLeft + width - marginRight) / 2; @@ -84,6 +88,7 @@ export class Text extends Mark { : text => text.attr("x", X ? i => X[i] : cx).attr("y", Y ? i => Y[i] : cy)) .call(applyAttr, "fill", F && (i => F[i])) .call(applyAttr, "fill-opacity", FO && (i => FO[i])) + .call(applyAttr, "font-size", FS && (i => FS[i] / fontSizeAdjust)) .text(i => T[i]) .call(title(L))) .node(); From 4a09bd07ace42367c24351ceee3a77643d97fc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 9 Apr 2021 15:10:57 +0200 Subject: [PATCH 2/3] fontSize is an unscaled numerical channel --- src/marks/text.js | 10 ++++------ test/plots/index.js | 1 + test/plots/letter-frequency-cloud.js | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 test/plots/letter-frequency-cloud.js diff --git a/src/marks/text.js b/src/marks/text.js index f32cd2a228..4c0175c22b 100644 --- a/src/marks/text.js +++ b/src/marks/text.js @@ -18,7 +18,6 @@ export class Text extends Mark { textAnchor, fontFamily, fontSize, - fontSizeAdjust = 1, fontStyle, fontVariant, fontWeight, @@ -38,7 +37,7 @@ export class Text extends Mark { {name: "x", value: x, scale: "x", optional: true}, {name: "y", value: y, scale: "y", optional: true}, {name: "z", value: z, optional: true}, - {name: "fontSize", value: vfontSize, scale: "r", optional: true}, + {name: "fontSize", value: numberChannel(vfontSize), optional: true}, {name: "rotate", value: numberChannel(vrotate), optional: true}, {name: "text", value: text}, {name: "title", value: title, optional: true}, @@ -51,8 +50,7 @@ export class Text extends Mark { this.rotate = crotate; this.textAnchor = string(textAnchor); this.fontFamily = string(fontFamily); - this.fontSize = string(cfontSize / fontSizeAdjust); - this.fontSizeAdjust = fontSizeAdjust; + this.fontSize = string(cfontSize); this.fontStyle = string(fontStyle); this.fontVariant = string(fontVariant); this.fontWeight = string(fontWeight); @@ -65,7 +63,7 @@ export class Text extends Mark { {x: X, y: Y, z: Z, rotate: R, text: T, title: L, fill: F, fillOpacity: FO, fontSize: FS}, {width, height, marginTop, marginRight, marginBottom, marginLeft} ) { - const {fontSizeAdjust, rotate} = this; + const {rotate} = this; const index = filter(I, X, Y, F, FO, R).filter(i => nonempty(T[i])); if (Z) index.sort((i, j) => ascending(Z[i], Z[j])); const cx = (marginLeft + width - marginRight) / 2; @@ -88,7 +86,7 @@ export class Text extends Mark { : text => text.attr("x", X ? i => X[i] : cx).attr("y", Y ? i => Y[i] : cy)) .call(applyAttr, "fill", F && (i => F[i])) .call(applyAttr, "fill-opacity", FO && (i => FO[i])) - .call(applyAttr, "font-size", FS && (i => FS[i] / fontSizeAdjust)) + .call(applyAttr, "font-size", FS && (i => FS[i])) .text(i => T[i]) .call(title(L))) .node(); diff --git a/test/plots/index.js b/test/plots/index.js index 3b0d94bfd9..a0ed7a444f 100644 --- a/test/plots/index.js +++ b/test/plots/index.js @@ -33,6 +33,7 @@ export {default as industryUnemploymentShare} from "./industry-unemployment-shar export {default as industryUnemploymentStream} from "./industry-unemployment-stream.js"; export {default as learningPoverty} from "./learning-poverty.js"; export {default as letterFrequencyBar} from "./letter-frequency-bar.js"; +export {default as letterFrequencyCloud} from "./letter-frequency-cloud.js"; export {default as letterFrequencyColumn} from "./letter-frequency-column.js"; export {default as letterFrequencyDot} from "./letter-frequency-dot.js"; export {default as letterFrequencyLollipop} from "./letter-frequency-lollipop.js"; diff --git a/test/plots/letter-frequency-cloud.js b/test/plots/letter-frequency-cloud.js new file mode 100644 index 0000000000..b116b0d799 --- /dev/null +++ b/test/plots/letter-frequency-cloud.js @@ -0,0 +1,17 @@ +import * as Plot from "@observablehq/plot"; +import * as d3 from "d3"; + +export default async function() { + const alphabet = await d3.csv("data/alphabet.csv", d3.autoType); + const random = d3.randomLcg(3); + const m = d3.max(alphabet, d => d.frequency); + const x = alphabet.map(random); + const y = alphabet.map(random); + return Plot.plot({ + x: { axis: null, domain: [-0.1, 1.1] }, + y: { axis: null, domain: [-0.1, 1.2] }, + marks: [ + Plot.text(alphabet, {x, y, text: "letter", fontSize: d => 10 + 200 * (d.frequency / m)}) + ] + }); +} From 85bebc9b5fd50c48e426773a93461990f72fff53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 9 Apr 2021 15:12:09 +0200 Subject: [PATCH 3/3] fix tests --- test/marks/text-test.js | 6 +++--- test/output/letterFrequencyCloud.svg | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 test/output/letterFrequencyCloud.svg diff --git a/test/marks/text-test.js b/test/marks/text-test.js index 6c182104f6..0f661a93a2 100644 --- a/test/marks/text-test.js +++ b/test/marks/text-test.js @@ -5,9 +5,9 @@ tape("text() has the expected defaults", test => { const text = Plot.text(); test.strictEqual(text.data, undefined); test.strictEqual(text.transform, undefined); - test.deepEqual(text.channels.map(c => c.name), ["x", "y", "rotate", "text"]); - test.deepEqual(text.channels.map(c => Plot.valueof([[1, 2], [3, 4]], c.value)), [[1, 3], [2, 4], undefined, [0, 1]]); - test.deepEqual(text.channels.map(c => c.scale), ["x", "y", undefined, undefined]); + test.deepEqual(text.channels.map(c => c.name), ["x", "y", "fontSize", "rotate", "text"]); + test.deepEqual(text.channels.map(c => Plot.valueof([[1, 2], [3, 4]], c.value)), [[1, 3], [2, 4], undefined, undefined, [0, 1]]); + test.deepEqual(text.channels.map(c => c.scale), ["x", "y", undefined, undefined, undefined]); test.strictEqual(text.fill, undefined); test.strictEqual(text.fillOpacity, undefined); test.strictEqual(text.stroke, undefined); diff --git a/test/output/letterFrequencyCloud.svg b/test/output/letterFrequencyCloud.svg new file mode 100644 index 0000000000..a7fab33146 --- /dev/null +++ b/test/output/letterFrequencyCloud.svg @@ -0,0 +1,3 @@ + + ETAOINSHRDLCUMWFGYPBVKJXQZ + \ No newline at end of file