{"id":"ba00c5f358979e12","slug":"delaunator-shards","trashed":false,"description":"","likes":24,"publish_level":"public","forks":0,"fork_of":null,"has_importers":false,"update_time":"2020-02-02T20:13:52.950Z","first_public_version":null,"paused_version":null,"publish_time":"2020-01-27T20:01:30.738Z","publish_version":314,"latest_version":314,"thumbnail":"6598bfbf76fbc7e51790f287c26d11e0eac1e0b99fe96140f800c38effb0ce49","default_thumbnail":"31796a8e10101196835054f38a2b8ed09918f28abf3e4c7cd4b4edb6545ab120","roles":[],"sharing":null,"owner":{"id":"898d35f8860c7f03","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","login":"mootari","name":"Fabian Iwand","bio":"Web dev and tinkerer.","home_url":"https://twitter.com/mootari","type":"team","tier":"starter_2024"},"creator":{"id":"07362516b5994994","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","login":"mootari","name":"Fabian Iwand","bio":"Web dev and tinkerer.","home_url":"https://mootari.de/","tier":"pro"},"authors":[{"id":"07362516b5994994","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","name":"Fabian Iwand","login":"mootari","bio":"Web dev and tinkerer.","home_url":"https://mootari.de/","tier":"pro","approved":true,"description":""}],"collections":[{"id":"414312a67ea30ac3","type":"public","slug":"generative","title":"Generative","description":"Stuff with knobs to make it look more or less pretty.","update_time":"2019-08-24T08:59:06.382Z","pinned":true,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"79bb0336ecb16ea71310e28b7f5ffc1c077b45f4fdc27ada83d98653018a8daf","thumbnail":"79bb0336ecb16ea71310e28b7f5ffc1c077b45f4fdc27ada83d98653018a8daf","listing_count":59,"parent_collection_count":0,"owner":{"id":"898d35f8860c7f03","avatar_url":"https://avatars.observableusercontent.com/avatar/0d7defa821f38094c03bad23b9b360a5364e6e97e21fc238c39ddc48db7994ad","login":"mootari","name":"Fabian Iwand","bio":"Web dev and tinkerer.","home_url":"https://twitter.com/mootari","type":"team","tier":"starter_2024"}}],"files":[],"comments":[],"commenting_lock":null,"suggestion_from":null,"suggestions_to":[],"version":314,"title":"Delaunator Shards","license":null,"copyright":"","nodes":[{"id":0,"value":"md`# Delaunator Shards\n\nhttps://twitter.com/mootari/status/1220805531113349120\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":286,"value":"viewof gif = Object.assign(html`<label><input type=checkbox style=\"vertical-align:baseline\"> Render a GIF`, {\n  value: false,\n  oninput(e) { this.value = e.target.checked }\n})","pinned":false,"mode":"js","data":null,"name":null},{"id":281,"value":"render({gif, invalidation})","pinned":false,"mode":"js","data":null,"name":null},{"id":10,"value":"function* render(options = {}) {\n  const {\n    size: s = 600, // Canvas width and height\n    pad = -.1, // Padding factor\n    seed = 'b', // PRNG seed\n    count = 500, // Number of random points\n    duration = 4000, // Loop duration in ms\n    gif = false,\n    invalidation: invalidate = invalidation, // Render as GIF\n  } = options;\n  \n  const p = s * pad, sp = s - (p * 2);\n  const rng = seedrandom(seed);\n  // Add corner points.\n  const coords = [p,p, s-p,p, p,s-p, s-p,s-p];\n  for(let n = 0; n < count; n++) coords.push(p+sp*rng(), p+sp*rng());\n  \n  const delaunay = new Delaunator(new Float64Array(coords));\n  // Inflate triangle indices to coordinates to avoid lookups later on.\n  const offsets = new Float64Array(delaunay.triangles.length * 2);\n  for(let i = 0; i < delaunay.triangles.length; i++) {\n    offsets[i*2  ] = delaunay.coords[2*delaunay.triangles[i]];\n    offsets[i*2+1] = delaunay.coords[2*delaunay.triangles[i]+1];\n  }\n  \n  const ctx = DOM.context2d(s, s, 1);\n  ctx.canvas.style.maxWidth = '100%';\n  \n  if(gif) return yield* renderGif(invalidate, drawFrame, duration, {\n    // Prepares canvas and returns a reference.\n    preview: drawFrame(0),\n    // We could include other parameters here (e.g. some options).\n    filename: `delaunator-shards-${Date.now()/1000|0}`,\n    fps: 40,\n  });\n\n  // Live loop.\n  const to = Date.now();\n  while(true) {\n    yield drawFrame(((Date.now()-to)/duration) % 1);\n  }\n  \n  // Render callback, draws animation at timestep t [0..1].\n  function drawFrame(t) {\n    ctx.fillStyle = '#000';\n    ctx.fillRect(0, 0, s, s);\n    // Change direction.\n    t = 1-t;\n    \n    // Loop over all triangles in the order in which they were\n    // produced by Delaunator.\n    const o = offsets;\n    for(let i = 0; i < o.length; i += 6) {\n      const\n        // Local easing offset.\n        ti = (((i / o.length) ** .1) * 2 + t) % 1,\n        // Scale easing.\n        _t = clamp(0, 1, 1.5 * easeCos(ti ** 4 + .4)),\n        // Triangle points.\n        x0 = o[i  ], y0 = o[i+1],\n        x1 = o[i+2], y1 = o[i+3],\n        x2 = o[i+4], y2 = o[i+5],\n        // Triangle clones with transform origins and lightness.\n        origins = [\n          [x0, y0, .3],\n          [x1, y1, .5],\n          [x2, y2, 1],\n        ];\n      // Draw one triangle for each origin.\n      for(let [xc, yc, l] of origins) {\n        ctx.fillStyle = `hsl(0,0%,${_t * l * 100}%)`;\n        ctx.beginPath();\n        // Scale towards origin.\n        ctx.moveTo(mix(xc, x0, _t), mix(yc, y0, _t));\n        ctx.lineTo(mix(xc, x1, _t), mix(yc, y1, _t));\n        ctx.lineTo(mix(xc, x2, _t), mix(yc, y2, _t));\n        ctx.closePath();\n        ctx.fill();\n      }\n    }\n    return ctx.canvas;\n  }\n  \n}","pinned":true,"mode":"js","data":null,"name":null},{"id":269,"value":"md`---\n## Helpers`","pinned":false,"mode":"js","data":null,"name":null},{"id":276,"value":"function mix(a, b, t) {\n  return a + t * (b - a);\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":274,"value":"function clamp(a, b, v) {\n  return v < a ? a : v > b ? b : v;\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":272,"value":"function easeCos(t) {\n  return Math.cos(t * Math.PI * 2) * .5 + .5;\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":181,"value":"md`---\n## Dependencies\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":6,"value":"Delaunator = require('delaunator')","pinned":false,"mode":"js","data":null,"name":null},{"id":7,"value":"seedrandom = require('seedrandom')","pinned":false,"mode":"js","data":null,"name":null},{"id":176,"value":"import {renderGif} from '88a8246df5677bae'","pinned":false,"mode":"js","data":null,"name":null}],"resolutions":[],"schedule":null,"last_view_time":null}