diff --git a/src/layers/index.js b/src/layers/index.js new file mode 100644 index 00000000..e69de29b diff --git a/src/layers/init.js b/src/layers/init.js new file mode 100644 index 00000000..e69de29b diff --git a/src/layers/renderers/index.js b/src/layers/renderers/index.js new file mode 100644 index 00000000..e69de29b diff --git a/src/layers/utils.js b/src/layers/utils.js new file mode 100644 index 00000000..e69de29b diff --git a/src/main.js b/src/main.js index 1c3d3a78..4dc5cd03 100644 --- a/src/main.js +++ b/src/main.js @@ -1,8 +1,6 @@ // Azgaar (azgaar.fmg@yandex.com). Minsk, 2017-2022. MIT License // https://github.com/Azgaar/Fantasy-Map-Generator -console.log("Hello World"); - import "./components"; import {ERROR, INFO, TIME, WARN} from "./config/logging"; import {UINT16_MAX} from "./constants"; @@ -954,7 +952,7 @@ function drawCoastline() { ); const landMask = defs.select("#land"); const waterMask = defs.select("#water"); - const lineGen = d3.line().curve(d3.curveBasis); + const lineGen = d3.line().curve(d3.curveBasisClosed); for (const i of cells.i) { const startFromEdge = !i && cells.h[i] >= 20; diff --git a/src/modules/io/load.js b/src/modules/io/load.js index ad97bc12..4e9db819 100644 --- a/src/modules/io/load.js +++ b/src/modules/io/load.js @@ -1,11 +1,12 @@ +import {updatePresetInput} from "/src/modules/ui/layers"; import {restoreDefaultEvents} from "/src/scripts/events"; -import {calculateVoronoi, findCell} from "/src/utils/graphUtils"; -import {last} from "/src/utils/arrayUtils"; -import {tip} from "/src/scripts/tooltips"; -import {parseError} from "/src/utils/errorUtils"; -import {rn, minmax} from "/src/utils/numberUtils"; -import {link} from "/src/utils/linkUtils"; import {ldb} from "/src/scripts/indexedDB"; +import {tip} from "/src/scripts/tooltips"; +import {last} from "/src/utils/arrayUtils"; +import {parseError} from "/src/utils/errorUtils"; +import {calculateVoronoi, findCell} from "/src/utils/graphUtils"; +import {link} from "/src/utils/linkUtils"; +import {minmax, rn} from "/src/utils/numberUtils"; function quickLoad() { ldb.get("lastMap", blob => { @@ -424,7 +425,7 @@ async function parseLoadedData(data) { if (notHidden(ruler)) turnOn("toggleRulers"); if (notHidden(scaleBar)) turnOn("toggleScaleBar"); - getCurrentPreset(); + updatePresetInput(); })(); void (function restoreEvents() { diff --git a/src/modules/ui/heightmap-editor.js b/src/modules/ui/heightmap-editor.js index eb184cbf..b9360468 100644 --- a/src/modules/ui/heightmap-editor.js +++ b/src/modules/ui/heightmap-editor.js @@ -1,14 +1,14 @@ +import {turnLayerButtonOff, turnLayerButtonOn, updatePresetInput} from "/src/modules/ui/layers"; import {restoreDefaultEvents} from "/src/scripts/events"; -import {findGridCell, findGridAll, findCell, getPackPolygon, getGridPolygon} from "/src/utils/graphUtils"; -import {last, createTypedArray} from "/src/utils/arrayUtils"; -import {tip, showMainTip, clearMainTip} from "/src/scripts/tooltips"; -import {byId} from "/src/utils/shorthands"; -import {rn, minmax, lim} from "/src/utils/numberUtils"; -import {link} from "/src/utils/linkUtils"; import {prompt} from "/src/scripts/prompt"; -import {throttle} from "/src/utils/functionUtils"; -import {turnLayerButtonOn, turnLayerButtonOff} from "/src/modules/ui/layers"; +import {clearMainTip, showMainTip, tip} from "/src/scripts/tooltips"; +import {createTypedArray, last} from "/src/utils/arrayUtils"; import {getColorScheme, getHeightColor} from "/src/utils/colorUtils"; +import {throttle} from "/src/utils/functionUtils"; +import {findCell, findGridAll, findGridCell, getGridPolygon, getPackPolygon} from "/src/utils/graphUtils"; +import {link} from "/src/utils/linkUtils"; +import {lim, minmax, rn} from "/src/utils/numberUtils"; +import {byId} from "/src/utils/shorthands"; export function editHeightmap(options) { const {mode, tool} = options || {}; @@ -202,7 +202,7 @@ export function editHeightmap(options) { // turn on else if (!editHeightmap.layers.includes(e.id) && layerIsOn(e.id)) e.click(); // turn off }); - getCurrentPreset(); + updatePresetInput(); } function regenerateErasedData() { diff --git a/src/modules/ui/hotkeys.js b/src/modules/ui/hotkeys.js index 288ec5fa..be58ef5a 100644 --- a/src/modules/ui/hotkeys.js +++ b/src/modules/ui/hotkeys.js @@ -1,7 +1,9 @@ -"use strict"; +import {byId} from "/src/utils/shorthands"; +import {toggleLayer} from "/src/modules/ui/layers"; + // Hotkeys, see github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys -document.addEventListener("keydown", handleKeydown); -document.addEventListener("keyup", handleKeyup); +document.on("keydown", handleKeydown); +document.on("keyup", handleKeyup); function handleKeydown(event) { if (!allowHotkeys()) return; // in some cases (e.g. in a textarea) hotkeys are not allowed @@ -29,7 +31,7 @@ function handleKeyup(event) { else if (code === "Tab") toggleOptions(event); else if (code === "Escape") closeAllDialogs(); else if (code === "Delete") removeElementOnKey(); - else if (code === "KeyO" && document.getElementById("canvas3d")) toggle3dOptions(); + else if (code === "KeyO" && byId("canvas3d")) toggle3dOptions(); else if (ctrl && code === "KeyQ") toggleSaveReminder(); else if (ctrl && code === "KeyS") dowloadMap(); else if (ctrl && code === "KeyC") saveToDropbox(); @@ -63,33 +65,33 @@ function handleKeyup(event) { else if (alt && code === "KeyC") console.table(pack.cultures); else if (alt && code === "KeyR") console.table(pack.religions); else if (alt && code === "KeyF") console.table(pack.features); - else if (code === "KeyX") toggleTexture(); - else if (code === "KeyH") toggleHeight(); - else if (code === "KeyB") toggleBiomes(); - else if (code === "KeyE") toggleCells(); - else if (code === "KeyG") toggleGrid(); - else if (code === "KeyO") toggleCoordinates(); - else if (code === "KeyW") toggleCompass(); - else if (code === "KeyV") toggleRivers(); - else if (code === "KeyF") toggleRelief(); - else if (code === "KeyC") toggleCultures(); - else if (code === "KeyS") toggleStates(); - else if (code === "KeyP") toggleProvinces(); - else if (code === "KeyZ") toggleZones(); - else if (code === "KeyD") toggleBorders(); - else if (code === "KeyR") toggleReligions(); - else if (code === "KeyU") toggleRoutes(); - else if (code === "KeyT") toggleTemp(); - else if (code === "KeyN") togglePopulation(); - else if (code === "KeyJ") toggleIce(); - else if (code === "KeyA") togglePrec(); - else if (code === "KeyY") toggleEmblems(); - else if (code === "KeyL") toggleLabels(); - else if (code === "KeyI") toggleIcons(); - else if (code === "KeyM") toggleMilitary(); - else if (code === "KeyK") toggleMarkers(); - else if (code === "Equal") toggleRulers(); - else if (code === "Slash") toggleScaleBar(); + else if (code === "KeyX") toggleLayer("toggleTexture"); + else if (code === "KeyH") toggleLayer("toggleHeight"); + else if (code === "KeyB") toggleLayer("toggleBiomes"); + else if (code === "KeyE") toggleLayer("toggleCells"); + else if (code === "KeyG") toggleLayer("toggleGrid"); + else if (code === "KeyO") toggleLayer("toggleCoordinates"); + else if (code === "KeyW") toggleLayer("toggleCompass"); + else if (code === "KeyV") toggleLayer("toggleRivers"); + else if (code === "KeyF") toggleLayer("toggleRelief"); + else if (code === "KeyC") toggleLayer("toggleCultures"); + else if (code === "KeyS") toggleLayer("toggleStates"); + else if (code === "KeyP") toggleLayer("toggleProvinces"); + else if (code === "KeyZ") toggleLayer("toggleZones"); + else if (code === "KeyD") toggleLayer("toggleBorders"); + else if (code === "KeyR") toggleLayer("toggleReligions"); + else if (code === "KeyU") toggleLayer("toggleRoutes"); + else if (code === "KeyT") toggleLayer("toggleTemp"); + else if (code === "KeyN") toggleLayer("togglePopulation"); + else if (code === "KeyJ") toggleLayer("toggleIce"); + else if (code === "KeyA") toggleLayer("togglePrec"); + else if (code === "KeyY") toggleLayer("toggleEmblems"); + else if (code === "KeyL") toggleLayer("toggleLabels"); + else if (code === "KeyI") toggleLayer("toggleIcons"); + else if (code === "KeyM") toggleLayer("toggleMilitary"); + else if (code === "KeyK") toggleLayer("toggleMarkers"); + else if (code === "Equal") toggleLayer("toggleRulers"); + else if (code === "Slash") toggleLayer("toggleScaleBar"); else if (code === "ArrowLeft") zoom.translateBy(svg, 10, 0); else if (code === "ArrowRight") zoom.translateBy(svg, -10, 0); else if (code === "ArrowUp") zoom.translateBy(svg, 0, 10); @@ -120,22 +122,17 @@ function pressNumpadSign(key) { const change = key === "+" ? 1 : -1; let brush = null; - if (document.getElementById("brushRadius")?.offsetParent) brush = document.getElementById("brushRadius"); - else if (document.getElementById("biomesManuallyBrush")?.offsetParent) - brush = document.getElementById("biomesManuallyBrush"); - else if (document.getElementById("statesManuallyBrush")?.offsetParent) - brush = document.getElementById("statesManuallyBrush"); - else if (document.getElementById("provincesManuallyBrush")?.offsetParent) - brush = document.getElementById("provincesManuallyBrush"); - else if (document.getElementById("culturesManuallyBrush")?.offsetParent) - brush = document.getElementById("culturesManuallyBrush"); - else if (document.getElementById("zonesBrush")?.offsetParent) brush = document.getElementById("zonesBrush"); - else if (document.getElementById("religionsManuallyBrush")?.offsetParent) - brush = document.getElementById("religionsManuallyBrush"); + if (byId("brushRadius")?.offsetParent) brush = byId("brushRadius"); + else if (byId("biomesManuallyBrush")?.offsetParent) brush = byId("biomesManuallyBrush"); + else if (byId("statesManuallyBrush")?.offsetParent) brush = byId("statesManuallyBrush"); + else if (byId("provincesManuallyBrush")?.offsetParent) brush = byId("provincesManuallyBrush"); + else if (byId("culturesManuallyBrush")?.offsetParent) brush = byId("culturesManuallyBrush"); + else if (byId("zonesBrush")?.offsetParent) brush = byId("zonesBrush"); + else if (byId("religionsManuallyBrush")?.offsetParent) brush = byId("religionsManuallyBrush"); if (brush) { const value = minmax(+brush.value + change, +brush.min, +brush.max); - brush.value = document.getElementById(brush.id + "Number").value = value; + brush.value = byId(brush.id + "Number").value = value; return; } diff --git a/src/modules/ui/layers.js b/src/modules/ui/layers.js index 3bb2b5cb..124936bb 100644 --- a/src/modules/ui/layers.js +++ b/src/modules/ui/layers.js @@ -13,35 +13,6 @@ import {convertTemperature} from "/src/utils/unitUtils"; import {getColorScheme, getHeightColor} from "/src/utils/colorUtils"; let presets = {}; -restoreCustomPresets(); // run on-load -addLayerListeners(); - -function restoreCustomPresets() { - const storedPresets = JSON.parse(stored("presets")); - if (!storedPresets) { - presets = defaultPresets.slice(); - return; - } - - for (const preset in storedPresets) { - if (presets[preset]) continue; - byId("layersPreset").add(new Option(preset, preset)); - } - - presets = storedPresets; -} - -function addLayerListeners() { - byId("mapLayers").on("click", toggleLayer); - byId("savePresetButton").on("click", savePreset); - byId("removePresetButton").on("click", removePreset); -} - -function toggleLayer(event) { - const targetId = event.target.id; - if (!targetId || targetId === "mapLayers" || !layerTogglesMap[targetId]) return; - layerTogglesMap[targetId](); -} const defaultPresets = { political: [ @@ -139,6 +110,81 @@ const layerTogglesMap = { toggleZones }; +restoreCustomPresets(); // run on-load +addLayerListeners(); + +function restoreCustomPresets() { + const storedPresets = JSON.parse(stored("presets")); + if (!storedPresets) { + presets = structuredClone(defaultPresets); + return; + } + + for (const preset in storedPresets) { + if (presets[preset]) continue; + byId("layersPreset").add(new Option(preset, preset)); + } + + presets = storedPresets; +} + +function addLayerListeners() { + byId("mapLayers").on("click", toggleLayerOnClick); + byId("savePresetButton").on("click", savePreset); + byId("removePresetButton").on("click", removePreset); + + // allow to move layers by dragging layer button (jquery) + $("#mapLayers").sortable({items: "li:not(.solid)", containment: "parent", cancel: ".solid", update: moveLayer}); +} + +// connection between option layer buttons and actual svg groups to move the element +const layerButtonToElementMap = { + toggleBiomes: "biomes", + toggleBorders: "borders", + toggleCells: "cells", + toggleCompass: "compass", + toggleCoordinates: "coordinates", + toggleCultures: "cults", + toggleEmblems: "emblems", + toggleGrid: "gridOverlay", + toggleHeight: "terrs", + toggleIce: "ice", + toggleIcons: "icons", + toggleLabels: "labels", + toggleMarkers: "markers", + toggleMilitary: "armies", + togglePopulation: "population", + togglePrec: "prec", + toggleProvinces: "provs", + toggleRelief: "terrain", + toggleReligions: "relig", + toggleRivers: "rivers", + toggleRoutes: "routes", + toggleRulers: "ruler", + toggleStates: "regions", + toggleTemp: "temperature", + toggleTexture: "texture", + toggleZones: "zones" +}; + +function moveLayer(event, $layerButton) { + const getLayer = buttonId => $("#" + layerButtonToElementMap[buttonId]); + const layer = getLayer($layerButton.item.attr("id")); + if (!layer) return; + + const prev = getLayer($layerButton.item.prev().attr("id")); + const next = getLayer($layerButton.item.next().attr("id")); + + if (prev) layer.insertAfter(prev); + else if (next) layer.insertBefore(next); +} + +function toggleLayerOnClick(event) { + const targetId = event.target.id; + if (!targetId || targetId === "mapLayers" || !layerTogglesMap[targetId]) return; + layerTogglesMap[targetId](); +} + // run on map generation export function applyPreset() { const preset = stored("preset") || byId("layersPreset")?.value || "political"; @@ -216,22 +262,26 @@ export function restoreLayers() { if (!layerIsOn("toggleRivers")) rivers.selectAll("*").remove(); } +export function toggleLayer(layerId) { + layerTogglesMap[layerId](); +} + export function layerIsOn(el) { const buttonoff = byId(el).classList.contains("buttonoff"); return !buttonoff; } -function turnLayerButtonOn(el) { +export function turnLayerButtonOn(el) { byId(el).classList.remove("buttonoff"); updatePresetInput(); } -function turnLayerButtonOff(el) { +export function turnLayerButtonOff(el) { byId(el).classList.add("buttonoff"); updatePresetInput(); } -function updatePresetInput() { +export function updatePresetInput() { const $toggledOnLayers = byId("mapLayers").querySelectorAll("li:not(.buttonoff)"); const currentLayers = Array.from($toggledOnLayers) .map(node => node.id) @@ -251,6 +301,10 @@ function updatePresetInput() { byId("savePresetButton").style.display = "inline-block"; } +// *** +// Specific layer toggles and renderers +// *** + function toggleHeight(event) { if (customization === 1) { tip("You cannot turn off the layer when heightmap is in edit mode", false, "error"); @@ -271,7 +325,7 @@ function toggleHeight(event) { } } -function drawHeightmap() { +export function drawHeightmap() { TIME && console.time("drawHeightmap"); terrs.selectAll("*").remove(); @@ -285,20 +339,9 @@ function drawHeightmap() { const skip = +terrs.attr("skip") + 1; const simplification = +terrs.attr("relax"); - const lineGen = d3.line().curve(d3.curveBasis); - switch (+terrs.attr("curve")) { - case 0: - lineGen.curve(d3.curveBasisClosed); - break; - case 1: - lineGen.curve(d3.curveLinear); - break; - case 2: - lineGen.curve(d3.curveStep); - break; - default: - lineGen.curve(d3.curveBasisClosed); - } + const curveMap = {0: d3.curveBasisClosed, 1: d3.curveLinear, 2: d3.curveStep}; + const curve = curveMap[+terrs.attr("curve") || 0]; + const lineGen = d3.line().curve(curve); let currentLayer = 20; const heights = cells.i.sort((a, b) => cells.h[a] - cells.h[b]); @@ -328,6 +371,7 @@ function drawHeightmap() { for (const i of d3.range(20, 101)) { if (paths[i].length < 10) continue; const color = getHeightColor(i, scheme); + if (terracing) terrs .append("path") @@ -385,7 +429,7 @@ function toggleTemp(event) { } } -function drawTemp() { +export function drawTemp() { TIME && console.time("drawTemp"); temperature.selectAll("*").remove(); @@ -517,7 +561,7 @@ function toggleBiomes(event) { } } -function drawBiomes() { +export function drawBiomes() { biomes.selectAll("path").remove(); const cells = pack.cells, vertices = pack.vertices, @@ -593,7 +637,7 @@ function togglePrec(event) { } } -function drawPrec() { +export function drawPrec() { prec.selectAll("circle").remove(); const {cells, points} = grid; @@ -652,7 +696,7 @@ function togglePopulation(event) { } } -function drawPopulation(event) { +export function drawPopulation(event) { population.selectAll("line").remove(); const cells = pack.cells, p = cells.p, @@ -707,7 +751,7 @@ function toggleCells(event) { } } -function drawCells() { +export function drawCells() { cells.selectAll("path").remove(); const data = customization === 1 ? grid.cells.i : pack.cells.i; const polygon = customization === 1 ? getGridPolygon : getPackPolygon; @@ -732,7 +776,7 @@ function toggleIce(event) { } } -function drawIce() { +export function drawIce() { const cells = grid.cells, vertices = grid.vertices, n = cells.i.length, @@ -822,7 +866,7 @@ function toggleCultures(event) { } } -function drawCultures() { +export function drawCultures() { TIME && console.time("drawCultures"); cults.selectAll("path").remove(); @@ -896,7 +940,7 @@ function toggleReligions(event) { } } -function drawReligions() { +export function drawReligions() { TIME && console.time("drawReligions"); relig.selectAll("path").remove(); @@ -1171,7 +1215,6 @@ function toggleBorders(event) { } } -// draw state and province borders export function drawBorders() { TIME && console.time("drawBorders"); borders.selectAll("path").remove(); @@ -1434,7 +1477,7 @@ function toggleGrid(event) { } } -function drawGrid() { +export function drawGrid() { gridOverlay.selectAll("*").remove(); const pattern = "#pattern_" + (gridOverlay.attr("type") || "pointyHex"); const stroke = gridOverlay.attr("stroke") || "#808080"; @@ -1692,36 +1735,35 @@ function drawMarkers() { markers.html(html.join("")); } -const getPin = (shape = "bubble", fill = "#fff", stroke = "#000") => { - if (shape === "bubble") - return ``; - if (shape === "pin") - return ``; - if (shape === "square") - return ``; - if (shape === "squarish") - return ``; - if (shape === "diamond") return ``; - if (shape === "hex") return ``; - if (shape === "hexy") return ``; - if (shape === "shieldy") - return ``; - if (shape === "shield") - return ``; - if (shape === "pentagon") return ``; - if (shape === "heptagon") - return ``; - if (shape === "circle") return ``; - if (shape === "no") return ""; +const pinShapeMap = { + bubble: (stroke, fill) => + ``, + pin: (stroke, fill) => + ``, + square: (stroke, fill) => + ``, + squarish: (stroke, fill) => ``, + diamond: (stroke, fill) => ``, + hex: (stroke, fill) => ``, + hexy: (stroke, fill) => ``, + shieldy: (stroke, fill) => + ``, + shield: (stroke, fill) => + ``, + pentagon: (stroke, fill) => ``, + heptagon: (stroke, fill) => + ``, + circle: (stroke, fill) => ``, + no: (stroke, fill) => "" }; function drawMarker(marker, rescale = 1) { - const {i, icon, x, y, dx = 50, dy = 50, px = 12, size = 30, pin, fill, stroke} = marker; + const {i, icon, x, y, dx = 50, dy = 50, px = 12, size = 30, pin = "bubble", fill = "#fff", stroke = "#000"} = marker; const id = `marker${i}`; const zoomSize = rescale ? Math.max(rn(size / 5 + 24 / scale, 2), 1) : size; const viewX = rn(x - zoomSize / 2, 1); const viewY = rn(y - zoomSize, 1); - const pinHTML = getPin(pin, fill, stroke); + const pinHTML = pinShapeMap[pin](fill, stroke); return `${pinHTML}${icon}`; } @@ -1820,7 +1862,7 @@ function toggleEmblems(event) { } } -function drawEmblems() { +export function drawEmblems() { TIME && console.time("drawEmblems"); const {states, provinces, burgs} = pack; @@ -1929,42 +1971,3 @@ function drawEmblems() { TIME && console.timeEnd("drawEmblems"); } - -// move layers on mapLayers dragging (jquery sortable) -$("#mapLayers").sortable({items: "li:not(.solid)", containment: "parent", cancel: ".solid", update: moveLayer}); -function moveLayer(event, ui) { - const el = getLayer(ui.item.attr("id")); - if (!el) return; - const prev = getLayer(ui.item.prev().attr("id")); - const next = getLayer(ui.item.next().attr("id")); - if (prev) el.insertAfter(prev); - else if (next) el.insertBefore(next); -} - -// define connection between option layer buttons and actual svg groups to move the element -function getLayer(id) { - if (id === "toggleHeight") return $("#terrs"); - if (id === "toggleBiomes") return $("#biomes"); - if (id === "toggleCells") return $("#cells"); - if (id === "toggleGrid") return $("#gridOverlay"); - if (id === "toggleCoordinates") return $("#coordinates"); - if (id === "toggleCompass") return $("#compass"); - if (id === "toggleRivers") return $("#rivers"); - if (id === "toggleRelief") return $("#terrain"); - if (id === "toggleReligions") return $("#relig"); - if (id === "toggleCultures") return $("#cults"); - if (id === "toggleStates") return $("#regions"); - if (id === "toggleProvinces") return $("#provs"); - if (id === "toggleBorders") return $("#borders"); - if (id === "toggleRoutes") return $("#routes"); - if (id === "toggleTemp") return $("#temperature"); - if (id === "togglePrec") return $("#prec"); - if (id === "togglePopulation") return $("#population"); - if (id === "toggleIce") return $("#ice"); - if (id === "toggleTexture") return $("#texture"); - if (id === "toggleEmblems") return $("#emblems"); - if (id === "toggleLabels") return $("#labels"); - if (id === "toggleIcons") return $("#icons"); - if (id === "toggleMarkers") return $("#markers"); - if (id === "toggleRulers") return $("#ruler"); -}