Archive for the ‘Examples’ Category

Particles/Flowers

Wednesday, July 2nd, 2008

This example uses a system of particles that travel upwards from the bottom of the canvas.  The paths taken by the particles are rendered as stems, and a “flower” is drawn at each endpoint.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# initialize canvas
canvas = Canvas.new :type => :image, :filename => 'particles.png', :size => [400,400]
canvas.background(Color.black)

# load images and grab colors
img = Image.new('italy.jpg').saturation(1.9)
redcolors = img.colors(100)
img = Image.new('v2.jpg').saturation(1.9)
bluecolors = img.colors(100)

# create flower head shape
head = Path.new.oval(0,0,10,10,:center)
petals = 3
for i in 1..petals do
  head.rotate(360/petals)
  head.oval(0,10,5,5,:center)
  head.oval(0,17,2,2,:center)
end
# randomize head attributes
head.randomize :fill, redcolors
head.randomize :stroke, bluecolors
head.randomize :scale, 0.2..2.0
head.randomize :rotation, 0..360

# create particles
total_particles = 100
total_iterations = 100
particles = []
for i in 0...total_particles do
  # start particle at random point at bottom of canvas
  x = random(canvas.width/2 - 50,canvas.width/2 + 50)
  p = Particle.new(x,0)
  p.velocity_x = random(-0.5,0.5)   # set initial x velocity
  p.velocity_y = random(1.0,3.0)    # set initial y velocity
  p.acceleration = 0.1            # set drag or acceleration
  particles[i] = p          # add particle to array
end

# animate particles
for frame in 0...total_iterations do
  for i in 0...total_particles do
    particles[i].move
  end
end

# draw particle trails and heads
for i in 0...total_particles do
  canvas.push
  # choose a stem color
  color = choose(bluecolors).a(0.7).analog(20,0.7)
  canvas.stroke(color)
  canvas.strokewidth(random(0.5,2.0))
  # draw the particle
  particles[i].draw(canvas)
  # go to the last particle position and draw the flower head
  canvas.translate(particles[i].points[-1][0],particles[i].points[-1][1])
  canvas.draw(head)
  canvas.pop
end

canvas.save

Text Spirograph

Wednesday, July 2nd, 2008

Text can be positioned and scaled arbitrarily on the canvas, rendered using any font that is installed on your system.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# set up the canvas and font
canvas = Canvas.new :type => :image, :filename => 'spiro.png', :size => [400,400]
canvas.background(Color.beige)
canvas.fill(Color.black)
canvas.font('Book Antiqua')
canvas.fontsize(12)
canvas.translate(200,200)

# rotate, draw text, repeat
for frame in 1..180
    canvas.push
    canvas.rotate((frame*2) + 120)
    canvas.translate(70,0)
    canvas.text("Going ...", 80, 0)
    canvas.rotate(frame * 6)
    canvas.text("Around and", 20, 0)
    canvas.pop
end

# save the canvas
canvas.save

Iterating Paths

Wednesday, July 2nd, 2008

By sending the “increment” method to a Path object, you can specify changes that will continue to add up or “drift” each time the path is drawn to the canvas.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# create a new 400x400 pixel canvas to draw on
canvas = Canvas.new :type => :image, :filename => 'iterating.png', :size => [400,400]
canvas.background(Color.white)

# create a petal shape with base at (0,0), size 40x150, and bulge at 30px
shape = Path.new.petal(0,0,40,150,30)
# add a circle
shape.oval(-10,20,20,20)
# color it red
shape.fill(Color.red)

# increment shape parameters by the specified amount each iteration,
# or by a random value selected from the specified range
shape.increment(:rotation, 5.0)
#shape.increment(:scale, 0.95)
shape.increment(:scalex, 0.99)
shape.increment(:scaley, 0.96)
shape.increment(:x, 10.0)
shape.increment(:y, 12.0)
shape.increment(:hue,-0.02..0.02)
shape.increment(:saturation, -0.1..0.1)
shape.increment(:brightness, -0.1..0.1)
shape.increment(:alpha, -0.1..0.1)

# draw 200 petals on the canvas starting at location 50,200
canvas.translate(50,220)
canvas.draw(shape,0,0,200)
canvas.save

Randomizing Paths

Wednesday, July 2nd, 2008

By applying the “randomize” method to a Path object, you can specify attributes that should be randomized each time the object is drawn to the canvas.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# create a new 400x400 pixel canvas to draw on
canvas = Canvas.new :type => :image, :filename => 'randomizing.png', :size => [400,400]
canvas.background(Color.white)

# create a flower shape
shape = Path.new
petals = 5
for i in 1..petals do
  shape.petal(0,0,40,100)       # petal at x,y with width,height
  shape.rotate(360 / petals)    # rotate by 1/5th
end

# randomize shape parameters
shape.randomize :fill, Color.blue.complementary
shape.randomize :stroke, Color.blue.complementary
shape.randomize :strokewidth, 1.0..10.0
shape.randomize :rotation, 0..360
shape.randomize :scale, 0.5..1.0
shape.randomize :scalex, 0.5..1.0
shape.randomize :scaley, 0.5..1.0
shape.randomize :alpha, 0.5..1.0
# shape.randomize :hue, 0.5..0.8
shape.randomize :saturation, 0.0..1.0
shape.randomize :brightness, 0.0..1.0
shape.randomize :x, -100.0..100.0
shape.randomize :y, -100.0..100.0

# draw 50 flowers starting at the center of the canvas
canvas.translate(200,200)
canvas.draw(shape,0,0,100)
canvas.save

Substrate

Wednesday, July 2nd, 2008

This example is based on “Substrate” by Jared Tarbell (complexification.net). Some modifications were made to the color rendering algorithm.

Code is shown after the break. (Warning: the new MacRuby version eats a lot more memory than the older RubyCocoa version, so you may want to lower the number of cracks or frames)
(more…)

Drawing Hair

Wednesday, July 2nd, 2008

Applying the “hair” method to a Rope object will render organic flowing threads to the canvas.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# create a new 400x400 pixel canvas to draw on
canvas = Canvas.new :type => :image, :filename => 'hair.png', :size => [400,400]

# choose a random color and set the background to a darker variant
clr = Color.random.a(0.5)
canvas.background(clr.copy.darken(0.6))

# create a new rope with 200 fibers
rope = Rope.new(canvas)
rope.width = 100
rope.fibers = 200
rope.strokewidth = 0.4
rope.roundness = 3.0

# randomly rotate the canvas from its center
canvas.translate(canvas.width/2,canvas.height/2)
canvas.rotate(random(0,360))
canvas.translate(-canvas.width/2,-canvas.height/2)

# draw 50 ropes
ropes = 50
for i in 0..ropes do
    canvas.stroke(clr.copy.analog(20, 0.8))   # rotate hue up to 20 deg left/right, vary brightness/saturation by up to 70%
    rope.x0 = -100                            # start rope off bottom left of canvas
    rope.y0 = -100
    rope.x1 = canvas.width + 100              # end rope off top right of canvas
    rope.y1 = canvas.height + 100
    rope.hair                                 # draw rope in organic "hair" style
end

# save the canvas
canvas.save

Drawing Ribbons

Wednesday, July 2nd, 2008

Applying the “ribbon” method to a Rope object will render smooth, flowing ribbons to the canvas.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# create a new 400x400 pixel canvas to draw on
canvas = Canvas.new :type => :image, :filename => 'ribbons.png', :size => [400,400]

# choose a random color and set the background to a darker variant
clr = Color.random.a(0.5)
canvas.background(clr.copy.darken(0.6))

# create a new rope with 200 fibers
rope = Rope.new(canvas)
rope.width = 500
rope.fibers = 200
rope.strokewidth = 1.0
rope.roundness = 1.5

# randomly rotate the canvas from its center
canvas.translate(canvas.width/2,canvas.height/2)
canvas.rotate(random(0,360))
canvas.translate(-canvas.width/2,-canvas.height/2)

# draw 20 ropes
ropes = 20
for i in 0..ropes do
    canvas.stroke(clr.copy.analog(10, 0.7))   # rotate hue up to 10 deg left/right, vary brightness/saturation by up to 70%
    rope.x0 = -100                            # start rope off bottom left of canvas
    rope.y0 = -100
    rope.x1 = canvas.width + 200              # end rope off top right of canvas
    rope.y1 = canvas.height + 200
    rope.ribbon                               # draw rope in smooth "ribbon" style
end

# save the canvas
canvas.save

Image Moving and Scaling

Wednesday, July 2nd, 2008

