NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// OUTDATED AND NOT WORKING (yet) // ==UserScript== // @name ConsulSegmentsOutliner // @namespace consulwar.ru // @description Basic space segments visual outliner // @require https://code.jquery.com/jquery-1.12.4.js // @version 0.2.1 // @author Xorboo // @license MIT // @match *://consulwar.ru/* // ==/UserScript== /* RELEASE NOTES 0.2.1 . fixed drawing outside of the screen view . improved performance a bit 0.1.1 + basic outliner */ var fillOpacity = "20"; // in hex var strokeOpacity = "AA"; // in hex var strokeWidth = 2; var palette = ["FFFFFF", "FF0000", "008000", "0000FF", "FFA500", "00FF00", "FF00FF", "FFFF00", "00FFFF"]; var canvasExtraSize = {x:2000, y:1000}; var zoomFactor = 1.0; var currentBounds = null; $(function() { log('============ Start'); Template.cosmosObjects.onRendered(function() { addZoomListener(); updatePolygons(); }); log('============ End'); }); function addZoomListener() { var basePlanet = Game.Planets.getBase(); var basePlanetElement = $(".leaflet-marker-pane div[data-id='" + basePlanet._id + "']"); var observer = new MutationObserver(function(mutations) { updatePolygons(); }); observer.observe(basePlanetElement[0], { attributes : true, attributeFilter : ['style', 'class'] }); } function updatePolygons() { log("Updating polygons..."); updateZoomFactor(); var planets = getGroupedPlanets(); drawPlanets(planets); log("Done!"); } function updateZoomFactor() { var basePlanet = Game.Planets.getAll().fetch()[0]; var el = $(".leaflet-marker-pane div[data-id='" + basePlanet._id + "']"); var zoomClass = ""; var classes = el.attr('class').split(' '); for (var i = classes.length - 1; i >= 0; i--) { var testClass = classes[i]; if (testClass.startsWith("zoom-lt-")) { zoomClass = testClass.trim(); break; } } if (zoomClass == "zoom-lt-5") { zoomFactor = 0.25; } else if (zoomClass == "zoom-lt-7") { zoomFactor = 0.5; } else if (zoomClass == "zoom-lt-9") { zoomFactor = 1.0; } else { zoomFactor = 1.8; } log("Zoom factor: " + zoomFactor); } function getGroupedPlanets() { var allPlanets = Game.Planets.getAll().fetch(); // Get planet groups for each segment var planetGroups = allPlanets.reduce(function(groups, planet) { var planetGroup = planet.hand + "_" + planet.segment; if (groups.indexOf(planetGroup) === -1) { groups.push(planetGroup); } return groups; }, []); // Read all planet transforms var regExp = new RegExp("matrix\\(\\d+, \\d+, \\d+, \\d+, (-?\\d+), (-?\\d+)\\)"); var planetElements = $(".leaflet-marker-pane").find("div.map-planet-marker"); var planetsDict = {}; planetElements.each(function(index) { var el = $(this); var matches = regExp.exec(el.css("transform")); planetsDict[el.attr("data-id")] = { //planet: planetElement, x: parseInt(matches[1]), y: parseInt(matches[2]) }; }); // Group planets var groups = planetGroups.map(function(group) { var groupPlanets = allPlanets.filter(function(planet) { var planetGroup = planet.hand + "_" + planet.segment; return planetGroup === group; }); var groupElements = groupPlanets.map(function(planet) { return planetsDict[planet._id]; }); var planet = groupPlanets[0]; var groupIndex = planet.segment * 2 + planet.hand * 3; return { planets: groupElements, index: groupIndex }; }); return groups; } function drawPlanets(planets) { var root = $("#segment-outliner"); if (!root.length) { $(".leaflet-map-pane").prepend(`<svg id="segment-outliner" height="3000px" width="5000px"></svg>`); root = $("#segment-outliner"); } currentBounds = null; $.each(planets, function(index, group) { var points = addExtraPoints(group.planets); var convexPolygon = convexHull(points); group.finalPolygon = extrudePolygon(convexPolygon); updateBounds(group.finalPolygon); }); var plygonsHtml = ""; $.each(planets, function(index, group) { var color = palette[group.index % palette.length]; plygonsHtml += drawGroup(group.finalPolygon, color, root); }); root.attr("width", currentBounds.maxX - currentBounds.minX); root.attr("height", currentBounds.maxY - currentBounds.minY); root.css("transform", "translate3d(" + currentBounds.minX + "px, " + currentBounds.minY + "px, 0px)"); root.html(plygonsHtml); } function updateBounds(polygon) { $.each(polygon, function(index, point) { if (!currentBounds) { currentBounds = { minX: point.x, maxX: point.x, minY: point.y, maxY: point.y }; } currentBounds.minX = Math.min(currentBounds.minX, point.x); currentBounds.maxX = Math.max(currentBounds.maxX, point.x); currentBounds.minY = Math.min(currentBounds.minY, point.y); currentBounds.maxY = Math.max(currentBounds.maxY, point.y); }); } function drawGroup(polygon, color, root) { var pointsText = ""; $.each(polygon, function(index, point) { pointsText += (point.x - currentBounds.minX) + "," + (point.y - currentBounds.minY) + " "; }); var fillColor = "#" + color + fillOpacity; var strokeColor = "#" + color + strokeOpacity; return `<polygon points="` + pointsText + `" style="fill:` + fillColor + `;stroke:` + strokeColor + `;stroke-width:` + strokeWidth + `" />`; } function addExtraPoints(points) { var shift = 10 * zoomFactor; if (points.length === 1) { var p = points[0]; return [ { x: p.x - shift, y: p.y - shift }, { x: p.x - shift, y: p.y + shift }, { x: p.x + shift, y: p.y - shift }, { x: p.x + shift, y: p.y + shift } ]; } if (points.length === 2) { var p1 = points[0]; var p2 = points[1]; var d = { x: p2.x - p1.x, y: p2.y - p1.y }; var len = Math.sqrt(d.x * d.x + d.y * d.y); var norm = { x: d.x * shift / len, y: -d.y * shift / len }; return [ { x: p1.x + norm.x, y: p1.y + norm.y }, { x: p1.x - norm.x, y: p1.y - norm.y }, { x: p2.x + norm.x, y: p2.y + norm.y }, { x: p2.x - norm.x, y: p2.y - norm.y } ]; } return points; } function extrudePolygon(polygon) { // Add extra points var normalShift = 25 * zoomFactor; var points = []; var len = polygon.length; for(var i = 0; i < len; i++) { var p = polygon[i]; var prev = polygon[(i - 1 + len) % len]; var anglePrev = Math.atan2(p.y - prev.y, p.x - prev.x) + Math.PI / 2; points.push({ x: p.x + Math.cos(anglePrev) * normalShift, y: p.y + Math.sin(anglePrev) * normalShift }); var next = polygon[(i + 1) % len]; var angleNext = Math.atan2(p.y - next.y, p.x - next.x) - Math.PI / 2; points.push({ x: p.x + Math.cos(angleNext) * normalShift, y: p.y + Math.sin(angleNext) * normalShift }); } // Extrude from the center var p0 = points[0]; var b = { minX: p0.x, minY: p0.y, maxX: p0.x, maxY: p0.y }; $.each(points, function(index, p) { b.minX = Math.min(b.minX, p.x); b.maxX = Math.max(b.maxX, p.x); b.minY = Math.min(b.minY, p.y); b.maxY = Math.max(b.maxY, p.y); }); var c = { x: (b.maxX + b.minX) / 2, y: (b.maxY + b.minY) / 2 }; var shift = 15 * zoomFactor; $.each(points, function(index, p) { var d = { x: p.x - c.x, y : p.y - c.y }; var len = Math.sqrt(d.x * d.x + d.y * d.y); p.x += d.x * shift / len; p.y += d.y * shift / len; }); return points; } function convexHull(points) { points.sort(function (a, b) { return a.x != b.x ? a.x - b.x : a.y - b.y; }); var n = points.length; var hull = []; for (var i = 0; i < 2 * n; i++) { var j = i < n ? i : 2 * n - 1 - i; while (hull.length >= 2 && removeMiddle(hull[hull.length - 2], hull[hull.length - 1], points[j])) hull.pop(); hull.push(points[j]); } hull.pop(); return hull; } function removeMiddle(a, b, c) { var cross = (a.x - b.x) * (c.y - b.y) - (a.y - b.y) * (c.x - b.x); var dot = (a.x - b.x) * (c.x - b.x) + (a.y - b.y) * (c.y - b.y); return cross < 0 || cross === 0 && dot <= 0; } // LOGGING function log(text) { console.log('ConsulSegmentsOutliner: ' + text); } function logObj(obj, obj_name='[Object]') { log('\'' + obj_name + '\' data:'); console.dir(obj); }