sketchingpy.abstracted

Interfaces for the strategies behind different rendering options.

License:

BSD

  1"""Interfaces for the strategies behind different rendering options.
  2
  3License:
  4    BSD
  5"""
  6
  7import copy
  8import math
  9import time
 10import typing
 11
 12import sketchingpy.const
 13import sketchingpy.control_struct
 14import sketchingpy.data_struct
 15import sketchingpy.dialog_struct
 16import sketchingpy.geo
 17import sketchingpy.shape_struct
 18import sketchingpy.state_struct
 19
 20StepCallback = typing.Callable[['Sketch'], None]
 21QuitCallback = StepCallback
 22
 23
 24class Image:
 25    """Information about an image as an abstract base class."""
 26
 27    def __init__(self, src: str):
 28        """Create a new image record.
 29
 30        Args:
 31            src: The location from which the image was loaded.
 32        """
 33        self._src = src
 34
 35    def get_src(self) -> str:
 36        """Get the location from which the image was loaded.
 37
 38        Returns:
 39            Location for the image.
 40        """
 41        return self._src
 42
 43    def get_width(self) -> float:
 44        """Get the width of this image in pixels.
 45
 46        Returns:
 47            Horizontal width of this image.
 48        """
 49        raise NotImplementedError('Use implementor.')
 50
 51    def get_height(self) -> float:
 52        """Get the height of this image in pixels.
 53
 54        Returns:
 55            Vertical height of this image.
 56        """
 57        raise NotImplementedError('Use implementor.')
 58
 59    def resize(self, width: float, height: float):
 60        """Resize this image by scaling.
 61
 62        Args:
 63            width: The new desired width of this image in pixels.
 64            height: The new desired height of this image in pixels.
 65        """
 66        raise NotImplementedError('Use implementor.')
 67
 68    def get_native(self):
 69        """Access the underlying native version of this image.
 70
 71        Returns:
 72            Renderer specific native version.
 73        """
 74        raise NotImplementedError('Use implementor.')
 75
 76    def get_is_loaded(self) -> bool:
 77        """Determine if this image has finished loading.
 78
 79        Returns:
 80            True if loaded and ready to draw. False otherwise.
 81        """
 82        raise NotImplementedError('Use implementor.')
 83
 84
 85class Sketch:
 86    """Abstract base class for a sketch renderer strategy."""
 87
 88    def __init__(self, width: typing.Optional[int] = None, height: typing.Optional[int] = None,
 89        title: typing.Optional[str] = None, loading_src: typing.Optional[str] = None):
 90        """Create a new sketch."""
 91        self._state_current_machine = self._create_state_machine()
 92        self._state_machine_stack: typing.List[sketchingpy.state_struct.SketchStateMachine] = []
 93
 94        self._map_current_view = self._create_map_view()
 95        self._map_view_stack: typing.List[sketchingpy.geo.GeoTransformation] = []
 96        self._start_millis = None
 97
 98    ##########
 99    # Buffer #
