From edc6907f574241edb2ebb48dd623505ff9c5cba2 Mon Sep 17 00:00:00 2001 From: "Trevor R.H. Clarke" Date: Fri, 22 Jul 2022 11:50:06 -0400 Subject: [PATCH] Update docs and add convenience methods to coordinate classes. Add full documentation for some members of Coordinate2. Add repr() to Coordinate2, Coordinate3, Box2, Box3. Add some operator implementations for Coordinate2, Coordinate3, Box2, and Box3. --- pyems/coordinate.py | 275 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 264 insertions(+), 11 deletions(-) diff --git a/pyems/coordinate.py b/pyems/coordinate.py index 8f0654d..da77e2a 100644 --- a/pyems/coordinate.py +++ b/pyems/coordinate.py @@ -17,39 +17,33 @@ def val_inside(val: float, bound1: float, bound2: float) -> bool: class Coordinate2: - """""" + """ A 2 dimentional coordinate + """ def __init__(self, x: float, y: float): - """""" self._x = x self._y = y @property def x(self) -> float: - """""" return self._x @property def y(self) -> float: - """""" return self._y @x.setter def x(self, value: float) -> None: - """""" self._x = value @y.setter def y(self, value: float) -> None: - """""" self._y = value def __getitem__(self, key): - """""" return self._int_to_coord(key) def __setitem__(self, key, val): - """""" if key == 0: self.x = val elif key == 1: @@ -58,13 +52,105 @@ def __setitem__(self, key, val): raise ValueError("Invalid index.") def __eq__(self, other: Coordinate2) -> bool: - """""" if self.x == other.x and self.y == other.y: return True return False + + def __add__(self, other: Coordinate2) -> Coordinate2: + """ + Positive translation by another Coordinate2. + This does not modify the coordinate. + + :param other: The coordinate to translate by + :type other: Coordinate2 + :return: The translated coordinate. + :rtype: Coordinate2 + """ + t = CSTransform() + t.Translate((other.x, other.y, 0)) + return self.transform(t) + + def __sub__(self, other: Coordinate2) -> Coordinate2: + """ + Negative translation by another Coordinate2. + This does not modify the coordinate. + + :param other: The coordinate to translate by + :type other: Coordinate2 + :return: The translated coordinate. + :rtype: Coordinate2 + """ + t = CSTransform() + t.Translate((-other.x, -other.y, 0)) + return self.transform(t) + + def __mul__(self, scale: float | Coordinate2) -> Coordinate2: + """ + Scale by a specified value. + This does not modify the coordinate. + + :param scale: The scale factor. If a float, scale x and y by this value. + If a Coordinate2, use separate x and y scale factors. + :type scale: float | Coordinate2 + :return: The scaled coordinate. + :rtype: Coordinate2 + """ + if isinstance(scale, Coordinate2): + scale = scale.x, scale.y, 1 + t = CSTransform() + t.Scale(scale) + return self.transform(t) + + def __truediv__(self, scale: float | Coordinate2) -> Coordinate2: + """ + Scale but a specified value. This is equivalent to A * (1 / scale) + This does not modify the coordinate. + + :param scale: The scale factor. If a float, scale x and y by this value. + If a Coordinate2, use separate x and y scale factors. + :type scale: float | Coordinate2 + :return: The scaled coordinate. + :rtype: Coordinate2 + """ + if isinstance(scale, Coordinate2): + scale = 1/scale.x, 1/scale.y, 1 + else: + scale = 1/scale + t = CSTransform() + t.Scale(scale) + return self.transform(t) + + def __neg__(self) -> Coordinate2: + """ + Negative both coordinate values. + This does not modify the coordinate. + + :return: The negated coordinate + :rtype: Coordinate2 + """ + return Coordinate2(-self.x, -self.y) + + def __abs__(self) -> Coordinate2: + """ + Absolute value of both coodinate values. + This does not modify the coordinate. + :return: The new coordinate. + :rtype: Coordinate2 + """ + return Coordinate2(abs(self.x), abs(self.y)) + + def __round__(self, ndigits: int=0) -> Coordinate2: + """ + Round to a specified precision. Calls round_prec() + This does not modify the coordinate. + + :return: A new coordinate with the specified precision. + :rtype: Coordinate2 + """ + return self.round_prec(ndigits) + def _int_to_coord(self, val) -> float: - """""" if val == 0: return self.x elif val == 1: @@ -76,6 +162,9 @@ def coordinate_list(self) -> np.ndarray: """ Retrieve an array of coordinate values for use by openems, which requires coordinates in a form that can be indexed. + + :return: The x, y coordinates + :rtype: np.ndarray """ return np.array([self._x, self._y]) @@ -85,6 +174,11 @@ def transform(self, transform: CSTransform) -> Coordinate2: simply returns a new transformed coordinate. If you want to replace the old coordinate you must assign it to the result of this function. + + :param transform: The transformation + :type transform: CSTransform + :return: The new coordinate + :rtype: Coordinate2 """ clist = list(self.coordinate_list()) clist.append(0) @@ -92,10 +186,21 @@ def transform(self, transform: CSTransform) -> Coordinate2: return Coordinate2(tclist[0], tclist[1]) def round_prec(self, prec: int) -> Coordinate2: - """""" + """ + Round the coordinate to a specified precision. + This does not modify the coordinate. + + :param prec: The number of decimal places to round. + :type prec: int + :return: A new coordinate with the rounded values. + :rtype: Coordinate2 + """ clist = self.coordinate_list() clist = np.around(clist, prec) return Coordinate2(clist[0], clist[1]) + + def __repr__(self) -> str: + return f'Coordinate2({self.x}, {self.y})' def list_center2(coords: List[Coordinate2]) -> Coordinate2: @@ -189,6 +294,100 @@ def __eq__(self, other: Coordinate3): if self.x == other.x and self.y == other.y and self.z == other.z: return True return False + + def __add__(self, other: Coordinate3) -> Coordinate3: + """ + Positive translation by another Coordinate3. + This does not modify the coordinate. + + :param other: The coordinate to translate by + :type other: Coordinate3 + :return: The translated coordinate. + :rtype: Coordinate3 + """ + t = CSTransform() + t.Translate((other.x, other.y, other.z)) + return self.transform(t) + + def __sub__(self, other: Coordinate3) -> Coordinate3: + """ + Negative translation by another Coordinate3. + This does not modify the coordinate. + + :param other: The coordinate to translate by + :type other: Coordinate3 + :return: The translated coordinate. + :rtype: Coordinate3 + """ + t = CSTransform() + t.Translate((-other.x, -other.y, -other.z)) + return self.transform(t) + + def __mul__(self, scale: float | Coordinate3) -> Coordinate3: + """ + Scale by a specified value. + This does not modify the coordinate. + + :param scale: The scale factor. If a float, scale x and y by this value. + If a Coordinate3, use separate x, y, and z scale factors. + :type scale: float | Coordinate3 + :return: The scaled coordinate. + :rtype: Coordinate3 + """ + if isinstance(scale, Coordinate3): + scale = scale.x, scale.y, scale.z + t = CSTransform() + t.Scale(scale) + return self.transform(t) + + def __truediv__(self, scale: float | Coordinate3) -> Coordinate3: + """ + Scale but a specified value. This is equivalent to A * (1 / scale) + This does not modify the coordinate. + + :param scale: The scale factor. If a float, scale x and y by this value. + If a Coordinate3, use separate x, y, and z scale factors. + :type scale: float | Coordinate3 + :return: The scaled coordinate. + :rtype: Coordinate3 + """ + if isinstance(scale, Coordinate3): + scale = 1/scale.x, 1/scale.y, 1/scale.z + else: + scale = 1/scale + t = CSTransform() + t.Scale(scale) + return self.transform(t) + + def __neg__(self) -> Coordinate3: + """ + Negative both coordinate values. + This does not modify the coordinate. + + :return: The negated coordinate + :rtype: Coordinate3 + """ + return Coordinate3(-self.x, -self.y, -self.z) + + def __abs__(self) -> Coordinate3: + """ + Absolute value of both coodinate values. + This does not modify the coordinate. + + :return: The new coordinate. + :rtype: Coordinate3 + """ + return Coordinate3(abs(self.x), abs(self.y), abs(self.z)) + + def __round__(self, ndigits: int=0) -> Coordinate3: + """ + Round to a specified precision. Calls round_prec() + This does not modify the coordinate. + + :return: A new coordinate with the specified precision. + :rtype: Coordinate3 + """ + return self.round_prec(ndigits) def _int_to_coord(self, val): """""" @@ -200,6 +399,9 @@ def _int_to_coord(self, val): return self.z else: raise ValueError("Invalid index.") + + def __repr__(self) -> str: + return f'Coordinate3({self.x}, {self.y}, {self.z})' class Axis: @@ -355,6 +557,9 @@ def __init__(self, min_corner: C2Tuple, max_corner: C2Tuple): """""" self._min_corner = c2_maybe_tuple(min_corner) self._max_corner = c2_maybe_tuple(max_corner) + + def __repr__(self) -> str: + return f'Box2({self.min_corner}, {self.max_corner})' @property def min_corner(self) -> Coordinate2: @@ -439,6 +644,30 @@ def has_zero_dim(self) -> bool: ): return True return False + + def __add__(self, coord: Coordinate2) -> Box2: + """ + Translate by a specified coordinate. + This does not modify the box + + :param coord: The coordinate to transform + :type coord: Coordinate2 + :return: The translated box + :rtype: Box2 + """ + return Box2(self.min_corner + coord, self.max_corner + coord) + + def __sub__(self, coord: Coordinate2) -> Box2: + """ + Translate by a specified coordinate. + This does not modify the box + + :param coord: The coordinate to transform + :type coord: Coordinate2 + :return: The translated box + :rtype: Box2 + """ + return Box2(self.min_corner - coord, self.max_corner - coord) class Box3: @@ -618,6 +847,30 @@ def has_zero_dim(self) -> bool: ): return True return False + + def __add__(self, coord: Coordinate3) -> Box3: + """ + Translate by a specified coordinate. + This does not modify the box + + :param coord: The coordinate to transform + :type coord: Coordinate3 + :return: The translated box + :rtype: Box3 + """ + return Box3(self.min_corner + coord, self.max_corner + coord) + + def __sub__(self, coord: Coordinate3) -> Box3: + """ + Translate by a specified coordinate. + This does not modify the box + + :param coord: The coordinate to transform + :type coord: Coordinate3 + :return: The translated box + :rtype: Box3 + """ + return Box3(self.min_corner - coord, self.max_corner - coord) def box_overlap(box1: Box3, box2: Box3) -> bool: