ColabTurtlePlus 2.1.0__tar.gz → 2.1.1__tar.gz

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.
@@ -32,7 +32,13 @@ Fixed Python 3.13+ SyntaxWarning by using raw strings in regular expressions.
32
32
 
33
33
  v2.1.0 September 2026
34
34
  Added register_shape() (alias addshape) to mimic the role of the function register_shape() from Python's turtle.
35
- This only works to add a polygonal shape. It does not work with images or components.
35
+ This only works to add a polygonal shape (and now component shapes with v2.1.1). It does not work with images.
36
+
37
+ v2.1.1 September 2026
38
+ Added addcomponent() to create new polygonal turtles. Because turtles are defined as svg strings, also added two
39
+ new functions, addellipsecomponent() and addpathcomponent, to create turtles bassed on ellipical shapes and svg
40
+ paths. To be consistent with Python's turtle, these are first invoked using a call to Shape("compound") and then
41
+ combined using register_shape().
36
42
  """
37
43
 
38
44
  DEFAULT_WINDOW_SIZE = (800, 600)
@@ -94,7 +100,7 @@ TURTLE_RING_SVG_TEMPLATE = """<g id="ring" visibility="{visibility}" transform="
94
100
  <polygon points="0,5 5,0 -5,0" transform="skewX({sk}) scale({sx},{sy})" style="fill:{turtle_color};stroke:{pcolor};stroke-width:1" />
95
101
  </g>"""
96
102
  TURTLE_CLASSIC_SVG_TEMPLATE = """<g id="classic" visibility="{visibility}" transform="rotate({degrees},{rotation_x},{rotation_y}) translate({turtle_x}, {turtle_y})">
97
- <polygon points="{points}" transform="skewX({sk}) scale({sx},{sy})" style="stroke:{pcolor};fill:{turtle_color};stroke-width:{pw}" />
103
+ <polygon points="-5,-4.5 0,-2.5 5,-4.5 0,4.5" transform="skewX({sk}) scale({sx},{sy})" style="stroke:{pcolor};fill:{turtle_color};stroke-width:{pw}" />
98
104
  </g>"""
99
105
  TURTLE_ARROW_SVG_TEMPLATE = """<g id="arrow" visibility="{visibility}" transform="rotate({degrees},{rotation_x},{rotation_y}) translate({turtle_x}, {turtle_y})">
100
106
  <polygon points="-10,-5 0,5 10,-5" transform="skewX({sk}) scale({sx},{sy})" style="stroke:{pcolor};fill:{turtle_color};stroke-width:{pw}" />
@@ -115,6 +121,18 @@ TURTLE_USER_SVG_TEMPLATE = """<g id="{id}" visibility="{visibility}" transform="
115
121
  <polygon points="{points}" transform="skewX({sk}) scale({sx},{sy})" style="stroke:{pcolor};fill:{turtle_color};stroke-width:{pw}" />
116
122
  </g>"""
117
123
 
124
+ TURTLE_COMPONENT_SVG_TEMPLATE = """<g id="user" visibility="{visibility}" transform="rotate({degrees},{rotation_x},{rotation_y}) translate({turtle_x}, {turtle_y})">
125
+ "{component}"
126
+ </g>"""
127
+
128
+ POLY_TEMPLATE = """<polygon points="{points}" transform="skewX({sk})
129
+ scale({sx},{sy})" style="stroke:{pcolor};fill:{turtle_color};stroke-width:{pw}" />"""
130
+ ELLIPSE_TEMPLATE = """<ellipse transform="skewX({sk}) scale({sx},{sy})"
131
+ style="stroke:{pcolor};fill:{turtle_color};stroke-width:{pw}" rx="{rx}" ry ="{ry}" cx="{cx}" cy="{cy}" />"""
132
+ PATH_TEMPLATE = """<path d="{path}" style="stroke:{pcolor};fill-rule:evenodd;fill:{turtle_color};fill-opacity:1;"
133
+ transform="skewX({sk}) scale({sx},{sy})" />"""
134
+
135
+
118
136
  SPEED_TO_SEC_MAP = {0: 0, 1: 1.0, 2: 0.8, 3: 0.5, 4: 0.3, 5: 0.25, 6: 0.20, 7: 0.15, 8: 0.125, 9: 0.10, 10: 0.08, 11: 0.04, 12: 0.02, 13: 0.005}
