Have fun whilst building your ruby skills
Daniel Shiffman has recently published the second edition of his Learning Processing book updated for processing-3.0. He has also published the code here. There is an opportunity to flex your ruby skills by porting some more examples. My suggestion is to simply translate the processing code to ruby, as a first pass, as I have done below for Dan Shiffmans Polymorphism sketch, and make sure it works. You can then run rubocop
or similar to check for the more egregious bits of ruby coding you’ve done. After that you should look at re-factoring the sketch, because what was good or passable in processing/java can often but be done more elegantly in ruby.
You can send me a tweet @monkstoneT
Here is a first pass to port the polymorphism sketch
It runs but there is room for improvement
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
# One array of Shapes, in ruby we don't need polymorphism to achieve that,
# this is only a 'first draft' of a JRubyArt port. The crucial thing it runs
# further it does demonstrate some useful things about inheritance in ruby.
require_relative 'circle'
require_relative 'square'
attr_reader :shps
def setup
sketch_title 'Polymorphism'
@shps = []
30.times do
if rand < 0.5
shps << Circle.new(320, 180, 32, color(rand(255), 100))
else
shps << Square.new(320, 180, 32)
end
end
end
def draw
background(255)
shps.each(&:jiggle)
shps.each { |shape| shape.change_color if shape.respond_to? :change_color }
shps.each(&:display)
end
def settings
size(480, 270)
end
Here is the parent Shape class
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
# class Shape does not require Processing::Proxy but can pass it on to
# the inheriting classes Square and Circle
class Shape
include Processing::Proxy
attr_reader :x, :y, :r
def initialize(x, y, r, *_args)
@x = x
@y = y
@r = r
end
def jiggle
@x += rand(-1..1.0)
@y += rand(-1..1.0)
end
# A generic shape does not really know how to be displayed.
# This will be overridden in the child classes.
def display
'not implemented'
end
end
Here is the Square class
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
require_relative 'shape'
# Square class can inherit Processing::Proxy methods from Shape
# Variables are inherited from the parent.
# We could also add variables unique to the Square class if we so desire
class Square < Shape
def initialize(x, y, r)
super
end
# Inherits jiggle from parent
# The square overrides its parent for display.
def display
rect_mode(CENTER)
fill(175)
stroke(0)
rect(x, y, r, r)
end
end
Here is the Circle class
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
require_relative 'shape'
# Circle class inherits Processing:Proxy module methods from Shape
# Inherits all instance variables from parent + adding one, This is one area
# we could improve in ruby by using hash args and a hook method, we might even
# get away without calling super (anticipating more shapes in the future).
class Circle < Shape
attr_reader :c, :x, :y, :r
def initialize(x, y, r, c)
super # Call the parent constructor
@c = c # Also deal with this new instance variable
end
# Call the parent jiggle, but do some more stuff too
def jiggle
super
# The Circle jiggles both size and location.
@r += rand(-1..1.0)
@r = constrain(r, 0, 100)
end
# The change_color function is unique to the Circle class.
def change_color
@c = color(rand(255))
end
def display
ellipse_mode(CENTER)
fill(c)
stroke(0)
ellipse(x, y, r, r)
end
end
Here is second go at porting the Dan Shiffman sketch
It is definetly getting better.
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
# One array of Shapes, in ruby we don't need polymorphism to achieve that,
# this is a 'second draft' of a JRubyArt port. Introducing the hook method,
# keyword args and the post_initialization hook for flexible inheritance.
require_relative 'circle'
require_relative 'square'
attr_reader :shps
def setup
sketch_title 'Polymorphism'
@shps = []
30.times do
if rand < 0.5
shps << Circle.new(x: 320, y: 180, r: 32, c: color(rand(255), 100))
else
shps << Square.new(x: 320, y: 180, r: 32)
end
end
end
def draw
background(255)
shps.each(&:jiggle)
shps.each { |shape| shape.change_color if shape.respond_to? :change_color }
shps.each(&:display)
end
def settings
size(480, 270)
end
The improved Square class, note no call to super is required (wasn’t previously either but might have been confusing if left out).
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
require_relative 'shape'
# Square class can inherit Processing::Proxy methods from Shape
# Variables are inherited from the parent.
# We could also add variables unique to the Square class if we so desire
class Square < Shape
# Inherits constructor from parent
# Inherits jiggle from parent
# The square overrides its parent for display.
def display
rect_mode(CENTER)
fill(175)
stroke(0)
rect(x, y, r, r)
end
end
The improved Circle class, now in color, to match improved ruby.
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
require_relative 'shape'
# Circle class inherits Processing:Proxy module methods from Shape
# Inherits all instance variables from parent + adding one using the
# post_initialize hook method
class Circle < Shape
COLORS = %w{#ff0000 #ffff00 #3333ff #33cc33}
attr_reader :c, :x, :y, :r
def post_initialize(args)
@c = args[:c] # initialize color using post_initialize hook in Shape
end
# Call the parent jiggle, but do some more stuff too
def jiggle
super
# The Circle jiggles both size and location.
@r += rand(-1..1.0)
@r = constrain(r, 0, 100)
end
# The change_color function is unique to the Circle class.
def change_color
@c = color(COLORS.sample)
end
def display
ellipse_mode(CENTER)
fill(c)
stroke(0)
ellipse(x, y, r, r)
end
end
Finally we have a suitably refactored port the Dan Shiffman sketch
Here the inheriting classes use a common interface (thus reducing coupling)
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
# One array of Shapes, in ruby we don't need polymorphism to achieve that,
# this is a JRubyArt port. Introducing the hook method,
# keyword args and the post_initialization hook for flexible inheritance.
# Important change we only really need to know 'run' the method initialization
# of color is now really irrelevant. Retained to illustrate how to use a hook.
require_relative 'circle'
require_relative 'square'
attr_reader :shps
def setup
sketch_title 'Polymorphism'
@shps = []
30.times do
if rand < 0.5
shps << Circle.new(x: 320, y: 180, r: 32, c: color(rand(255), 100))
else
shps << Square.new(x: 320, y: 180, r: 32)
end
end
end
def draw
background(255)
shps.each(&:run)
end
def settings
size(480, 270)
end
The abstract Shape class
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
# class Shape does not require Processing::Proxy but can pass it on to
# the inheriting classes Square and Circle (NB: change to using run)
class Shape
include Processing::Proxy
attr_reader :x, :y, :r
def initialize(x:, y:, r:, **opts)
@x = x
@y = y
@r = r
post_initialize(opts)
end
def jiggle
@x += rand(-1..1.0)
@y += rand(-1..1.0)
end
def post_initialize(_args)
'not implemented'
end
# A generic shape does not really know how to be displayed.
# This will be overridden in the child classes.
def display
'not implemented'
end
def run
'not implemented'
end
end
The Square class
# Learning Processing
# Daniel Shiffman
# https://www.learningprocessing.com
# Example 22-2: Polymorphism
require_relative 'shape'
# Square class can inherit Processing::Proxy methods from Shape
# Variables are inherited from the parent.
# We could also add variables unique to the Square class if we so desire
# NB: run could be the only visible method
class Square < Shape
# Inherits constructor from parent
# Inherits jiggle from parent
# The square overrides its parent for display.
def display
rect_mode(CENTER)
fill(175)
stroke(0)
rect(x, y, r, r)
end
def run
jiggle
display
end
end
The Circle class now has transaparent colors
# post_initialize hook method. Use of change_color makes initilization of @c
# irrelevant, but without example is spoiled.
class Circle < Shape
COLORS = %w{#ff0000 #ffff00 #3333ff #33cc33}
attr_reader :c, :x, :y, :r
def post_initialize(args)
@c = args[:c] # Also deal with this new instance variable
end
# Call the parent jiggle, but do some more stuff too
def jiggle
super
# The Circle jiggles both size and location.
@r += rand(-1..1.0)
@r = constrain(r, 0, 100)
end
# The change_color function is unique to the Circle class.
def change_color
@c = color(COLORS.sample) - (100<<24)
end
def display
ellipse_mode(CENTER)
fill(c)
stroke(0)
ellipse(x, y, r, r)
end
def run
jiggle
change_color
display
end
end
I have also ported The Nature of Code to JRubyArt.