simetri.canvas.draw

Canvas object uses these methods to draw shapes and text.

   1"""Canvas object uses these methods to draw shapes and text."""
   2
   3from math import cos, sin
   4from typing_extensions import Self, Sequence
   5
   6from ..geometry.geometry import homogenize, ellipse_point, close_points2
   7from ..graphics.all_enums import (
   8    Anchor,
   9    BackStyle,
  10    Drawable,
  11    FrameShape,
  12    PathOperation,
  13    Types,
  14    drawable_types,
  15    TexLoc,
  16)
  17from ..colors import colors
  18from ..tikz.tikz import scope_code_required
  19from ..graphics.sketch import (
  20    ArcSketch,
  21    BatchSketch,
  22    BezierSketch,
  23    CircleSketch,
  24    EllipseSketch,
  25    LineSketch,
  26    PathSketch,
  27    PatternSketch,
  28    RectSketch,
  29    ShapeSketch,
  30    TagSketch,
  31    TexSketch
  32)
  33from ..settings.settings import defaults
  34from ..canvas.style_map import line_style_map, shape_style_map, group_args
  35from ..helpers.illustration import Tag
  36from ..graphics.affine import identity_matrix
  37from ..graphics.shape import Shape
  38from ..graphics.common import Point
  39from ..geometry.bezier import bezier_points
  40from ..geometry.ellipse import elliptic_arc_points
  41from ..graphics.affine import rotation_matrix, translation_matrix
  42from ..helpers.utilities import decompose_transformations
  43
  44
  45Color = colors.Color
  46
  47
  48def help_lines(
  49    self,
  50    pos: Point = None,
  51    width: float = None,
  52    height: float = None,
  53    step_size=None,
  54    cs_size: float = None,
  55    **kwargs,
  56):
  57    """
  58    Draw a square grid with the given size.
  59
  60    Args:
  61        pos (Point, optional): Position of the grid. Defaults to None.
  62        width (float, optional): Length of the grid along the x-axis. Defaults to None.
  63        height (float, optional): Length of the grid along the y-axis. Defaults to None.
  64        step_size (optional): Step size for the grid. Defaults to None.
  65        cs_size (float, optional): Size of the coordinate system. Defaults to None.
  66        **kwargs: Additional keyword arguments.
  67
  68    Returns:
  69        Self: The canvas object.
  70    """
  71    self.grid(pos, width, height, step_size, **kwargs)
  72    if cs_size > 0:
  73        self.draw_CS(cs_size, **kwargs)
  74    return self
  75
  76
  77def arc(
  78    self,
  79    center: Point,
  80    radius_x: float,
  81    radius_y: float,
  82    start_angle: float,
  83    span_angle: float,
  84    rot_angle: float,
  85    n_points: int = None,
  86    **kwargs,
  87) -> None:
  88    """
  89    Draw an arc with the given center, radius, start and end angles in radians.
  90    Arc is drawn in counterclockwise direction from start to end.
  91
  92    Args:
  93        center (Point): Center of the arc.
  94        radius_x (float): Radius of the arc.
  95        radius_y (float): Second radius of the arc.
  96        start_angle (float): Start angle of the arc in radians.
  97        end_angle (float): End angle of the arc in radians.
  98
  99        rot_angle (float): Rotation angle of the arc.
 100        **kwargs: Additional keyword arguments.
 101    """
 102    if radius_y is None:
 103        radius_y = radius_x
 104    vertices = elliptic_arc_points(
 105        center, radius_x, radius_y, start_angle, span_angle, n_points
 106    )
 107    if rot_angle != 0:
 108        vertices = homogenize(vertices) @ rotation_matrix(rot_angle, center)
 109    self._all_vertices.extend(vertices.tolist() + [center])
 110
 111    sketch = ArcSketch(vertices=vertices, xform_matrix=self.xform_matrix)
 112    for attrib_name in shape_style_map:
 113        if hasattr(sketch, attrib_name):
 114            attrib_value = self.resolve_property(sketch, attrib_name)
 115        else:
 116            attrib_value = defaults[attrib_name]
 117        setattr(sketch, attrib_name, attrib_value)
 118    for k, v in kwargs.items():
 119        setattr(sketch, k, v)
 120    self.active_page.sketches.append(sketch)
 121
 122    return self
 123
 124
 125def bezier(self, control_points, **kwargs):
 126    """
 127    Draw a Bezier curve with the given control points.
 128
 129    Args:
 130        control_points: Control points for the Bezier curve.
 131        **kwargs: Additional keyword arguments.
 132
 133    Returns:
 134        Self: The canvas object.
 135    """
 136    self._all_vertices.extend(control_points)
 137    sketch = BezierSketch(control_points, self.xform_matrix)
 138    for attrib_name in shape_style_map:
 139        if hasattr(sketch, attrib_name):
 140            attrib_value = self._resolve_property(sketch, attrib_name)
 141        else:
 142            attrib_value = defaults[attrib_name]
 143        setattr(sketch, attrib_name, attrib_value)
 144    self.active_page.sketches.append(sketch)
 145
 146    for k, v in kwargs.items():
 147        setattr(sketch, k, v)
 148    return self
 149
 150
 151def circle(self, center: Point, radius: float, **kwargs) -> None:
 152    """
 153    Draw a circle with the given center and radius.
 154
 155    Args:
 156        center (Point): Center of the circle.
 157        radius (float): Radius of the circle.
 158        **kwargs: Additional keyword arguments.
 159    """
 160    x, y = center[:2]
 161    p1 = x - radius, y - radius
 162    p2 = x + radius, y + radius
 163    p3 = x - radius, y + radius
 164    p4 = x + radius, y - radius
 165    self._all_vertices.extend([p1, p2, p3, p4])
 166    sketch = CircleSketch(center, radius, self.xform_matrix)
 167    for attrib_name in shape_style_map:
 168        if hasattr(sketch, attrib_name):
 169            attrib_value = self.resolve_property(sketch, attrib_name)
 170        else:
 171            attrib_value = defaults[attrib_name]
 172        setattr(sketch, attrib_name, attrib_value)
 173    for k, v in kwargs.items():
 174        setattr(sketch, k, v)
 175    self.active_page.sketches.append(sketch)
 176
 177    return self
 178
 179
 180def ellipse(self, center: Point, width: float, height, angle, **kwargs) -> None:
 181    """
 182    Draw an ellipse with the given center and x_radius and y_radius.
 183
 184    Args:
 185        center (Point): Center of the ellipse.
 186        width (float): Width of the ellipse.
 187        height: Height of the ellipse.
 188        angle: Angle of the ellipse.
 189        **kwargs: Additional keyword arguments.
 190    """
 191    x, y = center[:2]
 192    x_radius = width / 2
 193    y_radius = height / 2
 194    p1 = x - x_radius, y - y_radius
 195    p2 = x + x_radius, y + y_radius
 196    p3 = x - x_radius, y + y_radius
 197    p4 = x + x_radius, y - y_radius
 198    self._all_vertices.extend([p1, p2, p3, p4])
 199    sketch = EllipseSketch(center, x_radius, y_radius, angle, self.xform_matrix)
 200    for attrib_name in shape_style_map:
 201        if hasattr(sketch, attrib_name):
 202            attrib_value = self.resolve_property(sketch, attrib_name)
 203        else:
 204            attrib_value = defaults[attrib_name]
 205        setattr(sketch, attrib_name, attrib_value)
 206    for k, v in kwargs.items():
 207        setattr(sketch, k, v)
 208    self.active_page.sketches.append(sketch)
 209
 210    return self
 211
 212
 213def text(
 214    self,
 215    txt: str,
 216    pos: Point,
 217    font_family: str = None,
 218    font_size: int = None,
 219    font_color: Color = None,
 220    anchor: Anchor = None,
 221    **kwargs,
 222) -> None:
 223    """
 224    Draw the given text at the given position.
 225
 226    Args:
 227        txt (str): Text to be drawn.
 228        pos (Point): Position of the text.
 229        font_family (str, optional): Font family of the text. Defaults to None.
 230        font_size (int, optional): Font size of the text. Defaults to None.
 231        font_color (Color, optional): Font color of the text. Defaults to None.
 232        anchor (Anchor, optional): Anchor of the text. Defaults to None.
 233        **kwargs: Additional keyword arguments.
 234    """
 235    # first create a Tag object
 236    tag_obj = Tag(
 237        txt,
 238        pos,
 239        font_family=font_family,
 240        font_size=font_size,
 241        font_color=font_color,
 242        anchor=anchor,
 243        **kwargs,
 244    )
 245    tag_obj.draw_frame = False
 246    # then call get_tag_sketch to create a TagSketch object
 247    sketch = create_sketch(tag_obj, self, **kwargs)
 248    self.active_page.sketches.append(sketch)
 249
 250    return self
 251
 252
 253def line(self, start, end, **kwargs):
 254    """
 255    Draw a line segment from start to end.
 256
 257    Args:
 258        start: Starting point of the line.
 259        end: Ending point of the line.
 260        **kwargs: Additional keyword arguments.
 261
 262    Returns:
 263        Self: The canvas object.
 264    """
 265    self._sketch_xform_matrix = self.xform_matrix
 266    line_shape = Shape([start, end], closed=False, **kwargs)
 267    line_sketch = create_sketch(line_shape, self, **kwargs)
 268    self.active_page.sketches.append(line_sketch)
 269    self._sketch_xform_matrix = identity_matrix()
 270    return self
 271
 272
 273def rectangle(self, center: Point, width: float, height: float, angle: float, **kwargs):
 274    """
 275    Draw a rectangle with the given center, width, height and angle.
 276
 277    Args:
 278        center (Point): Center of the rectangle.
 279        width (float): Width of the rectangle.
 280        height (float): Height of the rectangle.
 281        angle (float): Angle of the rectangle.
 282        **kwargs: Additional keyword arguments.
 283
 284    Returns:
 285        Self: The canvas object.
 286    """
 287    w2 = width / 2
 288    h2 = height / 2
 289    p1 = center[0] - w2, center[1] + h2
 290    p2 = center[0] - w2, center[1] - h2
 291    p3 = center[0] + w2, center[1] - h2
 292    p4 = center[0] + w2, center[1] + h2
 293    points = homogenize([p1, p2, p3, p4]) @ rotation_matrix(angle, center)
 294    rect_shape = Shape(points.tolist(), closed=True, **kwargs)
 295    rect_sketch = create_sketch(rect_shape, self, **kwargs)
 296    self.active_page.sketches.append(rect_sketch)
 297
 298    return self
 299
 300
 301def draw_CS(self, size: float = None, **kwargs):
 302    """
 303    Draw a coordinate system with the given size.
 304
 305    Args:
 306        size (float, optional): Size of the coordinate system. Defaults to None.
 307        **kwargs: Additional keyword arguments.
 308
 309    Returns:
 310        Self: The canvas object.
 311    """
 312    if size is None:
 313        size = defaults["CS_size"]
 314    if "colors" in kwargs:
 315        x_color, y_color = kwargs["colors"]
 316        del kwargs["colors"]
 317    else:
 318        x_color = defaults["CS_x_color"]
 319        y_color = defaults["CS_y_color"]
 320    if "line_width" not in kwargs:
 321        kwargs["line_width"] = defaults["CS_line_width"]
 322    self.line((0, 0), (size, 0), line_color=x_color, **kwargs)
 323    self.line((0, 0), (0, size), line_color=y_color, **kwargs)
 324    if "line_color" not in kwargs:
 325        kwargs["line_color"] = defaults["CS_origin_color"]
 326    self.circle((0, 0), radius=defaults["CS_origin_size"], **kwargs)
 327
 328    return self
 329
 330
 331def lines(self, points, **kwargs):
 332    """
 333    Draw connected line segments.
 334
 335    Args:
 336        points: Points to be connected.
 337        **kwargs: Additional keyword arguments.
 338
 339    Returns:
 340        Self: The canvas object.
 341    """
 342    self._all_vertices.extend(points)
 343    sketch = LineSketch(points, self.xform_matrix, **kwargs)
 344    for attrib_name in line_style_map:
 345        attrib_value = self._resolve_property(sketch, attrib_name)
 346        setattr(sketch, attrib_name, attrib_value)
 347    self.active_page.sketches.append(sketch)
 348
 349    return self
 350
 351def insert_code(self, code: str, location: TexLoc = TexLoc.NONE) -> Self:
 352        """
 353        Insert code into the canvas.
 354
 355        Args:
 356            code (str): The code to insert.
 357
 358        Returns:
 359            Self: The canvas object.
 360        """
 361        active_sketches = self.active_page.sketches
 362        sketch = TexSketch(code, location=location)
 363        active_sketches.append(sketch)
 364
 365        return self
 366
 367def draw_bbox(self, bbox, **kwargs):
 368    """
 369    Draw the bounding box object.
 370
 371    Args:
 372        bbox: Bounding box to be drawn.
 373        **kwargs: Additional keyword arguments.
 374
 375    Returns:
 376        Self: The canvas object.
 377    """
 378    sketch = create_sketch(bbox, self, **kwargs)
 379    self.active_page.sketches.append(sketch)
 380
 381    return self
 382
 383
 384def draw_pattern(self, pattern, **kwargs):
 385    """
 386    Draw the pattern object.
 387
 388    Args:
 389        pattern: Pattern object to be drawn.
 390        **kwargs: Additional keyword arguments.
 391
 392    Returns:
 393        Self: The canvas object.
 394    """
 395    sketch = create_sketch(pattern, self, **kwargs)
 396    self.active_page.sketches.append(sketch)
 397
 398    return self
 399
 400
 401def draw_hobby(
 402    self,
 403    points: Sequence[Point],
 404    controls: Sequence[Point],
 405    cyclic: bool = False,
 406    **kwargs,
 407):
 408    """Draw a Hobby curve through the given points using the control points.
 409
 410    Args:
 411        points (Sequence[Point]): Points through which the curve passes.
 412        controls (Sequence[Point]): Control points for the curve.
 413        cyclic (bool, optional): Whether the curve is cyclic. Defaults to False.
 414        **kwargs: Additional keyword arguments.
 415    """
 416    n = len(points)
 417    if cyclic:
 418        for i in range(n):
 419            ind = i * 2
 420            bezier_pnts = bezier_points(
 421                points[i], *controls[ind : ind + 2], points[(i + 1) % n], 20
 422            )
 423            bezier_ = Shape(bezier_pnts)
 424            self.draw(bezier_, **kwargs)
 425    else:
 426        for i in range(len(points) - 1):
 427            ind = i * 2
 428            bezier_pnts = bezier_points(
 429                points[i], *controls[ind : ind + 2], points[i + 1], 20
 430            )
 431            bezier_ = Shape(bezier_pnts)
 432            self.draw(bezier_, **kwargs)
 433
 434
 435def draw_lace(self, lace, **kwargs):
 436    """Draw the lace object.
 437
 438    Args:
 439        lace: Lace object to be drawn.
 440        **kwargs: Additional keyword arguments.
 441
 442    Returns:
 443        Self: The canvas object.
 444    """
 445    keys = list(lace.fragment_groups.keys())
 446    keys.sort()
 447    if lace.swatch is not None:
 448        n_colors = len(lace.swatch)
 449    for i, key in enumerate(keys):
 450        if lace.swatch is not None:
 451            fill_color = colors.Color(*lace.swatch[i % n_colors])
 452            kwargs["fill_color"] = fill_color
 453        for fragment in lace.fragment_groups[key]:
 454            self.active_page.sketches.append(create_sketch(fragment, self, **kwargs))
 455    for plait in lace.plaits:
 456        if lace.swatch is not None:
 457            fill_color = colors.white
 458            kwargs["fill_color"] = fill_color
 459        else:
 460            kwargs["fill_color"] = None
 461        self.active_page.sketches.append(create_sketch(plait, self, **kwargs))
 462        self._all_vertices.extend(plait.corners)
 463
 464    return self
 465
 466
 467def draw_dimension(self, item, **kwargs):
 468    """Draw the dimension object.
 469
 470    Args:
 471        item: Dimension object to be drawn.
 472        **kwargs: Additional keyword arguments.
 473
 474    Returns:
 475        Self: The canvas object.
 476    """
 477    for shape in item.all_shapes:
 478        self._all_vertices.extend(shape.corners)
 479    for ext in [item.ext1, item.ext2, item.ext3]:
 480        if ext:
 481            ext_sketch = create_sketch(ext, self, **kwargs)
 482            self.active_page.sketches.append(ext_sketch)
 483    if item.dim_line:
 484        dim_sketch = create_sketch(item.dim_line, self, **kwargs)
 485        self.active_page.sketches.extend(dim_sketch)
 486    if item.arrow1:
 487        arrow_sketch = create_sketch(item.arrow1, self, **kwargs)
 488        self.active_page.sketches.extend(arrow_sketch)
 489        self.active_page.sketches.append(create_sketch(item.mid_line, self))
 490    if item.arrow2:
 491        arrow_sketch = create_sketch(item.arrow2, self, **kwargs)
 492        self.active_page.sketches.extend(arrow_sketch)
 493    x, y = item.text_pos[:2]
 494
 495    tag = Tag(item.text, (x, y), font_size=item.font_size, **kwargs)
 496    tag_sketch = create_sketch(tag, self, **kwargs)
 497    tag_sketch.draw_frame = True
 498    tag_sketch.frame_shape = FrameShape.CIRCLE
 499    tag_sketch.fill = True
 500    tag_sketch.font_color = colors.black
 501    tag_sketch.frame_back_style = BackStyle.COLOR
 502    tag_sketch.back_style = BackStyle.COLOR
 503    tag_sketch.frame_back_color = colors.white
 504    tag_sketch.back_color = colors.white
 505    tag_sketch.stroke = False
 506    self.active_page.sketches.append(tag_sketch)
 507
 508    return self
 509
 510
 511def grid(
 512    self,
 513    pos=(0, 0),
 514    width: float = None,
 515    height: float = None,
 516    step_size=None,
 517    **kwargs,
 518):
 519    """Draw a square grid with the given size.
 520
 521    Args:
 522        pos (tuple, optional): Position of the grid. Defaults to (0, 0).
 523        width (float, optional): Length of the grid along the x-axis. Defaults to None.
 524        height (float, optional): Length of the grid along the y-axis. Defaults to None.
 525        step_size (optional): Step size for the grid. Defaults to None.
 526        **kwargs: Additional keyword arguments.
 527
 528    Returns:
 529        Self: The canvas object.
 530    """
 531    x, y = pos[:2]
 532    if width is None:
 533        width = defaults["grid_size"]
 534        height = defaults["grid_size"]
 535    if "line_width" not in kwargs:
 536        kwargs["line_width"] = defaults["grid_line_width"]
 537    if "line_color" not in kwargs:
 538        kwargs["line_color"] = defaults["grid_line_color"]
 539    if "line_dash_array" not in kwargs:
 540        kwargs["line_dash_array"] = defaults["grid_line_dash_array"]
 541    # draw x-axis
 542    # self.line((-size, 0), (size, 0), **kwargs)
 543    line_y = Shape([(x, y), (x + width, y)], **kwargs)
 544    line_x = Shape([(x, y), (x, y + height)], **kwargs)
 545    lines_x = line_y.translate(0, step_size, reps=int(height / step_size))
 546    lines_y = line_x.translate(step_size, 0, reps=int(width / step_size))
 547    self.draw(lines_x)
 548    self.draw(lines_y)
 549    return self
 550
 551
 552regular_sketch_types = [
 553    Types.ARC,
 554    Types.ARC_ARROW,
 555    Types.BATCH,
 556    Types.BEZIER,
 557    Types.CIRCLE,
 558    Types.CIRCULAR_GRID,
 559    Types.DIVISION,
 560    Types.DOT,
 561    Types.DOTS,
 562    Types.ELLIPSE,
 563    Types.FRAGMENT,
 564    Types.HEX_GRID,
 565    Types.LINPATH,
 566    Types.MIXED_GRID,
 567    Types.OUTLINE,
 568    Types.OVERLAP,
 569    Types.PARALLEL_POLYLINE,
 570    Types.PLAIT,
 571    Types.POLYLINE,
 572    Types.Q_BEZIER,
 573    Types.RECTANGLE,
 574    Types.SECTION,
 575    Types.SEGMENT,
 576    Types.SHAPE,
 577    Types.SINE_WAVE,
 578    Types.SQUARE_GRID,
 579    Types.STAR,
 580    Types.TAG,
 581]
 582
 583
 584def extend_vertices(canvas, item):
 585    """Extend the list of all vertices with the vertices of the given item.
 586
 587    Args:
 588        canvas: Canvas object.
 589        item: Item whose vertices are to be extended.
 590    """
 591    all_vertices = canvas._all_vertices
 592    if item.subtype == Types.DOTS:
 593        vertices = [x.pos for x in item.all_shapes]
 594        vertices = [x[:2] for x in homogenize(vertices) @ canvas._sketch_xform_matrix]
 595        all_vertices.extend(vertices)
 596    elif item.subtype == Types.DOT:
 597        vertices = [item.pos]
 598        vertices = [x[:2] for x in homogenize(vertices) @ canvas._sketch_xform_matrix]
 599        all_vertices.extend(vertices)
 600    elif item.subtype == Types.ARROW:
 601        for shape in item.all_shapes:
 602            all_vertices.extend(shape.corners)
 603    elif item.subtype == Types.LACE:
 604        for plait in item.plaits:
 605            all_vertices.extend(plait.corners)
 606        for fragment in item.fragments:
 607            all_vertices.extend(fragment.corners)
 608    elif item.subtype == Types.PATTERN:
 609        all_vertices.extend(item.get_all_vertices())
 610    else:
 611        corners = [x[:2] for x in homogenize(item.corners) @ canvas._sketch_xform_matrix]
 612        all_vertices.extend(corners)
 613
 614
 615def draw(self, item: Drawable, **kwargs) -> Self:
 616    """The item is drawn on the canvas with the given style properties.
 617
 618    Args:
 619        item (Drawable): Item to be drawn.
 620        **kwargs: Additional keyword arguments.
 621
 622    Returns:
 623        Self: The canvas object.
 624    """
 625    # check if the item has any points
 626    if not item:
 627        return self
 628
 629    active_sketches = self.active_page.sketches
 630    subtype = item.subtype
 631    extend_vertices(self, item)
 632    if subtype in regular_sketch_types:
 633        sketches = get_sketches(item, self, **kwargs)
 634        if sketches:
 635            active_sketches.extend(sketches)
 636    elif subtype == Types.PATTERN:
 637        draw_pattern(self, item, **kwargs)
 638    elif subtype == Types.DIMENSION:
 639        self.draw_dimension(item, **kwargs)
 640    elif subtype == Types.ARROW:
 641        for head in item.heads:
 642            active_sketches.append(create_sketch(head, self, **kwargs))
 643        active_sketches.append(create_sketch(item.line, self, **kwargs))
 644    elif subtype == Types.LACE:
 645        self.draw_lace(item, **kwargs)
 646    elif subtype == Types.BOUNDING_BOX:
 647        draw_bbox(self, item, **kwargs)
 648    return self
 649
 650
 651def get_sketches(item: Drawable, canvas: "Canvas" = None, **kwargs) -> list["Sketch"]:
 652    """Create sketches from the given item and return them as a list.
 653
 654    Args:
 655        item (Drawable): Item to be sketched.
 656        canvas (Canvas, optional): Canvas object. Defaults to None.
 657        **kwargs: Additional keyword arguments.
 658
 659    Returns:
 660        list[Sketch]: List of sketches.
 661    """
 662    if not (item.visible and item.active):
 663        res = []
 664    elif item.subtype in drawable_types:
 665        sketches = create_sketch(item, canvas, **kwargs)
 666        if isinstance(sketches, list):
 667            res = sketches
 668        elif sketches is not None:
 669            res = [sketches]
 670        else:
 671            res = []
 672    else:
 673        res = []
 674    return res
 675
 676
 677def set_shape_sketch_style(sketch, item, canvas, linear=False, **kwargs):
 678    """Set the style properties of the sketch.
 679
 680    Args:
 681        sketch: Sketch object.
 682        item: Item whose style properties are to be set.
 683        canvas: Canvas object.
 684        linear (bool, optional): Whether the style is linear. Defaults to False.
 685        **kwargs: Additional keyword arguments.
 686    """
 687    if linear:
 688        style_map = line_style_map
 689    else:
 690        style_map = shape_style_map
 691
 692    for attrib_name in style_map:
 693        attrib_value = canvas._resolve_property(item, attrib_name)
 694        setattr(sketch, attrib_name, attrib_value)
 695
 696    sketch.visible = item.visible
 697    sketch.active = item.active
 698    sketch.closed = item.closed
 699    sketch.fill = item.fill
 700    sketch.stroke = item.stroke
 701
 702    for k, v in kwargs.items():
 703        setattr(sketch, k, v)
 704
 705def get_verts_in_new_pos(item, **kwargs):
 706    """
 707    Get the vertices of the item in a new position.
 708
 709    Args:
 710        item: Item whose vertices are to be obtained.
 711        **kwargs: Additional keyword arguments.
 712
 713    Returns:
 714        list: List of vertices in the new position.
 715    """
 716    if "pos" in kwargs:
 717        x, y = item.midpoint[:2]
 718        x1, y1 = kwargs["pos"][:2]
 719        dx = x1 - x
 720        dy = y1 - y
 721        trans_mat = translation_matrix(dx, dy)
 722        vertices = item.primary_points.homogen_coords @ trans_mat
 723        vertices = vertices[:, :2].tolist()
 724    else:
 725        vertices = item.vertices
 726
 727    return vertices
 728
 729
 730def create_sketch(item, canvas, **kwargs):
 731    """Create a sketch from the given item.
 732
 733    Args:
 734        item: Item to be sketched.
 735        canvas: Canvas object.
 736        **kwargs: Additional keyword arguments.
 737
 738    Returns:
 739        Sketch: Created sketch.
 740    """
 741    if not (item.visible and item.active):
 742        return None
 743
 744    def get_tag_sketch(item, canvas, **kwargs):
 745        """Create a TagSketch from the given item.
 746
 747        Args:
 748            item: Item to be sketched.
 749            canvas: Canvas object.
 750            **kwargs: Additional keyword arguments.
 751
 752        Returns:
 753            TagSketch: Created TagSketch.
 754        """
 755        if "pos" in kwargs:
 756            pos = kwargs["pos"]
 757        else:
 758            pos = item.pos
 759
 760        sketch = TagSketch(text=item.text, pos=pos, anchor=item.anchor)
 761        for attrib_name in item._style_map:
 762            if attrib_name == "fill_color":
 763                if item.fill_color in [None, colors.black]:
 764                    setattr(sketch, "frame_back_color", defaults["frame_back_color"])
 765                else:
 766                    setattr(sketch, "frame_back_color", item.fill_color)
 767                continue
 768            attrib_value = canvas._resolve_property(item, attrib_name)
 769            setattr(sketch, attrib_name, attrib_value)
 770        sketch.text_width = item.text_width
 771        sketch.visible = item.visible
 772        sketch.active = item.active
 773        for k, v in kwargs.items():
 774            setattr(sketch, k, v)
 775        return sketch
 776
 777    def get_ellipse_sketch(item, canvas, **kwargs):
 778        """Create an EllipseSketch from the given item.
 779
 780        Args:
 781            item: Item to be sketched.
 782            canvas: Canvas object.
 783            **kwargs: Additional keyword arguments.
 784
 785        Returns:
 786            EllipseSketch: Created EllipseSketch.
 787        """
 788        if "pos" in kwargs:
 789            center = kwargs["pos"]
 790        else:
 791            center = item.center
 792
 793        sketch = EllipseSketch(
 794            center,
 795            item.a,
 796            item.b,
 797            item.angle,
 798            xform_matrix=canvas.xform_matrix,
 799            **kwargs,
 800        )
 801        set_shape_sketch_style(sketch, item, canvas, **kwargs)
 802
 803        return sketch
 804
 805    def get_pattern_sketch(item, canvas, **kwargs):
 806        """Create a PatternSketch from the given item.
 807
 808        Args:
 809            item: Item to be sketched.
 810            canvas: Canvas object.
 811            **kwargs: Additional keyword arguments.
 812
 813        Returns:
 814            PatternSketch: Created PatternSketch.
 815        """
 816        sketch = PatternSketch(item, xform_matrix=canvas.xform_matrix, **kwargs)
 817        set_shape_sketch_style(sketch, item, canvas, **kwargs)
 818
 819        return sketch
 820
 821    def get_circle_sketch(item, canvas, **kwargs):
 822        """Create a CircleSketch from the given item.
 823
 824        Args:
 825            item: Item to be sketched.
 826            canvas: Canvas object.
 827            **kwargs: Additional keyword arguments.
 828
 829        Returns:
 830            CircleSketch: Created CircleSketch.
 831        """
 832        if "pos" in kwargs:
 833            center = kwargs["pos"]
 834        else:
 835            center = item.center
 836        sketch = CircleSketch(
 837            center, item.radius, xform_matrix=canvas.xform_matrix
 838        )
 839        set_shape_sketch_style(sketch, item, canvas, **kwargs)
 840
 841        return sketch
 842
 843    def get_dots_sketch(item, canvas, **kwargs):
 844        """Create sketches for dots from the given item.
 845
 846        Args:
 847            item: Item to be sketched.
 848            canvas: Canvas object.
 849            **kwargs: Additional keyword arguments.
 850
 851        Returns:
 852            list: List of created sketches.
 853        """
 854        vertices = [x.pos for x in item.all_shapes]
 855        fill_color = item[0].fill_color
 856        radius = item[0].radius
 857        marker_size = item[0].marker_size
 858        marker_type = item[0].marker_type
 859        item = Shape(
 860            vertices,
 861            fill_color=fill_color,
 862            markers_only=True,
 863            draw_markers=True,
 864            marker_size=marker_size,
 865            marker_radius=radius,
 866            marker_type=marker_type,
 867        )
 868        sketches = get_sketches(item, canvas, **kwargs)
 869
 870        return sketches
 871
 872    def get_arc_sketch(item, canvas, **kwargs):
 873        """Create an ArcSketch from the given item.
 874
 875        Args:
 876            item: Item to be sketched.
 877            canvas: Canvas object.
 878            **kwargs: Additional keyword arguments.
 879
 880        Returns:
 881            ArcSketch: Created ArcSketch.
 882        """
 883
 884        # vertices = get_verts_in_new_pos(item, **kwargs)
 885        sketch = ArcSketch(item.vertices, xform_matrix=canvas._sketch_xform_matrix)
 886        set_shape_sketch_style(sketch, item, canvas, **kwargs)
 887
 888        return sketch
 889
 890    def get_lace_sketch(item, canvas, **kwargs):
 891        """Create sketches for lace from the given item.
 892
 893        Args:
 894            item: Item to be sketched.
 895            canvas: Canvas object.
 896            **kwargs: Additional keyword arguments.
 897
 898        Returns:
 899            list: List of created sketches.
 900        """
 901        sketches = [get_sketch(frag, canvas, **kwargs) for frag in item.fragments]
 902        sketches.extend([get_sketch(plait, canvas, **kwargs) for plait in item.plaits])
 903        return sketches
 904
 905    def get_batch_sketch(item, canvas, **kwargs):
 906        """Create a BatchSketch from the given item.
 907
 908        Args:
 909            item: Item to be sketched.
 910            canvas: Canvas object.
 911            **kwargs: Additional keyword arguments.
 912
 913        Returns:
 914            BatchSketch or list: Created BatchSketch or list of sketches.
 915        """
 916        if scope_code_required(item):
 917            sketches = []
 918            for element in item.elements:
 919                if element.visible and element.active:
 920                    sketches.extend(get_sketches(element, canvas, **kwargs))
 921
 922            sketch = BatchSketch(sketches=sketches)
 923            for arg in group_args:
 924                setattr(sketch, arg, getattr(item, arg))
 925
 926            res = sketch
 927        else:
 928            sketches = []
 929            for element in item.elements:
 930                if hasattr(element, "visible") and hasattr(element, "active"):
 931                    if element.visible and element.active:
 932                        sketches.extend(get_sketches(element, canvas, **kwargs))
 933
 934            res = sketches
 935
 936        return res
 937
 938    def get_path_sketch(item, canvas, **kwargs):
 939        """Create sketches for a path from the given item.
 940
 941        Args:
 942            item: Item to be sketched.
 943            canvas: Canvas object.
 944            **kwargs: Additional keyword arguments.
 945
 946        Returns:
 947            list: List of created sketches.
 948        """
 949
 950        def extend_verts(obj, vertices):
 951            obj_vertices = obj.vertices
 952            if obj_vertices:
 953                if vertices and close_points2(vertices[-1], obj_vertices[0]):
 954                    obj_vertices = obj_vertices[1:]
 955                vertices.extend(obj_vertices)
 956
 957        path_op = PathOperation
 958        linears = [
 959            path_op.ARC,
 960            path_op.ARC_TO,
 961            path_op.BLEND_ARC,
 962            path_op.BLEND_CUBIC,
 963            path_op.BLEND_QUAD,
 964            path_op.BLEND_SINE,
 965            path_op.CUBIC_TO,
 966            path_op.FORWARD,
 967            path_op.H_LINE,
 968            path_op.HOBBY_TO,
 969            path_op.QUAD_TO,
 970            path_op.R_LINE,
 971            path_op.SEGMENTS,
 972            path_op.SINE,
 973            path_op.V_LINE,
 974            path_op.LINE_TO,
 975        ]
 976        sketches = []
 977        vertices = []
 978        for i, op in enumerate(item.operations):
 979            if op.subtype in linears:
 980                obj = item.objects[i]
 981                extend_verts(obj, vertices)
 982            elif op.subtype in [path_op.MOVE_TO, path_op.R_MOVE]:
 983                if i == 0:
 984                    continue
 985                shape = Shape(vertices)
 986                sketch = create_sketch(shape, canvas, **kwargs)
 987                if sketch:
 988                    sketch.visible = item.visible
 989                    sketch.active = item.active
 990                    sketches.append(sketch)
 991                vertices = []
 992            elif op.subtype == path_op.CLOSE:
 993                shape = Shape(vertices, closed=True)
 994                sketch = create_sketch(shape, canvas, **kwargs)
 995                if sketch:
 996                    sketch.visible = item.visible
 997                    sketch.active = item.active
 998                    sketches.append(sketch)
 999                vertices = []
