sketchingpy.shape_struct

Data structures describing shapes.

License:

BSD

  1"""Data structures describing shapes.
  2
  3License:
  4    BSD
  5"""
  6
  7import typing
  8
  9
 10class Line:
 11    """Data structure describing a segment.
 12
 13    Data structure describing a segment which can be a straight line but also a curve like a bezier
 14    curve.
 15    """
 16
 17    def get_destination_x(self) -> float:
 18        """Get the ending horizontal coordinate of this segment.
 19
 20        Returns:
 21            The x coordinate that this segment ends on.
 22        """
 23        raise NotImplementedError('Use implementor.')
 24
 25    def get_destination_y(self) -> float:
 26        """Get the ending vertical coordinate of this segment.
 27
 28        Returns:
 29            The y coordinate that this segment ends on.
 30        """
 31        raise NotImplementedError('Use implementor.')
 32
 33    def get_min_x(self) -> float:
 34        """Get the minimum of the x coordinates in this segment.
 35
 36        Returns:
 37            Minimum x coordinate of this segment.
 38        """
 39        raise NotImplementedError('Use impelentor.')
 40
 41    def get_max_x(self) -> float:
 42        """Get the maximum of the x coordinates in this segment.
 43
 44        Returns:
 45            Maximum x coordinate of this segment.
 46        """
 47        raise NotImplementedError('Use impelentor.')
 48
 49    def get_min_y(self) -> float:
 50        """Get the minimum of the y coordinates in this segment.
 51
 52        Returns:
 53            Minimum y coordinate of this segment.
 54        """
 55        raise NotImplementedError('Use impelentor.')
 56
 57    def get_max_y(self) -> float:
 58        """Get the maximum of the y coordinates in this segment.
 59
 60        Returns:
 61            Maximum y coordinate of this segment.
 62        """
 63        raise NotImplementedError('Use impelentor.')
 64
 65    def get_strategy(self) -> str:
 66        """Get the type of line that this segment represents.
 67
 68        Returns:
 69            Line strategy like "straight" or "bezier" as a string.
 70        """
 71        raise NotImplementedError('Use implementor.')
 72
 73    def get_control_x1(self):
 74        """Get the horizontal coordinate of the first control point.
 75
 76        Returns:
 77            The x coordinate of the first control point.
 78        """
 79        raise NotImplementedError('Not supported by strategy.')
 80
 81    def get_control_y1(self):
 82        """Get the vertical coordinate of the first control point.
 83
 84        Returns:
 85            The y coordinate of the first control point.
 86        """
 87        raise NotImplementedError('Not supported by strategy.')
 88
 89    def get_control_x2(self):
 90        """Get the horizontal coordinate of the second control point.
 91
 92        Returns:
 93            The x coordinate of the second control point.
 94        """
 95        raise NotImplementedError('Not supported by strategy.')
 96
 97    def get_control_y2(self):
 98        """Get the vertical coordinate of the second control point.
 99
100        Returns:
101            The y coordinate of the second control point.
102        """
103        raise NotImplementedError('Not supported by strategy.')
104
105
106class StraightLine(Line):
107    """A segment which is a straight line between two points."""
108
109    def __init__(self, destination_x: float, destination_y: float):
110        """Create a new straight line segmenet.
111
112        Args:
113            destination_x: The vertical location of the end coordinate.
114            destination_y: The horizontal location of the end coordinate.
115        """
116        self._destination_x = destination_x
117        self._destination_y = destination_y
118
119    def get_destination_x(self) -> float:
120        return self._destination_x
121
122    def get_destination_y(self) -> float:
123        return self._destination_y
124
125    def get_min_x(self) -> float:
126        return self._destination_x
127
128    def get_max_x(self) -> float:
129        return self._destination_x
130
131    def get_min_y(self) -> float:
132        return self._destination_y
133
134    def get_max_y(self) -> float:
135        return self._destination_y
136
137    def get_strategy(self) -> str:
138        return 'straight'
139
140
141class BezierLine(Line):
142    """A segment which is a bezier curve between two points."""
143
144    def __init__(self, control_x1: float, control_y1: float, control_x2: float, control_y2: float,
145        destination_x: float, destination_y: float):
146        """Create a new bezier curve.
147
148        Args:
149            control_x1: The horizontal location of the first control point.
150            control_y1: The vertical location of the first control point.
151            control_x2: The horizontal location of the second control point.
152            control_y2: The vertical location of the second control point.
153            destination_x: The vertical location of the end coordinate.
154            destination_y: The horizontal location of the end coordinate.
155        """
156        self._control_x1 = control_x1
157        self._control_y1 = control_y1
158        self._control_x2 = control_x2
159        self._control_y2 = control_y2
160        self._destination_x = destination_x
161        self._destination_y = destination_y
162
163    def get_control_x1(self):
164        return self._control_x1
165
166    def get_control_y1(self):
167        return self._control_y1
168
169    def get_control_x2(self):
170        return self._control_x2
171
172    def get_control_y2(self):
173        return self._control_y2
174
175    def get_destination_x(self):
176        return self._destination_x
177
178    def get_destination_y(self):
179        return self._destination_y
180
181    def get_min_x(self) -> float:
182        return min([
183            self._control_x1,
184            self._control_x2,
185            self._destination_x
186        ])
187
188    def get_max_x(self) -> float:
189        return max([
190            self._control_x1,
191            self._control_x2,
192            self._destination_x
193        ])
194
195    def get_min_y(self) -> float:
196        return min([
197            self._control_y1,
198            self._control_y2,
199            self._destination_y
200        ])
201
202    def get_max_y(self) -> float:
203        return max([
204            self._control_y1,
205            self._control_y2,
206            self._destination_y
207        ])
208
209    def get_strategy(self) -> str:
210        return 'bezier'
211
212
213class Shape:
214    """Structure describing a multi-segement shape."""
215
216    def __init__(self, start_x: float, start_y: float):
217        """Create a new multi-segment shape.
218
219        Args:
220            start_x: The starting x position of the shape.
221            start_y: The starting y position of the shape.
222        """
223        self._start_x = start_x
224        self._start_y = start_y
225        self._closed = False
226        self._finished = False
227        self._segments: typing.List[Line] = []
228
229    def add_line_to(self, x: float, y: float):
230        """Add a straight line to a shape.
231
232        Draw a straight line from the current position to a new destination which is used as the
233        next "current" position.
234
235        Args:
236            x: The x coordinate to which the line should be drawn.
237            y: The y coordinate to which the line should be drawn.
238        """
239        self._assert_not_finished()
240        self._segments.append(StraightLine(x, y))
241
242    def add_bezier_to(self, control_x1: float, control_y1: float, control_x2: float,
243        control_y2: float, destination_x: float, destination_y: float):
244        """Add a bezier curve to a shape.
245
246        Draw a bezier curve from the current position to a new destination which is used as the next
247        "current" position.
248
249        Args:
250            control_x1: The horizontal location of the first control point.
251            control_y1: The vertical location of the first control point.
252            control_x2: The horizontal location of the second control point.
253            control_y2: The vertical location of the second control point.
254            destination_x: The vertical location of the end coordinate.
255            destination_y: The horizontal location of the end coordinate.
256        """
257        self._assert_not_finished()
258        self._segments.append(BezierLine(
259            control_x1,
260            control_y1,
261            control_x2,
262            control_y2,
263            destination_x,
264            destination_y
265        ))
266
267    def get_start_x(self) -> float:
268        """Retrieve the first x coordinate of this shape.
269
270        Get the horizontal coordinate from which the first segment draws.
271
272        Returns:
273            The starting x coordinate.
274        """
275        return self._start_x
276
277    def get_start_y(self) -> float:
278        """Retrieve the first y coordinate of this shape.
279
280        Get the vertical coordinate from which the first segment draws.
281
282        Returns:
283            The starting y coordinate.
284        """
285        return self._start_y
286
287    def get_segments(self) -> typing.Iterable[Line]:
288        """Retrieve shape segments.
289
290        Retrieve objects describing each of the segments in a shape.
291
292        Returns:
293            Segements in this shape.
294        """
295        return self._segments
296
297    def get_is_finished(self) -> bool:
298        """Determine if a shape is finished.
299
300        Determine if the shape is finished so can be drawn. Returns true if finished (can be drawn)
301        and false otherwise (still building). A shape cannot be drawn until it is finished which can
302        be accomplished by either calling end or close.
303
304        Returns:
305            True if finished and false otherwise.
306        """
307        return self._finished
308
309    def end(self):
310        """Draw a shape.
311
312        Draw a shape which consists of multiple line or curve segments and which can be either open
313        (stroke only) or closed (can be filled).
314        """
315        self._assert_not_finished()
316        self._finished = True
317        self._closed = False
318
319    def close(self):
320        """Add a straight line to the starting coordinate.
321
322        Add a line in the shape from the current position to the start position, marking the shape
323        as finished and closed which allows it to be filled.
324        """
325        self._assert_not_finished()
326        self._finished = True
327        self._closed = True
328
329    def get_is_closed(self) -> bool:
330        """Determine if a shape can be filled.
331
332        Determine if the shape is closed so can be filled. Returns true if closed (can be filled)
333        and false otherwise.
334
335        Returns:
336            True if closed and false otherwise.
337        """
338        self._assert_finished()
339        return self._closed
340
341    def get_min_x(self):
342        """Determine the minimum x coordinate of a shape.
343
344        Determine the minimum x coordinate (relative to start position) that this shape may reach
345        to. This includes bezier control points but does not try to include stroke weight in its
346        calculation.
347
348        Returns:
349            Minimum x coordinate.
350        """
351        self._assert_finished()
352        return min([self._start_x] + [x.get_min_x() for x in self._segments])
353
354    def get_max_x(self):
355        """Determine the maximum x coordinate of a shape.
356
357        Determine the maximum x coordinate (relative to start position) that this shape may reach
358        to. This includes bezier control points but does not try to include stroke weight in its
359        calculation.
360
361        Returns:
362            Maximum x coordinate.
363        """
364        self._assert_finished()
365        return max([self._start_x] + [x.get_max_x() for x in self._segments])
366
367    def get_min_y(self):
368        """Determine the minimum y coordinate of a shape.
369
370        Determine the minimum y coordinate (relative to start position) that this shape may reach
371        to. This includes bezier control points but does not try to include stroke weight in its
372        calculation.
373
374        Returns:
375            Minimum y coordinate.
376        """
377        self._assert_finished()
378        return min([self._start_y] + [x.get_min_y() for x in self._segments])
379
380    def get_max_y(self):
381        """Determine the maximum y coordinate of a shape.
382
383        Determine the maximum y coordinate (relative to start position) that this shape may reach
384        to. This includes bezier control points but does not try to include stroke weight in its
385        calculation.
386
387        Returns:
388            Maximum y coordinate.
389        """
390        self._assert_finished()
391        return max([self._start_y] + [x.get_max_y() for x in self._segments])
392
393    def _assert_not_finished(self):
394        if self._finished:
395            raise RuntimeError('Whoops! This shape is already finished.')
396
397    def _assert_finished(self):
398        if not self._finished:
399            raise RuntimeError('Whoops! This shape is not yet finished.')
class Line:
 11class Line:
 12    """Data structure describing a segment.
 13
 14    Data structure describing a segment which can be a straight line but also a curve like a bezier
 15    curve.
 16    """
 17
 18    def get_destination_x(self) -> float:
 19        """Get the ending horizontal coordinate of this segment.
 20
 21        Returns:
 22            The x coordinate that this segment ends on.
 23        """
 24        raise NotImplementedError('Use implementor.')
 25
 26    def get_destination_y(self) -> float:
 27        """Get the ending vertical coordinate of this segment.
 28
 29        Returns:
 30            The y coordinate that this segment ends on.
 31        """
 32        raise NotImplementedError('Use implementor.')
 33
 34    def get_min_x(self) -> float:
 35        """Get the minimum of the x coordinates in this segment.
 36
 37        Returns:
 38            Minimum x coordinate of this segment.
 39        """
 40        raise NotImplementedError('Use impelentor.')
 41
 42    def get_max_x(self) -> float:
 43        """Get the maximum of the x coordinates in this segment.
 44
 45        Returns:
 46            Maximum x coordinate of this segment.
 47        """
 48        raise NotImplementedError('Use impelentor.')
 49
 50    def get_min_y(self) -> float:
 51        """Get the minimum of the y coordinates in this segment.
 52
 53        Returns:
 54            Minimum y coordinate of this segment.
 55        """
 56        raise NotImplementedError('Use impelentor.')
 57
 58    def get_max_y(self) -> float:
 59        """Get the maximum of the y coordinates in this segment.
 60
 61        Returns:
 62            Maximum y coordinate of this segment.
 63        """
 64        raise NotImplementedError('Use impelentor.')
 65
 66    def get_strategy(self) -> str:
 67        """Get the type of line that this segment represents.
 68
 69        Returns:
 70            Line strategy like "straight" or "bezier" as a string.
 71        """
 72        raise NotImplementedError('Use implementor.')
 73
 74    def get_control_x1(self):
 75        """Get the horizontal coordinate of the first control point.
 76
 77        Returns:
 78            The x coordinate of the first control point.
 79        """
 80        raise NotImplementedError('Not supported by strategy.')
 81
 82    def get_control_y1(self):
 83        """Get the vertical coordinate of the first control point.
 84
 85        Returns:
 86            The y coordinate of the first control point.
 87        """
 88        raise NotImplementedError('Not supported by strategy.')
 89
 90    def get_control_x2(self):
 91        """Get the horizontal coordinate of the second control point.
 92
 93        Returns:
 94            The x coordinate of the second control point.
 95        """
 96        raise NotImplementedError('Not supported by strategy.')
 97
 98    def get_control_y2(self):
 99        """Get the vertical coordinate of the second control point.
100
101        Returns:
102            The y coordinate of the second control point.
103        """
104        raise NotImplementedError('Not supported by strategy.')

