227 if (
w < 3 ||
h < 3 ||
s.iterations <= 0 ||
s.incision <= 0.f ||
228 s.maxDepth <= 0.f ||
s.bankWidth < 0.f || !std::isfinite(
s.coordinateScale) ||
229 s.coordinateScale <= 0.f)
return diagnostics;
231 const std::vector<float> original =
terrain;
237 std::vector<float> next(
count), relaxed(
count);
238 std::vector<float> valleyTarget(
count), valleyWeight(
count);
239 std::vector<float> floodplainTarget(
count), floodplainWeight(
count);
240 std::vector<int> channelDistance(
count);
242 for (
int iteration = 0; iteration <
s.iterations; ++iteration) {
244 -std::numeric_limits<float>::infinity(),
245 s.coordinateScale,
false);
246 const float cutoff =
s.riverThreshold <= 1.f
247 ? std::max(2.f,
s.riverThreshold *
float(
terrain.size()))
249 std::iota(
order.begin(),
order.end(),
size_t(0));
250 std::stable_sort(
order.begin(),
order.end(), [&](
size_t a,
size_t b) {
251 return hydro.flowAccumulation[a] > hydro.flowAccumulation[b];
264 const float initiationCutoff = std::max(4.f, cutoff * 0.30f);
265 const float wideningCutoff = std::lerp(initiationCutoff, cutoff, 0.42f);
271 const std::vector<uint8_t> protectedLake =
272 protectedLakeBasins(hydro,
w,
h,
s.maxBreachDepth);
281 const float breachGrade = 0.00035f / std::max(0.001f,
s.coordinateScale);
282 for (
size_t i :
order) {
285 if (protectedLake[i])
continue;
286 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
287 const size_t receiver =
at(
x +
dx[
d],
y +
dy[
d],
w);
288 const float outletTarget = next[i] - breachGrade *
distance[
d];
289 if (next[receiver] > outletTarget) {
290 const int rx =
x +
dx[
d], ry =
y +
dy[
d];
291 const float boundaryDistance = float(std::min({rx, ry,
w - 1 - rx,
h - 1 - ry}));
292 const float boundaryT = saturate(boundaryDistance /
293 std::max(1.f,
s.bankWidth + 1.f));
294 const float boundaryFade = boundaryT * boundaryT * (3.f - 2.f * boundaryT);
295 const float target = std::max(original[receiver] -
s.maxDepth, outletTarget);
296 next[receiver] += (
target - next[receiver]) * boundaryFade;
302 for (
size_t i :
order) {
305 if (protectedLake[i])
continue;
306 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
307 const size_t receiver =
at(
x +
dx[
d],
y +
dy[
d],
w);
308 if (next[receiver] >=
terrain[i])
continue;
310 const float maturity = saturate((hydro.
flowAccumulation[i] - initiationCutoff) /
311 std::max(1.f, cutoff - initiationCutoff));
312 const float erodibility = 0.72f + 0.56f *
313 routeNoise(
float(
x) * 0.085f + 31.7f,
float(
y) * 0.085f - 19.3f);
314 const float boundaryDistance = float(std::min({
x,
y,
w - 1 -
x,
h - 1 -
y}));
315 const float boundaryT = saturate(boundaryDistance /
316 std::max(1.f,
s.bankWidth + 1.f));
317 const float boundaryFade = boundaryT * boundaryT * (3.f - 2.f * boundaryT);
318 const float c =
s.incision * erodibility * boundaryFade *
319 std::pow(std::max(0.04f, normalizedArea), 0.45f) /
distance[
d];
320 const float implicitHeight = (
terrain[i] +
c * next[receiver]) / (1.f +
c);
333 const float detachment =
s.incision * erodibility * boundaryFade *
334 (0.0030f + 0.0090f * maturity) *
335 std::pow(std::max(0.04f, normalizedArea), 0.30f);
336 next[i] = std::max(original[i] -
s.maxDepth,
337 std::min(
terrain[i], implicitHeight - detachment));
342 const int radius = std::max(0,
int(std::ceil(
s.bankWidth)));
343 std::fill(channelDistance.begin(), channelDistance.end(),
radius + 1);
344 std::queue<size_t> queue;
345 for (
size_t i = 0; i <
count; ++i)
348 channelDistance[i] = 0; queue.push(i);
350 constexpr std::array<int, 4> cardinal{1, 3, 4, 6};
351 while (!queue.empty()) {
352 const size_t i = queue.front(); queue.pop();
353 if (channelDistance[i] >=
radius)
continue;
354 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
355 for (
int d : cardinal) {
357 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
359 if (channelDistance[
n] > channelDistance[i] + 1) {
360 channelDistance[
n] = channelDistance[i] + 1; queue.push(
n);
365 for (
int y = 1;
y + 1 <
h; ++
y)
for (
int x = 1;
x + 1 <
w; ++
x) {
366 const size_t i =
at(
x,
y,
w);
367 if (channelDistance[i] == 0 || channelDistance[i] >
radius)
continue;
368 const float average = (next[
at(
x - 1,
y,
w)] + next[
at(
x + 1,
y,
w)] +
369 next[
at(
x,
y - 1,
w)] + next[
at(
x,
y + 1,
w)]) * 0.25f;
370 const float alpha = 0.12f * (1.f - float(channelDistance[i]) / float(
radius + 1));
371 relaxed[i] = std::clamp(next[i] + (average - next[i]) * alpha,
372 original[i] -
s.maxDepth, original[i]);
380 -std::numeric_limits<float>::infinity(),
381 s.coordinateScale,
false);
382 const float cutoff =
s.riverThreshold <= 1.f
383 ? std::max(2.f,
s.riverThreshold *
float(
count))
385 const std::vector<uint8_t> protectedLake =
386 protectedLakeBasins(hydro,
w,
h,
s.maxBreachDepth);
387 std::fill(valleyTarget.begin(), valleyTarget.end(),
388 std::numeric_limits<float>::infinity());
389 std::fill(valleyWeight.begin(), valleyWeight.end(), 0.f);
390 std::fill(floodplainTarget.begin(), floodplainTarget.end(), 0.f);
391 std::fill(floodplainWeight.begin(), floodplainWeight.end(), 0.f);
392 for (
size_t i = 0; i <
count; ++i) {
395 if (flow < cutoff ||
d < 0 || protectedLake[i])
continue;
396 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
397 const size_t receiver =
at(
x +
dx[
d],
y +
dy[
d],
w);
403 int donorX =
x, donorY =
y;
404 float donorHeight =
terrain[i];
405 float donorFlow = -1.f;
406 for (
int upstreamDirection = 0; upstreamDirection < 8; ++upstreamDirection) {
407 const int ux =
x +
dx[upstreamDirection], uy =
y +
dy[upstreamDirection];
408 if (ux < 0 || uy < 0 || ux >=
w || uy >=
h)
continue;
409 const size_t upstream =
at(ux, uy,
w);
411 if (upstreamReceiver < 0 ||
412 ux +
dx[upstreamReceiver] !=
x || uy +
dy[upstreamReceiver] !=
y)
416 donorX = ux; donorY = uy; donorHeight =
terrain[upstream];
425 float tangentLength = std::sqrt(tangentX * tangentX + tangentY * tangentY);
426 if (tangentLength < 0.001f) {
427 tangentX = float(
x +
dx[
d] - donorX);
428 tangentY = float(
y +
dy[
d] - donorY);
429 tangentLength = std::hypot(tangentX, tangentY);
431 if (tangentLength < 0.001f) {
432 tangentX = float(
dx[
d]); tangentY = float(
dy[
d]); tangentLength =
distance[
d];
434 tangentX /= tangentLength; tangentY /= tangentLength;
435 const float normalX = -tangentY, normalY = tangentX;
436 const float areaRatio = std::max(1.f, flow / cutoff);
442 const float areaScale = std::min(3.4f, std::pow(areaRatio, 0.32f));
443 const float riverMaturity = saturate(std::log2(areaRatio) / 6.f);
448 const bool alluvial = areaRatio > 3.5f && bedSlope < 0.045f;
449 const float width = std::max(0.75f,
s.bankWidth *
450 (0.18f + 0.50f * areaScale) * (alluvial ? 1.28f : 1.f));
456 const float alongReach = 2.75f;
457 const int radius = int(std::ceil(
width + alongReach));
461 const float bankResolutionScale = 3.f / std::max(1.f,
s.bankWidth);
462 const float bankRise = (alluvial ? 0.0032f
463 : 0.010f + 0.020f * (1.f - riverMaturity)) *
470 const float bedOffset =
s.maxDepth * (0.025f + 0.19f * riverMaturity);
473 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
474 const float lateral = std::abs(
float(
ox) * normalX +
float(
oy) * normalY);
475 const float along = float(
ox) * tangentX + float(
oy) * tangentY;
476 if (lateral >
width || std::abs(along) > alongReach)
continue;
481 float bed =
terrain[i] - bedOffset;
484 else if (donorFlow >= 0.f) {
485 const float donorDistance = std::hypot(
float(
x - donorX),
float(
y - donorY));
486 bed += along * (
terrain[i] - donorHeight) /
487 std::max(1.f, donorDistance);
495 const float channelHalfWidth = std::max(0.42f,
496 width * (0.045f + 0.025f * riverMaturity));
497 const float floorHalfWidth = alluvial
498 ?
width * (0.25f + 0.25f * riverMaturity)
500 const float shoulderStart = std::max(floorHalfWidth + 0.01f,
width * 0.78f);
501 float profileRise = 0.f;
502 if (lateral <= channelHalfWidth) {
503 profileRise = 0.0007f * lateral;
504 }
else if (lateral <= floorHalfWidth) {
505 profileRise = 0.0007f * channelHalfWidth +
506 0.0012f * (lateral - channelHalfWidth);
507 }
else if (lateral <= shoulderStart) {
508 const float wallT = (lateral - floorHalfWidth) /
509 std::max(0.001f, shoulderStart - floorHalfWidth);
510 const float wallShape = std::pow(wallT, alluvial ? 1.32f : 0.78f);
511 profileRise = 0.0007f * channelHalfWidth +
512 bankRise * (shoulderStart - floorHalfWidth) * wallShape;
514 const float shoulderRise = bankRise * (shoulderStart - floorHalfWidth);
515 const float shoulderT = (lateral - shoulderStart) /
516 std::max(0.001f,
width - shoulderStart);
518 const float rounded = shoulderT * shoulderT * (3.f - 2.f * shoulderT);
519 profileRise = 0.0007f * channelHalfWidth + shoulderRise +
520 bankRise * (
width - shoulderStart) * 0.22f * rounded;
522 const float target = bed + profileRise;
523 const float crossFade = 1.f - lateral /
width;
524 const float alongFade = 1.f - std::abs(along) / alongReach;
525 const float smoothCrossFade = crossFade * crossFade * (3.f - 2.f * crossFade);
526 const float domainDistance = float(std::min({
nx,
ny,
w - 1 -
nx,
h - 1 -
ny}));
527 const float domainT = saturate(domainDistance /
528 std::max(1.f,
s.bankWidth + 1.f));
529 const float domainFade = domainT * domainT * (3.f - 2.f * domainT);
535 const float edgeFade = smoothCrossFade * (0.68f + 0.32f * alongFade) *
538 valleyWeight[
n] = std::max(valleyWeight[
n], edgeFade);
539 if (alluvial && lateral <= floorHalfWidth) {
540 const float floorFade = 1.f - lateral / std::max(0.001f, floorHalfWidth);
543 const float floor = bed + 0.0015f * lateral;
544 const float weight = floorFade * floorFade * (0.5f + 0.5f * alongFade) *
546 floodplainTarget[
n] += floor *
weight;
551 for (
size_t i = 0; i <
count; ++i)
if (std::isfinite(valleyTarget[i])) {
552 const float target = std::max(original[i] -
s.maxDepth, valleyTarget[i]);
560 for (
size_t i = 0; i <
count; ++i)
if (floodplainWeight[i] > 0.f) {
561 const float desired = std::clamp(floodplainTarget[i] / floodplainWeight[i],
562 original[i] -
s.maxDepth, original[i]);
563 const float alpha = 0.34f * saturate(floodplainWeight[i]);
564 const float before =
terrain[i];
575 std::vector<float> deltaTarget(
count, -std::numeric_limits<float>::infinity());
576 std::vector<float> deltaWeight(
count, 0.f);
577 for (
size_t i = 0; i <
count; ++i) {
580 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
581 const int rx =
x +
dx[
d], ry =
y +
dy[
d];
582 if (rx < 0 || ry < 0 || rx >=
w || ry >=
h)
continue;
583 const size_t receiver =
at(rx, ry,
w);
584 if (!protectedLake[receiver])
continue;
586 const float fanLength =
s.bankWidth * (0.85f + 0.30f *
587 std::min(3.f, std::pow(areaRatio, 0.28f)));
588 const float fanWidth = fanLength * 0.72f;
589 const int radius = int(std::ceil(std::max(fanLength, fanWidth)));
592 const float normalX = -tangentY, normalY = tangentX;
596 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
598 if (protectedLake[
n])
continue;
599 const float along = float(
ox) * tangentX + float(
oy) * tangentY;
600 if (along > 0.35f || along < -fanLength)
continue;
601 const float upstreamT = std::clamp(-along / std::max(0.001f, fanLength), 0.f, 1.f);
602 const float localHalfWidth = std::lerp(fanWidth, std::max(0.7f,
s.bankWidth * 0.22f),
604 const float lateral = std::abs(
float(
ox) * normalX +
float(
oy) * normalY);
605 if (lateral > localHalfWidth)
continue;
606 const float crossFade = 1.f - lateral / localHalfWidth;
607 const float lengthFade = 1.f - upstreamT;
608 const float target = lakeSurface + (-along) *
609 (0.0018f / std::max(0.001f,
s.coordinateScale));
610 deltaTarget[
n] = std::max(deltaTarget[
n],
target);
611 deltaWeight[
n] = std::max(deltaWeight[
n], crossFade * crossFade *
612 (0.35f + 0.65f * lengthFade));
615 for (
size_t i = 0; i <
count; ++i)
if (deltaWeight[i] > 0.f) {
616 const float target = std::clamp(deltaTarget[i],
terrain[i], original[i]);
617 const float before =
terrain[i];
627 std::vector<uint8_t> valleyRegion(
count, 0);
628 for (
size_t i = 0; i <
count; ++i)
629 valleyRegion[i] = uint8_t(valleyWeight[i] > 1e-4f || floodplainWeight[i] > 1e-4f ||
630 deltaWeight[i] > 0.f);
631 std::vector<float> bankDelta(
count, 0.f);
632 constexpr std::array<int, 4> bankDx{1, 0, 1, -1};
633 constexpr std::array<int, 4> bankDy{0, 1, 1, 1};
634 constexpr std::array<float, 4> bankDistance{1.f, 1.f, 1.41421356f, 1.41421356f};
635 const float stableBankStep = 0.014f / std::max(0.001f,
s.coordinateScale);
636 for (
int pass = 0; pass < 4; ++pass) {
637 std::fill(bankDelta.begin(), bankDelta.end(), 0.f);
638 for (
int y = 0;
y <
h; ++
y)
for (
int x = 0;
x <
w; ++
x) {
639 const size_t a =
at(
x,
y,
w);
642 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
644 if (!valleyRegion[
a] && !valleyRegion[
b])
continue;
646 const float excess = std::abs(difference) - stableBankStep * bankDistance[
edge];
647 if (excess <= 0.f)
continue;
648 const float moved = excess * 0.11f;
649 if (difference > 0.f) { bankDelta[
a] -= moved; bankDelta[
b] += moved; }
650 else { bankDelta[
a] += moved; bankDelta[
b] -= moved; }
653 for (
size_t i = 0; i <
count; ++i) {
654 const float before =
terrain[i];
656 original[i] -
s.maxDepth, original[i]);
660 for (
size_t i = 0; i <
count; ++i) {
665 diagnostics.
wear[i] = std::max(0.f, original[i] -
terrain[i]) +
672 float coordinateScale,
bool classifyLakes) {
680 if (
w <= 0 ||
h <= 0)
return out;
681 const auto &
z = hm.
data();
685 auto greater = [](
const FloodCell &
a,
const FloodCell &
b) {
686 return a.elevation >
b.elevation || (
a.elevation ==
b.elevation &&
a.index >
b.index);
688 std::priority_queue<FloodCell, std::vector<FloodCell>,
decltype(greater)> frontier(greater);
689 std::vector<float> routed =
z;
690 std::vector<uint8_t> visited(
count, 0);
691 auto seed = [&](
int x,
int y) {
692 const size_t i =
at(
x,
y,
w);
693 if (!visited[i]) { visited[i] = 1; frontier.push({routed[i], i}); }
697 constexpr float epsilon = 1e-6f;
698 while (!frontier.empty()) {
699 const FloodCell
cell = frontier.top(); frontier.pop();
700 const int x = int(
cell.index %
size_t(
w)),
y = int(
cell.index /
size_t(
w));
701 for (
int d = 0;
d < 8; ++
d) {
703 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
705 if (visited[
n])
continue;
707 routed[
n] = std::max(routed[
n],
cell.elevation + epsilon);
708 for (
int back = 0; back < 8; ++back)
710 frontier.push({routed[
n],
n});
716 for (
size_t i = 0; i <
count; ++i) {
717 const float depth = routed[i] -
z[i];
724 for (
int y = 1;
y + 1 <
h; ++
y)
for (
int x = 1;
x + 1 <
w; ++
x) {
725 const size_t i =
at(
x,
y,
w);
726 int bestDirection = -1;
727 float gradientX = 0.f, gradientY = 0.f, totalGradientWeight = 0.f;
728 for (
int d = 0;
d < 8; ++
d) {
730 const float slope = (routed[i] - routed[
n]) /
distance[
d];
731 if (slope <= 0.f)
continue;
732 const float weight = std::pow(slope, 1.1f);
735 totalGradientWeight +=
weight;
737 float bestScore = -std::numeric_limits<float>::infinity();
738 const float gradientLength = std::hypot(gradientX, gradientY);
739 if (gradientLength > 1e-12f) {
743 for (
int d = 0;
d < 8; ++
d) {
746 const float slope = (routed[i] - routed[
n]) /
distance[
d];
747 if (slope <= 0.f)
continue;
751 const float alignment = totalGradientWeight > 0.f
752 ? (gradientX * float(
dx[
d]) + gradientY * float(
dy[
d])) /
753 (totalGradientWeight *
distance[
d]) : 0.f;
754 const float score = alignment + slope * 0.08f;
755 if (
score > bestScore) { bestScore =
score; bestDirection =
d; }
757 if (bestDirection >= 0) out.
flowDirection[i] = int8_t(bestDirection);
760 std::stable_sort(
order.begin(),
order.end(), [&](
size_t a,
size_t b) { return routed[a] > routed[b]; });
764 for (
size_t i :
order) {
765 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
766 std::array<float, 8>
weights{};
767 float weightSum = 0.f;
768 for (
int d = 0;
d < 8; ++
d) {
770 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
772 if (slope <= 0.f)
continue;
773 weights[size_t(
d)] = std::pow(slope, 1.1f);
776 if (weightSum <= 0.f)
continue;
777 for (
int d = 0;
d < 8; ++
d)
if (
weights[
size_t(
d)] > 0.f) {
783 const float cutoff = threshold <= 1.f ? std::max(2.f, threshold *
float(
count)) : threshold;
784 std::vector<uint8_t> suppressedBoundaryBasin(
count, 0);
785 std::vector<uint8_t> boundaryOutletChannel(
count, 0);
786 std::vector<size_t> boundaryBasinMouths;
794 std::fill(visited.begin(), visited.end(), uint8_t(0));
795 std::vector<size_t> basin;
796 std::queue<size_t> basinQueue;
797 const float samplesPerReferenceArea = coordinateScale * coordinateScale;
798 for (
size_t seedIndex = 0; seedIndex <
count; ++seedIndex) {
799 if (visited[seedIndex] || out.
lakeDepth[seedIndex] <= 0.f)
continue;
801 visited[seedIndex] = 1;
802 basinQueue.push(seedIndex);
803 float maximumDepth = 0.f, depthVolume = 0.f, maximumCatchment = 0.f;
804 size_t maximumCatchmentCell = seedIndex;
805 int minimumBoundaryDistance = std::min(
w,
h);
806 while (!basinQueue.empty()) {
807 const size_t i = basinQueue.front(); basinQueue.pop();
809 maximumDepth = std::max(maximumDepth, out.
lakeDepth[i]);
813 maximumCatchmentCell = i;
815 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
816 minimumBoundaryDistance = std::min(minimumBoundaryDistance,
817 std::min({
x,
y,
w - 1 -
x,
h - 1 -
y}));
818 for (
int d = 0;
d < 8; ++
d) {
820 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
823 visited[
n] = 1; basinQueue.push(
n);
827 const float referenceArea = float(basin.size()) /
828 std::max(0.001f, samplesPerReferenceArea);
829 const float meanDepth = depthVolume / float(basin.size());
830 const float catchmentRatio = maximumCatchment / std::max(1.f, cutoff);
831 const int boundaryHalo = std::max(1,
int(std::ceil(2.f * coordinateScale)));
832 const bool perennial = minimumBoundaryDistance > boundaryHalo &&
833 referenceArea >= 6.f && maximumDepth >= 0.004f &&
834 (meanDepth >= 0.0015f || maximumDepth >= 0.020f) &&
835 (catchmentRatio >= 0.35f || referenceArea >= 20.f);
837 if (minimumBoundaryDistance <= boundaryHalo) {
838 for (
size_t i : basin) suppressedBoundaryBasin[i] = 1;
839 boundaryBasinMouths.push_back(maximumCatchmentCell);
841 for (
size_t i : basin) out.
lakeDepth[i] = 0.f;
850 for (
size_t mouth : boundaryBasinMouths) {
853 boundaryOutletChannel[
current] = 1;
855 if (direction < 0 || direction >= 8)
break;
858 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
break;
859 const size_t nextCell =
at(
nx,
ny,
w);
860 if (nextCell ==
current)
break;
865 boundaryOutletChannel[
current] = 1;
867 size_t strongestDonor =
current;
868 float strongestFlow = -1.f;
869 for (
int neighbour = 0; neighbour < 8; ++neighbour) {
870 const int nx =
x +
dx[size_t(neighbour)],
ny =
y +
dy[size_t(neighbour)];
871 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
872 const size_t donor =
at(
nx,
ny,
w);
873 if (!suppressedBoundaryBasin[donor])
continue;
875 if (donorDirection < 0 || donorDirection >= 8 ||
876 nx +
dx[
size_t(donorDirection)] !=
x ||
877 ny +
dy[
size_t(donorDirection)] !=
y)
continue;
880 strongestDonor = donor;
883 if (strongestDonor ==
current)
break;
887 for (
size_t i = 0; i <
count; ++i)
889 (!suppressedBoundaryBasin[i] || boundaryOutletChannel[i]) &&
897 const std::vector<uint8_t> thresholdRivers = out.
rivers;
899 if (!thresholdRivers[
seed])
continue;
903 if (direction < 0 || direction >= 8)
break;
906 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
break;
907 const size_t nextCell =
at(
nx,
ny,
w);
908 if (
z[nextCell] <= seaLevel || out.
lakeDepth[nextCell] > 0.f ||
909 (suppressedBoundaryBasin[nextCell] && !boundaryOutletChannel[nextCell]))
break;
911 if (nextCell ==
current)
break;
919 std::vector<uint16_t> riverIndegree(
count, 0);
920 for (
size_t i = 0; i <
count; ++i) {
921 if (!out.
rivers[i])
continue;
923 if (direction < 0 || direction >= 8)
continue;
924 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
926 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
927 const size_t receiver =
at(
nx,
ny,
w);
928 if (out.
rivers[receiver]) ++riverIndegree[receiver];
930 std::vector<uint8_t> maximumIncomingOrder(
count, 0), equalMaximumDonors(
count, 0);
931 std::queue<size_t> orderQueue;
932 for (
size_t i = 0; i <
count; ++i)
if (out.
rivers[i] && riverIndegree[i] == 0) {
935 while (!orderQueue.empty()) {
936 const size_t i = orderQueue.front(); orderQueue.pop();
938 if (direction < 0 || direction >= 8)
continue;
939 const int x = int(i %
size_t(
w)),
y = int(i /
size_t(
w));
941 if (
nx < 0 || ny < 0 || nx >=
w ||
ny >=
h)
continue;
942 const size_t receiver =
at(
nx,
ny,
w);
943 if (!out.
rivers[receiver])
continue;
945 if (donorOrder > maximumIncomingOrder[receiver]) {
946 maximumIncomingOrder[receiver] = donorOrder;
947 equalMaximumDonors[receiver] = 1;
948 }
else if (donorOrder == maximumIncomingOrder[receiver]) {
949 equalMaximumDonors[receiver] = uint8_t(std::min(255,
950 int(equalMaximumDonors[receiver]) + 1));
952 if (riverIndegree[receiver] > 0 && --riverIndegree[receiver] == 0) {
954 int(maximumIncomingOrder[receiver]) +
955 (equalMaximumDonors[receiver] >= 2 ? 1 : 0)));
956 orderQueue.push(receiver);