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.

  1"""Shape object uses the Points class to store the coordinates of the points that make up the shape.
  2The Points class is a container for coordinates of multiple points.
  3It provides conversion to homogeneous coordinates in nd_arrays.
  4Shape.final_coords is computed by using the Points.homogen_coords property."""
  5
  6import copy
  7from typing import Sequence
  8
  9from numpy import allclose, ndarray
 10from typing_extensions import Self
 11
 12from ..geometry.geometry import homogenize
 13from .common import Point, common_properties
 14from .all_enums import *
 15from ..settings.settings import defaults
 16
 17
 18class Points:
 19    """Container for coordinates of multiple points. They provide conversion to homogeneous
 20    coordinates in nd_arrays. Used in Shape objects.
 21    """
 22
 23    def __init__(self, coords: Sequence[Point] = None) -> None:
 24        """Initialize a Points object.
 25
 26        Args:
 27            coords (Sequence[Point], optional): The coordinates of the points. Defaults to None.
 28        """
 29        # coords are a list of (x, y) values
 30        if coords is None:
 31            coords = []
 32        else:
 33            coords = [tuple(x) for x in coords]
 34        self.coords = coords
 35        if self.coords:
 36            self.nd_array = homogenize(coords)  # homogeneous coordinates
 37        self.type = Types.POINTS
 38        self.subtype = Types.POINTS
 39        self.dist_tol = defaults["dist_tol"]
 40        self.dist_tol2 = self.dist_tol**2
 41        common_properties(self, False)
 42
 43    def __str__(self):
 44        """Return a string representation of the points.
 45
 46        Returns:
 47            str: The string representation of the points.
 48        """
 49        return f"Points({self.coords})"
 50
 51    def __repr__(self):
 52        """Return a string representation of the points.
 53
 54        Returns:
 55            str: The string representation of the points.
 56        """
 57        return f"Points({self.coords})"
 58
 59    def __getitem__(self, subscript):
 60        """Get the point(s) at the given subscript.
 61
 62        Args:
 63            subscript (int or slice): The subscript to get the point(s) from.
 64
 65        Returns:
 66            Point or list[Point]: The point(s) at the given subscript.
 67
 68        Raises:
 69            TypeError: If the subscript type is invalid.
 70        """
 71        if isinstance(subscript, slice):
 72            res = self.coords[subscript.start : subscript.stop : subscript.step]
 73        elif isinstance(subscript, int):
 74            res = self.coords[subscript]
 75        else:
 76            raise TypeError("Invalid subscript type")
 77        return res
 78
 79    def _update_coords(self):
 80        """Update the homogeneous coordinates of the points."""
 81        self.nd_array = homogenize(self.coords)
 82
 83    def __setitem__(self, subscript, value):
 84        """Set the point(s) at the given subscript.
 85
 86        Args:
 87            subscript (int or slice): The subscript to set the point(s) at.
 88            value (Point or list[Point]): The value to set the point(s) to.
 89
 90        Raises:
 91            TypeError: If the subscript type is invalid.
 92        """
 93        if isinstance(subscript, slice):
 94            self.coords[subscript.start : subscript.stop : subscript.step] = value
 95            self._update_coords()
 96        elif isinstance(subscript, int):
 97            self.coords[subscript] = value
 98            self._update_coords()
 99        else:
