From 865a98199f232c4a8bcc1c79137dd805175f283f Mon Sep 17 00:00:00 2001 From: Azgaar Date: Tue, 28 Jun 2022 00:04:09 +0300 Subject: [PATCH] refactor(es modules): migrate a few more functions --- index.html | 10 +- src/main.js | 6 +- src/modules/dynamic/auto-update.js | 9 +- src/modules/dynamic/heightmap-selection.js | 1 + src/modules/io/export.js | 3 +- src/modules/io/load.js | 2 +- src/modules/submap.js | 2 +- src/modules/ui/elevation-profile.js | 7 +- src/modules/ui/heightmap-editor.js | 12 +- src/modules/ui/layers.js | 532 ++++++++++----------- src/modules/ui/provinces-editor.js | 3 +- src/modules/ui/style.js | 6 +- src/modules/ui/stylePresets.js | 2 +- src/modules/ui/submap.js | 3 +- src/modules/ui/tools.js | 3 +- src/modules/zoom.js | 15 +- src/types/common.d.ts | 3 + src/utils/colorUtils.ts | 20 +- 18 files changed, 330 insertions(+), 309 deletions(-) diff --git a/index.html b/index.html index 03e702de..0c7703f6 100644 --- a/index.html +++ b/index.html @@ -437,14 +437,12 @@ data-tip="Click to save displayed layers as a new preset" class="icon-plus sideButton" style="display: none" - onclick="savePreset()" >