1000        if vertices:
1001            shape = Shape(vertices)
1002            sketch = create_sketch(shape, canvas, **kwargs)
1003            sketches.append(sketch)
1004
1005        if "handles" in kwargs and kwargs["handles"]:
1006            handles = kwargs["handles"]
1007            del kwargs["handles"]
1008            for handle in item.handles:
1009                shape = Shape(handle)
1010                shape.subtype = Types.HANDLE
1011                handle_sketches = create_sketch(shape, canvas, **kwargs)
1012                sketches.extend(handle_sketches)
1013
1014        for sketch in sketches:
1015            item.closed = sketch.closed
1016            set_shape_sketch_style(sketch, item, canvas, **kwargs)
1017
1018        return sketches
1019
1020    def get_bbox_sketch(item, canvas, **kwargs):
1021        """Create a bounding box sketch from the given item.
1022
1023        Args:
1024            item: Item to be sketched.
1025            canvas: Canvas object.
1026            **kwargs: Additional keyword arguments.
1027
1028        Returns:
1029            ShapeSketch: Created bounding box sketch.
1030        """
1031        nround = defaults["tikz_nround"]
1032        vertices = [(round(x[0], nround), round(x[1], nround)) for x in item.corners]
1033        if not vertices:
1034            return None
1035
1036        sketch = ShapeSketch(vertices, canvas._sketch_xform_matrix)
1037        sketch.subtype = Types.BBOX_SKETCH
1038        sketch.visible = True
1039        sketch.active = True
1040        sketch.closed = True
1041        sketch.fill = False
1042        sketch.stroke = True
1043        sketch.line_color = colors.gray
1044        sketch.line_width = 1
1045        sketch.line_dash_array = [3, 3]
1046        sketch.draw_markers = False
1047        return sketch
1048
1049    def get_handle_sketch(item, canvas, **kwargs):
1050        """Create handle sketches from the given item.
1051
1052        Args:
1053            item: Item to be sketched.
1054            canvas: Canvas object.
1055            **kwargs: Additional keyword arguments.
1056
1057        Returns:
1058            list: List of created handle sketches.
1059        """
1060        nround = defaults["tikz_nround"]
1061        vertices = [(round(x[0], nround), round(x[1], nround)) for x in item.vertices]
1062        if not vertices:
1063            return None
1064        if 'pos' in kwargs:
1065            x, y = item.midpoint[:2]
1066            x1, y1 = kwargs["pos"][:2]
1067            dx = x1 - x
1068            dy = y1 - y
1069            vertices = [(x+dx, y+dy) for x, y in vertices]
1070        sketches = []
1071        sketch = ShapeSketch(vertices, canvas._sketch_xform_matrix)
1072        sketch.subtype = Types.HANDLE
1073        sketch.closed = False
1074        set_shape_sketch_style(sketch, item, canvas, **kwargs)
1075        sketches.append(sketch)
1076        temp_item = Shape()
1077        temp_item.closed = True
1078        handle1 = RectSketch(item.vertices[0], 3, 3, canvas._sketch_xform_matrix)
1079        set_shape_sketch_style(handle1, temp_item, canvas, **kwargs)
1080        handle2 = RectSketch(item.vertices[-1], 3, 3, canvas._sketch_xform_matrix)
1081        set_shape_sketch_style(handle2, temp_item, canvas, **kwargs)
1082        sketches.extend([handle1, handle2])
1083
1084        return sketches
1085
1086    def get_sketch(item, canvas, **kwargs):
1087        """Create a sketch from the given item.
1088
1089        Args:
1090            item: Item to be sketched.
1091            canvas: Canvas object.
1092            **kwargs: Additional keyword arguments.
1093
1094        Returns:
1095            ShapeSketch: Created sketch.
1096        """
1097        if not item.vertices:
1098            return None
1099
1100        # vertices = get_verts_in_new_pos(item, **kwargs)
1101        nround = defaults["tikz_nround"]
1102        vertices = [
1103            (round(x[0], nround), round(x[1], nround)) for x in item.vertices
1104        ]
1105
1106        sketch = ShapeSketch(vertices, canvas._sketch_xform_matrix)
1107        set_shape_sketch_style(sketch, item, canvas, **kwargs)
1108
1109        return sketch
1110
1111    d_subtype_sketch = {
1112        Types.ARC: get_arc_sketch,
1113        Types.ARC_ARROW: get_batch_sketch,
1114        Types.ARROW: get_batch_sketch,
1115        Types.ARROW_HEAD: get_sketch,
1116        Types.BATCH: get_batch_sketch,
1117        Types.BEZIER: get_sketch,
1118        Types.BOUNDING_BOX: get_bbox_sketch,
1119        Types.CIRCLE: get_circle_sketch,
1120        Types.CIRCULAR_GRID: get_batch_sketch,
1121        Types.DIVISION: get_sketch,
1122        Types.DOT: get_circle_sketch,
1123        Types.DOTS: get_dots_sketch,
1124        Types.ELLIPSE: get_sketch,
1125        Types.FRAGMENT: get_sketch,
1126        Types.HANDLE: get_handle_sketch,
1127        Types.HEX_GRID: get_batch_sketch,
1128        Types.LACE: get_lace_sketch,
1129        Types.LINPATH: get_path_sketch,
1130        Types.MIXED_GRID: get_batch_sketch,
1131        Types.OVERLAP: get_batch_sketch,
1132        Types.PARALLEL_POLYLINE: get_batch_sketch,
1133        Types.PATTERN: get_pattern_sketch,
1134        Types.PLAIT: get_sketch,
1135        Types.POLYLINE: get_sketch,
1136        Types.Q_BEZIER: get_sketch,
1137        Types.RECTANGLE: get_sketch,
1138        Types.SECTION: get_sketch,
1139        Types.SEGMENT: get_sketch,
1140        Types.SHAPE: get_sketch,
1141        Types.SINE_WAVE: get_sketch,
1142        Types.SQUARE_GRID: get_batch_sketch,
1143        Types.STAR: get_batch_sketch,
1144        Types.TAG: get_tag_sketch,
1145    }
1146
1147    return d_subtype_sketch[item.subtype](item, canvas, **kwargs)
@dataclass
class Color:
116@dataclass
117class Color:
118    """A class representing an RGB or RGBA color.
119
120    This class represents a color in RGB or RGBA color space. The default values
121    for the components are normalized between 0.0 and 1.0. Values outside this range
122    are automatically converted from the 0-255 range.
123
124    Attributes:
125        red: The red component of the color (0.0 to 1.0).
126        green: The green component of the color (0.0 to 1.0).
127        blue: The blue component of the color (0.0 to 1.0).
128        alpha: The alpha (transparency) component (0.0 to 1.0), default is 1.
129        space: The color space, default is "rgb".
130
131    Examples:
132        >>> red = Color(1.0, 0.0, 0.0)
133        >>> transparent_blue = Color(0.0, 0.0, 1.0, 0.5)
134        >>> rgb255 = Color(255, 0, 128)  # Will be automatically normalized
135    """
136    red: int = 0
137    green: int = 0
138    blue: int = 0
139    alpha: int = 1
140    space: ColorSpace = "rgb"  # for future use
141
142    def __post_init__(self):
143        """Post-initialization to ensure color values are in the correct range."""
144        r, g, b = self.red, self.green, self.blue
145        if r < 0 or r > 1 or g < 0 or g > 1 or b < 0 or b > 1:
146            self.red = r / 255
147            self.green = g / 255
148            self.blue = b / 255
149        if self.alpha < 0 or self.alpha > 1:
150            self.alpha = self.alpha / 255
151        common_properties(self)
152
153    def __str__(self):
154        return f"Color({self.red}, {self.green}, {self.blue})"
155
156    def __repr__(self):
157        return f"Color({self.red}, {self.green}, {self.blue})"
158
159    def copy(self):
160        return Color(self.red, self.green, self.blue, self.alpha)
161
162    @property
163    def __key__(self):
164        return (self.red, self.green, self.blue)
165
166    def __hash__(self):
167        return hash(self.__key__)
168
169    @property
170    def name(self):
171        # search for the color in the named colors
172        pass
173
174    def __eq__(self, other):
175        if isinstance(other, Color):
176            return self.__key__ == other.__key__
177        else:
178            return False
179
180    @property
181    def rgb(self):
182        return (self.red, self.green, self.blue)
183
184    @property
185    def rgba(self):
186        return (self.red, self.green, self.blue, self.alpha)
187
188    @property
189    def rgb255(self):
190        r, g, b = self.rgb
191        if r > 1 or g > 1 or b > 1:
192            return (r, g, b)
193        return tuple(round(i * 255) for i in self.rgb)
194
195    @property
196    def rgba255(self):
197        return tuple(round(i * 255) for i in self.rgba)