100    ##########
101
102    def create_buffer(self, name: str, width: int, height: int,
103        background: typing.Optional[str] = None):
104        """Create a new named in-memory (or equivalent) buffer.
105
106        Args:
107            name: The name of the buffer. If a prior buffer of this name exists, it will be
108                replaced.
109            width: The width of the buffer in pixels. In some renderers, the buffer will clip. In
110                others, out of buffer values may be drawn.
111            height: The height of the buffer in pixels. In some renderers, the buffer will clip. In
112                others, out of buffer values may be drawn.
113            background: The background to use for this buffer or None if transparent. Defaults to
114                None.
115        """
116        raise NotImplementedError('Use implementor.')
117
118    def enter_buffer(self, name: str):
119        """Switch rendering context to a buffer, exiting current buffer if active.
120
121        Args:
122            name: The name of the buffer to which context should switch.
123        """
124        raise NotImplementedError('Use implementor.')
125
126    def exit_buffer(self):
127        """Exit the current offscreen buffer.
128
129        Exit the current offscreen buffer, returning to the actual sketch. This will act as a noop
130        if not currently in a buffer.
131        """
132        raise NotImplementedError('Use implementor.')
133
134    def draw_buffer(self, x: float, y: float, name: str):
135        """Draw an offscreen buffer to the current buffer or sketch.
136
137        Args:
138            x: The horizontal position in pixels at which the left should be drawn.
139            y: The vertical position in pixels at which the top should be drawn.
140            name: The name of the buffer to draw.
141        """
142        raise NotImplementedError('Use implementor.')
143
144    ##########
145    # Colors #
146    ##########
147
148    def set_fill(self, color_hex: str):
149        """Set the fill color.
150
151        Set the color to use for filling shapes and figures.
152
153        Args:
154            color_hex: Name of the color or a hex code.
155        """
156        self._get_current_state_machine().set_fill(color_hex)
157
158    def clear_fill(self):
159        """Clear the fill color.
160
161        Set the fill color to fully transparent so that only outlines of shapes and figures are
162        drawn.
163        """
164        self._get_current_state_machine().clear_fill()
165
166    def set_stroke(self, color_hex: str):
167        """Set the stroke color.
168
169        Set the color to use for drawing outlines for shapes and figures as well as lines.
170
171        Args:
172            color_hex: Name of the color or a hex code.
173        """
174        self._get_current_state_machine().set_stroke(color_hex)
175
176    def clear_stroke(self):
177        """Clear the stroke color.
178
179        Set the stroke width to zero, disabling the drawing of outlines for shapes and figures as
180        well as lines.
181        """
182        self._get_current_state_machine().clear_stroke()
183
184    ############
185    # Controls #
186    ############
187
188    def get_keyboard(self) -> typing.Optional[sketchingpy.control_struct.Keyboard]:
189        """Get access to the keyboard.
190
191        Get access to the keyboard currently registered with the operating system for the sketch.
192        Different sketches running at the same time may have different keyboards depending on focus
193        or OS configuration.
194
195        Returns:
196            Current keyboard or None if not found / supported.
197        """
198        raise NotImplementedError('Use implementor.')
199
200    def get_mouse(self) -> typing.Optional[sketchingpy.control_struct.Mouse]:
201        """Get access to the mouse.
202
203        Get access to the mouse currently registered with the operating system for the sketch.
204        Different sketches running at the same time may have different mouse objects depending on
205        focus or OS configuration. Note that the mouse may also be emulated if the device uses a
206        touch screen.
207
208        Returns:
209            Current mouse or None if not found / supported.
210        """
211        raise NotImplementedError('Use implementor.')
212
213    ########
214    # Data #
215    ########
216
217    def get_data_layer(self) -> typing.Optional[sketchingpy.data_struct.DataLayer]:
218        """Get access to reading and writing data.
219
220        Open access to the file system, network, or browser to read or write data.
221
222        Returns:
223            Facade for data access or None if not supported or insufficient permissions.
224        """
225        raise NotImplementedError('Use implementor.')
226
227    ###########
228    # Dialogs #
229    ###########
230
231    def get_dialog_layer(self) -> typing.Optional[sketchingpy.dialog_struct.DialogLayer]:
232        """Get access to rendering and using simple dialogs.
233
234        Open access to a simple dialog prefabricated UI system to show alerts, prompts, and other
235        dialog boxes.
236
237        Returns:
238            Facade for rendering dialogs or None if not supported or insufficient permissions.
239        """
240        raise NotImplementedError('Use implementor.')
241
242    ###########
243    # Drawing #
244    ###########
245
246    def clear(self, color: str):
247        """Clear the sketch to a color.
248
249        Peform the equivalent of drawing a rectangle the size of the sketch without stroke and with
250        the given fill color.
251
252        Args:
253            color: The color to use in clearing.
254        """
255        raise NotImplementedError('Use implementor.')
256
257    def set_arc_mode(self, mode: str):
258        """Specify how Sketchingpy should interpret the position and size arguments for arcs.
259
260        Determine how arcs should be placed within the sketch and how they should be sized.
261
262        Args:
263            mode: String describing the mode to use.
264        """
265        self._get_current_state_machine().set_arc_mode(mode)
266
267    def draw_arc(self, x1: float, y1: float, x2: float, y2: float, a1: float, a2: float):
268        """Draw a partial ellipse using starting and ending angles.
269
270        Using starting and ending angles, draw a partial ellipse which is either drawn outside line
271        only (stroke) and / or filled from the center of that ellipse.
272
273        Args:
274            x1: The x location at which to draw the arc.
275            y1: The y location at which to draw the arc.
276            x2: Horizontal size.
277            y2: Vertical size.
278            a1: Starting angle.
279            a2: Ending angle.
280        """
281        raise NotImplementedError('Use implementor.')
282
283    def set_ellipse_mode(self, mode: str):
284        """Specify how Sketchingpy should interpret the position and size arguments.
285
286        Determine how arcs should be placed within the sketch and how they should be sized for
287        ellipses.
288
289        Args:
290            mode: String describing the mode to use.
291        """
292        self._get_current_state_machine().set_ellipse_mode(mode)
293
294    def draw_ellipse(self, x1: float, y1: float, x2: float, y2: float):
295        """Draw a circle or ellipse.
296
297        Draw an ellipse or, in the case of equal width and height, a circle.
298
299        Args:
300            x1: The x location at which to draw the ellipse.
301            y1: The y location at which to draw the ellipse.
302            x2: Horizontal size.
303            y2: Vertical size.
304        """
305        raise NotImplementedError('Use implementor.')
306
307    def draw_line(self, x1: float, y1: float, x2: float, y2: float):
308        """Draw a simple line.
309
310        Draw a line between two points.
311
312        Args:
313            x1: The x coordinate from which the line should be drawn.
314            y1: The y coordinate from which the line should be drawn.
315            x2: The x coordinate to which the line should be drawn.
316            y2: The y coordinate to which the line should be drawn.
317        """
318        raise NotImplementedError('Use implementor.')
319
320    def set_rect_mode(self, mode: str):
321        """Specify how Sketchingpy should interpret the position and size arguments.
322
323        Determine how arcs should be placed within the sketch and how they should be sized for
324        rectangles.
325
326        Args:
327            mode: String describing the mode to use.
328        """
329        self._get_current_state_machine().set_rect_mode(mode)
330
331    def draw_rect(self, x1: float, y1: float, x2: float, y2: float):
332        """Draw a rectangle.
333
334        Draw a rectangle or, if width and height are the same, a square.
335
336        Args:
337            x1: The x location at which to draw the rectangle.
338            y1: The y location at which to draw the rectangle.
339            x2: Horizontal size.
340            y2: Vertical size.
341        """
342        raise NotImplementedError('Use implementor.')
343
344    def draw_pixel(self, x: float, y: float):
345        """Draw a single pixel.
346
347        Draw a rectangle with a width of zero and height of zero, changing a single pixel.
348
349        Args:
350            x: The x location at which to draw the rectangle.
351            y: The y location at which to draw the rectangle.
352        """
353        self.push_style()
354        self.set_rect_mode('corner')
355        self.clear_stroke()
356        self.draw_rect(x, y, 0, 0)
357        self.pop_style()
358
359    def start_shape(self, x: float, y: float) -> sketchingpy.shape_struct.Shape:
360        """Create a new shape.
361
362        Create a new shape which consists of multiple line or curve segments and which can be either
363        open (stroke only) or closed (can be filled).
364
365        Args:
366            x: The starting x position of the shape.
367            y: The starting y position of the shape.
368        """
369        return sketchingpy.shape_struct.Shape(x, y)
370
371    def draw_shape(self, shape: sketchingpy.shape_struct.Shape):
372        """Draw a shape.
373
374        Draw a shape which consists of multiple line or curve segments and which can be either open
375        (stroke only) or closed (can be filled).
376
377        Args:
378            shape: The shape to draw.
379        """
380        raise NotImplementedError('Use implementor.')
381
382    def set_stroke_weight(self, weight: float):
383        """Set the stroke color.
384
385        Set the width of stroke for drawing outlines for shapes and figures as well as lines.
386
387        Args:
388            size: Number of pixels for the stroke weight.
389        """
390        self._get_current_state_machine().set_stroke_weight(weight)
391
392    def set_text_font(self, identifier: str, size: float):
393        """Set the type and size of text to draw.
394
395        Set the size and font to use for drawing text.
396
397        Args:
398            font: Path to the TTF font file.
399            size: Size of the font (px).
400        """
401        font = sketchingpy.state_struct.Font(identifier, size)
402        self._get_current_state_machine().set_text_font(font)
403
404    def set_text_align(self, horizontal_align: str, vertical_align: str = 'baseline'):
405        """Indicate the alignment of text to be drawn.
406
407        Indicate how the text should be aligned horizontally and vertically.
408
409        Args:
410            horizontal: Argument for horizontal alignment.
411            vertical: Optional additional argument for vertical alignment. If not provided, will
412                default to baseline.
413        """
414        align_struct = sketchingpy.state_struct.TextAlign(horizontal_align, vertical_align)
415        self._get_current_state_machine().set_text_align(align_struct)
416
417    def draw_text(self, x: float, y: float, content: str):
418        """Draw text using the current font.
419
420        Draw text using the current font and alignment.
421
422        Args:
423            x: The x coordinate at which to draw the text.
424            y: The y coordinate at which to draw the text.
425            text: The string to draw.
426        """
427        raise NotImplementedError('Use implementor.')
428
429    ##########
430    # Events #
431    ##########
432
433    def on_step(self, callback: StepCallback):
434        """Callback for when the sketch ends execution.
435
436        Register a callback for when the sketch redraws. This function should expect a single
437        parameter which is the sketch redrawing.
438
439        Args:
440            callback: The function to invoke when the sketch stops execution.
441        """
442        raise NotImplementedError('Use implementor.')
443
444    def on_quit(self, callback: QuitCallback):
445        """Callback for when the sketch ends execution.
446
447        Register a callback for when the sketch terminates.
448
449        Args:
450            callback: The function to invoke when the sketch stops execution.
451        """
452        raise NotImplementedError('Use implementor.')
453
454    #######
455    # Geo #
456    #######
457
458    def set_map_pan(self, longitude: float, latitude: float):
459        """Indicate where point should be at the center of the map geographically.
460
461        Indicate a latitude and longitude point which is where the map projection should be
462        centerered geographically.
463
464        Args:
465            longitude: The center longitude in degrees.
466            latitude: The center latitude in degrees.
467        """
468        self._map_current_view = sketchingpy.geo.GeoTransformation(
469            sketchingpy.geo.GeoPoint(longitude, latitude),
470            self._map_current_view.get_pixel_offset(),
471            self._map_current_view.get_scale()
472        )
473
474    def set_map_zoom(self, zoom: float):
475        """Indicate the map zoom level.
476
477        Specify the map scaling factor or map "zoom" level.
478
479        Args:
480            zoom: The zoom level to use.
481        """
482        self._map_current_view = sketchingpy.geo.GeoTransformation(
483            self._map_current_view.get_geo_offset(),
484            self._map_current_view.get_pixel_offset(),
485            zoom
486        )
487
488    def set_map_placement(self, x: float, y: float):
489        """Indicate where in the sketch the map view should be drawn.
490
491        Indicate where in the sketch in terms of pixel coordinates the map view should be centered
492        such that the map pan latitude and longitude map to this coordinate position in pixel space.
493
494        Args:
495            x: The horizontal coordinate in pixels.
496            y: The vertical coordinate in pixels.
497        """
498        self._map_current_view = sketchingpy.geo.GeoTransformation(
499            self._map_current_view.get_geo_offset(),
500            sketchingpy.geo.PixelOffset(x, y),
501            self._map_current_view.get_scale()
502        )
503
504    def convert_geo_to_pixel(self, longitude: float,
505        latitude: float) -> typing.Tuple[float, float]:
506        """Convert a geographic location to a pixel coordinate.
507
508        Convert a longitude / latitude coordinate pair in degrees to sketch coordinates in pixels
509        using the current map view parameters.
510
511        Args:
512            longitude: The longitude to convert in degrees.
513            latitude: The latitude to convert in degrees.
514
515        Returns:
516            Tuple with two elements: x coordinate and y coordinate.
517        """
518        point = sketchingpy.geo.GeoPoint(longitude, latitude, )
519        x = point.get_x(transform=self._map_current_view)
520        y = point.get_y(transform=self._map_current_view)
521        return (x, y)
522
523    def start_geo_polygon(self, longitude: float,
524        latitude: float) -> sketchingpy.geo.GeoPolygonBuilder:
525        """Start building a polygon using geographic coordinates.
526
527        Start building a closed shape using geographic coordinates (longitude and latitude provided
528        in degrees) instead of pixel coordinates.
529
530        Args:
531            longitude: The starting geographic point longitude coordinate or the E/W component of
532                the first point of the polygon.
533            latitude: The starting geographic point longitude coordinate or the N/S component of
534                the first point of the polygon.
535
536        Returns:
537            Object to build geographic polygons.
538        """
539        get_current_view = lambda: self._map_current_view
540        return sketchingpy.geo.GeoPolygonBuilder(longitude, latitude, get_current_view)
541
542    def push_map(self):
543        """Save current map view configuration.
544
545        Save current map pan, zoom, and pixel placement to the map history. This works as a stack
546        (like a stack of plates) where this puts a new plate on the top of the pile. This will leave
547        the current map configuration in the sketch unchanged.
548        """
549        self._map_view_stack.append(self._map_current_view)
550
551    def pop_map(self):
552        """Restore a previously saved map view configuration.
553
554        Restore the most recently saved map view configuration saved in style history, removing that
555        config from the history. This works as a stack (like a stack of plates) where the top of
556        the pile is taken off and restored, removing it from that stack. This will overwrite the
557        current map view configuration in the sketch.
558        """
559        if len(self._map_view_stack) == 0:
560            raise RuntimeError('Cannot pop an empty map view stack.')
561
562        self._map_current_view = self._map_view_stack.pop()
563
564    def parse_geojson(self, source: typing.Dict) -> typing.List[sketchingpy.geo.GeoPolygonBuilder]:
565        """Utility to parse GeoJSON into a series of GeoPolygons.
566
567        Utility to parse GeoJSON into a series of GeoPolygons which currently only supports
568        MultiPolygon and Polygon.
569
570        Args:
571            source: The loaded GeoJSON source to parse.
572
573        Returns:
574            Polygon builder which can be converted to a shape.
575        """
576        raw_polygons = sketchingpy.geo.parse_geojson(source)
577        return [self._build_geo_polygon_builder(polygon) for polygon in raw_polygons]
578
579    #########
580    # Image #
581    #########
582
583    def set_image_mode(self, mode: str):
584        """Specify how Sketchingpy should place images.
585
586        Determine how images' coordinates should be interpreted when drawing.
587
588        Args:
589            mode: String describing the mode to use.
590        """
591        self._get_current_state_machine().set_image_mode(mode)
592
593    def get_image(self, src: str) -> Image:
594        """Load an image file.
595
596        Load an image from the local file system or URL.
597
598        Args:
599            src: The location from which the file should be read.
600        """
601        raise NotImplementedError('Use implementor.')
602
603    def draw_image(self, x: float, y: float, image: Image):
604        """Draw an image at a location.
605
606        Draw a previously loaded image at a specific coordinate using its current size.
607
608        Args:
609            x: Horizontal coordinate at which to draw the image.
610            y: Vertical coordinate at which to draw the image.
611            image: The image to draw.
612        """
613        raise NotImplementedError('Use implementor.')
614
615    def save_image(self, path: str):
616        """Save an image file.
617
618        Save the sketch as an image file, either directly to the file system or as a download.
619
620        Args:
621            path: The location at which the file should be written.
622        """
623        raise NotImplementedError('Use implementor.')
624
625    ################
626    # Other Params #
627    ################
628
629    def set_angle_mode(self, mode: str):
630        """Indicate how angles should be provided to sketchingpy.
631
632        Change the units with which angles are expressed to Sketchingpy in transforms and shapes.
633
634        Args:
635            mode: The units (either 'degrees' or 'radians') in which to supply angles.
636        """
637        self._get_current_state_machine().set_angle_mode(mode)
638
639    #########
640    # State #
641    #########
642
643    def push_transform(self):
644        """Save current transformation state.
645
646        Save current sketch transformation state to the matrix history. This works as a stack (like
647        a stack of plates) where this puts a new plate on the top of the pile. This will leave the
648        current transformation matrix in the sketch unchanged.
649        """
650        raise NotImplementedError('Use implementor.')
651
652    def pop_transform(self):
653        """Restore a previously saved transformation state.
654
655        Restore the most recently transformation configuration saved in matrix history, removing
656        that "transform matrix" from the history. This works as a stack (like a stack of plates)
657        where the top of the pile is taken off and restored, removing it from that stack. This will
658        overwrite the current transformation configuration in the sketch.
659        """
660        raise NotImplementedError('Use implementor.')
661
662    def push_style(self):
663        """Save current styling.
664
665        Save current sketch styling to the style history. This works as a stack (like a stack of
666        plates) where this puts a new plate on the top of the pile. This will leave the current
667        style configuration in the sketch unchanged.
668        """
669        current = self._get_current_state_machine()
670        current_copy = copy.deepcopy(current)
671        self._state_machine_stack.append(current_copy)
672
673    def pop_style(self):
674        """Restore a previously saved styling.
675
676        Restore the most recently saved styling configuration saved in style history, removing that
677        styling from the history. This works as a stack (like a stack of plates) where the top of
678        the pile is taken off and restored, removing it from that stack. This will overwrite the
679        current style configuration in the sketch.
680        """
681        if len(self._state_machine_stack) == 0:
682            raise RuntimeError('Cannot pop an empty style stack.')
683
684        self._state_current_machine = self._state_machine_stack.pop()
685
686    ##########
687    # System #
688    ##########
689
690    def get_millis_shown(self) -> int:
691        """Get the milliseconds since the sketch was shown.
692
693        Returns:
694            The number of milliseconds since the sketch was shown or 0 if never shown.
695        """
696        return self._get_time_since_snapshot()
697
698    def print(self, message: str):
699        """Print a message to terminal or equivalent.
700
701        Args:
702            message: The string message to be printed.
703        """
704        print(message)
705
706    def get_native(self):
707        """Get a reference to the underlying native renderer object.
708
709        Returns:
710            Native render object.
711        """
712        raise NotImplementedError('Use implementor.')
713
714    def quit(self):
715        """Finish execution of the sketch.
716
717        Cause the sketch to stop execution.
718        """
719        raise NotImplementedError('Use implementor.')
720
721    def set_fps(self, rate: int):
722        """Indicate how fast the sketch should redraw.
723
724        Indicate a target frames per second that the sketch will take a "step" or redraw. Note that
725        this is a goal and, if the system fall behind, it will drop frames and cause the on_step
726        callback to be executed fewer times than the target.
727
728        Args:
729            rate: The number of frames to try to draw per second.
730        """
731        raise NotImplementedError('Use implementor.')
732
733    def set_title(self, title: str):
734        """Indicate the title to assign the window in the operating system.
735
736        Indicate the human-readable string title to assign to the sketch window.
737
738        Args:
739            title: The text of the title.
740        """
741        raise NotImplementedError('Use implementor.')
742
743    def show(self, ax=None):
744        """Show the sketch.
745
746        Show the sketch to the user and, if applicable, start the draw loop specified by set_fps.
747        For Sketch2DApp, will execute any waiting drawing instructions provided to the sketch prior
748        to showing. This is conceptually the same as "starting" the sketch.
749
750        Args:
751            ax: The container into which the sketch should be shown. Currently only supported for
752                Sketch2DStatic. Optional and ignored on most renderers.
753        """
754        raise NotImplementedError('Use implementor.')
755
756    def show_and_quit(self):
757        """Show the sketch and quit immediatley afterwards.
758
759        Show the sketch to the user and quit immediately afterwards, a routine potentially useful
760        for testing.
761        """
762        raise NotImplementedError('Use implementor.')
763
764    #############
765    # Transform #
766    #############
767
768    def translate(self, x: float, y: float):
769        """Change the location of the origin.
770
771        Change the transform matrix such that any drawing afterwards is moved by a set amount.
772
773        Args:
774            x: The number of pixels to offset horizontally.
775            y: The number of pixels to offset vertically.
776        """
777        raise NotImplementedError('Use implementor.')
778
779    def rotate(self, angle: float):
780        """Rotate around the current origin.
781
782        Change the transform matrix such that any drawing afterwards is rotated around the current
783        origin clock-wise.
784
785        Args:
786            angle: The angle by which to rotate.
787        """
788        raise NotImplementedError('Use implementor.')
789
790    def scale(self, scale: float):
791        """Scale outwards from the current origin.
792
793        Change the transform matrix such that any drawing afterwards is scaled from the current
794        origin.
795
796        Args:
797            scale: The factor by which to scale where values over 1 scale up and less than 1 scale
798                down. A value of 1 will have no effect.
799        """
800        raise NotImplementedError('Use implementor.')
801
802    ###########
803    # Support #
804    ###########
805
806    def _get_is_color_transparent(self, target: typing.Optional[str]) -> bool:
807        """Get if a color is transparent.
808
809        Args:
810            target: The color string to check if transparent. If None, assumes fully transparent.
811                Also assumes named colors like "blue" are fully opaque.
812        """
813        if target is None:
814            return True
815        elif target.startswith('#') and len(target) > 7 and target[-2:] != 'FF':
816            return True
817        else:
818            return False
819
820    def _build_geo_polygon_builder(self,
821        polygon: typing.List[typing.Tuple[float, float]]) -> sketchingpy.geo.GeoPolygonBuilder:
822        get_current_view = lambda: self._map_current_view
823        builder = sketchingpy.geo.GeoPolygonBuilder(polygon[0][0], polygon[0][1], get_current_view)
824
825        for point in polygon[1:]:
826            builder.add_coordinate(point[0], point[1])
827
828        return builder
829
830    def _convert_to_radians(self, angle: float) -> float:
831        current_angle_mode = self._get_current_state_machine()
832
833        if current_angle_mode == sketchingpy.const.RADIANS:
834            return angle
835        else:
836            return math.radians(angle)
837
838    def _create_map_view(self) -> sketchingpy.geo.GeoTransformation:
839        return sketchingpy.geo.GeoTransformation(
840            sketchingpy.geo.GeoPoint(sketchingpy.geo.BASE_LONGITUDE, sketchingpy.geo.BASE_LATITUDE),
841            sketchingpy.geo.PixelOffset(sketchingpy.geo.BASE_X, sketchingpy.geo.BASE_Y),
842            sketchingpy.geo.BASE_SCALE
843        )
844
845    def _create_state_machine(self) -> sketchingpy.state_struct.SketchStateMachine:
846        raise NotImplementedError('Use implementor.')
847
848    def _get_current_state_machine(self) -> sketchingpy.state_struct.SketchStateMachine:
849        return self._state_current_machine
850
851    def _snapshot_time(self):
852        self._start_millis = time.time() * 1000
853
854    def _get_time_since_snapshot(self):
855        if self._start_millis is None:
856            return 0
857        else:
858            return (time.time() * 1000) - self._start_millis
859
860
861def reorder_coords(x1: float, y1: float, x2: float, y2: float) -> typing.List[float]:
862    """Reorder coordinates so that the first comes before the second.
863
864    Args:
865        x1: The first x coordinate.
866        y1: The first y coordinate.
867        x2: The second x coordinate.
868        y2: The second y coordinate.
869    Returns:
870        List of form [min_x, min_y, max_x, max_y].
871    """
872    x_coords = [x1, x2]
873    y_coords = [y1, y2]
874    x_coords.sort()
875    y_coords.sort()
876    return [x_coords[0], y_coords[0], x_coords[1], y_coords[1]]
877
878
879def get_font_name(font, sep_char: str) -> str:
880    """Get the web version of a font.
881
882    Args:
883        font: The font to convert to a web font identifier.
884        sep_char: Path separator char.
885
886    Returns:
887        Web font identifier.
888    """
889    identifier = font.get_identifier()
890    identifier = identifier.split(sep_char)[-1]
891
892    if identifier.endswith('.ttf') or identifier.endswith('.otf'):
893        identifier = identifier[:-4]
894
895    return '%dpx %s' % (int(font.get_size()), identifier)
StepCallback = typing.Callable[[ForwardRef('Sketch')], NoneType]
QuitCallback = typing.Callable[[ForwardRef('Sketch')], NoneType]
class Image:
25class Image:
26    """Information about an image as an abstract base class."""
27
28    def __init__(self, src: str):
29        """Create a new image record.
30
31        Args:
32            src: The location from which the image was loaded.
33        """
34        self._src = src
35
36    def get_src(self) -> str:
37        """Get the location from which the image was loaded.
38
39        Returns:
40            Location for the image.
41        """
42        return self._src
43
44    def get_width(self) -> float:
45        """Get the width of this image in pixels.
46
47        Returns:
48            Horizontal width of this image.
49        """
50        raise NotImplementedError('Use implementor.')
51
52    def get_height(self) -> float:
53        """Get the height of this image in pixels.
54
55        Returns:
56            Vertical height of this image.
57        """
58        raise NotImplementedError('Use implementor.')
59
60    def resize(self, width: float, height: float):
61        """Resize this image by scaling.
62
63        Args:
64            width: The new desired width of this image in pixels.
65            height: The new desired height of this image in pixels.
66        """
67        raise NotImplementedError('Use implementor.')
68
69    def get_native(self):
70        """Access the underlying native version of this image.
71
72        Returns:
73            Renderer specific native version.
74        """
75        raise NotImplementedError('Use implementor.')
76
77    def get_is_loaded(self) -> bool:
78        """Determine if this image has finished loading.
79
80        Returns:
81            True if loaded and ready to draw. False otherwise.
82        """
83        raise NotImplementedError('Use implementor.')

