diff --git a/index.css b/index.css
index 05baf61c..c94d83ad 100644
--- a/index.css
+++ b/index.css
@@ -2309,11 +2309,8 @@ svg.button {
#mapOverlay {
position: absolute;
+ inset: 0;
display: flex;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
align-items: center;
justify-content: center;
z-index: 10;
diff --git a/index.html b/index.html
index b5bae9ca..892120d5 100644
--- a/index.html
+++ b/index.html
@@ -24,29 +24,21 @@
font-size: 10px;
overflow: hidden;
}
- #map {
- position: absolute;
+ #loading > * {
+ pointer-events: none;
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
}
- #initial {
+ #loading > #loading-rose > use {
fill: none;
stroke: black;
- pointer-events: none;
- }
- #init-rose {
opacity: 0.7;
transform-origin: center;
- opacity: 0.7;
- animation: 20s infinite spin;
+ animation: 20s linear 0s infinite spin;
}
- @keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(359deg);
- }
- }
- #loading {
+ #loading > #loading-typography {
opacity: 1;
font-size: 11px;
color: #fff5da;
@@ -54,11 +46,6 @@
text-shadow: 0px 1px 4px #4c3a35;
width: 80%;
max-width: 600px;
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- pointer-events: none;
}
#loading-text {
font-size: 1.8em;
@@ -106,6 +93,14 @@
opacity: 0.1;
}
}
+ @keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(359deg);
+ }
+ }
@@ -116,6 +111,7 @@
id="map"
width="100%"
height="100%"
+ style="position: absolute"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
@@ -267,18 +263,24 @@
-
-
-
-
-
diff --git a/main.js b/main.js
index e1d59b56..cd5f0244 100644
--- a/main.js
+++ b/main.js
@@ -201,9 +201,6 @@ document.addEventListener("DOMContentLoaded", async () => {
}
}
});
-
- d3.select("#loading-text").transition().duration(1000).style("opacity", 0);
- d3.select("#init-rose").transition().duration(4000).style("opacity", 0);
} else {
hideLoading();
await checkLoadParameters();
@@ -213,14 +210,12 @@ document.addEventListener("DOMContentLoaded", async () => {
function hideLoading() {
d3.select("#loading").transition().duration(4000).style("opacity", 0);
- d3.select("#initial").transition().duration(4000).attr("opacity", 0);
d3.select("#optionsContainer").transition().duration(3000).style("opacity", 1);
d3.select("#tooltip").transition().duration(4000).style("opacity", 1);
}
function showLoading() {
d3.select("#loading").transition().duration(200).style("opacity", 1);
- d3.select("#initial").transition().duration(200).attr("opacity", 1);
d3.select("#optionsContainer").transition().duration(100).style("opacity", 0);
d3.select("#tooltip").transition().duration(200).style("opacity", 0);
}
diff --git a/modules/dynamic/heightmap-selection.js b/modules/dynamic/heightmap-selection.js
index 4dc631d7..3f38cf5d 100644
--- a/modules/dynamic/heightmap-selection.js
+++ b/modules/dynamic/heightmap-selection.js
@@ -89,10 +89,23 @@ function appendStyleSheet() {
.heightmap-selection_container {
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
grid-gap: 8px;
}
+ @media (max-width: 600px) {
+ .heightmap-selection_container {
+ grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
+ grid-gap: 4px;
+ }
+ }
+
+ @media (min-width: 2000px) {
+ .heightmap-selection_container {
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ }
+ }
+
.heightmap-selection article {
padding: 4px;
border-radius: 8px;
@@ -128,13 +141,9 @@ function appendStyleSheet() {
.heightmap-selection article > img {
width: 100%;
- aspect-ratio: 16/9;
+ aspect-ratio: ${graphWidth}/${graphHeight};
border-radius: 8px;
- object-fit: cover;
- }
-
- img.heightmap-selection_precreated {
- filter: contrast(1.3);
+ object-fit: fill;
}
`;
@@ -153,21 +162,23 @@ function insertEditorHtml() {
const dataUrl = drawHeights(heights);
return /* html */ `
-
-
- ${name}
-
-
- `;
+

+
+ ${name}
+
+
+ `;
})
.join("");
const heightmapsHtml = heightmaps
.map(({id, name}) => {
+ drawPrecreatedHeightmap(id);
+
return /* html */ `
-
- ${name}
- `;
+
![${name}]()
+
${name}
+ `;
})
.join("");
@@ -222,15 +233,19 @@ function drawHeights(heights) {
canvas.height = grid.cellsY;
const ctx = canvas.getContext("2d");
const imageData = ctx.createImageData(grid.cellsX, grid.cellsY);
+ const scheme = getColorScheme();
+ const waterColor = scheme(1);
- heights.forEach((height, i) => {
- const h = height < 20 ? Math.max(height / 1.5, 0) : height;
- const v = (h / 100) * 255;
- imageData.data[i * 4] = v;
- imageData.data[i * 4 + 1] = v;
- imageData.data[i * 4 + 2] = v;
- imageData.data[i * 4 + 3] = 255;
- });
+ for (let i = 0; i < heights.length; i++) {
+ const color = heights[i] < 20 ? waterColor : scheme(1 - heights[i] / 100);
+ const {r, g, b} = d3.color(color);
+
+ const n = i * 4;
+ imageData.data[n] = r;
+ imageData.data[n + 1] = g;
+ imageData.data[n + 2] = b;
+ imageData.data[n + 3] = 255;
+ }
ctx.putImageData(imageData, 0, 0);
return canvas.toDataURL("image/png");
@@ -243,6 +258,13 @@ function generateHeightmap(id) {
return heights;
}
+async function drawPrecreatedHeightmap(id) {
+ const heights = await HeightmapGenerator.fromPrecreated(id);
+ const dataUrl = drawHeights(heights);
+ const article = byId("heightmapSelection").querySelector(`[data-id="${id}"]`);
+ article.querySelector("img").src = dataUrl;
+}
+
function regeneratePreview(article, id) {
const seed = generateSeed();
article.dataset.seed = seed;
diff --git a/modules/heightmap-generator.js b/modules/heightmap-generator.js
index d8ec192d..71f2ea2d 100644
--- a/modules/heightmap-generator.js
+++ b/modules/heightmap-generator.js
@@ -24,7 +24,6 @@ window.HeightmapGenerator = (function () {
const fromPrecreated = id => {
return new Promise(resolve => {
- TIME && console.time("defineHeightmap");
// create canvas where 1px corresponts to a cell
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
@@ -38,31 +37,26 @@ window.HeightmapGenerator = (function () {
img.onload = () => {
ctx.drawImage(img, 0, 0, cellsX, cellsY);
const imageData = ctx.getImageData(0, 0, cellsX, cellsY);
- assignColorsToHeight(imageData.data);
+ const heights = getHeightsFromImageData(imageData.data);
canvas.remove();
img.remove();
-
- grid.cells.h = heights;
- cleanup();
- TIME && console.timeEnd("defineHeightmap");
- resolve();
+ resolve(heights);
};
});
};
const generate = async function () {
Math.random = aleaPRNG(seed);
- resetHeights();
- const id = byId("templateInput").value;
- if (HeightmapTemplates[id]) {
- TIME && console.time("generateHeightmap");
- grid.cells.h = fromTemplate(id);
- cleanup();
- TIME && console.timeEnd("generateHeightmap");
- } else {
- return fromPrecreated(id);
- }
+ TIME && console.time("defineHeightmap");
+ const id = byId("templateInput").value;
+ resetHeights();
+
+ const isTemplate = id in HeightmapTemplates;
+ grid.cells.h = isTemplate ? fromTemplate(id) : await fromPrecreated(id);
+
+ cleanup();
+ TIME && console.timeEnd("defineHeightmap");
};
function addStep(tool, a2, a3, a4, a5) {
@@ -491,13 +485,32 @@ window.HeightmapGenerator = (function () {
return rand(min * length, max * length);
}
- function assignColorsToHeight(imageData) {
- for (let i = 0; i < grid.cells.i.length; i++) {
+ function getHeightsFromImageData(imageData) {
+ const heights = new Uint8Array(grid.points.length);
+ for (let i = 0; i < heights.length; i++) {
const lightness = imageData[i * 4] / 255;
const powered = lightness < 0.2 ? lightness : 0.2 + (lightness - 0.2) ** 0.8;
heights[i] = minmax(Math.floor(powered * 100), 0, 100);
}
+ return heights;
}
- return {setHeights, resetHeights, getHeights, cleanup, generate, fromTemplate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify, mask, invert};
+ return {
+ setHeights,
+ resetHeights,
+ getHeights,
+ cleanup,
+ generate,
+ fromTemplate,
+ fromPrecreated,
+ addHill,
+ addRange,
+ addTrough,
+ addStrait,
+ addPit,
+ smooth,
+ modify,
+ mask,
+ invert
+ };
})();
diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js
index 0ade51b7..f30004c1 100644
--- a/modules/ui/heightmap-editor.js
+++ b/modules/ui/heightmap-editor.js
@@ -1369,10 +1369,12 @@ function editHeightmap() {
grid.cells.h.forEach((height, i) => {
const h = height < 20 ? Math.max(height / 1.5, 0) : height;
const v = (h / 100) * 255;
- imageData.data[i * 4] = v;
- imageData.data[i * 4 + 1] = v;
- imageData.data[i * 4 + 2] = v;
- imageData.data[i * 4 + 3] = 255;
+
+ const n = i * 4;
+ imageData.data[n] = v;
+ imageData.data[n + 1] = v;
+ imageData.data[n + 2] = v;
+ imageData.data[n + 3] = 255;
});
ctx.putImageData(imageData, 0, 0);
diff --git a/modules/ui/layers.js b/modules/ui/layers.js
index 3e53c2f2..9ff93af4 100644
--- a/modules/ui/layers.js
+++ b/modules/ui/layers.js
@@ -151,9 +151,9 @@ function toggleHeight(event) {
function drawHeightmap() {
TIME && console.time("drawHeightmap");
terrs.selectAll("*").remove();
- const cells = pack.cells,
- vertices = pack.vertices,
- n = cells.i.length;
+
+ const {cells, vertices} = pack;
+ const n = cells.i.length;
const used = new Uint8Array(cells.i.length);
const paths = new Array(101).fill("");
@@ -161,6 +161,7 @@ function drawHeightmap() {
const terracing = terrs.attr("terracing") / 10; // add additional shifted darker layer for pseudo-3d effect
const skip = +terrs.attr("skip") + 1;
const simplification = +terrs.attr("relax");
+
switch (+terrs.attr("curve")) {
case 0:
lineGen.curve(d3.curveBasisClosed);