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