100            raise TypeError("Invalid subscript type")
101
102    def __eq__(self, other):
103        """Check if the points are equal to another Points object.
104
105        Args:
106            other (Points): The other Points object to compare against.
107
108        Returns:
109            bool: True if the points are equal, False otherwise.
110        """
111        return (
112            other.type == Types.POINTS
113            and len(self.coords) == len(other.coords)
114            and allclose(
115                self.nd_array,
116                other.nd_array,
117                rtol=defaults["rtol"],
118                atol=defaults["atol"],
119            )
120        )
121
122    def append(self, item: Point) -> Self:
123        """Append a point to the points.
124
125        Args:
126            item (Point): The point to append.
127
128        Returns:
129            Self: The updated Points object.
130        """
131        self.coords.append(item)
132        self._update_coords()
133        return self
134
135    def extend(self, items: Sequence[Point]) -> Self:
136        """Extend the points with a given sequence of points.
137
138        Args:
139            items (Sequence[Point]): The sequence of points to add.
140
141        Returns:
142            Self: The updated Points object.
143        """
144        self.coords.extend(items)
145        self._update_coords()
146        return self
147
148    def pop(self, index: int = -1) -> Point:
149        """Remove the point at the given index and return it.
150
151        Args:
152            index (int, optional): The index of the point to remove. Defaults to -1.
153
154        Returns:
155            Point: The removed point.
156        """
157        value = self.coords.pop(index)
158        self._update_coords()
159        return value
160
161    def __delitem__(self, subscript) -> Self:
162        """Delete the point(s) at the given subscript.
163
164        Args:
165            subscript (int or slice): The subscript to delete the point(s) from.
166
167        Raises:
168            TypeError: If the subscript type is invalid.
169        """
170        coords = self.coords
171        if isinstance(subscript, slice):
172            del coords[subscript.start : subscript.stop : subscript.step]
173        elif isinstance(subscript, int):
174            del coords[subscript]
175        else:
176            raise TypeError("Invalid subscript type")
177        self._update_coords()
178
179    def remove(self, value):
180        """Remove the first occurrence of the given point.
181
182        Args:
183            value (Point): The point value to remove.
184        """
185        self.coords.remove(value)
186        self._update_coords()
187
188    def insert(self, index, points):
189        """Insert a point at the specified index.
190
191        Args:
192            index (int): The index to insert the point at.
193            points (Point): The point to insert.
194        """
195        self.coords.insert(index, points)
196        self._update_coords()
197
198    def clear(self):
199        """Clear all points."""
200        self.coords.clear()
201        self.nd_array = ndarray((0, 3))
202
203    def reverse(self):
204        """Reverse the order of the points."""
205        self.coords.reverse()
206        self._update_coords()
207
208    def __iter__(self):
209        """Return an iterator over the points.
210
211        Returns:
212            Iterator[Point]: An iterator over the points.
213        """
214        return iter(self.coords)
215
216    def __len__(self):
217        """Return the number of points.
218
219        Returns:
220            int: The number of points.
221        """
222        return len(self.coords)
223
224    def __bool__(self):
225        """Return whether the Points object has any points.
226
227        Returns:
228            bool: True if there are points, False otherwise.
229        """
230        return bool(self.coords)
231
232    @property
233    def homogen_coords(self):
234        """Return the homogeneous coordinates of the points.
235
236        Returns:
237            ndarray: The homogeneous coordinates.
238        """
239        return self.nd_array
240
241    def copy(self):
242        """Return a copy of the Points object.
243
244        Returns:
245            Points: A copy of the Points object.
246        """
247        points = Points(copy.copy(self.coords))
248        points.nd_array = ndarray.copy(self.nd_array)
249        return points
250
251    @property
252    def pairs(self):
253        """Return a list of consecutive pairs of points.
254
255        Returns:
256            list[tuple[Point, Point]]: A list where each element is a tuple containing two consecutive points.
257        """
258        return list(zip(self.coords[:-1], self.coords[1:]))
class Points:
 19class Points:
 20    """Container for coordinates of multiple points. They provide conversion to homogeneous
 21    coordinates in nd_arrays. Used in Shape objects.
 22    """
 23
 24    def __init__(self, coords: Sequence[Point] = None) -> None:
 25        """Initialize a Points object.
 26
 27        Args:
 28            coords (Sequence[Point], optional): The coordinates of the points. Defaults to None.
 29        """
 30        # coords are a list of (x, y) values
 31        if coords is None:
 32            coords = []
 33        else:
 34            coords = [tuple(x) for x in coords]
 35        self.coords = coords
 36        if self.coords:
 37            self.nd_array = homogenize(coords)  # homogeneous coordinates
 38        self.type = Types.POINTS
 39        self.subtype = Types.POINTS
 40        self.dist_tol = defaults["dist_tol"]
 41        self.dist_tol2 = self.dist_tol**2
 42        common_properties(self, False)
 43
 44    def __str__(self):
 45        """Return a string representation of the points.
 46
 47        Returns:
 48            str: The string representation of the points.
 49        """
 50        return f"Points({self.coords})"
 51
 52    def __repr__(self):
 53        """Return a string representation of the points.
 54
 55        Returns:
 56            str: The string representation of the points.
 57        """
 58        return f"Points({self.coords})"
 59
 60    def __getitem__(self, subscript):
 61        """Get the point(s) at the given subscript.
 62
 63        Args:
 64            subscript (int or slice): The subscript to get the point(s) from.
 65
 66        Returns:
 67            Point or list[Point]: The point(s) at the given subscript.
 68
 69        Raises:
 70            TypeError: If the subscript type is invalid.
 71        """
 72        if isinstance(subscript, slice):
 73            res = self.coords[subscript.start : subscript.stop : subscript.step]
 74        elif isinstance(subscript, int):
 75            res = self.coords[subscript]
 76        else:
 77            raise TypeError("Invalid subscript type")
 78        return res
 79
 80    def _update_coords(self):
 81        """Update the homogeneous coordinates of the points."""
 82        self.nd_array = homogenize(self.coords)
 83
 84    def __setitem__(self, subscript, value):
 85        """Set the point(s) at the given subscript.
 86
 87        Args:
 88            subscript (int or slice): The subscript to set the point(s) at.
 89            value (Point or list[Point]): The value to set the point(s) to.
 90
 91        Raises:
 92            TypeError: If the subscript type is invalid.
 93        """
 94        if isinstance(subscript, slice):
 95            self.coords[subscript.start : subscript.stop : subscript.step] = value
 96            self._update_coords()
 97        elif isinstance(subscript, int):
 98            self.coords[subscript] = value
 99            self._update_coords()
