/** * @fileOverview tree facets * @author dxq613@gmail.com */ const Base = require('./base'); const Util = require('../util'); const assign = Util.assign; class Tree extends Base { getDefaultCfg() { const cfg = super.getDefaultCfg(); cfg.type = 'tree'; cfg.line = { lineWidth: 1, stroke: '#ddd' }; cfg.lineSmooth = false; return cfg; } generateFacets(data) { const self = this; const fields = self.fields; if (!fields.length) { throw 'Please specify for the fields for facet!'; } const rst = []; const root = self.getRootFacet(data); // if (self.showRoot) { rst.push(root); // } root.children = self.getChildFacets(data, 1, rst); self.setRegion(rst); return rst; } getRootFacet(data) { const self = this; const facet = { type: self.type, rows: self.getRows(), rowIndex: 0, colIndex: 0, colValue: self.rootTitle, data }; return facet; } getRows() { return this.fields.length + 1; } // get child getChildFacets(data, level, arr) { const self = this; const fields = self.fields; const length = fields.length; if (length < level) { return; } const rst = []; const field = fields[level - 1]; const values = self.getFieldValues(field, data); values.forEach((value, index) => { const conditions = [ { field, value, values } ]; const filter = self.getFilter(conditions); const subData = data.filter(filter); if (subData.length) { const facet = { type: self.type, colValue: value, colField: field, colIndex: index, rows: self.getRows(), rowIndex: level, data: subData, children: self.getChildFacets(subData, level + 1, arr) }; rst.push(facet); arr.push(facet); } }); return rst; } // 设置 region setRegion(facets) { const self = this; self.forceColIndex(facets); facets.forEach(facet => { facet.region = self.getRegion(facet.rows, facet.cols, facet.colIndex, facet.rowIndex); }); } // set column index of facets forceColIndex(facets) { const self = this; const leafs = []; let index = 0; facets.forEach(facet => { if (self.isLeaf(facet)) { leafs.push(facet); facet.colIndex = index; index++; } }); leafs.forEach(facet => { facet.cols = leafs.length; }); const maxLevel = self.fields.length; for (let i = maxLevel - 1; i >= 0; i--) { const levelFacets = self.getFacetsByLevel(facets, i); // var yIndex = maxLevel - i; for (let j = 0; j < levelFacets.length; j++) { const facet = levelFacets[j]; if (!self.isLeaf(facet)) { facet.originColIndex = facet.colIndex; facet.colIndex = self.getRegionIndex(facet.children); facet.cols = leafs.length; } } } } // get facet use level getFacetsByLevel(facets, level) { const rst = []; facets.forEach(facet => { if (facet.rowIndex === level) { rst.push(facet); } }); return rst; } // set facets region getRegion(rows, cols, xIndex, yIndex) { const xWidth = 1 / cols; // x轴方向的每个分面的偏移 const yWidth = 1 / rows; // y轴方向的每个分面的偏移 const start = { x: xWidth * xIndex, y: yWidth * yIndex }; const end = { x: start.x + xWidth, y: start.y + yWidth * 2 / 3 // 预留1/3的空隙,方便添加连接线 }; return { start, end }; } // if the facet has children , make it's column index in the middle of it's children getRegionIndex(children) { const first = children[0]; const last = children[children.length - 1]; return (last.colIndex - first.colIndex) / 2 + first.colIndex; } // is a leaf without children isLeaf(facet) { return !facet.children || !facet.children.length; } setXAxis(xField, axes, facet) { // 当是最后一行或者下面没有 view 时文本不显示 if (facet.rowIndex !== facet.rows - 1) { axes[xField].label = null; axes[xField].title = null; } } // 设置 y 坐标轴的文本、title 是否显示 setYAxis(yField, axes, facet) { if (facet.originColIndex !== 0 && facet.colIndex !== 0) { axes[yField].title = null; axes[yField].label = null; } } // 绘制完成后 onPaint() { super.onPaint(); this.group.clear(); if (this.facets && this.line) { this.drawLines(this.facets, this.group); } } drawLines(facets, group) { const self = this; const lineGroup = group.addGroup(); facets.forEach(facet => { if (!self.isLeaf(facet)) { const children = facet.children; self._addFacetLines(facet, children, lineGroup); } }); } // add lines with it's children _addFacetLines(facet, children, group) { const self = this; const view = facet.view; const region = view.getViewRegion(); const start = { x: region.start.x + (region.end.x - region.start.x) / 2, y: region.start.y }; children.forEach(subFacet => { const subRegion = subFacet.view.getViewRegion(); const end = { x: subRegion.start.x + (subRegion.end.x - subRegion.start.x) / 2, y: subRegion.end.y }; const middle1 = { x: start.x, y: start.y + (end.y - start.y) / 2 }; const middle2 = { x: end.x, y: middle1.y }; self._drawLine([ start, middle1, middle2, end ], group); }); } _getPath(points) { const self = this; const path = []; const smooth = self.lineSmooth; if (smooth) { path.push([ 'M', points[0].x, points[0].y ]); path.push([ 'C', points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y ]); } else { points.forEach((point, index) => { if (index === 0) { path.push([ 'M', point.x, point.y ]); } else { path.push([ 'L', point.x, point.y ]); } }); } return path; } // draw line width points _drawLine(points, group) { const self = this; const path = self._getPath(points); const line = self.line; group.addShape('path', { attrs: assign({ path }, line) }); } } module.exports = Tree;