src.utils.curve.simple_polyline_sanitizer

Module Attributes

DEFAULT_SPLIT_ZJUMP_BRIDGES_THRESHOLD

Default threshold (in radians) for the vertical-grade test of split_zjump_bridges().

Classes

SimplePolylineSanitizer([merge_radius, ...])

src.utils.curve.simple_polyline_sanitizer.DEFAULT_SPLIT_ZJUMP_BRIDGES_THRESHOLD = 0.7853981633974483

Default threshold (in radians) for the vertical-grade test of split_zjump_bridges(). A windowed-grade value greater than \(\tan(\text{threshold})\) flags a vertical column. Default \(\pi/4 = 45^{\circ}\), whose tangent is \(1.0\) (the pre-rename hardcoded value).

class src.utils.curve.simple_polyline_sanitizer.SimplePolylineSanitizer(merge_radius=None, z_tolerance=2.0, min_voxel_size=0.1)
Author:

Alberto M. Esmoris Pena

Polyline-sanitization helper for the curves produced by SimpleCurveExtractor. Runs the Z-jump bridge split, Z-reversal split, consecutive-vertex dedup and self-intersection clip cascade used by the clustering orchestrator.

Instances are constructed once per consuming SCE pipeline phase from the private orchestrator helpers _refine_coverage, _step_merge_resolve_split_pipeline, _merge_resolve_sanitize_triplet and _step_global_optimization; they take a snapshot of the relevant SCE attributes (merge_radius, z_tolerance, min_voxel_size) and run the split / dedup / self-intersection-clip cascade without further coupling to the orchestrator. See SimpleCurveExtractor for the upstream pipeline that produces smooth_curves and metadata.

Variables:
  • merge_radius (float or None) – Spatial radius governing the long-bridge XY threshold derivation in split_zjump_bridges(). None falls back to the legacy 10.0 m default at use time.

  • z_tolerance (float) – Z-tolerance (3-sigma bound) used as the long-bridge dz threshold by split_zjump_bridges().

  • min_voxel_size (float) – Floor scale used by split_zjump_bridges() to keep the bridge-XY threshold portable across coordinate systems.

__init__(merge_radius=None, z_tolerance=2.0, min_voxel_size=0.1)

Snapshot the SCE configuration relevant to the polyline-sanitisation cascade.

sanitize_polylines(smooth_curves, metadata)

Final geometric cleanup of all polylines before output. Performs two operations:

  1. Remove self-intersections by detecting where the polyline crosses itself and clipping the loop (keeping the shorter path).

  2. Remove consecutive duplicate points that could cause degenerate zero-length segments.

Parameters:
  • smooth_curves (list of dict) – Chained curve segments.

  • metadata (list of dict) – Per-chain metadata dicts.

Returns:

Sanitized (smooth_curves, metadata).

Return type:

tuple

split_zjump_bridges(smooth_curves, metadata, max_dz_split)

Split features at Z-jump bridge segments.

Uses a dual criterion to detect bridges:

  1. Any bridge: XY gap \(> 3\) times the median point spacing AND \(|\Delta z| >\) max_dz_split.

  2. Long bridge: XY gap \(> 10\) m AND \(|\Delta z| > 2\) m.

The long-bridge criterion catches moderate Z-jumps that are visually prominent because the bridge spans a large horizontal distance.

Parameters:
  • smooth_curves (list of dict) – Curve segments.

  • metadata (list of dict) – Per-segment metadata dicts.

  • max_dz_split (float) – Z-difference threshold for the general bridge criterion.

Returns:

(smooth_curves, metadata) after split.

Return type:

tuple

static split_z_reversals(smooth_curves, metadata, max_reversal)

Split features at Z-reversals.

A Z-reversal occurs when the elevation profile of a polyline climbs to a peak and then drops back by more than max_reversal. This detects multi-terrace chaining artifacts where the consecutive-point dZ is small but the cumulative pattern visits different elevation levels.

Uses drawdown analysis: the running maximum of Z is computed, and the feature is split wherever \(Z_{\max}^{\text{run}}(i) - Z(i) > \Delta z_{\text{rev}}\).

Parameters:
  • smooth_curves (list of dict) – Curve segments.

  • metadata (list of dict) – Per-segment metadata dicts.

  • max_reversal (float) – Maximum allowed drawdown.

Returns:

(smooth_curves, metadata) after split.

Return type:

tuple

static emit_split_segments(pts, slopes, meta, bounds, out_c, out_m)

Emit one polyline per consecutive bounds pair into the caller’s out_c / out_m lists. The caller is responsible for filling bounds (must be [0, ..., len(pts)]). Sub-segments shorter than 2 vertices are skipped. Each emitted polyline carries the same slopes slice and a copy of meta with LENGTH_3D / LENGTH_2D overwritten with the sub-polyline’s own length. Extracted in iter4 (Phase 1, L-20) to fold the duplicated 18-line tail of split_zjump_bridges() and split_z_reversals() into a single helper.

Parameters:
  • pts (np.ndarray) – Source polyline points (N x 3).

  • slopes (np.ndarray) – Source per-vertex slopes.

  • meta (dict) – Source feature metadata dict.

  • bounds (list of int) – Sorted list of split indices including the leading 0 and trailing len(pts).

  • out_c (list of dict) – Destination list for emitted curve dicts (mutated in place).

  • out_m (list of dict) – Destination list for emitted metadata dicts (mutated in place).

Returns:

None.

Return type:

NoneType

static slopes_from_z_diffs(pts)

Compute per-vertex slope (project-canonical slope = dZ / ds_3D = sin(theta)) from the consecutive-vertex differences of a polyline. Per-vertex slope is the average of adjacent segment slopes; the first and last vertex copy the slope of the touching segment. Bounded in \([-1, 1]\); well-defined at near-vertical segments where tan(theta) = dZ/dxy would diverge. Extracted in iter4 (Phase 1, L-19) to fold the duplicated formula at the SCE helpers _drop_hallucinated_features and _concatenate_orphan_into_neighbour.

Parameters:

pts (np.ndarray) – Polyline points (N x C, C >= 3).

Returns:

Per-vertex slopes (length N).

Return type:

np.ndarray

static dedup_points(pts, slopes)

Remove consecutive duplicate points from a polyline. Two points are considered duplicate when their Euclidean distance is below \(10^{-9}\).

Parameters:
  • pts (np.ndarray) – Polyline points (N x 3).

  • slopes (np.ndarray) – Per-point slopes (N,).

Returns:

Deduplicated (pts, slopes).

Return type:

tuple

static remove_self_ix(pts, slopes)

Detect and remove self-intersections in a polyline by iteratively finding the first crossing and clipping out the loop.

The grid-build, first-hit scan, and intersection point are computed by the pyvl3dpp.curve_self_intersection_{f,d} C++ binding. The binding returns (found, i, j, ix, iy, iz) with i < j and the intersection point already computed; the outer 20-pass loop and the polyline clip remain in Python. When segments i–(i+1) and j–(j+1) cross, the loop from i+1 to j is removed: the polyline jumps directly from point i to the intersection point to point j+1.

Precision dispatch follows the pts.dtype: float32 routes to _f, everything else to _d (mixed-precision arrays are cast to float64 to preserve numeric fidelity).

Parameters:
  • pts (np.ndarray) – Polyline points (N x 3).

  • slopes (np.ndarray) – Per-point slopes (N,).

Returns:

(cleaned pts, cleaned slopes, changed flag).

Return type:

tuple