<br />
<b>Warning</b>:  Declaration of Jetpack_IXR_Client::query() should be compatible with IXR_Client::query(...$args) in <b>/home/clients/7267bc096562fcdb78c0ab60d3ac51fb/web/blog/wp-content/plugins/jetpack/class.jetpack-ixr-client.php</b> on line <b>91</b><br />
{"id":651,"date":"2016-01-17T21:25:15","date_gmt":"2016-01-17T21:25:15","guid":{"rendered":"http:\/\/barradeau.com\/blog\/?p=651"},"modified":"2021-06-25T15:58:37","modified_gmt":"2021-06-25T15:58:37","slug":"graph","status":"publish","type":"post","link":"http:\/\/barradeau.com\/blog\/?p=651","title":{"rendered":"Graph, a hidden hero"},"content":{"rendered":"<p>a Graph is a series of vertices (nodes) connected by edges (&#8220;segments&#8221;). it\u00a0can articulate\u00a0data, perform operations and provide insights on them.\u00a0data visualization makes massive use of 2d planar graphs (you&#8217;ve probably seen many Force Directed Graphs) but a display list, a 2D polygon,\u00a0a\u00a03D mesh, a roadmap are also graphs of some sort.<\/p>\n<p>in its simplest form this would be a Graph:<\/p>\n<pre class=\"lang:js decode:true\">var Vertex = function( data ){ \/*store the data*\/ }\r\n\r\nvar Edge = function( v0, v1 ){\r\n    this.v0 = v0;\r\n    this.v1 = v1;\r\n}\r\n\r\nvar Graph = function( vertices, edges ){\r\n    this.vertices = vertices;\r\n    this.edges = edges;\r\n}\r\n\r\n\/\/create two vertices\r\nvar A = new Vertex( data );\r\nvar B = new Vertex( data );\r\n\r\n\/\/create an edge to bind the two vertices\r\nvar E = new Edge( A, B );\r\n\r\n\/\/create a graph\r\nvar graph = new Graph( [ A, B ], [ E ] );<\/pre>\n<p>the\u00a0graph contains 2 vertices A &amp; B connected with\u00a0an edge E.<\/p>\n<p>now\u00a0if vertices are letters, it can represent a word, if vertices are numbers,\u00a0the graph can be a\u00a0mathematical\u00a0operation, if vertices are 2D or 3D points, the graph is a line in 2 or 3 dimensions, etc.\u00a0depending on the nature of the vertices, the edge can also represent different things, from a concatenation if vertices are letters, an operation if vertices are numbers or just maintain two references to the vertices pointers. an edge can be <em>undirected<\/em>\u00a0meaning that the connection from E( A,B )\u00a0== E( B,\u00a0A )\u00a0or <em>directed<\/em>\u00a0which means that\u00a0E( A,B ) != E( B,A ).\u00a0if you think of our graph\u00a0as\u00a0a roadmap, the\u00a0edge we created earlier would be a\u00a0one way street between A\u00a0and B.\u00a0this is important, especially\u00a0in geometry\u00a0; for instance it\u00a0allows to test the <em>winding<\/em> of triangle\u00a0and eventually unify or flip it.\u00a0if we need to go back to A\u00a0from B in a directed graph, we have to add a new Edge like so :<\/p>\n<pre class=\"lang:js decode:true \">graph.edges.push( new Edge( B,A\u00a0) );<\/pre>\n<p>then we can go back from B\u00a0to A.<br \/>\na directed graphs can be <em>cyclic<\/em>\u00a0which means that there are\u00a0&#8220;loops&#8221;, if you take\u00a0a triangle with 3 vertices A, B, C and 3 edges between them,\u00a0it&#8217;s possible to go from A\u00a0to\u00a0B, then from B to C and from C to A, that&#8217;s a cycle.\u00a0it makes some operations a\u00a0bit tricky as we must be careful\u00a0not to fall into a cycle while traversing the graph.<\/p>\n<p>otherwise, both the vertices and the edges can be updated separately\u00a0making it\u00a0a very flexible data structure, \u00a0a graph is N dimensional, N being the dimension of its\u00a0vertices (the edges always have a cardinality of 2). the edges often have a value usually called a <em>weight <\/em>which can represent anything that\u00a0happens between the 2 vertices it binds.<\/p>\n<p>trees \u00a0are a special case of graphs ; trees\u00a0are <em>directed connected acyclic<\/em> graphs ; directed means that E( A,B ) != E( B,A ), everything originates from a single vertex\u00a0(the <em>root<\/em>) and every vertex\u00a0is\u00a0connected to a\u00a0parent or the root node (connected), an edge must not create a loop or cycle\u00a0(acyclic).<\/p>\n<p>I&#8217;ll leave the theory\u00a0here, it was just to give an overview, there are gazillions\u00a0of books and articles about graphs and graph theory.<\/p>\n<p>as I\u00a0care mostly\u00a0about graphics, let&#8217;s start with\u00a02d graphs,\u00a0usually they represent hierarchies, networks or relationships between nodes. below I create a bunch of random points, perform a Delaunay triangulation (the grey mesh) then I use the triangles&#8217; centers to build a second set of vertices. then I compute the Minimum Spanning Tree (MST: a tree that covers all the nodes of the graph) and paint it in blue (click to reset).<\/p>\n<p><iframe loading=\"lazy\" width=\"600\" height=\"600\" style=\"border: 1px solid black;\" src=\"https:\/\/www.barradeau.com\/js\/graph\/basic.html\"><\/iframe><\/p>\n<p>the MST is a thing of beauty, it&#8217;s the set of edges with the minimal weight (I believe there can be more than one MST), it links all the vertices of the graph ; if the graph is connected, you can reach any vertex from any vertex by traversing the tree. the header\u00a0picture of this article is a MST computed on a 45K vertices mesh, it seems disconnected because I didn&#8217;t render the back faces.<\/p>\n<p>most of the time in maps edges store their\u00a0length and vertices store some\u00a0extra information.\u00a0below is the map of the parisian metro, the\u00a0vertices are the stations,\u00a0each line is an edge set and the network as a whole is described by another edge set.<\/p>\n<p>drag the cursor\u00a0over a\u00a0station to see its\u00a0name,\u00a0tap\u00a0to select a starting point and a goal.<br \/>\n<iframe loading=\"lazy\" width=\"600\" height=\"600\" style=\"border: 1px solid black;\" src=\"https:\/\/www.barradeau.com\/js\/graph\/metro.html\"><\/iframe><br \/>\nthe multicolored band at the bottom represents the different lines to use from the start to the goal.\u00a0the important thing here is that different\u00a0edge sets can use the same pool of vertices\u00a0;\u00a0finding the shortest path between 2 nodes uses the global graph while the colors are retrieved from each\u00a0lines&#8217; graphs. the global graph ignores the colors of the lines, the lines ignore the interconnections.<\/p>\n<p>if this shortest path returns the shortest <em>distance\u00a0<\/em>from a station to another, you can tell by the colors alone\u00a0that it&#8217;s probably not the shortest in <em>time<\/em>.\u00a0that&#8217;s because the <em>metric<\/em>\u00a0used to compute the edge&#8217;s <em>weight<\/em>\u00a0is the distance and there&#8217;s no time penalty for changing line. the algorithm\u00a0could easily be modified\u00a0to include\u00a0those rules.<\/p>\n<p>as a graph is basically a series of segments, it&#8217;s easy to interpolate a value along the edges ; in the example above, a dot is going to\u00a0back and forth along the shortest path. to compute its position, I use this method:<\/p>\n<pre class=\"lang:js decode:true\">function getPositionAt( points, t ) {\r\n\r\n    var length = points.length-1;\r\n    var i0 = Math.floor( length * t );\r\n    i0 = i0 &lt; length - 1 ? i0 : length - 1;\r\n    var i1 = Math.min( i0 + 1, length );\r\n\r\n    var delta = 1 \/ length;\r\n    var nt =  ( t - ( i0 * delta ) ) \/ delta;\r\n    return {x:lerp( nt, points[i0].x, points[i1].x ),\r\n            y:lerp( nt, points[i0].y, points[i1].y ) };\r\n}<\/pre>\n<p>where <em>points<\/em> is a series of ordered vertices and\u00a0<em>t<\/em> is a normalized ratio [0,1]\u00a0first we find the 2 bounds i0 and i1 that are the\u00a0points of the path between which t will fall then it\u00a0computes a new t value <em>nt<\/em> that represents the <em>local<\/em> value of t between points[i0] and points[i1] the last step is to linearly interpolate <em>nt<\/em>\u00a0between\u00a0points[i0] and points[i1].<\/p>\n<p>which means that we can create agents\u00a0\\o\/\u00a0agents\u00a0can\u00a0move along the edges and bring life to a graph, in the above example, agents would probably represent trains.<\/p>\n<p>graphs\u00a0work in any dimensions, below is a glorious flying crab gracefully rotating in space, I compute its MST and let you select 2 nodes to see the shortest path between them.<br \/>\n<iframe loading=\"lazy\" width=\"600\" height=\"600\" style=\"border: 1px solid black;\" src=\"https:\/\/www.barradeau.com\/js\/graph\/mesh.html\"><\/iframe><\/p>\n<p>note that the Dijkstra algorithm is not the fastest, this is a screenshot of a 45k mesh vertices, the dijkstra algorithm took more than a 90 seconds\u00a0to compute the shortest path to all the vertices from the nose. on the other hand,\u00a0retrieving a path\u00a0from any point of the graph is extremely fast as it&#8217;s\u00a0a only a linked list traversal.<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-680\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/01\/dijkstra.png\" alt=\"dijkstra\" width=\"800\" height=\"945\" srcset=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/01\/dijkstra.png 800w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/01\/dijkstra-254x300.png 254w, http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/01\/dijkstra-768x907.png 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p>in a word, graphs are your friends, they are useful\u00a0for games, modeling and simulations.<\/p>\n<p><a href=\"https:\/\/github.com\/nicoptere\/graph\" target=\"_blank\" rel=\"noopener\">here&#8217;s the code for the demos<\/a>.<\/p>\n<p><a id=\"anchor\"><\/a>UPDATE 16\/01\/21:<\/p>\n<p>my first implementation of the Dijkstra algorithm was completely flawed :\/ I\u00a0stored the maximum distance for\u00a0each vertex instead of using the edge&#8217;s length. the results were not completely incoherent and that&#8217;s why I didn&#8217;t spot it at first. after Makc&#8217;s comment I tried to\u00a0improve the computation time and rewrote a second version that was not only twice faster but also gave differents (better)\u00a0results &amp;\u00a0more consistently.<\/p>\n<p>so to settle the score, I checked both the original (flawed) and the new versions of my Dijkstra against an <a href=\"\/\/http:\/\/www.briangrinstead.com\/blog\/astar-search-algorithm-in-javascript\" target=\"_blank\" rel=\"noopener\">A* pathfinder<\/a>. as expected the A* is insanely faster (~10 ms on my machine) yet much less accurate. below is a demo showing 5000 vertices and roughly 30.000 edges where I perform the searches:<\/p>\n<p><iframe loading=\"lazy\" width=\"600\" height=\"600\" style=\"border: 1px solid black;\" src=\"https:\/\/www.barradeau.com\/js\/graph\/index.html\"><\/iframe><br \/>\nin blue the new Dijkstra, in red, the old one, in orange, the A* pathfinder. on double tap\/click, the root is re-initialized and both Dijkstra are being performed. when you click \/\u00a0drag around, the shortest paths are being computed using the 3 algorithms.<\/p>\n<p>it is obvious that a game where a character moves frequently and re-evaluates both it&#8217;s location and its target will benefit from the A* approach on the other hand if you have to reach a fixed target (like with a GPS)\u00a0it might be much better to compute a Dijkstra once and for all then, whatever the way you&#8217;ll take or the obstacles you&#8217;ll encounter, the algorithm will be able to give you an alternative route to your goal almost instantly.<\/p>\n<p>another thing I didn&#8217;t mention is that a graph can be created from it&#8217;s edges and an edge length. this is enough to build a representation of the graph, below is an example of the LCF notation which describes the edge structure in a very compact form:<br \/>\n<iframe loading=\"lazy\" width=\"600\" height=\"600\" style=\"border: 1px solid black;\" src=\"https:\/\/www.barradeau.com\/js\/graph\/lcf.html\"><\/iframe><br \/>\nyou can choose from the dropdown list,\u00a0can watch the corresponding graph.\u00a0you can read about the <a href=\"http:\/\/mathworld.wolfram.com\/LCFNotation.html\" target=\"_blank\" rel=\"noopener\">LCF on wolfram<\/a>\u00a0the implementation is pretty straight forward, you need to split the code into an array of instructions and a cycle count, the code below does this:<\/p>\n<pre class=\"lang:js decode:true \">\/\/gets the operations\r\nvar operations = code.match(\/(\\[.*\\])\/);\r\nvar cycle = operations[1].substr( 1, operations[1].length - 1).split( ',').map( function(v){ return parseInt( v );});\r\n\r\n\/\/gets the vertices count\r\nvar count;\r\nvar c = code.match( \/]([0-9]+)\/ );\r\nif( c == null ){\r\n    count = cycle.length;\r\n}else {\r\n    count = parseInt( c[1] ) * cycle.length;\r\n}\r\n<\/pre>\n<p>then you need to build <em>count\u00a0<\/em>vertices and bind them together:<\/p>\n<pre class=\"lang:js decode:true \">\/\/builds the vertices\r\nvar vertices = [];\r\nfor( var i = 0; i &lt; count; i++ ){\r\n    var v = new Vertex( {   x:Math.random() - .5,y:Math.random() - .5,z:Math.random() - .5 });\r\n    vertices.push( v );\r\n}\r\n\r\n\/\/build the inital chain\r\nvar edges = [];\r\nfor( i = 0; i &lt; count; i++ ){\r\n    var e = new Edge( vertices[i], vertices[(i+1)%vertices.length] );\r\n    edges.push( e );\r\n}<\/pre>\n<p>note that the vertices are distributed at random, the connections between 2 vertices will make the shape emerge.\u00a0finally we create new edges according to the chosen rule which gives:<\/p>\n<pre class=\"lang:js decode:true\">\/\/applies the rule\r\nvar ruleId = 0;\r\nfor( i = 0; i &lt; count; i++ ){\r\n\r\n    \/\/determine the id of the vertex to bind to the current vertex\r\n    var id = ( i + cycle[ ruleId % cycle.length ] ) % vertices.length;\r\n    if( id &lt; 0 )id += vertices.length;\r\n\r\n    \/\/create the new edge\r\n    edges.push( new Edge( vertices[i], vertices[id] ) );\r\n    ruleId++;\r\n}<\/pre>\n<p>then during the update, I use a cheap attraction repulsion routine (where\u00a0restLength is the target length for the edges):<\/p>\n<pre class=\"lang:js decode:true\">graph.vertices.forEach(function( a ) {\r\n    \/\/attraction\r\n    a.neighbours.forEach(function( b ) {\r\n\r\n        var dx = b.x - a.x;\r\n        var dy = b.y - a.y;\r\n        var dz = b.z - a.z;\r\n        var d = Math.sqrt(dx * dx + dy * dy + dz * dz);\r\n        var diff = ( restLength \/ a.neighbours.length  ) - d;\r\n        var offx = (diff * dx \/ d) * .5;\r\n        var offy = (diff * dy \/ d) * .5;\r\n        var offz = (diff * dz \/ d) * .5;\r\n        a.x -= offx;\r\n        a.y -= offy;\r\n        a.z -= offz;\r\n        b.x += offx;\r\n        b.y += offy;\r\n        b.z += offz;\r\n\r\n    });\r\n    \/\/repulsion\r\n    graph.vertices.forEach(function( b ) {\r\n\r\n        if( a == b ) return;\r\n        var dx = b.x - a.x;\r\n        var dy = b.y - a.y;\r\n        var dz = b.z - a.z;\r\n        var d = Math.sqrt(dx * dx + dy * dy + dz * dz);\r\n        var diff = restLength - d;\r\n        var offx = (diff * dx \/ d) * .01;\r\n        var offy = (diff * dy \/ d) * .01;\r\n        var offz = (diff * dz \/ d) * .01;\r\n        a.x -= offx;\r\n        a.y -= offy;\r\n        a.z -= offz;\r\n        b.x += offx;\r\n        b.y += offy;\r\n        b.z += offz;\r\n\r\n    });\r\n});<\/pre>\n<p>the length of the edge depends on the number of connections the vertex has, each vertex tends to repel the others at restLength and that&#8217;s about it. indeed some vector would be more elegant in this situation but it works\u00a0well enough for a little demo.\u00a0I like very much the <em>minimal complexity<\/em> of such graphs, some of them are\u00a0mind bending even with a very low amount of vertices\/edges, they would sure make interesting puzzles :)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>a Graph is a series of vertices (nodes) connected by edges (&#8220;segments&#8221;). it\u00a0can articulate\u00a0data, perform operations and provide insights on them.\u00a0data visualization makes massive use of 2d planar graphs (you&#8217;ve probably seen many Force Directed Graphs) but a display list, a 2D polygon,\u00a0a\u00a03D mesh, a roadmap are also graphs of some sort. in its simplest &#8230; <span class=\"more\"><a class=\"more-link\" href=\"http:\/\/barradeau.com\/blog\/?p=651\">[Read more&#8230;]<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":681,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"sharing_disabled":false,"spay_email":"","jetpack_publicize_message":""},"categories":[3],"tags":[],"jetpack_featured_media_url":"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2016\/01\/mst.png","jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/s4oXhx-graph","_links":{"self":[{"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/651"}],"collection":[{"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=651"}],"version-history":[{"count":40,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/651\/revisions"}],"predecessor-version":[{"id":1278,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/651\/revisions\/1278"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/media\/681"}],"wp:attachment":[{"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=651"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=651"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=651"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}