Matthew Moss wrote:
However, _for extra credit_ ...
What about bonus points for using only *integer* arithmetic and no transcendentals?
I wrote C code for that which is hiding somewhere
Matthew Moss wrote:
However, _for extra credit_ ...
What about bonus points for using only *integer* arithmetic and no transcendentals?
I wrote C code for that which is hiding somewhere
Hi,
For example:
ruby circle.rb 7
Should produce a circle of radius 7
I'm not sure if this is intentional but the circle is 15 characters
high. Of course, the line has to be counted in too.
Nevertheless ...
Regards,
Thomas.
Matthew Moss ha scritto:
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
The three rules of Ruby Quiz 2:
1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have passed from the time on this message.2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at<http://splatbang.com/rubyquiz/>\.
3. Enjoy!Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-## Circle Drawing (#166)
This week we're going to keep it simple... very simple.
Given a radius, draw an ASCII circle.
For example:
ruby circle.rb 7
Should produce a circle of radius 7:
#####
## ##
# #
# #
# #
# #
# #
## ##
#####Note that most fonts do not have a square aspect ratio, which is why the
above output may look like an oval, despite my calculations for a circle. It
is acceptable if your code produces similar output.However, _for extra credit_ you may support an additional argument that
specifies the aspect ratio (height divided by width).ruby circle.rb 7 1.4
This should draw a circle of radius 7 with aspect ratio of 1.4. If done
correctly, your output will actually look like a circle (assuming 1.4 is an
accurate measure of the actual aspect ratio).
Here my solution. It is available on pastie:
http://pastie.org/215379
http://pastie.org/215380 (specs)
and it is also attached below:
#
# Solution to Ruby Quiz #166 - Circle Drawing
#
# Usage:
#
# Circle.new(5).to_s
#
# or:
#
# Circle.new(5, 2).to_s
#
# or:
#
# Circle.new(5, 2, 'x').to_s
#
# Objects of class Circle draw circles on stdout. The aspect ratio
# correction is actually made drawing an ellipse with semimajor axis
# (a) equals to the given circle radius and semiminor axis (b) equals
# to a / aspect_ratio.
#
# Circle class is responsible to
#
# * initialize a Circle object with the given radius, aspect ratio
# and drawing char
# # * initialize a canvas
#
# * draw the circle on its internal canvas
#
# * convert the canvas to string for output on stdout
#
class Circle
# cx, cy are the coordinates of the circle's center.
attr_reader :cx, :cy
attr_reader :radius
# w, h are width and height of the canvas
attr_reader :w, :h
# canvas is a linear array that is initially filled with spaces
attr_reader :canvas
#
# Initialize a Circle object passing a value for radius, aspect
# ratio and drawing character.
#
def initialize(radius, aspect_ratio = 1.0, char = '#')
@radius = radius.to_i
@aspect_ratio = aspect_ratio.to_f
@char = char
fail "Error: radius must be > 0" if @radius <= 0
fail "Error: aspect ratio must be > 0" if @aspect_ratio <= 0
# a is the semimajor axis of the ellipse and is equal to the given
# radius
@a = @radius
# b is the semiminor axis of the ellipse and is calculated from a
# and the given aspect ratio
@b = (@a / @aspect_ratio).ceil
# calculate the size of the canvas
@w, @h = (@a + 1) * 2, (@b + 1) * 2
# center coordinates correspond to the size of semiaxis.
@cx, @cy = @a, @b
# initialize the canvas with spaces
@canvas = Array.new(@w * @h, ' ')
# draw ellipse on canvas
draw_ellipse(@a, @b)
end
#
# Print circle on stdout.
#
def to_s
result = ""
(0..@h - 1).each do |line|
result << @canvas[line * @w..line * @w + @w - 1].to_s << "\n"
end
result
end
private
#
# Draw the given character on canvas to the given coordinates.
#
def point(x, y)
@canvas[y * @w + x] = @char
end
#
# Translates and mirrors point (x, y) in the quadrants taking
# advantage of the simmetries in the ellipse. Thus, for a given
# point (x, y) the method plot three other points in the remaining
# quadrants.
#
def plot_four_points(x, y)
point(@cx + x, @cy + y)
point(@cx - x, @cy + y)
point(@cx + x, @cy - y)
point(@cx - x, @cy - y)
end
#
# Draw an ellipse on canvas. This method implements a Bresenham
# based algorithm by John Kennedy
# (http://homepage.smc.edu/kennedy_john/BELIPSE.PDF\)
#
# The method calculates two set of points in the first quadrant. The
# first set starts on the positive x axis and wraps in a
# counterclockwise direction until the tangent line slope is equal
# to -1. The second set starts on the positive y axis and wraps in
# a clockwise direction until the tangent line slope is equal to -1.
#
def draw_ellipse(a, b)
a_square = 2 * a**2
b_square = 2 * b**2
draw_first_set(a, b, a_square, b_square)
draw_second_set(a, b, a_square, b_square)
end
#
# The method increments y and decides when to decrement x testing
# the sign of a function. In this case, the decision function is
# (2*ellipse_error+x_change) and its value is calculated
# iteratively.
#
def draw_first_set(a, b, a_square, b_square)
x, y = a, 0
x_change, y_change = b**2 * (1 - 2 * a), a**2
stopping_x, stopping_y = b_square * a, 0
ellipse_error = 0
while(stopping_x >= stopping_y) do
plot_four_points(x, y)
y += 1
stopping_y += a_square
ellipse_error += y_change
y_change += a_square
if (2 * ellipse_error + x_change) > 0
x -= 1
stopping_x -= b_square
ellipse_error += x_change
x_change += b_square
end
end
end
#
# The method increments x and decides when to decrement y testing
# the sign of a function. In this case, the decision function is
# (2*ellipse_error+y_change) and its value is calculated
# iteratively.
#
def draw_second_set(a, b, a_square, b_square)
x, y = 0, b
x_change, y_change = b**2, a**2 * (1 - 2 * b)
stopping_x, stopping_y = 0, a_square * b
ellipse_error = 0
while stopping_x <= stopping_y do
plot_four_points(x, y)
x += 1
stopping_x += b_square
ellipse_error += x_change
x_change += b_square
if (2 * ellipse_error + y_change) > 0
y -= 1
stopping_y -= a_square
ellipse_error += y_change
y_change += a_square
end
end
end
end
# Usage:
#
# ruby circle.rb 7 #=> print out a circle of radius 7
#
# ruby circle.rb 7 1.8 #=> print out a circle of radius 7 and aspect ratio 1.8
#
# ruby circle.rb 7 1.8 x #=> print out a circle of radius 7 and aspect ratio 1.8
# using the ascii char 'x'
#
print Circle.new(ARGV[0], ARGV[1] || 1.0, ARGV[2] || '#').to_s if $0 == __FILE__
Here's my solution, it creates a buffer to draw into, once done it puts it on
the screen:
----- circle.rb -----
class Circle
def initialize(rad, asp)
@rad, @asp = rad, asp # radius, horizontal aspect ratio
@height = rad*2+1 # hight/width of the pictures
@width = (@height*asp).round
@buf = Array.new(@height, ' ').map { |e| Array.new(@width, ' ') }
end
def draw
(0..Math::PI*2).step(1/(@rad*@asp*2)) do |deg|
@buf[((@height/2) + (Math::sin(deg)*@rad)).round] \
[((@width/2) + (Math::cos(deg)*@rad*@asp)).round] = '#'
end
@buf.map { |l| l.join + "\n"}.join
end
end
puts Circle.new((ARGV[0] || 7).to_i, (ARGV[1] || 1).to_f).draw
----- end circle.rb -----
On Friday 13 June 2008 14:47:38 Matthew Moss wrote:
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
The three rules of Ruby Quiz 2:
1. Please do not post any solutions or spoiler discussion for this
quiz until 48 hours have passed from the time on this message.2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Quiz 2. Until then,
please visit the temporary website at<http://splatbang.com/rubyquiz/>\.
3. Enjoy!Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone on Ruby Talk follow the discussion. Please reply to
the original quiz message, if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-## Circle Drawing (#166)
This week we're going to keep it simple... very simple.
Given a radius, draw an ASCII circle.
For example:
ruby circle.rb 7
Should produce a circle of radius 7:
#####
## ##
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
## ##
#####Note that most fonts do not have a square aspect ratio, which is why the
above output may look like an oval, despite my calculations for a circle.
It is acceptable if your code produces similar output.However, _for extra credit_ you may support an additional argument that
specifies the aspect ratio (height divided by width).ruby circle.rb 7 1.4
This should draw a circle of radius 7 with aspect ratio of 1.4. If done
correctly, your output will actually look like a circle (assuming 1.4 is an
accurate measure of the actual aspect ratio).
My solution makes circles with r=7 14 characters wide. This may be
incorrect. It's rather simple though.
Regards,
Thomas.
def draw_circle(r, ratio=1.0)
lines = []
a = 0.0
t2 = ratio / 2.0
(t2 - 0.1).step(r, ratio) do |h|
b = Math.sqrt(2.0 * h * r - h ** 2).round
u = r - b
v = [1.0, b - a].max
w = (r - u - v) * 2.0
lines << [(m = ' ' * u), (l = '#' * v), ' ' * w, l, m].join
a = b
end
out = lines.join("\n")
puts out
puts out.reverse
end
if __FILE__ == $0
draw_circle(*ARGV.map {|e| e.to_f})
end
My solution follows....
Regards,
Bill
# Ruby Quiz #166
#
# This draws a circle of the specified radius, modified
# by an optional aspect ratio and thickness factor.
#
# implementation notes:
# - for the fun of it, i forbade use of sqrt() and trancendentals
# - the circle is not drawn into a buffer before being printed
# - some values are empirically derived (thickness factor, in parciular)
#
# bugs:
# - the thickness factor causes bloat in small circles
ARGV.length >= 1 or abort("usage: #$0 radius [aspect] [thickness]")
radius = ARGV.shift.to_f
radius > 0 or abort("please provide radius of circle")
aspect = ARGV.shift.to_f
aspect > 0 or aspect = 1.0
thick = ARGV.shift.to_f
thick > 0 or thick = 1.0
hradius = (radius * aspect).ceil + (thick/2.0).round
vradius = radius.ceil + (thick/2.0).round
def get_radius_ch(rsq, dsq, tfactor)
(rsq - dsq).abs <= tfactor ? "*" : " "
end
tfactor = (thick * 4.0) + 2.5
rsq = radius**2
(-vradius).upto(vradius) do |y|
(-hradius).upto(hradius) do |x|
print(get_radius_ch(rsq, (x * (1.0/aspect))**2 + y**2, tfactor))
end
puts
end
# example: radius 7.0, aspect 1.0, thickness 1.0
#
# $ ruby 166_circle.rb 7 1 1
# # *****
# ** **
# * *
# * *
# * *
# * *
# * *
# ** **
# *****
#
# example: radius 10.0, aspect 2.0, thickness 5.0
#
# $ ruby 166_circle.rb 10 2 5
# # # *****
# *******************
# *************************
# ******** ********
# ******* *******
# ****** ******
# ***** *****
# ***** *****
# ***** *****
# **** ****
# ***** *****
# **** ****
# ***** *****
# ***** *****
# ***** *****
# ****** ******
# ******* *******
# ******** ********
# *************************
# *******************
# *****
#
# circle.rb
def draw(mtrx)
mtrx.each do |file|
puts file.to_s
end
nil
end
def new_mtrx(rdx,ratio)
size = rdx * (ratio.ceil) * 2
mtrx = Array.new(size).map!{ Array.new(size) }
(0..size-1).each do |file|
(0..size-1).each do |col|
mtrx[file][col] = " "
end
end
mtrx
end
def load_mtrx(mtrx,rdx,ratio=1)
(1..360).each do |grado|
x = ((Math.sin grado) * (rdx * ratio)) + (rdx * ratio)
y = ((Math.cos grado) * rdx) + rdx
mtrx[x.to_i][y.to_i] = "#"
end
nil
end
def main
rdx = ARGV[0].to_i
ratio = ARGV[1].to_f
mtrx = new_mtrx(rdx,ratio)
load_mtrx(mtrx,rdx,ratio)
draw(mtrx)
end
main
# end file
--
«Quien nunca ha cometido un error nunca ha probado algo nuevo.»
Here's mine before I delved into trying to add the aspect ratio feature.
class Circle
def initialize(radius)
@radius = radius.to_i
end
def draw
(0..@radius*2).each do |x|
(0..@radius*2).each do |y|
print distance_from_center(x,y).round == @radius ? '#' : '.'
end
puts
end
end
def distance_from_center(x,y)
a = calc_side(x)
b = calc_side(y)
return Math.sqrt(a**2 + b**2)
end
def calc_side(z)
z < @radius ? (@radius - z) : (z - @radius)
end
end
Circle.new(ARGV.shift).draw
Don't reinvent the wheel (no pun intended
require 'cairo'
radius = ARGV[0].to_f
aspect = (ARGV[1]||1.0).to_f
linewidth = (ARGV[2]||1.0).to_f
width = (aspect * radius * 2 + 1 + linewidth + 0.5).to_i
height = (radius * 2 + 1 + linewidth + 0.5).to_i
Cairo::ImageSurface.new(width, height) do |surface|
cr = Cairo::Context.new(surface)
cr.set_antialias(Cairo::ANTIALIAS_NONE)
cr.set_source_rgb(0,0,0)
cr.paint
cr.save
cr.scale(aspect, 1.0)
cr.arc(width / 2.0 / aspect + 0.5, height / 2.0 + 0.5, radius, 0, 2
* Math::PI)
cr.restore
cr.set_source_rgb(1,1,1)
cr.set_line_width(linewidth)
cr.stroke
height.times { |row|
puts cr.target.data[row * width * 4, width * 4].unpack("N*").map
{ |x| (x >> 8) > 0 ? '#' : ' ' }.join
}
end
Matthew Moss ha scritto:
## Circle Drawing (#166)
This week we're going to keep it simple... very simple.
Given a radius, draw an ASCII circle.
For example:
ruby circle.rb 7
Should produce a circle of radius 7:
#####
## ##
# #
# #
# #
# #
# #
## ##
#####Note that most fonts do not have a square aspect ratio, which is why the
above output may look like an oval, despite my calculations for a circle. It
is acceptable if your code produces similar output.However, _for extra credit_ you may support an additional argument that
specifies the aspect ratio (height divided by width).ruby circle.rb 7 1.4
This should draw a circle of radius 7 with aspect ratio of 1.4. If done
correctly, your output will actually look like a circle (assuming 1.4 is an
accurate measure of the actual aspect ratio).
A bit of ruby art
http://pastie.org/216459
http://pastie.org/216462
I don't know if my benchmark is done properly.. I do it quickly and just for fun Please give me feedback..
Benchmark details:
num_of_runs = 100
radius = 7
ratio = 2.0
This benchmark has been executed on a MacBook 2,16Ghz with 2Gb of RAM running Linux Ubuntu 7.10.
Andrea
Just make test pass.... nothing clever!
Jean Lazarou
---- code -----------------
def symbolify value
res = value.to_s
def res.delete other_str
""
end
res
end
1000.times do |i|
s = symbolify(i)
raise "Not a string!" unless s.is_a? String
raise "Invalid chars!" unless s.delete("?*()-").empty?
x = eval(s)
raise "Decode failed!" unless i == x
end
2*7 = 15, simple LOL.
I finally decided against it because of simplicity, but I believe that
it is more beautyful to add a "middle" line, especially for small r's.
Robert
On Sat, Jun 14, 2008 at 8:09 AM, ThoML <micathom@gmail.com> wrote:
Hi,
For example:
ruby circle.rb 7
Should produce a circle of radius 7
I'm not sure if this is intentional but the circle is 15 characters
high. Of course, the line has to be counted in too.
Nevertheless ...Regards,
Thomas.
--
http://ruby-smalltalk.blogspot.com/
---
As simple as possible, but not simpler.
Albert Einstein
Depends on where you are measuring the radius:
outside: 7.5
inside: 6.5 (white space is 13 characters high)
center: 7 (center of bottom to center of top is 14)
On 6/14/08, ThoML <micathom@gmail.com> wrote:
Hi,
> For example:
>
> ruby circle.rb 7
>
> Should produce a circle of radius 7I'm not sure if this is intentional but the circle is 15 characters
high. Of course, the line has to be counted in too.
Nevertheless ...Regards,
Thomas.
Oops.. I just realized that I made an error in canvas size calculation..
Here's the fixed code:
http://pastie.org/215379
http://pastie.org/215380 (specs)
Andrea
Here is my solution.
Aaron
class Circle
attr_accessor :radius, :aspect_ratio
def initialize(radius, aspect_ratio)
self.radius = radius
self.aspect_ratio = aspect_ratio == 0 ? 1 : aspect_ratio
end
def to_s
(0..y_diameter).inject('') do |rows, y|
rows + (0..x_diameter).inject('') do |row, x|
row + (on_circle?(x, y) ? '#' : ' ')
end + "\n"
end
end
private
def y_diameter
radius * 2
end
def x_diameter
y_diameter * aspect_ratio
end
def on_circle?(x,y)
Math.sqrt((x/aspect_ratio-radius)**2 + (y-radius)**2).round ==
radius
end
end
print Circle.new(ARGV[0].to_i, ARGV[1].to_f).to_s
Here's mine
# initial values
r = ARGV[0].to_i; k = 360.0/(Math::PI*2)
# if r < 1 write error and exit
puts "USAGE: circle.rb radius [with radius >= 1]"; exit if r < 1
# get some points and map them on a matrix
m = Array.new(r).collect{|e|Array.new(r).fill(" ")}
j = [0.0,90.0,*(1...p=((vr=(r-1))*2)).to_a.collect{|e|
e*(90.0/p.to_f) }].each{|a|
vl=[Math.sin(a/k),Math.cos(a/k)].collect{|c| (c*vr).round }
m[vl[0]][vl[1]]="#" }
s = m.collect{|a| w=a.join(""); w.reverse+w }.join("\n")
# print the result
puts s.reverse+"\n"+s
On Mon, Jun 16, 2008 at 3:54 PM, Jon Garvin <jgarvin.lists@gmail.com> wrote:
Here's mine before I delved into trying to add the aspect ratio feature.
class Circle
def initialize(radius)
@radius = radius.to_i
enddef draw
(0..@radius*2).each do |x|
(0..@radius*2).each do |y|
print distance_from_center(x,y).round == @radius ? '#' : '.'
end
puts
end
enddef distance_from_center(x,y)
a = calc_side(x)
b = calc_side(y)
return Math.sqrt(a**2 + b**2)
enddef calc_side(z)
z < @radius ? (@radius - z) : (z - @radius)
end
endCircle.new(ARGV.shift).draw
--
--
Go outside! The graphics are amazing!
Just make test pass....
According to my watch, you're about 24 hours too early.
> > For example:
> > ruby circle.rb 7
> > Should produce a circle of radius 7
> I'm not sure if this is intentional but the circle is 15 characters
> high. Of course, the line has to be counted in too.
> Nevertheless ...> Regards,
> Thomas.Depends on where you are measuring the radius:
outside: 7.5
inside: 6.5 (white space is 13 characters high)
center: 7 (center of bottom to center of top is 14)
It was quite intentional that my circle of radius 7 took up 15 rows of
characters. This is a common issue when dealing with computer
graphics: how do you measure distance on a field of discrete elements?
In computer graphics, this is often not a big deal when drawing 3d
objects, especially if you have blurring, other post-processing, or
anti-aliasing going on. It is much more important when you are trying
to render a HUD or UI elements, for example, that you want pixel-
perfect to the artwork provided. Many graphics cards have a setting
you can enable/disable to offset coordinates by half a pixel...
Putting it into the correct mode and setting your texturing unit to
point sampling mode (as opposed to tri-/bi-linear sampling) will give
you pixel-perfect results.
So, in the case as I presented it, I was measuring from the center of
the character cell, which is 15 rows high *if measured from the top
edge of the top row to the bottom edge of the bottom row*. But as Eric
pointed out, it's only 14 if you measure from character cell center's.