A class representing an RGB or RGBA color.

This class represents a color in RGB or RGBA color space. The default values for the components are normalized between 0.0 and 1.0. Values outside this range are automatically converted from the 0-255 range.

Attributes:
  • red: The red component of the color (0.0 to 1.0).
  • green: The green component of the color (0.0 to 1.0).
  • blue: The blue component of the color (0.0 to 1.0).
  • alpha: The alpha (transparency) component (0.0 to 1.0), default is 1.
  • space: The color space, default is "rgb".
Examples:
>>> red = Color(1.0, 0.0, 0.0)
>>> transparent_blue = Color(0.0, 0.0, 1.0, 0.5)
>>> rgb255 = Color(255, 0, 128)  # Will be automatically normalized
Color( red: int = 0, green: int = 0, blue: int = 0, alpha: int = 1, space: simetri.graphics.all_enums.ColorSpace = 'rgb')
red: int = 0
green: int = 0
blue: int = 0
alpha: int = 1
def copy(self):
159    def copy(self):
160        return Color(self.red, self.green, self.blue, self.alpha)
name
169    @property
170    def name(self):
171        # search for the color in the named colors
172        pass
rgb
180    @property
181    def rgb(self):
182        return (self.red, self.green, self.blue)
rgba
184    @property
185    def rgba(self):
186        return (self.red, self.green, self.blue, self.alpha)
rgb255
188    @property
189    def rgb255(self):
190        r, g, b = self.rgb
191        if r > 1 or g > 1 or b > 1:
192            return (r, g, b)
193        return tuple(round(i * 255) for i in self.rgb)
rgba255
195    @property
196    def rgba255(self):
197        return tuple(round(i * 255) for i in self.rgba)
def help_lines( self, pos: Sequence[float] = None, width: float = None, height: float = None, step_size=None, cs_size: float = None, **kwargs):
49def help_lines(
50    self,
51    pos: Point = None,
52    width: float = None,
53    height: float = None,
54    step_size=None,
55    cs_size: float = None,
56    **kwargs,
57):
58    """
59    Draw a square grid with the given size.
60
61    Args:
62        pos (Point, optional): Position of the grid. Defaults to None.
63        width (float, optional): Length of the grid along the x-axis. Defaults to None.
64        height (float, optional): Length of the grid along the y-axis. Defaults to None.
65        step_size (optional): Step size for the grid. Defaults to None.
66        cs_size (float, optional): Size of the coordinate system. Defaults to None.
67        **kwargs: Additional keyword arguments.
68
69    Returns:
70        Self: The canvas object.
71    """
72    self.grid(pos, width, height, step_size, **kwargs)
73    if cs_size > 0:
74        self.draw_CS(cs_size, **kwargs)
75    return self

