Skip to content

add a definition to the {transform} representation of a value #1070

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
77 changes: 38 additions & 39 deletions src/facet.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {column, maybeColorChannel, maybeNumberChannel, slice, valueof} from "./options.js";
import {maybeSymbolChannel} from "./symbols.js";
import {maybeFontSizeChannel} from "./marks/text.js";
import {maybePathChannel} from "./marks/image.js";
import {column, slice, valueof} from "./options.js";

export function facetReindex(facets, n) {
if (facets.length === 1) return {facets};
Expand Down Expand Up @@ -54,44 +51,46 @@ export function maybeExpand(X, plan) {

// Iterate over the options and pull out any that represent columns of values.
const knownChannels = [
["x"],
["x1"],
["x2"],
["y"],
["y1"],
["y2"],
["z"],
["ariaLabel"],
["href"],
["title"],
["fill", (value) => maybeColorChannel(value)[0]],
["stroke", (value) => maybeColorChannel(value)[0]],
["fillOpacity", (value) => maybeNumberChannel(value)[0]],
["strokeOpacity", (value) => maybeNumberChannel(value)[0]],
["opacity", (value) => maybeNumberChannel(value)[0]],
["strokeWidth", (value) => maybeNumberChannel(value)[0]],
["symbol", (value) => maybeSymbolChannel(value)[0]], // dot
["r", (value) => maybeNumberChannel(value)[0]], // dot
["rotate", (value) => maybeNumberChannel(value)[0]], // dot, text
["fontSize", (value) => maybeFontSizeChannel(value)[0]], // text
["text"], // text
["length", (value) => maybeNumberChannel(value)[0]], // vector
["width", (value) => maybeNumberChannel(value)[0]], // image
["height", (value) => maybeNumberChannel(value)[0]], // image
["src", (value) => maybePathChannel(value)[0]], // image
["weight", (value) => maybeNumberChannel(value)[0]] // density
"x",
"x1",
"x2",
"y",
"y1",
"y2",
"z",
"ariaLabel",
"href",
"title",
"fill",
"stroke",
"fillOpacity",
"strokeOpacity",
"opacity",
"strokeWidth",
"symbol", // dot
"r", // dot
"rotate", // dot, text
"fontSize", // text
"text", // text
"length", // vector
"width", // image
"height", // image
"src", // image
"weight" // density
];

export function maybeExpandOutputs(options) {
const other = {};
const outputs = [];
for (const [name, test = (value) => value] of knownChannels) {
const value = test(options[name]);
export function maybeExpandChannels(options) {
const channels = {};
const [{transform: plan}, setPlan] = column();
for (const name of knownChannels) {
let value = options[name];
if (value != null) {
const [V, setV] = column(value);
other[name] = V;
outputs.push((data, plan) => setV(maybeExpand(valueof(data, value), plan)));
if (value.definition) continue; // already planned
channels[name] = {
definition: value,
transform: (data) => maybeExpand(valueof(data, value), plan())
};
}
}
return [other, outputs];
return [channels, setPlan];
}
9 changes: 7 additions & 2 deletions src/marks/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,13 @@ function isUrl(string) {

// Disambiguates a constant src definition from a channel. A path or URL string
// is assumed to be a constant; any other string is assumed to be a field name.
export function maybePathChannel(value) {
return typeof value === "string" && (isPath(value) || isUrl(value)) ? [undefined, value] : [value, undefined];
function maybePathChannel(value) {
if (typeof value === "string" && (isPath(value) || isUrl(value))) return [undefined, value];
if (value && value.definition) {
const [, f] = maybePathChannel(value.definition);
if (f !== undefined) return [undefined, f];
}
return [value, undefined];
}

export class Image extends Mark {
Expand Down
6 changes: 5 additions & 1 deletion src/marks/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,12 @@ const fontSizes = new Set([
// - string <length>: e.g., "12px"
// - string <percentage>: e.g., "80%"
// Anything else is assumed to be a channel definition.
export function maybeFontSizeChannel(fontSize) {
function maybeFontSizeChannel(fontSize) {
if (fontSize == null || typeof fontSize === "number") return [undefined, fontSize];
if (fontSize && fontSize.definition) {
const [, f] = maybeFontSizeChannel(fontSize.definition);
if (f !== undefined) return [undefined, f];
}
if (typeof fontSize !== "string") return [fontSize, undefined];
fontSize = fontSize.trim().toLowerCase();
return fontSizes.has(fontSize) || /^[+-]?\d*\.?\d+(e[+-]?\d+)?(\w*|%)$/.test(fontSize)
Expand Down
12 changes: 11 additions & 1 deletion src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ export function percentile(reduce) {
// CSS color, use an accessor (d => d.red) instead.
export function maybeColorChannel(value, defaultValue) {
if (value === undefined) value = defaultValue;
if (value && value.definition && isColor(value.definition)) return [undefined, value.definition];
return value === null ? [undefined, "none"] : isColor(value) ? [undefined, value] : [value, undefined];
}

// Similar to maybeColorChannel, this tests whether the given value is a number
// indicating a constant, and otherwise assumes that it’s a channel value.
export function maybeNumberChannel(value, defaultValue) {
if (value === undefined) value = defaultValue;
if (value && value.definition && typeof value.definition === "number") return [undefined, value.definition];
return value === null || typeof value === "number" ? [undefined, value] : [value, undefined];
}

Expand Down Expand Up @@ -252,7 +254,15 @@ export function maybeColumn(source) {
}

export function labelof(value, defaultValue) {
return typeof value === "string" ? value : value && value.label !== undefined ? value.label : defaultValue;
return typeof value === "string"
? value
: value
? value.label !== undefined
? value.label
: typeof value.definition === "string"
? value.definition
: defaultValue
: defaultValue;
}

// Assuming that both x1 and x2 and lazy columns (per above), this derives a new
Expand Down
1 change: 1 addition & 0 deletions src/symbols.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export function maybeSymbol(symbol) {

export function maybeSymbolChannel(symbol) {
if (symbol == null || isSymbolObject(symbol)) return [undefined, symbol];
if (symbol && symbol.definition && isSymbolObject(symbol.definition)) return [undefined, symbol.definition];
if (typeof symbol === "string") {
const value = symbols.get(`${symbol}`.toLowerCase());
if (value) return [undefined, value];
Expand Down
6 changes: 3 additions & 3 deletions src/transforms/map.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {count, group, rank} from "d3";
import {maybeZ, take, valueof, maybeInput, column} from "../options.js";
import {basic} from "./basic.js";
import {maybeExpand, facetReindex, maybeExpandOutputs} from "../facet.js";
import {maybeExpand, facetReindex, maybeExpandChannels} from "../facet.js";

/**
* ```js
Expand Down Expand Up @@ -57,15 +57,15 @@ export function map(outputs = {}, options = {}) {
const [output, setOutput] = column(input);
return {key, input, output, setOutput, map: maybeMap(map)};
});
const [other, facetOutputs] = maybeExpandOutputs(options); // TODO wrap outputs in facetReindex
const [other, setPlan] = maybeExpandChannels(options);
return {
...basic(options, (data, facets) => {
let plan;
({facets, plan} = facetReindex(facets, data.length)); // make facets exclusive
const Z = maybeExpand(valueof(data, z), plan);
const X = channels.map(({input}) => maybeExpand(valueof(data, input), plan));
const MX = channels.map(({setOutput}) => setOutput(new Array(plan ? plan.length : data.length)));
for (const o of facetOutputs) o(data, plan); // expand any extra channels
setPlan(plan); // expand any extra channels
for (const facet of facets) {
for (const I of Z ? group(facet, (i) => Z[i]).values() : [facet]) {
channels.forEach(({map}, i) => map.map(I, X[i], MX[i]));
Expand Down
6 changes: 3 additions & 3 deletions src/transforms/stack.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {InternMap, cumsum, group, groupSort, greatest, max, min, rollup, sum} from "d3";
import {ascendingDefined} from "../defined.js";
import {maybeExpand, facetReindex, maybeExpandOutputs} from "../facet.js";
import {maybeExpand, facetReindex, maybeExpandChannels} from "../facet.js";
import {field, column, mid, range, valueof, one} from "../options.js";
import {maybeColumn, maybeZ, maybeZero} from "../options.js";
import {basic} from "./basic.js";
Expand Down Expand Up @@ -147,7 +147,7 @@ function stack(x, y = one, ky, {offset, order, reverse}, options) {
const [Y2, setY2] = column(y);
offset = maybeOffset(offset);
order = maybeOrder(order, offset, ky);
const [other, outputs] = maybeExpandOutputs(options); // TODO wrap outputs in facetReindex
const [other, setPlan] = maybeExpandChannels(options);
return [
basic(options, (data, facets) => {
let plan;
Expand All @@ -159,8 +159,8 @@ function stack(x, y = one, ky, {offset, order, reverse}, options) {
const Y = maybeExpand(YS, plan);
const Z = maybeExpand(ZS, plan);
const O = order && maybeExpand(order(data, XS, YS, ZS), plan);
setPlan(plan); // expand any extra channels
const n = plan ? plan.length : data.length;
for (const o of outputs) o(data, plan); // expand any extra channels
const Y1 = setY1(new Float64Array(n));
const Y2 = setY2(new Float64Array(n));
const facetstacks = [];
Expand Down