Merge remote-tracking branch 'upstream/master' into geodata

This commit is contained in:
Tom Vogt 2019-09-08 20:22:55 +02:00
commit 1aa668bd20
17 changed files with 278 additions and 64 deletions

BIN
.DS_Store vendored

Binary file not shown.

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018-2019 Max Ganiev (Azgaar)
Copyright 2018-2019 Max Ganiev (Azgaar), azgaar.fmg@yandex.by
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -12,6 +12,9 @@ furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
You can produce, without restrictions, any derivative works from the original
software and even reap commercial benefits from the sale of the secondary product.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

View file

@ -12,7 +12,7 @@ Refer to the [project wiki](https://github.com/Azgaar/Fantasy-Map-Generator/wiki
[![preview](https://cdn.discordapp.com/attachments/587406457725779968/594840632296734720/preview3.png)](https://cdn.discordapp.com/attachments/515359096925454350/593891237984206848/The_Wichin_Island_-_diplomacy.png)
Join our [Reddit community](https://www.reddit.com/r/FantasyMapGenerator) and [Discord server](https://discordapp.com/invite/X7E84HU) to share the created maps, discuss the Generator, suggest ideas and get a most recent updates. You may also contact me directly via [email](mailto:maxganiev@yandex.com). For bug reports please use the project [issues page](https://github.com/Azgaar/Fantasy-Map-Generator/issues) or Discord "Bugs" channel. If you are facing performance issues, please read [the tips](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Tips#performance-tips).
Join our [Reddit community](https://www.reddit.com/r/FantasyMapGenerator) and [Discord server](https://discordapp.com/invite/X7E84HU) to share the created maps, discuss the Generator, suggest ideas and get a most recent updates. You may also contact me directly via [email](mailto:azgaar.fmg@yandex.by). For bug reports please use the project [issues page](https://github.com/Azgaar/Fantasy-Map-Generator/issues) or Discord "Bugs" channel. If you are facing performance issues, please read [the tips](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Tips#performance-tips).
You can support the project [on Patreon](https://www.patreon.com/azgaar).

View file

@ -1931,7 +1931,7 @@ svg.button {
#errorBox {
font-size: .9em;
font-family: monospace;
font-family: Consolas, monospace;
color: #920303;
background-color: #dabdbd91;
padding: 2px;

View file

@ -1709,18 +1709,18 @@
<div id="toolsContent" class="tabcontent">
<div id="openEditor">
<p>Click to configure:</p>
<button id="editHeightmapButton" data-tip="Click to open Heightmap customization menu">Heightmap</button>
<button id="editBiomesButton" data-tip="Click to open Biomes Editor">Biomes</button>
<button id="editStatesButton" data-tip="Click to open States Editor">States</button>
<button id="editProvincesButton" data-tip="Click to open Provinces Editor">Provinces</button>
<button id="editDiplomacyButton" data-tip="Click to open Diplomatical relationships Editor">Diplomacy</button>
<button id="editCulturesButton" data-tip="Click to open Cultures Editor">Cultures</button>
<button id="editNamesBaseButton" data-tip="Click to open Namesbase Editor">Namesbase</button>
<button id="editZonesButton" data-tip="Click to open Zones Editor">Zones</button>
<button id="editReligions" data-tip="Click to open Religions Editor">Religions</button>
<button id="editBurgsButton" data-tip="Click to open Burgs Editor">Burgs</button>
<button id="editUnitsButton" data-tip="Click to open Units Editor">Units</button>
<button id="editNotesButton" data-tip="Click to open Notes Editor">Notes</button>
<button id="editHeightmapButton" data-tip="Click to open Heightmap customization menu. Shortcut: Shift + H">Heightmap</button>
<button id="editBiomesButton" data-tip="Click to open Biomes Editor. Shortcut: Shift + B">Biomes</button>
<button id="editStatesButton" data-tip="Click to open States Editor. Shortcut: Shift + S">States</button>
<button id="editProvincesButton" data-tip="Click to open Provinces Editor. Shortcut: Shift + P">Provinces</button>
<button id="editDiplomacyButton" data-tip="Click to open Diplomatical relationships Editor. Shortcut: Shift + D">Diplomacy</button>
<button id="editCulturesButton" data-tip="Click to open Cultures Editor. Shortcut: Shift + C">Cultures</button>
<button id="editNamesBaseButton" data-tip="Click to open Namesbase Editor. Shortcut: Shift + N">Namesbase</button>
<button id="editZonesButton" data-tip="Click to open Zones Editor. Shortcut: Shift + Z">Zones</button>
<button id="editReligions" data-tip="Click to open Religions Editor. Shortcut: Shift + R">Religions</button>
<button id="editBurgsButton" data-tip="Click to open Burgs Editor. Shortcut: Shift + T">Burgs</button>
<button id="editUnitsButton" data-tip="Click to open Units Editor. Shortcut: Shift + U">Units</button>
<button id="editNotesButton" data-tip="Click to open Notes Editor. Shortcut: Shift + O">Notes</button>
</div>
<div id="regenerateFeature">
@ -1738,11 +1738,11 @@
<div id="addFeature">
<p>Click to add:</p>
<button id="addBurgTool" data-tip="Click on map to place a burg. Hold Shift to add multiple">Burg</button>
<button id="addLabel" data-tip="Click on map to place label. Hold Shift to add multiple">Label</button>
<button id="addRiver" data-tip="Click on map to place a river. Hold Shift to add multiple">River</button>
<button id="addRoute" data-tip="Click on map to place a route">Route</button>
<button id="addMarker" data-tip="Click on map to place a marker. Hold Shift to add multiple">Marker</button>
<button id="addBurgTool" data-tip="Click on map to place a burg. Hold Shift to add multiple. Shortcut: Shift + G">Burg</button>
<button id="addLabel" data-tip="Click on map to place label. Hold Shift to add multiple. Shortcut: Shift + A">Label</button>
<button id="addRiver" data-tip="Click on map to place a river. Hold Shift to add multiple. Shortcut: Shift + I">River</button>
<button id="addRoute" data-tip="Click on map to place a route. Shortcut: Shift + E">Route</button>
<button id="addMarker" data-tip="Click on map to place a marker. Hold Shift to add multiple. Shortcut: Shift + K">Marker</button>
</div>
<p>Cell info: <i data-tip="Click to toggle the section" class="collapsible icon-down-open pointer"></i></p>
@ -1812,7 +1812,7 @@
<div id="aboutContent" class="tabcontent">
<p><a href="https://github.com/Azgaar/Fantasy-Map-Generator" target="_blank">Fantasy Map Generator</a> is a free <a href="https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE" target="_blank">open source</a> tool which procedurally generates fantasy maps. You may use auto-generated maps as they are, edit them or even create a new map from scratch. Check out the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial" target="_blank">quick start tutorial</a> and <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki" target="_blank">project wiki</a> for guidance.</p>
<p>Join our <a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a> and <a href="https://www.reddit.com/r/FantasyMapGenerator/" target="_blank">Reddit community</a> to ask questions, get help and share created maps. You may support the project on <a href='https://www.patreon.com/azgaar' target='_blank'>Patreon</a>.</p>
<p>The project is under active development. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</a>. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. Please report bugs <a href="https://github.com/Azgaar/Fantasy-Map-Generator/issues" target="_blank">here</a>. You can also contact me directly via <a href="mailto:maxganiev@yandex.ru" target="_blank">email</a>.</p>
<p>The project is under active development. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</a>. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. Please report bugs <a href="https://github.com/Azgaar/Fantasy-Map-Generator/issues" target="_blank">here</a>. You can also contact me directly via <a href="mailto:azgaar.fmg@yandex.by" target="_blank">email</a>.</p>
<p>A special thanks to all supporters! <i data-tip="Click to see supporters names" class="collapsible icon-down-open pointer"></i></p>
<p style="display:none">Supporters: Aaron Meyer, Ahmad Amerih, AstralJacks, aymeric, Billy Dean Goehring, Branndon Edwards,
Chase Mayers, Curt Flood, cyninge, Dino Princip, E.M. White, es, Fondue, Fritjof Olsson, Gatsu, Johan Fröberg, Jonathan Moore,
@ -1838,7 +1838,7 @@
<div id="saveMap" data-tip="Download the map as fully functional .map file to your machine. Shortcut: Ctrl + M">.map</div>
<div id="saveSVG" data-tip="Download the map as vector image (open in browser or Inkscape). Shortcut: Ctrl + S">.svg</div>
<div id="savePNG" data-tip="Download visible part of the map as .png image. Shortcut: Ctrl + P">.png</div>
<div id="saveGeo" data-tip="Download cell data as GeoJSON (for use in GIS tools such as QGIS).">.json</div>
<div id="saveGeo" data-tip="Download map data in GeoJSON format. Shortcut: Ctrl + G">.json</div>
<div id="quickSave" data-tip="Save map to browser storage. Shortcut: F6">storage</div>
<!-- <div id="saveDropbox" data-tip="Save fully functional .map file to Dropbox. Shortcut: Ctrl + B">Dropbox</div> -->
</div>
@ -2385,7 +2385,7 @@
<button id="templateRedo" data-tip="Redo the action" class="icon-cw" disabled></button>
<button id="templateSave" data-tip="Download the template as a text file" class="icon-download"></button>
<button id="templateLoad" data-tip="Open previously downloaded template" class="icon-upload"></button>
<button id="templateMail" onclick="window.open('mailto:maxganiev@yandex.com?Subject=Template%20suggestion', '_blank')" data-tip="Send a template suggestion to Azgaar" class="icon-mail-alt"></button>
<button id="templateMail" onclick="window.open('mailto:azgaar.fmg@yandex.by?Subject=Template%20suggestion', '_blank')" data-tip="Send a template suggestion to Azgaar" class="icon-mail-alt"></button>
</div>
</div>

1
libs/jquery-ui.css vendored
View file

@ -362,7 +362,6 @@ body .ui-dialog {
border: 0;
padding: .5em 1em;
background: none;
overflow: auto;
}
.ui-dialog .ui-dialog-buttonpane {
text-align: left;

15
main.js
View file

@ -1,5 +1,5 @@
// Fantasy Map Generator main script
// Azgaar (maxganiev@yandex.com). Minsk, 2017-2019
// Azgaar (azgaar.fmg@yandex.by). Minsk, 2017-2019
// https://github.com/Azgaar/Fantasy-Map-Generator
// MIT License
@ -688,17 +688,14 @@ function generate() {
console.error(error);
clearMainTip();
const regex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
const errorNoURL = error.stack.replace(regex, url => '<i>' + last(url.split("/")) + '</i>');
const errorParsed = errorNoURL.replace(/at /ig, "<br>&nbsp;&nbsp;at ");
alertMessage.innerHTML = `An error is occured on map generation. Please retry.
<br>If error persists, clear the stored data and try again.
<p id="errorBox">${errorParsed}</p>`;
<br>If error is critical, clear the stored data and try again.
<p id="errorBox">${parseError(error)}</p>`;
$("#alert").dialog({
resizable: false, title: "Generation error", maxWidth:500, buttons: {
resizable: false, title: "Generation error", width:320, buttons: {
"Clear data": function() {localStorage.clear(); localStorage.setItem("version", version);},
Regenerate: function() {regenerateMap(); $(this).dialog("close");}
Regenerate: function() {regenerateMap(); $(this).dialog("close");},
Ignore: function() {$(this).dialog("close");}
}, position: {my: "center", at: "center", of: "svg"}
});
}

View file

@ -0,0 +1,13 @@
Hill 2-3 20-40 20-40 20-40
Hill 2-3 20-40 20-40 40-60
Hill 2-3 20-40 40-60 20-40
Hill 2-3 20-40 60-80 20-40
Hill 2-3 20-40 20-40 60-80
Smooth 1 0 0 0
Range 1-2 40-50 20-40 20-80
Range 1-2 40-50 20-80 20-40
Trough 1-2 40-50 15-85 15-85
Pit 1-2 40-50 15-85 15-85
Hill 1-2 30-50 60-85 60-85
Smooth 2 0 0 0
Hill 6-8 10-20 20-80 20-80

View file

@ -436,6 +436,8 @@
void function drawLabels() {
const g = labels.select("#states"), t = defs.select("#textPaths");
const displayed = layerIsOn("toggleLabels");
if (!displayed) toggleLabels();
if (!list) {
g.selectAll("text").remove();
@ -520,6 +522,7 @@
});
example.remove();
if (!displayed) toggleLabels();
}()
console.timeEnd("drawStateLabels");
@ -583,6 +586,7 @@
const generateDiplomacy = function() {
console.time("generateDiplomacy");
const cells = pack.cells, states = pack.states;
const chronicle = states[0].diplomacy = [];
const valid = states.filter(s => s.i && !states.removed);
if (valid.length < 2) return;
@ -592,7 +596,6 @@
const navals = {"Neutral":1, "Suspicion":2, "Rival":1, "Unknown":1}; // relations of naval powers
valid.forEach(s => s.diplomacy = new Array(states.length).fill("x")); // clear all relationships
const chronicle = states[0].diplomacy = [];
const areaMean = d3.mean(valid.map(s => s.area)); // avarage state area
// generic relations

View file

@ -387,6 +387,146 @@ async function saveMap() {
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
}
// download map data as GeoJSON
function saveGeoJSON() {
alertMessage.innerHTML = `You can export map data in GeoJSON format used in GIS tools such as QGIS.
Check out <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/GIS-data-export" target="_blank">wiki-page</a> for guidance`;
$("#alert").dialog({title: "GIS data export", resizable: false, width: 320, position: {my: "center", at: "center", of: "svg"},
buttons: {
Cells: saveGeoJSON_Cells,
Routes: saveGeoJSON_Roads,
Rivers: saveGeoJSON_Rivers,
Close: function() {$(this).dialog("close");}
}
});
}
function saveGeoJSON_Roads() {
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
routes._groups[0][0].childNodes.forEach(n => {
n.childNodes.forEach(r => {
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"LineString\", \"coordinates\": ";
data += JSON.stringify(getRoadPoints(r));
data += " },\n \"properties\": {\n";
data += " \"id\": \""+r.id+"\",\n";
data += " \"type\": \""+n.id+"\"\n";
data +=" }\n},\n";
});
});
data = data.substring(0, data.length - 2)+"\n"; // remove trailing comma
data += "]}";
const dataBlob = new Blob([data], {type: "application/json"});
const url = window.URL.createObjectURL(dataBlob);
const link = document.createElement("a");
document.body.appendChild(link);
link.download = "fmg_routes_" + Date.now() + ".geojson";
link.href = url;
link.click();
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
}
function saveGeoJSON_Rivers() {
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
rivers._groups[0][0].childNodes.forEach(n => {
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"LineString\", \"coordinates\": ";
data += JSON.stringify(getRiverPoints(n));
data += " },\n \"properties\": {\n";
data += " \"id\": \""+n.id+"\",\n";
data += " \"width\": \""+n.dataset.width+"\",\n";
data += " \"increment\": \""+n.dataset.increment+"\"\n";
data +=" }\n},\n";
});
data = data.substring(0, data.length - 2)+"\n"; // remove trailing comma
data += "]}";
const dataBlob = new Blob([data], {type: "application/json"});
const url = window.URL.createObjectURL(dataBlob);
const link = document.createElement("a");
document.body.appendChild(link);
link.download = "fmg_rivers_" + Date.now() + ".geojson";
link.href = url;
link.click();
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
}
function getRoadPoints(node) {
let points = [];
const l = node.getTotalLength();
const increment = l / Math.ceil(l / 2);
for (let i=0; i <= l; i += increment) {
const p = node.getPointAtLength(i);
let x = mapCoordinates.lonW + (p.x / graphWidth) * mapCoordinates.lonT;
let y = mapCoordinates.latN - (p.y / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
points.push([x,y]);
}
return points;
}
function getRiverPoints(node) {
let points = [];
const l = node.getTotalLength() / 2; // half-length
const increment = 0.25; // defines density of points
for (let i=l, c=i; i >= 0; i -= increment, c += increment) {
const p1 = node.getPointAtLength(i);
const p2 = node.getPointAtLength(c);
let x = mapCoordinates.lonW + (((p1.x+p2.x)/2) / graphWidth) * mapCoordinates.lonT;
let y = mapCoordinates.latN - (((p1.y+p2.y)/2) / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
points.push([x,y]);
}
return points;
}
function saveGeoJSON_Cells() {
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
const cells = pack.cells, v = pack.vertices;
cells.i.forEach(i => {
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"Polygon\", \"coordinates\": [[";
cells.v[i].forEach(n => {
let x = mapCoordinates.lonW + (v.p[n][0] / graphWidth) * mapCoordinates.lonT;
let y = mapCoordinates.latN - (v.p[n][1] / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
data += "["+x+","+y+"],";
});
// close the ring
let x = mapCoordinates.lonW + (v.p[cells.v[i][0]][0] / graphWidth) * mapCoordinates.lonT;
let y = mapCoordinates.latN - (v.p[cells.v[i][0]][1] / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
data += "["+x+","+y+"]";
data += "]] },\n \"properties\": {\n";
let height = parseInt(getFriendlyHeight(cells.h[i]));
data += " \"id\": \""+i+"\",\n";
data += " \"height\": \""+height+"\",\n";
data += " \"biome\": \""+cells.biome[i]+"\",\n";
data += " \"population\": \""+cells.pop[i]+"\",\n";
data += " \"state\": \""+cells.state[i]+"\",\n";
data += " \"province\": \""+cells.province[i]+"\",\n";
data += " \"culture\": \""+cells.culture[i]+"\",\n";
data += " \"religion\": \""+cells.religion[i]+"\"\n";
data +=" }\n},\n";
});
data = data.substring(0, data.length - 2)+"\n"; // remove trailing comma
data += "]}";
const dataBlob = new Blob([data], {type: "application/json"});
const url = window.URL.createObjectURL(dataBlob);
const link = document.createElement("a");
document.body.appendChild(link);
link.download = "fmg_cells_" + Date.now() + ".geojson";
link.href = url;
link.click();
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
}
function uploadFile(file, callback) {
uploadFile.timeStart = performance.now();
@ -695,6 +835,7 @@ function parseLoadedData(data) {
// v 1.0 initially has Sympathy status then relaced with Friendly
for (const s of pack.states) {
if (!s.diplomacy) continue;
s.diplomacy = s.diplomacy.map(r => r === "Sympathy" ? "Friendly" : r);
}
}
@ -713,13 +854,9 @@ function parseLoadedData(data) {
console.error(error);
clearMainTip();
const regex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
const errorNoURL = error.stack.replace(regex, url => '<i>' + last(url.split("/")) + '</i>');
const errorParsed = errorNoURL.replace(/ at /ig, "<br>&nbsp;&nbsp;at ");
alertMessage.innerHTML = `An error is occured on map loading. Select a different file to load,
<br>generate a new random map or cancel the loading
<p id="errorBox">${errorParsed}</p>`;
<p id="errorBox">${parseError(error)}</p>`;
$("#alert").dialog({
resizable: false, title: "Loading error", maxWidth:500, buttons: {
"Select file": function() {$(this).dialog("close"); mapToLoad.click();},

View file

@ -2,7 +2,7 @@
function editDiplomacy() {
if (customization) return;
if (pack.states.filter(s => s.i && !s.removed).length < 2) {
tip("There should be at least 2 states to edit the diplomacy", false, "Error");
tip("There should be at least 2 states to edit the diplomacy", false, "error");
return;
}

View file

@ -231,28 +231,50 @@ document.addEventListener("keydown", function(event) {
const active = document.activeElement.tagName;
if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; // don't trigger if user inputs a text
if (active === "DIV" && document.activeElement.contentEditable === "true") return; // don't trigger if user inputs a text
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey;
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey, meta = event.metaKey;
if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
else if (key === 9) {toggleOptions(event); event.preventDefault();} // Tab to toggle options
else if (key === 113) regeneratePrompt(); // "F2" for new map
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
else if (key === 117) quickSave(); // "F6" for quick save
else if (key === 120) quickLoad(); // "F9" for quick load
else if (ctrl && key === 80) saveAsImage("png"); // Ctrl + "P" to save as PNG
else if (ctrl && key === 83) saveAsImage("svg"); // Ctrl + "S" to save as SVG
else if (ctrl && key === 77) saveMap(); // Ctrl + "M" to save MAP file
else if (ctrl && key === 71) saveGeoJSON(); // Ctrl + "G" to save as GeoJSON
else if (ctrl && key === 85) mapToLoad.click(); // Ctrl + "U" to load MAP from URL
else if (ctrl && key === 76) mapToLoad.click(); // Ctrl + "L" to load MAP from local file
else if (ctrl && key === 81) toggleSaveReminder(); // Ctrl + "Q" to toggle save reminder
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
else if (undo.offsetParent && ctrl && key === 90) undo.click(); // Ctrl + "Z" to undo
else if (redo.offsetParent && ctrl && key === 89) redo.click(); // Ctrl + "Y" to redo
else if (shift && key === 192) console.log(pack.cells); // Shift + "`" to log cells data
else if (shift && key === 66) console.table(pack.burgs); // Shift + "B" to log burgs data
else if (shift && key === 83) console.table(pack.states); // Shift + "S" to log states data
else if (shift && key === 67) console.table(pack.cultures); // Shift + "C" to log cultures data
else if (shift && key === 82) console.table(pack.religions); // Shift + "R" to log religions data
else if (shift && key === 70) console.table(pack.features); // Shift + "F" to log features data
else if (shift && key === 72) editHeightmap(); // Shift + "H" to edit Heightmap
else if (shift && key === 66) editBiomes(); // Shift + "B" to edit Biomes
else if (shift && key === 83) editStates(); // Shift + "S" to edit States
else if (shift && key === 80) editProvinces(); // Shift + "P" to edit Provinces
else if (shift && key === 68) editDiplomacy(); // Shift + "D" to edit Diplomacy
else if (shift && key === 67) editCultures(); // Shift + "C" to edit Cultures
else if (shift && key === 78) editNamesbase(); // Shift + "N" to edit Namesbase
else if (shift && key === 90) editZones(); // Shift + "Z" to edit Zones
else if (shift && key === 82) editReligions(); // Shift + "R" to edit Religions
else if (shift && key === 84) editBurgs(); // Shift + "T" to edit Burgs
else if (shift && key === 85) editUnits(); // Shift + "U" to edit Units
else if (shift && key === 79) editNotes(); // Shift + "O" to edit Notes
else if (shift && key === 71) toggleAddBurg(); // Shift + "G" to click to add Burg
else if (shift && key === 65) toggleAddLabel(); // Shift + "A" to click to add Label
else if (shift && key === 73) toggleAddRiver(); // Shift + "I" to click to add River
else if (shift && key === 69) toggleAddRoute(); // Shift + "E" to click to add Route
else if (shift && key === 75) toggleAddMarker(); // Shift + "K" to click to add Marker
else if (meta && key === 192) console.log(pack.cells); // Metakey + "`" to log cells data
else if (meta && key === 66) console.table(pack.burgs); // Metakey + "B" to log burgs data
else if (meta && key === 83) console.table(pack.states); // Metakey + "S" to log states data
else if (meta && key === 67) console.table(pack.cultures); // Metakey + "C" to log cultures data
else if (meta && key === 82) console.table(pack.religions); // Metakey + "R" to log religions data
else if (meta && key === 70) console.table(pack.features); // Metakey + "F" to log features data
else if (key === 88) toggleTexture(); // "X" to toggle Texture layer
else if (key === 72) toggleHeight(); // "H" to toggle Heightmap layer
@ -294,10 +316,6 @@ document.addEventListener("keydown", function(event) {
else if (key === 55 || key === 103) zoom.scaleTo(svg, 7); // 7 to zoom to 7
else if (key === 56 || key === 104) zoom.scaleTo(svg, 8); // 8 to zoom to 8
else if (key === 57 || key === 105) zoom.scaleTo(svg, 9); // 9 to zoom to 9
else if (ctrl && key === 90) undo.click(); // Ctrl + "Z" to undo
else if (ctrl && key === 89) redo.click(); // Ctrl + "Y" to redo
else if (ctrl) pressControl(); // Control to toggle mode
});

View file

@ -1117,7 +1117,8 @@ function getHeight(h) {
preview.width = grid.cellsX;
preview.height = grid.cellsY;
document.body.insertBefore(preview, optionsContainer);
preview.addEventListener("mouseover", () => tip("Heightmap preview. Right click and 'Save image as..' to download the image"));
preview.addEventListener("mouseover", () => tip("Heightmap preview. Click to download the image"));
preview.addEventListener("click", downloadPreview);
drawHeightmapPreview();
}
@ -1137,6 +1138,41 @@ function getHeight(h) {
ctx.putImageData(imageData, 0, 0);
}
function downloadPreview() {
const preview = document.getElementById("preview");
const dataURL = preview.toDataURL("image/png");
const img = new Image();
img.src = dataURL;
img.onload = function() {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = svgWidth;
canvas.height = svgHeight;
document.body.insertBefore(canvas, optionsContainer);
ctx.drawImage(img, 0, 0, svgWidth, svgHeight);
// const imageData = ctx.getImageData(0, 0, svgWidth, svgHeight);
// for (let i=0; i < imageData.data.length; i+=4) {
// const v = Math.min(rn(imageData.data[i] * gauss(1, .05, .9, 1.1, 3)), 255);
// imageData.data[i] = v;
// imageData.data[i+1] = v;
// imageData.data[i+2] = v;
// }
// ctx.putImageData(imageData, 0, 0);
const imgBig = canvas.toDataURL("image/png");
const link = document.createElement("a");
link.target = "_blank";
link.download = "heightmap_" + Date.now() + ".png";
link.href = imgBig;
document.body.appendChild(link);
link.click();
canvas.remove();
}
}
function openPerspectivePanel() {
if ($("#perspectivePanel").is(":visible")) return;
$("#perspectivePanel").dialog({

View file

@ -11,7 +11,7 @@ function editLabel() {
viewbox.on("touchmove mousemove", showEditorTips);
$("#labelEditor").dialog({
title: "Edit Label", resizable: false,
title: "Edit Label", resizable: false, width: fitContent(),
position: {my: "center top+10", at: "bottom", of: text, collision: "fit"},
close: closeLabelEditor
});

View file

@ -1011,11 +1011,11 @@ function toggleMarkers() {
function toggleLabels() {
if (!layerIsOn("toggleLabels")) {
turnButtonOn("toggleLabels");
$('#labels').fadeIn();
labels.attr("display", null)
invokeActiveZooming();
} else {
turnButtonOff("toggleLabels");
$('#labels').fadeOut();
labels.attr("display", "none");
}
}

View file

@ -1027,12 +1027,10 @@ function toggleSavePane() {
<br>Please check browser settings and turn off adBlocker if it is enabled`;
$("#alert").dialog({title: "File saver", resizable: false, position: {my: "center", at: "center", of: "svg"},
buttons: {
OK: function() {
buttons: {OK: function() {
localStorage.setItem("dns_allow_popup_message", true);
$(this).dialog("close");
}
}
}}
});
}
}

File diff suppressed because one or more lines are too long