# [QUIZ] Circle Drawing (#166)

Discussion in 'Ruby' started by Matthew Moss, Jun 13, 2008.

1. ### Matthew MossGuest

[Note: parts of this message were removed to make it a legal post.]

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

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

--
Matthew Moss <>

Matthew Moss, Jun 13, 2008

2. ### Clifford HeathGuest

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

Clifford Heath, Jun 13, 2008

3. ### ThoMLGuest

Re: Circle Drawing (#166)

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.

ThoML, Jun 14, 2008
4. ### Robert DoberGuest

Re: Circle Drawing (#166)

On Sat, Jun 14, 2008 at 8:09 AM, ThoML <> 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.
>
>

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

--
http://ruby-smalltalk.blogspot.com/

---
As simple as possible, but not simpler.
Albert Einstein

Robert Dober, Jun 14, 2008
5. ### Eric MahurinGuest

Re: Circle Drawing (#166)

[Note: parts of this message were removed to make it a legal post.]

On 6/14/08, ThoML <> 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.
>
>

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)

Eric Mahurin, Jun 14, 2008
6. ### Matthew MossGuest

Re: Circle Drawing (#166)

> > > For example:
>
> > > =A0 =A0 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.

Matthew Moss, Jun 14, 2008
7. ### Andrea FazziGuest

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

# w, h are width and height of the canvas

# canvas is a linear array that is initially filled with spaces

#
# Initialize a Circle object passing a value for radius, aspect
# ratio and drawing character.
#
def initialize(radius, aspect_ratio = 1.0, char = '#')

@aspect_ratio = aspect_ratio.to_f
@char = char

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

# 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
#
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__

Andrea Fazzi, Jun 15, 2008
8. ### Martin BoeseGuest

Re: [QUIZ][SOLUTION] Circle Drawing (#166)

Here's my solution, it creates a buffer to draw into, once done it puts it on
the screen:

----- circle.rb -----
class Circle
@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
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
> 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).

Martin Boese, Jun 15, 2008
9. ### ThoMLGuest

Re: Circle Drawing (#166)

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

ThoML, Jun 15, 2008
10. ### Bill KellyGuest

Re: [QUIZ][SOLUTION] Circle Drawing (#166)

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]")

aspect = ARGV.shift.to_f
aspect > 0 or aspect = 1.0

thick = ARGV.shift.to_f
thick > 0 or thick = 1.0

(rsq - dsq).abs <= tfactor ? "*" : " "
end

tfactor = (thick * 4.0) + 2.5
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
#
#
# *****
# *******************
# *************************
# ******** ********
# ******* *******
# ****** ******
# ***** *****
# ***** *****
# ***** *****
# **** ****
# ***** *****
# ***** *****
# ***** *****
# **** ****
# ***** *****
# ***** *****
# ***** *****
# ****** ******
# ******* *******
# ******** ********
# *************************
# *******************
# *****
#

Bill Kelly, Jun 15, 2008
11. ### Andrea FazziGuest

Andrea Fazzi, Jun 15, 2008
12. ### Juan MatíasGuest

# circle.rb

def draw(mtrx)
mtrx.each do |file|
puts file.to_s
end
nil
end

def new_mtrx(rdx,ratio)
size =3D rdx * (ratio.ceil) * 2
mtrx =3D Array.new(size).map!{ Array.new(size) }
(0..size-1).each do |file|
(0..size-1).each do |col|
mtrx[file][col] =3D " "
end
end
mtrx
end

x =3D ((Math.sin grado) * (rdx * ratio)) + (rdx * ratio)
y =3D ((Math.cos grado) * rdx) + rdx
mtrx[x.to_i][y.to_i] =3D "#"
end
nil
end

def main
rdx =3D ARGV[0].to_i
ratio =3D ARGV[1].to_f
mtrx =3D new_mtrx(rdx,ratio)
draw(mtrx)
end

main

# end file

--=20
=ABQuien nunca ha cometido un error nunca ha probado algo nuevo.=BB

Juan Matías, Jun 16, 2008
13. ### Aaron BaldwinGuest

Re: Circle Drawing (#166)

Here is my solution.

Aaron

class Circle

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
end

def x_diameter
y_diameter * aspect_ratio
end

def on_circle?(x,y)
end
end

print Circle.new(ARGV[0].to_i, ARGV[1].to_f).to_s

Aaron Baldwin, Jun 16, 2008
14. ### Jon GarvinGuest

Here's mine before I delved into trying to add the aspect ratio feature.

class Circle
end

def draw
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)
end
end

Circle.new(ARGV.shift).draw

--

http://www.5valleys.com/

http://www.workingwithrails.com/person/8078

Jon Garvin, Jun 16, 2008
15. ### Sandro PaganottiGuest

[Note: parts of this message were removed to make it a legal post.]

Here's mine

# initial values
r = ARGV[0].to_i; k = 360.0/(Math:I*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 <> wrote:

> Here's mine before I delved into trying to add the aspect ratio feature.
>
>
> class Circle
> end
>
> def draw
> 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)
> end
> end
>
> Circle.new(ARGV.shift).draw
>
> --
>
> http://www.5valleys.com/
>
> http://www.workingwithrails.com/person/8078
>
>
>

--
Go outside! The graphics are amazing!

Sandro Paganotti, Jun 17, 2008
16. ### Lars ChristensenGuest

Re: Circle Drawing (#166)

Don't reinvent the wheel (no pun intended

require 'cairo'
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:I)
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

Lars Christensen, Jun 17, 2008
17. ### Andrea FazziGuest

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
ratio = 2.0

This benchmark has been executed on a MacBook 2,16Ghz with 2Gb of RAM
running Linux Ubuntu 7.10.

Andrea

Andrea Fazzi, Jun 17, 2008
18. ### Guest

Re: [QUIZ] Symbolify (#169)

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

, Jul 12, 2008
19. ### ThoMLGuest

Re: Symbolify (#169)

> Just make test pass....

According to my watch, you're about 24 hours too early.

ThoML, Jul 12, 2008