Information about an image as an abstract base class.

Image(src: str)
28    def __init__(self, src: str):
29        """Create a new image record.
30
31        Args:
32            src: The location from which the image was loaded.
33        """
34        self._src = src

Create a new image record.

Arguments:
  • src: The location from which the image was loaded.
def get_src(self) -> str:
36    def get_src(self) -> str:
37        """Get the location from which the image was loaded.
38
39        Returns:
40            Location for the image.
41        """
42        return self._src

Get the location from which the image was loaded.

Returns:

Location for the image.

def get_width(self) -> float:
44    def get_width(self) -> float:
45        """Get the width of this image in pixels.
46
47        Returns:
48            Horizontal width of this image.
49        """
50        raise NotImplementedError('Use implementor.')

Get the width of this image in pixels.

Returns:

Horizontal width of this image.

def get_height(self) -> float:
52    def get_height(self) -> float:
53        """Get the height of this image in pixels.
54
55        Returns:
56            Vertical height of this image.
57        """
58        raise NotImplementedError('Use implementor.')

Get the height of this image in pixels.

Returns:

Vertical height of this image.

def resize(self, width: float, height: float):
60    def resize(self, width: float, height: float):
61        """Resize this image by scaling.
62
63        Args:
64            width: The new desired width of this image in pixels.
65            height: The new desired height of this image in pixels.
66        """
67        raise NotImplementedError('Use implementor.')

Resize this image by scaling.

Arguments:
  • width: The new desired width of this image in pixels.
  • height: The new desired height of this image in pixels.
def get_native(self):
69    def get_native(self):
70        """Access the underlying native version of this image.
71
72        Returns:
73            Renderer specific native version.
74        """
75        raise NotImplementedError('Use implementor.')

Access the underlying native version of this image.

Returns:

Renderer specific native version.

def get_is_loaded(self) -> bool:
77    def get_is_loaded(self) -> bool:
78        """Determine if this image has finished loading.
79
80        Returns:
81            True if loaded and ready to draw. False otherwise.
82        """
83        raise NotImplementedError('Use implementor.')

Determine if this image has finished loading.

Returns:

True if loaded and ready to draw. False otherwise.

class Sketch:
 86class Sketch:
 87    """Abstract base class for a sketch renderer strategy."""
 88
 89    def __init__(self, width: typing.Optional[int] = None, height: typing.Optional[int] = None,
 90        title: typing.Optional[str] = None, loading_src: typing.Optional[str] = None):
 91        """Create a new sketch."""
 92        self._state_current_machine = self._create_state_machine()
 93        self._state_machine_stack: typing.List[sketchingpy.state_struct.SketchStateMachine] = []
 94
 95        self._map_current_view = self._create_map_view()
 96        self._map_view_stack: typing.List[sketchingpy.geo.GeoTransformation] = []
 97        self._start_millis = None
 98
 99    ##########
100    # Buffer #
101    ##########
102
103    def create_buffer(self, name: str, width: int, height: int,
104        background: typing.Optional[str] = None):
105        """Create a new named in-memory (or equivalent) buffer.
106
107        Args:
108            name: The name of the buffer. If a prior buffer of this name exists, it will be
109                replaced.
110            width: The width of the buffer in pixels. In some renderers, the buffer will clip. In
111                others, out of buffer values may be drawn.
112            height: The height of the buffer in pixels. In some renderers, the buffer will clip. In
113                others, out of buffer values may be drawn.
114            background: The background to use for this buffer or None if transparent. Defaults to
115                None.
116        """
117        raise NotImplementedError('Use implementor.')
118
119    def enter_buffer(self, name: str):
120        """Switch rendering context to a buffer, exiting current buffer if active.
121
122        Args:
123            name: The name of the buffer to which context should switch.
124        """
125        raise NotImplementedError('Use implementor.')
126
127    def exit_buffer(self):
128        """Exit the current offscreen buffer.
129
130        Exit the current offscreen buffer, returning to the actual sketch. This will act as a noop
131        if not currently in a buffer.
132        """
133        raise NotImplementedError('Use implementor.')
134
135    def draw_buffer(self, x: float, y: float, name: str):
136        """Draw an offscreen buffer to the current buffer or sketch.
137
138        Args:
139            x: The horizontal position in pixels at which the left should be drawn.
140            y: The vertical position in pixels at which the top should be drawn.
141            name: The name of the buffer to draw.
142        """
143        raise NotImplementedError('Use implementor.')
144
145    ##########
146    # Colors #
147    ##########
148
149    def set_fill(self, color_hex: str):
150        """Set the fill color.
151
152        Set the color to use for filling shapes and figures.
153
154        Args:
155            color_hex: Name of the color or a hex code.
156        """
157        self._get_current_state_machine().set_fill(color_hex)
158
159    def clear_fill(self):
160        """Clear the fill color.
161
162        Set the fill color to fully transparent so that only outlines of shapes and figures are
163        drawn.
164        """
165        self._get_current_state_machine().clear_fill()
166
167    def set_stroke(self, color_hex: str):
168        """Set the stroke color.
169
170        Set the color to use for drawing outlines for shapes and figures as well as lines.
171
172        Args:
173            color_hex: Name of the color or a hex code.
174        """
175        self._get_current_state_machine().set_stroke(color_hex)
176
177    def clear_stroke(self):
178        """Clear the stroke color.
179
180        Set the stroke width to zero, disabling the drawing of outlines for shapes and figures as
181        well as lines.
182        """
183        self._get_current_state_machine().clear_stroke()
184
185    ############
186    # Controls #
187    ############
188
189    def get_keyboard(self) -> typing.Optional[sketchingpy.control_struct.Keyboard]:
190        """Get access to the keyboard.
191
192        Get access to the keyboard currently registered with the operating system for the sketch.
193        Different sketches running at the same time may have different keyboards depending on focus
194        or OS configuration.
195
196        Returns:
197            Current keyboard or None if not found / supported.
198        """
199        raise NotImplementedError('Use implementor.')
200
201    def get_mouse(self) -> typing.Optional[sketchingpy.control_struct.Mouse]:
202        """Get access to the mouse.
203
204        Get access to the mouse currently registered with the operating system for the sketch.
205        Different sketches running at the same time may have different mouse objects depending on
206        focus or OS configuration. Note that the mouse may also be emulated if the device uses a
207        touch screen.
208
209        Returns:
210            Current mouse or None if not found / supported.
211        """
212        raise NotImplementedError('Use implementor.')
213
214    ########
215    # Data #
216    ########
217
218    def get_data_layer(self) -> typing.Optional[sketchingpy.data_struct.DataLayer]:
219        """Get access to reading and writing data.
220
221        Open access to the file system, network, or browser to read or write data.
222
223        Returns:
224            Facade for data access or None if not supported or insufficient permissions.
225        """
226        raise NotImplementedError('Use implementor.')
227
228    ###########
229    # Dialogs #
230    ###########
231
232    def get_dialog_layer(self) -> typing.Optional[sketchingpy.dialog_struct.DialogLayer]:
233        """Get access to rendering and using simple dialogs.
234
235        Open access to a simple dialog prefabricated UI system to show alerts, prompts, and other
236        dialog boxes.
237
238        Returns:
239            Facade for rendering dialogs or None if not supported or insufficient permissions.
240        """
241        raise NotImplementedError('Use implementor.')
242
243    ###########
244    # Drawing #
245    ###########
246
247    def clear(self, color: str):
248        """Clear the sketch to a color.
249
250        Peform the equivalent of drawing a rectangle the size of the sketch without stroke and with
251        the given fill color.
252
253        Args:
254            color: The color to use in clearing.
255        """
256        raise NotImplementedError('Use implementor.')
257
258    def set_arc_mode(self, mode: str):
259        """Specify how Sketchingpy should interpret the position and size arguments for arcs.
260
261        Determine how arcs should be placed within the sketch and how they should be sized.
262
263        Args:
264            mode: String describing the mode to use.
265        """
266        self._get_current_state_machine().set_arc_mode(mode)
267
268    def draw_arc(self, x1: float, y1: float, x2: float, y2: float, a1: float, a2: float):
269        """Draw a partial ellipse using starting and ending angles.
270
271        Using starting and ending angles, draw a partial ellipse which is either drawn outside line
272        only (stroke) and / or filled from the center of that ellipse.
273
274        Args:
275            x1: The x location at which to draw the arc.
276            y1: The y location at which to draw the arc.
277            x2: Horizontal size.
278            y2: Vertical size.
279            a1: Starting angle.
280            a2: Ending angle.
281        """
282        raise NotImplementedError('Use implementor.')
283
284    def set_ellipse_mode(self, mode: str):
285        """Specify how Sketchingpy should interpret the position and size arguments.
286
287        Determine how arcs should be placed within the sketch and how they should be sized for
288        ellipses.
289
290        Args:
291            mode: String describing the mode to use.
292        """
293        self._get_current_state_machine().set_ellipse_mode(mode)
294
295    def draw_ellipse(self, x1: float, y1: float, x2: float, y2: float):
296        """Draw a circle or ellipse.
297
298        Draw an ellipse or, in the case of equal width and height, a circle.
299
300        Args:
301            x1: The x location at which to draw the ellipse.
302            y1: The y location at which to draw the ellipse.
303            x2: Horizontal size.
304            y2: Vertical size.
305        """
306        raise NotImplementedError('Use implementor.')
307
308    def draw_line(self, x1: float, y1: float, x2: float, y2: float):
309        """Draw a simple line.
310
311        Draw a line between two points.
312
313        Args:
314            x1: The x coordinate from which the line should be drawn.
315            y1: The y coordinate from which the line should be drawn.
316            x2: The x coordinate to which the line should be drawn.
317            y2: The y coordinate to which the line should be drawn.
318        """
319        raise NotImplementedError('Use implementor.')
320
321    def set_rect_mode(self, mode: str):
322        """Specify how Sketchingpy should interpret the position and size arguments.
323
324        Determine how arcs should be placed within the sketch and how they should be sized for
325        rectangles.
326
327        Args:
328            mode: String describing the mode to use.
329        """
330        self._get_current_state_machine().set_rect_mode(mode)
331
332    def draw_rect(self, x1: float, y1: float, x2: float, y2: float):
333        """Draw a rectangle.
334
335        Draw a rectangle or, if width and height are the same, a square.
336
337        Args:
338            x1: The x location at which to draw the rectangle.
339            y1: The y location at which to draw the rectangle.
340            x2: Horizontal size.
341            y2: Vertical size.
342        """
343        raise NotImplementedError('Use implementor.')
344
345    def draw_pixel(self, x: float, y: float):
346        """Draw a single pixel.
347
348        Draw a rectangle with a width of zero and height of zero, changing a single pixel.
349
350        Args:
351            x: The x location at which to draw the rectangle.
352            y: The y location at which to draw the rectangle.
353        """
354        self.push_style()
355        self.set_rect_mode('corner')
356        self.clear_stroke()
357        self.draw_rect(x, y, 0, 0)
358        self.pop_style()
359
360    def start_shape(self, x: float, y: float) -> sketchingpy.shape_struct.Shape:
361        """Create a new shape.
362
363        Create a new shape which consists of multiple line or curve segments and which can be either
364        open (stroke only) or closed (can be filled).
365
366        Args:
367            x: The starting x position of the shape.
368            y: The starting y position of the shape.
369        """
370        return sketchingpy.shape_struct.Shape(x, y)
371
372    def draw_shape(self, shape: sketchingpy.shape_struct.Shape):
373        """Draw a shape.
374
375        Draw a shape which consists of multiple line or curve segments and which can be either open
376        (stroke only) or closed (can be filled).
377
378        Args:
379            shape: The shape to draw.
380        """
381        raise NotImplementedError('Use implementor.')
382
383    def set_stroke_weight(self, weight: float):
384        """Set the stroke color.
385
386        Set the width of stroke for drawing outlines for shapes and figures as well as lines.
387
388        Args:
389            size: Number of pixels for the stroke weight.
390        """
391        self._get_current_state_machine().set_stroke_weight(weight)
392
393    def set_text_font(self, identifier: str, size: float):
394        """Set the type and size of text to draw.
395
396        Set the size and font to use for drawing text.
397
398        Args:
399            font: Path to the TTF font file.
400            size: Size of the font (px).
401        """
402        font = sketchingpy.state_struct.Font(identifier, size)
403        self._get_current_state_machine().set_text_font(font)
404
405    def set_text_align(self, horizontal_align: str, vertical_align: str = 'baseline'):
406        """Indicate the alignment of text to be drawn.
407
408        Indicate how the text should be aligned horizontally and vertically.
409
410        Args:
411            horizontal: Argument for horizontal alignment.
412            vertical: Optional additional argument for vertical alignment. If not provided, will
413                default to baseline.
414        """
415        align_struct = sketchingpy.state_struct.TextAlign(horizontal_align, vertical_align)
416        self._get_current_state_machine().set_text_align(align_struct)
417
418    def draw_text(self, x: float, y: float, content: str):
419        """Draw text using the current font.
420
421        Draw text using the current font and alignment.
422
423        Args:
424            x: The x coordinate at which to draw the text.
425            y: The y coordinate at which to draw the text.
426            text: The string to draw.
427        """
428        raise NotImplementedError('Use implementor.')
429
430    ##########
431    # Events #
432    ##########
433
434    def on_step(self, callback: StepCallback):
435        """Callback for when the sketch ends execution.
436
437        Register a callback for when the sketch redraws. This function should expect a single
438        parameter which is the sketch redrawing.
439
440        Args:
441            callback: The function to invoke when the sketch stops execution.
442        """
443        raise NotImplementedError('Use implementor.')
444
445    def on_quit(self, callback: QuitCallback):
446        """Callback for when the sketch ends execution.
447
448        Register a callback for when the sketch terminates.
449
450        Args:
451            callback: The function to invoke when the sketch stops execution.
452        """
453        raise NotImplementedError('Use implementor.')
454
455    #######
456    # Geo #
457    #######
458
459    def set_map_pan(self, longitude: float, latitude: float):
460        """Indicate where point should be at the center of the map geographically.
461
462        Indicate a latitude and longitude point which is where the map projection should be
463        centerered geographically.
464
465        Args:
466            longitude: The center longitude in degrees.
467            latitude: The center latitude in degrees.
468        """
469        self._map_current_view = sketchingpy.geo.GeoTransformation(
470            sketchingpy.geo.GeoPoint(longitude, latitude),
471            self._map_current_view.get_pixel_offset(),
472            self._map_current_view.get_scale()
473        )
474
475    def set_map_zoom(self, zoom: float):
476        """Indicate the map zoom level.
477
478        Specify the map scaling factor or map "zoom" level.
479
480        Args:
481            zoom: The zoom level to use.
482        """
483        self._map_current_view = sketchingpy.geo.GeoTransformation(
484            self._map_current_view.get_geo_offset(),
485            self._map_current_view.get_pixel_offset(),
486            zoom
487        )
488
489    def set_map_placement(self, x: float, y: float):
490        """Indicate where in the sketch the map view should be drawn.
491
492        Indicate where in the sketch in terms of pixel coordinates the map view should be centered
493        such that the map pan latitude and longitude map to this coordinate position in pixel space.
494
495        Args:
496            x: The horizontal coordinate in pixels.
497            y: The vertical coordinate in pixels.
498        """
499        self._map_current_view = sketchingpy.geo.GeoTransformation(
500            self._map_current_view.get_geo_offset(),
501            sketchingpy.geo.PixelOffset(x, y),
502            self._map_current_view.get_scale()
503        )
504
505    def convert_geo_to_pixel(self, longitude: float,
506        latitude: float) -> typing.Tuple[float, float]:
507        """Convert a geographic location to a pixel coordinate.
508
509        Convert a longitude / latitude coordinate pair in degrees to sketch coordinates in pixels
510        using the current map view parameters.
511
512        Args:
513            longitude: The longitude to convert in degrees.
514            latitude: The latitude to convert in degrees.
515
516        Returns:
517            Tuple with two elements: x coordinate and y coordinate.
518        """
519        point = sketchingpy.geo.GeoPoint(longitude, latitude, )
520        x = point.get_x(transform=self._map_current_view)
521        y = point.get_y(transform=self._map_current_view)
522        return (x, y)
523
524    def start_geo_polygon(self, longitude: float,
525        latitude: float) -> sketchingpy.geo.GeoPolygonBuilder:
526        """Start building a polygon using geographic coordinates.
527
528        Start building a closed shape using geographic coordinates (longitude and latitude provided
529        in degrees) instead of pixel coordinates.
530
531        Args:
532            longitude: The starting geographic point longitude coordinate or the E/W component of
533                the first point of the polygon.
534            latitude: The starting geographic point longitude coordinate or the N/S component of
535                the first point of the polygon.
536
537        Returns:
538            Object to build geographic polygons.
539        """
540        get_current_view = lambda: self._map_current_view
541        return sketchingpy.geo.GeoPolygonBuilder(longitude, latitude, get_current_view)
542
543    def push_map(self):
544        """Save current map view configuration.
545
546        Save current map pan, zoom, and pixel placement to the map history. This works as a stack
547        (like a stack of plates) where this puts a new plate on the top of the pile. This will leave
548        the current map configuration in the sketch unchanged.
549        """
550        self._map_view_stack.append(self._map_current_view)
551
552    def pop_map(self):
553        """Restore a previously saved map view configuration.
554
555        Restore the most recently saved map view configuration saved in style history, removing that
556        config from the history. This works as a stack (like a stack of plates) where the top of
557        the pile is taken off and restored, removing it from that stack. This will overwrite the
558        current map view configuration in the sketch.
559        """
560        if len(self._map_view_stack) == 0:
561            raise RuntimeError('Cannot pop an empty map view stack.')
562
563        self._map_current_view = self._map_view_stack.pop()
564
565    def parse_geojson(self, source: typing.Dict) -> typing.List[sketchingpy.geo.GeoPolygonBuilder]:
566        """Utility to parse GeoJSON into a series of GeoPolygons.
567
568        Utility to parse GeoJSON into a series of GeoPolygons which currently only supports
569        MultiPolygon and Polygon.
570
571        Args:
572            source: The loaded GeoJSON source to parse.
573
574        Returns:
575            Polygon builder which can be converted to a shape.
576        """
577        raw_polygons = sketchingpy.geo.parse_geojson(source)
578        return [self._build_geo_polygon_builder(polygon) for polygon in raw_polygons]
579
580    #########
581    # Image #
582    #########
583
584    def set_image_mode(self, mode: str):
585        """Specify how Sketchingpy should place images.
586
587        Determine how images' coordinates should be interpreted when drawing.
588
589        Args:
590            mode: String describing the mode to use.
591        """
592        self._get_current_state_machine().set_image_mode(mode)
593
594    def get_image(self, src: str) -> Image:
595        """Load an image file.
596
597        Load an image from the local file system or URL.
598
599        Args:
600            src: The location from which the file should be read.
601        """
602        raise NotImplementedError('Use implementor.')
603
604    def draw_image(self, x: float, y: float, image: Image):
605        """Draw an image at a location.
606
607        Draw a previously loaded image at a specific coordinate using its current size.
608
609        Args:
610            x: Horizontal coordinate at which to draw the image.
611            y: Vertical coordinate at which to draw the image.
612            image: The image to draw.
613        """
614        raise NotImplementedError('Use implementor.')
615
616    def save_image(self, path: str):
617        """Save an image file.
618
619        Save the sketch as an image file, either directly to the file system or as a download.
620
621        Args:
622            path: The location at which the file should be written.
623        """
624        raise NotImplementedError('Use implementor.')
625
626    ################
627    # Other Params #
628    ################
629
630    def set_angle_mode(self, mode: str):
631        """Indicate how angles should be provided to sketchingpy.
632
633        Change the units with which angles are expressed to Sketchingpy in transforms and shapes.
634
635        Args:
636            mode: The units (either 'degrees' or 'radians') in which to supply angles.
637        """
638        self._get_current_state_machine().set_angle_mode(mode)
639
640    #########
641    # State #
642    #########
643
644    def push_transform(self):
645        """Save current transformation state.
646
647        Save current sketch transformation state to the matrix history. This works as a stack (like
648        a stack of plates) where this puts a new plate on the top of the pile. This will leave the
649        current transformation matrix in the sketch unchanged.
650        """
651        raise NotImplementedError('Use implementor.')
652
653    def pop_transform(self):
654        """Restore a previously saved transformation state.
655
656        Restore the most recently transformation configuration saved in matrix history, removing
657        that "transform matrix" from the history. This works as a stack (like a stack of plates)
658        where the top of the pile is taken off and restored, removing it from that stack. This will
659        overwrite the current transformation configuration in the sketch.
660        """
661        raise NotImplementedError('Use implementor.')
662
663    def push_style(self):
664        """Save current styling.
665
666        Save current sketch styling to the style history. This works as a stack (like a stack of
667        plates) where this puts a new plate on the top of the pile. This will leave the current
668        style configuration in the sketch unchanged.
669        """
670        current = self._get_current_state_machine()
671        current_copy = copy.deepcopy(current)
672        self._state_machine_stack.append(current_copy)
673
674    def pop_style(self):
675        """Restore a previously saved styling.
676
677        Restore the most recently saved styling configuration saved in style history, removing that
678        styling from the history. This works as a stack (like a stack of plates) where the top of
679        the pile is taken off and restored, removing it from that stack. This will overwrite the
680        current style configuration in the sketch.
681        """
682        if len(self._state_machine_stack) == 0:
683            raise RuntimeError('Cannot pop an empty style stack.')
684
685        self._state_current_machine = self._state_machine_stack.pop()
686
687    ##########
688    # System #
689    ##########
690
691    def get_millis_shown(self) -> int:
692        """Get the milliseconds since the sketch was shown.
693
694        Returns:
695            The number of milliseconds since the sketch was shown or 0 if never shown.
696        """
697        return self._get_time_since_snapshot()
698
699    def print(self, message: str):
700        """Print a message to terminal or equivalent.
701
702        Args:
703            message: The string message to be printed.
704        """
705        print(message)
706
707    def get_native(self):
708        """Get a reference to the underlying native renderer object.
709
710        Returns:
711            Native render object.
712        """
713        raise NotImplementedError('Use implementor.')
714
715    def quit(self):
716        """Finish execution of the sketch.
717
718        Cause the sketch to stop execution.
719        """
720        raise NotImplementedError('Use implementor.')
721
722    def set_fps(self, rate: int):
723        """Indicate how fast the sketch should redraw.
724
725        Indicate a target frames per second that the sketch will take a "step" or redraw. Note that
726        this is a goal and, if the system fall behind, it will drop frames and cause the on_step
727        callback to be executed fewer times than the target.
728
729        Args:
730            rate: The number of frames to try to draw per second.
731        """
732        raise NotImplementedError('Use implementor.')
733
734    def set_title(self, title: str):
735        """Indicate the title to assign the window in the operating system.
736
737        Indicate the human-readable string title to assign to the sketch window.
738
739        Args:
740            title: The text of the title.
741        """
742        raise NotImplementedError('Use implementor.')
743
744    def show(self, ax=None):
745        """Show the sketch.
746
747        Show the sketch to the user and, if applicable, start the draw loop specified by set_fps.
748        For Sketch2DApp, will execute any waiting drawing instructions provided to the sketch prior
749        to showing. This is conceptually the same as "starting" the sketch.
750
751        Args:
752            ax: The container into which the sketch should be shown. Currently only supported for
753                Sketch2DStatic. Optional and ignored on most renderers.
754        """
755        raise NotImplementedError('Use implementor.')
756
757    def show_and_quit(self):
758        """Show the sketch and quit immediatley afterwards.
759
760        Show the sketch to the user and quit immediately afterwards, a routine potentially useful
761        for testing.
762        """
763        raise NotImplementedError('Use implementor.')
764
765    #############
766    # Transform #
767    #############
768
769    def translate(self, x: float, y: float):
770        """Change the location of the origin.
771
772        Change the transform matrix such that any drawing afterwards is moved by a set amount.
773
774        Args:
775            x: The number of pixels to offset horizontally.
776            y: The number of pixels to offset vertically.
777        """
778        raise NotImplementedError('Use implementor.')
779
780    def rotate(self, angle: float):
781        """Rotate around the current origin.
782
783        Change the transform matrix such that any drawing afterwards is rotated around the current
784        origin clock-wise.
785
786        Args:
787            angle: The angle by which to rotate.
788        """
789        raise NotImplementedError('Use implementor.')
790
791    def scale(self, scale: float):
792        """Scale outwards from the current origin.
793
794        Change the transform matrix such that any drawing afterwards is scaled from the current
795        origin.
796
797        Args:
798            scale: The factor by which to scale where values over 1 scale up and less than 1 scale
799                down. A value of 1 will have no effect.
800        """
801        raise NotImplementedError('Use implementor.')
802
803    ###########
804    # Support #
805    ###########
806
807    def _get_is_color_transparent(self, target: typing.Optional[str]) -> bool:
808        """Get if a color is transparent.
809
810        Args:
811            target: The color string to check if transparent. If None, assumes fully transparent.
812                Also assumes named colors like "blue" are fully opaque.
813        """
814        if target is None:
815            return True
816        elif target.startswith('#') and len(target) > 7 and target[-2:] != 'FF':
817            return True
818        else:
819            return False
820
821    def _build_geo_polygon_builder(self,
822        polygon: typing.List[typing.Tuple[float, float]]) -> sketchingpy.geo.GeoPolygonBuilder:
823        get_current_view = lambda: self._map_current_view
824        builder = sketchingpy.geo.GeoPolygonBuilder(polygon[0][0], polygon[0][1], get_current_view)
825
826        for point in polygon[1:]:
827            builder.add_coordinate(point[0], point[1])
828
829        return builder
830
831    def _convert_to_radians(self, angle: float) -> float:
832        current_angle_mode = self._get_current_state_machine()
833
834        if current_angle_mode == sketchingpy.const.RADIANS:
835            return angle
836        else:
837            return math.radians(angle)
838
839    def _create_map_view(self) -> sketchingpy.geo.GeoTransformation:
840        return sketchingpy.geo.GeoTransformation(
841            sketchingpy.geo.GeoPoint(sketchingpy.geo.BASE_LONGITUDE, sketchingpy.geo.BASE_LATITUDE),
842            sketchingpy.geo.PixelOffset(sketchingpy.geo.BASE_X, sketchingpy.geo.BASE_Y),
843            sketchingpy.geo.BASE_SCALE
844        )
845
846    def _create_state_machine(self) -> sketchingpy.state_struct.SketchStateMachine:
847        raise NotImplementedError('Use implementor.')
848
849    def _get_current_state_machine(self) -> sketchingpy.state_struct.SketchStateMachine:
850        return self._state_current_machine
851
852    def _snapshot_time(self):
853        self._start_millis = time.time() * 1000
854
855    def _get_time_since_snapshot(self):
856        if self._start_millis is None:
857            return 0
858        else:
859            return (time.time() * 1000) - self._start_millis