Data structure describing a segment.

Data structure describing a segment which can be a straight line but also a curve like a bezier curve.

def get_destination_x(self) -> float:
18    def get_destination_x(self) -> float:
19        """Get the ending horizontal coordinate of this segment.
20
21        Returns:
22            The x coordinate that this segment ends on.
23        """
24        raise NotImplementedError('Use implementor.')

Get the ending horizontal coordinate of this segment.

Returns:

The x coordinate that this segment ends on.

def get_destination_y(self) -> float:
26    def get_destination_y(self) -> float:
27        """Get the ending vertical coordinate of this segment.
28
29        Returns:
30            The y coordinate that this segment ends on.
31        """
32        raise NotImplementedError('Use implementor.')

Get the ending vertical coordinate of this segment.

Returns:

The y coordinate that this segment ends on.

def get_min_x(self) -> float:
34    def get_min_x(self) -> float:
35        """Get the minimum of the x coordinates in this segment.
36
37        Returns:
38            Minimum x coordinate of this segment.
39        """
40        raise NotImplementedError('Use impelentor.')

Get the minimum of the x coordinates in this segment.

Returns:

Minimum x coordinate of this segment.

def get_max_x(self) -> float:
42    def get_max_x(self) -> float:
43        """Get the maximum of the x coordinates in this segment.
44
45        Returns:
46            Maximum x coordinate of this segment.
47        """
48        raise NotImplementedError('Use impelentor.')