119
137
 
120
138
  shapeDict = {"turtle":TURTLE_TURTLE_SVG_TEMPLATE,
@@ -125,18 +143,8 @@ shapeDict = {"turtle":TURTLE_TURTLE_SVG_TEMPLATE,
125
143
  "triangle":TURTLE_TRIANGLE_SVG_TEMPLATE,
126
144
  "circle":TURTLE_CIRCLE_SVG_TEMPLATE,
127
145
  "turtle2":TURTLE_TURTLE2_SVG_TEMPLATE,
128
- "user":TURTLE_USER_SVG_TEMPLATE,
129
- "blank":""}
130
- pointsDict = {"turtle":"",
131
- "ring":"",
132
- "classic":"",
133
- "arrow":"",
134
- "square":"",
135
- "triangle":"",
136
- "circle":"",
137
- "turtle2":"",
138
- "user":"",
139
146
  "blank":""}
147
+
140
148
  #------------------------------------------------------------------------------------------------
141
149
 
142
150
  def Screen():
@@ -194,9 +202,9 @@ class _Screen:
194
202
 
195
203
  turtle_x = turtle.turtle_pos[0]
196
204
  turtle_y = turtle.turtle_pos[1]
197
- if self._mode == "standard":
205
+ if self._mode == 'standard':
198
206
  degrees = turtle.turtle_degree - turtle.tilt_angle
199
- elif self._mode == "world":
207
+ elif self._mode == 'world':
200
208
  degrees = turtle.turtle_orient - turtle.tilt_angle
201
209
  else:
202
210
  degrees = turtle.turtle_degree + turtle.tilt_angle
@@ -206,8 +214,9 @@ class _Screen:
206
214
  elif turtle.turtle_shape == 'ring':
207
215
  turtle_y += 10*turtle.stretchfactor[1]+4
208
216
  degrees -= 90
209
- else:
217
+ else: #turtle.turtle_shape in {'classic', 'arrow', 'square', 'triangle', 'circle', 'turtle2', 'blank'}:
210
218
  degrees -= 90
219
+
211
220
 
212
221
  svg = shapeDict[turtle.turtle_shape].format(
213
222
  turtle_color=turtle.fill_color,
@@ -216,7 +225,7 @@ class _Screen:
216
225
  turtle_y=turtle_y,
217
226
  visibility=vis,
218
227
  degrees=degrees,
219
- sx=turtle.stretchfactor[0],
228
+ sx=-turtle.stretchfactor[0],
220
229
  sy=turtle.stretchfactor[1],
221
230
  sk=turtle.shear_factor,
222
231
  rx=10*turtle.stretchfactor[0],
@@ -225,7 +234,6 @@ class _Screen:
225
234
  pw = turtle.outline_width,
226
235
  rotation_x=turtle.turtle_pos[0],
227
236
  rotation_y=turtle.turtle_pos[1],
228
- points=pointsDict[turtle.turtle_shape],
229
237
  id = turtle.turtle_shape)
230
238
  return svg
231
239
 
@@ -345,25 +353,25 @@ class _Screen:
345
353
  text_file.write(output)
346
354
  text_file.close()
347
355
 
348
- def register_shape(self, name, points=None):
349
- """Adds a polygonal turtle shape to to the shape list.
356
+ def register_shape(self, name, shape=None):
357
+ """Adds a turtle shape to to the shape list.
350
358
 
351
359
  Arg:
352
360
  name is an arbitrary string
353
- points is a list or tuple of pairs of coordinates that define the polygon.
361
+ points is a list or tuple of pairs of coordinates that define a polygon,
362
+ or a (compound) Shape object
354
363
 
355
- Installs the corresponding polygon shape.
364
+ Installs the corresponding polygon shape or the corresponding compound shape.
356
365
  If no points are given, the turtle shape will be blank.
357
- Note: This version does NOT include shapes that are images or components.
366
+ Note: This version does NOT include shapes that are images
358
367
  """
359
368
 
360
369
  if not isinstance(name,str):
361
370
  raise TypeError("The name must be a string")
362
- if points is None:
371
+ if shape is None:
363
372
  self.points = None
364
- else:
365
- if not isinstance(points, (list, tuple)):
366
- raise TypeError("The points must be a list or tuple of coordinate pairs.")
373
+ elif isinstance(shape, (list, tuple)):
374
+ points = shape
367
375
  if len(points) < 2:
368
376
  raise ValueError("The points must contain at least 2 coordinate pairs.")
369
377
  for i, point in enumerate(points):
@@ -379,10 +387,24 @@ class _Screen:
379
387
  raise TypeError(
380
388
  f"The point[{i}] must contain numeric coordinates."
381
389
  )
382
- name = name.lower()
383
- VALID_TURTLE_SHAPES.add(name)
384
- pointsDict[name] = " ".join(f"{x},{y}" for x, y in points)
385
- shapeDict[name] = TURTLE_USER_SVG_TEMPLATE
390
+ name = name.lower()
391
+ VALID_TURTLE_SHAPES.add(name)
392
+ pointstr = " ".join(f"{x},{y}" for x, y in points)
393
+ shapeDict[name] = TURTLE_USER_SVG_TEMPLATE.replace("{points}",pointstr) #TURTLE_USER_SVG_TEMPLATE
394
+ else: #assume compound shape
395
+ tmp=TURTLE_COMPONENT_SVG_TEMPLATE.format(
396
+ component=shape._data,
397
+ visibility="{visibility}",
398
+ degrees="{degrees}",
399
+ rotation_x="{rotation_x}",
400
+ rotation_y="{rotation_y}",
401
+ turtle_x="{turtle_x}",
402
+ turtle_y="{turtle_y}",
403
+ )
404
+ # componentDict[shape] = tm
405
+ name = name.lower()
406
+ VALID_TURTLE_SHAPES.add(name)
407
+ shapeDict[name] = tmp
386
408
  addshape=register_shape
387
409
 
388
410
  #=========================
@@ -793,7 +815,7 @@ class _Screen:
793
815
  mode: (optional) one of "standard, "logo", "world", or "sv
794
816
 
795
817
  The defaults are (800,600) and "standard".
796
- """
818
+ """
797
819
  if window is not None:
798
820
  if not (isinstance(window, tuple) and len(window) == 2 and isinstance(
799
821
  window[0], int) and isinstance(window[1], int)):
@@ -847,7 +869,109 @@ class _Screen:
847
869
  err = 'The color parameter ' + color + ' must be a color string or a tuple'
848
870
  raise ValueError(err)
849
871
 
872
+ #----------------------------------------------------------------------------------------------
873
+ class Shape(object):
874
+ def __init__(self, type_, data=None):
875
+ self._type = type_
876
+ if type_ == "compound":
877
+ data = ""
878
+ self._data = data
879
+
880
+
881
+ def addcomponent(self, points, fill=None, outline=None):
882
+ """Add polygonal component to a shape of type compound.
883
+
884
+ Arguments: poly is a polygon, i. e. a tuple of number pairs.
885
+ fill is the fillcolor of the polygon,
886
+ outline is the outline color of the polygon.
887
+
888
+ Example:
889
+ >>> poly = ((0,0),(10,-5),(0,10),(-10,-5))
890
+ >>> s = Shape("compound")
891
+ >>> s.addcomponent(poly, "red", "blue")
892
+ >>> # .. add more components and then use register_shape()
893
+ """
894
+
895
+ tmp = self._data
896
+ p = " ".join(f"{x},{y}" for x, y in points)
897
+ template = POLY_TEMPLATE.replace("{points}",p) + "\n"
898
+ if fill is not None:
899
+ template = template.replace("{turtle_color}",fill)
900
+ if outline is None:
901
+ template = template.replace("{pcolor}",fill)
902
+ else:
903
+ template = template.replace("{pcolor}", outline)
904
+ elif outline is not None:
905
+ template = template.replace("{pcolor}",outline)
906
+ self._data = tmp + template
907
+
908
+ def addellipsecomponent(self, center, radii, fill=None, outline=None):
909
+ """Add elliptical component to a shape of type compound.
910
+
911
+ Arguments: center is a tuple (cx,cy) that is the center of the ellipse
912
+ radii is a tuple (rx, ry) giving the radius of the ellipse in the x and y directions.
913
+ Can use just radii = r as a substitute for (r,r) to do a circle of radius r
914
+ fill is the fillcolor of the ellipse,
915
+ outline is the outline color of the ellipse.
916
+
917
+ Example:
918
+ >>> s = Shape("compound")
919
+ >>> s.addellipsecomponent((0,0), (50,100), "red", "blue")
920
+ >>> # .. add more components and then use register_shape()
921
+ """
922
+ tmp = self._data
923
+ if isinstance(radii, (float,int)):
924
+ xradius = radii
925
+ yradius = radii
926
+ elif isinstance(radii, tuple):
927
+ xradius = radii[0]
928
+ yradius = radii[1]
929
+ replacements = {
930
+ "{cx}": str(center[0]),
931
+ "{cy}": str(center[1]),
932
+ "{rx}": str(xradius),
933
+ "{ry}": str(yradius)
934
+ }
935
+ pattern = re.compile("|".join(re.escape(key) for key in replacements.keys()))
936
+ template = pattern.sub(lambda match: replacements[match.group(0)], ELLIPSE_TEMPLATE)
937
+ if fill is not None:
938
+ template = template.replace("{turtle_color}",fill)
939
+ if outline is None:
940
+ template = template.replace("{pcolor}",fill)
941
+ else:
942
+ template = template.replace("{pcolor}", outline)
943
+ elif outline is not None:
944
+ template = template.replace("{pcolor}",outline)
945
+ self._data = tmp + template + "\n"
946
+
947
+ def addpathcomponent(self, path, fill=None, outline=None):
948
+ """Add an SVG path component to a shape of type compound.
949
+
950
+ Arguments: path is an SVG string defining a path
951
+ fill is the fillcolor of the component,
952
+ outline is the outline color of the component.
953
+
954
+ Example:
955
+ >>> curve = "M -50 -50 Q 0 100 50 -50" (quadratic Bezier curve)
956
+ >>> s = Shape("compound")
957
+ >>> s.addpathcomponent(curve, "red", "blue")
958
+ >>> # .. add more components and then use register_shape()
959
+ """
960
+ tmp = self._data
961
+ template = PATH_TEMPLATE.replace("{path}",path)
962
+ if fill is not None:
963
+ template = template.replace("{turtle_color}",fill)
964
+ if outline is None:
965
+ template = template.replace("{pcolor}",fill)
966
+ else:
967
+ template = template.replace("{pcolor}", outline)
968
+ elif outline is not None:
969
+ template = template.replace("{pcolor}",outline)
970
+ self._data = tmp + template + "\n"
850
971
 
972
+ addEllipseComponet = addellipsecomponent
973
+ addPathComponent = addpathcomponent
974
+ #-------------------------------------------------------------
851
975
 
852
976
  #----------------------------------------------------------------------------------------------
853
977
 
@@ -975,7 +1099,7 @@ class RawTurtle:
975
1099
  template = shapeDict[self.turtle_shape]
976
1100
  tmp = """<animateTransform id = "one" attributeName="transform"
977
1101
  type="scale"
978
- from="1 1" to="{sx} {sy}"
1102
+ from="{sx} {sy}" to="{sx} {sy}"
979
1103
  begin="0s" dur="0.01s"
980
1104
  repeatCount="1"
981
1105
  additive="sum"
@@ -992,7 +1116,7 @@ class RawTurtle:
992
1116
  shapeDict.update({self.turtle_shape:newtemplate})
993
1117
  self.stretchfactor = 1,1
994
1118
  self.timeout = self.timeout*abs(deg)/90+0.001
995
- #self.screen._updateDrawing(self)
1119
+ self.screen._updateDrawing(self)
996
1120
  self.turtle_degree = (self.turtle_degree + deg) % 360
997
1121
  self.turtle_orient = self._turtleOrientation()
998
1122
  shapeDict.update({self.turtle_shape:template})
@@ -1647,6 +1771,19 @@ class RawTurtle:
1647
1771
  deg = math.degrees(math.atan2(-Dxy[1],Dxy[0])) % 360
1648
1772
  return 360-deg
1649
1773
 
1774
+ def extract_points(self):
1775
+ svg_string = shapeDict[self.turtle_shape]
1776
+ match = re.search(r'points="([^"]*)"', svg_string)
1777
+ if not match:
1778
+ raise ValueError("No points attribute found")
1779
+ def number(s):
1780
+ value = float(s)
1781
+ return int(value) if value.is_integer() else value
1782
+ return tuple(
1783
+ (number(x), number(y))
1784
+ for x, y in (point.split(",") for point in match.group(1).split())
1785
+ )
1786
+
1650
1787
  #========================================
1651
1788
  # Turtle Motion - Setting and Measurement
1652
1789
  #========================================
@@ -1922,6 +2059,7 @@ class RawTurtle:
1922
2059
 
1923
2060
  return self.is_filling
1924
2061
 
2062
+
1925
2063
  # Initialize the string for the svg path of the filled shape.
1926
2064
  # Modified from aronma/ColabTurtle_2 github repo
1927
2065
  # The current _svg_lines_string is stored to be used when the fill is finished because the svg_fill_string will include
@@ -2241,7 +2379,8 @@ class RawTurtle:
2241
2379
  outline = self.outline_width
2242
2380
  elif not isinstance(outline, (int,float)):
2243
2381
  raise ValueError('The outline must be a positive number.')
2244
- self.outline_width = outline
2382
+ self.outline_width = outline
2383
+ self.screen._updateDrawing(turtle=self, delay=False)
2245
2384
  turtlesize = shapesize #alias
2246
2385
 
2247
2386
  # Set or return the current shearfactor. Shear the turtleshape according to the given shearfactor shear, which is the tangent of the shear angle.
@@ -2449,9 +2588,11 @@ _tg_screen_functions = ['addshape', 'bgcolor', 'clearscreen', 'drawline', 'hideb
2449
2588
  'initializescreen','initializeTurtle', 'showSVG', 'saveSVG', 'line', 'mode', 'register_shape', 'resetscreen', 'setup',
2450
2589
  'setworldcoordinates', 'showborder', 'turtles', 'window_width', 'window_height' ]
2451
2590
 
2591
+ _tg_shape_functions = ['addcomponent', 'addellipsecomponent', 'addpathcomponent']
2592
+
2452
2593
  _tg_turtle_functions = ['animationOff', 'animationOn', 'bk', 'back', 'backward', 'begin_fill',
2453
2594
  'circle', 'clear', 'clearstamp', 'clearstamps', 'color', 'degrees', 'delay', 'distance', 'done',
2454
- 'dot', 'down', 'end_fill', 'face', 'fd', 'fillcolor', 'filling', 'fillopacity', 'fillrule', 'forward',
2595
+ 'dot', 'down', 'end_fill', 'extract_points', 'face', 'fd', 'fillcolor', 'filling', 'fillopacity', 'fillrule', 'forward',
2455
2596
  'getheading', 'getx', 'gety', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown',
2456
2597
  'isvisible', 'jumpto', 'left', 'lt', 'pd', 'pen', 'pencolor', 'pensize', 'pendown', 'penup', 'pos',
2457
2598
  'position', 'pu', 'radians', 'regularPolygon', 'reset', 'right', 'rt', 'setheading', 'seth',
@@ -2514,7 +2655,6 @@ def _screen_docrevise(docstr):
2514
2655
  newdocstr = parexp.sub(":", newdocstr)
2515
2656
  return newdocstr
2516
2657
 
2517
-
2518
2658
  __func_body = """\
2519
2659
  def {name}{paramslist}:
2520
2660
  if {obj} is None:
@@ -2540,3 +2680,4 @@ _make_global_funcs(_tg_screen_functions, _Screen, 'Turtle._screen', 'Screen()',_
2540
2680
 
2541
2681
 
2542
2682
 
2683
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ColabTurtlePlus
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: An HTML based Turtle implementation with classes for Google Colab and Jupyter Labs
5
5
  Home-page: https://github.com/mathriddle/ColabTurtlePlus
6
6
  Author: Larry Riddle
@@ -35,7 +35,9 @@ To use multiple turtles on a screen one has to use the object-oriented interface
35
35
 
36
36
  What's New:
37
37
  ----
38
- Sept. 2026: Version 2.1.0 adds a register_shape (alias addshape) function to create a polygonal turtle shape. It does not work with images or components.
38
+ Sept. 2026: Version 2.1.0 adds a register_shape (alias addshape) function to create a polygonal turtle shape. It does not work with images.
39
+
40
+ Sept. 2026: Version 2.1.1 allows use of addcomponent() to add a polygon to a component turtle shape. There are two new ways to build additional components: addellipsecomponent() adds an elliptical shape, and addpathcompenent adds a path defined using svg path commands.
39
41
 
40
42
  Installation
41
43
  ----
@@ -57,7 +59,6 @@ Example 1
57
59
  ---
58
60
  This example uses the procedure-oriented interface.
59
61
  ```
60
- from ColabTurtlePlus.Turtle import *
61
62
  clearscreen()
62
63
  setup(300,300)
63
64
  showborder()
@@ -81,7 +82,6 @@ Example 2
81
82
  ----
82
83
  This example has two turtles and uses the object-oriented interface.
83
84
  ```
84
- from ColabTurtlePlus.Turtle import *
85
85
  clearscreen()
86
86
  setup(500,300)
87
87
  T = Turtle()
@@ -109,6 +109,48 @@ S.end_fill()
109
109
  The resulting image is
110
110
  ![](https://github.com/mathriddle/ColabTurtlePlus/raw/main/stars.svg)
111
111
 
112
+ Example 3:
113
+ ----
114
+ This example uses components.
115
+ ```
116
+ clearscreen()
117
+ setup(300,300)
118
+
119
+ comp_shape = Shape("compound")
120
+ poly = ((-50,0),(-50,100),(50,100),(50,0))
121
+ comp_shape.addcomponent(poly,"blue")
122
+ comp_shape.addellipsecomponent((0,120),50,"red")
123
+
124
+ star_coords = ((-10,-13.8), (0,17), (10,-13.8), (-16.2,5.3), (16.2,5.3))
125
+
126
+ addshape("box_with_ball", comp_shape)
127
+ addshape("star", star_coords)
128
+
129
+ t = Turtle()
130
+ t.shapesize(0.5,0.5)
131
+ t.fillcolor("yellow")
132
+ t.shapesize(0.25)
133
+ t.shape("box_with_ball")
134
+ t.pensize(2)
135
+ t.speed(5)
136
+ t.begin_fill()
137
+
138
+ for _ in range(4):
139
+ t.forward(100)
140
+ t.left(90)
141
+ t.end_fill()
142
+ t.forward(100)
143
+ t.left(90)
144
+
145
+ s = Turtle()
146
+ s.shapesize(1.5)
147
+ s.shape("star")
148
+ s.circle(-50)
149
+ s.color("black","green")
150
+ ```
151
+ The resulting image is
152
+ ![](https://github.com/mathriddle/ColabTurtlePlus/raw/main/example3.svg)
153
+
112
154
  Main differences with ColabTurtle
113
155
  ----
114
156
  This version implements classes.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ColabTurtlePlus
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: An HTML based Turtle implementation with classes for Google Colab and Jupyter Labs
5
5
  Home-page: https://github.com/mathriddle/ColabTurtlePlus
6
6
  Author: Larry Riddle
@@ -35,7 +35,9 @@ To use multiple turtles on a screen one has to use the object-oriented interface
35
35
 
36
36
  What's New:
37
37
  ----
38
- Sept. 2026: Version 2.1.0 adds a register_shape (alias addshape) function to create a polygonal turtle shape. It does not work with images or components.
38
+ Sept. 2026: Version 2.1.0 adds a register_shape (alias addshape) function to create a polygonal turtle shape. It does not work with images.
39
+
40
+ Sept. 2026: Version 2.1.1 allows use of addcomponent() to add a polygon to a component turtle shape. There are two new ways to build additional components: addellipsecomponent() adds an elliptical shape, and addpathcompenent adds a path defined using svg path commands.
39
41
 
40
42
  Installation
41
43
  ----
@@ -57,7 +59,6 @@ Example 1
57
59
  ---
58
60
  This example uses the procedure-oriented interface.
59
61
  ```
60
- from ColabTurtlePlus.Turtle import *
61
62
  clearscreen()
62
63
  setup(300,300)
63
64
  showborder()
@@ -81,7 +82,6 @@ Example 2
81
82
  ----
82
83
  This example has two turtles and uses the object-oriented interface.
83
84
  ```
84
- from ColabTurtlePlus.Turtle import *
85
85
  clearscreen()
86
86
  setup(500,300)
87
87
  T = Turtle()
@@ -109,6 +109,48 @@ S.end_fill()
109
109
  The resulting image is
110
110
  ![](https://github.com/mathriddle/ColabTurtlePlus/raw/main/stars.svg)
111
111
 
112
+ Example 3:
113
+ ----
114
+ This example uses components.
115
+ ```
116
+ clearscreen()
117
+ setup(300,300)
118
+
119
+ comp_shape = Shape("compound")
120
+ poly = ((-50,0),(-50,100),(50,100),(50,0))
121
+ comp_shape.addcomponent(poly,"blue")
122
+ comp_shape.addellipsecomponent((0,120),50,"red")
123
+
124
+ star_coords = ((-10,-13.8), (0,17), (10,-13.8), (-16.2,5.3), (16.2,5.3))
125
+
126
+ addshape("box_with_ball", comp_shape)
127
+ addshape("star", star_coords)
128
+
129
+ t = Turtle()
130
+ t.shapesize(0.5,0.5)
131
+ t.fillcolor("yellow")
132
+ t.shapesize(0.25)
133
+ t.shape("box_with_ball")
134
+ t.pensize(2)
135
+ t.speed(5)
136
+ t.begin_fill()
137
+
138
+ for _ in range(4):
139
+ t.forward(100)
140
+ t.left(90)
141
+ t.end_fill()
142
+ t.forward(100)
143
+ t.left(90)
144
+
145
+ s = Turtle()
146
+ s.shapesize(1.5)
147
+ s.shape("star")
148
+ s.circle(-50)
149
+ s.color("black","green")
150
+ ```
151
+ The resulting image is
152
+ ![](https://github.com/mathriddle/ColabTurtlePlus/raw/main/example3.svg)
153
+
112
154
  Main differences with ColabTurtle
113
155
  ----
114
156
  This version implements classes.
@@ -10,7 +10,9 @@ To use multiple turtles on a screen one has to use the object-oriented interface
10
10
 
11
11
  What's New:
12
12
  ----
13
- Sept. 2026: Version 2.1.0 adds a register_shape (alias addshape) function to create a polygonal turtle shape. It does not work with images or components.
13
+ Sept. 2026: Version 2.1.0 adds a register_shape (alias addshape) function to create a polygonal turtle shape. It does not work with images.
14
+
15
+ Sept. 2026: Version 2.1.1 allows use of addcomponent() to add a polygon to a component turtle shape. There are two new ways to build additional components: addellipsecomponent() adds an elliptical shape, and addpathcompenent adds a path defined using svg path commands.
14
16
 
15
17
  Installation
16
18
  ----
@@ -32,7 +34,6 @@ Example 1
32
34
  ---
33
35
  This example uses the procedure-oriented interface.
34
36
  ```
35
- from ColabTurtlePlus.Turtle import *
36
37
  clearscreen()
37
38
  setup(300,300)
38
39
  showborder()
@@ -56,7 +57,6 @@ Example 2
56
57
  ----
57
58
  This example has two turtles and uses the object-oriented interface.
58
59
  ```
59
- from ColabTurtlePlus.Turtle import *
60
60
  clearscreen()
61
61
  setup(500,300)
62
62
  T = Turtle()
@@ -84,6 +84,48 @@ S.end_fill()
84
84
  The resulting image is
85
85
  ![](https://github.com/mathriddle/ColabTurtlePlus/raw/main/stars.svg)
86
86
 
87
+ Example 3:
88
+ ----
89
+ This example uses components.
90
+ ```
91
+ clearscreen()
92
+ setup(300,300)
93
+
94
+ comp_shape = Shape("compound")
95
+ poly = ((-50,0),(-50,100),(50,100),(50,0))
96
+ comp_shape.addcomponent(poly,"blue")
97
+ comp_shape.addellipsecomponent((0,120),50,"red")
98
+
99
+ star_coords = ((-10,-13.8), (0,17), (10,-13.8), (-16.2,5.3), (16.2,5.3))
100
+
101
+ addshape("box_with_ball", comp_shape)
102
+ addshape("star", star_coords)
103
+
104
+ t = Turtle()
105
+ t.shapesize(0.5,0.5)
106
+ t.fillcolor("yellow")
107
+ t.shapesize(0.25)
108
+ t.shape("box_with_ball")
109
+ t.pensize(2)
110
+ t.speed(5)
111
+ t.begin_fill()
112
+
113
+ for _ in range(4):
114
+ t.forward(100)
115
+ t.left(90)
116
+ t.end_fill()
117
+ t.forward(100)
118
+ t.left(90)
119
+
120
+ s = Turtle()
121
+ s.shapesize(1.5)
122
+ s.shape("star")
123
+ s.circle(-50)
124
+ s.color("black","green")
125
+ ```
126
+ The resulting image is
127
+ ![](https://github.com/mathriddle/ColabTurtlePlus/raw/main/example3.svg)
128
+
87
129
  Main differences with ColabTurtle
88
130
  ----
89
131
  This version implements classes.
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = ColabTurtlePlus
3
- version = 2.1.0
3
+ version = 2.1.1
4
4
  author = Larry Riddle
5
5
  author_email = lriddle@agnesscott.edu
6
6
  description = 'An HTML based Turtle implementation with classes for Google Colab and Jupyter Labs'
@@ -9,7 +9,7 @@ README = (HERE / "README.md").read_text()
9
9
 
10
10
  setup(
11
11
  name='ColabTurtlePlus',
12
- version='2.1.0',
12
+ version='2.1.1',
13
13
  packages=['ColabTurtlePlus'],
14
14
  url='https://github.com/mathriddle/ColabTurtlePlus',
15
15
  license='MIT',
File without changes