From 9cfc2bdfd4795ddd44a336eaf5bf70f2849a8c3e Mon Sep 17 00:00:00 2001 From: Fedjmike Date: Thu, 27 Aug 2015 11:12:11 +0100 Subject: [PATCH 1/5] Updated to the version found at https://dl.dropboxusercontent.com/u/4189520/GraphJS/graphjs.html --- graph.js | 201 +++++++++++++++++++---------- graphjs.css | 352 +++++++++++++++++++++++++++++++++++++++++++++++++++ graphjs.html | 336 +++--------------------------------------------- 3 files changed, 507 insertions(+), 382 deletions(-) create mode 100644 graphjs.css diff --git a/graph.js b/graph.js index efdb78a..d978c70 100644 --- a/graph.js +++ b/graph.js @@ -1,5 +1,7 @@ // GRAPH JS ================================================================================================================= -// By Andy Graulund + David Kofoed Wind +// By Andy Graulund + +var GraphJS = (function($){ // Basic stuff -------------------------------------------------------------------------------------------------------------- var n = "number", @@ -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 } @@ -183,6 +185,7 @@ function Graph(vertices, edges){ } this.addVertex = function(v, cx){ + if(!cx){ cx = context } if(v instanceof Array && v.length == 2){ v = new Vertex(cid++, "", v[0], v[1]) } @@ -197,6 +200,7 @@ function Graph(vertices, edges){ } this.addEdge = function(e, cx){ + if(!cx){ cx = context } if(e instanceof Array && e.length == 2){ e = new Edge(cid++, "", e[0], e[1]) } @@ -284,7 +288,7 @@ 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){ @@ -360,8 +364,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) } } @@ -371,6 +375,21 @@ 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() } @@ -404,7 +423,7 @@ 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) } } @@ -569,7 +588,7 @@ function Edge(id, value, from, to, directed){ 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]) } } } @@ -792,16 +811,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 +828,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 +839,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() @@ -852,7 +872,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,9 +885,9 @@ function canvasMove(evt){ el.oy = el.y } - // New coordinates - el.x = el.ox + (x - dp[0]) - el.y = el.oy + (y - dp[1]) + // New coordinates (not too close to the edge) + el.x = Math.max(5, el.ox + (x - dp[0])) + el.y = Math.max(5, el.oy + (y - dp[1])) } } updateState() // <-- Inefficient @@ -880,11 +900,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 +914,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 +933,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 } @@ -958,44 +982,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 +1070,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 +1104,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 +1126,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){ @@ -1176,20 +1204,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 +1311,11 @@ function clearModal(){ } } +function setCanvasSize(){ + context.canvas.width = $(window).width() - 310 + context.canvas.height = $(window).height() - 190 +} + // Our example ------------------------------------------------------------------------------------------------------------- // Lucy in the sky with the diamond @@ -1294,14 +1344,14 @@ $(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") @@ -1320,23 +1370,40 @@ $(document).ready(function(){ $(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

    + +
    +
    -
    +
    + From 5f7c85962ffe97e8c49c0bd6f58e3131f12fed0a Mon Sep 17 00:00:00 2001 From: Fedjmike Date: Thu, 27 Aug 2015 11:36:35 +0100 Subject: [PATCH 2/5] Fixed an issue with canvas redrawing Update the w and h when the canvas is resized so that the full canvas is cleared on a redraw (see drawAll) --- graph.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/graph.js b/graph.js index d978c70..471b3d4 100644 --- a/graph.js +++ b/graph.js @@ -1314,6 +1314,8 @@ 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 ------------------------------------------------------------------------------------------------------------- From f3af588f48f28e85d2930c82daab8e971ce3ea3a Mon Sep 17 00:00:00 2001 From: Satyarth Mishra Sharma Date: Thu, 27 Aug 2015 15:08:58 +0300 Subject: [PATCH 3/5] Allow elements to move to -ve coordinates --- graph.js | 168 +++++++++++++++++++++++++++---------------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/graph.js b/graph.js index 471b3d4..2388cd7 100644 --- a/graph.js +++ b/graph.js @@ -4,11 +4,11 @@ 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 = [], @@ -81,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 + "]" @@ -113,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"]) @@ -133,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){ @@ -154,7 +154,7 @@ function Graph(vertices, edges){ } return o } - + this.toTikZ = function(){ var o = "\\begin{tikzpicture}\n" var maxY = 0 @@ -172,7 +172,7 @@ function Graph(vertices, edges){ o += "\\end{tikzpicture}" return o } - + this.degreeSeq = function(){ var seq = [], v for(var i in this.vertices){ @@ -183,7 +183,7 @@ function Graph(vertices, edges){ } return seq } - + this.addVertex = function(v, cx){ if(!cx){ cx = context } if(v instanceof Array && v.length == 2){ @@ -198,7 +198,7 @@ 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){ @@ -215,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 } @@ -233,11 +233,11 @@ function Graph(vertices, edges){ listRemove(list, el) return el } - + this.contractEdge = function(e){ e.contract(this) } - + this.adjacencyList = function(){ var l = [], e for(var i in this.edges){ @@ -246,7 +246,7 @@ function Graph(vertices, edges){ } return l } - + this.adjacencyMatrix = function(mobj){ var M = [], v, u, r // TODO: Better support for directed graphs and multigraphs @@ -262,7 +262,7 @@ function Graph(vertices, edges){ } return mobj ? $M(M.length > 0 ? M : [0]) : M } - + this.degreeMatrix = function(mobj){ // Requires Sylvester var ds = this.degreeSeq() @@ -270,7 +270,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) @@ -278,7 +278,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) @@ -290,7 +290,7 @@ function Graph(vertices, edges){ } return this.vertices.length >= 1 ? 1 : 0 } - + this.edgeGroups = function(groupname){ var l = {}, e, name for(var i in this.edges){ @@ -304,7 +304,7 @@ function Graph(vertices, edges){ } return l } - + this.isWeighted = function(){ if(this.edges.length <= 0){ return false } for(var i in this.edges){ @@ -314,7 +314,7 @@ function Graph(vertices, edges){ } return true } - + this.isComplete = function(){ // Autocompleted? Yes. if(this.cmpltd){ return true } @@ -331,7 +331,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), @@ -349,7 +349,7 @@ function Graph(vertices, edges){ // ???? return els } - + this.complete = function(cx){ this.edges = [] for(var i in this.vertices){ @@ -374,7 +374,7 @@ 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)){ @@ -389,7 +389,7 @@ function Graph(vertices, edges){ } } } - + this.attach() } @@ -397,7 +397,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) @@ -406,15 +406,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() @@ -426,17 +426,17 @@ function Vertex(id, value, x, y){ 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) @@ -449,17 +449,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){ @@ -473,7 +473,7 @@ function Vertex(id, value, x, y){ } return false } - + this.edgeMultiplicity = function(v){ var e, c = 0 for(var i in this.edges){ @@ -487,7 +487,7 @@ function Vertex(id, value, x, y){ } return c } - + this.neighbours = function(){ var n = [], e for(var i in this.edges){ @@ -500,13 +500,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() } @@ -515,7 +515,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 @@ -523,27 +523,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) { @@ -557,11 +557,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) @@ -581,22 +581,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(cx, this.value, mid[0], mid[1]) } } } - + this.update = function(graph){ - + } - + this.attach = function(){ this.visible = true edges.push(this) @@ -605,7 +605,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) @@ -619,7 +619,7 @@ function Edge(id, value, from, to, directed){ this.to.degree-- } } - + this.contract = function(graph){ var e, m = this.midpoint() this.detach(graph) @@ -648,11 +648,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 !== ""){ @@ -660,17 +660,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() } @@ -728,7 +728,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) @@ -736,7 +736,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] @@ -767,14 +767,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 @@ -784,11 +784,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) { @@ -797,7 +797,7 @@ function getElement(x, y){ } } } - + return null // None found } @@ -858,7 +858,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){ @@ -886,8 +886,8 @@ function canvasMove(evt){ } // New coordinates (not too close to the edge) - el.x = Math.max(5, el.ox + (x - dp[0])) - el.y = Math.max(5, el.oy + (y - dp[1])) + el.x = el.ox + (x - dp[0]) + el.y = el.oy + (y - dp[1]) } } updateState() // <-- Inefficient @@ -962,7 +962,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] @@ -1165,7 +1165,7 @@ function displayInfo(panel, el, cx){ '

    Vertex

    ' + '

    ' + '

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

    ' + - '

    ' + + '

    ' + '

    ' ) $("input#label", info).keyup (function(){ el.value = this.value; drawAll() }) @@ -1342,35 +1342,35 @@ 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) context = canvas.getContext("2d") var scale = 1 - + // Canvas settings 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 if(touch){ document.body.addEventListener("touchstart", touchStart, false) @@ -1378,7 +1378,7 @@ $(document).ready(function(){ document.body.addEventListener("touchend", touchEnd, false) document.body.addEventListener("touchmove", touchMove, false) } - + // Buttons $("#btnselect" ).click(function(){ clearUimode() }) $("#btnaddvertex").click(function(){ canvasStartAddVertex(context) }) @@ -1389,10 +1389,10 @@ $(document).ready(function(){ $("#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() }) From 4c6f5a9898e1eaf088c1782082dcd9137e3707f3 Mon Sep 17 00:00:00 2001 From: Fedjmike Date: Thu, 27 Aug 2015 12:47:24 +0100 Subject: [PATCH 4/5] Added a button for splitting an edge from AC into ABC (a new vertex) --- graph.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/graph.js b/graph.js index 2388cd7..96ced7d 100644 --- a/graph.js +++ b/graph.js @@ -234,6 +234,10 @@ function Graph(vertices, edges){ return el } + this.splitEdge = function(e){ + e.split(this) + } + this.contractEdge = function(e){ e.contract(this) } @@ -620,6 +624,16 @@ function Edge(id, value, from, to, directed){ } } + this.split = function(graph){ + /*Add a vertex at the midpoint*/ + var midpoint = this.midpoint() + var mid = graph.addVertex([midpoint[0], midpoint[1]]) + + /*Join edges to it*/ + graph.addEdge([mid, this.to]) + this.to = mid + } + this.contract = function(graph){ var e, m = this.midpoint() this.detach(graph) @@ -1177,13 +1191,14 @@ function displayInfo(panel, el, cx){ if(el instanceof Edge){ // Edge info var info = $( - '
    ' + + '
    ' + '

    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) }) } From a37f111851290e71c4b14998a71a640cb25984e9 Mon Sep 17 00:00:00 2001 From: Fedjmike Date: Thu, 27 Aug 2015 13:12:45 +0100 Subject: [PATCH 5/5] Fixed splitting I didn't realise vertices had to be notified of changed edges --- graph.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/graph.js b/graph.js index 96ced7d..afa6aa6 100644 --- a/graph.js +++ b/graph.js @@ -625,13 +625,26 @@ function Edge(id, value, from, to, directed){ } this.split = function(graph){ - /*Add a vertex at the midpoint*/ + /*Add a vertex M at the midpoint*/ var midpoint = this.midpoint() - var mid = graph.addVertex([midpoint[0], midpoint[1]]) + var M = graph.addVertex([midpoint[0], midpoint[1]]) - /*Join edges to it*/ - graph.addEdge([mid, this.to]) - this.to = mid + /*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){