Get the maximum of the x coordinates in this segment.

Returns:

Maximum x coordinate of this segment.

def get_min_y(self) -> float:
50    def get_min_y(self) -> float:
51        """Get the minimum of the y coordinates in this segment.
52
53        Returns:
54            Minimum y coordinate of this segment.
55        """
56        raise NotImplementedError('Use impelentor.')

Get the minimum of the y coordinates in this segment.

Returns:

Minimum y coordinate of this segment.

def get_max_y(self) -> float:
58    def get_max_y(self) -> float:
59        """Get the maximum of the y coordinates in this segment.
60
61        Returns:
62            Maximum y coordinate of this segment.
63        """
64        raise NotImplementedError('Use impelentor.')

Get the maximum of the y coordinates in this segment.

Returns:

Maximum y coordinate of this segment.

def get_strategy(self) -> str:
66    def get_strategy(self) -> str:
67        """Get the type of line that this segment represents.
68
69        Returns:
70            Line strategy like "straight" or "bezier" as a string.
71        """
72        raise NotImplementedError('Use implementor.')

Get the type of line that this segment represents.

Returns:

Line strategy like "straight" or "bezier" as a string.

def get_control_x1(self):
74    def get_control_x1(self):
75        """Get the horizontal coordinate of the first control point.
76
77        Returns:
78            The x coordinate of the first control point.
79        """
80        raise NotImplementedError('Not supported by strategy.')