100        else:
101            raise TypeError("Invalid subscript type")
102
103    def __eq__(self, other):
104        """Check if the points are equal to another Points object.
105
106        Args:
107            other (Points): The other Points object to compare against.
108
109        Returns:
110            bool: True if the points are equal, False otherwise.
111        """
112        return (
113            other.type == Types.POINTS
114            and len(self.coords) == len(other.coords)
115            and allclose(
116                self.nd_array,
117                other.nd_array,
118                rtol=defaults["rtol"],
119                atol=defaults["atol"],
120            )
121        )
122
123    def append(self, item: Point) -> Self:
124        """Append a point to the points.
125
126        Args:
127            item (Point): The point to append.
128
129        Returns:
130            Self: The updated Points object.
131        """
132        self.coords.append(item)
133        self._update_coords()
134        return self
135
136    def extend(self, items: Sequence[Point]) -> Self:
137        """Extend the points with a given sequence of points.
138
139        Args:
140            items (Sequence[Point]): The sequence of points to add.
141
142        Returns:
143            Self: The updated Points object.
144        """
145        self.coords.extend(items)
146        self._update_coords()
147        return self
148
149    def pop(self, index: int = -1) -> Point:
150        """Remove the point at the given index and return it.
151
152        Args:
153            index (int, optional): The index of the point to remove. Defaults to -1.
154
155        Returns:
156            Point: The removed point.
157        """
158        value = self.coords.pop(index)
159        self._update_coords()
160        return value
161
162    def __delitem__(self, subscript) -> Self:
163        """Delete the point(s) at the given subscript.
164
165        Args:
166            subscript (int or slice): The subscript to delete the point(s) from.
167
168        Raises:
169            TypeError: If the subscript type is invalid.
170        """
171        coords = self.coords
172        if isinstance(subscript, slice):
173            del coords[subscript.start : subscript.stop : subscript.step]
174        elif isinstance(subscript, int):
175            del coords[subscript]
176        else:
177            raise TypeError("Invalid subscript type")
178        self._update_coords()
179
180    def remove(self, value):
181        """Remove the first occurrence of the given point.
182
183        Args:
184            value (Point): The point value to remove.
185        """
186        self.coords.remove(value)
187        self._update_coords()
188
189    def insert(self, index, points):
190        """Insert a point at the specified index.
191
192        Args:
193            index (int): The index to insert the point at.
194            points (Point): The point to insert.
195        """
196        self.coords.insert(index, points)
197        self._update_coords()
198
199    def clear(self):
200        """Clear all points."""
201        self.coords.clear()
202        self.nd_array = ndarray((0, 3))
203
204    def reverse(self):
205        """Reverse the order of the points."""
206        self.coords.reverse()
207        self._update_coords()
208
209    def __iter__(self):
210        """Return an iterator over the points.
211
212        Returns:
213            Iterator[Point]: An iterator over the points.
214        """
215        return iter(self.coords)
216
217    def __len__(self):
218        """Return the number of points.
219
220        Returns:
221            int: The number of points.
222        """
223        return len(self.coords)
224
225    def __bool__(self):
226        """Return whether the Points object has any points.
227
228        Returns:
229            bool: True if there are points, False otherwise.
230        """
231        return bool(self.coords)
232
233    @property
234    def homogen_coords(self):
235        """Return the homogeneous coordinates of the points.
236
237        Returns:
238            ndarray: The homogeneous coordinates.
239        """
240        return self.nd_array
241
242    def copy(self):
243        """Return a copy of the Points object.
244
245        Returns:
246            Points: A copy of the Points object.
247        """
248        points = Points(copy.copy(self.coords))
249        points.nd_array = ndarray.copy(self.nd_array)
250        return points
251
252    @property
253    def pairs(self):
254        """Return a list of consecutive pairs of points.
255
256        Returns:
257            list[tuple[Point, Point]]: A list where each element is a tuple containing two consecutive points.
258        """
259        return list(zip(self.coords[:-1], self.coords[1:]))

