Source code for simetri.graphics.points

"""Shape object uses the Points class to store the coordinates of the points that make up the shape.
The Points class is a container for coordinates of multiple points.
It provides conversion to homogeneous coordinates in nd_arrays.
Shape.final_coords is computed by using the Points.homogen_coords property."""

import copy
from typing import Sequence

from numpy import allclose, ndarray
from typing_extensions import Self

from ..geometry.geometry import homogenize
from .common import Point, common_properties
from .all_enums import *
from ..settings.settings import defaults


[docs] class Points: """Container for coordinates of multiple points. They provide conversion to homogeneous coordinates in nd_arrays. Used in Shape objects. """ def __init__(self, coords: Sequence[Point] = None) -> None: """Initialize a Points object. Args: coords (Sequence[Point], optional): The coordinates of the points. Defaults to None. """ # coords are a list of (x, y) values if coords is None: coords = [] else: coords = [tuple(x) for x in coords] self.coords = coords if self.coords: self.nd_array = homogenize(coords) # homogeneous coordinates self.type = Types.POINTS self.subtype = Types.POINTS self.dist_tol = defaults["dist_tol"] self.dist_tol2 = self.dist_tol**2 common_properties(self, False) def __str__(self): """Return a string representation of the points. Returns: str: The string representation of the points. """ return f"Points({self.coords})" def __repr__(self): """Return a string representation of the points. Returns: str: The string representation of the points. """ return f"Points({self.coords})" def __getitem__(self, subscript): """Get the point(s) at the given subscript. Args: subscript (int or slice): The subscript to get the point(s) from. Returns: Point or list[Point]: The point(s) at the given subscript. Raises: TypeError: If the subscript type is invalid. """ if isinstance(subscript, slice): res = self.coords[subscript.start : subscript.stop : subscript.step] elif isinstance(subscript, int): res = self.coords[subscript] else: raise TypeError("Invalid subscript type") return res def _update_coords(self): """Update the homogeneous coordinates of the points.""" self.nd_array = homogenize(self.coords) def __setitem__(self, subscript, value): """Set the point(s) at the given subscript. Args: subscript (int or slice): The subscript to set the point(s) at. value (Point or list[Point]): The value to set the point(s) to. Raises: TypeError: If the subscript type is invalid. """ if isinstance(subscript, slice): self.coords[subscript.start : subscript.stop : subscript.step] = value self._update_coords() elif isinstance(subscript, int): self.coords[subscript] = value self._update_coords() else: raise TypeError("Invalid subscript type") def __eq__(self, other): """Check if the points are equal to another Points object. Args: other (Points): The other Points object to compare against. Returns: bool: True if the points are equal, False otherwise. """ return ( other.type == Types.POINTS and len(self.coords) == len(other.coords) and allclose( self.nd_array, other.nd_array, rtol=defaults["rtol"], atol=defaults["atol"], ) )
[docs] def append(self, item: Point) -> Self: """Append a point to the points. Args: item (Point): The point to append. Returns: Self: The updated Points object. """ self.coords.append(item) self._update_coords() return self
[docs] def extend(self, items: Sequence[Point]) -> Self: """Extend the points with a given sequence of points. Args: items (Sequence[Point]): The sequence of points to add. Returns: Self: The updated Points object. """ self.coords.extend(items) self._update_coords() return self
[docs] def pop(self, index: int = -1) -> Point: """Remove the point at the given index and return it. Args: index (int, optional): The index of the point to remove. Defaults to -1. Returns: Point: The removed point. """ value = self.coords.pop(index) self._update_coords() return value
def __delitem__(self, subscript) -> Self: """Delete the point(s) at the given subscript. Args: subscript (int or slice): The subscript to delete the point(s) from. Raises: TypeError: If the subscript type is invalid. """ coords = self.coords if isinstance(subscript, slice): del coords[subscript.start : subscript.stop : subscript.step] elif isinstance(subscript, int): del coords[subscript] else: raise TypeError("Invalid subscript type") self._update_coords()
[docs] def remove(self, value): """Remove the first occurrence of the given point. Args: value (Point): The point value to remove. """ self.coords.remove(value) self._update_coords()
[docs] def insert(self, index, points): """Insert a point at the specified index. Args: index (int): The index to insert the point at. points (Point): The point to insert. """ self.coords.insert(index, points) self._update_coords()
[docs] def clear(self): """Clear all points.""" self.coords.clear() self.nd_array = ndarray((0, 3))
[docs] def reverse(self): """Reverse the order of the points.""" self.coords.reverse() self._update_coords()
def __iter__(self): """Return an iterator over the points. Returns: Iterator[Point]: An iterator over the points. """ return iter(self.coords) def __len__(self): """Return the number of points. Returns: int: The number of points. """ return len(self.coords) def __bool__(self): """Return whether the Points object has any points. Returns: bool: True if there are points, False otherwise. """ return bool(self.coords) @property def homogen_coords(self): """Return the homogeneous coordinates of the points. Returns: ndarray: The homogeneous coordinates. """ return self.nd_array
[docs] def copy(self): """Return a copy of the Points object. Returns: Points: A copy of the Points object. """ points = Points(copy.copy(self.coords)) points.nd_array = ndarray.copy(self.nd_array) return points
@property def pairs(self): """Return a list of consecutive pairs of points. Returns: list[tuple[Point, Point]]: A list where each element is a tuple containing two consecutive points. """ return list(zip(self.coords[:-1], self.coords[1:]))