Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

Tuesday 29 September 2009

Real Menger Sponge in Ruby Processing

# menger.rb by Martin Prout
require 'ruby-processing'
load_library 'opengl'

# full_screen required for linux with OPENGL (a bug)
full_screen if java.lang.System.get_property('os.name') == "Linux"

ANGLE_STEP = Math::PI/180
DATA = [-1, 0, 1]
MIN_SIZE = 40       # controls recursion depth (20 is pushing it for me!!)

def setup
  size(600, 600, OPENGL)
  library_loaded?(:opengl) ? render_mode(OPENGL) : render_mode(P3D)
  @start_t = Time.now.to_f
  @data = []              # initialize empty array
  cube(0, 0, 0, 300)   # fill data array
  @angle = 0
end

# Fill @data array with cube center coordinate
# and size data (this can be done at setup)

def cube x, y, z, sz
  unless (sz <= MIN_SIZE) then
    u = sz/3.0
    DATA.each do |i|
      DATA.each do |j|
        DATA.each do |k|
          cube(x+(i*u), y+(j*u), z+(k*u), u) unless (i.abs + j.abs + k.abs) == 1
        end
      end
    end
  end
  @data.push [x, y, z, sz] unless sz > MIN_SIZE
end

# Render the cubes (data from @data) using
# translate and custom box function, uses ruby splat

def draw_cubes
  @data.each do |point|
    x, y, z, sz = *point
    push_matrix
    translate(x, y, z)
    my_box(sz)
    pop_matrix
  end
end

def draw
  background 0
  ambient_light 155, 155, 155
  directional_light 225, 225, 225, 1, 1, -1
  @angle = (ANGLE_STEP + @angle) % (Math::PI * 2)
  translate(width/2, height/2, 0)
  rotate_x @angle
  rotate_y @angle
  rotate_z @angle
  draw_cubes
end

def my_box sz
  no_stroke
  begin_shape QUADS
  # +Z "front" face
  fill 200, 0, 0
  vertex -sz/2, -sz/2,  sz/2
  vertex  sz/2, -sz/2,  sz/2
  vertex  sz/2,  sz/2,  sz/2
  vertex -sz/2,  sz/2,  sz/2

  # -Z "back" face
  vertex  sz/2, -sz/2, -sz/2
  vertex -sz/2, -sz/2, -sz/2
  vertex -sz/2,  sz/2, -sz/2
  vertex  sz/2,  sz/2, -sz/2

  # +Y "bottom" face
  fill 0, 200, 0
  vertex -sz/2,  sz/2,  sz/2
  vertex  sz/2,  sz/2,  sz/2
  vertex  sz/2,  sz/2, -sz/2
  vertex -sz/2,  sz/2, -sz/2

  # -Y "top" face
  vertex -sz/2, -sz/2, -sz/2
  vertex  sz/2, -sz/2, -sz/2
  vertex  sz/2, -sz/2,  sz/2
  vertex -sz/2, -sz/2,  sz/2

  # +X "right" face
  fill 0, 0, 200
  vertex  sz/2, -sz/2,  sz/2
  vertex  sz/2, -sz/2, -sz/2
  vertex  sz/2,  sz/2, -sz/2
  vertex  sz/2,  sz/2,  sz/2

  # -X "left" face
  vertex -sz/2, -sz/2, -sz/2
  vertex -sz/2, -sz/2,  sz/2
  vertex -sz/2,  sz/2,  sz/2
  vertex -sz/2,  sz/2, -sz/2
 
  end_shape
end


I would be interested to know how this sketch would work on Windows or Mac os, with a more powerful graphics card. The static screen shot below was taken with min size set to 40 on my linux box (33 is practical limit).  P3D is not so good as you get to see the joins.


Thursday 24 September 2009

A Simple Rotating Cube using PVector and splat


