standard_draw_tk 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +675 -0
- data/README.md +74 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/arcs.rb +6 -0
- data/examples/circles.rb +8 -0
- data/examples/ellipse.rb +5 -0
- data/examples/example1.rb +13 -0
- data/examples/example2.rb +17 -0
- data/examples/example3.rb +19 -0
- data/examples/points.rb +8 -0
- data/examples/polygon.rb +10 -0
- data/examples/rectangle.rb +5 -0
- data/examples/square.rb +5 -0
- data/examples/triangle.rb +8 -0
- data/lib/coordinates.rb +61 -0
- data/lib/standard_draw_tk.rb +196 -0
- data/lib/standard_draw_tk/version.rb +3 -0
- data/standard_draw_tk.gemspec +28 -0
- metadata +123 -0
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
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
data/examples/arcs.rb
ADDED
data/examples/circles.rb
ADDED
data/examples/ellipse.rb
ADDED
@@ -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
|
data/examples/points.rb
ADDED
data/examples/polygon.rb
ADDED
data/examples/square.rb
ADDED
data/lib/coordinates.rb
ADDED
@@ -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
|