Draw a square grid with the given size.

Arguments:
  • pos (Point, optional): Position of the grid. Defaults to None.
  • width (float, optional): Length of the grid along the x-axis. Defaults to None.
  • height (float, optional): Length of the grid along the y-axis. Defaults to None.
  • step_size (optional): Step size for the grid. Defaults to None.
  • cs_size (float, optional): Size of the coordinate system. Defaults to None.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def arc( self, center: Sequence[float], radius_x: float, radius_y: float, start_angle: float, span_angle: float, rot_angle: float, n_points: int = None, **kwargs) -> None:
 78def arc(
 79    self,
 80    center: Point,
 81    radius_x: float,
 82    radius_y: float,
 83    start_angle: float,
 84    span_angle: float,
 85    rot_angle: float,
 86    n_points: int = None,
 87    **kwargs,
 88) -> None:
 89    """
 90    Draw an arc with the given center, radius, start and end angles in radians.
 91    Arc is drawn in counterclockwise direction from start to end.
 92
 93    Args:
 94        center (Point): Center of the arc.
 95        radius_x (float): Radius of the arc.
 96        radius_y (float): Second radius of the arc.
 97        start_angle (float): Start angle of the arc in radians.
 98        end_angle (float): End angle of the arc in radians.
 99
100        rot_angle (float): Rotation angle of the arc.
101        **kwargs: Additional keyword arguments.
102    """
103    if radius_y is None:
104        radius_y = radius_x
105    vertices = elliptic_arc_points(
106        center, radius_x, radius_y, start_angle, span_angle, n_points
107    )
108    if rot_angle != 0:
109        vertices = homogenize(vertices) @ rotation_matrix(rot_angle, center)
110    self._all_vertices.extend(vertices.tolist() + [center])
111
112    sketch = ArcSketch(vertices=vertices, xform_matrix=self.xform_matrix)
113    for attrib_name in shape_style_map:
114        if hasattr(sketch, attrib_name):
115            attrib_value = self.resolve_property(sketch, attrib_name)
116        else:
117            attrib_value = defaults[attrib_name]
118        setattr(sketch, attrib_name, attrib_value)
119    for k, v in kwargs.items():
120        setattr(sketch, k, v)
121    self.active_page.sketches.append(sketch)
122
123    return self

