ColabTurtle 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ColabTurtle/Turtle.py ADDED
@@ -0,0 +1,850 @@
1
+ from IPython.display import display, DisplayHandle, HTML
2
+ import time
3
+ import math
4
+ import re
5
+ from typing import Literal, overload
6
+
7
+ TurtleShape = Literal["turtle", "circle"]
8
+ TextAlign = Literal["left", "center", "right"]
9
+ FontType = Literal["bold", "italic", "underline", "normal"]
10
+
11
+ DEFAULT_WINDOW_SIZE = (800, 500)
12
+ DEFAULT_SPEED = 4
13
+ DEFAULT_TURTLE_VISIBILITY = True
14
+ DEFAULT_PEN_COLOR = "white"
15
+ DEFAULT_TURTLE_DEGREE = 270
16
+ DEFAULT_BACKGROUND_COLOR = "black"
17
+ DEFAULT_IS_PEN_DOWN = True
18
+ DEFAULT_SVG_LINES_STRING = ""
19
+ DEFAULT_PEN_WIDTH = 4
20
+ # all 140 color names that modern browsers support. taken from https://www.w3schools.com/colors/colors_names.asp
21
+ VALID_COLORS = (
22
+ "black",
23
+ "navy",
24
+ "darkblue",
25
+ "mediumblue",
26
+ "blue",
27
+ "darkgreen",
28
+ "green",
29
+ "teal",
30
+ "darkcyan",
31
+ "deepskyblue",
32
+ "darkturquoise",
33
+ "mediumspringgreen",
34
+ "lime",
35
+ "springgreen",
36
+ "aqua",
37
+ "cyan",
38
+ "midnightblue",
39
+ "dodgerblue",
40
+ "lightseagreen",
41
+ "forestgreen",
42
+ "seagreen",
43
+ "darkslategray",
44
+ "darkslategrey",
45
+ "limegreen",
46
+ "mediumseagreen",
47
+ "turquoise",
48
+ "royalblue",
49
+ "steelblue",
50
+ "darkslateblue",
51
+ "mediumturquoise",
52
+ "indigo",
53
+ "darkolivegreen",
54
+ "cadetblue",
55
+ "cornflowerblue",
56
+ "rebeccapurple",
57
+ "mediumaquamarine",
58
+ "dimgray",
59
+ "dimgrey",
60
+ "slateblue",
61
+ "olivedrab",
62
+ "slategray",
63
+ "slategrey",
64
+ "lightslategray",
65
+ "lightslategrey",
66
+ "mediumslateblue",
67
+ "lawngreen",
68
+ "chartreuse",
69
+ "aquamarine",
70
+ "maroon",
71
+ "purple",
72
+ "olive",
73
+ "gray",
74
+ "grey",
75
+ "skyblue",
76
+ "lightskyblue",
77
+ "blueviolet",
78
+ "darkred",
79
+ "darkmagenta",
80
+ "saddlebrown",
81
+ "darkseagreen",
82
+ "lightgreen",
83
+ "mediumpurple",
84
+ "darkviolet",
85
+ "palegreen",
86
+ "darkorchid",
87
+ "yellowgreen",
88
+ "sienna",
89
+ "brown",
90
+ "darkgray",
91
+ "darkgrey",
92
+ "lightblue",
93
+ "greenyellow",
94
+ "paleturquoise",
95
+ "lightsteelblue",
96
+ "powderblue",
97
+ "firebrick",
98
+ "darkgoldenrod",
99
+ "mediumorchid",
100
+ "rosybrown",
101
+ "darkkhaki",
102
+ "silver",
103
+ "mediumvioletred",
104
+ "indianred",
105
+ "peru",
106
+ "chocolate",
107
+ "tan",
108
+ "lightgray",
109
+ "lightgrey",
110
+ "thistle",
111
+ "orchid",
112
+ "goldenrod",
113
+ "palevioletred",
114
+ "crimson",
115
+ "gainsboro",
116
+ "plum",
117
+ "burlywood",
118
+ "lightcyan",
119
+ "lavender",
120
+ "darksalmon",
121
+ "violet",
122
+ "palegoldenrod",
123
+ "lightcoral",
124
+ "khaki",
125
+ "aliceblue",
126
+ "honeydew",
127
+ "azure",
128
+ "sandybrown",
129
+ "wheat",
130
+ "beige",
131
+ "whitesmoke",
132
+ "mintcream",
133
+ "ghostwhite",
134
+ "salmon",
135
+ "antiquewhite",
136
+ "linen",
137
+ "lightgoldenrodyellow",
138
+ "oldlace",
139
+ "red",
140
+ "fuchsia",
141
+ "magenta",
142
+ "deeppink",
143
+ "orangered",
144
+ "tomato",
145
+ "hotpink",
146
+ "coral",
147
+ "darkorange",
148
+ "lightsalmon",
149
+ "orange",
150
+ "lightpink",
151
+ "pink",
152
+ "gold",
153
+ "peachpuff",
154
+ "navajowhite",
155
+ "moccasin",
156
+ "bisque",
157
+ "mistyrose",
158
+ "blanchedalmond",
159
+ "papayawhip",
160
+ "lavenderblush",
161
+ "seashell",
162
+ "cornsilk",
163
+ "lemonchiffon",
164
+ "floralwhite",
165
+ "snow",
166
+ "yellow",
167
+ "lightyellow",
168
+ "ivory",
169
+ "white",
170
+ )
171
+ VALID_COLORS_SET = set(VALID_COLORS)
172
+ DEFAULT_TURTLE_SHAPE: TurtleShape = "turtle"
173
+ VALID_TURTLE_SHAPES: tuple[TurtleShape, ...] = ("turtle", "circle")
174
+ SVG_TEMPLATE = """
175
+ <svg width="{window_width}" height="{window_height}">
176
+ <rect width="100%" height="100%" fill="{background_color}"/>
177
+ {lines}
178
+ {turtle}
179
+ </svg>
180
+ """
181
+ TURTLE_TURTLE_SVG_TEMPLATE = """<g visibility={visibility} transform="rotate({degrees},{rotation_x},{rotation_y}) translate({turtle_x}, {turtle_y})">
182
+ <path style=" stroke:none;fill-rule:evenodd;fill:{turtle_color};fill-opacity:1;" d="M 18.214844 0.632812 C 16.109375 1.800781 15.011719 4.074219 15.074219 7.132812 L 15.085938 7.652344 L 14.785156 7.496094 C 13.476562 6.824219 11.957031 6.671875 10.40625 7.066406 C 8.46875 7.550781 6.515625 9.15625 4.394531 11.992188 C 3.0625 13.777344 2.679688 14.636719 3.042969 15.027344 L 3.15625 15.152344 L 3.519531 15.152344 C 4.238281 15.152344 4.828125 14.886719 8.1875 13.039062 C 9.386719 12.378906 10.371094 11.839844 10.378906 11.839844 C 10.386719 11.839844 10.355469 11.929688 10.304688 12.035156 C 9.832031 13.09375 9.257812 14.820312 8.96875 16.078125 C 7.914062 20.652344 8.617188 24.53125 11.070312 27.660156 C 11.351562 28.015625 11.363281 27.914062 10.972656 28.382812 C 8.925781 30.84375 7.945312 33.28125 8.238281 35.1875 C 8.289062 35.527344 8.28125 35.523438 8.917969 35.523438 C 10.941406 35.523438 13.074219 34.207031 15.136719 31.6875 C 15.359375 31.417969 15.328125 31.425781 15.5625 31.574219 C 16.292969 32.042969 18.023438 32.964844 18.175781 32.964844 C 18.335938 32.964844 19.941406 32.210938 20.828125 31.71875 C 20.996094 31.625 21.136719 31.554688 21.136719 31.558594 C 21.203125 31.664062 21.898438 32.414062 22.222656 32.730469 C 23.835938 34.300781 25.5625 35.132812 27.582031 35.300781 C 27.90625 35.328125 27.9375 35.308594 28.007812 34.984375 C 28.382812 33.242188 27.625 30.925781 25.863281 28.425781 L 25.542969 27.96875 L 25.699219 27.785156 C 28.945312 23.960938 29.132812 18.699219 26.257812 11.96875 L 26.207031 11.84375 L 27.945312 12.703125 C 31.53125 14.476562 32.316406 14.800781 33.03125 14.800781 C 33.976562 14.800781 33.78125 13.9375 32.472656 12.292969 C 28.519531 7.355469 25.394531 5.925781 21.921875 7.472656 L 21.558594 7.636719 L 21.578125 7.542969 C 21.699219 6.992188 21.761719 5.742188 21.699219 5.164062 C 21.496094 3.296875 20.664062 1.964844 19.003906 0.855469 C 18.480469 0.503906 18.457031 0.5 18.214844 0.632812"/>
183
+ </g>"""
184
+ TURTLE_CIRCLE_SVG_TEMPLATE = """
185
+ <g visibility={visibility} transform="rotate({degrees},{rotation_x},{rotation_y}) translate({turtle_x}, {turtle_y})">
186
+ <circle stroke="{turtle_color}" stroke-width="3" fill="transparent" r="12" cx="0" cy="0"/>
187
+ <polygon points="0,19 3,16 -3,16" style="fill:{turtle_color};stroke:{turtle_color};stroke-width:2"/>
188
+ </g>
189
+ """
190
+
191
+
192
+ SPEED_TO_SEC_MAP = {
193
+ 1: 1.5,
194
+ 2: 0.9,
195
+ 3: 0.7,
196
+ 4: 0.5,
197
+ 5: 0.3,
198
+ 6: 0.18,
199
+ 7: 0.12,
200
+ 8: 0.06,
201
+ 9: 0.04,
202
+ 10: 0.02,
203
+ 11: 0.01,
204
+ 12: 0.001,
205
+ 13: 0.0001,
206
+ }
207
+
208
+
209
+ def _speedToSec(speed: int) -> float:
210
+ """Helper function that maps [1,13] speed values to ms delays."""
211
+ return SPEED_TO_SEC_MAP[speed]
212
+
213
+
214
+ turtle_speed = DEFAULT_SPEED
215
+
216
+ is_turtle_visible = DEFAULT_TURTLE_VISIBILITY
217
+ pen_color = DEFAULT_PEN_COLOR
218
+ window_size: tuple[int, int] = DEFAULT_WINDOW_SIZE
219
+ turtle_pos: tuple[int | float, int | float] = (
220
+ DEFAULT_WINDOW_SIZE[0] // 2,
221
+ DEFAULT_WINDOW_SIZE[1] // 2,
222
+ )
223
+ turtle_degree: int | float = DEFAULT_TURTLE_DEGREE
224
+ background_color = DEFAULT_BACKGROUND_COLOR
225
+ is_pen_down = DEFAULT_IS_PEN_DOWN
226
+ svg_lines_string = DEFAULT_SVG_LINES_STRING
227
+ pen_width = DEFAULT_PEN_WIDTH
228
+ turtle_shape: TurtleShape = DEFAULT_TURTLE_SHAPE
229
+
230
+ drawing_window: DisplayHandle | None = None
231
+
232
+
233
+ def initializeTurtle(
234
+ initial_speed: int = DEFAULT_SPEED,
235
+ initial_window_size: tuple[int, int] = DEFAULT_WINDOW_SIZE,
236
+ ) -> None:
237
+ """Construct the display for turtle."""
238
+ global window_size
239
+ global drawing_window
240
+ global turtle_speed
241
+ global is_turtle_visible
242
+ global pen_color
243
+ global turtle_pos
244
+ global turtle_degree
245
+ global background_color
246
+ global is_pen_down
247
+ global svg_lines_string
248
+ global pen_width
249
+ global turtle_shape
250
+
251
+ if not isinstance(initial_speed, int) or initial_speed not in range(1, 14):
252
+ raise ValueError("initial_speed must be an integer in interval [1,13]")
253
+ turtle_speed = initial_speed
254
+
255
+ if not (
256
+ isinstance(initial_window_size, tuple)
257
+ and len(initial_window_size) == 2
258
+ and isinstance(initial_window_size[0], int)
259
+ and isinstance(initial_window_size[1], int)
260
+ ):
261
+ raise ValueError("window_size must be a tuple of 2 integers")
262
+
263
+ window_size = initial_window_size
264
+
265
+ is_turtle_visible = DEFAULT_TURTLE_VISIBILITY
266
+ pen_color = DEFAULT_PEN_COLOR
267
+ turtle_pos = (window_size[0] // 2, window_size[1] // 2)
268
+ turtle_degree = DEFAULT_TURTLE_DEGREE
269
+ background_color = DEFAULT_BACKGROUND_COLOR
270
+ is_pen_down = DEFAULT_IS_PEN_DOWN
271
+ svg_lines_string = DEFAULT_SVG_LINES_STRING
272
+ pen_width = DEFAULT_PEN_WIDTH
273
+ turtle_shape = DEFAULT_TURTLE_SHAPE
274
+
275
+ drawing_window = display(HTML(_generateSvgDrawing()), display_id=True)
276
+
277
+
278
+ def _generateTurtleSvgDrawing() -> str:
279
+ """Helper function for generating svg string of the turtle."""
280
+ if is_turtle_visible:
281
+ vis = "visible"
282
+ else:
283
+ vis = "hidden"
284
+
285
+ turtle_x = turtle_pos[0]
286
+ turtle_y = turtle_pos[1]
287
+ degrees = turtle_degree
288
+ template = ""
289
+
290
+ if turtle_shape == "turtle":
291
+ turtle_x -= 18
292
+ turtle_y -= 18
293
+ degrees += 90
294
+ template = TURTLE_TURTLE_SVG_TEMPLATE
295
+ else: # circle
296
+ degrees -= 90
297
+ template = TURTLE_CIRCLE_SVG_TEMPLATE
298
+
299
+ return template.format(
300
+ turtle_color=pen_color,
301
+ turtle_x=turtle_x,
302
+ turtle_y=turtle_y,
303
+ visibility=vis,
304
+ degrees=degrees,
305
+ rotation_x=turtle_pos[0],
306
+ rotation_y=turtle_pos[1],
307
+ )
308
+
309
+
310
+ def _generateSvgDrawing() -> str:
311
+ """Helper function for generating the whole svg string."""
312
+ return SVG_TEMPLATE.format(
313
+ window_width=window_size[0],
314
+ window_height=window_size[1],
315
+ background_color=background_color,
316
+ lines=svg_lines_string,
317
+ turtle=_generateTurtleSvgDrawing(),
318
+ )
319
+
320
+
321
+ def _updateDrawing() -> None:
322
+ """Helper functions for updating the screen using the latest positions/angles/lines etc."""
323
+ if drawing_window is None:
324
+ raise AttributeError(
325
+ "Display has not been initialized yet. Call initializeTurtle() before using."
326
+ )
327
+ time.sleep(_speedToSec(turtle_speed))
328
+ drawing_window.update(HTML(_generateSvgDrawing()))
329
+
330
+
331
+ def _moveToNewPosition(new_pos: tuple[int | float, int | float]) -> None:
332
+ """Helper function for managing any kind of move to a given 'new_pos' and draw lines if pen is down."""
333
+ global turtle_pos
334
+ global svg_lines_string
335
+
336
+ # rounding the new_pos to eliminate floating point errors.
337
+ new_pos = (round(new_pos[0], 3), round(new_pos[1], 3))
338
+
339
+ start_pos = turtle_pos
340
+ if is_pen_down:
341
+ svg_lines_string += """<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke-linecap="round" style="stroke:{pen_color};stroke-width:{pen_width}"/>""".format(
342
+ x1=start_pos[0],
343
+ y1=start_pos[1],
344
+ x2=new_pos[0],
345
+ y2=new_pos[1],
346
+ pen_color=pen_color,
347
+ pen_width=pen_width,
348
+ )
349
+
350
+ turtle_pos = new_pos
351
+ _updateDrawing()
352
+
353
+
354
+ def forward(units: int | float) -> None:
355
+ """Makes the turtle move forward by 'units' units."""
356
+ if not isinstance(units, (int, float)):
357
+ raise ValueError("units must be a number.")
358
+
359
+ alpha = math.radians(turtle_degree)
360
+ ending_point = (
361
+ turtle_pos[0] + units * math.cos(alpha),
362
+ turtle_pos[1] + units * math.sin(alpha),
363
+ )
364
+
365
+ _moveToNewPosition(ending_point)
366
+
367
+
368
+ fd = forward # alias
369
+
370
+
371
+ def backward(units: int | float) -> None:
372
+ """Makes the turtle move backward by 'units' units."""
373
+ if not isinstance(units, (int, float)):
374
+ raise ValueError("units must be a number.")
375
+ forward(-1 * units)
376
+
377
+
378
+ bk = backward # alias
379
+ back = backward # alias
380
+
381
+
382
+ def right(degrees: int | float) -> None:
383
+ """Makes the turtle move right by 'degrees' degrees (NOT radians)."""
384
+ global turtle_degree
385
+
386
+ if not isinstance(degrees, (int, float)):
387
+ raise ValueError("degrees must be a number.")
388
+
389
+ turtle_degree = (turtle_degree + degrees) % 360
390
+ _updateDrawing()
391
+
392
+
393
+ rt = right # alias
394
+
395
+
396
+ def face(degrees: int | float) -> None:
397
+ """Makes the turtle face a given direction."""
398
+ global turtle_degree
399
+
400
+ if not isinstance(degrees, (int, float)):
401
+ raise ValueError("degrees must be a number.")
402
+
403
+ turtle_degree = degrees % 360
404
+ _updateDrawing()
405
+
406
+
407
+ setheading = face # alias
408
+ seth = face # alias
409
+
410
+
411
+ def left(degrees: int | float) -> None:
412
+ """Makes the turtle move left by 'degrees' degrees (NOT radians, this library does not support radians right now)."""
413
+ if not isinstance(degrees, (int, float)):
414
+ raise ValueError("degrees must be a number.")
415
+ right(-1 * degrees)
416
+
417
+
418
+ lt = left
419
+
420
+
421
+ def penup() -> None:
422
+ """Raises the pen such that following turtle moves will not cause any drawings."""
423
+ global is_pen_down
424
+
425
+ is_pen_down = False
426
+ # TODO: decide if we should put the timout after lifting the pen
427
+ # _updateDrawing()
428
+
429
+
430
+ pu = penup # alias
431
+ up = penup # alias
432
+
433
+
434
+ def pendown() -> None:
435
+ """Lowers the pen such that following turtle moves will now cause drawings."""
436
+ global is_pen_down
437
+
438
+ is_pen_down = True
439
+ # TODO: decide if we should put the timout after releasing the pen
440
+ # _updateDrawing()
441
+
442
+
443
+ pd = pendown # alias
444
+ down = pendown # alias
445
+
446
+
447
+ def isdown() -> bool:
448
+ """Returns whether the pen is currently down."""
449
+ return is_pen_down
450
+
451
+
452
+ @overload
453
+ def speed(speed: None = None) -> int: ...
454
+ @overload
455
+ def speed(speed: int) -> None: ...
456
+ def speed(speed: int | None = None) -> int | None:
457
+ """Update the speed of the moves, [1,13].
458
+ If argument is omitted, it returns the speed.
459
+ """
460
+ global turtle_speed
461
+
462
+ if speed is None:
463
+ return turtle_speed
464
+
465
+ if not isinstance(speed, int) or speed not in range(1, 14):
466
+ raise ValueError("speed must be an integer in the interval [1,13].")
467
+ turtle_speed = speed
468
+ # TODO: decide if we should put the timout after changing the speed
469
+ # _updateDrawing()
470
+ return None
471
+
472
+
473
+ def setx(x: int | float) -> None:
474
+ """Move the turtle to a designated 'x' x-coordinate, y-coordinate stays the same."""
475
+ if not isinstance(x, (int, float)):
476
+ raise ValueError("new x position must be a number.")
477
+ if x < 0:
478
+ raise ValueError("new x position must be non-negative.")
479
+ _moveToNewPosition((x, turtle_pos[1]))
480
+
481
+
482
+ def sety(y: int | float) -> None:
483
+ """Move the turtle to a designated 'y' y-coordinate, x-coordinate stays the same."""
484
+ if not isinstance(y, (int, float)):
485
+ raise ValueError("new y position must be a number.")
486
+ if y < 0:
487
+ raise ValueError("new y position must be non-negative.")
488
+ _moveToNewPosition((turtle_pos[0], y))
489
+
490
+
491
+ def home() -> None:
492
+ """Takes the turtle to the beginning position and angle.
493
+ The turtle will continue drawing during this operation if the pen is down.
494
+ """
495
+ global turtle_degree
496
+
497
+ turtle_degree = DEFAULT_TURTLE_DEGREE
498
+ _moveToNewPosition(
499
+ (window_size[0] // 2, window_size[1] // 2)
500
+ ) # this will handle updating the drawing.
501
+
502
+
503
+ def getx() -> int | float:
504
+ """Retrieve the turtle's current 'x' x-coordinate."""
505
+ return turtle_pos[0]
506
+
507
+
508
+ xcor = getx # alias
509
+
510
+
511
+ def gety() -> int | float:
512
+ """Retrieve the turtle's current 'y' y-coordinate."""
513
+ return turtle_pos[1]
514
+
515
+
516
+ ycor = gety # alias
517
+
518
+
519
+ def position() -> tuple[int | float, int | float]:
520
+ """Retrieve the turtle's current position as a (x,y) tuple vector."""
521
+ return turtle_pos
522
+
523
+
524
+ pos = position # alias
525
+
526
+
527
+ def getheading() -> int | float:
528
+ """Retrieve the turtle's current angle."""
529
+ return turtle_degree
530
+
531
+
532
+ heading = getheading # alias
533
+
534
+
535
+ @overload
536
+ def goto(x: tuple[int | float, int | float]) -> None: ...
537
+ @overload
538
+ def goto(x: int | float, y: int | float) -> None: ...
539
+ def goto(
540
+ x: int | float | tuple[int | float, int | float], y: int | float | None = None
541
+ ) -> None:
542
+ """Move the turtle to a designated 'x'-'y' coordinate."""
543
+ if isinstance(x, tuple) and y is None:
544
+ if len(x) != 2:
545
+ raise ValueError("the tuple argument must be of length 2.")
546
+
547
+ y = x[1]
548
+ x = x[0]
549
+
550
+ if not isinstance(x, (int, float)):
551
+ raise ValueError("new x position must be a number.")
552
+ if x < 0:
553
+ raise ValueError("new x position must be non-negative")
554
+ if not isinstance(y, (int, float)):
555
+ raise ValueError("new y position must be a number.")
556
+ if y < 0:
557
+ raise ValueError("new y position must be non-negative.")
558
+ _moveToNewPosition((x, y))
559
+
560
+
561
+ setpos = goto # alias
562
+ setposition = goto # alias
563
+
564
+
565
+ def showturtle() -> None:
566
+ """Switch turtle visibility to ON."""
567
+ global is_turtle_visible
568
+
569
+ is_turtle_visible = True
570
+ _updateDrawing()
571
+
572
+
573
+ st = showturtle # alias
574
+
575
+
576
+ def hideturtle() -> None:
577
+ """Switch turtle visibility to OFF."""
578
+ global is_turtle_visible
579
+
580
+ is_turtle_visible = False
581
+ _updateDrawing()
582
+
583
+
584
+ ht = hideturtle # alias
585
+
586
+
587
+ def isvisible() -> bool:
588
+ """Returns whether turtle is currently visible."""
589
+ return is_turtle_visible
590
+
591
+
592
+ def _validateColorString(color: str) -> bool:
593
+ if color in VALID_COLORS_SET: # 140 predefined html color names
594
+ return True
595
+ if re.search("^#(?:[0-9a-fA-F]{3}){1,2}$", color): # 3 or 6 digit hex color code
596
+ return True
597
+ v = r"(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])" # an integer in [0,255]
598
+ if re.fullmatch(
599
+ rf"rgb\(\s*(?:{v}\s*,\s*{v}\s*,\s*{v}|{v}\s+{v}\s+{v})\s*\)", color
600
+ ): # rgb color code, comma or space separated
601
+ return True
602
+ return False
603
+
604
+
605
+ def _validateColorTuple(color: tuple[object, ...]) -> bool:
606
+ if len(color) != 3:
607
+ return False
608
+ if (
609
+ not isinstance(color[0], int)
610
+ or not isinstance(color[1], int)
611
+ or not isinstance(color[2], int)
612
+ ):
613
+ return False
614
+ if not 0 <= color[0] <= 255 or not 0 <= color[1] <= 255 or not 0 <= color[2] <= 255:
615
+ return False
616
+ return True
617
+
618
+
619
+ def _processColor(color: object) -> str:
620
+ if isinstance(color, str):
621
+ color = color.lower()
622
+ if not _validateColorString(color):
623
+ raise ValueError(
624
+ "color is invalid. it can be a known html color name, 3-6 digit hex string or rgb string."
625
+ )
626
+ return color
627
+ elif isinstance(color, tuple):
628
+ if not _validateColorTuple(color):
629
+ raise ValueError(
630
+ "color tuple is invalid. it must be a tuple of three integers, which are in the interval [0,255]"
631
+ )
632
+ return "rgb(" + str(color[0]) + "," + str(color[1]) + "," + str(color[2]) + ")"
633
+ else:
634
+ raise ValueError("the first parameter must be a color string or a tuple")
635
+
636
+
637
+ @overload
638
+ def bgcolor(color: None = None, c2: None = None, c3: None = None) -> str: ...
639
+ @overload
640
+ def bgcolor(
641
+ color: str | tuple[int, int, int], c2: None = None, c3: None = None
642
+ ) -> None: ...
643
+ @overload
644
+ def bgcolor(color: int, c2: int, c3: int) -> None: ...
645
+ def bgcolor(
646
+ color: str | tuple[int, int, int] | int | None = None,
647
+ c2: int | None = None,
648
+ c3: int | None = None,
649
+ ) -> str | None:
650
+ """Change the background color of the drawing area.
651
+ If no params, return the current background color.
652
+ """
653
+ global background_color
654
+
655
+ if color is None:
656
+ return background_color
657
+ elif c2 is not None:
658
+ if c3 is None:
659
+ raise ValueError(
660
+ "if the second argument is set, the third arguments must be set as well to complete the rgb set."
661
+ )
662
+ background_color = _processColor((color, c2, c3))
663
+ else:
664
+ background_color = _processColor(color)
665
+ _updateDrawing()
666
+ return None
667
+
668
+
669
+ @overload
670
+ def color(color: None = None, c2: None = None, c3: None = None) -> str: ...
671
+ @overload
672
+ def color(
673
+ color: str | tuple[int, int, int], c2: None = None, c3: None = None
674
+ ) -> None: ...
675
+ @overload
676
+ def color(color: int, c2: int, c3: int) -> None: ...
677
+ def color(
678
+ color: str | tuple[int, int, int] | int | None = None,
679
+ c2: int | None = None,
680
+ c3: int | None = None,
681
+ ) -> str | None:
682
+ """Change the color of the pen.
683
+ If no params, return the current pen color.
684
+ """
685
+ global pen_color
686
+
687
+ if color is None:
688
+ return pen_color
689
+ elif c2 is not None:
690
+ if c3 is None:
691
+ raise ValueError(
692
+ "if the second argument is set, the third arguments must be set as well to complete the rgb set."
693
+ )
694
+ pen_color = _processColor((color, c2, c3))
695
+ else:
696
+ pen_color = _processColor(color)
697
+ _updateDrawing()
698
+ return None
699
+
700
+
701
+ pencolor = color
702
+
703
+
704
+ @overload
705
+ def width(width: None = None) -> int: ...
706
+ @overload
707
+ def width(width: int) -> None: ...
708
+ def width(width: int | None = None) -> int | None:
709
+ """Change the width of the lines drawn by the turtle, in pixels.
710
+ If the function is called without arguments, it returns the current width.
711
+ """
712
+ global pen_width
713
+
714
+ if width is None:
715
+ return pen_width
716
+ else:
717
+ if not isinstance(width, int):
718
+ raise ValueError("new width position must be an integer.")
719
+ if not width > 0:
720
+ raise ValueError("new width position must be positive.")
721
+
722
+ pen_width = width
723
+ # TODO: decide if we should put the timout after changing the pen_width
724
+ # _updateDrawing()
725
+ return None
726
+
727
+
728
+ pensize = width
729
+
730
+
731
+ @overload
732
+ def distance(x: tuple[int | float, int | float]) -> float: ...
733
+ @overload
734
+ def distance(x: int | float, y: int | float) -> float: ...
735
+ def distance(
736
+ x: int | float | tuple[int | float, int | float], y: int | float | None = None
737
+ ) -> float:
738
+ """Calculate the distance between the turtle and a given point."""
739
+ if isinstance(x, tuple) and y is None:
740
+ if len(x) != 2:
741
+ raise ValueError("the tuple argument must be of length 2.")
742
+
743
+ y = x[1]
744
+ x = x[0]
745
+
746
+ if not isinstance(x, (int, float)):
747
+ raise ValueError("given x position must be a number.")
748
+ if x < 0:
749
+ raise ValueError("given x position must be non-negative")
750
+ if not isinstance(y, (int, float)):
751
+ raise ValueError("given y position must be a number.")
752
+ if y < 0:
753
+ raise ValueError("given y position must be non-negative.")
754
+
755
+ return round(math.sqrt((turtle_pos[0] - x) ** 2 + (turtle_pos[1] - y) ** 2), 4)
756
+
757
+
758
+ def clear() -> None:
759
+ """Clear any text or drawing on the screen."""
760
+ global svg_lines_string
761
+
762
+ svg_lines_string = ""
763
+ _updateDrawing()
764
+
765
+
766
+ def write(
767
+ obj: object,
768
+ *,
769
+ align: TextAlign = "left",
770
+ font: tuple[int, str, FontType] = (12, "Arial", "normal"),
771
+ **kwargs: object,
772
+ ) -> None:
773
+ """Writes the string equivalent of any value to the screen.
774
+ 'align' and 'font' named parameters can be given as arguments optionally.
775
+ """
776
+ global svg_lines_string
777
+ global turtle_pos
778
+ text = str(obj)
779
+ if align == "center":
780
+ text_anchor = "middle"
781
+ elif align == "right":
782
+ text_anchor = "end"
783
+ else: # "left", and invalid values are silently treated as "left"
784
+ text_anchor = "start"
785
+
786
+ if (
787
+ len(font) != 3
788
+ or not isinstance(font[0], int)
789
+ or not isinstance(font[1], str)
790
+ or font[2] not in {"bold", "italic", "underline", "normal"}
791
+ ):
792
+ raise ValueError(
793
+ "font parameter must be a triplet consisting of font size (int), font family (str) and font type. font type can be one of {bold, italic, underline, normal}"
794
+ )
795
+ font_size = font[0]
796
+ font_family = font[1]
797
+ font_type = font[2]
798
+
799
+ style_string = ""
800
+ style_string += "font-size:" + str(font_size) + "px;"
801
+ style_string += "font-family:'" + font_family + "';"
802
+
803
+ if font_type == "bold":
804
+ style_string += "font-weight:bold;"
805
+ elif font_type == "italic":
806
+ style_string += "font-style:italic;"
807
+ elif font_type == "underline":
808
+ style_string += "text-decoration: underline;"
809
+
810
+ svg_lines_string += """<text x="{x}" y="{y}" fill="{fill_color}" text-anchor="{align}" style="{style}">{text}</text>""".format(
811
+ x=turtle_pos[0],
812
+ y=turtle_pos[1],
813
+ text=text,
814
+ fill_color=pen_color,
815
+ align=text_anchor,
816
+ style=style_string,
817
+ )
818
+
819
+ _updateDrawing()
820
+
821
+
822
+ @overload
823
+ def shape(shape: None = None) -> TurtleShape: ...
824
+ @overload
825
+ def shape(shape: TurtleShape) -> None: ...
826
+ def shape(shape: TurtleShape | None = None) -> TurtleShape | None:
827
+ """Takes a shape name and transforms the main character's look.
828
+ If no argument is supplied, this function returns the name of the current shape.
829
+ """
830
+ global turtle_shape
831
+ if shape is None:
832
+ return turtle_shape
833
+ elif shape not in VALID_TURTLE_SHAPES:
834
+ raise ValueError(
835
+ "shape is invalid. valid options are: " + str(VALID_TURTLE_SHAPES)
836
+ )
837
+
838
+ turtle_shape = shape
839
+ _updateDrawing()
840
+ return None
841
+
842
+
843
+ def window_width() -> int:
844
+ """Return turtle window width."""
845
+ return window_size[0]
846
+
847
+
848
+ def window_height() -> int:
849
+ """Return turtle window height."""
850
+ return window_size[1]
File without changes
ColabTurtle/py.typed ADDED
File without changes
@@ -0,0 +1,278 @@
1
+ Metadata-Version: 2.5
2
+ Name: ColabTurtle
3
+ Version: 2.2.0
4
+ Summary: An HTML based Turtle implementation, in order to work in Google Colab
5
+ Project-URL: Homepage, https://github.com/tolgaatam/ColabTurtle
6
+ Author-email: Tolga Atam <atam@sabanciuniv.edu>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Framework :: IPython
10
+ Classifier: Framework :: Jupyter
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Typing :: Typed
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: ipython>=6.0
15
+ Description-Content-Type: text/markdown
16
+
17
+ Turtle for Google Colab notebooks
18
+ ===================
19
+
20
+ Module for drawing classic Turtle figures on Google Colab notebooks (and other IPython supported environments like Jupyter Lab)
21
+ It uses HTML capabilities of IPython library to draw svg shapes inline.
22
+ Looks of the figures are inspired from [Blockly Games / Turtle](https://blockly-games.appspot.com/turtle).
23
+
24
+ Installation for Google Colab:
25
+ ----
26
+ Create an empty code cell and type:
27
+
28
+ !pip3 install ColabTurtle
29
+
30
+ Run the code cell. Google Colab will install the library.
31
+
32
+
33
+ Usage
34
+ ----
35
+ In any code cell, import like following:
36
+
37
+ from ColabTurtle.Turtle import *
38
+
39
+ As Colab stores the declared variables in the runtime, call this before using:
40
+
41
+ initializeTurtle()
42
+
43
+ Example
44
+ ----
45
+
46
+ The following code illustrates some usages of the library. For all functions available in this library, please check the API documentation below.
47
+
48
+ ```
49
+ from ColabTurtle.Turtle import *
50
+ initializeTurtle(initial_speed=4, initial_window_size=(800,500))
51
+
52
+ color('mediumblue')
53
+ penup()
54
+ goto(100, 250)
55
+ pendown()
56
+ forward(100)
57
+ left(90)
58
+ forward(40)
59
+ right(180)
60
+ forward(80)
61
+ penup()
62
+ right(90)
63
+ forward(50)
64
+ left(90)
65
+ forward(50)
66
+ left(90)
67
+ backward(10)
68
+ pendown()
69
+ speed(10)
70
+ for i in range(18):
71
+ forward(17)
72
+ right(20)
73
+ penup()
74
+ shape('circle')
75
+ color(230, 90, 120)
76
+ speed(7)
77
+ right(90)
78
+ forward(160)
79
+ left(90)
80
+ forward(60)
81
+ right(180)
82
+ pendown()
83
+ forward(100)
84
+ left(90)
85
+ forward(80)
86
+ penup()
87
+ shape('turtle')
88
+ forward(50)
89
+ left(90)
90
+ forward(40)
91
+ pendown()
92
+ for i in range(5):
93
+ forward(17)
94
+ right(20)
95
+ left(200)
96
+ for i in range(13):
97
+ forward(17)
98
+ left(20)
99
+ left(90)
100
+ forward(50)
101
+ penup()
102
+ backward(100)
103
+ left(90)
104
+ forward(40)
105
+ pendown()
106
+ left(155)
107
+ forward(105)
108
+ right(130)
109
+ forward(105)
110
+ backward(45)
111
+ right(115)
112
+ forward(50)
113
+ bgcolor("#efefef")
114
+ penup()
115
+ backward(70)
116
+ left(90)
117
+ forward(100)
118
+ right(90)
119
+ pencolor('black')
120
+ pensize(7)
121
+ pendown()
122
+ forward(650)
123
+ left(180)
124
+ speed(12)
125
+ for i in range(90):
126
+ forward(7)
127
+ right(0.2)
128
+ speed(2)
129
+ left(18+180)
130
+ penup()
131
+ forward(300)
132
+ setheading(270)
133
+ write("ColabTurtle", align="center", font=(25, "Arial", "italic"))
134
+ color('rgb(70 110 70)')
135
+ backward(40)
136
+ ```
137
+
138
+ This code ends up with the following drawing:
139
+
140
+ ![Drawing that reads "Tolga"](https://raw.githubusercontent.com/tolgaatam/ColabTurtle/master/sample_image.jpg "Example Code's Final Look")
141
+
142
+
143
+ API
144
+ ----
145
+ This module's API is mostly identical to the traditional turtle API. There are some differences, most notably: the angles are handled differently. 0 angle is east and the angles increase clockwise. Some functions from the traditional turtle library is missing here; however almost all the main functionality is implemented. The functions that this library implements are explained below:
146
+
147
+
148
+ `forward(units) | fd(units)` -> Moves the turtle in the direction it is facing, by `units` pixels
149
+
150
+ `backward(units) | bk(units) | back(units)` -> Moves the turtle in the opposite of the direction it is facing, by `units` pixels
151
+
152
+ <br/>
153
+
154
+ `right(degrees) | rt(degrees)` -> Turns the turtle to right by the given `degrees` many degrees.
155
+
156
+ `face(degrees) | heading(degrees) | setheading(degrees)` -> Turns the turtle to the direction given as `degrees`
157
+
158
+ `left(degrees) | lt(degrees)` -> Turns the turtle to left by the given `degrees` many degrees.
159
+
160
+ <br/>
161
+
162
+ `penup() | pu() | up()` -> Lifts the pen, turtles movement will not draw anything after this function is called.
163
+
164
+ `pendown() | pd()` -> Puts the pen down, causing the turtle movements to start drawing again.
165
+
166
+ <br/>
167
+
168
+ `speed(s)` -> Sets the speed of turtle's movements. `s` can be a value in interval [1,13] where 1 is the slowest and 13 is the fastest. If `s` is omitted, the function returns the current speed.
169
+
170
+ <br/>
171
+
172
+ `setx(x)` -> Moves the turtle to the given `x` position, the y coordinate of the turtle stays the same.
173
+
174
+ `sety(y)` -> Moves the turtle to the given `y` position, the x coordinate of the turtle stays the same.
175
+
176
+ `home()` -> Takes the turtle to the beginning position and angle. The turtle will continue drawing during this operation if the pen is down.
177
+
178
+ <br/>
179
+
180
+ `getx() | xcor()` -> Returns the current x coordinate of the turtle.
181
+
182
+ `gety() | ycor()` -> Returns the current y coordinate of the turtle.
183
+
184
+ `position() | pos()` -> Returns the current x,y coordinates of the turtle as a tuple.
185
+
186
+ `heading() | getheading()` -> Returns the direction that the turtle is looking at right now, in degrees.
187
+
188
+ <br/>
189
+
190
+ <br/>
191
+
192
+ ```
193
+ goto(x,y) | setpos(x,y) | setposition(x,y)`
194
+ goto((x,y)) | setpos((x,y)) | setposition((x,y))`
195
+ ```
196
+ Moves the turtle to the point defined by x,y. The coordinates can be given separately, or in a single tuple.
197
+
198
+ <br/>
199
+
200
+ <br/>
201
+
202
+ `showturtle() | st()` -> Makes the turtle visible.
203
+
204
+ `hideturtle() | ht()` -> Makes the turtle invisible.
205
+
206
+ `isvisible()` -> Returns whether turtle is currently visible as boolean.
207
+
208
+ <br/>
209
+
210
+ <br/>
211
+
212
+ ```
213
+ bgcolor()
214
+ bgcolor(r,g,b)
215
+ bgcolor((r,g,b))
216
+ bgcolor(colorstring)
217
+ ```
218
+ If no parameter given, returns the current background color as string. Else, changes the background color of the drawing area. The color can be given as three separate color arguments as in the RGB color encoding: red,green,blue. These three numbers can be given in a single tuple as well. The color can be given as a single color string, too! The following formats are accepted for this color string:
219
+ - HTML standard color names: 140 color names defined as standard ( https://www.w3schools.com/colors/colors_names.asp ) . Examples: `"red"`, `"black"`, `"magenta"`, `"cyan"` etc.
220
+ - Hex string with 3 or 6 digits, like `"#fff"`, `"FFF"`, `"#dfdfdf"`, `"#DFDFDF"`
221
+ - RGB string, like `"rgb(10 20 30)"`, `"rgb(10, 20, 30)"`
222
+
223
+ <br/>
224
+
225
+ <br/>
226
+
227
+ ```
228
+ color() | pencolor()
229
+ color(r,g,b) | pencolor(r,g,b)
230
+ color((r,g,b)) | pencolor((r,g,b))
231
+ color(colorstring) | pencolor(colorstring)
232
+ ```
233
+ The same as `bgcolor` but works with the turtle's pen's color.
234
+
235
+ <br/>
236
+
237
+ <br/>
238
+
239
+ `width(w) | pensize(w)` -> Changes the width of the pen. If the parameter is omitted, returns the current pen width.
240
+
241
+ <br/>
242
+
243
+ <br/>
244
+
245
+ ```
246
+ distance(x,y)
247
+ distance((x,y))
248
+ ```
249
+ Returns the turtle's distance to a given point x,y. The coordinates can be given separately or as a single tuple.
250
+
251
+ <br/>
252
+
253
+ <br/>
254
+
255
+ `clear()` -> Clear any drawing on the screen.
256
+
257
+ `write(obj, align=, font=)` -> Writes the string equivalent of any value to the screen. `align` and `font` **named** parameters can be given as arguments optionally. `align` must be one of `"left","center","right"`. It specifies where to put the text with respect to the turtle. `font` must be a tuple of three values like `(20, "Arial", "bold")`. The first value is the size, second value is the font family (only the ones that your browser natively supports must be used), the third value is font style that must be one of `"normal","bold","italic","underline"`.
258
+
259
+ `shape(sh)` -> Takes a shape name `sh` and transforms the main character's look. This library only has `'circle'` and `'turtle'` shapes available. If no argument is supplied, this function returns the name of the current shape.
260
+
261
+ `window_width()` -> Return the width of the turtle window.
262
+
263
+ `window_height()` -> Return the height of the turtle window.
264
+
265
+ Release History
266
+ ----
267
+ - **v2.2.0** (3rd November 2026): Docstrings and type hints for all functions (the package ships a `py.typed` marker), so Python 3.10+ is now required. Fixes `distance()` and the invalid escape sequence warning on Python 3.12+, and validates `rgb(...)` color strings more strictly. Packaging moved to `pyproject.toml`.
268
+ - **v2.1.0** (19th March 2021): Better compatibility with the original turtle API: aliases like `fd`, `rt` and `pu`, new functions such as `face()`, `home()`, `getx()`/`gety()`, `distance()`, `write()`, `shape()` and `window_width()`/`window_height()`, all 140 HTML color names plus hex and rgb colors, speed range extended to [1,13], and moves by floating point units.
269
+ - **v2.0.0** (7th November 2018): Switched from a class-based API to module-level functions for simpler usage, like the procedural interface of Python's built-in turtle module. `initializeTurtle()` replaces creating a `ColabTurtle` object.
270
+ - **v1.0.0** (7th November 2018): Initial release by Tolga Atam.
271
+
272
+ <br/>
273
+
274
+ <br/>
275
+
276
+ <br/>
277
+
278
+ HAVE FUN DRAWING!
@@ -0,0 +1,7 @@
1
+ ColabTurtle/Turtle.py,sha256=RmSjCHGqqoG_m1qlcqkGS8v5IqxOQIqSzi-vrOEk-5E,24656
2
+ ColabTurtle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ ColabTurtle/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ colabturtle-2.2.0.dist-info/METADATA,sha256=htvfE3IdIkBbFZ1ZD5D4yaJrY7D8W-89wmNG_jigVXU,8638
5
+ colabturtle-2.2.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
6
+ colabturtle-2.2.0.dist-info/licenses/LICENSE,sha256=td8h_PGLdgk1Gxb8u2FAdAlLQvSJhGbnS5XbXNq3OQA,1067
7
+ colabturtle-2.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Tolga Atam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.