<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":1001,"date":"2017-01-03T09:10:36","date_gmt":"2017-01-03T09:10:36","guid":{"rendered":"http:\/\/barradeau.com\/blog\/?p=1001"},"modified":"2017-01-03T17:55:33","modified_gmt":"2017-01-03T17:55:33","slug":"woven-maps","status":"publish","type":"post","link":"https:\/\/barradeau.com\/blog\/?p=1001","title":{"rendered":"woven maps"},"content":{"rendered":"<p>last christmas I\u00a0offered\u00a0a series of prints and plots as gifts to my family &amp; relatives. for some\u00a0of them, I used vector maps as a way to\u00a0<em>seed<\/em> a graphic post-processor. it looked like this:<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-1007 size-medium_large\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics0-768x768.jpg\" alt=\"pics0\" width=\"700\" height=\"700\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics0-768x768.jpg 768w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics0-150x150.jpg 150w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics0-300x300.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics0.jpg 1024w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>rue Lecourbe, Paris.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-1008 size-medium_large\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics1-768x768.jpg\" alt=\"pics1\" width=\"700\" height=\"700\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics1-768x768.jpg 768w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics1-150x150.jpg 150w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics1-300x300.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics1.jpg 1024w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>rue Pelleport, Paris.<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-1009 size-medium_large\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics3-768x768.jpg\" alt=\"pics3\" width=\"700\" height=\"700\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics3-768x768.jpg 768w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics3-150x150.jpg 150w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics3-300x300.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics3.jpg 1024w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>l&#8217;op\u00e9ra de Paris.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-1004 size-medium_large\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics2-768x768.jpg\" alt=\"pics2\" width=\"700\" height=\"700\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics2-768x768.jpg 768w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics2-150x150.jpg 150w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics2-300x300.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics2-1024x1024.jpg 1024w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics2.jpg 1570w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>detail<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-1006 size-large\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/Untitled-1-649x1024.jpg\" alt=\"untitled-1\" width=\"649\" height=\"1024\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/Untitled-1.jpg 649w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/Untitled-1-190x300.jpg 190w\" sizes=\"(max-width: 649px) 100vw, 649px\" \/><\/p>\n<p>Notre-Dame de Paris<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-1005 size-medium_large\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics4-768x768.jpg\" alt=\"pics4\" width=\"700\" height=\"700\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics4-768x768.jpg 768w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics4-150x150.jpg 150w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics4-300x300.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pics4.jpg 1024w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>these are the steps I followed:<\/p>\n<ol>\n<li>collect a set of points<\/li>\n<li>triangulate the set<\/li>\n<li>compute the triangles&#8217; edges&#8217; lengths<\/li>\n<li>draw the gradient. for a given height:\n<ol>\n<li>translate the canvas vertically and give it a\u00a0small opacity<\/li>\n<li>render only the edges which length is below a given threshold<\/li>\n<\/ol>\n<\/li>\n<li>draw a given amount of extra wireframes as 4 with a higher opacity<\/li>\n<li>overlay an additive picture<\/li>\n<li>render white wireframes &amp; glow<\/li>\n<\/ol>\n<p>it may seem complicated\u00a0but apart from the triangulation, it is mostly a <em>canvas<\/em>\u00a0work.<\/p>\n<p>the first 3 steps work together\u00a0in a function called <em>process()<\/em>, the 4 last steps are grouped under <em>render().<\/em><\/p>\n<p>first the code of the <em>process()<\/em> function<\/p>\n<pre class=\"lang:js decode:true\">\/\/#1 we collected some points\r\nfunction process( points ) {\r\n\r\n    \/\/not enough points: bail out\r\n    if (points.length &lt; 3)return;\r\n    \r\n    \/\/there are enough points to compute something\r\n    \/\/first make sure there are no duplicates ( create a point set )\r\n    points = cleanup(points);\r\n\r\n\/\/#2 second compute the delaunay triangulation on the set\r\n    var tris = delaunay.compute(points);\r\n\r\n\/\/#3 compute the edges lengths and associate the endpoints\r\n    edges = [];\r\n    for (var i = 0; i &lt; tris.length; i += 3) {\r\n        var p0 = points[tris[i]];\r\n        var p1 = points[tris[i+1]];\r\n        var p2 = points[tris[i+2]];\r\n        edges.push(\r\n            [distance(p0, p1), p0, p1],\r\n            [distance(p1, p2), p1, p2],\r\n            [distance(p2, p0), p2, p0]\r\n        );\r\n    }\r\n    render();\r\n}<\/pre>\n<h3>1 collect a set of points<\/h3>\n<p>this is a variably trivial task, in the demo below I collect the X\/Y corrdinates of the mouse\/finger. for the actual pieces, I used the vector data of the <a href=\"https:\/\/mapzen.com\/projects\/vector-tiles\/\">Mapzen API<\/a>,\u00a0it could basically be anything in 2D. One restriction though, because of the next step, we need a\u00a0<em>set <\/em>and not only a<em> list<\/em> ; the difference being that a<em> set<\/em> doesn&#8217;t contain duplicates.\u00a0<em>cleanup()<\/em> is a method that removes duplicate\u00a0points from a list.<\/p>\n<h3>2 triangulation<\/h3>\n<p>the heavy lifting, I used <a href=\"https:\/\/github.com\/ironwallaby\/delaunay\/blob\/master\/delaunay.js\" target=\"_blank\">this delaunay triangulation<\/a> lib, even though it&#8217;s not extra robust, it works in most cases.<\/p>\n<h3>3 compute the triangles&#8217; edges&#8217; lengths<\/h3>\n<p>again, not much to say, only storing the lengths of each edge and the 2 vertices they&#8217;re made of in an array to batch draw\u00a0them without having to compute their length each time (computing edges&#8217; lengths all the time is not necessary as\u00a0they\u00a0don&#8217;t change from a render to the next).<\/p>\n<p>when <em>process()<\/em> is over, we call the <em>render()<\/em> method<\/p>\n<pre class=\"lang:js decode:true\">function render() {\r\n\r\n    \/\/reset the canvas\r\n    ctx.restore();\r\n    ctx.save();\r\n    ctx.globalAlpha = 1;\r\n    ctx.fillStyle = \"#FFF\";\r\n    ctx.fillRect(0, 0, w, h);\r\n\r\n\/\/#4 draw the gradient\r\n    ctx.save();\r\n    var max = config.height;\r\n    for (i = 0; i &lt; max; i++) {\r\n\r\n        \/\/#4.1\r\n        ctx.translate(0, 1);\r\n        ctx.globalAlpha = ( 1 - i \/ max ) * 0.05;\r\n\r\n        \/\/#4.2\r\n        renderEdges(edges, i);\r\n    }\r\n    ctx.restore();\r\n\r\n\/\/#5 render the wireframes\r\n    if (config.wireframe) {\r\n\r\n        var m = config.wireCount;\r\n        for (i = 0; i &lt; m; i++) {\r\n\r\n            var t = ( i \/ m );\r\n            ctx.save();\r\n            ctx.translate(0, max * ( 1 - t ));\r\n            ctx.globalAlpha = .05 + .15 * t;\r\n            renderEdges(edges, (i * 10) );\r\n            ctx.restore();\r\n        }\r\n    }\r\n<\/pre>\n<h3>steps 4 &amp; 5<\/h3>\n<p>both work the same : the loops will translate the canvas by a certain amount, change the opacity and call the <em>renderEdges()<\/em>\u00a0method many times.<\/p>\n<p><em>renderEdges( edges, min )<\/em><\/p>\n<pre class=\"lang:js decode:true\">\/\/the method to render edges \r\nfunction renderEdges(edges, min ){\r\n    if( edges.length == 0 )return;\r\n    ctx.beginPath();\r\n\r\n    \/\/for each edge of the list\r\n    for( var i=0; i &lt; edges.length; i++ ){\r\n        var edge = edges[ i ];\r\n\r\n        \/\/if the edge's length is inferior to the given threshold\r\n        if( edge[ 0 ] &lt; min ){\r\n\r\n            \/\/draw a line between endpoints\r\n            ctx.moveTo( edge[ 1 ].x, edge[ 1 ].y);\r\n            ctx.lineTo( edge[ 2 ].x, edge[ 2 ].y);\r\n        }\r\n    }\r\n    ctx.stroke();\r\n}<\/pre>\n<p>for each computed edge, we check its length against a <em>min<\/em> value and, if the edge is shorter than the value, we\u00a0draw it otherwise, we continue.<\/p>\n<p>in the #4 loop, <em>min<\/em>\u00a0is slowly incremented while in #5 it jumps from 10 to 10. the smaller the value of min, the <em>denser<\/em> the mesh, the bigger the value of <em>min<\/em> the<em> sparser <\/em>the\u00a0mesh<em>.<\/em><\/p>\n<p>this does most of the work, the rest is a cosmetic layer:<\/p>\n<pre class=\"lang:js decode:true\">\/\/#6 draw a color overlay\r\n    var cw = canvas.width;\r\n    var ch = canvas.height;\r\n    ctx.save();\r\n    ctx.globalAlpha = 1;\r\n    ctx.globalCompositeOperation = \"screen\";\r\n    \/\/draw the image over the whole image\r\n    ctx.drawImage(img, 0, 0, img.width, img.height, 0,0,cw,ch);\r\n    ctx.restore();\r\n\r\n\/\/#7 white wireframe\r\n    if( config.glow ){\r\n        ctx.globalCompositeOperation = \"source-over\";\r\n        ctx.strokeStyle = \"#FFF\";\r\n        ctx.globalAlpha = .2;\r\n        renderEdges(edges, config.glowSize \/ 2);\r\n\r\n        \/\/white glow\r\n        ctx.globalCompositeOperation = \"screen\";\r\n        ctx.globalAlpha = 1;\r\n        ctx.filter = \"blur( 6px )\";\r\n        renderEdges(edges, config.glowSize );\r\n    }\r\n\r\n}<\/pre>\n<h3>6 overlay<\/h3>\n<p>this is a single <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/CanvasRenderingContext2D\/drawImage\">context.drawImage()<\/a> call\u00a0with the context&#8217;s <a href=\"https:\/\/developer.mozilla.org\/fr\/docs\/Web\/API\/CanvasRenderingContext2D\/globalCompositeOperation\">globalCompositeOperation\u00a0<\/a>set to\u00a0&#8220;screen&#8221;. the 9\u00a0arguments of the drawImage call are: the image to draw, the 4 value of the source rectangle and the 4 values of the destination rectangle. I&#8217;m using a variety of images from this <a href=\"https:\/\/www.pexels.com\/search\/forest\/\">license free website<\/a>\u00a0and consequently downscale\u00a0them. this is the one I use in the demo below\u00a0<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1019 size-full\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/pattern.png\" alt=\"pattern\" width=\"32\" height=\"32\" \/>. as the image is very small and is stretched over a big area, the bilinear interpolation used by drawImage() prevents the\u00a0end result from pixelating\u00a0(it could be nice though&#8230;)<\/p>\n<h3>7\u00a0glow<\/h3>\n<p>the glow can be obtained by drawing 2 versions of the edges: one\u00a0regular, the other with a wider strokeStyle &amp; a blur filter.<\/p>\n<p>here&#8217;s a demo if you want to try it out:<br \/>\n<iframe loading=\"lazy\" width=\"100%\" height=\"800px\" src=\"http:\/\/www.barradeau.com\/2017\/wovenmaps\/index.html\"><\/iframe><\/p>\n<p>you can download a zip <a href=\"http:\/\/www.barradeau.com\/2017\/wovenmaps\/woven_map.zip\">here<\/a>\u00a0and check the <a href=\"http:\/\/www.barradeau.com\/2017\/wovenmaps\/index.html\">demo page here<\/a>.<br \/>\ndespite it&#8217;s simplicty and the limited amount of parameters, it&#8217;s quite a versatile process. here are some doodles obtained with the above demo.<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-1022\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t1.jpg\" alt=\"t1\" width=\"800\" height=\"584\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t1.jpg 800w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t1-300x219.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t1-768x561.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-1023\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t2.jpg\" alt=\"t2\" width=\"800\" height=\"584\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t2.jpg 800w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t2-300x219.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t2-768x561.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-1024\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t3.jpg\" alt=\"t3\" width=\"800\" height=\"584\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t3.jpg 800w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t3-300x219.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t3-768x561.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-1021\" src=\"http:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t0.jpg\" alt=\"t0\" width=\"800\" height=\"584\" srcset=\"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t0.jpg 800w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t0-300x219.jpg 300w, https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/t0-768x561.jpg 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/p>\n<p>and that was &#8230;\u00a0no wait!<\/p>\n<p>there are some prints for sale here\u00a0<a href=\"https:\/\/society6.com\/barradeau\/collection\/woven-maps\">https:\/\/society6.com\/barradeau\/collection\/woven-maps<\/a>\u00a0if you would like a place to appear in the list, let me know :)<\/p>\n<p>NOW that\u00a0was it\u00a0:)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>last christmas I\u00a0offered\u00a0a series of prints and plots as gifts to my family &amp; relatives. for some\u00a0of them, I used vector maps as a way to\u00a0seed a graphic post-processor. it looked like this: rue Lecourbe, Paris. rue Pelleport, Paris. l&#8217;op\u00e9ra de Paris. detail Notre-Dame de Paris these are the steps I followed: collect a set &#8230; <span class=\"more\"><a class=\"more-link\" href=\"https:\/\/barradeau.com\/blog\/?p=1001\">[Read more&#8230;]<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":1010,"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":"https:\/\/barradeau.com\/blog\/wp-content\/uploads\/2017\/01\/cover.jpg","jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/p4oXhx-g9","_links":{"self":[{"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1001"}],"collection":[{"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1001"}],"version-history":[{"count":15,"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1001\/revisions"}],"predecessor-version":[{"id":1030,"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1001\/revisions\/1030"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=\/wp\/v2\/media\/1010"}],"wp:attachment":[{"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1001"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1001"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/barradeau.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1001"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}