made with requirebin
Created
May 21, 2017 08:16
-
-
Save davidguttman/64d21543caa237d53ba1d2248d23be88 to your computer and use it in GitHub Desktop.
requirebin sketch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var Delaunator = require('delaunator') | |
| var SPEED = 0.20 | |
| var N_POINTS = 550 | |
| var width = window.innerWidth | |
| var height = window.innerHeight | |
| document.body.style.background = 'rgb(20, 20, 20)' | |
| var points = createPoints(N_POINTS, width, height) | |
| points.forEach(function (point) { | |
| // document.body.appendChild(point.el) | |
| }) | |
| var triangles = createTrianges(points) | |
| document.body.appendChild(triangles.el) | |
| requestAnimationFrame(updateLoop) | |
| function updateLoop () { | |
| requestAnimationFrame(updateLoop) | |
| points.forEach(function (point) { | |
| var theta = (0.5 - Math.random()) * (Math.PI / 16) | |
| point.move(theta, SPEED) | |
| }) | |
| triangles.update(points) | |
| } | |
| function createPoints (n, w, h) { | |
| var points = [] | |
| var x, y | |
| for (var i = 0; i < n; i++) { | |
| x = Math.floor(Math.random() * w) | |
| y = Math.floor(Math.random() * h) | |
| points.push(createPoint([x, y])) | |
| } | |
| return points | |
| } | |
| function createPoint (coords) { | |
| var w = 20 | |
| var h = w | |
| var el = document.createElement('div') | |
| el.style.position = 'absolute' | |
| el.style.left = coords[0] + 'px' | |
| el.style.top = coords[1] + 'px' | |
| el.style.width = w + 'px' | |
| el.style.height = h + 'px' | |
| el.style.borderRadius = w / 2 + 'px' | |
| el.style.background = '#aaa' | |
| var point = { | |
| el: el, | |
| x: coords[0], | |
| y: coords[1], | |
| theta: Math.random() * 2 * Math.PI, | |
| move: move | |
| } | |
| return point | |
| } | |
| function mod (a, n) { | |
| return ((a % n) + n) % n | |
| } | |
| function move (theta, r) { | |
| this.theta += theta | |
| this.x += r * Math.cos(this.theta) | |
| this.y += r * Math.sin(this.theta) | |
| this.x = mod(this.x, width) | |
| this.y = mod(this.y, height) | |
| this.el.style.left = this.x + 'px' | |
| this.el.style.top = this.y + 'px' | |
| } | |
| function createTrianges (points) { | |
| var stroke = '#ccc' | |
| var fill = 'rgba(0,0,0,0)' | |
| var viewBox = [0, 0, width, height].join(' ') | |
| var pathData = createPathData(points) | |
| var parent = document.createElement('div') | |
| parent.innerHTML = ` | |
| <svg xmlns='http://www.w3.org/svg/2000' | |
| viewBox='${viewBox}' | |
| width=${width} | |
| height=${height} | |
| stroke='${stroke}' | |
| fill='${fill}'> | |
| <path d='${pathData}' /> | |
| </svg> | |
| ` | |
| return { | |
| el: parent, | |
| path: parent.querySelector('path'), | |
| update: update | |
| } | |
| } | |
| function createPathData (points) { | |
| var coords = points.map(function (point) { | |
| return [point.x, point.y] | |
| }) | |
| var triangles = new Delaunator(coords).triangles | |
| var pathData = [] | |
| var x0, y0, x1, y1, x2, y2 | |
| for (var i = 0; i < triangles.length; i += 3) { | |
| x0 = coords[triangles[i]][0] | |
| y0 = coords[triangles[i]][1] | |
| x1 = coords[triangles[i + 1]][0] | |
| y1 = coords[triangles[i + 1]][1] | |
| x2 = coords[triangles[i + 2]][0] | |
| y2 = coords[triangles[i + 2]][1] | |
| pathData.push('M') | |
| pathData.push(x0) | |
| pathData.push(y0) | |
| pathData.push('L') | |
| pathData.push(x1) | |
| pathData.push(y1) | |
| pathData.push('L') | |
| pathData.push(x2) | |
| pathData.push(y2) | |
| pathData.push('L') | |
| pathData.push(x0) | |
| pathData.push(y0) | |
| } | |
| return pathData.join(' ') | |
| } | |
| function update (points) { | |
| this.path.setAttribute('d', createPathData(points)) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| setTimeout(function(){ | |
| ;require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({"delaunator":[function(require,module,exports){ | |
| 'use strict'; | |
| module.exports = Delaunator; | |
| function Delaunator(points, getX, getY) { | |
| if (!getX) getX = defaultGetX; | |
| if (!getY) getY = defaultGetY; | |
| var minX = Infinity; | |
| var minY = Infinity; | |
| var maxX = -Infinity; | |
| var maxY = -Infinity; | |
| var coords = this.coords = []; | |
| var ids = this.ids = new Uint32Array(points.length); | |
| for (var i = 0; i < points.length; i++) { | |
| var p = points[i]; | |
| var x = getX(p); | |
| var y = getY(p); | |
| ids[i] = i; | |
| coords[2 * i] = x; | |
| coords[2 * i + 1] = y; | |
| if (x < minX) minX = x; | |
| if (y < minY) minY = y; | |
| if (x > maxX) maxX = x; | |
| if (y > maxY) maxY = y; | |
| } | |
| var cx = (minX + maxX) / 2; | |
| var cy = (minY + maxY) / 2; | |
| var minDist = Infinity; | |
| var i0, i1, i2; | |
| // pick a seed point close to the centroid | |
| for (i = 0; i < points.length; i++) { | |
| var d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]); | |
| if (d < minDist) { | |
| i0 = i; | |
| minDist = d; | |
| } | |
| } | |
| minDist = Infinity; | |
| // find the point closest to the seed | |
| for (i = 0; i < points.length; i++) { | |
| if (i === i0) continue; | |
| d = dist(coords[2 * i0], coords[2 * i0 + 1], coords[2 * i], coords[2 * i + 1]); | |
| if (d < minDist && d > 0) { | |
| i1 = i; | |
| minDist = d; | |
| } | |
| } | |
| var minRadius = Infinity; | |
| // find the third point which forms the smallest circumcircle with the first two | |
| for (i = 0; i < points.length; i++) { | |
| if (i === i0 || i === i1) continue; | |
| var r = circumradius( | |
| coords[2 * i0], coords[2 * i0 + 1], | |
| coords[2 * i1], coords[2 * i1 + 1], | |
| coords[2 * i], coords[2 * i + 1]); | |
| if (r < minRadius) { | |
| i2 = i; | |
| minRadius = r; | |
| } | |
| } | |
| if (minRadius === Infinity) { | |
| throw new Error('No Delaunay triangulation exists for this input.'); | |
| } | |
| // swap the order of the seed points for counter-clockwise orientation | |
| if (area(coords[2 * i0], coords[2 * i0 + 1], | |
| coords[2 * i1], coords[2 * i1 + 1], | |
| coords[2 * i2], coords[2 * i2 + 1]) < 0) { | |
| var tmp = i1; | |
| i1 = i2; | |
| i2 = tmp; | |
| } | |
| var i0x = coords[2 * i0]; | |
| var i0y = coords[2 * i0 + 1]; | |
| var i1x = coords[2 * i1]; | |
| var i1y = coords[2 * i1 + 1]; | |
| var i2x = coords[2 * i2]; | |
| var i2y = coords[2 * i2 + 1]; | |
| var center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y); | |
| this._cx = center.x; | |
| this._cy = center.y; | |
| // sort the points by distance from the seed triangle circumcenter | |
| quicksort(ids, coords, 0, ids.length - 1, center.x, center.y); | |
| // initialize a hash table for storing edges of the advancing convex hull | |
| this._hashSize = Math.ceil(Math.sqrt(points.length)); | |
| this._hash = []; | |
| for (i = 0; i < this._hashSize; i++) this._hash[i] = null; | |
| // initialize a circular doubly-linked list that will hold an advancing convex hull | |
| var e = this.hull = insertNode(coords, i0); | |
| this._hashEdge(e); | |
| e.t = 0; | |
| e = insertNode(coords, i1, e); | |
| this._hashEdge(e); | |
| e.t = 1; | |
| e = insertNode(coords, i2, e); | |
| this._hashEdge(e); | |
| e.t = 2; | |
| var maxTriangles = 2 * points.length - 5; | |
| var triangles = this.triangles = new Uint32Array(maxTriangles * 3); | |
| triangles[0] = i0; | |
| triangles[1] = i1; | |
| triangles[2] = i2; | |
| this.trianglesLen = 3; | |
| var adjacent = this.adjacent = new Int32Array(maxTriangles * 3); | |
| adjacent[0] = -1; | |
| adjacent[1] = -1; | |
| adjacent[2] = -1; | |
| var xp, yp; | |
| for (var k = 0; k < ids.length; k++) { | |
| i = ids[k]; | |
| x = coords[2 * i]; | |
| y = coords[2 * i + 1]; | |
| // skip duplicate points | |
| if (x === xp && y === yp) continue; | |
| xp = x; | |
| yp = y; | |
| // skip seed triangle points | |
| if ((x === i0x && y === i0y) || | |
| (x === i1x && y === i1y) || | |
| (x === i2x && y === i2y)) continue; | |
| // find a visible edge on the convex hull using edge hash | |
| var startKey = this._hashKey(x, y); | |
| var key = startKey; | |
| var start; | |
| do { | |
| start = this._hash[key]; | |
| key = (key + 1) % this._hashSize; | |
| } while ((!start || start.removed) && key !== startKey); | |
| e = start; | |
| while (area(x, y, e.x, e.y, e.next.x, e.next.y) >= 0) { | |
| e = e.next; | |
| if (e === start) { | |
| throw new Error('Something is wrong with the input points.'); | |
| } | |
| } | |
| var walkBack = e === start; | |
| // add the first triangle from the point | |
| var t = this._addTriangle(i, e); | |
| adjacent[t] = -1; | |
| adjacent[t + 1] = -1; | |
| this._link(t + 2, e.t); | |
| e.t = t; // keep track of boundary triangles on the hull | |
| e = insertNode(coords, i, e); | |
| // recursively flip triangles from the point until they satisfy the Delaunay condition | |
| e.t = this._legalize(t + 2); | |
| // walk forward through the hull, adding more triangles and flipping recursively | |
| var q = e.next; | |
| while (area(x, y, q.x, q.y, q.next.x, q.next.y) < 0) { | |
| t = this._addTriangle(i, q); | |
| this._link(t, q.prev.t); | |
| adjacent[t + 1] = -1; | |
| this._link(t + 2, q.t); | |
| q.prev.t = this._legalize(t + 2); | |
| this.hull = removeNode(q); | |
| q = q.next; | |
| } | |
| if (walkBack) { | |
| // walk backward from the other side, adding more triangles and flipping | |
| q = e.prev; | |
| while (area(x, y, q.prev.x, q.prev.y, q.x, q.y) < 0) { | |
| t = this._addTriangle(i, q.prev); | |
| adjacent[t] = -1; | |
| this._link(t + 1, q.t); | |
| this._link(t + 2, q.prev.t); | |
| this._legalize(t + 2); | |
| q.prev.t = t; | |
| this.hull = removeNode(q); | |
| q = q.prev; | |
| } | |
| } | |
| // save the two new edges in the hash table | |
| this._hashEdge(e); | |
| this._hashEdge(e.prev); | |
| } | |
| // trim typed triangle mesh arrays | |
| this.triangles = triangles.subarray(0, this.trianglesLen); | |
| this.adjacent = adjacent.subarray(0, this.trianglesLen); | |
| } | |
| Delaunator.prototype = { | |
| _hashEdge: function (e) { | |
| this._hash[this._hashKey(e.x, e.y)] = e; | |
| }, | |
| _hashKey: function (x, y) { | |
| var dx = x - this._cx; | |
| var dy = y - this._cy; | |
| // use pseudo-angle: a measure that monotonically increases | |
| // with real angle, but doesn't require expensive trigonometry | |
| var p = 1 - dx / (Math.abs(dx) + Math.abs(dy)); | |
| return Math.floor((2 + (dy < 0 ? -p : p)) * (this._hashSize / 4)); | |
| }, | |
| _legalize: function (a) { | |
| var triangles = this.triangles; | |
| var coords = this.coords; | |
| var adjacent = this.adjacent; | |
| var b = adjacent[a]; | |
| var a0 = a - a % 3; | |
| var b0 = b - b % 3; | |
| var al = a0 + (a + 1) % 3; | |
| var ar = a0 + (a + 2) % 3; | |
| var br = b0 + (b + 1) % 3; | |
| var bl = b0 + (b + 2) % 3; | |
| var p0 = triangles[ar]; | |
| var pr = triangles[a]; | |
| var pl = triangles[al]; | |
| var p1 = triangles[bl]; | |
| var illegal = inCircle( | |
| coords[2 * p0], coords[2 * p0 + 1], | |
| coords[2 * pr], coords[2 * pr + 1], | |
| coords[2 * pl], coords[2 * pl + 1], | |
| coords[2 * p1], coords[2 * p1 + 1]); | |
| if (illegal) { | |
| triangles[a] = p1; | |
| triangles[b] = p0; | |
| this._link(a, adjacent[bl]); | |
| this._link(b, adjacent[ar]); | |
| this._link(ar, bl); | |
| this._legalize(a); | |
| return this._legalize(br); | |
| } | |
| return ar; | |
| }, | |
| _link: function (a, b) { | |
| this.adjacent[a] = b; | |
| if (b !== -1) this.adjacent[b] = a; | |
| }, | |
| _addTriangle(i, e) { | |
| var t = this.trianglesLen; | |
| this.triangles[t] = e.i; | |
| this.triangles[t + 1] = i; | |
| this.triangles[t + 2] = e.next.i; | |
| this.trianglesLen += 3; | |
| return t; | |
| } | |
| }; | |
| function dist(ax, ay, bx, by) { | |
| var dx = ax - bx; | |
| var dy = ay - by; | |
| return dx * dx + dy * dy; | |
| } | |
| function area(px, py, qx, qy, rx, ry) { | |
| return (qy - py) * (rx - qx) - (qx - px) * (ry - qy); | |
| } | |
| function inCircle(ax, ay, bx, by, cx, cy, px, py) { | |
| ax -= px; | |
| ay -= py; | |
| bx -= px; | |
| by -= py; | |
| cx -= px; | |
| cy -= py; | |
| var ap = ax * ax + ay * ay; | |
| var bp = bx * bx + by * by; | |
| var cp = cx * cx + cy * cy; | |
| var det = ax * (by * cp - bp * cy) - | |
| ay * (bx * cp - bp * cx) + | |
| ap * (bx * cy - by * cx); | |
| return det < 0; | |
| } | |
| function circumradius(ax, ay, bx, by, cx, cy) { | |
| bx -= ax; | |
| by -= ay; | |
| cx -= ax; | |
| cy -= ay; | |
| var bl = bx * bx + by * by; | |
| var cl = cx * cx + cy * cy; | |
| if (bl === 0 || cl === 0) return Infinity; | |
| var d = bx * cy - by * cx; | |
| if (d === 0) return Infinity; | |
| var x = (cy * bl - by * cl) * 0.5 / d; | |
| var y = (bx * cl - cx * bl) * 0.5 / d; | |
| return x * x + y * y; | |
| } | |
| function circumcenter(ax, ay, bx, by, cx, cy) { | |
| bx -= ax; | |
| by -= ay; | |
| cx -= ax; | |
| cy -= ay; | |
| var bl = bx * bx + by * by; | |
| var cl = cx * cx + cy * cy; | |
| var d = bx * cy - by * cx; | |
| var x = (cy * bl - by * cl) * 0.5 / d; | |
| var y = (bx * cl - cx * bl) * 0.5 / d; | |
| return { | |
| x: ax + x, | |
| y: ay + y | |
| }; | |
| } | |
| // create a new node in a doubly linked list | |
| function insertNode(coords, i, prev) { | |
| var node = { | |
| i: i, | |
| x: coords[2 * i], | |
| y: coords[2 * i + 1], | |
| t: 0, | |
| prev: null, | |
| next: null, | |
| removed: false | |
| }; | |
| if (!prev) { | |
| node.prev = node; | |
| node.next = node; | |
| } else { | |
| node.next = prev.next; | |
| node.prev = prev; | |
| prev.next.prev = node; | |
| prev.next = node; | |
| } | |
| return node; | |
| } | |
| function removeNode(node) { | |
| node.prev.next = node.next; | |
| node.next.prev = node.prev; | |
| node.removed = true; | |
| return node.prev; | |
| } | |
| function quicksort(ids, coords, left, right, cx, cy) { | |
| var i, j, temp; | |
| if (right - left <= 20) { | |
| for (i = left + 1; i <= right; i++) { | |
| temp = ids[i]; | |
| j = i - 1; | |
| while (j >= left && compare(coords, ids[j], temp, cx, cy) > 0) ids[j + 1] = ids[j--]; | |
| ids[j + 1] = temp; | |
| } | |
| } else { | |
| var median = (left + right) >> 1; | |
| i = left + 1; | |
| j = right; | |
| swap(ids, median, i); | |
| if (compare(coords, ids[left], ids[right], cx, cy) > 0) swap(ids, left, right); | |
| if (compare(coords, ids[i], ids[right], cx, cy) > 0) swap(ids, i, right); | |
| if (compare(coords, ids[left], ids[i], cx, cy) > 0) swap(ids, left, i); | |
| temp = ids[i]; | |
| while (true) { | |
| do i++; while (compare(coords, ids[i], temp, cx, cy) < 0); | |
| do j--; while (compare(coords, ids[j], temp, cx, cy) > 0); | |
| if (j < i) break; | |
| swap(ids, i, j); | |
| } | |
| ids[left + 1] = ids[j]; | |
| ids[j] = temp; | |
| if (right - i + 1 >= j - left) { | |
| quicksort(ids, coords, i, right, cx, cy); | |
| quicksort(ids, coords, left, j - 1, cx, cy); | |
| } else { | |
| quicksort(ids, coords, left, j - 1, cx, cy); | |
| quicksort(ids, coords, i, right, cx, cy); | |
| } | |
| } | |
| } | |
| function compare(coords, i, j, cx, cy) { | |
| var d1 = dist(coords[2 * i], coords[2 * i + 1], cx, cy); | |
| var d2 = dist(coords[2 * j], coords[2 * j + 1], cx, cy); | |
| return (d1 - d2) || (coords[2 * i] - coords[2 * j]) || (coords[2 * i + 1] - coords[2 * j + 1]); | |
| } | |
| function swap(arr, i, j) { | |
| var tmp = arr[i]; | |
| arr[i] = arr[j]; | |
| arr[j] = tmp; | |
| } | |
| function defaultGetX(p) { | |
| return p[0]; | |
| } | |
| function defaultGetY(p) { | |
| return p[1]; | |
| } | |
| },{}]},{},[]) | |
| //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2hvbWUvYWRtaW4vYnJvd3NlcmlmeS1jZG4vbm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2Jyb3dzZXItcGFjay9fcHJlbHVkZS5qcyIsImRlbGF1bmF0b3IiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiJ3VzZSBzdHJpY3QnO1xuXG5tb2R1bGUuZXhwb3J0cyA9IERlbGF1bmF0b3I7XG5cbmZ1bmN0aW9uIERlbGF1bmF0b3IocG9pbnRzLCBnZXRYLCBnZXRZKSB7XG5cbiAgICBpZiAoIWdldFgpIGdldFggPSBkZWZhdWx0R2V0WDtcbiAgICBpZiAoIWdldFkpIGdldFkgPSBkZWZhdWx0R2V0WTtcblxuICAgIHZhciBtaW5YID0gSW5maW5pdHk7XG4gICAgdmFyIG1pblkgPSBJbmZpbml0eTtcbiAgICB2YXIgbWF4WCA9IC1JbmZpbml0eTtcbiAgICB2YXIgbWF4WSA9IC1JbmZpbml0eTtcblxuICAgIHZhciBjb29yZHMgPSB0aGlzLmNvb3JkcyA9IFtdO1xuICAgIHZhciBpZHMgPSB0aGlzLmlkcyA9IG5ldyBVaW50MzJBcnJheShwb2ludHMubGVuZ3RoKTtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcG9pbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBwID0gcG9pbnRzW2ldO1xuICAgICAgICB2YXIgeCA9IGdldFgocCk7XG4gICAgICAgIHZhciB5ID0gZ2V0WShwKTtcbiAgICAgICAgaWRzW2ldID0gaTtcbiAgICAgICAgY29vcmRzWzIgKiBpXSA9IHg7XG4gICAgICAgIGNvb3Jkc1syICogaSArIDFdID0geTtcbiAgICAgICAgaWYgKHggPCBtaW5YKSBtaW5YID0geDtcbiAgICAgICAgaWYgKHkgPCBtaW5ZKSBtaW5ZID0geTtcbiAgICAgICAgaWYgKHggPiBtYXhYKSBtYXhYID0geDtcbiAgICAgICAgaWYgKHkgPiBtYXhZKSBtYXhZID0geTtcbiAgICB9XG5cbiAgICB2YXIgY3ggPSAobWluWCArIG1heFgpIC8gMjtcbiAgICB2YXIgY3kgPSAobWluWSArIG1heFkpIC8gMjtcblxuICAgIHZhciBtaW5EaXN0ID0gSW5maW5pdHk7XG4gICAgdmFyIGkwLCBpMSwgaTI7XG5cbiAgICAvLyBwaWNrIGEgc2VlZCBwb2ludCBjbG9zZSB0byB0aGUgY2VudHJvaWRcbiAgICBmb3IgKGkgPSAwOyBpIDwgcG9pbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBkID0gZGlzdChjeCwgY3ksIGNvb3Jkc1syICogaV0sIGNvb3Jkc1syICogaSArIDFdKTtcbiAgICAgICAgaWYgKGQgPCBtaW5EaXN0KSB7XG4gICAgICAgICAgICBpMCA9IGk7XG4gICAgICAgICAgICBtaW5EaXN0ID0gZDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG1pbkRpc3QgPSBJbmZpbml0eTtcblxuICAgIC8vIGZpbmQgdGhlIHBvaW50IGNsb3Nlc3QgdG8gdGhlIHNlZWRcbiAgICBmb3IgKGkgPSAwOyBpIDwgcG9pbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChpID09PSBpMCkgY29udGludWU7XG4gICAgICAgIGQgPSBkaXN0KGNvb3Jkc1syICogaTBdLCBjb29yZHNbMiAqIGkwICsgMV0sIGNvb3Jkc1syICogaV0sIGNvb3Jkc1syICogaSArIDFdKTtcbiAgICAgICAgaWYgKGQgPCBtaW5EaXN0ICYmIGQgPiAwKSB7XG4gICAgICAgICAgICBpMSA9IGk7XG4gICAgICAgICAgICBtaW5EaXN0ID0gZDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHZhciBtaW5SYWRpdXMgPSBJbmZpbml0eTtcblxuICAgIC8vIGZpbmQgdGhlIHRoaXJkIHBvaW50IHdoaWNoIGZvcm1zIHRoZSBzbWFsbGVzdCBjaXJjdW1jaXJjbGUgd2l0aCB0aGUgZmlyc3QgdHdvXG4gICAgZm9yIChpID0gMDsgaSA8IHBvaW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAoaSA9PT0gaTAgfHwgaSA9PT0gaTEpIGNvbnRpbnVlO1xuXG4gICAgICAgIHZhciByID0gY2lyY3VtcmFkaXVzKFxuICAgICAgICAgICAgY29vcmRzWzIgKiBpMF0sIGNvb3Jkc1syICogaTAgKyAxXSxcbiAgICAgICAgICAgIGNvb3Jkc1syICogaTFdLCBjb29yZHNbMiAqIGkxICsgMV0sXG4gICAgICAgICAgICBjb29yZHNbMiAqIGldLCBjb29yZHNbMiAqIGkgKyAxXSk7XG5cbiAgICAgICAgaWYgKHIgPCBtaW5SYWRpdXMpIHtcbiAgICAgICAgICAgIGkyID0gaTtcbiAgICAgICAgICAgIG1pblJhZGl1cyA9IHI7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAobWluUmFkaXVzID09PSBJbmZpbml0eSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIERlbGF1bmF5IHRyaWFuZ3VsYXRpb24gZXhpc3RzIGZvciB0aGlzIGlucHV0LicpO1xuICAgIH1cblxuICAgIC8vIHN3YXAgdGhlIG9yZGVyIG9mIHRoZSBzZWVkIHBvaW50cyBmb3IgY291bnRlci1jbG9ja3dpc2Ugb3JpZW50YXRpb25cbiAgICBpZiAoYXJlYShjb29yZHNbMiAqIGkwXSwgY29vcmRzWzIgKiBpMCArIDFdLFxuICAgICAgICAgICAgIGNvb3Jkc1syICogaTFdLCBjb29yZHNbMiAqIGkxICsgMV0sXG4gICAgICAgICAgICAgY29vcmRzWzIgKiBpMl0sIGNvb3Jkc1syICogaTIgKyAxXSkgPCAwKSB7XG5cbiAgICAgICAgdmFyIHRtcCA9IGkxO1xuICAgICAgICBpMSA9IGkyO1xuICAgICAgICBpMiA9IHRtcDtcbiAgICB9XG5cbiAgICB2YXIgaTB4ID0gY29vcmRzWzIgKiBpMF07XG4gICAgdmFyIGkweSA9IGNvb3Jkc1syICogaTAgKyAxXTtcbiAgICB2YXIgaTF4ID0gY29vcmRzWzIgKiBpMV07XG4gICAgdmFyIGkxeSA9IGNvb3Jkc1syICogaTEgKyAxXTtcbiAgICB2YXIgaTJ4ID0gY29vcmRzWzIgKiBpMl07XG4gICAgdmFyIGkyeSA9IGNvb3Jkc1syICogaTIgKyAxXTtcblxuICAgIHZhciBjZW50ZXIgPSBjaXJjdW1jZW50ZXIoaTB4LCBpMHksIGkxeCwgaTF5LCBpMngsIGkyeSk7XG4gICAgdGhpcy5fY3ggPSBjZW50ZXIueDtcbiAgICB0aGlzLl9jeSA9IGNlbnRlci55O1xuXG4gICAgLy8gc29ydCB0aGUgcG9pbnRzIGJ5IGRpc3RhbmNlIGZyb20gdGhlIHNlZWQgdHJpYW5nbGUgY2lyY3VtY2VudGVyXG4gICAgcXVpY2tzb3J0KGlkcywgY29vcmRzLCAwLCBpZHMubGVuZ3RoIC0gMSwgY2VudGVyLngsIGNlbnRlci55KTtcblxuICAgIC8vIGluaXRpYWxpemUgYSBoYXNoIHRhYmxlIGZvciBzdG9yaW5nIGVkZ2VzIG9mIHRoZSBhZHZhbmNpbmcgY29udmV4IGh1bGxcbiAgICB0aGlzLl9oYXNoU2l6ZSA9IE1hdGguY2VpbChNYXRoLnNxcnQocG9pbnRzLmxlbmd0aCkpO1xuICAgIHRoaXMuX2hhc2ggPSBbXTtcbiAgICBmb3IgKGkgPSAwOyBpIDwgdGhpcy5faGFzaFNpemU7IGkrKykgdGhpcy5faGFzaFtpXSA9IG51bGw7XG5cbiAgICAvLyBpbml0aWFsaXplIGEgY2lyY3VsYXIgZG91Ymx5LWxpbmtlZCBsaXN0IHRoYXQgd2lsbCBob2xkIGFuIGFkdmFuY2luZyBjb252ZXggaHVsbFxuICAgIHZhciBlID0gdGhpcy5odWxsID0gaW5zZXJ0Tm9kZShjb29yZHMsIGkwKTtcbiAgICB0aGlzLl9oYXNoRWRnZShlKTtcbiAgICBlLnQgPSAwO1xuICAgIGUgPSBpbnNlcnROb2RlKGNvb3JkcywgaTEsIGUpO1xuICAgIHRoaXMuX2hhc2hFZGdlKGUpO1xuICAgIGUudCA9IDE7XG4gICAgZSA9IGluc2VydE5vZGUoY29vcmRzLCBpMiwgZSk7XG4gICAgdGhpcy5faGFzaEVkZ2UoZSk7XG4gICAgZS50ID0gMjtcblxuICAgIHZhciBtYXhUcmlhbmdsZXMgPSAyICogcG9pbnRzLmxlbmd0aCAtIDU7XG4gICAgdmFyIHRyaWFuZ2xlcyA9IHRoaXMudHJpYW5nbGVzID0gbmV3IFVpbnQzMkFycmF5KG1heFRyaWFuZ2xlcyAqIDMpO1xuICAgIHRyaWFuZ2xlc1swXSA9IGkwO1xuICAgIHRyaWFuZ2xlc1sxXSA9IGkxO1xuICAgIHRyaWFuZ2xlc1syXSA9IGkyO1xuICAgIHRoaXMudHJpYW5nbGVzTGVuID0gMztcblxuICAgIHZhciBhZGphY2VudCA9IHRoaXMuYWRqYWNlbnQgPSBuZXcgSW50MzJBcnJheShtYXhUcmlhbmdsZXMgKiAzKTtcbiAgICBhZGphY2VudFswXSA9IC0xO1xuICAgIGFkamFjZW50WzFdID0gLTE7XG4gICAgYWRqYWNlbnRbMl0gPSAtMTtcblxuICAgIHZhciB4cCwgeXA7XG4gICAgZm9yICh2YXIgayA9IDA7IGsgPCBpZHMubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgaSA9IGlkc1trXTtcbiAgICAgICAgeCA9IGNvb3Jkc1syICogaV07XG4gICAgICAgIHkgPSBjb29yZHNbMiAqIGkgKyAxXTtcblxuICAgICAgICAvLyBza2lwIGR1cGxpY2F0ZSBwb2ludHNcbiAgICAgICAgaWYgKHggPT09IHhwICYmIHkgPT09IHlwKSBjb250aW51ZTtcbiAgICAgICAgeHAgPSB4O1xuICAgICAgICB5cCA9IHk7XG5cbiAgICAgICAgLy8gc2tpcCBzZWVkIHRyaWFuZ2xlIHBvaW50c1xuICAgICAgICBpZiAoKHggPT09IGkweCAmJiB5ID09PSBpMHkpIHx8XG4gICAgICAgICAgICAoeCA9PT0gaTF4ICYmIHkgPT09IGkxeSkgfHxcbiAgICAgICAgICAgICh4ID09PSBpMnggJiYgeSA9PT0gaTJ5KSkgY29udGludWU7XG5cbiAgICAgICAgLy8gZmluZCBhIHZpc2libGUgZWRnZSBvbiB0aGUgY29udmV4IGh1bGwgdXNpbmcgZWRnZSBoYXNoXG4gICAgICAgIHZhciBzdGFydEtleSA9IHRoaXMuX2hhc2hLZXkoeCwgeSk7XG4gICAgICAgIHZhciBrZXkgPSBzdGFydEtleTtcbiAgICAgICAgdmFyIHN0YXJ0O1xuICAgICAgICBkbyB7XG4gICAgICAgICAgICBzdGFydCA9IHRoaXMuX2hhc2hba2V5XTtcbiAgICAgICAgICAgIGtleSA9IChrZXkgKyAxKSAlIHRoaXMuX2hhc2hTaXplO1xuICAgICAgICB9IHdoaWxlICgoIXN0YXJ0IHx8IHN0YXJ0LnJlbW92ZWQpICYmIGtleSAhPT0gc3RhcnRLZXkpO1xuXG4gICAgICAgIGUgPSBzdGFydDtcbiAgICAgICAgd2hpbGUgKGFyZWEoeCwgeSwgZS54LCBlLnksIGUubmV4dC54LCBlLm5leHQueSkgPj0gMCkge1xuICAgICAgICAgICAgZSA9IGUubmV4dDtcbiAgICAgICAgICAgIGlmIChlID09PSBzdGFydCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignU29tZXRoaW5nIGlzIHdyb25nIHdpdGggdGhlIGlucHV0IHBvaW50cy4nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHZhciB3YWxrQmFjayA9IGUgPT09IHN0YXJ0O1xuXG4gICAgICAgIC8vIGFkZCB0aGUgZmlyc3QgdHJpYW5nbGUgZnJvbSB0aGUgcG9pbnRcbiAgICAgICAgdmFyIHQgPSB0aGlzLl9hZGRUcmlhbmdsZShpLCBlKTtcbiAgICAgICAgYWRqYWNlbnRbdF0gPSAtMTtcbiAgICAgICAgYWRqYWNlbnRbdCArIDFdID0gLTE7XG4gICAgICAgIHRoaXMuX2xpbmsodCArIDIsIGUudCk7XG5cbiAgICAgICAgZS50ID0gdDsgLy8ga2VlcCB0cmFjayBvZiBib3VuZGFyeSB0cmlhbmdsZXMgb24gdGhlIGh1bGxcbiAgICAgICAgZSA9IGluc2VydE5vZGUoY29vcmRzLCBpLCBlKTtcblxuICAgICAgICAvLyByZWN1cnNpdmVseSBmbGlwIHRyaWFuZ2xlcyBmcm9tIHRoZSBwb2ludCB1bnRpbCB0aGV5IHNhdGlzZnkgdGhlIERlbGF1bmF5IGNvbmRpdGlvblxuICAgICAgICBlLnQgPSB0aGlzLl9sZWdhbGl6ZSh0ICsgMik7XG5cbiAgICAgICAgLy8gd2FsayBmb3J3YXJkIHRocm91Z2ggdGhlIGh1bGwsIGFkZGluZyBtb3JlIHRyaWFuZ2xlcyBhbmQgZmxpcHBpbmcgcmVjdXJzaXZlbHlcbiAgICAgICAgdmFyIHEgPSBlLm5leHQ7XG4gICAgICAgIHdoaWxlIChhcmVhKHgsIHksIHEueCwgcS55LCBxLm5leHQueCwgcS5uZXh0LnkpIDwgMCkge1xuXG4gICAgICAgICAgICB0ID0gdGhpcy5fYWRkVHJpYW5nbGUoaSwgcSk7XG4gICAgICAgICAgICB0aGlzLl9saW5rKHQsIHEucHJldi50KTtcbiAgICAgICAgICAgIGFkamFjZW50W3QgKyAxXSA9IC0xO1xuICAgICAgICAgICAgdGhpcy5fbGluayh0ICsgMiwgcS50KTtcblxuICAgICAgICAgICAgcS5wcmV2LnQgPSB0aGlzLl9sZWdhbGl6ZSh0ICsgMik7XG5cbiAgICAgICAgICAgIHRoaXMuaHVsbCA9IHJlbW92ZU5vZGUocSk7XG4gICAgICAgICAgICBxID0gcS5uZXh0O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHdhbGtCYWNrKSB7XG4gICAgICAgICAgICAvLyB3YWxrIGJhY2t3YXJkIGZyb20gdGhlIG90aGVyIHNpZGUsIGFkZGluZyBtb3JlIHRyaWFuZ2xlcyBhbmQgZmxpcHBpbmdcbiAgICAgICAgICAgIHEgPSBlLnByZXY7XG4gICAgICAgICAgICB3aGlsZSAoYXJlYSh4LCB5LCBxLnByZXYueCwgcS5wcmV2LnksIHEueCwgcS55KSA8IDApIHtcblxuICAgICAgICAgICAgICAgIHQgPSB0aGlzLl9hZGRUcmlhbmdsZShpLCBxLnByZXYpO1xuICAgICAgICAgICAgICAgIGFkamFjZW50W3RdID0gLTE7XG4gICAgICAgICAgICAgICAgdGhpcy5fbGluayh0ICsgMSwgcS50KTtcbiAgICAgICAgICAgICAgICB0aGlzLl9saW5rKHQgKyAyLCBxLnByZXYudCk7XG5cbiAgICAgICAgICAgICAgICB0aGlzLl9sZWdhbGl6ZSh0ICsgMik7XG5cbiAgICAgICAgICAgICAgICBxLnByZXYudCA9IHQ7XG4gICAgICAgICAgICAgICAgdGhpcy5odWxsID0gcmVtb3ZlTm9kZShxKTtcbiAgICAgICAgICAgICAgICBxID0gcS5wcmV2O1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gc2F2ZSB0aGUgdHdvIG5ldyBlZGdlcyBpbiB0aGUgaGFzaCB0YWJsZVxuICAgICAgICB0aGlzLl9oYXNoRWRnZShlKTtcbiAgICAgICAgdGhpcy5faGFzaEVkZ2UoZS5wcmV2KTtcbiAgICB9XG5cbiAgICAvLyB0cmltIHR5cGVkIHRyaWFuZ2xlIG1lc2ggYXJyYXlzXG4gICAgdGhpcy50cmlhbmdsZXMgPSB0cmlhbmdsZXMuc3ViYXJyYXkoMCwgdGhpcy50cmlhbmdsZXNMZW4pO1xuICAgIHRoaXMuYWRqYWNlbnQgPSBhZGphY2VudC5zdWJhcnJheSgwLCB0aGlzLnRyaWFuZ2xlc0xlbik7XG59XG5cbkRlbGF1bmF0b3IucHJvdG90eXBlID0ge1xuXG4gICAgX2hhc2hFZGdlOiBmdW5jdGlvbiAoZSkge1xuICAgICAgICB0aGlzLl9oYXNoW3RoaXMuX2hhc2hLZXkoZS54LCBlLnkpXSA9IGU7XG4gICAgfSxcblxuICAgIF9oYXNoS2V5OiBmdW5jdGlvbiAoeCwgeSkge1xuICAgICAgICB2YXIgZHggPSB4IC0gdGhpcy5fY3g7XG4gICAgICAgIHZhciBkeSA9IHkgLSB0aGlzLl9jeTtcbiAgICAgICAgLy8gdXNlIHBzZXVkby1hbmdsZTogYSBtZWFzdXJlIHRoYXQgbW9ub3RvbmljYWxseSBpbmNyZWFzZXNcbiAgICAgICAgLy8gd2l0aCByZWFsIGFuZ2xlLCBidXQgZG9lc24ndCByZXF1aXJlIGV4cGVuc2l2ZSB0cmlnb25vbWV0cnlcbiAgICAgICAgdmFyIHAgPSAxIC0gZHggLyAoTWF0aC5hYnMoZHgpICsgTWF0aC5hYnMoZHkpKTtcbiAgICAgICAgcmV0dXJuIE1hdGguZmxvb3IoKDIgKyAoZHkgPCAwID8gLXAgOiBwKSkgKiAodGhpcy5faGFzaFNpemUgLyA0KSk7XG4gICAgfSxcblxuICAgIF9sZWdhbGl6ZTogZnVuY3Rpb24gKGEpIHtcbiAgICAgICAgdmFyIHRyaWFuZ2xlcyA9IHRoaXMudHJpYW5nbGVzO1xuICAgICAgICB2YXIgY29vcmRzID0gdGhpcy5jb29yZHM7XG4gICAgICAgIHZhciBhZGphY2VudCA9IHRoaXMuYWRqYWNlbnQ7XG5cbiAgICAgICAgdmFyIGIgPSBhZGphY2VudFthXTtcblxuICAgICAgICB2YXIgYTAgPSBhIC0gYSAlIDM7XG4gICAgICAgIHZhciBiMCA9IGIgLSBiICUgMztcblxuICAgICAgICB2YXIgYWwgPSBhMCArIChhICsgMSkgJSAzO1xuICAgICAgICB2YXIgYXIgPSBhMCArIChhICsgMikgJSAzO1xuICAgICAgICB2YXIgYnIgPSBiMCArIChiICsgMSkgJSAzO1xuICAgICAgICB2YXIgYmwgPSBiMCArIChiICsgMikgJSAzO1xuXG4gICAgICAgIHZhciBwMCA9IHRyaWFuZ2xlc1thcl07XG4gICAgICAgIHZhciBwciA9IHRyaWFuZ2xlc1thXTtcbiAgICAgICAgdmFyIHBsID0gdHJpYW5nbGVzW2FsXTtcbiAgICAgICAgdmFyIHAxID0gdHJpYW5nbGVzW2JsXTtcblxuICAgICAgICB2YXIgaWxsZWdhbCA9IGluQ2lyY2xlKFxuICAgICAgICAgICAgY29vcmRzWzIgKiBwMF0sIGNvb3Jkc1syICogcDAgKyAxXSxcbiAgICAgICAgICAgIGNvb3Jkc1syICogcHJdLCBjb29yZHNbMiAqIHByICsgMV0sXG4gICAgICAgICAgICBjb29yZHNbMiAqIHBsXSwgY29vcmRzWzIgKiBwbCArIDFdLFxuICAgICAgICAgICAgY29vcmRzWzIgKiBwMV0sIGNvb3Jkc1syICogcDEgKyAxXSk7XG5cbiAgICAgICAgaWYgKGlsbGVnYWwpIHtcbiAgICAgICAgICAgIHRyaWFuZ2xlc1thXSA9IHAxO1xuICAgICAgICAgICAgdHJpYW5nbGVzW2JdID0gcDA7XG5cbiAgICAgICAgICAgIHRoaXMuX2xpbmsoYSwgYWRqYWNlbnRbYmxdKTtcbiAgICAgICAgICAgIHRoaXMuX2xpbmsoYiwgYWRqYWNlbnRbYXJdKTtcbiAgICAgICAgICAgIHRoaXMuX2xpbmsoYXIsIGJsKTtcblxuICAgICAgICAgICAgdGhpcy5fbGVnYWxpemUoYSk7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fbGVnYWxpemUoYnIpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGFyO1xuICAgIH0sXG5cbiAgICBfbGluazogZnVuY3Rpb24gKGEsIGIpIHtcbiAgICAgICAgdGhpcy5hZGphY2VudFthXSA9IGI7XG4gICAgICAgIGlmIChiICE9PSAtMSkgdGhpcy5hZGphY2VudFtiXSA9IGE7XG4gICAgfSxcblxuICAgIF9hZGRUcmlhbmdsZShpLCBlKSB7XG4gICAgICAgIHZhciB0ID0gdGhpcy50cmlhbmdsZXNMZW47XG4gICAgICAgIHRoaXMudHJpYW5nbGVzW3RdID0gZS5pO1xuICAgICAgICB0aGlzLnRyaWFuZ2xlc1t0ICsgMV0gPSBpO1xuICAgICAgICB0aGlzLnRyaWFuZ2xlc1t0ICsgMl0gPSBlLm5leHQuaTtcbiAgICAgICAgdGhpcy50cmlhbmdsZXNMZW4gKz0gMztcbiAgICAgICAgcmV0dXJuIHQ7XG4gICAgfVxufTtcblxuZnVuY3Rpb24gZGlzdChheCwgYXksIGJ4LCBieSkge1xuICAgIHZhciBkeCA9IGF4IC0gYng7XG4gICAgdmFyIGR5ID0gYXkgLSBieTtcbiAgICByZXR1cm4gZHggKiBkeCArIGR5ICogZHk7XG59XG5cbmZ1bmN0aW9uIGFyZWEocHgsIHB5LCBxeCwgcXksIHJ4LCByeSkge1xuICAgIHJldHVybiAocXkgLSBweSkgKiAocnggLSBxeCkgLSAocXggLSBweCkgKiAocnkgLSBxeSk7XG59XG5cbmZ1bmN0aW9uIGluQ2lyY2xlKGF4LCBheSwgYngsIGJ5LCBjeCwgY3ksIHB4LCBweSkge1xuICAgIGF4IC09IHB4O1xuICAgIGF5IC09IHB5O1xuICAgIGJ4IC09IHB4O1xuICAgIGJ5IC09IHB5O1xuICAgIGN4IC09IHB4O1xuICAgIGN5IC09IHB5O1xuXG4gICAgdmFyIGFwID0gYXggKiBheCArIGF5ICogYXk7XG4gICAgdmFyIGJwID0gYnggKiBieCArIGJ5ICogYnk7XG4gICAgdmFyIGNwID0gY3ggKiBjeCArIGN5ICogY3k7XG5cbiAgICB2YXIgZGV0ID0gYXggKiAoYnkgKiBjcCAtIGJwICogY3kpIC1cbiAgICAgICAgICAgICAgYXkgKiAoYnggKiBjcCAtIGJwICogY3gpICtcbiAgICAgICAgICAgICAgYXAgKiAoYnggKiBjeSAtIGJ5ICogY3gpO1xuXG4gICAgcmV0dXJuIGRldCA8IDA7XG59XG5cbmZ1bmN0aW9uIGNpcmN1bXJhZGl1cyhheCwgYXksIGJ4LCBieSwgY3gsIGN5KSB7XG4gICAgYnggLT0gYXg7XG4gICAgYnkgLT0gYXk7XG4gICAgY3ggLT0gYXg7XG4gICAgY3kgLT0gYXk7XG5cbiAgICB2YXIgYmwgPSBieCAqIGJ4ICsgYnkgKiBieTtcbiAgICB2YXIgY2wgPSBjeCAqIGN4ICsgY3kgKiBjeTtcblxuICAgIGlmIChibCA9PT0gMCB8fCBjbCA9PT0gMCkgcmV0dXJuIEluZmluaXR5O1xuXG4gICAgdmFyIGQgPSBieCAqIGN5IC0gYnkgKiBjeDtcbiAgICBpZiAoZCA9PT0gMCkgcmV0dXJuIEluZmluaXR5O1xuXG4gICAgdmFyIHggPSAoY3kgKiBibCAtIGJ5ICogY2wpICogMC41IC8gZDtcbiAgICB2YXIgeSA9IChieCAqIGNsIC0gY3ggKiBibCkgKiAwLjUgLyBkO1xuXG4gICAgcmV0dXJuIHggKiB4ICsgeSAqIHk7XG59XG5cbmZ1bmN0aW9uIGNpcmN1bWNlbnRlcihheCwgYXksIGJ4LCBieSwgY3gsIGN5KSB7XG4gICAgYnggLT0gYXg7XG4gICAgYnkgLT0gYXk7XG4gICAgY3ggLT0gYXg7XG4gICAgY3kgLT0gYXk7XG5cbiAgICB2YXIgYmwgPSBieCAqIGJ4ICsgYnkgKiBieTtcbiAgICB2YXIgY2wgPSBjeCAqIGN4ICsgY3kgKiBjeTtcblxuICAgIHZhciBkID0gYnggKiBjeSAtIGJ5ICogY3g7XG5cbiAgICB2YXIgeCA9IChjeSAqIGJsIC0gYnkgKiBjbCkgKiAwLjUgLyBkO1xuICAgIHZhciB5ID0gKGJ4ICogY2wgLSBjeCAqIGJsKSAqIDAuNSAvIGQ7XG5cbiAgICByZXR1cm4ge1xuICAgICAgICB4OiBheCArIHgsXG4gICAgICAgIHk6IGF5ICsgeVxuICAgIH07XG59XG5cbi8vIGNyZWF0ZSBhIG5ldyBub2RlIGluIGEgZG91Ymx5IGxpbmtlZCBsaXN0XG5mdW5jdGlvbiBpbnNlcnROb2RlKGNvb3JkcywgaSwgcHJldikge1xuICAgIHZhciBub2RlID0ge1xuICAgICAgICBpOiBpLFxuICAgICAgICB4OiBjb29yZHNbMiAqIGldLFxuICAgICAgICB5OiBjb29yZHNbMiAqIGkgKyAxXSxcbiAgICAgICAgdDogMCxcbiAgICAgICAgcHJldjogbnVsbCxcbiAgICAgICAgbmV4dDogbnVsbCxcbiAgICAgICAgcmVtb3ZlZDogZmFsc2VcbiAgICB9O1xuXG4gICAgaWYgKCFwcmV2KSB7XG4gICAgICAgIG5vZGUucHJldiA9IG5vZGU7XG4gICAgICAgIG5vZGUubmV4dCA9IG5vZGU7XG5cbiAgICB9IGVsc2Uge1xuICAgICAgICBub2RlLm5leHQgPSBwcmV2Lm5leHQ7XG4gICAgICAgIG5vZGUucHJldiA9IHByZXY7XG4gICAgICAgIHByZXYubmV4dC5wcmV2ID0gbm9kZTtcbiAgICAgICAgcHJldi5uZXh0ID0gbm9kZTtcbiAgICB9XG4gICAgcmV0dXJuIG5vZGU7XG59XG5cbmZ1bmN0aW9uIHJlbW92ZU5vZGUobm9kZSkge1xuICAgIG5vZGUucHJldi5uZXh0ID0gbm9kZS5uZXh0O1xuICAgIG5vZGUubmV4dC5wcmV2ID0gbm9kZS5wcmV2O1xuICAgIG5vZGUucmVtb3ZlZCA9IHRydWU7XG4gICAgcmV0dXJuIG5vZGUucHJldjtcbn1cblxuZnVuY3Rpb24gcXVpY2tzb3J0KGlkcywgY29vcmRzLCBsZWZ0LCByaWdodCwgY3gsIGN5KSB7XG4gICAgdmFyIGksIGosIHRlbXA7XG5cbiAgICBpZiAocmlnaHQgLSBsZWZ0IDw9IDIwKSB7XG4gICAgICAgIGZvciAoaSA9IGxlZnQgKyAxOyBpIDw9IHJpZ2h0OyBpKyspIHtcbiAgICAgICAgICAgIHRlbXAgPSBpZHNbaV07XG4gICAgICAgICAgICBqID0gaSAtIDE7XG4gICAgICAgICAgICB3aGlsZSAoaiA+PSBsZWZ0ICYmIGNvbXBhcmUoY29vcmRzLCBpZHNbal0sIHRlbXAsIGN4LCBjeSkgPiAwKSBpZHNbaiArIDFdID0gaWRzW2otLV07XG4gICAgICAgICAgICBpZHNbaiArIDFdID0gdGVtcDtcbiAgICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBtZWRpYW4gPSAobGVmdCArIHJpZ2h0KSA+PiAxO1xuICAgICAgICBpID0gbGVmdCArIDE7XG4gICAgICAgIGogPSByaWdodDtcbiAgICAgICAgc3dhcChpZHMsIG1lZGlhbiwgaSk7XG4gICAgICAgIGlmIChjb21wYXJlKGNvb3JkcywgaWRzW2xlZnRdLCBpZHNbcmlnaHRdLCBjeCwgY3kpID4gMCkgc3dhcChpZHMsIGxlZnQsIHJpZ2h0KTtcbiAgICAgICAgaWYgKGNvbXBhcmUoY29vcmRzLCBpZHNbaV0sIGlkc1tyaWdodF0sIGN4LCBjeSkgPiAwKSBzd2FwKGlkcywgaSwgcmlnaHQpO1xuICAgICAgICBpZiAoY29tcGFyZShjb29yZHMsIGlkc1tsZWZ0XSwgaWRzW2ldLCBjeCwgY3kpID4gMCkgc3dhcChpZHMsIGxlZnQsIGkpO1xuXG4gICAgICAgIHRlbXAgPSBpZHNbaV07XG4gICAgICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAgICAgICBkbyBpKys7IHdoaWxlIChjb21wYXJlKGNvb3JkcywgaWRzW2ldLCB0ZW1wLCBjeCwgY3kpIDwgMCk7XG4gICAgICAgICAgICBkbyBqLS07IHdoaWxlIChjb21wYXJlKGNvb3JkcywgaWRzW2pdLCB0ZW1wLCBjeCwgY3kpID4gMCk7XG4gICAgICAgICAgICBpZiAoaiA8IGkpIGJyZWFrO1xuICAgICAgICAgICAgc3dhcChpZHMsIGksIGopO1xuICAgICAgICB9XG4gICAgICAgIGlkc1tsZWZ0ICsgMV0gPSBpZHNbal07XG4gICAgICAgIGlkc1tqXSA9IHRlbXA7XG5cbiAgICAgICAgaWYgKHJpZ2h0IC0gaSArIDEgPj0gaiAtIGxlZnQpIHtcbiAgICAgICAgICAgIHF1aWNrc29ydChpZHMsIGNvb3JkcywgaSwgcmlnaHQsIGN4LCBjeSk7XG4gICAgICAgICAgICBxdWlja3NvcnQoaWRzLCBjb29yZHMsIGxlZnQsIGogLSAxLCBjeCwgY3kpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcXVpY2tzb3J0KGlkcywgY29vcmRzLCBsZWZ0LCBqIC0gMSwgY3gsIGN5KTtcbiAgICAgICAgICAgIHF1aWNrc29ydChpZHMsIGNvb3JkcywgaSwgcmlnaHQsIGN4LCBjeSk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbmZ1bmN0aW9uIGNvbXBhcmUoY29vcmRzLCBpLCBqLCBjeCwgY3kpIHtcbiAgICB2YXIgZDEgPSBkaXN0KGNvb3Jkc1syICogaV0sIGNvb3Jkc1syICogaSArIDFdLCBjeCwgY3kpO1xuICAgIHZhciBkMiA9IGRpc3QoY29vcmRzWzIgKiBqXSwgY29vcmRzWzIgKiBqICsgMV0sIGN4LCBjeSk7XG4gICAgcmV0dXJuIChkMSAtIGQyKSB8fCAoY29vcmRzWzIgKiBpXSAtIGNvb3Jkc1syICogal0pIHx8IChjb29yZHNbMiAqIGkgKyAxXSAtIGNvb3Jkc1syICogaiArIDFdKTtcbn1cblxuZnVuY3Rpb24gc3dhcChhcnIsIGksIGopIHtcbiAgICB2YXIgdG1wID0gYXJyW2ldO1xuICAgIGFycltpXSA9IGFycltqXTtcbiAgICBhcnJbal0gPSB0bXA7XG59XG5cbmZ1bmN0aW9uIGRlZmF1bHRHZXRYKHApIHtcbiAgICByZXR1cm4gcFswXTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRHZXRZKHApIHtcbiAgICByZXR1cm4gcFsxXTtcbn1cbiJdfQ== | |
| var Delaunator = require('delaunator') | |
| var SPEED = 0.20 | |
| var N_POINTS = 550 | |
| var width = window.innerWidth | |
| var height = window.innerHeight | |
| document.body.style.background = 'rgb(20, 20, 20)' | |
| var points = createPoints(N_POINTS, width, height) | |
| points.forEach(function (point) { | |
| // document.body.appendChild(point.el) | |
| }) | |
| var triangles = createTrianges(points) | |
| document.body.appendChild(triangles.el) | |
| requestAnimationFrame(updateLoop) | |
| function updateLoop () { | |
| requestAnimationFrame(updateLoop) | |
| points.forEach(function (point) { | |
| var theta = (0.5 - Math.random()) * (Math.PI / 16) | |
| point.move(theta, SPEED) | |
| }) | |
| triangles.update(points) | |
| } | |
| function createPoints (n, w, h) { | |
| var points = [] | |
| var x, y | |
| for (var i = 0; i < n; i++) { | |
| x = Math.floor(Math.random() * w) | |
| y = Math.floor(Math.random() * h) | |
| points.push(createPoint([x, y])) | |
| } | |
| return points | |
| } | |
| function createPoint (coords) { | |
| var w = 20 | |
| var h = w | |
| var el = document.createElement('div') | |
| el.style.position = 'absolute' | |
| el.style.left = coords[0] + 'px' | |
| el.style.top = coords[1] + 'px' | |
| el.style.width = w + 'px' | |
| el.style.height = h + 'px' | |
| el.style.borderRadius = w / 2 + 'px' | |
| el.style.background = '#aaa' | |
| var point = { | |
| el: el, | |
| x: coords[0], | |
| y: coords[1], | |
| theta: Math.random() * 2 * Math.PI, | |
| move: move | |
| } | |
| return point | |
| } | |
| function mod (a, n) { | |
| return ((a % n) + n) % n | |
| } | |
| function move (theta, r) { | |
| this.theta += theta | |
| this.x += r * Math.cos(this.theta) | |
| this.y += r * Math.sin(this.theta) | |
| this.x = mod(this.x, width) | |
| this.y = mod(this.y, height) | |
| this.el.style.left = this.x + 'px' | |
| this.el.style.top = this.y + 'px' | |
| } | |
| function createTrianges (points) { | |
| var stroke = '#ccc' | |
| var fill = 'rgba(0,0,0,0)' | |
| var viewBox = [0, 0, width, height].join(' ') | |
| var pathData = createPathData(points) | |
| var parent = document.createElement('div') | |
| parent.innerHTML = ` | |
| <svg xmlns='http://www.w3.org/svg/2000' | |
| viewBox='${viewBox}' | |
| width=${width} | |
| height=${height} | |
| stroke='${stroke}' | |
| fill='${fill}'> | |
| <path d='${pathData}' /> | |
| </svg> | |
| ` | |
| return { | |
| el: parent, | |
| path: parent.querySelector('path'), | |
| update: update | |
| } | |
| } | |
| function createPathData (points) { | |
| var coords = points.map(function (point) { | |
| return [point.x, point.y] | |
| }) | |
| var triangles = new Delaunator(coords).triangles | |
| var pathData = [] | |
| var x0, y0, x1, y1, x2, y2 | |
| for (var i = 0; i < triangles.length; i += 3) { | |
| x0 = coords[triangles[i]][0] | |
| y0 = coords[triangles[i]][1] | |
| x1 = coords[triangles[i + 1]][0] | |
| y1 = coords[triangles[i + 1]][1] | |
| x2 = coords[triangles[i + 2]][0] | |
| y2 = coords[triangles[i + 2]][1] | |
| pathData.push('M') | |
| pathData.push(x0) | |
| pathData.push(y0) | |
| pathData.push('L') | |
| pathData.push(x1) | |
| pathData.push(y1) | |
| pathData.push('L') | |
| pathData.push(x2) | |
| pathData.push(y2) | |
| pathData.push('L') | |
| pathData.push(x0) | |
| pathData.push(y0) | |
| } | |
| return pathData.join(' ') | |
| } | |
| function update (points) { | |
| this.path.setAttribute('d', createPathData(points)) | |
| } | |
| ;}, 0) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "name": "requirebin-sketch", | |
| "version": "1.0.0", | |
| "dependencies": { | |
| "delaunator": "1.0.2" | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- contents of this file will be placed inside the <body> --> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- contents of this file will be placed inside the <head> --> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment