src.utils.curve.simple_topological_stitcher

Classes

SimpleTopologicalStitcher([merge_radius, ...])

class src.utils.curve.simple_topological_stitcher.SimpleTopologicalStitcher(merge_radius=None, spatial_scale=0.0, sce=None)
Author:

Alberto M. Esmoris Pena

Endpoint-merger and chain-builder helper for the curves produced by SimpleCurveExtractor. Bundles the union-find endpoint merge with bridge creation, the direction-aware per-CURVE_ID chain builder, the post-chain merge cascade with its fixed-point wrapper and the sub-chain attachment helper.

Instances are constructed once per consuming SCE pipeline phase from the private orchestrator helpers _step_endpoint_merge, _step_chain_segments, _step_merge_resolve_split_pipeline, _merge_resolve_sanitize_triplet, _step_adopt_short_cids and _step_global_optimization (and the refine_coverage path on SimpleCoverageRefiner); they take a snapshot of the relevant SCE attributes (merge_radius, spatial_scale) plus a back-reference to the extractor for the _max_dz cross-class invocation. See SimpleCurveExtractor for the upstream pipeline that produces smooth_curves and metadata.

Two methods are user-mandated public entry points: merge_endpoints() (cross-CURVE_ID union-find endpoint merge that creates bridge segments) and chain_segments() (direction-aware per-CID chain builder). The remaining four methods (post_chain_merge(), attach_chain(), post_chain_merge_until_stable() and the static chain_curve_segments_extend()) are phase helpers consumed by the post-chain cascade.

Variables:
__init__(merge_radius=None, spatial_scale=0.0, sce=None)

Snapshot the SCE configuration relevant to the topological stitching cascade.

merge_endpoints(smooth_curves, metadata)

Merge curves whose segment endpoints are within ``merge_radius``. Creates bridge segments (straight lines) between merged endpoints to fill visual gaps.

Uses union-find on original CURVE_IDs from metadata so un-merged segments keep their original curve assignment. Only segments from DIFFERENT curves are merged.

Modifies smooth_curves and metadata in-place: updates CURVE_IDs and appends bridge segments.

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

  • metadata (list of dict) – Per-segment metadata dicts with CURVE_ID and SEG_ID keys.

chain_segments(smooth_curves, metadata, chain_radius)

Chain segments within each curve into continuous polylines using direction-aware topological adjacency. Only segments whose endpoints are within chain_radius (typically chain_radius_factor times the skeleton voxel size) are connected, preventing wrong long-distance connections.

Extension is direction-aware: at each step the algorithm computes the cosine between the current segment’s exit direction and each candidate neighbor’s connecting direction, picking the neighbor with the best (highest) cosine alignment. This ensures the chain follows the smoothest geometric continuation.

At T-junctions, the chain follows the longest branch. Remaining branches become separate polylines. Segments are flipped as needed so concatenated points flow continuously; bridge points are inserted across gaps.

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

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

  • chain_radius (float) – Max distance between endpoints to consider them adjacent.

Returns:

Chained (smooth_curves, metadata).

Return type:

tuple

post_chain_merge(smooth_curves, metadata, merge_radius, max_dz_override=None)

Merge chains with the same CURVE_ID whose endpoints are within merge_radius. Uses union-find on chain indices for transitive merges (A–B and B–C become one chain).

Concatenation uses a global best-first strategy: at each iteration, the remaining chain whose endpoint is closest to the current polyline head or tail is attached next, with automatic flipping to maintain continuity. A bridge point is inserted across each gap. The loop is convergent: it terminates when no remaining chain has an endpoint within merge_radius.

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

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

  • merge_radius (float) – Maximum endpoint distance for merging.

Returns:

Merged (smooth_curves, metadata).

Return type:

tuple

attach_chain(chain_pts, chain_sl, head, tail, mp, ms, best_type, svs)

Attach a sub-chain (mp, ms) to the running (chain_pts, chain_sl) polyline using one of the four best_type orientation codes used by post_chain_merge(). Extracted in iter4 (Phase 6, L-17).

Behaviour mirrors the original inline branches verbatim:

  • 'ts' – tail to mp start: append mp (no flip), update tail to mp[-1].

  • 'te' – tail to mp end: append mp[::-1] (flip), update tail to mp[0].

  • 'he' – head to mp end: prepend mp (no flip), update head to mp[0].

  • any other ('hs') – head to mp start: prepend mp[::-1] (flip), update head to mp[-1].

For each branch, a bridge is inserted in the same order as the original code when the gap between the touching endpoints exceeds 1e-3. The bridge endpoints (and the slope-segment list ordering) match the inline sequence exactly so make_bridge (now on SimpleGlobalCurveOptimizer) is invoked with the same (p0, p1, svs) tuple as before.

Parameters:
  • chain_pts (list of np.ndarray) – List of point arrays.

  • chain_sl (list of np.ndarray) – List of slope arrays.

  • head (np.ndarray) – Current head endpoint.

  • tail (np.ndarray) – Current tail endpoint.

  • mp (np.ndarray) – Sub-chain points.

  • ms (np.ndarray) – Sub-chain slopes.

  • best_type (str) – One of 'ts', 'te', 'he', or any other value (treated as 'hs') – the original code uses an else branch for 'hs', which this helper preserves.

  • svs (float) – Spatial scale forwarded to make_bridge on SimpleGlobalCurveOptimizer.

Returns:

Updated (chain_pts, chain_sl, head, tail).

Return type:

tuple

post_chain_merge_until_stable(smooth_curves, metadata, radius=None, max_dz_override=None)

Run post_chain_merge() to fixed point via the apply_until_stable helper on SimpleCurveExtractor. Wraps the lambda boilerplate that was repeated at every cascade site (eight call-sites in _step_merge_resolve_split_pipeline / _step_adopt_short_cids / _step_global_optimization). Extracted in iter4 (Phase 1, L-25).

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

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

  • radius (float or None) – Override merge radius. None falls back to self.merge_radius.

  • max_dz_override (float or None) – Optional Z-tolerance override forwarded verbatim to post_chain_merge().

Returns:

Final (smooth_curves, metadata).

Return type:

tuple

static chain_curve_segments_extend(segs, used, seg_adj, max_dz, start_k, from_end)

Extend a chain from one end of segment start_k, picking the neighbour that best continues the current direction (best 2D cosine alignment). Mutates used in place to mark visited segments.

Lifted from a closure inside chain_segments() (L-21). The arithmetic and control flow are byte-output preserving relative to the original closure; the only structural change is the bounded for _ in range(len(segs)) which terminates at the same step as the original while True because each iteration consumes one used slot.

Parameters:
  • segs (list of dict) – Per-CID segments (list of dict with 'pts', 'slopes', 'arc', 'meta_idx' keys).

  • used (list of bool) – Mutable boolean list (length n_segs) tracking visited segments; updated in place.

  • seg_adj (dict) – Endpoint adjacency map; for each segment index, a list of (neighbour, my_end, their_end) triples.

  • max_dz (float) – Max vertical distance for cross-terrace rejection.

  • start_k (int) – Index of the segment to extend from.

  • from_end (bool) – True to extend from the tail of segs[start_k], False from the head.

Returns:

Ordered list of newly chained neighbour indices.

Return type:

list of int