diff --git a/src/ansys/geometry/core/__init__.py b/src/ansys/geometry/core/__init__.py index 4c323e796c..2ad83333f5 100644 --- a/src/ansys/geometry/core/__init__.py +++ b/src/ansys/geometry/core/__init__.py @@ -14,18 +14,3 @@ __version__ = importlib_metadata.version(__name__.replace(".", "-")) """The installed version of PyGeometry.""" - - -# Units -# ------------------------------------------------------------------------------ - -from pint import UnitRegistry - -UNITS = UnitRegistry() -"""Unit manager.""" - -UNIT_LENGTH = UNITS.meter -"""Default length unit for PyGeometry.""" - -UNIT_ANGLE = UNITS.radian -"""Default angle unit for PyGeometry.""" diff --git a/src/ansys/geometry/core/math/__init__.py b/src/ansys/geometry/core/math/__init__.py index db8bbe20e0..ec7a8027d0 100644 --- a/src/ansys/geometry/core/math/__init__.py +++ b/src/ansys/geometry/core/math/__init__.py @@ -1,41 +1,32 @@ """PyGeometry math subpackage.""" +import numpy as np + +from ansys.geometry.core.math.constants import ( + DEFAULT_POINT, + UNIT_VECTOR_X, + UNIT_VECTOR_Y, + UNIT_VECTOR_Z, + ZERO_VECTOR3D, +) from ansys.geometry.core.math.frame import Frame from ansys.geometry.core.math.matrix import Matrix, Matrix33, Matrix44 from ansys.geometry.core.math.plane import Plane -from ansys.geometry.core.math.point import Point2D, Point3D -from ansys.geometry.core.math.vector import ( - QuantityVector2D, - QuantityVector3D, - UnitVector2D, - UnitVector3D, - Vector2D, - Vector3D, -) +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import QuantityVector, UnitVector, Vector __all__ = [ + "DEFAULT_POINT", + "UNIT_VECTOR_X", + "UNIT_VECTOR_Y", + "UNIT_VECTOR_Z", + "ZERO_VECTOR3D", "Frame", "Matrix", "Matrix33", "Matrix44", "Plane", - "Point2D", - "Point3D", - "QuantityVector2D", - "QuantityVector3D", - "UnitVector2D", - "UnitVector3D", - "Vector2D", - "Vector3D", + "Point", + "QuantityVector", + "UnitVector", + "Vector", ] - -UNIT_VECTOR_X = UnitVector3D([1, 0, 0]) -"""Unit vector in the cartesian traditional X direction.""" - -UNIT_VECTOR_Y = UnitVector3D([0, 1, 0]) -"""Unit vector in the cartesian traditional Y direction.""" - -UNIT_VECTOR_Z = UnitVector3D([0, 0, 1]) -"""Unit vector in the cartesian traditional Z direction.""" - -ZERO_VECTOR3D = Vector3D([0, 0, 0]) -"""Zero-valued Vector3D object.""" diff --git a/src/ansys/geometry/core/math/constants.py b/src/ansys/geometry/core/math/constants.py new file mode 100644 index 0000000000..eef2a5fc80 --- /dev/null +++ b/src/ansys/geometry/core/math/constants.py @@ -0,0 +1,26 @@ +"""Mathematical constants for PyGeometry.""" + +from ansys.geometry.core.math.point import DEFAULT_POINT_VALUES, Point +from ansys.geometry.core.math.vector import UnitVector, Vector + +DEFAULT_POINT = Point(DEFAULT_POINT_VALUES) +"""Default value for a 3D ``Point``.""" + +UNIT_VECTOR_X = UnitVector([1, 0, 0]) +"""Unit vector in the cartesian traditional X direction.""" + +UNIT_VECTOR_Y = UnitVector([0, 1, 0]) +"""Unit vector in the cartesian traditional Y direction.""" + +UNIT_VECTOR_Z = UnitVector([0, 0, 1]) +"""Unit vector in the cartesian traditional Z direction.""" + +ZERO_VECTOR3D = Vector([0, 0, 0]) +"""Zero-valued Vector object.""" + +# Define the numpy.ndarrays as read-only - just for the sake of being "safe" +DEFAULT_POINT.setflags(write=False) +UNIT_VECTOR_X.setflags(write=False) +UNIT_VECTOR_Y.setflags(write=False) +UNIT_VECTOR_Z.setflags(write=False) +ZERO_VECTOR3D.setflags(write=False) diff --git a/src/ansys/geometry/core/math/frame.py b/src/ansys/geometry/core/math/frame.py index 930f0652d1..64c7c6db0c 100644 --- a/src/ansys/geometry/core/math/frame.py +++ b/src/ansys/geometry/core/math/frame.py @@ -4,8 +4,8 @@ import numpy as np -from ansys.geometry.core.math.point import Point3D -from ansys.geometry.core.math.vector import UnitVector3D, Vector3D +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import UnitVector, Vector from ansys.geometry.core.misc.checks import check_type, check_type_equivalence from ansys.geometry.core.typing import RealSequence @@ -16,32 +16,32 @@ class Frame: Parameters ---------- - origin : Optional[Union[~numpy.ndarray, RealSequence, Point3D]] + origin : Optional[Union[~numpy.ndarray, RealSequence, Point]] Centered origin of the ``Frame``. By default, cartesian origin. - direction_x: Optional[Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]] + direction_x: Optional[Union[~numpy.ndarray, RealSequence, UnitVector, Vector]] X-axis direction. By default, ``UNIT_VECTOR_X`` - direction_y: Optional[Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]] + direction_y: Optional[Union[~numpy.ndarray, RealSequence, UnitVector, Vector]] Y-axis direction. By default, ``UNIT_VECTOR_Y`` """ def __init__( self, - origin: Union[np.ndarray, RealSequence, Point3D] = [0, 0, 0], - direction_x: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D] = [1, 0, 0], - direction_y: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D] = [0, 1, 0], + origin: Union[np.ndarray, RealSequence, Point] = [0, 0, 0], + direction_x: Union[np.ndarray, RealSequence, UnitVector, Vector] = [1, 0, 0], + direction_y: Union[np.ndarray, RealSequence, UnitVector, Vector] = [0, 1, 0], ): """Constructor method for ``Frame``.""" - check_type(origin, (np.ndarray, List, Point3D)) - check_type(direction_x, (np.ndarray, List, UnitVector3D, Vector3D)) - check_type(direction_y, (np.ndarray, List, UnitVector3D, Vector3D)) + check_type(origin, (np.ndarray, List, Point)) + check_type(direction_x, (np.ndarray, List, UnitVector, Vector)) + check_type(direction_y, (np.ndarray, List, UnitVector, Vector)) - self._origin = Point3D(origin) if not isinstance(origin, Point3D) else origin + self._origin = Point(origin) if not isinstance(origin, Point) else origin self._direction_x = ( - UnitVector3D(direction_x) if not isinstance(direction_x, UnitVector3D) else direction_x + UnitVector(direction_x) if not isinstance(direction_x, UnitVector) else direction_x ) self._direction_y = ( - UnitVector3D(direction_y) if not isinstance(direction_y, UnitVector3D) else direction_y + UnitVector(direction_y) if not isinstance(direction_y, UnitVector) else direction_y ) # origin is fixed once the frame is built @@ -50,25 +50,25 @@ def __init__( if not self._direction_x.is_perpendicular_to(self._direction_y): raise ValueError("Direction x and direction y must be perpendicular") - self._direction_z = UnitVector3D(self._direction_x % self._direction_y) + self._direction_z = UnitVector(self._direction_x % self._direction_y) @property - def origin(self) -> Point3D: + def origin(self) -> Point: """Return the origin of the ``Frame``.""" return self._origin @property - def direction_x(self) -> UnitVector3D: + def direction_x(self) -> UnitVector: """Return the X-axis direction of the ``Frame``.""" return self._direction_x @property - def direction_y(self) -> UnitVector3D: + def direction_y(self) -> UnitVector: """Return the Y-axis direction of the ``Frame``.""" return self._direction_y @property - def direction_z(self) -> UnitVector3D: + def direction_z(self) -> UnitVector: """Return the Z-axis direction of the ``Frame``.""" return self._direction_z diff --git a/src/ansys/geometry/core/math/plane.py b/src/ansys/geometry/core/math/plane.py index 9d963f1242..26c5199ec3 100644 --- a/src/ansys/geometry/core/math/plane.py +++ b/src/ansys/geometry/core/math/plane.py @@ -5,8 +5,8 @@ import numpy as np from ansys.geometry.core.math.frame import Frame -from ansys.geometry.core.math.point import Point3D -from ansys.geometry.core.math.vector import UnitVector3D, Vector3D +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import UnitVector, Vector from ansys.geometry.core.typing import RealSequence @@ -16,19 +16,19 @@ class Plane(Frame): Parameters ---------- - origin : Optional[Union[~numpy.ndarray, RealSequence, Point3D]] + origin : Optional[Union[~numpy.ndarray, RealSequence, Point]] Centered origin of the ``Frame``. By default, cartesian origin. - direction_x: Optional[Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]] + direction_x: Optional[Union[~numpy.ndarray, RealSequence, UnitVector, Vector]] X-axis direction. By default, ``UNIT_VECTOR_X`` - direction_y: Optional[Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D]] + direction_y: Optional[Union[~numpy.ndarray, RealSequence, UnitVector, Vector]] Y-axis direction. By default, ``UNIT_VECTOR_Y`` """ def __init__( self, - origin: Union[np.ndarray, RealSequence, Point3D] = [0, 0, 0], - direction_x: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D] = [1, 0, 0], - direction_y: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D] = [0, 1, 0], + origin: Union[np.ndarray, RealSequence, Point] = [0, 0, 0], + direction_x: Union[np.ndarray, RealSequence, UnitVector, Vector] = [1, 0, 0], + direction_y: Union[np.ndarray, RealSequence, UnitVector, Vector] = [0, 1, 0], ): """Constructor method for ``Plane``.""" super().__init__(origin, direction_x, direction_y) diff --git a/src/ansys/geometry/core/math/point.py b/src/ansys/geometry/core/math/point.py index 2a30769b5f..b4715b7402 100644 --- a/src/ansys/geometry/core/math/point.py +++ b/src/ansys/geometry/core/math/point.py @@ -1,223 +1,134 @@ -"""``Point3D`` and ``Point2D`` class module.""" +"""``Point`` class module.""" -from typing import List, Optional, Union +from typing import Optional, Union import numpy as np -from pint import Unit +from pint import Quantity, Unit -from ansys.geometry.core import UNIT_LENGTH, UNITS from ansys.geometry.core.misc import ( - check_is_float_int, - check_is_pint_unit, + UNIT_LENGTH, check_ndarray_is_float_int, - check_pint_unit_compatibility, check_type_equivalence, + only_for_3d, ) -from ansys.geometry.core.typing import Real, RealSequence +from ansys.geometry.core.misc.units import PhysicalQuantity +from ansys.geometry.core.typing import RealSequence -DEFAULT_POINT3D = [None, None, None] -"""Default value for ``Point3D``""" +DEFAULT_POINT_VALUES = [np.nan, np.nan, np.nan] +"""Default values for a ``Point``.""" -DEFAULT_POINT2D = [None, None] -"""Default value for ``Point2D``""" - -class Point3D(np.ndarray): +class Point(np.ndarray, PhysicalQuantity): """ - Provides Point3D geometry primitive representation. + Provides Point geometry primitive representation. + + 2D and 3D points are supported with the same class upon initialization. Parameters ---------- - input : Union[~numpy.ndarray, RealSequence, List[None]], optional + input : Union[~numpy.ndarray, RealSequence], optional The direction arguments, either as a :class:`numpy.ndarray`, or as a RealSequence. - By default, ``DEFAULT_POINT3D``. + By default, ``[np.nan, np.nan, np.nan]``. unit : ~pint.Unit, optional - Units employed to define the Point3D values, by default ``UNIT_LENGTH`` + Units employed to define the Point values, by default ``UNIT_LENGTH``. """ def __new__( cls, - input: Optional[Union[np.ndarray, RealSequence, List[None]]] = DEFAULT_POINT3D, + input: Optional[Union[np.ndarray, RealSequence]] = DEFAULT_POINT_VALUES, unit: Optional[Unit] = UNIT_LENGTH, ): - """Constructor for ``Point3D``.""" - - # Check if we are dealing with the default value - if input is DEFAULT_POINT3D: - obj = np.asarray(DEFAULT_POINT3D).view(cls) - obj._unit = None - _, obj._base_unit = UNITS.get_base_units(UNIT_LENGTH) - return obj - - # Check that units provided (if any) are compatible - check_is_pint_unit(unit, "unit") - check_pint_unit_compatibility(unit, UNIT_LENGTH) - - # Transform to numpy.ndarray - obj = np.asarray([(elem * unit).to_base_units().magnitude for elem in input]).view(cls) - obj._unit = unit - _, obj._base_unit = UNITS.get_base_units(unit) - - # Check that the size is as expected - if obj is None or len(obj) != 3: - raise ValueError("Point3D must have three coordinates.") - - # If we are not dealing with the default value... check the inputs - check_ndarray_is_float_int(obj, "input") - - # If all checks went through, return the Point3D - return obj - - @property - def x(self) -> Union[Real, None]: - """Returns the X plane component value.""" - return UNITS.convert(self[0], self._base_unit, self._unit) - - @x.setter - def x(self, x: Real) -> None: - """Set the X plane component value.""" - check_is_float_int(x, "x") - self[0] = (x * self._unit).to_base_units().magnitude - - @property - def y(self) -> Union[Real, None]: - """Returns the Y plane component value.""" - return UNITS.convert(self[1], self._base_unit, self._unit) - - @y.setter - def y(self, y: Real) -> None: - """Set the Y plane component value.""" - check_is_float_int(y, "y") - self[1] = (y * self._unit).to_base_units().magnitude - - @property - def z(self) -> Union[Real, None]: - """Returns the Z plane component value.""" - return UNITS.convert(self[2], self._base_unit, self._unit) - - @z.setter - def z(self, z: Real) -> None: - """Set the Z plane component value.""" - check_is_float_int(z, "z") - self[2] = (z * self._unit).to_base_units().magnitude - - @property - def unit(self) -> Unit: - """Returns the unit of the object.""" - return self._unit - - @unit.setter - def unit(self, unit: Unit) -> None: - """Sets the unit of the object.""" - check_is_pint_unit(unit, "unit") - check_pint_unit_compatibility(unit, UNIT_LENGTH) - self._unit = unit - - @property - def base_unit(self) -> Unit: - """Returns the base unit of the object.""" - return self._base_unit - - def __eq__(self, other: "Point3D") -> bool: - """Equals operator for ``Point3D``.""" - check_type_equivalence(other, self) - return np.array_equal(self, other) - - def __ne__(self, other: "Point3D") -> bool: - """Not equals operator for ``Point3D``.""" - return not self == other - - -class Point2D(np.ndarray): - """ - Provides Point2D geometry primitive representation. - - Parameters - ---------- - input : Union[~numpy.ndarray, RealSequence, List[None]], optional - The direction arguments, either as a :class:`numpy.ndarray`, or as a RealSequence. - By default, ``DEFAULT_POINT3D``. - unit : ~pint.Unit, optional - Units employed to define the Point3D values, by default ``UNIT_LENGTH`` - """ + """Constructor for ``Point``.""" + # Build an empty np.ndarray object + return np.zeros(len(input)).view(cls) - def __new__( - cls, - input: Optional[Union[np.ndarray, RealSequence, List[None]]] = DEFAULT_POINT2D, + def __init__( + self, + input: Union[np.ndarray, RealSequence] = DEFAULT_POINT_VALUES, unit: Optional[Unit] = UNIT_LENGTH, ): - """Constructor for ``Point2D``.""" - - # Check if we are dealing with the default value - if input is DEFAULT_POINT2D: - obj = np.asarray(DEFAULT_POINT2D).view(cls) - obj._unit = None - _, obj._base_unit = UNITS.get_base_units(UNIT_LENGTH) - return obj - - # Check that units provided (if any) are compatible - check_is_pint_unit(unit, "unit") - check_pint_unit_compatibility(unit, UNIT_LENGTH) + # Call the PhysicalQuantity ctor + super().__init__(unit, expected_dimensions=UNIT_LENGTH) + + # Check the inputs + check_ndarray_is_float_int(input, "input") if isinstance( + input, np.ndarray + ) else check_ndarray_is_float_int(np.asarray(input), "input") + + # Check dimensions + if len(input) == 2: + self._is_3d = False + elif len(input) == 3: + self._is_3d = True + else: + raise ValueError( + "Point class can only receive 2 or 3 arguments, creating a 2D or 3D point, respectively." # noqa: E501 + ) + + # Store values + self.flat = [(elem * self.unit).to_base_units().magnitude for elem in input] + + def __eq__(self, other: "Point") -> bool: + """Equals operator for ``Point``.""" + check_type_equivalence(other, self) + return self.is_3d == other.is_3d and np.array_equal(self, other) - # Transform to numpy.ndarray - obj = np.asarray([(elem * unit).to_base_units().magnitude for elem in input]).view(cls) - obj._unit = unit - _, obj._base_unit = UNITS.get_base_units(unit) + def __ne__(self, other: "Point") -> bool: + """Not equals operator for ``Point``.""" + return not self == other - # Check that the size is as expected - if obj is None or len(obj) != 2: - raise ValueError("Point2D must have two coordinates.") + def __set_value(self, input: Quantity, idx: int) -> None: + """General setter method for ``Point`` class.""" + self[idx] = self._base_units_magnitude(input) - # If we are not dealing with the default value... check the inputs - check_ndarray_is_float_int(obj, "input") + @property + def is_3d(self) -> bool: + """Returns ``True`` if our ``Point`` is defined in 3D space.""" + return self._is_3d - # If all checks went through, return the Point2D - return obj + @property + def is_2d(self) -> bool: + """Returns ``True`` if our ``Point`` is defined in 2D space.""" + return not self.is_3d @property - def x(self) -> Union[Real, None]: + def x(self) -> Quantity: """Returns the X plane component value.""" - return UNITS.convert(self[0], self._base_unit, self._unit) + return self._get_quantity(self[0]) @x.setter - def x(self, x: Real) -> None: + def x(self, x: Quantity) -> None: """Set the X plane component value.""" - check_is_float_int(x, "x") - self[0] = (x * self._unit).to_base_units().magnitude + self.__set_value(x, 0) @property - def y(self) -> Union[Real, None]: + def y(self) -> Quantity: """Returns the Y plane component value.""" - return UNITS.convert(self[1], self._base_unit, self._unit) + return self._get_quantity(self[1]) @y.setter - def y(self, y: Real) -> None: + def y(self, y: Quantity) -> None: """Set the Y plane component value.""" - check_is_float_int(y, "y") - self[1] = (y * self._unit).to_base_units().magnitude + self.__set_value(y, 1) @property - def unit(self) -> Unit: - """Returns the unit of the object.""" - return self._unit - - @unit.setter - def unit(self, unit: Unit) -> None: - """Sets the unit of the object.""" - check_is_pint_unit(unit, "unit") - check_pint_unit_compatibility(unit, UNIT_LENGTH) - self._unit = unit + @only_for_3d + def z(self) -> Quantity: + """Returns the Z plane component value. - @property - def base_unit(self) -> Unit: - """Returns the base unit of the object.""" - return self._base_unit + Notes + ----- + Only valid for ``Point`` objects defined in 3D space. + """ + return self._get_quantity(self[2]) - def __eq__(self, other: "Point2D") -> bool: - """Equals operator for ``Point2D``.""" - check_type_equivalence(other, self) - return np.array_equal(self, other) - - def __ne__(self, other: "Point2D") -> bool: - """Not equals operator for ``Point2D``.""" - return not self == other + @z.setter + @only_for_3d + def z(self, z: Quantity) -> None: + """Set the Z plane component value. + + Notes + ----- + Only valid for ``Point`` objects defined in 3D space. + """ + self.__set_value(z, 2) diff --git a/src/ansys/geometry/core/math/vector.py b/src/ansys/geometry/core/math/vector.py index 743372a942..7f60624323 100644 --- a/src/ansys/geometry/core/math/vector.py +++ b/src/ansys/geometry/core/math/vector.py @@ -3,72 +3,114 @@ from typing import Union import numpy as np -from pint import Unit +from pint import Quantity, Unit -from ansys.geometry.core import UNITS -from ansys.geometry.core.accuracy import Accuracy -from ansys.geometry.core.math.point import Point2D, Point3D +from ansys.geometry.core.math.point import Point from ansys.geometry.core.misc import ( + Accuracy, check_is_float_int, - check_is_pint_unit, check_ndarray_is_float_int, check_pint_unit_compatibility, check_type, check_type_equivalence, + only_for_3d, ) +from ansys.geometry.core.misc.units import PhysicalQuantity from ansys.geometry.core.typing import Real, RealSequence -class Vector3D(np.ndarray): - """A three-dimensional vector with Cartesian coordinates. +class Vector(np.ndarray): + """A 2- or 3-dimensional vector class. Parameters ---------- input : Union[~numpy.ndarray, RealSequence] - One dimensional :class:`numpy.ndarray` with shape(3,) + 2-/3-dimensional :class:`numpy.ndarray` with shape(X,). """ def __new__(cls, input: Union[np.ndarray, RealSequence]): - """Constructor for ``Vector3D``""" + """Constructor for ``Vector``""" obj = np.asarray(input).view(cls) # Check that the size is as expected - if len(obj) != 3: - raise ValueError("Vector3D must have three coordinates.") + obj = Vector._set_vector_dimensions(obj) # Check the input data check_ndarray_is_float_int(obj, "input") return obj + @classmethod + def _set_vector_dimensions(cls, obj: "Vector") -> "Vector": + # Check that the size is as expected + if len(obj) == 2: + obj._is_3d = False + elif len(obj) == 3: + obj._is_3d = True + else: + raise ValueError( + "Vector class can only receive 2 or 3 arguments, creating a 2D or 3D vector, respectively." # noqa: E501 + ) + + return obj + + @property + def is_3d(self) -> bool: + """Returns ``True`` if our ``Vector`` is defined in 3D space.""" + return self._is_3d + + @property + def is_2d(self) -> bool: + """Returns ``True`` if our ``Vector`` is defined in 2D space.""" + return not self.is_3d + + def same_dimension_as(self, other: "Vector") -> bool: + """Returns ``True`` if both ``Vector`` objects have the same dimensions.""" + return self.is_3d == other.is_3d + @property def x(self) -> Real: - """X coordinate of ``Vector3D``""" + """X coordinate of ``Vector``.""" return self[0] @x.setter def x(self, value: Real) -> None: + """Sets the Y coordinate of ``Vector``.""" check_is_float_int(value, "x") self[0] = value @property def y(self) -> Real: - """Y coordinate of ``Vector3D``""" + """Y coordinate of ``Vector``.""" return self[1] @y.setter def y(self, value: Real) -> None: + """Sets the Y coordinate of ``Vector``.""" check_is_float_int(value, "y") self[1] = value @property + @only_for_3d def z(self) -> Real: - """Z coordinate of ``Vector3D``""" + """Z coordinate of ``Vector``. + + Notes + ----- + Only valid for ``Vector`` objects defined in 3D space. + """ return self[2] @z.setter + @only_for_3d def z(self, value: Real) -> None: + """Sets the Z coordinate of ``Vector``. + + Notes + ----- + Only valid for ``Vector`` objects defined in 3D space. + """ check_is_float_int(value, "z") self[2] = value @@ -76,8 +118,8 @@ def z(self, value: Real) -> None: def norm(self) -> float: return np.linalg.norm(self) - def is_perpendicular_to(self, other_vector: "Vector3D") -> bool: - """Verifies if the two ``Vector3D`` instances are perpendicular.""" + def is_perpendicular_to(self, other_vector: "Vector") -> bool: + """Verifies if the two ``Vector`` instances are perpendicular.""" if self.is_zero or other_vector.is_zero: return False @@ -86,31 +128,36 @@ def is_perpendicular_to(self, other_vector: "Vector3D") -> bool: @property def is_zero(self) -> bool: - """Confirms whether all components of ``Vector3D`` are zero.""" - return self.x == 0 and self.y == 0 and self.z == 0 + """Confirms whether all components of the ``Vector`` are zero.""" + return all([comp == 0 for comp in self]) - def normalize(self) -> "Vector3D": - """Return a normalized version of the ``Vector3D``""" + def normalize(self) -> "Vector": + """Return a normalized version of the ``Vector``.""" norm = self.norm if norm > 0: - return self / norm + return Vector(self / norm) else: - raise ValueError("The norm of the Vector3D is not valid.") - - def cross(self, v: "Vector3D") -> "Vector3D": - """Return cross product of Vector3D""" - return Vector3D(np.cross(self, v)) + raise ValueError("The norm of the Vector is not valid.") + + @only_for_3d + def cross(self, v: "Vector") -> "Vector": + """Return cross product of 3D Vector objects""" + check_type_equivalence(v, self) + if self.same_dimension_as(v): + return Vector(np.cross(self, v)) + else: + raise ValueError("Invalid Vector dimensions for cross product.") - def __eq__(self, other: "Vector3D") -> bool: - """Equals operator for ``Vector3D``.""" + def __eq__(self, other: "Vector") -> bool: + """Equals operator for ``Vector``.""" check_type_equivalence(other, self) - return np.array_equal(self, other) + return self.is_3d == other.is_3d and np.array_equal(self, other) - def __ne__(self, other: "Vector3D") -> bool: - """Not equals operator for ``Vector3D``.""" + def __ne__(self, other: "Vector") -> bool: + """Not equals operator for ``Vector``.""" return not self == other - def __mul__(self, other: Union["Vector3D", Real]) -> Union["Vector3D", Real]: + def __mul__(self, other: Union["Vector", Real]) -> Union["Vector", Real]: """Overload * operator with dot product. Note @@ -118,451 +165,207 @@ def __mul__(self, other: Union["Vector3D", Real]) -> Union["Vector3D", Real]: Also admits scalar multiplication. """ if isinstance(other, (int, float)): - return np.multiply(self, other).view(self.__class__) + return Vector(np.multiply(self, other)) else: check_type_equivalence(other, self) - return self.dot(other) + if self.same_dimension_as(other): + return self.dot(other) + else: + raise ValueError("Invalid Vector dimensions for dot product.") - def __mod__(self, other: "Vector3D") -> "Vector3D": + @only_for_3d + def __mod__(self, other: "Vector") -> "Vector": """Overload % operator with cross product.""" - check_type_equivalence(other, self) - return self.cross(other).view(self.__class__) + return self.cross(other) @classmethod def from_points( cls, - point_a: Union[np.ndarray, RealSequence, Point3D], - point_b: Union[np.ndarray, RealSequence, Point3D], + point_a: Union[np.ndarray, RealSequence, Point], + point_b: Union[np.ndarray, RealSequence, Point], ): - """Create a ``Vector3D`` from two distinct ``Point3D``. + """Create a ``Vector3D`` from two distinct ``Point``. Parameters ---------- - point_a : Point3D - A :class:`Point3D` representing the first point. - point_b : Point3D - A :class:`Point3D` representing the second point. + point_a : Point + A :class:`Point` representing the first point. + point_b : Point + A :class:`Point` representing the second point. Returns ------- Vector3D A ``Vector3D`` from ``point_a`` to ``point_b``. """ - if point_a == point_b: - raise ValueError("The two points cannot have the exact same coordinates.") - - x = point_b[0] - point_a[0] - y = point_b[1] - point_a[1] - z = point_b[2] - point_a[2] - return Vector3D([x, y, z]) - - -class Vector2D(np.ndarray): - """A two-dimensional vector with Cartesian coordinates. - - Parameters - ---------- - input : Union[~numpy.ndarray, RealSequence] - One dimensional :class:`numpy.ndarray` with shape(2,) - """ - - def __new__(cls, input: Union[np.ndarray, RealSequence]): - - obj = np.asarray(input).view(cls) - - # Check that the size is as expected - if len(obj) != 2: - raise ValueError("Vector2D must have two coordinates.") - - # Check the input data - check_ndarray_is_float_int(obj, "input") - - return obj - - @property - def x(self) -> Real: - """X coordinate of ``Vector2D``""" - return self[0] - - @x.setter - def x(self, value: Real) -> None: - check_is_float_int(value, "x") - self[0] = value - - @property - def y(self) -> Real: - """Y coordinate of ``Vector2D``""" - return self[1] - - @y.setter - def y(self, value: Real) -> None: - check_is_float_int(value, "y") - self[1] = value - - @property - def norm(self) -> float: - return np.linalg.norm(self) - - def normalize(self) -> "Vector2D": - """Return a normalized version of the ``Vector2D``""" - norm = self.norm - if norm > 0: - return self / norm - else: - raise ValueError("The norm of the Vector2D is not valid.") - - def __eq__(self, other: "Vector2D") -> bool: - """Equals operator for ``Vector2D``.""" - check_type_equivalence(other, self) - return np.array_equal(self, other) - - def __ne__(self, other: "Vector2D") -> bool: - """Not equals operator for ``Vector2D``.""" - return not self == other - - def __mul__(self, other: Union["Vector2D", Real]) -> Union["Vector2D", Real]: - """Overload * operator with dot product. - - Note - ---- - Also admits scalar multiplication. - """ - if isinstance(other, (int, float)): - return np.multiply(self, other).view(self.__class__) - else: - check_type_equivalence(other, self) - return self.dot(other) - - @classmethod - def from_points( - cls, - point_a: Union[np.ndarray, RealSequence, Point2D], - point_b: Union[np.ndarray, RealSequence, Point2D], - ): - """Create a ``Vector2D`` from two distinct ``Point2D``. - - Parameters - ---------- - point_a : Point2D - A :class:`Point2D` representing the first point. - point_b : Point2D - A :class:`Point2D` representing the second point. - - Returns - ------- - Vector2D - A ``Vecto2D`` from ``point_a`` to ``point_b``. - """ - if point_a == point_b: - raise ValueError("The two points cannot have the exact same coordinates.") - - return Vector2D([point_b[0] - point_a[0], point_b[1] - point_a[1]]) + check_type(point_a, (Point, np.ndarray, list)) + check_type(point_b, (Point, np.ndarray, list)) + return Vector(point_b - point_a) -class UnitVector3D(Vector3D): - """A three-dimensional ``UnitVector`` class. +class UnitVector(Vector): + """A 2-/3-dimensional ``UnitVector`` class. Parameters ---------- - input : ~numpy.ndarray, ``Vector3D`` - * One dimensional :class:`numpy.ndarray` with shape(3,) - * Vector3D + input : ~numpy.ndarray, ``Vector`` + * One dimensional :class:`numpy.ndarray` with shape(X,) + * Vector """ - def __new__(cls, input: Union[np.ndarray, RealSequence, Vector3D]): - obj = Vector3D(input) if not isinstance(input, Vector3D) else input + def __new__(cls, input: Union[np.ndarray, RealSequence, Vector]): + obj = Vector(input) if not isinstance(input, Vector) else input obj = obj.normalize().view(cls) + obj = Vector._set_vector_dimensions(obj) obj.setflags(write=False) return obj - @Vector3D.x.setter + @Vector.x.setter def x(self, value: Real) -> None: - raise UnsupportedOperation("UnitVector3D is immutable.") + raise UnsupportedOperation("UnitVector is immutable.") - @Vector3D.y.setter + @Vector.y.setter def y(self, value: Real) -> None: - raise UnsupportedOperation("UnitVector3D is immutable.") + raise UnsupportedOperation("UnitVector is immutable.") - @Vector3D.z.setter + @Vector.z.setter def z(self, value: Real) -> None: - raise UnsupportedOperation("UnitVector3D is immutable.") - - -class UnitVector2D(Vector2D): - """A two-dimensional ``UnitVector`` with Cartesian coordinates. - - Parameters - ---------- - input : ~numpy.ndarray, Vector2D - * One dimensional :class:`numpy.ndarray` with shape(2,) - * Vector2D - """ + raise UnsupportedOperation("UnitVector is immutable.") - def __new__(cls, input: Union[np.ndarray, RealSequence, Vector2D]): - obj = Vector2D(input) if not isinstance(input, Vector2D) else input - obj = obj.normalize().view(cls) - obj.setflags(write=False) - return obj - @Vector2D.x.setter - def x(self, value: Real) -> None: - raise UnsupportedOperation("UnitVector2D is immutable.") - - @Vector2D.y.setter - def y(self, value: Real) -> None: - raise UnsupportedOperation("UnitVector2D is immutable.") +class QuantityVector(Vector, PhysicalQuantity): + def __new__(cls, vector: Union[np.ndarray, RealSequence, Vector], unit: Unit): + """Constructor for ``QuantityVector``.""" + # Build an empty np.ndarray object + return np.zeros(len(vector)).view(cls) + def __init__( + self, + vector: Union[np.ndarray, RealSequence, Vector], + unit: Unit, + ): + # Call the PhysicalQuantity ctor + super().__init__(unit, expected_dimensions=None) + + # Check the inputs + check_ndarray_is_float_int(vector, "vector") if isinstance( + vector, np.ndarray + ) else check_ndarray_is_float_int(np.asarray(vector), "vector") + + # Check dimensions + if len(vector) == 2: + self._is_3d = False + elif len(vector) == 3: + self._is_3d = True + else: + raise ValueError( + "Vector class can only receive 2 or 3 arguments, creating a 2D or 3D vector, respectively." # noqa: E501 + ) -class QuantityVector3D(Vector3D): - def __new__(cls, vector: Union[np.ndarray, RealSequence, Vector3D], unit: Unit): - """Constructor for ``QuantityVector3D``.""" - - # Transform to base units - check_is_pint_unit(unit, "unit") - vector_base_units = [(elem * unit).to_base_units().magnitude for elem in vector] - - # Build the Vector3D object - obj = Vector3D(vector_base_units) - obj = obj.view(cls) - - # Store the units - obj._unit = unit - _, obj._base_unit = UNITS.get_base_units(unit) - - return obj + # Store values + self.flat = [(elem * self.unit).to_base_units().magnitude for elem in vector] @property - def x(self) -> Real: - """X coordinate of ``QuantityVector3D``.""" - return UNITS.convert(Vector3D.x.fget(self), self._base_unit, self._unit) + def x(self) -> Quantity: + """X coordinate of ``QuantityVector``.""" + return self._get_quantity(Vector.x.fget(self)) @x.setter - def x(self, x: Real) -> None: - """Set X coordinate of ``QuantityVector3D``.""" - check_is_float_int(x, "x") - Vector3D.x.fset(self, (x * self._unit).to_base_units().magnitude) + def x(self, x: Quantity) -> None: + """Set X coordinate of ``QuantityVector``.""" + Vector.x.fset(self, self._base_units_magnitude(x)) @property - def y(self) -> Real: - """Y coordinate of ``QuantityVector3D``.""" - return UNITS.convert(Vector3D.y.fget(self), self._base_unit, self._unit) + def y(self) -> Quantity: + """Y coordinate of ``QuantityVector``.""" + return self._get_quantity(Vector.y.fget(self)) @y.setter - def y(self, y: Real) -> None: - """Set Y coordinate of ``QuantityVector3D``.""" - check_is_float_int(y, "y") - Vector3D.y.fset(self, (y * self._unit).to_base_units().magnitude) + def y(self, y: Quantity) -> None: + """Set Y coordinate of ``QuantityVector``.""" + Vector.y.fset(self, self._base_units_magnitude(y)) @property - def z(self) -> Real: - """Z coordinate of ``QuantityVector3D``.""" - return UNITS.convert(Vector3D.z.fget(self), self._base_unit, self._unit) + @only_for_3d + def z(self) -> Quantity: + """Z coordinate of ``QuantityVector``.""" + return self._get_quantity(Vector.z.fget(self)) @z.setter - def z(self, z: Real) -> None: - """Set Z coordinate of ``QuantityVector3D``.""" - check_is_float_int(z, "z") - Vector3D.z.fset(self, (z * self._unit).to_base_units().magnitude) + @only_for_3d + def z(self, z: Quantity) -> None: + """Set Z coordinate of ``QuantityVector``.""" + Vector.z.fset(self, self._base_units_magnitude(z)) @property - def norm(self) -> float: - """Norm of ``QuantityVector3D``.""" - return UNITS.convert(Vector3D.norm.fget(self), self._base_unit, self._unit) + def norm(self) -> Quantity: + """Norm of ``QuantityVector``.""" + return self._get_quantity(Vector.norm.fget(self)) - @property - def unit(self) -> Unit: - """Returns the unit of the ``QuantityVector3D``.""" - return self._unit - - @unit.setter - def unit(self, unit: Unit) -> None: - """Sets the unit of the ``QuantityVector3D``.""" - check_is_pint_unit(unit, "unit") - check_pint_unit_compatibility(unit, self._base_unit) - self._unit = unit - - @property - def base_unit(self) -> Unit: - """Returns the base unit of the ``QuantityVector3D``.""" - return self._base_unit - - def normalize(self) -> "QuantityVector3D": - """Return a normalized version of the ``QuantityVector3D``""" - vec = Vector3D.normalize(self).view(QuantityVector3D) - vec._unit = self._unit - vec._base_unit = self._base_unit - return vec + def normalize(self) -> Vector: + """Return a normalized version of the ``QuantityVector``. - def cross(self, v: "QuantityVector3D") -> "QuantityVector3D": - """Return cross product of ``QuantityVector3D``""" - check_pint_unit_compatibility(v._base_unit, self._base_unit) - vec = Vector3D.cross(self, v).view(QuantityVector3D) + Notes + ----- + This will return a simple ``Vector`` class. Units will + be lost since they no longer hold any meaning. + """ + norm = self.norm.to_base_units().magnitude + if norm > 0: + return Vector(self / norm) + else: + raise ValueError("The norm of the Vector is not valid.") - # At this point, data is stored as base_unit^2 - factor, _ = UNITS.get_base_units(self._unit) - vec /= factor + @only_for_3d + def cross(self, v: "QuantityVector") -> "QuantityVector": + """Return cross product of ``QuantityVector``. - vec._unit = self._unit - vec._base_unit = self._base_unit + Notes + ----- + ``QuantityVector`` returned will hold the same units as self. + """ + check_type_equivalence(v, self) + check_pint_unit_compatibility(v.base_unit, self.base_unit) + vec = QuantityVector(Vector.cross(self, v), self.base_unit) + vec.unit = self.unit return vec - def __eq__(self, other: "QuantityVector3D") -> bool: - """Equals operator for ``QuantityVector3D``.""" + def __eq__(self, other: "QuantityVector") -> bool: + """Equals operator for ``QuantityVector``.""" check_type_equivalence(other, self) - return np.array_equal(self, other) and self._base_unit == other._base_unit + return self.base_unit == other.base_unit and Vector.__eq__(self, other) - def __ne__(self, other: "QuantityVector3D") -> bool: - """Not equals operator for ``QuantityVector3D``.""" + def __ne__(self, other: "QuantityVector") -> bool: + """Not equals operator for ``QuantityVector``.""" return not self == other - def __mul__(self, other: "QuantityVector3D") -> Real: + def __mul__(self, other: Union["QuantityVector", Real]) -> Union["QuantityVector", Real]: """Overload * operator with dot product.""" - check_type_equivalence(other, self) - check_pint_unit_compatibility(other._base_unit, self._base_unit) - return self.dot(other) + if isinstance(other, QuantityVector): + check_pint_unit_compatibility(other._base_unit, self._base_unit) + return Vector.__mul__(self, other) - def __mod__(self, other: "QuantityVector3D") -> "QuantityVector3D": + @only_for_3d + def __mod__(self, other: "QuantityVector") -> "QuantityVector": """Overload % operator with cross product.""" - check_type_equivalence(other, self) - check_pint_unit_compatibility(other._base_unit, self._base_unit) return self.cross(other) @classmethod - def from_points(cls, point_a: Point3D, point_b: Point3D): - """Create a ``QuantityVector3D`` from two distinct ``Point3D``. - - Parameters - ---------- - point_a : Point3D - A :class:`Point3D` representing the first point. - point_b : Point3D - A :class:`Point3D` representing the second point. - - Returns - ------- - QuantityVector3D - A ``QuantityVector3D`` from ``point_a`` to ``point_b``. - """ - - check_type(point_a, Point3D) - check_type(point_b, Point3D) - - if point_a == point_b: - raise ValueError("The two points cannot have the exact same coordinates.") - - x = point_b[0] - point_a[0] - y = point_b[1] - point_a[1] - z = point_b[2] - point_a[2] - return QuantityVector3D([x, y, z], point_a.base_unit) - - -class QuantityVector2D(Vector2D): - def __new__(cls, vector: Union[np.ndarray, RealSequence, Vector3D], unit: Unit): - """Constructor for ``QuantityVector2D``.""" - - # Transform to base units - check_is_pint_unit(unit, "unit") - vector_base_units = [(elem * unit).to_base_units().magnitude for elem in vector] - - # Build the Vector2D object - obj = Vector2D(vector_base_units) - obj = obj.view(cls) - - # Store the units - obj._unit = unit - _, obj._base_unit = UNITS.get_base_units(unit) - - return obj - - @property - def x(self) -> Real: - """X coordinate of ``QuantityVector2D``.""" - return UNITS.convert(Vector2D.x.fget(self), self._base_unit, self._unit) - - @x.setter - def x(self, x: Real) -> None: - """Set X coordinate of ``QuantityVector2D``.""" - check_is_float_int(x, "x") - Vector2D.x.fset(self, (x * self._unit).to_base_units().magnitude) - - @property - def y(self) -> Real: - """Y coordinate of ``QuantityVector2D``.""" - return UNITS.convert(Vector2D.y.fget(self), self._base_unit, self._unit) - - @y.setter - def y(self, y: Real) -> None: - """Set Y coordinate of ``QuantityVector2D``.""" - check_is_float_int(y, "y") - Vector2D.y.fset(self, (y * self._unit).to_base_units().magnitude) - - @property - def norm(self) -> float: - """Norm of ``QuantityVector2D``.""" - return UNITS.convert(Vector2D.norm.fget(self), self._base_unit, self._unit) - - @property - def unit(self) -> Unit: - """Returns the unit of the ``QuantityVector2D``.""" - return self._unit - - @property - def base_unit(self) -> Unit: - """Returns the base unit of the ``QuantityVector2D``.""" - return self._base_unit - - @unit.setter - def unit(self, unit: Unit) -> None: - """Sets the unit of the ``QuantityVector2D``.""" - check_is_pint_unit(unit, "unit") - check_pint_unit_compatibility(unit, self._base_unit) - self._unit = unit - - def normalize(self) -> "QuantityVector2D": - """Return a normalized version of the ``QuantityVector2D``""" - vec = Vector2D.normalize(self).view(QuantityVector2D) - vec._unit = self._unit - vec._base_unit = self._base_unit - return vec - - def __eq__(self, other: "QuantityVector2D") -> bool: - """Equals operator for ``QuantityVector2D``.""" - check_type_equivalence(other, self) - return np.array_equal(self, other) and self._base_unit == other._base_unit - - def __ne__(self, other: "QuantityVector2D") -> bool: - """Not equals operator for ``QuantityVector2D``.""" - return not self == other - - def __mul__(self, other: "QuantityVector2D") -> Real: - """Overload * operator with dot product.""" - check_type_equivalence(other, self) - check_pint_unit_compatibility(other._base_unit, self._base_unit) - return self.dot(other) - - @classmethod - def from_points(cls, point_a: Point2D, point_b: Point2D): - """Create a ``QuantityVector2D`` from two distinct ``Point2D``. + def from_points(cls, point_a: Point, point_b: Point): + """Create a ``QuantityVector`` from two distinct ``Point``. Parameters ---------- - point_a : Point2D - A :class:`Point2D` representing the first point. - point_b : Point2D - A :class:`Point2D` representing the second point. + point_a : Point + A :class:`Point` representing the first point. + point_b : Point + A :class:`Point` representing the second point. Returns ------- - QuantityVector2D - A ``QuantityVector2D`` from ``point_a`` to ``point_b``. + QuantityVector + A ``QuantityVector`` from ``point_a`` to ``point_b``. """ - - check_type(point_a, Point2D) - check_type(point_b, Point2D) - - if point_a == point_b: - raise ValueError("The two points cannot have the exact same coordinates.") - - return QuantityVector2D( - [point_b[0] - point_a[0], point_b[1] - point_a[1]], point_a.base_unit - ) + check_type(point_a, Point) + check_type(point_b, Point) + return QuantityVector(Vector.from_points(point_a, point_b), point_a.base_unit) diff --git a/src/ansys/geometry/core/misc/__init__.py b/src/ansys/geometry/core/misc/__init__.py index a38442d694..15b26dfca6 100644 --- a/src/ansys/geometry/core/misc/__init__.py +++ b/src/ansys/geometry/core/misc/__init__.py @@ -1,22 +1,31 @@ """PyGeometry misc subpackage.""" +from ansys.geometry.core.misc.accuracy import Accuracy from ansys.geometry.core.misc.checks import ( check_is_float_int, - check_is_pint_unit, + check_ndarray_is_all_nan, check_ndarray_is_float_int, check_ndarray_is_non_zero, check_ndarray_is_not_none, check_pint_unit_compatibility, check_type, check_type_equivalence, + only_for_3d, ) +from ansys.geometry.core.misc.units import UNIT_ANGLE, UNIT_LENGTH, UNITS, PhysicalQuantity __all__ = [ + "Accuracy", "check_is_float_int", - "check_is_pint_unit", + "check_ndarray_is_all_nan", "check_ndarray_is_float_int", "check_ndarray_is_not_none", "check_ndarray_is_non_zero", "check_pint_unit_compatibility", "check_type", "check_type_equivalence", + "only_for_3d", + "PhysicalQuantity", + "UNIT_ANGLE", + "UNIT_LENGTH", + "UNITS", ] diff --git a/src/ansys/geometry/core/accuracy.py b/src/ansys/geometry/core/misc/accuracy.py similarity index 100% rename from src/ansys/geometry/core/accuracy.py rename to src/ansys/geometry/core/misc/accuracy.py diff --git a/src/ansys/geometry/core/misc/checks.py b/src/ansys/geometry/core/misc/checks.py index 9e6c51b96b..cb67abd856 100644 --- a/src/ansys/geometry/core/misc/checks.py +++ b/src/ansys/geometry/core/misc/checks.py @@ -1,6 +1,5 @@ """Checking common functions.""" - -from typing import Optional, Tuple, Union +from typing import Any, Optional, Tuple, Union import numpy as np from pint import Unit @@ -87,11 +86,11 @@ def check_ndarray_is_not_none( ) -def check_ndarray_is_non_zero( +def check_ndarray_is_all_nan( param: np.ndarray, param_name: Optional[Union[str, None]] = None ) -> None: """ - Checks if the :class:`numpy.ndarray` is not zero-valued. + Checks if the :class:`numpy.ndarray` is all nan-valued. Parameters ---------- @@ -103,38 +102,40 @@ def check_ndarray_is_non_zero( Raises ------ ValueError - In case the :class:`numpy.ndarray` is zero-valued. + In case the :class:`numpy.ndarray` is all nan-valued. """ - param_data = np.ravel(param) - if all(value == 0 for value in param_data): + if np.isnan(param).all(): raise ValueError( - f"The numpy.ndarray provided should not be a numpy.ndarray of zeros." + f"The numpy.ndarray provided should not be a nan numpy.ndarray." if param_name is None - else f"The numpy.ndarray '{param_name}' should not be a numpy.ndarray of zeros." + else f"The numpy.ndarray '{param_name}' should not be a nan numpy.ndarray." ) -def check_is_pint_unit(param: object, param_name: Optional[Union[str, None]] = None) -> None: +def check_ndarray_is_non_zero( + param: np.ndarray, param_name: Optional[Union[str, None]] = None +) -> None: """ - Checks if the parameter provided is a :class:`pint.Unit`. + Checks if the :class:`numpy.ndarray` is not zero-valued. Parameters ---------- - param : object - Object instance to be checked. + param : ~numpy.ndarray + :class:`numpy.ndarray` instance to be checked. param_name : str or None, optional - The object instance name (if any). By default, ``None``. + The :class:`numpy.ndarray` instance name (if any). By default, ``None``. Raises ------ - TypeError - In case the parameter is not a :class:`pint.Unit`. + ValueError + In case the :class:`numpy.ndarray` is zero-valued. """ - if not isinstance(param, Unit): - raise TypeError( - "The parameter 'unit' should be a pint.Unit object." + param_data = np.ravel(param) + if all(value == 0 for value in param_data): + raise ValueError( + f"The numpy.ndarray provided should not be a numpy.ndarray of zeros." if param_name is None - else f"The parameter '{param_name}' should be a pint.Unit object." + else f"The numpy.ndarray '{param_name}' should not be a numpy.ndarray of zeros." ) @@ -200,3 +201,21 @@ def check_type(input: object, expected_type: Union[type, Tuple[type, ...]]) -> N if not isinstance(input, expected_type): raise TypeError(f"Provided type {type(input)} is invalid, type {expected_type} expected.") + + +def only_for_3d(func): + """Class decorator for checking if an object is 3D or not. + + Notes + ----- + To use this decorator it is necessary to call it on top of a + class method, and the class should have an ``is_3d`` property. + """ + + def wrapper(*args) -> Any: + if not args[0].is_3d: + raise ValueError("Instance is not 3D. Z component not accessible.") + + return func(*args) + + return wrapper diff --git a/src/ansys/geometry/core/misc/units.py b/src/ansys/geometry/core/misc/units.py new file mode 100644 index 0000000000..cc88a4ba0a --- /dev/null +++ b/src/ansys/geometry/core/misc/units.py @@ -0,0 +1,90 @@ +"""``Units`` module for PyGeometry.""" + +from typing import Optional, Union + +from pint import Quantity, Unit, UnitRegistry + +from ansys.geometry.core.misc.checks import check_pint_unit_compatibility, check_type +from ansys.geometry.core.typing import Real + +UNITS = UnitRegistry() +"""Unit manager.""" + +UNIT_LENGTH = UNITS.meter +"""Default length unit for PyGeometry.""" + +UNIT_ANGLE = UNITS.radian +"""Default angle unit for PyGeometry.""" + + +class PhysicalQuantity: + """Base class to handle units homogeneously throughout PyGeometry. + + Parameters + ---------- + unit : ~pint.Unit + The units to be considered for our class. + expected_dimensions : ~pint.Unit + The units containing the dimensionality of the ``PhysicalQuantity``. + By default, None. + """ + + def __init__(self, unit: Unit, expected_dimensions: Optional[Union[Unit, None]] = None): + """Constructor for ``PhysicalQuantity``.""" + check_type(unit, Unit) + if expected_dimensions: + check_type(expected_dimensions, Unit) + check_pint_unit_compatibility(unit, expected_dimensions) + + self._unit = unit + _, self._base_unit = UNITS.get_base_units(unit) + + @property + def unit(self) -> Unit: + """Returns the unit of the object.""" + return self._unit + + @unit.setter + def unit(self, unit: Unit) -> None: + """Sets the unit of the object.""" + check_type(unit, Unit) + check_pint_unit_compatibility(unit, self._base_unit) + self._unit = unit + + @property + def base_unit(self) -> Unit: + """Returns the base unit of the object.""" + return self._base_unit + + def _get_quantity(self, input: Real) -> Quantity: + """Returns input value as a ~:class:`pint.Quantity`. + + Parameters + ---------- + input : Real + The number to be expressed as a quantity. + + Returns + ------- + ~pint.Quantity + The physical quantity the number represents. + """ + return (input * self.base_unit).to(self.unit) + + def _base_units_magnitude(self, input: Quantity) -> Real: + """Returns input's :class:`pint.Quantity` magnitude + in base units. + + Parameters + ---------- + input : ~pint.Quantity + The :class:`pint.Quantity` to be processed. + + Returns + ------- + Real + The input's magnitude in base units. + """ + check_type(input, Quantity) + check_pint_unit_compatibility(input.units, self._base_unit) + return input.m_as(self._base_unit) diff --git a/src/ansys/geometry/core/primitives/cone.py b/src/ansys/geometry/core/primitives/cone.py index ed0a54ee42..7065d7a8bc 100644 --- a/src/ansys/geometry/core/primitives/cone.py +++ b/src/ansys/geometry/core/primitives/cone.py @@ -6,11 +6,12 @@ import numpy as np from pint import Unit -from ansys.geometry.core import UNIT_ANGLE, UNIT_LENGTH, UNITS -from ansys.geometry.core.math import Point3D, UnitVector3D, Vector3D +from ansys.geometry.core.math import Point, UnitVector, Vector from ansys.geometry.core.misc import ( + UNIT_ANGLE, + UNIT_LENGTH, + UNITS, check_is_float_int, - check_is_pint_unit, check_pint_unit_compatibility, check_type, check_type_equivalence, @@ -24,11 +25,11 @@ class Cone: Parameters ---------- - origin : Union[~numpy.ndarray, RealSequence, Point3D] + origin : Union[~numpy.ndarray, RealSequence, Point] Centered origin of the ``Cone``. - direction_x: Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D] + direction_x: Union[~numpy.ndarray, RealSequence, UnitVector, Vector] X-plane direction. - direction_y: Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D] + direction_y: Union[~numpy.ndarray, RealSequence, UnitVector, Vector] Y-plane direction. radius: Real Radius of ``Cone``. @@ -42,9 +43,9 @@ class Cone: def __init__( self, - origin: Union[np.ndarray, RealSequence, Point3D], - direction_x: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D], - direction_y: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D], + origin: Union[np.ndarray, RealSequence, Point], + direction_x: Union[np.ndarray, RealSequence, UnitVector, Vector], + direction_y: Union[np.ndarray, RealSequence, UnitVector, Vector], radius: Real, half_angle: Real, length_unit: Optional[Unit] = UNIT_LENGTH, @@ -52,17 +53,17 @@ def __init__( ): """Constructor method for ``Cone``.""" - check_type(origin, (np.ndarray, List, Point3D)) - check_type(direction_x, (np.ndarray, List, UnitVector3D, Vector3D)) - check_type(direction_y, (np.ndarray, List, UnitVector3D, Vector3D)) + check_type(origin, (np.ndarray, List, Point)) + check_type(direction_x, (np.ndarray, List, UnitVector, Vector)) + check_type(direction_y, (np.ndarray, List, UnitVector, Vector)) check_is_float_int(radius, "radius") check_is_float_int(half_angle, "half_angle") - check_is_pint_unit(length_unit, "length_unit") + check_type(length_unit, Unit) check_pint_unit_compatibility(length_unit, UNIT_LENGTH) - check_is_pint_unit(angle_unit, "angle_unit") + check_type(angle_unit, Unit) check_pint_unit_compatibility(angle_unit, UNIT_ANGLE) self._length_unit = length_unit @@ -71,12 +72,12 @@ def __init__( self._angle_unit = angle_unit _, self._base_angle_unit = UNITS.get_base_units(angle_unit) - self._origin = Point3D(origin) if not isinstance(origin, Point3D) else origin + self._origin = Point(origin) if not isinstance(origin, Point) else origin self._direction_x = ( - UnitVector3D(direction_x) if not isinstance(direction_x, UnitVector3D) else direction_x + UnitVector(direction_x) if not isinstance(direction_x, UnitVector) else direction_x ) self._direction_y = ( - UnitVector3D(direction_y) if not isinstance(direction_y, UnitVector3D) else direction_y + UnitVector(direction_y) if not isinstance(direction_y, UnitVector) else direction_y ) # Store values in base unit @@ -84,14 +85,14 @@ def __init__( self._half_angle = UNITS.convert(half_angle, self._angle_unit, self._base_angle_unit) @property - def origin(self) -> Point3D: + def origin(self) -> Point: """Origin of the ``Cone``.""" return self._origin @origin.setter - def origin(self, origin: Point3D) -> None: - if not isinstance(origin, Point3D): - raise TypeError(f"origin is invalid, type {Point3D} expected.") + def origin(self, origin: Point) -> None: + if not isinstance(origin, Point): + raise TypeError(f"origin is invalid, type {Point} expected.") self._origin = origin @property @@ -121,7 +122,7 @@ def length_unit(self) -> Unit: @length_unit.setter def length_unit(self, length_unit: Unit) -> None: - check_is_pint_unit(length_unit, "length_unit") + check_type(length_unit, Unit) check_pint_unit_compatibility(length_unit, UNIT_LENGTH) self._length_unit = length_unit @@ -132,7 +133,7 @@ def angle_unit(self) -> Unit: @angle_unit.setter def angle_unit(self, angle_unit: Unit) -> None: - check_is_pint_unit(angle_unit, "angle_unit") + check_type(angle_unit, Unit) check_pint_unit_compatibility(angle_unit, UNIT_ANGLE) self._angle_unit = angle_unit diff --git a/src/ansys/geometry/core/primitives/cylinder.py b/src/ansys/geometry/core/primitives/cylinder.py index 5f940cc8b2..0c133aa235 100644 --- a/src/ansys/geometry/core/primitives/cylinder.py +++ b/src/ansys/geometry/core/primitives/cylinder.py @@ -5,11 +5,11 @@ import numpy as np from pint import Unit -from ansys.geometry.core import UNIT_LENGTH, UNITS -from ansys.geometry.core.math import Point3D, UnitVector3D, Vector3D +from ansys.geometry.core.math import Point, UnitVector, Vector from ansys.geometry.core.misc import ( + UNIT_LENGTH, + UNITS, check_is_float_int, - check_is_pint_unit, check_pint_unit_compatibility, check_type, check_type_equivalence, @@ -23,11 +23,11 @@ class Cylinder: Parameters ---------- - origin : Union[~numpy.ndarray, RealSequence, Point3D] + origin : Union[~numpy.ndarray, RealSequence, Point] Origin of the ``Cylinder``. - direction_x : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D] + direction_x : Union[~numpy.ndarray, RealSequence, UnitVector, Vector] X-plane direction. - direction_y : Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D] + direction_y : Union[~numpy.ndarray, RealSequence, UnitVector, Vector] Y-plane direction. radius : Real Radius of the ``Cylinder``. @@ -39,34 +39,34 @@ class Cylinder: def __init__( self, - origin: Union[np.ndarray, RealSequence, Point3D], - direction_x: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D], - direction_y: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D], + origin: Union[np.ndarray, RealSequence, Point], + direction_x: Union[np.ndarray, RealSequence, UnitVector, Vector], + direction_y: Union[np.ndarray, RealSequence, UnitVector, Vector], radius: Real, height: Real, unit: Optional[Unit] = UNIT_LENGTH, ): """Constructor method for ``Cylinder``.""" - check_type(origin, (np.ndarray, List, Point3D)) - check_type(direction_x, (np.ndarray, List, UnitVector3D, Vector3D)) - check_type(direction_y, (np.ndarray, List, UnitVector3D, Vector3D)) + check_type(origin, (np.ndarray, List, Point)) + check_type(direction_x, (np.ndarray, List, UnitVector, Vector)) + check_type(direction_y, (np.ndarray, List, UnitVector, Vector)) check_is_float_int(radius, "radius") check_is_float_int(height, "height") - check_is_pint_unit(unit, "unit") + check_type(unit, Unit) check_pint_unit_compatibility(unit, UNIT_LENGTH) self._unit = unit _, self._base_unit = UNITS.get_base_units(unit) - self._origin = Point3D(origin) if not isinstance(origin, Point3D) else origin + self._origin = Point(origin) if not isinstance(origin, Point) else origin self._direction_x = ( - UnitVector3D(direction_x) if not isinstance(direction_x, UnitVector3D) else direction_x + UnitVector(direction_x) if not isinstance(direction_x, UnitVector) else direction_x ) self._direction_y = ( - UnitVector3D(direction_y) if not isinstance(direction_y, UnitVector3D) else direction_y + UnitVector(direction_y) if not isinstance(direction_y, UnitVector) else direction_y ) # Store values in base unit @@ -74,14 +74,14 @@ def __init__( self._height = UNITS.convert(height, self._unit, self._base_unit) @property - def origin(self) -> Point3D: + def origin(self) -> Point: """Origin of the ``Cylinder``.""" return self._origin @origin.setter - def origin(self, origin: Point3D) -> None: - if not isinstance(origin, Point3D): - raise TypeError(f"origin is invalid, type {Point3D} expected.") + def origin(self, origin: Point) -> None: + if not isinstance(origin, Point): + raise TypeError(f"origin is invalid, type {Point} expected.") self._origin = origin @property @@ -114,7 +114,7 @@ def unit(self) -> Unit: @unit.setter def unit(self, unit: Unit) -> None: """Sets the unit of the object.""" - check_is_pint_unit(unit, "unit") + check_type(unit, Unit) check_pint_unit_compatibility(unit, UNIT_LENGTH) self._unit = unit diff --git a/src/ansys/geometry/core/primitives/sphere.py b/src/ansys/geometry/core/primitives/sphere.py index fdbc805d37..2372cbfde8 100644 --- a/src/ansys/geometry/core/primitives/sphere.py +++ b/src/ansys/geometry/core/primitives/sphere.py @@ -6,11 +6,11 @@ import numpy as np from pint import Unit -from ansys.geometry.core import UNIT_LENGTH, UNITS -from ansys.geometry.core.math.point import Point3D +from ansys.geometry.core.math.point import Point from ansys.geometry.core.misc import ( + UNIT_LENGTH, + UNITS, check_is_float_int, - check_is_pint_unit, check_pint_unit_compatibility, check_type, check_type_equivalence, @@ -24,7 +24,7 @@ class Sphere: Parameters ---------- - origin : Union[~numpy.ndarray, RealSequence, Point3D] + origin : Union[~numpy.ndarray, RealSequence, Point] Centered origin of the ``Sphere``. radius: Real Radius of ``Sphere``. @@ -34,36 +34,36 @@ class Sphere: def __init__( self, - origin: Union[np.ndarray, RealSequence, Point3D], + origin: Union[np.ndarray, RealSequence, Point], radius: Real, unit: Optional[Unit] = UNIT_LENGTH, ): """Constructor method for ``Sphere``.""" - check_type(origin, (np.ndarray, List, Point3D)) + check_type(origin, (np.ndarray, List, Point)) check_is_float_int(radius, "radius") - check_is_pint_unit(unit, "unit") + check_type(unit, Unit) check_pint_unit_compatibility(unit, UNIT_LENGTH) self._unit = unit _, self._base_unit = UNITS.get_base_units(unit) - self._origin = Point3D(origin) if not isinstance(origin, Point3D) else origin + self._origin = Point(origin) if not isinstance(origin, Point) else origin # Store values in base unit self._radius = UNITS.convert(radius, self._unit, self._base_unit) @property - def origin(self) -> Point3D: + def origin(self) -> Point: """Origin of the ``Sphere``.""" return self._origin @origin.setter - def origin(self, origin: Point3D) -> None: - if not isinstance(origin, Point3D): - raise TypeError(f"origin is invalid, type {Point3D} expected.") + def origin(self, origin: Point) -> None: + if not isinstance(origin, Point): + raise TypeError(f"origin is invalid, type {Point} expected.") self._origin = origin @property @@ -83,7 +83,7 @@ def unit(self) -> Unit: @unit.setter def unit(self, unit: Unit) -> None: - check_is_pint_unit(unit, "unit") + check_type(unit, Unit) check_pint_unit_compatibility(unit, UNIT_LENGTH) self._unit = unit diff --git a/src/ansys/geometry/core/primitives/torus.py b/src/ansys/geometry/core/primitives/torus.py index 7ba6460e1d..b30d1e3377 100644 --- a/src/ansys/geometry/core/primitives/torus.py +++ b/src/ansys/geometry/core/primitives/torus.py @@ -5,11 +5,11 @@ import numpy as np from pint import Unit -from ansys.geometry.core import UNIT_LENGTH, UNITS -from ansys.geometry.core.math import Point3D, UnitVector3D, Vector3D +from ansys.geometry.core.math import Point, UnitVector, Vector from ansys.geometry.core.misc import ( + UNIT_LENGTH, + UNITS, check_is_float_int, - check_is_pint_unit, check_pint_unit_compatibility, check_type, check_type_equivalence, @@ -23,11 +23,11 @@ class Torus: Parameters ---------- - origin : Union[~numpy.ndarray, RealSequence, Point3D], + origin : Union[~numpy.ndarray, RealSequence, Point], Centered origin of the ``Torus``. - direction_x: Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D] + direction_x: Union[~numpy.ndarray, RealSequence, UnitVector, Vector] X-plane direction. - direction_y: Union[~numpy.ndarray, RealSequence, UnitVector3D, Vector3D] + direction_y: Union[~numpy.ndarray, RealSequence, UnitVector, Vector] Y-plane direction. major_radius: Real Major radius of ``Torus``. @@ -39,34 +39,34 @@ class Torus: def __init__( self, - origin: Union[np.ndarray, RealSequence, Point3D], - direction_x: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D], - direction_y: Union[np.ndarray, RealSequence, UnitVector3D, Vector3D], + origin: Union[np.ndarray, RealSequence, Point], + direction_x: Union[np.ndarray, RealSequence, UnitVector, Vector], + direction_y: Union[np.ndarray, RealSequence, UnitVector, Vector], major_radius: Real, minor_radius: Real, unit: Optional[Unit] = UNIT_LENGTH, ): """Constructor method for ``Torus``.""" - check_type(origin, (np.ndarray, List, Point3D)) - check_type(direction_x, (np.ndarray, List, UnitVector3D, Vector3D)) - check_type(direction_y, (np.ndarray, List, UnitVector3D, Vector3D)) + check_type(origin, (np.ndarray, List, Point)) + check_type(direction_x, (np.ndarray, List, UnitVector, Vector)) + check_type(direction_y, (np.ndarray, List, UnitVector, Vector)) check_is_float_int(major_radius, "major_radius") check_is_float_int(minor_radius, "minor_radius") - check_is_pint_unit(unit, "unit") + check_type(unit, Unit) check_pint_unit_compatibility(unit, UNIT_LENGTH) self._unit = unit _, self._base_unit = UNITS.get_base_units(unit) - self._origin = Point3D(origin) if not isinstance(origin, Point3D) else origin + self._origin = Point(origin) if not isinstance(origin, Point) else origin self._direction_x = ( - UnitVector3D(direction_x) if not isinstance(direction_x, UnitVector3D) else direction_x + UnitVector(direction_x) if not isinstance(direction_x, UnitVector) else direction_x ) self._direction_y = ( - UnitVector3D(direction_y) if not isinstance(direction_y, UnitVector3D) else direction_y + UnitVector(direction_y) if not isinstance(direction_y, UnitVector) else direction_y ) # Store values in base unit @@ -74,14 +74,14 @@ def __init__( self._minor_radius = UNITS.convert(minor_radius, self._unit, self._base_unit) @property - def origin(self) -> Point3D: + def origin(self) -> Point: """Origin of the ``Torus``.""" return self._origin @origin.setter - def origin(self, origin: Point3D) -> None: - if not isinstance(origin, Point3D): - raise TypeError(f"origin is invalid, type {Point3D} expected.") + def origin(self, origin: Point) -> None: + if not isinstance(origin, Point): + raise TypeError(f"origin is invalid, type {Point} expected.") self._origin = origin @property @@ -111,7 +111,7 @@ def unit(self) -> Unit: @unit.setter def unit(self, unit: Unit) -> None: - check_is_pint_unit(unit, "unit") + check_type(unit, Unit) check_pint_unit_compatibility(unit, UNIT_LENGTH) self._unit = unit diff --git a/src/ansys/geometry/core/shapes/base.py b/src/ansys/geometry/core/shapes/base.py index 63af8bf044..5ca4ff249e 100644 --- a/src/ansys/geometry/core/shapes/base.py +++ b/src/ansys/geometry/core/shapes/base.py @@ -3,11 +3,11 @@ from typing import List, Optional from ansys.geometry.core.math import UNIT_VECTOR_X, UNIT_VECTOR_Y -from ansys.geometry.core.math.point import Point3D -from ansys.geometry.core.math.vector import UnitVector3D +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import UnitVector from ansys.geometry.core.misc.checks import ( + check_ndarray_is_all_nan, check_ndarray_is_non_zero, - check_ndarray_is_not_none, check_type, ) from ansys.geometry.core.typing import Real @@ -18,14 +18,14 @@ class BaseShape: Parameters ---------- - origin : Point3D - A :class:`Point3D` representing the origin of the shape. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + origin : Point + A :class:`Point` representing the origin of the shape. + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. is_closed: Optional[bool] @@ -35,18 +35,18 @@ class BaseShape: def __init__( self, - origin: Point3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, is_closed: Optional[bool] = False, ): """Initializes the base shape.""" - check_type(origin, Point3D) - check_ndarray_is_not_none(origin, "origin") - check_type(dir_1, UnitVector3D) + check_type(origin, Point) + check_ndarray_is_all_nan(origin, "origin") + check_type(dir_1, UnitVector) check_ndarray_is_non_zero(dir_1, "dir_1") - check_type(dir_2, UnitVector3D) + check_type(dir_2, UnitVector) check_ndarray_is_non_zero(dir_2, "dir_2") # TODO: assign a reference frame to the base shape @@ -62,7 +62,7 @@ def __init__( raise ValueError("Reference vectors must be linearly independent.") self._i, self._j = dir_1, dir_2 - self._k = UnitVector3D(self.i.cross(self._j)) + self._k = UnitVector(self.i.cross(self._j)) self._origin = origin self._is_closed = is_closed @@ -70,26 +70,26 @@ def __init__( self._origin.setflags(write=False) @property - def i(self) -> UnitVector3D: + def i(self) -> UnitVector: """The fundamental vector along the first axis of the reference frame.""" return self._i @property - def j(self) -> UnitVector3D: + def j(self) -> UnitVector: """The fundamental vector along the second axis of the reference frame.""" return self._j @property - def k(self) -> UnitVector3D: + def k(self) -> UnitVector: """The fundamental vector along the third axis of the reference frame.""" return self._k @property - def origin(self) -> Point3D: + def origin(self) -> Point: """The origin of the reference frame.""" return self._origin - def points(self, num_points: Optional[int] = 100) -> List[Point3D]: + def points(self, num_points: Optional[int] = 100) -> List[Point]: """Returns a list containing all the points belonging to the shape. Parameters @@ -99,7 +99,7 @@ def points(self, num_points: Optional[int] = 100) -> List[Point3D]: Returns ------- - List[Point3D] + List[Point] A list of points representing the shape. """ return self.frame.from_local_to_global @ self.local_points diff --git a/src/ansys/geometry/core/shapes/circle.py b/src/ansys/geometry/core/shapes/circle.py index 8e50f93abd..4f4ad8856d 100644 --- a/src/ansys/geometry/core/shapes/circle.py +++ b/src/ansys/geometry/core/shapes/circle.py @@ -4,8 +4,8 @@ import numpy as np from ansys.geometry.core.math import UNIT_VECTOR_X, UNIT_VECTOR_Y -from ansys.geometry.core.math.point import Point3D -from ansys.geometry.core.math.vector import UnitVector3D +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import UnitVector from ansys.geometry.core.shapes.base import BaseShape from ansys.geometry.core.typing import Real @@ -17,14 +17,14 @@ class Circle(BaseShape): ---------- radius : Real The radius of the circle. - origin : Point3D - A :class:`Point3D` representing the origin of the shape. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + origin : Point + A :class:`Point` representing the origin of the shape. + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. """ @@ -32,9 +32,9 @@ class Circle(BaseShape): def __init__( self, radius: Real, - origin: Point3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Initializes the circle shape.""" super().__init__(origin, dir_1=dir_1, dir_2=dir_2, is_closed=True) @@ -86,7 +86,7 @@ def area(self) -> Real: """ return np.pi * self.radius**2 - def local_points(self, num_points: Optional[int] = 100) -> List[Point3D]: + def local_points(self, num_points: Optional[int] = 100) -> List[Point]: """Returns a list containing all the points belonging to the shape. Points are given in the local space. @@ -98,21 +98,19 @@ def local_points(self, num_points: Optional[int] = 100) -> List[Point3D]: Returns ------- - List[Point3D] + List[Point] A list of points representing the shape. """ theta = np.linspace(0, 2 * np.pi, num_points) - return [ - Point3D([self.radius * np.cos(ang), self.radius * np.sin(ang), 0.0]) for ang in theta - ] + return [Point([self.radius * np.cos(ang), self.radius * np.sin(ang), 0.0]) for ang in theta] @classmethod def from_radius( cls, radius: Real, - origin: Optional[Point3D] = Point3D([0, 0, 0]), - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Optional[Point] = Point([0, 0, 0]), + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Create a circle from its origin and radius. @@ -120,15 +118,15 @@ def from_radius( ---------- radius : Real The radius of the circle. - origin : Optional[Point3D] - A :class:`Point3D` representing the origin of the ellipse. + origin : Optional[Point] + A :class:`Point` representing the origin of the ellipse. By default, [0, 0, 0]. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. diff --git a/src/ansys/geometry/core/shapes/ellipse.py b/src/ansys/geometry/core/shapes/ellipse.py index c4fd6e9ed4..2d12b8ec9f 100644 --- a/src/ansys/geometry/core/shapes/ellipse.py +++ b/src/ansys/geometry/core/shapes/ellipse.py @@ -5,8 +5,8 @@ from scipy.integrate import quad from ansys.geometry.core.math import UNIT_VECTOR_X, UNIT_VECTOR_Y -from ansys.geometry.core.math.point import Point3D -from ansys.geometry.core.math.vector import UnitVector3D +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import UnitVector from ansys.geometry.core.shapes.base import BaseShape from ansys.geometry.core.typing import Real @@ -20,14 +20,14 @@ class Ellipse(BaseShape): The semi-major axis of the ellipse. b : Real The semi-minor axis of the ellipse. - origin : Point3D - A :class:`Point3D` representing the origin of the shape. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + origin : Point + A :class:`Point` representing the origin of the shape. + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. """ @@ -36,9 +36,9 @@ def __init__( self, a: Real, b: Real, - origin: Point3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Initializes the ellipse shape.""" super().__init__(origin, dir_1=dir_1, dir_2=dir_2, is_closed=True) @@ -136,7 +136,7 @@ def area(self) -> Real: """ return np.pi * self.semi_major_axis * self.semi_minor_axis - def local_points(self, num_points: Optional[int] = 100) -> List[Point3D]: + def local_points(self, num_points: Optional[int] = 100) -> List[Point]: """Returns a list containing all the points belonging to the shape. Parameters @@ -146,12 +146,12 @@ def local_points(self, num_points: Optional[int] = 100) -> List[Point3D]: Returns ------- - List[Point3D] + List[Point] A list of points representing the shape. """ theta = np.linspace(0, 2 * np.pi, num_points) return [ - Point3D([self.semi_major_axis * np.cos(ang), self.semi_minor_axis * np.sin(ang), 0.0]) + Point([self.semi_major_axis * np.cos(ang), self.semi_minor_axis * np.sin(ang), 0.0]) for ang in theta ] @@ -160,9 +160,9 @@ def from_axes( cls, a: Real, b: Real, - origin: Optional[Point3D] = Point3D([0, 0, 0]), - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Optional[Point] = Point([0, 0, 0]), + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Create an ellipse from its semi-major and semi-minor axes. @@ -172,15 +172,15 @@ def from_axes( The semi-major axis of the ellipse. b : Real The semi-minor axis of the ellipse. - origin : Optional[Point3D] - A :class:`Point3D` representing the origin of the ellipse. + origin : Optional[Point] + A :class:`Point` representing the origin of the ellipse. By default, [0, 0, 0]. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. diff --git a/src/ansys/geometry/core/shapes/line.py b/src/ansys/geometry/core/shapes/line.py index 3ddfe6e122..5776e24095 100644 --- a/src/ansys/geometry/core/shapes/line.py +++ b/src/ansys/geometry/core/shapes/line.py @@ -4,21 +4,18 @@ import numpy as np from pint import Unit -from ansys.geometry.core import UNIT_LENGTH, UNITS from ansys.geometry.core.math import UNIT_VECTOR_X, UNIT_VECTOR_Y -from ansys.geometry.core.math.point import Point3D -from ansys.geometry.core.math.vector import QuantityVector3D, UnitVector3D, Vector3D +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import QuantityVector, UnitVector, Vector +from ansys.geometry.core.misc import UNIT_LENGTH, UNITS from ansys.geometry.core.misc.checks import ( - check_is_pint_unit, + check_ndarray_is_all_nan, check_ndarray_is_non_zero, - check_ndarray_is_not_none, check_pint_unit_compatibility, check_type, ) from ansys.geometry.core.shapes.base import BaseShape -# TODO: Line at the moment is not a BaseShape... - class Line(BaseShape): """ @@ -26,16 +23,16 @@ class Line(BaseShape): Parameters ---------- - origin: Point3D + origin: Point Origin of the line. - direction: Union[Vector3D, UnitVector3D] + direction: Union[Vector, UnitVector] Direction of the line. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. @@ -50,21 +47,21 @@ class Line(BaseShape): def __init__( self, - origin: Point3D, - direction: Union[Vector3D, UnitVector3D], - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + direction: Union[Vector, UnitVector], + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Initializes the line shape.""" # Perform some sanity checks - check_type(direction, Vector3D) + check_type(direction, Vector) check_ndarray_is_non_zero(direction, "direction") - # If a Vector3D was provided, we should store a UnitVector3D + # If a Vector was provided, we should store a UnitVector try: - check_type(direction, UnitVector3D) + check_type(direction, UnitVector) except TypeError: - direction = UnitVector3D(direction) + direction = UnitVector(direction) # Store instance attributes self._direction = direction @@ -76,24 +73,24 @@ def __init__( super().__init__(origin, dir_1=direction, dir_2=dir_2, is_closed=False) @property - def direction(self) -> UnitVector3D: + def direction(self) -> UnitVector: """Returns the direction of the line.""" return self._direction def _is_linearly_dependent( - self, direction: UnitVector3D, dir_1: UnitVector3D, dir_2: UnitVector3D + self, direction: UnitVector, dir_1: UnitVector, dir_2: UnitVector ) -> bool: """Private method for checking if the provided directions are linearly dependent. Parameters ---------- - direction : UnitVector3D - The line's direction vector as a :class:`UnitVector3D`. - dir_1 : UnitVector3D - A :class:`UnitVector3D` representing the first fundamental direction + direction : UnitVector + The line's direction vector as a :class:`UnitVector`. + dir_1 : UnitVector + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. - dir_2 : UnitVector3D - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : UnitVector + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. Returns @@ -106,7 +103,7 @@ def _is_linearly_dependent( (lambdas, _) = np.linalg.eig(mat.T) return True if any(np.isclose(lambdas, 0.0)) else False - def local_points(self, num_points: Optional[int] = 100) -> List[Point3D]: + def local_points(self, num_points: Optional[int] = 100) -> List[Point]: """ Returns a list containing all the points belonging to the shape. @@ -117,12 +114,12 @@ def local_points(self, num_points: Optional[int] = 100) -> List[Point3D]: Returns ------- - List[Point3D] + List[Point] A list of points representing the shape. """ quantified_dir = UNITS.convert(self.direction, self.origin.unit, self.origin.base_unit) line_start = self.origin - quantified_dir * int(num_points / 2) - return [line_start + delta * quantified_dir for delta in range(0, num_points)] + return [Point(line_start + delta * quantified_dir) for delta in range(0, num_points)] class Segment(Line): @@ -131,33 +128,33 @@ class Segment(Line): Parameters ---------- - start: Point3D + start: Point Start of the line segment. - end: Point3D + end: Point End of the line segment. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. """ def __init__( self, - start: Point3D, - end: Point3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + start: Point, + end: Point, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Constructor method for ``Segment``.""" - # Perform sanity checks on Point3D values given - check_type(start, Point3D) - check_ndarray_is_not_none(start, "start") - check_type(end, Point3D) - check_ndarray_is_not_none(end, "end") + # Perform sanity checks on Point values given + check_type(start, Point) + check_ndarray_is_all_nan(start, "start") + check_type(end, Point) + check_ndarray_is_all_nan(end, "end") # Assign values to start and end self._origin = start @@ -168,7 +165,7 @@ def __init__( self._rebase_point_units() # Build the direction vector - direction = UnitVector3D(end - start) + direction = UnitVector(end - start) # Call the super ctor (i.e. Line). super().__init__(start, direction, dir_1=dir_1, dir_2=dir_2) @@ -176,28 +173,28 @@ def __init__( @classmethod def from_origin_and_vector( cls, - origin: Point3D, - vector: Vector3D, + origin: Point, + vector: Vector, vector_units: Optional[Unit] = UNIT_LENGTH, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Create a ``Segment`` from an origin and a vector. Parameters ---------- - origin : Point3D + origin : Point Start of the line segment. - vector : Vector3D + vector : Vector Vector defining the line segment. vector_units : Optional[Unit], optional The length units of the vector, by default ``UNIT_LENGTH``. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. @@ -206,9 +203,9 @@ def from_origin_and_vector( Segment The ``Segment`` object resulting from the inputs. """ - check_is_pint_unit(vector_units, "vector_units") + check_type(vector_units, Unit) check_pint_unit_compatibility(vector_units, UNIT_LENGTH) - end_vec_as_point = Point3D(vector, vector_units) + end_vec_as_point = Point(vector, vector_units) end_vec_as_point += origin return cls(origin, end_vec_as_point, dir_1=dir_1, dir_2=dir_2) @@ -216,25 +213,25 @@ def from_origin_and_vector( @classmethod def from_origin_and_quantity_vector( cls, - origin: Point3D, - quantity_vector: QuantityVector3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + quantity_vector: QuantityVector, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Create a ``Segment`` from an origin and a vector. Parameters ---------- - origin : Point3D + origin : Point Start of the line segment. - quantity_vector : QuantityVector3D + quantity_vector : QuantityVector QuantityVector defining the line segment (with units). - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. @@ -243,7 +240,7 @@ def from_origin_and_quantity_vector( Segment The ``Segment`` object resulting from the inputs. """ - check_type(quantity_vector, QuantityVector3D) + check_type(quantity_vector, QuantityVector) return Segment.from_origin_and_vector( origin=origin, vector=quantity_vector, @@ -270,16 +267,16 @@ def _rebase_point_units(self): self._origin.unit = self._end.unit = UNIT_LENGTH @property - def start(self) -> Point3D: + def start(self) -> Point: """Returns the start of the ``Segment``.""" return self.origin @property - def end(self) -> Point3D: + def end(self) -> Point: """Returns the end of the ``Segment``.""" return self._end - def local_points(self, num_points: Optional[int] = 100) -> List[Point3D]: + def local_points(self, num_points: Optional[int] = 100) -> List[Point]: """ Returns a list containing all the points belonging to the shape. @@ -290,8 +287,8 @@ def local_points(self, num_points: Optional[int] = 100) -> List[Point3D]: Returns ------- - List[Point3D] + List[Point] A list of points representing the shape. """ delta_segm = (self.end - self.start) / (num_points - 1) - return [self.start + delta * delta_segm for delta in range(0, num_points)] + return [Point(self.start + delta * delta_segm) for delta in range(0, num_points)] diff --git a/src/ansys/geometry/core/shapes/polygon.py b/src/ansys/geometry/core/shapes/polygon.py index 4f579d1dfa..e78c304d08 100644 --- a/src/ansys/geometry/core/shapes/polygon.py +++ b/src/ansys/geometry/core/shapes/polygon.py @@ -5,8 +5,8 @@ import numpy as np from ansys.geometry.core.math import UNIT_VECTOR_X, UNIT_VECTOR_Y -from ansys.geometry.core.math.point import Point3D -from ansys.geometry.core.math.vector import UnitVector3D +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import UnitVector from ansys.geometry.core.shapes.base import BaseShape from ansys.geometry.core.typing import Real @@ -20,15 +20,15 @@ class Polygon(BaseShape): The inradius(apothem) of the polygon. sides : int Number of sides of the polygon. - origin : Point3D - A :class:``Point3D`` representing the origin of the shape. + origin : Point + A :class:``Point`` representing the origin of the shape. By default, [0, 0, 0]. - dir_1 : UnitVector3D - A :class:``UnitVector3D`` representing the first fundamental direction + dir_1 : UnitVector + A :class:``UnitVector`` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : UnitVector3D - A :class:``UnitVector3D`` representing the second fundamental direction + dir_2 : UnitVector + A :class:``UnitVector`` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. """ @@ -37,9 +37,9 @@ def __init__( self, inner_radius: Real, sides: int, - origin: Point3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Initializes the polygon shape.""" super().__init__(origin, dir_1=dir_1, dir_2=dir_2, is_closed=True) @@ -126,19 +126,19 @@ def area(self) -> Real: """ return (self.inner_radius * self.perimeter) / 2 - def local_points(self) -> List[Point3D]: + def local_points(self) -> List[Point]: """Returns a list containing all the vertices of the polygon. Vertices are given in the local space. Returns ------- - list[Point3D] + list[Point] A list of vertices representing the shape. """ theta = np.linspace(0, 2 * np.pi, self.n_sides + 1) return [ - Point3D([self.outer_radius * np.cos(ang), self.outer_radius * np.sin(ang), 0.0]) + Point([self.outer_radius * np.cos(ang), self.outer_radius * np.sin(ang), 0.0]) for ang in theta ] diff --git a/src/ansys/geometry/core/sketch.py b/src/ansys/geometry/core/sketch.py index 61ecb1445e..222d80fd31 100644 --- a/src/ansys/geometry/core/sketch.py +++ b/src/ansys/geometry/core/sketch.py @@ -3,8 +3,8 @@ from typing import Optional, Union from ansys.geometry.core.math import UNIT_VECTOR_X, UNIT_VECTOR_Y, ZERO_VECTOR3D -from ansys.geometry.core.math.point import Point3D -from ansys.geometry.core.math.vector import UnitVector3D, Vector3D +from ansys.geometry.core.math.point import Point +from ansys.geometry.core.math.vector import UnitVector, Vector from ansys.geometry.core.shapes.base import BaseShape from ansys.geometry.core.shapes.circle import Circle from ansys.geometry.core.shapes.ellipse import Ellipse @@ -20,9 +20,9 @@ class Sketch: def __init__( self, - origin: Optional[Point3D] = Point3D([0, 0, 0]), - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Optional[Point] = Point([0, 0, 0]), + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Constructor method for ``Sketch``.""" # TODO: assign a reference frame to the base shape @@ -61,9 +61,9 @@ def append_shape(self, shape: BaseShape): def draw_circle( self, radius: Real, - origin: Point3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Create a circle shape on the sketch. @@ -71,14 +71,14 @@ def draw_circle( ---------- radius : Real The radius of the circle. - origin : Point3D - A :class:`Point3D` representing the origin of the shape. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + origin : Point + A :class:`Point` representing the origin of the shape. + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. Returns @@ -95,9 +95,9 @@ def draw_ellipse( self, a: Real, b: Real, - origin: Point3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ): """Create an ellipse shape on the sketch. @@ -107,14 +107,14 @@ def draw_ellipse( The semi-major axis of the ellipse. b : Real The semi-minor axis of the ellipse. - origin : Point3D - A :class:`Point3D` representing the origin of the shape. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + origin : Point + A :class:`Point` representing the origin of the shape. + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. @@ -130,26 +130,26 @@ def draw_ellipse( def draw_segment( self, - start: Point3D, - end: Point3D, - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + start: Point, + end: Point, + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ) -> Segment: """ Add a segment sketch object to the sketch plane. Parameters ---------- - start : Point3D + start : Point Start of the line segment. - end : Point3D + end : Point End of the line segment. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. @@ -165,26 +165,26 @@ def draw_segment( def draw_line( self, - origin: Point3D, - direction: Union[Vector3D, UnitVector3D], - dir_1: Optional[UnitVector3D] = UNIT_VECTOR_X, - dir_2: Optional[UnitVector3D] = UNIT_VECTOR_Y, + origin: Point, + direction: Union[Vector, UnitVector], + dir_1: Optional[UnitVector] = UNIT_VECTOR_X, + dir_2: Optional[UnitVector] = UNIT_VECTOR_Y, ) -> Line: """ Add a line sketch object to the sketch plane. Parameters ---------- - origin : Point3D + origin : Point Origin of the line. - direction: Union[Vector3D, UnitVector3D] + direction: Union[Vector, UnitVector] Direction of the line. - dir_1 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the first fundamental direction + dir_1 : Optional[UnitVector] + A :class:`UnitVector` representing the first fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_X``. - dir_2 : Optional[UnitVector3D] - A :class:`UnitVector3D` representing the second fundamental direction + dir_2 : Optional[UnitVector] + A :class:`UnitVector` representing the second fundamental direction of the reference plane where the shape is contained. By default, ``UNIT_VECTOR_Y``. @@ -202,9 +202,9 @@ def draw_polygon( self, radius: Real, sides: int, - origin: Point3D, - dir_1: UnitVector3D = UnitVector3D([1, 0, 0]), - dir_2: UnitVector3D = UnitVector3D([0, 1, 0]), + origin: Point, + dir_1: UnitVector = UnitVector([1, 0, 0]), + dir_2: UnitVector = UnitVector([0, 1, 0]), ): """Create a polygon shape on the sketch. @@ -214,13 +214,13 @@ def draw_polygon( The inradius(apothem) of the polygon. sides : int Number of sides of the polygon. - origin : Point3D - A :class:``Point3D`` representing the origin of the polygon. - dir_1 : UnitVector3D - A :class:``UnitVector3D`` representing the first fundamental direction + origin : Point + A :class:``Point`` representing the origin of the polygon. + dir_1 : UnitVector + A :class:``UnitVector`` representing the first fundamental direction of the reference plane where the shape is contained. - dir_2 : UnitVector3D - A :class:``UnitVector3D`` representing the second fundamental direction + dir_2 : UnitVector + A :class:``UnitVector`` representing the second fundamental direction of the reference plane where the shape is contained. Returns diff --git a/tests/test_math.py b/tests/test_math.py index a37121d899..fd3d6b5ae0 100644 --- a/tests/test_math.py +++ b/tests/test_math.py @@ -3,7 +3,6 @@ import numpy as np import pytest -from ansys.geometry.core import UNITS from ansys.geometry.core.math import ( UNIT_VECTOR_X, UNIT_VECTOR_Y, @@ -14,162 +13,173 @@ Matrix33, Matrix44, Plane, - Point2D, - Point3D, - QuantityVector2D, - QuantityVector3D, - UnitVector2D, - UnitVector3D, - Vector2D, - Vector3D, + Point, + QuantityVector, + UnitVector, + Vector, ) +from ansys.geometry.core.misc import UNITS DOUBLE_EPS = np.finfo(float).eps -def test_point3d(): - """Simple test to create a ``Point3D``.""" - - # Test the null Point3D - p_null = Point3D() - assert len(p_null) == 3 - assert all(x == None for x in p_null) - p_null.unit = UNITS.cm - p_null.x = 10 - p_null.y = 20 - p_null.z = 30 - assert p_null.x == 10 - assert p_null.y == 20 - assert p_null.z == 30 - assert p_null[0] == 0.1 - assert p_null[1] == 0.2 - assert p_null[2] == 0.3 - - # Create two Point3D objects - p_1 = Point3D([0, 1, 3]) - p_1_copy = Point3D([0, 1, 3]) - p_2 = Point3D([0, 4, 7]) +def test_point(): + """Simple test to create a ``Point``.""" + + # Test the default Point + p_default = Point() + assert len(p_default) == 3 + assert np.isnan(p_default).all() + assert p_default.is_3d + assert not p_default.is_2d + p_default.unit = UNITS.cm + p_default.x = new_x = 10 * UNITS.cm + p_default.y = new_y = 20 * UNITS.cm + p_default.z = new_z = 30 * UNITS.cm + assert p_default.x == new_x + assert p_default.y == new_y + assert p_default.z == new_z + assert p_default[0] == new_x.to_base_units().magnitude + assert p_default[1] == new_y.to_base_units().magnitude + assert p_default[2] == new_z.to_base_units().magnitude + + # Create two Point objects + p_1 = Point([0, 1, 3], UNITS.cm) + p_1_copy = Point([0, 1, 3], UNITS.cm) + p_2 = Point([0, 4, 7], UNITS.cm) # Check that the equals operator works assert p_1 == p_1_copy assert p_1 != p_2 # Check its X, Y, Z components - assert p_1.x == 0 - assert p_1.y == 1 - assert p_1.z == 3 + assert p_1.x == 0 * UNITS.cm + assert p_1.y == 1 * UNITS.cm + assert p_1.z == 3 * UNITS.cm # Check that the setter works properly in p_1_copy - p_1_copy.x = 3 - p_1_copy.y = 3 - p_1_copy.z = 3 + p_1_copy.x = p_1_copy.y = p_1_copy.z = 3 * UNITS.cm # Check that the equals operator works (p_1 and p_1_copy should no longer be equal) assert p_1 != p_1_copy assert p_1 != p_2 + # Create a 2D Point + point_2d = Point([1, 2], UNITS.cm) + assert point_2d.x == 1 * UNITS.cm + assert point_2d.y == 2 * UNITS.cm + assert point_2d.unit == UNITS.cm + _, base_unit = UNITS.get_base_units(UNITS.cm) + assert point_2d.base_unit == base_unit + assert point_2d.is_2d + assert not point_2d.is_3d -def test_point2d(): - """Simple test to create a ``Point2D``.""" - # Test the null Point2D - p_null = Point2D() - assert len(p_null) == 2 - assert all(x == None for x in p_null) - p_null.unit = UNITS.cm - p_null.x = 10 - p_null.y = 20 - assert p_null.x == 10 - assert p_null.y == 20 - assert p_null[0] == 0.1 - assert p_null[1] == 0.2 +def test_point_errors(): + """Testing multiple ``Point`` errors.""" - # Create two Point2D objects - p_1 = Point2D([0, 1]) - p_1_copy = Point2D([0, 1]) - p_2 = Point2D([0, 4]) - - # Check that the equals operator works - assert p_1 == p_1_copy - assert p_1 != p_2 - - # Check its X, Y, Z components - assert p_1.x == 0 - assert p_1.y == 1 - - # Check that the setter works properly in p_1_copy - p_1_copy.x = 3 - p_1_copy.y = 3 - - # Check that the equals operator works (p_1 and p_1_copy should no longer be equal) - assert p_1 != p_1_copy - assert p_1 != p_2 - - -def test_point3d_errors(): - """Testing multiple ``Point3D`` errors.""" + with pytest.raises( + ValueError, + match="Point class can only receive 2 or 3 arguments, creating a 2D or 3D point, respectively.", # noqa: E501 + ): + Point([1, 4, 3, 5]) - with pytest.raises(ValueError, match="Point3D must have three coordinates."): - Point3D([1, 4]) + with pytest.raises( + ValueError, + match="Point class can only receive 2 or 3 arguments, creating a 2D or 3D point, respectively.", # noqa: E501 + ): + Point([1]) with pytest.raises( TypeError, match="The numpy.ndarray 'input' should contain float or integer values." ): - Point3D(["a", "b", "c"]) + Point(["a", "b", "c"]) # Create a point - point = Point3D([1, 4, 4]) + point = Point([1, 4, 4]) # Test setter error checks - with pytest.raises(TypeError, match="The parameter 'x' should be a float or an integer value."): + with pytest.raises(TypeError, match="Provided type"): point.x = "a" - with pytest.raises(TypeError, match="The parameter 'y' should be a float or an integer value."): + with pytest.raises(TypeError, match="Provided type"): point.y = "a" - with pytest.raises(TypeError, match="The parameter 'z' should be a float or an integer value."): + with pytest.raises(TypeError, match="Provided type"): point.z = "a" - # Build a Point2D and try to compare against it - with pytest.raises(TypeError, match=f"Provided type {Point2D} is invalid"): - point_2d = Point2D([1, 4]) - assert point == point_2d + with pytest.raises( + TypeError, match=r"The pint.Unit provided as input should be a \[length\] quantity." + ): + point.z = 10 * UNITS.degrees + # Build a 2D Point and try to compare against it + point_2d = Point([1, 4]) + assert not point == point_2d -def test_point2d_errors(): - """Testing multiple ``Point2D`` errors.""" + with pytest.raises(ValueError, match="Instance is not 3D. Z component not accessible."): + point_2d.z = 10 * UNITS.cm - with pytest.raises(ValueError, match="Point2D must have two coordinates."): - Point2D([1, 4, 4]) - with pytest.raises( - TypeError, match="The numpy.ndarray 'input' should contain float or integer values." - ): - Point2D(["a", "b"]) +def test_point_units(): + """``Point`` units testing.""" - # Create a point - point = Point2D([1, 4]) + # Create a Point with some units + p_cm_to_mm = Point([10, 20, 30], UNITS.cm) - # Test setter error checks - with pytest.raises(TypeError, match="The parameter 'x' should be a float or an integer value."): - point.x = "a" + # Check that the units are correctly in place + assert p_cm_to_mm.unit == UNITS.cm - with pytest.raises(TypeError, match="The parameter 'y' should be a float or an integer value."): - point.y = "a" + # Request for X, Y, Z and ensure they are in cm + assert p_cm_to_mm.x == 10 * UNITS.cm + assert p_cm_to_mm.y == 20 * UNITS.cm + assert p_cm_to_mm.z == 30 * UNITS.cm + + # Check that the actual values are in base units (i.e. UNIT_LENGTH) + assert p_cm_to_mm[0] == p_cm_to_mm.x.to_base_units().magnitude + assert p_cm_to_mm[1] == p_cm_to_mm.y.to_base_units().magnitude + assert p_cm_to_mm[2] == p_cm_to_mm.z.to_base_units().magnitude + + # Store the numpy array values + (raw_x, raw_y, raw_z) = p_cm_to_mm[0:3] - # Build a Point3D and try to compare against it - with pytest.raises(TypeError, match=f"Provided type {Point3D} is invalid"): - point_3d = Point3D([1, 4, 4]) - assert point == point_3d + # Set unit to mm now... and check if the values changed + p_cm_to_mm.unit = UNITS.mm + assert p_cm_to_mm.x == 100 * UNITS.mm + assert p_cm_to_mm.y == 200 * UNITS.mm + assert p_cm_to_mm.z == 300 * UNITS.mm + # Check that the values are still the same in the array + assert raw_x == p_cm_to_mm[0] + assert raw_y == p_cm_to_mm[1] + assert raw_z == p_cm_to_mm[2] -def test_vector3d(): - """Simple test to create a ``Vector3D``.""" + # Now change the value of a X being in millimeters + p_cm_to_mm.x = 20 * p_cm_to_mm.unit # Basically 1/5 of original x + assert not raw_x == p_cm_to_mm[0] + assert raw_x == p_cm_to_mm[0] * 5 - # Create two Vector3D objects - v1 = Vector3D([0, 1, 3]) - v1_copy = Vector3D([0, 1, 3]) - v2 = Vector3D([0, 4, 7]) + # Now change the value of a Y being in millimeters + p_cm_to_mm.y = 10 * p_cm_to_mm.unit # Basically 1/20 of original y + assert not raw_y == p_cm_to_mm[1] + assert raw_y == p_cm_to_mm[1] * 20 + + # Now change the value of a Z being in millimeters + p_cm_to_mm.z = 30 * p_cm_to_mm.unit # Basically 1/10 of original z + assert not raw_z == p_cm_to_mm[2] + assert raw_z == p_cm_to_mm[2] * 10 + + +def test_vector(): + """Simple test to create a ``Vector``.""" + + # Define the tolerance for the QuantityVector tests + TOLERANCE = 5e-15 + + # Create two Vector objects + v1 = Vector([0, 1, 3]) + v1_copy = Vector([0, 1, 3]) + v2 = Vector([0, 4, 7]) # Check that the equals operator works assert v1 == v1_copy @@ -204,14 +214,16 @@ def test_vector3d(): assert v_cross.y == 0 assert v_cross.z == 0 - assert Vector3D([1, 0, 0]).is_perpendicular_to(Vector3D([0, 1, 0])) - assert Vector3D([1, 0, 0]).is_perpendicular_to(Vector3D([0, 0, 1])) - assert not Vector3D([1, 0, 0]).is_perpendicular_to(Vector3D([1, 1, 1])) - assert not Vector3D([1, 0, 0]).is_perpendicular_to(Vector3D([-1, 0, 0])) - assert Vector3D([1, 1, 1]).is_perpendicular_to(Vector3D([0, -1, 1])) + assert Vector([1, 0, 0]).is_perpendicular_to(Vector([0, 1, 0])) + assert Vector([1, 0, 0]).is_perpendicular_to(Vector([0, 0, 1])) + assert not Vector([1, 0, 0]).is_perpendicular_to(Vector([1, 1, 1])) + assert not Vector([1, 0, 0]).is_perpendicular_to(Vector([-1, 0, 0])) + assert Vector([1, 1, 1]).is_perpendicular_to(Vector([0, -1, 1])) + assert not Vector([0, 0, 0]).is_perpendicular_to(Vector([0, -1, 1])) + assert not Vector([0, -1, 1]).is_perpendicular_to(Vector([0, 0, 0])) - assert Vector3D([0, 0, 0]).is_zero - assert not Vector3D([0, 1, 0]).is_zero + assert Vector([0, 0, 0]).is_zero + assert not Vector([0, 1, 0]).is_zero # Check that the dot and cross product overload is fine assert abs(round(v1 * v2 - 25)) <= DOUBLE_EPS @@ -219,7 +231,7 @@ def test_vector3d(): assert v_cross_overload == v_cross # Checking that scalar times vector also works - v1_x_3 = Vector3D([0, 3, 9]) + v1_x_3 = Vector([0, 3, 9]) assert all( [ abs(round(v1_comp * 3 - v1_x_3_comp)) <= DOUBLE_EPS @@ -227,187 +239,210 @@ def test_vector3d(): ] ) - # Create a vector3D from 2 points - point_a = Point3D([1, 2, 3]) - point_b = Point3D([1, 6, 3]) - vector_from_points = Vector3D.from_points(point_a, point_b) - vector_from_points.x == 0 - vector_from_points.y == 4 - vector_from_points.z == 0 - - # Create a vector3D from 2 points - point_a = Point3D([1, 2, 3], UNITS.mm) - point_b = Point3D([1, 6, 3], UNITS.cm) - vector_from_points = Vector3D.from_points(point_a, point_b) - vector_from_points.x == 9 - vector_from_points.y == 58 - vector_from_points.z == 27 - - -def test_vector2d(): - """Simple test to create a ``Vector2D``.""" - - # Create two Vector2D objects - v_1 = Vector2D([2, 1]) - v_1_copy = Vector2D([2, 1]) - v_2 = Vector2D([0, 4]) - - # Check that the equals operator works - assert v_1 == v_1_copy - assert v_1 != v_2 - - # Check its X, Y components - assert v_1.x == 2 - assert v_1.y == 1 - - # Check that the setter works properly in v_1_copy - v_1_copy.x = 3 - v_1_copy.y = 3 - - # Check that the equals operator works (v_1 and v_1_copy should no longer be equal) - assert v_1 != v_1_copy - assert v_1 != v_2 - - # Check the norm value of vector v1 - assert abs(round(v_1.norm, 3) - 2.236) <= DOUBLE_EPS - - v1_n = v_1.normalize() - assert abs(round(v1_n.x, 3) - 0.894) <= DOUBLE_EPS - assert abs(round(v1_n.y, 3) - 0.447) <= DOUBLE_EPS - - # Check that the dot product overload is fine - v_3 = Vector2D([2, 8]) - v_4 = Vector2D([3, 7]) - assert abs(round(v_3 * v_4 - 62)) <= DOUBLE_EPS - - # Checking that scalar times vector also works - v1_x_3 = Vector2D([6, 3]) - assert all( - [ - abs(round(v1_comp * 3 - v1_x_3_comp)) <= DOUBLE_EPS - for v1_comp, v1_x_3_comp in zip(v_1, v1_x_3) - ] - ) - - # Create a vector2D from 2 points - point_a = Point2D([1, 2]) - point_b = Point2D([1, 6]) - vector_from_points = Vector2D.from_points(point_a, point_b) - vector_from_points.x == 0 - vector_from_points.y == 4 - - # Create a vector2D from 2 points - point_a = Point2D([1, 2], UNITS.mm) - point_b = Point2D([1, 6], UNITS.cm) - vector_from_points = Vector2D.from_points(point_a, point_b) - vector_from_points.x == 9 - vector_from_points.y == 58 - - -def test_unit_vector_3d(): - """Simple test to create a ``UnitVector3D``.""" - - # Create UnitVector3D objects from Vector3D - v1 = Vector3D([0, 1, 3]) - v2 = UnitVector3D(v1) + # Create a 3D vector from 2 points + point_a = Point([1, 2, 3]) + point_b = Point([1, 6, 3]) + vector_from_points = Vector.from_points(point_a, point_b) + assert vector_from_points.x == 0 + assert vector_from_points.y == 4 + assert vector_from_points.z == 0 + + # Create a 3D vector from 2 points + point_a = Point([1, 2, 3], UNITS.mm) + point_b = Point([1, 6, 3], UNITS.cm) + vector_from_points = Vector.from_points(point_a, point_b) + assert abs(vector_from_points.x - 0.009) <= DOUBLE_EPS + assert abs(vector_from_points.y - 0.058) <= DOUBLE_EPS + assert abs(vector_from_points.z - 0.027) <= DOUBLE_EPS + + # Create a 2D vector from 2 points + point_a = Point([1, 2]) + point_b = Point([1, 6]) + vector_from_points = Vector.from_points(point_a, point_b) + assert vector_from_points.x == 0 + assert vector_from_points.y == 4 + + # Create a 2D vector from 2 points + point_a = Point([1, 2], UNITS.mm) + point_b = Point([1, 6], UNITS.cm) + + vector_from_points = Vector.from_points(point_a, point_b) + assert abs(vector_from_points.x - 0.009) <= DOUBLE_EPS + assert abs(vector_from_points.y - 0.058) <= DOUBLE_EPS + + +def test_unit_vector(): + """Simple test to create a ``UnitVector``.""" + + # Create UnitVector objects from Vector + v1 = Vector([0, 1, 3]) + v2 = UnitVector(v1) assert abs(round(v2.x, 3) - 0.0) <= DOUBLE_EPS assert abs(round(v2.y, 3) - 0.316) <= DOUBLE_EPS assert abs(round(v2.z, 3) - 0.949) <= DOUBLE_EPS - # Create UnitVector3D objects from numpy.ndarray - v3 = UnitVector3D([1, 2, 3]) + # Create UnitVector objects from numpy.ndarray + v3 = UnitVector([1, 2, 3]) assert abs(round(v3.x, 3) - 0.267) <= DOUBLE_EPS assert abs(round(v3.y, 3) - 0.535) <= DOUBLE_EPS assert abs(round(v3.z, 3) - 0.802) <= DOUBLE_EPS - assert not UnitVector3D([1, 1, 1]).is_perpendicular_to(UnitVector3D([1, 1, -1])) - assert UnitVector3D([1, 1, 1]).is_perpendicular_to(UnitVector3D([0, -1, 1])) + assert not UnitVector([1, 1, 1]).is_perpendicular_to(UnitVector([1, 1, -1])) + assert UnitVector([1, 1, 1]).is_perpendicular_to(UnitVector([0, -1, 1])) # Check that UnitVector2D is immutable - with pytest.raises(UnsupportedOperation, match="UnitVector3D is immutable."): + with pytest.raises(UnsupportedOperation, match="UnitVector is immutable."): v2.x = 3 - with pytest.raises(UnsupportedOperation, match="UnitVector3D is immutable."): + with pytest.raises(UnsupportedOperation, match="UnitVector is immutable."): v2.y = 3 - with pytest.raises(UnsupportedOperation, match="UnitVector3D is immutable."): + with pytest.raises(UnsupportedOperation, match="UnitVector is immutable."): v2.z = 3 -def test_unit_vector_2d(): - """Simple test to create a ``UnitVector2D``.""" +def test_quantity_vector(): + """Simple tests to create ``QuantityVector``.""" - # Create UnitVector2D objects from Vector2D - v1 = Vector2D([2, 1]) - v2 = UnitVector2D(v1) - assert abs(round(v2.x, 3) - 0.894) <= DOUBLE_EPS - assert abs(round(v2.y, 3) - 0.447) <= DOUBLE_EPS + # Define the tolerance for the QuantityVector tests + TOLERANCE = 5e-15 - # Create UnitVector2D objects from numpy.ndarray - v3 = UnitVector2D([2, 1]) - assert abs(round(v3.x, 3) - 0.894) <= DOUBLE_EPS - assert abs(round(v3.y, 3) - 0.447) <= DOUBLE_EPS + # Create QuantityVector from a Vector + vec = Vector([1, 2, 3]) + quantity_vec = QuantityVector(vec, UNITS.mm) + assert abs(quantity_vec.x.magnitude - vec.x) <= TOLERANCE + assert abs(quantity_vec.y.magnitude - vec.y) <= TOLERANCE + assert abs(quantity_vec.z.magnitude - vec.z) <= TOLERANCE + assert quantity_vec.unit == UNITS.mm + assert abs(quantity_vec.norm.magnitude - vec.norm) <= TOLERANCE + _, base_unit = UNITS.get_base_units(UNITS.mm) + assert quantity_vec.base_unit == base_unit - # Check that UnitVector2D is immutable - with pytest.raises(UnsupportedOperation, match="UnitVector2D is immutable."): - v2.x = 3 - with pytest.raises(UnsupportedOperation, match="UnitVector2D is immutable."): - v2.y = 3 + # Check that the actual values are in base units (i.e. UNIT_LENGTH) + assert quantity_vec[0] == (quantity_vec.x).to_base_units().magnitude + assert quantity_vec[1] == (quantity_vec.y).to_base_units().magnitude + assert quantity_vec[2] == (quantity_vec.z).to_base_units().magnitude + # Change the values using the setters + vec_end_mm = Vector([70, 80, 90]) + vec_end_cm = Vector([7, 8, 9]) + quantity_vec.unit = UNITS.mm + quantity_vec.x = 70 * quantity_vec.unit + quantity_vec.y = 80 * quantity_vec.unit + quantity_vec.z = 90 * quantity_vec.unit + assert abs(quantity_vec.x.magnitude - vec_end_mm.x) <= TOLERANCE + assert abs(quantity_vec.y.magnitude - vec_end_mm.y) <= TOLERANCE + assert abs(quantity_vec.z.magnitude - vec_end_mm.z) <= TOLERANCE + assert quantity_vec.unit == UNITS.mm + assert abs(quantity_vec.norm.magnitude - vec_end_mm.norm) <= TOLERANCE + assert quantity_vec[0] == (quantity_vec.x).to_base_units().magnitude + assert quantity_vec[1] == (quantity_vec.y).to_base_units().magnitude + assert quantity_vec[2] == (quantity_vec.z).to_base_units().magnitude -def test_vector3d_errors(): - """Testing multiple ``Vector3D`` errors.""" + # Change back to cm and check that the values are modified according to units + quantity_vec.unit = UNITS.cm + assert abs(quantity_vec.x.magnitude - vec_end_cm.x) <= TOLERANCE + assert abs(quantity_vec.y.magnitude - vec_end_cm.y) <= TOLERANCE + assert abs(quantity_vec.z.magnitude - vec_end_cm.z) <= TOLERANCE + assert quantity_vec.unit == UNITS.cm - with pytest.raises(ValueError, match="Vector3D must have three coordinates."): - Vector3D([1, 2]) + # Check that two quantity vectors with the same input vector + # and different units are not the same + quantity_vec_cm = QuantityVector([1, 2, 3], UNITS.cm) + quantity_vec_mm_eq = QuantityVector([10, 20, 30], UNITS.mm) + quantity_vec_mm_ne = QuantityVector([1, 2, 3], UNITS.mm) + assert quantity_vec_cm != quantity_vec_mm_ne + assert quantity_vec_cm == quantity_vec_mm_eq - with pytest.raises( - TypeError, match="The numpy.ndarray 'input' should contain float or integer values." - ): - Vector3D(["a", "b", "c"]) + # Let's do some vector operations with the below + quantity_vec_a = QuantityVector([1, 2, 3], UNITS.cm) + quantity_vec_b = QuantityVector([70, 0, 10], UNITS.mm) + dot_a_b = quantity_vec_a * quantity_vec_b + assert dot_a_b == 0.001 - # Create a Vector3D - v1 = Vector3D([1, 2, 3]) + cross_a_x_b = quantity_vec_a % quantity_vec_b # Resulting vector: [2, 20, -14] cm + cross_result = QuantityVector([2, 20, -14], UNITS.cm) + assert abs(cross_a_x_b.x.magnitude - cross_result.x.to_base_units().magnitude) <= TOLERANCE + assert abs(cross_a_x_b.y.magnitude - cross_result.y.to_base_units().magnitude) <= TOLERANCE + assert abs(cross_a_x_b.z.magnitude - cross_result.z.to_base_units().magnitude) <= TOLERANCE + assert cross_a_x_b.unit == UNITS.cm - # Test setter error checks - with pytest.raises(TypeError, match="The parameter 'x' should be a float or an integer value."): - v1.x = "x" + normalized_b = quantity_vec_b.normalize() + vec_b_normalized = Vector([70, 0, 10]).normalize() + assert abs(normalized_b.x - vec_b_normalized.x) <= TOLERANCE + assert abs(normalized_b.y - vec_b_normalized.y) <= TOLERANCE + assert abs(normalized_b.z - vec_b_normalized.z) <= TOLERANCE - with pytest.raises(TypeError, match="The parameter 'y' should be a float or an integer value."): - v1.y = "y" + # Create a QuantityVector3D from 2 points + point_a = Point([1, 2, 3], UNITS.cm) + point_b = Point([1, 6, 3], UNITS.cm) + quantity_vector_from_points = QuantityVector.from_points(point_a, point_b) + assert abs(quantity_vector_from_points.x.m - (0 * UNITS.cm).to_base_units().m) <= TOLERANCE + assert abs(quantity_vector_from_points.y.m - (4 * UNITS.cm).to_base_units().m) <= TOLERANCE + assert abs(quantity_vector_from_points.z.m - (0 * UNITS.cm).to_base_units().m) <= TOLERANCE - with pytest.raises(TypeError, match="The parameter 'z' should be a float or an integer value."): - v1.z = "z" + with pytest.raises( + TypeError, + match="Provided type is invalid", + ): + QuantityVector.from_points(np.array([2, 5, 8]), point_b) - # Build a Vector2D and try to compare against it - with pytest.raises(TypeError, match=f"Provided type {Vector2D} is invalid"): - v2 = Vector2D([1, 2]) - assert v1 == v2 + with pytest.raises( + TypeError, + match="Provided type is invalid", + ): + QuantityVector.from_points(point_a, np.array([2, 5, 8])) + + # Create a 2D QuantityVector from 2 points with same units + point_a = Point([1, 2], UNITS.cm) + point_b = Point([1, 6], UNITS.cm) + quantity_vector_from_points = QuantityVector.from_points(point_a, point_b) + assert abs(quantity_vector_from_points.x.m - (0 * UNITS.cm).to_base_units().m) <= TOLERANCE + assert abs(quantity_vector_from_points.y.m - (4 * UNITS.cm).to_base_units().m) <= TOLERANCE + assert quantity_vector_from_points.unit == point_a.base_unit + + # Create a 2D QuantityVector from 2 points with different units + point_a = Point([1, 2], UNITS.dm) + point_b = Point([1, 6], UNITS.cm) + quantity_vector_from_points = QuantityVector.from_points(point_a, point_b) + assert abs(quantity_vector_from_points.x.m - (-9 * UNITS.cm).to_base_units().m) <= TOLERANCE + assert abs(quantity_vector_from_points.y.m - (-14 * UNITS.cm).to_base_units().m) <= TOLERANCE + assert quantity_vector_from_points.unit == point_a.base_unit + assert quantity_vector_from_points.is_2d - # Try to normalize a 0-value vector - with pytest.raises(ValueError, match="The norm of the Vector3D is not valid."): - v2 = ZERO_VECTOR3D - v2.normalize() + with pytest.raises( + TypeError, + match="Provided type is invalid", + ): + QuantityVector.from_points(np.array([2, 5]), point_b) - # Try to create a vector from two identical points. with pytest.raises( - ValueError, - match="The two points cannot have the exact same coordinates.", + TypeError, + match="Provided type is invalid", ): - Vector3D.from_points(Point3D([10, 20, 30]), Point3D([10, 20, 30])) + QuantityVector.from_points(point_a, np.array([2, 5])) -def test_vector2d_errors(): - """Testing multiple ``Vector2D`` errors.""" +def test_vector_errors(): + """Testing multiple ``Vector`` errors.""" - with pytest.raises(ValueError, match="Vector2D must have two coordinates."): - Vector2D([1]) + with pytest.raises( + ValueError, + match="Vector class can only receive 2 or 3 arguments, creating a 2D or 3D vector, respectively.", # noqa: E501 + ): + Vector([1]) + + with pytest.raises( + ValueError, + match="Vector class can only receive 2 or 3 arguments, creating a 2D or 3D vector, respectively.", # noqa: E501 + ): + Vector([1, 2, 3, 4]) with pytest.raises( TypeError, match="The numpy.ndarray 'input' should contain float or integer values." ): - Vector2D(["a", "b"]) + Vector(["a", "b", "c"]) - # Create a Vector2D - v1 = Vector2D([1, 2]) + # Create a Vector + v1 = Vector([1, 2, 3]) # Test setter error checks with pytest.raises(TypeError, match="The parameter 'x' should be a float or an integer value."): @@ -416,111 +451,44 @@ def test_vector2d_errors(): with pytest.raises(TypeError, match="The parameter 'y' should be a float or an integer value."): v1.y = "y" - # Build a Vector3D and try to compare against it - with pytest.raises(TypeError, match=f"Provided type {Vector3D} is invalid"): - v2 = Vector3D([1, 5, 6]) - assert v1 == v2 + with pytest.raises(TypeError, match="The parameter 'z' should be a float or an integer value."): + v1.z = "z" + + # Build a 2D Vector and try to compare against it + v2 = Vector([1, 2]) + assert not v1 == v2 # Try to normalize a 0-value vector - with pytest.raises(ValueError, match="The norm of the Vector2D is not valid."): - v2 = Vector2D([0, 0]) + with pytest.raises(ValueError, match="The norm of the Vector is not valid."): + v2 = ZERO_VECTOR3D v2.normalize() - # Try to create a vector from two identical points. + # Having V1 and V2 - let's try to do a cross product + v2 = Vector([1, 2]) + with pytest.raises(ValueError, match="Invalid Vector dimensions for cross product."): + v1 % v2 + + # Having V1 and V2 - let's try to do a dot product + with pytest.raises(ValueError, match="Invalid Vector dimensions for dot product."): + v1 * v2 + with pytest.raises( ValueError, - match="The two points cannot have the exact same coordinates.", + match="Vector class can only receive 2 or 3 arguments, creating a 2D or 3D vector, respectively.", # noqa: E501 ): - Vector2D.from_points(Point2D([10, 20]), Point2D([10, 20])) - - -def test_point3D_units(): - """``Point3D`` units testing.""" - - # Create a Point3D with some units - p_cm_to_mm = Point3D([10, 20, 30], UNITS.cm) - - # Check that the units are correctly in place - assert p_cm_to_mm.unit == UNITS.cm - - # Request for X, Y, Z and ensure they are in cm - assert p_cm_to_mm.x == 10 - assert p_cm_to_mm.y == 20 - assert p_cm_to_mm.z == 30 - - # Check that the actual values are in base units (i.e. UNIT_LENGTH) - assert p_cm_to_mm[0] == (p_cm_to_mm.x * p_cm_to_mm.unit).to_base_units().magnitude - assert p_cm_to_mm[1] == (p_cm_to_mm.y * p_cm_to_mm.unit).to_base_units().magnitude - assert p_cm_to_mm[2] == (p_cm_to_mm.z * p_cm_to_mm.unit).to_base_units().magnitude - - # Store the numpy array values - (raw_x, raw_y, raw_z) = p_cm_to_mm[0:3] - - # Set unit to mm now... and check if the values changed - p_cm_to_mm.unit = UNITS.mm - assert p_cm_to_mm.x == 100 - assert p_cm_to_mm.y == 200 - assert p_cm_to_mm.z == 300 - - # Check that the values are still the same in the array - assert raw_x == p_cm_to_mm[0] - assert raw_y == p_cm_to_mm[1] - assert raw_z == p_cm_to_mm[2] - - # Now change the value of a X being in millimeters - p_cm_to_mm.x = 20 # Basically 1/5 of original x - assert not raw_x == p_cm_to_mm[0] - assert raw_x == p_cm_to_mm[0] * 5 - - # Now change the value of a Y being in millimeters - p_cm_to_mm.y = 10 # Basically 1/20 of original y - assert not raw_y == p_cm_to_mm[1] - assert raw_y == p_cm_to_mm[1] * 20 - - # Now change the value of a Z being in millimeters - p_cm_to_mm.z = 30 # Basically 1/10 of original z - assert not raw_z == p_cm_to_mm[2] - assert raw_z == p_cm_to_mm[2] * 10 - - -def test_point2D_units(): - """``Point2D`` units testing.""" + QuantityVector([1], UNITS.cm) - # Create a Point2D with some units - p_cm_to_mm = Point2D([10, 20], UNITS.cm) - - # Check that the units are correctly in place - assert p_cm_to_mm.unit == UNITS.cm - - # Request for X, Y, Z and ensure they are in cm - assert p_cm_to_mm.x == 10 - assert p_cm_to_mm.y == 20 - - # Check that the actual values are in base units (i.e. UNIT_LENGTH) - assert p_cm_to_mm[0] == (p_cm_to_mm.x * p_cm_to_mm.unit).to_base_units().magnitude - assert p_cm_to_mm[1] == (p_cm_to_mm.y * p_cm_to_mm.unit).to_base_units().magnitude - - # Store the numpy array values - (raw_x, raw_y) = p_cm_to_mm[0:2] - - # Set unit to mm now... and check if the values changed - p_cm_to_mm.unit = UNITS.mm - assert p_cm_to_mm.x == 100 - assert p_cm_to_mm.y == 200 - - # Check that the values are still the same in the array - assert raw_x == p_cm_to_mm[0] - assert raw_y == p_cm_to_mm[1] - - # Now change the value of a X being in millimeters - p_cm_to_mm.x = 20 # Basically 1/5 of original x - assert not raw_x == p_cm_to_mm[0] - assert raw_x == p_cm_to_mm[0] * 5 + with pytest.raises( + ValueError, + match="Vector class can only receive 2 or 3 arguments, creating a 2D or 3D vector, respectively.", # noqa: E501 + ): + QuantityVector([1, 2, 3, 4], UNITS.cm) - # Now change the value of a Y being in millimeters - p_cm_to_mm.y = 10 # Basically 1/20 of original y - assert not raw_y == p_cm_to_mm[1] - assert raw_y == p_cm_to_mm[1] * 20 + with pytest.raises( + ValueError, + match="The norm of the Vector is not valid.", + ): + QuantityVector(ZERO_VECTOR3D, UNITS.cm).normalize() def test_matrix(): @@ -650,192 +618,13 @@ def test_matrix_44(): assert m_1 == m_2 -def test_quantity_vector_3d(): - """Simple tests to create ``QuantityVector3D``.""" - - # Define the tolerance for the QuantityVector3D tests - TOLERANCE = 5e-15 - - # Create QuantityVector3D from a Vector3D - vec = Vector3D([1, 2, 3]) - quantity_vec = QuantityVector3D(vec, UNITS.mm) - assert abs(quantity_vec.x - vec.x) <= TOLERANCE - assert abs(quantity_vec.y - vec.y) <= TOLERANCE - assert abs(quantity_vec.z - vec.z) <= TOLERANCE - assert quantity_vec.unit == UNITS.mm - assert abs(quantity_vec.norm - vec.norm) <= TOLERANCE - _, base_unit = UNITS.get_base_units(UNITS.mm) - assert quantity_vec.base_unit == base_unit - - # Check that the actual values are in base units (i.e. UNIT_LENGTH) - assert quantity_vec[0] == (quantity_vec.x * quantity_vec.unit).to_base_units().magnitude - assert quantity_vec[1] == (quantity_vec.y * quantity_vec.unit).to_base_units().magnitude - assert quantity_vec[2] == (quantity_vec.z * quantity_vec.unit).to_base_units().magnitude - - # Change the values using the setters - vec_end_mm = Vector3D([70, 80, 90]) - vec_end_cm = Vector3D([7, 8, 9]) - quantity_vec.unit = UNITS.mm - quantity_vec.x = 70 - quantity_vec.y = 80 - quantity_vec.z = 90 - assert abs(quantity_vec.x - vec_end_mm.x) <= TOLERANCE - assert abs(quantity_vec.y - vec_end_mm.y) <= TOLERANCE - assert abs(quantity_vec.z - vec_end_mm.z) <= TOLERANCE - assert quantity_vec.unit == UNITS.mm - assert abs(quantity_vec.norm - vec_end_mm.norm) <= TOLERANCE - assert quantity_vec[0] == (quantity_vec.x * quantity_vec.unit).to_base_units().magnitude - assert quantity_vec[1] == (quantity_vec.y * quantity_vec.unit).to_base_units().magnitude - assert quantity_vec[2] == (quantity_vec.z * quantity_vec.unit).to_base_units().magnitude - - # Change back to cm and check that the values are modified according to units - quantity_vec.unit = UNITS.cm - assert abs(quantity_vec.x - vec_end_cm.x) <= TOLERANCE - assert abs(quantity_vec.y - vec_end_cm.y) <= TOLERANCE - assert abs(quantity_vec.z - vec_end_cm.z) <= TOLERANCE - assert quantity_vec.unit == UNITS.cm - - # Check that two quantity vectors with the same input vector - # and different units are not the same - quantity_vec_cm = QuantityVector3D([1, 2, 3], UNITS.cm) - quantity_vec_mm_eq = QuantityVector3D([10, 20, 30], UNITS.mm) - quantity_vec_mm_ne = QuantityVector3D([1, 2, 3], UNITS.mm) - assert quantity_vec_cm != quantity_vec_mm_ne - assert quantity_vec_cm == quantity_vec_mm_eq - - # Let's do some vector operations with the below - quantity_vec_a = QuantityVector3D([1, 2, 3], UNITS.cm) - quantity_vec_b = QuantityVector3D([70, 0, 10], UNITS.mm) - dot_a_b = quantity_vec_a * quantity_vec_b - assert dot_a_b == 0.001 - - cross_a_x_b = quantity_vec_a % quantity_vec_b # Resulting vector: [2, 20, -14] cm - assert abs(cross_a_x_b.x - 2) <= TOLERANCE - assert abs(cross_a_x_b.y - 20) <= TOLERANCE - assert abs(cross_a_x_b.z - (-14)) <= TOLERANCE - assert cross_a_x_b.unit == UNITS.cm - - normalized_b = quantity_vec_b.normalize() - vec_b_normalized = Vector3D([70, 0, 10]).normalize() - assert abs(normalized_b.x - vec_b_normalized.x) <= TOLERANCE - assert abs(normalized_b.y - vec_b_normalized.y) <= TOLERANCE - assert abs(normalized_b.z - vec_b_normalized.z) <= TOLERANCE - - # Create a QuantityVector3D from 2 points - point_a = Point3D([1, 2, 3], UNITS.cm) - point_b = Point3D([1, 6, 3], UNITS.cm) - quantity_vector_from_points = QuantityVector3D.from_points(point_a, point_b) - quantity_vector_from_points.x == 0 - quantity_vector_from_points.y == 4 - quantity_vector_from_points.z == 0 - - with pytest.raises( - TypeError, - match="Provided type is invalid", - ): - QuantityVector3D.from_points(np.array([2, 5, 8]), point_b) - - with pytest.raises( - TypeError, - match="Provided type is invalid", - ): - QuantityVector3D.from_points(point_a, np.array([2, 5, 8])) - - -def test_quantity_vector_2d(): - """Simple tests to create ``QuantityVector2D``.""" - - # Define the tolerance for the QuantityVector2D tests - TOLERANCE = 5e-15 - - # Create QuantityVector2D from a Vector3D - vec = Vector2D([1, 2]) - quantity_vec = QuantityVector2D(vec, UNITS.mm) - assert abs(quantity_vec.x - vec.x) <= TOLERANCE - assert abs(quantity_vec.y - vec.y) <= TOLERANCE - assert quantity_vec.unit == UNITS.mm - assert abs(quantity_vec.norm - vec.norm) <= TOLERANCE - _, base_unit = UNITS.get_base_units(UNITS.mm) - assert quantity_vec.base_unit == base_unit - - # Check that the actual values are in base units (i.e. UNIT_LENGTH) - assert quantity_vec[0] == (quantity_vec.x * quantity_vec.unit).to_base_units().magnitude - assert quantity_vec[1] == (quantity_vec.y * quantity_vec.unit).to_base_units().magnitude - - # Change the values using the setters - vec_end_mm = Vector2D([70, 80]) - vec_end_cm = Vector2D([7, 8]) - quantity_vec.unit = UNITS.mm - quantity_vec.x = 70 - quantity_vec.y = 80 - assert abs(quantity_vec.x - vec_end_mm.x) <= TOLERANCE - assert abs(quantity_vec.y - vec_end_mm.y) <= TOLERANCE - assert quantity_vec.unit == UNITS.mm - assert abs(quantity_vec.norm - vec_end_mm.norm) <= TOLERANCE - assert quantity_vec[0] == (quantity_vec.x * quantity_vec.unit).to_base_units().magnitude - assert quantity_vec[1] == (quantity_vec.y * quantity_vec.unit).to_base_units().magnitude - - # Change back to cm and check that the values are modified according to units - quantity_vec.unit = UNITS.cm - assert abs(quantity_vec.x - vec_end_cm.x) <= TOLERANCE - assert abs(quantity_vec.y - vec_end_cm.y) <= TOLERANCE - assert quantity_vec.unit == UNITS.cm - - # Check that two quantity vectors with the same input vector - # and different units are not the same - quantity_vec_cm = QuantityVector2D([1, 2], UNITS.cm) - quantity_vec_mm_eq = QuantityVector2D([10, 20], UNITS.mm) - quantity_vec_mm_ne = QuantityVector2D([1, 2], UNITS.mm) - assert quantity_vec_cm != quantity_vec_mm_ne - assert quantity_vec_cm == quantity_vec_mm_eq - - # Let's do some vector operations with the below - quantity_vec_a = QuantityVector2D([1, 2], UNITS.cm) - quantity_vec_b = QuantityVector2D([70, 10], UNITS.mm) - dot_a_b = quantity_vec_a * quantity_vec_b - assert round(dot_a_b, 4) == 0.0009 - - normalized_b = quantity_vec_b.normalize() - vec_b_normalized = Vector2D([70, 10]).normalize() - assert abs(normalized_b.x - vec_b_normalized.x) <= TOLERANCE - assert abs(normalized_b.y - vec_b_normalized.y) <= TOLERANCE - - # Create a QuantityVector2D from 2 points with same units - point_a = Point2D([1, 2], UNITS.cm) - point_b = Point2D([1, 6], UNITS.cm) - quantity_vector_from_points = QuantityVector2D.from_points(point_a, point_b) - quantity_vector_from_points.x == 0 - quantity_vector_from_points.y == 4 - quantity_vector_from_points.unit == UNITS.cm - - # Create a QuantityVector2D from 2 points with different units - point_a = Point2D([1, 2], UNITS.dm) - point_b = Point2D([1, 6], UNITS.cm) - quantity_vector_from_points = QuantityVector2D.from_points(point_a, point_b) - quantity_vector_from_points.x == 9 - quantity_vector_from_points.y == 14 - quantity_vector_from_points.unit == UNITS.cm - - with pytest.raises( - TypeError, - match="Provided type is invalid", - ): - QuantityVector2D.from_points(np.array([2, 5]), point_b) - - with pytest.raises( - TypeError, - match="Provided type is invalid", - ): - QuantityVector2D.from_points(point_a, np.array([2, 5])) - - def test_frame(): """``Frame`` construction and equivalency.""" - origin = Point3D([42, 99, 13]) - f_1 = Frame(origin, UnitVector3D([1, 0, 0]), UnitVector3D([0, 1, 0])) - f_1_duplicate = Frame(origin, UnitVector3D([1, 0, 0]), UnitVector3D([0, 1, 0])) - f_2 = Frame(Point3D([5, 8, 9]), UnitVector3D([1, 1, 1]), UnitVector3D([0, -1, 1])) + origin = Point([42, 99, 13]) + f_1 = Frame(origin, UnitVector([1, 0, 0]), UnitVector([0, 1, 0])) + f_1_duplicate = Frame(origin, UnitVector([1, 0, 0]), UnitVector([0, 1, 0])) + f_2 = Frame(Point([5, 8, 9]), UnitVector([1, 1, 1]), UnitVector([0, -1, 1])) f_with_array_definitions = Frame([5, 8, 9], [1, 1, 1], [0, -1, 1]) f_defaults = Frame() @@ -855,22 +644,22 @@ def test_frame(): assert f_defaults.direction_z == UNIT_VECTOR_Z with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Frame(origin, "A", UnitVector3D([25, 39, 82])) + Frame(origin, "A", UnitVector([25, 39, 82])) with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Frame(origin, UnitVector3D([12, 31, 99]), "A") + Frame(origin, UnitVector([12, 31, 99]), "A") with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Frame("A", UnitVector3D([12, 31, 99]), UnitVector3D([23, 67, 45])) + Frame("A", UnitVector([12, 31, 99]), UnitVector([23, 67, 45])) def test_plane(): """``Plane`` construction and equivalency.""" - origin = Point3D([42, 99, 13]) - p_1 = Plane(origin, UnitVector3D([1, 0, 0]), UnitVector3D([0, 1, 0])) - p_1_duplicate = Plane(origin, UnitVector3D([1, 0, 0]), UnitVector3D([0, 1, 0])) - p_2 = Plane(Point3D([5, 8, 9]), UnitVector3D([1, 1, 1]), UnitVector3D([0, -1, 1])) + origin = Point([42, 99, 13]) + p_1 = Plane(origin, UnitVector([1, 0, 0]), UnitVector([0, 1, 0])) + p_1_duplicate = Plane(origin, UnitVector([1, 0, 0]), UnitVector([0, 1, 0])) + p_2 = Plane(Point([5, 8, 9]), UnitVector([1, 1, 1]), UnitVector([0, -1, 1])) p_with_array_definitions = Plane([5, 8, 9], [1, 1, 1], [0, -1, 1]) p_defaults = Plane() @@ -890,10 +679,10 @@ def test_plane(): assert p_defaults.direction_z == UNIT_VECTOR_Z with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Plane(origin, "A", UnitVector3D([25, 39, 82])) + Plane(origin, "A", UnitVector([25, 39, 82])) with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Plane(origin, UnitVector3D([12, 31, 99]), "A") + Plane(origin, UnitVector([12, 31, 99]), "A") with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Plane("A", UnitVector3D([12, 31, 99]), UnitVector3D([23, 67, 45])) + Plane("A", UnitVector([12, 31, 99]), UnitVector([23, 67, 45])) diff --git a/tests/test_accuracy.py b/tests/test_misc_accuracy.py similarity index 98% rename from tests/test_accuracy.py rename to tests/test_misc_accuracy.py index 1e3214fbb3..d96f2aabc0 100644 --- a/tests/test_accuracy.py +++ b/tests/test_misc_accuracy.py @@ -1,6 +1,6 @@ import math -from ansys.geometry.core.accuracy import Accuracy +from ansys.geometry.core.misc.accuracy import Accuracy def test_length(): diff --git a/tests/test_misc_checks.py b/tests/test_misc_checks.py index ae3cdf17f4..02edf6dbd1 100644 --- a/tests/test_misc_checks.py +++ b/tests/test_misc_checks.py @@ -1,11 +1,10 @@ import numpy as np import pytest -from ansys.geometry.core import UNITS -from ansys.geometry.core.math import Point2D, Point3D +from ansys.geometry.core.math import Point from ansys.geometry.core.misc import ( + UNITS, check_is_float_int, - check_is_pint_unit, check_ndarray_is_float_int, check_ndarray_is_non_zero, check_ndarray_is_not_none, @@ -21,10 +20,9 @@ def test_check_type(): Both objects must be of the same type to be compared. """ - a_2d = Point2D([1, 2]) + a_2d = Point([1, 2]) - check_type(a_2d, Point2D) - check_type(a_2d, (Point2D, Point3D)) + check_type(a_2d, Point) with pytest.raises(TypeError, match="Provided type"): check_type(a_2d, int) @@ -39,15 +37,13 @@ def test_check_type_equivalence(): Both objects must be of the same type to be compared. """ - a_2d = Point2D([1, 2]) - a_3d = Point3D([1, 2, 0]) - b_2d = Point2D([3, 4]) + a_2d = Point([1, 2]) + b_2d = Point([3, 4]) - # Check that a_2d and a_3d are not the same + # Check that a_2d and 3 are not the same with pytest.raises(TypeError, match="Provided type"): - check_type_equivalence(a_2d, a_3d) + check_type_equivalence(3, a_2d) - # Check that a_2d and a_3d are not the same check_type_equivalence(a_2d, b_2d) @@ -69,28 +65,6 @@ def test_check_pint_unit_compatibility(): check_pint_unit_compatibility(length_1, length_2) -def test_check_is_pint_unit(): - """ - Test that the :class:`pint.Unit` type check is working properly. - - The object provided must be a :class:`pint.Unit`. - """ - pint_unit = UNITS.meter - not_a_pint_unit = "myUnit" - - # Check that not_a_pint_unit is not a pint.Unit object - with pytest.raises(TypeError, match="The parameter 'unit' should be a pint.Unit object."): - check_is_pint_unit(not_a_pint_unit) - - with pytest.raises( - TypeError, match="The parameter 'not_a_pint_unit' should be a pint.Unit object." - ): - check_is_pint_unit(not_a_pint_unit, "not_a_pint_unit") - - # Check that pint_unit is indeed a pint.Unit - check_is_pint_unit(pint_unit) - - def test_check_ndarray_is_float_int(): """ Test that :class:`numpy.ndarray` object contains ``float`` or ``int`` values only. diff --git a/tests/test_primitives.py b/tests/test_primitives.py index d2fc49efe7..1fe4b35146 100644 --- a/tests/test_primitives.py +++ b/tests/test_primitives.py @@ -1,7 +1,7 @@ import pytest -from ansys.geometry.core import UNITS -from ansys.geometry.core.math import Point3D, UnitVector3D +from ansys.geometry.core.math import Point, UnitVector +from ansys.geometry.core.misc import UNITS from ansys.geometry.core.primitives import Cone, Cylinder, Sphere, Torus @@ -9,16 +9,14 @@ def test_cylinder(): """``Cylinder`` construction and equivalency.""" # Create two Cylinder objects - origin = Point3D([42, 99, 13]) + origin = Point([42, 99, 13]) radius = 100 height = 200 - c_1 = Cylinder(origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), radius, height) + c_1 = Cylinder(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), radius, height) c_1_duplicate = Cylinder( - origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), radius, height - ) - c_2 = Cylinder( - Point3D([5, 8, 9]), UnitVector3D([55, 16, 73]), UnitVector3D([23, 67, 45]), 88, 76 + origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), radius, height ) + c_2 = Cylinder(Point([5, 8, 9]), UnitVector([55, 16, 73]), UnitVector([23, 67, 45]), 88, 76) c_with_array_definitions = Cylinder([5, 8, 9], [55, 16, 73], [23, 67, 45], 88, 76) # Check that the equals operator works @@ -33,7 +31,7 @@ def test_cylinder(): assert c_1.radius == radius assert c_1.height == height - c_1.origin = new_origin = Point3D([42, 88, 99]) + c_1.origin = new_origin = Point([42, 88, 99]) c_1.radius = new_radius = 1000 c_1.height = new_height = 2000 @@ -47,13 +45,13 @@ def test_cylinder(): TypeError, match="The parameter 'radius' should be a float or an integer value.", ): - Cylinder(origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), "A", 200) + Cylinder(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), "A", 200) with pytest.raises( TypeError, match="The parameter 'height' should be a float or an integer value.", ): - Cylinder(origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), 100, "A") + Cylinder(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), 100, "A") with pytest.raises( TypeError, @@ -74,41 +72,39 @@ def test_cylinder(): c_1.origin = "A" with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Cylinder(origin, "A", UnitVector3D([25, 39, 82]), 100, 200) + Cylinder(origin, "A", UnitVector([25, 39, 82]), 100, 200) with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Cylinder(origin, UnitVector3D([12, 31, 99]), "A", 100, 200) + Cylinder(origin, UnitVector([12, 31, 99]), "A", 100, 200) def test_cylinder_units(): """``Cylinder`` units validation.""" - origin = Point3D([42, 99, 13]) + origin = Point([42, 99, 13]) radius = 100 height = 200 unit = UNITS.mm # Verify rejection of invalid base unit type with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a \[length\] quantity.", + match=r"The pint.Unit provided as input should be a \[length\] quantity.", ): Cylinder( origin, - UnitVector3D([12, 31, 99]), - UnitVector3D([25, 39, 82]), + UnitVector([12, 31, 99]), + UnitVector([25, 39, 82]), radius, height, UNITS.celsius, ) - c_1 = Cylinder( - origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), radius, height, unit - ) + c_1 = Cylinder(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), radius, height, unit) # Verify rejection of invalid base unit type with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a \[length\] quantity.", + match=r"The pint.Unit provided as input should be a \[length\] quantity.", ): c_1.unit = UNITS.celsius @@ -133,11 +129,11 @@ def test_sphere(): """``Sphere`` construction and equivalency.""" # Create two Sphere objects - origin = Point3D([42, 99, 13]) + origin = Point([42, 99, 13]) radius = 100 s_1 = Sphere(origin, 100) s_1_duplicate = Sphere(origin, 100) - s_2 = Sphere(Point3D([5, 8, 9]), 88) + s_2 = Sphere(Point([5, 8, 9]), 88) s_with_array_definitions = Sphere([5, 8, 9], 88) # Check that the equals operator works @@ -151,7 +147,7 @@ def test_sphere(): assert s_1.origin.z == origin.z assert s_1.radius == radius - s_1.origin = new_origin = Point3D([42, 88, 99]) + s_1.origin = new_origin = Point([42, 88, 99]) s_1.radius = new_radius = 1000 assert s_1.origin.x == new_origin.x @@ -185,13 +181,13 @@ def test_sphere(): def test_sphere_units(): """``Sphere`` units validation.""" - origin = Point3D([42, 99, 13]) + origin = Point([42, 99, 13]) radius = 100 unit = UNITS.mm # Verify rejection of invalid base unit type with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a \[length\] quantity.", + match=r"The pint.Unit provided as input should be a \[length\] quantity.", ): Sphere(origin, radius, UNITS.celsius) @@ -200,7 +196,7 @@ def test_sphere_units(): # Verify rejection of invalid base unit type with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a \[length\] quantity.", + match=r"The pint.Unit provided as input should be a \[length\] quantity.", ): s_1.unit = UNITS.celsius @@ -222,14 +218,14 @@ def test_cone(): """``Cone`` construction and equivalency.""" # Create two Cone objects - origin = Point3D([42, 99, 13]) + origin = Point([42, 99, 13]) radius = 100 half_angle = 0.78539816 - c_1 = Cone(origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), radius, half_angle) + c_1 = Cone(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), radius, half_angle) c_1_duplicate = Cone( - origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), radius, half_angle + origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), radius, half_angle ) - c_2 = Cone(Point3D([5, 8, 9]), UnitVector3D([55, 16, 73]), UnitVector3D([23, 67, 45]), 88, 0.65) + c_2 = Cone(Point([5, 8, 9]), UnitVector([55, 16, 73]), UnitVector([23, 67, 45]), 88, 0.65) c_with_array_definitions = Cone([5, 8, 9], [55, 16, 73], [23, 67, 45], 88, 0.65) # Check that the equals operator works @@ -244,7 +240,7 @@ def test_cone(): assert c_1.radius == radius assert c_1.half_angle == half_angle - c_1.origin = new_origin = Point3D([42, 88, 99]) + c_1.origin = new_origin = Point([42, 88, 99]) c_1.radius = new_radius = 1000 c_1.half_angle = new_half_angle = 0.78539816 @@ -258,13 +254,13 @@ def test_cone(): TypeError, match="The parameter 'radius' should be a float or an integer value.", ): - Cone(origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), "A", 200) + Cone(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), "A", 200) with pytest.raises( TypeError, match="The parameter 'half_angle' should be a float or an integer value.", ): - Cone(origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), 100, "A") + Cone(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), 100, "A") with pytest.raises( TypeError, @@ -285,16 +281,16 @@ def test_cone(): c_1.origin = "A" with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Cone(origin, "A", UnitVector3D([25, 39, 82]), 100, 200) + Cone(origin, "A", UnitVector([25, 39, 82]), 100, 200) with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Cone(origin, UnitVector3D([12, 31, 99]), "A", 100, 200) + Cone(origin, UnitVector([12, 31, 99]), "A", 100, 200) def test_cone_units(): """``Cone`` units validation.""" - origin = Point3D([42, 99, 13]) + origin = Point([42, 99, 13]) radius = 100 half_angle = 45 unit_radius = UNITS.mm @@ -302,12 +298,12 @@ def test_cone_units(): # Verify rejection of invalid base unit type with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a \[length\] quantity.", + match=r"The pint.Unit provided as input should be a \[length\] quantity.", ): Cone( origin, - UnitVector3D([12, 31, 99]), - UnitVector3D([25, 39, 82]), + UnitVector([12, 31, 99]), + UnitVector([25, 39, 82]), radius, half_angle, UNITS.celsius, @@ -319,8 +315,8 @@ def test_cone_units(): ): Cone( origin, - UnitVector3D([12, 31, 99]), - UnitVector3D([25, 39, 82]), + UnitVector([12, 31, 99]), + UnitVector([25, 39, 82]), radius, half_angle, unit_radius, @@ -329,8 +325,8 @@ def test_cone_units(): c_1 = Cone( origin, - UnitVector3D([12, 31, 99]), - UnitVector3D([25, 39, 82]), + UnitVector([12, 31, 99]), + UnitVector([25, 39, 82]), radius, half_angle, unit_radius, @@ -340,13 +336,13 @@ def test_cone_units(): # Verify rejection of invalid base unit type with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a \[length\] quantity.", + match=r"The pint.Unit provided as input should be a \[length\] quantity.", ): c_1.length_unit = UNITS.celsius with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a dimensionless quantity.", + match=r"The pint.Unit provided as input should be a dimensionless quantity.", ): c_1.angle_unit = UNITS.celsius @@ -373,16 +369,16 @@ def test_torus(): """``Torus`` construction and equivalency.""" # Create two Torus objects - origin = Point3D([42, 99, 13]) + origin = Point([42, 99, 13]) major_radius = 200 minor_radius = 100 t_1 = Torus( - origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), major_radius, minor_radius + origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), major_radius, minor_radius ) t_1_duplicate = Torus( - origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), major_radius, minor_radius + origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), major_radius, minor_radius ) - t_2 = Torus(Point3D([5, 8, 9]), UnitVector3D([55, 16, 73]), UnitVector3D([23, 67, 45]), 88, 76) + t_2 = Torus(Point([5, 8, 9]), UnitVector([55, 16, 73]), UnitVector([23, 67, 45]), 88, 76) t_with_array_definitions = Torus([5, 8, 9], [55, 16, 73], [23, 67, 45], 88, 76) # Check that the equals operator works @@ -406,7 +402,7 @@ def test_torus(): assert t_1.major_radius == new_major_radius assert t_1.minor_radius == new_minor_radius - t_1.origin = new_origin = Point3D([42, 88, 99]) + t_1.origin = new_origin = Point([42, 88, 99]) assert t_1.origin.x == new_origin.x assert t_1.origin.y == new_origin.y assert t_1.origin.z == new_origin.z @@ -417,13 +413,13 @@ def test_torus(): TypeError, match="The parameter 'major_radius' should be a float or an integer value.", ): - Torus(origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), "A", 200) + Torus(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), "A", 200) with pytest.raises( TypeError, match="The parameter 'minor_radius' should be a float or an integer value.", ): - Torus(origin, UnitVector3D([12, 31, 99]), UnitVector3D([25, 39, 82]), 100, "A") + Torus(origin, UnitVector([12, 31, 99]), UnitVector([25, 39, 82]), 100, "A") with pytest.raises( TypeError, @@ -444,16 +440,16 @@ def test_torus(): t_1.origin = "A" with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Torus(origin, "A", UnitVector3D([25, 39, 82]), 100, 200) + Torus(origin, "A", UnitVector([25, 39, 82]), 100, 200) with pytest.raises(TypeError, match=f"Provided type {str} is invalid,"): - Torus(origin, UnitVector3D([12, 31, 99]), "A", 100, 200) + Torus(origin, UnitVector([12, 31, 99]), "A", 100, 200) def test_torus_units(): """``Torus`` units validation.""" - origin = Point3D([42, 99, 13]) + origin = Point([42, 99, 13]) major_radius = 200 minor_radius = 100 unit = UNITS.mm @@ -461,12 +457,12 @@ def test_torus_units(): # Verify rejection of invalid base unit type with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a \[length\] quantity.", + match=r"The pint.Unit provided as input should be a \[length\] quantity.", ): Torus( origin, - UnitVector3D([12, 31, 99]), - UnitVector3D([25, 39, 82]), + UnitVector([12, 31, 99]), + UnitVector([25, 39, 82]), major_radius, minor_radius, UNITS.celsius, @@ -474,8 +470,8 @@ def test_torus_units(): t_1 = Torus( origin, - UnitVector3D([12, 31, 99]), - UnitVector3D([25, 39, 82]), + UnitVector([12, 31, 99]), + UnitVector([25, 39, 82]), major_radius, minor_radius, unit, @@ -484,7 +480,7 @@ def test_torus_units(): # Verify rejection of invalid base unit type with pytest.raises( TypeError, - match="The pint.Unit provided as input should be a \[length\] quantity.", + match=r"The pint.Unit provided as input should be a \[length\] quantity.", ): t_1.unit = UNITS.celsius diff --git a/tests/test_shapes.py b/tests/test_shapes.py index 9eeddfec2c..90ad90c6a9 100644 --- a/tests/test_shapes.py +++ b/tests/test_shapes.py @@ -2,8 +2,8 @@ from numpy.testing import assert_allclose import pytest -from ansys.geometry.core import UNIT_LENGTH, UNITS -from ansys.geometry.core.math import ZERO_VECTOR3D, Point3D, UnitVector3D, Vector3D +from ansys.geometry.core.math import ZERO_VECTOR3D, Point, UnitVector, Vector +from ansys.geometry.core.misc import UNIT_LENGTH, UNITS from ansys.geometry.core.shapes.line import Line, Segment from ansys.geometry.core.sketch import Sketch @@ -17,7 +17,7 @@ def test_create_circle(): sketch = Sketch() # Draw a circle in previous sketch - radius, origin = 1 * UNITS.m, Point3D([0, 0, 0], UNITS.m) + radius, origin = (1 * UNITS.m).magnitude, Point([0, 0, 0], UNITS.m) circle = sketch.draw_circle(radius, origin) # Check attributes are expected ones @@ -28,8 +28,8 @@ def test_create_circle(): # Check points are expected ones local_points = circle.local_points(num_points=5) - assert abs(all(local_points[0] - Point3D([1, 0, 0]))) <= DOUBLE_EPS - assert abs(all(local_points[2] - Point3D([-1, 0, 0]))) <= DOUBLE_EPS + assert abs(all(local_points[0] - Point([1, 0, 0]))) <= DOUBLE_EPS + assert abs(all(local_points[2] - Point([-1, 0, 0]))) <= DOUBLE_EPS def test_create_ellipse(): @@ -39,7 +39,7 @@ def test_create_ellipse(): sketch = Sketch() # Draw a circle in previous sketch - a, b, origin = 2 * UNITS.m, 1 * UNITS.m, Point3D([0, 0, 0], UNITS.m) + a, b, origin = (2 * UNITS.m).magnitude, (1 * UNITS.m).magnitude, Point([0, 0, 0], UNITS.m) ecc = np.sqrt(1 - (b / a) ** 2) ellipse = sketch.draw_ellipse(a, b, origin) @@ -53,8 +53,8 @@ def test_create_ellipse(): # Check points are expected ones local_points = ellipse.local_points(num_points=5) - assert abs(all(local_points[0] - Point3D([2, 0, 0]))) <= DOUBLE_EPS - assert abs(all(local_points[2] - Point3D([-2, 0, 0]))) <= DOUBLE_EPS + assert abs(all(local_points[0] - Point([2, 0, 0]))) <= DOUBLE_EPS + assert abs(all(local_points[2] - Point([-2, 0, 0]))) <= DOUBLE_EPS def test_create_polygon(): @@ -64,7 +64,7 @@ def test_create_polygon(): sketch = Sketch() # Draw a pentagon in previous sketch - radius, sides, origin = 1 * UNITS.m, 5, Point3D([0, 0, 0], UNITS.m) + radius, sides, origin = (1 * UNITS.m).magnitude, 5, Point([0, 0, 0], UNITS.m) pentagon = sketch.draw_polygon(radius, sides, origin) # Check attributes are expected ones @@ -75,7 +75,7 @@ def test_create_polygon(): assert_allclose(pentagon.perimeter, sides * side_length) # Draw a square in previous sketch - radius, sides, origin = 1 * UNITS.m, 4, Point3D([0, 0, 0], UNITS.m) + radius, sides, origin = (1 * UNITS.m).magnitude, 4, Point([0, 0, 0], UNITS.m) square = sketch.draw_polygon(radius, sides, origin) # Check attributes are expected ones @@ -88,31 +88,31 @@ def test_create_polygon(): # Check points are expected ones local_points = square.local_points() - assert abs(all(local_points[0] - Point3D([1.41421356, 0, 0]))) <= DOUBLE_EPS - assert abs(all(local_points[2] - Point3D([-1.41421356, 0, 0]))) <= DOUBLE_EPS + assert abs(all(local_points[0] - Point([1.41421356, 0, 0]))) <= DOUBLE_EPS + assert abs(all(local_points[2] - Point([-1.41421356, 0, 0]))) <= DOUBLE_EPS with pytest.raises( ValueError, match="The minimum number of sides to construct a polygon should be 3." ): - radius, sides, origin = 1 * UNITS.m, 2, Point3D([0, 0, 0], UNITS.m) + radius, sides, origin = (1 * UNITS.m).magnitude, 2, Point([0, 0, 0], UNITS.m) sketch.draw_polygon(radius, sides, origin) with pytest.raises(ValueError, match="Radius must be a real positive value."): - radius, sides, origin = -1 * UNITS.m, 6, Point3D([0, 0, 0], UNITS.m) + radius, sides, origin = (-1 * UNITS.m).magnitude, 6, Point([0, 0, 0], UNITS.m) sketch.draw_polygon(radius, sides, origin) def test_create_line_no_sketch(): """Simple test to create a ``Line`` (w/o a Sketch object).""" - # Test line - Create a line using a Point3D and a Vector3D - origin = Point3D([1, 2, 3], unit=UNITS.mm) - direction_x = UnitVector3D([1, 0, 0]) + # Test line - Create a line using a Point and a Vector + origin = Point([1, 2, 3], unit=UNITS.mm) + direction_x = UnitVector([1, 0, 0]) line = Line(origin=origin, direction=direction_x) assert line.direction == direction_x assert line.origin == origin - # Test line_2 - Create a line using a Point3D and a Vector3D - direction_x_vector3D = Vector3D([45, 0, 0]) # Equivalent (as a UnitVector3D) to "direction_x" + # Test line_2 - Create a line using a Point and a Vector + direction_x_vector3D = Vector([45, 0, 0]) # Equivalent (as a UnitVector) to "direction_x" line_2 = Line(origin=origin, direction=direction_x_vector3D) assert line_2.direction == direction_x assert line_2.origin == origin @@ -120,21 +120,21 @@ def test_create_line_no_sketch(): # From test 1, get the local points local_points_even = line.local_points(num_points=80) local_points_odd = line.local_points(num_points=81) - assert local_points_even[0] == Point3D([-39, 2, 3], unit=UNITS.mm) - assert local_points_even[-1] == Point3D([40, 2, 3], unit=UNITS.mm) + assert local_points_even[0] == Point([-39, 2, 3], unit=UNITS.mm) + assert local_points_even[-1] == Point([40, 2, 3], unit=UNITS.mm) assert len(local_points_even) == 80 - assert local_points_odd[0] == Point3D([-39, 2, 3], unit=UNITS.mm) - assert local_points_odd[-1] == Point3D([41, 2, 3], unit=UNITS.mm) + assert local_points_odd[0] == Point([-39, 2, 3], unit=UNITS.mm) + assert local_points_odd[-1] == Point([41, 2, 3], unit=UNITS.mm) assert len(local_points_odd) == 81 def test_create_segment_no_sketch(): """Simple test to create a ``Segment`` (w/o a Sketch object).""" - # Test segment - Create a segment using two Point3D objects - start = Point3D([1, 2, 3], unit=UNITS.mm) - end = Point3D([1, 5, 9], unit=UNITS.mm) - unit_vector = UnitVector3D(end - start) + # Test segment - Create a segment using two Point objects + start = Point([1, 2, 3], unit=UNITS.mm) + end = Point([1, 5, 9], unit=UNITS.mm) + unit_vector = UnitVector(end - start) segment = Segment(start, end) assert segment.start == start assert segment.end == end @@ -148,10 +148,10 @@ def test_create_segment_no_sketch(): assert_allclose(segment.end.z, 9) assert segment.end.unit == UNITS.mm - # Test segment_2 - Create a segment using a Point3D and a vector - start_2 = Point3D([1, 2, 3], unit=UNITS.mm) - end_2 = Point3D([1, 5, 9], unit=UNITS.mm) - vector_2 = Vector3D(end_2 - start_2) + # Test segment_2 - Create a segment using a Point and a vector + start_2 = Point([1, 2, 3], unit=UNITS.mm) + end_2 = Point([1, 5, 9], unit=UNITS.mm) + vector_2 = Vector(end_2 - start_2) segment_2 = Segment.from_origin_and_vector(start_2, vector_2, vector_units=UNITS.meter) assert segment_2.start == start assert segment_2.end == end @@ -165,10 +165,10 @@ def test_create_segment_no_sketch(): assert_allclose(segment_2.end.z, 0.009) assert segment_2.end.unit == UNIT_LENGTH - # Test segment_2b - Create a segment using a Point3D and a vector (same units as the point) - start_2b = Point3D([1, 2, 3], unit=UNITS.mm) - end_2b = Point3D([1, 5, 9], unit=UNITS.mm) - vector_2b = Vector3D(end_2b - start_2b) + # Test segment_2b - Create a segment using a Point and a vector (same units as the point) + start_2b = Point([1, 2, 3], unit=UNITS.mm) + end_2b = Point([1, 5, 9], unit=UNITS.mm) + vector_2b = Vector(end_2b - start_2b) vector_2b = vector_2b * 1e3 # The vector would be in meters --> convert to mm segment_2b = Segment.from_origin_and_vector(start_2b, vector_2b, vector_units=UNITS.mm) assert segment_2b.start == start @@ -183,10 +183,10 @@ def test_create_segment_no_sketch(): assert_allclose(segment_2b.end.z, 9) assert segment_2b.end.unit == UNITS.mm - # Test segment_3 - Create a segment using two Point3D objects (in different units) - start_3 = Point3D([1, 2, 3], unit=UNITS.mm) - end_3 = Point3D([1, 5, 9], unit=UNITS.cm) # Point3D([10, 50, 90], unit=UNITS.mm) - unit_vector_3 = UnitVector3D(end_3 - start_3) + # Test segment_3 - Create a segment using two Point objects (in different units) + start_3 = Point([1, 2, 3], unit=UNITS.mm) + end_3 = Point([1, 5, 9], unit=UNITS.cm) # Point([10, 50, 90], unit=UNITS.mm) + unit_vector_3 = UnitVector(end_3 - start_3) segment_3 = Segment(start_3, end_3) assert segment_3.start == start_3 assert segment_3.end == end_3 @@ -216,18 +216,18 @@ def test_create_segment_no_sketch(): def test_errors_line(): """Check errors when handling a ``Line``.""" with pytest.raises(TypeError, match="Provided type"): - Line(Point3D([10, 20, 30], unit=UNITS.meter), "b") + Line(Point([10, 20, 30], unit=UNITS.meter), "b") with pytest.raises( ValueError, match="The numpy.ndarray 'direction' should not be a numpy.ndarray of zeros." ): - Line(Point3D([10, 20, 30], unit=UNITS.meter), ZERO_VECTOR3D) + Line(Point([10, 20, 30], unit=UNITS.meter), ZERO_VECTOR3D) with pytest.raises(TypeError, match="Provided type"): - Line("a", Vector3D([1, 0, 0])) + Line("a", Vector([1, 0, 0])) with pytest.raises( - ValueError, match="The numpy.ndarray 'origin' should not be a None numpy.ndarray." + ValueError, match="The numpy.ndarray 'origin' should not be a nan numpy.ndarray." ): - Line(Point3D(), Vector3D([1, 0, 0])) + Line(Point(), Vector([1, 0, 0])) def test_errors_segment(): @@ -235,17 +235,17 @@ def test_errors_segment(): with pytest.raises(TypeError, match="Provided type"): Segment("a", "b") with pytest.raises( - ValueError, match="The numpy.ndarray 'start' should not be a None numpy.ndarray." + ValueError, match="The numpy.ndarray 'start' should not be a nan numpy.ndarray." ): - Segment(Point3D(), "b") + Segment(Point(), "b") with pytest.raises(TypeError, match="Provided type"): - Segment(Point3D([10, 20, 30], unit=UNITS.meter), "b") + Segment(Point([10, 20, 30], unit=UNITS.meter), "b") with pytest.raises( - ValueError, match="The numpy.ndarray 'end' should not be a None numpy.ndarray." + ValueError, match="The numpy.ndarray 'end' should not be a nan numpy.ndarray." ): - Segment(Point3D([10, 20, 30]), Point3D()) + Segment(Point([10, 20, 30]), Point()) with pytest.raises( ValueError, match="Parameters 'origin' and 'end' have the same values. No segment can be created.", ): - Segment(Point3D([10, 20, 30]), Point3D([10, 20, 30])) + Segment(Point([10, 20, 30]), Point([10, 20, 30]))