提交 7a7dc4e2 编写于 作者: P pah100

treemap update

上级 edc86f78
......@@ -5,6 +5,7 @@ define(function (require) {
require('./treemap/TreemapSeries');
require('./treemap/TreemapView');
require('./treemap/TreemapAction');
echarts.registerVisualCoding('chart', require('./treemap/treemapVisual'));
......
/**
* @file Treemap action
*/
define(function(require) {
// var zrUtil = require('zrender/core/util');
var echarts = require('../../echarts');
// var modelUtil = require('../../util/model');
var actionInfo = {
type: 'zoomToNode',
update: 'updateView'
};
echarts.registerAction(actionInfo, function (payload, ecModel) {
// do nothing
});
});
\ No newline at end of file
......@@ -18,7 +18,7 @@ define(function (require) {
/**
* @override
*/
update: function (ecModel, api, event) {
update: function (ecModel, api, payload) {
var ecWidth = api.getWidth();
var ecHeight = api.getHeight();
......@@ -41,6 +41,7 @@ define(function (require) {
);
this.squarify(seriesModel.getViewRoot(), options);
}, this);
},
......@@ -75,14 +76,13 @@ define(function (require) {
}
// Considering border and gap
var borderWidth = node.modelGet('itemStyle.normal.borderWidth');
var halfGapWidth = node.modelGet('itemStyle.normal.gapWidth') / 2;
var itemStyleModel = node.getModel('itemStyle.normal');
var borderWidth = itemStyleModel.get('borderWidth');
var halfGapWidth = itemStyleModel.get('gapWidth') / 2;
var layoutOffset = borderWidth - halfGapWidth;
console.log(borderWidth, halfGapWidth, layoutOffset);
width -= 2 * layoutOffset;
height -= 2 * layoutOffset;
width = mathMax(width - 2 * layoutOffset, 0);
height = mathMax(height - 2 * layoutOffset, 0);
node.setLayout({borderWidth: borderWidth}, true);
......@@ -149,19 +149,20 @@ console.log(borderWidth, halfGapWidth, layoutOffset);
? b.getValue() - a.getValue() : a.getValue() - b.getValue();
});
}
var sum = 0;
for (var i = 0, len = viewChildren.length; i < len; i++) {
sum += viewChildren[i].getValue();
}
var totalArea = width * height;
var nodeModel = node.getModel();
// Filter by thredshold.
for (var i = viewChildren.length - 1; i >= 0; i--) {
var value = viewChildren[i].getValue();
if (value / sum * totalArea < node.modelGet('itemStyle.normal.visibleMin')) {
viewChildren.slice(i, 1);
if (value / sum * totalArea < nodeModel.get('visibleMin')) {
viewChildren.splice(i, 1);
sum -= value;
}
else {
......
......@@ -27,6 +27,7 @@ define(function(require) {
squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio
root: '',
colorDimension: 'value', // 默认第一个维度。
zoomStep: 10, // 0表示不zoom。
breadcrumb: {
show: true,
itemStyle: {
......@@ -83,12 +84,8 @@ define(function(require) {
// FIXME
// sereis.mergeOption 的 getInitData是否放在merge后,从而能直接获取merege后的结果而非手动判断。
var levels = option.levels || (this.option || {}).levels || [];
var levelModels = [];
zrUtil.each(levels, function (levelDefine) {
levelModels.push(new Model(levelDefine));
}, this);
return Tree.createTree(data, this, levelModels).list;
return Tree.createTree(data, this, levels).list;
},
/**
......
define(function(require) {
define(function(require) {
var zrUtil = require('zrender/core/util');
var graphic = require('../../util/graphic');
var layout = require('../../util/layout');
var DataDiffer = require('../../data/DataDiffer');
var Group = graphic.Group;
var Rect = graphic.Rect;
......@@ -15,20 +16,61 @@ define(function(require) {
*/
init: function () {
/**
* @private
* @type {module:zrender/container/Group}
*/
this._drawGroup;
/**
* @private
* @type {Object.<string, Array.<module:zrender/container/Group>>}
*/
this._storage;
/**
* @private
* @type {module:echarts/data/Tree}
*/
this._oldTree;
},
/**
* @override
*/
render: function (seriesModel, ecModel, api) {
var contentGroup = new Group();
this.seriesModel = seriesModel;
this._renderNodes(seriesModel.getViewRoot(), contentGroup);
var drawGroup = this._drawGroup;
if (!drawGroup) {
drawGroup = this._drawGroup = new Group({
onclick: zrUtil.bind(this._onClick, this)
});
this.group.add(drawGroup);
}
this.group.add(contentGroup);
var thisTree = seriesModel.getData().tree;
var oldTree = this._oldTree;
this._storage = [];
var thisStorage = {nodeGroup: [], background: [], content: []};
var oldStorage = this._storage;
var renderNode = zrUtil.bind(this._renderNode, this);
var viewRoot = seriesModel.getViewRoot();
dualTravel(
[thisTree.root],
oldTree ? [oldTree.root] : [],
drawGroup,
viewRoot === thisTree.root
);
this._oldTree = thisTree;
this._storage = thisStorage;
layout.positionGroup(
contentGroup,
drawGroup,
{
x: seriesModel.get('x'),
y: seriesModel.get('y'),
......@@ -40,62 +82,187 @@ define(function(require) {
height: api.getHeight()
}
);
function dualTravel(thisViewChildren, oldViewChildren, parentGroup, inView) {
// When update, 'this' and 'old' are in the same tree.
if (thisViewChildren === oldViewChildren
|| !thisViewChildren.length
|| !oldViewChildren.length
) {
zrUtil.each(thisViewChildren, function (child, index) {
processNode(index, index);
});
}
// When setOption, data changed, use data differ.
else {
// Diff hierarchically (diff only in each subtree, but not whole).
// because, consistency of view is important.
(new DataDiffer(
oldViewChildren,
thisViewChildren,
function (node) {
// Identify by name or raw index.
return node.name != null ? node.name : node.getRawIndex();
}
))
.add(function (newIndex) {
processNode(newIndex);
})
.update(function (newIndex, oldIndex) {
processNode(newIndex, oldIndex);
})
.remove(function (oldIndex) {
processNode(null, oldIndex);
});
}
function processNode(newIndex, oldIndex) {
var thisNode = newIndex != null ? thisViewChildren[newIndex] : null;
var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null;
// Whether under viewRoot.
var subInView = inView || thisNode === viewRoot;
// If not under viewRoot, only remove.
if (!subInView) {
thisNode = null;
}
var group = renderNode(
thisNode, oldNode, parentGroup, thisStorage, oldStorage
);
dualTravel(
thisNode && thisNode.viewChildren || [],
oldNode && oldNode.viewChildren || [],
group,
subInView
);
}
}
},
/**
* @private
*/
_renderNodes: function (node, parentGroup) {
var layout = node.getLayout();
_renderNode: function (thisNode, oldNode, parentGroup, thisStorage, oldStorage) {
// FIXME
// 考虑 viewRoot ??????????????
var thisRawIndex = thisNode && thisNode.getRawIndex();
var oldRawIndex = oldNode && oldNode.getRawIndex();
if (!thisNode) {
oldNode && oldStorage && parentGroup.remove(oldStorage.nodeGroup[oldRawIndex]);
return;
}
var layout = thisNode.getLayout();
var thisWidth = layout.width;
var thisHeight = layout.height;
var viewChildren = node.viewChildren;
var group = new Group();
var group = makeGraphic('nodeGroup', Group);
group.position = [layout.x, layout.y];
parentGroup.add(group);
var itemStyleModel = thisNode.getModel('itemStyle.normal');
var borderColor = itemStyleModel.get('borderColor') || itemStyleModel.get('gapColor');
// Background
var borderColor = node.modelGet('itemStyle.normal.borderColor')
|| node.modelGet('itemStyle.normal.gapColor');
group.add(new Rect({
shape: {x: 0, y: 0, width: thisWidth, height: thisHeight},
style: {fill: borderColor}
}));
var background = makeGraphic('background', Rect);
background.setShape({x: 0, y: 0, width: thisWidth, height: thisHeight});
background.setStyle({fill: borderColor});
group.add(background);
var thisViewChildren = thisNode.viewChildren;
// No children, render content.
if (!viewChildren || !viewChildren.length) {
if (!thisViewChildren || !thisViewChildren.length) {
var borderWidth = layout.borderWidth;
group.add(new Rect({
shape: {
x: borderWidth,
y: borderWidth,
width: thisWidth - 2 * borderWidth,
height: thisHeight - 2 * borderWidth
},
style: {
fill: node.getVisual('color', true)
}
// ?????????? text
}));
var content = makeGraphic('content', Rect);
content.setShape({
x: borderWidth,
y: borderWidth,
width: Math.max(thisWidth - 2 * borderWidth, 0),
height: Math.max(thisHeight - 2 * borderWidth, 0)
});
content.setStyle({
fill: thisNode.getVisual('color', true)
});
group.add(content);
}
// Render children recursively.
// Remove old content for children rendering.
else {
zrUtil.each(viewChildren, function (child) {
this._renderNodes(child, group);
}, this);
var content = (oldNode && oldStorage)
? oldStorage.content[oldRawIndex] : null;
content && group.remove(content);
}
parentGroup.add(group);
return group;
function makeGraphic(storage, Ctor) {
return thisStorage[storage][thisRawIndex] = (oldNode && oldStorage)
? oldStorage[storage][oldRawIndex]
: new Ctor({});
}
},
/**
* @override
*/
remove: function () {
// var group = this.group;
// group.remove(this._polyline);
// group.remove(this._polygon);
// this._dataSymbol.remove(true);
this.group.removeAll();
this._drawGroup = null;
this._storage = null;
this._oldTree = null;
},
/**
* @private
*/
_onClick: function (e) {
var zoomStep = this.seriesModel.get('zoomToNode');
var targetInfo = this.findTarget(e.offsetX, e.offsetY);
if (!targetInfo) {
return;
}
if (zoomStep <= 0) {
return;
}
this.api.dispatch({
type: 'zoomToNode',
from: this.uid,
targetNode: ''
});
},
/**
* @public
* @return {Object} info If not found, return undefined;
* @return {number} info.node Target node.
* @return {number} info.offsetX x refer to target node.
* @return {number} info.offsetY y refer to target node.
*/
findTarget: function (x, y) {
var targetInfo;
var viewRoot = this.seriesModel.getViewRoot();
viewRoot.eachNode({attr: 'viewChildren', order: 'preorder'}, function (node) {
var nodeGroup = this._storage.nodeGroup[node.getRawIndex()];
var point = nodeGroup.transformCoordToLocal(x, y);
if (nodeGroup.getBoundingRect().contain(point[0], point[1])) {
targetInfo = {node: node, offsetX: point[0], offsetY: point[1]};
}
else {
return false; // Suppress visit subtree.
}
}, this);
return targetInfo;
}
});
......
......@@ -20,15 +20,19 @@ define(function (require) {
!rootVisual.color && (rootVisual.color = globalColorList);
travelTree(seriesModel.getViewRoot(), rootVisual, seriesModel);
travelTree(
seriesModel.getData().tree.root,
rootVisual,
seriesModel,
seriesModel.getViewRoot().getAncestors()
);
});
};
function travelTree(node, designatedVisual, seriesModel) {
function travelTree(node, designatedVisual, seriesModel, viewRootAncestors) {
var visuals = buildVisuals(node, designatedVisual, seriesModel);
var viewChildren = node.viewChildren;
if (!viewChildren || !viewChildren.length) {
// Apply visual to this node.
node.setVisual('color', calculateColor(visuals, node));
......@@ -37,8 +41,11 @@ define(function (require) {
var mappingWrap = buildVisualMapping(node, visuals, viewChildren);
// Designate visual to children.
zrUtil.each(viewChildren, function (child, index) {
var childVisual = mapVisual(node, visuals, child, index, mappingWrap);
travelTree(child, childVisual, seriesModel);
// If higher than viewRoot, only ancestors of viewRoot is needed to visit.
if (child.depth >= viewRootAncestors.length || child === viewRootAncestors[child.depth]) {
var childVisual = mapVisual(node, visuals, child, index, mappingWrap);
travelTree(child, childVisual, seriesModel, viewRootAncestors);
}
});
}
}
......@@ -48,12 +55,11 @@ define(function (require) {
zrUtil.each(VISUAL_LIST, function (visualName) {
// Priority: thisNode > thisLevel > parentNodeDesignated
var path = 'itemStyle.normal.' + visualName;
var visualValue = node.modelGet(path); // Ignore parent
var visualValue = node.getModel('itemStyle.normal').get(visualName, true); // Ignore parent
if (visualValue == null) {
visualValue = retrieve(path, seriesModel.option.levels[node.depth]);
var levelModel = node.getLevelModel();
visualValue = levelModel ? levelModel.get(visualName, true) : null;
}
if (visualValue == null) {
visualValue = designatedVisual[visualName];
......@@ -104,7 +110,7 @@ define(function (require) {
var mappingType = mappingVisualName === 'color'
? (
node.modelGet('itemStyle.normal.colorMapping') === 'byValue'
node.getModel('itemStyle.normal').get('colorMapping') === 'byValue'
? 'color' : 'colorByIndex'
)
: mappingVisualName;
......@@ -125,7 +131,7 @@ define(function (require) {
}
function calculateDataExtent(node, viewChildren) {
var dimension = node.modelGet('colorDimension');
var dimension = node.getModel().get('colorDimension');
// The same as area dimension.
if (dimension === 'value') {
......@@ -151,7 +157,7 @@ define(function (require) {
if (mappingWrap) {
var mapping = mappingWrap.mapping;
var value = mapping.type === 'colorByIndex'
? index : child.getValue(node.modelGet('colorDimension'));
? index : child.getValue(node.getModel().get('colorDimension'));
childVisuals[mappingWrap.visualName] = mapping.mapValueToVisual(value);
}
......@@ -173,20 +179,4 @@ define(function (require) {
}
}
// FIXME
// 这段代码是否和model中的复用?
function retrieve(path, base) {
path = path.split('.');
var obj = base;
for (var i = 0; i < path.length; i++) {
obj = obj && obj[path[i]];
if (obj == null) {
break;
}
}
return obj;
}
});
\ No newline at end of file
......@@ -47,6 +47,8 @@ define(function(require) {
/**
* Reference to list item.
* Do not persistent dataIndex outside,
* besause it may be changed by list.
*
* @type {Object}
* @readOnly
......@@ -72,43 +74,44 @@ define(function(require) {
this.hostTree = hostTree;
},
/**
* @param {(module:echarts/data/Tree~TreeNode|Object)} child
*/
add: function (child) {
var children = this.children;
if (child.parentNode === this) {
return;
}
children.push(child);
child.parentNode = this;
this.hostTree._nodes.push(child);
},
/**
* Travel this subtree (include this node).
* Usage:
* node.eachNode(function () { ... }); // preorder
* node.eachNode('preorder', function () { ... }); // preorder
* node.eachNode('postorder', function () { ... }); // postorder
* node.eachNode(
* {order: 'postorder', attr: 'viewChildren'},
* function () { ... }
* ); // postorder
*
* @param {string} order 'preorder' or 'postorder'
* @param {Function} cb
* @param {(Object|string)} options If string, means order.
* @param {string=} options.order 'preorder' or 'postorder'
* @param {string=} options.attr 'children' or 'viewChildren'
* @param {Function} cb If in preorder and return false,
* its subtree will not be visited.
* @param {Object} [context]
*/
eachNode: function (order, cb, context) {
if (typeof order === 'function') {
eachNode: function (options, cb, context) {
if (typeof options === 'function') {
context = cb;
cb = order;
order = 'preorder';
cb = options;
options = null;
}
order === 'preorder' && cb.call(context, this);
options = options || {};
if (zrUtil.isString(options)) {
options = {order: options};
}
for (var i = 0; i < this.children.length; i++) {
this.children[i].eachNode(order, cb, context);
var order = options.order || 'preorder';
var children = this[options.attr || 'children'];
var suppressVisitSub;
order === 'preorder' && (suppressVisitSub = cb.call(context, this));
for (var i = 0; !suppressVisitSub && i < children.length; i++) {
children[i].eachNode(options, cb, context);
}
order === 'postorder' && cb.call(context, this);
......@@ -134,7 +137,7 @@ define(function(require) {
/**
* @param {string} name
* @return module:echarts/data/Tree~TreeNode
* @return {module:echarts/data/Tree~TreeNode}
*/
getNodeByName: function (name) {
if (this.name === name) {
......@@ -148,6 +151,21 @@ define(function(require) {
}
},
/**
* @param {boolean} includeSelf Default false.
* @return {Array.<module:echarts/data/Tree~TreeNode>} order: [root, child, grandchild, ...]
*/
getAncestors: function (includeSelf) {
var ancestors = [];
var node = includeSelf ? this : this.parentNode;
while (node) {
ancestors.push(node);
node = node.parentNode;
}
ancestors.reverse();
return ancestors;
},
/**
* @param {string=} dimension Default 'value'. can be 'a', 'b', 'c', 'd', 'e'.
* @return {number} Value.
......@@ -172,22 +190,22 @@ define(function(require) {
},
/**
* @param {string} path
* @return {module:echarts/model/Model}
*/
modelGet: function (path) {
var itemModel = this.hostTree.list.getItemModel(this.dataIndex);
var parentModel = itemModel.parentModel;
var levelModel = (this.levelModels || [])[this.depth];
var value = itemModel.get(path, true);
if (value == null && levelModel) {
value = levelModel.get(path, true);
}
if (value == null && parentModel) {
value = parentModel.get(path);
}
getModel: function (path) {
var hostTree = this.hostTree;
var itemModel = hostTree.list.getItemModel(this.dataIndex);
var levelModel = (hostTree.levelModels || [])[this.depth];
return value;
return itemModel.getModel(path, (levelModel || hostTree.hostModel).getModel(path));
},
/**
* @return {module:echarts/model/Model}
*/
getLevelModel: function () {
return (this.hostTree.levelModels || [])[this.depth];
},
/**
......@@ -206,6 +224,13 @@ define(function(require) {
*/
getVisual: function (key, ignoreParent) {
return this.hostTree.list.getItemVisual(this.dataIndex, key, ignoreParent);
},
/**
* @public
*/
getRawIndex: function () {
return this.hostTree.list.getRawIndex(this.dataIndex);
}
});
......@@ -213,9 +238,10 @@ define(function(require) {
* @constructor
* @alias module:echarts/data/Tree
* @param {string=} name Root name
* @param {Array.<modules:echarts/model/Model>} levelModels
* @param {module:echarts/model/Model} hostModel
* @param {Array.<Object>} levelOptions
*/
function Tree(name, levelModels) {
function Tree(name, hostModel, levelOptions) {
/**
* @type {module:echarts/data/Tree~TreeNode}
* @readOnly
......@@ -229,6 +255,7 @@ define(function(require) {
this.list;
/**
* Index of each item is the same as the raw index of coresponding list item.
* @private
* @type {Array.<module:echarts/data/Tree~TreeNode}
*/
......@@ -237,8 +264,18 @@ define(function(require) {
/**
* @private
* @readOnly
* @type {Array.<module:echarts/model/Model}
*/
this.levelModels = levelModels;
this.hostModel = hostModel;
/**
* @private
* @readOnly
* @type {Array.<module:echarts/model/Model}
*/
this.levelModels = zrUtil.map(levelOptions || [], function (levelDefine) {
return new Model(levelDefine, hostModel);
});
}
Tree.prototype = {
......@@ -253,19 +290,19 @@ define(function(require) {
* node.eachNode(function () { ... }); // preorder
* node.eachNode('preorder', function () { ... }); // preorder
* node.eachNode('postorder', function () { ... }); // postorder
* node.eachNode(
* {order: 'postorder', attr: 'viewChildren'},
* function () { ... }
* ); // postorder
*
* @param {string} order 'preorder' or 'postorder'
* @param {(Object|string)} options If string, means order.
* @param {string=} options.order 'preorder' or 'postorder'
* @param {string=} options.attr 'children' or 'viewChildren'
* @param {Function} cb
* @param {Object} [context]
*/
eachNode: function(order, cb, context) {
if (typeof order === 'function') {
context = cb;
cb = order;
order = 'preorder';
}
this.root.eachNode(order, cb, context);
eachNode: function(options, cb, context) {
this.root.eachNode(options, cb, context);
},
/**
......@@ -315,20 +352,21 @@ define(function(require) {
* @static
* @param {Array.<Object>} data
* @param {module:echarts/model/Model} hostModel
* @param {Array.<module:echarts/model/Model} levelModels
* @param {Array.<Object>} levelOptions
* @return module:echarts/data/Tree
*/
Tree.createTree = function (data, hostModel, levelModels) {
var listData = [];
Tree.createTree = function (data, hostModel, levelOptions) {
var tree = new Tree('', hostModel, levelOptions);
var tree = new Tree('', levelModels);
var listData = [];
var rootNode = tree.root;
function buildHierarchy(dataNode, parentNode) {
listData.push(dataNode);
var node = new TreeNode(dataNode.name, listData.length - 1, tree);
parentNode.add(node);
addChild(node, parentNode);
var children = dataNode.children;
if (children) {
......@@ -353,6 +391,24 @@ define(function(require) {
return tree;
};
/**
* It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote,
* so this function is not ready and not necessary to be public.
*
* @param {(module:echarts/data/Tree~TreeNode|Object)} child
*/
function addChild(child, node) {
var children = node.children;
if (child.parentNode === node) {
return;
}
children.push(child);
child.parentNode = node;
node.hostTree._nodes.push(child);
}
function createList(listData, hostModel) {
var firstValue = listData[0] && listData[0].value;
var dimSize = zrUtil.isArray(firstValue) ? firstValue.length : 1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册