require 'ruby-processing'
class RotatingCube < Processing::App

  def setup
    render_mode P3D
    @data = [
    PVector.new( -1, -1,  1),  # 0  twice unit cube vertices
    PVector.new(  1, -1,  1),  # 1  use halves for unit cube
    PVector.new(  1,  1,  1),  # 2
    PVector.new( -1,  1,  1),  # 3
    PVector.new(  1, -1, -1),  # 4
    PVector.new( -1, -1, -1),  # 5
    PVector.new( -1,  1, -1),  # 6
    PVector.new(  1,  1, -1),  # 7
    PVector.new(  1, -1,  1)   # 8
    ]
    frame_rate 60
    @theta = 0
  end

  def draw
    background 0
    @theta += 0.005
    ambient_light 255, 255, 255
    directional_light 255, 255, 255, 1, -1, 0   
    translate width/2, height/2, 0
    rotate_x @theta
    rotate_y @theta
    rotate_z @theta
    pts = scale_vectors width/4
    draw_cube pts
  end

  def draw_cube pts
    stroke 0
    begin_shape QUADS
    fill 255, 0, 0   
    # +Z "front" face
    vertex *pts[0].array # 0
    vertex *pts[1].array # 1
    vertex *pts[2].array # 2
    vertex *pts[3].array # 3

    # -Z "back" face
    vertex *pts[4].array # 4
    vertex *pts[5].array # 5
    vertex *pts[6].array # 6
    vertex *pts[7].array # 7
    fill 0, 255, 0
    # +Y "bottom" face
    vertex *pts[3].array # 3
    vertex *pts[2].array # 2
    vertex *pts[7].array # 7
    vertex *pts[6].array # 6

    # -Y "top" face
    vertex *pts[5].array # 5
    vertex *pts[4].array # 4
    vertex *pts[8].array # 8
    vertex *pts[0].array # 0
    fill 0, 0, 255
    # +X "right" face
    vertex *pts[8].array # 8
    vertex *pts[4].array # 4
    vertex *pts[7].array # 7
    vertex *pts[2].array # 2

    # -X "left" face
    vertex *pts[5].array # 5
    vertex *pts[0].array # 0
    vertex *pts[3].array # 3
    vertex *pts[6].array # 6
    
    end_shape
  end
 
  def scale_vectors sz
    points = Array.new
    @data.each do |point
      points.push(PVector.mult(point, sz))
    end
    return points
  end
end

RotatingCube.new :title => "Rotating Cube",  :width => 300,  :height => 300

Wednesday 23 September 2009

Tunnel a popular theme in context free

Here's another ruby-processing context-free DSL example:-

# tunnel.rb ruby-processing
load_library 'context_free'

def setup_the_tunnel
  @tunnel = ContextFree.define do
    rule :start do
      body :brightness => 1.0
    end
    rule :body do
      square :brightness => 0.6
      square :size =>  0.996, :brightness => 0.3
      body  :size =>  0.994, :rotation => 0.12
    end
  end
end


def setup
  size 500, 500
  setup_the_tunnel
  color_mode HSB, 360, 1, 1
  smooth
  draw_it
  save_frame("tunnel.png")
end


def draw
  # Do nothing.
end


def draw_it
  background 0
  @tunnel.render :start, :size => height,  :stop_size => 2,
        :start_x => width/2, :start_y => height/2
end



 

Fake Menger Sponge

 Here I have used a combination of context free art ('cfdg') and ruby-processing to produce a fake "Menger Sponge" here is the cfdg file used create a Sierpinksi carpet image.


startshape carpet
background{b 1 h 360 sat 1}

rule carpet{
patch{}
carpet{s .3333 y .3333}
carpet{s .3333 x .3333 y .3333}
carpet{s .3333 x .3333}
carpet{s .3333 x .3333 y -.3333}
carpet{s .3333 y -.3333}
carpet{s .3333 x -.3333 y -.3333}
carpet{s .3333 x -.3333}
carpet{s .3333 x -.3333 y .3333}
}

rule patch{
SQUARE{s .3333 b -1}
}




Here is the Sierpinski carpet used for the cube faces (needs to be put in data folder). Image has been cropped to remove a blank border.