Container for coordinates of multiple points. They provide conversion to homogeneous coordinates in nd_arrays. Used in Shape objects.

Points(coords: Sequence[Sequence[float]] = None)
24    def __init__(self, coords: Sequence[Point] = None) -> None:
25        """Initialize a Points object.
26
27        Args:
28            coords (Sequence[Point], optional): The coordinates of the points. Defaults to None.
29        """
30        # coords are a list of (x, y) values
31        if coords is None:
32            coords = []
33        else:
34            coords = [tuple(x) for x in coords]
35        self.coords = coords
36        if self.coords:
37            self.nd_array = homogenize(coords)  # homogeneous coordinates
38        self.type = Types.POINTS
39        self.subtype = Types.POINTS
40        self.dist_tol = defaults["dist_tol"]
41        self.dist_tol2 = self.dist_tol**2
42        common_properties(self, False)

Initialize a Points object.

Arguments:
  • coords (Sequence[Point], optional): The coordinates of the points. Defaults to None.
coords
type
subtype
dist_tol
dist_tol2
def append(self, item: Sequence[float]) -> typing_extensions.Self:
123    def append(self, item: Point) -> Self:
124        """Append a point to the points.
125
126        Args:
127            item (Point): The point to append.
128
129        Returns:
130            Self: The updated Points object.
131        """
132        self.coords.append(item)
133        self._update_coords()
134        return self

Append a point to the points.

Arguments:
  • item (Point): The point to append.
Returns:

Self: The updated Points object.

def extend(self, items: Sequence[Sequence[float]]) -> typing_extensions.Self:
136    def extend(self, items: Sequence[Point]) -> Self:
137        """Extend the points with a given sequence of points.
138
139        Args:
140            items (Sequence[Point]): The sequence of points to add.
141
142        Returns:
143            Self: The updated Points object.
144        """
145        self.coords.extend(items)
146        self._update_coords()
147        return self

Extend the points with a given sequence of points.

Arguments:
  • items (Sequence[Point]): The sequence of points to add.
Returns:

Self: The updated Points object.

def pop(self, index: int = -1) -> Sequence[float]:
149    def pop(self, index: int = -1) -> Point:
150        """Remove the point at the given index and return it.
151
152        Args:
153            index (int, optional): The index of the point to remove. Defaults to -1.
154
155        Returns:
156            Point: The removed point.
157        """
158        value = self.coords.pop(index)
159        self._update_coords()
160        return value

Remove the point at the given index and return it.

Arguments:
  • index (int, optional): The index of the point to remove. Defaults to -1.
Returns:

Point: The removed point.

def remove(self, value):
180    def remove(self, value):
181        """Remove the first occurrence of the given point.
182
183        Args:
184            value (Point): The point value to remove.
185        """
186        self.coords.remove(value)
187        self._update_coords()

Remove the first occurrence of the given point.

Arguments:
  • value (Point): The point value to remove.
def insert(self, index, points):
189    def insert(self, index, points):
190        """Insert a point at the specified index.
191
192        Args:
193            index (int): The index to insert the point at.
194            points (Point): The point to insert.
195        """
196        self.coords.insert(index, points)
197        self._update_coords()

Insert a point at the specified index.

Arguments:
  • index (int): The index to insert the point at.
  • points (Point): The point to insert.
def clear(self):
199    def clear(self):
200        """Clear all points."""
201        self.coords.clear()
202        self.nd_array = ndarray((0, 3))

Clear all points.

def reverse(self):
204    def reverse(self):
205        """Reverse the order of the points."""
206        self.coords.reverse()
207        self._update_coords()

Reverse the order of the points.

homogen_coords
233    @property
234    def homogen_coords(self):
235        """Return the homogeneous coordinates of the points.
236
237        Returns:
238            ndarray: The homogeneous coordinates.
239        """
240        return self.nd_array

Return the homogeneous coordinates of the points.

Returns:

ndarray: The homogeneous coordinates.

def copy(self):
242    def copy(self):
243        """Return a copy of the Points object.
244
245        Returns:
246            Points: A copy of the Points object.
247        """
248        points = Points(copy.copy(self.coords))
249        points.nd_array = ndarray.copy(self.nd_array)
250        return points

Return a copy of the Points object.

Returns:

Points: A copy of the Points object.

pairs
252    @property
253    def pairs(self):
254        """Return a list of consecutive pairs of points.
255
256        Returns:
257            list[tuple[Point, Point]]: A list where each element is a tuple containing two consecutive points.
258        """
259        return list(zip(self.coords[:-1], self.coords[1:]))

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.