@visactor/vgrammar-hierarchy
Version:
Layout of hierarchical data for VGrammar
147 lines (142 loc) • 7.91 kB
JavaScript
"use strict";
var __importDefault = this && this.__importDefault || function(mod) {
return mod && mod.__esModule ? mod : {
default: mod
};
};
Object.defineProperty(exports, "__esModule", {
value: !0
}), exports.TreemapLayout = void 0;
const vutils_1 = require("@visactor/vutils"), binary_1 = __importDefault(require("./binary")), dice_1 = __importDefault(require("./dice")), slice_1 = __importDefault(require("./slice")), sliceDice_1 = __importDefault(require("./sliceDice")), squarify_1 = require("./squarify"), vgrammar_util_1 = require("@visactor/vgrammar-util"), utils_1 = require("../utils"), algorithms = {
binary: binary_1.default,
dice: dice_1.default,
slice: slice_1.default,
sliceDice: sliceDice_1.default
};
class TreemapLayout {
constructor(options) {
var _a;
this._filterByArea = (node, ratio) => {
var _a;
const minArea = this._getMinAreaByDepth(node.depth);
if (minArea > 0 && node.value * ratio < minArea) return !1;
if (null === (_a = node.children) || void 0 === _a ? void 0 : _a.length) {
const newChildren = node.children.filter((child => this._filterByArea(child, ratio)));
newChildren.length ? newChildren.length !== node.children.length && (node.children = newChildren) : (node.isLeaf = !0,
node.children = null);
}
return !0;
}, this._getMinAreaByDepth = depth => {
var _a;
return depth < 0 ? 0 : null !== (_a = (0, vutils_1.isArray)(this.options.minVisibleArea) ? this.options.minVisibleArea[depth] : this.options.minVisibleArea) && void 0 !== _a ? _a : 0;
}, this._getGapWidthByDepth = depth => {
var _a;
return depth < 0 ? 0 : null !== (_a = (0, vutils_1.isArray)(this.options.gapWidth) ? this.options.gapWidth[depth] : this.options.gapWidth) && void 0 !== _a ? _a : 0;
}, this._getPaddingByDepth = depth => {
var _a;
return depth < 0 ? 0 : null !== (_a = (0, vutils_1.isArray)(this.options.padding) ? this.options.padding[depth] : this.options.padding) && void 0 !== _a ? _a : 0;
}, this._getLabelPaddingByDepth = depth => {
var _a;
return depth < 0 ? 0 : null !== (_a = (0, vutils_1.isArray)(this.options.labelPadding) ? this.options.labelPadding[depth] : this.options.labelPadding) && void 0 !== _a ? _a : 0;
}, this._layoutNode = parent => {
const gapWidth = this._getGapWidthByDepth(parent.depth);
let x0 = parent.x0, y0 = parent.y0, x1 = parent.x1, y1 = parent.y1;
if (parent.maxDepth = this._maxDepth, gapWidth > 0 && (x0 += gapWidth / 2, x1 -= gapWidth / 2,
y0 += gapWidth / 2, y1 -= gapWidth / 2, x0 > x1 && (x0 = (x0 + x1) / 2, x1 = x0),
y0 > y1 && (y0 = (y0 + y1) / 2, y1 = y0), parent.x0 = x0, parent.x1 = x1, parent.y0 = y0,
parent.y1 = y1), parent.children) {
const labelPadding = this._getLabelPaddingByDepth(parent.depth), padding = this._getPaddingByDepth(parent.depth);
padding > 0 && padding < Math.min(x1 - x0, y1 - y0) / 2 && (y0 += padding, y1 -= padding,
x0 += padding, x1 -= padding), labelPadding > 0 && ("top" === this.options.labelPosition && y0 + labelPadding < y1 ? (parent.labelRect = {
x0: x0,
y0: y0,
x1: x1,
y1: y0 + labelPadding
}, y0 += labelPadding) : "bottom" === this.options.labelPosition && y1 - labelPadding > y0 ? (parent.labelRect = {
x0: x0,
y0: y1 - labelPadding,
x1: x1,
y1: y1
}, y1 -= labelPadding) : "left" === this.options.labelPosition && x0 + labelPadding < x1 ? (parent.labelRect = {
x0: x0,
y0: y0,
x1: x0 + labelPadding,
y1: y1
}, x0 += labelPadding) : "right" === this.options.labelPosition && x1 - labelPadding > x0 && (parent.labelRect = {
x0: x1 - labelPadding,
y0: y0,
x1: x1,
y1: y1
}, x1 -= labelPadding));
const childGapWidth = this._getGapWidthByDepth(parent.depth + 1);
childGapWidth > 0 && (x0 -= childGapWidth / 2, x1 += childGapWidth / 2, y0 -= childGapWidth / 2,
y1 += childGapWidth / 2), this._splitNode(parent, x0, y0, x1, y1);
}
}, this.options = Object.assign({}, TreemapLayout.defaultOpionts, options);
const keyOption = this.options.nodeKey, keyFunc = (0, vutils_1.isFunction)(keyOption) ? keyOption : keyOption ? (0,
vgrammar_util_1.field)(keyOption) : null;
this._getNodeKey = keyFunc, this._splitNode = "squarify" === this.options.splitType ? (0,
squarify_1.generateSquarify)(this.options.aspectRatio) : null !== (_a = algorithms[this.options.splitType]) && void 0 !== _a ? _a : algorithms.binary,
this._maxDepth = -1;
}
layout(data, config) {
var _a;
if (!data || !data.length) return [];
const viewBox = "width" in config ? {
x0: 0,
x1: config.width,
y0: 0,
y1: config.height,
width: config.width,
height: config.height
} : {
x0: Math.min(config.x0, config.x1),
x1: Math.max(config.x0, config.x1),
y0: Math.min(config.y0, config.y1),
y1: Math.max(config.y0, config.y1),
width: Math.abs(config.x1 - config.x0),
height: Math.abs(config.y1 - config.y0)
}, nodes = [], res = (0, utils_1.calculateNodeValue)(data, nodes, 0, -1, null, this._getNodeKey, this.options.valueField);
if (this._maxDepth = res.maxDepth, res.sum <= 0) return [];
const root = {
flattenIndex: -1,
maxDepth: -1,
key: "-1",
depth: -1,
index: -1,
value: res.sum,
datum: null,
x0: viewBox.x0,
x1: viewBox.x1,
y0: viewBox.y0,
y1: viewBox.y1,
children: nodes
}, areaRatio = viewBox.width * viewBox.height / res.sum;
return this._filterByArea(root, areaRatio), this._layout(root), null !== (_a = root.children) && void 0 !== _a ? _a : [];
}
_filterChildren(node) {
const maxDepth = this.options.maxDepth;
if ((0, vutils_1.isNumber)(maxDepth) && maxDepth >= 0 && node.depth >= maxDepth) return !1;
const minChildrenVisibleArea = this.options.minChildrenVisibleArea;
if ((0, vutils_1.isNumber)(minChildrenVisibleArea) && Math.abs((node.x1 - node.x0) * (node.y1 - node.y0)) < minChildrenVisibleArea) return !1;
const minChildrenVisibleSize = this.options.minChildrenVisibleSize;
return !(0, vutils_1.isNumber)(minChildrenVisibleSize) || !(Math.abs(node.x1 - node.x0) < minChildrenVisibleSize || Math.abs(node.y1 - node.y0) < minChildrenVisibleSize);
}
_layout(parent) {
var _a;
this._filterChildren(parent) || (parent.children = null, parent.isLeaf = !0), this._layoutNode(parent),
(null === (_a = parent.children) || void 0 === _a ? void 0 : _a.length) && parent.children.forEach((child => {
var _a;
(null === (_a = null == child ? void 0 : child.children) || void 0 === _a ? void 0 : _a.length) ? this._layout(child) : this._layoutNode(child);
}));
}
}
exports.TreemapLayout = TreemapLayout, TreemapLayout.defaultOpionts = {
aspectRatio: (1 + Math.sqrt(5)) / 2,
gapWidth: 0,
labelPadding: 0,
labelPosition: "top",
splitType: "binary",
minVisibleArea: 10
};
//# sourceMappingURL=layout.js.map