stentfit.core.plotting

Functions

_downsample_df(→ pandas.DataFrame)

Randomly subsample rows so a plot draws at most max_display points.

plot_points_3d_html(→ str)

Draw a point cloud as an interactive 3D scatter and save it as HTML.

_skeleton_edge_segments(→ tuple[numpy.ndarray, ...)

Build the x/y/z arrays to draw every skeleton edge as one Plotly line trace.

plot_skeleton_html(→ str)

Draw the 3D skeleton graph as edges and node-type markers, save as HTML.

plot_skeleton_with_cloud_html(→ str)

Draw the final 3D skeleton overlaid on a sparse stent surface cloud, as HTML.

plot_splines_html(→ str)

Draw every fitted spline curve in 3D, each in its own color, as HTML.

plot_ring_dips_html(→ str)

Draw the ring-boundary dip detection profile and save it as HTML.

plot_thickness_diagnostics_html(→ str)

plot_ring_convergence_html(→ str)

Draw one ring's auto-tune convergence (or a quality summary) and save it as HTML.

plot_ring_skeleton_2d_html(→ str)

Draw one ring's flat 2D skeleton over its surface points, with any

_render_ring_2d(→ str)

Render one ring's current 2D skeleton via plot_ring_skeleton_2d_html().

_to_arc_z(→ tuple[numpy.ndarray, numpy.ndarray])

Unroll 3D points onto the (z, arc) plane, recomputing angle from x/y.

_break_seam(→ tuple[numpy.ndarray, numpy.ndarray])

Insert a NaN wherever an unrolled curve jumps across the arc seam.

_plotly_decode(→ numpy.ndarray)

Decode one Plotly-exported array back into a plain numpy array.

_load_convergence(→ dict | None)

Re-extract the tuning data plotted in a saved ring convergence HTML file.

_hue_gap(→ float)

Circular distance between two hues in [0, 1).

_band_conv(→ tuple[str | None, int | None])

Resolve the convergence-plot file and ring ID for the k-th ring band.

plot_skeleton_splines_2d(→ dict)

Draw the unrolled 2D splines over the stent cloud, with per-ring tuning

plot_skeleton_splines_trimesh(→ trimesh.Trimesh | None)

Build a 3D tube mesh of every fitted spline and save it as GLB + HTML.

Module Contents

stentfit.core.plotting._downsample_df(df: pandas.DataFrame, max_display: int | None, random_state: int = 0) pandas.DataFrame[source]

Randomly subsample rows so a plot draws at most max_display points.

Parameters:
  • df – Rows to subsample.

  • max_display – Maximum rows to keep. None or a value at least as large as len(df) returns df unchanged.

  • random_state – Seed for the row sampling, for repeatable plots.

Returns:

df itself, or a random max_display-row subset of it.

stentfit.core.plotting.plot_points_3d_html(df: pandas.DataFrame, id_col: str, out_path: str, color_col: str | None = None, max_display: int = 40000, title: str = '', point_size: float = 1, categorical: bool = False) str[source]

Draw a point cloud as an interactive 3D scatter and save it as HTML.

df is downsampled to max_display points first, so large clouds stay responsive in the browser. Coloring has three modes: no color_col draws every point in one flat color; color_col with categorical=True draws one trace per label with its own legend entry; color_col without categorical draws a single trace with a continuous colorbar.

Parameters:
  • df – Point cloud with at least x, y, z, and id_col columns.

  • id_col – Column shown as the point ID on hover.

  • out_path – File path the HTML view is written to.

  • color_col – Column used to color the points. None disables coloring.

  • max_display – Maximum number of points drawn, downsampled if df is larger.

  • title – Plot title. The shown/total point count is appended automatically.

  • point_size – Marker size for the scatter points.

  • categorical – Treat color_col as discrete labels instead of a continuous value.

Returns:

out_path, for chaining into a caller’s own return value.

stentfit.core.plotting._skeleton_edge_segments(skeleton_df: pandas.DataFrame) tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray][source]

Build the x/y/z arrays to draw every skeleton edge as one Plotly line trace.

Each edge contributes its two endpoints followed by a NaN, which breaks the line so Plotly draws many disconnected segments from a single Scatter3d trace instead of one per edge. Each undirected edge (neighbor_ids is stored both ways) is only emitted once.

Parameters:

skeleton_df – Skeleton graph with skeleton_point_id, x, y, z, and neighbor_ids columns.

Returns:

(xe, ye, ze) coordinate arrays, NaN-separated, ready to pass straight to go.Scatter3d(mode='lines').

stentfit.core.plotting.plot_skeleton_html(skeleton_df: pandas.DataFrame, out_path: str, title: str = 'Skeleton', max_display: int = 40000) str[source]

