[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[QUIZ] Circle Drawing (#166

Matthew Moss

6/13/2008 1:48:00 PM

[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/rub....
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).



--
Matthew Moss <matthew.moss@gmail.com>

18 Answers

Clifford Heath

6/13/2008 3:22:00 PM

0

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

ThoML

6/14/2008 6:06:00 AM

0

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.

Robert Dober

6/14/2008 11:09:00 AM

0

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

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

Eric Mahurin

6/14/2008 1:44:00 PM

0

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

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

Matthew Moss

6/14/2008 9:34:00 PM

0

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

Andrea Fazzi

6/15/2008 2:38:00 PM

0

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/rub....
> 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....
http://pastie.... (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/B...)
#
# 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__

Martin Boese

6/15/2008 2:50:00 PM

0

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



ThoML

6/15/2008 3:10:00 PM

0

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

Bill Kelly

6/15/2008 8:30:00 PM

0

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



Andrea Fazzi

6/15/2008 8:32:00 PM

0

Oops.. I just realized that I made an error in canvas size calculation..

Here's the fixed code:

http://pastie....
http://pastie.... (specs)

Andrea