Skip to content

Commit b7dcc81

Browse files
AzgaarCopilot
andauthored
feat: minimap functionality (#1389)
Co-authored-by: Copilot <copilot@github.com>
1 parent 5935ff6 commit b7dcc81

4 files changed

Lines changed: 157 additions & 11 deletions

File tree

public/main.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,15 +223,15 @@ function zoomRaf() {
223223
}
224224

225225
if (didScaleChange) {
226-
postZoom();
226+
invokeActiveZooming();
227+
drawScaleBar(scaleBar, scale);
228+
fitScaleBar(scaleBar, svgWidth, svgHeight);
227229
}
228-
})
229-
}
230230

231-
const postZoom = () => {
232-
invokeActiveZooming();
233-
drawScaleBar(scaleBar, scale);
234-
fitScaleBar(scaleBar, svgWidth, svgHeight);
231+
if (didPositionChange || didScaleChange) {
232+
window.updateMinimap && updateMinimap();
233+
}
234+
});
235235
}
236236

237237
const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", zoomRaf);

public/modules/ui/minimap.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"use strict";
2+
3+
let minimapInitialized = false;
4+
5+
export function openMinimapDialog() {
6+
closeDialogs("#minimap, .stable");
7+
ensureMinimapStyles();
8+
ensureMinimapMarkup();
9+
10+
updateMinimap();
11+
12+
$("#minimap").dialog({
13+
title: "Minimap",
14+
resizable: false,
15+
width: "auto",
16+
position: {my: "left bottom", at: "left+10 bottom-25", of: "svg", collision: "fit"},
17+
open: function () {
18+
$(this).parent().addClass("minimap-dialog");
19+
},
20+
close: function () {
21+
$(this).dialog("destroy");
22+
}
23+
});
24+
}
25+
26+
function ensureMinimapStyles() {
27+
if (byId("minimapStyles")) return;
28+
29+
const style = document.createElement("style");
30+
style.id = "minimapStyles";
31+
style.textContent = /* css */ `
32+
.minimap-dialog .ui-dialog-content {
33+
padding: 0 !important;
34+
overflow: hidden;
35+
}
36+
37+
#minimap {
38+
padding: 0 !important;
39+
background: transparent;
40+
}
41+
42+
#minimapViewportWrap {
43+
position: relative;
44+
width: 20em;
45+
border: 0;
46+
}
47+
48+
#minimapSurface {
49+
display: block;
50+
width: 100%;
51+
height: auto;
52+
cursor: crosshair;
53+
}
54+
55+
#minimapMapUse {
56+
pointer-events: none;
57+
}
58+
59+
#minimapViewport {
60+
fill: rgba(190, 255, 137, 0.1);
61+
stroke: #624954;
62+
stroke-width: 1;
63+
stroke-dasharray: 4;
64+
vector-effect: non-scaling-stroke;
65+
pointer-events: none;
66+
}
67+
`;
68+
69+
document.head.append(style);
70+
}
71+
72+
function ensureMinimapMarkup() {
73+
if (minimapInitialized) return;
74+
75+
const container = byId("minimapContent");
76+
if (!container) return;
77+
78+
minimapInitialized = true;
79+
container.innerHTML = /* html */ `
80+
<div id="minimapViewportWrap">
81+
<svg id="minimapSurface" preserveAspectRatio="xMidYMid meet" aria-label="Map minimap">
82+
<use id="minimapMapUse" href="#viewbox"></use>
83+
<rect id="minimapViewport"></rect>
84+
</svg>
85+
</div>
86+
`;
87+
88+
byId("minimapSurface")?.addEventListener("click", minimapClickToPan);
89+
window.updateMinimap = updateMinimap;
90+
}
91+
92+
function minimapClickToPan(event) {
93+
const minimap = byId("minimapSurface");
94+
if (!minimap) return;
95+
96+
const point = minimap.createSVGPoint();
97+
point.x = event.clientX;
98+
point.y = event.clientY;
99+
100+
const ctm = minimap.getScreenCTM();
101+
if (!ctm) return;
102+
103+
const svgPoint = point.matrixTransform(ctm.inverse());
104+
const x = minmax(svgPoint.x, 0, graphWidth);
105+
const y = minmax(svgPoint.y, 0, graphHeight);
106+
zoomTo(x, y, scale, 450);
107+
}
108+
109+
function updateMinimap() {
110+
const minimap = byId("minimapSurface");
111+
const viewport = byId("minimapViewport");
112+
const mapUse = byId("minimapMapUse");
113+
if (!minimap || !viewport || !mapUse) return;
114+
115+
minimap.setAttribute("viewBox", `0 0 ${graphWidth} ${graphHeight}`);
116+
117+
// #viewbox already has the current transform; invert it in minimap to show the whole world map.
118+
const inverseScale = scale ? 1 / scale : 1;
119+
mapUse.setAttribute(
120+
"transform",
121+
`translate(${rn(-viewX * inverseScale, 3)} ${rn(-viewY * inverseScale, 3)}) scale(${rn(inverseScale, 6)})`
122+
);
123+
124+
const left = Math.max(0, -viewX * inverseScale);
125+
const top = Math.max(0, -viewY * inverseScale);
126+
const right = Math.min(graphWidth, left + svgWidth * inverseScale);
127+
const bottom = Math.min(graphHeight, top + svgHeight * inverseScale);
128+
129+
viewport.setAttribute("x", rn(left, 3));
130+
viewport.setAttribute("y", rn(top, 3));
131+
viewport.setAttribute("width", rn(Math.max(0, right - left), 3));
132+
viewport.setAttribute("height", rn(Math.max(0, bottom - top), 3));
133+
}

