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
swmm2dmeshqsgrenderer.h
Go to the documentation of this file.
1
32#ifndef SWMM2DMESHQSGRENDERER_H
33#define SWMM2DMESHQSGRENDERER_H
34
36#include "map/mapextent.h"
37#include "render/colorramp.h"
40
41#include <QByteArray>
42#include <QHash>
43#include <QPointer>
44#include <QQuickItem>
45#include <QRectF>
46#include <QSet>
47#include <QString>
48
49#include <limits>
50#include <vector>
51
52class QSGTexture;
53class QSGGeometryNode;
54class QSGTransformNode;
55class SWMM2DMeshLayer;
56
57class SWMM2DMeshQSGRenderer : public QQuickItem
58{
59 Q_OBJECT
60
61public:
62 explicit SWMM2DMeshQSGRenderer(QQuickItem *parent = nullptr);
63 ~SWMM2DMeshQSGRenderer() override;
64
65 void setLayer(SWMM2DMeshLayer *layer);
66 void setMapExtent(const MapExtent &extent);
67
74 [[nodiscard]] quint64 contentRevision() const noexcept
75 { return m_contentRev; }
76
77protected:
78 QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
79
80private:
83 void noteContentChanged() { ++m_contentRev; update(); }
84
85 QPointer<SWMM2DMeshLayer> m_layer;
86 quint64 m_contentRev = 0;
87 MapExtent m_extent;
88
97 struct SceneNodes {
98 QSGGeometryNode *tri = nullptr;
99 QSGGeometryNode *isoband = nullptr;
100 QSGGeometryNode *edgeThin = nullptr;
101 QSGGeometryNode *edgeWide = nullptr;
102 QSGGeometryNode *edgeBc[6] = {}; // non-Wall BC types
103 QSGGeometryNode *contour = nullptr;
104 QSGNode *contourLabels = nullptr;
105 QSGGeometryNode *nodeMark = nullptr;
106 QSGGeometryNode *coupledMark = nullptr;
107 QSGGeometryNode *selTri = nullptr;
108 QSGGeometryNode *selEdge = nullptr;
109 QSGGeometryNode *selVert = nullptr;
110 };
111 SceneNodes m_nodes;
112 QSGTransformNode *m_sceneRoot = nullptr;
113
114 // Fixed scene-space anchor (bbox centre) — keeps float vertex coords
115 // small even in UTM coordinates; stable across pans. Recomputed only
116 // when the geometry domain is dirty.
117 double m_anchorX = 0.0;
118 double m_anchorY = 0.0;
119
120 // ── QSG-2D-1M dirty/LOD state ──────────────────────────────────────
122
125 quint64 m_builtLodKey = ~quint64(0);
126 QRectF m_builtCoverage;
127 int m_lastBucket = -1;
128 int m_lastZoomStep = std::numeric_limits<int>::min();
129
132 quint64 m_lastGeomRev = ~quint64(0);
133 QSet<int> m_lastSelVerts;
134 QSet<int> m_lastSelEdges;
135 QSet<int> m_lastSelTris;
136
137 // ── Isoline-label texture cache ────────────────────────────────────
138 // Keyed by the rendered label string ("12.4", "20.0", …). QSGTexture
139 // ownership is held by us; we delete them in ~SWMM2DMeshQSGRenderer
140 // via deleteLater() because QSGTextures must be released on the
141 // QQuickWindow's render thread. Cached so we don't rasterise the
142 // same level value every frame.
143 mutable QHash<QString, QSGTexture *> m_labelTextureCache;
144
145 void clearLabelTextureCache();
146
147 // ── Contour-geometry cache ─────────────────────────────────────────
148 // Marching-triangles output (isobands + isolines) is invariant under
149 // pan and zoom — it only depends on the scene-space triangle set, the
150 // anchor (which moves with the mesh, not the view), and the level
151 // counts derived from the band/line styles. Cache it here and reuse
152 // on every paint until any of the key inputs change.
153 //
154 // Cache key: (geomRevision, zMin, zMax, level count).
155 // geomRevision covers the anchor (ox, oy) and the sceneTris vector
156 // since both are rewritten in lockstep inside rebuildSceneGeometry().
157 // zMin/zMax are belt-and-suspenders for the (unlikely) case where the
158 // revision didn't change but z-range did.
159 quint64 m_isobandCacheRev = 0;
160 double m_isobandCacheZMin = 0.0;
161 double m_isobandCacheZMax = 0.0;
162 int m_isobandCacheBands = -1;
163 quint64 m_isobandCacheScheme = 0;
164 std::vector<OpenSWMM::Contour::IsoBandPolygon> m_cachedBands;
165
166 quint64 m_isolineCacheRev = 0;
167 double m_isolineCacheZMin = 0.0;
168 double m_isolineCacheZMax = 0.0;
169 int m_isolineCacheLevels = -1;
170 quint64 m_isolineCacheScheme = 0;
171 std::vector<OpenSWMM::Contour::IsoLineSegment> m_cachedSegs;
172
173 // ── Pass 1 per-triangle fill-colour cache ──────────────────────────
174 // The ramp branch of Pass 1 computes, for every visible triangle, an
175 // elevation-ramp sample × hillshade lighting × alpha blend. The RGB
176 // part of that computation is invariant under pan and zoom (it only
177 // depends on the mesh geometry, the elevation range, the ramp, and
178 // the hillshade direction / strength parameters). Cache it here so
179 // zoom interactions and style-edits don't re-run ~15 float ops + a
180 // sqrt per triangle every paint tick.
181 //
182 // Storage is per-scene-triangle (NOT per-visible-triangle), packed as
183 // a quint32 holding the RGB in the low 24 bits. Alpha is intentionally
184 // outside the key — it depends on the active flag and the sublayer
185 // opacity slider, both cheap to apply at upload time.
186 //
187 // Cache key (compared structurally each paint):
188 // geomRevision — mesh geometry version from the layer
189 // zMin / zMax — elevation range used to normalise zAvg
190 // azimuth, altitude, zExaggeration, shadowFloor, strength
191 // — hillshade lighting inputs
192 // ramp — RasterColorRamp; compared structurally (minValue,
193 // maxValue, clampMin/Max, interp, stops)
194 //
195 // The flat-fill branch (useRamp=false / no elevation) produces a
196 // constant colour for every triangle and is not cached. The flat
197 // branch also doesn't invalidate the cache, so toggling the ramp off
198 // and back on can reuse a still-valid cache if all other inputs
199 // matched.
200 //
201 // Lazy fill: only visible triangles are computed per paint; entries
202 // for off-screen triangles stay at the sentinel 0u until their
203 // triangle becomes visible. Bit 24 is set on every written entry so
204 // a legitimate black triangle (RGB 0,0,0) is still distinguishable
205 // from "not yet computed".
206 //
207 // colorByAttribute (MeshFillStyle) widens two of these fields:
208 // m_fillCacheRev also XORs the layer's attrRevision(), so a
209 // per-cell Manning's edit invalidates the colours.
210 // m_fillCacheZMin/Max hold the *classification* range — the attribute's
211 // observed span in attribute mode, the elevation
212 // span in the historic elevation mode.
213 // m_fillCacheAttrKey which attribute the cached colours were built
214 // from; switching attributes changes nothing else
215 // in the key, so it must be compared on its own.
216 bool m_fillCacheValid = false;
217 quint64 m_fillCacheRev = 0;
218 QByteArray m_fillCacheAttrKey;
219 double m_fillCacheZMin = 0.0;
220 double m_fillCacheZMax = 0.0;
221 double m_fillCacheAzimuth = 0.0;
222 double m_fillCacheAltitude = 0.0;
223 double m_fillCacheZExag = 0.0;
224 double m_fillCacheShadowFloor = 0.0;
225 double m_fillCacheStrength = 0.0;
226 RasterColorRamp m_fillCacheRamp;
227 std::vector<quint32> m_cachedFillRgb;
228};
229
230#endif // SWMM2DMESHQSGRENDERER_H
An axis-aligned bounding box in the coordinate space of a given CRS.
Definition mapextent.h:26
Definition qsg2ddirtystate.h:55
Definition swmm2dmeshlayer.h:64
Definition swmm2dmeshqsgrenderer.h:58
void setLayer(SWMM2DMeshLayer *layer)
Definition swmm2dmeshqsgrenderer.cpp:329
~SWMM2DMeshQSGRenderer() override
Definition swmm2dmeshqsgrenderer.cpp:314
quint64 contentRevision() const noexcept
Definition swmm2dmeshqsgrenderer.h:74
QSGNode * updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override
Definition swmm2dmeshqsgrenderer.cpp:371
void setMapExtent(const MapExtent &extent)
Definition swmm2dmeshqsgrenderer.cpp:356
RasterColorRamp — gradient mapping from a normalised value in [0,1] to a display QColor.
Axis-aligned bounding box in a map coordinate system, used by MapCanvas and all layer types to define...
QVector< int > parent
union-find
Definition pslgminsize.cpp:176
Gradient mapping from a normalised value in [0,1] to a QColor.
Definition colorramp.h:71