require 'ruby-processing'
class FakeMenger < Processing::App
  # load_library 'opengl' ## for opengl uncomment this and adjust render mode
  # full_screen ## uncomment this for linux opengl or just if you like fullscreen

  def setup
    render_mode P3D ## OPENGL would be better if you're not on linux
    @theta = 0.0
    frame_rate 60
    @img = load_image "menger.png" ## put image in data folder
    texture_mode NORMALIZED
  end

  def draw
    background 0
    @theta += 0.01
    ambient_light 255, 0, 0
    directional_light 255, 0, 0, 1, -1, 0
    translate width/2, height/2, 0
    rotate_x @theta
    rotate_y @theta
    draw_fake_menger height/4
  end

  def draw_fake_menger sz
    stroke 0
    begin_shape QUADS
    texture @img
    # +Z "front" face
    vertex -sz, -sz,  sz, 0, 0
    vertex  sz, -sz,  sz, sz, 0
    vertex  sz,  sz,  sz, sz, sz
    vertex -sz,  sz,  sz, 0, sz

    # -Z "back" face
    vertex  sz, -sz, -sz, 0, 0
    vertex -sz, -sz, -sz, sz, 0
    vertex -sz,  sz, -sz, sz, sz
    vertex  sz,  sz, -sz, 0, sz

    # +Y "bottom" face
    vertex -sz,  sz,  sz, 0, 0
    vertex  sz,  sz,  sz, sz, 0
    vertex  sz,  sz, -sz, sz, sz
    vertex -sz,  sz, -sz, 0, sz

    # -Y "top" face
    vertex -sz, -sz, -sz, 0, 0
    vertex  sz, -sz, -sz, sz, 0
    vertex  sz, -sz,  sz, sz, sz
    vertex -sz, -sz,  sz, 0, sz

    # +X "right" face
    vertex  sz, -sz,  sz, 0, 0
    vertex  sz, -sz, -sz, sz, 0
    vertex  sz,  sz, -sz, sz, sz
    vertex  sz,  sz,  sz, 0, sz

    # -X "left" face
    vertex -sz, -sz, -sz, 0, 0
    vertex -sz, -sz,  sz, sz, 0
    vertex -sz,  sz,  sz, sz, sz
    vertex -sz,  sz, -sz, 0, sz
    
    end_shape
  end

end

FakeMenger.new :title => "Fake Menger Sponge",  :width => 300,  :height => 300
# FakeMenger.new :title => "Fake Menger Sponge",  :width => 1024,  :height => 1024



If you are not on linux try using 'opengl' it should run smoother than with P3D, where the cube actually looks decidedly spongy. It actually works tolerably well using opengl with linux, but you need to set full_screen mode and you might as well beef up the size a bit. Just for a bit of fun you can replace the black on the image with transparency, then you a bit of a ghostly image. I have implemented the menger in ruby-processing, follow the link.

Saturday 19 September 2009

Using Multidimensional Data Arrays



require 'ruby-processing'

class Sierpinski < Processing::App
  def setup
    size 800, 800
    ### data is an array of calculated coordinates for the Sierpinski triangle    
    data= [[[400.000000, -0.546875,387.500000, 21.103760,412.500000, 21.103760]],
    [[387.500000, 21.103760,375.000000, 42.754395,400.000000, 42.754395]],
    ####################################################################
    # Most data lines omitted
    ####################################################################
    [[787.500000, 670.622803,775.000000, 692.273438,800.000000, 692.273438]]]
    fill 0
    background 255
    no_stroke
    data.each do |line|
      line.each do |tri|
        triangle(*tri) ## use 'splat' to de-array coordinates
      end
    end
  end
end


There are some neat tricks you can use in ruby to read data from multidimensional arrays. The above code includes only a very partial amount of the data array, but the code illustrates how easy it is to extract the data from a multidimensional array in ruby. The "splat" prefix '*' strips the values out of the array, which can then be used directly to draw the triangle.  This approach may be useful if there is performance need to avoid avoid recursion overhead. There's a lot of gibberish on the web about tail recursion optimization on the way for java 7 (and hence ruby 1.9? and may'be jruby so you won't need to worry about such optimization in the future, whenever that comes).

Friday 18 September 2009

Another of Lazydogs Little Gems translated to ruby-processing


require 'ruby-processing'
load_library 'opengl'
########################################################
# Original code
# http://lazydog-bookfragments.blogspot.com/2009/05/mirror-of-confusion.html
# Original version written for java processing author 'lazydog'
# aka Ben Notorianni translated to ruby-processing by 'monkstone'
# aka Martin Prout
########################################################

#
# Dimensions of screen.
#
K_WIDTH = 800
K_HEIGHT = 600

#
# Dimensions of texture.
# For fast PCs reduce or remove the denominator.
#
K_TEXTURE_WIDTH = K_WIDTH/2
K_TEXTURE_HEIGHT = K_HEIGHT/2


#
# following currently required by linux? Otherwise use 'lazydogs' dimensions
#
if java.lang.System.get_property('os.name') == "Linux" then
  full_screen
end  


def setup
  size(K_WIDTH, K_HEIGHT, OPENGL )
  #
  # P3D doesn't work well in this sketch!
  #
  library_loaded?(:opengl) ? render_mode(OPENGL) : render_mode(P3D)
  @start_t = Time.now.to_f
  color_mode RGB, 255
  smooth
 
  #
  # @history used to store the current frame buffer as a texture.
  #

  @history = create_image( K_TEXTURE_WIDTH, K_TEXTURE_HEIGHT, RGB )

  texture_mode( NORMALIZED )
end

def millis
  (@start_t - Time.now.to_f) * 10**6
end

def draw
  background( 255 )

  translate( width/2, height/2 )

  #
  # Draw the ground - use a simple grid for detail.
  #
  # All numbers are fudged to make the grid and ground look about right.
  #
  hint(DISABLE_DEPTH_TEST)

  no_stroke
  fill( 240, 189, 180 )
  rect( -width/2, 24, width/2, 200 )

  fill( 200, 220, 255 )
  rect( 0, 24, width, 200 )

  stroke( 0 )
  stroke_weight( 1.0 )

  10.times do |i|
     z = -2000 + i * 200
    line( -width*10, 200, z, width* 10, 200, z )

     x = i* 200

    line( -x, 200, -2000, -x, 200, 200 )
    line( x, 200, -2000, x, 200, 200 )
  end

  hint(ENABLE_DEPTH_TEST)

  #
  # Draw the mirror.
  #
  # The mirror is animated by rotating around the y-axis.
  # The mirror is drawn using the last frame buffer as the reflection in the mirror.
  # The view in the mirror is dimmed slightly by drawing an alpha rectangle over it.
  # Also, the mirror is oultined to make it stand out a bit.
  #
  push_matrix()

#  rotate_x( 0.03 * Math.sin( millis * 0.0023)  )
  rotate_y( 0.3 * Math.sin( millis * 0.001) )
  translate( -width/2, -height/2, -200 )

  no_stroke
  begin_shape
  texture( @history )
  vertex( 0.0, 0.0, 0, 0 )
  vertex( width, 0, 1, 0 )
  vertex( width, height, 1, 1 )
  vertex( 0, height, 0, 1 )
  end_shape

  fill( 0, 0, 0, 30 )
  begin_shape
  vertex( 0.0, 0.0 )
  vertex( width, 0 )
  vertex( width, height )
  vertex( 0, height )
  end_shape

  stroke( 200, 200, 200 )
  stroke_weight( 2 )
  no_fill
  begin_shape()
  vertex( 0, 0 )
  vertex( width, 0 )
  vertex( width, height )
  vertex( 0, height )
  end_shape(CLOSE)

  pop_matrix

  #
  # Draw the foreground scene.
  #
  translate( 200 * Math.sin( millis * 0.001 ), 200 * Math.sin( millis* 0.0013 ) )

  lights
  no_stroke
  fill( 238, 80, 70 )
  sphere( 100 )

  #
  # Use the current frame as the texture for the
  # mirror in the next frame.
  #
  # if ( frame_count % 4 == 0 )
  @history.copy( get, 0, 0, width, height, 0, 0, K_TEXTURE_WIDTH, K_TEXTURE_HEIGHT )
end

A More Complicated Sierpinski

Here is a more elegant use of the mathematics, the main credit to lazydog for figuring it all out, all I did was translate it to ruby-processing. If you just want to see a boring old sphere, remove the comment in front of the last recursive drawTriangle (in drawTriangle of course). If you want to see more amazing processing opengl apps check out lazydogs blog.


# elegant.rb

