diff --git a/graph.js b/graph.js index efdb78a..afa6aa6 100644 --- a/graph.js +++ b/graph.js @@ -1,12 +1,14 @@ // GRAPH JS ================================================================================================================= -// By Andy Graulund + David Kofoed Wind +// By Andy Graulund + +var GraphJS = (function($){ // Basic stuff -------------------------------------------------------------------------------------------------------------- -var n = "number", - s = "string", - o = "object", - b = "boolean", - u = "undefined", +var n = "number", + s = "string", + o = "object", + b = "boolean", + u = "undefined", uimode = 0, dragging = false, moved = false, dp = [null,null], dontclick = false, selected = [], @@ -20,11 +22,11 @@ var click = touch ? "tap" : "click" var vertices = [], edges = [], graphs = [], - states = [], // For undos/redos + states = [], // For undos/redos gi = 0 // Current graph (index) var ce = null, canvas = null, - cx = null + context = null // UI panels/elements var ui = { properties: null, styles: null, elements: null, graphs: null } @@ -79,7 +81,7 @@ function ElementStyle(strokecolor, fillcolor, strokewidth, vertexradius){ this.fillcolor = fillcolor this.strokewidth = strokewidth this.vertexradius = vertexradius - + this.toTikZ = function(){ // TODO: convert colors return "\\tikzstyle{style" + this.id + "}=[" + this.shape + ",fill=" + this.fillcolor + ",draw=" + this.strokecolor + ",line width=" + this.strokewidth + "]" @@ -111,7 +113,7 @@ function Graph(vertices, edges){ this.y = 0 this.visible = true this.cmpltd = false - + this.draw = function(cx){ if(cx != null){ //dlog(["Drawing graph"]) @@ -131,17 +133,17 @@ function Graph(vertices, edges){ }*/ } } - + this.attach = function(){ this.visible = true graphs.push(this) } - + this.detach = function(){ this.visible = false listRemove(graphs, this) } - + this.toJSON = function(){ var o = { vertices: [], edges: [], x: this.x, y: this.y } for(var i in this.vertices){ @@ -152,7 +154,7 @@ function Graph(vertices, edges){ } return o } - + this.toTikZ = function(){ var o = "\\begin{tikzpicture}\n" var maxY = 0 @@ -170,7 +172,7 @@ function Graph(vertices, edges){ o += "\\end{tikzpicture}" return o } - + this.degreeSeq = function(){ var seq = [], v for(var i in this.vertices){ @@ -181,8 +183,9 @@ function Graph(vertices, edges){ } return seq } - + this.addVertex = function(v, cx){ + if(!cx){ cx = context } if(v instanceof Array && v.length == 2){ v = new Vertex(cid++, "", v[0], v[1]) } @@ -195,8 +198,9 @@ function Graph(vertices, edges){ this.cmpltd = false return v } - + this.addEdge = function(e, cx){ + if(!cx){ cx = context } if(e instanceof Array && e.length == 2){ e = new Edge(cid++, "", e[0], e[1]) } @@ -211,7 +215,7 @@ function Graph(vertices, edges){ } return null } - + this.detachChild = function(el){ var list, edges = [] if(el instanceof Vertex){ list = this.vertices; edges = el.edges } @@ -229,11 +233,15 @@ function Graph(vertices, edges){ listRemove(list, el) return el } - + + this.splitEdge = function(e){ + e.split(this) + } + this.contractEdge = function(e){ e.contract(this) } - + this.adjacencyList = function(){ var l = [], e for(var i in this.edges){ @@ -242,7 +250,7 @@ function Graph(vertices, edges){ } return l } - + this.adjacencyMatrix = function(mobj){ var M = [], v, u, r // TODO: Better support for directed graphs and multigraphs @@ -258,7 +266,7 @@ function Graph(vertices, edges){ } return mobj ? $M(M.length > 0 ? M : [0]) : M } - + this.degreeMatrix = function(mobj){ // Requires Sylvester var ds = this.degreeSeq() @@ -266,7 +274,7 @@ function Graph(vertices, edges){ var DM = dl ? Matrix.Diagonal(ds) : $M([0]) return mobj ? DM : (dl ? DM.elements : []) } - + this.laplacianMatrix = function(mobj){ // Requires Sylvester var AM = this.adjacencyMatrix(true) @@ -274,7 +282,7 @@ function Graph(vertices, edges){ var LM = DM.subtract(AM) return mobj ? LM : LM.elements } - + this.spanningTreeCount = function(){ // Requires Sylvester var LM = this.laplacianMatrix(true) @@ -284,9 +292,9 @@ function Graph(vertices, edges){ } else if(n == 1){ return Math.round(Math.abs(LM.elements[0][0])) } - return 0 + return this.vertices.length >= 1 ? 1 : 0 } - + this.edgeGroups = function(groupname){ var l = {}, e, name for(var i in this.edges){ @@ -300,7 +308,7 @@ function Graph(vertices, edges){ } return l } - + this.isWeighted = function(){ if(this.edges.length <= 0){ return false } for(var i in this.edges){ @@ -310,7 +318,7 @@ function Graph(vertices, edges){ } return true } - + this.isComplete = function(){ // Autocompleted? Yes. if(this.cmpltd){ return true } @@ -327,7 +335,7 @@ function Graph(vertices, edges){ } return false } - + this.elementsWithinRect = function(x1, y1, x2, y2){ var xh = Math.max(x1, x2), xl = Math.min(x1, x2), @@ -345,7 +353,7 @@ function Graph(vertices, edges){ // ???? return els } - + this.complete = function(cx){ this.edges = [] for(var i in this.vertices){ @@ -360,8 +368,8 @@ function Graph(vertices, edges){ if(v instanceof Vertex){ for(var j in this.vertices){ u = this.vertices[j] - if(u instanceof Vertex){ - if(!v.isNeighbour(u) && v !== u){ + if(u instanceof Vertex && u !== v){ + if(!v.isNeighbour(u)){ this.addEdge([v,u], cx) } } @@ -370,7 +378,22 @@ function Graph(vertices, edges){ } this.cmpltd = true } - + + this.semiComplete = function(v, cx){ + dlog(["Semicomplete", v, v instanceof Vertex]) + if(v instanceof Vertex && inArray(v, this.vertices)){ + for(var j in this.vertices){ + u = this.vertices[j] + dlog([u,v]) + if(u instanceof Vertex && u !== v){ + if(!v.isNeighbour(u)){ + this.addEdge([v,u], cx) + } + } + } + } + } + this.attach() } @@ -378,7 +401,7 @@ function Vertex(id, value, x, y){ if(typeof id != n){ return false } if(typeof value != n && typeof value != s){ return false } if(typeof x != n || typeof y != n){ return false } - + this.id = id this.value = value this.x = Math.round(x) @@ -387,15 +410,15 @@ function Vertex(id, value, x, y){ this.edges = [] this.visible = true this.style = defaultStyle - + this.draw = function(cx){ this._draw(cx, this.style, true) } - + this.drawSel = function(cx){ this._draw(cx, selectedStyle, false) } - + this._draw = function(cx, style, label){ styleContext(cx, style) cx.beginPath() @@ -404,20 +427,20 @@ function Vertex(id, value, x, y){ cx.fill() cx.stroke() if(label && (typeof this.value == s || typeof this.value == n) && this.value !== ""){ - drawLabel(this.value, this.x, this.y - 14) + drawLabel(cx, this.value, this.x, this.y - 14) } } - + this.update = function(graph){ // Update various properties this.degree = this.edges.length } - + this.attach = function(){ this.visible = true vertices.push(this) } - + this.detach = function(graph){ if(graph instanceof Graph){ graph.detachChild(this) @@ -430,17 +453,17 @@ function Vertex(id, value, x, y){ } } } - + this.toJSON = function(){ return { id: this.id, value: this.value, x: this.x, y: this.y } // No style support yet } - + this.toTikZ = function(size, maxY){ if(typeof size != n){ size = 1 } var y = (typeof maxY == n && maxY > 0) ? maxY/size - this.y/size : this.y/size return "\\draw (" + this.x/size + "," + y + ") node(v" + this.id + ") {};" } - + this.isNeighbour = function(v){ var e for(var i in this.edges){ @@ -454,7 +477,7 @@ function Vertex(id, value, x, y){ } return false } - + this.edgeMultiplicity = function(v){ var e, c = 0 for(var i in this.edges){ @@ -468,7 +491,7 @@ function Vertex(id, value, x, y){ } return c } - + this.neighbours = function(){ var n = [], e for(var i in this.edges){ @@ -481,13 +504,13 @@ function Vertex(id, value, x, y){ } return n } - + this.edgeGroupName = function(to){ var a = this.id, b = this.to.id var c = (a <= b) return (c ? a : b) + "," + (c ? b : a) } - + this.attach() } @@ -496,7 +519,7 @@ function Edge(id, value, from, to, directed){ if(typeof value != n && typeof value != s){ value = "" } if(typeof from != o || typeof to != o){ return false } if(typeof directed != b){ directed = false } - + this.id = id this.value = value this.from = from @@ -504,27 +527,27 @@ function Edge(id, value, from, to, directed){ this.visible = true this.style = defaultStyle this.directed = directed - + this.draw = function(cx, total, i){ this._draw(cx, this.style, true, total, i) } - + this.drawSel = function(cx, total, i){ this._draw(cx, selectedStyle, false, total, i) } - + this._draw = function(cx, style, label, total, i){ if(this.visible){ if(typeof total == u || total <= 0){ total = 1 } if(typeof i == u || i < 0){ i = 0 } - + // Edge curvature var space = 50 var span = (total-1) * space var t = -span/2 + i*space //dlog(["Drawing", total, i, t]) - + // Draw! // Edge goes from a to b, and a is to the left of b (this has do be rewritten when we introduced directed edges) if(this.from.x < this.to.x) { @@ -538,11 +561,11 @@ function Edge(id, value, from, to, directed){ var bx = this.from.x var by = this.from.y } - + styleContext(cx, style, false) cx.beginPath() cx.moveTo(ax, ay) - + if(t == 0){ // Straight edge cx.lineTo(bx, by) @@ -562,22 +585,22 @@ function Edge(id, value, from, to, directed){ cx.quadraticCurveTo(alphax, alphay, bx, by) } - + cx.stroke() - + //TODO: Draw arrowhead - + if(label && (typeof this.value == s || typeof this.value == n) && this.value !== ""){ var mid = this.midpoint() - drawLabel(this.value, mid[0], mid[1]) + drawLabel(cx, this.value, mid[0], mid[1]) } } } - + this.update = function(graph){ - + } - + this.attach = function(){ this.visible = true edges.push(this) @@ -586,7 +609,7 @@ function Edge(id, value, from, to, directed){ this.from.degree++ this.to.degree++ } - + this.detach = function(graph){ if(graph instanceof Graph){ graph.detachChild(this) @@ -600,6 +623,29 @@ function Edge(id, value, from, to, directed){ this.to.degree-- } } + + this.split = function(graph){ + /*Add a vertex M at the midpoint*/ + var midpoint = this.midpoint() + var M = graph.addVertex([midpoint[0], midpoint[1]]) + + /*We want edges AM (this) and MB (other)*/ + var A = this.from, B = this.to + + /*Move this to AM*/ + this.to = M + M.edges.push(this) + listRemove(B.edges, this) + + /*Add other as MB*/ + var other = graph.addEdge([M, B]) + M.edges.push(other) + B.edges.push(other) + + A.update() + M.update() + B.update() + } this.contract = function(graph){ var e, m = this.midpoint() @@ -629,11 +675,11 @@ function Edge(id, value, from, to, directed){ this.from.y = m[1] this.from.update() } - + this.toJSON = function(){ return { id: this.id, value: this.value, from: this.from.id, to: this.to.id, directed: this.directed } // No style support yet } - + this.toTikZ = function(){ var n = "" if(this.value !== ""){ @@ -641,17 +687,17 @@ function Edge(id, value, from, to, directed){ } return "\\path[draw] (v" + this.from.id + ") -" + (this.directed ? ">" : "-") + n + " (v" + this.to.id + ");" } - + this.midpoint = function(){ return [(this.from.x + this.to.x)/2, (this.from.y + this.to.y)/2] } - + this.groupName = function(){ var a = this.from.id, b = this.to.id var c = (a <= b) return (c ? a : b) + "," + (c ? b : a) } - + this.attach() } @@ -709,7 +755,7 @@ function getElement(x, y){ return el // This is the one! } } - + // Edges // New approach because of bezier curves // Toletance tol (max distance from check point to mouse) @@ -717,7 +763,7 @@ function getElement(x, y){ var edgegroups = graphs[0].edgeGroups() // We find points on e with distance h (in pixels) var h = 5 - + for(var s in edgegroups){ //alert("Here") g = edgegroups[s] @@ -748,14 +794,14 @@ function getElement(x, y){ } var mx = ax + 0.5 * (bx - ax) var my = ay + 0.5 * (by - ay) - + // Length of am, since we need to normalize it var l = Math.sqrt((ay-by)*(ay-by) + (bx-ax)*(bx-ax)) // Find the center point of the quadrature var alphax = mx + bend * (-my+ay)/l var alphay = my + bend * (mx-ax)/l - + // Now we have the following function of the quadratic curve Q(t) // Q(t) = (1-t)^2 a + 2(1-t)t alpha + t^2 b, where t = 0..1 // Compute each of the d points @@ -765,11 +811,11 @@ function getElement(x, y){ for (var j = 0; j < n; j += h) { // Find the corresponding value for t var t = j/n - + // Compute coordinates of Q(t) = [Qx,Qy] var Qx = (1-t)*(1-t) * ax + 2*(1-t)*t * alphax + t*t * bx var Qy = (1-t)*(1-t) * ay + 2*(1-t)*t * alphay + t*t * by - + distToMouse = Math.sqrt((Qy-y)*(Qy-y) + (Qx-x)*(Qx-x)) if (distToMouse < tol) { @@ -778,7 +824,7 @@ function getElement(x, y){ } } } - + return null // None found } @@ -792,16 +838,16 @@ function evtPosition(evt, ce){ // Drawing function drawAll(cx){ - if(!cx){ cx = window.cx } + if(!cx){ cx = context } //dlog("DRAWING ALL") - var el + var el, graph = graphs[gi] cx.clearRect(0, 0, w, h) - for(var i = 0; i < graphs.length; i++){ + /*for(var i = 0; i < graphs.length; i++){ graphs[i].draw(cx) - } + }*/ + graph.draw(cx) // Selected item if(selected.length > 0){ - var graph = graphs[gi] for(var n in selected){ el = selected[n] if(el instanceof Vertex){// || el instanceof Edge){ @@ -809,7 +855,7 @@ function drawAll(cx){ } if(el instanceof Edge){ // Get edgegroup - var g = graphs[gi].edgeGroups(el.groupName()) + var g = graph.edgeGroups(el.groupName()) for(var i in g){ if(g[i] == el){ g[i].drawSel(cx, g.length, i) @@ -820,7 +866,8 @@ function drawAll(cx){ } } -function drawLabel(label, x, y){ +function drawLabel(cx, label, x, y){ + if(!cx){ cx = context } if((typeof label == s || typeof label == n) && label !== ""){ styleContext(cx, labelStyle) cx.beginPath() @@ -838,7 +885,7 @@ function canvasMove(evt){ var p = evtPosition(evt, ce), el var x = p[0] var y = p[1] - + if(dragging && uimode == GJ_TOOL_SELECT){ // Set dragging position if(dp[0] == null){ @@ -852,7 +899,7 @@ function canvasMove(evt){ if(hovered != null){ // Set selection if(!inArray(hovered, selected)){ - setSelected(hovered, evt, cx) + setSelected(hovered, evt, context) dp = [x,y] } // Drag each selected element @@ -865,7 +912,7 @@ function canvasMove(evt){ el.oy = el.y } - // New coordinates + // New coordinates (not too close to the edge) el.x = el.ox + (x - dp[0]) el.y = el.oy + (y - dp[1]) } @@ -880,11 +927,13 @@ function canvasMove(evt){ selected[selection[i].id] = selection[i] } drawAll() - cx.strokeStyle = "rgba(153,153,153,0.5)" - cx.lineWidth = 1 - cx.strokeRect(dp[0], dp[1], x-dp[0], y-dp[1]) + context.strokeStyle = "rgba(153,153,153,0.5)" + context.lineWidth = 1 + context.strokeRect(dp[0], dp[1], x-dp[0], y-dp[1]) + } + if(!touch){ + dontclick = true // Prevent click event on mouseup } - dontclick = true // Prevent click event on mouseup } else { //hovered = getElement(x,y) if(uimode == GJ_TOOL_ADD_EDGE){ @@ -892,7 +941,7 @@ function canvasMove(evt){ if(touch && hovered instanceof Vertex){ dp[(dp[0] == null) ? 0 : 1] = hovered selected = [hovered] - canvasFinishAddEdge(cx) + canvasFinishAddEdge(context) } } else { dp = [null,null] @@ -911,22 +960,24 @@ function canvasClick(evt){ dlog([el, uimode, $("#vertexautocomplete")]) if(uimode == GJ_TOOL_SELECT){ dlog(1) - setSelected(el, evt, cx) + setSelected(el, evt, context) updateState() } if(uimode == GJ_TOOL_SELECT && alt && evt.shiftKey && el instanceof Vertex){ dlog(2) - canvasStartAddEdge(cx) + canvasStartAddEdge(context) } if(uimode == GJ_TOOL_ADD_EDGE && el instanceof Vertex){ dlog(3) dp[(dp[0] == null) ? 0 : 1] = el - selected = [el] - canvasFinishAddEdge(cx) + selected = [] + selected[el.id] = el + updateState(false) + canvasFinishAddEdge(context) } if((uimode == GJ_TOOL_ADD_VERTEX || (uimode == GJ_TOOL_SELECT && alt && !evt.shiftKey)) && el == null){ dlog(4) - canvasFinishAddVertex(x, y, cx) + canvasFinishAddVertex(x, y, context) } } if(dontclick){ dontclick = false } @@ -938,7 +989,7 @@ function canvasKey(evt){ var up = 38, down = 40, left = 37, right = 39, s if((k == up || k == down || k == left || k == right) && (selected.length > 0 && selected[0] != null)){ evt.preventDefault() - var inc = (k == down || k == right), + var inc = (k == down || k == right), x = (k == left || k == right) for(var i in selected){ s = selected[i] @@ -958,44 +1009,42 @@ function canvasKey(evt){ // Touch events function touchMove(evt){ + evt.preventDefault() if(evt.touches.length == 1 && evt.touches[0].target == canvas){ - evt.preventDefault() moved = true var ct = evt.changedTouches + dlog(ct) canvasMove({ - layerX: ct[0].pageX, - layerY: ct[0].pageY + clientX: ct[0].pageX, + clientY: ct[0].pageY }) } } function touchEnd(evt){ - //evt.preventDefault() dragging = false dp = [null,null] var ct = evt.changedTouches if(!moved && ct.length == 1 && ct[0].target == canvas){ canvasClick({ - layerX: ct[0].pageX, - layerY: ct[0].pageY + clientX: ct[0].pageX, + clientY: ct[0].pageY }) } - moved = false + moved = false } -function touchStart(evt) { +function touchStart(evt){ if(evt.touches.length == 1 && evt.touches[0].target == canvas){ evt.preventDefault() dragging = true moved = false var ct = evt.changedTouches - for(var i = 0; i < ct.length; i++){ - var touch = ct[i] + if(ct.length > 0){ canvasMove({ - layerX: touch.pageX, - layerY: touch.pageY + clientX: ct[0].pageX, + clientY: ct[0].pageY }) - return } } } @@ -1048,10 +1097,10 @@ function canvasStartAddVertex(cx){ function canvasFinishAddVertex(x, y, cx){ if(x >= 0 && y >= 0){ - graphs[gi].addVertex([x,y], cx) - dlog($("#vertexautocomplete")) + var v = graphs[gi].addVertex([x,y], cx) + //dlog($("#vertexautocomplete")) if($("#vertexautocomplete").is(":checked")){ // Kind of rough right now - graphs[gi].complete() + graphs[gi].semiComplete(v) updateState(false) } else { clearUimode() @@ -1082,7 +1131,7 @@ function setSelected(el, evt, cx){ } function clearCanvas(cx, bypass){ - if(bypass || confirm("Are you sure you want to clear the canvas? This cannot be undoed.")){ + if(bypass || confirm("Are you sure you want to clear the canvas? This cannot be undone.")){ edges = [] vertices = [] graphs = []; new Graph() @@ -1104,21 +1153,27 @@ function removeElement(el, graph, cx){ return r } +function addGraph(){ + new Graph(); updateState() +} + // Information display methods --------------------------------------------------------------------------------------------- function updateState(info){ // Canvas + setCanvasSize() drawAll() // Properties if(typeof info != b || (typeof info == b && info)){ displayInfo(ui.properties, selected) } // Graphs + displayGraphList(ui.graphs) // Elements displayElements(ui.elements) // Styles } function displayInfo(panel, el, cx){ - if(!cx){ cx = window.cx } + if(!cx){ cx = context } if(el instanceof Array){ el = trimArray(el) if(el.length > 1){ @@ -1137,7 +1192,7 @@ function displayInfo(panel, el, cx){ '