Draw an arc with the given center, radius, start and end angles in radians. Arc is drawn in counterclockwise direction from start to end.

Arguments:
  • center (Point): Center of the arc.
  • radius_x (float): Radius of the arc.
  • radius_y (float): Second radius of the arc.
  • start_angle (float): Start angle of the arc in radians.
  • end_angle (float): End angle of the arc in radians.
  • rot_angle (float): Rotation angle of the arc.
  • **kwargs: Additional keyword arguments.
def bezier(self, control_points, **kwargs):
126def bezier(self, control_points, **kwargs):
127    """
128    Draw a Bezier curve with the given control points.
129
130    Args:
131        control_points: Control points for the Bezier curve.
132        **kwargs: Additional keyword arguments.
133
134    Returns:
135        Self: The canvas object.
136    """
137    self._all_vertices.extend(control_points)
138    sketch = BezierSketch(control_points, self.xform_matrix)
139    for attrib_name in shape_style_map:
140        if hasattr(sketch, attrib_name):
141            attrib_value = self._resolve_property(sketch, attrib_name)
142        else:
143            attrib_value = defaults[attrib_name]
144        setattr(sketch, attrib_name, attrib_value)
145    self.active_page.sketches.append(sketch)
146
147    for k, v in kwargs.items():
148        setattr(sketch, k, v)
149    return self

Draw a Bezier curve with the given control points.

Arguments:
  • control_points: Control points for the Bezier curve.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def circle(self, center: Sequence[float], radius: float, **kwargs) -> None:
152def circle(self, center: Point, radius: float, **kwargs) -> None:
153    """
154    Draw a circle with the given center and radius.
155
156    Args:
157        center (Point): Center of the circle.
158        radius (float): Radius of the circle.
159        **kwargs: Additional keyword arguments.
160    """
161    x, y = center[:2]
162    p1 = x - radius, y - radius
163    p2 = x + radius, y + radius
164    p3 = x - radius, y + radius
165    p4 = x + radius, y - radius
166    self._all_vertices.extend([p1, p2, p3, p4])
167    sketch = CircleSketch(center, radius, self.xform_matrix)
168    for attrib_name in shape_style_map:
169        if hasattr(sketch, attrib_name):
170            attrib_value = self.resolve_property(sketch, attrib_name)
171        else:
172            attrib_value = defaults[attrib_name]
173        setattr(sketch, attrib_name, attrib_value)
174    for k, v in kwargs.items():
175        setattr(sketch, k, v)
176    self.active_page.sketches.append(sketch)
177
178    return self

Draw a circle with the given center and radius.

Arguments:
  • center (Point): Center of the circle.
  • radius (float): Radius of the circle.
  • **kwargs: Additional keyword arguments.
def ellipse( self, center: Sequence[float], width: float, height, angle, **kwargs) -> None:
181def ellipse(self, center: Point, width: float, height, angle, **kwargs) -> None:
182    """
183    Draw an ellipse with the given center and x_radius and y_radius.
184
185    Args:
186        center (Point): Center of the ellipse.
187        width (float): Width of the ellipse.
188        height: Height of the ellipse.
189        angle: Angle of the ellipse.
190        **kwargs: Additional keyword arguments.
191    """
192    x, y = center[:2]
193    x_radius = width / 2
194    y_radius = height / 2
195    p1 = x - x_radius, y - y_radius
196    p2 = x + x_radius, y + y_radius
197    p3 = x - x_radius, y + y_radius
198    p4 = x + x_radius, y - y_radius
199    self._all_vertices.extend([p1, p2, p3, p4])
200    sketch = EllipseSketch(center, x_radius, y_radius, angle, self.xform_matrix)
201    for attrib_name in shape_style_map:
202        if hasattr(sketch, attrib_name):
203            attrib_value = self.resolve_property(sketch, attrib_name)
204        else:
205            attrib_value = defaults[attrib_name]
206        setattr(sketch, attrib_name, attrib_value)
207    for k, v in kwargs.items():
208        setattr(sketch, k, v)
209    self.active_page.sketches.append(sketch)
210
211    return self

Draw an ellipse with the given center and x_radius and y_radius.

Arguments:
  • center (Point): Center of the ellipse.
  • width (float): Width of the ellipse.
  • height: Height of the ellipse.
  • angle: Angle of the ellipse.
  • **kwargs: Additional keyword arguments.
def text( self, txt: str, pos: Sequence[float], font_family: str = None, font_size: int = None, font_color: Color = None, anchor: simetri.graphics.all_enums.Anchor = None, **kwargs) -> None:
214def text(
215    self,
216    txt: str,
217    pos: Point,
218    font_family: str = None,
219    font_size: int = None,
220    font_color: Color = None,
221    anchor: Anchor = None,
222    **kwargs,
223) -> None:
224    """
225    Draw the given text at the given position.
226
227    Args:
228        txt (str): Text to be drawn.
229        pos (Point): Position of the text.
230        font_family (str, optional): Font family of the text. Defaults to None.
231        font_size (int, optional): Font size of the text. Defaults to None.
232        font_color (Color, optional): Font color of the text. Defaults to None.
233        anchor (Anchor, optional): Anchor of the text. Defaults to None.
234        **kwargs: Additional keyword arguments.
235    """
236    # first create a Tag object
237    tag_obj = Tag(
238        txt,
239        pos,
240        font_family=font_family,
241        font_size=font_size,
242        font_color=font_color,
243        anchor=anchor,
244        **kwargs,
245    )
246    tag_obj.draw_frame = False
247    # then call get_tag_sketch to create a TagSketch object
248    sketch = create_sketch(tag_obj, self, **kwargs)
249    self.active_page.sketches.append(sketch)
250
251    return self

Draw the given text at the given position.

Arguments:
  • txt (str): Text to be drawn.
  • pos (Point): Position of the text.
  • font_family (str, optional): Font family of the text. Defaults to None.
  • font_size (int, optional): Font size of the text. Defaults to None.
  • font_color (Color, optional): Font color of the text. Defaults to None.
  • anchor (Anchor, optional): Anchor of the text. Defaults to None.
  • **kwargs: Additional keyword arguments.
def line(self, start, end, **kwargs):
254def line(self, start, end, **kwargs):
255    """
256    Draw a line segment from start to end.
257
258    Args:
259        start: Starting point of the line.
260        end: Ending point of the line.
261        **kwargs: Additional keyword arguments.
262
263    Returns:
264        Self: The canvas object.
265    """
266    self._sketch_xform_matrix = self.xform_matrix
267    line_shape = Shape([start, end], closed=False, **kwargs)
268    line_sketch = create_sketch(line_shape, self, **kwargs)
269    self.active_page.sketches.append(line_sketch)
270    self._sketch_xform_matrix = identity_matrix()
271    return self

Draw a line segment from start to end.

Arguments:
  • start: Starting point of the line.
  • end: Ending point of the line.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def rectangle( self, center: Sequence[float], width: float, height: float, angle: float, **kwargs):
274def rectangle(self, center: Point, width: float, height: float, angle: float, **kwargs):
275    """
276    Draw a rectangle with the given center, width, height and angle.
277
278    Args:
279        center (Point): Center of the rectangle.
280        width (float): Width of the rectangle.
281        height (float): Height of the rectangle.
282        angle (float): Angle of the rectangle.
283        **kwargs: Additional keyword arguments.
284
285    Returns:
286        Self: The canvas object.
287    """
288    w2 = width / 2
289    h2 = height / 2
290    p1 = center[0] - w2, center[1] + h2
291    p2 = center[0] - w2, center[1] - h2
292    p3 = center[0] + w2, center[1] - h2
293    p4 = center[0] + w2, center[1] + h2
294    points = homogenize([p1, p2, p3, p4]) @ rotation_matrix(angle, center)
295    rect_shape = Shape(points.tolist(), closed=True, **kwargs)
296    rect_sketch = create_sketch(rect_shape, self, **kwargs)
297    self.active_page.sketches.append(rect_sketch)
298
299    return self

Draw a rectangle with the given center, width, height and angle.

Arguments:
  • center (Point): Center of the rectangle.
  • width (float): Width of the rectangle.
  • height (float): Height of the rectangle.
  • angle (float): Angle of the rectangle.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def draw_CS(self, size: float = None, **kwargs):
302def draw_CS(self, size: float = None, **kwargs):
303    """
304    Draw a coordinate system with the given size.
305
306    Args:
307        size (float, optional): Size of the coordinate system. Defaults to None.
308        **kwargs: Additional keyword arguments.
309
310    Returns:
311        Self: The canvas object.
312    """
313    if size is None:
314        size = defaults["CS_size"]
315    if "colors" in kwargs:
316        x_color, y_color = kwargs["colors"]
317        del kwargs["colors"]
318    else:
319        x_color = defaults["CS_x_color"]
320        y_color = defaults["CS_y_color"]
321    if "line_width" not in kwargs:
322        kwargs["line_width"] = defaults["CS_line_width"]
323    self.line((0, 0), (size, 0), line_color=x_color, **kwargs)
324    self.line((0, 0), (0, size), line_color=y_color, **kwargs)
325    if "line_color" not in kwargs:
326        kwargs["line_color"] = defaults["CS_origin_color"]
327    self.circle((0, 0), radius=defaults["CS_origin_size"], **kwargs)
328
329    return self

Draw a coordinate system with the given size.

Arguments:
  • size (float, optional): Size of the coordinate system. Defaults to None.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def lines(self, points, **kwargs):
332def lines(self, points, **kwargs):
333    """
334    Draw connected line segments.
335
336    Args:
337        points: Points to be connected.
338        **kwargs: Additional keyword arguments.
339
340    Returns:
341        Self: The canvas object.
342    """
343    self._all_vertices.extend(points)
344    sketch = LineSketch(points, self.xform_matrix, **kwargs)
345    for attrib_name in line_style_map:
346        attrib_value = self._resolve_property(sketch, attrib_name)
347        setattr(sketch, attrib_name, attrib_value)
348    self.active_page.sketches.append(sketch)
349
350    return self

Draw connected line segments.

Arguments:
  • points: Points to be connected.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def insert_code( self, code: str, location: simetri.graphics.all_enums.TexLoc = <TexLoc.NONE: 'NONE'>) -> typing_extensions.Self:
352def insert_code(self, code: str, location: TexLoc = TexLoc.NONE) -> Self:
353        """
354        Insert code into the canvas.
355
356        Args:
357            code (str): The code to insert.
358
359        Returns:
360            Self: The canvas object.
361        """
362        active_sketches = self.active_page.sketches
363        sketch = TexSketch(code, location=location)
364        active_sketches.append(sketch)
365
366        return self

Insert code into the canvas.

Arguments:
  • code (str): The code to insert.
Returns:

Self: The canvas object.

