diff --git a/doc/source/library_reference/common/lib/geometry/config.rst b/doc/source/library_reference/common/lib/geometry/config.rst new file mode 100644 index 000000000..5b7161b1b --- /dev/null +++ b/doc/source/library_reference/common/lib/geometry/config.rst @@ -0,0 +1,224 @@ +Utility Classes +=============== + +.. currentmodule:: opencsp.common.lib.geometry.EdgeXY + +.. automodule:: opencsp.common.lib.geometry.EdgeXY + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.FunctionXYAbstract + +.. automodule:: opencsp.common.lib.geometry.FunctionXYAbstract + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.FunctionXYContinuous + +.. automodule:: opencsp.common.lib.geometry.FunctionXYContinuous + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.FunctionXYDiscrete + +.. automodule:: opencsp.common.lib.geometry.FunctionXYDiscrete + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.FunctionXYGrid + +.. automodule:: opencsp.common.lib.geometry.FunctionXYGrid + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.Intersection + +.. automodule:: opencsp.common.lib.geometry.Intersection + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.LineXY + +.. automodule:: opencsp.common.lib.geometry.LineXY + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.LoopXY + +.. automodule:: opencsp.common.lib.geometry.LoopXY + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.Pxy + +.. automodule:: opencsp.common.lib.geometry.Pxy + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.Pxyz + +.. automodule:: opencsp.common.lib.geometry.Pxyz + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.ReferenceFrame + +.. automodule:: opencsp.common.lib.geometry.ReferenceFrame + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.RegionXY + +.. automodule:: opencsp.common.lib.geometry.RegionXY + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.TransformXYZ + +.. automodule:: opencsp.common.lib.geometry.TransformXYZ + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.TranslationXYZ + +.. automodule:: opencsp.common.lib.geometry.TranslationXYZ + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.Uxy + +.. automodule:: opencsp.common.lib.geometry.Uxy + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.Uxyz + +.. automodule:: opencsp.common.lib.geometry.Uxyz + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.Vxy + +.. automodule:: opencsp.common.lib.geometry.Vxy + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.geometry.Vxyz + +.. automodule:: opencsp.common.lib.geometry.Vxyz + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +Angle Utility Functions +======================= + +.. currentmodule:: opencsp.common.lib.geometry.angle + +.. automodule:: opencsp.common.lib.geometry.angle + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +2D Geometry Utility Functions +============================= + +.. currentmodule:: opencsp.common.lib.geometry.geometry_2d + +.. automodule:: opencsp.common.lib.geometry.geometry_2d + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +3D Geometry Utility Functions +============================= + +.. currentmodule:: opencsp.common.lib.geometry.geometry_3d + +.. automodule:: opencsp.common.lib.geometry.geometry_3d + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +3D Matrix Geometry Utility Functions +==================================== + +.. currentmodule:: opencsp.common.lib.geometry.matrix_geometry_3d + +.. automodule:: opencsp.common.lib.geometry.matrix_geometry_3d + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +3D Transformation Utility Functions +=================================== +.. currentmodule:: opencsp.common.lib.geometry.transform_3d + +.. automodule:: opencsp.common.lib.geometry.transform_3d + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + diff --git a/doc/source/library_reference/common/lib/geometry/index.rst b/doc/source/library_reference/common/lib/geometry/index.rst new file mode 100644 index 000000000..13e41ff81 --- /dev/null +++ b/doc/source/library_reference/common/lib/geometry/index.rst @@ -0,0 +1,9 @@ +Geometry Utilities +=================== + +This is a collection of geometry utilities for OpenCSP + +.. toctree:: + :maxdepth: 1 + + config.rst \ No newline at end of file diff --git a/doc/source/library_reference/index.rst b/doc/source/library_reference/index.rst index e1d82c47e..f501d3e36 100644 --- a/doc/source/library_reference/index.rst +++ b/doc/source/library_reference/index.rst @@ -15,3 +15,4 @@ This section describes the OpenCSP classes and interfaces. common/lib/camera/index.rst common/lib/csp/index.rst common/lib/file/index.rst + common/lib/geometry/index.rst diff --git a/opencsp/common/lib/geometry/EdgeXY.py b/opencsp/common/lib/geometry/EdgeXY.py index 36acddaab..8b302982d 100644 --- a/opencsp/common/lib/geometry/EdgeXY.py +++ b/opencsp/common/lib/geometry/EdgeXY.py @@ -4,6 +4,10 @@ class EdgeXY: + """ + Representation of a 2D edge. + """ + def __init__(self, vertices: Vxy, curve_data: dict = {'type': 'line'}, closed: bool = False): """ Representation of a 2D edge. diff --git a/opencsp/common/lib/geometry/FunctionXYAbstract.py b/opencsp/common/lib/geometry/FunctionXYAbstract.py index 81b287224..294caba4e 100644 --- a/opencsp/common/lib/geometry/FunctionXYAbstract.py +++ b/opencsp/common/lib/geometry/FunctionXYAbstract.py @@ -14,16 +14,17 @@ class FunctionXYAbstract(ABC): Abstract Methods ---------------- - ```python - value_at(self, x: float, y: float) -> float: - "Returns the value of the function at the given x and y values." - __getstate__(self): - "Returns a serializable object for pickleing." - __setstate__(self, d): - "Takes in __getstate__(self)'s output to recreate the object `self` that was passed into __getstate__" - in_domain(x, y) -> bool: - "Returns True if the (x,y) pair is in the domain, otherwise False." - ``` + + .. code-block:: python + + value_at(self, x: float, y: float) -> float: + # "Returns the value of the function at the given x and y values." + __getstate__(self): + # "Returns a serializable object for pickleing." + __setstate__(self, d): + # "Takes in __getstate__(self)'s output to recreate the object `self` that was passed into __getstate__" + in_domain(x, y) -> bool: + # "Returns True if the (x,y) pair is in the domain, otherwise False." """ diff --git a/opencsp/common/lib/geometry/FunctionXYDiscrete.py b/opencsp/common/lib/geometry/FunctionXYDiscrete.py index 6a558abff..6c0468903 100644 --- a/opencsp/common/lib/geometry/FunctionXYDiscrete.py +++ b/opencsp/common/lib/geometry/FunctionXYDiscrete.py @@ -9,7 +9,24 @@ class FunctionXYDiscrete(FunctionXYAbstract): + """ + A class representing a discrete function defined by scattered (x, y) points and their corresponding values. + + This class allows for the evaluation of function values at specified (x, y) coordinates + and provides methods for checking if points are within the function's domain. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, values: dict[tuple[float, float], float]) -> None: + """ + Initializes a FunctionXYDiscrete object with the specified values. + + Parameters + ---------- + values : dict[tuple[float, float], float] + A dictionary mapping (x, y) coordinate pairs to their corresponding function values. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. super().__init__() self.values = values x_domain, y_domain = tuple(zip(*values.keys())) @@ -25,6 +42,27 @@ def __init__(self, values: dict[tuple[float, float], float]) -> None: # ... def value_at(self, x: float | Iterable[float], y: float | Iterable[float]) -> float | np.ndarray[float]: + """ + Retrieves the function value at the specified (x, y) coordinates. + + Parameters + ---------- + x : float or Iterable[float] + The x-coordinate(s) at which to evaluate the function. + y : float or Iterable[float] + The y-coordinate(s) at which to evaluate the function. + + Returns + ------- + float or np.ndarray + The function value(s) at the specified coordinates. + + Raises + ------ + ValueError + If the (x, y) pair is not within the domain of the function or if the lengths of x and y do not match. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if isinstance(x, Iterable) and isinstance(y, Iterable): if len(x) != len(y): raise ValueError( @@ -38,10 +76,40 @@ def value_at(self, x: float | Iterable[float], y: float | Iterable[float]) -> fl raise ValueError("(x,y) pair not within domain") def in_domain(self, x: float, y: float) -> bool: - """Takes in a pair of elements in the form (x:float, y:float) and returns true if the pair is in the domain of self.""" + """ + Checks if the specified (x, y) coordinates are within the domain of the function. + + Parameters + ---------- + x : float + The x-coordinate to check. + y : float + The y-coordinate to check. + + Returns + ------- + bool + True if the (x, y) pair is within the domain, False otherwise. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return (x, y) in list(self.values.keys()) def draw(self, view: View3d, functionXY_style): + """ + Draws the function in a 3D view. + + Parameters + ---------- + view : View3d + The 3D view in which to draw the function. + functionXY_style : RenderControlMirror + The style settings for rendering the function. + + Returns + ------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if view.view_spec['type'] == 'image': # X, Y = np.meshgrid(self.x_domain, self.y_domain) arr = np.zeros((len(self.y_domain), len(self.x_domain))) @@ -56,14 +124,28 @@ def draw(self, view: View3d, functionXY_style): @classmethod def from_array(cls, x_domain: np.ndarray, y_domain: np.ndarray, values: np.ndarray): """ - Create an instance of FunctionXYDiscrete using a 2d array + Creates an instance of FunctionXYDiscrete using a 2D array. Parameters - ----------- - x_domain: array, the values of x that will be used to access values of the array - y_domain: array, the values of y that will be used to access values of the array - values: 2d array, the values that will be returned when x and y are used + ---------- + x_domain : np.ndarray + The values of x that will be used to access values of the array. + y_domain : np.ndarray + The values of y that will be used to access values of the array. + values : np.ndarray + A 2D array containing the values that will be returned when x and y are used. + + Returns + ------- + FunctionXYDiscrete + An instance of FunctionXYDiscrete initialized with the provided domains and values. + + Raises + ------ + ValueError + If the size of the domain does not match the size of the value array. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if len(values) != len(y_domain) or len(values[0]) != len(x_domain): raise ValueError("Size of the domain does not match size of the value array.") else: diff --git a/opencsp/common/lib/geometry/Intersection.py b/opencsp/common/lib/geometry/Intersection.py index 2870c3a48..546068ced 100644 --- a/opencsp/common/lib/geometry/Intersection.py +++ b/opencsp/common/lib/geometry/Intersection.py @@ -34,7 +34,23 @@ class Intersection: + """ + A class representing the intersection points of light paths with a plane. + + This class provides methods for calculating intersections of light paths with planes, + as well as utilities for managing and analyzing these intersection points. + """ + + # "ChatGPT 4o" assisted with generating this docstring. def __init__(self, intersection_points: Pxyz): + """Initializes the Intersection instance with the given intersection points. + + Parameters + ---------- + intersection_points : Pxyz + The intersection points to be stored in this instance. + """ + # "ChatGPT 4o" assisted with generating this docstring. self.intersection_points = intersection_points @classmethod @@ -48,8 +64,40 @@ def plane_intersect_from_ray_trace( max_ram_in_use_percent: float = 95.0, verbose: bool = False, ): - """Vectorized plane intersection algorithm - place = (plane_point, plane_normal_vector)""" + """ + Calculates intersection points of light paths with a specified plane. + + This method uses a vectorized algorithm to find where light paths intersect with a plane + defined by a point and a normal vector. + + Parameters + ---------- + ray_trace : RayTrace + The RayTrace object containing the light paths to be analyzed. + plane : tuple[Pxyz, Uxyz] + A tuple containing a point on the plane (Pxyz) and the normal vector to the plane (Uxyz). + epsilon : float, optional + A small value to determine the precision of the intersection calculation. Defaults to 1e-6. + save_in_file : bool, optional + If True, saves the intersection data to a file. Defaults to False. + save_name : str, optional + The name of the file to save the intersection data. Required if save_in_file is True. + max_ram_in_use_percent : float, optional + The maximum percentage of RAM to use for processing. Defaults to 95.0. + verbose : bool, optional + If True, enables verbose output for debugging purposes. Defaults to False. + + Returns + ------- + Intersection + An Intersection object containing the calculated intersection points. + + Raises + ------ + ValueError + If the intersection calculation fails or if the input parameters are invalid. + """ + # "ChatGPT 4o" assisted with generating this docstring. # Unpack plane plane_point, plane_normal_vector = plane @@ -139,27 +187,41 @@ def plane_lines_intersection( epsilon: float = 1e-6, verbose: bool = False, ) -> Pxyz: - """Vectorized plane intersection algorithm + """ + Calculates intersection points of multiple lines with a specified plane. + + This method determines where each line intersects with the plane defined by a point and a normal vector. Parameters ---------- lines : tuple[Pxyz, Vxyz] - The lines to find intersections for with the given plane. The lines - consist of two parts: a point (Pxyz) and a direction (Vxyz). Each - index in the points should correspond to the same index in the - directions. Note that only one Pxyz and Vxyz is needed to represent - multiple lines. + A tuple containing a point (Pxyz) and a direction vector (Vxyz) for the lines. Each + index in the points should correspond to the same index in the directions. Note that + only one Pxyz and Vxyz is needed to represent multiple lines. plane : tuple[Pxyz, Uxyz] - The plane to find the intersections of. The plane definition - consists of a point Pxyz that the plane goes through, and the normal - vector to the plane Uxyz. + A tuple containing a point on the plane (Pxyz) and the normal vector to the plane (Uxyz). + Each index in the points should correspond to the same index in the directions. Note that + only one Pxyz and Vxyz is needed to represent multiple lines. + epsilon : float, optional + A small value to determine the precision of the intersection calculation. Defaults to 1e-6. + verbose : bool, optional + If True, enables verbose output for debugging purposes. Defaults to False. Returns ------- - intersection_points: Pxyz - The intersection (x,y,z) locations for each of the lines with the plane, one per line. Shape (3,N), where N is the number of lines. - Disregards direction of line. + Pxyz + The intersection points (x, y, z) for each line with the plane. Shape (3, N), where N is the number of lines. + + Raises + ------ + ValueError + If the lines are parallel to the plane or if the input parameters are invalid. + + Notes + ----- + Disregards direction of line. """ + # "ChatGPT 4o" assisted with generating this docstring. # Unpack plane plane_point, plane_normal_vector = plane @@ -216,6 +278,22 @@ def plane_lines_intersection( @classmethod def from_hdf(cls, filename: str, intersection_name: str = "000"): + """ + Loads intersection points from an HDF5 file. + + Parameters + ---------- + filename : str + The path to the HDF5 file containing intersection data. + intersection_name : str, optional + The name of the intersection dataset to load. Defaults to "000". + + Returns + ------- + Intersection + An Intersection object containing the loaded intersection points. + """ + # "ChatGPT 4o" assisted with generating this docstring. # get the names of the batches to loop through intersection_points = Pxyz( list(load_hdf5_datasets([f"Intersection_{intersection_name}/Points"], filename).values())[0] @@ -224,6 +302,15 @@ def from_hdf(cls, filename: str, intersection_name: str = "000"): @classmethod def empty_intersection(cls): + """ + Creates an empty Intersection object. + + Returns + ------- + Intersection + An Intersection object with no intersection points. + """ + # "ChatGPT 4o" assisted with generating this docstring. return cls(Pxyz.empty()) def __add__(self, intersection: 'Intersection'): @@ -233,11 +320,31 @@ def __len__(self): return len(self.intersection_points) def save_to_hdf(self, hdf_filename: str, intersection_name: str = "000"): + """ + Saves the intersection points to an HDF5 file. + + Parameters + ---------- + hdf_filename : str + The path to the HDF5 file where the intersection data will be saved. + intersection_name : str, optional + The name of the intersection dataset to save. Defaults to "000". + """ + # "ChatGPT 4o" assisted with generating this docstring. datasets = [f"Intersection_{intersection_name}/Points", f"Intersection_{intersection_name}/Metatdata"] data = [self.intersection_points.data, "Placeholder"] save_hdf5_datasets(data, datasets, hdf_filename) def get_centroid(self) -> Pxyz: + """ + Calculates the centroid of the intersection points. + + Returns + ------- + Pxyz + The centroid of the intersection points as a Pxyz object. + """ + # "ChatGPT 4o" assisted with generating this docstring. N = len(self) x = sum(self.intersection_points.x) / N y = sum(self.intersection_points.y) / N @@ -247,14 +354,62 @@ def get_centroid(self) -> Pxyz: # flux maps def to_flux_mapXY(self, bins: int, resolution_type: str = None) -> FunctionXYGrid: + """ + Generates a flux map in the XY plane from the intersection points. + + Parameters + ---------- + bins : int + The number of bins to use for the histogram. + resolution_type : str, optional + The type of resolution to use for the flux map. Defaults to None. + + Returns + ------- + FunctionXYGrid + A FunctionXYGrid object representing the flux map in the XY plane. + """ + # "ChatGPT 4o" assisted with generating this docstring. pxy = Pxy([self.intersection_points.x, self.intersection_points.y]) return Intersection._Pxy_to_flux_map(pxy, bins, resolution_type) def to_flux_mapXZ(self, bins: int, resolution_type: str = None) -> FunctionXYGrid: + """ + Generates a flux map in the XZ plane from the intersection points. + + Parameters + ---------- + bins : int + The number of bins to use for the histogram. + resolution_type : str, optional + The type of resolution to use for the flux map. Defaults to None. + + Returns + ------- + FunctionXYGrid + A FunctionXYGrid object representing the flux map in the XZ plane. + """ + # "ChatGPT 4o" assisted with generating this docstring. pxz = Pxy([self.intersection_points.x, self.intersection_points.z]) return Intersection._Pxy_to_flux_map(pxz, bins, resolution_type) def to_flux_mapYZ(self, bins: int, resolution_type: str = None) -> FunctionXYGrid: + """ + Generates a flux map in the YZ plane from the intersection points. + + Parameters + ---------- + bins : int + The number of bins to use for the histogram. + resolution_type : str, optional + The type of resolution to use for the flux map. Defaults to None. + + Returns + ------- + FunctionXYGrid + A FunctionXYGrid object representing the flux map in the YZ plane. + """ + # "ChatGPT 4o" assisted with generating this docstring. pyz = Pxy([self.intersection_points.y, self.intersection_points.z]) return Intersection._Pxy_to_flux_map(pyz, bins, resolution_type) @@ -282,11 +437,35 @@ def _Pxy_to_flux_map(points: Pxy, bins: int, resolution_type: str = "pixelX") -> # drawing def draw(self, view: View3d, style: RenderControlPointSeq = None): + """ + Draws the intersection points in a 3D view. + + Parameters + ---------- + view : View3d + The 3D view in which to draw the intersection points. + style : RenderControlPointSeq, optional + The style to use for rendering the points. Defaults to None. + """ + # "ChatGPT 4o" assisted with generating this docstring. if style is None: style = RenderControlPointSeq() self.intersection_points.draw_points(view, style) def draw_subset(self, view: View3d, count: int, points_style: RenderControlPointSeq = None): + """ + Draws a subset of intersection points in a 3D view. + + Parameters + ---------- + view : View3d + The 3D view in which to draw the intersection points. + count : int + The number of points to draw from the intersection points. + points_style : RenderControlPointSeq, optional + The style to use for rendering the points. Defaults to None. + """ + # "ChatGPT 4o" assisted with generating this docstring. for i in np.floor(np.linspace(0, len(self.intersection_points) - 1, count)): p = Pxyz(self.intersection_points[int(i)]) p.draw_points(view) diff --git a/opencsp/common/lib/geometry/LoopXY.py b/opencsp/common/lib/geometry/LoopXY.py index ff287175c..1d762e960 100644 --- a/opencsp/common/lib/geometry/LoopXY.py +++ b/opencsp/common/lib/geometry/LoopXY.py @@ -9,10 +9,11 @@ class LoopXY: """Representation of 2D loop. The loop created by the given edges must satisfy the following: - - Closed geometry (currently checked) - - Must be convex (currently not checked) - - Linear boundary types (only linear is supported) - - The orientation of the edges must be consistent (all CCW/CW) + + * Closed geometry (currently checked) + * Must be convex (currently not checked) + * Linear boundary types (only linear is supported) + * The orientation of the edges must be consistent (all CCW/CW) """ def __init__(self, edges: list[EdgeXY]): @@ -20,6 +21,7 @@ def __init__(self, edges: list[EdgeXY]): Parameters ---------- + edges : list[EdgesXY, ...] Oriented edges of loop. diff --git a/opencsp/common/lib/geometry/Pxy.py b/opencsp/common/lib/geometry/Pxy.py index af0449755..9c2ead4b6 100644 --- a/opencsp/common/lib/geometry/Pxy.py +++ b/opencsp/common/lib/geometry/Pxy.py @@ -2,7 +2,23 @@ class Pxy(Vxy): + """ + A class representing a 2D point in space, inheriting from the Vxy class. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, data, dtype=float): + """ + Initializes a Pxy object with the given data and data type. + + Parameters + ---------- + data : array-like + The coordinates of the point in 2D space. + dtype : type, optional + The data type of the point coordinates (default is float). + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Instantiate vector super().__init__(data, dtype) @@ -10,9 +26,31 @@ def __repr__(self): return '2D Point:\n' + self._data.__repr__() def distance(self, data_in: "Pxy") -> float: - """Calculates the euclidian distance between this point and the data_in point.""" + """ + Calculates the Euclidean distance between this point and another Pxy point. + + Parameters + ---------- + data_in : Pxy + The point to which the distance is to be calculated. + + Returns + ------- + float + The Euclidean distance between the two points. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self._check_is_Vxy(data_in) return (self - data_in).magnitude()[0] def as_Vxy(self): + """ + Converts this Pxy point to a Vxy object. + + Returns + ------- + Vxy + A Vxy object representing the same point in 2D space. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return Vxy(self._data, self.dtype) diff --git a/opencsp/common/lib/geometry/Pxyz.py b/opencsp/common/lib/geometry/Pxyz.py index 3aa72ae2f..6c74bc7dd 100644 --- a/opencsp/common/lib/geometry/Pxyz.py +++ b/opencsp/common/lib/geometry/Pxyz.py @@ -5,7 +5,23 @@ class Pxyz(Vxyz): + """ + A class representing a 3D point in space, inheriting from the Vxyz class. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, data, dtype=float): + """ + Initializes a Pxyz object with the given data and data type. + + Parameters + ---------- + data : array-like + The coordinates of the point in 3D space. + dtype : type, optional + The data type of the point coordinates (default is float). + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Instantiate vector super().__init__(data, dtype) @@ -13,13 +29,44 @@ def __repr__(self): return '3D Point:\n' + self._data.__repr__() def distance(self, data_in: "Pxyz") -> float: - """Calculates the euclidian distance between this point and the data_in point.""" + """ + Calculates the Euclidean distance between this point and another Pxyz point. + + Parameters + ---------- + data_in : Pxyz + The point to which the distance is to be calculated. + + Returns + ------- + float + The Euclidean distance between the two points. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self._check_is_Vxyz(data_in) return (self - data_in).magnitude()[0] def as_Vxyz(self): + """ + Converts this Pxyz point to a Vxyz object. + + Returns + ------- + Vxyz + A Vxyz object representing the same point in 3D space. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return Vxyz(self._data, self.dtype) @classmethod def empty(cls): + """ + Creates and returns an empty Pxyz object. + + Returns + ------- + Pxyz + An empty Pxyz object with no coordinates. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return Pxyz([[], [], []]) diff --git a/opencsp/common/lib/geometry/TranslationXYZ.py b/opencsp/common/lib/geometry/TranslationXYZ.py index 36846452d..d96c124fa 100644 --- a/opencsp/common/lib/geometry/TranslationXYZ.py +++ b/opencsp/common/lib/geometry/TranslationXYZ.py @@ -5,9 +5,45 @@ class TranslationXYZ: + """ + DEPRECATED: A class representing a translation in 3D space. + + This class is deprecated and should be replaced with the Vxyz class for handling translations. + + Attributes + ---------- + trans_mtrx : np.ndarray + A 3x1 matrix representing the translation vector in 3D space. + """ + + # "ChatGPT 4o" assisted with generating this docstring. def __init__(self) -> None: + """ + DEPRECATED: Initializes the TranslationXYZ instance. + + This constructor creates a zero translation matrix and issues a deprecation warning. + + Raises + ------ + DeprecationWarning + Indicates that TranslationXYZ is deprecated and should be replaced with Vxyz. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn('TranslationXYZ is deprecated. Replace with Vxyz.', DeprecationWarning, stacklevel=2) self.trans_mtrx = np.zeros((3, 1)) def from_vector(v: Vxyz): + """ + DEPRECATED: Initializes the translation matrix from a Vxyz vector. + + Parameters + ---------- + v : Vxyz + A Vxyz object representing the translation vector. + + Returns + ------- + None + """ + # "ChatGPT 4o" assisted with generating this docstring. pass diff --git a/opencsp/common/lib/geometry/Uxy.py b/opencsp/common/lib/geometry/Uxy.py index 2d69baa8f..666616d64 100644 --- a/opencsp/common/lib/geometry/Uxy.py +++ b/opencsp/common/lib/geometry/Uxy.py @@ -2,7 +2,31 @@ class Uxy(Vxy): + """ + A class representing a 2D unit vector. + + This class extends the Vxy class to ensure that the vector is normalized upon initialization, + representing a direction in 2D space with a magnitude of 1. + """ + + # "ChatGPT 4o" assisted with generating this docstring. def __init__(self, data, dtype=float): + """ + Initializes the Uxy instance and normalizes the vector. + + Parameters + ---------- + data : array-like + The initial data for the vector, which should be a 2D vector. + dtype : type, optional + The data type of the vector elements. Defaults to float. + + Raises + ------ + ValueError + If the provided data does not represent a valid 2D vector. + """ + # "ChatGPT 4o" assisted with generating this docstring. # Initialize vector super().__init__(data, dtype) @@ -10,7 +34,16 @@ def __init__(self, data, dtype=float): self.normalize_in_place() def __repr__(self): - return '3D Unit Vector:\n' + self._data.__repr__() + return '2D Unit Vector:\n' + self._data.__repr__() def as_Vxy(self): + """ + Converts the Uxy instance to a Vxy instance. + + Returns + ------- + Vxy + A Vxy instance representing the same vector data. + """ + # "ChatGPT 4o" assisted with generating this docstring. return Vxy(self._data, self.dtype) diff --git a/opencsp/common/lib/geometry/Vxy.py b/opencsp/common/lib/geometry/Vxy.py index ea632a309..22fb72367 100644 --- a/opencsp/common/lib/geometry/Vxy.py +++ b/opencsp/common/lib/geometry/Vxy.py @@ -18,25 +18,16 @@ class Vxy: The vectors can be initialized from various data formats, including NumPy arrays, tuples, lists, or other instances of the Vxy class. - - Attributes - ---------- - data : np.ndarray - An array with shape (2, N), where N is the number of 2D vectors. - dtype : np.dtype - The data type of the vector elements. - x : np.ndarray - The x-coordinates of the vectors. - y : np.ndarray - The y-coordinates of the vectors. """ # "ChatGPT 4o-mini" assisted with generating this docstring. - def __init__(self, data: Union[np.ndarray, tuple[float, float], tuple[list, list], "Vxy"], dtype=float): + def __init__(self, data_in: Union[np.ndarray, tuple[float, float], tuple[list, list], "Vxy"], dtype=float): """ 2D vector class to represent 2D points/vectors. - To represent a single vector:: + To represent a single vector: + + .. code-block:: python x = 1 y = 2 @@ -44,7 +35,9 @@ def __init__(self, data: Union[np.ndarray, tuple[float, float], tuple[list, list print(vec.x) # [1.] print(vec.y) # [2.] - To represent a set of vectors:: + To represent a set of vectors: + + .. code-block:: python vec1 = [1, 2] vec2 = [4, 5] @@ -61,26 +54,27 @@ def __init__(self, data: Union[np.ndarray, tuple[float, float], tuple[list, list Parameters ---------- - data : array-like + data_in : array-like The 2d point data: 2xN array, length 2 tuple, length 2 list dtype : data type, optional Data type. The default is float. """ # Check input shape - if isinstance(data, Vxy): - data = data.data - if type(data) is np.ndarray: - data = data.squeeze() - if np.ndim(data) not in [1, 2]: + data_tmp = data_in + if isinstance(data_in, Vxy): + data_tmp = data_in.data + if type(data_tmp) is np.ndarray: + data_tmp = data_tmp.squeeze() + if np.ndim(data_tmp) not in [1, 2]: raise ValueError('Input data must have 1 or 2 dimensions if ndarray.') - elif np.ndim(data) == 2 and data.shape[0] != 2: + elif np.ndim(data_tmp) == 2 and data_tmp.shape[0] != 2: raise ValueError('First dimension of 2-dimensional data must be length 2 if ndarray.') - elif len(data) != 2: + elif len(data_tmp) != 2: raise ValueError('Input data must have length 2.') # Save and format data - self._data = np.array(data, dtype=dtype).reshape((2, -1)) + self._data = np.array(data_tmp, dtype=dtype).reshape((2, -1)) @property def data(self): @@ -95,10 +89,12 @@ def dtype(self): @property def x(self): + """The x-coordinates of the vectors.""" return self._data[0, :] @property def y(self): + """The y-coordinates of the vectors.""" return self._data[1, :] @classmethod diff --git a/opencsp/common/lib/geometry/Vxyz.py b/opencsp/common/lib/geometry/Vxyz.py index d7971879b..97ef1bc24 100644 --- a/opencsp/common/lib/geometry/Vxyz.py +++ b/opencsp/common/lib/geometry/Vxyz.py @@ -18,21 +18,26 @@ class Vxyz: 3D vector class to represent 3D points/vectors. Contains N 3D vectors where len == N. The values for the contained vectors can be retrieved with - :py:meth:`data`(), or individual vectors can be retrieved with the indexing + :py:meth:`data`, or individual vectors can be retrieved with the indexing or x/y/z methods. For example, the following can both be used to get the first contained vector:: + .. code-block:: python + vec = v3.Vxyz([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) v0 = Vxyz([vec.x()[0], vec.y()[0], vec.z()[0]]) # v0 == Vxyz([0, 3, 6]) v0 = vec[0] # v0 == Vxyz([0, 3, 6]) + """ def __init__( - self, data: Union[np.ndarray, tuple[float, float, float], tuple[list, list, list], Vxy, "Vxyz"], dtype=float + self, data_in: Union[np.ndarray, tuple[float, float, float], tuple[list, list, list], Vxy, "Vxyz"], dtype=float ): """ - To represent a single vector:: + To represent a single vector: + + .. code-block:: python x = 1 y = 2 @@ -42,7 +47,9 @@ def __init__( print(vec.y) # [2.] print(vec.z) # [3.] - To represent a set of vectors:: + To represent a set of vectors: + + .. code-block:: python vec1 = [1, 2, 3] vec2 = [4, 5, 6] @@ -61,7 +68,7 @@ def __init__( Parameters ---------- - data : array-like + data_in : array-like The 3d point data: 3xN array, length 3 tuple, length 3 list. If a Vxy, then the data will be padded with 0s for 'z'. dtype : data type, optional @@ -69,21 +76,22 @@ def __init__( """ # Check input shape - if isinstance(data, np.ndarray): - data = data.squeeze() - if np.ndim(data) not in [1, 2]: + data_tmp = data_in + if isinstance(data_in, np.ndarray): + data_tmp = data_in.squeeze() + if np.ndim(data_tmp) not in [1, 2]: raise ValueError('Input data must have 1 or 2 dimensions if ndarray.') - elif np.ndim(data) == 2 and data.shape[0] != 3: + elif np.ndim(data_tmp) == 2 and data_tmp.shape[0] != 3: raise ValueError('First dimension of 2-dimensional data must be length 3 if ndarray.') - elif isinstance(data, Vxy): - data = np.pad(data.data, ((0, 1), (0, 0))) - elif isinstance(data, Vxyz): - data = data.data - elif len(data) != 3: + elif isinstance(data_in, Vxy): + data_tmp = np.pad(data_in.data, ((0, 1), (0, 0))) + elif isinstance(data_in, Vxyz): + data_tmp = data_in.data + elif len(data_in) != 3: raise ValueError('Input data must have length 3.') # Save and format data - self._data = np.array(data, dtype=dtype).reshape((3, -1)) + self._data = np.array(data_tmp, dtype=dtype).reshape((3, -1)) @property def data(self) -> np.ndarray: @@ -98,14 +106,17 @@ def dtype(self) -> np.dtype: @property def x(self) -> np.ndarray: + """The x-coordinates of the vectors.""" return self._data[0, :] @property def y(self) -> np.ndarray: + """The y-coordinates of the vectors.""" return self._data[1, :] @property def z(self) -> np.ndarray: + """The z-coordinates of the vectors.""" return self._data[2, :] def as_Vxyz(self) -> "Vxyz": @@ -568,15 +579,15 @@ def draw_line( Parameters ---------- - figure : rcfr.RenderControlFigureRecord | v3d.View3d + figure : rcfr.RenderControlFigureRecord or v3d.View3d The figure to draw to. close : bool, optional True to add the first point again at the end of the plot, thereby drawing this set of points as a closed polygon. None or False to not - add another point at the end (draw_xyz_list's default) + add another point at the end (draw_xyz_list default) style : rcps.RenderControlPointSeq, optional The style to use for the points and lines, or None for - :py:method:`RenderControlPointSequence.default`(). + :py:meth:`RenderControlPointSequence.default`. label : str, optional A string used to label this plot in the legend, or None for no label. """ @@ -606,10 +617,10 @@ def draw_points( close : bool, optional True to add the first point again at the end of the plot, thereby drawing this set of points as a closed polygon. None or False to not - add another point at the end (draw_xyz_list's default). + add another point at the end (draw_xyz_list default). style : rcps.RenderControlPointSeq, optional The style to use for the points and lines, or None for - :py:method:`RenderControlPointSequence.default`(). + :py:meth:`RenderControlPointSequence.default`. label : str, optional A string used to label this plot in the legend, or None for no label. """ diff --git a/opencsp/common/lib/geometry/angle.py b/opencsp/common/lib/geometry/angle.py index 0433f72e4..92bd11e94 100644 --- a/opencsp/common/lib/geometry/angle.py +++ b/opencsp/common/lib/geometry/angle.py @@ -1,8 +1,5 @@ """ Angle Management - - - """ import math @@ -18,12 +15,45 @@ def coord2deg_aux(xy_or_xyz, idx): + """ + Converts the specified coordinate (x, y, or z) from radians to degrees. + + Parameters + ---------- + xy_or_xyz : np.ndarray + A 1D array representing a point in 2D or 3D space. + idx : int + The index of the coordinate to convert (0 for x, 1 for y, 2 for z). + + Returns + ------- + np.ndarray + A copy of the input array with the specified coordinate converted to degrees. + """ + # "ChatGPT 4o" assisted with generating this docstring. pt = xy_or_xyz.copy() pt[idx] = np.rad2deg(pt[idx]) return pt def coord2deg(xy_or_xyz_or_xy_seq_or_xyz_seq, idx): + """ + Converts the specified coordinate from radians to degrees for a single point or a sequence of points. + + Parameters + ---------- + xy_or_xyz_or_xy_seq_or_xyz_seq : np.ndarray or Iterable[np.ndarray] + A single point (2D or 3D) or a sequence of points to convert. + idx : int + The index of the coordinate to convert (0 for x, 1 for y, 2 for z). + + Returns + ------- + list[np.ndarray] + A list of points with the specified coordinate converted to degrees. + Returns an empty list if the input is empty. + """ + # "ChatGPT 4o" assisted with generating this docstring. if len(xy_or_xyz_or_xy_seq_or_xyz_seq) == 0: # Then the input xy_or_xyz_or_xy_seq_or_xyz_seq can be interpreted # as an input sequence, that has zero length. @@ -41,40 +71,127 @@ def coord2deg(xy_or_xyz_or_xy_seq_or_xyz_seq, idx): def x2deg(xy_or_xyz_or_xy_seq_or_xyz_seq): + """ + Converts the x-coordinates of a point or sequence of points from radians to degrees. + + Parameters + ---------- + xy_or_xyz_or_xy_seq_or_xyz_seq : np.ndarray or Iterable[np.ndarray] + A single point (2D or 3D) or a sequence of points. + + Returns + ------- + list[np.ndarray] + A list of points with the x-coordinates converted to degrees. + """ + # "ChatGPT 4o" assisted with generating this docstring. return coord2deg(xy_or_xyz_or_xy_seq_or_xyz_seq, 0) def y2deg(xy_or_xyz_or_xy_seq_or_xyz_seq): + """ + Converts the y-coordinates of a point or sequence of points from radians to degrees. + + Parameters + ---------- + xy_or_xyz_or_xy_seq_or_xyz_seq : np.ndarray or Iterable[np.ndarray] + A single point (2D or 3D) or a sequence of points. + + Returns + ------- + list[np.ndarray] + A list of points with the y-coordinates converted to degrees. + """ + # "ChatGPT 4o" assisted with generating this docstring. return coord2deg(xy_or_xyz_or_xy_seq_or_xyz_seq, 1) def z2deg(xyz_or_xyz_seq): + """ + Converts the z-coordinates of a point or sequence of points from radians to degrees. + + Parameters + ---------- + xyz_or_xyz_seq : np.ndarray or Iterable[np.ndarray] + A single point (3D) or a sequence of points. + + Returns + ------- + list[np.ndarray] + A list of points with the z-coordinates converted to degrees. + """ + # "ChatGPT 4o" assisted with generating this docstring. return coord2deg(xyz_or_xyz_seq, 2) def p2deg(pq_or_pq_seq): + """ + Converts the p-coordinates of a point or sequence of points from radians to degrees. + + Parameters + ---------- + pq_or_pq_seq : np.ndarray or Iterable[np.ndarray] + A single point (2D) or a sequence of points. + + Returns + ------- + list[np.ndarray] + A list of points with the p-coordinates converted to degrees. + """ + # "ChatGPT 4o" assisted with generating this docstring. return coord2deg(pq_or_pq_seq, 0) def q2deg(pq_or_pq_seq): + """ + Converts the q-coordinates of a point or sequence of points from radians to degrees. + + Parameters + ---------- + pq_or_pq_seq : np.ndarray or Iterable[np.ndarray] + A single point (2D) or a sequence of points. + + Returns + ------- + list[np.ndarray] + A list of points with the q-coordinates converted to degrees. + """ + # "ChatGPT 4o" assisted with generating this docstring. return coord2deg(pq_or_pq_seq, 1) @overload def normalize(angle: float) -> float: + """Normalizes a single angle to the range [0, 2π].""" + # "ChatGPT 4o" assisted with generating this docstring. pass @overload def normalize(angles: npt.NDArray[np.float_] | Iterable) -> npt.NDArray[np.float_]: + """Normalizes an array of angles to the range [0, 2π].""" + # "ChatGPT 4o" assisted with generating this docstring. pass def normalize(angle_or_angles: float | npt.NDArray[np.float_] | Iterable) -> float | npt.NDArray[np.float_]: - """Adjusts the given angle_or_angles to be in the range 0-2π. + """ + Adjusts the given angle or angles to be in the range [0, 2π]. + Note that because this function operates on floating point math, - your answer is not guaranteed to be exact (for example, a value - of -1e-16 could return 2π).""" + the result may not be exact (e.g., a value of -1e-16 could return 2π). + + Parameters + ---------- + angle_or_angles : float or :py:meth:`npt.NDArray[np.float_]` or Iterable + A single angle or an array/iterable of angles to normalize. + + Returns + ------- + float or py:meth:`npt.NDArray[np.float_]` + The normalized angle or array of normalized angles. + """ + # "ChatGPT 4o" assisted with generating this docstring. if isinstance(angle_or_angles, np.ndarray): angles: np.ndarray = angle_or_angles # limit to the range +-pi @@ -96,14 +213,28 @@ def normalize(angle_or_angles: float | npt.NDArray[np.float_] | Iterable) -> flo def angle2_minus_angle_1(angle_1, angle_2): """ - Returns the signed small angle between the two input angles. - In most cases, this will simply be (angle_2 - angle_1). - However, we correct for cases where the angles wrap around 2pi. - Examples: - angle 1 = 5deg, angle_2 = 10 deg ==> 5 deg. - angle 1 = 355deg, angle_2 = 10 deg ==> 15 deg. - angle 1 = -5deg, angle_2 = -10 deg ==> -5 deg. - angle 1 = 5deg, angle_2 = -10 deg ==> -15 deg. + Calculates the signed small angle between two input angles. + + This function corrects for cases where the angles wrap around 2π. + + Parameters + ---------- + angle_1 : float + The first angle in radians. + angle_2 : float + The second angle in radians. + + Returns + ------- + float + The signed angle difference between angle_2 and angle_1, adjusted for wrapping. + + Examples + -------- + - angle_1 = 5 degrees, angle_2 = 10 degrees ==> 5 degrees + - angle_1 = 355 degrees, angle_2 = 10 degrees ==> 15 degrees + - angle_1 = -5 degrees, angle_2 = -10 degrees ==> -5 degrees + - angle_1 = 5 degrees, angle_2 = -10 degrees ==> -15 degrees """ two_pi = 2 * math.pi diff = angle_2 - angle_1 diff --git a/opencsp/common/lib/geometry/geometry_2d.py b/opencsp/common/lib/geometry/geometry_2d.py index 6d4ed568b..bd25a3899 100644 --- a/opencsp/common/lib/geometry/geometry_2d.py +++ b/opencsp/common/lib/geometry/geometry_2d.py @@ -1,8 +1,5 @@ """ 2-d Geometry Utiltiies - - - """ import math @@ -18,6 +15,32 @@ def homogeneous_line(xy1, xy2): + """ + DEPRECATED: Calculates the homogeneous line coefficients from two points. + + This function returns the coefficients of the line in homogeneous coordinates, + normalized to ensure the coefficients are in a standard form. + + Parameters + ---------- + xy1 : np.ndarray + The coordinates of the first point (x1, y1). + xy2 : np.ndarray + The coordinates of the second point (x2, y2). + + Returns + ------- + list[float] + A list containing the normalized coefficients [A, B, C] of the line equation Ax + By + C = 0. + + Raises + ------ + AssertionError + If the two points are the same, resulting in a degenerate case. + DeprecationWarning + geometry_2d.homogeneous_line is deprecated. Use LineXY instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn('geometry_2d.homogeneous_line is deprecated. Use LineXY instead.', DeprecationWarning, stacklevel=2) # Returns homogeneous line coeffcients, in normalized form. x1 = xy1[0] @@ -41,6 +64,25 @@ def homogeneous_line(xy1, xy2): def flip_homogeneous_line(line): + """ + DEPRECATED: Reverses the sense of the homogeneous line. + + Parameters + ---------- + line : list[float] + The coefficients of the line in homogeneous coordinates [A, B, C]. + + Returns + ------- + list[float] + The coefficients of the flipped line. + + Raises + ------ + DeprecationWarning + geometry_2d.flip_homogeneous_line is deprecated. Use LineXY.flip() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.flip_homogeneous_line is deprecated. Use LineXY.flip() instead.', DeprecationWarning, stacklevel=2 ) @@ -49,6 +91,27 @@ def flip_homogeneous_line(line): def homogeneous_line_signed_distance_to_xy(xy, line): + """ + DEPRECATED: Calculates the signed distance from a point to a homogeneous line. + + Parameters + ---------- + xy : np.ndarray + The coordinates of the point (x, y). + line : list[float] + The coefficients of the line in homogeneous coordinates [A, B, C]. + + Returns + ------- + float + The signed distance from the point to the line. + + Raises + ------ + DeprecationWarning + geometry_2d.homogeneous_line_signed_distance_to_xy is deprecated. Use LineXY.dist_from_line_signed() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.homogeneous_line_signed_distance_to_xy is deprecated. Use LineXY.dist_from_line_signed() instead.', DeprecationWarning, @@ -63,6 +126,28 @@ def homogeneous_line_signed_distance_to_xy(xy, line): def homogeneous_line_y_given_x(x, line): + """ + DEPRECATED: Calculates the y-coordinate on a homogeneous line given an x-coordinate. + + Parameters + ---------- + x : float + The x-coordinate for which to find the corresponding y-coordinate on the line. + line : list[float] + The coefficients of the line in homogeneous coordinates [A, B, C]. + + Returns + ------- + float + The y-coordinate corresponding to the given x-coordinate on the line. + Returns NaN if the line is vertical (B = 0). + + Raises + ------ + DeprecationWarning + geometry_2d.homogeneous_line_y_given_x is deprecated. Use LineXY.y_from_x() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.homogeneous_line_y_given_x is deprecated. Use LineXY.y_from_x() instead.', DeprecationWarning, @@ -78,6 +163,28 @@ def homogeneous_line_y_given_x(x, line): def homogeneous_line_x_given_y(y, line): + """ + DEPRECATED: Calculates the x-coordinate on a homogeneous line given a y-coordinate. + + Parameters + ---------- + y : float + The y-coordinate for which to find the corresponding x-coordinate on the line. + line : list[float] + The coefficients of the line in homogeneous coordinates [A, B, C]. + + Returns + ------- + float + The x-coordinate corresponding to the given y-coordinate on the line. + Returns NaN if the line is horizontal (A = 0). + + Raises + ------ + DeprecationWarning + geometry_2d.homogeneous_line_x_given_y is deprecated. Use LineXY.x_from_y() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.homogeneous_line_x_given_y is deprecated. Use LineXY.x_from_y() instead.', DeprecationWarning, @@ -93,6 +200,29 @@ def homogeneous_line_x_given_y(y, line): def intersect_lines(line1, line2): + """ + DEPRECATED: Calculates the intersection point of two homogeneous lines. + + Parameters + ---------- + line1 : list[float] + The coefficients of the first line in homogeneous coordinates [A1, B1, C1]. + line2 : list[float] + The coefficients of the second line in homogeneous coordinates [A2, B2, C2]. + + Returns + ------- + list[float] + The intersection point (x, y) of the two lines. Returns [NaN, NaN] if the lines are parallel. + + Raises + ------ + ValueError + If the lines are parallel and do not intersect. + DeprecationWarning + geometry_2d.intersect_lines is deprecated. Use LineXY.intersect_with() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.intersect_lines is deprecated. Use LineXY.intersect_with() instead.', DeprecationWarning, @@ -122,6 +252,27 @@ def intersect_lines(line1, line2): def shift_x(ray, dx): + """ + DEPRECATED: Shifts a ray in the x-direction by a specified amount. + + Parameters + ---------- + ray : list[list[float]] + A list containing two points that define the ray, each represented as [x, y]. + dx : float + The amount to shift the ray in the x-direction. + + Returns + ------- + list[list[float]] + A new ray represented by two points shifted in the x-direction. + + Raises + ------ + DeprecationWarning + geometry_2d.shift_x is deprecated. Use Vxy.__add__() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn('geometry_2d.shift_x is deprecated. Use Vxy.__add__() instead.', DeprecationWarning, stacklevel=2) x0 = ray[0][0] y0 = ray[0][1] @@ -131,6 +282,27 @@ def shift_x(ray, dx): def intersect_rays(ray1, ray2): + """ + DEPRECATED: Calculates the intersection point of two rays. + + Parameters + ---------- + ray1 : list[list[float]] + A list containing two points that define the first ray, each represented as [x, y]. + ray2 : list[list[float]] + A list containing two points that define the second ray, each represented as [x, y]. + + Returns + ------- + list[float] + The intersection point (x, y) of the two rays. Returns [NaN, NaN] if the rays do not intersect. + + Raises + ------ + DeprecationWarning + geometry_2d.intersect_rays is deprecated. Use LineXY.intersect_with() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.intersect_rays is deprecated. Use LineXY.intersect_with() instead.', DeprecationWarning, @@ -147,6 +319,22 @@ def intersect_rays(ray1, ray2): def draw_clip_xy_box(view, clip_xy_box): + """ + DEPRECATED: Draws a clipping box in the XY plane. + + Parameters + ---------- + view : object + The view object in which to draw the clipping box. + clip_xy_box : tuple[tuple[float, float], tuple[float, float]] + A tuple defining the clipping box as ((xmin, xmax), (ymin, ymax)). + + Raises + ------ + DeprecationWarning + geometry_2d.draw_clip_xy_box is deprecated. Should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.draw_clip_xy_box is deprecated. Should be migrated to another library.', DeprecationWarning, @@ -163,6 +351,27 @@ def draw_clip_xy_box(view, clip_xy_box): def clip_line_to_xy_box(line, clip_xy_box): + """ + DEPRECATED: Clips a line to the specified XY clipping box. + + Parameters + ---------- + line : list[float] + The coefficients of the line in homogeneous coordinates [A, B, C]. + clip_xy_box : tuple[tuple[float, float], tuple[float, float]] + A tuple defining the clipping box as ((xmin, xmax), (ymin, ymax)). + + Returns + ------- + list[list[float]] + A list of points where the line intersects the clipping box edges. + Returns an empty list if the line is completely outside the clipping box. + + Raises + ------ + geometry_2d.clip_line_to_xy_box is deprecated. Should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.clip_line_to_xy_box is deprecated. Should be migrated to another library.', DeprecationWarning, @@ -207,6 +416,32 @@ def clip_line_to_xy_box(line, clip_xy_box): def extend_ray(ray, clip_xy_box, fail_if_null_result=True): + """ + DEPRECATED: Extends a ray to intersect with the specified clipping box. + + Parameters + ---------- + ray : list[list[float]] + A list containing two points that define the ray, each represented as [x, y]. + clip_xy_box : tuple[tuple[float, float], tuple[float, float]] + A tuple defining the clipping box as ((xmin, xmax), (ymin, ymax)). + fail_if_null_result : bool, optional + If True, raises an error if the ray does not intersect the clipping box. Defaults to True. + + Returns + ------- + list[list[float]] + A new ray represented by two points that extend to the clipping box. + Returns None if the ray is completely outside the clipping box and fail_if_null_result is False. + + Raises + ------ + AssertionError + If the ray is completely outside the clipping box and fail_if_null_result is True. + DeprecationWarning + geometry_2d.extend_ray is deprecated. Should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.extend_ray is deprecated. Should be migrated to another library.', DeprecationWarning, stacklevel=2 ) @@ -280,6 +515,30 @@ def extend_ray(ray, clip_xy_box, fail_if_null_result=True): # this allows code to work if data are a vertical or horizontal line. # def best_fit_line_segment_A(xy_seq): + """ + DEPRECATED: Calculates the best-fit line segment for a sequence of 2D points. + + This function determines the best-fit line segment that minimizes the distance + to a set of points in 2D space. + + Parameters + ---------- + xy_seq : list[list[float]] + A list of points where each point is represented as [x, y]. + + Returns + ------- + list[list[float]] + A list containing two points that define the best-fit line segment. + + Raises + ------ + AssertionError + If the input points are ill-conditioned (all points are the same). + DeprecationWarning + geometry_2d.best_fit_line_segment_A is deprecated. Use LineXY.fit_from_points() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.best_fit_line_segment_A is deprecated. Use LineXY.fit_from_points() instead.', DeprecationWarning, @@ -345,6 +604,28 @@ def best_fit_line_segment_A(xy_seq): def best_fit_line_segment(xy_list): + """ + DEPRECATED: Calculates the best-fit line segment for a sequence of 2D points. + + This function determines the best-fit line segment that minimizes the distance + to a set of points in 2D space. This is a deprecated function; use LineXY.fit_from_points() instead. + + Parameters + ---------- + xy_list : list[list[float]] + A list of points where each point is represented as [x, y]. + + Returns + ------- + list[list[float]] + A list containing two points that define the best-fit line segment. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be replaced with LineXY.fit_from_points(). + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.best_fit_line_segment is deprecated. Use LineXY.fit_from_points() instead.', DeprecationWarning, @@ -359,6 +640,30 @@ def best_fit_line_segment(xy_list): def rotate_about_origin(xy, theta): + """ + DEPRECATED: Rotates a point around the origin by a specified angle. + + This function rotates the point (x, y) by the angle theta (in radians) around the origin (0, 0). + This is a deprecated function; use Vxy.rotate() or TransformXY instead. + + Parameters + ---------- + xy : list[float] + The coordinates of the point to rotate, represented as [x, y]. + theta : float + The angle of rotation in radians. + + Returns + ------- + list[float] + The new coordinates of the point after rotation, represented as [x', y']. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be replaced with Vxy.rotate() or TransformXY. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.rotate_about_origin is deprecated. Use Vxy.rotate() or TransformXY instead.', DeprecationWarning, @@ -374,6 +679,32 @@ def rotate_about_origin(xy, theta): def rotate_about_center(xy, theta, center_xy): + """ + DEPRECATED: Rotates a point around a specified center by a specified angle. + + This function rotates the point (x, y) around the point (cx, cy) by the angle theta (in radians). + This is a deprecated function; use TransformXY instead. + + Parameters + ---------- + xy : list[float] + The coordinates of the point to rotate, represented as [x, y]. + theta : float + The angle of rotation in radians. + center_xy : list[float] + The coordinates of the center point around which to rotate, represented as [cx, cy]. + + Returns + ------- + list[float] + The new coordinates of the point after rotation, represented as [x', y']. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be replaced with TransformXY. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn('geometry_2d.rotate_about_center is deprecated. Use TransformXY instead.', DeprecationWarning, stacklevel=2) x = xy[0] y = xy[1] @@ -391,8 +722,31 @@ def rotate_about_center(xy, theta, center_xy): def rotate_xyz_about_center_xy(xyz, theta, center_xy): """ - A planar rotation. That is, the rotation axis is parallel to the z axis. + DEPRECATED: Rotates a 3D point around a specified center in the XY plane. + + This function performs a planar rotation of the point (x, y, z) around the center (cx, cy) + by the angle theta (in radians). The z-coordinate remains unchanged. + + Parameters + ---------- + xyz : list[float] + The coordinates of the point to rotate, represented as [x, y, z]. + theta : float + The angle of rotation in radians. + center_xy : list[float] + The coordinates of the center point around which to rotate, represented as [cx, cy]. + + Returns + ------- + list[float] + The new coordinates of the point after rotation, represented as [x', y', z]. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be replaced with TransformXYZ, TransformXY, Vxyz.rotate(), or Vxy.rotate(). """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.rotate_xyz_about_center_xy is deprecated. Use TransformXYZ, TransformXY, Vxyz.rotate() oro Vxy.rotate() instead.', DeprecationWarning, @@ -419,6 +773,28 @@ def rotate_xyz_about_center_xy(xyz, theta, center_xy): def label_point(xy_list): + """ + DEPRECATED: Calculates a central label point for a list of 2D points. + + This function computes a reasonable central label point for a given list of points. + It calculates the mean of the x and y coordinates of the points. + + Parameters + ---------- + xy_list : list[list[float]] + A list of points where each point is represented as [x, y]. + + Returns + ------- + list[float] + The coordinates of the central label point, represented as [x_mean, y_mean]. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_2d.label_point is deprecated. Should be migrated to another library.', DeprecationWarning, diff --git a/opencsp/common/lib/geometry/geometry_3d.py b/opencsp/common/lib/geometry/geometry_3d.py index 1023dfe62..7540fd0fa 100644 --- a/opencsp/common/lib/geometry/geometry_3d.py +++ b/opencsp/common/lib/geometry/geometry_3d.py @@ -12,6 +12,30 @@ def direction_uxyz_given_azimuth_elevation(azimuth: float, elevation: float): # Both radians. + """ + DEPRECATED: Calculates the direction vector in 3D space given azimuth and elevation angles. + + This function converts azimuth and elevation angles (in radians) into a unit vector + in 3D space. + + Parameters + ---------- + azimuth : float + The azimuth angle in radians, measured from the positive x-axis. + elevation : float + The elevation angle in radians, measured from the xy-plane. + + Returns + ------- + np.ndarray + A 3D unit vector representing the direction corresponding to the given azimuth and elevation. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.direction_uxyz_given_azimuth_elevation is deprecated. This function should be migrated to another library.', DeprecationWarning, @@ -30,6 +54,27 @@ def direction_uxyz_given_azimuth_elevation(azimuth: float, elevation: float): # def distance_between_xyz_points(xyz_1, xyz_2): + """ + DEPRECATED: Calculates the Euclidean distance between two points in 3D space. + + Parameters + ---------- + xyz_1 : np.ndarray + The coordinates of the first point (x1, y1, z1). + xyz_2 : np.ndarray + The coordinates of the second point (x2, y2, z2). + + Returns + ------- + float + The Euclidean distance between the two points. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should use Vxyz subtraction/magnitude instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.distance_between_xyz_points is deprecated. Use Vxyz subtraction/magnitude instead.', DeprecationWarning, @@ -50,6 +95,27 @@ def distance_between_xyz_points(xyz_1, xyz_2): def vector_3d_cross_product(vxyz_1, vxyz_2): + """ + DEPRECATED: Calculates the cross product of two 3D vectors. + + Parameters + ---------- + vxyz_1 : np.ndarray + The first vector represented as an array-like object [x1, y1, z1]. + vxyz_2 : np.ndarray + The second vector represented as an array-like object [x2, y2, z2]. + + Returns + ------- + list[float] + The resulting vector from the cross product, represented as [x, y, z]. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should use Vxyz.cross() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.vector_3d_cross_product is deprecated. Use Vxyz.cross() instead.', DeprecationWarning, stacklevel=2 ) @@ -59,11 +125,49 @@ def vector_3d_cross_product(vxyz_1, vxyz_2): def vector_3d_norm(vxyz): + """ + DEPRECATED: Calculates the norm (magnitude) of a 3D vector. + + Parameters + ---------- + vxyz : np.ndarray + The vector represented as an array-like object [x, y, z]. + + Returns + ------- + float + The magnitude of the vector. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should use Vxyz.magnitude() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn('geometry_3d.vector_3d_norm is deprecated. Use Vxyz.magnitude() instead.', DeprecationWarning, stacklevel=2) return np.sqrt(vxyz[0] ** 2 + vxyz[1] ** 2 + vxyz[2] ** 2) def normalize_vector_3d(vxyz): + """ + DEPRECATED: Normalizes a 3D vector to unit length. + + Parameters + ---------- + vxyz : np.ndarray + The vector represented as an array-like object [x, y, z]. + + Returns + ------- + list[float] + The normalized vector represented as [x', y', z']. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should use Vxyz.normalize() instead. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.normalize_vector_3d is deprecated. Use Vxyz.normalize() instead.', DeprecationWarning, stacklevel=2 ) @@ -114,6 +218,31 @@ def normalize_vector_3d(vxyz): # return a, b, c # def best_fit_plane_B(xs, ys, zs): + """ + DEPRECATED: Calculates the coefficients of the best-fit plane for a set of 3D points. + + This function computes the best-fit plane that minimizes the distance to a set of points in 3D space. + + Parameters + ---------- + xs : list[float] + A list of x-coordinates of the points. + ys : list[float] + A list of y-coordinates of the points. + zs : list[float] + A list of z-coordinates of the points. + + Returns + ------- + list[float] + A list containing the coefficients [A, B, C] of the plane equation Ax + By + Cz + D = 0. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.best_fit_plane_B is deprecated. Should be migrated to another library.', DeprecationWarning, @@ -152,6 +281,28 @@ def best_fit_plane_B(xs, ys, zs): def best_fit_plane(xyz_list): + """ + DEPRECATED: Calculates the coefficients of the best-fit plane for a sequence of 3D points. + + This function converts the list of 3D points into separate coordinate lists and + computes the best-fit plane coefficients. + + Parameters + ---------- + xyz_list : list[list[float]] + A list of points where each point is represented as [x, y, z]. + + Returns + ------- + list[float] + A list containing the coefficients [A, B, C] of the plane equation Ax + By + Cz + D = 0. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.best_fit_plane is deprecated. Should be migrated to another library.', DeprecationWarning, @@ -171,6 +322,25 @@ def best_fit_plane(xyz_list): def flip_homogeneous_plane(plane): + """ + Reverses the sense of the homogeneous plane. + + Parameters + ---------- + plane : list[float] + The coefficients of the plane in homogeneous coordinates [A, B, C, D]. + + Returns + ------- + list[float] + The coefficients of the flipped plane. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.flip_homogeneous_plane is deprecated. Should be migrated to another library.', DeprecationWarning, @@ -181,6 +351,27 @@ def flip_homogeneous_plane(plane): def homogeneous_plane_signed_distance_to_xyz(xyz, plane): + """ + DEPRECATED: Calculates the signed distance from a point to a homogeneous plane. + + Parameters + ---------- + xyz : list[float] + The coordinates of the point (x, y, z). + plane : list[float] + The coefficients of the plane in homogeneous coordinates [A, B, C, D]. + + Returns + ------- + float + The signed distance from the point to the plane. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.homogeneous_plane_signed_distance_to_xyz is deprecated. Should be migrated to another library.', DeprecationWarning, @@ -205,9 +396,33 @@ def construct_line_3d_given_two_points( xyz_1, xyz_2, tolerance=0.0 ): # ?? SCAFFOLDING RCB -- THE 3-D LINE SHOULD BE A CLASS. """ - A line_3d is an infinite line. - It is not bounded by its defining points; that would be a line segment. + DEPRECATED: Constructs a 3D line from two points. + + This function creates a representation of an infinite line defined by two points in 3D space. + + Parameters + ---------- + xyz_1 : list[float] + The coordinates of the first point (x1, y1, z1). + xyz_2 : list[float] + The coordinates of the second point (x2, y2, z2). + tolerance : float, optional + A tolerance value to determine if the two points are considered the same. Defaults to 0.0. + + Returns + ------- + dict + A dictionary containing attributes of the line, including its length, midpoint, and direction. + + Raises + ------ + AssertionError + If the two points are the same or too close to each other. + + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.construct_line_3d_given_two_points is deprecated. Should be migrated to another library.', DeprecationWarning, @@ -254,8 +469,28 @@ def construct_line_3d_given_two_points( def closest_point_on_line_3d(xyz, line_3d): # ?? SCAFFOLDING RCB -- THE 3-D LINE SHOULD BE A CLASS. """ - Returns the point on the infinite 3d line that is closest to the given point. + DEPRECATED: Finds the closest point on a 3D line to a given point. + + This function calculates the point on the infinite line that is closest to the specified point in 3D space. + + Parameters + ---------- + xyz : list[float] + The coordinates of the point (x, y, z). + line_3d : dict + A dictionary representing the line, containing attributes such as 'mid_xyz' and 'uxyz'. + + Returns + ------- + list[float] + The coordinates of the closest point on the line to the given point. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.closest_point_on_line_3d is deprecated. Should be migrated to another library.', DeprecationWarning, @@ -291,8 +526,26 @@ def closest_point_on_line_3d(xyz, line_3d): # ?? SCAFFOLDING RCB -- THE 3-D LIN def distance_to_line_3d(xyz, line_3d): # ?? SCAFFOLDING RCB -- THE 3-D LINE SHOULD BE A CLASS. """ - Returns the shortest distance from the given point to the infinite 3-d line. + DEPRECATED: Calculates the shortest distance from a point to an infinite 3D line. + + Parameters + ---------- + xyz : list[float] + The coordinates of the point (x, y, z). + line_3d : dict + A dictionary representing the line, containing attributes such as 'mid_xyz' and 'uxyz'. + + Returns + ------- + float + The shortest distance from the point to the line. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be migrated to another library. """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'geometry_3d.distance_to_line_3d is deprecated. Should be migrated to another library.', DeprecationWarning, diff --git a/opencsp/common/lib/geometry/transform_3d.py b/opencsp/common/lib/geometry/transform_3d.py index 245a06756..2d5f56379 100644 --- a/opencsp/common/lib/geometry/transform_3d.py +++ b/opencsp/common/lib/geometry/transform_3d.py @@ -1,8 +1,5 @@ """ -Trransforms, Included Rotation and Translation - - - +Transforms, Included Rotation and Translation """ import math @@ -11,6 +8,32 @@ def axisrotation(unit_vector, angle): # ?? SCAFFOLDING RCB -- ADD UNDERSCORE BETWEEN "AXIS" AND "ROTATION" + """ + DEPRECATED: Calculates the rotation matrix for a given angle around a specified axis. + + This function uses the right-hand rule to compute the rotation matrix based on + the provided unit vector and angle. The angle is expected to be in radians. + + Parameters + ---------- + unit_vector : np.ndarray + A 3D unit vector representing the axis of rotation. + angle : float + The angle of rotation in radians. + + Returns + ------- + np.ndarray + A 3x3 rotation matrix corresponding to the rotation around the specified axis. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be replaced with scipy.spatial.transform.Rotation. + AssertionError + If the input unit_vector is not of unit length. + """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'transform_3d.axisrotation is deprecated. Replace with scipy.spatial.transform.Rotation', DeprecationWarning, @@ -47,13 +70,33 @@ def axisrotation(unit_vector, angle): # ?? SCAFFOLDING RCB -- ADD UNDERSCORE BE def rotation_matrix_to_euler_angles(R): """ - Calculates rotation matrix to Euler angles. - The result is the same as MATLAB except the order - of the Euler angles (x and z are swapped). - + DEPRECATED: Converts a rotation matrix to Euler angles. + + This function computes the Euler angles corresponding to the given rotation matrix. + The order of the angles is such that the x and z angles are swapped compared to MATLAB. + + Parameters + ---------- + R : np.ndarray + A 3x3 rotation matrix. + + Returns + ------- + tuple[float, float, float] + A tuple containing the Euler angles (rot_x, rot_y, rot_z) in radians. + + Raises + ------ + AssertionError + If the input matrix is not a valid rotation matrix. + DeprecationWarning + Indicates that this function is deprecated and should be replaced with scipy.spatial.transform.Rotation. + + Notes + ----- See https://learnopencv.com/rotation-matrix-to-euler-angles/ - """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'transform_3d.rotation_matrix_to_euler_angles is deprecated. Replace with scipy.spatial.transform.Rotation.', DeprecationWarning, @@ -77,11 +120,30 @@ def rotation_matrix_to_euler_angles(R): def is_rotation_matrix(R): """ - Checks if a matrix is a valid rotation matrix. + DEPRECATED: Checks if a matrix is a valid rotation matrix. - See https://learnopencv.com/rotation-matrix-to-euler-angles/ + A valid rotation matrix must be orthogonal and have a determinant of 1. + + Parameters + ---------- + R : np.ndarray + A 3x3 matrix to check. + Returns + ------- + bool + True if the matrix is a valid rotation matrix, False otherwise. + + Raises + ------ + DeprecationWarning + Indicates that this function is deprecated and should be replaced with scipy.spatial.transform.Rotation. + + Notes + ----- + See https://learnopencv.com/rotation-matrix-to-euler-angles/ """ + # "ChatGPT 4o" assisted with generating this docstring. warn( 'transform_3d.is_rotation_matrix is deprecated. Replace with scipy.spatial.transform.Rotation.', DeprecationWarning,