Skip to content

Commit ae5b7fa

Browse files
committed
refactor: simplify circle fast-path internals
- Extract _affine_major_scale() for the SVD major-axis stretch duplicated by the fast-path and _circle_buffer_quad_segs. - Fast-path: coerce only the first radius value (gate guarantees uniform+finite) instead of re-coercing the whole column — drops an O(n) pass at HD scale. - Drop a comment that restated the adjacent log line; drop a redundant bool().
1 parent afa8d15 commit ae5b7fa

2 files changed

Lines changed: 11 additions & 6 deletions

File tree

src/spatialdata_plot/pl/_datashader.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,11 @@ def _pad_degenerate_extent(ext: list[Any]) -> list[Any]:
723723
return [ext[0] - 0.5, ext[1] + 0.5] if ext[1] == ext[0] else ext
724724

725725

726+
def _affine_major_scale(tm: np.ndarray) -> float:
727+
"""Largest singular value of the affine's linear part — a circle's major-axis scale under ``tm``."""
728+
return float(np.linalg.svd(tm[:2, :2], compute_uv=False).max())
729+
730+
726731
def _circle_quad_segs(max_radius_px: float) -> int:
727732
"""Segments-per-quadrant for buffering circles to polygons, by the largest disc's pixel radius.
728733
@@ -747,8 +752,7 @@ def _circle_buffer_quad_segs(
747752
transform turns the circle into an ellipse, so size to its largest stretch (major axis).
748753
"""
749754
linear = tm[:2, :2]
750-
stretch = float(np.linalg.svd(linear, compute_uv=False).max()) # circle -> ellipse major-axis scale
751-
r_t = float(max_radius) * stretch
755+
r_t = float(max_radius) * _affine_major_scale(tm) # circle -> ellipse major-axis scale
752756
xy_t = centroids_xy @ linear.T + tm[:2, 2]
753757
ext_w = (xy_t[:, 0].max() + r_t) - (xy_t[:, 0].min() - r_t)
754758
ext_h = (xy_t[:, 1].max() + r_t) - (xy_t[:, 1].min() - r_t)

src/spatialdata_plot/pl/render.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
resolve_color,
4545
)
4646
from spatialdata_plot.pl._datashader import (
47+
_affine_major_scale,
4748
_ax_show_and_transform,
4849
_build_ds_colorbar,
4950
_circle_buffer_quad_segs,
@@ -592,7 +593,7 @@ def _circles_render_as_points(shapes: gpd.GeoDataFrame, is_point: Any, render_pa
592593
or len(shapes) <= _CIRCLE_FAST_PATH_MIN
593594
or render_params.outline_alpha[0] > 0
594595
or render_params.outline_alpha[1] > 0
595-
or not bool(is_point.all())
596+
or not is_point.all()
596597
):
597598
return False
598599
radius = pd.to_numeric(shapes["radius"], errors="coerce").to_numpy()
@@ -765,7 +766,6 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
765766
)
766767

767768
if render_params.as_points:
768-
# Fast mode: draw one dot per shape at its centroid instead of its geometry.
769769
logger.info("`as_points=True`: rendering shape centroids; `outline_*` and `shape` are ignored.")
770770
centroids = shapes.geometry.centroid # intrinsic; transform so dots land under non-identity transforms
771771
_draw_centroids(trans.transform(np.column_stack([centroids.x.to_numpy(), centroids.y.to_numpy()])))
@@ -806,8 +806,9 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
806806
# pixel) the same as spread points, skipping the per-circle buffer/polygon-aggregation cost.
807807
if _circles_render_as_points(shapes, is_point, render_params):
808808
logger.info(f"Rendering {len(shapes)} uniform circles as datashader points (fast path).")
809-
stretch = float(np.linalg.svd(tm[:2, :2], compute_uv=False).max()) # circle radius in CS units
810-
radius_cs = float(pd.to_numeric(shapes["radius"], errors="coerce").iloc[0]) * render_params.scale * stretch
809+
# radius is gate-guaranteed uniform + finite, so coerce only the first value (avoids an O(n) pass).
810+
radius_one = float(pd.to_numeric(shapes["radius"].iloc[:1], errors="coerce").iloc[0])
811+
radius_cs = radius_one * render_params.scale * _affine_major_scale(tm)
811812
xy = trans.transform(np.column_stack([_geometry.x.to_numpy(), _geometry.y.to_numpy()]))
812813
_draw_centroids(xy, radius=radius_cs)
813814
return

0 commit comments

Comments
 (0)