mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-21 19:41:23 +01:00
feat: allow to render ocean heightmap - test
This commit is contained in:
parent
cd45ad91fd
commit
94280e0acf
7 changed files with 171 additions and 104 deletions
107
index.html
107
index.html
|
|
@ -824,6 +824,63 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="styleHeightmap">
|
||||
<tr id="styleHeightmapRenderOceanOption" data-tip="Check to render ocean heights">
|
||||
<td colspan="2">
|
||||
<input id="styleHeightmapRenderOcean" class="checkbox" type="checkbox" />
|
||||
<label for="styleHeightmapRenderOcean" class="checkbox-label">Render ocean heights</label>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Terracing rate. Set to 0 (toggle off) to improve performance">
|
||||
<td>Terracing</td>
|
||||
<td>
|
||||
<input id="styleHeightmapTerracingInput" type="range" min="0" max="20" step="1" />
|
||||
<output id="styleHeightmapTerracingOutput">0</output>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Layers reduction rate. Increase to improve performance">
|
||||
<td>Reduce layers</td>
|
||||
<td>
|
||||
<input id="styleHeightmapSkipInput" type="range" min="0" max="10" step="1" value="5" />
|
||||
<output id="styleHeightmapSkipOutput">5</output>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Line simplification rate. Increase to slightly improve performance">
|
||||
<td>Simplify line</td>
|
||||
<td>
|
||||
<input id="styleHeightmapSimplificationInput" type="range" min="0" max="10" step="1" value="0" />
|
||||
<output id="styleHeightmapSimplificationOutput">0</output>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Select line interpolation type">
|
||||
<td>Line style</td>
|
||||
<td>
|
||||
<select id="styleHeightmapCurve">
|
||||
<option value="curveBasisClosed" selected>Curved</option>
|
||||
<option value="curveLinear">Linear</option>
|
||||
<option value="curveStep">Rectangular</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Select color scheme for the element">
|
||||
<td>Color scheme</td>
|
||||
<td>
|
||||
<select id="styleHeightmapScheme"></select>
|
||||
<button
|
||||
id="openCreateHeightmapSchemeButton"
|
||||
data-tip="Click to add a custom heightmap color scheme"
|
||||
data-stops="#ffffff,#EEEECC,#D2B48C,#008000,#008080"
|
||||
class="icon-plus sideButton"
|
||||
></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="styleOpacity" style="display: none">
|
||||
<tr data-tip="Set opacity. 0: transparent, 1: solid">
|
||||
<td>Opacity</td>
|
||||
|
|
@ -1281,56 +1338,6 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="styleHeightmap">
|
||||
<tr data-tip="Terracing rate. Set to 0 (toggle off) to improve performance">
|
||||
<td>Terracing</td>
|
||||
<td>
|
||||
<input id="styleHeightmapTerracingInput" type="range" min="0" max="20" step="1" />
|
||||
<output id="styleHeightmapTerracingOutput">0</output>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Layers reduction rate. Increase to improve performance">
|
||||
<td>Reduce layers</td>
|
||||
<td>
|
||||
<input id="styleHeightmapSkipInput" type="range" min="0" max="10" step="1" value="5" />
|
||||
<output id="styleHeightmapSkipOutput">5</output>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Line simplification rate. Increase to slightly improve performance">
|
||||
<td>Simplify line</td>
|
||||
<td>
|
||||
<input id="styleHeightmapSimplificationInput" type="range" min="0" max="10" step="1" value="0" />
|
||||
<output id="styleHeightmapSimplificationOutput">0</output>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Select line interpolation type">
|
||||
<td>Line style</td>
|
||||
<td>
|
||||
<select id="styleHeightmapCurve">
|
||||
<option value="curveBasisClosed" selected>Curved</option>
|
||||
<option value="curveLinear">Linear</option>
|
||||
<option value="curveStep">Rectangular</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Select color scheme for the element">
|
||||
<td>Color scheme</td>
|
||||
<td>
|
||||
<select id="styleHeightmapScheme"></select>
|
||||
<button
|
||||
id="openCreateHeightmapSchemeButton"
|
||||
data-tip="Click to add a custom heightmap color scheme"
|
||||
data-stops="#ffffff,#EEEECC,#D2B48C,#008000,#008080"
|
||||
class="icon-plus sideButton"
|
||||
></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="styleArmies">
|
||||
<tr data-tip="Set fill transparency. Set to 0 to make it fully transparent">
|
||||
<td>Fill opacity</td>
|
||||
|
|
|
|||
48
main.js
48
main.js
|
|
@ -604,6 +604,7 @@ void (function addDragToUpload() {
|
|||
|
||||
async function generate(options) {
|
||||
try {
|
||||
console.log("--- 1 ---", Math.random());
|
||||
const timeStart = performance.now();
|
||||
const {seed: precreatedSeed, graph: precreatedGraph} = options || {};
|
||||
|
||||
|
|
@ -639,7 +640,9 @@ async function generate(options) {
|
|||
Biomes.define();
|
||||
|
||||
rankCells();
|
||||
console.log("--- 6 ---", Math.random());
|
||||
Cultures.generate();
|
||||
console.log("--- 7 ---", Math.random());
|
||||
Cultures.expand();
|
||||
BurgsAndStates.generate();
|
||||
Religions.generate();
|
||||
|
|
@ -722,10 +725,30 @@ function markFeatures() {
|
|||
grid.features = [0];
|
||||
|
||||
for (let i = 1, queue = [0]; queue[0] !== -1; i++) {
|
||||
// [1864, 1731]
|
||||
// if (queue[0] === 1864 || queue[0] === 1731) {
|
||||
// cells.f[queue[0]] = 1;
|
||||
// cells.t[queue[0]] = -1;
|
||||
// queue = [cells.f.findIndex(f => !f)];
|
||||
// i--;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
cells.f[queue[0]] = i; // feature number
|
||||
const land = heights[queue[0]] >= 20;
|
||||
let border = false; // true if feature touches map border
|
||||
|
||||
// if (i === 11) {
|
||||
// debug
|
||||
// .append("g")
|
||||
// .attr("id", "feature11")
|
||||
// .append("circle")
|
||||
// .attr("cx", grid.points[queue[0]][0])
|
||||
// .attr("cy", grid.points[queue[0]][1])
|
||||
// .attr("r", 2)
|
||||
// .attr("fill", "red");
|
||||
// }
|
||||
|
||||
while (queue.length) {
|
||||
const q = queue.pop();
|
||||
if (cells.b[q]) border = true;
|
||||
|
|
@ -859,6 +882,8 @@ function openNearSeaLakes() {
|
|||
}
|
||||
|
||||
function removeLake(threshold, lake, ocean) {
|
||||
debugger;
|
||||
console.log("removeLake", threshold, lake, ocean);
|
||||
cells.h[threshold] = 19;
|
||||
cells.t[threshold] = -1;
|
||||
cells.f[threshold] = ocean;
|
||||
|
|
@ -1349,22 +1374,14 @@ function drawCoastline() {
|
|||
// Re-mark features (ocean, lakes, islands)
|
||||
function reMarkFeatures() {
|
||||
TIME && console.time("reMarkFeatures");
|
||||
const cells = pack.cells,
|
||||
features = (pack.features = [0]);
|
||||
const cells = pack.cells;
|
||||
const features = (pack.features = [0]);
|
||||
|
||||
cells.f = new Uint16Array(cells.i.length); // cell feature number
|
||||
cells.t = new Int8Array(cells.i.length); // cell type: 1 = land along coast; -1 = water along coast;
|
||||
cells.haven = cells.i.length < 65535 ? new Uint16Array(cells.i.length) : new Uint32Array(cells.i.length); // cell haven (opposite water cell);
|
||||
cells.harbor = new Uint8Array(cells.i.length); // cell harbor (number of adjacent water cells);
|
||||
|
||||
const defineHaven = i => {
|
||||
const water = cells.c[i].filter(c => cells.h[c] < 20);
|
||||
const dist2 = water.map(c => (cells.p[i][0] - cells.p[c][0]) ** 2 + (cells.p[i][1] - cells.p[c][1]) ** 2);
|
||||
const closest = water[dist2.indexOf(Math.min.apply(Math, dist2))];
|
||||
|
||||
cells.haven[i] = closest;
|
||||
cells.harbor[i] = water.length;
|
||||
};
|
||||
|
||||
if (!cells.i.length) return; // no cells -> there is nothing to do
|
||||
for (let i = 1, queue = [0]; queue[0] !== -1; i++) {
|
||||
const start = queue[0]; // first cell
|
||||
|
|
@ -1405,6 +1422,15 @@ function reMarkFeatures() {
|
|||
// markupPackLand
|
||||
markup(pack.cells, 3, 1, 0);
|
||||
|
||||
function defineHaven(i) {
|
||||
const water = cells.c[i].filter(c => cells.h[c] < 20);
|
||||
const dist2 = water.map(c => (cells.p[i][0] - cells.p[c][0]) ** 2 + (cells.p[i][1] - cells.p[c][1]) ** 2);
|
||||
const closest = water[dist2.indexOf(Math.min.apply(Math, dist2))];
|
||||
|
||||
cells.haven[i] = closest;
|
||||
cells.harbor[i] = water.length;
|
||||
}
|
||||
|
||||
function defineOceanGroup(number) {
|
||||
if (number > grid.cells.i.length / 25) return "ocean";
|
||||
if (number > grid.cells.i.length / 100) return "sea";
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ window.Cultures = (function () {
|
|||
const colors = getColors(count);
|
||||
const emblemShape = document.getElementById("emblemShape").value;
|
||||
|
||||
console.log("--- c1 ---", Math.random());
|
||||
|
||||
const codes = [];
|
||||
|
||||
cultures.forEach(function (c, i) {
|
||||
|
|
@ -71,28 +73,40 @@ window.Cultures = (function () {
|
|||
return;
|
||||
}
|
||||
|
||||
const cell = (c.center = placeCenter(c.sort ? c.sort : i => cells.s[i]));
|
||||
centers.add(cells.p[cell]);
|
||||
console.log(JSON.stringify(c, null, 2));
|
||||
console.log(Array.from(pack.cells.s).join());
|
||||
|
||||
const sortingFn = c.sort ? c.sort : i => cells.s[i];
|
||||
const center = placeCenter(sortingFn);
|
||||
|
||||
console.log("--- c2-1 ---", i, Math.random());
|
||||
centers.add(cells.p[center]);
|
||||
c.center = center;
|
||||
c.i = newId;
|
||||
delete c.odd;
|
||||
delete c.sort;
|
||||
c.color = colors[i];
|
||||
c.type = defineCultureType(cell);
|
||||
console.log("--- c2-2 ---", i, Math.random());
|
||||
c.type = defineCultureType(center);
|
||||
console.log("--- c2-3 ---", i, Math.random());
|
||||
c.expansionism = defineCultureExpansionism(c.type);
|
||||
c.origins = [0];
|
||||
c.code = abbreviate(c.name, codes);
|
||||
codes.push(c.code);
|
||||
cultureIds[cell] = newId;
|
||||
cultureIds[center] = newId;
|
||||
if (emblemShape === "random") c.shield = getRandomShield();
|
||||
console.log("--- c2-4 ---", i, Math.random());
|
||||
});
|
||||
|
||||
console.log("--- c3 ---", Math.random());
|
||||
|
||||
cells.culture = cultureIds;
|
||||
|
||||
function placeCenter(v) {
|
||||
function placeCenter(sortingFn) {
|
||||
let spacing = (graphWidth + graphHeight) / 2 / count;
|
||||
const MAX_ATTEMPTS = 100;
|
||||
|
||||
const sorted = [...populated].sort((a, b) => v(b) - v(a));
|
||||
const sorted = [...populated].sort((a, b) => sortingFn(b) - sortingFn(a));
|
||||
const max = Math.floor(sorted.length / 2);
|
||||
|
||||
let cellId = 0;
|
||||
|
|
|
|||
|
|
@ -268,12 +268,14 @@ function drawHeightmap() {
|
|||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
// render paths
|
||||
for (const h of d3.range(0, 101)) {
|
||||
const group = h < 20 ? ocean : land;
|
||||
for (const height of d3.range(0, 101)) {
|
||||
const group = height < 20 ? ocean : land;
|
||||
const scheme = getColorScheme(group.attr("scheme"));
|
||||
|
||||
if (h === 0 && renderOceanCells) {
|
||||
if (height === 0 && renderOceanCells) {
|
||||
// draw base ocean layer
|
||||
group
|
||||
.append("rect")
|
||||
|
|
@ -284,7 +286,7 @@ function drawHeightmap() {
|
|||
.attr("fill", scheme(1));
|
||||
}
|
||||
|
||||
if (h === 20) {
|
||||
if (height === 20) {
|
||||
// draw base land layer
|
||||
group
|
||||
.append("rect")
|
||||
|
|
@ -295,21 +297,21 @@ function drawHeightmap() {
|
|||
.attr("fill", scheme(0.8));
|
||||
}
|
||||
|
||||
if (paths[h] && paths[h].length >= 10) {
|
||||
if (paths[height] && paths[height].length >= 10) {
|
||||
const curve = group.attr("curve") || "curveBasisClosed";
|
||||
lineGen.curve(d3[curve]);
|
||||
const terracing = group.attr("terracing") / 10 || 0; // shifted darker layer for pseudo-3d effect
|
||||
const color = getColor(h, scheme);
|
||||
const terracing = group.attr("terracing") / 10 || 0;
|
||||
const color = getColor(height, scheme);
|
||||
|
||||
if (terracing && h >= 20) {
|
||||
land
|
||||
if (terracing) {
|
||||
group
|
||||
.append("path")
|
||||
.attr("d", paths[h])
|
||||
.attr("d", paths[height])
|
||||
.attr("transform", "translate(.7,1.4)")
|
||||
.attr("fill", d3.color(color).darker(terracing))
|
||||
.attr("data-height", h);
|
||||
.attr("data-height", height);
|
||||
}
|
||||
group.append("path").attr("d", paths[h]).attr("fill", color).attr("data-height", h);
|
||||
group.append("path").attr("d", paths[height]).attr("fill", color).attr("data-height", height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ function editStyle(element, group) {
|
|||
|
||||
styleElementSelect.classList.add("glow");
|
||||
if (group) styleGroupSelect.classList.add("glow");
|
||||
|
||||
setTimeout(() => {
|
||||
styleElementSelect.classList.remove("glow");
|
||||
if (group) styleGroupSelect.classList.remove("glow");
|
||||
|
|
@ -81,10 +82,10 @@ function selectStyleElement() {
|
|||
styleIsOff.style.display = isLayerOff ? "block" : "none";
|
||||
|
||||
// active group element
|
||||
const group = styleGroupSelect.value;
|
||||
if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders"].includes(styleElement)) {
|
||||
const gEl = group && el.select("#" + group);
|
||||
el = group && gEl.size() ? gEl : el.select("g");
|
||||
if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders", "terrs"].includes(styleElement)) {
|
||||
const group = styleGroupSelect.value;
|
||||
const defaultGroupSelector = styleElement === "terrs" ? "#landHeights" : "g";
|
||||
el = group && el.select("#" + group).size() ? el.select("#" + group) : el.select(defaultGroupSelector);
|
||||
}
|
||||
|
||||
// opacity
|
||||
|
|
@ -170,11 +171,14 @@ function selectStyleElement() {
|
|||
|
||||
if (styleElement === "terrs") {
|
||||
styleHeightmap.style.display = "block";
|
||||
styleHeightmapScheme.value = terrs.attr("scheme");
|
||||
styleHeightmapTerracingInput.value = styleHeightmapTerracingOutput.value = terrs.attr("terracing");
|
||||
styleHeightmapSkipInput.value = styleHeightmapSkipOutput.value = terrs.attr("skip");
|
||||
styleHeightmapSimplificationInput.value = styleHeightmapSimplificationOutput.value = terrs.attr("relax");
|
||||
styleHeightmapCurve.value = terrs.attr("curve");
|
||||
styleHeightmapRenderOceanOption.style.display = el.attr("id") === "oceanHeights" ? "block" : "none";
|
||||
styleHeightmapRenderOcean.checked = +el.attr("data-render");
|
||||
|
||||
styleHeightmapScheme.value = el.attr("scheme");
|
||||
styleHeightmapTerracingInput.value = styleHeightmapTerracingOutput.value = el.attr("terracing");
|
||||
styleHeightmapSkipInput.value = styleHeightmapSkipOutput.value = el.attr("skip");
|
||||
styleHeightmapSimplificationInput.value = styleHeightmapSimplificationOutput.value = el.attr("relax");
|
||||
styleHeightmapCurve.value = el.attr("curve");
|
||||
}
|
||||
|
||||
if (styleElement === "markers") {
|
||||
|
|
@ -336,7 +340,7 @@ function selectStyleElement() {
|
|||
|
||||
// update group options
|
||||
styleGroupSelect.options.length = 0; // remove all options
|
||||
if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders"].includes(styleElement)) {
|
||||
if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders", "terrs"].includes(styleElement)) {
|
||||
const groups = byId(styleElement).querySelectorAll("g");
|
||||
groups.forEach(el => {
|
||||
if (el.id === "burgLabels") return;
|
||||
|
|
@ -519,18 +523,16 @@ outlineLayers.addEventListener("change", function () {
|
|||
});
|
||||
|
||||
styleHeightmapScheme.addEventListener("change", function () {
|
||||
terrs.attr("scheme", this.value);
|
||||
getEl().attr("scheme", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
openCreateHeightmapSchemeButton.addEventListener("click", function () {
|
||||
// start with current scheme
|
||||
this.dataset.stops = terrs.attr("scheme").startsWith("#")
|
||||
? terrs.attr("scheme")
|
||||
: (function () {
|
||||
const scheme = heightmapColorSchemes[terrs.attr("scheme")];
|
||||
return [0, 0.25, 0.5, 0.75, 1].map(scheme).map(toHEX).join(",");
|
||||
})();
|
||||
const scheme = getEl().attr("scheme");
|
||||
this.dataset.stops = scheme.startsWith("#")
|
||||
? scheme
|
||||
: (() => [0, 0.25, 0.5, 0.75, 1].map(heightmapColorSchemes[scheme]).map(toHEX).join(","))();
|
||||
|
||||
// render dialog base structure
|
||||
alertMessage.innerHTML = /* html */ `<div>
|
||||
|
|
@ -622,7 +624,7 @@ openCreateHeightmapSchemeButton.addEventListener("click", function () {
|
|||
if (stops in heightmapColorSchemes) return tip("This scheme already exists", false, "error");
|
||||
|
||||
addCustomColorScheme(stops);
|
||||
terrs.attr("scheme", stops);
|
||||
getEl().attr("scheme", stops);
|
||||
drawHeightmap();
|
||||
|
||||
handleClose();
|
||||
|
|
@ -644,23 +646,28 @@ openCreateHeightmapSchemeButton.addEventListener("click", function () {
|
|||
});
|
||||
});
|
||||
|
||||
styleHeightmapRenderOcean.addEventListener("change", function () {
|
||||
getEl().attr("data-render", +this.checked);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
styleHeightmapTerracingInput.addEventListener("input", function () {
|
||||
terrs.attr("terracing", this.value);
|
||||
getEl().attr("terracing", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
styleHeightmapSkipInput.addEventListener("input", function () {
|
||||
terrs.attr("skip", this.value);
|
||||
getEl().attr("skip", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
styleHeightmapSimplificationInput.addEventListener("input", function () {
|
||||
terrs.attr("relax", this.value);
|
||||
getEl().attr("relax", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
styleHeightmapCurve.addEventListener("change", function () {
|
||||
terrs.attr("curve", this.value);
|
||||
getEl().attr("curve", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -239,7 +239,18 @@ function addStylePreset() {
|
|||
"#oceanLayers": ["filter", "layers"],
|
||||
"#oceanBase": ["fill"],
|
||||
"#oceanicPattern": ["href", "opacity"],
|
||||
"#terrs": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"],
|
||||
"#terrs #oceanHeights": [
|
||||
"data-render",
|
||||
"opacity",
|
||||
"scheme",
|
||||
"terracing",
|
||||
"skip",
|
||||
"relax",
|
||||
"curve",
|
||||
"filter",
|
||||
"mask"
|
||||
],
|
||||
"#terrs #landHeights": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"],
|
||||
"#legend": [
|
||||
"data-size",
|
||||
"font-size",
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ function drawCellsValue(data) {
|
|||
function drawPolygons(data) {
|
||||
const max = d3.max(data),
|
||||
min = d3.min(data),
|
||||
scheme = getColorScheme(terrs.attr("scheme"));
|
||||
scheme = getColorScheme(terrs.select("#landHeights").attr("scheme"));
|
||||
data = data.map(d => 1 - normalize(d, min, max));
|
||||
|
||||
debug.selectAll("polygon").remove();
|
||||
|
|
@ -338,7 +338,7 @@ function drawPolygons(data) {
|
|||
.data(data)
|
||||
.enter()
|
||||
.append("polygon")
|
||||
.attr("points", (d, i) => getPackPolygon(i))
|
||||
.attr("points", (d, i) => getGridPolygon(i))
|
||||
.attr("fill", d => scheme(d))
|
||||
.attr("stroke", d => scheme(d));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue