add customer exporter

This commit is contained in:
ntchung 2020-03-15 00:50:30 +07:00
parent d6ee8f55ec
commit f29f319254
2 changed files with 217 additions and 2 deletions

View file

@ -3346,12 +3346,13 @@
<div id="saveMapData" style="display: none" class="dialog">
<div style="margin-bottom: .3em">Please select a saving variant:</div>
<div>
<button onclick="saveMap()" data-tip="Download the map as fully-functional .map file to your machine">.map</button>
<!--<button onclick="saveMap()" data-tip="Download the map as fully-functional .map file to your machine">.map</button>
<button onclick="saveSVG()" data-tip="Download the map as vector image (open in browser or Inkscape)">.svg</button>
<button onclick="savePNG()" data-tip="Download visible part of the map as .png (lossless compressed)">.png</button>
<button onclick="saveJPEG()" data-tip="Download visible part of the map as .jpeg (lossy compressed) image">.jpeg</button>
<button onclick="saveGeoJSON()" data-tip="Download map data in GeoJSON format">.json</button>
<button onclick="quickSave()" data-tip="Save fully-functional map to browser storage. Shortcut: F6">storage</button>
<button onclick="quickSave()" data-tip="Save fully-functional map to browser storage. Shortcut: F6">storage</button>-->
<button onclick="customExport()" data-tip="Export to map to our game engine">export</button>
</div>
<p style="font-style: italic">Generator uses pop-up window to download files. Please ensure your browser does not block popups</p>
</div>

View file