Abstract base class for a sketch renderer strategy.

Sketch( width: Optional[int] = None, height: Optional[int] = None, title: Optional[str] = None, loading_src: Optional[str] = None)
89    def __init__(self, width: typing.Optional[int] = None, height: typing.Optional[int] = None,
90        title: typing.Optional[str] = None, loading_src: typing.Optional[str] = None):
91        """Create a new sketch."""
92        self._state_current_machine = self._create_state_machine()
93        self._state_machine_stack: typing.List[sketchingpy.state_struct.SketchStateMachine] = []
94
95        self._map_current_view = self._create_map_view()
96        self._map_view_stack: typing.List[sketchingpy.geo.GeoTransformation] = []
97        self._start_millis = None

Create a new sketch.

def create_buffer( self, name: str, width: int, height: int, background: Optional[str] = None):
103    def create_buffer(self, name: str, width: int, height: int,
104        background: typing.Optional[str] = None):
105        """Create a new named in-memory (or equivalent) buffer.
106
107        Args:
108            name: The name of the buffer. If a prior buffer of this name exists, it will be
109                replaced.
110            width: The width of the buffer in pixels. In some renderers, the buffer will clip. In
111                others, out of buffer values may be drawn.
112            height: The height of the buffer in pixels. In some renderers, the buffer will clip. In
113                others, out of buffer values may be drawn.
114            background: The background to use for this buffer or None if transparent. Defaults to
115                None.
116        """
117        raise NotImplementedError('Use implementor.')

Create a new named in-memory (or equivalent) buffer.

Arguments:
  • name: The name of the buffer. If a prior buffer of this name exists, it will be replaced.
  • width: The width of the buffer in pixels. In some renderers, the buffer will clip. In others, out of buffer values may be drawn.
  • height: The height of the buffer in pixels. In some renderers, the buffer will clip. In others, out of buffer values may be drawn.
  • background: The background to use for this buffer or None if transparent. Defaults to None.
def enter_buffer(self, name: str):
119    def enter_buffer(self, name: str):
120        """Switch rendering context to a buffer, exiting current buffer if active.
121
122        Args:
123            name: The name of the buffer to which context should switch.
124        """
125        raise NotImplementedError('Use implementor.')

Switch rendering context to a buffer, exiting current buffer if active.

