From f0ff23a119fcb216155ebd277cd5bc21ef0117e6 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 13 Oct 2024 15:08:40 +0200 Subject: [PATCH 1/7] fix: #1152, don't keep removed locked cultures on regenerate --- modules/cultures-generator.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js index 3096f96c..f2203146 100644 --- a/modules/cultures-generator.js +++ b/modules/cultures-generator.js @@ -123,26 +123,26 @@ window.Cultures = (function () { cultures.forEach(c => (c.base = c.base % nameBases.length)); function selectCultures(culturesNumber) { - let def = getDefault(culturesNumber); + let defaultCultures = getDefault(culturesNumber); const cultures = []; pack.cultures?.forEach(function (culture) { - if (culture.lock) cultures.push(culture); + if (culture.lock && !culture.removed) cultures.push(culture); }); if (!cultures.length) { - if (culturesNumber === def.length) return def; - if (def.every(d => d.odd === 1)) return def.splice(0, culturesNumber); + if (culturesNumber === defaultCultures.length) return defaultCultures; + if (defaultCultures.every(d => d.odd === 1)) return defaultCultures.splice(0, culturesNumber); } - for (let culture, rnd, i = 0; cultures.length < culturesNumber && def.length > 0; ) { + for (let culture, rnd, i = 0; cultures.length < culturesNumber && defaultCultures.length > 0; ) { do { - rnd = rand(def.length - 1); - culture = def[rnd]; + rnd = rand(defaultCultures.length - 1); + culture = defaultCultures[rnd]; i++; } while (i < 200 && !P(culture.odd)); cultures.push(culture); - def.splice(rnd, 1); + defaultCultures.splice(rnd, 1); } return cultures; } From 6c37c7babf7b1ded5ddfee65749b681da27e7ca6 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 13 Oct 2024 15:10:31 +0200 Subject: [PATCH 2/7] fix: #1152, don't keep removed locked cultures on regenerate --- index.html | 2 +- versioning.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index eb3c2ef5..2671ad1d 100644 --- a/index.html +++ b/index.html @@ -8053,7 +8053,7 @@ - + diff --git a/versioning.js b/versioning.js index ade2a04c..a4cfb23c 100644 --- a/versioning.js +++ b/versioning.js @@ -12,7 +12,7 @@ * * Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2 */ -const VERSION = "1.105.12"; +const VERSION = "1.105.13"; if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function"); { From c447afb829c6d0e35a6fc0aedaff74c691237577 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 13 Oct 2024 20:32:37 +0200 Subject: [PATCH 3/7] fix: features rendering - close the ring --- index.html | 4 ++-- modules/renderers/draw-features.js | 2 +- modules/ui/lakes-editor.js | 10 +++++----- versioning.js | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index 2671ad1d..a54ede5b 100644 --- a/index.html +++ b/index.html @@ -8090,7 +8090,7 @@ - + @@ -8123,7 +8123,7 @@ - + diff --git a/modules/renderers/draw-features.js b/modules/renderers/draw-features.js index 0221a1c1..ac2a395e 100644 --- a/modules/renderers/draw-features.js +++ b/modules/renderers/draw-features.js @@ -55,7 +55,7 @@ function getFeaturePath(feature) { const clippedPoints = clipPoly(simplifiedPoints, 1); const lineGen = d3.line().curve(d3.curveBasisClosed); - const path = round(lineGen(clippedPoints)); + const path = round(lineGen(clippedPoints)) + "Z"; return path; } diff --git a/modules/ui/lakes-editor.js b/modules/ui/lakes-editor.js index a6ce80d1..55f3fb5b 100644 --- a/modules/ui/lakes-editor.js +++ b/modules/ui/lakes-editor.js @@ -15,7 +15,7 @@ function editLake() { debug.append("g").attr("id", "vertices"); elSelected = d3.select(node); updateLakeValues(); - selectLakeGroup(node); + selectLakeGroup(); drawLakeVertices(); viewbox.on("touchmove mousemove", null); @@ -140,13 +140,13 @@ function editLake() { lake.name = lakeName.value = Names.getBase(rand(nameBases.length - 1)); } - function selectLakeGroup(node) { - const group = node.parentNode.id; + function selectLakeGroup() { + const lake = getLake(); + const select = byId("lakeGroup"); select.options.length = 0; // remove all options - lakes.selectAll("g").each(function () { - select.options.add(new Option(this.id, this.id, false, this.id === group)); + select.options.add(new Option(this.id, this.id, false, this.id === lake.group)); }); } diff --git a/versioning.js b/versioning.js index a4cfb23c..ef1bf42f 100644 --- a/versioning.js +++ b/versioning.js @@ -12,7 +12,7 @@ * * Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2 */ -const VERSION = "1.105.13"; +const VERSION = "1.105.14"; if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function"); { From efbe0373b04d29c6d40109982fc72783b468ef9d Mon Sep 17 00:00:00 2001 From: Azgaar Date: Wed, 16 Oct 2024 15:20:44 +0200 Subject: [PATCH 4/7] fix: lock all burgs --- index.html | 2 +- modules/ui/burgs-overview.js | 2 +- versioning.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index a54ede5b..96a8c1aa 100644 --- a/index.html +++ b/index.html @@ -8102,7 +8102,7 @@ - + diff --git a/modules/ui/burgs-overview.js b/modules/ui/burgs-overview.js index 1170b5f1..2a487b67 100644 --- a/modules/ui/burgs-overview.js +++ b/modules/ui/burgs-overview.js @@ -608,7 +608,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) { const activeBurgs = pack.burgs.filter(b => b.i && !b.removed); const allLocked = activeBurgs.every(burg => burg.lock); - pack.burgs.forEach(burg => { + activeBurgs.forEach(burg => { burg.lock = !allLocked; }); diff --git a/versioning.js b/versioning.js index ef1bf42f..1aa1326c 100644 --- a/versioning.js +++ b/versioning.js @@ -12,7 +12,7 @@ * * Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2 */ -const VERSION = "1.105.14"; +const VERSION = "1.105.15"; if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function"); { From 87e1dc2c5de5f7dcb1e510f32a43d46681747586 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sat, 19 Oct 2024 13:25:34 +0200 Subject: [PATCH 5/7] Draw state labels improvement (#1155) * chore: render debug elements * feat: redo draw state labels algo --------- Co-authored-by: Azgaar --- index.html | 6 +- main.js | 2 +- modules/renderers/draw-state-labels.js | 258 +++++++++++++------------ modules/ui/labels-editor.js | 2 +- utils/debugUtils.js | 14 ++ versioning.js | 2 +- 6 files changed, 156 insertions(+), 128 deletions(-) diff --git a/index.html b/index.html index 96a8c1aa..1e2c4356 100644 --- a/index.html +++ b/index.html @@ -8073,7 +8073,7 @@ - + @@ -8092,7 +8092,7 @@ - + @@ -8131,7 +8131,7 @@ - + diff --git a/main.js b/main.js index 48afa810..7beab61f 100644 --- a/main.js +++ b/main.js @@ -106,10 +106,10 @@ coastline.append("g").attr("id", "lake_island"); terrs.append("g").attr("id", "oceanHeights"); terrs.append("g").attr("id", "landHeights"); +let burgLabels = labels.append("g").attr("id", "burgLabels"); labels.append("g").attr("id", "states"); labels.append("g").attr("id", "addedLabels"); -let burgLabels = labels.append("g").attr("id", "burgLabels"); burgIcons.append("g").attr("id", "cities"); burgLabels.append("g").attr("id", "cities"); anchors.append("g").attr("id", "cities"); diff --git a/modules/renderers/draw-state-labels.js b/modules/renderers/draw-state-labels.js index 15bb7ea5..ab49437d 100644 --- a/modules/renderers/draw-state-labels.js +++ b/modules/renderers/draw-state-labels.js @@ -14,11 +14,11 @@ function drawStateLabels(list) { // increase step to 15 or 30 to make it faster and more horyzontal // decrease step to 5 to improve accuracy const ANGLE_STEP = 9; - const raycast = precalculateAngles(ANGLE_STEP); + const angles = precalculateAngles(ANGLE_STEP); - const INITIAL_DISTANCE = 10; - const DISTANCE_STEP = 15; - const MAX_ITERATIONS = 100; + const LENGTH_START = 5; + const LENGTH_STEP = 5; + const LENGTH_MAX = 300; const labelPaths = getLabelPaths(); const letterLength = checkExampleLetterLength(); @@ -35,87 +35,25 @@ function drawStateLabels(list) { if (list && !list.includes(state.i)) continue; const offset = getOffsetWidth(state.cells); - const maxLakeSize = state.cells / 50; + const maxLakeSize = state.cells / 20; const [x0, y0] = state.pole; - const offsetPoints = new Map( - (offset ? raycast : []).map(({angle, x: x1, y: y1}) => { - const [x, y] = [x0 + offset * x1, y0 + offset * y1]; - return [angle, {x, y}]; - }) - ); - - const distances = raycast.map(({angle, x: dx, y: dy, modifier}) => { - let distanceMin; - const distance1 = getMaxDistance(state.i, {x: x0, y: y0}, dx, dy, maxLakeSize); - - if (offset) { - const point2 = offsetPoints.get(angle - 90 < 0 ? angle + 270 : angle - 90); - const distance2 = getMaxDistance(state.i, point2, dx, dy, maxLakeSize); - - const point3 = offsetPoints.get(angle + 90 >= 360 ? angle - 270 : angle + 90); - const distance3 = getMaxDistance(state.i, point3, dx, dy, maxLakeSize); - - distanceMin = Math.min(distance1, distance2, distance3); - } else { - distanceMin = distance1; - } - - const [x, y] = [x0 + distanceMin * dx, y0 + distanceMin * dy]; - return {angle, distance: distanceMin * modifier, x, y}; + const rays = angles.map(({angle, dx, dy}) => { + const {length, x, y} = raycast({stateId: state.i, x0, y0, dx, dy, maxLakeSize, offset}); + return {angle, length, x, y}; }); + const [ray1, ray2] = findBestRayPair(rays); - const { - angle, - x: x1, - y: y1 - } = distances.reduce( - (acc, {angle, distance, x, y}) => { - if (distance > acc.distance) return {angle, distance, x, y}; - return acc; - }, - {angle: 0, distance: 0, x: 0, y: 0} - ); + const pathPoints = [[ray1.x, ray1.y], state.pole, [ray2.x, ray2.y]]; + if (ray1.x > ray2.x) pathPoints.reverse(); - const oppositeAngle = angle >= 180 ? angle - 180 : angle + 180; - const {x: x2, y: y2} = distances.reduce( - (acc, {angle, distance, x, y}) => { - const angleDif = getAnglesDif(angle, oppositeAngle); - const score = distance * getAngleModifier(angleDif); - if (score > acc.score) return {angle, score, x, y}; - return acc; - }, - {angle: 0, score: 0, x: 0, y: 0} - ); + DEBUG && drawPoint(state.pole, {color: "black", radius: 1}); + DEBUG && drawPath(pathPoints, {color: "black", width: 0.2}); - const pathPoints = [[x1, y1], state.pole, [x2, y2]]; - if (x1 > x2) pathPoints.reverse(); labelPaths.push([state.i, pathPoints]); } return labelPaths; - - function getMaxDistance(stateId, point, dx, dy, maxLakeSize) { - let distance = INITIAL_DISTANCE; - - for (let i = 0; i < MAX_ITERATIONS; i++) { - const [x, y] = [point.x + distance * dx, point.y + distance * dy]; - const cellId = findCell(x, y, DISTANCE_STEP); - - // drawPoint([x, y], {color: cellId && isPassable(cellId) ? "blue" : "red", radius: 0.8}); - - if (!cellId || !isPassable(cellId)) break; - distance += DISTANCE_STEP; - } - - return distance; - - function isPassable(cellId) { - const feature = features[cells.f[cellId]]; - if (feature.type === "lake") return feature.cells <= maxLakeSize; - return stateIds[cellId] === stateId; - } - } } function checkExampleLetterLength() { @@ -129,7 +67,7 @@ function drawStateLabels(list) { function drawLabelPath(letterLength) { const mode = options.stateLabelsMode || "auto"; - const lineGen = d3.line().curve(d3.curveBundle.beta(1)); + const lineGen = d3.line().curve(d3.curveNatural); const textGroup = d3.select("g#labels > g#states"); const pathGroup = d3.select("defs > g#deftemp > g#textPaths"); @@ -192,35 +130,15 @@ function drawStateLabels(list) { const text = pathLength > state.fullName.length * 1.8 ? state.fullName : state.name; textElement.innerHTML = `${text}`; - const correctedRatio = minmax(rn((pathLength / text.length) * 50), 40, 130); + const correctedRatio = minmax(rn((pathLength / text.length) * 50), 50, 130); textElement.setAttribute("font-size", correctedRatio + "%"); } } - // point offset to reduce label overlap with state borders function getOffsetWidth(cellsNumber) { - if (cellsNumber < 80) return 0; - if (cellsNumber < 140) return 5; - if (cellsNumber < 200) return 15; - if (cellsNumber < 300) return 20; - if (cellsNumber < 500) return 25; - return 30; - } - - // difference between two angles in range [0, 180] - function getAnglesDif(angle1, angle2) { - return 180 - Math.abs(Math.abs(angle1 - angle2) - 180); - } - - // score multiplier based on angle difference betwee left and right sides - function getAngleModifier(angleDif) { - if (angleDif === 0) return 1; - if (angleDif <= 15) return 0.95; - if (angleDif <= 30) return 0.9; - if (angleDif <= 45) return 0.6; - if (angleDif <= 60) return 0.3; - if (angleDif <= 90) return 0.1; - return 0; // >90 + if (cellsNumber < 40) return 0; + if (cellsNumber < 200) return 5; + return 10; } function precalculateAngles(step) { @@ -228,37 +146,133 @@ function drawStateLabels(list) { const RAD = Math.PI / 180; for (let angle = 0; angle < 360; angle += step) { - const x = Math.cos(angle * RAD); - const y = Math.sin(angle * RAD); - const angleDif = 90 - Math.abs((angle % 180) - 90); - const modifier = 1 - angleDif / 120; // [0.25, 1] - angles.push({angle, modifier, x, y}); + const dx = Math.cos(angle * RAD); + const dy = Math.sin(angle * RAD); + angles.push({angle, dx, dy}); } return angles; } + function raycast({stateId, x0, y0, dx, dy, maxLakeSize, offset}) { + let ray = {length: 0, x: x0, y: y0}; + + for (let length = LENGTH_START; length < LENGTH_MAX; length += LENGTH_STEP) { + const [x, y] = [x0 + length * dx, y0 + length * dy]; + // offset points are perpendicular to the ray + const offset1 = [x + -dy * offset, y + dx * offset]; + const offset2 = [x + dy * offset, y + -dx * offset]; + + DEBUG && drawPoint([x, y], {color: isInsideState(x, y) ? "blue" : "red", radius: 0.8}); + DEBUG && drawPoint(offset1, {color: isInsideState(...offset1) ? "blue" : "red", radius: 0.4}); + DEBUG && drawPoint(offset2, {color: isInsideState(...offset2) ? "blue" : "red", radius: 0.4}); + + const inState = isInsideState(x, y) && isInsideState(...offset1) && isInsideState(...offset2); + if (!inState) break; + ray = {length, x, y}; + } + + return ray; + + function isInsideState(x, y) { + if (x < 0 || x > graphWidth || y < 0 || y > graphHeight) return false; + const cellId = findCell(x, y); + + const feature = features[cells.f[cellId]]; + if (feature.type === "lake") return isInnerLake(feature) || isSmallLake(feature); + + return stateIds[cellId] === stateId; + } + + function isInnerLake(feature) { + return feature.shoreline.every(cellId => stateIds[cellId] === stateId); + } + + function isSmallLake(feature) { + return feature.cells <= maxLakeSize; + } + } + + function findBestRayPair(rays) { + let bestPair = null; + let bestScore = -Infinity; + + for (let i = 0; i < rays.length; i++) { + const score1 = rays[i].length * scoreRayAngle(rays[i].angle); + + for (let j = i + 1; j < rays.length; j++) { + const score2 = rays[j].length * scoreRayAngle(rays[j].angle); + const pairScore = (score1 + score2) * scoreCurvature(rays[i].angle, rays[j].angle); + + if (pairScore > bestScore) { + bestScore = pairScore; + bestPair = [rays[i], rays[j]]; + } + } + } + + return bestPair; + } + + function scoreRayAngle(angle) { + const normalizedAngle = Math.abs(angle % 180); // [0, 180] + const horizontality = Math.abs(normalizedAngle - 90) / 90; // [0, 1] + + if (horizontality === 1) return 1; // Best: horizontal + if (horizontality >= 0.75) return 0.9; // Very good: slightly slanted + if (horizontality >= 0.5) return 0.6; // Good: moderate slant + if (horizontality >= 0.25) return 0.5; // Acceptable: more slanted + if (horizontality >= 0.15) return 0.2; // Poor: almost vertical + return 0.1; // Very poor: almost vertical + } + + function scoreCurvature(angle1, angle2) { + const delta = getAngleDelta(angle1, angle2); + const similarity = evaluateArc(angle1, angle2); + + if (delta === 180) return 1; // straight line: best + if (delta < 90) return 0; // acute: not allowed + if (delta < 120) return 0.6 * similarity; + if (delta < 140) return 0.7 * similarity; + if (delta < 160) return 0.8 * similarity; + + return similarity; + } + + function getAngleDelta(angle1, angle2) { + let delta = Math.abs(angle1 - angle2) % 360; + if (delta > 180) delta = 360 - delta; // [0, 180] + return delta; + } + + // compute arc similarity towards x-axis + function evaluateArc(angle1, angle2) { + const proximity1 = Math.abs((angle1 % 180) - 90); + const proximity2 = Math.abs((angle2 % 180) - 90); + return 1 - Math.abs(proximity1 - proximity2) / 90; + } + function getLinesAndRatio(mode, name, fullName, pathLength) { - // short name - if (mode === "short" || (mode === "auto" && pathLength <= name.length)) { - const lines = splitInTwo(name); + if (mode === "short") return getShortOneLine(); + if (pathLength > fullName.length * 2) return getFullOneLine(); + return getFullTwoLines(); + + function getShortOneLine() { + const ratio = pathLength / name.length; + return [[name], minmax(rn(ratio * 60), 50, 150)]; + } + + function getFullOneLine() { + const ratio = pathLength / fullName.length; + return [[fullName], minmax(rn(ratio * 70), 70, 170)]; + } + + function getFullTwoLines() { + const lines = splitInTwo(fullName); const longestLineLength = d3.max(lines.map(({length}) => length)); const ratio = pathLength / longestLineLength; - return [lines, minmax(rn(ratio * 60), 50, 150)]; + return [lines, minmax(rn(ratio * 60), 70, 150)]; } - - // full name: one line - if (pathLength > fullName.length * 2) { - const lines = [fullName]; - const ratio = pathLength / lines[0].length; - return [lines, minmax(rn(ratio * 70), 70, 170)]; - } - - // full name: two lines - const lines = splitInTwo(fullName); - const longestLineLength = d3.max(lines.map(({length}) => length)); - const ratio = pathLength / longestLineLength; - return [lines, minmax(rn(ratio * 60), 70, 150)]; } // check whether multi-lined label is mostly inside the state. If no, replace it with short name label diff --git a/modules/ui/labels-editor.js b/modules/ui/labels-editor.js index 809be210..8c47ec99 100644 --- a/modules/ui/labels-editor.js +++ b/modules/ui/labels-editor.js @@ -122,7 +122,7 @@ function editLabel() { function redrawLabelPath() { const path = byId("textPath_" + elSelected.attr("id")); - lineGen.curve(d3.curveBundle.beta(1)); + lineGen.curve(d3.curveNatural); const points = []; debug .select("#controlPoints") diff --git a/utils/debugUtils.js b/utils/debugUtils.js index 6fd46860..1859f3ae 100644 --- a/utils/debugUtils.js +++ b/utils/debugUtils.js @@ -56,3 +56,17 @@ function drawRouteConnections() { } } } + +function drawPoint([x, y], {color = "red", radius = 0.5}) { + debug.append("circle").attr("cx", x).attr("cy", y).attr("r", radius).attr("fill", color); +} + +function drawPath(points, {color = "red", width = 0.5}) { + const lineGen = d3.line().curve(d3.curveBundle); + debug + .append("path") + .attr("d", round(lineGen(points))) + .attr("stroke", color) + .attr("stroke-width", width) + .attr("fill", "none"); +} diff --git a/versioning.js b/versioning.js index 1aa1326c..025c183a 100644 --- a/versioning.js +++ b/versioning.js @@ -12,7 +12,7 @@ * * Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2 */ -const VERSION = "1.105.15"; +const VERSION = "1.105.16"; if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function"); { From 8a4f28b32105cb7790c52dd69f26df130a7cf51e Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sat, 19 Oct 2024 13:32:59 +0200 Subject: [PATCH 6/7] fix: CRLF issue --- index.html | 2 +- modules/io/load.js | 14 ++++++++++++-- versioning.js | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 1e2c4356..790a4d54 100644 --- a/index.html +++ b/index.html @@ -8119,7 +8119,7 @@ - + diff --git a/modules/io/load.js b/modules/io/load.js index bc9781fe..cd00b28c 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -153,11 +153,21 @@ async function uncompress(compressedData) { async function parseLoadedResult(result) { try { const resultAsString = new TextDecoder().decode(result); + // data can be in FMG internal format or base64 encoded const isDelimited = resultAsString.substring(0, 10).includes("|"); - const decoded = isDelimited ? resultAsString : decodeURIComponent(atob(resultAsString)); + let content = isDelimited ? resultAsString : decodeURIComponent(atob(resultAsString)); - const mapData = decoded.split("\r\n"); // split by CRLF + // fix if svg part has CRLF line endings instead of LF + const svgMatch = content.match(/]*id="map"[\s\S]*?<\/svg>/); + const svgContent = svgMatch[0]; + const hasCrlfEndings = svgContent.includes("\r\n"); + if (hasCrlfEndings) { + const correctedSvgContent = svgContent.replace(/\r\n/g, "\n"); + content = content.replace(svgContent, correctedSvgContent); + } + + const mapData = content.split("\r\n"); // split by CRLF const mapVersion = parseMapVersion(mapData[0].split("|")[0] || mapData[0] || ""); return {mapData, mapVersion}; diff --git a/versioning.js b/versioning.js index 025c183a..24c2f84a 100644 --- a/versioning.js +++ b/versioning.js @@ -12,7 +12,7 @@ * * Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2 */ -const VERSION = "1.105.16"; +const VERSION = "1.105.17"; if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function"); { From 6d69eb855fd11ba3899409b319806512b4177aab Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sat, 19 Oct 2024 14:02:04 +0200 Subject: [PATCH 7/7] fix: zones editor - legend to be toggable --- index.html | 2 +- modules/ui/zones-editor.js | 2 ++ versioning.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 790a4d54..d14c9381 100644 --- a/index.html +++ b/index.html @@ -8101,7 +8101,7 @@ - + diff --git a/modules/ui/zones-editor.js b/modules/ui/zones-editor.js index 0b5d069a..d575a544 100644 --- a/modules/ui/zones-editor.js +++ b/modules/ui/zones-editor.js @@ -341,6 +341,8 @@ function editZones() { } function toggleLegend() { + if (legend.selectAll("*").size()) return clearLegend(); // hide legend + const filterBy = byId("zonesFilterType").value; const isFiltered = filterBy && filterBy !== "all"; const visibleZones = pack.zones.filter(zone => !zone.hidden && (!isFiltered || zone.type === filterBy)); diff --git a/versioning.js b/versioning.js index 24c2f84a..acb67e2b 100644 --- a/versioning.js +++ b/versioning.js @@ -12,7 +12,7 @@ * * Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2 */ -const VERSION = "1.105.17"; +const VERSION = "1.105.18"; if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function"); {