Get the horizontal coordinate of the first control point.

Returns:

The x coordinate of the first control point.

def get_control_y1(self):
82    def get_control_y1(self):
83        """Get the vertical coordinate of the first control point.
84
85        Returns:
86            The y coordinate of the first control point.
87        """
88        raise NotImplementedError('Not supported by strategy.')

Get the vertical coordinate of the first control point.

Returns:

The y coordinate of the first control point.

def get_control_x2(self):
90    def get_control_x2(self):
91        """Get the horizontal coordinate of the second control point.
92
93        Returns:
94            The x coordinate of the second control point.
95        """
96        raise NotImplementedError('Not supported by strategy.')

Get the horizontal coordinate of the second control point.

Returns:

The x coordinate of the second control point.

def get_control_y2(self):
 98    def get_control_y2(self):
 99        """Get the vertical coordinate of the second control point.
100
101        Returns:
102            The y coordinate of the second control point.
103        """
104        raise NotImplementedError('Not supported by strategy.')

Get the vertical coordinate of the second control point.

Returns:

The y coordinate of the second control point.

class StraightLine(Line):
107class StraightLine(Line):
108    """A segment which is a straight line between two points."""
109
110    def __init__(self, destination_x: float, destination_y: float):
111        """Create a new straight line segmenet.
112
113        Args:
114            destination_x: The vertical location of the end coordinate.
115            destination_y: The horizontal location of the end coordinate.
116        """
117        self._destination_x = destination_x
118        self._destination_y = destination_y
119
120    def get_destination_x(self) -> float:
121        return self._destination_x
122
123    def get_destination_y(self) -> float:
124        return self._destination_y
125
126    def get_min_x(self) -> float:
127        return self._destination_x
128
129    def get_max_x(self) -> float:
130        return self._destination_x
131
132    def get_min_y(self) -> float:
133        return self._destination_y
134
135    def get_max_y(self) -> float:
136        return self._destination_y
137
138    def get_strategy(self) -> str:
139        return 'straight'

A segment which is a straight line between two points.

StraightLine(destination_x: float, destination_y: float)
110    def __init__(self, destination_x: float, destination_y: float):
111        """Create a new straight line segmenet.
112
113        Args:
114            destination_x: The vertical location of the end coordinate.
115            destination_y: The horizontal location of the end coordinate.
116        """
117        self._destination_x = destination_x
118        self._destination_y = destination_y

Create a new straight line segmenet.

Arguments:
  • destination_x: The vertical location of the end coordinate.
  • destination_y: The horizontal location of the end coordinate.
def get_destination_x(self) -> float:
120    def get_destination_x(self) -> float:
121        return self._destination_x

Get the ending horizontal coordinate of this segment.

Returns:

The x coordinate that this segment ends on.

def get_destination_y(self) -> float:
123    def get_destination_y(self) -> float:
124        return self._destination_y

Get the ending vertical coordinate of this segment.

Returns:

The y coordinate that this segment ends on.

def get_min_x(self) -> float:
126    def get_min_x(self) -> float:
127        return self._destination_x

Get the minimum of the x coordinates in this segment.

Returns:

Minimum x coordinate of this segment.