Various scaling and moving operations are available for images. You can scale an image proportionally by a percentage, scale it to fit proportionally within a bounding box, or scale width and height to fit exactly within a bounding box.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# set up the canvas and font
canvas = Canvas.new :type => :image, :filename => 'moving.png', :size => [400,400]
canvas.background Color.white
canvas.font 'Skia'
canvas.fontsize 14
canvas.fill Color.black
canvas.stroke Color.red

# load an image
img = Image.new 'v2.jpg'

# SCALE (multiply both dimensions by a scaling factor)
img.scale(0.2)
canvas.draw(img,0,240)  # draw the image at the specified coordinates
canvas.text("scale to 20%",0,220)

# FIT (scale to fit within the given dimensions, maintaining original aspect ratio)
img.reset               # first reset the image to its original size
img.fit(100,100)
canvas.fill(Color.white)
canvas.rect(120,240,100,100)
canvas.fill(Color.black)
canvas.draw(img,133,240)
canvas.text("fit into 100x100",120,220)

# RESIZE (scale to fit exactly within the given dimensions)
img.reset
img.resize(100,100)
canvas.draw(img,240,240)
canvas.text("resize to 100x100",240,220)

# CROP (to the largest square containing image data)
img.reset
img.scale(0.2).crop
canvas.draw(img,0,100)
canvas.text("crop max square",0,80)

# CROP (within a rectangle starting at x,y with width,height)
img.reset
img.scale(0.3).crop(0,0,100,100)
canvas.draw(img,120,100)
canvas.text("crop to 100x100",120,80)

# ROTATE
img.origin(:center)
img.rotate(45)
canvas.draw(img,300,140)
canvas.text("rotate 45 degrees",250,50)

#img.origin(:center)   # center the image
#img.translate(0,-150)    # move the image

canvas.save

Image Effects

Wednesday, July 2nd, 2008

You can apply various Photoshop-style filters to images, then render them to the canvas.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# set up the canvas and font
canvas = Canvas.new :type => :image, :filename => 'effects.png', :size => [400,400]
canvas.background(Color.white)
canvas.font('Skia')
canvas.fontsize(14)
canvas.fill(Color.black)

# load image file
img = Image.new('v2.jpg')

# set image width/height, starting position, and increment position
w,h = [100,100]
x,y = [0,290]
xoffset = 105
yoffset = 130

# ORIGINAL image, resized to fit within w,h:
img.fit(w,h)
canvas.draw(img,x,y)
canvas.text("original",x,y-15)
x += xoffset

# CRYSTALLIZE: apply a "crystallize" effect with the given radius
img.reset.fit(w,h)
img.crystallize(5.0)
canvas.draw(img,x,y)
canvas.text("crystallize",x,y-15)
x += xoffset

# BLOOM: apply a "bloom" effect with the given radius and intensity
img.reset.fit(w,h)
img.bloom(10, 1.0)
canvas.draw(img,x,y)
canvas.text("bloom",x,y-15)
x += xoffset

# EDGES: detect edges
img.reset.fit(w,h)
img.edges(10)
canvas.draw(img,x,y)
canvas.text("edges",x,y-15)
x += xoffset

# (go to next row)
x = 0
y -= yoffset

# POSTERIZE: reduce image to the specified number of colors
img.reset.fit(w,h)
img.posterize(8)
canvas.draw(img,x,y)
canvas.text("posterize",x,y-15)
x += xoffset

# TWIRL: rotate around x,y with radius and angle
img.reset.fit(w,h)
img.twirl(35,50,40,90)
canvas.draw(img,x,y)
canvas.text("twirl",x,y-15)
x += xoffset

# EDGEWORK: simulate a woodcut print
img.reset.fit(w,h)
canvas.rect(x,y,img.width,img.height) # needs a black background
img.edgework(0.5)
canvas.draw(img,x,y)
canvas.text("edgework",x,y-15)
x += xoffset

# DISPLACEMENT: use a second image as a displacement map
img.reset.fit(w,h)
img2 = Image.new('italy.jpg').resize(img.width,img.height)
img.displacement(img2, 30.0)
canvas.draw(img,x,y)
canvas.text("displacement",x,y-15)
x += xoffset

# (go to next row)
x = 0
y -= yoffset

