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
pslgminsize.h File Reference
#include "mesh/meshgenerator.h"
#include <QPointF>
#include <QPolygonF>
#include <QString>
#include <QVector>
#include <functional>
Include dependency graph for pslgminsize.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  mesh::pslg::MinSizePolicy
 Policy for minimum-feature-size conditioning. More...
 
struct  mesh::pslg::Violation
 
struct  mesh::pslg::ConditionReport
 

Namespaces

namespace  mesh
 
namespace  mesh::pslg
 

Enumerations

enum class  mesh::pslg::ViolationCause {
  mesh::pslg::ShortSegment ,
  mesh::pslg::CloseFeatures ,
  mesh::pslg::SmallAngle ,
  mesh::pslg::SubScaleRing ,
  mesh::pslg::IdentityMerged
}
 

Functions

QString mesh::pslg::violationCauseName (ViolationCause c)
 
QVector< Violationmesh::pslg::analyseLocalFeatureSize (const QVector< QPolygonF > &domains, const QVector< QVector< QPointF > > &holeRings, const QVector< ConstraintSegment > &segs, const QVector< SteinerPoint > &pts, double h, int maxReported=200)
 Analyse the PSLG's local feature size. Read-only.
 
bool mesh::pslg::conditionMinSize (QVector< QPolygonF > *domains, QVector< QVector< QPointF > > *holeRings, QVector< ConstraintSegment > *segs, QVector< SteinerPoint > *pts, const MinSizePolicy &policy, ConditionReport *report, const std::function< bool()> &isCancelled={})
 Condition the PSLG so it can support cells of size policy.minCellSize.
 

Detailed Description

Author
Caleb Buahin caleb.nosp@m..bua.nosp@m.hin@g.nosp@m.mail.nosp@m..com
Date
2026
License\n GPL-3.0-or-later

Minimum-feature-size conditioning for the input PSLG (MIN_CELL_SIZE_ENFORCEMENT_PLAN_2026-08-17.md §4).

WHY THIS EXISTS. Ruppert refinement — what Triangle's -q implements — produces output edge lengths of Θ(lfs), where lfs is the LOCAL FEATURE SIZE of the input: the radius of the smallest disk centred at a point that touches two non-incident input features. Triangle therefore cannot emit cells much smaller than the input demands, and it cannot emit them much larger either. Constraining lines and polylines routinely carry features far below any usable cell size — GIS-digitised vertices centimetres apart, two conduits passing within a hair, breaklines grazing the domain edge, conduits meeting at a manhole at 10° — and every one of those forces cells at that scale. On the explicit 2D marcher a single sliver sets the CFL timestep for the whole domain.

So enforcing a minimum CELL size means enforcing a minimum FEATURE size on the PSLG, which necessarily changes the geometry slightly. That is the whole bargain of this file. No amount of work inside Triangle can substitute: an area floor in the refinement hook only stops FURTHER subdivision (Triangle never coarsens), and post-mesh cleanup cannot touch a sliver whose edges are constrained.

WHAT IS GUARANTEED after conditionMinSize() returns true:

  1. every pair of surviving PSLG vertices is >= weldRadius apart, EXCEPT pairs of distinct tagged vertices, which are never merged into each other (they are coupling identities) and are reported instead;
  2. no vertex lies within weldRadius of a non-incident segment, EXCEPT around a pair of protected identities that were already closer than that: neither may move, and once each has been spliced into the other's path the path doubles back past the first within weldRadius. Those locations are reported as CloseFeatures residuals instead (test_pslgminsize.cpp::condition_weldingProperties);
  3. no vertex moved further than weldRadius, and tagged vertices did not move at all;
  4. no constrained segment is shorter than minSegmentLen, except where the maxDeviation cap forced one to survive (counted and reported);
  5. the conditioned PSLG is no more degenerate than the input: no more proper segment crossings, and no more duplicate, zero-length, or collinearly-overlapping segments either. Crossings alone are not enough — every degenerate case has a zero orientation determinant and so is invisible to a crossing test, while Triangle aborts on it all the same.

On failure to reach (5) the function restores every argument and returns false: a slow correct mesh beats a Triangle abort.

DEVIATION FROM THE PLAN, deliberate. The plan specified snap-rounding to a quantized grid. This implements the equivalent guarantee with greedy priority-ordered welding instead (the greedyMinSeparation idiom already in pslgprep.h), because grid quantization moves EVERY vertex — including the tagged SWMM nodes that are coupling locations — whereas welding leaves all representatives, and therefore every tagged node, exactly where it was. Same separation property, strictly less geometry disturbance, less code.