Draw the 3D skeleton graph as edges and node-type markers, save as HTML.

Every edge is drawn once as a single line trace (_skeleton_edge_segments()); nodes are downsampled to max_display and colored by node_type (line, junction, endpoint, isolated), each as its own legend-toggleable trace.

Parameters:
  • skeleton_df – Skeleton graph with x, y, z, skeleton_point_id, degree, node_type, and neighbor_ids columns.

  • out_path – File path the HTML view is written to.

  • title – Plot title.

  • max_display – Maximum number of nodes drawn, downsampled if skeleton_df is larger. Edges are always drawn in full.

Returns:

out_path, for chaining into a caller’s own return value.

stentfit.core.plotting.plot_skeleton_with_cloud_html(skeleton_df: pandas.DataFrame, stent_df: pandas.DataFrame, out_path: str, max_cloud: int = 40000) str[source]

Draw the final 3D skeleton overlaid on a sparse stent surface cloud, as HTML.

Both the surface cloud and the skeleton nodes are downsampled to max_cloud points; skeleton edges are always drawn in full (_skeleton_edge_segments()). The cloud is drawn faint and small so the skeleton stays the clear focal point.

Parameters:
  • skeleton_df – Final 3D skeleton graph with x, y, z, skeleton_point_id, and neighbor_ids columns.

  • stent_df – Stent surface point cloud with x, y, z, and point_id columns.

  • out_path – File path the HTML view is written to.

  • max_cloud – Maximum number of points drawn for the surface cloud and for the skeleton nodes, each downsampled independently.

Returns:

out_path, for chaining into a caller’s own return value.

stentfit.core.plotting.plot_splines_html(splines: list, out_path: str, n_eval: int = 100) str[source]

Draw every fitted spline curve in 3D, each in its own color, as HTML.

Each spline is evaluated at n_eval points along its parameter range (scipy.interpolate.splev); a curve with no fitted spline (the polyline fallback from fit_curve_spline()) is drawn from its raw control points instead. None entries (curves where fitting produced nothing) are skipped.

Parameters:
  • splines – Per-curve fit results from fit_skeleton_splines().

  • out_path – File path the HTML view is written to.

  • n_eval – Number of points each spline is evaluated at for drawing.

Returns:

out_path, for chaining into a caller’s own return value.

stentfit.core.plotting.plot_ring_dips_html(ring_res: dict, out_path: str) str[source]

Draw the ring-boundary dip detection profile and save it as HTML.

Plots the smoothed points-per-slice curve along z, marks the candidate dips and the depth cutoff used to filter them, shades each detected ring as an alternating background band labelled ring i, and draws a vertical line at each boundary that was actually used to cut the stent into rings.

Parameters:
  • ring_res – Dict returned by find_rings(); must have dip_z_centers, dip_counts_smoothed, dip_indices, dip_depth_thresh, and optionally n_bands / boundary_z.

  • out_path – File path the HTML view is written to.

Returns:

out_path, for chaining into a caller’s own return value.

stentfit.core.plotting.plot_thickness_diagnostics_html(df_thick: pandas.DataFrame, r: numpy.ndarray, out_path: str, strut_thickness: float) str[source]
stentfit.core.plotting.plot_ring_convergence_html(history: pandas.DataFrame | None, out_path: str, ring_id: int, quality_report: dict | None = None, pps: float | None = None, dil_px: int | None = None) str[source]

Draw one ring’s auto-tune convergence (or a quality summary) and save it as HTML.

Three cases: with a non-empty history (from tune_skeleton_params()), draws the total/defect/quality error trajectory across tuning steps. Without a history but with a quality_report, draws a bar chart of the defect counts instead (used for the fixed-params, no-auto-tune case). With neither, draws a placeholder noting auto-tune was off.

Parameters:
  • history – Per-step tuning history from tune_skeleton_params(). None or empty falls back to the quality-summary or placeholder case.

  • out_path – File path the HTML view is written to.

  • ring_id – Ring identifier, used in the plot title.

  • quality_report – Dict from check_skeleton_quality(), used for the quality-summary bar chart when history is unavailable.

  • ppspixels_per_strut used, shown in the quality-summary title if given.

  • dil_pxdilate_px used, shown in the quality-summary title if given.

Returns:

out_path, for chaining into a caller’s own return value.

stentfit.core.plotting.plot_ring_skeleton_2d_html(arc: numpy.ndarray, z: numpy.ndarray, surface_arc: numpy.ndarray, surface_z: numpy.ndarray, out_path: str, ring_label: str, ring_band: tuple[float, float] | None = None, changed_idx: numpy.ndarray | None = None, quality_report: dict | None = None, title: str = '') str[source]