@@ -1348,7 +1346,7 @@ - + @@ -1386,14 +1384,14 @@ - + - + @@ -5576,7 +5574,7 @@ id="showLabels" class="checkbox" type="checkbox" - onchange="hideLabels.checked = !this.checked; invokeActiveZooming()" + onchange="hideLabels.checked = !this.checked; Zoom.invoke()" checked /> diff --git a/src/main.js b/src/main.js index 825d00f6..1c3d3a78 100644 --- a/src/main.js +++ b/src/main.js @@ -6,7 +6,6 @@ console.log("Hello World"); import "./components"; import {ERROR, INFO, TIME, WARN} from "./config/logging"; import {UINT16_MAX} from "./constants"; -import {invokeActiveZooming} from "./modules/activeZooming"; import {clearLegend} from "./modules/legend"; import {drawScaleBar, Ruler, Rulers} from "./modules/measurers"; import {applyPreset, drawBorders, drawRivers, drawStates} from "./modules/ui/layers"; @@ -33,6 +32,7 @@ import {minmax, normalize, rn} from "./utils/numberUtils"; import {gauss, generateSeed, P, ra, rand, rw} from "./utils/probabilityUtils"; import {byId} from "./utils/shorthands"; import {round} from "./utils/stringUtils"; +import {restoreLayers} from "./modules/ui/layers"; addGlobalListeners(); @@ -286,7 +286,7 @@ function findBurgForMFCG(params) { } Zoom.to(b.x, b.y, 8, 1600); - invokeActiveZooming(); + Zoom.invoke(); tip("Here stands the glorious city of " + b.name, true, "success", 15000); } @@ -357,7 +357,7 @@ async function generate(options) { const timeStart = performance.now(); const {seed: precreatedSeed, graph: precreatedGraph} = options || {}; - invokeActiveZooming(); + Zoom.invoke(); setSeed(precreatedSeed); INFO && console.group("Generated Map " + seed); diff --git a/src/modules/dynamic/auto-update.js b/src/modules/dynamic/auto-update.js index 52627212..0249d940 100644 --- a/src/modules/dynamic/auto-update.js +++ b/src/modules/dynamic/auto-update.js @@ -2,6 +2,7 @@ import {findCell} from "/src/utils/graphUtils"; import {rn} from "/src/utils/numberUtils"; import {rand, P, rw} from "/src/utils/probabilityUtils"; import {parseTransform} from "/src/utils/stringUtils"; +import {turnLayerButtonOn, turnLayerButtonOff} from "/src/modules/ui/layers"; // update old .map version to the current one export function resolveVersionConflicts(version) { @@ -69,7 +70,7 @@ export function resolveVersionConflicts(version) { addZones(); if (!markers.selectAll("*").size()) { Markers.generate(); - turnButtonOn("toggleMarkers"); + turnLayerButtonOn("toggleMarkers"); } // v1.0 add fogging layer (state focus) @@ -282,7 +283,7 @@ export function resolveVersionConflicts(version) { .attr("box-size", 3) .attr("stroke", "#000") .attr("stroke-width", 0.3); - turnButtonOn("toggleMilitary"); + turnLayerButtonOn("toggleMilitary"); Military.generate(); } @@ -443,9 +444,9 @@ export function resolveVersionConflicts(version) { ruler.selectAll("*").remove(); if (rulers.data.length) { - turnButtonOn("toggleRulers"); + turnLayerButtonOn("toggleRulers"); rulers.draw(); - } else turnButtonOff("toggleRulers"); + } else turnLayerButtonOff("toggleRulers"); // 1.61 changed oceanicPattern from rect to image const pattern = document.getElementById("oceanic"); diff --git a/src/modules/dynamic/heightmap-selection.js b/src/modules/dynamic/heightmap-selection.js index 3e747751..41f38f9b 100644 --- a/src/modules/dynamic/heightmap-selection.js +++ b/src/modules/dynamic/heightmap-selection.js @@ -1,6 +1,7 @@ import {shouldRegenerateGrid, generateGrid} from "/src/utils/graphUtils"; import {byId} from "/src/utils/shorthands"; import {generateSeed} from "/src/utils/probabilityUtils"; +import {getColorScheme} from "/src/utils/colorUtils"; const initialSeed = generateSeed(); let graph = getGraph(grid); diff --git a/src/modules/io/export.js b/src/modules/io/export.js index 028ddaec..bd81913c 100644 --- a/src/modules/io/export.js +++ b/src/modules/io/export.js @@ -4,6 +4,7 @@ import {tip} from "/src/scripts/tooltips"; import {getCoordinates} from "/src/utils/coordinateUtils"; import {rn} from "/src/utils/numberUtils"; import {getBase64} from "/src/utils/functionUtils"; +import {getColorScheme, getHeightColor} from "/src/utils/colorUtils"; // download map as SVG async function saveSVG() { @@ -368,7 +369,7 @@ function updateMeshCells(clone) { .join("polygon") .attr("points", d => getGridPolygon(d)) .attr("id", d => "cell" + d) - .attr("stroke", d => getColor(grid.cells.h[d], scheme)); + .attr("stroke", d => getHeightColor(grid.cells.h[d], scheme)); } // for each g element get inline style diff --git a/src/modules/io/load.js b/src/modules/io/load.js index d430886b..ad97bc12 100644 --- a/src/modules/io/load.js +++ b/src/modules/io/load.js @@ -579,7 +579,7 @@ async function parseLoadedData(data) { restoreDefaultEvents(); focusOn(); // based on searchParams focus on point, cell or burg - invokeActiveZooming(); + Zoom.invoke(); WARN && console.warn(`TOTAL: ${rn((performance.now() - uploadMap.timeStart) / 1000, 2)}s`); showStatistics(); diff --git a/src/modules/submap.js b/src/modules/submap.js index 684d25ee..dec90236 100644 --- a/src/modules/submap.js +++ b/src/modules/submap.js @@ -28,7 +28,7 @@ window.Submap = (function () { const inverse = options.inverse; const stage = s => INFO && console.log("SUBMAP:", s); const timeStart = performance.now(); - invokeActiveZooming(); + Zoom.invoke(); // copy seed seed = parentMap.seed; diff --git a/src/modules/ui/elevation-profile.js b/src/modules/ui/elevation-profile.js index 597e8d48..9b808544 100644 --- a/src/modules/ui/elevation-profile.js +++ b/src/modules/ui/elevation-profile.js @@ -1,5 +1,6 @@ import {findCell} from "/src/utils/graphUtils"; import {rn} from "/src/utils/numberUtils"; +import {getColorScheme, getHeightColor} from "/src/utils/colorUtils"; export function showEPForRoute(node) { const points = []; @@ -208,18 +209,18 @@ function showElevationProfile(data, routeLen, isRiver) { landdef .append("stop") .attr("offset", "0%") - .attr("style", "stop-color:" + getColor(chartData.mih, colors) + ";stop-opacity:1"); + .attr("style", "stop-color:" + getHeightColor(chartData.mih, colors) + ";stop-opacity:1"); landdef .append("stop") .attr("offset", "100%") - .attr("style", "stop-color:" + getColor(chartData.mah, colors) + ";stop-opacity:1"); + .attr("style", "stop-color:" + getHeightColor(chartData.mah, colors) + ";stop-opacity:1"); } else { for (let k = chartData.mah; k >= chartData.mih; k--) { let perc = 1 - (k - chartData.mih) / (chartData.mah - chartData.mih); landdef .append("stop") .attr("offset", perc * 100 + "%") - .attr("style", "stop-color:" + getColor(k, colors) + ";stop-opacity:1"); + .attr("style", "stop-color:" + getHeightColor(k, colors) + ";stop-opacity:1"); } } diff --git a/src/modules/ui/heightmap-editor.js b/src/modules/ui/heightmap-editor.js index f29bc0ac..eb184cbf 100644 --- a/src/modules/ui/heightmap-editor.js +++ b/src/modules/ui/heightmap-editor.js @@ -7,6 +7,8 @@ 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 {getColorScheme, getHeightColor} from "/src/utils/colorUtils"; export function editHeightmap(options) { const {mode, tool} = options || {}; @@ -115,7 +117,7 @@ export function editHeightmap(options) { .style("transform", "scale(1)"); } else exitCustomization.style.display = "block"; - turnButtonOn("toggleHeight"); + turnLayerButtonOn("toggleHeight"); layersPreset.value = "heightmap"; layersPreset.disabled = true; mockHeightmap(); @@ -191,7 +193,7 @@ export function editHeightmap(options) { // restore initial layers //viewbox.select("#heights").remove(); byId("heights").remove(); - turnButtonOff("toggleHeight"); + turnLayerButtonOff("toggleHeight"); document .getElementById("mapLayers") .querySelectorAll("li") @@ -492,7 +494,7 @@ export function editHeightmap(options) { .join("polygon") .attr("points", d => getGridPolygon(d)) .attr("id", d => "cell" + d) - .attr("fill", d => getColor(grid.cells.h[d], scheme)); + .attr("fill", d => getHeightColor(grid.cells.h[d], scheme)); } // draw or update heightmap for a selection of cells @@ -512,7 +514,7 @@ export function editHeightmap(options) { .append("polygon") .attr("points", getGridPolygon(i)) .attr("id", "cell" + i); - cell.attr("fill", getColor(grid.cells.h[i], scheme)); + cell.attr("fill", getHeightColor(grid.cells.h[i], scheme)); }); } @@ -1279,7 +1281,7 @@ export function editHeightmap(options) { return lum | 0; // land }; - const scheme = d3.range(101).map(i => getColor(i, color())); + const scheme = d3.range(101).map(i => getHeightColor(i, color())); const hues = scheme.map(rgb => d3.hsl(rgb).h | 0); const getHeightByScheme = function (color) { let height = scheme.indexOf(color); diff --git a/src/modules/ui/layers.js b/src/modules/ui/layers.js index 51f880d0..3bb2b5cb 100644 --- a/src/modules/ui/layers.js +++ b/src/modules/ui/layers.js @@ -1,202 +1,115 @@ import {TIME} from "/src/config/logging"; -import {invokeActiveZooming} from "../activeZooming"; -import {getGridPolygon} from "/src/utils/graphUtils"; -import {last} from "/src/utils/arrayUtils"; -import {stored, store} from "/src/utils/shorthands"; -import {tip} from "/src/scripts/tooltips"; -import {byId} from "/src/utils/shorthands"; -import {clipPoly} from "/src/utils/lineUtils"; -import {rn, minmax, normalize} from "/src/utils/numberUtils"; -import {isCtrlClick} from "/src/utils/keyboardUtils"; import {prompt} from "/src/scripts/prompt"; -import {rand, P} from "/src/utils/probabilityUtils"; -import {convertTemperature} from "/src/utils/unitUtils"; +import {tip} from "/src/scripts/tooltips"; +import {last} from "/src/utils/arrayUtils"; import {getBase64} from "/src/utils/functionUtils"; +import {getGridPolygon} from "/src/utils/graphUtils"; +import {isCtrlClick} from "/src/utils/keyboardUtils"; +import {clipPoly} from "/src/utils/lineUtils"; +import {minmax, normalize, rn} from "/src/utils/numberUtils"; +import {P, rand} from "/src/utils/probabilityUtils"; +import {byId, store, stored} from "/src/utils/shorthands"; +import {convertTemperature} from "/src/utils/unitUtils"; +import {getColorScheme, getHeightColor} from "/src/utils/colorUtils"; let presets = {}; restoreCustomPresets(); // run on-load -addToggleLayersListener(); - -function getDefaultPresets() { - return { - political: [ - "toggleBorders", - "toggleIcons", - "toggleIce", - "toggleLabels", - "toggleRivers", - "toggleRoutes", - "toggleScaleBar", - "toggleStates" - ], - cultural: [ - "toggleBorders", - "toggleCultures", - "toggleIcons", - "toggleLabels", - "toggleRivers", - "toggleRoutes", - "toggleScaleBar" - ], - religions: [ - "toggleBorders", - "toggleIcons", - "toggleLabels", - "toggleReligions", - "toggleRivers", - "toggleRoutes", - "toggleScaleBar" - ], - provinces: ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"], - biomes: ["toggleBiomes", "toggleIce", "toggleRivers", "toggleScaleBar"], - heightmap: ["toggleHeight", "toggleRivers"], - physical: ["toggleCoordinates", "toggleHeight", "toggleIce", "toggleRivers", "toggleScaleBar"], - poi: [ - "toggleBorders", - "toggleHeight", - "toggleIce", - "toggleIcons", - "toggleMarkers", - "toggleRivers", - "toggleRoutes", - "toggleScaleBar" - ], - military: [ - "toggleBorders", - "toggleIcons", - "toggleLabels", - "toggleMilitary", - "toggleRivers", - "toggleRoutes", - "toggleScaleBar", - "toggleStates" - ], - emblems: [ - "toggleBorders", - "toggleIcons", - "toggleIce", - "toggleEmblems", - "toggleRivers", - "toggleRoutes", - "toggleScaleBar", - "toggleStates" - ], - landmass: ["toggleScaleBar"] - }; -} +addLayerListeners(); function restoreCustomPresets() { - presets = getDefaultPresets(); - const storedPresets = JSON.parse(localStorage.getItem("presets")); - if (!storedPresets) return; + const storedPresets = JSON.parse(stored("presets")); + if (!storedPresets) { + presets = defaultPresets.slice(); + return; + } for (const preset in storedPresets) { if (presets[preset]) continue; - layersPreset.add(new Option(preset, preset)); + byId("layersPreset").add(new Option(preset, preset)); } presets = storedPresets; } -// run on map generation -export function applyPreset() { - const preset = stored("preset") || byId("layersPreset")?.value || "political"; - changePreset(preset); +function addLayerListeners() { + byId("mapLayers").on("click", toggleLayer); + byId("savePresetButton").on("click", savePreset); + byId("removePresetButton").on("click", removePreset); } -// toggle layers on preset change -function changePreset(preset) { - const layers = presets[preset]; // layers to be turned on - const $layerButtons = byId("mapLayers").querySelectorAll("li"); - - $layerButtons.forEach(function ($layerButton) { - const {id} = $layerButton; - if (layers.includes(id) && !layerIsOn(id)) $layerButton.click(); - else if (!layers.includes(id) && layerIsOn(id)) $layerButton.click(); - }); - - byId("layersPreset").value = preset; - store("preset", preset); - - const isDefault = getDefaultPresets()[preset]; - byId("removePresetButton").style.display = isDefault ? "none" : "inline-block"; - byId("savePresetButton").style.display = "none"; - if (byId("canvas3d")) setTimeout(ThreeD.update(), 400); +function toggleLayer(event) { + const targetId = event.target.id; + if (!targetId || targetId === "mapLayers" || !layerTogglesMap[targetId]) return; + layerTogglesMap[targetId](); } -function savePreset() { - prompt("Please provide a preset name", {default: ""}, preset => { - presets[preset] = Array.from(byId("mapLayers").querySelectorAll("li:not(.buttonoff)")) - .map(node => node.id) - .sort(); - layersPreset.add(new Option(preset, preset, false, true)); - localStorage.setItem("presets", JSON.stringify(presets)); - localStorage.setItem("preset", preset); - removePresetButton.style.display = "inline-block"; - savePresetButton.style.display = "none"; - }); -} +const defaultPresets = { + political: [ + "toggleBorders", + "toggleIcons", + "toggleIce", + "toggleLabels", + "toggleRivers", + "toggleRoutes", + "toggleScaleBar", + "toggleStates" + ], + cultural: [ + "toggleBorders", + "toggleCultures", + "toggleIcons", + "toggleLabels", + "toggleRivers", + "toggleRoutes", + "toggleScaleBar" + ], + religions: [ + "toggleBorders", + "toggleIcons", + "toggleLabels", + "toggleReligions", + "toggleRivers", + "toggleRoutes", + "toggleScaleBar" + ], + provinces: ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"], + biomes: ["toggleBiomes", "toggleIce", "toggleRivers", "toggleScaleBar"], + heightmap: ["toggleHeight", "toggleRivers"], + physical: ["toggleCoordinates", "toggleHeight", "toggleIce", "toggleRivers", "toggleScaleBar"], + poi: [ + "toggleBorders", + "toggleHeight", + "toggleIce", + "toggleIcons", + "toggleMarkers", + "toggleRivers", + "toggleRoutes", + "toggleScaleBar" + ], + military: [ + "toggleBorders", + "toggleIcons", + "toggleLabels", + "toggleMilitary", + "toggleRivers", + "toggleRoutes", + "toggleScaleBar", + "toggleStates" + ], + emblems: [ + "toggleBorders", + "toggleIcons", + "toggleIce", + "toggleEmblems", + "toggleRivers", + "toggleRoutes", + "toggleScaleBar", + "toggleStates" + ], + landmass: ["toggleScaleBar"] +}; -function removePreset() { - const preset = layersPreset.value; - delete presets[preset]; - const index = Array.from(layersPreset.options).findIndex(o => o.value === preset); - layersPreset.options.remove(index); - layersPreset.value = "custom"; - removePresetButton.style.display = "none"; - savePresetButton.style.display = "inline-block"; - - localStorage.setItem("presets", JSON.stringify(presets)); - localStorage.removeItem("preset"); -} - -function getCurrentPreset() { - const $toggledOnLayers = byId("mapLayers").querySelectorAll("li:not(.buttonoff)"); - const currentLayers = Array.from($toggledOnLayers) - .map(node => node.id) - .sort(); - - const defaultPresets = getDefaultPresets(); - for (const preset in presets) { - if (JSON.stringify(presets[preset].sort()) !== JSON.stringify(currentLayers)) continue; - - byId("layersPreset").value = preset; - byId("removePresetButton").style.display = defaultPresets[preset] ? "none" : "inline-block"; - byId("savePresetButton").style.display = "none"; - return; - } - - byId("layersPreset").value = "custom"; - byId("removePresetButton").style.display = "none"; - byId("savePresetButton").style.display = "inline-block"; -} - -// run on map regeneration -export function restoreLayers() { - if (layerIsOn("toggleHeight")) drawHeightmap(); - if (layerIsOn("toggleCells")) drawCells(); - if (layerIsOn("toggleGrid")) drawGrid(); - if (layerIsOn("toggleCoordinates")) drawCoordinates(); - if (layerIsOn("toggleCompass")) compass.style("display", "block"); - if (layerIsOn("toggleTemp")) drawTemp(); - if (layerIsOn("togglePrec")) drawPrec(); - if (layerIsOn("togglePopulation")) drawPopulation(); - if (layerIsOn("toggleBiomes")) drawBiomes(); - if (layerIsOn("toggleRelief")) ReliefIcons(); - if (layerIsOn("toggleCultures")) drawCultures(); - if (layerIsOn("toggleProvinces")) drawProvinces(); - if (layerIsOn("toggleReligions")) drawReligions(); - if (layerIsOn("toggleIce")) drawIce(); - if (layerIsOn("toggleEmblems")) drawEmblems(); - if (layerIsOn("toggleMarkers")) drawMarkers(); - - // some layers are rendered each time, remove them if they are not on - if (!layerIsOn("toggleBorders")) borders.selectAll("path").remove(); - if (!layerIsOn("toggleStates")) regions.selectAll("path").remove(); - if (!layerIsOn("toggleRivers")) rivers.selectAll("*").remove(); -} - -export const layerTogglesMap = { +const layerTogglesMap = { toggleBiomes, toggleBorders, toggleCells, @@ -226,14 +139,116 @@ export const layerTogglesMap = { toggleZones }; -function toggleLayer(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"; + changePreset(preset); } -export function addToggleLayersListener() { - byId("mapLayers").on("click", toggleLayer); +// toggle layers on preset change +function changePreset(preset) { + const layers = presets[preset]; // layers to be turned on + const $layerButtons = byId("mapLayers").querySelectorAll("li"); + + $layerButtons.forEach(function ($layerButton) { + const {id} = $layerButton; + if (layers.includes(id) && !layerIsOn(id)) $layerButton.click(); + else if (!layers.includes(id) && layerIsOn(id)) $layerButton.click(); + }); + + byId("layersPreset").value = preset; + store("preset", preset); + + const isDefault = defaultPresets[preset]; + byId("removePresetButton").style.display = isDefault ? "none" : "inline-block"; + byId("savePresetButton").style.display = "none"; + if (byId("canvas3d")) setTimeout(ThreeD.update(), 400); +} + +function savePreset() { + prompt("Please provide a preset name", {default: ""}, preset => { + presets[preset] = Array.from(byId("mapLayers").querySelectorAll("li:not(.buttonoff)")) + .map(node => node.id) + .sort(); + layersPreset.add(new Option(preset, preset, false, true)); + localStorage.setItem("presets", JSON.stringify(presets)); + localStorage.setItem("preset", preset); + removePresetButton.style.display = "inline-block"; + savePresetButton.style.display = "none"; + }); +} + +function removePreset() { + const preset = layersPreset.value; + delete presets[preset]; + const index = Array.from(layersPreset.options).findIndex(o => o.value === preset); + layersPreset.options.remove(index); + layersPreset.value = "custom"; + removePresetButton.style.display = "none"; + savePresetButton.style.display = "inline-block"; + + store("presets", JSON.stringify(presets)); + localStorage.removeItem("preset"); +} + +// run on map regeneration +export function restoreLayers() { + if (layerIsOn("toggleHeight")) drawHeightmap(); + if (layerIsOn("toggleCells")) drawCells(); + if (layerIsOn("toggleGrid")) drawGrid(); + if (layerIsOn("toggleCoordinates")) drawCoordinates(); + if (layerIsOn("toggleCompass")) compass.style("display", "block"); + if (layerIsOn("toggleTemp")) drawTemp(); + if (layerIsOn("togglePrec")) drawPrec(); + if (layerIsOn("togglePopulation")) drawPopulation(); + if (layerIsOn("toggleBiomes")) drawBiomes(); + if (layerIsOn("toggleRelief")) ReliefIcons(); + if (layerIsOn("toggleCultures")) drawCultures(); + if (layerIsOn("toggleProvinces")) drawProvinces(); + if (layerIsOn("toggleReligions")) drawReligions(); + if (layerIsOn("toggleIce")) drawIce(); + if (layerIsOn("toggleEmblems")) drawEmblems(); + if (layerIsOn("toggleMarkers")) drawMarkers(); + + // some layers are rendered each time, remove them if they are not on + if (!layerIsOn("toggleBorders")) borders.selectAll("path").remove(); + if (!layerIsOn("toggleStates")) regions.selectAll("path").remove(); + if (!layerIsOn("toggleRivers")) rivers.selectAll("*").remove(); +} + +export function layerIsOn(el) { + const buttonoff = byId(el).classList.contains("buttonoff"); + return !buttonoff; +} + +function turnLayerButtonOn(el) { + byId(el).classList.remove("buttonoff"); + updatePresetInput(); +} + +function turnLayerButtonOff(el) { + byId(el).classList.add("buttonoff"); + updatePresetInput(); +} + +function updatePresetInput() { + const $toggledOnLayers = byId("mapLayers").querySelectorAll("li:not(.buttonoff)"); + const currentLayers = Array.from($toggledOnLayers) + .map(node => node.id) + .sort(); + + for (const preset in presets) { + if (JSON.stringify(presets[preset].sort()) !== JSON.stringify(currentLayers)) continue; + + byId("layersPreset").value = preset; + byId("removePresetButton").style.display = defaultPresets[preset] ? "none" : "inline-block"; + byId("savePresetButton").style.display = "none"; + return; + } + + byId("layersPreset").value = "custom"; + byId("removePresetButton").style.display = "none"; + byId("savePresetButton").style.display = "inline-block"; } function toggleHeight(event) { @@ -243,7 +258,7 @@ function toggleHeight(event) { } if (!terrs.selectAll("*").size()) { - turnButtonOn("toggleHeight"); + turnLayerButtonOn("toggleHeight"); drawHeightmap(); if (event && isCtrlClick(event)) editStyle("terrs"); } else { @@ -251,7 +266,7 @@ function toggleHeight(event) { editStyle("terrs"); return; } - turnButtonOff("toggleHeight"); + turnLayerButtonOff("toggleHeight"); terrs.selectAll("*").remove(); } } @@ -309,9 +324,10 @@ function drawHeightmap() { .attr("width", graphWidth) .attr("height", graphHeight) .attr("fill", scheme(0.8)); // draw base layer + for (const i of d3.range(20, 101)) { if (paths[i].length < 10) continue; - const color = getColor(i, scheme); + const color = getHeightColor(i, scheme); if (terracing) terrs .append("path") @@ -354,21 +370,9 @@ function drawHeightmap() { TIME && console.timeEnd("drawHeightmap"); } -function getColorScheme(scheme) { - if (scheme === "bright") return d3.scaleSequential(d3.interpolateSpectral); - if (scheme === "light") return d3.scaleSequential(d3.interpolateRdYlGn); - if (scheme === "green") return d3.scaleSequential(d3.interpolateGreens); - if (scheme === "monochrome") return d3.scaleSequential(d3.interpolateGreys); - return d3.scaleSequential(d3.interpolateSpectral); -} - -function getColor(value, scheme = getColorScheme()) { - return scheme(1 - (value < 20 ? value - 5 : value) / 100); -} - function toggleTemp(event) { if (!temperature.selectAll("*").size()) { - turnButtonOn("toggleTemp"); + turnLayerButtonOn("toggleTemp"); drawTemp(); if (event && isCtrlClick(event)) editStyle("temperature"); } else { @@ -376,7 +380,7 @@ function toggleTemp(event) { editStyle("temperature"); return; } - turnButtonOff("toggleTemp"); + turnLayerButtonOff("toggleTemp"); temperature.selectAll("*").remove(); } } @@ -500,7 +504,7 @@ function drawTemp() { function toggleBiomes(event) { if (!biomes.selectAll("path").size()) { - turnButtonOn("toggleBiomes"); + turnLayerButtonOn("toggleBiomes"); drawBiomes(); if (event && isCtrlClick(event)) editStyle("biomes"); } else { @@ -509,7 +513,7 @@ function toggleBiomes(event) { return; } biomes.selectAll("path").remove(); - turnButtonOff("toggleBiomes"); + turnLayerButtonOff("toggleBiomes"); } } @@ -573,7 +577,7 @@ function drawBiomes() { function togglePrec(event) { if (!prec.selectAll("circle").size()) { - turnButtonOn("togglePrec"); + turnLayerButtonOn("togglePrec"); drawPrec(); if (event && isCtrlClick(event)) editStyle("prec"); } else { @@ -581,7 +585,7 @@ function togglePrec(event) { editStyle("prec"); return; } - turnButtonOff("togglePrec"); + turnLayerButtonOff("togglePrec"); const hide = d3.transition().duration(1000).ease(d3.easeSinIn); prec.selectAll("text").attr("opacity", 1).transition(hide).attr("opacity", 0); prec.selectAll("circle").transition(hide).attr("r", 0).remove(); @@ -615,7 +619,7 @@ function drawPrec() { function togglePopulation(event) { if (!population.selectAll("line").size()) { - turnButtonOn("togglePopulation"); + turnLayerButtonOn("togglePopulation"); drawPopulation(); if (event && isCtrlClick(event)) editStyle("population"); } else { @@ -623,7 +627,7 @@ function togglePopulation(event) { editStyle("population"); return; } - turnButtonOff("togglePopulation"); + turnLayerButtonOff("togglePopulation"); const isD3data = population.select("line").datum(); if (!isD3data) { // just remove @@ -690,7 +694,7 @@ function drawPopulation(event) { function toggleCells(event) { if (!cells.selectAll("path").size()) { - turnButtonOn("toggleCells"); + turnLayerButtonOn("toggleCells"); drawCells(); if (event && isCtrlClick(event)) editStyle("cells"); } else { @@ -699,7 +703,7 @@ function toggleCells(event) { return; } cells.selectAll("path").remove(); - turnButtonOff("toggleCells"); + turnLayerButtonOff("toggleCells"); } } @@ -714,7 +718,7 @@ function drawCells() { function toggleIce(event) { if (!layerIsOn("toggleIce")) { - turnButtonOn("toggleIce"); + turnLayerButtonOn("toggleIce"); $("#ice").fadeIn(); if (!ice.selectAll("*").size()) drawIce(); if (event && isCtrlClick(event)) editStyle("ice"); @@ -724,7 +728,7 @@ function toggleIce(event) { return; } $("#ice").fadeOut(); - turnButtonOff("toggleIce"); + turnLayerButtonOff("toggleIce"); } } @@ -805,7 +809,7 @@ function toggleCultures(event) { const cultures = pack.cultures.filter(c => c.i && !c.removed); const empty = !cults.selectAll("path").size(); if (empty && cultures.length) { - turnButtonOn("toggleCultures"); + turnLayerButtonOn("toggleCultures"); drawCultures(); if (event && isCtrlClick(event)) editStyle("cults"); } else { @@ -814,7 +818,7 @@ function toggleCultures(event) { return; } cults.selectAll("path").remove(); - turnButtonOff("toggleCultures"); + turnLayerButtonOff("toggleCultures"); } } @@ -879,7 +883,7 @@ function drawCultures() { function toggleReligions(event) { const religions = pack.religions.filter(r => r.i && !r.removed); if (!relig.selectAll("path").size() && religions.length) { - turnButtonOn("toggleReligions"); + turnLayerButtonOn("toggleReligions"); drawReligions(); if (event && isCtrlClick(event)) editStyle("relig"); } else { @@ -888,7 +892,7 @@ function toggleReligions(event) { return; } relig.selectAll("path").remove(); - turnButtonOff("toggleReligions"); + turnLayerButtonOff("toggleReligions"); } } @@ -990,7 +994,7 @@ function drawReligions() { function toggleStates(event) { if (!layerIsOn("toggleStates")) { - turnButtonOn("toggleStates"); + turnLayerButtonOn("toggleStates"); regions.style("display", null); drawStates(); if (event && isCtrlClick(event)) editStyle("regions"); @@ -1000,7 +1004,7 @@ function toggleStates(event) { return; } regions.style("display", "none").selectAll("path").remove(); - turnButtonOff("toggleStates"); + turnLayerButtonOff("toggleStates"); } } @@ -1148,13 +1152,13 @@ export function drawStates() { return chain; } - invokeActiveZooming(); + Zoom.invoke(); TIME && console.timeEnd("drawStates"); } function toggleBorders(event) { if (!layerIsOn("toggleBorders")) { - turnButtonOn("toggleBorders"); + turnLayerButtonOn("toggleBorders"); drawBorders(); if (event && isCtrlClick(event)) editStyle("borders"); } else { @@ -1162,7 +1166,7 @@ function toggleBorders(event) { editStyle("borders"); return; } - turnButtonOff("toggleBorders"); + turnLayerButtonOff("toggleBorders"); borders.selectAll("path").remove(); } } @@ -1276,7 +1280,7 @@ export function drawBorders() { function toggleProvinces(event) { if (!layerIsOn("toggleProvinces")) { - turnButtonOn("toggleProvinces"); + turnLayerButtonOn("toggleProvinces"); drawProvinces(); if (event && isCtrlClick(event)) editStyle("provs"); } else { @@ -1285,7 +1289,7 @@ function toggleProvinces(event) { return; } provs.selectAll("*").remove(); - turnButtonOff("toggleProvinces"); + turnLayerButtonOff("toggleProvinces"); } } @@ -1415,7 +1419,7 @@ function getProvincesVertices() { function toggleGrid(event) { if (!gridOverlay.selectAll("*").size()) { - turnButtonOn("toggleGrid"); + turnLayerButtonOn("toggleGrid"); drawGrid(); calculateFriendlyGridSize(); @@ -1425,7 +1429,7 @@ function toggleGrid(event) { editStyle("gridOverlay"); return; } - turnButtonOff("toggleGrid"); + turnLayerButtonOff("toggleGrid"); gridOverlay.selectAll("*").remove(); } } @@ -1461,7 +1465,7 @@ function drawGrid() { function toggleCoordinates(event) { if (!coordinates.selectAll("*").size()) { - turnButtonOn("toggleCoordinates"); + turnLayerButtonOn("toggleCoordinates"); drawCoordinates(); if (event && isCtrlClick(event)) editStyle("coordinates"); } else { @@ -1469,7 +1473,7 @@ function toggleCoordinates(event) { editStyle("coordinates"); return; } - turnButtonOff("toggleCoordinates"); + turnLayerButtonOff("toggleCoordinates"); coordinates.selectAll("*").remove(); } } @@ -1540,7 +1544,7 @@ function getViewPoint(x, y) { function toggleCompass(event) { if (!layerIsOn("toggleCompass")) { - turnButtonOn("toggleCompass"); + turnLayerButtonOn("toggleCompass"); $("#compass").fadeIn(); if (!compass.selectAll("*").size()) { compass.append("use").attr("xlink:href", "#rose"); @@ -1553,13 +1557,13 @@ function toggleCompass(event) { return; } $("#compass").fadeOut(); - turnButtonOff("toggleCompass"); + turnLayerButtonOff("toggleCompass"); } } function toggleRelief(event) { if (!layerIsOn("toggleRelief")) { - turnButtonOn("toggleRelief"); + turnLayerButtonOn("toggleRelief"); if (!terrain.selectAll("*").size()) ReliefIcons(); $("#terrain").fadeIn(); if (event && isCtrlClick(event)) editStyle("terrain"); @@ -1569,13 +1573,13 @@ function toggleRelief(event) { return; } $("#terrain").fadeOut(); - turnButtonOff("toggleRelief"); + turnLayerButtonOff("toggleRelief"); } } function toggleTexture(event) { if (!layerIsOn("toggleTexture")) { - turnButtonOn("toggleTexture"); + turnLayerButtonOn("toggleTexture"); // append default texture image selected by default. Don't append on load to not harm performance if (!texture.selectAll("*").size()) { const x = +styleTextureShiftX.value; @@ -1596,19 +1600,19 @@ function toggleTexture(event) { } else { if (event && isCtrlClick(event)) return editStyle("texture"); $("#texture").fadeOut(); - turnButtonOff("toggleTexture"); + turnLayerButtonOff("toggleTexture"); } } function toggleRivers(event) { if (!layerIsOn("toggleRivers")) { - turnButtonOn("toggleRivers"); + turnLayerButtonOn("toggleRivers"); drawRivers(); if (event && isCtrlClick(event)) editStyle("rivers"); } else { if (event && isCtrlClick(event)) return editStyle("rivers"); rivers.selectAll("*").remove(); - turnButtonOff("toggleRivers"); + turnLayerButtonOff("toggleRivers"); } } @@ -1639,7 +1643,7 @@ export function drawRivers() { function toggleRoutes(event) { if (!layerIsOn("toggleRoutes")) { - turnButtonOn("toggleRoutes"); + turnLayerButtonOn("toggleRoutes"); $("#routes").fadeIn(); if (event && isCtrlClick(event)) editStyle("routes"); } else { @@ -1648,13 +1652,13 @@ function toggleRoutes(event) { return; } $("#routes").fadeOut(); - turnButtonOff("toggleRoutes"); + turnLayerButtonOff("toggleRoutes"); } } function toggleMilitary(event) { if (!layerIsOn("toggleMilitary")) { - turnButtonOn("toggleMilitary"); + turnLayerButtonOn("toggleMilitary"); $("#armies").fadeIn(); if (event && isCtrlClick(event)) editStyle("armies"); } else { @@ -1663,19 +1667,19 @@ function toggleMilitary(event) { return; } $("#armies").fadeOut(); - turnButtonOff("toggleMilitary"); + turnLayerButtonOff("toggleMilitary"); } } function toggleMarkers(event) { if (!layerIsOn("toggleMarkers")) { - turnButtonOn("toggleMarkers"); + turnLayerButtonOn("toggleMarkers"); drawMarkers(); if (event && isCtrlClick(event)) editStyle("markers"); } else { if (event && isCtrlClick(event)) return editStyle("markers"); markers.selectAll("*").remove(); - turnButtonOff("toggleMarkers"); + turnLayerButtonOff("toggleMarkers"); } } @@ -1724,23 +1728,23 @@ function drawMarker(marker, rescale = 1) { function toggleLabels(event) { if (!layerIsOn("toggleLabels")) { - turnButtonOn("toggleLabels"); + turnLayerButtonOn("toggleLabels"); labels.style("display", null); - invokeActiveZooming(); + Zoom.invoke(); if (event && isCtrlClick(event)) editStyle("labels"); } else { if (event && isCtrlClick(event)) { editStyle("labels"); return; } - turnButtonOff("toggleLabels"); + turnLayerButtonOff("toggleLabels"); labels.style("display", "none"); } } function toggleIcons(event) { if (!layerIsOn("toggleIcons")) { - turnButtonOn("toggleIcons"); + turnLayerButtonOn("toggleIcons"); $("#icons").fadeIn(); if (event && isCtrlClick(event)) editStyle("burgIcons"); } else { @@ -1748,14 +1752,14 @@ function toggleIcons(event) { editStyle("burgIcons"); return; } - turnButtonOff("toggleIcons"); + turnLayerButtonOff("toggleIcons"); $("#icons").fadeOut(); } } function toggleRulers(event) { if (!layerIsOn("toggleRulers")) { - turnButtonOn("toggleRulers"); + turnLayerButtonOn("toggleRulers"); if (event && isCtrlClick(event)) editStyle("ruler"); rulers.draw(); ruler.style("display", null); @@ -1764,7 +1768,7 @@ function toggleRulers(event) { editStyle("ruler"); return; } - turnButtonOff("toggleRulers"); + turnLayerButtonOff("toggleRulers"); ruler.selectAll("*").remove(); ruler.style("display", "none"); } @@ -1772,7 +1776,7 @@ function toggleRulers(event) { function toggleScaleBar(event) { if (!layerIsOn("toggleScaleBar")) { - turnButtonOn("toggleScaleBar"); + turnLayerButtonOn("toggleScaleBar"); $("#scaleBar").fadeIn(); if (event && isCtrlClick(event)) editUnits(); } else { @@ -1781,13 +1785,13 @@ function toggleScaleBar(event) { return; } $("#scaleBar").fadeOut(); - turnButtonOff("toggleScaleBar"); + turnLayerButtonOff("toggleScaleBar"); } } function toggleZones(event) { if (!layerIsOn("toggleZones")) { - turnButtonOn("toggleZones"); + turnLayerButtonOn("toggleZones"); $("#zones").fadeIn(); if (event && isCtrlClick(event)) editStyle("zones"); } else { @@ -1795,14 +1799,14 @@ function toggleZones(event) { editStyle("zones"); return; } - turnButtonOff("toggleZones"); + turnLayerButtonOff("toggleZones"); $("#zones").fadeOut(); } } function toggleEmblems(event) { if (!layerIsOn("toggleEmblems")) { - turnButtonOn("toggleEmblems"); + turnLayerButtonOn("toggleEmblems"); if (!emblems.selectAll("use").size()) drawEmblems(); $("#emblems").fadeIn(); if (event && isCtrlClick(event)) editStyle("emblems"); @@ -1812,7 +1816,7 @@ function toggleEmblems(event) { return; } $("#emblems").fadeOut(); - turnButtonOff("toggleEmblems"); + turnLayerButtonOff("toggleEmblems"); } } @@ -1914,34 +1918,18 @@ function drawEmblems() { const stateString = stateNodes .map( d => - `` + `` ) .join(""); emblems.select("#stateEmblems").attr("font-size", sizeStates).html(stateString); - invokeActiveZooming(); + Zoom.invoke(); }); TIME && console.timeEnd("drawEmblems"); } -export function layerIsOn(el) { - const buttonoff = byId(el).classList.contains("buttonoff"); - return !buttonoff; -} - -function turnButtonOff(el) { - byId(el).classList.add("buttonoff"); - getCurrentPreset(); -} - -function turnButtonOn(el) { - byId(el).classList.remove("buttonoff"); - getCurrentPreset(); -} - // move layers on mapLayers dragging (jquery sortable) $("#mapLayers").sortable({items: "li:not(.solid)", containment: "parent", cancel: ".solid", update: moveLayer}); function moveLayer(event, ui) { diff --git a/src/modules/ui/provinces-editor.js b/src/modules/ui/provinces-editor.js index b50b7304..0161cf93 100644 --- a/src/modules/ui/provinces-editor.js +++ b/src/modules/ui/provinces-editor.js @@ -7,6 +7,7 @@ import {rn} from "/src/utils/numberUtils"; import {rand, P} from "/src/utils/probabilityUtils"; import {parseTransform} from "/src/utils/stringUtils"; import {si} from "/src/utils/unitUtils"; +import {turnLayerButtonOff} from "/src/modules/ui/layers"; export function editProvinces() { if (customization) return; @@ -1118,7 +1119,7 @@ export function editProvinces() { if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); provs.select("#provincesBody").remove(); - turnButtonOff("toggleProvinces"); + turnLayerButtonOff("toggleProvinces"); provincesEditorAddLines(); }, diff --git a/src/modules/ui/style.js b/src/modules/ui/style.js index 3b1aad53..b57c5c63 100644 --- a/src/modules/ui/style.js +++ b/src/modules/ui/style.js @@ -430,13 +430,13 @@ function shiftElement() { styleRescaleMarkers.addEventListener("change", function () { markers.attr("rescale", +this.checked); - invokeActiveZooming(); + Zoom.invoke(); }); styleCoastlineAuto.addEventListener("change", function () { coastline.select("#sea_island").attr("auto-filter", +this.checked); styleFilter.style.display = this.checked ? "none" : "block"; - invokeActiveZooming(); + Zoom.invoke(); }); styleOceanFill.addEventListener("input", function () { @@ -834,7 +834,7 @@ function updateElements() { if (legend.selectAll("*").size() && window.redrawLegend) redrawLegend(); oceanLayers.selectAll("path").remove(); OceanLayers(); - invokeActiveZooming(); + Zoom.invoke(); } // GLOBAL FILTERS diff --git a/src/modules/ui/stylePresets.js b/src/modules/ui/stylePresets.js index 1d2bcc5b..7d07e026 100644 --- a/src/modules/ui/stylePresets.js +++ b/src/modules/ui/stylePresets.js @@ -127,7 +127,7 @@ function applyStyleWithUiRefresh(style) { updateMapFilter(); stylePreset.dataset.old = stylePreset.value; - invokeActiveZooming(); + Zoom.invoke(); setPresetRemoveButtonVisibiliy(); } diff --git a/src/modules/ui/submap.js b/src/modules/ui/submap.js index c3a8d351..f4bf62bb 100644 --- a/src/modules/ui/submap.js +++ b/src/modules/ui/submap.js @@ -3,6 +3,7 @@ import {clearMainTip} from "/src/scripts/tooltips"; import {parseError} from "/src/utils/errorUtils"; import {rn, minmax} from "/src/utils/numberUtils"; import {debounce} from "/src/utils/functionUtils"; +import {restoreLayers} from "/src/modules/ui/layers"; window.UISubmap = (function () { byId("submapPointsInput").addEventListener("input", function () { @@ -298,7 +299,7 @@ window.UISubmap = (function () { moveAllBurgsToGroup("towns", groupName); changeRadius(rn(oldScale * 0.8, 2), groupName); changeFontSize(svg.select(`#labels #${groupName}`), rn(oldScale * 2, 2)); - invokeActiveZooming(); + Zoom.invoke(); } if (options.rescaleStyles) changeStyles(oldScale); } catch (error) { diff --git a/src/modules/ui/tools.js b/src/modules/ui/tools.js index 1560d567..bd738a54 100644 --- a/src/modules/ui/tools.js +++ b/src/modules/ui/tools.js @@ -7,6 +7,7 @@ import {isCtrlClick} from "/src/utils/keyboardUtils"; import {prompt} from "/src/scripts/prompt"; import {getNextId} from "/src/utils/nodeUtils"; import {P, generateSeed} from "/src/utils/probabilityUtils"; +import {turnLayerButtonOn} from "/src/modules/ui/layers"; toolsContent.addEventListener("click", function (event) { if (customization) return tip("Please exit the customization mode first", false, "warning"); @@ -436,7 +437,7 @@ function regenerateIce() { function regenerateMarkers() { Markers.regenerate(); - turnButtonOn("toggleMarkers"); + turnLayerButtonOn("toggleMarkers"); drawMarkers(); if (document.getElementById("markersOverviewRefresh").offsetParent) markersOverviewRefresh.click(); } diff --git a/src/modules/zoom.js b/src/modules/zoom.js index 065e8fbc..2163eefe 100644 --- a/src/modules/zoom.js +++ b/src/modules/zoom.js @@ -1,12 +1,15 @@ import {debounce} from "/src/utils/functionUtils"; -import {handleZoom} from "/src/modules/activeZooming"; +import {handleZoom, invokeActiveZooming} from "/src/modules/activeZooming"; -// temporary expose to global +// temporary expose to window window.scale = 1; window.viewX = 0; window.viewY = 0; window.Zoom = (function () { + const onZoomDebouced = debounce(onZoom, 50); + const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", onZoomDebouced); + function onZoom() { if (!d3.event?.transform) return; const {k, x, y} = d3.event.transform; @@ -21,13 +24,15 @@ window.Zoom = (function () { handleZoom(isScaleChanged, isPositionChanged); } - const onZoomDebouced = debounce(onZoom, 50); - const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", onZoomDebouced); function setZoomBehavior() { svg.call(zoom); } + function invoke() { + invokeActiveZooming(); + } + // zoom to a specific point function to(x, y, z = 8, d = 2000) { const transform = d3.zoomIdentity.translate(x * -z + graphWidth / 2, y * -z + graphHeight / 2).scale(z); @@ -54,5 +59,5 @@ window.Zoom = (function () { zoom.scaleTo(element, scale); } - return {setZoomBehavior, to, reset, scaleExtent, translateExtent, scaleTo}; + return {setZoomBehavior, invoke, to, reset, scaleExtent, translateExtent, scaleTo}; })(); diff --git a/src/types/common.d.ts b/src/types/common.d.ts index ef2b66a5..63afecd1 100644 --- a/src/types/common.d.ts +++ b/src/types/common.d.ts @@ -3,3 +3,6 @@ type UnknownObject = {[key: string]: unknown}; interface Dict { [key: string]: T; } + +type RGB = `rgb(${number}, ${number}, ${number})`; +type Hex = `#${string}`; diff --git a/src/utils/colorUtils.ts b/src/utils/colorUtils.ts index a71b51b7..732290a5 100644 --- a/src/utils/colorUtils.ts +++ b/src/utils/colorUtils.ts @@ -1,6 +1,6 @@ const d3 = window.d3; -const c12 = [ +const c12: Hex[] = [ "#dababf", "#fb8072", "#80b1d3", @@ -38,3 +38,21 @@ export function getMixedColor(hexColor: string, mixation = 0.2, bright = 0.3) { return d3.color(mixedColor).brighter(bright).hex(); } + +export function getColorScheme(schemeName: string) { + return colorSchemeMap[schemeName] || colorSchemeMap.default; +} + +type ColorScheme = (n: number) => RGB; +const colorSchemeMap: Dict = { + default: d3.scaleSequential(d3.interpolateSpectral), + bright: d3.scaleSequential(d3.interpolateSpectral), + light: d3.scaleSequential(d3.interpolateRdYlGn), + green: d3.scaleSequential(d3.interpolateGreens), + monochrome: d3.scaleSequential(d3.interpolateGreys) +}; + +export function getHeightColor(height: number, scheme = getColorScheme("default")) { + const fittedValue = height < 20 ? height - 5 : height; + return scheme(1 - fittedValue / 100); +}