@visactor/vgrammar-hierarchy
Version:
Layout of hierarchical data for VGrammar
137 lines (132 loc) • 7.63 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.SunburstLayout = void 0;
const vutils_1 = require("@visactor/vutils"), dice_1 = __importDefault(require("../treemap/dice")), vgrammar_util_1 = require("@visactor/vgrammar-util"), utils_1 = require("../utils"), keyMap = {
x0: "startAngle",
x1: "endAngle",
y0: "innerRadius",
y1: "outerRadius"
};
class SunburstLayout {
constructor(options) {
this._layoutNode = parent => {
if (parent.maxDepth = this._maxDepth, parent.children) {
const ir = this._parsedInnerRadius[parent.depth + 1], or = this._parsedOutterRadius[parent.depth + 1];
(0, dice_1.default)(parent, parent.startAngle, Math.min(ir, or), parent.endAngle, Math.max(ir, or), keyMap);
const labelOption = (0, vutils_1.isArray)(this.options.label) ? this.options.label[parent.depth + 1] : this.options.label;
parent.children.forEach((child => {
if (child.x = this._parsedCenter[0], child.y = this._parsedCenter[1], labelOption) return this._layoutLabel(child, (0,
vutils_1.isBoolean)(labelOption) ? {
align: "center",
rotate: "radial"
} : labelOption);
}));
}
}, this.options = options ? Object.assign({}, SunburstLayout.defaultOpionts, options) : Object.assign({}, SunburstLayout.defaultOpionts);
const keyOption = this.options.nodeKey, keyFunc = (0, vutils_1.isFunction)(keyOption) ? keyOption : keyOption ? (0,
vgrammar_util_1.field)(keyOption) : null;
this._getNodeKey = keyFunc, this._maxDepth = -1;
}
_parseRadius(viewBox, maxDepth) {
const cx = viewBox.x0 + (0, vgrammar_util_1.toPercent)(this.options.center[0], viewBox.width), cy = viewBox.y0 + (0,
vgrammar_util_1.toPercent)(this.options.center[1], viewBox.height), maxRadius = Math.min(viewBox.width / 2, viewBox.height / 2), innerRadius = this.options.innerRadius, outerRadius = this.options.outerRadius, isInnerArray = (0,
vutils_1.isArray)(innerRadius), parsedInnerRadius = isInnerArray ? innerRadius.map((entry => (0,
vgrammar_util_1.toPercent)(entry, maxRadius))) : (0, vgrammar_util_1.toPercent)(innerRadius, maxRadius), isOuterArray = (0,
vutils_1.isArray)(outerRadius), gapRadius = this.options.gapRadius, parsedOuterRadius = isOuterArray ? outerRadius.map((entry => (0,
vgrammar_util_1.toPercent)(entry, maxRadius))) : (0, vgrammar_util_1.toPercent)(outerRadius, maxRadius), rangeArr = (0,
vutils_1.range)(0, maxDepth + 1);
if (isInnerArray) this._parsedInnerRadius = rangeArr.map(((entry, index) => {
const ir = parsedInnerRadius[index];
return (0, vutils_1.isNil)(ir) ? maxRadius : ir;
})), this._parsedOutterRadius = rangeArr.map(((entry, index) => {
var _a, _b;
return isOuterArray ? null !== (_a = parsedOuterRadius[index]) && void 0 !== _a ? _a : maxRadius : index < maxDepth ? this._parsedInnerRadius[index + 1] - ((0,
vutils_1.isArray)(gapRadius) ? null !== (_b = gapRadius[index]) && void 0 !== _b ? _b : 0 : gapRadius) : parsedOuterRadius;
})); else if (isOuterArray) this._parsedOutterRadius = rangeArr.map(((entry, index) => (0,
vutils_1.isNil)(parsedOuterRadius[index]) ? maxRadius : parsedOuterRadius[index])),
this._parsedInnerRadius = rangeArr.map(((entry, index) => {
var _a;
return 0 === index ? parsedInnerRadius : this._parsedOutterRadius[index - 1] - ((0,
vutils_1.isArray)(gapRadius) ? null !== (_a = gapRadius[index]) && void 0 !== _a ? _a : 0 : gapRadius);
})); else {
const ir = (0, vgrammar_util_1.toPercent)(innerRadius, maxRadius), step = (parsedOuterRadius - ir) / (maxDepth + 1);
this._parsedInnerRadius = rangeArr.map(((entry, index) => ir + index * step)), this._parsedOutterRadius = rangeArr.map(((entry, index) => {
var _a;
return this._parsedInnerRadius[index] + step - ((0, vutils_1.isArray)(gapRadius) ? null !== (_a = gapRadius[index]) && void 0 !== _a ? _a : 0 : gapRadius);
}));
}
this._parsedCenter = [ cx, cy ], this._maxRadius = maxRadius;
}
layout(data, config) {
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)
};
if (!data || !data.length) return [];
const nodes = [], res = (0, utils_1.calculateNodeValue)(data, nodes, 0, -1, null, this._getNodeKey);
return this._parseRadius(viewBox, res.maxDepth), this._maxDepth = res.maxDepth,
this._layout(nodes, {
flattenIndex: -1,
maxDepth: -1,
key: "-1",
depth: -1,
index: -1,
value: res.sum,
datum: null,
children: nodes,
startAngle: this.options.startAngle,
endAngle: this.options.endAngle
}), nodes;
}
_layout(nodes, parent) {
this._layoutNode(parent), nodes.forEach((node => {
var _a;
(null === (_a = null == node ? void 0 : node.children) || void 0 === _a ? void 0 : _a.length) ? this._layout(node.children, node) : this._layoutNode(node);
}));
}
_layoutLabel(child, labelOption) {
var _a;
const angle = (child.startAngle + child.endAngle) / 2, r = ("start" === labelOption.align ? child.innerRadius : "end" === labelOption.align ? child.outerRadius : (child.innerRadius + child.outerRadius) / 2) + (null !== (_a = labelOption.offset) && void 0 !== _a ? _a : 0), pos = (0,
vutils_1.polarToCartesian)({
x: this._parsedCenter[0],
y: this._parsedCenter[1]
}, r, angle);
if (child.label = {
x: pos.x,
y: pos.y,
textBaseline: "middle"
}, "tangential" === labelOption.rotate) child.label.angle = angle - Math.PI / 2,
child.label.textAlign = "center", child.label.maxLineWidth = Math.abs(child.endAngle - child.startAngle) * r; else {
const uniformAngle = angle % (2 * Math.PI), formatAngle = uniformAngle < 0 ? uniformAngle + 2 * Math.PI : uniformAngle;
formatAngle > Math.PI / 2 && formatAngle < 1.5 * Math.PI ? (child.label.angle = formatAngle + Math.PI,
child.label.textAlign = "start" === labelOption.align ? "end" : "end" === labelOption.align ? "start" : "center") : (child.label.angle = formatAngle,
child.label.textAlign = labelOption.align), child.label.maxLineWidth = child.isLeaf ? void 0 : Math.abs(child.outerRadius - child.innerRadius);
}
}
}
exports.SunburstLayout = SunburstLayout, SunburstLayout.defaultOpionts = {
startAngle: Math.PI / 2,
endAngle: -3 * Math.PI / 2,
center: [ "50%", "50%" ],
gapRadius: 0,
innerRadius: 0,
outerRadius: "70%"
};
//# sourceMappingURL=layout.js.map