def draw_bbox(self, bbox, **kwargs):
368def draw_bbox(self, bbox, **kwargs):
369    """
370    Draw the bounding box object.
371
372    Args:
373        bbox: Bounding box to be drawn.
374        **kwargs: Additional keyword arguments.
375
376    Returns:
377        Self: The canvas object.
378    """
379    sketch = create_sketch(bbox, self, **kwargs)
380    self.active_page.sketches.append(sketch)
381
382    return self

Draw the bounding box object.

Arguments:
  • bbox: Bounding box to be drawn.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def draw_pattern(self, pattern, **kwargs):
385def draw_pattern(self, pattern, **kwargs):
386    """
387    Draw the pattern object.
388
389    Args:
390        pattern: Pattern object to be drawn.
391        **kwargs: Additional keyword arguments.
392
393    Returns:
394        Self: The canvas object.
395    """
396    sketch = create_sketch(pattern, self, **kwargs)
397    self.active_page.sketches.append(sketch)
398
399    return self

Draw the pattern object.

Arguments:
  • pattern: Pattern object to be drawn.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def draw_hobby( self, points: Sequence[Sequence[float]], controls: Sequence[Sequence[float]], cyclic: bool = False, **kwargs):
402def draw_hobby(
403    self,
404    points: Sequence[Point],
405    controls: Sequence[Point],
406    cyclic: bool = False,
407    **kwargs,
408):
409    """Draw a Hobby curve through the given points using the control points.
410
411    Args:
412        points (Sequence[Point]): Points through which the curve passes.
413        controls (Sequence[Point]): Control points for the curve.
414        cyclic (bool, optional): Whether the curve is cyclic. Defaults to False.
415        **kwargs: Additional keyword arguments.
416    """
417    n = len(points)
418    if cyclic:
419        for i in range(n):
420            ind = i * 2
421            bezier_pnts = bezier_points(
422                points[i], *controls[ind : ind + 2], points[(i + 1) % n], 20
423            )
424            bezier_ = Shape(bezier_pnts)
425            self.draw(bezier_, **kwargs)
426    else:
427        for i in range(len(points) - 1):
428            ind = i * 2
429            bezier_pnts = bezier_points(
430                points[i], *controls[ind : ind + 2], points[i + 1], 20
431            )
432            bezier_ = Shape(bezier_pnts)
433            self.draw(bezier_, **kwargs)

Draw a Hobby curve through the given points using the control points.

Arguments:
  • points (Sequence[Point]): Points through which the curve passes.
  • controls (Sequence[Point]): Control points for the curve.
  • cyclic (bool, optional): Whether the curve is cyclic. Defaults to False.
  • **kwargs: Additional keyword arguments.
def draw_lace(self, lace, **kwargs):
436def draw_lace(self, lace, **kwargs):
437    """Draw the lace object.
438
439    Args:
440        lace: Lace object to be drawn.
441        **kwargs: Additional keyword arguments.
442
443    Returns:
444        Self: The canvas object.
445    """
446    keys = list(lace.fragment_groups.keys())
447    keys.sort()
448    if lace.swatch is not None:
449        n_colors = len(lace.swatch)
450    for i, key in enumerate(keys):
451        if lace.swatch is not None:
452            fill_color = colors.Color(*lace.swatch[i % n_colors])
453            kwargs["fill_color"] = fill_color
454        for fragment in lace.fragment_groups[key]:
455            self.active_page.sketches.append(create_sketch(fragment, self, **kwargs))
456    for plait in lace.plaits:
457        if lace.swatch is not None:
458            fill_color = colors.white
459            kwargs["fill_color"] = fill_color
460        else:
461            kwargs["fill_color"] = None
462        self.active_page.sketches.append(create_sketch(plait, self, **kwargs))
463        self._all_vertices.extend(plait.corners)
464
465    return self

Draw the lace object.

Arguments:
  • lace: Lace object to be drawn.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def draw_dimension(self, item, **kwargs):
468def draw_dimension(self, item, **kwargs):
469    """Draw the dimension object.
470
471    Args:
472        item: Dimension object to be drawn.
473        **kwargs: Additional keyword arguments.
474
475    Returns:
476        Self: The canvas object.
477    """
478    for shape in item.all_shapes:
479        self._all_vertices.extend(shape.corners)
480    for ext in [item.ext1, item.ext2, item.ext3]:
481        if ext:
482            ext_sketch = create_sketch(ext, self, **kwargs)
483            self.active_page.sketches.append(ext_sketch)
484    if item.dim_line:
485        dim_sketch = create_sketch(item.dim_line, self, **kwargs)
486        self.active_page.sketches.extend(dim_sketch)
487    if item.arrow1:
488        arrow_sketch = create_sketch(item.arrow1, self, **kwargs)
489        self.active_page.sketches.extend(arrow_sketch)
490        self.active_page.sketches.append(create_sketch(item.mid_line, self))
491    if item.arrow2:
492        arrow_sketch = create_sketch(item.arrow2, self, **kwargs)
493        self.active_page.sketches.extend(arrow_sketch)
494    x, y = item.text_pos[:2]
495
496    tag = Tag(item.text, (x, y), font_size=item.font_size, **kwargs)
497    tag_sketch = create_sketch(tag, self, **kwargs)
498    tag_sketch.draw_frame = True
499    tag_sketch.frame_shape = FrameShape.CIRCLE
500    tag_sketch.fill = True
501    tag_sketch.font_color = colors.black
502    tag_sketch.frame_back_style = BackStyle.COLOR
503    tag_sketch.back_style = BackStyle.COLOR
504    tag_sketch.frame_back_color = colors.white
505    tag_sketch.back_color = colors.white
506    tag_sketch.stroke = False
507    self.active_page.sketches.append(tag_sketch)
508
509    return self

Draw the dimension object.

Arguments:
  • item: Dimension object to be drawn.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def grid( self, pos=(0, 0), width: float = None, height: float = None, step_size=None, **kwargs):
512def grid(
513    self,
514    pos=(0, 0),
515    width: float = None,
516    height: float = None,
517    step_size=None,
518    **kwargs,
519):
520    """Draw a square grid with the given size.
521
522    Args:
523        pos (tuple, optional): Position of the grid. Defaults to (0, 0).
524        width (float, optional): Length of the grid along the x-axis. Defaults to None.
525        height (float, optional): Length of the grid along the y-axis. Defaults to None.
526        step_size (optional): Step size for the grid. Defaults to None.
527        **kwargs: Additional keyword arguments.
528
529    Returns:
530        Self: The canvas object.
531    """
532    x, y = pos[:2]
533    if width is None:
534        width = defaults["grid_size"]
535        height = defaults["grid_size"]
536    if "line_width" not in kwargs:
537        kwargs["line_width"] = defaults["grid_line_width"]
538    if "line_color" not in kwargs:
539        kwargs["line_color"] = defaults["grid_line_color"]
540    if "line_dash_array" not in kwargs:
541        kwargs["line_dash_array"] = defaults["grid_line_dash_array"]
542    # draw x-axis
543    # self.line((-size, 0), (size, 0), **kwargs)
544    line_y = Shape([(x, y), (x + width, y)], **kwargs)
545    line_x = Shape([(x, y), (x, y + height)], **kwargs)
546    lines_x = line_y.translate(0, step_size, reps=int(height / step_size))
547    lines_y = line_x.translate(step_size, 0, reps=int(width / step_size))
548    self.draw(lines_x)
549    self.draw(lines_y)
550    return self

Draw a square grid with the given size.

Arguments:
  • pos (tuple, optional): Position of the grid. Defaults to (0, 0).
  • width (float, optional): Length of the grid along the x-axis. Defaults to None.
  • height (float, optional): Length of the grid along the y-axis. Defaults to None.
  • step_size (optional): Step size for the grid. Defaults to None.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

regular_sketch_types = [<Types.ARC: 'ARC'>, <Types.ARC_ARROW: 'ARC_ARROW'>, <Types.BATCH: 'BATCH'>, <Types.BEZIER: 'BEZIER'>, <Types.CIRCLE: 'CIRCLE'>, <Types.CIRCULAR_GRID: 'CIRCULAR_GRID'>, <Types.DIVISION: 'DIVISION'>, <Types.DOT: 'DOT'>, <Types.DOTS: 'DOTS'>, <Types.ELLIPSE: 'ELLIPSE'>, <Types.FRAGMENT: 'FRAGMENT'>, <Types.HEX_GRID: 'HEX_GRID'>, <Types.LINPATH: 'LINPATH'>, <Types.MIXED_GRID: 'MIXED_GRID'>, <Types.OUTLINE: 'OUTLINE'>, <Types.OVERLAP: 'OVERLAP'>, <Types.PARALLEL_POLYLINE: 'PARALLEL_POLYLINE'>, <Types.PLAIT: 'PLAIT'>, <Types.POLYLINE: 'POLYLINE'>, <Types.Q_BEZIER: 'Q_BEZIER'>, <Types.RECTANGLE: 'RECTANGLE'>, <Types.SECTION: 'SECTION'>, <Types.SEGMENT: 'SEGMENT'>, <Types.SHAPE: 'SHAPE'>, <Types.SINE_WAVE: 'SINE_WAVE'>, <Types.SQUARE_GRID: 'SQUARE_GRID'>, <Types.STAR: 'STAR'>, <Types.TAG: 'TAG'>]
def extend_vertices(canvas, item):
585def extend_vertices(canvas, item):
586    """Extend the list of all vertices with the vertices of the given item.
587
588    Args:
589        canvas: Canvas object.
590        item: Item whose vertices are to be extended.
591    """
592    all_vertices = canvas._all_vertices
593    if item.subtype == Types.DOTS:
594        vertices = [x.pos for x in item.all_shapes]
595        vertices = [x[:2] for x in homogenize(vertices) @ canvas._sketch_xform_matrix]
596        all_vertices.extend(vertices)
597    elif item.subtype == Types.DOT:
598        vertices = [item.pos]
599        vertices = [x[:2] for x in homogenize(vertices) @ canvas._sketch_xform_matrix]
600        all_vertices.extend(vertices)
601    elif item.subtype == Types.ARROW:
602        for shape in item.all_shapes:
603            all_vertices.extend(shape.corners)
604    elif item.subtype == Types.LACE:
605        for plait in item.plaits:
606            all_vertices.extend(plait.corners)
607        for fragment in item.fragments:
608            all_vertices.extend(fragment.corners)
609    elif item.subtype == Types.PATTERN:
610        all_vertices.extend(item.get_all_vertices())
611    else:
612        corners = [x[:2] for x in homogenize(item.corners) @ canvas._sketch_xform_matrix]
613        all_vertices.extend(corners)

Extend the list of all vertices with the vertices of the given item.

Arguments:
  • canvas: Canvas object.
  • item: Item whose vertices are to be extended.
