Skip to content

Commit 8cdb25d

Browse files
committed
refactor: simplify per-instance ring actor code per review feedback
1 parent b7a6475 commit 8cdb25d

File tree

1 file changed

+39
-161
lines changed

1 file changed

+39
-161
lines changed

fury/actor/planar.py

Lines changed: 39 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,7 @@ def marker(
446446
RGB or RGBA values in the range [0, 1].
447447
marker : str or MarkerShape, optional
448448
The shape of the marker.
449-
Options are "●": "circle", "+": "plus", "x": "cross", "♥": "heart",
450-
"✳": "asterix".
449+
Options are "circle", "plus", "cross", "heart", "asterix".
451450
edge_color : str or tuple or Color, optional
452451
The color of line marking the edge of the markers.
453452
edge_width : float, optional
@@ -756,113 +755,6 @@ def ring(
756755
centers = np.asarray(centers)
757756
n_centers = len(centers)
758757

759-
def _as_per_center(values, *, value_name, cast=None):
760-
"""Normalize scalar or 1D input to per-center values.
761-
762-
Parameters
763-
----------
764-
values : float or int or ndarray
765-
Scalar-like or one-dimensional input values.
766-
value_name : str
767-
Name of the parameter, used in error messages.
768-
cast : type, optional
769-
Optional dtype constructor used to cast the output values.
770-
771-
Returns
772-
-------
773-
ndarray
774-
One-dimensional array with one value per center.
775-
"""
776-
arr = np.asarray(values)
777-
if arr.ndim == 0:
778-
arr = np.full(n_centers, arr.item())
779-
elif arr.ndim == 1 and arr.size == 1:
780-
arr = np.full(n_centers, arr.item())
781-
elif arr.ndim == 1 and arr.size == n_centers:
782-
pass
783-
else:
784-
raise ValueError(
785-
f"{value_name} size should be 1 or equal to the numbers of centers"
786-
)
787-
788-
if cast is not None:
789-
arr = arr.astype(cast)
790-
return arr
791-
792-
def _color_at(values, idx):
793-
"""Return color for one center from scalar-like or per-center colors.
794-
795-
Parameters
796-
----------
797-
values : tuple or list or ndarray
798-
Either one color (RGB/RGBA) or one color per center.
799-
idx : int
800-
Center index.
801-
802-
Returns
803-
-------
804-
ndarray
805-
Color values for one center.
806-
"""
807-
arr = np.asarray(values)
808-
if arr.ndim == 1 and arr.size in (3, 4):
809-
return arr
810-
if arr.ndim == 2 and arr.shape[0] in (1, n_centers):
811-
return arr[0] if arr.shape[0] == 1 else arr[idx]
812-
raise ValueError("colors size should be 1 or equal to the numbers of centers")
813-
814-
def _direction_at(values, idx):
815-
"""Return direction for one center from scalar-like or per-center input.
816-
817-
Parameters
818-
----------
819-
values : tuple or list or ndarray
820-
Either one direction or one direction per center.
821-
idx : int
822-
Center index.
823-
824-
Returns
825-
-------
826-
ndarray
827-
Direction vector for one center.
828-
"""
829-
arr = np.asarray(values)
830-
if arr.ndim == 1 and arr.size == 3:
831-
return arr
832-
if arr.ndim == 2 and arr.shape[0] in (1, n_centers):
833-
return arr[0] if arr.shape[0] == 1 else arr[idx]
834-
raise ValueError(
835-
"directions size should be 1 or equal to the numbers of centers"
836-
)
837-
838-
def _scale_at(values, idx):
839-
"""Return scale for one center from scalar-like or per-center scales.
840-
841-
Parameters
842-
----------
843-
values : float or tuple or ndarray
844-
Either one scale, one XYZ scale, or one scale per center.
845-
idx : int
846-
Center index.
847-
848-
Returns
849-
-------
850-
float or ndarray
851-
Scale value(s) for one center.
852-
"""
853-
arr = np.asarray(values)
854-
if arr.ndim == 0:
855-
return arr.item()
856-
if arr.ndim == 1 and arr.size == 1:
857-
return arr.item()
858-
if arr.ndim == 1 and arr.size == 3:
859-
return arr
860-
if arr.ndim == 1 and arr.size == n_centers:
861-
return arr[idx]
862-
if arr.ndim == 2 and arr.shape[0] in (1, n_centers) and arr.shape[1] == 3:
863-
return arr[0] if arr.shape[0] == 1 else arr[idx]
864-
raise ValueError("scales size should be 1 or equal to the numbers of centers")
865-
866758
if n_centers == 0:
867759
vertices = np.empty((0, 3), dtype=np.float32)
868760
faces = np.empty((0, 3), dtype=np.int32)
@@ -906,75 +798,61 @@ def _scale_at(values, idx):
906798
enable_picking=enable_picking,
907799
)
908800

909-
inner_radius_arr = _as_per_center(
910-
inner_radius, value_name="inner_radius", cast=np.float64
911-
)
912-
outer_radius_arr = _as_per_center(
913-
outer_radius, value_name="outer_radius", cast=np.float64
914-
)
915-
radial_segments_arr = _as_per_center(
916-
radial_segments, value_name="radial_segments", cast=np.int64
917-
)
918-
circumferential_segments_arr = _as_per_center(
919-
circumferential_segments,
920-
value_name="circumferential_segments",
921-
cast=np.int64,
922-
)
801+
# Per-instance ring parameters -- each ring may have different topology,
802+
# so we generate each mesh separately and concatenate.
803+
ring_params = {
804+
"inner_radius": np.atleast_1d(np.asarray(inner_radius)),
805+
"outer_radius": np.atleast_1d(np.asarray(outer_radius)),
806+
"radial_segments": np.atleast_1d(np.asarray(radial_segments)),
807+
"circumferential_segments": np.atleast_1d(np.asarray(circumferential_segments)),
808+
}
809+
for name, arr in ring_params.items():
810+
if arr.size not in (1, n_centers):
811+
raise ValueError(
812+
f"{name} size should be 1 or equal to the numbers of centers"
813+
)
814+
815+
inner_radius_arr = ring_params["inner_radius"]
816+
outer_radius_arr = ring_params["outer_radius"]
817+
radial_segments_arr = ring_params["radial_segments"]
818+
circumferential_segments_arr = ring_params["circumferential_segments"]
923819

924820
all_vertices = []
925821
all_faces = []
926-
all_colors = []
927822
face_offset = 0
928823

929824
for idx in range(n_centers):
930-
vertices, faces = fp.prim_ring(
825+
verts, tri = fp.prim_ring(
931826
inner_radius=float(inner_radius_arr[idx]),
932827
outer_radius=float(outer_radius_arr[idx]),
933828
radial_segments=int(radial_segments_arr[idx]),
934829
circumferential_segments=int(circumferential_segments_arr[idx]),
935830
)
936831

937-
vertices = vertices.copy()
938-
939-
# Scale
940-
vertices *= _scale_at(scales, idx)
941-
942-
# Rotate according to direction
943-
dirs = _direction_at(directions, idx)
944-
dir_abs = np.linalg.norm(dirs)
945-
if dir_abs:
946-
normal = np.array([1.0, 0.0, 0.0])
947-
dirs = dirs / dir_abs
948-
v = np.cross(normal, dirs)
949-
c = np.dot(normal, dirs)
950-
951-
vmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])
952-
953-
if c == -1.0:
954-
rotation_matrix = -np.eye(3, dtype=np.float64)
955-
else:
956-
h = 1 / (1 + c)
957-
rotation_matrix = (
958-
np.eye(3, dtype=np.float64) + vmat + (vmat.dot(vmat) * h)
959-
)
960-
else:
961-
rotation_matrix = np.identity(3)
962-
963-
vertices = np.dot(rotation_matrix[:3, :3], vertices.T).T
964-
965832
# Translate to center
966-
vertices += centers[idx]
833+
verts = verts + centers[idx]
967834

968-
all_vertices.append(vertices)
969-
all_faces.append(faces + face_offset)
970-
color_i = _color_at(colors, idx)
971-
all_colors.append(np.repeat(color_i[np.newaxis, :], len(vertices), axis=0))
972-
973-
face_offset += len(vertices)
835+
all_vertices.append(verts)
836+
all_faces.append(tri + face_offset)
837+
face_offset += len(verts)
974838

975839
big_vertices = np.concatenate(all_vertices, axis=0)
976840
big_faces = np.concatenate(all_faces, axis=0)
977-
big_colors = np.concatenate(all_colors, axis=0)
841+
842+
# Expand colors to per-vertex
843+
colors_arr = np.asarray(colors)
844+
if colors_arr.ndim == 1 and colors_arr.size in (3, 4):
845+
big_colors = np.tile(colors_arr, (len(big_vertices), 1))
846+
elif colors_arr.ndim == 2 and colors_arr.shape[0] == n_centers:
847+
per_ring_sizes = [len(v) for v in all_vertices]
848+
big_colors = np.concatenate(
849+
[np.tile(colors_arr[i], (per_ring_sizes[i], 1)) for i in range(n_centers)],
850+
axis=0,
851+
)
852+
else:
853+
big_colors = np.tile(
854+
np.asarray([1, 1, 1], dtype=np.float32), (len(big_vertices), 1)
855+
)
978856

979857
obj = actor_from_primitive(
980858
big_vertices,

0 commit comments

Comments
 (0)