def get_max_x(self) -> float:
129    def get_max_x(self) -> float:
130        return self._destination_x

Get the maximum of the x coordinates in this segment.

Returns:

Maximum x coordinate of this segment.

def get_min_y(self) -> float:
132    def get_min_y(self) -> float:
133        return self._destination_y

Get the minimum of the y coordinates in this segment.

Returns:

Minimum y coordinate of this segment.

def get_max_y(self) -> float:
135    def get_max_y(self) -> float:
136        return self._destination_y

Get the maximum of the y coordinates in this segment.

Returns:

Maximum y coordinate of this segment.

def get_strategy(self) -> str:
138    def get_strategy(self) -> str:
139        return 'straight'

Get the type of line that this segment represents.

Returns:

Line strategy like "straight" or "bezier" as a string.

class BezierLine(Line):
142class BezierLine(Line):
143    """A segment which is a bezier curve between two points."""
144
145    def __init__(self, control_x1: float, control_y1: float, control_x2: float, control_y2: float,
146        destination_x: float, destination_y: float):
147        """Create a new bezier curve.
148
149        Args:
150            control_x1: The horizontal location of the first control point.
151            control_y1: The vertical location of the first control point.
152            control_x2: The horizontal location of the second control point.
153            control_y2: The vertical location of the second control point.
154            destination_x: The vertical location of the end coordinate.
155            destination_y: The horizontal location of the end coordinate.
156        """
157        self._control_x1 = control_x1
158        self._control_y1 = control_y1
159        self._control_x2 = control_x2
160        self._control_y2 = control_y2
161        self._destination_x = destination_x
162        self._destination_y = destination_y
163
164    def get_control_x1(self):
165        return self._control_x1
166
167    def get_control_y1(self):
168        return self._control_y1
169
170    def get_control_x2(self):
171        return self._control_x2
172
173    def get_control_y2(self):
174        return self._control_y2
175
176    def get_destination_x(self):
177        return self._destination_x
178
179    def get_destination_y(self):
180        return self._destination_y
181
182    def get_min_x(self) -> float:
183        return min([
184            self._control_x1,
185            self._control_x2,
186            self._destination_x
187        ])
188
189    def get_max_x(self) -> float:
190        return max([
191            self._control_x1,
192            self._control_x2,
193            self._destination_x
194        ])
195
196    def get_min_y(self) -> float:
197        return min([
198            self._control_y1,
199            self._control_y2,
200            self._destination_y
201        ])
202
203    def get_max_y(self) -> float:
204        return max([
205            self._control_y1,
206            self._control_y2,
207            self._destination_y
208        ])
209
210    def get_strategy(self) -> str:
211        return 'bezier'

A segment which is a bezier curve between two points.

BezierLine( control_x1: float, control_y1: float, control_x2: float, control_y2: float, destination_x: float, destination_y: float)
145    def __init__(self, control_x1: float, control_y1: float, control_x2: float, control_y2: float,
146        destination_x: float, destination_y: float):
147        """Create a new bezier curve.
148
149        Args:
150            control_x1: The horizontal location of the first control point.
151            control_y1: The vertical location of the first control point.
152            control_x2: The horizontal location of the second control point.
153            control_y2: The vertical location of the second control point.
154            destination_x: The vertical location of the end coordinate.
155            destination_y: The horizontal location of the end coordinate.
156        """
157        self._control_x1 = control_x1
158        self._control_y1 = control_y1
159        self._control_x2 = control_x2
160        self._control_y2 = control_y2
161        self._destination_x = destination_x
162        self._destination_y = destination_y

Create a new bezier curve.

Arguments:
  • control_x1: The horizontal location of the first control point.
  • control_y1: The vertical location of the first control point.
  • control_x2: The horizontal location of the second control point.
  • control_y2: The vertical location of the second control point.
  • destination_x: The vertical location of the end coordinate.
  • destination_y: The horizontal location of the end coordinate.
def get_control_x1(self):
164    def get_control_x1(self):
165        return self._control_x1

Get the horizontal coordinate of the first control point.

Returns:

The x coordinate of the first control point.

def get_control_y1(self):
167    def get_control_y1(self):
168        return self._control_y1

Get the vertical coordinate of the first control point.

Returns:

The y coordinate of the first control point.

def get_control_x2(self):
170    def get_control_x2(self):
171        return self._control_x2

Get the horizontal coordinate of the second control point.

Returns:

The x coordinate of the second control point.

def get_control_y2(self):
173    def get_control_y2(self):
174        return self._control_y2

Get the vertical coordinate of the second control point.

Returns:

The y coordinate of the second control point.

def get_destination_x(self):
176    def get_destination_x(self):
177        return self._destination_x

Get the ending horizontal coordinate of this segment.

Returns:

The x coordinate that this segment ends on.

def get_destination_y(self):
179    def get_destination_y(self):
180        return self._destination_y

Get the ending vertical coordinate of this segment.

Returns:

The y coordinate that this segment ends on.

def get_min_x(self) -> float:
182    def get_min_x(self) -> float:
183        return min([
184            self._control_x1,
185            self._control_x2,
186            self._destination_x
187        ])