def draw( self, item: Union[ForwardRef(<Types.ARC: 'ARC'>), ForwardRef(<Types.ARC_ARROW: 'ARC_ARROW'>), ForwardRef(<Types.ARROW: 'ARROW'>), ForwardRef(<Types.ARROW_HEAD: 'ARROW_HEAD'>), ForwardRef(<Types.BATCH: 'BATCH'>), ForwardRef(<Types.CIRCLE: 'CIRCLE'>), ForwardRef(<Types.CIRCULAR_GRID: 'CIRCULAR_GRID'>), ForwardRef(<Types.DIMENSION: 'DIMENSION'>), ForwardRef(<Types.DOT: 'DOT'>), ForwardRef(<Types.DOTS: 'DOTS'>), ForwardRef(<Types.EDGE: 'EDGE'>), ForwardRef(<Types.ELLIPSE: 'ELLIPSE'>), ForwardRef(<Types.FRAGMENT: 'FRAGMENT'>), ForwardRef(<Types.HEX_GRID: 'HEX_GRID'>), ForwardRef(<Types.INTERSECTION: 'INTERSECTION'>), ForwardRef(<Types.LACE: 'LACE'>), ForwardRef(<Types.LINPATH: 'LINPATH'>), ForwardRef(<Types.MIXED_GRID: 'MIXED_GRID'>), ForwardRef(<Types.OUTLINE: 'OUTLINE'>), ForwardRef(<Types.OVERLAP: 'OVERLAP'>), ForwardRef(<Types.PARALLEL_POLYLINE: 'PARALLEL_POLYLINE'>), ForwardRef(<Types.PATTERN: 'PATTERN'>), ForwardRef(<Types.PLAIT: 'PLAIT'>), ForwardRef(<Types.POLYLINE: 'POLYLINE'>), ForwardRef(<Types.RECTANGLE: 'RECTANGLE'>), ForwardRef(<Types.SECTION: 'SECTION'>), ForwardRef(<Types.SEGMENT: 'SEGMENT'>), ForwardRef(<Types.SHAPE: 'SHAPE'>), ForwardRef(<Types.SINE_WAVE: 'SINE_WAVE'>), ForwardRef(<Types.SQUARE_GRID: 'SQUARE_GRID'>), ForwardRef(<Types.STAR: 'STAR'>), ForwardRef(<Types.SVG_PATH: 'SVG_PATH'>), ForwardRef(<Types.TAG: 'TAG'>), ForwardRef(<Types.TURTLE: 'TURTLE'>)], **kwargs) -> typing_extensions.Self:
616def draw(self, item: Drawable, **kwargs) -> Self:
617    """The item is drawn on the canvas with the given style properties.
618
619    Args:
620        item (Drawable): Item to be drawn.
621        **kwargs: Additional keyword arguments.
622
623    Returns:
624        Self: The canvas object.
625    """
626    # check if the item has any points
627    if not item:
628        return self
629
630    active_sketches = self.active_page.sketches
631    subtype = item.subtype
632    extend_vertices(self, item)
633    if subtype in regular_sketch_types:
634        sketches = get_sketches(item, self, **kwargs)
635        if sketches:
636            active_sketches.extend(sketches)
637    elif subtype == Types.PATTERN:
638        draw_pattern(self, item, **kwargs)
639    elif subtype == Types.DIMENSION:
640        self.draw_dimension(item, **kwargs)
641    elif subtype == Types.ARROW:
642        for head in item.heads:
643            active_sketches.append(create_sketch(head, self, **kwargs))
644        active_sketches.append(create_sketch(item.line, self, **kwargs))
645    elif subtype == Types.LACE:
646        self.draw_lace(item, **kwargs)
647    elif subtype == Types.BOUNDING_BOX:
648        draw_bbox(self, item, **kwargs)
649    return self

The item is drawn on the canvas with the given style properties.

Arguments:
  • item (Drawable): Item to be drawn.
  • **kwargs: Additional keyword arguments.
Returns:

Self: The canvas object.

def get_sketches( item: Union[ForwardRef(<Types.ARC: 'ARC'>), ForwardRef(<Types.ARC_ARROW: 'ARC_ARROW'>), ForwardRef(<Types.ARROW: 'ARROW'>), ForwardRef(<Types.ARROW_HEAD: 'ARROW_HEAD'>), ForwardRef(<Types.BATCH: 'BATCH'>), ForwardRef(<Types.CIRCLE: 'CIRCLE'>), ForwardRef(<Types.CIRCULAR_GRID: 'CIRCULAR_GRID'>), ForwardRef(<Types.DIMENSION: 'DIMENSION'>), ForwardRef(<Types.DOT: 'DOT'>), ForwardRef(<Types.DOTS: 'DOTS'>), ForwardRef(<Types.EDGE: 'EDGE'>), ForwardRef(<Types.ELLIPSE: 'ELLIPSE'>), ForwardRef(<Types.FRAGMENT: 'FRAGMENT'>), ForwardRef(<Types.HEX_GRID: 'HEX_GRID'>), ForwardRef(<Types.INTERSECTION: 'INTERSECTION'>), ForwardRef(<Types.LACE: 'LACE'>), ForwardRef(<Types.LINPATH: 'LINPATH'>), ForwardRef(<Types.MIXED_GRID: 'MIXED_GRID'>), ForwardRef(<Types.OUTLINE: 'OUTLINE'>), ForwardRef(<Types.OVERLAP: 'OVERLAP'>), ForwardRef(<Types.PARALLEL_POLYLINE: 'PARALLEL_POLYLINE'>), ForwardRef(<Types.PATTERN: 'PATTERN'>), ForwardRef(<Types.PLAIT: 'PLAIT'>), ForwardRef(<Types.POLYLINE: 'POLYLINE'>), ForwardRef(<Types.RECTANGLE: 'RECTANGLE'>), ForwardRef(<Types.SECTION: 'SECTION'>), ForwardRef(<Types.SEGMENT: 'SEGMENT'>), ForwardRef(<Types.SHAPE: 'SHAPE'>), ForwardRef(<Types.SINE_WAVE: 'SINE_WAVE'>), ForwardRef(<Types.SQUARE_GRID: 'SQUARE_GRID'>), ForwardRef(<Types.STAR: 'STAR'>), ForwardRef(<Types.SVG_PATH: 'SVG_PATH'>), ForwardRef(<Types.TAG: 'TAG'>), ForwardRef(<Types.TURTLE: 'TURTLE'>)], canvas: 'Canvas' = None, **kwargs) -> list['Sketch']:
652def get_sketches(item: Drawable, canvas: "Canvas" = None, **kwargs) -> list["Sketch"]:
653    """Create sketches from the given item and return them as a list.
654
655    Args:
656        item (Drawable): Item to be sketched.
657        canvas (Canvas, optional): Canvas object. Defaults to None.
658        **kwargs: Additional keyword arguments.
659
660    Returns:
661        list[Sketch]: List of sketches.
662    """
663    if not (item.visible and item.active):
664        res = []
665    elif item.subtype in drawable_types:
666        sketches = create_sketch(item, canvas, **kwargs)
667        if isinstance(sketches, list):
668            res = sketches
669        elif sketches is not None:
670            res = [sketches]
671        else:
672            res = []
673    else:
674        res = []
675    return res

Create sketches from the given item and return them as a list.

Arguments:
  • item (Drawable): Item to be sketched.
  • canvas (Canvas, optional): Canvas object. Defaults to None.
  • **kwargs: Additional keyword arguments.
Returns:

list[Sketch]: List of sketches.

def set_shape_sketch_style(sketch, item, canvas, linear=False, **kwargs):
678def set_shape_sketch_style(sketch, item, canvas, linear=False, **kwargs):
679    """Set the style properties of the sketch.
680
681    Args:
682        sketch: Sketch object.
683        item: Item whose style properties are to be set.
684        canvas: Canvas object.
685        linear (bool, optional): Whether the style is linear. Defaults to False.
686        **kwargs: Additional keyword arguments.
687    """
688    if linear:
689        style_map = line_style_map
690    else:
691        style_map = shape_style_map
692
693    for attrib_name in style_map:
694        attrib_value = canvas._resolve_property(item, attrib_name)
695        setattr(sketch, attrib_name, attrib_value)
696
697    sketch.visible = item.visible
698    sketch.active = item.active
699    sketch.closed = item.closed
700    sketch.fill = item.fill
701    sketch.stroke = item.stroke
702
703    for k, v in kwargs.items():
704        setattr(sketch, k, v)

Set the style properties of the sketch.

Arguments:
  • sketch: Sketch object.
  • item: Item whose style properties are to be set.
  • canvas: Canvas object.
  • linear (bool, optional): Whether the style is linear. Defaults to False.
  • **kwargs: Additional keyword arguments.
def get_verts_in_new_pos(item, **kwargs):
706def get_verts_in_new_pos(item, **kwargs):
707    """
708    Get the vertices of the item in a new position.
709
710    Args:
711        item: Item whose vertices are to be obtained.
712        **kwargs: Additional keyword arguments.
713
714    Returns:
715        list: List of vertices in the new position.
716    """
717    if "pos" in kwargs:
718        x, y = item.midpoint[:2]
719        x1, y1 = kwargs["pos"][:2]
720        dx = x1 - x
721        dy = y1 - y
722        trans_mat = translation_matrix(dx, dy)
723        vertices = item.primary_points.homogen_coords @ trans_mat
724        vertices = vertices[:, :2].tolist()
725    else:
726        vertices = item.vertices
727
728    return vertices

Get the vertices of the item in a new position.

Arguments:
  • item: Item whose vertices are to be obtained.
  • **kwargs: Additional keyword arguments.
Returns:

list: List of vertices in the new position.