class ElegantBall < Processing::App
  load_library :opengl
  attr_reader :start_t

  def setup()
    library_loaded?(:opengl) ? configure_opengl : render_mode(P3D)
    color_mode(RGB, 1)
  end

  def configure_opengl
    render_mode OPENGL
    hint ENABLE_OPENGL_4X_SMOOTH     # optional
    hint DISABLE_OPENGL_ERROR_REPORT # optional
  end

  def draw()
    #background(0.25)
    background(0)
    # Move the origin so that the scene is centered on the screen.
    translate(width/2, height/2, 0.0)
    # Set up the lighting.
    setup_lights
    # Rotate the local coordinate system.
    smooth_rotation(5.0, 6.7, 7.3)
    # Draw the inner object.
    no_stroke()
    fill(smooth_colour(10.0, 12.0, 7.0))
    draw_icosahedron(5, 60.0, false)
    # Rotate the local coordinate system again.
    smooth_rotation(4.5, 3.7, 7.3)
    # Draw the outer object.
    stroke(0.2)
    fill(smooth_colour(6.0, 9.2, 0.7))
    draw_icosahedron(5, 200.0, true)
  end

  def setup_lights
    ambient_light(0.025, 0.025, 0.025)
    directional_light(0.2, 0.2, 0.2, -1, -1, -1)
    spot_light(1.0, 1.0, 1.0, -200, 0, 300, 1, 0, -1, Math::PI/4, 20)
  end

  ##
  # Generate a vector whose components change smoothly over time in the range [ 0, 1 ].
  # Each component uses a Math.sin() function to map the current time in milliseconds somewhere
  # in the range [ 0, 1 ].A 'speed' factor is specified for each component.
  #
  def smooth_vector(s1, s2, s3)
    mills = millis * 0.00003 ## Lazydogs factor
    # mills = millis * 0.0000001 ## worked for me a bit slower!!
    x = 0.5 * Math.sin(mills * s1) + 0.5
    y = 0.5 * Math.sin(mills * s2) + 0.5
    z = 0.5 * Math.sin(mills * s3) + 0.5
    PVector.new(x, y, z)
  end

  ##
  # Generate a colour which smoothly changes over time.
  # The speed of each component is controlled by the parameters s1, s2 and s3.
  #
  def smooth_colour(s1, s2, s3)
    v = smooth_vector(s1, s2, s3)
    color(v.x, v.y, v.z)
  end

  ##
  # Rotate the current coordinate system.
  # Uses smooth_vector() to smoothly animate the rotation.
  #
  def smooth_rotation(s1, s2, s3)
    r1 = smooth_vector(s1, s2, s3)
    rotate_x(2.0 * Math::PI * r1.x)
    rotate_y(2.0 * Math::PI * r1.y)
    rotate_x(2.0 * Math::PI * r1.z)
  end

  ##
  # Draw an icosahedron defined by a radius r and recursive depth d.
  # Geometry data will be saved into dst. If spherical is true then the icosahedron
  # is projected onto the sphere with radius r.
  #
  def draw_icosahedron(depth, r, spherical)
    # Calculate the vertex data for an icosahedron inscribed by a sphere radius 'r'.
    # Use 4 Golden Ratio rectangles as the basis.
    gr = (1.0 + Math.sqrt(5.0)) / 2.0
    h = r / Math.sqrt(1.0 + gr * gr)
    v =
      [
      PVector.new(0, -h, h*gr), PVector.new(0, -h, -h*gr), PVector.new(0, h, -h*gr), PVector.new(0, h, h*gr),
      PVector.new(h, -h*gr, 0), PVector.new(h, h*gr, 0), PVector.new(-h, h*gr, 0), PVector.new(-h, -h*gr, 0),
      PVector.new(-h*gr, 0, h), PVector.new(-h*gr, 0, -h), PVector.new(h*gr, 0, -h), PVector.new(h*gr, 0, h)
    ]

    # Draw the 20 triangular faces of the icosahedron.
    unless spherical then
      r = 0.0
    end

    begin_shape(TRIANGLES)
     
    draw_triangle(depth, r, v[0], v[7],v[4])
    draw_triangle(depth, r, v[0], v[4], v[11])
    draw_triangle(depth, r, v[0], v[11], v[3])
    draw_triangle(depth, r, v[0], v[3], v[8])
    draw_triangle(depth, r, v[0], v[8], v[7])
 
    draw_triangle(depth, r, v[1], v[4], v[7])
    draw_triangle(depth, r, v[1], v[10], v[4])
    draw_triangle(depth, r, v[10], v[11], v[4])
    draw_triangle(depth, r, v[11], v[5], v[10])
    draw_triangle(depth, r, v[5], v[3], v[11])
    draw_triangle(depth, r, v[3], v[6], v[5])
    draw_triangle(depth, r, v[6], v[8], v[3])
    draw_triangle(depth, r, v[8], v[9], v[6])
    draw_triangle(depth, r, v[9], v[7], v[8])
    draw_triangle(depth, r, v[7], v[1], v[9])
 
    draw_triangle(depth, r, v[2], v[1], v[9])
    draw_triangle(depth, r, v[2], v[10], v[1])
    draw_triangle(depth, r, v[2], v[5], v[10])
    draw_triangle(depth, r, v[2], v[6], v[5])
    draw_triangle(depth, r, v[2], v[9], v[6])
   
    end_shape()
  end

  ##
  # Draw a triangle either immediately or subdivide it first.
  # If depth is 1 then draw the triangle otherwise subdivide first.
  #
  def draw_triangle(depth, r, p1, p2, p3)

    if (depth == 1) then
      vertex(p1.x, p1.y, p1.z)
      vertex(p2.x, p2.y, p2.z)
      vertex(p3.x, p3.y, p3.z)
    else
      # Calculate the mid points of this triangle.
      v1 = PVector.mult(PVector.add(p1, p2), 0.5)
      v2 = PVector.mult(PVector.add(p2, p3), 0.5)
      v3 = PVector.mult(PVector.add(p3, p1), 0.5)
    unless (r == 0.0) then
      # Project the verticies out onto the sphere with radius r.
      v1.normalize()
      v1.mult(r)
      v2.normalize()
      v2.mult(r)
      v3.normalize()
      v3.mult(r)
    end
      ## Generate the next level of detail
      depth -= 1
      draw_triangle(depth, r, p1, v1, v3)
      draw_triangle(depth, r, v1, p2, v2)
      draw_triangle(depth, r, v2, p3, v3)
      # Uncomment out the next line to include the central part of the triangle.
      # draw_triangle(depth, r, v1, v2, v3)
    end

  end