Get the minimum of the x coordinates in this segment.

Returns:

Minimum x coordinate of this segment.

def get_max_x(self) -> float:
189    def get_max_x(self) -> float:
190        return max([
191            self._control_x1,
192            self._control_x2,
193            self._destination_x
194        ])

Get the maximum of the x coordinates in this segment.

Returns:

Maximum x coordinate of this segment.

def get_min_y(self) -> float:
196    def get_min_y(self) -> float:
197        return min([
198            self._control_y1,
199            self._control_y2,
200            self._destination_y
201        ])

Get the minimum of the y coordinates in this segment.

Returns:

Minimum y coordinate of this segment.

def get_max_y(self) -> float:
203    def get_max_y(self) -> float:
204        return max([
205            self._control_y1,
206            self._control_y2,
207            self._destination_y
208        ])

Get the maximum of the y coordinates in this segment.

Returns:

Maximum y coordinate of this segment.

def get_strategy(self) -> str:
210    def get_strategy(self) -> str:
211        return 'bezier'

Get the type of line that this segment represents.

Returns:

Line strategy like "straight" or "bezier" as a string.

class Shape:
214class Shape:
215    """Structure describing a multi-segement shape."""
216
217    def __init__(self, start_x: float, start_y: float):
218        """Create a new multi-segment shape.
219
220        Args:
221            start_x: The starting x position of the shape.
222            start_y: The starting y position of the shape.
223        """
224        self._start_x = start_x
225        self._start_y = start_y
226        self._closed = False
227        self._finished = False
228        self._segments: typing.List[Line] = []
229
230    def add_line_to(self, x: float, y: float):
231        """Add a straight line to a shape.
232
233        Draw a straight line from the current position to a new destination which is used as the
234        next "current" position.
235
236        Args:
237            x: The x coordinate to which the line should be drawn.
238            y: The y coordinate to which the line should be drawn.
239        """
240        self._assert_not_finished()
241        self._segments.append(StraightLine(x, y))
242
243    def add_bezier_to(self, control_x1: float, control_y1: float, control_x2: float,
244        control_y2: float, destination_x: float, destination_y: float):
245        """Add a bezier curve to a shape.
246
247        Draw a bezier curve from the current position to a new destination which is used as the next
248        "current" position.
249
250        Args:
251            control_x1: The horizontal location of the first control point.
252            control_y1: The vertical location of the first control point.
253            control_x2: The horizontal location of the second control point.
254            control_y2: The vertical location of the second control point.
255            destination_x: The vertical location of the end coordinate.
256            destination_y: The horizontal location of the end coordinate.
257        """
258        self._assert_not_finished()
259        self._segments.append(BezierLine(
260            control_x1,
261            control_y1,
262            control_x2,
263            control_y2,
264            destination_x,
265            destination_y
266        ))
267
268    def get_start_x(self) -> float:
269        """Retrieve the first x coordinate of this shape.
270
271        Get the horizontal coordinate from which the first segment draws.
272
273        Returns:
274            The starting x coordinate.
275        """
276        return self._start_x
277
278    def get_start_y(self) -> float:
279        """Retrieve the first y coordinate of this shape.
280
281        Get the vertical coordinate from which the first segment draws.
282
283        Returns:
284            The starting y coordinate.
285        """
286        return self._start_y
287
288    def get_segments(self) -> typing.Iterable[Line]:
289        """Retrieve shape segments.
290
291        Retrieve objects describing each of the segments in a shape.
292
293        Returns:
294            Segements in this shape.
295        """
296        return self._segments
297
298    def get_is_finished(self) -> bool:
299        """Determine if a shape is finished.
300
301        Determine if the shape is finished so can be drawn. Returns true if finished (can be drawn)
302        and false otherwise (still building). A shape cannot be drawn until it is finished which can
303        be accomplished by either calling end or close.
304
305        Returns:
306            True if finished and false otherwise.
307        """
308        return self._finished
309
310    def end(self):
311        """Draw a shape.
312
313        Draw a shape which consists of multiple line or curve segments and which can be either open
314        (stroke only) or closed (can be filled).
315        """
316        self._assert_not_finished()
317        self._finished = True
318        self._closed = False
319
320    def close(self):
321        """Add a straight line to the starting coordinate.
322
323        Add a line in the shape from the current position to the start position, marking the shape
324        as finished and closed which allows it to be filled.
325        """
326        self._assert_not_finished()
327        self._finished = True
328        self._closed = True
329
330    def get_is_closed(self) -> bool:
331        """Determine if a shape can be filled.
332
333        Determine if the shape is closed so can be filled. Returns true if closed (can be filled)
334        and false otherwise.
335
336        Returns:
337            True if closed and false otherwise.
338        """
339        self._assert_finished()
340        return self._closed
341
342    def get_min_x(self):
343        """Determine the minimum x coordinate of a shape.
344
345        Determine the minimum x coordinate (relative to start position) that this shape may reach
346        to. This includes bezier control points but does not try to include stroke weight in its
347        calculation.
348
349        Returns:
350            Minimum x coordinate.
351        """
352        self._assert_finished()
353        return min([self._start_x] + [x.get_min_x() for x in self._segments])
354
355    def get_max_x(self):
356        """Determine the maximum x coordinate of a shape.
357
358        Determine the maximum x coordinate (relative to start position) that this shape may reach
359        to. This includes bezier control points but does not try to include stroke weight in its
360        calculation.
361
362        Returns:
363            Maximum x coordinate.
364        """
365        self._assert_finished()
366        return max([self._start_x] + [x.get_max_x() for x in self._segments])
367
368    def get_min_y(self):
369        """Determine the minimum y coordinate of a shape.
370
371        Determine the minimum y coordinate (relative to start position) that this shape may reach
372        to. This includes bezier control points but does not try to include stroke weight in its
373        calculation.
374
375        Returns:
376            Minimum y coordinate.
377        """
378        self._assert_finished()
379        return min([self._start_y] + [x.get_min_y() for x in self._segments])
380
381    def get_max_y(self):
382        """Determine the maximum y coordinate of a shape.
383
384        Determine the maximum y coordinate (relative to start position) that this shape may reach
385        to. This includes bezier control points but does not try to include stroke weight in its
386        calculation.
387
388        Returns:
389            Maximum y coordinate.
390        """
391        self._assert_finished()
392        return max([self._start_y] + [x.get_max_y() for x in self._segments])
393
394    def _assert_not_finished(self):
395        if self._finished:
396            raise RuntimeError('Whoops! This shape is already finished.')
397
398    def _assert_finished(self):
399        if not self._finished:
400            raise RuntimeError('Whoops! This shape is not yet finished.')