public/modules/ui/tools.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ toolsContent.addEventListener("click", function (event) {
2727
else if (button === "overviewMilitaryButton") overviewMilitary();
2828
else if (button === "overviewMarkersButton") overviewMarkers();
2929
else if (button === "overviewCellsButton") viewCellDetails();
30+
else if (button === "openMinimapButton") openMinimap();
3031

3132
// click on Regenerate buttons
3233
if (event.target.parentNode.id === "regenerateFeature") {
@@ -327,8 +328,8 @@ function recreateStates() {
327328
const type = nomadic
328329
? "Nomadic"
329330
: pack.cultures[culture].type === "Nomadic"
330-
? "Generic"
331-
: pack.cultures[culture].type;
331+
? "Generic"
332+
: pack.cultures[culture].type;
332333
const expansionism = rn(Math.random() * byId("sizeVariety").value + 1, 1);
333334

334335
const cultureType = pack.cultures[culture].type;
@@ -898,8 +899,8 @@ function configMarkersGeneration() {
898899
<td><input class="type" value="${type}" /></td>
899900
<td style="position: relative">
900901
<img class="image" src="${isExternal ? icon : ""}" ${
901-
isExternal ? "" : "hidden"
902-
} style="width:1.2em; height:1.2em; vertical-align: middle;">
902+
isExternal ? "" : "hidden"
903+
} style="width:1.2em; height:1.2em; vertical-align: middle;">
903904
<span class="emoji" style="font-size:1.2em">${isExternal ? "" : icon}</span>
904905
<button class="changeIcon icon-pencil"></button>
905906
</td>
@@ -987,3 +988,8 @@ async function overviewCharts() {
987988
const Overview = await import("../dynamic/overview/charts-overview.js?v=1.99.00");
988989
Overview.open();
989990
}
991+
992+
async function openMinimap() {
993+
const Minimap = await import("./minimap.js?v=1.99.00");
994+
Minimap.openMinimapDialog();
995+
}

src/index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,9 @@
22692269
>
22702270
Charts
22712271
</button>
2272+
<button id="openMinimapButton" data-tip="Click to open minimap overview. Click minimap to center view">
2273+
Minimap
2274+
</button>
22722275
</div>
22732276

22742277
<div class="separator">Create</div>
@@ -6004,6 +6007,10 @@
60046007
</div>
60056008
</div>
60066009

6010+
<div id="minimap" style="display: none" class="dialog stable">
6011+
<div id="minimapContent"></div>
6012+
</div>
6013+
60076014
<div id="options3d" class="dialog stable" style="display: none">
60086015
<div id="options3dMesh" style="display: none">
60096016
<div data-tip="Set map rotation speed. Set to 0 is you want to toggle off the rotation">

0 commit comments

Comments
 (0)