river editor rework - data checking

This commit is contained in:
Azgaar 2021-07-25 01:37:48 +03:00
parent 424980f5be
commit a1425bcb54
8 changed files with 586 additions and 218 deletions

View file

@ -202,6 +202,7 @@ a {
stroke: none; stroke: none;
mask: url(#land); mask: url(#land);
cursor: pointer; cursor: pointer;
fill-rule: nonzero;
} }
#anchors { #anchors {
@ -984,6 +985,24 @@ body button.noicon {
cursor: pointer; cursor: pointer;
} }
#controlCells > .current {
fill: #82c8ff40;
stroke: #82c8ff;
stroke-width: 0.4;
}
#controlCells > .available {
fill: #82ff9b40;
stroke: #82ff9b;
stroke-width: 0.4;
}
#controlCells > .occupied {
fill: #ff828240;
stroke: #ff8282;
stroke-width: 0.4;
}
#vertices > circle { #vertices > circle {
fill: #ff0000; fill: #ff0000;
stroke: #841f1f; stroke: #841f1f;

View file

@ -1644,12 +1644,12 @@
<input id="riverWidth" disabled/> <input id="riverWidth" disabled/>
</div> </div>
<div data-tip="River source width in pixels"> <div data-tip="River additional width. Default value is 0">
<div class="label">Source width:</div> <div class="label">Source width:</div>
<input id="riverSourceWidth" type="number" min=0 max=3 step=.1 /> <input id="riverSourceWidth" type="number" min=0 max=3 step=.1 />
</div> </div>
<div data-tip="River width multiplier"> <div data-tip="River width multiplier. Default value is 1">
<div class="label">Width modifier:</div> <div class="label">Width modifier:</div>
<input id="riverWidthFactor" type="number" min=.1 max=4 step=.1 /> <input id="riverWidthFactor" type="number" min=.1 max=4 step=.1 />
</div> </div>

View file

@ -706,6 +706,14 @@ function parseLoadedData(data) {
statesHalo.attr("opacity", opacity).attr("filter", "blur(5px)"); statesHalo.attr("opacity", opacity).attr("filter", "blur(5px)");
regions.attr("opacity", null).attr("filter", null); regions.attr("opacity", null).attr("filter", null);
} }
if (version < 1.65) {
// v 1.65 changed rivers data
for (const river of pack.rivers) {
river.sourceWidth = 0;
// get points and cells
}
}
})(); })();
void (function checkDataIntegrity() { void (function checkDataIntegrity() {

View file

@ -171,7 +171,7 @@
const meanderedPoints = addMeandering(riverCells); const meanderedPoints = addMeandering(riverCells);
const discharge = cells.fl[mouth]; // m3 in second const discharge = cells.fl[mouth]; // m3 in second
const length = getApproximateLength(meanderedPoints); const length = getApproximateLength(meanderedPoints);
const width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor)); const width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor, 0));
pack.rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth: 0, parent, cells: riverCells}); pack.rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth: 0, parent, cells: riverCells});
} }
@ -441,5 +441,5 @@
return getBasin(parent); return getBasin(parent);
}; };
return {generate, alterHeights, resolveDepressions, addMeandering, getRiverPath, specify, getName, getType, getBasin, getWidth, getOffset, getApproximateLength, remove}; return {generate, alterHeights, resolveDepressions, addMeandering, getRiverPath, specify, getName, getType, getBasin, getWidth, getOffset, getApproximateLength, getRiverPoints, remove};
}); });

View file

