1 define(['app/ovsdb/lib/d3.min', 'app/ovsdb/OvsCore', 'app/ovsdb/matrix', 'underscore'], function(d3, OvsCore, Geom, _) {
14 function Graph(id, width, height) {
15 var x = d3.scale.linear()
19 var y = d3.scale.linear()
23 var tmp = d3.select(id).append("svg")
25 .attr('height', height)
27 .attr('class', 'layer_0');
29 tmp.append('svg:rect')
31 .attr('height', height)
32 .attr('fill', 'white');
34 root = tmp.call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 6]).on("zoom", zoom))
37 this.matrix = new Geom.Matrix();
39 // this._bg = tmp.insert('svg:polygon', ':first-child');
40 // this._bg.attr('id', 'ttt').attr('fill', 'rgb(250, 220, 220)').attr('stroke', 'rgb(250,0,0)');
42 drag = d3.behavior.drag()
46 .on("dragstart", dragstarted.bind(this))
47 .on("drag", dragmove.bind(this))
48 .on("dragend", dragend.bind(this));
50 // a layout for the bridge and his link
51 forceLayout = new d3.layout.force()
55 .size([width, height])
56 .on('tick', this.update.bind(this));
62 root.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
65 function dragstarted(d) {
66 d3.event.sourceEvent.stopPropagation();
70 function dragmove(d) {
85 // Define reusable svg item like box-shadow
88 var defs = d3.select('svg').insert('svg:defs', ':first-child');
89 var filter = defs.append('svg:filter').attr('id', 'selectNode').attr('x', '-20%').attr('y', '-20%').attr('width', '140%').attr('height', '140%');
90 filter.append('feGaussianBlur').attr('stdDeviation', '2').attr('result', 'coloredBlur');
91 var femerge = filter.append('feMerge');
92 femerge.append('feMergeNode').attr('in', 'coloredBlur');
93 femerge.append('feMergeNode').attr('in', 'SourceGraphic');
96 function sortLink(links) {
97 links.sort(function(a,b) {
98 if (a.source > b.source) {
100 } else if (a.source < b.source) {
104 if (a.target > b.target) {
107 if (a.target < b.target) {
116 function setLinkIndexAndNum(links) {
117 _.each(links, function(link, i) {
118 if (i != 0 && links[i].source == links[i-1].source &&
119 links[i].target == links[i-1].target) {
120 links[i].linkindex = links[i-1].linkindex + 1;
122 links[i].linkindex = 1;
127 // Properties to quick access and set nodes and links
128 Object.defineProperties(Graph.prototype, {
133 set: function(value) {
135 setLinkIndexAndNum(value);
137 forceLayout.links(value);
138 links = root.selectAll('links')
140 .enter().append('svg:path')
141 .attr('class', function(d) {
144 .attr('fill', 'none')
145 .attr('stroke-dasharray', function(d) {
148 .attr('stroke-width', function(d) {
151 .attr('stroke', function(d) {
160 set: function(value) {
161 var _this = this; // context change in callback function and we need both
163 forceLayout.nodes(value);
164 nodes = root.selectAll('.nodes')
166 .enter().append('svg:g')
168 .attr('class', function(d) {
169 return (d.node instanceof OvsCore.BridgeNode) ? 'bridge' : 'switch';
171 .on('click', function(d) {
172 if (d3.event.defaultPrevented) return;
173 _this.onNodeClick(d, nodes, links, this);
175 .on("mouseover", function(d) {
176 _this.onNodeOver(d, nodes, links, this);
178 .on("mouseout", function(d) {
179 _this.onNodeOut(d, nodes, links, this);
185 .attr('fill', 'black')
186 .attr('text-anchor', 'middle')
188 if (d.node instanceof OvsCore.BridgeNode)
189 return d.node.flowInfo.ip;
191 return d.node.otherLocalIp;
195 var layer = d3.selectAll('g.switch').append('svg:g').attr('class', 'switch').attr('transform', 'translate(-16 -16)');
196 layer.append('svg:rect').style('fill-rule', 'evenodd').attr('ry', '3.6808').attr('height', '28.901')
197 .attr('width', '28.784').style('stroke', '#002b69').attr('y', '0').attr('x', '0').style('stroke-width', "3px").style('fill', '#2a7fff');
198 layer.append('svg:path').attr('d', 'm27.043 6.2-5.0754 3.3764-.01018-2.1082-5.9209-.022.01773-2.6018 5.9031-.037.08118-2.1164z').style('fill', "#002b69");
199 layer.append('svg:path').attr('d', "m26.866 19.4-5.0754 3.3764-.01018-2.1082-5.9209-.022.01773-2.6018 5.9031-.037.08118-2.1164z").style('fill', "#002b69");
200 layer.append('svg:path').attr('d', "m3.0872 11.6 5.0754 3.3764.01018-2.1082 5.9209-.022-.01773-2.6018-5.9031-.037-.08118-2.1164z").style('fill', "#002b69");
201 layer.append('svg:path').attr('d', "m3.2639 24.8 5.0754 3.3764.01018-2.1082 5.9209-.022-.01773-2.6018-5.9031-.037-.08118-2.1164z").style('fill', "#002b69");
204 var layer = d3.selectAll('g.bridge').append('svg:g').attr('transform', 'translate(-16 -16)');
205 layer.append('svg:path').style('fill', '#d40000').style('stroke', '#ff0000').style('stroke-width', '0.10413094')
206 .style('stroke-linecap', 'round') //stroke-linejoin:round
207 .attr('d', 'm 2.9656662,3.4868 c 4.8978761,7.5117 16.2156478,6.1742 21.9929178,2.0807 l 2.154019,-1.5814 -0.08265,18.1941 -2.055088,2.0313 -24.17161713,0.055 -0.0255688,-18.6893 z');
208 layer.append('svg:path').style('fill', '#ff5555').style('stroke', '#ff0000').style('stroke-width', '0.10413094')
209 .style('stroke-linecap', 'round') //stroke-linejoin:round
210 .attr('d', 'm 0.83642587,5.5637 c 4.89787603,7.5117 18.41115613,4.1109 24.18842613,0.018 l -0.06627,18.6546 -24.12215693,0 z');
215 // Update the node and link position on the canvas
216 Graph.prototype.update = function(e) {
217 var k = 1 , //.1 * e.alpha
218 isDragging = (e !== null)
225 forceLayout.nodes().forEach(function(o) {
226 var i = (o.node instanceof OvsCore.OvsNode) ? 1 : 0;
227 o.y += (layerAnchor[i].y - o.y) * k;
228 o.x += (layerAnchor[i].x - o.x) * k;
233 links.attr('d', (function(d) {
234 var srcT = this.matrix.transformPoint(d.source.x, d.source.y),
235 targetT = this.matrix.transformPoint(d.target.x, d.target.y),
236 src = {x:srcT.elements[0],y:srcT.elements[1]},
237 tgt = {x:targetT.elements[0],y:targetT.elements[1]},
238 anchor1 = {}, anchor2 = {};
240 if (d.linkType === 'tunnel') {
241 // curve the line and arc it on a direction of a 90 degree vector
242 var perp = { x : -tgt.y, y: tgt.x};
243 var srcNorm = Math.sqrt(src.x * src.x + src.y * src.y);
244 var perpNorm = Math.sqrt(perp.x * perp.x + perp.y * perp.y);
245 var dy = (perp.y/perpNorm - src.y/srcNorm);
246 var dx = (perp.x/perpNorm - src.x/srcNorm);
248 anchor1 = {x: src.x - dx * 30, y: src.y - dy * 30 };
249 anchor2 = {x: tgt.x - dx * 30, y: tgt.y - dy * 30 };
251 // default strait line
256 return OvsCore.Util.String.Format('M{0},{1} C{2},{3} {4},{5} {6},{7}',
257 srcT.elements[0], srcT.elements[1],
258 anchor1.x, anchor1.y,
259 anchor2.x, anchor2.y,
260 targetT.elements[0], targetT.elements[1]
265 nodes.attr("transform", (function(d) {
266 var a = new Geom.Matrix.fromString(root.attr('transform'));
267 var tmp = Geom.Matrix.combine(a, this.matrix);
269 var transV = this.matrix.transformPoint(d.x, d.y);
270 var v = tmp.transformPoint(d.x, d.y);
275 nodePosCache[d.node.nodeId] = { pos: d.pos, fixed: d.fixed};
276 return "translate(" + transV.elements[0] + ',' + transV.elements[1] + ')';
280 Graph.prototype.setPosCache = function(cache) {
281 nodePosCache = cache;
284 // Apply few matrix transform to fake a perspective effet
285 Graph.prototype.applyPerspective = function(value) {
287 baseBB = root.node().getBBox();
290 var persMatrix = new Geom.Matrix()
291 .translate(-(baseBB.x + baseBB.width / 2), -(baseBB.y + baseBB.height / 2))
294 .translate(baseBB.x + baseBB.width / 2, baseBB.y + baseBB.height / 2);
296 this.matrix.transform = value ? this.matrix.transform.x(persMatrix.transform) : this.matrix.transform.x(persMatrix.transform.inverse());
298 var p1 = this.matrix.transformPoint(baseBB.x - padding, baseBB.y - padding);
299 var p2 = this.matrix.transformPoint(baseBB.x - padding, baseBB.y + baseBB.height + padding);
300 var p3 = this.matrix.transformPoint(baseBB.x + baseBB.width + padding, baseBB.y + baseBB.height + padding);
301 var p4 = this.matrix.transformPoint(baseBB.x + baseBB.width + padding, baseBB.y - padding);
303 this._bg.attr('points', OvsCore.Util.String.Format("{0},{1} {2},{3} {4},{5} {6},{7}",
314 p4.elements[1])).style('display', (value) ? 'block' : 'none');
319 function positionateBaseOnCache() {
321 forceLayout.nodes().forEach(function(d) {
322 var nodeCached = nodePosCache[d.node.nodeId];
323 d.px = d.x = nodeCached.pos.x;
324 d.py = d.y = nodeCached.pos.y;
325 d.fixed = nodeCached.fixed;
327 forceLayout.resume();
330 // start the force layout
331 Graph.prototype.start = function() {
332 forceLayout.linkStrength(function(link) {
333 if (link.linkType === 'bridgeOvsLink') {
339 if (_.size(nodePosCache) > 0) {
340 positionateBaseOnCache();
344 Graph.prototype.freeDOM = function() {
347 forceLayout.nodes([]);
348 forceLayout.links([]);
351 // Enable to manipulate attributes of the graph group node
352 Graph.prototype.attr = function(name, value) {
353 return root.attr(name, value);
356 // Enable the monipulate the css of the graph group node
357 Graph.prototype.style = function(name, value) {
358 return root.style(name, value);
361 Graph.prototype.selectAll = function(a) {
362 return root.selectAll(a);
366 Graph.prototype.onNodeOver = _.noop;
367 Graph.prototype.onNodeOut = _.noop;
368 Graph.prototype.onNodeClick = _.noop;