Skip to content

Instantly share code, notes, and snippets.

@asnapper
Last active September 3, 2015 15:29
Show Gist options
  • Select an option

  • Save asnapper/8821309 to your computer and use it in GitHub Desktop.

Select an option

Save asnapper/8821309 to your computer and use it in GitHub Desktop.

Revisions

  1. asnapper revised this gist Feb 5, 2014. 1 changed file with 325 additions and 0 deletions.
    325 changes: 325 additions & 0 deletions fly-links.user.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,325 @@
    // ==UserScript==
    // @id fly-links@fly
    // @name IITC plugin: Fly Links
    // @category Layer
    // @version 0.2.1.20140205.104258
    // @updateURL none
    // @downloadURL none
    // @description [local-2014-02-05-104258] Calculate how to link the portals to create the largest tidy set of nested fields. Enable from the layer chooser.
    // @include https://www.ingress.com/intel*
    // @include http://www.ingress.com/intel*
    // @match https://www.ingress.com/intel*
    // @match http://www.ingress.com/intel*
    // @grant none
    // ==/UserScript==


    function wrapper(plugin_info) {
    // ensure plugin framework is there, even if iitc is not yet loaded
    if(typeof window.plugin !== 'function') window.plugin = function() {};

    //PLUGIN AUTHORS: writing a plugin outside of the IITC build environment? if so, delete these lines!!
    //(leaving them in place might break the 'About IITC' page or break update checks)
    plugin_info.buildName = 'local';
    plugin_info.dateTimeVersion = '20140205.104258';
    plugin_info.pluginId = 'fly-links';
    //END PLUGIN AUTHORS NOTE



    // PLUGIN START ////////////////////////////////////////////////////////

    // use own namespace for plugin
    window.plugin.flyLinks = function() {};

    // const values
    window.plugin.flyLinks.MAX_PORTALS_TO_OBSERVE = 1000;
    window.plugin.flyLinks.MAX_PORTALS_TO_LINK = 100;
    // zoom level used for projecting points between latLng and pixel coordinates. may affect precision of triangulation
    window.plugin.flyLinks.PROJECT_ZOOM = 16;


    window.plugin.flyLinks.linksLayerGroup = null;
    window.plugin.flyLinks.fieldsLayerGroup = null;

    window.plugin.flyLinks.updateLayer = function() {
    if (!window.map.hasLayer(window.plugin.flyLinks.linksLayerGroup) &&
    !window.map.hasLayer(window.plugin.flyLinks.fieldsLayerGroup))
    return;

    window.plugin.flyLinks.linksLayerGroup.clearLayers();
    window.plugin.flyLinks.fieldsLayerGroup.clearLayers();
    var ctrl = [$('.leaflet-control-layers-selector + span:contains("Fly links")').parent(),
    $('.leaflet-control-layers-selector + span:contains("Fly fields")').parent()];
    if (Object.keys(window.portals).length > window.plugin.flyLinks.MAX_PORTALS_TO_OBSERVE) {
    $.each(ctrl, function(guid, ctl) {ctl.addClass('disabled').attr('title', 'Too many portals: ' + Object.keys(window.portals).length);});
    return;
    }

    var locations = [];

    var bounds = map.getBounds();
    $.each(window.portals, function(guid, portal) {
    var ll = portal.getLatLng();
    if (bounds.contains(ll)) {
    var p = map.project(portal.getLatLng(), window.plugin.flyLinks.PROJECT_ZOOM);
    if (!window.plugin.disablePortal || !(guid in window.plugin.disablePortal.disabledGUID))
    locations.push(p);
    }
    });

    var distance = function(a, b) {
    return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
    };

    var drawLink = function(a, b, style) {
    var alatlng = map.unproject(a, window.plugin.flyLinks.PROJECT_ZOOM);
    var blatlng = map.unproject(b, window.plugin.flyLinks.PROJECT_ZOOM);

    var poly = L.polyline([alatlng, blatlng], style);
    poly.addTo(window.plugin.flyLinks.linksLayerGroup);
    }

    var drawField = function(a, b, c, style) {
    var alatlng = map.unproject(a, window.plugin.flyLinks.PROJECT_ZOOM);
    var blatlng = map.unproject(b, window.plugin.flyLinks.PROJECT_ZOOM);
    var clatlng = map.unproject(c, window.plugin.flyLinks.PROJECT_ZOOM);

    var poly = L.polygon([alatlng, blatlng, clatlng], style);
    poly.addTo(window.plugin.flyLinks.fieldsLayerGroup);
    }

    if (locations.length > window.plugin.flyLinks.MAX_PORTALS_TO_LINK) {
    $.each(ctrl, function(guid, ctl) {ctl.addClass('disabled').attr('title', 'Too many portals (linked/observed): ' + locations.length + '/' + Object.keys(window.portals).length);});
    return;
    }
    $.each(ctrl, function(guid, ctl) {ctl.removeClass('disabled').attr('title', 'portals (linked/observed): ' + locations.length + '/' + Object.keys(window.portals).length);});

    var EPS = 1e-9;
    var det = function(a, b, c) {
    return a.x * b.y - a.y * b.x + b.x * c.y - b.y * c.x + c.x * a.y - c.y * a.x;
    }

    var convexHull = function(points) {
    if (points.length < 3)
    return [];
    var result = [];
    var func = function _func(ai, bi, index) {
    var maxd = 0;
    var maxdi = -1;
    var a = points[ai];
    var b = points[bi];
    var _index = [];
    for (var i = 0; i < index.length; ++i) {
    var c = points[index[i]];
    var d = -det(a, b, c);
    if (d > EPS) {
    _index.push(index[i]);
    }
    if (maxd < d - EPS) {
    maxd = d;
    maxdi = index[i];
    }
    }
    if (maxdi != -1) {
    _func(ai, maxdi, _index);
    _func(maxdi, bi, _index);
    } else {
    result.push(ai);
    }
    }
    var minxi = 0;
    var maxxi = 0;
    var index = [];
    for (var i = 0; i < points.length; ++i) {
    index.push(i);
    if (points[minxi].x > points[i].x)
    minxi = i;
    if (points[maxxi].x < points[i].x)
    maxxi = i;
    }
    func(minxi, maxxi, index);
    func(maxxi, minxi, index);
    return result;
    }

    var index = convexHull(locations);

    var triangulate = function(index, locations) {
    if (index.length == 0)
    return {edges: [], triangles: []};
    var data = [];
    var subtriangulate = function _subtriangulate(ai, bi, ci, index) {
    var _i = [ai, bi, ci].sort(function(a,b){return a-b;});
    if (data[_i[0]] === undefined)
    data[_i[0]] = [];
    if (data[_i[0]][_i[1]-_i[0]] === undefined)
    data[_i[0]][_i[1]-_i[0]] = [];
    if (data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]] === undefined) {
    var _index = [];
    for (var i = 0; i < index.length; ++i) {
    var detc = det(locations[ai], locations[bi], locations[index[i]]);
    var deta = det(locations[bi], locations[ci], locations[index[i]]);
    var detb = det(locations[ci], locations[ai], locations[index[i]]);
    if (deta > EPS && detb > EPS && detc > EPS) {
    _index.push(index[i]);
    }
    }
    var besth = 0;
    var besthi = -1;
    if (_index.length == 0) {
    var a = locations[ai];
    var b = locations[bi];
    var c = locations[ci];
    var s = Math.abs(det(a, b, c));
    var ch = s / distance(a, b);
    var ah = s / distance(b, c);
    var bh = s / distance(c, a);
    besth = Math.min(ah, bh, ch);
    besthi = -1;
    } else {
    var besths = 0;
    for (var i = 0; i < _index.length; ++i) {
    var ch = _subtriangulate(ai, bi, _index[i], _index);
    var ah = _subtriangulate(bi, ci, _index[i], _index);
    var bh = _subtriangulate(ci, ai, _index[i], _index);
    var _besth = Math.min(ah, bh, ch);
    var _besths = ah + bh + ch;
    if (besth < _besth || Math.abs(besth - _besth) <= EPS && besths < _besths) {
    besth = _besth;
    besths = _besths;
    besthi = _index[i];
    }
    }
    }
    data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]] = {height: besth, index: besthi};
    }
    return data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].height;
    }
    var subindex = [];
    for (var i = 0; i < locations.length; ++i) {
    subindex.push(i);
    }
    var best = [];
    for (var len = 1; len <= index.length - 1; ++len) {
    best[len] = [];
    for (var k = 0; k < index.length - len; ++k) {
    var t = 0;
    var tlen = -1;
    for (var _len = 1; _len <= len - 1; ++_len) {
    var _t = 0;
    $.each([best[_len][k].height, best[len-_len][k+_len].height, subtriangulate(index[k], index[k+_len], index[k+len], subindex)], function(guid, __t) {
    if (__t == 0)
    return;
    if (_t == 0 || _t > __t)
    _t = __t;
    });
    if (t == 0 || t < _t) {
    t = _t;
    tlen = _len;
    }
    }
    best[len][k] = {height: t, length: tlen};
    }
    }

    var edges = [];
    var triangles = [];
    var makesubtriangulation = function _makesubtriangulation(ai, bi, ci, depth) {
    var _i = [ai, bi, ci].sort(function(a,b){return a-b;});
    if (data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index == -1) {
    triangles.push(new window.plugin.flyLinks.Triangle(locations[ai], locations[bi], locations[ci], depth));
    } else {
    _makesubtriangulation(ai, bi, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
    _makesubtriangulation(bi, ci, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
    _makesubtriangulation(ci, ai, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
    edges.push(new window.plugin.flyLinks.Edge(locations[ai], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
    edges.push(new window.plugin.flyLinks.Edge(locations[bi], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
    edges.push(new window.plugin.flyLinks.Edge(locations[ci], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
    }
    }
    var maketriangulation = function _maketriangulation(len, a) {
    edges.push(new window.plugin.flyLinks.Edge(locations[index[a]], locations[index[a+len]], 0));
    if (best[len][a].length == -1)
    return;
    makesubtriangulation(index[a], index[a+best[len][a].length], index[a+len], 1);
    _maketriangulation(best[len][a].length, a);
    _maketriangulation(len - best[len][a].length, a + best[len][a].length);
    }
    maketriangulation(index.length - 1, 0);
    return {edges: edges, triangles: triangles};
    }

    var triangulation = triangulate(index, locations);
    var edges = triangulation.edges;
    var triangles = triangulation.triangles;

    $.each(edges, function(idx, edge) {
    drawLink(edge.a, edge.b, {
    color: '#FF0000',
    opacity: 1,
    weight: 1.5,
    clickable: false,
    smoothFactor: 10,
    dashArray: [6, 4],
    });
    });

    $.each(triangles, function(idx, triangle) {
    drawField(triangle.a, triangle.b, triangle.c, {
    stroke: false,
    fill: true,
    fillColor: '#FF0000',
    fillOpacity: 1 - Math.pow(0.85, triangle.depth),
    clickable: false,
    });
    });
    }

    window.plugin.flyLinks.Edge = function(a, b, depth) {
    this.a = a;
    this.b = b;
    this.depth = depth;
    }

    window.plugin.flyLinks.Triangle = function(a, b, c, depth) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.depth = depth;
    }

    window.plugin.flyLinks.setup = function() {
    window.plugin.flyLinks.linksLayerGroup = new L.LayerGroup();
    window.plugin.flyLinks.fieldsLayerGroup = new L.LayerGroup();

    window.addHook('mapDataRefreshEnd', function(e) {
    window.plugin.flyLinks.updateLayer();
    });

    window.map.on('moveend', function() {
    window.plugin.flyLinks.updateLayer();
    });

    window.addLayerGroup('Fly links', window.plugin.flyLinks.linksLayerGroup, false);
    window.addLayerGroup('Fly fields', window.plugin.flyLinks.fieldsLayerGroup, false);
    }
    var setup = window.plugin.flyLinks.setup;

    // PLUGIN END //////////////////////////////////////////////////////////


    setup.info = plugin_info; //add the script info data to the function as a property
    if(!window.bootPlugins) window.bootPlugins = [];
    window.bootPlugins.push(setup);
    // if IITC has already booted, immediately run the 'setup' function
    if(window.iitcLoaded && typeof setup === 'function') setup();
    } // wrapper end
    // inject code into site context
    var script = document.createElement('script');
    var info = {};
    if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description };
    script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');'));
    (document.body || document.head || document.documentElement).appendChild(script);


  2. asnapper created this gist Feb 5, 2014.
    106 changes: 106 additions & 0 deletions disable-portal.user.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,106 @@
    // ==UserScript==
    // @id disable-portal@asnapper
    // @name IITC plugin: Disable Portal
    // @category Tweaks
    // @version 0.0.1.20140205.104101
    // @updateURL none
    // @downloadURL none
    // @description [local-2014-02-05-104101] Allows you disable specific portals for plugins like fly-links
    // @include https://www.ingress.com/intel*
    // @include http://www.ingress.com/intel*
    // @match https://www.ingress.com/intel*
    // @match http://www.ingress.com/intel*
    // @grant none
    // ==/UserScript==


    function wrapper(plugin_info) {
    // ensure plugin framework is there, even if iitc is not yet loaded
    if(typeof window.plugin !== 'function') window.plugin = function() {};

    //PLUGIN AUTHORS: writing a plugin outside of the IITC build environment? if so, delete these lines!!
    //(leaving them in place might break the 'About IITC' page or break update checks)
    plugin_info.buildName = 'local';
    plugin_info.dateTimeVersion = '20140205.104101';
    plugin_info.pluginId = 'disable-portal';
    //END PLUGIN AUTHORS NOTE



    // PLUGIN START ////////////////////////////////////////////////////////

    // use own namespace for plugin
    window.plugin.disablePortal = function() {};

    // array to hold all disabled portal GUIDs
    window.plugin.disablePortal.disabledGUID = [];

    window.plugin.disablePortal.disable = function(guid) {
    window.plugin.disablePortal.disabledGUID[guid] = true;
    window.plugin.disablePortal.addControl({guid:guid});
    //if (window.plugin.flyLinks)
    // window.plugin.flyLinks.updateLayer();
    window.runHooks('mapDataRefreshEnd');
    };

    window.plugin.disablePortal.enable = function(guid) {
    delete window.plugin.disablePortal.disabledGUID[guid];
    window.plugin.disablePortal.addControl({guid:guid});
    //if (window.plugin.flyLinks)
    // window.plugin.flyLinks.updateLayer();
    window.runHooks('mapDataRefreshEnd');
    };

    window.plugin.disablePortal.addControl = function(data) {
    // delete html control
    var node = document.getElementById('disablePortal');
    if (node) {
    node.parentNode.removeChild(node);
    }

    // build onClick attribute strings
    var onClickEnable = 'window.plugin.disablePortal.enable(\'' + data.guid+ '\')';
    var onClickDisable = 'window.plugin.disablePortal.disable(\'' + data.guid+ '\')';

    // (re-)create html control
    var element;
    if (data.guid in window.plugin.disablePortal.disabledGUID) {
    element = $('<div>').html( $('<a>').attr({onclick:onClickEnable, title:'Enable'}).text('Enable') );
    } else {
    element = $('<div>').html( $('<a>').attr({onclick:onClickDisable, title:'Disable'}).text('Disable') );
    }
    //element.attr({id:'disablePortal'});

    // find location to insert
    // DEBUG: verify XPath query!!
    var htmlParent = document.evaluate('//*[@id="portaldetails"]/div[3]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

    // insert (probably ugly...)
    if (htmlParent)
    htmlParent.innerHTML += '<aside id="disablePortal">' + element.html() + '</aside>';
    };

    window.plugin.disablePortal.updateControl = function(guid) {
    //var data.guid = guid;
    //window.plugin.disablePortal.addControl(data);
    };

    window.addHook('portalDetailsUpdated', window.plugin.disablePortal.addControl);

    var setup = function() {};

    // PLUGIN END //////////////////////////////////////////////////////////


    setup.info = plugin_info; //add the script info data to the function as a property
    if(!window.bootPlugins) window.bootPlugins = [];
    window.bootPlugins.push(setup);
    // if IITC has already booted, immediately run the 'setup' function
    if(window.iitcLoaded && typeof setup === 'function') setup();
    } // wrapper end
    // inject code into site context
    var script = document.createElement('script');
    var info = {};
    if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description };
    script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');'));
    (document.body || document.head || document.documentElement).appendChild(script);