standard_draw_tk 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.
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