mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
confluence to reflect real value
This commit is contained in:
parent
36bae4e705
commit
aed9d9d768
4 changed files with 127 additions and 93 deletions
|
|
@ -1122,7 +1122,7 @@ div#regimentSelectorBody > div > div {
|
|||
}
|
||||
|
||||
#debug > text {
|
||||
font-size: 3px;
|
||||
font-size: 2px;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
Lakes.prepareLakeData(h);
|
||||
resolveDepressions(h);
|
||||
drainWater();
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
defineRivers();
|
||||
calculateConfluenceFlux();
|
||||
Lakes.cleanupLakeData();
|
||||
|
||||
if (allowErosion) cells.h = Uint8Array.from(h); // apply changed heights as basic one
|
||||
|
|
@ -28,32 +30,29 @@
|
|||
|
||||
function drainWater() {
|
||||
const MIN_FLUX_TO_FORM_RIVER = 30;
|
||||
const prec = grid.cells.prec;
|
||||
const land = cells.i.filter(i => h[i] >= 20).sort((a, b) => h[b] - h[a]);
|
||||
const lakeOutCells = Lakes.setClimateData(h);
|
||||
|
||||
land.forEach(function (i) {
|
||||
cells.fl[i] += grid.cells.prec[cells.g[i]]; // flux from precipitation
|
||||
const [x, y] = p[i];
|
||||
cells.fl[i] += prec[cells.g[i]]; // add flux from precipitation
|
||||
|
||||
// create lake outlet if lake is not in deep depression and flux > evaporation
|
||||
const lakes = lakeOutCells[i] ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) : [];
|
||||
for (const lake of lakes) {
|
||||
const lakeCell = cells.c[i].find(c => h[c] < 20 && cells.f[c] === lake.i);
|
||||
|
||||
cells.fl[lakeCell] += Math.max(lake.flux - lake.evaporation, 0); // not evaporated lake water drains to outlet
|
||||
|
||||
// allow chain lakes to retain identity
|
||||
if (cells.r[lakeCell] !== lake.river) {
|
||||
const sameRiver = cells.c[lakeCell].some(c => cells.r[c] === lake.river);
|
||||
const [x, y] = p[lakeCell];
|
||||
const flux = cells.fl[lakeCell];
|
||||
|
||||
if (sameRiver) {
|
||||
cells.r[lakeCell] = lake.river;
|
||||
riversData.push({river: lake.river, cell: lakeCell, x, y, flux});
|
||||
riversData.push({river: lake.river, cell: lakeCell});
|
||||
} else {
|
||||
cells.r[lakeCell] = riverNext;
|
||||
riversData.push({river: riverNext, cell: lakeCell, x, y, flux});
|
||||
riversData.push({river: riverNext, cell: lakeCell});
|
||||
riverNext++;
|
||||
}
|
||||
}
|
||||
|
|
@ -69,8 +68,7 @@
|
|||
|
||||
// near-border cell: pour water out of the screen
|
||||
if (cells.b[i] && cells.r[i]) {
|
||||
const [x, y] = getBorderPoint(i);
|
||||
riversData.push({river: cells.r[i], cell: -1, x, y, flux: cells.fl[i]});
|
||||
riversData.push({river: cells.r[i], cell: -1});
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -89,14 +87,15 @@
|
|||
if (h[i] <= h[min]) return;
|
||||
|
||||
if (cells.fl[i] < MIN_FLUX_TO_FORM_RIVER) {
|
||||
// flux is too small to operate as a river
|
||||
if (h[min] >= 20) cells.fl[min] += cells.fl[i];
|
||||
return; // flux is too small to operate as river
|
||||
return;
|
||||
}
|
||||
|
||||
// proclaim a new river
|
||||
if (!cells.r[i]) {
|
||||
cells.r[i] = riverNext;
|
||||
riversData.push({river: riverNext, cell: i, x, y, flux: cells.fl[i]});
|
||||
riversData.push({river: riverNext, cell: i});
|
||||
riverNext++;
|
||||
}
|
||||
|
||||
|
|
@ -111,11 +110,19 @@
|
|||
// downhill cell already has river assigned
|
||||
if (fromFlux > toFlux) {
|
||||
cells.conf[toCell] += cells.fl[toCell]; // mark confluence
|
||||
if (h[toCell] >= 20) riversData.find(r => r.river === cells.r[toCell]).parent = river; // min river is a tributary of current river
|
||||
if (h[toCell] >= 20) {
|
||||
// min river is a tributary of current river
|
||||
const toRiver = riversData.find(r => r.river === cells.r[toCell]);
|
||||
if (toRiver) toRiver.parent = river;
|
||||
}
|
||||
cells.r[toCell] = river; // re-assign river if downhill part has less flux
|
||||
} else {
|
||||
cells.conf[toCell] += fromFlux; // mark confluence
|
||||
if (h[toCell] >= 20) riversData.find(r => r.river === river).parent = cells.r[toCell]; // current river is a tributary of min river
|
||||
if (h[toCell] >= 20) {
|
||||
// current river is a tributary of min river
|
||||
const thisRiver = riversData.find(r => r.river === river);
|
||||
if (thisRiver) thisRiver.parent = cells.r[toCell];
|
||||
}
|
||||
}
|
||||
} else cells.r[toCell] = river; // assign the river to the downhill cell
|
||||
|
||||
|
|
@ -128,46 +135,52 @@
|
|||
waterBody.enteringFlux = fromFlux;
|
||||
}
|
||||
waterBody.flux = waterBody.flux + fromFlux;
|
||||
waterBody.inlets ? waterBody.inlets.push(river) : (waterBody.inlets = [river]);
|
||||
if (!waterBody.inlets) waterBody.inlets = [river];
|
||||
else waterBody.inlets.push(river);
|
||||
}
|
||||
} else {
|
||||
// propagate flux and add next river segment
|
||||
cells.fl[toCell] += fromFlux;
|
||||
}
|
||||
|
||||
const [x, y] = p[toCell];
|
||||
riversData.push({river, cell: toCell, x, y, flux: fromFlux});
|
||||
riversData.push({river, cell: toCell});
|
||||
}
|
||||
|
||||
function defineRivers() {
|
||||
cells.r = new Uint16Array(cells.i.length); // re-initiate rivers array
|
||||
pack.rivers = []; // rivers data
|
||||
// re-initialize rivers and confluence arrays
|
||||
cells.r = new Uint16Array(cells.i.length);
|
||||
cells.conf = new Uint16Array(cells.i.length);
|
||||
pack.rivers = [];
|
||||
const riverPaths = [];
|
||||
|
||||
for (let r = 1; r <= riverNext; r++) {
|
||||
const riverPoints = riversData.filter(d => d.river === r);
|
||||
if (riverPoints.length < 3) continue;
|
||||
const riverData = riversData.filter(d => d.river === r);
|
||||
if (riverData.length < 3) continue; // exclude tiny rivers
|
||||
|
||||
for (const segment of riverPoints) {
|
||||
for (const segment of riverData) {
|
||||
const i = segment.cell;
|
||||
if (cells.r[i]) continue;
|
||||
if (cells.h[i] < 20) continue;
|
||||
cells.r[i] = r;
|
||||
if (i < 0 || cells.h[i] < 20) continue;
|
||||
|
||||
// mark real confluences and assign river to cells
|
||||
if (cells.r[i]) cells.conf[i] = 1;
|
||||
else cells.r[i] = r;
|
||||
}
|
||||
|
||||
const source = riverPoints[0].cell;
|
||||
const mouth = riverPoints[riverPoints.length - 2].cell;
|
||||
const parent = riverPoints[0].parent || 0;
|
||||
const source = riverData[0].cell;
|
||||
const mouth = riverData[riverData.length - 2].cell;
|
||||
const parent = riverData[0].parent || 0;
|
||||
|
||||
const riverCells = riverPoints.map(point => point.cell);
|
||||
const widthFactor = parent ? 1 : 1.4;
|
||||
const riverCells = riverData.map(point => point.cell);
|
||||
const widthFactor = !parent || parent === r ? 1.2 : 1;
|
||||
const initStep = cells.h[source] >= 20 ? 1 : 10;
|
||||
const riverMeandered = addMeandering(riverCells, initStep, 0.5);
|
||||
const [path, length, offset] = getPath(riverMeandered, widthFactor);
|
||||
const [path, length, offset] = getRiverPath(riverMeandered, widthFactor);
|
||||
riverPaths.push([path, r]);
|
||||
|
||||
const width = rn(offset ** 2, 2); // mounth width in km
|
||||
const discharge = last(riverPoints).flux; // in m3/s
|
||||
// Real mounth width examples: Amazon 6000m, Volga 6000m, Dniepr 3000m, Mississippi 1300m, Themes 900m,
|
||||
// Danube 800m, Daugava 600m, Neva 500m, Nile 450m, Don 400m, Wisla 300m, Pripyat 150m, Bug 140m, Muchavets 40m
|
||||
const width = rn((offset / 1.4) ** 2, 2); // mounth width in km
|
||||
const discharge = last(riverData).flux; // in m3/s
|
||||
|
||||
pack.rivers.push({i: r, source, mouth, discharge, length, width, widthFactor, sourceWidth: 0, parent, cells: riverCells});
|
||||
}
|
||||
|
|
@ -175,6 +188,18 @@
|
|||
// draw rivers
|
||||
rivers.html(riverPaths.map(d => `<path id="river${d[1]}" d="${d[0]}"/>`).join(""));
|
||||
}
|
||||
|
||||
function calculateConfluenceFlux() {
|
||||
for (const i of cells.i) {
|
||||
if (!cells.conf[i]) continue;
|
||||
|
||||
const sortedInflux = cells.c[i]
|
||||
.filter(c => cells.r[c] && h[c] > h[i])
|
||||
.map(c => cells.fl[c])
|
||||
.sort((a, b) => b - a);
|
||||
cells.conf[i] = sortedInflux.reduce((acc, flux, index) => (index ? acc + flux : acc), 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// add distance to water value to land cells to make map less depressed
|
||||
|
|
@ -248,7 +273,7 @@
|
|||
// add points at 1/3 and 2/3 of a line between adjacents river cells
|
||||
const addMeandering = function (riverCells, step = 1, meandering = 0.5) {
|
||||
const meandered = [];
|
||||
const {p, fl} = pack.cells;
|
||||
const {p, fl, conf} = pack.cells;
|
||||
|
||||
const lastStep = riverCells.length - 1;
|
||||
let fluxPrev = 0;
|
||||
|
|
@ -259,7 +284,8 @@
|
|||
const isLastCell = i === lastStep;
|
||||
|
||||
const [x1, y1] = p[cell];
|
||||
const flux1 = (fluxPrev = getFlux(i, fl[cell]));
|
||||
const flux1 = getFlux(i, fl[cell]);
|
||||
fluxPrev = flux1;
|
||||
|
||||
meandered.push([x1, y1, flux1]);
|
||||
|
||||
|
|
@ -268,34 +294,36 @@
|
|||
const nextCell = riverCells[i + 1];
|
||||
if (nextCell === -1) {
|
||||
const [x, y] = getBorderPoint(cell);
|
||||
meandered.push([x, y, flux1]);
|
||||
meandered.push([x, y, fluxPrev]);
|
||||
break;
|
||||
}
|
||||
|
||||
const [x2, y2] = p[nextCell];
|
||||
const dist2 = (x2 - x1) ** 2 + (y2 - y1) ** 2; // square distance between cells
|
||||
if (dist2 <= 25 && riverCells.length >= 6) continue;
|
||||
|
||||
const flux2 = getFlux(i + 1, fl[nextCell]);
|
||||
const angle = Math.atan2(y2 - y1, x2 - x1);
|
||||
const sin = Math.sin(angle);
|
||||
const cos = Math.cos(angle);
|
||||
const keepInitialFlux = conf[nextCell] || flux1 === flux2;
|
||||
|
||||
const meander = meandering + 1 / step + Math.random() * Math.max(meandering - step / 100, 0);
|
||||
const dist2 = (x2 - x1) ** 2 + (y2 - y1) ** 2; // square distance between cells
|
||||
const angle = Math.atan2(y2 - y1, x2 - x1);
|
||||
const sinMeander = Math.sin(angle) * meander;
|
||||
const cosMeander = Math.cos(angle) * meander;
|
||||
|
||||
if (step < 10 && (dist2 > 64 || (dist2 > 36 && riverCells.length < 5))) {
|
||||
// if dist2 is big or river is small add extra points at 1/3 and 2/3 of segment
|
||||
const p1x = (x1 * 2 + x2) / 3 + -sin * meander;
|
||||
const p1y = (y1 * 2 + y2) / 3 + cos * meander;
|
||||
const fluxThird1 = (flux1 * 2 + flux2) / 3;
|
||||
const p2x = (x1 + x2 * 2) / 3 + sin * meander;
|
||||
const p2y = (y1 + y2 * 2) / 3 + cos * meander;
|
||||
const fluxThird2 = (flux1 + flux2 * 2) / 3;
|
||||
meandered.push([p1x, p1y, fluxThird1], [p2x, p2y, fluxThird2]);
|
||||
const p1x = (x1 * 2 + x2) / 3 + -sinMeander;
|
||||
const p1y = (y1 * 2 + y2) / 3 + cosMeander;
|
||||
const p2x = (x1 + x2 * 2) / 3 + sinMeander;
|
||||
const p2y = (y1 + y2 * 2) / 3 + cosMeander;
|
||||
const [p1fl, p2fl] = keepInitialFlux ? [flux1, flux1] : [(flux1 * 2 + flux2) / 3, (flux1 + flux2 * 2) / 3];
|
||||
meandered.push([p1x, p1y, p1fl], [p2x, p2y, p2fl]);
|
||||
} else if (dist2 > 25 || riverCells.length < 6) {
|
||||
// if dist is medium or river is small add 1 extra middlepoint
|
||||
const p1x = (x1 + x2) / 2 + -sin * meander;
|
||||
const p1y = (y1 + y2) / 2 + cos * meander;
|
||||
const fluxMid = (flux1 + flux2) / 2;
|
||||
meandered.push([p1x, p1y, fluxMid]);
|
||||
const p1x = (x1 + x2) / 2 + -sinMeander;
|
||||
const p1y = (y1 + y2) / 2 + cosMeander;
|
||||
const p1fl = keepInitialFlux ? flux1 : (flux1 + flux2) / 2;
|
||||
meandered.push([p1x, p1y, p1fl]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -303,18 +331,17 @@
|
|||
};
|
||||
|
||||
const fluxFactor = 500;
|
||||
const maxFluxWidth = 1;
|
||||
const maxFluxWidth = 2;
|
||||
const widthFactor = 200;
|
||||
const stepWidth = 1 / widthFactor;
|
||||
const lengthProgression = [1, 1, 2, 3, 5, 8, 13, 21, 34].map(n => n / widthFactor);
|
||||
const maxProgression = last(lengthProgression);
|
||||
|
||||
const getPath = function (points, widthFactor = 1, startingWidth = 0) {
|
||||
// build polygon from a list of points and calculated offset (width)
|
||||
const getRiverPath = function (points, widthFactor = 1, startingWidth = 0) {
|
||||
const riverLength = points.reduce((s, v, i, p) => s + (i ? Math.hypot(v[0] - p[i - 1][0], v[1] - p[i - 1][1]) : 0), 0);
|
||||
let width = 0;
|
||||
console.log("---------");
|
||||
|
||||
// store points on both sides to build a polygon
|
||||
const riverPointsLeft = [];
|
||||
const riverPointsRight = [];
|
||||
|
||||
|
|
@ -323,7 +350,7 @@
|
|||
const [x1, y1, flux] = points[p];
|
||||
const [x2, y2] = points[p + 1] || points[p];
|
||||
|
||||
const fluxWidth = Math.min(flux ** 0.9 / fluxFactor, 1);
|
||||
const fluxWidth = Math.min(flux ** 0.9 / fluxFactor, maxFluxWidth);
|
||||
const lengthWidth = p * stepWidth + (lengthProgression[p] || maxProgression);
|
||||
width = widthFactor * (lengthWidth + fluxWidth) + startingWidth;
|
||||
|
||||
|
|
@ -331,17 +358,10 @@
|
|||
const sinOffset = Math.sin(angle) * width;
|
||||
const cosOffset = Math.cos(angle) * width;
|
||||
|
||||
//const text = `${p}: ${rn(flux, 1)} - ${rn(width, 1)}`;
|
||||
//debug.append("text").attr("x", x1).attr("y", y1).text(text);
|
||||
|
||||
console.log({fluxWidth, lengthWidth, width});
|
||||
|
||||
riverPointsLeft.push([x1 - sinOffset, y1 + cosOffset]);
|
||||
riverPointsRight.push([x1 + sinOffset, y1 - cosOffset]);
|
||||
}
|
||||
|
||||
// generate polygon path and return
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
const right = lineGen(riverPointsRight.reverse());
|
||||
let left = lineGen(riverPointsLeft);
|
||||
left = left.substring(left.indexOf("C"));
|
||||
|
|
@ -398,5 +418,5 @@
|
|||
return [graphWidth, y];
|
||||
};
|
||||
|
||||
return {generate, alterHeights, resolveDepressions, addMeandering, getPath, specify, getName, getBasin, remove};
|
||||
return {generate, alterHeights, resolveDepressions, addMeandering, getPath: getRiverPath, specify, getName, getBasin, remove};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ function editRiver(id) {
|
|||
drawControlPoints(node);
|
||||
|
||||
$("#riverEditor").dialog({
|
||||
title: "Edit River", resizable: false,
|
||||
title: "Edit River",
|
||||
resizable: false,
|
||||
position: {my: "center top+80", at: "top", of: node, collision: "fit"},
|
||||
close: closeRiverEditor
|
||||
});
|
||||
|
|
@ -39,8 +40,8 @@ function editRiver(id) {
|
|||
|
||||
function showEditorTips() {
|
||||
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");
|
||||
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() {
|
||||
|
|
@ -58,7 +59,7 @@ function editRiver(id) {
|
|||
const parentSelect = document.getElementById("riverMainstem");
|
||||
parentSelect.options.length = 0;
|
||||
const parent = r.parent || r.i;
|
||||
const sortedRivers = pack.rivers.slice().sort((a, b) => a.name > b.name ? 1 : -1);
|
||||
const sortedRivers = pack.rivers.slice().sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||
sortedRivers.forEach(river => {
|
||||
const opt = new Option(river.name, river.i, false, river.i === parent);
|
||||
parentSelect.options.add(opt);
|
||||
|
|
@ -79,7 +80,7 @@ function editRiver(id) {
|
|||
function drawControlPoints(node) {
|
||||
const length = getRiver().length;
|
||||
const segments = Math.ceil(length / 4);
|
||||
const increment = rn(length / segments * 1e5);
|
||||
const increment = rn((length / segments) * 1e5);
|
||||
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);
|
||||
|
|
@ -88,10 +89,7 @@ function editRiver(id) {
|
|||
}
|
||||
|
||||
function addControlPoint(point, before = null) {
|
||||
debug.select("#controlPoints").insert("circle", before)
|
||||
.attr("cx", point[0]).attr("cy", point[1]).attr("r", .6)
|
||||
.call(d3.drag().on("drag", dragControlPoint))
|
||||
.on("click", clickControlPoint);
|
||||
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);
|
||||
}
|
||||
|
||||
function dragControlPoint() {
|
||||
|
|
@ -102,22 +100,28 @@ function editRiver(id) {
|
|||
|
||||
function redrawRiver() {
|
||||
const points = [];
|
||||
debug.select("#controlPoints").selectAll("circle").each(function() {
|
||||
points.push([+this.getAttribute("cx"), +this.getAttribute("cy")]);
|
||||
});
|
||||
debug
|
||||
.select("#controlPoints")
|
||||
.selectAll("circle")
|
||||
.each(function () {
|
||||
points.push([+this.getAttribute("cx"), +this.getAttribute("cy")]);
|
||||
});
|
||||
|
||||
if (points.length < 2) return;
|
||||
if (points.length === 2) {
|
||||
const p0 = points[0], p1 = points[1];
|
||||
const p0 = points[0],
|
||||
p1 = points[1];
|
||||
const angle = Math.atan2(p1[1] - p0[1], p1[0] - p0[0]);
|
||||
const sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
elSelected.attr("d", `M${p0[0]},${p0[1]} L${p1[0]},${p1[1]} l${-sin/2},${cos/2} Z`);
|
||||
const sin = Math.sin(angle),
|
||||
cos = Math.cos(angle);
|
||||
elSelected.attr("d", `M${p0[0]},${p0[1]} L${p1[0]},${p1[1]} l${-sin / 2},${cos / 2} Z`);
|
||||
return;
|
||||
}
|
||||
|
||||
const widthFactor = +document.getElementById("riverWidthFactor").value;
|
||||
const sourceWidth = +document.getElementById("riverSourceWidth").value;
|
||||
const [path, length, offset] = Rivers.getPath(points, widthFactor, sourceWidth);
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
const [path, length, offset] = Rivers.getRiverPath(points, widthFactor, sourceWidth);
|
||||
elSelected.attr("d", path);
|
||||
|
||||
const r = getRiver();
|
||||
|
|
@ -140,7 +144,7 @@ function editRiver(id) {
|
|||
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) + ")");
|
||||
addControlPoint(point, ":nth-child(" + (index + 1) + ")");
|
||||
|
||||
redrawRiver();
|
||||
}
|
||||
|
|
@ -160,7 +164,7 @@ function editRiver(id) {
|
|||
|
||||
function generateNameRandom() {
|
||||
const r = getRiver();
|
||||
if (r) r.name = riverName.value = Names.getBase(rand(nameBases.length-1));
|
||||
if (r) r.name = riverName.value = Names.getBase(rand(nameBases.length - 1));
|
||||
}
|
||||
|
||||
function changeParent() {
|
||||
|
|
@ -225,10 +229,13 @@ function editRiver(id) {
|
|||
|
||||
// add a river
|
||||
const r = +elSelected.attr("id").slice(5);
|
||||
const node = elSelected.node(), length = node.getTotalLength() / 2;
|
||||
const node = elSelected.node();
|
||||
const length = node.getTotalLength() / 2;
|
||||
|
||||
const cells = [];
|
||||
const segments = Math.ceil(length / 4), increment = rn(length / segments * 1e5);
|
||||
|
||||
const segments = Math.ceil(length / 4);
|
||||
const increment = rn((length / segments) * 1e5);
|
||||
for (let i = increment * segments, c = i; i >= 0; i -= increment, c += increment) {
|
||||
const p = node.getPointAtLength(i / 1e5);
|
||||
const cell = findCell(p.x, p.y);
|
||||
|
|
@ -236,30 +243,36 @@ function editRiver(id) {
|
|||
cells.push(cell);
|
||||
}
|
||||
|
||||
const source = cells[0], mouth = last(cells);
|
||||
const source = cells[0];
|
||||
const mouth = last(cells);
|
||||
const name = Rivers.getName(mouth);
|
||||
const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)];
|
||||
const type = length < smallLength ? rw({"Creek":9, "River":3, "Brook":3, "Stream":1}) : "River";
|
||||
const smallLength = pack.rivers.map(r => r.length || 0).sort((a, b) => a - b)[Math.ceil(pack.rivers.length * 0.15)];
|
||||
const type = length < smallLength ? rw({Creek: 9, River: 3, Brook: 3, Stream: 1}) : "River";
|
||||
|
||||
const discharge = rn(cells.length * 20 * Math.random());
|
||||
const widthFactor = +document.getElementById("riverWidthFactor").value;
|
||||
const sourceWidth = +document.getElementById("riverSourceWidth").value;
|
||||
|
||||
pack.rivers.push({i:r, source, mouth, discharge, length, width: sourceWidth, widthFactor, sourceWidth, parent:0, name, type, basin:r});
|
||||
pack.rivers.push({i: r, source, mouth, discharge, length, width: sourceWidth, widthFactor, sourceWidth, parent: 0, name, type, basin: r});
|
||||
}
|
||||
|
||||
function removeRiver() {
|
||||
alertMessage.innerHTML = "Are you sure you want to remove the river? All tributaries will be auto-removed";
|
||||
$("#alert").dialog({resizable: false, width: "22em", title: "Remove river",
|
||||
$("#alert").dialog({
|
||||
resizable: false,
|
||||
width: "22em",
|
||||
title: "Remove river",
|
||||
buttons: {
|
||||
Remove: function() {
|
||||
Remove: function () {
|
||||
$(this).dialog("close");
|
||||
const river = +elSelected.attr("id").slice(5);
|
||||
Rivers.remove(river);
|
||||
elSelected.remove(); // keep if river if missed in pack.rivers
|
||||
$("#riverEditor").dialog("close");
|
||||
},
|
||||
Cancel: function() {$(this).dialog("close");}
|
||||
Cancel: function () {
|
||||
$(this).dialog("close");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -616,10 +616,11 @@ function addRiverOnClick() {
|
|||
|
||||
const river = rivers.find(r => r.i === riverId);
|
||||
const sourceWidth = 0.1;
|
||||
const widthFactor = river?.widthFactor || (parent ? 1 : 1.4);
|
||||
const widthFactor = river?.widthFactor || (!parent || parent === r ? 1.2 : 1);
|
||||
|
||||
const riverMeandered = Rivers.addMeandering(riverCells, sourceWidth * 10, 0.5);
|
||||
const [path, length, offset] = Rivers.getPath(riverMeandered, widthFactor, sourceWidth);
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
const [path, length, offset] = Rivers.getRiverPath(riverMeandered, widthFactor, sourceWidth);
|
||||
viewbox
|
||||
.select("#rivers")
|
||||
.append("path")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue