src.utils.curve.simple_global_curve_optimizer

Classes

SimpleGlobalCurveOptimizer([merge_radius, ...])

class src.utils.curve.simple_global_curve_optimizer.SimpleGlobalCurveOptimizer(merge_radius=None, min_segment_length=None, z_tolerance=2.0, min_voxel_size=0.1)
Author:

Alberto M. Esmoris Pena

Energy-minimising global optimiser for the polylines produced by SimpleCurveExtractor. Runs a per-vertex energy descent over candidate polylines and bridges coverage-refine sub-features into their main siblings inside each mixed CURVE_ID.

Three objectives drive the energy formulation:

  1. Maximise the count of covered curve points.

  2. Minimise non-curve traversal.

  3. Maximise smoothness (minimise local curvature).

Instances are constructed once per pipeline run from the private SCE pipeline phase _step_global_optimization; they take a snapshot of the relevant SCE configuration (merge_radius, min_segment_length, z_tolerance, min_voxel_size) and run the three-pass per-vertex descent plus the mixed-CID bridging pass 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 coverage / max-\(\Delta z\) derivation in oce_setup(). None falls back to a skeleton-voxel-scaled default at use time.

  • min_segment_length (float or None) – Bridge-distance limit used in oce_run_bridging(). None falls back to a skeleton-voxel-scaled default at use time.

  • z_tolerance (float) – Z-tolerance (3-sigma bound) used by max_dz() to derive the coverage Z-window.

  • min_voxel_size (float) – Floor scale used by max_dz() to keep the coverage Z-window portable across coordinate systems.

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

Snapshot the SCE configuration relevant to the energy descent and the mixed-CID bridging pass.

optimize_curves_energy(smooth_curves, metadata, X_orig, skel_vs, spacing)

Energy-minimizing global optimization.

Processes ALL features with per-vertex energy descent. For mixed CIDs (main + coverage_refine), additionally bridges coverage-refine features to main features using the lowest-energy path.

Three objectives: 1. Maximize covered curve points 2. Minimize non-curve traversal 3. Maximize smoothness (min curvature)

Implemented as a thin driver that orchestrates four private phase helpers (Phase 6 CL-6c decomposition; behaviour is byte-output preserved relative to the pre-decomposition monolith):

  1. oce_setup() — Phase 1 setup, builds the KD-tree on the downsampled curve-class points and resolves the energy weights.

  2. oce_run_descent() — Phase 2 per-vertex descent (mutates smooth_curves in place).

  3. oce_run_bridging() — Phase 3 bridging for mixed CIDs (mutates smooth_curves and metadata in place; returns the set of absorbed indices).

  4. oce_cleanup() — Phase 4 list filter by absorbed indices.

Parameters:
  • smooth_curves (list of dict) – Output curves.

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

  • X_orig (np.ndarray) – Centered curve points (Nx3).

  • skel_vs (float) – Skeleton voxel size.

  • spacing (float) – Output point spacing.

Returns:

Optimized (curves, metadata).

Return type:

tuple

oce_setup(X_orig, skel_vs)

Phase 1 of optimize_curves_energy().

Build the KD-tree on the downsampled curve-class points and resolve the per-iteration energy weights (coverage radius, max dZ, alpha, gamma, curvature threshold).

Byte-output-preserving: every numerical expression is reproduced verbatim from the pre-decomposition monolith; the only change is the packaging of the resolved scalars and arrays into a context dictionary.

Parameters:
  • X_orig (np.ndarray) – Centered curve points (Nx3).

  • skel_vs (float) – Skeleton voxel size.

Returns:

Context dict with keys ds_pts, ds_tree, ds_z, max_dz, snap_r, alpha, gamma, pi_12, mr; or None when downsampling produced an empty point set (caller short-circuits).

Return type:

dict or None

oce_run_descent(smooth_curves, ctx, spacing)

Phase 2 of optimize_curves_energy().

Per-vertex energy descent over all features and all step sizes. Each accepted descent at smooth_curves[fi] is committed in place via rebuild_feature(); rejected steps are reverted to the pre-step vertex.

Byte-output-preserving: the outer step-size list, the per-curve loop ordering, the per-vertex range(1, n - 1) loop, the inline coverage / curvature / centroid / displacement formulas, and the per-curve coverage-comparison fallback are all reproduced verbatim. The mutable smooth_curves list is the SAME instance the driver passed in, so vertex acceptance is observed by the caller.

Parameters:
  • smooth_curves (list of dict) – Output curves (mutated).

  • ctx (dict) – Context dict from oce_setup().

  • spacing (float) – Output point spacing.

Returns:

None — mutation is in place.

oce_run_bridging(smooth_curves, metadata, ctx, skel_vs, spacing)

Phase 3 of optimize_curves_energy().

Bridge coverage-refine features to their main siblings inside each mixed CID, scoring candidate bridges with the energy helper. The smooth_curves[best_mi] slot is replaced by the merged feature, and the absorbed coverage feature index is recorded so Phase 4 can prune it.

Byte-output-preserving: the iteration order of mixed_cids is governed by cid_has_main & cid_has_cov over the same metadata mutation (set ordering matches the pre-decomposition monolith because we receive the SAME metadata list). The KDTree.query nearest-neighbour tie-break is preserved by reusing _collect_endpoints exactly as the monolith did. The inline np.sum(np.sqrt(np.sum(...))) length writes are retained verbatim (NOT replaced with _polyline_lengths) to avoid the float-reduction-order divergence flagged in the Phase-1 SC-09 review.

Parameters:
  • smooth_curves (list of dict) – Output curves (mutated).

  • metadata (list of dict) – Per-curve metadata (mutated).

  • ctx (dict) – Context dict from oce_setup().

  • skel_vs (float) – Skeleton voxel size (used only for the bridge_limit fallback).

  • spacing (float) – Output point spacing.

Returns:

Tuple (absorbed, n_bridged) where absorbed is the set of indices into the CALLER’s smooth_curves / metadata lists that should be filtered in Phase 4.

Return type:

tuple

static oce_cleanup(smooth_curves, metadata, absorbed, n_bridged)

Phase 4 of optimize_curves_energy().

Filter smooth_curves and metadata by the absorbed index set produced in Phase 3 and log the bridge count.

Byte-output-preserving: the index-keyed comprehensions reproduce the pre-decomposition loop exactly; the resulting NEW lists replace the caller’s bindings via the return tuple.

Parameters:
  • smooth_curves (list of dict) – Output curves.

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

  • absorbed (set) – Indices to drop.

  • n_bridged (int) – Bridge count, for logging.

Returns:

Tuple (smooth_curves, metadata).

Return type:

tuple

static grid_subsample(pts, cell_size)

Spatial grid subsampling: retain one point per axis-aligned cell. Fully vectorized with numpy for performance on large point clouds.

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

  • cell_size (float) – Cell edge length.

Returns:

Subsampled points (M x 3).

Return type:

np.ndarray

static z_monotone_ok(pts, i, new_z)

Check that new_z preserves local Z monotonicity at vertex i.

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

  • i (int) – Vertex index.

  • new_z (float) – Candidate Z value.

Returns:

True if Z-trend is preserved.

Return type:

bool

static vertex_energy(pts, i, ds_tree, ds_z, snap_r, max_dz, alpha, gamma)

Energy at vertex i of polyline pts.

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

  • i (int) – Vertex index.

  • ds_tree – KDTree on downsampled curve-class points (XY).

  • ds_z (np.ndarray) – Z values of downsampled pts.

  • snap_r (float) – Search radius.

  • max_dz (float) – Max Z-difference.

  • alpha (float) – Coverage weight.

  • gamma (float) – Curvature weight.

Returns:

Scalar energy.

Return type:

float

static rebuild_feature(pts, spacing)

Rebuild arc-lengths and slopes for a polyline after modification.

static make_bridge(p1, p2, spacing)

Create a smooth bridge between two 3D points.

Instead of a 2-point bridge (which creates an abrupt Z-jump), linearly interpolates N points so that each step has a small dZ. The number of points is determined by the 3D distance and the given spacing.

Parameters:
  • p1 (np.ndarray) – Start point (3D).

  • p2 (np.ndarray) – End point (3D).

  • spacing (float) – Target point spacing.

Returns:

(bridge_pts, bridge_slopes) where bridge_pts is (N x 3) and bridge_slopes is (N,).

Return type:

tuple

static endpoint_tangent(pts, is_end, n=5)

Estimate the tangent direction at one end of a polyline using the last n points.

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

  • is_end (bool) – True for the tail, False for the head.

  • n (int) – Number of trailing points.

Returns:

Unit direction vector (3D), or zero vector if degenerate.

Return type:

np.ndarray

max_dz(radius)

Compute the maximum Z-difference threshold for merging/chaining operations. Uses the smaller of \(3 \sigma_z\) and the given radius, with a floor of \(10 \times\) min_voxel_size to prevent degenerate behaviour when ``z_tolerance`` is zero.

Parameters:

radius (float) – The spatial radius context.

Returns:

Maximum Z-difference.

Return type:

float