Arguments:
  • name: The name of the buffer to which context should switch.
def exit_buffer(self):
127    def exit_buffer(self):
128        """Exit the current offscreen buffer.
129
130        Exit the current offscreen buffer, returning to the actual sketch. This will act as a noop
131        if not currently in a buffer.
132        """
133        raise NotImplementedError('Use implementor.')

Exit the current offscreen buffer.

Exit the current offscreen buffer, returning to the actual sketch. This will act as a noop if not currently in a buffer.

def draw_buffer(self, x: float, y: float, name: str):
135    def draw_buffer(self, x: float, y: float, name: str):
136        """Draw an offscreen buffer to the current buffer or sketch.
137
138        Args:
139            x: The horizontal position in pixels at which the left should be drawn.
140            y: The vertical position in pixels at which the top should be drawn.
141            name: The name of the buffer to draw.
142        """
143        raise NotImplementedError('Use implementor.')

Draw an offscreen buffer to the current buffer or sketch.

Arguments:
  • x: The horizontal position in pixels at which the left should be drawn.
  • y: The vertical position in pixels at which the top should be drawn.
  • name: The name of the buffer to draw.
def set_fill(self, color_hex: str):
149    def set_fill(self, color_hex: str):
150        """Set the fill color.
151
152        Set the color to use for filling shapes and figures.
153
154        Args:
155            color_hex: Name of the color or a hex code.
156        """
157        self._get_current_state_machine().set_fill(color_hex)

Set the fill color.

Set the color to use for filling shapes and figures.

Arguments:
  • color_hex: Name of the color or a hex code.
def clear_fill(self):
159    def clear_fill(self):
160        """Clear the fill color.
161
162        Set the fill color to fully transparent so that only outlines of shapes and figures are
163        drawn.
164        """
165        self._get_current_state_machine().clear_fill()

Clear the fill color.

Set the fill color to fully transparent so that only outlines of shapes and figures are drawn.

def set_stroke(self, color_hex: str):
167    def set_stroke(self, color_hex: str):
168        """Set the stroke color.
169
170        Set the color to use for drawing outlines for shapes and figures as well as lines.
171
172        Args:
173            color_hex: Name of the color or a hex code.
174        """
175        self._get_current_state_machine().set_stroke(color_hex)

Set the stroke color.

Set the color to use for drawing outlines for shapes and figures as well as lines.

Arguments:
  • color_hex: Name of the color or a hex code.
def clear_stroke(self):
177    def clear_stroke(self):
178        """Clear the stroke color.
179
180        Set the stroke width to zero, disabling the drawing of outlines for shapes and figures as
181        well as lines.
182        """
183        self._get_current_state_machine().clear_stroke()

Clear the stroke color.

Set the stroke width to zero, disabling the drawing of outlines for shapes and figures as well as lines.

def get_keyboard(self) -> Optional[sketchingpy.control_struct.Keyboard]:
189    def get_keyboard(self) -> typing.Optional[sketchingpy.control_struct.Keyboard]:
190        """Get access to the keyboard.
191
192        Get access to the keyboard currently registered with the operating system for the sketch.
193        Different sketches running at the same time may have different keyboards depending on focus
194        or OS configuration.
195
196        Returns:
197            Current keyboard or None if not found / supported.
198        """
199        raise NotImplementedError('Use implementor.')

Get access to the keyboard.

Get access to the keyboard currently registered with the operating system for the sketch. Different sketches running at the same time may have different keyboards depending on focus or OS configuration.

Returns:

Current keyboard or None if not found / supported.

def get_mouse(self) -> Optional[sketchingpy.control_struct.Mouse]:
201    def get_mouse(self) -> typing.Optional[sketchingpy.control_struct.Mouse]:
202        """Get access to the mouse.
203
204        Get access to the mouse currently registered with the operating system for the sketch.
205        Different sketches running at the same time may have different mouse objects depending on
206        focus or OS configuration. Note that the mouse may also be emulated if the device uses a
207        touch screen.
208
209        Returns:
210            Current mouse or None if not found / supported.
211        """
212        raise NotImplementedError('Use implementor.')

Get access to the mouse.

Get access to the mouse currently registered with the operating system for the sketch. Different sketches running at the same time may have different mouse objects depending on focus or OS configuration. Note that the mouse may also be emulated if the device uses a touch screen.

Returns:

Current mouse or None if not found / supported.

def get_data_layer(self) -> Optional[sketchingpy.data_struct.DataLayer]:
218    def get_data_layer(self) -> typing.Optional[sketchingpy.data_struct.DataLayer]:
219        """Get access to reading and writing data.
220
221        Open access to the file system, network, or browser to read or write data.
222
223        Returns:
224            Facade for data access or None if not supported or insufficient permissions.
225        """
226        raise NotImplementedError('Use implementor.')

Get access to reading and writing data.

Open access to the file system, network, or browser to read or write data.

Returns:

Facade for data access or None if not supported or insufficient permissions.

def get_dialog_layer(self) -> Optional[sketchingpy.dialog_struct.DialogLayer]:
232    def get_dialog_layer(self) -> typing.Optional[sketchingpy.dialog_struct.DialogLayer]:
233        """Get access to rendering and using simple dialogs.
234
235        Open access to a simple dialog prefabricated UI system to show alerts, prompts, and other
236        dialog boxes.
237
238        Returns:
239            Facade for rendering dialogs or None if not supported or insufficient permissions.
240        """
241        raise NotImplementedError('Use implementor.')

Get access to rendering and using simple dialogs.

Open access to a simple dialog prefabricated UI system to show alerts, prompts, and other dialog boxes.

Returns:

Facade for rendering dialogs or None if not supported or insufficient permissions.

def clear(self, color: str):
247    def clear(self, color: str):
248        """Clear the sketch to a color.
249
250        Peform the equivalent of drawing a rectangle the size of the sketch without stroke and with
251        the given fill color.
252
253        Args:
254            color: The color to use in clearing.
255        """
256        raise NotImplementedError('Use implementor.')

Clear the sketch to a color.

Peform the equivalent of drawing a rectangle the size of the sketch without stroke and with the given fill color.

Arguments:
  • color: The color to use in clearing.
def set_arc_mode(self, mode: str):
258    def set_arc_mode(self, mode: str):
259        """Specify how Sketchingpy should interpret the position and size arguments for arcs.
260
261        Determine how arcs should be placed within the sketch and how they should be sized.
262
263        Args:
264            mode: String describing the mode to use.
265        """
266        self._get_current_state_machine().set_arc_mode(mode)

Specify how Sketchingpy should interpret the position and size arguments for arcs.

Determine how arcs should be placed within the sketch and how they should be sized.

Arguments:
  • mode: String describing the mode to use.
def draw_arc( self, x1: float, y1: float, x2: float, y2: float, a1: float, a2: float):
268    def draw_arc(self, x1: float, y1: float, x2: float, y2: float, a1: float, a2: float):
269        """Draw a partial ellipse using starting and ending angles.
270
271        Using starting and ending angles, draw a partial ellipse which is either drawn outside line
272        only (stroke) and / or filled from the center of that ellipse.
273
274        Args:
275            x1: The x location at which to draw the arc.
276            y1: The y location at which to draw the arc.
277            x2: Horizontal size.
278            y2: Vertical size.
279            a1: Starting angle.
280            a2: Ending angle.
281        """
282        raise NotImplementedError('Use implementor.')

Draw a partial ellipse using starting and ending angles.

Using starting and ending angles, draw a partial ellipse which is either drawn outside line only (stroke) and / or filled from the center of that ellipse.

Arguments:
  • x1: The x location at which to draw the arc.
  • y1: The y location at which to draw the arc.
  • x2: Horizontal size.
  • y2: Vertical size.
  • a1: Starting angle.
  • a2: Ending angle.
def set_ellipse_mode(self, mode: str):
284    def set_ellipse_mode(self, mode: str):
285        """Specify how Sketchingpy should interpret the position and size arguments.
286
287        Determine how arcs should be placed within the sketch and how they should be sized for
288        ellipses.
289
290        Args:
291            mode: String describing the mode to use.
292        """
293        self._get_current_state_machine().set_ellipse_mode(mode)

Specify how Sketchingpy should interpret the position and size arguments.

Determine how arcs should be placed within the sketch and how they should be sized for ellipses.

Arguments:
  • mode: String describing the mode to use.
def draw_ellipse(self, x1: float, y1: float, x2: float, y2: float):
295    def draw_ellipse(self, x1: float, y1: float, x2: float, y2: float):
296        """Draw a circle or ellipse.
297
298        Draw an ellipse or, in the case of equal width and height, a circle.
299
300        Args:
301            x1: The x location at which to draw the ellipse.
302            y1: The y location at which to draw the ellipse.
303            x2: Horizontal size.
304            y2: Vertical size.
305        """
306        raise NotImplementedError('Use implementor.')

Draw a circle or ellipse.

Draw an ellipse or, in the case of equal width and height, a circle.

Arguments:
  • x1: The x location at which to draw the ellipse.
  • y1: The y location at which to draw the ellipse.
  • x2: Horizontal size.
  • y2: Vertical size.
def draw_line(self, x1: float, y1: float, x2: float, y2: float):
308    def draw_line(self, x1: float, y1: float, x2: float, y2: float):
309        """Draw a simple line.
310
311        Draw a line between two points.
312
313        Args:
314            x1: The x coordinate from which the line should be drawn.
315            y1: The y coordinate from which the line should be drawn.
316            x2: The x coordinate to which the line should be drawn.
317            y2: The y coordinate to which the line should be drawn.
318        """
319        raise NotImplementedError('Use implementor.')

Draw a simple line.

Draw a line between two points.

Arguments:
  • x1: The x coordinate from which the line should be drawn.
  • y1: The y coordinate from which the line should be drawn.
  • x2: The x coordinate to which the line should be drawn.
  • y2: The y coordinate to which the line should be drawn.
def set_rect_mode(self, mode: str):
321    def set_rect_mode(self, mode: str):
322        """Specify how Sketchingpy should interpret the position and size arguments.
323
324        Determine how arcs should be placed within the sketch and how they should be sized for
325        rectangles.
326
327        Args:
328            mode: String describing the mode to use.
329        """
330        self._get_current_state_machine().set_rect_mode(mode)

Specify how Sketchingpy should interpret the position and size arguments.

Determine how arcs should be placed within the sketch and how they should be sized for rectangles.

Arguments:
  • mode: String describing the mode to use.
def draw_rect(self, x1: float, y1: float, x2: float, y2: float):
332    def draw_rect(self, x1: float, y1: float, x2: float, y2: float):
333        """Draw a rectangle.
334
335        Draw a rectangle or, if width and height are the same, a square.
336
337        Args:
338            x1: The x location at which to draw the rectangle.
339            y1: The y location at which to draw the rectangle.
340            x2: Horizontal size.
341            y2: Vertical size.
342        """
343        raise NotImplementedError('Use implementor.')

Draw a rectangle.

Draw a rectangle or, if width and height are the same, a square.

Arguments:
  • x1: The x location at which to draw the rectangle.
  • y1: The y location at which to draw the rectangle.
  • x2: Horizontal size.
  • y2: Vertical size.
def draw_pixel(self, x: float, y: float):
345    def draw_pixel(self, x: float, y: float):
346        """Draw a single pixel.
347
348        Draw a rectangle with a width of zero and height of zero, changing a single pixel.
349
350        Args:
351            x: The x location at which to draw the rectangle.
352            y: The y location at which to draw the rectangle.
353        """
354        self.push_style()
355        self.set_rect_mode('corner')
356        self.clear_stroke()
357        self.draw_rect(x, y, 0, 0)
358        self.pop_style()

Draw a single pixel.

Draw a rectangle with a width of zero and height of zero, changing a single pixel.

Arguments:
  • x: The x location at which to draw the rectangle.
  • y: The y location at which to draw the rectangle.
