From 78a1ad842bdce8a53dca0a68fa545171755bbeb2 Mon Sep 17 00:00:00 2001 From: boombuler Date: Fri, 22 Feb 2013 20:22:11 +0100 Subject: [PATCH] new plugin to calculate how to set links for max field count --- plugins/README.md | 1 + plugins/max-links.user.js | 252 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 plugins/max-links.user.js diff --git a/plugins/README.md b/plugins/README.md index 35eb5d86..95a611b7 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -14,6 +14,7 @@ Available Plugins - [**Highlight Weakened Portals**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-portal-weakness.user.js) fill portals with red to indicate portal's state of disrepair. The brighter the color the more attention needed (recharge, shields, resonators). A dashed portal means a resonator is missing. - [**Render Limit Increase**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/render-limit-increase.user.js) increases render limits. Good for high density areas (e.g. London, UK) and faster PCs. - [**Resonator Display Zoom Level Decrease**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/resonator-display-zoom-level-decrease.user.js) Resonator start displaying earlier. +- [**Max-Links**]((https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/rmax-links.user.js) Calculates how to link the portals to create the maximum number of fields. ### available only with the development version diff --git a/plugins/max-links.user.js b/plugins/max-links.user.js new file mode 100644 index 00000000..57074a10 --- /dev/null +++ b/plugins/max-links.user.js @@ -0,0 +1,252 @@ +// ==UserScript== +// @id max-links@boombuler +// @name iitc: Max-Links-Plugin +// @version 0.1 +// @description Calculates how to link the portals to create the maximum number of fields. +// @include http://www.ingress.com/intel* +// @match http://www.ingress.com/intel* +// ==/UserScript== + +// The algorithm is taken from https://github.com/ironwallaby/delaunay + +function wrapper() { + // ensure plugin framework is there, even if iitc is not yet loaded + if(typeof window.plugin !== 'function') + window.plugin = function() {}; + + // PLUGIN START //////////////////////////////////////////////////////// + + // use own namespace for plugin + window.plugin.portalfolding = function() {}; + + var MAX_LINK_COLOR = '#FF0000'; + + var Triangle = function (a, b, c) { + this.a = a + this.b = b + this.c = c + + var A = b.x - a.x, + B = b.y - a.y, + C = c.x - a.x, + D = c.y - a.y, + E = A * (a.x + b.x) + B * (a.y + b.y), + F = C * (a.x + c.x) + D * (a.y + c.y), + G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)), + minx, miny, dx, dy + + /* If the points of the triangle are collinear, then just find the + * extremes and use the midpoint as the center of the circumcircle. */ + if(Math.abs(G) < 0.000001) { + minx = Math.min(a.x, b.x, c.x) + miny = Math.min(a.y, b.y, c.y) + dx = (Math.max(a.x, b.x, c.x) - minx) * 0.5 + dy = (Math.max(a.y, b.y, c.y) - miny) * 0.5 + + this.x = minx + dx + this.y = miny + dy + this.r = dx * dx + dy * dy + } + + else { + this.x = (D*E - B*F) / G + this.y = (A*F - C*E) / G + dx = this.x - a.x + dy = this.y - a.y + this.r = dx * dx + dy * dy + } + } + + Triangle.prototype.draw = function(layer) { + var drawLine = function(src, dest) { + var poly = L.polyline([[src.y, src.x], [dest.y, dest.x]], { + color: MAX_LINK_COLOR, + opacity: 1, + weight:2, + clickable: false, + smoothFactor: 10 + }); + poly.addTo(layer); + }; + + drawLine(this.a, this.b); + drawLine(this.b, this.c); + drawLine(this.c, this.a); + } + + var dedup = function (edges) { + var j = edges.length, + a, b, i, m, n + + outer: while(j) { + b = edges[--j] + a = edges[--j] + i = j + while(i) { + n = edges[--i] + m = edges[--i] + if((a === m && b === n) || (a === n && b === m)) { + edges.splice(j, 2) + edges.splice(i, 2) + j -= 2 + continue outer + } + } + } + } + + var triangulate = function (vertices) { + /* Bail if there aren't enough vertices to form any triangles. */ + if(vertices.length < 3) + return [] + + /* Ensure the vertex array is in order of descending X coordinate + * (which is needed to ensure a subquadratic runtime), and then find + * the bounding box around the points. */ + vertices.sort(function (a, b) { return b.x - a.x }); + + var i = vertices.length - 1, + xmin = vertices[i].x, + xmax = vertices[0].x, + ymin = vertices[i].y, + ymax = ymin + + while(i--) { + if(vertices[i].y < ymin) ymin = vertices[i].y + if(vertices[i].y > ymax) ymax = vertices[i].y + } + + /* Find a supertriangle, which is a triangle that surrounds all the + * vertices. This is used like something of a sentinel value to remove + * cases in the main algorithm, and is removed before we return any + * results. + * + * Once found, put it in the "open" list. (The "open" list is for + * triangles who may still need to be considered; the "closed" list is + * for triangles which do not.) */ + var dx = xmax - xmin, + dy = ymax - ymin, + dmax = (dx > dy) ? dx : dy, + xmid = (xmax + xmin) * 0.5, + ymid = (ymax + ymin) * 0.5, + open = [ + new Triangle( + {x: xmid - 20 * dmax, y: ymid - dmax, __sentinel: true}, + {x: xmid , y: ymid + 20 * dmax, __sentinel: true}, + {x: xmid + 20 * dmax, y: ymid - dmax, __sentinel: true} + ) + ], + closed = [], + edges = [], + j, a, b + + /* Incrementally add each vertex to the mesh. */ + i = vertices.length + while(i--) { + /* For each open triangle, check to see if the current point is + * inside it's circumcircle. If it is, remove the triangle and add + * it's edges to an edge list. */ + edges.length = 0 + j = open.length + while(j--) { + /* If this point is to the right of this triangle's circumcircle, + * then this triangle should never get checked again. Remove it + * from the open list, add it to the closed list, and skip. */ + dx = vertices[i].x - open[j].x + if(dx > 0 && dx * dx > open[j].r) { + closed.push(open[j]) + open.splice(j, 1) + continue + } + + /* If not, skip this triangle. */ + dy = vertices[i].y - open[j].y + if(dx * dx + dy * dy > open[j].r) + continue + + /* Remove the triangle and add it's edges to the edge list. */ + edges.push( + open[j].a, open[j].b, + open[j].b, open[j].c, + open[j].c, open[j].a + ) + open.splice(j, 1) + } + + /* Remove any doubled edges. */ + dedup(edges) + + /* Add a new triangle for each edge. */ + j = edges.length + while(j) { + b = edges[--j] + a = edges[--j] + open.push(new Triangle(a, b, vertices[i])) + } + } + + /* Copy any remaining open triangles to the closed list, and then + * remove any triangles that share a vertex with the supertriangle. */ + Array.prototype.push.apply(closed, open) + + i = closed.length + while(i--) + if(closed[i].a.__sentinel || + closed[i].b.__sentinel || + closed[i].c.__sentinel) + closed.splice(i, 1) + + /* Yay, we're done! */ + return closed + } + + + var layer = null; + + window.plugin.portalfolding.toggle = function() { + if (layer) { + // toggle off + window.map.removeLayer(layer); + layer = null; + return; + } + + var locations = []; + for (var key in window.portals) { + var loc = window.portals[key].options.details.locationE6; + var nloc = { x: loc.lngE6/1E6, y: loc.latE6/1E6 }; + locations.push(nloc); + } + + layer = L.layerGroup([]) + + + var triangles = triangulate(locations); + var i = triangles.length + while(i) { + triangles[--i].draw(layer) + } + + window.map.addLayer(layer); + return layer; + } + + var setup = function() { + $('#toolbox').append('toggle MaxLinks '); + } + + // PLUGIN END ////////////////////////////////////////////////////////// + if(window.iitcLoaded && typeof setup === 'function') { + setup(); + } else { + if(window.bootPlugins) + window.bootPlugins.push(setup); + else + window.bootPlugins = [setup]; + } +} // wrapper end + +// inject code into site context +var script = document.createElement('script'); +script.appendChild(document.createTextNode('('+ wrapper +')();')); +(document.body || document.head || document.documentElement).appendChild(script); \ No newline at end of file