SWMMVis  6.0.0-alpha.4
Qt6/C++ GIS-based graphical user interface for the SWMMVis engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
meshprofileinterp.h
Go to the documentation of this file.
1
40#ifndef MESH_PROFILE_INTERP_H
41#define MESH_PROFILE_INTERP_H
42
44
45#include <QVector>
46
47#include <cmath>
48#include <functional>
49#include <limits>
50
52{
53
58inline constexpr double kFilm = 1e-6;
59
69inline QVector<double> bridgedTops(
70 const QVector<MeshProfileSampler::Sample> &s,
71 const std::function<double(const MeshProfileSampler::Sample &)> &topElev)
72{
73 const int n = s.size();
74 const double kNaN = std::numeric_limits<double>::quiet_NaN();
75 QVector<double> top(n, kNaN);
76
77 // 1) Raw per-sample surface — films included via kFilm.
78 for (int i = 0; i < n; ++i) {
79 if (!std::isfinite(s[i].ground)) continue;
80 const double t = topElev(s[i]);
81 if (t > s[i].ground + kFilm) top[i] = t;
82 }
83
84 // 2) Bridge dry gaps that lie between two wet runs.
85 int i = 0;
86 while (i < n) {
87 if (std::isnan(top[i])) { ++i; continue; }
88
89 // End of the current wet run.
90 int runEnd = i;
91 while (runEnd + 1 < n && !std::isnan(top[runEnd + 1])) ++runEnd;
92
93 // Walk the gap to the next wet sample. An off-mesh (NaN ground) sample
94 // blocks the bridge — a genuine mesh hole must stay a true gap. A gap
95 // whose samples ALL carry a valid free surface (cellHasSurface) is
96 // genuinely dry ground, not missing data — never bridge it.
97 const int gapStart = runEnd + 1;
98 int next = gapStart;
99 bool blocked = false;
100 bool sawNoData = false;
101 while (next < n && std::isnan(top[next])) {
102 if (!std::isfinite(s[next].ground)) { blocked = true; break; }
103 if (!s[next].cellHasSurface) sawNoData = true;
104 ++next;
105 }
106
107 if (!blocked && sawNoData && next < n && gapStart < next) {
108 const double cL = s[runEnd].chainage, wseL = top[runEnd];
109 const double cR = s[next].chainage, wseR = top[next];
110 const double dC = cR - cL;
111 const double wseUp = std::max(wseL, wseR); // upstream (higher) level
112 for (int k = gapStart; k < next; ++k) {
113 if (!std::isfinite(s[k].ground)) continue; // safety
114 double wse = (dC > 1e-12)
115 ? wseL + (s[k].chainage - cL) / dC * (wseR - wseL)
116 : wseL;
117 if (wse > wseUp) wse = wseUp; // no uphill water
118 if (wse > s[k].ground + kFilm) // above the bed → wet
119 top[k] = wse;
120 // else: dry crest — leave NaN so the run splits at the hill.
121 }
122 }
123
124 i = blocked ? (runEnd + 1) : std::max(next, runEnd + 1);
125 }
126
127 return top;
128}
129
162inline bool shorelineIntercept(const QVector<MeshProfileSampler::Sample> &s,
163 const QVector<double> &top,
164 int runFirst, int runLast, bool trailing,
165 double *outChainage, double *outElev)
166{
167 const int n = s.size();
168 if (runFirst < 0 || runLast >= n || runFirst > runLast || top.size() != n)
169 return false;
170 const int i = trailing ? runLast : runFirst; // boundary wet sample
171 const int j = trailing ? runLast - 1 : runFirst + 1; // inner wet neighbour
172 const int k = trailing ? runLast + 1 : runFirst - 1; // adjacent dry sample
173 if (k < 0 || k >= n) return false; // run ends at the data edge
174 if (!std::isfinite(s[k].ground)) return false; // off-mesh — keep the hard edge
175 if (!std::isnan(top[k])) return false; // neighbour not dry (defensive)
176 if (std::isnan(top[i]) || !std::isfinite(s[i].ground)) return false;
177
178 const double ci = s[i].chainage, ck = s[k].chainage;
179 const double dSeg = ck - ci; // signed, toward the dry side
180 if (std::abs(dSeg) < 1e-12) return false;
181
182 // Wet-side surface slope from the two boundary-most wet samples; a
183 // single-sample run extends flat.
184 double mTop = 0.0;
185 if (j >= runFirst && j <= runLast) {
186 const double dcw = ci - s[j].chainage;
187 if (std::abs(dcw) > 1e-12 && !std::isnan(top[j]))
188 mTop = (top[i] - top[j]) / dcw;
189 }
190
191 // Along t ∈ [0,1] from sample i to sample k:
192 // surface(t) = top_i + mTop·dSeg·t
193 // ground(t) = g_i + (g_k − g_i)·t
194 // They cross where surface == ground.
195 const double gi = s[i].ground, gk = s[k].ground;
196 const double denom = (gk - gi) - mTop * dSeg;
197 if (denom <= 1e-12) return false; // ground never rises through the surface
198 const double t = (top[i] - gi) / denom;
199 if (t <= 0.0 || t > 1.0) return false; // no crossing inside the segment
200
201 if (outChainage) *outChainage = ci + t * dSeg;
202 if (outElev) *outElev = gi + t * (gk - gi);
203 return true;
204}
205
206} // namespace MeshProfileInterp
207
208#endif // MESH_PROFILE_INTERP_H
size_t i
Definition contourjob.cpp:27
DiagramType t
Definition diagramspec.cpp:20
double s
Scene px per model length unit.
Definition inletdrawingview.cpp:82
int e
Definition inpmeshreader.cpp:41
std::size_t n
Definition mesh2dresultsexport.cpp:426
int k
Definition mesh2dresultsexport.cpp:549
Pure-logic sampler that turns a user-traced polyline across the 2D surface mesh into a longitudinal c...
Definition meshprofileinterp.h:52
QVector< double > bridgedTops(const QVector< MeshProfileSampler::Sample > &s, const std::function< double(const MeshProfileSampler::Sample &)> &topElev)
Compute the per-sample water-surface elevation to paint for one wet-band pass, with shallow-film rend...
Definition meshprofileinterp.h:69
bool shorelineIntercept(const QVector< MeshProfileSampler::Sample > &s, const QVector< double > &top, int runFirst, int runLast, bool trailing, double *outChainage, double *outElev)
Shoreline intercept just outside one end of a wet run — the exact chainage where the (wet-side-extrap...
Definition meshprofileinterp.h:162
constexpr double kFilm
Definition meshprofileinterp.h:58
One point along the traced cross-section.
Definition profilesection.h:37