Skip to content

Commit 17d3155

Browse files
committed
add --spline-subdiv
1 parent da35272 commit 17d3155

3 files changed

Lines changed: 54 additions & 9 deletions

File tree

src/cardiotensor/scripts/visualize_streamlines.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ def script() -> None:
139139
default=None,
140140
help="Path to save a PNG screenshot, no file is saved if omitted",
141141
)
142+
parser.add_argument(
143+
"--spline-subdiv",
144+
type=int,
145+
default=16,
146+
help="Spline subdivisions for tube rendering (higher = smoother, heavier)",
147+
)
142148
parser.add_argument("--width", type=int, default=800, help="Window width in pixels")
143149
parser.add_argument(
144150
"--height", type=int, default=800, help="Window height in pixels"
@@ -221,6 +227,7 @@ def script() -> None:
221227
screenshot_path=args.screenshot,
222228
window_size=(args.width, args.height),
223229
colormap=chosen_cmap,
230+
spline_subdiv=args.spline_subdiv,
224231
)
225232

226233

src/cardiotensor/visualization/fury_plotting_streamlines.py

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ def __init__(
9999
window_size,
100100
lut,
101101
background_color="black",
102+
spline_subdiv=16,
102103
):
103104
self.streamlines_xyz = streamlines_xyz
104105
self.color_values = color_values
@@ -113,6 +114,7 @@ def __init__(
113114

114115
# thickness state
115116
self.linewidth = max(1.0, float(line_width)) # used for both line and tube
117+
self.spline_subdiv = spline_subdiv
116118

117119
self.scale_bar = None
118120
self.scale_bar_on = False
@@ -154,11 +156,12 @@ def _build_pipeline(self):
154156
self.streamlines_xyz,
155157
colors=self.flat_vals,
156158
linewidth=self.linewidth,
157-
spline_subdiv=0,
159+
spline_subdiv=self.spline_subdiv,
158160
lookup_colormap=self.lut,
159-
lod=False, # <—
160-
lod_points=20000, # optional, tune
161-
lod_points_size=2, # optional, tune
161+
tube_sides=20,
162+
# lod=False, # <—
163+
# lod_points=20000, # optional, tune
164+
# lod_points_size=2, # optional, tune
162165
)
163166
else:
164167
self.actor0 = actor.line(
@@ -169,14 +172,16 @@ def _build_pipeline(self):
169172
)
170173

171174
self.scene.add(self.actor0)
175+
self._style_streamline_actor()
176+
172177

173178
# fast actor for interaction (cheap line rendering)
174179
self.actor_fast = actor.line(
175180
self.streamlines_xyz,
176181
colors=self.flat_vals, # pass scalars
177182
linewidth=1.0, # very light
178183
lookup_colormap=self.lut,
179-
lod=False, # make it deterministic
184+
# lod=False, # make it deterministic
180185
fake_tube=True, # a bit of shading to hint tubes
181186
)
182187
self.actor_fast.SetVisibility(False)
@@ -200,6 +205,14 @@ def _build_pipeline(self):
200205
self.mapper0 = self.actor0.GetMapper()
201206
self.mapper0.RemoveAllClippingPlanes() # start with clipping off
202207

208+
def _style_streamline_actor(self):
209+
prop = self.actor0.GetProperty()
210+
prop.SetInterpolationToPhong()
211+
prop.SetAmbient(0.1)
212+
prop.SetDiffuse(0.95)
213+
prop.SetSpecular(0.25)
214+
prop.SetSpecularPower(12)
215+
203216
def _add_scalar_bar(self):
204217
self.scene.add(
205218
fury.actor.scalar_bar(lookup_table=self.lut, title="Angle (deg)")
@@ -270,11 +283,12 @@ def _rebuild_unclipped_actor(self):
270283
self.streamlines_xyz,
271284
colors=self.flat_vals,
272285
linewidth=self.linewidth,
273-
spline_subdiv=0,
286+
spline_subdiv=self.spline_subdiv,
287+
tube_sides=20,
274288
lookup_colormap=self.lut,
275-
lod=False, # <—
276-
lod_points=20000, # optional, tune
277-
lod_points_size=2, # optional, tune
289+
# lod=False, # <—
290+
# lod_points=20000, # optional, tune
291+
# lod_points_size=2, # optional, tune
278292
)
279293
else:
280294
self.actor0 = actor.line(
@@ -285,6 +299,9 @@ def _rebuild_unclipped_actor(self):
285299
)
286300

287301
self.scene.add(self.actor0)
302+
self._style_streamline_actor()
303+
304+
288305
self.mapper0 = self.actor0.GetMapper()
289306
if clipping_on:
290307
self.mapper0.RemoveAllClippingPlanes()
@@ -462,6 +479,12 @@ def run(self, interactive: bool, screenshot_path: str | None):
462479
self.showm.iren.AddObserver("KeyPressEvent", self._on_keypress)
463480

464481
self.scene.reset_camera()
482+
483+
self.scene.azimuth(15)
484+
self.scene.elevation(10)
485+
self.scene.zoom(1.1)
486+
487+
465488
print(
466489
"Keys: O toggle plane, H hide gizmo, I flip side, R reset plane, +/- thickness, B background, S scale bar, P save PNG"
467490
)
@@ -494,6 +517,7 @@ def show_streamlines(
494517
| None = None,
495518
colormap=None,
496519
background_color: str | tuple[float, float, float] = "black",
520+
spline_subdiv: int = 16,
497521
):
498522
print(f"Initial number of streamlines: {len(streamlines_xyz)}")
499523

@@ -547,6 +571,17 @@ def show_streamlines(
547571
color_values = [color_values[i] for i in keep_idx]
548572

549573
print(f"Final number of streamlines to render: {len(streamlines_xyz)}")
574+
points_per_streamline = np.array(
575+
[len(streamline) for streamline in streamlines_xyz], dtype=float
576+
)
577+
avg_points_per_streamline = float(np.mean(points_per_streamline))
578+
std_points_per_streamline = float(np.std(points_per_streamline))
579+
print(
580+
f"Average points per streamline: {avg_points_per_streamline:.1f} +/- "
581+
f"{std_points_per_streamline:.1f} "
582+
"(more points usually means more detail, but heavier rendering)."
583+
)
584+
550585
if not color_values:
551586
raise ValueError("No color arrays after filtering.")
552587

@@ -578,6 +613,7 @@ def show_streamlines(
578613
window_size,
579614
lut,
580615
background_color=background_color,
616+
spline_subdiv=spline_subdiv,
581617
)
582618
viewer.run(interactive=interactive, screenshot_path=screenshot_path)
583619

src/cardiotensor/visualization/streamlines.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ def visualize_streamlines(
9999
screenshot_path: str | None = None,
100100
window_size: tuple[int, int] = (800, 800),
101101
colormap=None,
102+
spline_subdiv: int = 16,
102103
):
103104
"""
104105
Visualize .trk streamlines with per-point angle-based coloring.
@@ -173,4 +174,5 @@ def visualize_streamlines(
173174
subsample_factor=subsample_factor,
174175
crop_bounds=crop_bounds,
175176
colormap=colormap,
177+
spline_subdiv=spline_subdiv,
176178
)

0 commit comments

Comments
 (0)