提交 242830b3 编写于 作者: L lang

Update graph

上级 21f1da3b
......@@ -6,9 +6,12 @@ define(function (require) {
require('./graph/GraphSeries');
require('./graph/GraphView');
echarts.registerProcessor('filter', require('./graph/categoryFilter'));
echarts.registerVisualCoding('chart', zrUtil.curry(
require('../visual/symbol'), 'graph', 'circle', null
));
echarts.registerVisualCoding('chart', require('./graph/categoryVisual'));
echarts.registerLayout(require('./graph/simpleLayout'));
});
\ No newline at end of file
......@@ -3,22 +3,61 @@ define(function (require) {
'use strict';
var SeriesModel = require('../../model/Series');
var List = require('../../data/List');
var zrUtil = require('zrender/core/util');
var createGraphNodeLinks = require('../helper/createGraphFromNodeLinks');
var createGraphFromNodeEdge = require('../helper/createGraphFromNodeEdge');
return SeriesModel.extend({
type: 'series.graph',
init: function (option) {
SeriesModel.prototype.init.apply(this, arguments);
this._udpateCategoriesData();
// Provide data for legend select
this.legendDataProvider = function () {
return this._categoriesData;
};
},
mergeOption: function (option) {
SeriesModel.prototype.mergeOption.apply(this, arguments);
this._udpateCategoriesData();
},
_udpateCategoriesData: function () {
var categories = zrUtil.map(this.option.categories || [], function (category) {
// Data must has value
return category.value != null ? category : zrUtil.extend({value : 0}, category);
});
var categoriesData = new List(['value'], this);
categoriesData.initData(categories);
this._categoriesData = categoriesData;
},
getInitialData: function (option, ecModel) {
if (option.data && option.links) {
var graph = createGraphNodeLinks(
option.data, option.links, this, true
);
var edges = option.edges || option.links;
var nodes = option.data || option.nodes;
if (nodes && edges) {
var graph = createGraphFromNodeEdge(nodes, edges, this, true);
return graph.data;
}
},
/**
* Get category model by index
* @param {number} id Category index
* @return {module:echarts/model/Model}
*/
getCategoriesData: function () {
return this._categoriesData;
},
defaultOption: {
zlevel: 0,
z: 2,
......@@ -31,14 +70,30 @@ define(function (require) {
y: 'center',
x2: null,
y2: null,
width: '80%',
height: '80%',
width: '90%',
height: '90%',
symbol: 'circle',
symbolSize: 10,
roam: true,
// Symbol size scale ratio in roam
nodeScaleRatio: 0.6,
// Line width scale ratio in roam
edgeScaleRatio: 0.1,
// categories: [],
// data: []
// Or
// nodes: []
//
// links: []
// Or
// edges: []
label: {
normal: {
show: false
......
......@@ -2,30 +2,39 @@ define(function (require) {
var SymbolDraw = require('../helper/SymbolDraw');
var LineDraw = require('../helper/LineDraw');
var RoamController = require('../../component/helper/RoamController');
require('../../echarts').extendChartView({
type: 'graph',
init: function () {
init: function (ecModel, api) {
var symbolDraw = new SymbolDraw();
var lineDraw = new LineDraw();
var group = this.group;
this.group.add(symbolDraw.group);
this.group.add(lineDraw.group);
group.add(symbolDraw.group);
group.add(lineDraw.group);
this._symbolDraw = symbolDraw;
this._lineDraw = lineDraw;
this._controller = new RoamController(api.getZr(), group);
},
render: function (seriesModel, ecModel, api) {
var coordSys = seriesModel.coordinateSystem;
// Only support view and geo coordinate system
if (coordSys.type !== 'geo' && coordSys.type !== 'view') {
return;
}
var data = seriesModel.getData();
this._model = seriesModel;
var symbolDraw = this._symbolDraw;
var lineDraw = this._lineDraw;
var coordSys = seriesModel.coordinateSystem;
symbolDraw.updateData(
data, seriesModel, api, false
);
......@@ -35,9 +44,57 @@ define(function (require) {
seriesModel, api, false
);
var scalarScale = coordSys && coordSys.getScalarScale();
// Save the original lineWidth
data.graph.eachEdge(function (edge) {
edge.__lineWidth = edge.getModel('lineStyle.normal').get('width');
});
var group = this.group;
group.attr({
position: coordSys.position,
scale: coordSys.scale
});
this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');
this._edgeScaleRatio = seriesModel.get('edgeScaleRatio');
this._updateNodeAndLinkScale();
var controller = this._controller;
controller.rect = coordSys.getViewRect();
controller.disable();
seriesModel.get('roam') && controller.enable();
controller.on('zoom', this._updateNodeAndLinkScale, this);
},
_updateNodeAndLinkScale: function () {
var seriesModel = this._model;
var data = seriesModel.getData();
var group = this.group;
var nodeScaleRatio = this._nodeScaleRatio;
var edgeScaleRatio = this._edgeScaleRatio;
// Assume scale aspect is 1
var groupScale = group.scale[0];
var nodeScale = (groupScale - 1) * nodeScaleRatio + 1;
var edgeScale = (groupScale - 1) * edgeScaleRatio + 1;
var invScale = [
nodeScale / groupScale,
nodeScale / groupScale
];
data.eachItemGraphicEl(function (el, idx) {
el.scale[0] = el.scale[1] = scalarScale;
el.attr('scale', invScale);
});
data.graph.eachEdge(function (edge) {
edge.getGraphicEl().setStyle(
'lineWidth',
edge.__lineWidth * edgeScale / groupScale
);
});
},
......
define(function (require) {
return function (ecModel) {
var legendModel = ecModel.getComponent('legend');
if (!legendModel) {
return;
}
ecModel.eachSeriesByType('graph', function (graphSeries) {
var categoriesData = graphSeries.getCategoriesData();
var data = graphSeries.getData();
var categoryNames = categoriesData.mapArray(categoriesData.getName);
data.filterSelf(function (idx) {
var model = data.getItemModel(idx);
var category = model.getShallow('category');
if (category != null) {
if (typeof category === 'number') {
category = categoryNames[category];
}
return legendModel.isSelected(category);
}
return true;
});
}, this);
};
});
\ No newline at end of file
define(function (require) {
return function (ecModel) {
var offset = 0;
var colorList = ecModel.get('color');
ecModel.eachSeriesByType('graph', function (seriesModel) {
var categoriesData = seriesModel.getCategoriesData();
var data = seriesModel.getData();
var categoryNameIdxMap = {};
categoriesData.each(function (idx) {
categoryNameIdxMap[categoriesData.getName(idx)] = idx;
var itemModel = categoriesData.getItemModel(idx);
var rawIdx = categoriesData.getRawIndex(idx);
var color = itemModel.get('itemStyle.normal.color')
|| colorList[(offset + rawIdx) % colorList.length];
categoriesData.setItemVisual(idx, 'color', color);
});
offset += categoriesData.count();
// Assign category color to visual
if (categoriesData.count()) {
data.each(function (idx) {
var model = data.getItemModel(idx);
var category = model.getShallow('category');
if (category != null) {
if (typeof category === 'string') {
category = categoryNameIdxMap[category];
}
data.setItemVisual(
idx, 'color',
categoriesData.getItemVisual(category, 'color')
);
}
});
}
});
};
});
\ No newline at end of file
define(function (require) {
// FIXME Where to create the simple view layout
// FIXME Where to create the simple view coordinate system
var View = require('../../coord/View');
var bbox = require('zrender/core/bbox');
var layout = require('../../util/layout');
......@@ -36,18 +36,60 @@ define(function (require) {
bbox.fromPoints(positions, min, max);
var bbWidth = max[0] - min[0];
var bbHeight = max[1] - min[1];
var aspect = bbWidth / bbHeight;
var viewWidth = viewRect.width;
var viewHeight = viewRect.height;
// Uniform scale on width and height
if (viewWidth / viewHeight > aspect) {
viewWidth = aspect * viewHeight;
}
else {
viewHeight = aspect * viewWidth;
}
// Adjust x and y
viewRect.x += (viewRect.width - viewWidth) / 2;
viewRect.y += (viewRect.height - viewHeight) / 2;
coordSys.setBoundingRect(
min[0], min[1], max[0] - min[0], max[1] - min[1]
min[0], min[1], bbWidth, bbHeight
);
coordSys.setViewRect(
viewRect.x, viewRect.y, viewRect.width, viewRect.height
viewRect.x, viewRect.y, viewWidth, viewHeight
);
graph.eachNode(function (node) {
node.setLayout(
coordSys.dataToPoint(positions[node.dataIndex])
);
node.setLayout(positions[node.dataIndex]);
});
graph.eachEdge(function (edge) {
var curveness = edge.getModel().get('lineStyle.normal.curveness');
var p1 = edge.node1.getLayout();
var p2 = edge.node2.getLayout();
var cp1;
var inv = 1;
if (curveness > 0) {
cp1 = [
(p1[0] + p2[0]) / 2 - inv * (p1[1] - p2[1]) * curveness,
(p1[1] + p2[1]) / 2 - inv * (p2[0] - p1[0]) * curveness
];
}
var shape = {
x1: p1[0],
y1: p1[1],
x2: p2[0],
y2: p2[1]
};
if (cp1) {
shape.cpx1 = cp1[0];
shape.cpy1 = cp1[1];
}
edge.setLayout(shape);
});
}
});
......
......@@ -12,7 +12,7 @@ define(function (require) {
* @param {module:echarts/data/List} data
* @param {module:echarts/model/Series} seriesModel
* @param {module:echarts/ExtensionAPI} api
* @param {boolean} enableAnimation
* @param {boolean} [enableAnimation=false]
*/
lineDrawProto.updateData = function (data, seriesModel, api, enableAnimation) {
var group = this.group;
......@@ -21,16 +21,22 @@ define(function (require) {
data.diff(oldData)
.add(function (idx) {
var shape = data.getItemLayout(idx);
var line = new graphic[shape.cpx1 != null ? 'BezierCurve' : 'Line']({
shape: shape
});
if (shape) {
var line = new graphic[shape.cpx1 != null ? 'BezierCurve' : 'Line']({
shape: shape
});
data.setItemGraphicEl(idx, line);
group.add(line);
data.setItemGraphicEl(idx, line);
group.add(line);
}
})
.update(function (newIdx, oldIdx) {
var line = oldData.getItemGraphicEl(oldIdx);
var shape = data.getItemLayout(newIdx);
if (!shape) {
group.remove(line);
return;
}
if (shape.cpx1 != null && shape.type === 'line') {
var oldShape = line.shape;
line = new graphic.BezierCurve({
......@@ -42,6 +48,9 @@ define(function (require) {
});
}
api.updateGraphicEl(line, shape);
data.setItemGraphicEl(newIdx, line);
group.add(line);
})
.remove(function (idx) {
group.remove(oldData.getItemGraphicEl(idx));
......
......@@ -64,7 +64,7 @@ define(function (require) {
* @param {module:echarts/data/List} data
* @param {module:echarts/model/Series} seriesModel
* @param {module:echarts/ExtensionAPI} api
* @param {boolean} enableAnimation
* @param {boolean} [enableAnimation=false]
* @param {Array.<boolean>} [ignoreMap]
*/
symbolProto.updateData = function (
......
......@@ -5,13 +5,16 @@ define(function (require) {
var linkList = require('../../data/helper/linkList');
var zrUtil = require('zrender/core/util');
return function (nodes, links, hostModel, directed) {
return function (nodes, edges, hostModel, directed) {
var graph = new Graph(directed);
for (var i = 0; i < nodes.length; i++) {
graph.addNode(nodes[i].name, i);
graph.addNode(nodes[i].id || nodes[i].name, i);
}
for (var i = 0; i < links.length; i++) {
var link = links[i];
var linkNameList = [];
for (var i = 0; i < edges.length; i++) {
var link = edges[i];
linkNameList[i] = link.id || link.source + ' - ' + link.target;
graph.addEdge(link.source, link.target, i);
}
......@@ -20,7 +23,12 @@ define(function (require) {
var dimSize = zrUtil.isArray(firstValue) ? firstValue.length : 1;
var dimensionNames = ['value', 'a', 'b', 'c', 'd', 'e', 'f'].slice(0, dimSize);
var nodeData = new List(dimensionNames, hostModel);
var edgeData = new List(['weight'], hostModel);
nodeData.initData(nodes);
edgeData.initData(edges, linkNameList, 'weight');
graph.edgeData = edgeData;
linkList.linkToGraph(nodeData, graph);
......
......@@ -13,14 +13,10 @@ define(function (require) {
updateSelectedMap: function () {
var option = this.option;
// FIXME Keep selected status?
var dataOptMap = {};
zrUtil.each(option.data, function (dataOpt) {
this._dataOptMap = zrUtil.reduce(option.data, function (dataOptMap, dataOpt) {
dataOptMap[dataOpt.name] = dataOpt;
});
this._dataOptMap = dataOptMap;
return dataOptMap;
}, {});
},
/**
* @param {string} name
......
......@@ -46,14 +46,12 @@ define(function (require) {
*/
seriesGroup: [],
init: function (option, parentModel, ecModel, dependentModels, seriesIndex) {
init: function (option) {
option = this._fillOption(option);
this.option = option;
SeriesModel.prototype.init.call(
this, option, parentModel, ecModel, dependentModels, seriesIndex
);
SeriesModel.prototype.init.apply(this, arguments);
this.updateSelectedMap();
},
......
......@@ -12,16 +12,14 @@ define(function(require) {
type: 'series.pie',
init: function (option, parentModel, ecModel, dependentModels, seriesIndex) {
SeriesModel.prototype.init.call(
this, option, parentModel, ecModel, dependentModels, seriesIndex
);
init: function (option) {
SeriesModel.prototype.init.apply(this, arguments);
// Enable legend selection for each data item
// Use a function instead of direct access because data reference may changed
this.legendDataProvider = function () {
return this._dataBeforeProcessed;
}
};
this.updateSelectedMap();
},
......
......@@ -5,9 +5,9 @@ define(function (require) {
var colorList = ecModel.get('color');
ecModel.eachSeriesByTypeAll('pie', function (seriesModel) {
var dataAll = seriesModel.getDataAll();
if (!ecModel.isSeriesFiltered(seriesModel)) {
var data = seriesModel.getData();
var dataAll = seriesModel.getDataAll();
data.each(function (idx) {
var itemModel = data.getItemModel(idx);
var rawIdx = data.getRawIndex(idx);
......@@ -18,8 +18,7 @@ define(function (require) {
data.setItemVisual(idx, 'color', color);
});
}
offset += dataAll.count();
});
}
};
});
\ No newline at end of file
/**
* @module echarts/component/helper/RoamController
*/
define(function (require) {
var Eventful = require('zrender/mixin/Eventful');
......@@ -78,6 +82,10 @@ define(function (require) {
}
/**
* @alias module:echarts/component/helper/RoamController
* @constructor
* @mixin {module:zrender/mixin/Eventful}
*
* @param {module:zrender/zrender~ZRender} zr
* @param {module:zrender/Element} target
* @param {module:zrender/core/BoundingRect} rect
......@@ -101,23 +109,27 @@ define(function (require) {
var mouseupHandler = bind(mouseup, this);
var mousewheelHandler = bind(mousewheel, this);
zr.on('mousedown', mousedownHandler);
zr.on('mousemove', mousemoveHandler);
zr.on('mouseup', mouseupHandler);
zr.on('mousewheel', mousewheelHandler);
Eventful.call(this);
this.dispose = function () {
this.enable = function () {
zr.on('mousedown', mousedownHandler);
zr.on('mousemove', mousemoveHandler);
zr.on('mouseup', mouseupHandler);
zr.on('mousewheel', mousewheelHandler);
};
this.disable = this.dispose = function () {
zr.off('mousedown', mousedownHandler);
zr.off('mousemove', mousemoveHandler);
zr.off('mouseup', mouseupHandler);
zr.off('mousewheel', mousewheelHandler);
}
};
this.enable();
this.isDragging = function () {
return this._dragging;
}
};
}
zrUtil.mixin(RoamController, Eventful);
......
......@@ -251,15 +251,26 @@ define(function(require) {
graphProto.update = function () {
var data = this.data;
var edgeData = this.edgeData;
var nodes = this.nodes;
var edges = this.edges;
for (var i = 0, len = nodes.length; i < len; i++) {
nodes[i].dataIndex = -1;
}
for (var i = 0, len = data.count(); i < len; i++) {
nodes[data.getRawIndex(i)].dataIndex = i;
}
// Update edge
for (var i = 0, len = edges.length; i < len; i++) {
edges[i].dataIndex = -1;
}
for (var i = 0, len = edgeData.count(); i < len; i++) {
if (edges[i].node1.dataIndex >= 0 && edges[i].node2.dataIndex >= 0) {
edges[edgeData.getRawIndex(i)].dataIndex = i;
}
}
};
/**
......@@ -390,14 +401,14 @@ define(function(require) {
return itemModel.getModel(path);
};
var createGraphDataProxyMixin = function (dataName) {
var createGraphDataProxyMixin = function (hostName, dataName) {
return {
/**
* @param {string=} [dimension='value'] Default 'value'. can be 'a', 'b', 'c', 'd', 'e'.
* @return {number}
*/
getValue: function (dimension) {
return this.hostGraph[dataName].get(dimension || 'value', this.dataIndex);
return this[hostName][dataName].get(dimension || 'value', this.dataIndex);
},
/**
......@@ -406,7 +417,7 @@ define(function(require) {
*/
setVisual: function (key, value) {
this.dataIndex >= 0
&& this.hostGraph[dataName].setItemVisual(this.dataIndex, key, value);
&& this[hostName][dataName].setItemVisual(this.dataIndex, key, value);
},
/**
......@@ -414,7 +425,7 @@ define(function(require) {
* @return {boolean}
*/
getVisual: function (key, ignoreParent) {
return this.hostGraph[dataName].getItemVisual(this.dataIndex, key, ignoreParent);
return this[hostName][dataName].getItemVisual(this.dataIndex, key, ignoreParent);
},
/**
......@@ -423,27 +434,34 @@ define(function(require) {
*/
setLayout: function (layout, merge) {
this.dataIndex >= 0
&& this.hostGraph[dataName].setItemLayout(this.dataIndex, layout, merge);
&& this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge);
},
/**
* @return {Object}
*/
getLayout: function () {
return this.hostGraph[dataName].getItemLayout(this.dataIndex);
return this[hostName][dataName].getItemLayout(this.dataIndex);
},
/**
* @return {module:zrender/Element}
*/
getGraphicEl: function () {
return this[hostName][dataName].getItemGraphicEl(this.dataIndex);
},
/**
* @return {number}
*/
getRawIndex: function () {
return this.hostGraph[dataName].getRawIndex(this.dataIndex);
return this[hostName][dataName].getRawIndex(this.dataIndex);
}
};
};
zrUtil.mixin(Node, createGraphDataProxyMixin('data'));
zrUtil.mixin(Edge, createGraphDataProxyMixin('edgeData'));
zrUtil.mixin(Node, createGraphDataProxyMixin('hostGraph', 'data'));
zrUtil.mixin(Edge, createGraphDataProxyMixin('hostGraph', 'edgeData'));
Graph.Node = Node;
Graph.Edge = Edge;
......
......@@ -116,7 +116,7 @@ define(function (require) {
each(optionPreprocessorFuncs, function (preProcess) {
preProcess(option);
})
});
var ecModel = this._model;
if (!ecModel || notMerge) {
......
......@@ -141,22 +141,22 @@ define(function(require) {
name: label,
source: sourceId,
target: targetId,
linkStyle: {
lineStyle: {
normal: {}
}
};
var linkStyle = edge.linkStyle.normal;
var lineStyle = edge.lineStyle.normal;
var vizThicknessDom = getChildByTagName(edgeDom, 'viz:thickness');
var vizColorDom = getChildByTagName(edgeDom, 'viz:color');
// var vizShapeDom = getChildByTagName(edgeDom, 'viz:shape');
if (vizThicknessDom) {
linkStyle.thickness = parseFloat(vizThicknessDom.getAttribute('value'));
lineStyle.width = parseFloat(vizThicknessDom.getAttribute('value'));
}
if (vizColorDom) {
linkStyle.color = 'rgb(' + [
lineStyle.color = 'rgb(' + [
getAttr(vizColorDom, 'r') | 0,
getAttr(vizColorDom, 'g') | 0,
getAttr(vizColorDom, 'b') | 0
......
......@@ -38,19 +38,36 @@
$.get('./data/les-miserables.gexf', function (xml) {
var graph = gexf.parse(xml);
var categories = [];
for (var i = 0; i < 9; i++) {
categories[i] = {
name: '类目' + i
};
}
graph.nodes.forEach(function (node) {
node.itemStyle = null;
node.value = 1;
node.value = node.symbolSize;
node.label.normal.show = node.symbolSize > 30;
node.category = node.attributes['modularity_class'];
});
chart.setOption({
tooltip: {},
legend: [{
data: categories.map(function (a) {return a.name})
}],
dataRange: {
max: 100,
inRange: {
colorS: [1, 0.3]
}
},
series : [
{
name: 'les-miserables',
name: 'Les Miserables',
type: 'graph',
data: graph.nodes,
links: graph.links,
categories: categories,
label: {
normal: {
position: 'right'
......
......@@ -48,6 +48,8 @@
stack: 'all',
symbol: 'circle',
symbolSize: 10,
selectedMode: 'single',
selectedOffset: 30,
data:[
{value:335, name:'直接访问'},
{value:310, name:'邮件营销'},
......
......@@ -134,7 +134,9 @@
type : 'time',
boundaryGap : false,
axisLine: {onZero: false},
splitNumber: 20
splitNumber: 20,
min: 'dataMin',
max: 'dataMax'
}
],
yAxis : [
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册