def start_shape(self, x: float, y: float) -> sketchingpy.shape_struct.Shape:
360    def start_shape(self, x: float, y: float) -> sketchingpy.shape_struct.Shape:
361        """Create a new shape.
362
363        Create a new shape which consists of multiple line or curve segments and which can be either
364        open (stroke only) or closed (can be filled).
365
366        Args:
367            x: The starting x position of the shape.
368            y: The starting y position of the shape.
369        """
370        return sketchingpy.shape_struct.Shape(x, y)

Create a new shape.

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

Arguments:
  • x: The starting x position of the shape.
  • y: The starting y position of the shape.
def draw_shape(self, shape: sketchingpy.shape_struct.Shape):
372    def draw_shape(self, shape: sketchingpy.shape_struct.Shape):
373        """Draw a shape.
374
375        Draw a shape which consists of multiple line or curve segments and which can be either open
376        (stroke only) or closed (can be filled).
377
378        Args:
379            shape: The shape to draw.
380        """
381        raise NotImplementedError('Use implementor.')

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).

Arguments:
  • shape: The shape to draw.
def set_stroke_weight(self, weight: float):
383    def set_stroke_weight(self, weight: float):
384        """Set the stroke color.
385
386        Set the width of stroke for drawing outlines for shapes and figures as well as lines.
387
388        Args:
389            size: Number of pixels for the stroke weight.
390        """
391        self._get_current_state_machine().set_stroke_weight(weight)

Set the stroke color.

Set the width of stroke for drawing outlines for shapes and figures as well as lines.

Arguments:
  • size: Number of pixels for the stroke weight.
def set_text_font(self, identifier: str, size: float):
393    def set_text_font(self, identifier: str, size: float):
394        """Set the type and size of text to draw.
395
396        Set the size and font to use for drawing text.
397
398        Args:
399            font: Path to the TTF font file.
400            size: Size of the font (px).
401        """
402        font = sketchingpy.state_struct.Font(identifier, size)
403        self._get_current_state_machine().set_text_font(font)

Set the type and size of text to draw.

Set the size and font to use for drawing text.

Arguments:
  • font: Path to the TTF font file.
  • size: Size of the font (px).
def set_text_align(self, horizontal_align: str, vertical_align: str = 'baseline'):
405    def set_text_align(self, horizontal_align: str, vertical_align: str = 'baseline'):
406        """Indicate the alignment of text to be drawn.
407
408        Indicate how the text should be aligned horizontally and vertically.
409
410        Args:
411            horizontal: Argument for horizontal alignment.
412            vertical: Optional additional argument for vertical alignment. If not provided, will
413                default to baseline.
414        """
415        align_struct = sketchingpy.state_struct.TextAlign(horizontal_align, vertical_align)
416        self._get_current_state_machine().set_text_align(align_struct)

Indicate the alignment of text to be drawn.

Indicate how the text should be aligned horizontally and vertically.

Arguments:
  • horizontal: Argument for horizontal alignment.
  • vertical: Optional additional argument for vertical alignment. If not provided, will default to baseline.
def draw_text(self, x: float, y: float, content: str):
418    def draw_text(self, x: float, y: float, content: str):
419        """Draw text using the current font.
420
421        Draw text using the current font and alignment.
422
423        Args:
424            x: The x coordinate at which to draw the text.
425            y: The y coordinate at which to draw the text.
426            text: The string to draw.
427        """
428        raise NotImplementedError('Use implementor.')

Draw text using the current font.

Draw text using the current font and alignment.

Arguments:
  • x: The x coordinate at which to draw the text.
  • y: The y coordinate at which to draw the text.
  • text: The string to draw.
def on_step(self, callback: Callable[[Sketch], NoneType]):
434    def on_step(self, callback: StepCallback):
435        """Callback for when the sketch ends execution.
436
437        Register a callback for when the sketch redraws. This function should expect a single
438        parameter which is the sketch redrawing.
439
440        Args:
441            callback: The function to invoke when the sketch stops execution.
442        """
443        raise NotImplementedError('Use implementor.')

Callback for when the sketch ends execution.

Register a callback for when the sketch redraws. This function should expect a single parameter which is the sketch redrawing.

Arguments:
  • callback: The function to invoke when the sketch stops execution.
def on_quit(self, callback: Callable[[Sketch], NoneType]):
445    def on_quit(self, callback: QuitCallback):
446        """Callback for when the sketch ends execution.
447
448        Register a callback for when the sketch terminates.
449
450        Args:
451            callback: The function to invoke when the sketch stops execution.
452        """
453        raise NotImplementedError('Use implementor.')

Callback for when the sketch ends execution.

Register a callback for when the sketch terminates.

Arguments:
  • callback: The function to invoke when the sketch stops execution.
def set_map_pan(self, longitude: float, latitude: float):
459    def set_map_pan(self, longitude: float, latitude: float):
460        """Indicate where point should be at the center of the map geographically.
461
462        Indicate a latitude and longitude point which is where the map projection should be
463        centerered geographically.
464
465        Args:
466            longitude: The center longitude in degrees.
467            latitude: The center latitude in degrees.
468        """
469        self._map_current_view = sketchingpy.geo.GeoTransformation(
470            sketchingpy.geo.GeoPoint(longitude, latitude),
471            self._map_current_view.get_pixel_offset(),
472            self._map_current_view.get_scale()
473        )

Indicate where point should be at the center of the map geographically.

Indicate a latitude and longitude point which is where the map projection should be centerered geographically.

Arguments:
  • longitude: The center longitude in degrees.
  • latitude: The center latitude in degrees.
def set_map_zoom(self, zoom: float):
475    def set_map_zoom(self, zoom: float):
476        """Indicate the map zoom level.
477
478        Specify the map scaling factor or map "zoom" level.
479
480        Args:
481            zoom: The zoom level to use.
482        """
483        self._map_current_view = sketchingpy.geo.GeoTransformation(
484            self._map_current_view.get_geo_offset(),
485            self._map_current_view.get_pixel_offset(),
486            zoom
487        )

Indicate the map zoom level.

Specify the map scaling factor or map "zoom" level.

Arguments:
  • zoom: The zoom level to use.
def set_map_placement(self, x: float, y: float):
489    def set_map_placement(self, x: float, y: float):
490        """Indicate where in the sketch the map view should be drawn.
491
492        Indicate where in the sketch in terms of pixel coordinates the map view should be centered
493        such that the map pan latitude and longitude map to this coordinate position in pixel space.
494
495        Args:
496            x: The horizontal coordinate in pixels.
497            y: The vertical coordinate in pixels.
498        """
499        self._map_current_view = sketchingpy.geo.GeoTransformation(
500            self._map_current_view.get_geo_offset(),
501            sketchingpy.geo.PixelOffset(x, y),
502            self._map_current_view.get_scale()
503        )

Indicate where in the sketch the map view should be drawn.

Indicate where in the sketch in terms of pixel coordinates the map view should be centered such that the map pan latitude and longitude map to this coordinate position in pixel space.

Arguments:
  • x: The horizontal coordinate in pixels.
  • y: The vertical coordinate in pixels.
def convert_geo_to_pixel(self, longitude: float, latitude: float) -> Tuple[float, float]:
505    def convert_geo_to_pixel(self, longitude: float,
506        latitude: float) -> typing.Tuple[float, float]:
507        """Convert a geographic location to a pixel coordinate.
508
509        Convert a longitude / latitude coordinate pair in degrees to sketch coordinates in pixels
510        using the current map view parameters.
511
512        Args:
513            longitude: The longitude to convert in degrees.
514            latitude: The latitude to convert in degrees.
515
516        Returns:
517            Tuple with two elements: x coordinate and y coordinate.
518        """
519        point = sketchingpy.geo.GeoPoint(longitude, latitude, )
520        x = point.get_x(transform=self._map_current_view)
521        y = point.get_y(transform=self._map_current_view)
522        return (x, y)

Convert a geographic location to a pixel coordinate.

Convert a longitude / latitude coordinate pair in degrees to sketch coordinates in pixels using the current map view parameters.

Arguments:
  • longitude: The longitude to convert in degrees.
  • latitude: The latitude to convert in degrees.
Returns:

Tuple with two elements: x coordinate and y coordinate.

def start_geo_polygon( self, longitude: float, latitude: float) -> sketchingpy.geo.GeoPolygonBuilder:
524    def start_geo_polygon(self, longitude: float,
525        latitude: float) -> sketchingpy.geo.GeoPolygonBuilder:
526        """Start building a polygon using geographic coordinates.
527
528        Start building a closed shape using geographic coordinates (longitude and latitude provided
529        in degrees) instead of pixel coordinates.
530
531        Args:
532            longitude: The starting geographic point longitude coordinate or the E/W component of
533                the first point of the polygon.
534            latitude: The starting geographic point longitude coordinate or the N/S component of
535                the first point of the polygon.
536
537        Returns:
538            Object to build geographic polygons.
539        """
540        get_current_view = lambda: self._map_current_view
541        return sketchingpy.geo.GeoPolygonBuilder(longitude, latitude, get_current_view)

Start building a polygon using geographic coordinates.

Start building a closed shape using geographic coordinates (longitude and latitude provided in degrees) instead of pixel coordinates.

Arguments:
  • longitude: The starting geographic point longitude coordinate or the E/W component of the first point of the polygon.
  • latitude: The starting geographic point longitude coordinate or the N/S component of the first point of the polygon.
Returns:

Object to build geographic polygons.

def push_map(self):
543    def push_map(self):
544        """Save current map view configuration.
545
546        Save current map pan, zoom, and pixel placement to the map history. This works as a stack
547        (like a stack of plates) where this puts a new plate on the top of the pile. This will leave
548        the current map configuration in the sketch unchanged.
549        """
550        self._map_view_stack.append(self._map_current_view)

Save current map view configuration.

Save current map pan, zoom, and pixel placement to the map history. This works as a stack (like a stack of plates) where this puts a new plate on the top of the pile. This will leave the current map configuration in the sketch unchanged.

def pop_map(self):
552    def pop_map(self):
553        """Restore a previously saved map view configuration.
554
555        Restore the most recently saved map view configuration saved in style history, removing that
556        config from the history. This works as a stack (like a stack of plates) where the top of
557        the pile is taken off and restored, removing it from that stack. This will overwrite the
558        current map view configuration in the sketch.
559        """
560        if len(self._map_view_stack) == 0:
561            raise RuntimeError('Cannot pop an empty map view stack.')
562
563        self._map_current_view = self._map_view_stack.pop()

Restore a previously saved map view configuration.

Restore the most recently saved map view configuration saved in style history, removing that config from the history. This works as a stack (like a stack of plates) where the top of the pile is taken off and restored, removing it from that stack. This will overwrite the current map view configuration in the sketch.

def parse_geojson(self, source: Dict) -> List[sketchingpy.geo.GeoPolygonBuilder]:
565    def parse_geojson(self, source: typing.Dict) -> typing.List[sketchingpy.geo.GeoPolygonBuilder]:
566        """Utility to parse GeoJSON into a series of GeoPolygons.
567
568        Utility to parse GeoJSON into a series of GeoPolygons which currently only supports
569        MultiPolygon and Polygon.
570
571        Args:
572            source: The loaded GeoJSON source to parse.
573
574        Returns:
575            Polygon builder which can be converted to a shape.
576        """
577        raw_polygons = sketchingpy.geo.parse_geojson(source)
578        return [self._build_geo_polygon_builder(polygon) for polygon in raw_polygons]

Utility to parse GeoJSON into a series of GeoPolygons.

Utility to parse GeoJSON into a series of GeoPolygons which currently only supports MultiPolygon and Polygon.

Arguments:
  • source: The loaded GeoJSON source to parse.
Returns:

Polygon builder which can be converted to a shape.

