 # Circle Drawing (#166)

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

The three rules of Ruby Q. 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 Q. 2 by submitting ideas as often as you can! (A
permanent, new website is in the works for Ruby Q. 2. Until then,
please visit the temporary website at

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

On 6/14/08, ThoML [email protected] wrote:

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)

On Sat, Jun 14, 2008 at 8:09 AM, ThoML [email protected] wrote:

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

As simple as possible, but not simpler.
Albert Einstein

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 M. ha scritto:

This week we’re going to keep it simple… very simple.
## ##
# #
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:

and it is also attached below:

class Circle

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

# 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

# a clockwise direction until the tangent line slope is equal to -1.

def draw_ellipse(a, b)
a_square = 2 * a2
b_square = 2 * b
2

``````draw_first_set(a, b, a_square, b_square)
draw_second_set(a, b, a_square, b_square)
``````

end

# 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

# 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

ratio 1.8

ratio 1.8

# using the ascii char ‘x’

print Circle.new(ARGV, ARGV || 1.0, ARGV || ‘#’).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
@height = rad2+1 # hight/width of the pictures
@width = (@height
asp).round
@buf = Array.new(@height, ’ ').map { |e| Array.new(@width, ’ ') }
end
def draw
@buf[((@height/2) + (Math::sin(deg)
end
@buf.map { |l| l.join + “\n”}.join
end
end
puts Circle.new((ARGV || 7).to_i, (ARGV || 1).to_f).draw
----- end circle.rb -----

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

parciular)

# - 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 + y2, tfactor))
end
puts
end

# 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

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.to_i
ratio = ARGV.to_f
mtrx = new_mtrx(rdx,ratio)
draw(mtrx)
end

main

# end file

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

Here’s the fixed code:

Andrea

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.to_i, ARGV.to_f).to_s

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

class Circle
end

def draw
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(a2 + b2)
end

def calc_side(z)
end
end

Circle.new(ARGV.shift).draw

http://www.5valleys.com/

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

Don’t reinvent the wheel (no pun intended require ‘cairo’
aspect = (ARGV||1.0).to_f
linewidth = (ARGV||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 M. ha scritto:

Should produce a circle of radius 7:
# #

A bit of ruby art 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

Just make test pass… nothing clever!

Jean L.

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

Here’s mine

# initial values

r = ARGV.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®.collect{|e|Array.new®.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][vl]=”#" }
s = m.collect{|a| w=a.join(""); w.reverse+w }.join("\n")

# print the result

puts s.reverse+"\n"+s

Just make test pass…

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

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.