1
0
Fork 0
arangodb/html/admin/js/graphViewer/graph/edgeShaper.js

381 lines
9.7 KiB
JavaScript

/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */
/*global _, $, d3*/
/*global ColourMapper*/
////////////////////////////////////////////////////////////////////////////////
/// @brief Graph functionality
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
/*
* flags example format:
* {
* shape: {
* type: EdgeShaper.shapes.ARROW
* }
* label: "key" \\ function(node)
* actions: {
* click: function(edge)
* }
* }
*
*
*/
function EdgeShaper(parent, flags, idfunc) {
"use strict";
var self = this,
edges = [],
toplevelSVG,
visibleLabels = true,
followEdge = {},
followEdgeG,
idFunction = function(d) {
return d.source._id + "-" + d.target._id;
},
noop = function (line, g) {
},
colourMapper = new ColourMapper(),
events,
addUpdate,
addShape = noop,
addLabel = noop,
addColor = noop,
unbindEvents = function() {
events = {
click: noop,
dblclick: noop,
mousedown: noop,
mouseup: noop,
mousemove: noop,
mouseout: noop,
mouseover: noop
};
addUpdate = noop;
},
getCorner = function(s, t) {
return Math.atan2(t.y - s.y, t.x - s.x) * 180 / Math.PI;
},
getDistance = function(s, t) {
return Math.sqrt(
(t.y - s.y)
* (t.y - s.y)
+ (t.x - s.x)
* (t.x - s.x)
);
},
addEvents = function (line, g) {
_.each(events, function (func, type) {
g.on(type, func);
});
},
bindEvent = function (type, func) {
if (type === "update") {
addUpdate = func;
} else if (events[type] === undefined) {
throw "Sorry Unknown Event " + type + " cannot be bound.";
} else {
events[type] = func;
}
},
addPosition = function (line, g) {
g.attr("transform", function(d) {
return "translate("
+ d.source.position.x + ", "
+ d.source.position.y + ")"
+ "rotate("
+ getCorner(d.source.position, d.target.position)
+ ")";
});
line.attr("x2", function(d) {
return getDistance(d.source.position, d.target.position);
});
},
addQue = function (line, g) {
addShape(line, g);
if (visibleLabels) {
addLabel(line, g);
}
addColor(line, g);
addEvents(line, g);
addPosition(line, g);
},
shapeEdges = function (newEdges) {
if (newEdges !== undefined) {
edges = newEdges;
}
var line,
g = self.parent
.selectAll(".link")
.data(edges, idFunction);
// Append the group and class to all new
g.enter()
.append("g")
.attr("class", "link") // link is CSS class that might be edited
.attr("id", idFunction);
// Remove all old
g.exit().remove();
// Remove all elements that are still included.
g.selectAll("* > *").remove();
line = g.append("line");
addQue(line, g);
},
updateEdges = function () {
var g = self.parent.selectAll(".link"),
line = g.select("line");
addPosition(line, g);
addUpdate(g);
},
parseShapeFlag = function (shape) {
$("svg defs marker#arrow").remove();
switch (shape.type) {
case EdgeShaper.shapes.NONE:
addShape = noop;
break;
case EdgeShaper.shapes.ARROW:
addShape = function (line, g) {
line.attr("marker-end", "url(#arrow)");
};
if (toplevelSVG.selectAll("defs")[0].length === 0) {
toplevelSVG.append("defs");
}
toplevelSVG
.select("defs")
.append("marker")
.attr("id", "arrow")
.attr("refX", "22")
.attr("refY", "5")
.attr("markerUnits", "strokeWidth")
.attr("markerHeight", "10")
.attr("markerWidth", "10")
.attr("orient", "auto")
.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z");
break;
default:
throw "Sorry given Shape not known!";
}
},
parseLabelFlag = function (label) {
if (_.isFunction(label)) {
addLabel = function (line, g) {
g.append("text") // Append a label for the edge
.attr("text-anchor", "middle") // Define text-anchor
.text(label);
};
} else {
addLabel = function (line, g) {
g.append("text") // Append a label for the edge
.attr("text-anchor", "middle") // Define text-anchor
.text(function(d) {
// Which value should be used as label
return d._data[label] !== undefined ? d._data[label] : "";
});
};
}
addUpdate = function (edges) {
edges.select("text")
.attr("transform", function(d) {
return "translate("
+ getDistance(d.source.position, d.target.position) / 2
+ ", -3)";
});
};
},
parseActionFlag = function (actions) {
if (actions.reset !== undefined && actions.reset) {
unbindEvents();
}
_.each(actions, function(func, type) {
if (type !== "reset") {
bindEvent(type, func);
}
});
},
parseColorFlag = function (color) {
$("svg defs #gradientEdgeColor").remove();
switch (color.type) {
case "single":
addColor = function (line, g) {
line.attr("stroke", color.stroke);
};
break;
case "gradient":
if (toplevelSVG.selectAll("defs")[0].length === 0) {
toplevelSVG.append("defs");
}
var gradient = toplevelSVG
.select("defs")
.append("linearGradient")
.attr("id", "gradientEdgeColor");
gradient.append("stop")
.attr("offset", "0")
.attr("stop-color", color.source);
gradient.append("stop")
.attr("offset", "0.4")
.attr("stop-color", color.source);
gradient.append("stop")
.attr("offset", "0.6")
.attr("stop-color", color.target);
gradient.append("stop")
.attr("offset", "1")
.attr("stop-color", color.target);
addColor = function (line, g) {
line.attr("stroke", "url(#gradientEdgeColor)");
line.attr("y2", "0.0000000000000001");
};
break;
case "attribute":
addColor = function (line, g) {
g.attr("stroke", function(e) {
return colourMapper.getColour(e._data[color.key]);
});
};
break;
default:
throw "Sorry given colour-scheme not known";
}
},
parseConfig = function(config) {
if (config.shape !== undefined) {
parseShapeFlag(config.shape);
}
if (config.label !== undefined) {
parseLabelFlag(config.label);
}
if (config.actions !== undefined) {
parseActionFlag(config.actions);
}
if (config.color !== undefined) {
parseColorFlag(config.color);
}
};
self.parent = parent;
unbindEvents();
toplevelSVG = parent;
while (toplevelSVG[0][0] && toplevelSVG[0][0].ownerSVGElement) {
toplevelSVG = d3.select(toplevelSVG[0][0].ownerSVGElement);
}
if (flags === undefined) {
flags = {
color: {
type: "single",
stroke: "#686766"
}
};
}
if (flags.color === undefined) {
flags.color = {
type: "single",
stroke: "#686766"
};
}
parseConfig(flags);
if (_.isFunction(idfunc)) {
idFunction = idfunc;
}
followEdgeG = toplevelSVG.append("g");
/////////////////////////////////////////////////////////
/// Public functions
/////////////////////////////////////////////////////////
self.changeTo = function(config) {
parseConfig(config);
shapeEdges();
updateEdges();
};
self.drawEdges = function (edges) {
shapeEdges(edges);
updateEdges();
};
self.updateEdges = function () {
updateEdges();
};
self.reshapeEdges = function() {
shapeEdges();
};
self.activateLabel = function(toogle) {
if (toogle) {
visibleLabels = true;
} else {
visibleLabels = false;
}
shapeEdges();
};
self.addAnEdgeFollowingTheCursor = function(x, y) {
followEdge = followEdgeG.append("line");
followEdge.attr("stroke", "black")
.attr("id", "connectionLine")
.attr("x1", x)
.attr("y1", y)
.attr("x2", x)
.attr("y2", y);
return function(x, y) {
followEdge.attr("x2", x).attr("y2", y);
};
};
self.removeCursorFollowingEdge = function() {
followEdge.remove();
followEdge = {};
};
}
EdgeShaper.shapes = Object.freeze({
"NONE": 0,
"ARROW": 1
});