standard_draw_tk 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # Standard Draw TK
2
+
3
+ I was going through the [Algorithms 4th edition](https://algs4.cs.princeton.edu/home/) and wanted to write the algorithms in ruby instead of the default java. The book uses the [Princeton Standard Libraries](https://introcs.cs.princeton.edu/java/stdlib/) to help clarify the code. I've added a simple [jruby wrapper gem](https://rubygems.org/gems/princeton_standard_libraries) that lets you call the original java code in Jruby.
4
+
5
+ I really would like to call this code in standard ruby. So I'm attempting to keep the original interface while recreating the logic using the [ruby TK gem](https://github.com/ruby/tk).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'standard_draw_tk'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install standard_draw_tk
22
+ Ruby doesn't contain TK by default anymore. And installing the TK gem requires a bit of manual installation depending on the system. I followed this [blog post](https://saveriomiroddi.github.io/Installing-ruby-tk-bindings-gem-on-ubuntu/) to get it working on linux.
23
+
24
+ ## Usage
25
+
26
+ The drawing of geometric shapes has been implemented though there are probably some small tweaks that need to be added, e.g. the line ends of arcs.
27
+
28
+ ## Development
29
+
30
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
31
+
32
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
33
+
34
+ ## Things to implement:
35
+
36
+ ### Drawing geometric shapes
37
+ * [x] line
38
+ * [x] point
39
+ * [x] pixel
40
+ * [x] rectangle
41
+ * [x] filled_rectangle
42
+ * [x] polygon
43
+ * [x] filled_polygon
44
+ * [x] circle
45
+ * [x] filled_circle
46
+ * [x] ellipse
47
+ * [x] filled_ellipse
48
+ * [x] arc
49
+ * [x] square
50
+ * [x] filled_square
51
+
52
+ ### Drawing images
53
+
54
+ ### Drawing text
55
+ * [ ] get/set font
56
+ * [ ] text centered at x,y
57
+ * [ ] text centered at x,y with degree rotation
58
+ * [ ] text left aligned x,y
59
+ * [ ] text right aligned x,y
60
+
61
+ ### Saving drawing to a file
62
+ * [ ] save to jpg
63
+
64
+ ### Mouse interactions
65
+
66
+ ### Keyboard interactions
67
+
68
+ ## Contributing
69
+
70
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gregors/standard_draw_tk.
71
+
72
+ ## License
73
+
74
+ The gem is available as open source under the terms of the [GNU General Public License, version 3 (GPLv3)](http://www.gnu.org/copyleft/gpl.html).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "standard_draw_tk"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/examples/arcs.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'standard_draw_tk'
2
+
3
+ StdDraw.pen_color = 'red'
4
+ StdDraw.pen_radius = 0.02
5
+ StdDraw.arc(0.8, 0.2, 0.1, 200, 45)
6
+ StdDraw.pause
@@ -0,0 +1,8 @@
1
+ require 'standard_draw_tk'
2
+
3
+ StdDraw.filled_circle(0.5, 0.5, 0.10)
4
+
5
+ 1.upto(50) do |i|
6
+ StdDraw.circle(0.5, 0.5, 1.0/i)
7
+ end
8
+ StdDraw.pause
@@ -0,0 +1,5 @@
1
+ require 'standard_draw_tk'
2
+
3
+ StdDraw.ellipse(0.5, 0.5, 0.20, 0.10)
4
+ StdDraw.filled_ellipse(0.20, 0.20, 0.05, 0.20)
5
+ StdDraw.pause
@@ -0,0 +1,13 @@
1
+ require 'standard_draw_tk'
2
+
3
+ n = 100
4
+ StdDraw.set_xscale(0,n)
5
+ StdDraw.set_yscale(0,n*n)
6
+ StdDraw.pen_radius = 0.01
7
+
8
+ 0.upto(n) do |i|
9
+ StdDraw.point(i, i)
10
+ StdDraw.point(i, i*i)
11
+ StdDraw.point(i, i*Math.log(i))
12
+ end
13
+ StdDraw.pause
@@ -0,0 +1,17 @@
1
+ require 'standard_draw_tk'
2
+
3
+ n = 50
4
+ a = []
5
+
6
+ 0.upto(n) do |i|
7
+ a[i] = rand
8
+ end
9
+
10
+ 0.upto(n) do |i|
11
+ x = 1.0 * i / n
12
+ y = a[i] / 2.0
13
+ rw = 0.5 / n
14
+ rh = a[i] / 2.0
15
+ StdDraw.filled_rectangle(x, y, rw, rh)
16
+ end
17
+ StdDraw.pause
@@ -0,0 +1,19 @@
1
+ require 'standard_draw_tk'
2
+
3
+ n = 50
4
+ a = []
5
+
6
+ 0.upto(50) do |i|
7
+ a[i] = rand
8
+ end
9
+
10
+ a.sort!
11
+
12
+ 0.upto(50) do |i|
13
+ x = 1.0 * i / n
14
+ y = a[i] / 2.0
15
+ rw = 0.5 / n
16
+ rh = a[i] / 2.0
17
+ StdDraw.filled_rectangle(x, y, rw, rh)
18
+ end
19
+ StdDraw.pause
@@ -0,0 +1,8 @@
1
+ require 'standard_draw_tk'
2
+
3
+ StdDraw.pen_radius = 0.05
4
+ StdDraw.pen_color = 'blue'
5
+ StdDraw.point(0.5, 0.5)
6
+ StdDraw.pen_color = 'red'
7
+ StdDraw.point(0.8, 0.8)
8
+ StdDraw.pause
@@ -0,0 +1,10 @@
1
+ require 'standard_draw_tk'
2
+
3
+ x = [ 0.1, 0.2, 0.3, 0.2 ]
4
+ y = [ 0.2, 0.3, 0.2, 0.1 ]
5
+ StdDraw.filled_polygon(x, y)
6
+
7
+ x = [ 0.3, 0.4, 0.5, 0.4 ]
8
+ y = [ 0.4, 0.5, 0.4, 0.3 ]
9
+ StdDraw.polygon(x, y)
10
+ StdDraw.pause
@@ -0,0 +1,5 @@
1
+ require 'standard_draw_tk'
2
+
3
+ StdDraw.filled_rectangle(0.5, 0.5, 0.05, 0.08)
4
+ StdDraw.rectangle(0.10, 0.5, 0.08, 0.05)
5
+ StdDraw.pause
@@ -0,0 +1,5 @@
1
+ require 'standard_draw_tk'
2
+
3
+ StdDraw.filled_square(0.5, 0.5, 0.05)
4
+ StdDraw.square(0.10, 0.5, 0.05)
5
+ StdDraw.pause
@@ -0,0 +1,8 @@
1
+ require 'standard_draw_tk'
2
+
3
+ t = Math.sqrt(3.0) / 2.0
4
+ StdDraw.line(0.0, 0.0, 1.0, 0.0)
5
+ StdDraw.line(1.0, 0.0, 0.5, t)
6
+ StdDraw.line(0.5, t, 0.0, 0.0)
7
+ StdDraw.point(0.5, t/3.0)
8
+ StdDraw.pause
@@ -0,0 +1,61 @@
1
+ class StandardDrawTk::Coordinates
2
+ BORDER = 0.00
3
+ DEFAULT_MIN = 0.0
4
+ DEFAULT_MAX = 1.0
5
+ DEFAULT_SIZE = 512
6
+
7
+ attr_reader :width, :height
8
+
9
+ def initialize
10
+ @height = @width = DEFAULT_SIZE
11
+ @xmin = @ymin = DEFAULT_MIN
12
+ @xmax = @ymax = DEFAULT_MAX
13
+ end
14
+
15
+ def init
16
+ set_xscale(DEFAULT_XMIN, DEFAULT_XMAX)
17
+ set_yscale(DEFAULT_YMIN, DEFAULT_YMAX)
18
+ end
19
+
20
+ def scale_x(x)
21
+ @width * (x - @xmin) / (@xmax - @xmin)
22
+ end
23
+
24
+ def scale_y(y)
25
+ @height * (@ymax - y) / (@ymax - @ymin)
26
+ end
27
+
28
+ def factor_x(w)
29
+ w * @width / (@xmax - @xmin).abs
30
+ end
31
+
32
+ def factor_y(h)
33
+ h * @height / (@ymax - @ymin).abs
34
+ end
35
+
36
+ def user_x(x)
37
+ @xmin + x * (@xmax - @xmin) / @width
38
+ end
39
+
40
+ def user_y(y)
41
+ @ymax - y * (@ymax - @ymin) / @height
42
+ end
43
+
44
+ def box(x, y, r)
45
+ [x - r, y - r, x + r, y + r]
46
+ end
47
+
48
+ def set_xscale(min, max)
49
+ size = max - min
50
+ throw 'the min and max are the same' if size == 0.0
51
+ @xmin = min - BORDER * size
52
+ @xmax = max + BORDER * size
53
+ end
54
+
55
+ def set_yscale(min, max)
56
+ size = max - min
57
+ throw 'the min and max are the same' if size == 0.0
58
+ @ymin = min - BORDER * size
59
+ @ymax = max + BORDER * size
60
+ end
61
+ end
@@ -0,0 +1,196 @@
1
+ require 'standard_draw_tk/version'
2
+ require 'tk'
3
+ require_relative './coordinates'
4
+
5
+ class StdDraw
6
+ @@pen_radius = DEFAULT_PEN_RADIUS = 0.002
7
+
8
+ @@coords = StandardDrawTk::Coordinates.new
9
+
10
+ @@color = 'black'
11
+ @@bg_color = 'white'
12
+
13
+ @@root = TkRoot.new
14
+ @@root.title = 'Standard Draw'
15
+ @@canvas = TkCanvas.new(@@root) do
16
+ height @@coords.height
17
+ width @@coords.width
18
+ bg @@bg_color
19
+ end
20
+ @@canvas.pack
21
+ @@thread = Thread.new do
22
+ Tk.mainloop
23
+ end
24
+
25
+ def self.coords
26
+ @@coords
27
+ end
28
+
29
+ def self.set_xscale(min,max)
30
+ coords.set_xscale(min,max)
31
+ end
32
+
33
+ def self.set_yscale(min,max)
34
+ coords.set_yscale(min,max)
35
+ end
36
+
37
+ def self.pen_radius=(radius)
38
+ throw 'pen radius must be nonnegative' if radius <= 0
39
+ @@pen_radius = radius
40
+ end
41
+
42
+ def self.pen_color=(color)
43
+ @@color = color
44
+ end
45
+
46
+ def self.color
47
+ @@color
48
+ end
49
+
50
+ def self.line(x0, y0, x1, y1)
51
+ x0 = coords.scale_x(x0)
52
+ y0 = coords.scale_y(y0)
53
+ x1 = coords.scale_x(x1)
54
+ y1 = coords.scale_y(y1)
55
+ TkcLine.new(@@canvas, x0, y0, x1, y1, fill: color)
56
+ end
57
+
58
+ def self.arc(x, y, radius, angle1, angle2)
59
+ throw 'arc radius must be nonnegative' if radius < 0
60
+ while angle2 < angle1
61
+ angle2 += 360
62
+ end
63
+ xs = coords.scale_x(x)
64
+ ys = coords.scale_y(y)
65
+ ws = coords.factor_x(2*radius)
66
+ hs = coords.factor_y(2*radius)
67
+ if ws <= 1 && hs <= 1
68
+ pixel(x, y)
69
+ else
70
+ x1 = xs - ws/2
71
+ y1 = ys - hs/2
72
+ x2 = x1 + ws
73
+ y2 = y1 + hs
74
+ scaled_pen_radius = @@pen_radius * StandardDrawTk::Coordinates::DEFAULT_SIZE
75
+ TkcArc.new(@@canvas, x1, y1, x2, y2, style: :arc, start: angle1, extent: angle2 - angle1, fill: color, width: scaled_pen_radius, outline: color)
76
+ end
77
+ end
78
+
79
+ def self.circle(x, y, radius)
80
+ draw_circle(x, y, radius, outline: color)
81
+ end
82
+
83
+ def self.ellipse(x, y, w, h)
84
+ draw_ellipse(x, y, w, h, outline: color)
85
+ end
86
+
87
+ def self.filled_ellipse(x, y, w, h)
88
+ draw_ellipse(x, y, w, h, fill: color)
89
+ end
90
+
91
+ def self.filled_circle(x, y, radius)
92
+ draw_circle(x, y, radius, fill: color)
93
+ end
94
+
95
+ def self.rectangle(x, y, half_width, half_height)
96
+ draw_rectangle(x, y, half_width, half_height, outline: color)
97
+ end
98
+
99
+ def self.filled_rectangle(x, y, half_width, half_height)
100
+ draw_rectangle(x, y, half_width, half_height, fill: color)
101
+ end
102
+
103
+ def self.square(x, y, half_length)
104
+ draw_rectangle(x, y, half_length, half_length, outline: color)
105
+ end
106
+
107
+ def self.filled_square(x, y, half_length)
108
+ draw_rectangle(x, y, half_length, half_length, fill: color)
109
+ end
110
+
111
+ def self.point(x, y)
112
+ draw_circle(x, y, @@pen_radius, outline: color, fill: color)
113
+ end
114
+
115
+ def self.pixel(x, y)
116
+ puts 'pixel'
117
+ x1 = coords.scale_x(x).round
118
+ y1 = coords.scale_y(y).round
119
+ TkcRectangle.new(@@canvas, [x1, y1, x1 + 1, y1 + 1], outline: color, fill: color)
120
+ end
121
+
122
+ def self.pause
123
+ @@thread.join
124
+ end
125
+
126
+ def self.polygon(x, y)
127
+ draw_polygon(x, y, outline: color)
128
+ end
129
+
130
+ def self.filled_polygon(x, y)
131
+ draw_polygon(x, y, fill: color)
132
+ end
133
+
134
+ private
135
+
136
+ def self.draw_polygon(x, y, fill: '', outline: nil)
137
+ throw 'x-coordinate array is null' if x.nil?
138
+ throw 'y-coordinate array is null' if y.nil?
139
+ throw 'arrays must be of the same length' if x.size != y.size
140
+ return if x.empty?
141
+ x = x.map{|i| coords.scale_x(i)}
142
+ y = y.map{|i| coords.scale_y(i)}
143
+ points = x.zip(y).flatten
144
+ scaled_pen_radius = @@pen_radius * StandardDrawTk::Coordinates::DEFAULT_SIZE
145
+ TkcPolygon.new(@@canvas, *points, width: scaled_pen_radius, fill: fill, outline: outline)
146
+ end
147
+
148
+ def self.draw_ellipse(x, y, w, h, outline: nil, fill: nil)
149
+ throw 'semi_major_axis must be nonnegative' if w < 0
150
+ throw 'semi_minor_axis must be nonnegative' if h < 0
151
+
152
+ xs = coords.scale_x(x)
153
+ ys = coords.scale_y(y)
154
+ ws = coords.factor_x(2*w)
155
+ hs = coords.factor_y(2*h)
156
+ if ws <= 1 && hs <= 1
157
+ pixel(x, y)
158
+ else
159
+ x1 = xs - ws/2
160
+ y1 = ys - hs/2
161
+ b = [x1, y1, x1 + ws, y1 + hs]
162
+ TkcOval.new(@@canvas, b, outline: outline, fill: fill)
163
+ end
164
+ end
165
+
166
+ def self.draw_circle(x, y, radius, outline: nil, fill: nil)
167
+ throw 'radius must be nonnegative' if radius < 0
168
+ xs = coords.scale_x(x)
169
+ ys = coords.scale_y(y)
170
+ scaled_pen_radius = radius * StandardDrawTk::Coordinates::DEFAULT_SIZE
171
+ if scaled_pen_radius <= 1
172
+ pixel(x, y)
173
+ else
174
+ b = coords.box(xs, ys, scaled_pen_radius/2)
175
+ TkcOval.new(@@canvas, b, outline: outline, fill: fill)
176
+ end
177
+ end
178
+
179
+ def self.draw_rectangle(x, y, half_width, half_height, outline: nil, fill: nil)
180
+ throw 'half width must be nonnegative' if half_width < 0
181
+ throw 'half height must be nonnegative' if half_height < 0
182
+
183
+ ws = coords.factor_x(2*half_width)
184
+ hs = coords.factor_y(2*half_height)
185
+
186
+ if (ws <= 1 && hs <= 1)
187
+ pixel(x, y)
188
+ else
189
+ xs = coords.scale_x(x)
190
+ ys = coords.scale_y(y)
191
+ x = xs - ws/2
192
+ y = ys - hs/2
193
+ TkcRectangle.new(@@canvas, x, y, x + (ws-1), y + (hs-1), outline: outline, fill: fill)
194
+ end
195
+ end
196
+ end