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);
}