Draw one ring’s flat 2D skeleton over its surface points, with any flagged defects overlaid, and save it as HTML.

The surface points are cropped to ring_band first, if given, so a ring skeletonised with a z-halo is shown next to only its own surface band. When quality_report is passed, its bad connections, loops, and empty regions are drawn as markers, tagged with the nearest skeleton point’s index so they line up with the manual-edit prompts. When changed_idx is passed, those skeleton points are highlighted, useful for showing what a manual edit changed.

Parameters:
  • arc – Flat arc-coordinates of the ring’s 2D skeleton.

  • z – Flat z-coordinates of the ring’s 2D skeleton.

  • surface_arc – Flat arc-coordinates of the ring’s surface points.

  • surface_z – Flat z-coordinates of the ring’s surface points.

  • out_path – File path the HTML view is written to.

  • ring_label – Ring label used in the default title.

  • ring_band(z_lo, z_hi) the surface points are cropped to. None shows every surface point passed in.

  • changed_idx – Skeleton point indices to highlight as changed.

  • quality_report – Dict from check_skeleton_quality(); its bad_edge_xy, loop_points_xy, and empty_xy are drawn as defect markers, and the issue count is appended to the title.

  • title – Plot title. ring_label and the issue count are used if empty.

Returns:

out_path, for chaining into a caller’s own return value.

stentfit.core.plotting._render_ring_2d(ring_2d: dict, label: str, plots_dir: str, changed_idx: numpy.ndarray | None = None, suffix: str | None = None) str[source]

Render one ring’s current 2D skeleton via plot_ring_skeleton_2d_html().

Used by the interactive edit loop to preview a tentative edit: with suffix='edited', the file is named <label>_edited_<rec['n_edits']>.html instead of <label>.html, so each edit gets its own preview without overwriting the original.

Parameters:
  • ring_2d – Per-ring 2D skeletons, keyed by label.

  • label – Ring label to render (e.g. "ring_01").

  • plots_dir – Folder the HTML view is written into.

  • changed_idx – Skeleton point indices to highlight as changed.

  • suffix'edited' names the file after the ring’s current edit count instead of its plain label.

Returns:

Path to the written HTML file.

stentfit.core.plotting._to_arc_z(x: numpy.ndarray, y: numpy.ndarray, z: numpy.ndarray, r_mid: float) tuple[numpy.ndarray, numpy.ndarray][source]

Unroll 3D points onto the (z, arc) plane, recomputing angle from x/y.

Unlike open_stent_to_plane(), which reads a theta column directly, this recomputes it from x/y via arctan2 — used for spline points, which only have xyz coordinates.

Parameters:
  • x – X-coordinates.

  • y – Y-coordinates.

  • z – Z-coordinates.

  • r_mid – Mid-wall radius, used to convert angle to arc length.

Returns:

(z, arc) coordinate arrays.

stentfit.core.plotting._break_seam(z_ax: numpy.ndarray, arc: numpy.ndarray, thresh: float) tuple[numpy.ndarray, numpy.ndarray][source]

Insert a NaN wherever an unrolled curve jumps across the arc seam.

A curve that crosses the seam (e.g. from +circumference/2 back to -circumference/2) would otherwise be drawn as one long spurious line all the way across the plot. Any gap in arc wider than thresh is cut by inserting a NaN at that point in both arrays.

Parameters:
  • z_ax – Z-coordinates (or another axial coordinate) of the unrolled curve.

  • arc – Arc-coordinates of the unrolled curve.

  • thresh – Minimum arc jump between consecutive points that counts as a seam crossing.

Returns:

(z_ax, arc), each with a NaN inserted at every seam crossing.

stentfit.core.plotting._plotly_decode(o: dict | list) numpy.ndarray[source]

Decode one Plotly-exported array back into a plain numpy array.

Plotly’s HTML export sometimes stores array data compactly as base64-encoded typed arrays (a dict with bdata/dtype, optionally shape) instead of a plain JSON list. This reverses that encoding; anything else is passed straight to np.asarray.

Parameters:

o – A trace’s raw x/y value from the parsed Plotly JSON — either a typed-array dict or a plain list.

Returns:

The decoded array.

stentfit.core.plotting._load_convergence(path: str) dict | None[source]

Re-extract the tuning data plotted in a saved ring convergence HTML file.

Reads the Plotly figure written by plot_ring_convergence_html() back off disk: finds its embedded Plotly.newPlot(...) call with a string-aware bracket scan (so brackets inside trace names don’t confuse it), parses that JSON, and pulls out either the total/defect/ quality trajectory traces (auto-tune on) or the single quality-summary bar trace (auto-tune off), decoding any typed-array values via _plotly_decode(). Used to redraw those tuning plots as small matplotlib strips in plot_skeleton_splines_2d(), without needing the original tuning history in memory.