def create_sketch(item, canvas, **kwargs):
 731def create_sketch(item, canvas, **kwargs):
 732    """Create a sketch from the given item.
 733
 734    Args:
 735        item: Item to be sketched.
 736        canvas: Canvas object.
 737        **kwargs: Additional keyword arguments.
 738
 739    Returns:
 740        Sketch: Created sketch.
 741    """
 742    if not (item.visible and item.active):
 743        return None
 744
 745    def get_tag_sketch(item, canvas, **kwargs):
 746        """Create a TagSketch from the given item.
 747
 748        Args:
 749            item: Item to be sketched.
 750            canvas: Canvas object.
 751            **kwargs: Additional keyword arguments.
 752
 753        Returns:
 754            TagSketch: Created TagSketch.
 755        """
 756        if "pos" in kwargs:
 757            pos = kwargs["pos"]
 758        else:
 759            pos = item.pos
 760
 761        sketch = TagSketch(text=item.text, pos=pos, anchor=item.anchor)
 762        for attrib_name in item._style_map:
 763            if attrib_name == "fill_color":
 764                if item.fill_color in [None, colors.black]:
 765                    setattr(sketch, "frame_back_color", defaults["frame_back_color"])
 766                else:
 767                    setattr(sketch, "frame_back_color", item.fill_color)
 768                continue
 769            attrib_value = canvas._resolve_property(item, attrib_name)
 770            setattr(sketch, attrib_name, attrib_value)
 771        sketch.text_width = item.text_width
 772        sketch.visible = item.visible
 773        sketch.active = item.active
 774        for k, v in kwargs.items():
 775            setattr(sketch, k, v)
 776        return sketch
 777
 778    def get_ellipse_sketch(item, canvas, **kwargs):
 779        """Create an EllipseSketch from the given item.
 780
 781        Args:
 782            item: Item to be sketched.
 783            canvas: Canvas object.
 784            **kwargs: Additional keyword arguments.
 785
 786        Returns:
 787            EllipseSketch: Created EllipseSketch.
 788        """
 789        if "pos" in kwargs:
 790            center = kwargs["pos"]
 791        else:
 792            center = item.center
 793
 794        sketch = EllipseSketch(
 795            center,
 796            item.a,
 797            item.b,
 798            item.angle,
 799            xform_matrix=canvas.xform_matrix,
 800            **kwargs,
 801        )
 802        set_shape_sketch_style(sketch, item, canvas, **kwargs)
 803
 804        return sketch
 805
 806    def get_pattern_sketch(item, canvas, **kwargs):
 807        """Create a PatternSketch from the given item.
 808
 809        Args:
 810            item: Item to be sketched.
 811            canvas: Canvas object.
 812            **kwargs: Additional keyword arguments.
 813
 814        Returns:
 815            PatternSketch: Created PatternSketch.
 816        """
 817        sketch = PatternSketch(item, xform_matrix=canvas.xform_matrix, **kwargs)
 818        set_shape_sketch_style(sketch, item, canvas, **kwargs)
 819
 820        return sketch
 821
 822    def get_circle_sketch(item, canvas, **kwargs):
 823        """Create a CircleSketch from the given item.
 824
 825        Args:
 826            item: Item to be sketched.
 827            canvas: Canvas object.
 828            **kwargs: Additional keyword arguments.
 829
 830        Returns:
 831            CircleSketch: Created CircleSketch.
 832        """
 833        if "pos" in kwargs:
 834            center = kwargs["pos"]
 835        else:
 836            center = item.center
 837        sketch = CircleSketch(
 838            center, item.radius, xform_matrix=canvas.xform_matrix
 839        )
 840        set_shape_sketch_style(sketch, item, canvas, **kwargs)
 841
 842        return sketch
 843
 844    def get_dots_sketch(item, canvas, **kwargs):
 845        """Create sketches for dots from the given item.
 846
 847        Args:
 848            item: Item to be sketched.
 849            canvas: Canvas object.
 850            **kwargs: Additional keyword arguments.
 851
 852        Returns:
 853            list: List of created sketches.
 854        """
 855        vertices = [x.pos for x in item.all_shapes]
 856        fill_color = item[0].fill_color
 857        radius = item[0].radius
 858        marker_size = item[0].marker_size
 859        marker_type = item[0].marker_type
 860        item = Shape(
 861            vertices,
 862            fill_color=fill_color,
 863            markers_only=True,
 864            draw_markers=True,
 865            marker_size=marker_size,
 866            marker_radius=radius,
 867            marker_type=marker_type,
 868        )
 869        sketches = get_sketches(item, canvas, **kwargs)
 870
 871        return sketches
 872
 873    def get_arc_sketch(item, canvas, **kwargs):
 874        """Create an ArcSketch from the given item.
 875
 876        Args:
 877            item: Item to be sketched.
 878            canvas: Canvas object.
 879            **kwargs: Additional keyword arguments.
 880
 881        Returns:
 882            ArcSketch: Created ArcSketch.
 883        """
 884
 885        # vertices = get_verts_in_new_pos(item, **kwargs)
 886        sketch = ArcSketch(item.vertices, xform_matrix=canvas._sketch_xform_matrix)
 887        set_shape_sketch_style(sketch, item, canvas, **kwargs)
 888
 889        return sketch
 890
 891    def get_lace_sketch(item, canvas, **kwargs):
 892        """Create sketches for lace from the given item.
 893
 894        Args:
 895            item: Item to be sketched.
 896            canvas: Canvas object.
 897            **kwargs: Additional keyword arguments.
 898
 899        Returns:
 900            list: List of created sketches.
 901        """
 902        sketches = [get_sketch(frag, canvas, **kwargs) for frag in item.fragments]
 903        sketches.extend([get_sketch(plait, canvas, **kwargs) for plait in item.plaits])
 904        return sketches
 905
 906    def get_batch_sketch(item, canvas, **kwargs):
 907        """Create a BatchSketch from the given item.
 908
 909        Args:
 910            item: Item to be sketched.
 911            canvas: Canvas object.
 912            **kwargs: Additional keyword arguments.
 913
 914        Returns:
 915            BatchSketch or list: Created BatchSketch or list of sketches.
 916        """
 917        if scope_code_required(item):
 918            sketches = []
 919            for element in item.elements:
 920                if element.visible and element.active:
 921                    sketches.extend(get_sketches(element, canvas, **kwargs))
 922
 923            sketch = BatchSketch(sketches=sketches)
 924            for arg in group_args:
 925                setattr(sketch, arg, getattr(item, arg))
 926
 927            res = sketch
 928        else:
 929            sketches = []
 930            for element in item.elements:
 931                if hasattr(element, "visible") and hasattr(element, "active"):
 932                    if element.visible and element.active:
 933                        sketches.extend(get_sketches(element, canvas, **kwargs))
 934
 935            res = sketches
 936
 937        return res
 938
 939    def get_path_sketch(item, canvas, **kwargs):
 940        """Create sketches for a path from the given item.
 941
 942        Args:
 943            item: Item to be sketched.
 944            canvas: Canvas object.
 945            **kwargs: Additional keyword arguments.
 946
 947        Returns:
 948            list: List of created sketches.
 949        """
 950
 951        def extend_verts(obj, vertices):
 952            obj_vertices = obj.vertices
 953            if obj_vertices:
 954                if vertices and close_points2(vertices[-1], obj_vertices[0]):
 955                    obj_vertices = obj_vertices[1:]
 956                vertices.extend(obj_vertices)
 957
 958        path_op = PathOperation
 959        linears = [
 960            path_op.ARC,
 961            path_op.ARC_TO,
 962            path_op.BLEND_ARC,
 963            path_op.BLEND_CUBIC,
 964            path_op.BLEND_QUAD,
 965            path_op.BLEND_SINE,
 966            path_op.CUBIC_TO,
 967            path_op.FORWARD,
 968            path_op.H_LINE,
 969            path_op.HOBBY_TO,
 970            path_op.QUAD_TO,
 971            path_op.R_LINE,
 972            path_op.SEGMENTS,
 973            path_op.SINE,
 974            path_op.V_LINE,
 975            path_op.LINE_TO,
 976        ]
 977        sketches = []
 978        vertices = []
 979        for i, op in enumerate(item.operations):
 980            if op.subtype in linears:
 981                obj = item.objects[i]
 982                extend_verts(obj, vertices)
 983            elif op.subtype in [path_op.MOVE_TO, path_op.R_MOVE]:
 984                if i == 0:
 985                    continue
 986                shape = Shape(vertices)
 987                sketch = create_sketch(shape, canvas, **kwargs)
 988                if sketch:
 989                    sketch.visible = item.visible
 990                    sketch.active = item.active
 991                    sketches.append(sketch)
 992                vertices = []
 993            elif op.subtype == path_op.CLOSE:
 994                shape = Shape(vertices, closed=True)
 995                sketch = create_sketch(shape, canvas, **kwargs)
 996                if sketch:
 997                    sketch.visible = item.visible
 998                    sketch.active = item.active
 999                    sketches.append(sketch)
1000                vertices = []
1001        if vertices:
1002            shape = Shape(vertices)
1003            sketch = create_sketch(shape, canvas, **kwargs)
1004            sketches.append(sketch)
1005
1006        if "handles" in kwargs and kwargs["handles"]:
1007            handles = kwargs["handles"]
1008            del kwargs["handles"]
1009            for handle in item.handles:
1010                shape = Shape(handle)
1011                shape.subtype = Types.HANDLE
1012                handle_sketches = create_sketch(shape, canvas, **kwargs)
1013                sketches.extend(handle_sketches)
1014
1015        for sketch in sketches:
1016            item.closed = sketch.closed
1017            set_shape_sketch_style(sketch, item, canvas, **kwargs)
1018
1019        return sketches
1020
1021    def get_bbox_sketch(item, canvas, **kwargs):
1022        """Create a bounding box sketch from the given item.
1023
1024        Args:
1025            item: Item to be sketched.
1026            canvas: Canvas object.
1027            **kwargs: Additional keyword arguments.
1028
1029        Returns:
1030            ShapeSketch: Created bounding box sketch.
1031        """
1032        nround = defaults["tikz_nround"]
1033        vertices = [(round(x[0], nround), round(x[1], nround)) for x in item.corners]
1034        if not vertices:
1035            return None
1036
1037        sketch = ShapeSketch(vertices, canvas._sketch_xform_matrix)
1038        sketch.subtype = Types.BBOX_SKETCH
1039        sketch.visible = True
1040        sketch.active = True
1041        sketch.closed = True
1042        sketch.fill = False
1043        sketch.stroke = True
1044        sketch.line_color = colors.gray
1045        sketch.line_width = 1
1046        sketch.line_dash_array = [3, 3]
1047        sketch.draw_markers = False
1048        return sketch
1049
1050    def get_handle_sketch(item, canvas, **kwargs):
1051        """Create handle sketches from the given item.
1052
1053        Args:
1054            item: Item to be sketched.
1055            canvas: Canvas object.
1056            **kwargs: Additional keyword arguments.
1057
1058        Returns:
1059            list: List of created handle sketches.
1060        """
1061        nround = defaults["tikz_nround"]
1062        vertices = [(round(x[0], nround), round(x[1], nround)) for x in item.vertices]
1063        if not vertices:
1064            return None
1065        if 'pos' in kwargs:
1066            x, y = item.midpoint[:2]
1067            x1, y1 = kwargs["pos"][:2]
1068            dx = x1 - x
1069            dy = y1 - y
1070            vertices = [(x+dx, y+dy) for x, y in vertices]
1071        sketches = []
1072        sketch = ShapeSketch(vertices, canvas._sketch_xform_matrix)
1073        sketch.subtype = Types.HANDLE
1074        sketch.closed = False
1075        set_shape_sketch_style(sketch, item, canvas, **kwargs)
1076        sketches.append(sketch)
1077        temp_item = Shape()
1078        temp_item.closed = True
1079        handle1 = RectSketch(item.vertices[0], 3, 3, canvas._sketch_xform_matrix)
1080        set_shape_sketch_style(handle1, temp_item, canvas, **kwargs)
1081        handle2 = RectSketch(item.vertices[-1], 3, 3, canvas._sketch_xform_matrix)
1082        set_shape_sketch_style(handle2, temp_item, canvas, **kwargs)
1083        sketches.extend([handle1, handle2])
1084
1085        return sketches
1086
1087    def get_sketch(item, canvas, **kwargs):
1088        """Create a sketch from the given item.
1089
1090        Args:
1091            item: Item to be sketched.
1092            canvas: Canvas object.
1093            **kwargs: Additional keyword arguments.
1094
1095        Returns:
1096            ShapeSketch: Created sketch.
1097        """
1098        if not item.vertices:
1099            return None
1100
1101        # vertices = get_verts_in_new_pos(item, **kwargs)
1102        nround = defaults["tikz_nround"]
1103        vertices = [
1104            (round(x[0], nround), round(x[1], nround)) for x in item.vertices
1105        ]
1106
1107        sketch = ShapeSketch(vertices, canvas._sketch_xform_matrix)
1108        set_shape_sketch_style(sketch, item, canvas, **kwargs)
1109
1110        return sketch
1111
1112    d_subtype_sketch = {
1113        Types.ARC: get_arc_sketch,
1114        Types.ARC_ARROW: get_batch_sketch,
1115        Types.ARROW: get_batch_sketch,
1116        Types.ARROW_HEAD: get_sketch,
1117        Types.BATCH: get_batch_sketch,
1118        Types.BEZIER: get_sketch,
1119        Types.BOUNDING_BOX: get_bbox_sketch,
1120        Types.CIRCLE: get_circle_sketch,
1121        Types.CIRCULAR_GRID: get_batch_sketch,
1122        Types.DIVISION: get_sketch,
1123        Types.DOT: get_circle_sketch,
1124        Types.DOTS: get_dots_sketch,
1125        Types.ELLIPSE: get_sketch,
1126        Types.FRAGMENT: get_sketch,
1127        Types.HANDLE: get_handle_sketch,
1128        Types.HEX_GRID: get_batch_sketch,
1129        Types.LACE: get_lace_sketch,
1130        Types.LINPATH: get_path_sketch,
1131        Types.MIXED_GRID: get_batch_sketch,
1132        Types.OVERLAP: get_batch_sketch,
1133        Types.PARALLEL_POLYLINE: get_batch_sketch,
1134        Types.PATTERN: get_pattern_sketch,
1135        Types.PLAIT: get_sketch,
1136        Types.POLYLINE: get_sketch,
1137        Types.Q_BEZIER: get_sketch,
1138        Types.RECTANGLE: get_sketch,
1139        Types.SECTION: get_sketch,
1140        Types.SEGMENT: get_sketch,
1141        Types.SHAPE: get_sketch,
1142        Types.SINE_WAVE: get_sketch,
1143        Types.SQUARE_GRID: get_batch_sketch,
1144        Types.STAR: get_batch_sketch,
1145        Types.TAG: get_tag_sketch,
1146    }
1147
1148    return d_subtype_sketch[item.subtype](item, canvas, **kwargs)

Create a sketch from the given item.

Arguments:
  • item: Item to be sketched.
  • canvas: Canvas object.
  • **kwargs: Additional keyword arguments.
Returns:

Sketch: Created sketch.