"""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."""importcopyfromtypingimportSequencefromnumpyimportallclose,ndarrayfromtyping_extensionsimportSelffrom..geometry.geometryimporthomogenizefrom.commonimportPoint,common_propertiesfrom.all_enumsimport*from..settings.settingsimportdefaults
[docs]classPoints:"""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) valuesifcoordsisNone:coords=[]else:coords=[tuple(x)forxincoords]self.coords=coordsifself.coords:self.nd_array=homogenize(coords)# homogeneous coordinatesself.type=Types.POINTSself.subtype=Types.POINTSself.dist_tol=defaults["dist_tol"]self.dist_tol2=self.dist_tol**2common_properties(self,False)def__str__(self):"""Return a string representation of the points. Returns: str: The string representation of the points. """returnf"Points({self.coords})"def__repr__(self):"""Return a string representation of the points. Returns: str: The string representation of the points. """returnf"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. """ifisinstance(subscript,slice):res=self.coords[subscript.start:subscript.stop:subscript.step]elifisinstance(subscript,int):res=self.coords[subscript]else:raiseTypeError("Invalid subscript type")returnresdef_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. """ifisinstance(subscript,slice):self.coords[subscript.start:subscript.stop:subscript.step]=valueself._update_coords()elifisinstance(subscript,int):self.coords[subscript]=valueself._update_coords()else:raiseTypeError("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.POINTSandlen(self.coords)==len(other.coords)andallclose(self.nd_array,other.nd_array,rtol=defaults["rtol"],atol=defaults["atol"],))
[docs]defappend(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()returnself
[docs]defextend(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()returnself
[docs]defpop(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()returnvalue
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.coordsifisinstance(subscript,slice):delcoords[subscript.start:subscript.stop:subscript.step]elifisinstance(subscript,int):delcoords[subscript]else:raiseTypeError("Invalid subscript type")self._update_coords()
[docs]defremove(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]definsert(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]defclear(self):"""Clear all points."""self.coords.clear()self.nd_array=ndarray((0,3))
[docs]defreverse(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. """returniter(self.coords)def__len__(self):"""Return the number of points. Returns: int: The number of points. """returnlen(self.coords)def__bool__(self):"""Return whether the Points object has any points. Returns: bool: True if there are points, False otherwise. """returnbool(self.coords)@propertydefhomogen_coords(self):"""Return the homogeneous coordinates of the points. Returns: ndarray: The homogeneous coordinates. """returnself.nd_array
[docs]defcopy(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)returnpoints
@propertydefpairs(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. """returnlist(zip(self.coords[:-1],self.coords[1:]))