diff --git a/examples/tutorials/01_Introduction_And_Object_Creation.py b/examples/tutorials/01_Introduction_And_Object_Creation.py index a71a502d..c98f6fae 100644 --- a/examples/tutorials/01_Introduction_And_Object_Creation.py +++ b/examples/tutorials/01_Introduction_And_Object_Creation.py @@ -44,7 +44,7 @@ # %% [markdown] # ## 1. `Point` # -# A `Point` object defines a location in 3D space. It can also have a radius, making it a sphere. +# A `Point` object defines a location in 3D space. # Positions are typically specified in millimeters. # %% diff --git a/examples/tutorials/03_Solution_Generation_and_Analysis.py b/examples/tutorials/03_Solution_Generation_and_Analysis.py index 587c646d..f343257c 100644 --- a/examples/tutorials/03_Solution_Generation_and_Analysis.py +++ b/examples/tutorials/03_Solution_Generation_and_Analysis.py @@ -52,7 +52,7 @@ # This is a `Point` object representing the desired focal location. # %% -target = Point(position=np.array([0, 0, 50]), units="mm", radius=0.5) # 50mm depth, small radius +target = Point(position=np.array([0, 0, 50]), units="mm") # 50 mm depth print(f"Target: {target}") # %% [markdown] diff --git a/src/openlifu/bf/focal_patterns/wheel.py b/src/openlifu/bf/focal_patterns/wheel.py index c768ceeb..4911754d 100644 --- a/src/openlifu/bf/focal_patterns/wheel.py +++ b/src/openlifu/bf/focal_patterns/wheel.py @@ -56,11 +56,12 @@ def get_targets(self, target: Point): theta = 2*np.pi*i/self.num_spokes local_position = self.spoke_radius * np.array([np.cos(theta), np.sin(theta), 0.0]) position = np.dot(m, np.append(local_position, 1.0))[:3] - spoke = Point(id=f"{target.id}_{np.rad2deg(theta):.0f}deg", - name=f"{target.name} ({np.rad2deg(theta):.0f}°)", - position=position, - units=self.distance_units, - radius=target.radius) + spoke = Point( + id=f"{target.id}_{np.rad2deg(theta):.0f}deg", + name=f"{target.name} ({np.rad2deg(theta):.0f}°)", + position=position, + units=self.distance_units, +) targets.append(spoke) return targets diff --git a/src/openlifu/geo/point.py b/src/openlifu/geo/point.py index 11586a45..d92e62a0 100644 --- a/src/openlifu/geo/point.py +++ b/src/openlifu/geo/point.py @@ -3,7 +3,7 @@ import copy import json from dataclasses import dataclass, field -from typing import Annotated, Any, Dict, Tuple +from typing import Annotated, Dict, Tuple import numpy as np @@ -22,12 +22,6 @@ class Point: name: Annotated[str, OpenLIFUFieldData("Point name", "Name of the point")] = "Point" """Name of the point""" - color: Annotated[Any, OpenLIFUFieldData("Color (RGB)", "RGB color of the point")] = (1.0, 0.0, 0.0) - """RGB color of the point""" - - radius: Annotated[float, OpenLIFUFieldData("Radius", "Radius for rendering the point in the provided units")] = 1.0 # mm - """Radius for rendering the point in the provided units""" - dims: Annotated[Tuple[str, str, str], OpenLIFUFieldData("Dimensions", "Names of the axes of the coordinate system being used")] = ("x", "y", "z") """Names of the axes of the coordinate system being used""" @@ -74,33 +68,51 @@ def get_matrix(self, origin: np.ndarray = np.eye(4), center_on_point: bool = Tru m = np.dot(origin, m) return m - def get_polydata(self, transform: np.ndarray = np.eye(4), units=None): + def get_polydata( + self, + transform: np.ndarray = np.eye(4), + units=None, + radius: float = 1.0, + ): import vtk + units = self.units if units is None else units - colors = vtk.vtkNamedColors() sphereSource = vtk.vtkSphereSource() scl = getunitconversion(self.units, units) pos = np.dot(transform, np.append(self.position * scl, 1.0))[:3] sphereSource.SetCenter(*pos) - sphereSource.SetRadius(self.radius * scl) + sphereSource.SetRadius(radius * scl) sphereSource.SetPhiResolution(100) sphereSource.SetThetaResolution(100) return sphereSource - def get_actor(self, transform: np.ndarray = np.eye(4), units=None): + def get_actor( + self, + transform: np.ndarray = np.eye(4), + units=None, + color=(1.0, 0.0, 0.0), + radius: float = 1.0, + ): import vtk - polydata = self.get_polydata(transform=transform, units=units) + + polydata = self.get_polydata( + transform=transform, + units=units, + radius=radius, + ) + mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(polydata.GetOutputPort()) + actor = vtk.vtkActor() actor.SetMapper(mapper) - actor.GetProperty().SetColor(self.color) + actor.GetProperty().SetColor(color) + return actor def rescale(self, units: str): scl = getunitconversion(self.units, units) self.position = self.position * scl - self.radius = self.radius * scl self.units = units def transform( @@ -117,24 +129,22 @@ def transform( def to_dict(self): return { - "id": self.id, - "name": self.name, - "color": self.color, - "radius": self.radius, - "position": self.position.tolist(), - "dims": self.dims, - "units": self.units, - } + "id": self.id, + "name": self.name, + "position": self.position.tolist(), + "dims": self.dims, + "units": self.units, + } @staticmethod def from_dict(point_data: Dict): """Create a Point object from a dictionary.""" - if "color" in point_data: - if len(point_data["color"]) != 3: - raise ValueError(f"Color should have three components; got {point_data['color']}.") - point_data["color"] = tuple(float(point_data["color"][i]) for i in range(3)) - if "radius" in point_data: - point_data["radius"] = float(point_data["radius"]) + point_data = point_data.copy() + + # Ignore legacy rendering properties. + point_data.pop("color", None) + point_data.pop("radius", None) + if "position" in point_data: point_data["position"] = np.array(point_data["position"]) if "dims" in point_data: diff --git a/tests/test_point.py b/tests/test_point.py index e2164193..e22cce06 100644 --- a/tests/test_point.py +++ b/tests/test_point.py @@ -10,13 +10,11 @@ @pytest.fixture() def example_point() -> Point: return Point( - id = "example_point", + id="example_point", name="Example point", - color=(0.,0.7, 0.2), - radius=1.5, - position=np.array([-10.,0,25]), - dims = ("R", "A", "S"), - units = "m", + position=np.array([-10., 0, 25]), + dims=("R", "A", "S"), + units="m", ) @pytest.mark.parametrize("compact_representation", [True, False])