end

### Guard against current issues with Linux and opengl with ruby-processing (18 Sept 2009)
if java.lang.System.get_property('os.name') == "Linux" then
  ElegantBall.new(:width => 800, :height => 800, :title => "Elegant Ball", :full_screen => true)
else

  ElegantBall.new(:width => 800, :height => 800, :title => "Elegant Ball", :full_screen => false)
end


Currently there is an issue with resizing on linux with opengl and ruby-processing, so best to specify full screen (NB to run this script you will need ruby-processing follow the link in my blog header to find out how, to run the script:-
"rp5 run elegant_ball.rb". See screenshot of running sketch below:-

Thursday 3 September 2009

An Advanced Pretzel

Just another idle context free rule set in ruby processing, whilst I try to figure out how to do a pentaflake.


load_library 'context_free'
def setup_the_spiral
 
  @spiral = ContextFree.define do
    rule :start do
       split do
       star :rotation => 0
       rewind
       star :rotation =>  120
       rewind
       star :rotation =>  240
       end
    end
    rule :base do
       triangle :hue => 0.5
       base :rotation =>  3, :size => 0.98, :x => 0.09
    end
    rule :star do
       split do            
       base :brightness => 0.8, :rotation => 0
       rewind
       base :brightness => 0.8, :rotation =>  40
       rewind
       base :brightness => 0.8, :rotation =>  80
       end
    end
end
end

 
def setup
  size 600, 600
  setup_the_spiral
  no_stroke
  color_mode HSB, 1.0
  smooth
  draw_it
end

 
def draw
  # Do nothing.
end

 
def draw_it
  background 0.33, 0.25, 0.2
  @spiral.render :start, :size => height/5, :color => [0.35, 0.4, 0.9, 0.55], :stop_size => 1,
                       :start_x => width/2, :start_y => height/2
end

Tuesday 1 September 2009

Non cfdg DSL Sierpinski triangle using ruby processing

A genuine Sierpinski triangle using ruby-processing no DSL required:-


# Sierpinski.rb by Martin Prout
require 'ruby-processing'

T_HEIGHT = Math.sqrt(3)/2 # use ruby Math
TOP_Y = 1/Math.sqrt(3)
BOT_Y = Math.sqrt(3)/6
TRIANGLE_SIZE = 800

def setup()
  size(TRIANGLE_SIZE, (T_HEIGHT *  TRIANGLE_SIZE))
  smooth() 
  fill(255)
  background(0)
  no_stroke()
  draw_sierpinski(width/2, height/1.5, TRIANGLE_SIZE)
  save_frame("sierpinski.png")
end

def draw_sierpinski(cx, cy, sz)
  if (sz < 5)  then # Limit no of recursions on size
    draw_triangle(cx, cy, sz) # Only draw terminals
    no_loop()
  else
    cx0 = cx
    cy0 = cy - BOT_Y * sz
    cx1 = cx - sz/4
    cy1 = cy + (BOT_Y/2) * sz
    cx2 = cx + sz/4
    cy2 = cy + (BOT_Y/2) * sz
    draw_sierpinski(cx0, cy0, sz/2)
    draw_sierpinski(cx1, cy1, sz/2)
    draw_sierpinski(cx2, cy2, sz/2)
  end
end

def draw_triangle(cx, cy, sz)
  cx0 = cx
  cy0 = cy - TOP_Y * sz
  cx1 = cx - sz/2
  cy1 = cy + BOT_Y * sz
  cx2 = cx + sz/2
  cy2 = cy + BOT_Y * sz
  triangle(cx0, cy0, cx1, cy1, cx2, cy2)
end

Followers

Blog Archive

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2