From 407d07723df7d2e472d0928f79227ed4cb5dc218 Mon Sep 17 00:00:00 2001 From: emkael Date: Thu, 19 Feb 2015 10:46:55 +0100 Subject: * initial commit --- main.js | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 main.js (limited to 'main.js') diff --git a/main.js b/main.js new file mode 100644 index 0000000..bc488f4 --- /dev/null +++ b/main.js @@ -0,0 +1,252 @@ +// +// main.js +// +// A project template for using arbor.js +// + +(function($){ + + var Renderer = function(canvas){ + var canvas = $(canvas).get(0) + var ctx = canvas.getContext("2d"); + var particleSystem + + var that = { + init:function(system){ + // + // the particle system will call the init function once, right before the + // first frame is to be drawn. it's a good place to set up the canvas and + // to pass the canvas size to the particle system + // + // save a reference to the particle system for use in the .redraw() loop + particleSystem = system + + // inform the system of the screen dimensions so it can map coords for us. + // if the canvas is ever resized, screenSize should be called again with + // the new dimensions + particleSystem.screenSize(canvas.width, canvas.height) + particleSystem.screenPadding(50) // leave an extra 80px of whitespace per side + + // set up some event handlers to allow for node-dragging + that.initMouseHandling() + }, + + redraw:function(){ + // + // redraw will be called repeatedly during the run whenever the node positions + // change. the new positions for the nodes can be accessed by looking at the + // .p attribute of a given node. however the p.x & p.y values are in the coordinates + // of the particle system rather than the screen. you can either map them to + // the screen yourself, or use the convenience iterators .eachNode (and .eachEdge) + // which allow you to step through the actual node objects but also pass an + // x,y point in the screen's coordinate system + // + ctx.fillStyle = "white" + ctx.fillRect(0,0, canvas.width, canvas.height) + + // JESTEM BOGIEM PRZEKSZTAŁCEŃ 2D :O + var drawArrow = function(pt1, pt2, length, style) { + var angle = Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x); + ctx.fillStyle = style; + ctx.setLineDash([]); + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(pt2.x, pt2.y); + ctx.lineTo(pt2.x - length * Math.cos(angle - Math.PI/6), pt2.y - length * Math.sin(angle - Math.PI/6)); + ctx.lineTo(pt2.x - length * (Math.cos(angle + Math.PI/6) + Math.cos(angle - Math.PI/6))/3, + pt2.y - length * (Math.sin(angle - Math.PI/6) + Math.sin(angle + Math.PI/6))/3); + ctx.lineTo(pt2.x - length * Math.cos(angle + Math.PI/6), pt2.y - length * Math.sin(angle + Math.PI/6)); + ctx.lineTo(pt2.x, pt2.y); + ctx.stroke(); + ctx.fill(); + } + + particleSystem.eachEdge(function(edge, pt1, pt2){ + // edge: {source:Node, target:Node, length:#, data:{}} + // pt1: {x:#, y:#} source position in screen coords + // pt2: {x:#, y:#} target position in screen coords + + ctx.strokeStyle = edge.data.style || "rgba(0,0,0, .333)" + ctx.lineWidth = edge.data.width || 1 + ctx.setLineDash(edge.data.dash || []) + + // draw a line from pt1 to pt2 + ctx.beginPath() + ctx.moveTo(pt1.x, pt1.y) + + if (edge.data.arrow) { + if (edge.target.data.imageObject) { + var destination = {}; + var intersectingSegment = undefined; + if (pt1.x != pt2.x) { + var lineSlope = (pt1.y - pt2.y) / (pt1.x - pt2.x); + var width = edge.target.data.imageObject.width; + var height = edge.target.data.imageObject.height; + if ((-height / 2 <= lineSlope * width / 2) + && (lineSlope * width / 2 <= height / 2)) { + if (pt1.x > pt2.x) { + intersectingSegment = [ + { x: pt2.x + width / 2, + y: pt2.y - height / 2 }, + { x: pt2.x + width / 2, + y: pt2.y + height / 2 } + ]; + } + else { + intersectingSegment = [ + { x: pt2.x - width / 2, + y: pt2.y - height / 2 }, + { x: pt2.x - width / 2, + y: pt2.y + height / 2 } + ]; + } + } + if ((-width / 2 <= (height / 2) / lineSlope) + && ((height / 2) / lineSlope <= width / 2)) { + if (pt1.y > pt2.y) { + intersectingSegment = [ + { x: pt2.x - width / 2, + y: pt2.y + height / 2 }, + { x: pt2.x + width / 2, + y: pt2.y + height / 2 } + ]; + } + else { + intersectingSegment = [ + { x: pt2.x - width / 2, + y: pt2.y - height / 2 }, + { x: pt2.x + width / 2, + y: pt2.y - height / 2 } + ]; + } + } + } + else { + intersectingSegment = (pt1.y > pt2.y) ? [ + { x: pt2.x - width / 2, + y: pt2.y - height / 2 }, + { x: pt2.x + width / 2, + y: pt2.y - height / 2 } + ] : [ + { x: pt2.x - width / 2, + y: pt2.y + height / 2 }, + { x: pt2.x + width / 2, + y: pt2.y + height / 2 } + ]; + } + if (intersectingSegment) { + var intersectAngle = ((intersectingSegment[1].y - intersectingSegment[0].y) * (pt2.x - pt1.x)) + - ((intersectingSegment[1].x - intersectingSegment[0].x) * (pt2.y - pt1.y)); + var aSlope = pt1.y - intersectingSegment[0].y; + var bSlope = pt1.x - intersectingSegment[0].x; + var numerator1 = ((intersectingSegment[1].x - intersectingSegment[0].x) * aSlope) + - ((intersectingSegment[1].y - intersectingSegment[0].y) * bSlope); + var intersectSlope = numerator1 / intersectAngle; + destination = { x: pt1.x + (intersectSlope * (pt2.x - pt1.x)), + y: pt1.y + (intersectSlope * (pt2.y - pt1.y)) }; + } + } + } + + if (destination) { + pt2 = destination; + } + ctx.lineTo(pt2.x, pt2.y); + ctx.stroke(); + if (edge.data.arrow) { + drawArrow(pt1, pt2, edge.data.arrow, ctx.strokeStyle); + } + }) + + var drawImage = function(image, coords) { + ctx.drawImage(image, + coords.x - image.width / 2, + coords.y - image.height / 2); + }; + + + particleSystem.eachNode(function(node, pt){ + // node: {mass:#, p:{x,y}, name:"", data:{}} + // pt: {x:#, y:#} node position in screen coords + + + if (node.data.imageObject) { + drawImage(node.data.imageObject, pt); + } + else { + var img = new Image(); + img.onload = function() { + node.data.imageObject = img; + drawImage(node.data.imageObject, pt); + }; + img.src = '_img/' + node.data.image; + } + }) + }, + + initMouseHandling:function(){ + // no-nonsense drag and drop (thanks springy.js) + var dragged = null; + + // set up a handler object that will initially listen for mousedowns then + // for moves and mouseups while dragging + var handler = { + clicked:function(e){ + var pos = $(canvas).offset(); + _mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top) + dragged = particleSystem.nearest(_mouseP); + + if (dragged && dragged.node !== null){ + // while we're dragging, don't let physics move the node + dragged.node.fixed = true + } + + $(canvas).bind('mousemove', handler.dragged) + $(window).bind('mouseup', handler.dropped) + + return false + }, + dragged:function(e){ + var pos = $(canvas).offset(); + var s = arbor.Point(e.pageX-pos.left, e.pageY-pos.top) + + if (dragged && dragged.node !== null){ + var p = particleSystem.fromScreen(s) + dragged.node.p = p + } + + return false + }, + + dropped:function(e){ + if (dragged===null || dragged.node===undefined) return + if (dragged.node !== null) dragged.node.fixed = false + dragged.node.tempMass = 1000 + dragged = null + $(canvas).unbind('mousemove', handler.dragged) + $(window).unbind('mouseup', handler.dropped) + _mouseP = null + return false + } + } + + // start listening + $(canvas).mousedown(handler.clicked); + + }, + + } + return that + } + + $(document).ready(function() { + var sys = arbor.ParticleSystem(500, 2000, 0.7); // create the system with sensible repulsion/stiffness/friction + sys.parameters({gravity:true}); // use center-gravity to make the graph settle nicely (ymmv) + sys.renderer = Renderer("#viewport"); // our newly created renderer will have its .init() method called shortly by sys... + + $.getJSON('graph.json', function(graph) { + sys.graft(graph); + }); + }) + +})(this.jQuery) -- cgit v1.2.3