UNPKG

sponsorkit

Version:

Toolkit for generating sponsors images

591 lines (511 loc) 13.9 kB
function count(node) { var sum = 0, children = node.children, i = children && children.length; if (!i) sum = 1; else while (--i >= 0) sum += children[i].value; node.value = sum; } function node_count() { return this.eachAfter(count); } function node_each(callback, that) { let index = -1; for (const node of this) { callback.call(that, node, ++index, this); } return this; } function node_eachBefore(callback, that) { var node = this, nodes = [node], children, i, index = -1; while (node = nodes.pop()) { callback.call(that, node, ++index, this); if (children = node.children) { for (i = children.length - 1; i >= 0; --i) { nodes.push(children[i]); } } } return this; } function node_eachAfter(callback, that) { var node = this, nodes = [node], next = [], children, i, n, index = -1; while (node = nodes.pop()) { next.push(node); if (children = node.children) { for (i = 0, n = children.length; i < n; ++i) { nodes.push(children[i]); } } } while (node = next.pop()) { callback.call(that, node, ++index, this); } return this; } function node_find(callback, that) { let index = -1; for (const node of this) { if (callback.call(that, node, ++index, this)) { return node; } } } function node_sum(value) { return this.eachAfter(function(node) { var sum = +value(node.data) || 0, children = node.children, i = children && children.length; while (--i >= 0) sum += children[i].value; node.value = sum; }); } function node_sort(compare) { return this.eachBefore(function(node) { if (node.children) { node.children.sort(compare); } }); } function node_path(end) { var start = this, ancestor = leastCommonAncestor(start, end), nodes = [start]; while (start !== ancestor) { start = start.parent; nodes.push(start); } var k = nodes.length; while (end !== ancestor) { nodes.splice(k, 0, end); end = end.parent; } return nodes; } function leastCommonAncestor(a, b) { if (a === b) return a; var aNodes = a.ancestors(), bNodes = b.ancestors(), c = null; a = aNodes.pop(); b = bNodes.pop(); while (a === b) { c = a; a = aNodes.pop(); b = bNodes.pop(); } return c; } function node_ancestors() { var node = this, nodes = [node]; while (node = node.parent) { nodes.push(node); } return nodes; } function node_descendants() { return Array.from(this); } function node_leaves() { var leaves = []; this.eachBefore(function(node) { if (!node.children) { leaves.push(node); } }); return leaves; } function node_links() { var root = this, links = []; root.each(function(node) { if (node !== root) { // Don’t include the root’s parent, if any. links.push({source: node.parent, target: node}); } }); return links; } function* node_iterator() { var node = this, current, next = [node], children, i, n; do { current = next.reverse(), next = []; while (node = current.pop()) { yield node; if (children = node.children) { for (i = 0, n = children.length; i < n; ++i) { next.push(children[i]); } } } } while (next.length); } function hierarchy(data, children) { if (data instanceof Map) { data = [undefined, data]; if (children === undefined) children = mapChildren; } else if (children === undefined) { children = objectChildren; } var root = new Node$1(data), node, nodes = [root], child, childs, i, n; while (node = nodes.pop()) { if ((childs = children(node.data)) && (n = (childs = Array.from(childs)).length)) { node.children = childs; for (i = n - 1; i >= 0; --i) { nodes.push(child = childs[i] = new Node$1(childs[i])); child.parent = node; child.depth = node.depth + 1; } } } return root.eachBefore(computeHeight); } function node_copy() { return hierarchy(this).eachBefore(copyData); } function objectChildren(d) { return d.children; } function mapChildren(d) { return Array.isArray(d) ? d[1] : null; } function copyData(node) { if (node.data.value !== undefined) node.value = node.data.value; node.data = node.data.data; } function computeHeight(node) { var height = 0; do node.height = height; while ((node = node.parent) && (node.height < ++height)); } function Node$1(data) { this.data = data; this.depth = this.height = 0; this.parent = null; } Node$1.prototype = hierarchy.prototype = { constructor: Node$1, count: node_count, each: node_each, eachAfter: node_eachAfter, eachBefore: node_eachBefore, find: node_find, sum: node_sum, sort: node_sort, path: node_path, ancestors: node_ancestors, descendants: node_descendants, leaves: node_leaves, links: node_links, copy: node_copy, [Symbol.iterator]: node_iterator }; function optional(f) { return f == null ? null : required(f); } function required(f) { if (typeof f !== "function") throw new Error; return f; } function constantZero() { return 0; } function constant(x) { return function() { return x; }; } // https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use const a = 1664525; const c = 1013904223; const m = 4294967296; // 2^32 function lcg() { let s = 1; return () => (s = (a * s + c) % m) / m; } function array(x) { return typeof x === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like : Array.from(x); // Map, Set, iterable, string, or anything else } function shuffle(array, random) { let m = array.length, t, i; while (m) { i = random() * m-- | 0; t = array[m]; array[m] = array[i]; array[i] = t; } return array; } function packEncloseRandom(circles, random) { var i = 0, n = (circles = shuffle(Array.from(circles), random)).length, B = [], p, e; while (i < n) { p = circles[i]; if (e && enclosesWeak(e, p)) ++i; else e = encloseBasis(B = extendBasis(B, p)), i = 0; } return e; } function extendBasis(B, p) { var i, j; if (enclosesWeakAll(p, B)) return [p]; // If we get here then B must have at least one element. for (i = 0; i < B.length; ++i) { if (enclosesNot(p, B[i]) && enclosesWeakAll(encloseBasis2(B[i], p), B)) { return [B[i], p]; } } // If we get here then B must have at least two elements. for (i = 0; i < B.length - 1; ++i) { for (j = i + 1; j < B.length; ++j) { if (enclosesNot(encloseBasis2(B[i], B[j]), p) && enclosesNot(encloseBasis2(B[i], p), B[j]) && enclosesNot(encloseBasis2(B[j], p), B[i]) && enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) { return [B[i], B[j], p]; } } } // If we get here then something is very wrong. throw new Error; } function enclosesNot(a, b) { var dr = a.r - b.r, dx = b.x - a.x, dy = b.y - a.y; return dr < 0 || dr * dr < dx * dx + dy * dy; } function enclosesWeak(a, b) { var dr = a.r - b.r + Math.max(a.r, b.r, 1) * 1e-9, dx = b.x - a.x, dy = b.y - a.y; return dr > 0 && dr * dr > dx * dx + dy * dy; } function enclosesWeakAll(a, B) { for (var i = 0; i < B.length; ++i) { if (!enclosesWeak(a, B[i])) { return false; } } return true; } function encloseBasis(B) { switch (B.length) { case 1: return encloseBasis1(B[0]); case 2: return encloseBasis2(B[0], B[1]); case 3: return encloseBasis3(B[0], B[1], B[2]); } } function encloseBasis1(a) { return { x: a.x, y: a.y, r: a.r }; } function encloseBasis2(a, b) { var x1 = a.x, y1 = a.y, r1 = a.r, x2 = b.x, y2 = b.y, r2 = b.r, x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1, l = Math.sqrt(x21 * x21 + y21 * y21); return { x: (x1 + x2 + x21 / l * r21) / 2, y: (y1 + y2 + y21 / l * r21) / 2, r: (l + r1 + r2) / 2 }; } function encloseBasis3(a, b, c) { var x1 = a.x, y1 = a.y, r1 = a.r, x2 = b.x, y2 = b.y, r2 = b.r, x3 = c.x, y3 = c.y, r3 = c.r, a2 = x1 - x2, a3 = x1 - x3, b2 = y1 - y2, b3 = y1 - y3, c2 = r2 - r1, c3 = r3 - r1, d1 = x1 * x1 + y1 * y1 - r1 * r1, d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2, d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3, ab = a3 * b2 - a2 * b3, xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1, xb = (b3 * c2 - b2 * c3) / ab, ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1, yb = (a2 * c3 - a3 * c2) / ab, A = xb * xb + yb * yb - 1, B = 2 * (r1 + xa * xb + ya * yb), C = xa * xa + ya * ya - r1 * r1, r = -(Math.abs(A) > 1e-6 ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B); return { x: x1 + xa + xb * r, y: y1 + ya + yb * r, r: r }; } function place(b, a, c) { var dx = b.x - a.x, x, a2, dy = b.y - a.y, y, b2, d2 = dx * dx + dy * dy; if (d2) { a2 = a.r + c.r, a2 *= a2; b2 = b.r + c.r, b2 *= b2; if (a2 > b2) { x = (d2 + b2 - a2) / (2 * d2); y = Math.sqrt(Math.max(0, b2 / d2 - x * x)); c.x = b.x - x * dx - y * dy; c.y = b.y - x * dy + y * dx; } else { x = (d2 + a2 - b2) / (2 * d2); y = Math.sqrt(Math.max(0, a2 / d2 - x * x)); c.x = a.x + x * dx - y * dy; c.y = a.y + x * dy + y * dx; } } else { c.x = a.x + c.r; c.y = a.y; } } function intersects(a, b) { var dr = a.r + b.r - 1e-6, dx = b.x - a.x, dy = b.y - a.y; return dr > 0 && dr * dr > dx * dx + dy * dy; } function score(node) { var a = node._, b = node.next._, ab = a.r + b.r, dx = (a.x * b.r + b.x * a.r) / ab, dy = (a.y * b.r + b.y * a.r) / ab; return dx * dx + dy * dy; } function Node(circle) { this._ = circle; this.next = null; this.previous = null; } function packSiblingsRandom(circles, random) { if (!(n = (circles = array(circles)).length)) return 0; var a, b, c, n, aa, ca, i, j, k, sj, sk; // Place the first circle. a = circles[0], a.x = 0, a.y = 0; if (!(n > 1)) return a.r; // Place the second circle. b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0; if (!(n > 2)) return a.r + b.r; // Place the third circle. place(b, a, c = circles[2]); // Initialize the front-chain using the first three circles a, b and c. a = new Node(a), b = new Node(b), c = new Node(c); a.next = c.previous = b; b.next = a.previous = c; c.next = b.previous = a; // Attempt to place each remaining circle… pack: for (i = 3; i < n; ++i) { place(a._, b._, c = circles[i]), c = new Node(c); // Find the closest intersecting circle on the front-chain, if any. // “Closeness” is determined by linear distance along the front-chain. // “Ahead” or “behind” is likewise determined by linear distance. j = b.next, k = a.previous, sj = b._.r, sk = a._.r; do { if (sj <= sk) { if (intersects(j._, c._)) { b = j, a.next = b, b.previous = a, --i; continue pack; } sj += j._.r, j = j.next; } else { if (intersects(k._, c._)) { a = k, a.next = b, b.previous = a, --i; continue pack; } sk += k._.r, k = k.previous; } } while (j !== k.next); // Success! Insert the new circle c between a and b. c.previous = a, c.next = b, a.next = b.previous = b = c; // Compute the new closest circle pair to the centroid. aa = score(a); while ((c = c.next) !== b) { if ((ca = score(c)) < aa) { a = c, aa = ca; } } b = a.next; } // Compute the enclosing circle of the front chain. a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = packEncloseRandom(a, random); // Translate the circles to put the enclosing circle around the origin. for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y; return c.r; } function defaultRadius(d) { return Math.sqrt(d.value); } function index() { var radius = null, dx = 1, dy = 1, padding = constantZero; function pack(root) { const random = lcg(); root.x = dx / 2, root.y = dy / 2; if (radius) { root.eachBefore(radiusLeaf(radius)) .eachAfter(packChildrenRandom(padding, 0.5, random)) .eachBefore(translateChild(1)); } else { root.eachBefore(radiusLeaf(defaultRadius)) .eachAfter(packChildrenRandom(constantZero, 1, random)) .eachAfter(packChildrenRandom(padding, root.r / Math.min(dx, dy), random)) .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r))); } return root; } pack.radius = function(x) { return arguments.length ? (radius = optional(x), pack) : radius; }; pack.size = function(x) { return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy]; }; pack.padding = function(x) { return arguments.length ? (padding = typeof x === "function" ? x : constant(+x), pack) : padding; }; return pack; } function radiusLeaf(radius) { return function(node) { if (!node.children) { node.r = Math.max(0, +radius(node) || 0); } }; } function packChildrenRandom(padding, k, random) { return function(node) { if (children = node.children) { var children, i, n = children.length, r = padding(node) * k || 0, e; if (r) for (i = 0; i < n; ++i) children[i].r += r; e = packSiblingsRandom(children, random); if (r) for (i = 0; i < n; ++i) children[i].r -= r; node.r = e + r; } }; } function translateChild(k) { return function(node) { var parent = node.parent; node.r *= k; if (parent) { node.x = parent.x + k * node.x; node.y = parent.y + k * node.y; } }; } export { Node$1 as Node, hierarchy, index as pack };