Vertex

' + '

' + '

Degree: ' + he(el.degree) + '

' + - '

' + + '

' + '

' ) $("input#label", info).keyup (function(){ el.value = this.value; drawAll() }) @@ -1149,13 +1204,14 @@ function displayInfo(panel, el, cx){ if(el instanceof Edge){ // Edge info var info = $( - '
Contract edge Remove edge
' + + '
' + '

Edge

' + '

' + '
' ) $("input#label", info).keyup (function(){ el.value = this.value; drawAll() }) $("input#label", info).change(function(){ el.value = this.value; updateState() }) + $("a.splitbtn", info).click(function(){ graph.splitEdge(el); updateState() }) $("a.contractbtn", info).click(function(){ graph.contractEdge(el); updateState() }) $("a.removebtn", info).click(function(){ removeElement(el, graph, cx) }) } @@ -1176,20 +1232,37 @@ function displayInfo(panel, el, cx){ var title = (adj.length > 0 ? ucf(adj.join(" ")) + " g" : "G") + "raph" + (autoname ? " " + autoname : "") var info = $( // Graph info - '
' + + '
' + '

' + title + '

' + - '

Vertices: ' + graph.vertices.length + '

' + - '

Edges: ' + graph.edges.length + '

' + - '

Deg. seq.: ' + (seq ? seq : ' ') + '

' + - '

Sp. trees: ' + graph.spanningTreeCount() + '

' + + '
' + + '

Vertices: ' + graph.vertices.length + '

' + + '

Edges: ' + graph.edges.length + '

' + + '
' + + '

Deg. seq.: ' + (seq ? seq : ' ') + '

' + + '

Sp. trees: ' + graph.spanningTreeCount() + '

' + + '
' + '
' ) - //$("a.sizebtn", info).click(function(){}) - $("a.tikzbtn", info).click(function(){ alert(graphs[gi].toTikZ()) }) // Need modal } panel.empty().append(info) } +function displayGraphList(panel){ + var list = "" + for(var i in graphs){ + list += '
  • Graph ' + (parseInt(i)+1) + '
  • ' + } + list = $(list) + $("a", list).click(function(evt){ + var id = this.parentNode.id.split("-") + if(id.length > 1 && id[0] == "graph" && is_numeric(id[1])){ + gi = id[1] + updateState() + } + }) + $("ul.elements", panel).empty().append(list) +} + function displayElements(panel){ var list = "", graph = graphs[gi], a // Vertices @@ -1266,6 +1339,13 @@ function clearModal(){ } } +function setCanvasSize(){ + context.canvas.width = $(window).width() - 310 + context.canvas.height = $(window).height() - 190 + w = context.canvas.width + h = context.canvas.height +} + // Our example ------------------------------------------------------------------------------------------------------------- // Lucy in the sky with the diamond @@ -1290,53 +1370,70 @@ var dedges = [ // [ [1, 2], [2, 4], [1, 3], [3, 4], [2, 3] ] // Initialising the document ------------------------------------------------------------------------------------------------ $(document).ready(function(){ - + // Variables (I really need to move these out of global scope...) ce = $("canvas") canvas = ce.get(0) - cx = canvas.getContext("2d") + context = canvas.getContext("2d") var scale = 1 - + // Canvas settings - cx.scale(scale, scale) - cx.font = "sans-serif" - cx.textAlign = "center" - cx.textBaseline = "middle" - + context.scale(scale, scale) + context.font = "sans-serif" + context.textAlign = "center" + context.textBaseline = "middle" + // Elements ui.properties = $("#info") ui.graphs = $("#graphs") ui.elements = $("#elements") ui.styles = $("#styles") - + // Example var diamond = new Graph(dvertices, dedges) - + // Mouse ce.mousedown (function(){ dragging = true }) $("body").mouseup(function(){ dragging = false }) ce.mousemove(canvasMove) ce.click(canvasClick) $(document).keydown(canvasKey) - + // Touch - document.body.addEventListener("touchstart", touchStart, false) - document.body.addEventListener("gesturechanged", noevt, false) - document.body.addEventListener("touchend", touchEnd, false) - document.body.addEventListener("touchmove", touchMove, false) - + if(touch){ + document.body.addEventListener("touchstart", touchStart, false) + document.body.addEventListener("gesturechanged", noevt, false) + document.body.addEventListener("touchend", touchEnd, false) + document.body.addEventListener("touchmove", touchMove, false) + } + // Buttons $("#btnselect" ).click(function(){ clearUimode() }) - $("#btnaddvertex").click(function(){ canvasStartAddVertex(cx) }) - $("#btnaddedge" ).click(function(){ canvasStartAddEdge(cx) }) + $("#btnaddvertex").click(function(){ canvasStartAddVertex(context) }) + $("#btnaddedge" ).click(function(){ canvasStartAddEdge(context) }) $("#btninfo" ).click(function(){ ui.properties.toggle() }) - $("#btnload" ).click(function(){ var j = prompt("Paste here a graph saved as a string by this app:"); if(j != null && j.length > 0){ clearCanvas(cx, true); graphs = []; graphFromJSON(j); drawAll(cx) } }) + $("#btnload" ).click(function(){ var j = prompt("Paste here a graph saved as a string by this app:"); if(j != null && j.length > 0){ clearCanvas(context, true); graphs = []; graphFromJSON(j); drawAll(context) } }) $("#btnsave" ).click(function(){ prompt("Store the following string somewhere and paste it back here when you want to load this graph again.", JSON.stringify(graphs[gi].toJSON())) }) - $("#btnclear" ).click(function(){ clearCanvas(cx) }) - + $("#btnexport" ).click(function(){ alert(graphs[gi].toTikZ()) }) // Need modal + $("#btnclear" ).click(function(){ clearCanvas(context) }) + $("#graphs a.add").click(function(){ addGraph() }) + + // Resize + $(window).resize(updateState) + // Let's go! updateState() }) +// Expose certain things to the world + +return { + Graph: Graph, + Vertex: Vertex, + Edge: Edge +} + +})(jQuery) + // JSON runtime, if you do not already have it if(!("JSON" in window)){eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('3(!o.p){p={}}(5(){5 f(n){7 n<10?\'0\'+n:n}3(6 1b.z.q!==\'5\'){1b.z.q=5(h){7 o.1C()+\'-\'+f(o.1T()+1)+\'-\'+f(o.1O())+\'T\'+f(o.1D())+\':\'+f(o.1M())+\':\'+f(o.1Q())+\'Z\'};X.z.q=1K.z.q=1I.z.q=5(h){7 o.1V()}}y L=/[\\1W\\13\\1o-\\1l\\1m\\1i\\1n\\1s-\\1p\\1j-\\15\\17-\\14\\18\\1f-\\19]/g,M=/[\\\\\\"\\1B-\\1z\\1w-\\1y\\13\\1o-\\1l\\1m\\1i\\1n\\1s-\\1p\\1j-\\15\\17-\\14\\18\\1f-\\19]/g,8,H,1e={\'\\b\':\'\\\\b\',\'\\t\':\'\\\\t\',\'\\n\':\'\\\\n\',\'\\f\':\'\\\\f\',\'\\r\':\'\\\\r\',\'"\':\'\\\\"\',\'\\\\\':\'\\\\\\\\\'},l;5 N(m){M.1h=0;7 M.11(m)?\'"\'+m.C(M,5(a){y c=1e[a];7 6 c===\'m\'?c:\'\\\\u\'+(\'1k\'+a.1r(0).12(16)).1g(-4)})+\'"\':\'"\'+m+\'"\'}5 E(h,w){y i,k,v,e,K=8,9,2=w[h];3(2&&6 2===\'x\'&&6 2.q===\'5\'){2=2.q(h)}3(6 l===\'5\'){2=l.P(w,h,2)}1u(6 2){J\'m\':7 N(2);J\'S\':7 1v(2)?X(2):\'D\';J\'1x\':J\'D\':7 X(2);J\'x\':3(!2){7\'D\'}8+=H;9=[];3(Q.z.12.1S(2)===\'[x 1R]\'){e=2.e;G(i=0;iGraph Theory JS/HTML5 - - - + + + + -
    -

    GraphJS

    - -
    +
    +
    +

    GraphJS

    + +
    +
    -
    +
    +