@ -962,3 +962,217 @@ function parseLoadedData(data) {
}
}
// export the map for our game engine
async function customExport () {
if (customization) {tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); return;}
closeDialogs("#alert");
drawProvinces();
const landmassImage = await getMapImageBase64(["landmass"]);
const provincesImage = await getMapImageBase64(["provs"]);
const blob = await getMapExportData(landmassImage, provincesImage);
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName() + ".json";
link.href = URL;
document.body.appendChild(link);
link.click();
tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000);
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
}
function getMapImageBase64 (visibles) {
return new Promise(async resolve => {
const url = await getMapLayerURL("png", null, visibles);
const canvas = document.createElement("canvas");
canvas.width = mapWidthInput.value * pngResolutionInput.value;
canvas.height = mapHeightInput.value * pngResolutionInput.value;
const ctx = canvas.getContext("2d");
const img = new Image();
img.src = url;
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
resolve(canvas.toDataURL());
}
});
}
// parse map svg to object url
async function getMapLayerURL(type, subtype, visibles) {
const cloneEl = document.getElementById("map").cloneNode(true); // clone svg
cloneEl.id = "fantasyMap";
document.body.appendChild(cloneEl);
const clone = d3.select(cloneEl);
clone.attr("width", mapWidthInput.value).attr("height", mapHeightInput.value).attr("background-color", "transparent");
clone.select("#debug").remove();
clone.select('#ruler').remove();
clone.select('#scaleBar').remove();
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (isFirefox && type === "mesh") clone.select("#oceanPattern").remove();
if (subtype === "noWater") {clone.select("#oceanBase").attr("opacity", 0); clone.select("#oceanPattern").attr("opacity", 0);}
if (type !== "png") {
// reset transform to show the whole map
clone.attr("width", graphWidth).attr("height", graphHeight);
clone.select("#viewbox").attr("transform", null);
}
if (type === "svg") removeUnusedElements(clone);
if (customization && type === "mesh") updateMeshCells(clone);
const LAYERS = ["ocean", "lake", "landmass", "texture", "terrs", "biomes", "cells", "gridOverlay", "coordinates", "compass",
"rivers", "terrain", "relig", "cults", "regions", "statesBody", "statesHalo", "provs", "zones", "borders", "stateBorders",
"provinceBorders", "routes", "roads", "trails", "searoutes", "temperature", "coastline", "prec", "population", "labels",
"icons", "burgIcons", "anchors", "markers", "fogging-cont", "fogging"];
LAYERS.forEach((layer) => {
if (visibles.indexOf(layer) < 0) {
clone.select('#' + layer).remove();
} else {
clone.select('#' + layer).attr("opacity", 1);
}
});
inlineStyle(clone);
const fontStyle = await GFontToDataURI(getFontsToLoad()); // load non-standard fonts
if (fontStyle) clone.select("defs").append("style").text(fontStyle.join('\n')); // add font to style
clone.append("metadata").text("<dc:format>image/svg+xml</dc:format>");
const serialized = (new XMLSerializer()).serializeToString(clone.node());
const svg_xml = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>` + serialized;
clone.remove();
const blob = new Blob([svg_xml], {type: 'image/svg+xml;charset=utf-8'});
const url = window.URL.createObjectURL(blob);
window.setTimeout(() => window.URL.revokeObjectURL(url), 5000);
return url;
}
function getMapExportData (landmassImage, provincesImage) {
console.time("getMapExportData");
return new Promise(resolve => {
const date = new Date();
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
const params = { version, license, dateString, seed, graphWidth, graphHeight };
const options = {
distanceUnitInput: distanceUnitInput.value,
distanceScaleInput: distanceScaleInput.value,
areaUnit: areaUnit.value,
heightUnit: heightUnit.value,
heightExponentInput: heightExponentInput.value,
temperatureScale: temperatureScale.value,
barSize: barSize.value,
barLabel: barLabel.value,
barBackOpacity: barBackOpacity.value,
barBackColor: barBackColor.value,
barPosX: barPosX.value,
barPosY: barPosY.value,
populationRate: populationRate.value,
urbanization: urbanization.value,
mapSizeOutput: mapSizeOutput.value,
latitudeOutput: latitudeOutput.value,
temperatureEquatorOutput: temperatureEquatorOutput.value,
temperaturePoleOutput: temperaturePoleOutput.value,
precOutput: precOutput.value,
winds,
mapName: mapName.value
};
const coords = mapCoordinates;
const biomes = {
color: biomesData.color,
habitability: biomesData.habitability,
name: biomesData.name
};
const notesData = notes;
const cloneEl = document.getElementById("map").cloneNode(true); // clone svg
// set transform values to default
cloneEl.setAttribute("width", graphWidth);
cloneEl.setAttribute("height", graphHeight);
cloneEl.querySelector("#viewbox").setAttribute("transform", null);
const svg_xml = (new XMLSerializer()).serializeToString(cloneEl);
const gridGeneral = {spacing:grid.spacing, cellsX:grid.cellsX, cellsY:grid.cellsY, boundary:grid.boundary, points:grid.points, features:grid.features};
const features = pack.features;
const cultures = pack.cultures;
const states = pack.states;
const burgs = pack.burgs;
const religions = pack.religions;
const provinces = pack.provinces;
const rivers = pack.rivers;
// store name array only if it is not the same as default
const defaultNB = Names.getNameBases();
const namesData = nameBases.map((b,i) => {
const names = defaultNB[i] && defaultNB[i].b === b.b ? "" : b.b;
return `${b.name}|${b.min}|${b.max}|${b.d}|${b.m}|${names}`;
});
// round population to save resources
const pop = Array.from(pack.cells.pop).map(p => rn(p, 4));
// data format as below
const data = JSON.stringify(
{
params,
options,
coords,
biomes,
notesData,
gridGeneral,
grid: {
cells: {
h: grid.cells.h,
prec: grid.cells.prec,
f: grid.cells.f,
t: grid.cells.t,
temp: grid.cells.temp
}
},
features,
cultures,
states,
burgs,
pack: {
cells: {
r: pack.cells.r,
road: pack.cells.road,
s: pack.cells.s,
state: pack.cells.state,
religion: pack.cells.religion,
province: pack.cells.province,
crossroad: pack.cells.crossroad
}
},
pop,
religions,
provinces,
namesData,
rivers,
landmassImage,
provincesImage
},
null,
2
);
const blob = new Blob([data], {type: "text/plain"});
console.timeEnd("getMapExportData");
resolve(blob);
});
}