trtl 0.0.2 → 0.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 80fa5fb101dd5fe7e3ed22055cea60244ab076e5
4
- data.tar.gz: f15e874499332c044aa3fd9cfed00bbf75c6587b
2
+ SHA256:
3
+ metadata.gz: 9dfd21924c3f6626d0b10b91df30b90dce0f8adce0c861fd6adda1fbf0bac6c1
4
+ data.tar.gz: fbcc9c05df13cb1109aff89f30b2e8932eaef3e04f70a157c27ea9e61c6bd9f2
5
5
  SHA512:
6
- metadata.gz: 7b662109d8892ca4533a9e8f0c967caa2a606a1de38f5791f97b83a78d039d1079579ba560cac9631f1e3df33a21fa09d66614a778a86964bebc94d58b6451ae
7
- data.tar.gz: 034af7c28de84e94045db0e2d693d6d1c8e556e4935317375c04f9416fc210863ceaa1fa8647901ba8c287f3c859b11ff70ca3f8d99fc1191787b1929fd72208
6
+ metadata.gz: f7a6013033c7c941e35e796d44181f69bae075866248203eaee8ab29e814f8bc1106e11ab46265fa6bcb65b290aea7e89bcdc11ca009b8b90f047580280a4d88
7
+ data.tar.gz: 94f13a513d82e4b182accc6eae2eea70d52ade1b44364d80acd0657e585d607d689f300cc654fc564f9bb935dac9d6311181b671b7a55d145c3b4cf3b38e8b6d
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Peter Cooper
1
+ Copyright (c) 2012-2026 Peter Cooper
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
data/README.md CHANGED
@@ -1,20 +1,19 @@
1
1
  # Trtl - Simple Ruby Turtle Graphics
2
2
 
