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
quadquality.hpp
Go to the documentation of this file.
1
22#ifndef MESHKIT_NATIVE_MESHQUADQUALITY_HPP
23#define MESHKIT_NATIVE_MESHQUADQUALITY_HPP
24
25#include <algorithm>
26#include <cmath>
27#include <limits>
28
29namespace meshkit::plan
30{
31
36 double minAngleDeg = 60.0;
37 double maxAngleDeg = 120.0;
38 double minScaledJacobian = 0.866;
39 double maxAspect = 2.0;
40};
41
43 double minAngleDeg = 0.0;
44 double maxAngleDeg = 0.0;
45 double scaledJacobian = 0.0;
46 double rectangularity = 0.0;
47 double aspect = 1.0;
48 double skew =
49 0.0;
50 double area = 0.0;
51 bool convex = false;
52};
53
72inline QuadQuality quadQuality(const Point2 &a, const Point2 &b, const Point2 &c, const Point2 &d)
73{
75 const Point2 p[4] = {a, b, c, d};
76 Point2 local[4];
77 double xScale = 0.0, yScale = 0.0;
78 for (int i = 0; i < 4; ++i) {
79 if (!std::isfinite(p[i].x()) || !std::isfinite(p[i].y()))
80 return {};
81 local[i] = p[i] - a;
82 if (!std::isfinite(local[i].x()) || !std::isfinite(local[i].y()))
83 return {};
84 xScale = std::max(xScale, std::abs(local[i].x()));
85 yScale = std::max(yScale, std::abs(local[i].y()));
86 }
87 if (!(xScale > 0.0) || !(yScale > 0.0))
88 return {};
89 // Local coordinates remove origin cancellation. Independent axis scales
90 // keep products finite even for representable highly anisotropic quads.
91 const auto areaCross = [&](int i, int j) {
92 return (local[i].x() / xScale) * (local[j].y() / yScale) -
93 (local[i].y() / yScale) * (local[j].x() / xScale);
94 };
95 const double area2 = areaCross(1, 2) + areaCross(2, 3);
96 if (area2 == 0.0 || !std::isfinite(area2))
97 return {};
98 const double orient = area2 > 0.0 ? 1.0 : -1.0;
99 // Rescale through exponents: neither xScale*yScale nor twice the final
100 // area needs to be representable as an intermediate value.
101 int areaExponent = 0, xExponent = 0, yExponent = 0;
102 const double areaMantissa = std::frexp(std::abs(area2), &areaExponent);
103 const double xMantissa = std::frexp(xScale, &xExponent);
104 const double yMantissa = std::frexp(yScale, &yExponent);
105 q.area =
106 std::scalbn(areaMantissa * xMantissa * yMantissa, areaExponent + xExponent + yExponent - 1);
107 if (!(q.area > 0.0) || !std::isfinite(q.area))
108 return {};
109
110 q.minAngleDeg = 360.0;
111 q.maxAngleDeg = 0.0;
112 q.scaledJacobian = 1.0;
113 q.convex = true;
114 double maxDev = 0.0;
115 double side[4];
116 for (int i = 0; i < 4; ++i) {
117 const Point2 e1 = p[(i + 3) % 4] - p[i], e2 = p[(i + 1) % 4] - p[i];
118 const double l1 = std::hypot(e1.x(), e1.y()), l2 = std::hypot(e2.x(), e2.y());
119 if (!(l1 > 0.0) || !(l2 > 0.0) || !std::isfinite(l1) || !std::isfinite(l2))
120 return {};
121 side[i] = l2;
122 const Point2 u1 = e1 / l1, u2 = e2 / l2;
123 // Normalize first; raw cross/dot products and length products may
124 // overflow or underflow even when the angular metrics are well-defined.
125 const double cross = std::clamp(u2.x() * u1.y() - u2.y() * u1.x(), -1.0, 1.0);
126 const double dot = std::clamp(u1.x() * u2.x() + u1.y() * u2.y(), -1.0, 1.0);
127 const double ang = std::atan2(std::abs(cross), dot) * 180.0 / pi;
128 const double sj = cross * orient;
129 if (sj <= 0.0)
130 q.convex = false;
131 q.scaledJacobian = std::min(q.scaledJacobian, sj);
132 q.minAngleDeg = std::min(q.minAngleDeg, ang);
133 q.maxAngleDeg = std::max(q.maxAngleDeg, ang);
134 maxDev = std::max(maxDev, std::abs(ang - 90.0));
135 }
136 q.rectangularity = std::clamp(1.0 - maxDev / 90.0, 0.0, 1.0);
137 const double s02 = 0.5 * side[0] + 0.5 * side[2];
138 const double s13 = 0.5 * side[1] + 0.5 * side[3];
139 const double lo = std::min(s02, s13), hi = std::max(s02, s13);
140 if (!(lo > 0.0))
141 return {};
142 q.aspect = hi / lo;
143 if (!std::isfinite(q.aspect))
144 return {};
145 const Point2 d1 = c - a, d2 = d - b;
146 const double ld1 = std::hypot(d1.x(), d1.y()), ld2 = std::hypot(d2.x(), d2.y());
147 if (!(ld1 > 0.0) || !(ld2 > 0.0) || !std::isfinite(ld1) || !std::isfinite(ld2))
148 return {};
149 const Point2 ud1 = d1 / ld1, ud2 = d2 / ld2;
150 q.skew = std::clamp(std::abs(ud1.x() * ud2.x() + ud1.y() * ud2.y()), 0.0, 1.0);
151 return q;
152}
153
167{
168 return quadQuality(vertices[t.v0].xy, vertices[t.v1].xy, vertices[t.v2].xy, vertices[t.v3].xy);
169}
170
171namespace quadquality_detail
172{
182inline bool validMetrics(const QuadQuality &q)
183{
184 // These checks also protect callers constructing QuadQuality manually.
185 return q.convex && std::isfinite(q.area) && q.area > 0.0 && std::isfinite(q.minAngleDeg) &&
186 std::isfinite(q.maxAngleDeg) && q.minAngleDeg > 0.0 && q.maxAngleDeg < 180.0 &&
187 q.minAngleDeg <= q.maxAngleDeg && std::isfinite(q.scaledJacobian) &&
188 q.scaledJacobian > 0.0 && q.scaledJacobian <= 1.0 && std::isfinite(q.rectangularity) &&
189 q.rectangularity >= 0.0 && q.rectangularity <= 1.0 && std::isfinite(q.aspect) &&
190 q.aspect >= 1.0 && std::isfinite(q.skew) && q.skew >= 0.0 && q.skew <= 1.0;
191}
201{
202 return std::isfinite(b.minAngleDeg) && std::isfinite(b.maxAngleDeg) &&
203 std::isfinite(b.minScaledJacobian) && std::isfinite(b.maxAspect);
204}
205} // namespace quadquality_detail
206
220inline bool quadAcceptable(const QuadQuality &q, const QuadQualityBounds &b)
221{
223 return false;
224 if (q.minAngleDeg < b.minAngleDeg || q.maxAngleDeg > b.maxAngleDeg)
225 return false;
226 if (q.scaledJacobian < b.minScaledJacobian)
227 return false;
228 if (b.maxAspect > 0.0 && q.aspect > b.maxAspect)
229 return false;
230 return true;
231}
232
247inline double quadScore(const QuadQuality &q, const QuadQualityBounds &b)
248{
250 return 0.0;
251 const double aspectTerm =
252 (b.maxAspect > 0.0 && q.aspect > b.maxAspect) ? b.maxAspect / q.aspect : 1.0;
253 return std::clamp(q.scaledJacobian * q.rectangularity * aspectTerm, 0.0, 1.0);
254}
255
271inline double triangleScaledJacobian(const Point2 &a, const Point2 &b, const Point2 &c)
272{
273 const Point2 p[3] = {a, b, c};
274 double mn = 2.0;
275 double area2 = (b.x() - a.x()) * (c.y() - a.y()) - (c.x() - a.x()) * (b.y() - a.y());
276 const double orient = area2 >= 0.0 ? 1.0 : -1.0;
277 for (int i = 0; i < 3; ++i) {
278 const Point2 e1 = p[(i + 2) % 3] - p[i], e2 = p[(i + 1) % 3] - p[i];
279 const double l1 = std::hypot(e1.x(), e1.y()), l2 = std::hypot(e2.x(), e2.y());
280 if (l1 <= 0.0 || l2 <= 0.0)
281 return 0.0;
282 mn = std::min(mn, (e2.x() * e1.y() - e2.y() * e1.x()) * orient / (l1 * l2));
283 }
284 return std::min(mn, 1.0);
285}
286
298inline double cellCornerAngleDeg(const Vector<MeshVertex> &v, const MeshTriangle &t, int i)
299{
300 const int n = t.vertexCount();
301 const Point2 &prev = v[t.vertex((i + n - 1) % n)].xy;
302 const Point2 &cur = v[t.vertex(i)].xy;
303 const Point2 &next = v[t.vertex((i + 1) % n)].xy;
304 const Point2 e1 = prev - cur, e2 = next - cur;
305 return std::atan2(std::abs(e2.x() * e1.y() - e2.y() * e1.x()),
306 e1.x() * e2.x() + e1.y() * e2.y()) *
307 180.0 / pi;
308}
309
310} // namespace meshkit::plan
311
312#endif // MESHKIT_NATIVE_MESHQUADQUALITY_HPP
Definition geometry.hpp:11
constexpr double y() const noexcept
Definition geometry.hpp:18
constexpr double x() const noexcept
Definition geometry.hpp:17
Definition storage.hpp:18
size_t i
Definition contourjob.cpp:30
DiagramType t
Definition diagramspec.cpp:23
ArrowPlacement p
Definition linesymbollayer.cpp:29
bool finiteBounds(const QuadQualityBounds &b)
Check every supplied quality threshold is finite.
Definition quadquality.hpp:200
bool validMetrics(const QuadQuality &q)
Check the internal finite/range/convexity invariants required before acceptance or scoring.
Definition quadquality.hpp:182
Definition blocks.hpp:25
QuadQuality quadQuality(const Point2 &a, const Point2 &b, const Point2 &c, const Point2 &d)
Measure one quadrilateral using cyclic corners and translation/scale-resistant planar arithmetic.
Definition quadquality.hpp:72
constexpr double pi
Definition planmodel.hpp:12
double triangleScaledJacobian(const Point2 &a, const Point2 &b, const Point2 &c)
Compute the minimum orientation-normalized corner sine of a triangle.
Definition quadquality.hpp:271
bool quadAcceptable(const QuadQuality &q, const QuadQualityBounds &b)
Apply inclusive hard angle/Jacobian/aspect limits to a valid convex metric record.
Definition quadquality.hpp:220
double cellCornerAngleDeg(const Vector< MeshVertex > &v, const MeshTriangle &t, int i)
Compute the unsigned corner angle of a triangle or quad.
Definition quadquality.hpp:298
double quadScore(const QuadQuality &q, const QuadQualityBounds &b)
Rank valid convex quads by Jacobian, rectangularity and a soft aspect penalty.
Definition quadquality.hpp:247
Definition planmodel.hpp:78
Acceptance bounds shared by pairing, cleanup, smoothing and the legacy tri-pair merge....
Definition quadquality.hpp:35
double minScaledJacobian
Definition quadquality.hpp:38
double maxAngleDeg
Definition quadquality.hpp:37
double maxAspect
longest / shortest side (opposite-side means). <= 0 = unbounded.
Definition quadquality.hpp:39
double minAngleDeg
Definition quadquality.hpp:36
Definition quadquality.hpp:42
double aspect
mean(s0,s2) vs mean(s1,s3), >= 1.
Definition quadquality.hpp:47
bool convex
all four cross products share the sign of the total area.
Definition quadquality.hpp:51
double maxAngleDeg
Definition quadquality.hpp:44
double minAngleDeg
Definition quadquality.hpp:43
double rectangularity
1 - max_i |theta_i - 90| / 90.
Definition quadquality.hpp:46
double area
positive finite area, evaluated in local coordinates.
Definition quadquality.hpp:50
double skew
|cos(angle between diagonals)|; zero for perpendicular diagonals (e.g. squares/rhombi),...
Definition quadquality.hpp:48
double scaledJacobian
min_i sin(theta_i), signed (negative = folded).
Definition quadquality.hpp:45
int y
Definition terrainerrorfield.cpp:60
double a
Definition terrainerrorfield.cpp:42
double b
Definition terrainerrorfield.cpp:42
double c
Definition terrainerrorfield.cpp:42
int x
Definition terrainerrorfield.cpp:60