Skip to content

Commit 7a47351

Browse files
committed
mark initializers
1 parent 66142e2 commit 7a47351

File tree

15 files changed

+490
-167
lines changed

15 files changed

+490
-167
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@rollup/plugin-json": "4",
3939
"@rollup/plugin-node-resolve": "13",
4040
"canvas": "2",
41+
"d3-hexbin": "^0.2.2",
4142
"eslint": "8",
4243
"htl": "0.3",
4344
"js-beautify": "1",

src/channel.js

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,25 @@ import {first, labelof, maybeValue, range, valueof} from "./options.js";
33
import {registry} from "./scales/index.js";
44
import {maybeReduce} from "./transforms/group.js";
55

6+
export function channelObject(channelDescriptors, data) {
7+
const channels = {};
8+
for (const channel of channelDescriptors) {
9+
channels[channel.name] = Channel(data, channel);
10+
}
11+
return channels;
12+
}
13+
14+
// TODO use Float64Array.from for position and radius scales?
15+
export function valueObject(channels, scales) {
16+
const values = {};
17+
for (const channelName in channels) {
18+
const {scale: scaleName, value} = channels[channelName];
19+
const scale = scales[scaleName];
20+
values[channelName] = scale === undefined ? value : Array.from(value, scale);
21+
}
22+
return values;
23+
}
24+
625
// TODO Type coercion?
726
export function Channel(data, {scale, type, value, filter, hint}) {
827
return {
@@ -15,19 +34,22 @@ export function Channel(data, {scale, type, value, filter, hint}) {
1534
};
1635
}
1736

37+
// Note: mutates channel.domain! This is set to a function so that it is lazily
38+
// computed; i.e., if the scale’s domain is set explicitly, that takes priority
39+
// over the sort option, and we don’t need to do additional work.
1840
export function channelSort(channels, facetChannels, data, options) {
1941
const {reverse: defaultReverse, reduce: defaultReduce = true, limit: defaultLimit} = options;
2042
for (const x in options) {
2143
if (!registry.has(x)) continue; // ignore unknown scale keys
2244
let {value: y, reverse = defaultReverse, reduce = defaultReduce, limit = defaultLimit} = maybeValue(options[x]);
2345
if (reverse === undefined) reverse = y === "width" || y === "height"; // default to descending for lengths
2446
if (reduce == null || reduce === false) continue; // disabled reducer
25-
const X = channels.find(([, {scale}]) => scale === x) || facetChannels && facetChannels.find(([, {scale}]) => scale === x);
47+
const X = findScaleChannel(channels, x) || facetChannels && findScaleChannel(facetChannels, x);
2648
if (!X) throw new Error(`missing channel for scale: ${x}`);
27-
const XV = X[1].value;
49+
const XV = X.value;
2850
const [lo = 0, hi = Infinity] = limit && typeof limit[Symbol.iterator] === "function" ? limit : limit < 0 ? [limit] : [0, limit];
2951
if (y == null) {
30-
X[1].domain = () => {
52+
X.domain = () => {
3153
let domain = XV;
3254
if (reverse) domain = domain.slice().reverse();
3355
if (lo !== 0 || hi !== Infinity) domain = domain.slice(lo, hi);
@@ -39,7 +61,7 @@ export function channelSort(channels, facetChannels, data, options) {
3961
: y === "width" ? difference(channels, "x1", "x2")
4062
: values(channels, y, y === "y" ? "y2" : y === "x" ? "x2" : undefined);
4163
const reducer = maybeReduce(reduce === true ? "max" : reduce, YV);
42-
X[1].domain = () => {
64+
X.domain = () => {
4365
let domain = rollup(range(XV), I => reducer.reduce(I, YV), i => XV[i]);
4466
domain = sort(domain, reverse ? descendingGroup : ascendingGroup);
4567
if (lo !== 0 || hi !== Infinity) domain = domain.slice(lo, hi);
@@ -49,16 +71,23 @@ export function channelSort(channels, facetChannels, data, options) {
4971
}
5072
}
5173

74+
function findScaleChannel(channels, scale) {
75+
for (const name in channels) {
76+
const channel = channels[name];
77+
if (channel.scale === scale) return channel;
78+
}
79+
}
80+
5281
function difference(channels, k1, k2) {
5382
const X1 = values(channels, k1);
5483
const X2 = values(channels, k2);
5584
return Float64Array.from(X2, (x2, i) => Math.abs(x2 - X1[i]));
5685
}
5786

5887
function values(channels, name, alias) {
59-
let channel = channels.find(([n]) => n === name);
60-
if (!channel && alias !== undefined) channel = channels.find(([n]) => n === alias);
61-
if (channel) return channel[1].value;
88+
let channel = channels[name];
89+
if (!channel && alias !== undefined) channel = channels[alias];
90+
if (channel) return channel.value;
6291
throw new Error(`missing channel: ${name}`);
6392
}
6493

src/marks/dot.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import {create, path, symbolCircle} from "d3";
22
import {positive} from "../defined.js";
3-
import {identity, maybeFrameAnchor, maybeNumberChannel, maybeSymbolChannel, maybeTuple} from "../options.js";
3+
import {identity, maybeFrameAnchor, maybeNumberChannel, maybeTuple} from "../options.js";
44
import {Mark} from "../plot.js";
55
import {applyChannelStyles, applyDirectStyles, applyFrameAnchor, applyIndirectStyles, applyTransform, offset} from "../style.js";
6+
import {maybeSymbolChannel} from "../symbols.js";
67

78
const defaults = {
89
ariaLabel: "dot",

src/options.js

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import {parse as isoParse} from "isoformat";
22
import {color, descending, quantile} from "d3";
3-
import {symbolAsterisk, symbolDiamond2, symbolPlus, symbolSquare2, symbolTriangle2, symbolX as symbolTimes} from "d3";
4-
import {symbolCircle, symbolCross, symbolDiamond, symbolSquare, symbolStar, symbolTriangle, symbolWye} from "d3";
53

64
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
75
const TypedArray = Object.getPrototypeOf(Uint8Array);
@@ -22,6 +20,7 @@ export const field = name => d => d[name];
2220
export const indexOf = (d, i) => i;
2321
export const identity = {transform: d => d};
2422
export const zero = () => 0;
23+
export const yes = () => true;
2524
export const string = x => x == null ? x : `${x}`;
2625
export const number = x => x == null ? x : +x;
2726
export const boolean = x => x == null ? x : !!x;
@@ -305,48 +304,6 @@ export function isRound(value) {
305304
return /^\s*round\s*$/i.test(value);
306305
}
307306

308-
const symbols = new Map([
309-
["asterisk", symbolAsterisk],
310-
["circle", symbolCircle],
311-
["cross", symbolCross],
312-
["diamond", symbolDiamond],
313-
["diamond2", symbolDiamond2],
314-
["plus", symbolPlus],
315-
["square", symbolSquare],
316-
["square2", symbolSquare2],
317-
["star", symbolStar],
318-
["times", symbolTimes],
319-
["triangle", symbolTriangle],
320-
["triangle2", symbolTriangle2],
321-
["wye", symbolWye]
322-
]);
323-
324-
function isSymbolObject(value) {
325-
return value && typeof value.draw === "function";
326-
}
327-
328-
export function isSymbol(value) {
329-
if (isSymbolObject(value)) return true;
330-
if (typeof value !== "string") return false;
331-
return symbols.has(value.toLowerCase());
332-
}
333-
334-
export function maybeSymbol(symbol) {
335-
if (symbol == null || isSymbolObject(symbol)) return symbol;
336-
const value = symbols.get(`${symbol}`.toLowerCase());
337-
if (value) return value;
338-
throw new Error(`invalid symbol: ${symbol}`);
339-
}
340-
341-
export function maybeSymbolChannel(symbol) {
342-
if (symbol == null || isSymbolObject(symbol)) return [undefined, symbol];
343-
if (typeof symbol === "string") {
344-
const value = symbols.get(`${symbol}`.toLowerCase());
345-
if (value) return [undefined, value];
346-
}
347-
return [symbol, undefined];
348-
}
349-
350307
export function maybeFrameAnchor(value = "middle") {
351308
return keyword(value, "frameAnchor", ["middle", "top-left", "top", "top-right", "right", "bottom-right", "bottom", "bottom-left", "left"]);
352309
}

0 commit comments

Comments
 (0)