diff --git a/public/main.js b/public/main.js
index 6786852a6..8b2b4c6f9 100644
--- a/public/main.js
+++ b/public/main.js
@@ -223,15 +223,15 @@ function zoomRaf() {
}
if (didScaleChange) {
- postZoom();
+ invokeActiveZooming();
+ drawScaleBar(scaleBar, scale);
+ fitScaleBar(scaleBar, svgWidth, svgHeight);
}
- })
-}
-const postZoom = () => {
- invokeActiveZooming();
- drawScaleBar(scaleBar, scale);
- fitScaleBar(scaleBar, svgWidth, svgHeight);
+ if (didPositionChange || didScaleChange) {
+ window.updateMinimap && updateMinimap();
+ }
+ });
}
const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", zoomRaf);
diff --git a/public/modules/ui/minimap.js b/public/modules/ui/minimap.js
new file mode 100644
index 000000000..bbc8f3613
--- /dev/null
+++ b/public/modules/ui/minimap.js
@@ -0,0 +1,133 @@
+"use strict";
+
+let minimapInitialized = false;
+
+export function openMinimapDialog() {
+ closeDialogs("#minimap, .stable");
+ ensureMinimapStyles();
+ ensureMinimapMarkup();
+
+ updateMinimap();
+
+ $("#minimap").dialog({
+ title: "Minimap",
+ resizable: false,
+ width: "auto",
+ position: {my: "left bottom", at: "left+10 bottom-25", of: "svg", collision: "fit"},
+ open: function () {
+ $(this).parent().addClass("minimap-dialog");
+ },
+ close: function () {
+ $(this).dialog("destroy");
+ }
+ });
+}
+
+function ensureMinimapStyles() {
+ if (byId("minimapStyles")) return;
+
+ const style = document.createElement("style");
+ style.id = "minimapStyles";
+ style.textContent = /* css */ `
+ .minimap-dialog .ui-dialog-content {
+ padding: 0 !important;
+ overflow: hidden;
+ }
+
+ #minimap {
+ padding: 0 !important;
+ background: transparent;
+ }
+
+ #minimapViewportWrap {
+ position: relative;
+ width: 20em;
+ border: 0;
+ }
+
+ #minimapSurface {
+ display: block;
+ width: 100%;
+ height: auto;
+ cursor: crosshair;
+ }
+
+ #minimapMapUse {
+ pointer-events: none;
+ }
+
+ #minimapViewport {
+ fill: rgba(190, 255, 137, 0.1);
+ stroke: #624954;
+ stroke-width: 1;
+ stroke-dasharray: 4;
+ vector-effect: non-scaling-stroke;
+ pointer-events: none;
+ }
+ `;
+
+ document.head.append(style);
+}
+
+function ensureMinimapMarkup() {
+ if (minimapInitialized) return;
+
+ const container = byId("minimapContent");
+ if (!container) return;
+
+ minimapInitialized = true;
+ container.innerHTML = /* html */ `
+
+
+
+ `;
+
+ byId("minimapSurface")?.addEventListener("click", minimapClickToPan);
+ window.updateMinimap = updateMinimap;
+}
+
+function minimapClickToPan(event) {
+ const minimap = byId("minimapSurface");
+ if (!minimap) return;
+
+ const point = minimap.createSVGPoint();
+ point.x = event.clientX;
+ point.y = event.clientY;
+
+ const ctm = minimap.getScreenCTM();
+ if (!ctm) return;
+
+ const svgPoint = point.matrixTransform(ctm.inverse());
+ const x = minmax(svgPoint.x, 0, graphWidth);
+ const y = minmax(svgPoint.y, 0, graphHeight);
+ zoomTo(x, y, scale, 450);
+}
+
+function updateMinimap() {
+ const minimap = byId("minimapSurface");
+ const viewport = byId("minimapViewport");
+ const mapUse = byId("minimapMapUse");
+ if (!minimap || !viewport || !mapUse) return;
+
+ minimap.setAttribute("viewBox", `0 0 ${graphWidth} ${graphHeight}`);
+
+ // #viewbox already has the current transform; invert it in minimap to show the whole world map.
+ const inverseScale = scale ? 1 / scale : 1;
+ mapUse.setAttribute(
+ "transform",
+ `translate(${rn(-viewX * inverseScale, 3)} ${rn(-viewY * inverseScale, 3)}) scale(${rn(inverseScale, 6)})`
+ );
+
+ const left = Math.max(0, -viewX * inverseScale);
+ const top = Math.max(0, -viewY * inverseScale);
+ const right = Math.min(graphWidth, left + svgWidth * inverseScale);
+ const bottom = Math.min(graphHeight, top + svgHeight * inverseScale);
+
+ viewport.setAttribute("x", rn(left, 3));
+ viewport.setAttribute("y", rn(top, 3));
+ viewport.setAttribute("width", rn(Math.max(0, right - left), 3));
+ viewport.setAttribute("height", rn(Math.max(0, bottom - top), 3));
+}
diff --git a/public/modules/ui/tools.js b/public/modules/ui/tools.js
index 34c1cd120..c98fa28c9 100644
--- a/public/modules/ui/tools.js
+++ b/public/modules/ui/tools.js
@@ -27,6 +27,7 @@ toolsContent.addEventListener("click", function (event) {
else if (button === "overviewMilitaryButton") overviewMilitary();
else if (button === "overviewMarkersButton") overviewMarkers();
else if (button === "overviewCellsButton") viewCellDetails();
+ else if (button === "openMinimapButton") openMinimap();
// click on Regenerate buttons
if (event.target.parentNode.id === "regenerateFeature") {
@@ -327,8 +328,8 @@ function recreateStates() {
const type = nomadic
? "Nomadic"
: pack.cultures[culture].type === "Nomadic"
- ? "Generic"
- : pack.cultures[culture].type;
+ ? "Generic"
+ : pack.cultures[culture].type;
const expansionism = rn(Math.random() * byId("sizeVariety").value + 1, 1);
const cultureType = pack.cultures[culture].type;
@@ -898,8 +899,8 @@ function configMarkersGeneration() {
|
+ isExternal ? "" : "hidden"
+ } style="width:1.2em; height:1.2em; vertical-align: middle;">
${isExternal ? "" : icon}
|
@@ -987,3 +988,8 @@ async function overviewCharts() {
const Overview = await import("../dynamic/overview/charts-overview.js?v=1.99.00");
Overview.open();
}
+
+async function openMinimap() {
+ const Minimap = await import("./minimap.js?v=1.99.00");
+ Minimap.openMinimapDialog();
+}
diff --git a/src/index.html b/src/index.html
index 2a74b3a73..623bafd40 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2269,6 +2269,9 @@
>
Charts
+
Create
@@ -6004,6 +6007,10 @@
+
+