springy/demo.html

209 lines
5.0 KiB
HTML
Raw Normal View History

2010-04-16 01:42:51 +00:00
<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script src="raphael-min.js"></script>
<script src="springy.js"></script>
<script>
var graph = new Graph();
2010-04-16 14:20:55 +00:00
// generate a random graph
var n = [];
for (var i=0; i<15; i += 1)
{
n[i] = graph.newNode({label: ""+i});
}
for (i=0; i<20; i += 1)
{
var i1 = Math.floor(Math.random() * n.length);
var i2 = i1;
while (i1 === i2) {
i2 = Math.floor(Math.random() * n.length);
}
var n1 = n[i1];
var n2 = n[i2];
var e = graph.newEdge(n1, n2);
var colors = ['#00A0B0', '#6A4A3C', '#CC333F', '#EB6841', '#EDC951', '#7DBE3C'];
e.data.stroke = colors[Math.floor(Math.random() * colors.length)];
}
2010-04-16 01:42:51 +00:00
// -----------
var width = 800;
var height = 600;
2010-04-16 14:20:55 +00:00
var zoom = 40.0;
2010-04-16 01:42:51 +00:00
// convert to/from screen coordinates
toScreen = function(p) {
2010-04-16 14:20:55 +00:00
return new Vector(p.x * zoom + width/2.0, p.y * zoom + height/2.0);
2010-04-16 01:42:51 +00:00
};
fromScreen = function(s) {
2010-04-16 14:20:55 +00:00
return new Vector((s.x - width/2.0) / zoom, (s.y - height/2.0) / zoom);
2010-04-16 01:42:51 +00:00
};
var paper = Raphael(10, 10, width, height);
var layout = new Layout.ForceDirected(graph, 500.0, 300.0, 0.5);
2010-04-16 14:20:55 +00:00
var boxWidth = 30;
2010-04-16 01:42:51 +00:00
var boxHeight = 20;
2010-04-16 14:20:55 +00:00
var renderer = new Renderer(1, layout,
2010-04-16 01:42:51 +00:00
function clear()
{
paper.clear();
var r = paper.rect(0,0,width-1,height-1);
2010-04-16 14:20:55 +00:00
r.attr({"fill": "#FFFFFF", "stroke": "none"});
2010-04-16 01:42:51 +00:00
},
function drawEdge(edge, p1, p2)
{
2010-04-16 14:20:55 +00:00
var x1 = toScreen(p1).x;
var y1 = toScreen(p1).y;
var x2 = toScreen(p2).x;
var y2 = toScreen(p2).y;
var direction = new Vector(x2-x1, y2-y1);
var normal = direction.normal().normalise();
var from = graph.getEdges(edge.source, edge.target);
var to = graph.getEdges(edge.target, edge.source);
var total = from.length + to.length;
var n = from.indexOf(edge);
var spacing = 6.0;
var totalWidth = total * spacing;
// Figure out how far off centre the line should be drawn
var offset = normal.multiply(-((total - 1) * spacing)/2.0 + (n * spacing));
var stroke = typeof(edge.data.stroke) !== 'undefined' ? edge.data.stroke : "#000000";
var s1 = toScreen(p1).add(offset);
var s2 = toScreen(p2).add(offset);
var intersection = intersect_line_box(s1, s2, {x: x2-boxWidth/2.0, y: y2-boxHeight/2.0}, boxWidth, boxHeight);
var c2 = paper.path(["M", s1.x, s1.y, "L", intersection.x, intersection.y]);
c2.attr({stroke: stroke, "stroke-width": 2});
var arrow = paper.path(["M", -7, 4, "L", 0, 0, "L", 7, 0, "L", 0, 0, "L", -7, -4, "L", -5, 0, "z"]);
arrow.rotate(Math.atan2(y2 - y1, x2 - x1) * (180.0 / Math.PI), 0, 0);
arrow.translate(intersection.x, intersection.y);
arrow.attr({fill: stroke, stroke: "none"});
2010-04-16 01:42:51 +00:00
},
function drawNode(node, p)
{
var fill = typeof(node.data.fill) !== 'undefined' ? node.data.fill : "#FFFFFF";
var s = toScreen(p);
2010-04-16 14:20:55 +00:00
var rect = paper.rect(s.x - boxWidth/2.0, s.y - boxHeight/2.0, boxWidth, boxHeight, 4);
rect.attr({fill: fill, "stroke-width": 2});
2010-04-16 01:42:51 +00:00
if (typeof(node.data.label) !== 'undefined')
{
var text = paper.text(s.x, s.y, node.data.label);
2010-04-16 14:20:55 +00:00
text.attr({
"font-family": "Helvetica Neue",
"font-size": "12px",
"font-weight": "bold",
});
2010-04-16 01:42:51 +00:00
}
}
);
renderer.start();
// half-assed drag and drop
var selected = null;
jQuery('svg').mousedown(function(e){
var pos = jQuery(this).position();
var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
selected = layout.nearest(p);
selected.oldm = selected.point.m;
selected.olddata = selected.node.data;
selected.node.data = jQuery.extend(true, {}, selected.node.data); // deep copy
selected.point.m = 1000.0;
selected.node.data.fill = '#EEEEEE';
});
jQuery('svg').mousemove(function(e){
if (selected !== null)
{
var pos = jQuery(this).position();
var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
selected.point.p.x = p.x;
selected.point.p.y = p.y;
2010-04-16 14:20:55 +00:00
renderer.start();
2010-04-16 01:42:51 +00:00
}
});
jQuery(window).bind('mouseup',function(e){
if (selected !== null)
{
selected.node.data = selected.olddata;
}
selected = null;
});
// helpers for figuring out where to draw arrows
function intersect_line_line(p1, p2, p3, p4)
{
var denom = ((p4.y - p3.y)*(p2.x - p1.x) - (p4.x - p3.x)*(p2.y - p1.y));
// lines are parallel
if (denom === 0) {
return false;
}
var ua = ((p4.x - p3.x)*(p1.y - p3.y) - (p4.y - p3.y)*(p1.x - p3.x)) / denom;
var ub = ((p2.x - p1.x)*(p1.y - p3.y) - (p2.y - p1.y)*(p1.x - p3.x)) / denom;
if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
return false;
}
return {
x: p1.x + ua * (p2.x - p1.x),
y: p1.y + ua * (p2.y - p1.y)
};
}
function intersect_line_box(p1, p2, p3, w, h)
{
var tl = {x: p3.x, y: p3.y};
var tr = {x: p3.x + w, y: p3.y};
var bl = {x: p3.x, y: p3.y + h};
var br = {x: p3.x + w, y: p3.y + h};
var result;
if (result = intersect_line_line(p1, p2, tl, tr)) { return result; } // top
if (result = intersect_line_line(p1, p2, tr, br)) { return result; } // right
if (result = intersect_line_line(p1, p2, br, bl)) { return result; } // bottom
if (result = intersect_line_line(p1, p2, bl, tl)) { return result; } // left
return false;
}
</script>
</body>
</html>