@ -6,10 +6,7 @@ restoreDefaultEvents(); // apply default viewbox events on load
// restore default viewbox events // restore default viewbox events
function restoreDefaultEvents() { function restoreDefaultEvents() {
svg.call(zoom); svg.call(zoom);
viewbox.style("cursor", "default") viewbox.style("cursor", "default").on(".drag", null).on("click", clicked).on("touchmove mousemove", moved);
.on(".drag", null)
.on("click", clicked)
.on("touchmove mousemove", moved);
legend.call(d3.drag().on("start", dragLegendBox)); legend.call(d3.drag().on("start", dragLegendBox));
} }
@ -17,12 +14,14 @@ function restoreDefaultEvents() {
function clicked() { function clicked() {
const el = d3.event.target; const el = d3.event.target;
if (!el || !el.parentElement || !el.parentElement.parentElement) return; if (!el || !el.parentElement || !el.parentElement.parentElement) return;
const parent = el.parentElement, grand = parent.parentElement, great = grand.parentElement; const parent = el.parentElement;
const grand = parent.parentElement;
const great = grand.parentElement;
const p = d3.mouse(this); const p = d3.mouse(this);
const i = findCell(p[0], p[1]); const i = findCell(p[0], p[1]);
if (grand.id === "emblems") editEmblem(); if (grand.id === "emblems") editEmblem();
else if (parent.id === "rivers") editRiver(); else if (parent.id === "rivers") editRiver(el.id);
else if (grand.id === "routes") editRoute(); else if (grand.id === "routes") editRoute();
else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel(); else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel();
else if (grand.id === "burgLabels") editBurg(); else if (grand.id === "burgLabels") editBurg();
@ -35,8 +34,7 @@ function clicked() {
else if (pack.cells.t[i] === 1) { else if (pack.cells.t[i] === 1) {
const node = document.getElementById("island_" + pack.cells.f[i]); const node = document.getElementById("island_" + pack.cells.f[i]);
editCoastline(node); editCoastline(node);
} } else if (grand.id === "lakes") editLake();
else if (grand.id === "lakes") editLake();
} }
// clear elSelected variable // clear elSelected variable
@ -51,7 +49,9 @@ function unselect() {
// close all dialogs except stated // close all dialogs except stated
function closeDialogs(except = "#except") { function closeDialogs(except = "#except") {
$(".dialog:visible").not(except).each(function() { $(".dialog:visible")
.not(except)
.each(function () {
$(this).dialog("close"); $(this).dialog("close");
}); });
} }
@ -80,7 +80,9 @@ function fitContent() {
// apply sorting behaviour for lines on Editor header click // apply sorting behaviour for lines on Editor header click
document.querySelectorAll(".sortable").forEach(function (e) { document.querySelectorAll(".sortable").forEach(function (e) {
e.addEventListener("click", function(e) {sortLines(this);}); e.addEventListener("click", function (e) {
sortLines(this);
});
}); });
function sortLines(header) { function sortLines(header) {
@ -90,7 +92,9 @@ function sortLines(header) {
const headers = header.parentNode; const headers = header.parentNode;
headers.querySelectorAll("div.sortable").forEach(e => { headers.querySelectorAll("div.sortable").forEach(e => {
e.classList.forEach(c => {if(c.includes("icon-sort")) e.classList.remove(c);}); e.classList.forEach(c => {
if (c.includes("icon-sort")) e.classList.remove(c);
});
}); });
header.classList.add("icon-sort-" + type + order); header.classList.add("icon-sort-" + type + order);
applySorting(headers); applySorting(headers);
@ -105,16 +109,19 @@ function applySorting(headers) {
const list = headers.nextElementSibling; const list = headers.nextElementSibling;
const lines = Array.from(list.children); const lines = Array.from(list.children);
lines.sort((a, b) => { lines
.sort((a, b) => {
const an = name ? a.dataset[sortby] : +a.dataset[sortby]; const an = name ? a.dataset[sortby] : +a.dataset[sortby];
const bn = name ? b.dataset[sortby] : +b.dataset[sortby]; const bn = name ? b.dataset[sortby] : +b.dataset[sortby];
return (an > bn ? 1 : an < bn ? -1 : 0) * desc; return (an > bn ? 1 : an < bn ? -1 : 0) * desc;
}).forEach(line => list.appendChild(line)); })
.forEach(line => list.appendChild(line));
} }
function addBurg(point) { function addBurg(point) {
const cells = pack.cells; const cells = pack.cells;
const x = rn(point[0], 2), y = rn(point[1], 2); const x = rn(point[0], 2),
y = rn(point[1], 2);
const cell = findCell(x, point[1]); const cell = findCell(x, point[1]);
const i = pack.burgs.length; const i = pack.burgs.length;
const culture = cells.culture[cell]; const culture = cells.culture[cell];
@ -123,11 +130,11 @@ function addBurg(point) {
const feature = cells.f[cell]; const feature = cells.f[cell];
const temple = pack.states[state].form === "Theocracy"; const temple = pack.states[state].form === "Theocracy";
const population = Math.max((cells.s[cell] + cells.road[cell]) / 3 + i / 1000 + cell % 100 / 1000, .1); const population = Math.max((cells.s[cell] + cells.road[cell]) / 3 + i / 1000 + (cell % 100) / 1000, 0.1);
const type = BurgsAndStates.getType(cell, false); const type = BurgsAndStates.getType(cell, false);
// generate emblem // generate emblem
const coa = COA.generate(pack.states[state].coa, .25, null, type); const coa = COA.generate(pack.states[state].coa, 0.25, null, type);
coa.shield = COA.getShield(culture, state); coa.shield = COA.getShield(culture, state);
COArenderer.add("burg", i, coa, x, y); COArenderer.add("burg", i, coa, x, y);
@ -135,10 +142,23 @@ function addBurg(point) {
cells.burg[cell] = i; cells.burg[cell] = i;
const townSize = burgIcons.select("#towns").attr("size") || 0.5; const townSize = burgIcons.select("#towns").attr("size") || 0.5;
burgIcons.select("#towns").append("circle").attr("id", "burg"+i).attr("data-id", i) burgIcons
.attr("cx", x).attr("cy", y).attr("r", townSize); .select("#towns")
burgLabels.select("#towns").append("text").attr("id", "burgLabel"+i).attr("data-id", i) .append("circle")
.attr("x", x).attr("y", y).attr("dy", `${townSize * -1.5}px`).text(name); .attr("id", "burg" + i)
.attr("data-id", i)
.attr("cx", x)
.attr("cy", y)
.attr("r", townSize);
burgLabels
.select("#towns")
.append("text")
.attr("id", "burgLabel" + i)
.attr("data-id", i)
.attr("x", x)
.attr("y", y)
.attr("dy", `${townSize * -1.5}px`)
.text(name);
BurgsAndStates.defineBurgFeatures(pack.burgs[i]); BurgsAndStates.defineBurgFeatures(pack.burgs[i]);
return i; return i;
@ -148,7 +168,10 @@ function moveBurgToGroup(id, g) {
const label = document.querySelector("#burgLabels [data-id='" + id + "']"); const label = document.querySelector("#burgLabels [data-id='" + id + "']");
const icon = document.querySelector("#burgIcons [data-id='" + id + "']"); const icon = document.querySelector("#burgIcons [data-id='" + id + "']");
const anchor = document.querySelector("#anchors [data-id='" + id + "']"); const anchor = document.querySelector("#anchors [data-id='" + id + "']");
if (!label || !icon) {ERROR && console.error("Cannot find label or icon elements"); return;} if (!label || !icon) {
ERROR && console.error("Cannot find label or icon elements");
return;
}
document.querySelector("#burgLabels > #" + g).appendChild(label); document.querySelector("#burgLabels > #" + g).appendChild(label);
document.querySelector("#burgIcons > #" + g).appendChild(icon); document.querySelector("#burgIcons > #" + g).appendChild(icon);
@ -175,7 +198,8 @@ function removeBurg(id) {
if (icon) icon.remove(); if (icon) icon.remove();
if (anchor) anchor.remove(); if (anchor) anchor.remove();
const cells = pack.cells, burg = pack.burgs[id]; const cells = pack.cells,
burg = pack.burgs[id];
burg.removed = true; burg.removed = true;
cells.burg[burg.cell] = 0; cells.burg[burg.cell] = 0;
@ -189,8 +213,14 @@ function removeBurg(id) {
function toggleCapital(burg) { function toggleCapital(burg) {
const state = pack.burgs[burg].state; const state = pack.burgs[burg].state;
if (!state) {tip("Neutral lands cannot have a capital", false, "error"); return;} if (!state) {
if (pack.burgs[burg].capital) {tip("To change capital please assign a capital status to another burg of this state", false, "error"); return;} tip("Neutral lands cannot have a capital", false, "error");
return;
}
if (pack.burgs[burg].capital) {
tip("To change capital please assign a capital status to another burg of this state", false, "error");
return;
}
const old = pack.states[state].capital; const old = pack.states[state].capital;
// change statuses // change statuses
@ -206,7 +236,10 @@ function togglePort(burg) {
const anchor = document.querySelector("#anchors [data-id='" + burg + "']"); const anchor = document.querySelector("#anchors [data-id='" + burg + "']");
if (anchor) anchor.remove(); if (anchor) anchor.remove();
const b = pack.burgs[burg]; const b = pack.burgs[burg];
if (b.port) {b.port = 0; return;} // not a port anymore if (b.port) {
b.port = 0;
return;
} // not a port anymore
const haven = pack.cells.haven[b.cell]; const haven = pack.cells.haven[b.cell];
const port = haven ? pack.cells.f[haven] : -1; const port = haven ? pack.cells.f[haven] : -1;
@ -216,9 +249,14 @@ function togglePort(burg) {
const g = b.capital ? "cities" : "towns"; const g = b.capital ? "cities" : "towns";
const group = anchors.select("g#" + g); const group = anchors.select("g#" + g);
const size = +group.attr("size"); const size = +group.attr("size");
group.append("use").attr("xlink:href", "#icon-anchor").attr("data-id", burg) group
.attr("x", rn(b.x - size * .47, 2)).attr("y", rn(b.y - size * .47, 2)) .append("use")
.attr("width", size).attr("height", size); .attr("xlink:href", "#icon-anchor")
.attr("data-id", burg)
.attr("x", rn(b.x - size * 0.47, 2))
.attr("y", rn(b.y - size * 0.47, 2))
.attr("width", size)
.attr("height", size);
} }
function toggleBurgLock(burg) { function toggleBurgLock(burg) {
@ -251,7 +289,7 @@ function drawLegend(name, data) {
const vOffset = fontSize / 2; const vOffset = fontSize / 2;
// append items // append items
const boxes = legend.append("g").attr("stroke-width", .5).attr("stroke", "#111111").attr("stroke-dasharray", "none"); const boxes = legend.append("g").attr("stroke-width", 0.5).attr("stroke", "#111111").attr("stroke-dasharray", "none");
const labels = legend.append("g").attr("fill", "#000000").attr("stroke", "none"); const labels = legend.append("g").attr("fill", "#000000").attr("stroke", "none");
const columns = Math.ceil(data.length / itemsInCol); const columns = Math.ceil(data.length / itemsInCol);
@ -260,29 +298,40 @@ function drawLegend(name, data) {
const offset = column ? colOffset * 2 + legend.node().getBBox().width : colOffset; const offset = column ? colOffset * 2 + legend.node().getBBox().width : colOffset;
for (let l = 0; l < linesInColumn && data[i]; l++, i++) { for (let l = 0; l < linesInColumn && data[i]; l++, i++) {
boxes.append("rect").attr("fill", data[i][1]) boxes
.attr("x", offset).attr("y", lineHeight + l*lineHeight + vOffset) .append("rect")
.attr("width", colorBoxSize).attr("height", colorBoxSize); .attr("fill", data[i][1])
.attr("x", offset)
.attr("y", lineHeight + l * lineHeight + vOffset)
.attr("width", colorBoxSize)
.attr("height", colorBoxSize);
labels.append("text").text(data[i][2]) labels
.attr("x", offset + colorBoxSize * 1.6).attr("y", fontSize/1.6 + lineHeight + l*lineHeight + vOffset); .append("text")
.text(data[i][2])
.attr("x", offset + colorBoxSize * 1.6)
.attr("y", fontSize / 1.6 + lineHeight + l * lineHeight + vOffset);
} }
} }
// append label // append label
const offset = colOffset + legend.node().getBBox().width / 2; const offset = colOffset + legend.node().getBBox().width / 2;
labels.append("text") labels
.attr("text-anchor", "middle").attr("font-weight", "bold").attr("font-size", "1.2em") .append("text")
.attr("id", "legendLabel").text(name).attr("x", offset).attr("y", fontSize * 1.1 + vOffset / 2); .attr("text-anchor", "middle")
.attr("font-weight", "bold")
.attr("font-size", "1.2em")
.attr("id", "legendLabel")
.text(name)
.attr("x", offset)
.attr("y", fontSize * 1.1 + vOffset / 2);
// append box // append box
const bbox = legend.node().getBBox(); const bbox = legend.node().getBBox();
const width = bbox.width + colOffset * 2; const width = bbox.width + colOffset * 2;
const height = bbox.height + colOffset / 2 + vOffset; const height = bbox.height + colOffset / 2 + vOffset;
legend.insert("rect", ":first-child").attr("id", "legendBox") legend.insert("rect", ":first-child").attr("id", "legendBox").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", backClr).attr("fill-opacity", opacity);
.attr("x", 0).attr("y", 0).attr("width", width).attr("height", height)
.attr("fill", backClr).attr("fill-opacity", opacity);
fitLegendBox(); fitLegendBox();
} }
@ -293,7 +342,8 @@ function fitLegendBox() {
const px = isNaN(+legend.attr("data-x")) ? 99 : legend.attr("data-x") / 100; const px = isNaN(+legend.attr("data-x")) ? 99 : legend.attr("data-x") / 100;
const py = isNaN(+legend.attr("data-y")) ? 93 : legend.attr("data-y") / 100; const py = isNaN(+legend.attr("data-y")) ? 93 : legend.attr("data-y") / 100;
const bbox = legend.node().getBBox(); const bbox = legend.node().getBBox();
const x = rn(svgWidth * px - bbox.width), y = rn(svgHeight * py - bbox.height); const x = rn(svgWidth * px - bbox.width),
y = rn(svgHeight * py - bbox.height);
legend.attr("transform", `translate(${x},${y})`); legend.attr("transform", `translate(${x},${y})`);
} }
@ -301,19 +351,23 @@ function fitLegendBox() {
function redrawLegend() { function redrawLegend() {
if (!legend.select("rect").size()) return; if (!legend.select("rect").size()) return;
const name = legend.select("#legendLabel").text(); const name = legend.select("#legendLabel").text();
const data = legend.attr("data").split("|").map(l => l.split(",")); const data = legend
.attr("data")
.split("|")
.map(l => l.split(","));
drawLegend(name, data); drawLegend(name, data);
} }
function dragLegendBox() { function dragLegendBox() {
const tr = parseTransform(this.getAttribute("transform")); const tr = parseTransform(this.getAttribute("transform"));
const x = +tr[0] - d3.event.x, y = +tr[1] - d3.event.y; const x = +tr[0] - d3.event.x,
y = +tr[1] - d3.event.y;
const bbox = legend.node().getBBox(); const bbox = legend.node().getBBox();
d3.event.on("drag", function () { d3.event.on("drag", function () {
const px = rn((x + d3.event.x + bbox.width) / svgWidth * 100, 2); const px = rn(((x + d3.event.x + bbox.width) / svgWidth) * 100, 2);
const py = rn((y + d3.event.y + bbox.height) / svgHeight * 100, 2); const py = rn(((y + d3.event.y + bbox.height) / svgHeight) * 100, 2);
const transform = `translate(${(x + d3.event.x)},${(y + d3.event.y)})`; const transform = `translate(${x + d3.event.x},${y + d3.event.y})`;
legend.attr("transform", transform).attr("data-x", px).attr("data-y", py); legend.attr("transform", transform).attr("data-x", px).attr("data-y", py);
}); });
} }
@ -330,9 +384,16 @@ function createPicker() {
const closePicker = () => contaiter.style("display", "none"); const closePicker = () => contaiter.style("display", "none");
const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%"); const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%");
contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", .2) contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", 0.2).on("mousemove", cl).on("click", closePicker);
.on("mousemove", cl).on("click", closePicker); const picker = contaiter
const picker = contaiter.append("g").attr("id", "picker").call(d3.drag().filter(() => event.target.tagName !== "INPUT").on("start", dragPicker)); .append("g")
.attr("id", "picker")
.call(
d3
.drag()
.filter(() => event.target.tagName !== "INPUT")
.on("start", dragPicker)
);
const controls = picker.append("g").attr("id", "pickerControls"); const controls = picker.append("g").attr("id", "pickerControls");
const h = controls.append("g"); const h = controls.append("g");
@ -343,7 +404,7 @@ function createPicker() {
const s = controls.append("g"); const s = controls.append("g");
s.append("text").attr("x", 113).attr("y", 14).text("S:"); s.append("text").attr("x", 113).attr("y", 14).text("S:");
s.append("line").attr("x1", 124).attr("y1", 10).attr("x2", 206).attr("y2", 10) s.append("line").attr("x1", 124).attr("y1", 10).attr("x2", 206).attr("y2", 10);
s.append("circle").attr("cx", 181.4).attr("cy", 10).attr("r", 5).attr("id", "pickerS"); s.append("circle").attr("cx", 181.4).attr("cy", 10).attr("r", 5).attr("id", "pickerS");
s.on("mousemove", () => tip("Set palette saturation")); s.on("mousemove", () => tip("Set palette saturation"));
@ -356,8 +417,13 @@ function createPicker() {
controls.selectAll("line").on("click", clickPickerControl); controls.selectAll("line").on("click", clickPickerControl);
controls.selectAll("circle").call(d3.drag().on("start", dragPickerControl)); controls.selectAll("circle").call(d3.drag().on("start", dragPickerControl));
const spaces = picker.append("foreignObject").attr("id", "pickerSpaces") const spaces = picker
.attr("x", 4).attr("y", 20).attr("width", 303).attr("height", 20) .append("foreignObject")
.attr("id", "pickerSpaces")
.attr("x", 4)
.attr("y", 20)
.attr("width", 303)
.attr("height", 20)
.on("mousemove", () => tip("Color value in different color spaces. Edit to change")); .on("mousemove", () => tip("Color value in different color spaces. Edit to change"));
const html = ` const html = `
<label style="margin-right: 6px">HSL: <label style="margin-right: 6px">HSL:
@ -371,7 +437,7 @@ function createPicker() {
<input type="number" id="pickerRGB_B" data-space="rgb" min=0 max=255 value="232"> <input type="number" id="pickerRGB_B" data-space="rgb" min=0 max=255 value="232">
</label> </label>
<label>HEX: <input type="text" id="pickerHEX" data-space="hex" style="width:42px" autocorrect="off" spellcheck="false" value="#7d8ee8"></label>`; <label>HEX: <input type="text" id="pickerHEX" data-space="hex" style="width:42px" autocorrect="off" spellcheck="false" value="#7d8ee8"></label>`;
spaces.node().insertAdjacentHTML('beforeend', html); spaces.node().insertAdjacentHTML("beforeend", html);
spaces.selectAll("input").on("change", changePickerSpace); spaces.selectAll("input").on("change", changePickerSpace);
const colors = picker.append("g").attr("id", "pickerColors").attr("stroke", "#333333"); const colors = picker.append("g").attr("id", "pickerColors").attr("stroke", "#333333");
@ -379,19 +445,38 @@ function createPicker() {
const hatching = d3.selectAll("g#hatching > pattern"); const hatching = d3.selectAll("g#hatching > pattern");
const number = hatching.size(); const number = hatching.size();
const clr = d3.range(number).map(i => d3.hsl(i/number*360, .7, .7).hex()); const clr = d3.range(number).map(i => d3.hsl((i / number) * 360, 0.7, 0.7).hex());
clr.forEach(function (d, i) { clr.forEach(function (d, i) {
colors.append("rect").attr("id", "picker_" + d).attr("fill", d).attr("class", i?"":"selected") colors
.attr("x", i*22+4).attr("y", 40).attr("width", 16).attr("height", 16); .append("rect")
.attr("id", "picker_" + d)
.attr("fill", d)
.attr("class", i ? "" : "selected")
.attr("x", i * 22 + 4)
.attr("y", 40)
.attr("width", 16)
.attr("height", 16);
}); });
hatching.each(function (d, i) { hatching.each(function (d, i) {
hatches.append("rect").attr("id", "picker_" + this.id).attr("fill", "url(#" + this.id + ")") hatches
.attr("x", i*22+4).attr("y", 61).attr("width", 16).attr("height", 16); .append("rect")
.attr("id", "picker_" + this.id)
.attr("fill", "url(#" + this.id + ")")
.attr("x", i * 22 + 4)
.attr("y", 61)
.attr("width", 16)
.attr("height", 16);
}); });
colors.selectAll("rect").on("click", pickerFillClicked).on("mousemove", () => tip("Click to fill with the color")); colors
hatches.selectAll("rect").on("click", pickerFillClicked).on("mousemove", () => tip("Click to fill with the hatching")); .selectAll("rect")
.on("click", pickerFillClicked)
.on("mousemove", () => tip("Click to fill with the color"));
hatches
.selectAll("rect")
.on("click", pickerFillClicked)
.on("mousemove", () => tip("Click to fill with the hatching"));
// append box // append box
const bbox = picker.node().getBBox(); const bbox = picker.node().getBBox();
@ -408,7 +493,10 @@ function createPicker() {
function updateSelectedRect(fill) { function updateSelectedRect(fill) {
document.getElementById("picker").querySelector("rect.selected").classList.remove("selected"); document.getElementById("picker").querySelector("rect.selected").classList.remove("selected");
document.getElementById("picker").querySelector("rect[fill='"+fill.toLowerCase()+"']").classList.add("selected"); document
.getElementById("picker")
.querySelector("rect[fill='" + fill.toLowerCase() + "']")
.classList.add("selected");
} }
function updateSpaces() { function updateSpaces() {
@ -439,7 +527,7 @@ function updatePickerColors() {
const l = getPickerControl(pickerL, 1); const l = getPickerControl(pickerL, 1);
colors.each(function (d, i) { colors.each(function (d, i) {
const clr = d3.hsl(i/number*180+h, s, l).hex(); const clr = d3.hsl((i / number) * 180 + h, s, l).hex();
this.setAttribute("id", "picker_" + clr); this.setAttribute("id", "picker_" + clr);
this.setAttribute("fill", clr); this.setAttribute("fill", clr);
}); });
@ -465,7 +553,7 @@ function openPicker(fill, callback) {
const selected = document.getElementById("picker").querySelector("rect.selected"); const selected = document.getElementById("picker").querySelector("rect.selected");
if (!selected) return; if (!selected) return;
callback(selected.getAttribute("fill")); callback(selected.getAttribute("fill"));
} };
} }
function setPickerControl(control, value, max) { function setPickerControl(control, value, max) {
@ -479,19 +567,20 @@ function getPickerControl(control, max) {
const min = +control.previousSibling.getAttribute("x1"); const min = +control.previousSibling.getAttribute("x1");
const delta = +control.previousSibling.getAttribute("x2") - min; const delta = +control.previousSibling.getAttribute("x2") - min;
const current = +control.getAttribute("cx") - min; const current = +control.getAttribute("cx") - min;
return current / delta * max; return (current / delta) * max;
} }
function dragPicker() { function dragPicker() {
const tr = parseTransform(this.getAttribute("transform")); const tr = parseTransform(this.getAttribute("transform"));
const x = +tr[0] - d3.event.x, y = +tr[1] - d3.event.y; const x = +tr[0] - d3.event.x,
y = +tr[1] - d3.event.y;
const picker = d3.select("#picker"); const picker = d3.select("#picker");
const bbox = picker.node().getBBox(); const bbox = picker.node().getBBox();
d3.event.on("drag", function () { d3.event.on("drag", function () {
const px = rn((x + d3.event.x + bbox.width) / svgWidth * 100, 2); const px = rn(((x + d3.event.x + bbox.width) / svgWidth) * 100, 2);
const py = rn((y + d3.event.y + bbox.height) / svgHeight * 100, 2); const py = rn(((y + d3.event.y + bbox.height) / svgHeight) * 100, 2);
const transform = `translate(${(x + d3.event.x)},${(y + d3.event.y)})`; const transform = `translate(${x + d3.event.x},${y + d3.event.y})`;
picker.attr("transform", transform).attr("data-x", px).attr("data-y", py); picker.attr("transform", transform).attr("data-x", px).attr("data-y", py);
}); });
} }
@ -530,16 +619,20 @@ function dragPickerControl() {
function changePickerSpace() { function changePickerSpace() {
const valid = this.checkValidity(); const valid = this.checkValidity();
if (!valid) {tip("You must provide a correct value", false, "error"); return;} if (!valid) {
tip("You must provide a correct value", false, "error");
return;
}
const space = this.dataset.space; const space = this.dataset.space;
const i = Array.from(this.parentNode.querySelectorAll("input")).map(input => input.value); // inputs const i = Array.from(this.parentNode.querySelectorAll("input")).map(input => input.value); // inputs
const fill = space === "hex" ? d3.rgb(this.value) const fill = space === "hex" ? d3.rgb(this.value) : space === "rgb" ? d3.rgb(i[0], i[1], i[2]) : d3.hsl(i[0], i[1] / 100, i[2] / 100);
: space === "rgb" ? d3.rgb(i[0], i[1], i[2])
: d3.hsl(i[0], i[1]/100, i[2]/100);
const hsl = d3.hsl(fill); const hsl = d3.hsl(fill);
if (isNaN(hsl.l)) {tip("You must provide a correct value", false, "error"); return;} if (isNaN(hsl.l)) {
tip("You must provide a correct value", false, "error");
return;
}
if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360); if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360);
if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1); if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1);
if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1); if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1);
@ -572,7 +665,7 @@ function unfog(id) {
} }
function getFileName(dataType) { function getFileName(dataType) {
const formatTime = time => time < 10 ? "0" + time : time; const formatTime = time => (time < 10 ? "0" + time : time);
const name = mapName.value; const name = mapName.value;
const type = dataType ? dataType + " " : ""; const type = dataType ? dataType + " " : "";
const date = new Date(); const date = new Date();
@ -581,7 +674,7 @@ function getFileName(dataType) {
const day = formatTime(date.getDate()); const day = formatTime(date.getDate());
const hour = formatTime(date.getHours()); const hour = formatTime(date.getHours());
const minutes = formatTime(date.getMinutes()); const minutes = formatTime(date.getMinutes());
const dateString = [year, month, day, hour, minutes].join('-'); const dateString = [year, month, day, hour, minutes].join("-");
return name + " " + type + dateString; return name + " " + type + dateString;
} }
@ -609,12 +702,9 @@ function highlightElement(element) {
const enter = d3.transition().duration(1000).ease(d3.easeBounceOut); const enter = d3.transition().duration(1000).ease(d3.easeBounceOut);
const exit = d3.transition().duration(500).ease(d3.easeLinear); const exit = d3.transition().duration(500).ease(d3.easeLinear);
const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y) const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y).attr("width", box.width).attr("height", box.height).attr("transform", transform);
.attr("width", box.width).attr("height", box.height).attr("transform", transform);
highlight.classed("highlighted", 1) highlight.classed("highlighted", 1).transition(enter).style("outline-offset", "0px").transition(exit).style("outline-color", "transparent").delay(1000).remove();
.transition(enter).style("outline-offset", "0px")
.transition(exit).style("outline-color", "transparent").delay(1000).remove();
const tr = parseTransform(transform); const tr = parseTransform(transform);
let x = box.x + box.width / 2; let x = box.x + box.width / 2;
@ -633,45 +723,239 @@ function selectIcon(initial, callback) {
input.value = initial; input.value = initial;
if (!table.innerHTML) { if (!table.innerHTML) {
const icons = ["⚔️","🏹","🐴","💣","🌊","🎯","⚓","🔮","📯","⚒️","🛡️","👑","⚜️", const icons = [
"☠️","🎆","🗡️","🔪","⛏️","🔥","🩸","💧","🐾","🎪","🏰","🏯","⛓️","❤️","💘","💜","📜","🔔", "⚔️",
"🔱","💎","🌈","🌠","✨","💥","☀️","🌙","⚡","❄️","♨️","🎲","🚨","🌉","🗻","🌋","🧱", "🏹",
"⚖️","✂️","🎵","👗","🎻","🎨","🎭","⛲","💉","📖","📕","🎁","💍","⏳","🕸️","⚗️","☣️","☢️", "🐴",
"🔰","🎖️","🚩","🏳️","🏴","💪","✊","👊","🤜","🤝","🙏","🧙","🧙‍♀️","💂","🤴","🧛","🧟","🧞","🧝","👼", "💣",
"👻","👺","👹","🦄","🐲","🐉","🐎","🦓","🐺","🦊","🐱","🐈","🦁","🐯","🐅","🐆","🐕","🦌","🐵","🐒","🦍", "🌊",
"🦅","🕊️","🐓","🦇","🦜","🐦","🦉","🐮","🐄","🐂","🐃","🐷","🐖","🐗","🐏","🐑","🐐","🐫","🦒","🐘","🦏","🐭","🐁","🐀", "🎯",
"🐹","🐰","🐇","🦔","🐸","🐊","🐢","🦎","🐍","🐳","🐬","🦈","🐠","🐙","🦑","🐌","🦋","🐜","🐝","🐞","🦗","🕷️","🦂","🦀", "⚓",
"🌳","🌲","🎄","🌴","🍂","🍁","🌵","☘️","🍀","🌿","🌱","🌾","🍄","🌽","🌸","🌹","🌻", "🔮",
"🍒","🍏","🍇","🍉","🍅","🍓","🥔","🥕","🥩","🍗","🍞","🍻","🍺","🍲","🍷" "📯",
"⚒️",
"🛡️",
"👑",
"⚜️",
"☠️",
"🎆",
"🗡️",
"🔪",
"⛏️",
"🔥",
"🩸",
"💧",
"🐾",
"🎪",
"🏰",
"🏯",
"⛓️",
"❤️",
"💘",
"💜",
"📜",
"🔔",
"🔱",
"💎",
"🌈",
"🌠",
"✨",
"💥",
"☀️",
"🌙",
"⚡",
"❄️",
"♨️",
"🎲",
"🚨",
"🌉",
"🗻",
"🌋",
"🧱",
"⚖️",
"✂️",
"🎵",
"👗",
"🎻",
"🎨",
"🎭",
"⛲",
"💉",
"📖",
"📕",
"🎁",
"💍",
"⏳",
"🕸️",
"⚗️",
"☣️",
"☢️",
"🔰",
"🎖️",
"🚩",
"🏳️",
"🏴",
"💪",
"✊",
"👊",
"🤜",
"🤝",
"🙏",
"🧙",
"🧙‍♀️",
"💂",
"🤴",
"🧛",
"🧟",
"🧞",
"🧝",
"👼",
"👻",
"👺",
"👹",
"🦄",
"🐲",
"🐉",
"🐎",
"🦓",
"🐺",
"🦊",
"🐱",
"🐈",
"🦁",
"🐯",
"🐅",
"🐆",
"🐕",
"🦌",
"🐵",
"🐒",
"🦍",
"🦅",
"🕊️",
"🐓",
"🦇",
"🦜",
"🐦",
"🦉",
"🐮",
"🐄",
"🐂",
"🐃",
"🐷",
"🐖",
"🐗",
"🐏",
"🐑",
"🐐",
"🐫",
"🦒",
"🐘",
"🦏",
"🐭",
"🐁",
"🐀",
"🐹",
"🐰",
"🐇",
"🦔",
"🐸",
"🐊",
"🐢",
"🦎",
"🐍",
"🐳",
"🐬",
"🦈",
"🐠",
"🐙",
"🦑",
"🐌",
"🦋",
"🐜",
"🐝",
"🐞",
"🦗",
"🕷️",
"🦂",
"🦀",
"🌳",
"🌲",
"🎄",
"🌴",
"🍂",
"🍁",
"🌵",
"☘️",
"🍀",
"🌿",
"🌱",
"🌾",
"🍄",
"🌽",
"🌸",
"🌹",
"🌻",
"🍒",
"🍏",
"🍇",
"🍉",
"🍅",
"🍓",
"🥔",
"🥕",
"🥩",
"🍗",
"🍞",
"🍻",
"🍺",
"🍲",
"🍷"
]; ];
let row = ""; let row = "";
for (let i = 0; i < icons.length; i++) { for (let i = 0; i < icons.length; i++) {
if (i%17 === 0) row = table.insertRow(i/17|0); if (i % 17 === 0) row = table.insertRow((i / 17) | 0);
const cell = row.insertCell(i % 17); const cell = row.insertCell(i % 17);
cell.innerHTML = icons[i]; cell.innerHTML = icons[i];
} }
} }
table.onclick = e => {if (e.target.tagName === "TD") {input.value = e.target.innerHTML; callback(input.value)}}; table.onclick = e => {
table.onmouseover = e => {if (e.target.tagName === "TD") tip(`Click to select ${e.target.innerHTML} icon`)}; if (e.target.tagName === "TD") {
input.value = e.target.innerHTML;
callback(input.value);
}
};
table.onmouseover = e => {
if (e.target.tagName === "TD") tip(`Click to select ${e.target.innerHTML} icon`);
};
$("#iconSelector").dialog({width: fitContent(), title: "Select Icon", $("#iconSelector").dialog({
width: fitContent(),
title: "Select Icon",
buttons: { buttons: {
Apply: function() {callback(input.value||""); $(this).dialog("close")}, Apply: function () {
Close: function() {callback(initial); $(this).dialog("close")}} callback(input.value || "");
$(this).dialog("close");
},
Close: function () {
callback(initial);
$(this).dialog("close");
}
}
}); });
} }
// Calls the refresh functionality on all editors currently open. // Calls the refresh functionality on all editors currently open.
function refreshAllEditors() { function refreshAllEditors() {
TIME && console.time('refreshAllEditors'); TIME && console.time("refreshAllEditors");
if (document.getElementById('culturesEditorRefresh').offsetParent) culturesEditorRefresh.click(); if (document.getElementById("culturesEditorRefresh").offsetParent) culturesEditorRefresh.click();
if (document.getElementById('biomesEditorRefresh').offsetParent) biomesEditorRefresh.click(); if (document.getElementById("biomesEditorRefresh").offsetParent) biomesEditorRefresh.click();
if (document.getElementById('diplomacyEditorRefresh').offsetParent) diplomacyEditorRefresh.click(); if (document.getElementById("diplomacyEditorRefresh").offsetParent) diplomacyEditorRefresh.click();
if (document.getElementById('provincesEditorRefresh').offsetParent) provincesEditorRefresh.click(); if (document.getElementById("provincesEditorRefresh").offsetParent) provincesEditorRefresh.click();
if (document.getElementById('religionsEditorRefresh').offsetParent) religionsEditorRefresh.click(); if (document.getElementById("religionsEditorRefresh").offsetParent) religionsEditorRefresh.click();
if (document.getElementById('statesEditorRefresh').offsetParent) statesEditorRefresh.click(); if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
if (document.getElementById('zonesEditorRefresh').offsetParent) zonesEditorRefresh.click(); if (document.getElementById("zonesEditorRefresh").offsetParent) zonesEditorRefresh.click();
TIME && console.timeEnd('refreshAllEditors'); TIME && console.timeEnd("refreshAllEditors");
} }

View file

@ -1457,10 +1457,10 @@ function drawRivers() {
const {addMeandering, getRiverPath} = Rivers; const {addMeandering, getRiverPath} = Rivers;
lineGen.curve(d3.curveCatmullRom.alpha(0.1)); lineGen.curve(d3.curveCatmullRom.alpha(0.1));
const riverPaths = pack.rivers.map(river => { const riverPaths = pack.rivers.map(river => {
const riverMeandered = addMeandering(river.cells, river.points); const meanderedPoints = addMeandering(river.cells, river.points);
const widthFactor = river.widthFactor || 1; const widthFactor = river.widthFactor || 1;
const startingWidth = river.startingWidth || 0; const startingWidth = river.sourceWidth || 0;
const path = getRiverPath(riverMeandered, widthFactor, startingWidth); const path = getRiverPath(meanderedPoints, widthFactor, startingWidth);
return `<path id="river${river.i}" d="${path}"/>`; return `<path id="river${river.i}" d="${path}"/>`;
}); });
rivers.html(riverPaths.join("")); rivers.html(riverPaths.join(""));

View file

@ -1,21 +1,31 @@
"use strict"; "use strict";
function editRiver(id) { function editRiver(id) {
if (customization) return; if (customization) return;
if (elSelected && d3.event && d3.event.target.id === elSelected.attr("id")) return; if (elSelected && id === elSelected.attr("id")) return;
closeDialogs(".stable"); closeDialogs(".stable");
if (!layerIsOn("toggleRivers")) toggleRivers(); if (!layerIsOn("toggleRivers")) toggleRivers();
const node = id ? document.getElementById(id) : d3.event.target; document.getElementById("toggleCells").dataset.forced = +!layerIsOn("toggleCells");
elSelected = d3.select(node).on("click", addInterimControlPoint); if (!layerIsOn("toggleCells")) toggleCells();
elSelected = d3.select("#" + id);
viewbox.on("touchmove mousemove", showEditorTips); viewbox.on("touchmove mousemove", showEditorTips);
debug.append("g").attr("id", "controlPoints").attr("transform", elSelected.attr("transform")); debug.append("g").attr("id", "controlCells");
debug.append("g").attr("id", "controlPoints");
updateRiverData(); updateRiverData();
drawControlPoints(node);
const river = getRiver();
const {cells, points} = river;
const riverPoints = Rivers.getRiverPoints(cells, points);
drawControlPoints(riverPoints, cells);
drawRiverCells(cells);
$("#riverEditor").dialog({ $("#riverEditor").dialog({
title: "Edit River", title: "Edit River",
resizable: false, resizable: false,
position: {my: "center top+80", at: "top", of: node, collision: "fit"}, position: {my: "left+40 center", at: "center", of: "svg", collision: "fit"},
close: closeRiverEditor close: closeRiverEditor
}); });
@ -28,10 +38,8 @@ function editRiver(id) {
document.getElementById("riverNameCulture").addEventListener("click", generateNameCulture); document.getElementById("riverNameCulture").addEventListener("click", generateNameCulture);
document.getElementById("riverNameRandom").addEventListener("click", generateNameRandom); document.getElementById("riverNameRandom").addEventListener("click", generateNameRandom);
document.getElementById("riverMainstem").addEventListener("change", changeParent); document.getElementById("riverMainstem").addEventListener("change", changeParent);
document.getElementById("riverSourceWidth").addEventListener("input", changeSourceWidth); document.getElementById("riverSourceWidth").addEventListener("input", changeSourceWidth);
document.getElementById("riverWidthFactor").addEventListener("input", changeWidthFactor); document.getElementById("riverWidthFactor").addEventListener("input", changeWidthFactor);
document.getElementById("riverNew").addEventListener("click", toggleRiverCreationMode); document.getElementById("riverNew").addEventListener("click", toggleRiverCreationMode);
document.getElementById("riverEditStyle").addEventListener("click", () => editStyle("rivers")); document.getElementById("riverEditStyle").addEventListener("click", () => editStyle("rivers"));
document.getElementById("riverElevationProfile").addEventListener("click", showElevationProfile); document.getElementById("riverElevationProfile").addEventListener("click", showElevationProfile);
@ -39,9 +47,8 @@ function editRiver(id) {
document.getElementById("riverRemove").addEventListener("click", removeRiver); document.getElementById("riverRemove").addEventListener("click", removeRiver);
function showEditorTips() { function showEditorTips() {
tip("Drag control points for minor change, to change cells add a new river", true);
showMainTip(); showMainTip();
if (d3.event.target.parentNode.id === elSelected.attr("id")) tip("Drag to move, click to add a control point");
else if (d3.event.target.parentNode.id === "controlPoints") tip("Drag to move, click to delete the control point");
} }
function getRiver() { function getRiver() {
@ -67,88 +74,129 @@ function editRiver(id) {
document.getElementById("riverBasin").value = pack.rivers.find(river => river.i === r.basin).name; document.getElementById("riverBasin").value = pack.rivers.find(river => river.i === r.basin).name;
document.getElementById("riverDischarge").value = r.discharge + " m³/s"; document.getElementById("riverDischarge").value = r.discharge + " m³/s";
r.length = elSelected.node().getTotalLength() / 2;
const length = rn(r.length * distanceScaleInput.value) + " " + distanceUnitInput.value;
document.getElementById("riverLength").value = length;
const width = rn(r.width * distanceScaleInput.value, 3) + " " + distanceUnitInput.value;
document.getElementById("riverWidth").value = width;
document.getElementById("riverSourceWidth").value = r.sourceWidth; document.getElementById("riverSourceWidth").value = r.sourceWidth;
document.getElementById("riverWidthFactor").value = r.widthFactor; document.getElementById("riverWidthFactor").value = r.widthFactor;
updateRiverLength(r);
updateRiverWidth(r);
} }
function drawControlPoints(node) { function updateRiverLength(river) {
const length = getRiver().length; river.length = rn(elSelected.node().getTotalLength() / 2, 2);
const segments = Math.ceil(length / 4); const length = `${river.length * distanceScaleInput.value} ${distanceUnitInput.value}`;
const increment = rn((length / segments) * 1e5); document.getElementById("riverLength").value = length;
for (let i = increment * segments, c = i; i >= 0; i -= increment, c += increment) {
const p1 = node.getPointAtLength(i / 1e5);
const p2 = node.getPointAtLength(c / 1e5);
addControlPoint([(p1.x + p2.x) / 2, (p1.y + p2.y) / 2]);
}
} }
function addControlPoint(point, before = null) { function updateRiverWidth(river) {
debug.select("#controlPoints").insert("circle", before).attr("cx", point[0]).attr("cy", point[1]).attr("r", 0.6).call(d3.drag().on("drag", dragControlPoint)).on("click", clickControlPoint); const {addMeandering, getWidth, getOffset} = Rivers;
const {cells, discharge, widthFactor, sourceWidth} = river;
const meanderedPoints = addMeandering(cells);
river.width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor, sourceWidth));
const width = `${rn(river.width * distanceScaleInput.value, 3)} ${distanceUnitInput.value}`;
document.getElementById("riverWidth").value = width;
} }
function dragControlPoint() { function drawControlPoints(points, cells) {
this.setAttribute("cx", d3.event.x);
this.setAttribute("cy", d3.event.y);
redrawRiver();
}
function redrawRiver() {
const points = [];
debug debug
.select("#controlPoints") .select("#controlPoints")
.selectAll("circle") .selectAll("circle")
.each(function () { .data(points)
points.push([+this.getAttribute("cx"), +this.getAttribute("cy")]); .enter()
.append("circle")
.attr("cx", d => d[0])
.attr("cy", d => d[1])
.attr("r", 0.6)
.attr("data-cell", (d, i) => cells[i])
.attr("data-i", (d, i) => i)
.call(d3.drag().on("start", dragControlPoint));
}
function drawRiverCells(cells) {
debug
.select("#controlCells")
.selectAll("polygon.current")
.data(cells)
.join("polygon")
.attr("points", d => getPackPolygon(d))
.attr("class", "current");
}
function drawAvailableCells(cells) {
debug
.select("#controlCells")
.selectAll("polygon.available")
.data(cells)
.join("polygon")
.attr("points", d => getPackPolygon(d))
.attr("class", "available");
}
function dragControlPoint() {
const {c, r, fl, conf} = pack.cells;
const river = getRiver();
const {cells} = river;
const initCell = +this.dataset.cell;
const index = +this.dataset.i;
const prev = cells[index - 1];
const next = cells[index + 1];
const availableCells = conf[initCell]
? []
: c[initCell]
.filter(neib => !r[neib])
.filter(neib => !prev || c[neib].includes(prev))
.filter(neib => !next || c[neib].includes(next));
let movedToCell = null;
drawAvailableCells(availableCells);
d3.event.on("drag", function () {
const {x, y} = d3.event;
const currentCell = findCell(x, y);
if (initCell !== currentCell) {
if (availableCells.includes(currentCell)) movedToCell = currentCell;
else return;
} else movedToCell = null;
this.setAttribute("cx", x);
this.setAttribute("cy", y);
this.__data__ = [rn(x, 1), rn(y, 1)];
redrawRiver();
}); });
if (points.length < 2) return; d3.event.on("end", () => {
if (points.length === 2) { if (movedToCell) {
const p0 = points[0], this.dataset.cell = movedToCell;
p1 = points[1]; river.cells[index] = movedToCell;
const angle = Math.atan2(p1[1] - p0[1], p1[0] - p0[0]);
const sin = Math.sin(angle), r[initCell] = 0;
cos = Math.cos(angle); r[movedToCell] = river.i;
elSelected.attr("d", `M${p0[0]},${p0[1]} L${p1[0]},${p1[1]} l${-sin / 2},${cos / 2} Z`); const sourceFlux = fl[initCell];
return; fl[initCell] = fl[movedToCell];
fl[movedToCell] = sourceFlux;
drawRiverCells(river.cells);
} }
const widthFactor = +document.getElementById("riverWidthFactor").value; debug.select("#controlCells").selectAll("polygon.available").remove();
const sourceWidth = +document.getElementById("riverSourceWidth").value; });
}
function redrawRiver() {
const river = getRiver();
river.points = debug.selectAll("#controlPoints > *").data();
const {cells, widthFactor, sourceWidth} = river;
const meanderedPoints = Rivers.addMeandering(cells, river.points);
lineGen.curve(d3.curveCatmullRom.alpha(0.1)); lineGen.curve(d3.curveCatmullRom.alpha(0.1));
const [path, length, offset] = Rivers.getRiverPath(points, widthFactor, sourceWidth); const path = Rivers.getRiverPath(meanderedPoints, widthFactor, sourceWidth);
elSelected.attr("d", path); elSelected.attr("d", path);
const r = getRiver(); updateRiverLength(river);
if (r) {
r.width = rn(offset ** 2, 2);
r.length = length;
updateRiverData();
}
if (modules.elevation) showEPForRiver(elSelected.node()); if (modules.elevation) showEPForRiver(elSelected.node());
} }
function clickControlPoint() {
this.remove();
redrawRiver();
}
function addInterimControlPoint() {
const point = d3.mouse(this);
const controls = document.getElementById("controlPoints").querySelectorAll("circle");
const points = Array.from(controls).map(circle => [+circle.getAttribute("cx"), +circle.getAttribute("cy")]);
const index = getSegmentId(points, point, 2);
addControlPoint(point, ":nth-child(" + (index + 1) + ")");
redrawRiver();
}
function changeName() { function changeName() {
getRiver().name = this.value; getRiver().name = this.value;
} }
@ -175,12 +223,16 @@ function editRiver(id) {
} }
function changeSourceWidth() { function changeSourceWidth() {
getRiver().sourceWidth = +this.value; const river = getRiver();
river.sourceWidth = +this.value;
updateRiverWidth(river);
redrawRiver(); redrawRiver();
} }
function changeWidthFactor() { function changeWidthFactor() {
getRiver().widthFactor = +this.value; const river = getRiver();
river.widthFactor = +this.value;
updateRiverWidth(river);
redrawRiver(); redrawRiver();
} }
@ -214,7 +266,7 @@ function editRiver(id) {
// add control point // add control point
const point = d3.mouse(this); const point = d3.mouse(this);
addControlPoint([point[0], point[1]]); // addControlPoint([point[0], point[1]]);
redrawRiver(); redrawRiver();
} }
@ -222,7 +274,6 @@ function editRiver(id) {
riverNew.classList.remove("pressed"); riverNew.classList.remove("pressed");
clearMainTip(); clearMainTip();
viewbox.on("click", clicked).style("cursor", "default"); viewbox.on("click", clicked).style("cursor", "default");
elSelected.on("click", addInterimControlPoint);
if (!elSelected.attr("data-new")) return; // no need to create a new river if (!elSelected.attr("data-new")) return; // no need to create a new river
elSelected.attr("data-new", null); elSelected.attr("data-new", null);
@ -257,11 +308,11 @@ function editRiver(id) {
} }
function removeRiver() { function removeRiver() {
alertMessage.innerHTML = "Are you sure you want to remove the river? All tributaries will be auto-removed"; alertMessage.innerHTML = "Are you sure you want to remove the river and all its tributaries";
$("#alert").dialog({ $("#alert").dialog({
resizable: false, resizable: false,
width: "22em", width: "22em",
title: "Remove river", title: "Remove river and tributaries",
buttons: { buttons: {
Remove: function () { Remove: function () {
$(this).dialog("close"); $(this).dialog("close");
@ -281,6 +332,11 @@ function editRiver(id) {
exitRiverCreationMode(); exitRiverCreationMode();
elSelected.on("click", null); elSelected.on("click", null);
debug.select("#controlPoints").remove(); debug.select("#controlPoints").remove();
debug.select("#controlCells").remove();
unselect(); unselect();
const forced = +document.getElementById("toggleCells").dataset.forced;
document.getElementById("toggleCells").dataset.forced = 0;
if (forced && layerIsOn("toggleCells")) toggleCells();
} }
} }

View file

@ -129,7 +129,8 @@ function overviewRivers() {
} }
function openRiverEditor() { function openRiverEditor() {
editRiver("river" + this.parentNode.dataset.id); const id = "river" + this.parentNode.dataset.id;
editRiver(id);
} }
function triggerRiverRemove() { function triggerRiverRemove() {