Parameters:

path – Path to a ring_XX_convergence.html file.

Returns:

None if the file can’t be read or parsed. Otherwise a dict with kind set to 'convergence' (plus total/defect/ quality as (x, y) arrays) or 'quality_bar' (plus x, y, colors).

stentfit.core.plotting._hue_gap(a: float, b: float) float[source]

Circular distance between two hues in [0, 1).

Hue wraps around (0 and 1 are the same color), so a plain difference would overstate the gap between hues on opposite sides of the wrap.

Parameters:
  • a – First hue, in [0, 1).

  • b – Second hue, in [0, 1).

Returns:

The shorter of the two distances around the circle.

stentfit.core.plotting._band_conv(k: int, ring_order: list | None, n_bands: int, conv_files: list[str], conv_dir: str) tuple[str | None, int | None][source]

Resolve the convergence-plot file and ring ID for the k-th ring band.

If ring_order is available and matches n_bands, the file path is built directly from the k-th ring’s ID. Otherwise, falls back to indexing into conv_files (sorted by filename) and parsing the ring ID back out of that file’s name.

Parameters:
  • k – Index of the ring band, in axial order.

  • ring_order – Ring IDs in axial order, from detect_rings() / skeletonize_rings_2d(). None or a length mismatch falls back to conv_files.

  • n_bands – Total number of ring bands.

  • conv_files – Sorted list of ring_XX_convergence.html paths, used as the fallback.

  • conv_dir – Folder the convergence files live in, used to build the path when ring_order is available.

Returns:

(path, ring_id), or (None, None) if neither source could resolve this band.

stentfit.core.plotting.plot_skeleton_splines_2d(skeleton_curves: list[list[int]], skeleton_splines: list[dict | None], stent_df: pandas.DataFrame, r_mid: float, circumference: float, ring_edges: numpy.ndarray | None, ring_order: list | None, output_dir: str, stent_name: str) dict[source]

Draw the unrolled 2D splines over the stent cloud, with per-ring tuning plots stacked above their band, and save it as a static PNG + HTML.

Curves are colored with a greedy rotating palette so any two curves that share a point differ in hue. Ring boundaries are read from stent_features.json if present (else ring_edges, else derived from stent_df’s ring_id groups) and drawn as vertical dashed lines; each ring’s saved convergence/quality-summary HTML (plot_ring_convergence_html()) is parsed back out (_load_convergence()) and redrawn as a small matplotlib strip above that ring’s band. The figure is saved as a PNG, embedded as a self-contained HTML page, and also shown inline.

Parameters:
  • skeleton_curves – Grouped point-id curves, from group_skeleton_curves().

  • skeleton_splines – Per-curve fit results, from fit_skeleton_splines().

  • stent_df – Stent surface point cloud, drawn as a grey underlay.

  • r_mid – Mid-wall radius, used to unroll splines and the cloud to (z, arc) coordinates.

  • circumference – Full circumference at r_mid, used to detect and break the seam when unrolling each spline.

  • ring_edges – Z-boundaries between rings, used if stent_features.json has no ring_boundaries.

  • ring_order – Ring IDs in axial order, used to match each band to its convergence file. None falls back to parsing the ring ID from each convergence file’s name.

  • output_dir – Folder the PNG/HTML are written into, and where stent_features.json and the per-ring convergence plots are read from.

  • stent_name – Name used to label the plot title.

Returns:

Dict with the paths to the written PNG and HTML (png, html).

stentfit.core.plotting.plot_skeleton_splines_trimesh(skeleton_splines: list[dict | None], output_dir: str, show: bool = False, tube_radius: float | None = None, sections: int = 6) trimesh.Trimesh | None[source]

Build a 3D tube mesh of every fitted spline and save it as GLB + HTML.

Each spline is evaluated (or, for the polyline fallback, taken as-is) and turned into a chain of cylinder segments, colored per-curve. The combined mesh is exported as a .glb and, where the trimesh notebook viewer supports it, as a self-contained HTML page.

Parameters:
  • skeleton_splines – Per-curve fit results, from fit_skeleton_splines().

  • output_dir – Folder the GLB and HTML are written into.

  • show – Open an interactive trimesh viewer window.

  • tube_radius – Cylinder radius for each curve. None picks it automatically as a fraction of the mesh’s bounding-box diagonal.

  • sections – Number of sides on each cylinder’s cross-section.

Returns:

The combined mesh, or None if there were no curves to draw.