@thi.ng/viz
Version:
Declarative, functional & multi-format data visualization toolkit based around @thi.ng/hiccup
152 lines (151 loc) • 3.26 kB
JavaScript
import { eqDelta } from "@thi.ng/math/eqdelta";
import { comp } from "@thi.ng/transducers/comp";
import { iterator } from "@thi.ng/transducers/iterator";
import { filter } from "@thi.ng/transducers/filter";
import { mapcat } from "@thi.ng/transducers/mapcat";
const __gridAxis = ({ domain, major, minor }, majorTickFn, minorTickFn) => {
const majorTicks = [...major.ticks(domain)];
return [
["path", {}, [...mapcat(majorTickFn, majorTicks)]],
[
"path",
{},
[
...iterator(
comp(
filter(
(x) => majorTicks.find((y) => eqDelta(x, y)) === void 0
),
mapcat(minorTickFn)
),
minor.ticks(domain)
)
]
]
];
};
const NONE = () => null;
const __gridCartesian = ({ xaxis, yaxis, grid }) => {
grid = {
attribs: { stroke: [0, 0, 0, 0.2], "stroke-dasharray": "1 1" },
xmajor: true,
xminor: true,
ymajor: true,
yminor: true,
...grid
};
const [x1, x2] = xaxis.range;
const [y1, y2] = yaxis.range;
const lineX = (x) => [
["M", [xaxis.scale(x), y1]],
["V", y2]
];
const lineY = (x) => [
["M", [x1, yaxis.scale(x)]],
["H", x2]
];
return [
"g",
grid.attribs,
...__gridAxis(
xaxis,
grid.xmajor ? lineX : NONE,
grid.xminor ? lineX : NONE
),
...__gridAxis(
yaxis,
grid.ymajor ? lineY : NONE,
grid.yminor ? lineY : NONE
)
];
};
const __axisCommon = (spec, axis, majorTickFn, minorTickFn, labelFn) => {
const majorTicks = [...spec.major.ticks(spec.domain)];
return [
"g",
spec.attribs,
axis,
...__gridAxis(spec, majorTickFn, minorTickFn),
[
"g",
{ stroke: "none", ...spec.labelAttribs },
...majorTicks.map(labelFn)
]
];
};
const cartesianAxisX = (spec) => {
const {
pos,
scale,
format,
label,
labelOffset: [lx, ly],
range: [r1, r2]
} = spec;
const tick = (dy) => (x) => [
["M", [scale(x), pos]],
["v", dy]
];
return __axisCommon(
spec,
[
"path",
{},
[
["M", [r1, pos]],
["L", [r2, pos]]
]
],
tick(spec.major.size),
tick(spec.minor.size),
(x) => label([scale(x) + lx, pos + ly], format(x))
);
};
const cartesianAxisY = (spec) => {
const {
pos,
scale,
format,
label,
labelOffset: [lx, ly],
range: [r1, r2]
} = spec;
const tick = (dx) => (y) => [
["M", [pos, scale(y)]],
["h", dx]
];
return __axisCommon(
spec,
[
"path",
{},
[
["M", [pos, r1]],
["L", [pos, r2]]
]
],
tick(-spec.major.size),
tick(-spec.minor.size),
(y) => label([pos + lx, scale(y) + ly], format(y))
);
};
const DEFAULT_ATTRIBS = {
"font-family": "Arial, Helvetica, sans-serif",
"font-size": "10px"
};
const plotCartesian = (spec) => {
const { xaxis, yaxis, plots } = spec;
return [
"g",
{ ...DEFAULT_ATTRIBS, ...spec.attribs },
spec.grid ? __gridCartesian(spec) : null,
...plots.map((fn) => fn(spec)),
xaxis.visible ? cartesianAxisX(xaxis) : null,
yaxis.visible ? cartesianAxisY(yaxis) : null
];
};
export {
cartesianAxisX,
cartesianAxisY,
plotCartesian
};