Structure describing a multi-segement shape.

Shape(start_x: float, start_y: float)
217    def __init__(self, start_x: float, start_y: float):
218        """Create a new multi-segment shape.
219
220        Args:
221            start_x: The starting x position of the shape.
222            start_y: The starting y position of the shape.
223        """
224        self._start_x = start_x
225        self._start_y = start_y
226        self._closed = False
227        self._finished = False
228        self._segments: typing.List[Line] = []

Create a new multi-segment shape.

Arguments:
  • start_x: The starting x position of the shape.
  • start_y: The starting y position of the shape.
def add_line_to(self, x: float, y: float):
230    def add_line_to(self, x: float, y: float):
231        """Add a straight line to a shape.
232
233        Draw a straight line from the current position to a new destination which is used as the
234        next "current" position.
235
236        Args:
237            x: The x coordinate to which the line should be drawn.
238            y: The y coordinate to which the line should be drawn.
239        """
240        self._assert_not_finished()
241        self._segments.append(StraightLine(x, y))

Add a straight line to a shape.

Draw a straight line from the current position to a new destination which is used as the next "current" position.

Arguments:
  • x: The x coordinate to which the line should be drawn.
  • y: The y coordinate to which the line should be drawn.
def add_bezier_to( self, control_x1: float, control_y1: float, control_x2: float, control_y2: float, destination_x: float, destination_y: float):
243    def add_bezier_to(self, control_x1: float, control_y1: float, control_x2: float,
244        control_y2: float, destination_x: float, destination_y: float):
245        """Add a bezier curve to a shape.
246
247        Draw a bezier curve from the current position to a new destination which is used as the next
248        "current" position.
249
250        Args:
251            control_x1: The horizontal location of the first control point.
252            control_y1: The vertical location of the first control point.
253            control_x2: The horizontal location of the second control point.
254            control_y2: The vertical location of the second control point.
255            destination_x: The vertical location of the end coordinate.
256            destination_y: The horizontal location of the end coordinate.
257        """
258        self._assert_not_finished()
259        self._segments.append(BezierLine(
260            control_x1,
261            control_y1,
262            control_x2,
263            control_y2,
264            destination_x,
265            destination_y
266        ))

Add a bezier curve to a shape.

Draw a bezier curve from the current position to a new destination which is used as the next "current" position.

Arguments:
  • control_x1: The horizontal location of the first control point.
  • control_y1: The vertical location of the first control point.
  • control_x2: The horizontal location of the second control point.
  • control_y2: The vertical location of the second control point.
  • destination_x: The vertical location of the end coordinate.
  • destination_y: The horizontal location of the end coordinate.
def get_start_x(self) -> float:
268    def get_start_x(self) -> float:
269        """Retrieve the first x coordinate of this shape.
270
271        Get the horizontal coordinate from which the first segment draws.
272
273        Returns:
274            The starting x coordinate.
275        """
276        return self._start_x

Retrieve the first x coordinate of this shape.

Get the horizontal coordinate from which the first segment draws.

Returns:

The starting x coordinate.

def get_start_y(self) -> float:
278    def get_start_y(self) -> float:
279        """Retrieve the first y coordinate of this shape.
280
281        Get the vertical coordinate from which the first segment draws.
282
283        Returns:
284            The starting y coordinate.
285        """
286        return self._start_y

Retrieve the first y coordinate of this shape.

Get the vertical coordinate from which the first segment draws.

Returns:

