diff --git a/index.html b/index.html
index 7405ff94..c0a4abc9 100644
--- a/index.html
+++ b/index.html
@@ -1310,9 +1310,9 @@
Line style |
|
diff --git a/main.js b/main.js
index 6db57f59..c065fe51 100644
--- a/main.js
+++ b/main.js
@@ -92,16 +92,19 @@ let fogging = viewbox
let ruler = viewbox.append("g").attr("id", "ruler").style("display", "none");
let debug = viewbox.append("g").attr("id", "debug");
-// lake and coast groups
lakes.append("g").attr("id", "freshwater");
lakes.append("g").attr("id", "salt");
lakes.append("g").attr("id", "sinkhole");
lakes.append("g").attr("id", "frozen");
lakes.append("g").attr("id", "lava");
lakes.append("g").attr("id", "dry");
+
coastline.append("g").attr("id", "sea_island");
coastline.append("g").attr("id", "lake_island");
+terrs.append("g").attr("id", "oceanHeights");
+terrs.append("g").attr("id", "landHeights");
+
labels.append("g").attr("id", "states");
labels.append("g").attr("id", "addedLabels");
@@ -135,7 +138,6 @@ fogging
.attr("filter", "url(#splotch)");
// assign events separately as not a viewbox child
-scaleBar.on("mousemove", () => tip("Click to open Units Editor")).on("click", () => editUnits());
legend
.on("mousemove", () => tip("Drag to change the position. Click to hide the legend"))
.on("click", () => clearLegend());
diff --git a/modules/dynamic/auto-update.js b/modules/dynamic/auto-update.js
index 5dbebb60..124e32ff 100644
--- a/modules/dynamic/auto-update.js
+++ b/modules/dynamic/auto-update.js
@@ -736,4 +736,46 @@ export function resolveVersionConflicts(version) {
.style("display", "none");
vignette.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
}
+
+ if (version < 1.96) {
+ // v1.96 added ocean rendering for heightmap
+ terrs.selectAll("*").remove();
+
+ const opacity = terrs.attr("opacity");
+ const filter = terrs.attr("filter");
+ const scheme = terrs.attr("scheme");
+ const terracing = terrs.attr("terracing");
+ const skip = terrs.attr("skip");
+ const relax = terrs.attr("relax");
+
+ const curveTypes = {0: "curveBasisClosed", 1: "curveLinear", 2: "curveStep"};
+ const curve = curveTypes[terrs.attr("curve")] || "curveBasisClosed";
+
+ terrs.attr("scheme", null).attr("terracing", null).attr("skip", null).attr("relax", null).attr("curve", null);
+
+ terrs
+ .append("g")
+ .attr("id", "oceanHeights")
+ .attr("data-render", 0)
+ .attr("opacity", opacity)
+ .attr("filter", filter)
+ .attr("scheme", scheme)
+ .attr("terracing", 0)
+ .attr("skip", 0)
+ .attr("relax", 1)
+ .attr("curve", curve);
+ terrs
+ .append("g")
+ .attr("id", "landHeights")
+ .attr("opacity", opacity)
+ .attr("scheme", scheme)
+ .attr("filter", filter)
+ .attr("terracing", terracing)
+ .attr("skip", skip)
+ .attr("relax", relax)
+ .attr("curve", curve)
+ .attr("mask", "url(#land)");
+
+ if (layerIsOn("toggleHeight")) drawHeightmap();
+ }
}
diff --git a/modules/io/load.js b/modules/io/load.js
index 769c7d6a..60741753 100644
--- a/modules/io/load.js
+++ b/modules/io/load.js
@@ -468,10 +468,10 @@ async function parseLoadedData(data) {
{
// add custom heightmap color scheme if any
- const scheme = terrs.attr("scheme");
- if (!(scheme in heightmapColorSchemes)) {
- addCustomColorScheme(scheme);
- }
+ const oceanScheme = terrs.select("#oceanHeights").attr("scheme");
+ const landScheme = terrs.select("#landHeights").attr("scheme");
+ if (!(oceanScheme in heightmapColorSchemes)) addCustomColorScheme(oceanScheme);
+ if (!(landScheme in heightmapColorSchemes)) addCustomColorScheme(landScheme);
}
{
diff --git a/modules/ui/layers.js b/modules/ui/layers.js
index 945f3df9..e20fe0cf 100644
--- a/modules/ui/layers.js
+++ b/modules/ui/layers.js
@@ -188,92 +188,134 @@ function restoreLayers() {
}
function toggleHeight(event) {
- if (customization === 1) {
- tip("You cannot turn off the layer when heightmap is in edit mode", false, "error");
- return;
- }
+ if (customization === 1) return tip("You cannot turn off the layer when heightmap is in edit mode", false, "error");
- if (!terrs.selectAll("*").size()) {
+ const children = terrs.selectAll("#oceanHeights > *, #landHeights > *");
+ if (!children.size()) {
turnButtonOn("toggleHeight");
drawHeightmap();
if (event && isCtrlClick(event)) editStyle("terrs");
} else {
- if (event && isCtrlClick(event)) {
- editStyle("terrs");
- return;
- }
+ if (event && isCtrlClick(event)) return editStyle("terrs");
turnButtonOff("toggleHeight");
- terrs.selectAll("*").remove();
+ children.remove();
}
}
function drawHeightmap() {
TIME && console.time("drawHeightmap");
- terrs.selectAll("*").remove();
- const {cells, vertices} = pack;
- const n = cells.i.length;
- const used = new Uint8Array(cells.i.length);
- const paths = new Array(101).fill("");
+ const ocean = terrs.select("#oceanHeights");
+ const land = terrs.select("#landHeights");
- const scheme = getColorScheme(terrs.attr("scheme"));
- 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");
+ ocean.selectAll("*").remove();
+ land.selectAll("*").remove();
- switch (+terrs.attr("curve")) {
- case 0:
- lineGen.curve(d3.curveBasisClosed);
- break;
- case 1:
- lineGen.curve(d3.curveLinear);
- break;
- case 2:
- lineGen.curve(d3.curveStep);
- break;
- default:
- lineGen.curve(d3.curveBasisClosed);
+ const paths = new Array(101);
+
+ // ocean cells
+ const renderOceanCells = Boolean(+ocean.attr("data-render"));
+ if (renderOceanCells) {
+ const {cells, vertices} = grid;
+ const used = new Uint8Array(cells.i.length);
+
+ const skip = +ocean.attr("skip") + 1 || 1;
+ const relax = +ocean.attr("relax") || 0;
+
+ let currentLayer = 0;
+ const heights = cells.i.sort((a, b) => cells.h[a] - cells.h[b]);
+ for (const i of heights) {
+ const h = cells.h[i];
+ if (h > currentLayer) currentLayer += skip;
+ if (h < currentLayer) continue;
+ if (currentLayer >= 20) break;
+ if (used[i]) continue; // already marked
+ const onborder = cells.c[i].some(n => cells.h[n] < h);
+ if (!onborder) continue;
+ const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.h[i] < h));
+ const chain = connectVertices(cells, vertices, vertex, h, used);
+ if (chain.length < 3) continue;
+ const points = simplifyLine(chain, relax).map(v => vertices.p[v]);
+ if (!paths[h]) paths[h] = "";
+ paths[h] += round(lineGen(points));
+ }
}
- let currentLayer = 20;
- const heights = cells.i.sort((a, b) => cells.h[a] - cells.h[b]);
- for (const i of heights) {
- const h = cells.h[i];
- if (h > currentLayer) currentLayer += skip;
- if (currentLayer > 100) break; // no layers possible with height > 100
- if (h < currentLayer) continue;
- if (used[i]) continue; // already marked
- const onborder = cells.c[i].some(n => cells.h[n] < h);
- if (!onborder) continue;
- const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.h[i] < h));
- const chain = connectVertices(vertex, h);
- if (chain.length < 3) continue;
- const points = simplifyLine(chain).map(v => vertices.p[v]);
- paths[h] += round(lineGen(points));
+ // land cells
+ {
+ const {cells, vertices} = pack;
+ const used = new Uint8Array(cells.i.length);
+
+ const skip = +land.attr("skip") + 1 || 1;
+ const relax = +land.attr("relax") || 0;
+
+ let currentLayer = 20;
+ const heights = cells.i.sort((a, b) => cells.h[a] - cells.h[b]);
+ for (const i of heights) {
+ const h = cells.h[i];
+ if (h > currentLayer) currentLayer += skip;
+ if (h < currentLayer) continue;
+ if (currentLayer > 100) break; // no layers possible with height > 100
+ if (used[i]) continue; // already marked
+ const onborder = cells.c[i].some(n => cells.h[n] < h);
+ if (!onborder) continue;
+ const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.h[i] < h));
+ const chain = connectVertices(cells, vertices, vertex, h, used);
+ if (chain.length < 3) continue;
+ const points = simplifyLine(chain, relax).map(v => vertices.p[v]);
+ if (!paths[h]) paths[h] = "";
+ paths[h] += round(lineGen(points));
+ }
}
- terrs
- .append("rect")
- .attr("x", 0)
- .attr("y", 0)
- .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);
- if (terracing)
- terrs
- .append("path")
- .attr("d", paths[i])
- .attr("transform", "translate(.7,1.4)")
- .attr("fill", d3.color(color).darker(terracing))
- .attr("data-height", i);
- terrs.append("path").attr("d", paths[i]).attr("fill", color).attr("data-height", i);
+ // render paths
+ for (const h of d3.range(0, 101)) {
+ const group = h < 20 ? ocean : land;
+ const scheme = getColorScheme(group.attr("scheme"));
+
+ if (h === 0 && renderOceanCells) {
+ // draw base ocean layer
+ group
+ .append("rect")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", graphWidth)
+ .attr("height", graphHeight)
+ .attr("fill", scheme(1));
+ }
+
+ if (h === 20) {
+ // draw base land layer
+ group
+ .append("rect")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", graphWidth)
+ .attr("height", graphHeight)
+ .attr("fill", scheme(0.8));
+ }
+
+ if (paths[h] && paths[h].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);
+
+ if (terracing && h >= 20) {
+ land
+ .append("path")
+ .attr("d", paths[h])
+ .attr("transform", "translate(.7,1.4)")
+ .attr("fill", d3.color(color).darker(terracing))
+ .attr("data-height", h);
+ }
+ group.append("path").attr("d", paths[h]).attr("fill", color).attr("data-height", h);
+ }
}
// connect vertices to chain
- function connectVertices(start, h) {
+ function connectVertices(cells, vertices, start, h, used) {
+ const n = cells.i.length;
const chain = []; // vertices chain to form a path
for (let i = 0, current = start; i === 0 || (current !== start && i < 20000); i++) {
const prev = chain[chain.length - 1]; // previous vertex in chain
@@ -295,7 +337,7 @@ function drawHeightmap() {
return chain;
}
- function simplifyLine(chain) {
+ function simplifyLine(chain, simplification) {
if (!simplification) return chain;
const n = simplification + 1; // filter each nth element
return chain.filter((d, i) => i % n === 0);
diff --git a/styles/ancient.json b/styles/ancient.json
index e57f2d87..7d675a3f 100644
--- a/styles/ancient.json
+++ b/styles/ancient.json
@@ -292,7 +292,9 @@
"terracing": 0,
"skip": 2,
"relax": 1,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": "url(#blur3)",
"mask": "url(#land)"
},
diff --git a/styles/atlas.json b/styles/atlas.json
index 6366e5f4..79ba9a87 100644
--- a/styles/atlas.json
+++ b/styles/atlas.json
@@ -292,7 +292,9 @@
"terracing": 0,
"skip": 0,
"relax": 0,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": null,
"mask": "url(#land)"
},
diff --git a/styles/clean.json b/styles/clean.json
index ba3685a4..53419fba 100644
--- a/styles/clean.json
+++ b/styles/clean.json
@@ -294,7 +294,9 @@
"terracing": 0,
"skip": 5,
"relax": 0,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": null,
"mask": "url(#land)"
},
diff --git a/styles/cyberpunk.json b/styles/cyberpunk.json
index e69f5c43..c8b99264 100644
--- a/styles/cyberpunk.json
+++ b/styles/cyberpunk.json
@@ -292,7 +292,9 @@
"terracing": 6,
"skip": 0,
"relax": 2,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": "",
"mask": "url(#land)"
},
diff --git a/styles/default.json b/styles/default.json
index e2cf0e6d..45b28bc5 100644
--- a/styles/default.json
+++ b/styles/default.json
@@ -286,13 +286,24 @@
"href": "./images/pattern1.png",
"opacity": 0.2
},
- "#terrs": {
- "opacity": null,
+ "#terrs > #oceanHeights": {
+ "data-render": 0,
+ "opacity": 1,
+ "scheme": "bright",
+ "terracing": 0,
+ "skip": 0,
+ "relax": 1,
+ "curve": "curveBasisClosed",
+ "filter": null,
+ "mask": null
+ },
+ "#terrs > #landHeights": {
+ "opacity": 1,
"scheme": "bright",
"terracing": 0,
"skip": 5,
"relax": 0,
- "curve": 0,
+ "curve": "curveBasisClosed",
"filter": null,
"mask": "url(#land)"
},
diff --git a/styles/gloom.json b/styles/gloom.json
index ec4084d2..80e4f196 100644
--- a/styles/gloom.json
+++ b/styles/gloom.json
@@ -294,7 +294,9 @@
"terracing": 2,
"skip": 1,
"relax": 2,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": "url(#filter-grayscale)",
"mask": "url(#land)"
},
diff --git a/styles/light.json b/styles/light.json
index 978a039b..43f7acd4 100644
--- a/styles/light.json
+++ b/styles/light.json
@@ -292,7 +292,9 @@
"terracing": 10,
"skip": 5,
"relax": 0,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": "url(#turbulence)",
"mask": "url(#land)"
},
diff --git a/styles/monochrome.json b/styles/monochrome.json
index 23ff7a48..7f93bb43 100644
--- a/styles/monochrome.json
+++ b/styles/monochrome.json
@@ -287,7 +287,9 @@
"terracing": 0,
"skip": 5,
"relax": 0,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": "url(#blur3)",
"mask": "url(#land)"
},
diff --git a/styles/night.json b/styles/night.json
index 99df7867..83b278e2 100644
--- a/styles/night.json
+++ b/styles/night.json
@@ -292,7 +292,9 @@
"terracing": 0,
"skip": 10,
"relax": 0,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": "url(#blurFilter)",
"mask": "url(#land)"
},
diff --git a/styles/pale.json b/styles/pale.json
index 4bddc4d2..4267f47d 100644
--- a/styles/pale.json
+++ b/styles/pale.json
@@ -292,7 +292,9 @@
"terracing": 0,
"skip": 2,
"relax": 1,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": "",
"mask": "url(#land)"
},
diff --git a/styles/watercolor.json b/styles/watercolor.json
index 01763c1b..fc0201bc 100644
--- a/styles/watercolor.json
+++ b/styles/watercolor.json
@@ -292,7 +292,9 @@
"terracing": 0,
"skip": 5,
"relax": 1,
- "curve": 0,
+ "curve": "curveBasisClosed",
+ "skipOcean": 0,
+ "relaxOcean": 1,
"filter": null,
"mask": "url(#land)"
},
diff --git a/versioning.js b/versioning.js
index 4e4fb075..5bf367ac 100644
--- a/versioning.js
+++ b/versioning.js
@@ -1,7 +1,7 @@
"use strict";
// version and caching control
-const version = "1.95.04"; // generator version, update each time
+const version = "1.96.00"; // generator version, update each time
{
document.title += " v" + version;
@@ -28,6 +28,7 @@ const version = "1.95.04"; // generator version, update each time
Latest changes:
+ - Ability to render ocean heightmap
- Vignette visual layer and vignette styling options
- Ability to define custom heightmap color scheme
- New style preset Night and new heightmap color schemes