3
- ![example session](http://no.gd/p/trtl-20120708-034138.jpg)
3
+ > [!NOTE]
4
+ > Trtl originally drew its graphics with Tk, which shipped with Ruby until Ruby 2.4 moved it out of the standard library and into a separate `tk` gem. That gem has to be compiled against a Tcl/Tk installation, which made Trtl a pain to set up, particularly on macOS. In 2026, Trtl was ported to [LibUI](https://github.com/kojix2/LibUI), whose gem includes a precompiled native library for macOS, Windows and Linux, so on most systems `gem install` is all it takes.
4
5
 
5
6
  ## Installation
7
+
6
8
  gem install trtl
7
9
 
8
10
  ## Description
9
11
 
10
- Trtl is a simple turtle system inspired by Python's turtle.py. It provides
11
- simple turtle drawing capabilities in Ruby, even if you're just at an IRb
12
- prompt. It leans on Tk which is part of MRI 1.9's standard library so in theory it should
13
- work 'out of the box' with most MRI 1.9 installs.
12
+ Trtl is a simple 'turtle' system inspired by Python's turtle.py. It provides simple turtle drawing capabilities in Ruby, even if you're just at an IRB prompt. It draws using [LibUI](https://github.com/kojix2/LibUI).
14
13
 
15
14
  ## Usage
16
15
 
17
- If Trtl detects you're in an IRb or Pry session, it'll automatically make turtle
16
+ If Trtl detects you're in an IRB or Pry session, it'll automatically make turtle
18
17
  methods available directly at the prompt. For example:
19
18
 
20
19
  $ irb
@@ -28,48 +27,50 @@ can create a Trtl instance and use it directly:
28
27
  require 'trtl'
29
28
  t = Trtl.new
30
29
  10.times { t.left(24); t.forward(30); t.ensure_drawn }
31
- t.wait
30
+ t.wait # keeps the window open until it's closed or Enter is pressed
32
31
 
33
32
  You can use Trtl's `run` method to use it in a more interactive fashion:
34
33
 
35
- Trtl.new.run { 10.times { left(24); forward(30); ensure_drawn } }
34
+ Trtl.new.run { 10.times { left(24); forward(30); ensure_drawn }; wait }
36
35
 
37
36
  Or you can include InteractiveTurtle and get a similar effect as if you were in
38
- IRb:
37
+ IRB:
39
38
 
40
39
  include InteractiveTurtle
41
40
  10.times { left(24); forward(30) }
41
+ wait
42
42
 
43
43
  Note: Using InteractiveTurtle makes drawing slower as it ensures all graphics
44
- are drawn after every action (as necessary for IRb use).
44
+ are drawn after every action (as necessary for IRB use).
45
45
 
46
46
  ## Commands
47
47
 
48
48
  Only a small number of commands are currently implemented, but they're enough for the major actions:
49
49
 
50
- * `title(title_name)`
50
+ * `title(title_name)`
51
51
  * `forward(distance)` - aliased as `fd`
52
52
  * `back(distance)` - aliased as `bk` and `backward`
53
- * `left(angle)` - aliased as `lt`
54
- * `right(angle)` - aliased as `rt`
53
+ * `left(angle)` - aliased as `lt` and `turnleft`
54
+ * `right(angle)` - aliased as `rt` and `turnright`
55
55
  * `pen_up` - aliased as `pu` and `up` and `penup`
56
56
  * `pen_down` - aliased as `pd` and `down` and `pendown`
57
- * `color(color_name)` - aliased as `pencolor`
58
- * `move(x, y)` - aliased as `goto`
57
+ * `color(color_name)` - aliased as `pencolor`. Accepts X11 color names (`"darkolivegreen"`) or hex (`"#1e90ff"`), and returns the current color if called with no argument
58
+ * `move(x, y)` - aliased as `goto`, `setpos` and `setposition`
59
+ * `home` - returns to the center of the canvas, facing right
59
60
  * `position` - aliased as `pos`
60
61
  * `circle(radius, extent = 360, steps = 360)`
61
62
  * `dot(size)` - draws a dot, defaults to a sensible size but you can supply if you want
62
63
  * `is_drawing?`
63
64
  * `width(width_in_pixels)`
64
-
65
- More documentation to come later.
65
+ * `ensure_drawn` - makes sure everything drawn so far is on screen (useful in loops in scripts)
66
+ * `wait` - keeps the window open until it's closed or Enter is pressed (needed at the end of scripts, not in IRB)
66
67
 
67
68
  ## Examples
68
69
 
69
70
  The examples in the `examples` folder should be reasonably illustrative. If you
70
71
  try any of them, try `example4.rb` - it renders an awesome looking tree.
71
72
 
72
- ![tree](http://no.gd/p/trtltree-20120708-035127.jpg)
73
+ ![tree](images/tree.png)
73
74
 
74
75
  ## Credits
75
76
 
@@ -78,6 +79,6 @@ try any of them, try `example4.rb` - it renders an awesome looking tree.
78
79
 
79
80
  ## Copyright and License
80
81
 
81
- Copyright (c) 2012 Peter Cooper (other than minor parts of some samples.)
82
+ Copyright (c) 2012-2026 Peter Cooper (other than minor parts of some samples.)
82
83
 
83
84
  MIT licensed. See LICENSE.md
data/examples/example2.rb CHANGED
@@ -11,4 +11,4 @@ t = Trtl.new
11
11
  t.ensure_drawn
12
12
  end
13
13
 
14
- gets
14
+ t.wait
@@ -0,0 +1,273 @@
1
+ require 'libui'
2
+
3
+ class Trtl
4
+ # A window with a drawing area, built on LibUI. LibUI areas are repainted
5
+ # from scratch whenever the OS asks, so the canvas keeps a list of everything
6
+ # drawn so far and replays it on each repaint.
7
+ class Canvas
8
+ UI = LibUI
9
+ FRAME = 1 / 60.0
10
+ TWO_PI = Math::PI * 2
11
+
12
+ attr_reader :width, :height, :title
13
+
14
+ class << self
15
+ def canvases
16
+ @canvases ||= []
17
+ end
18
+
19
+ def init_ui
20
+ return if @ui_ready
21
+ UI.init
22
+ UI.main_steps
23
+ # Cmd+Q closes the windows rather than stopping the event loop
24
+ UI.on_should_quit do
25
+ canvases.each(&:close)
26
+ 0
27
+ end
28
+ @ui_ready = true
29
+ end
30
+
31
+ # UI calls must not be re-entered from the signal handler used by keep_responsive!
32
+ def busy
33
+ was_busy, @busy = @busy, true
34
+ yield
35
+ ensure
36
+ @busy = was_busy
37
+ end
38
+
39
+ def busy?
40
+ @busy
41
+ end
42
+
43
+ # IRb and Pry spend most of their time blocked waiting for keyboard input,
44
+ # when nothing would service the window (so it couldn't be moved, resized,
45
+ # or even repainted). A background thread signals the process regularly
46
+ # and the trap handler, which Ruby always runs on the main thread as the
47
+ # UI requires, processes any pending window events. Windows has no USR2,
48
+ # so there the window only updates when turtle commands run.
49
+ def keep_responsive!
50
+ return if @keeper || !Signal.list.key?('USR2')
51
+ Signal.trap('USR2') { canvases.each(&:pump) unless busy? }
52
+ @keeper = Thread.new do
53
+ loop do
54
+ Kernel.sleep(FRAME * 2)
55
+ Process.kill('USR2', Process.pid)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def initialize(width, height, title: 'trtl', background: 'black')
62
+ @width, @height, @title = width, height, title
63
+ @background = Colors.rgb(background)
64
+ @items = []
65
+ @turtles = []
66
+ self.class.canvases << self
67
+ end
68
+
69
+ def line(x1, y1, x2, y2, width, color)
70
+ @items << [:line, x1.to_f, y1.to_f, x2.to_f, y2.to_f, width.to_f, Colors.rgb(color)]
71
+ changed
72
+ end
73
+
74
+ def dot(x, y, size, color)
75
+ @items << [:dot, x.to_f, y.to_f, size / 2.0, Colors.rgb(color)]
76
+ changed
77
+ end
78
+
79
+ def add_turtle(turtle)
80
+ @turtles << turtle
81
+ changed
82
+ end
83
+
84
+ # Marks the canvas as needing a repaint the next time events are processed
85
+ def changed
86
+ @dirty = true
87
+ end
88
+
89
+ def title=(title)
90
+ @title = title.to_s
91
+ UI.window_set_title(@window, @title) if open?
92
+ end
93
+
94
+ def open?
95
+ !@window.nil?
96
+ end
97
+
98
+ # Opens the window if necessary, then processes window events for about
99
+ # +ms+ milliseconds, which is what gets the latest drawing on screen
100
+ def update(ms = 0)
101
+ open unless open?
102
+ deadline = clock + ms / 1000.0
103
+ pump
104
+ while (remaining = deadline - clock) > 0
105
+ Kernel.sleep([FRAME, remaining].min)
106
+ pump
107
+ end
108
+ end
109
+
110
+ # Keeps the window responsive until it's closed or Enter is pressed
111
+ def wait
112
+ update
113
+ while open?
114
+ if IO.select([$stdin], nil, nil, FRAME)
115
+ $stdin.gets
116
+ break
117
+ end
118
+ pump
119
+ end
120
+ end
121
+
122
+ def pump
123
+ return unless open?
124
+ self.class.busy do
125
+ if @dirty
126
+ UI.area_queue_redraw_all(@area)
127
+ @dirty = false
128
+ end
129
+ # Each step handles at most one pending event
130
+ 20.times { UI.main_step(0) }
131
+ end
132
+ end
133
+
134
+ def close
135
+ return unless open?
136
+ self.class.busy { UI.control_destroy(@window) }
137
+ closed
138
+ end
139
+
140
+ private
141
+
142
+ def clock
143
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
144
+ end
145
+
146
+ def open
147
+ self.class.init_ui
148
+ self.class.busy do
149
+ @window = UI.new_window(@title, @width, @height, 0)
150
+
151
+ @handler = struct(UI::FFI::AreaHandler)
152
+ # Fiddle closures are kept in instance variables so they aren't garbage collected
153
+ @draw_callback = Fiddle::Closure::BlockCaller.new(Fiddle::TYPE_VOID, [Fiddle::TYPE_VOIDP] * 3) do |_, _, params|
154
+ draw(UI::FFI::AreaDrawParams.new(params))
155
+ rescue Exception => e
156
+ warn "trtl: error while drawing: #{e.class}: #{e.message}"
157
+ end
158
+ @ignore_callback = Fiddle::Closure::BlockCaller.new(Fiddle::TYPE_VOID, [Fiddle::TYPE_VOIDP]) {}
159
+ @key_callback = Fiddle::Closure::BlockCaller.new(Fiddle::TYPE_INT, [Fiddle::TYPE_VOIDP]) { 0 }
160
+ @handler.Draw = @draw_callback
161
+ @handler.MouseEvent = @ignore_callback
162
+ @handler.MouseCrossed = @ignore_callback
163
+ @handler.DragBroken = @ignore_callback
164
+ @handler.KeyEvent = @key_callback
165
+
166
+ @area = UI.new_area(@handler)
167
+ UI.window_set_child(@window, @area)
168
+ # Returning 1 lets LibUI destroy the window (and the area inside it)
169
+ UI.window_on_closing(@window) do
170
+ closed
171
+ 1
172
+ end
173
+ UI.control_show(@window)
174
+ end
175
+ changed
176
+ end
177
+
178
+ def closed
179
+ @window = @area = nil
180
+ end
181
+
182
+ def struct(type)
183
+ type.malloc.tap { |s| s.to_ptr.free = Fiddle::RUBY_FREE }
184
+ end
185
+
186
+ def draw(params)
187
+ ctx = params.Context
188
+ @brush ||= struct(UI::FFI::DrawBrush).tap { |b| b.Type = UI::DrawBrushTypeSolid }
189
+ @stroke ||= struct(UI::FFI::DrawStrokeParams).tap do |s|
190
+ s.Cap = UI::DrawLineCapRound
191
+ s.Join = UI::DrawLineJoinRound
192
+ s.MiterLimit = UI::DrawDefaultMiterLimit
193
+ s.NumDashes = 0
194
+ s.DashPhase = 0
195
+ end
196
+
197
+ fill(ctx, @background) { |path| UI.draw_path_add_rectangle(path, 0, 0, params.AreaWidth, params.AreaHeight) }
198
+
199
+ # Keep the drawing centered if the window is resized
200
+ @matrix ||= struct(UI::FFI::DrawMatrix)
201
+ UI.draw_matrix_set_identity(@matrix)
202
+ UI.draw_matrix_translate(@matrix, (params.AreaWidth - @width) / 2.0, (params.AreaHeight - @height) / 2.0)
203
+ UI.draw_transform(ctx, @matrix)
204
+
205
+ draw_items(ctx)
206
+ @turtles.each { |turtle| draw_turtle(ctx, turtle) }
207
+ end
208
+
209
+ # Consecutive lines sharing a width and color go into a single path, joined
210
+ # up where one starts at the end of the last, which is much faster than
211
+ # stroking each line alone and gives cleaner joins
212
+ def draw_items(ctx)
213
+ path = style = last_x = last_y = nil
214
+
215
+ @items.each do |kind, *args|
216
+ if kind == :line
217
+ x1, y1, x2, y2, width, rgb = args
218
+ if path.nil? || style[0] != width || style[1] != rgb
219
+ stroke(ctx, path, style) if path
220
+ path = UI.draw_new_path(UI::DrawFillModeWinding)
221
+ style = [width, rgb]
222
+ UI.draw_path_new_figure(path, x1, y1)
223
+ elsif x1 != last_x || y1 != last_y
224
+ UI.draw_path_new_figure(path, x1, y1)
225
+ end
226
+ UI.draw_path_line_to(path, x2, y2)
227
+ last_x, last_y = x2, y2
228
+ else
229
+ stroke(ctx, path, style) if path
230
+ path = nil
231
+ x, y, radius, rgb = args
232
+ fill(ctx, rgb) { |p| UI.draw_path_new_figure_with_arc(p, x, y, radius, 0, TWO_PI, 0) }
233
+ end
234
+ end
235
+
236
+ stroke(ctx, path, style) if path
237
+ end
238
+
239
+ # An arrowhead pointing in the turtle's direction of travel
240
+ def draw_turtle(ctx, turtle)
241
+ x, y = turtle.position
242
+ angle = turtle.heading * DEG
243
+ dx, dy = Math.cos(angle), Math.sin(angle)
244
+ fill(ctx, Colors.rgb(turtle.color)) do |path|
245
+ UI.draw_path_new_figure(path, x + dx * 10, y + dy * 10)
246
+ UI.draw_path_line_to(path, x - dx * 2 - dy * 6, y - dy * 2 + dx * 6)
247
+ UI.draw_path_line_to(path, x - dx * 2 + dy * 6, y - dy * 2 - dx * 6)
248
+ UI.draw_path_close_figure(path)
249
+ end
250
+ end
251
+
252
+ def stroke(ctx, path, (width, rgb))
253
+ UI.draw_path_end(path)
254
+ set_brush(rgb)
255
+ @stroke.Thickness = width
256
+ UI.draw_stroke(ctx, path, @brush, @stroke)
257
+ UI.draw_free_path(path)
258
+ end
259
+
260
+ def fill(ctx, rgb)
261
+ path = UI.draw_new_path(UI::DrawFillModeWinding)
262
+ yield path
263
+ UI.draw_path_end(path)
264
+ set_brush(rgb)
265
+ UI.draw_fill(ctx, path, @brush)
266
+ UI.draw_free_path(path)
267
+ end
268
+
269
+ def set_brush((r, g, b))
270
+ @brush.R, @brush.G, @brush.B, @brush.A = r, g, b, 1.0
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,188 @@
1
+ class Trtl
2
+ # Named colors follow the X11 color list, the same names (and values) Tk accepted.
3
+ # Lookup ignores case and spaces, so "DarkOliveGreen" and "dark olive green" both work.
4
+ module Colors
5
+ TABLE = <<~LIST.split.each_slice(2).to_h { |name, hex| [name, hex] }.freeze
6
+ aliceblue f0f8ff antiquewhite faebd7 antiquewhite1 ffefdb antiquewhite2 eedfcc
7
+ antiquewhite3 cdc0b0 antiquewhite4 8b8378 aqua 00ffff aquamarine 7fffd4
8
+ aquamarine1 7fffd4 aquamarine2 76eec6 aquamarine3 66cdaa aquamarine4 458b74
9
+ azure f0ffff azure1 f0ffff azure2 e0eeee azure3 c1cdcd
10
+ azure4 838b8b beige f5f5dc bisque ffe4c4 bisque1 ffe4c4
11
+ bisque2 eed5b7 bisque3 cdb79e bisque4 8b7d6b black 000000
12
+ blanchedalmond ffebcd blue 0000ff blue1 0000ff blue2 0000ee
13
+ blue3 0000cd blue4 00008b blueviolet 8a2be2 brown a52a2a
14
+ brown1 ff4040 brown2 ee3b3b brown3 cd3333 brown4 8b2323
15
+ burlywood deb887 burlywood1 ffd39b burlywood2 eec591 burlywood3 cdaa7d
16
+ burlywood4 8b7355 cadetblue 5f9ea0 cadetblue1 98f5ff cadetblue2 8ee5ee
17
+ cadetblue3 7ac5cd cadetblue4 53868b chartreuse 7fff00 chartreuse1 7fff00
18
+ chartreuse2 76ee00 chartreuse3 66cd00 chartreuse4 458b00 chocolate d2691e
19
+ chocolate1 ff7f24 chocolate2 ee7621 chocolate3 cd661d chocolate4 8b4513
20
+ coral ff7f50 coral1 ff7256 coral2 ee6a50 coral3 cd5b45
21
+ coral4 8b3e2f cornflowerblue 6495ed cornsilk fff8dc cornsilk1 fff8dc
22
+ cornsilk2 eee8cd cornsilk3 cdc8b1 cornsilk4 8b8878 crimson dc143c
23
+ cyan 00ffff cyan1 00ffff cyan2 00eeee cyan3 00cdcd
24
+ cyan4 008b8b darkblue 00008b darkcyan 008b8b darkgoldenrod b8860b
25
+ darkgoldenrod1 ffb90f darkgoldenrod2 eead0e darkgoldenrod3 cd950c darkgoldenrod4 8b6508
26
+ darkgray a9a9a9 darkgreen 006400 darkgrey a9a9a9 darkkhaki bdb76b
27
+ darkmagenta 8b008b darkolivegreen 556b2f darkolivegreen1 caff70 darkolivegreen2 bcee68
28
+ darkolivegreen3 a2cd5a darkolivegreen4 6e8b3d darkorange ff8c00 darkorange1 ff7f00
29
+ darkorange2 ee7600 darkorange3 cd6600 darkorange4 8b4500 darkorchid 9932cc
30
+ darkorchid1 bf3eff darkorchid2 b23aee darkorchid3 9a32cd darkorchid4 68228b
31
+ darkred 8b0000 darksalmon e9967a darkseagreen 8fbc8f darkseagreen1 c1ffc1
32
+ darkseagreen2 b4eeb4 darkseagreen3 9bcd9b darkseagreen4 698b69 darkslateblue 483d8b
33
+ darkslategray 2f4f4f darkslategray1 97ffff darkslategray2 8deeee darkslategray3 79cdcd
34
+ darkslategray4 528b8b darkslategrey 2f4f4f darkturquoise 00ced1 darkviolet 9400d3
35
+ deeppink ff1493 deeppink1 ff1493 deeppink2 ee1289 deeppink3 cd1076
36
+ deeppink4 8b0a50 deepskyblue 00bfff deepskyblue1 00bfff deepskyblue2 00b2ee
37
+ deepskyblue3 009acd deepskyblue4 00688b dimgray 696969 dimgrey 696969
38
+ dodgerblue 1e90ff dodgerblue1 1e90ff dodgerblue2 1c86ee dodgerblue3 1874cd
39
+ dodgerblue4 104e8b firebrick b22222 firebrick1 ff3030 firebrick2 ee2c2c
40
+ firebrick3 cd2626 firebrick4 8b1a1a floralwhite fffaf0 forestgreen 228b22
41
+ fractal 808080 freeze 000000 fuchsia ff00ff gainsboro dcdcdc
42
+ ghostwhite f8f8ff gold ffd700 gold1 ffd700 gold2 eec900
43
+ gold3 cdad00 gold4 8b7500 goldenrod daa520 goldenrod1 ffc125
44
+ goldenrod2 eeb422 goldenrod3 cd9b1d goldenrod4 8b6914 gray bebebe
45
+ gray0 000000 gray1 030303 gray10 1a1a1a gray100 ffffff
46
+ gray11 1c1c1c gray12 1f1f1f gray13 212121 gray14 242424
47
+ gray15 262626 gray16 292929 gray17 2b2b2b gray18 2e2e2e
48
+ gray19 303030 gray2 050505 gray20 333333 gray21 363636
49
+ gray22 383838 gray23 3b3b3b gray24 3d3d3d gray25 404040
50
+ gray26 424242 gray27 454545 gray28 474747 gray29 4a4a4a
51
+ gray3 080808 gray30 4d4d4d gray31 4f4f4f gray32 525252
52
+ gray33 545454 gray34 575757 gray35 595959 gray36 5c5c5c
53
+ gray37 5e5e5e gray38 616161 gray39 636363 gray4 0a0a0a
54
+ gray40 666666 gray41 696969 gray42 6b6b6b gray43 6e6e6e
55
+ gray44 707070 gray45 737373 gray46 757575 gray47 787878
56
+ gray48 7a7a7a gray49 7d7d7d gray5 0d0d0d gray50 7f7f7f
57
+ gray51 828282 gray52 858585 gray53 878787 gray54 8a8a8a
58
+ gray55 8c8c8c gray56 8f8f8f gray57 919191 gray58 949494
59
+ gray59 969696 gray6 0f0f0f gray60 999999 gray61 9c9c9c
60
+ gray62 9e9e9e gray63 a1a1a1 gray64 a3a3a3 gray65 a6a6a6
61
+ gray66 a8a8a8 gray67 ababab gray68 adadad gray69 b0b0b0
62
+ gray7 121212 gray70 b3b3b3 gray71 b5b5b5 gray72 b8b8b8
63
+ gray73 bababa gray74 bdbdbd gray75 bfbfbf gray76 c2c2c2
64
+ gray77 c4c4c4 gray78 c7c7c7 gray79 c9c9c9 gray8 141414
65
+ gray80 cccccc gray81 cfcfcf gray82 d1d1d1 gray83 d4d4d4
66
+ gray84 d6d6d6 gray85 d9d9d9 gray86 dbdbdb gray87 dedede
67
+ gray88 e0e0e0 gray89 e3e3e3 gray9 171717 gray90 e5e5e5
68
+ gray91 e8e8e8 gray92 ebebeb gray93 ededed gray94 f0f0f0
69
+ gray95 f2f2f2 gray96 f5f5f5 gray97 f7f7f7 gray98 fafafa
70
+ gray99 fcfcfc green 00ff00 green1 00ff00 green2 00ee00
71
+ green3 00cd00 green4 008b00 greenyellow adff2f grey bebebe
72
+ grey0 000000 grey1 030303 grey10 1a1a1a grey100 ffffff
73
+ grey11 1c1c1c grey12 1f1f1f grey13 212121 grey14 242424
74
+ grey15 262626 grey16 292929 grey17 2b2b2b grey18 2e2e2e
75
+ grey19 303030 grey2 050505 grey20 333333 grey21 363636
76
+ grey22 383838 grey23 3b3b3b grey24 3d3d3d grey25 404040
77
+ grey26 424242 grey27 454545 grey28 474747 grey29 4a4a4a
78
+ grey3 080808 grey30 4d4d4d grey31 4f4f4f grey32 525252
79
+ grey33 545454 grey34 575757 grey35 595959 grey36 5c5c5c
80
+ grey37 5e5e5e grey38 616161 grey39 636363 grey4 0a0a0a
81
+ grey40 666666 grey41 696969 grey42 6b6b6b grey43 6e6e6e
82
+ grey44 707070 grey45 737373 grey46 757575 grey47 787878
83
+ grey48 7a7a7a grey49 7d7d7d grey5 0d0d0d grey50 7f7f7f
84
+ grey51 828282 grey52 858585 grey53 878787 grey54 8a8a8a
85
+ grey55 8c8c8c grey56 8f8f8f grey57 919191 grey58 949494
86
+ grey59 969696 grey6 0f0f0f grey60 999999 grey61 9c9c9c
87
+ grey62 9e9e9e grey63 a1a1a1 grey64 a3a3a3 grey65 a6a6a6
88
+ grey66 a8a8a8 grey67 ababab grey68 adadad grey69 b0b0b0
89
+ grey7 121212 grey70 b3b3b3 grey71 b5b5b5 grey72 b8b8b8
90
+ grey73 bababa grey74 bdbdbd grey75 bfbfbf grey76 c2c2c2
91
+ grey77 c4c4c4 grey78 c7c7c7 grey79 c9c9c9 grey8 141414
92
+ grey80 cccccc grey81 cfcfcf grey82 d1d1d1 grey83 d4d4d4
93
+ grey84 d6d6d6 grey85 d9d9d9 grey86 dbdbdb grey87 dedede
94
+ grey88 e0e0e0 grey89 e3e3e3 grey9 171717 grey90 e5e5e5
95
+ grey91 e8e8e8 grey92 ebebeb grey93 ededed grey94 f0f0f0
96
+ grey95 f2f2f2 grey96 f5f5f5 grey97 f7f7f7 grey98 fafafa
97
+ grey99 fcfcfc honeydew f0fff0 honeydew1 f0fff0 honeydew2 e0eee0
98
+ honeydew3 c1cdc1 honeydew4 838b83 hotpink ff69b4 hotpink1 ff6eb4
99
+ hotpink2 ee6aa7 hotpink3 cd6090 hotpink4 8b3a62 indianred cd5c5c
100
+ indianred1 ff6a6a indianred2 ee6363 indianred3 cd5555 indianred4 8b3a3a
101
+ indigo 4b0082 ivory fffff0 ivory1 fffff0 ivory2 eeeee0
102
+ ivory3 cdcdc1 ivory4 8b8b83 khaki f0e68c khaki1 fff68f
103
+ khaki2 eee685 khaki3 cdc673 khaki4 8b864e lavender e6e6fa
104
+ lavenderblush fff0f5 lavenderblush1 fff0f5 lavenderblush2 eee0e5 lavenderblush3 cdc1c5
105
+ lavenderblush4 8b8386 lawngreen 7cfc00 lemonchiffon fffacd lemonchiffon1 fffacd
106
+ lemonchiffon2 eee9bf lemonchiffon3 cdc9a5 lemonchiffon4 8b8970 lightblue add8e6
107
+ lightblue1 bfefff lightblue2 b2dfee lightblue3 9ac0cd lightblue4 68838b
108
+ lightcoral f08080 lightcyan e0ffff lightcyan1 e0ffff lightcyan2 d1eeee
109
+ lightcyan3 b4cdcd lightcyan4 7a8b8b lightgoldenrod eedd82 lightgoldenrod1 ffec8b
110
+ lightgoldenrod2 eedc82 lightgoldenrod3 cdbe70 lightgoldenrod4 8b814c lightgoldenrodyellow fafad2
111
+ lightgray d3d3d3 lightgreen 90ee90 lightgrey d3d3d3 lightpink ffb6c1
112
+ lightpink1 ffaeb9 lightpink2 eea2ad lightpink3 cd8c95 lightpink4 8b5f65
113
+ lightsalmon ffa07a lightsalmon1 ffa07a lightsalmon2 ee9572 lightsalmon3 cd8162
114
+ lightsalmon4 8b5742 lightseagreen 20b2aa lightskyblue 87cefa lightskyblue1 b0e2ff
115
+ lightskyblue2 a4d3ee lightskyblue3 8db6cd lightskyblue4 607b8b lightslateblue 8470ff
116
+ lightslategray 778899 lightslategrey 778899 lightsteelblue b0c4de lightsteelblue1 cae1ff
117
+ lightsteelblue2 bcd2ee lightsteelblue3 a2b5cd lightsteelblue4 6e7b8b lightyellow ffffe0
118
+ lightyellow1 ffffe0 lightyellow2 eeeed1 lightyellow3 cdcdb4 lightyellow4 8b8b7a
119
+ lime 00ff00 limegreen 32cd32 linen faf0e6 magenta ff00ff
120
+ magenta1 ff00ff magenta2 ee00ee magenta3 cd00cd magenta4 8b008b
121
+ maroon b03060 maroon1 ff34b3 maroon2 ee30a7 maroon3 cd2990
122
+ maroon4 8b1c62 matte 000000 mediumaquamarine 66cdaa mediumblue 0000cd
123
+ mediumforestgreen 32814b mediumgoldenrod d1c166 mediumorchid ba55d3 mediumorchid1 e066ff
124
+ mediumorchid2 d15fee mediumorchid3 b452cd mediumorchid4 7a378b mediumpurple 9370db
125
+ mediumpurple1 ab82ff mediumpurple2 9f79ee mediumpurple3 8968cd mediumpurple4 5d478b
126
+ mediumseagreen 3cb371 mediumslateblue 7b68ee mediumspringgreen 00fa9a mediumturquoise 48d1cc
127
+ mediumvioletred c71585 midnightblue 191970 mintcream f5fffa mistyrose ffe4e1
128
+ mistyrose1 ffe4e1 mistyrose2 eed5d2 mistyrose3 cdb7b5 mistyrose4 8b7d7b
129
+ moccasin ffe4b5 navajowhite ffdead navajowhite1 ffdead navajowhite2 eecfa1
130
+ navajowhite3 cdb38b navajowhite4 8b795e navy 000080 navyblue 000080
131
+ none 000000 oldlace fdf5e6 olive 808000 olivedrab 6b8e23
132
+ olivedrab1 c0ff3e olivedrab2 b3ee3a olivedrab3 9acd32 olivedrab4 698b22
133
+ opaque 000000 orange ffa500 orange1 ffa500 orange2 ee9a00
134
+ orange3 cd8500 orange4 8b5a00 orangered ff4500 orangered1 ff4500
135
+ orangered2 ee4000 orangered3 cd3700 orangered4 8b2500 orchid da70d6
136
+ orchid1 ff83fa orchid2 ee7ae9 orchid3 cd69c9 orchid4 8b4789
137
+ palegoldenrod eee8aa palegreen 98fb98 palegreen1 9aff9a palegreen2 90ee90
138
+ palegreen3 7ccd7c palegreen4 548b54 paleturquoise afeeee paleturquoise1 bbffff
139
+ paleturquoise2 aeeeee paleturquoise3 96cdcd paleturquoise4 668b8b palevioletred db7093
140
+ palevioletred1 ff82ab palevioletred2 ee799f palevioletred3 cd6889 palevioletred4 8b475d
141
+ papayawhip ffefd5 peachpuff ffdab9 peachpuff1 ffdab9 peachpuff2 eecbad
142
+ peachpuff3 cdaf95 peachpuff4 8b7765 peru cd853f pink ffc0cb
143
+ pink1 ffb5c5 pink2 eea9b8 pink3 cd919e pink4 8b636c
144
+ plum dda0dd plum1 ffbbff plum2 eeaeee plum3 cd96cd
145
+ plum4 8b668b powderblue b0e0e6 purple a020f0 purple1 9b30ff
146
+ purple2 912cee purple3 7d26cd purple4 551a8b red ff0000
147
+ red1 ff0000 red2 ee0000 red3 cd0000 red4 8b0000
148
+ rosybrown bc8f8f rosybrown1 ffc1c1 rosybrown2 eeb4b4 rosybrown3 cd9b9b
149
+ rosybrown4 8b6969 royalblue 4169e1 royalblue1 4876ff royalblue2 436eee
150
+ royalblue3 3a5fcd royalblue4 27408b saddlebrown 8b4513 salmon fa8072
151
+ salmon1 ff8c69 salmon2 ee8262 salmon3 cd7054 salmon4 8b4c39
152
+ sandybrown f4a460 seagreen 2e8b57 seagreen1 54ff9f seagreen2 4eee94
153
+ seagreen3 43cd80 seagreen4 2e8b57 seashell fff5ee seashell1 fff5ee
154
+ seashell2 eee5de seashell3 cdc5bf seashell4 8b8682 sienna a0522d
155
+ sienna1 ff8247 sienna2 ee7942 sienna3 cd6839 sienna4 8b4726
156
+ silver c0c0c0 skyblue 87ceeb skyblue1 87ceff skyblue2 7ec0ee
157
+ skyblue3 6ca6cd skyblue4 4a708b slateblue 6a5acd slateblue1 836fff
158
+ slateblue2 7a67ee slateblue3 6959cd slateblue4 473c8b slategray 708090
159
+ slategray1 c6e2ff slategray2 b9d3ee slategray3 9fb6cd slategray4 6c7b8b
160
+ slategrey 708090 snow fffafa snow1 fffafa snow2 eee9e9
161
+ snow3 cdc9c9 snow4 8b8989 springgreen 00ff7f springgreen1 00ff7f
162
+ springgreen2 00ee76 springgreen3 00cd66 springgreen4 008b45 steelblue 4682b4
163
+ steelblue1 63b8ff steelblue2 5cacee steelblue3 4f94cd steelblue4 36648b
164
+ tan d2b48c tan1 ffa54f tan2 ee9a49 tan3 cd853f
165
+ tan4 8b5a2b teal 008080 thistle d8bfd8 thistle1 ffe1ff
166
+ thistle2 eed2ee thistle3 cdb5cd thistle4 8b7b8b tomato ff6347
167
+ tomato1 ff6347 tomato2 ee5c42 tomato3 cd4f39 tomato4 8b3626
168
+ transparent 000000 turquoise 40e0d0 turquoise1 00f5ff turquoise2 00e5ee
169
+ turquoise3 00c5cd turquoise4 00868b violet ee82ee violetred d02090
170
+ violetred1 ff3e96 violetred2 ee3a8c violetred3 cd3278 violetred4 8b2252
171
+ wheat f5deb3 wheat1 ffe7ba wheat2 eed8ae wheat3 cdba96
172
+ wheat4 8b7e66 white ffffff whitesmoke f5f5f5 yellow ffff00
173
+ yellow1 ffff00 yellow2 eeee00 yellow3 cdcd00 yellow4 8b8b00
174
+ yellowgreen 9acd32
175
+ LIST
176
+
177
+ # Returns [r, g, b] with components between 0.0 and 1.0
178
+ def self.rgb(color)
179
+ case color
180
+ when /\A#(\h{3})\z/ then $1.chars.map { |c| (c * 2).to_i(16) / 255.0 }
181
+ when /\A#(\h{6})\z/ then $1.scan(/../).map { |c| c.to_i(16) / 255.0 }
182
+ else
183
+ hex = TABLE[color.to_s.downcase.delete(" ")] or raise ArgumentError, "unknown color: #{color.inspect}"
184
+ hex.scan(/../).map { |c| c.to_i(16) / 255.0 }
185
+ end
186
+ end
187
+ end
188
+ end
data/lib/trtl.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'tk'
1
+ require_relative 'trtl/colors'
2
+ require_relative 'trtl/canvas'
2
3
 
3
4
  class Trtl
4
5
  CANVAS_WIDTH = 800
@@ -18,21 +19,17 @@ class Trtl
18
19
  @canvas = options[:canvas] || self.class.canvas
19
20
  @width = options[:width] || 1
20
21
  @drawing = true
22
+ @canvas.add_turtle(self)
21
23
  home
22
24
  draw
23
25
  end
24
26
 
25
27
  def self.canvas
26
- return @canvas if @canvas
27
-
28
- root = TkRoot.new(:title => 'trtl', :minsize => [CANVAS_WIDTH, CANVAS_HEIGHT])
29
- @canvas = TkCanvas.new(root, :bg => 'black', :highlightthickness => 0, :width => CANVAS_WIDTH, :height => CANVAS_HEIGHT)
30
- @canvas.pack(:fill => 'both', :expand => 1)
31
- @canvas
28
+ @canvas ||= Canvas.new(CANVAS_WIDTH, CANVAS_HEIGHT)
32
29
  end
33
30
 
34
31
  def title(title)
35
- TkRoot.new(:title => title)
32
+ canvas.title = title
36
33
  end
37
34
 
38
35
  def pen_up
@@ -47,7 +44,9 @@ class Trtl
47
44
  @drawing
48
45
  end
49
46
 
50
- def color(color)
47
+ # With no argument, returns the current color
48
+ def color(color = nil)
49
+ return @color if color.nil?
51
50
  @color = color.to_s
52
51
  end
53
52
 
@@ -68,7 +67,7 @@ class Trtl
68
67
  end
69
68
 
70
69
  def move(new_x, new_y)
71
- TkcLine.new(canvas, @x, @y, new_x, new_y, :width => @width, :fill => @color) if @drawing
70
+ canvas.line(@x, @y, new_x, new_y, @width, @color) if @drawing
72
71
  @x, @y = new_x, new_y
73
72
  draw
74
73
  end
@@ -85,7 +84,7 @@ class Trtl
85
84
 
86
85
  def dot(size = nil)
87
86
  size ||= [@width + 4, @width * 2].max
88
- TkcOval.new(canvas, @x - size / 2, @y - size / 2, @x + size / 2, @y + size / 2, :fill => @color, :outline => @color)
87
+ canvas.dot(@x, @y, size, @color)
89
88
  end
90
89
 
91
90
  # TODO / TOFIX: This is horribly wrong with the fewer steps due to circumference varying ;-)
@@ -108,16 +107,18 @@ class Trtl
108
107
  draw
109
108
  end
110
109
 
110
+ # Shows the window and keeps it responsive for +time+ milliseconds
111
111
  def sleep(time = 100000)
112
- Tk.sleep(time)
112
+ canvas.update(time)
113
113
  end
114
114
 
115
115
  def ensure_drawn
116
116
  sleep 30
117
117
  end
118
118
 
119
+ # Keeps the window open until it's closed or Enter is pressed
119
120
  def wait
120
- ensure_drawn and gets
121
+ canvas.wait
121
122
  end
122
123
 
123
124
  alias :run :instance_eval
@@ -152,10 +153,8 @@ class Trtl
152
153
  end
153
154
 
154
155
  def draw
155
- canvas.delete(@turtle_line) if @turtle_line
156
- @turtle_line = TkcLine.new(canvas, @x, @y, @x + dx * 5 , @y + dy * 5, :arrow => 'last', :width => 10, :fill => @color)
157
- # Can probably just use ensure_drawn actually..
158
- TkTimer.new(60, 1) { Tk.update }.start.wait if @interactive
156
+ canvas.changed
157
+ canvas.update(60) if @interactive
159
158
  true
160
159
  end
161
160
  end
@@ -170,4 +169,7 @@ module InteractiveTurtle
170
169
  end
171
170
  end
172
171
 
173
- include InteractiveTurtle if %w{irb pry}.include?($0)
172
+ if %w{irb pry}.include?(File.basename($0))
173
+ include InteractiveTurtle
174
+ Trtl::Canvas.keep_responsive!
175
+ end
metadata CHANGED
@@ -1,29 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trtl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Cooper
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2017-02-14 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: tk
13
+ name: libui
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '0.1'
18
+ version: '0.2'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '0.1'
25
+ version: '0.2'
27
26
  description: A Logo / turtle.py style turtle graphics system for Ruby
28
27
  email:
29
28
  - git@peterc.org
@@ -31,11 +30,8 @@ executables: []
31
30
  extensions: []
32
31
  extra_rdoc_files: []
33
32
  files:
34
- - ".gitignore"
35
- - Gemfile
36
33
  - LICENSE.md
37
34
  - README.md
38
- - Rakefile
39
35
  - examples/example1.rb
40
36
  - examples/example2.rb
41
37
  - examples/example3.rb
@@ -43,13 +39,12 @@ files:
43
39
  - examples/example5.rb
44
40
  - examples/example6.rb
45
41
  - lib/trtl.rb
46
- - test/helper.rb
47
- - test/test_trtl.rb
48
- - trtl.gemspec
42
+ - lib/trtl/canvas.rb
43
+ - lib/trtl/colors.rb
49
44
  homepage: https://github.com/peterc/trtl
50
- licenses: []
45
+ licenses:
46
+ - MIT
51
47
  metadata: {}
52
- post_install_message:
53
48
  rdoc_options: []
54
49
  require_paths:
55
50
  - lib
@@ -57,18 +52,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
52
  requirements:
58
53
  - - ">="
59
54
  - !ruby/object:Gem::Version
60
- version: '0'
55
+ version: '2.6'
61
56
  required_rubygems_version: !ruby/object:Gem::Requirement
62
57
  requirements:
63
58
  - - ">="
64
59
  - !ruby/object:Gem::Version
65
60
  version: '0'
66
61
  requirements: []
67
- rubyforge_project: trtl
68
- rubygems_version: 2.6.8
69
- signing_key:
62
+ rubygems_version: 4.0.6
70
63
  specification_version: 4
71
- summary: Ruby turtle graphics (ideal for use from IRb)
72
- test_files:
73
- - test/helper.rb
74
- - test/test_trtl.rb
64
+ summary: Ruby turtle graphics (ideal for use from IRB)
65
+ test_files: []
data/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- *.gem
2
- .bundle
3
- Gemfile.lock
4
- pkg/*
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in trtl.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,5 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
4
- Rake::TestTask.new
5
- task :default => :test
data/test/helper.rb DELETED
@@ -1,3 +0,0 @@
1
- require 'minitest/spec'
2
- require 'minitest/autorun'
3
- require_relative '../lib/trtl'
data/test/test_trtl.rb DELETED
@@ -1,64 +0,0 @@
1
- require_relative 'helper'
2
-
3
- # Note: Only very simple high level tests here. Just stretching
4
- # some of the public API. One day I may care to stub out Tk ;-)
5
-
6
- describe Trtl do
7
- before do
8
- @trtl = Trtl.new
9
- end
10
-
11
- it "gets a default canvas when none is provided" do
12
- @trtl.canvas.must_be_instance_of TkCanvas
13
- end
14
-
15
- it "has pen down by default" do
16
- @trtl.is_drawing?.must_equal true
17
- end
18
-
19
- it "can have pen moved up" do
20
- @trtl.pen_up
21
- @trtl.is_drawing?.must_equal false
22
- end
23
-
24
- it "has default X and Y set" do
25
- @trtl.x.must_equal Trtl::CANVAS_WIDTH / 2
26
- @trtl.y.must_equal Trtl::CANVAS_HEIGHT / 2
27
- end
28
-
29
- it "can report its position" do
30
- @trtl.position.must_equal [@trtl.x, @trtl.y]
31
- end
32
-
33
- it "can rotate and change heading" do
34
- @trtl.left 20
35
- @trtl.heading.must_equal 340
36
- @trtl.right 40
37
- @trtl.heading.must_equal 20
38
- end
39
-
40
- it "can move in straight lines" do
41
- start_x, start_y = @trtl.position
42
- @trtl.heading = 0
43
- @trtl.forward 50
44
- @trtl.x.must_equal start_x + 50
45
- @trtl.heading = 90
46
- @trtl.forward 50
47
- @trtl.y.must_equal start_y + 50
48
- @trtl.back 50
49
- @trtl.y.must_equal start_y
50
- end
51
-
52
- it "can move at angles" do
53
- start_x, start_y = @trtl.position
54
- @trtl.heading = 45
55
- @trtl.forward 100
56
- @trtl.x.must_be_close_to start_x + 100 / Math.sqrt(2)
57
- @trtl.y.must_be_close_to start_y + 100 / Math.sqrt(2)
58
- end
59
-
60
- it "can be moved absolutely" do
61
- @trtl.move(310, 201)
62
- @trtl.position.must_equal [310, 201]
63
- end
64
- end
data/trtl.gemspec DELETED
@@ -1,21 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
-
4
- Gem::Specification.new do |s|
5
- s.name = "trtl"
6
- s.version = "0.0.2"
7
- s.authors = ["Peter Cooper"]
8
- s.email = ["git@peterc.org"]
9
- s.homepage = "https://github.com/peterc/trtl"
10
- s.summary = %q{Ruby turtle graphics (ideal for use from IRb)}
11
- s.description = %q{A Logo / turtle.py style turtle graphics system for Ruby}
12
-
13
- s.rubyforge_project = "trtl"
14
-
15
- s.files = `git ls-files`.split("\n")
16
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
- s.require_paths = ["lib"]
19
-
20
- s.add_runtime_dependency 'tk', '~> 0.1'
21
- end