# DOTSCREEN: simulate a dot screen: center point, angle(0-360), width(1-50), and sharpness(0-1)
img.reset.fit(w,h)
img.dotscreen(0,0,45,5,0.7)
canvas.draw(img,x,y)
canvas.text("dotscreen",x,y-15)
x += xoffset

# SHARPEN: sharpen using the given radius and intensity
img.reset.fit(w,h)
img.sharpen(2.0,2.0)
canvas.draw(img,x,y)
canvas.text("sharpen",x,y-15)
x += xoffset

# BLUR: apply a gaussian blur with the given radius
img.reset.fit(w,h)
img.blur(3.0)
canvas.draw(img,x,y)
canvas.text("blur",x,y-15)
x += xoffset

# MOTION BLUR: apply a motion blur with the given radius and angle
img.reset.fit(w,h)
img.motionblur(10.0,90)
canvas.draw(img,x,y)
canvas.text("motion blur",x,y-15)
x += xoffset

# save the canvas
canvas.save

Image Color Adjustments

Wednesday, July 2nd, 2008


You can apply various color adjustments to a loaded image, then render it to the canvas.  You can also grab colors from an image for use in a gradient or to paint objects on the canvas.

#!/usr/local/bin/macruby

framework 'cocoa'
require 'rubygems'
require 'hotcocoa/graphics'
include HotCocoa
include Graphics

# set up the canvas and font
canvas = Canvas.new :type => :image, :filename => 'colors.png', :size => [400,400]
canvas.background(Color.white)
canvas.font('Skia')
canvas.fontsize(14)
canvas.fill(Color.black)

# LOAD IMAGE
img = Image.new('v2.jpg')

w,h = [100,100]
x,y = [0,290]
xoffset = 105
yoffset = 130

# ORIGINAL image, resized to fit within w,h:
img.fit(w,h)
canvas.draw(img,x,y)
canvas.text("original",x,y-15)
x += xoffset

# HUE: rotate color wheel by degrees
img.reset.fit(w,h)
img.hue(45)
canvas.draw(img,x,y)
canvas.text("hue",x,y-15)
x += xoffset

# EXPOSURE: increase/decrease exposure by f-stops
img.reset.fit(w,h)
img.exposure(1.0)
canvas.draw(img,x,y)
canvas.text("exposure",x,y-15)
x += xoffset

# SATURATION: adjust saturation by multiplier
img.reset.fit(w,h)
img.saturation(2.0)
canvas.draw(img,x,y)
canvas.text("saturation",x,y-15)
x += xoffset

# (go to next row)
x = 0
y -= yoffset

# CONTRAST: adjust contrast by multiplier
img.reset.fit(w,h)
img.contrast(2.0)
canvas.draw(img,x,y)
canvas.text("contrast",x,y-15)
x += xoffset

# BRIGHTNESS: adjust brightness
img.reset.fit(w,h)
img.brightness(0.5)
canvas.draw(img,x,y)
canvas.text("brightness",x,y-15)
x += xoffset

# MONOCHROME: convert to a monochrome image
img.reset.fit(w,h)
img.monochrome(Color.orange)
canvas.draw(img,x,y)
canvas.text("monochrome",x,y-15)
x += xoffset

# WHITEPOINT: remap the white point
img.reset.fit(w,h)
img.whitepoint(Color.whiteish)
canvas.draw(img,x,y)
canvas.text("white point",x,y-15)
x += xoffset

# (go to next row)
x = 0
y -= yoffset

# CHAINING: apply multiple effects at once
img.reset.fit(w,h)
img.hue(60).saturation(2.0).contrast(2.5)
canvas.draw(img,x,y)
canvas.text("multi effects",x,y-15)
x += xoffset

# COLORS: sample random colors from the image and render as a gradient
img.reset.fit(w,h)              # reset the image and scale to fit within w,h
colors = img.colors(10).sort!   # select 10 random colors and sort by brightness
gradient = Gradient.new(colors) # create a new gradient using the selected colors
rect = Path.new.rect(x,y,img.width,img.height) # create a rectangle the size of the image
canvas.beginclip(rect)          # begin clipping so the gradient will only fill the rectangle
canvas.gradient(gradient,x,y,x+img.width,y+img.height) # draw the gradient between opposite corners of the rectangle
canvas.endclip                  # end clipping so we can draw on the rest of the canvas
canvas.text("get colors",x,y-15)    # add text label
x += xoffset

canvas.save