def set_image_mode(self, mode: str):
584    def set_image_mode(self, mode: str):
585        """Specify how Sketchingpy should place images.
586
587        Determine how images' coordinates should be interpreted when drawing.
588
589        Args:
590            mode: String describing the mode to use.
591        """
592        self._get_current_state_machine().set_image_mode(mode)

Specify how Sketchingpy should place images.

Determine how images' coordinates should be interpreted when drawing.

Arguments:
  • mode: String describing the mode to use.
def get_image(self, src: str) -> Image:
594    def get_image(self, src: str) -> Image:
595        """Load an image file.
596
597        Load an image from the local file system or URL.
598
599        Args:
600            src: The location from which the file should be read.
601        """
602        raise NotImplementedError('Use implementor.')

Load an image file.

Load an image from the local file system or URL.

Arguments:
  • src: The location from which the file should be read.
def draw_image(self, x: float, y: float, image: Image):
604    def draw_image(self, x: float, y: float, image: Image):
605        """Draw an image at a location.
606
607        Draw a previously loaded image at a specific coordinate using its current size.
608
609        Args:
610            x: Horizontal coordinate at which to draw the image.
611            y: Vertical coordinate at which to draw the image.
612            image: The image to draw.
613        """
614        raise NotImplementedError('Use implementor.')

Draw an image at a location.

Draw a previously loaded image at a specific coordinate using its current size.

Arguments:
  • x: Horizontal coordinate at which to draw the image.
  • y: Vertical coordinate at which to draw the image.
  • image: The image to draw.
def save_image(self, path: str):
616    def save_image(self, path: str):
617        """Save an image file.
618
619        Save the sketch as an image file, either directly to the file system or as a download.
620
621        Args:
622            path: The location at which the file should be written.
623        """
624        raise NotImplementedError('Use implementor.')

Save an image file.

Save the sketch as an image file, either directly to the file system or as a download.

Arguments:
  • path: The location at which the file should be written.
def set_angle_mode(self, mode: str):
630    def set_angle_mode(self, mode: str):
631        """Indicate how angles should be provided to sketchingpy.
632
633        Change the units with which angles are expressed to Sketchingpy in transforms and shapes.
634
635        Args:
636            mode: The units (either 'degrees' or 'radians') in which to supply angles.
637        """
638        self._get_current_state_machine().set_angle_mode(mode)

Indicate how angles should be provided to sketchingpy.

Change the units with which angles are expressed to Sketchingpy in transforms and shapes.

Arguments:
  • mode: The units (either 'degrees' or 'radians') in which to supply angles.
def push_transform(self):
644    def push_transform(self):
645        """Save current transformation state.
646
647        Save current sketch transformation state to the matrix history. This works as a stack (like
648        a stack of plates) where this puts a new plate on the top of the pile. This will leave the
649        current transformation matrix in the sketch unchanged.
650        """
651        raise NotImplementedError('Use implementor.')

Save current transformation state.

Save current sketch transformation state to the matrix history. This works as a stack (like a stack of plates) where this puts a new plate on the top of the pile. This will leave the current transformation matrix in the sketch unchanged.

def pop_transform(self):
653    def pop_transform(self):
654        """Restore a previously saved transformation state.
655
656        Restore the most recently transformation configuration saved in matrix history, removing
657        that "transform matrix" from the history. This works as a stack (like a stack of plates)
658        where the top of the pile is taken off and restored, removing it from that stack. This will
659        overwrite the current transformation configuration in the sketch.
660        """
661        raise NotImplementedError('Use implementor.')

Restore a previously saved transformation state.

Restore the most recently transformation configuration saved in matrix history, removing that "transform matrix" from the history. This works as a stack (like a stack of plates) where the top of the pile is taken off and restored, removing it from that stack. This will overwrite the current transformation configuration in the sketch.

def push_style(self):
663    def push_style(self):
664        """Save current styling.
665
666        Save current sketch styling to the style history. This works as a stack (like a stack of
667        plates) where this puts a new plate on the top of the pile. This will leave the current
668        style configuration in the sketch unchanged.
669        """
670        current = self._get_current_state_machine()
671        current_copy = copy.deepcopy(current)
672        self._state_machine_stack.append(current_copy)

Save current styling.

Save current sketch styling to the style history. This works as a stack (like a stack of plates) where this puts a new plate on the top of the pile. This will leave the current style configuration in the sketch unchanged.

def pop_style(self):
674    def pop_style(self):
675        """Restore a previously saved styling.
676
677        Restore the most recently saved styling configuration saved in style history, removing that
678        styling from the history. This works as a stack (like a stack of plates) where the top of
679        the pile is taken off and restored, removing it from that stack. This will overwrite the
680        current style configuration in the sketch.
681        """
682        if len(self._state_machine_stack) == 0:
683            raise RuntimeError('Cannot pop an empty style stack.')
684
685        self._state_current_machine = self._state_machine_stack.pop()

Restore a previously saved styling.

Restore the most recently saved styling configuration saved in style history, removing that styling from the history. This works as a stack (like a stack of plates) where the top of the pile is taken off and restored, removing it from that stack. This will overwrite the current style configuration in the sketch.

def get_millis_shown(self) -> int:
691    def get_millis_shown(self) -> int:
692        """Get the milliseconds since the sketch was shown.
693
694        Returns:
695            The number of milliseconds since the sketch was shown or 0 if never shown.
696        """
697        return self._get_time_since_snapshot()

Get the milliseconds since the sketch was shown.

Returns:

The number of milliseconds since the sketch was shown or 0 if never shown.

def print(self, message: str):
699    def print(self, message: str):
700        """Print a message to terminal or equivalent.
701
702        Args:
703            message: The string message to be printed.
704        """
705        print(message)

Print a message to terminal or equivalent.

Arguments:
  • message: The string message to be printed.
def get_native(self):
707    def get_native(self):
708        """Get a reference to the underlying native renderer object.
709
710        Returns:
711            Native render object.
712        """
713        raise NotImplementedError('Use implementor.')

Get a reference to the underlying native renderer object.

Returns:

Native render object.

def quit(self):
715    def quit(self):
716        """Finish execution of the sketch.
717
718        Cause the sketch to stop execution.
719        """
720        raise NotImplementedError('Use implementor.')

Finish execution of the sketch.

Cause the sketch to stop execution.

def set_fps(self, rate: int):
722    def set_fps(self, rate: int):
723        """Indicate how fast the sketch should redraw.
724
725        Indicate a target frames per second that the sketch will take a "step" or redraw. Note that
726        this is a goal and, if the system fall behind, it will drop frames and cause the on_step
727        callback to be executed fewer times than the target.
728
729        Args:
730            rate: The number of frames to try to draw per second.
731        """
732        raise NotImplementedError('Use implementor.')

Indicate how fast the sketch should redraw.

Indicate a target frames per second that the sketch will take a "step" or redraw. Note that this is a goal and, if the system fall behind, it will drop frames and cause the on_step callback to be executed fewer times than the target.

Arguments:
  • rate: The number of frames to try to draw per second.
def set_title(self, title: str):
734    def set_title(self, title: str):
735        """Indicate the title to assign the window in the operating system.
736
737        Indicate the human-readable string title to assign to the sketch window.
738
739        Args:
740            title: The text of the title.
741        """
742        raise NotImplementedError('Use implementor.')

Indicate the title to assign the window in the operating system.

Indicate the human-readable string title to assign to the sketch window.

Arguments:
  • title: The text of the title.
def show(self, ax=None):
744    def show(self, ax=None):
745        """Show the sketch.
746
747        Show the sketch to the user and, if applicable, start the draw loop specified by set_fps.
748        For Sketch2DApp, will execute any waiting drawing instructions provided to the sketch prior
749        to showing. This is conceptually the same as "starting" the sketch.
750
751        Args:
752            ax: The container into which the sketch should be shown. Currently only supported for
753                Sketch2DStatic. Optional and ignored on most renderers.
754        """
755        raise NotImplementedError('Use implementor.')

Show the sketch.

Show the sketch to the user and, if applicable, start the draw loop specified by set_fps. For Sketch2DApp, will execute any waiting drawing instructions provided to the sketch prior to showing. This is conceptually the same as "starting" the sketch.

Arguments:
  • ax: The container into which the sketch should be shown. Currently only supported for Sketch2DStatic. Optional and ignored on most renderers.
def show_and_quit(self):
757    def show_and_quit(self):
758        """Show the sketch and quit immediatley afterwards.
759
760        Show the sketch to the user and quit immediately afterwards, a routine potentially useful
761        for testing.
762        """
763        raise NotImplementedError('Use implementor.')

Show the sketch and quit immediatley afterwards.

Show the sketch to the user and quit immediately afterwards, a routine potentially useful for testing.

def translate(self, x: float, y: float):
769    def translate(self, x: float, y: float):
770        """Change the location of the origin.
771
772        Change the transform matrix such that any drawing afterwards is moved by a set amount.
773
774        Args:
775            x: The number of pixels to offset horizontally.
776            y: The number of pixels to offset vertically.
777        """
778        raise NotImplementedError('Use implementor.')

Change the location of the origin.

Change the transform matrix such that any drawing afterwards is moved by a set amount.

Arguments:
  • x: The number of pixels to offset horizontally.
  • y: The number of pixels to offset vertically.
def rotate(self, angle: float):
780    def rotate(self, angle: float):
781        """Rotate around the current origin.
782
783        Change the transform matrix such that any drawing afterwards is rotated around the current
784        origin clock-wise.
785
786        Args:
787            angle: The angle by which to rotate.
788        """
789        raise NotImplementedError('Use implementor.')

Rotate around the current origin.

Change the transform matrix such that any drawing afterwards is rotated around the current origin clock-wise.

Arguments:
  • angle: The angle by which to rotate.
def scale(self, scale: float):
791    def scale(self, scale: float):
792        """Scale outwards from the current origin.
793
794        Change the transform matrix such that any drawing afterwards is scaled from the current
795        origin.
796
797        Args:
798            scale: The factor by which to scale where values over 1 scale up and less than 1 scale
799                down. A value of 1 will have no effect.
800        """
801        raise NotImplementedError('Use implementor.')

Scale outwards from the current origin.

Change the transform matrix such that any drawing afterwards is scaled from the current origin.

Arguments:
  • scale: The factor by which to scale where values over 1 scale up and less than 1 scale down. A value of 1 will have no effect.
def reorder_coords(x1: float, y1: float, x2: float, y2: float) -> List[float]:
862def reorder_coords(x1: float, y1: float, x2: float, y2: float) -> typing.List[float]:
863    """Reorder coordinates so that the first comes before the second.
864
865    Args:
866        x1: The first x coordinate.
867        y1: The first y coordinate.
868        x2: The second x coordinate.
869        y2: The second y coordinate.
870    Returns:
871        List of form [min_x, min_y, max_x, max_y].
872    """
873    x_coords = [x1, x2]
874    y_coords = [y1, y2]
875    x_coords.sort()
876    y_coords.sort()
877    return [x_coords[0], y_coords[0], x_coords[1], y_coords[1]]

Reorder coordinates so that the first comes before the second.

Arguments:
  • x1: The first x coordinate.
  • y1: The first y coordinate.
  • x2: The second x coordinate.
  • y2: The second y coordinate.
Returns:

List of form [min_x, min_y, max_x, max_y].

def get_font_name(font, sep_char: str) -> str:
880def get_font_name(font, sep_char: str) -> str:
881    """Get the web version of a font.
882
883    Args:
884        font: The font to convert to a web font identifier.
885        sep_char: Path separator char.
886
887    Returns:
888        Web font identifier.
889    """
890    identifier = font.get_identifier()
891    identifier = identifier.split(sep_char)[-1]
892
893    if identifier.endswith('.ttf') or identifier.endswith('.otf'):
894        identifier = identifier[:-4]
895
896    return '%dpx %s' % (int(font.get_size()), identifier)

Get the web version of a font.

Arguments:
  • font: The font to convert to a web font identifier.
  • sep_char: Path separator char.
Returns:

Web font identifier.