The starting y coordinate.

def get_segments(self) -> Iterable[Line]:
288    def get_segments(self) -> typing.Iterable[Line]:
289        """Retrieve shape segments.
290
291        Retrieve objects describing each of the segments in a shape.
292
293        Returns:
294            Segements in this shape.
295        """
296        return self._segments

Retrieve shape segments.

Retrieve objects describing each of the segments in a shape.

Returns:

Segements in this shape.

def get_is_finished(self) -> bool:
298    def get_is_finished(self) -> bool:
299        """Determine if a shape is finished.
300
301        Determine if the shape is finished so can be drawn. Returns true if finished (can be drawn)
302        and false otherwise (still building). A shape cannot be drawn until it is finished which can
303        be accomplished by either calling end or close.
304
305        Returns:
306            True if finished and false otherwise.
307        """
308        return self._finished

Determine if a shape is finished.

Determine if the shape is finished so can be drawn. Returns true if finished (can be drawn) and false otherwise (still building). A shape cannot be drawn until it is finished which can be accomplished by either calling end or close.

Returns:

True if finished and false otherwise.

def end(self):
310    def end(self):
311        """Draw a shape.
312
313        Draw a shape which consists of multiple line or curve segments and which can be either open
314        (stroke only) or closed (can be filled).
315        """
316        self._assert_not_finished()
317        self._finished = True
318        self._closed = False

Draw a shape.

Draw a shape which consists of multiple line or curve segments and which can be either open (stroke only) or closed (can be filled).

def close(self):
320    def close(self):
321        """Add a straight line to the starting coordinate.
322
323        Add a line in the shape from the current position to the start position, marking the shape
324        as finished and closed which allows it to be filled.
325        """
326        self._assert_not_finished()
327        self._finished = True
328        self._closed = True

Add a straight line to the starting coordinate.

Add a line in the shape from the current position to the start position, marking the shape as finished and closed which allows it to be filled.

def get_is_closed(self) -> bool:
330    def get_is_closed(self) -> bool:
331        """Determine if a shape can be filled.
332
333        Determine if the shape is closed so can be filled. Returns true if closed (can be filled)
334        and false otherwise.
335
336        Returns:
337            True if closed and false otherwise.
338        """
339        self._assert_finished()
340        return self._closed

Determine if a shape can be filled.

Determine if the shape is closed so can be filled. Returns true if closed (can be filled) and false otherwise.

Returns:

True if closed and false otherwise.

def get_min_x(self):
342    def get_min_x(self):
343        """Determine the minimum x coordinate of a shape.
344
345        Determine the minimum x coordinate (relative to start position) that this shape may reach
346        to. This includes bezier control points but does not try to include stroke weight in its
347        calculation.
348
349        Returns:
350            Minimum x coordinate.
351        """
352        self._assert_finished()
353        return min([self._start_x] + [x.get_min_x() for x in self._segments])

Determine the minimum x coordinate of a shape.

Determine the minimum x coordinate (relative to start position) that this shape may reach to. This includes bezier control points but does not try to include stroke weight in its calculation.

Returns:

Minimum x coordinate.

def get_max_x(self):
355    def get_max_x(self):
356        """Determine the maximum x coordinate of a shape.
357
358        Determine the maximum x coordinate (relative to start position) that this shape may reach
359        to. This includes bezier control points but does not try to include stroke weight in its
360        calculation.
361
362        Returns:
363            Maximum x coordinate.
364        """
365        self._assert_finished()
366        return max([self._start_x] + [x.get_max_x() for x in self._segments])

Determine the maximum x coordinate of a shape.

Determine the maximum x coordinate (relative to start position) that this shape may reach to. This includes bezier control points but does not try to include stroke weight in its calculation.

Returns:

Maximum x coordinate.

def get_min_y(self):
368    def get_min_y(self):
369        """Determine the minimum y coordinate of a shape.
370
371        Determine the minimum y coordinate (relative to start position) that this shape may reach
372        to. This includes bezier control points but does not try to include stroke weight in its
373        calculation.
374
375        Returns:
376            Minimum y coordinate.
377        """
378        self._assert_finished()
379        return min([self._start_y] + [x.get_min_y() for x in self._segments])

Determine the minimum y coordinate of a shape.

Determine the minimum y coordinate (relative to start position) that this shape may reach to. This includes bezier control points but does not try to include stroke weight in its calculation.

Returns:

Minimum y coordinate.

def get_max_y(self):
381    def get_max_y(self):
382        """Determine the maximum y coordinate of a shape.
383
384        Determine the maximum y coordinate (relative to start position) that this shape may reach
385        to. This includes bezier control points but does not try to include stroke weight in its
386        calculation.
387
388        Returns:
389            Maximum y coordinate.
390        """
391        self._assert_finished()
392        return max([self._start_y] + [x.get_max_y() for x in self._segments])

Determine the maximum y coordinate of a shape.

Determine the maximum y coordinate (relative to start position) that this shape may reach to. This includes bezier control points but does not try to include stroke weight in its calculation.

Returns:

Maximum y coordinate.