# The Golden Fibonacci Ratio (#69)

**URL:** <https://www.ruby-forum.com/t/the-golden-fibonacci-ratio-69/76998>\
**Category:** Ruby\
**Created:** [March 5, 2006, 7:59pm UTC](https://www.ruby-forum.com/t/the-golden-fibonacci-ratio-69/76998 "2006-03-05T19:59:44Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [March 5, 2006, 7:59pm UTC](https://www.ruby-forum.com/t/the-golden-fibonacci-ratio-69/76998/1 "2006-03-05T19:59:44Z")

</div>

# Author: Shane E.

# 

# Quiz 69: The Golden Fibonacci Ratio.

# 

# I decided for this quiz to try and use the curses library to make

# screen placement easier. I think it turned out well, though I am sure

# there are more clever ways of achieving the same results. Press the

# ‘enter’ key to advance through each successive iteration of the

# algorithm. Five is the optimal number of times to iterate for

standard

# consoles.

# 

# usage: ruby quiz69.rb [optional: number of times to add sqr]

require ‘curses’

class Square

```
attr_reader :height, :width
attr_writer :height, :width

def initialize( screen, length = 1, width = 1 )
    @screen, @height, @width = screen, length, width
end

def max_side
    @height >= @width ? @height : @width
end

def add_to_screen
    @screen.setpos( 0, 0 )
    ( 0 .. @width + 1 ).each { @screen.addstr( '#' ) }
    ( 1 .. @height ).each do |height|
        @screen.setpos( height, 0 )
        @screen.addstr( '#' )
        @screen.setpos( height, @width + 1 )
        @screen.addstr( '#' )
    end
    @screen.setpos( @height + 1, 0 )
    ( 0 .. @width + 1 ).each { @screen.addstr( '#' ) }
    @screen.getch
end

```

end

add\_to = :right  
sqr = Square.new( Curses::init\_screen )  
sqr.add\_to\_screen

times = ARGV[0] || 5  
( 1 … times.to\_i ).each do |x|

```
if add_to == :right
    sqr.width += sqr.max_side + 1
else
    sqr.height += sqr.max_side + 1
end

add_to = add_to == :right ? :bottom : :right
sqr.add_to_screen

```

end

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [March 5, 2006, 8:30pm UTC](https://www.ruby-forum.com/t/the-golden-fibonacci-ratio-69/76998/2 "2006-03-05T20:30:28Z")

</div>

I’ve never done OpenGL before. So after looking a some examples here’s  
what I came up with. I did this pretty quick so there are many things  
that ought to be fixed (like the mess of global variables), but it gets  
the job done. Like the curses submission, pressing enter repeatedly  
will add the next iteration. I don’t have automatic resizing yet so  
after you press enter, to get it to look right you have to resize the  
window. Maybe I’ll think about that a bit later (suggestions are  
welcome on how resizing should be done). Anyways thanks for the quiz!

-----Jay A.

require ‘opengl’  
require ‘glut’

class Array  
def rotate!  
push shift  
end  
def rotate  
self.dup.rotate!  
end  
end

key\_func = lambda do |key,x,y|  
case key  
when ?Q, ?q  
exit 0  
when ?\r, ?\n  
side = $width\>$height ? $width : $height  
sq\_x, sq\_y = $x, $y  
case $add\_to.first  
when :right  
sq\_x = $x + $width  
$width += side  
when :left  
sq\_x = $x - side  
$x = sq\_x  
$width += side  
when :top  
sq\_y = $y + $height  
$height += side  
when :bottom  
sq\_y = $y - side  
$y = sq\_y  
$height += side  
end  
$squares \<\< {  
:side =\> side,  
:x =\> sq\_x,  
:y =\> sq\_y,  
:color =\> [rand, rand, rand]  
}

```
    $add_to.rotate!
end
GLUT.PostRedisplay

```

end

reshape\_func = lambda do |w,h|  
$screen\_width = w  
$screen\_height = h  
range = ($width\>$height ? $width : $height)  
border = range \* 0.05  
left, right = $x-border, $x+range+border  
bottom, top = $y-border, $y+range+border  
aspect = w.to\_f/h.to\_f  
if w \<= h then  
bottom /= aspect  
top /= aspect  
else  
left \*= aspect  
right \*= aspect  
end  
GL.Viewport(0, 0, w, h)  
GL.MatrixMode GL::PROJECTION  
GL.LoadIdentity  
GLU::Ortho2D(left, right, bottom, top)  
GL.MatrixMode GL::MODELVIEW  
end

display\_func = lambda do  
GL.Clear GL::COLOR\_BUFFER\_BIT | GL::DEPTH\_BUFFER\_BIT

```
$squares.each do |s|
    GL.Translate(s[:x], s[:y], 0.0)
    side = s[:side]
    GL.Begin(GL::QUADS)
    GL.Color(*s[:color])
    GL.Vertex(0.0, 0.0)
    GL.Vertex(side, 0.0)
    GL.Vertex(side, side)
    GL.Vertex(0.0, side)
    GL.End
    GL.Translate(-s[:x], -s[:y], 0.0)
end

GL.Flush

```

end

GLUT.Init  
GLUT.InitDisplayMode GLUT::SINGLE | GLUT::RGB | GLUT::DEPTH  
GLUT.CreateWindow “Fibonacci”  
GL.FrontFace GL::CW  
GL.Enable(GL::DEPTH\_TEST)

$screen\_width = GLUT.Get(GLUT::WINDOW\_WIDTH)  
$screen\_height = GLUT.Get(GLUT::WINDOW\_HEIGHT)  
$squares = []  
$squares \<\< {  
:side =\> 1.0,  
:x =\> 0.0,  
:y =\> 0.0,  
:color =\> [rand, rand, rand]  
}  
$width = 1.0  
$height = 1.0  
$x = 0.0  
$y = 0.0  
$add\_to = [:right, :bottom, :left, :top]

GLUT.KeyboardFunc key\_func  
GLUT.ReshapeFunc reshape\_func  
GLUT.DisplayFunc display\_func

GLUT.MainLoop

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [March 5, 2006, 9:00pm UTC](https://www.ruby-forum.com/t/the-golden-fibonacci-ratio-69/76998/3 "2006-03-05T21:00:48Z")

</div>

Here’s my attempt at the quiz. Instead of producing a text output I  
tried myself at SVG. The first version I came up with didn’t use any  
special SVG commands and takes care of finding out the positions for  
each square by itself. After seeing the Postscript solution I looked in  
the SVG specs for something similar… the result is the second version,  
which seems a bit more unclear for human beings but makes the algorithm  
much easier (no more “case @side”)

So here they are…

First version with explicit placement of squares:

require ‘rubygems’  
require ‘svg/svg’  
require ‘memoize’

module GoldenRectangle  
COLORS = [‘red’, ‘green’, ‘blue’, ‘yellow’]  
SEED\_POS = {:x =\> 0, :y =\> 0}

```
    class PreSeedSquare
            def height
                    0
            end

            def x
                    SEED_POS[:x]
            end

            def y
                    SEED_POS[:y]
            end

            def color
                    COLORS[0]
            end

            def parent
                    @parent
            end
    end

    class SeedSquare < PreSeedSquare
            def initialize
                    @parent = PreSeedSquare.new
            end

            def fib(n)
                    a, b = 1, 1
                    n.times { a, b = b, a + b }
                    a
            end

            def size
                    fib(level)
            end

            def level
                    0
            end

            def width
                    size
            end

            alias_method :height, :width

            def side
                    :top
            end

            def color
                    COLORS[1]
            end

            def as_spiral
                    ["M#{x},#{y + height}", "A#{width},#{height} 0

```

0,1 #{x + width},#{y}"]  
end

```
            def as_squares
                    [svg_square]
            end

            def svg_square
                    col = self.color
                    SVG::Rect.new(x, y, width, height) { self.style

```

= SVG::Style.new(:fill =\> col, :opacity =\> 0.5) }  
end  
end

```
    class Square < SeedSquare
            include Memoize

            attr_reader :parent

            def initialize(parent)
                    @parent = parent
                    @grandparent = @parent.parent

                    # without memoization this implementation

```

wouldn’t be feasible  
[:parent, :color, :size, :side, :x, :y].each  
{|f| memoize f }  
end

```
            class << self
                    alias_method :attach_to, :new
            end

            def as_squares
                    @parent.as_squares << svg_square
            end

            def as_spiral
                    @parent.as_spiral << "A#{width},#{height} 0 0,1

```

" + spiral\_point.join(",")  
end

```
            def color
                    if @grandparent.parent.nil?
                            COLORS[2]
                    elsif
                            @grandparent.parent.parent.nil?
                            COLORS[3]
                    else
                            (COLORS -

```

[@parent.parent.parent.parent.color, @parent.parent.parent.color,  
@parent.color]).first  
end  
end

```
            def level
                    @parent.level + 1
            end

            def side
                    case @parent.side
                    when :right
                            :bottom
                    when :bottom
                            :left
                    when :left
                            :top
                    when :top
                            :right
                    end
            end

            def x
                    case side
                    when :right
                            @parent.x + @parent.width
                    when :bottom
                            @grandparent.x
                    when :left
                            @parent.x - width
                    when :top
                            @parent.x
                    end
            end

            def y
                    case side
                    when :left
                            @grandparent.y
                    when :bottom
                            @parent.y + @parent.height
                    when :top
                            @parent.y - height
                    when :right
                            @parent.y
                    end
            end

            def spiral_point
                    case side
                    when :right
                            [x + width, y + height]
                    when :left
                            [x, y]
                    when :top
                            [x + width, y]
                    when :bottom
                            [x, y + height]
                    end
            end
    end

```

end

def fixpoint(start, limit, &blk)  
limit.times do  
start = blk.call(start)  
end  
start  
end

iterations = ARGV.shift || 17

# the golden rectangle is the fixpoint of the function that attaches a

square

# to a golden-rectangle-approximation

# we start with a 1x1 seed

the\_golden\_rectangle = fixpoint(GoldenRectangle::SeedSquare.new,  
iterations) do |rectangle|  
GoldenRectangle::Square.attach\_to rectangle  
end

svg = SVG.new(‘1024’, ‘768’, ‘-550 -300 1024 768’)  
svg \<\< the\_golden\_rectangle.as\_squares  
svg \<\< SVG::Path.new(the\_golden\_rectangle.as\_spiral) {  
self.style = SVG::Style.new(:fill =\> ‘none’, :stroke =\> ‘#000’,  
:stroke\_width =\> 1, :stroke\_opacity =\> 1.0)  
}  
puts svg.to\_s

###############  
Second solution involving rotate & translate

require ‘rubygems’  
require ‘svg/svg’  
require ‘memoize’

module GoldenRectangle  
COLORS = [‘red’, ‘green’, ‘blue’, ‘yellow’]  
SEED\_POS = {:x =\> 0, :y =\> 0}

```
    class PreSeedSquare
            def height
                    0
            end

            def x
                    SEED_POS[:x]
            end

            def y
                    SEED_POS[:y]
            end

            def color
                    COLORS[0]
            end

            def parent
                    @parent
            end
    end

    class SeedSquare < PreSeedSquare
            def initialize
                    @parent = PreSeedSquare.new
            end

            def fibo(n)
                    a, b = 1, 1
                    n.times { a, b = b, a + b}
                    a
            end

            def size
                    fibo(level)
            end

            def width
                    size
            end

            def level
                    0
            end

            alias_method :height, :width

            def color
                    COLORS[1]
            end

            def as_spiral
                    ["M#{x},#{y + height}", "A#{width},#{height} 0

```

0,1 #{x + width},#{y}"]  
end

```
            def as_squares
                    svg_square
            end

            def svg_square
                    col = self.color
                    group = SVG::Group.new
                    group.transform = "rotate(90)

```

translate(#{-width},#{-height})"  
group.id = level  
group \<\< SVG::Rect.new(0, 0, width, height) {  
self.style = SVG::Style.new(:fill =\> col, :opacity =\> 0.5) }

```
                    path = ["M#{0},#{0}", "A#{width},#{height} 0

```

0,0 #{width},#{height}"]  
group \<\< SVG::Path.new(path) {  
self.style = SVG::Style.new(:fill =\>  
‘none’, :stroke =\> ‘#000’, :stroke\_width =\> 1, :stroke\_opacity =\> 1.0)  
}  
group  
end  
end

```
    class Square < SeedSquare
            include Memoize

            attr_reader :parent

            def initialize(parent)
                    @parent = parent
                    @grandparent = @parent.parent

                    # without memoization this implementation

```

wouldn’t be feasible  
[:level, :parent, :color, :size].each {|f|  
memoize f }  
end

```
            class << self
                    alias_method :attach_to, :new
            end

            def as_squares
                    g1 = @parent.as_squares
                    g2 = svg_square
                    g2 << g1
                    g2
            end

            def color
                    if @grandparent.parent.nil?
                            COLORS[2]
                    elsif
                            @grandparent.parent.parent.nil?
                            COLORS[3]
                    else
                            (COLORS -

```

[@parent.parent.parent.parent.color, @parent.parent.parent.color,  
@parent.color]).first  
end  
end

```
            def level
                    @parent.level + 1
            end
    end

```

end

def fixpoint(start, limit, &blk)  
limit.times do  
start = blk.call(start)  
end  
start  
end

iterations = ARGV.shift || 17

# the golden rectangle is the fixpoint of the function that attaches a

square

# to a golden-rectangle-approximation

# we start with a 1x1 seed

the\_golden\_rectangle = fixpoint(GoldenRectangle::SeedSquare.new,  
iterations) do |rectangle|  
GoldenRectangle::Square.attach\_to rectangle  
end

svg = SVG.new(‘1024’, ‘768’, ‘2500 -1100 1024 768’)  
svg \<\< the\_golden\_rectangle.as\_squares  
svg \<\< SVG::Path.new(the\_golden\_rectangle.as\_spiral) {  
self.style = SVG::Style.new(:fill =\> ‘none’, :stroke =\> ‘#000’,  
:stroke\_width =\> 1, :stroke\_opacity =\> 1.0)  
}  
puts svg.to\_s

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [March 9, 2006, 7:09am UTC](https://www.ruby-forum.com/t/the-golden-fibonacci-ratio-69/76998/4 "2006-03-09T07:09:27Z")

</div>

I fixed a few things and added mouse callbacks (click drag for zoom,  
shift-click-drag for panning). I’d still like to figure out how to make  
it more OO, but at least my understanding of OpenGL is better.

-----Jay A.

usr/bin/ruby

require ‘opengl’  
require ‘glut’

class Array  
def rotate!  
push shift  
end  
def rotate  
self.dup.rotate!  
end  
end

mouse\_func = lambda do |button,state,x,y|  
shift\_down = (GLUT.GetModifiers & GLUT::ACTIVE\_SHIFT) != 0  
case button  
when GLUT::LEFT\_BUTTON  
case state  
when GLUT::UP  
$mode = :none  
when GLUT::DOWN  
if shift\_down then  
$mode = :pan  
else  
$mode = :zoom  
end  
$mouse\_x = x  
$mouse\_y = y  
end  
when 3 #scroll up  
if state == GLUT::DOWN then  
$zoom \*= 1.25  
GLUT.PostRedisplay  
end  
when 4 #scroll down  
if state == GLUT::DOWN then  
$zoom /= 1.25  
GLUT.PostRedisplay  
end  
end  
end

motion\_func = lambda do |x,y|  
case $mode  
when :zoom  
$zoom _= 1.0 + (y-$mouse\_y)/100.0  
when :pan  
$pan\_x += (x-$mouse\_x)_$units\_per\_pixel  
$pan\_y += ($mouse\_y-y)\*$units\_per\_pixel  
when :rotate  
end  
if $mode != :none then  
$mouse\_x = x  
$mouse\_y = y  
GLUT.PostRedisplay  
end  
end

key\_func = lambda do |key,x,y|  
case key  
when ?Q, ?q  
GLUT.DestroyWindow($window);  
exit 0  
when ?-  
$zoom /= 2.0  
when ?+  
$zoom \*= 2.0  
when ?\r, ?\n  
side = $width\>$height ? $width : $height  
sq\_x, sq\_y = $box\_x, $box\_y  
case $add\_to.first  
when :right  
sq\_x = $box\_x + $width  
$width += side  
when :left  
sq\_x = $box\_x - side  
$box\_x = sq\_x  
$width += side  
when :top  
sq\_y = $box\_y + $height  
$height += side  
when :bottom  
sq\_y = $box\_y - side  
$box\_y = sq\_y  
$height += side  
end  
$squares \<\< {  
:side =\> side,  
:x =\> sq\_x,  
:y =\> sq\_y,  
:color =\> [rand, rand, rand]  
}

```
	$add_to.rotate!
end
GLUT.PostRedisplay

```

end

reshape\_func = lambda do |w,h|  
h = 1 if h == 0  
$screen\_width = w  
$screen\_height = h  
GL.Viewport(0, 0, w, h)  
GL.MatrixMode GL::PROJECTION  
GL.LoadIdentity  
GLU.Perspective(45.0, w.to\_f/h.to\_f, 0.1, 100.0);  
GL.MatrixMode GL::MODELVIEW  
end

display\_func = lambda do  
GL.Clear GL::COLOR\_BUFFER\_BIT | GL::DEPTH\_BUFFER\_BIT

```
GL.LoadIdentity
GL.Translate($pan_x, $pan_y, -6.0)
GL.Scale($zoom, $zoom, 0.0)
$squares.each do |s|
	GL.Translate(s[:x], s[:y], 0.0)
	side = s[:side]
	GL.Begin(GL::QUADS)
	GL.Color(*s[:color])
	GL.Vertex(0.0, 0.0)
	GL.Vertex(side, 0.0)
	GL.Vertex(side, side)
	GL.Vertex(0.0, side)
	GL.End
	GL.Translate(-s[:x], -s[:y], 0.0)
end

GLUT.SwapBuffers;

```

end

GLUT.Init  
GLUT.InitDisplayMode(GLUT::DOUBLE | GLUT::RGBA | GLUT::DEPTH)

$screen\_width = 640  
$screen\_height = 480  
$units\_per\_pixel = 1.0/100.0 #TODO: should be determined by screen size  
GLUT.InitWindowSize($screen\_width, $screen\_height)  
GLUT.InitWindowPosition(0, 0)

$window = GLUT.CreateWindow “Fibonacci”

GLUT.KeyboardFunc key\_func  
GLUT.ReshapeFunc reshape\_func  
GLUT.DisplayFunc display\_func  
GLUT.MotionFunc motion\_func  
GLUT.MouseFunc mouse\_func  
#GLUT.IdleFunc display\_func

GL.ClearColor(0.0, 0.0, 0.0, 0.0)  
GL.ClearDepth(1.0)  
GL.DepthFunc(GL::LESS)  
GL.Enable(GL::DEPTH\_TEST)  
GL.ShadeModel(GL::SMOOTH)

$squares = []  
$squares \<\< {  
:side =\> 1.0,  
:x =\> 0.0,  
:y =\> 0.0,  
:color =\> [rand, rand, rand]  
}  
$width = 1.0  
$height = 1.0  
$box\_x = 0.0  
$box\_y = 0.0  
$pan\_x = 0.0  
$pan\_y = 0.0  
$zoom = 1.0  
$mode = :none  
$add\_to = [:right, :bottom, :left, :top]

GLUT.MainLoop
