# Ruby Processing

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

## Sunday, 12 January 2014

### Frame of Reference Example Sketch (Original by Ira Greenberg)

Here's another processing sketch that I've translated to ruby-processing, where I replace PVector with Vec3D from the ruby processing vecmath library. It also makes use of the ArcBall functionality built into the vecamth library.
```###############
# Frame of Reference example by Ira Greenberg
# https://github.com/irajgreenberg/ProcessingTips
# Translated to ruby-processing by Martin Prout January 2014
# Now use mouse drag for ArcBall manipulation, and  +/- keys for zoom
###############

FACE_COUNT = 50

def setup
size(800, 800, P3D)
@zoom = 1.0
camera(0, 0, (height/2.0) / tan(PI*30.0 / 180.0), 0, 0, 0, 0, -1, 0) # point camera at origin
# create an Arcball at centre that pretty much fills the screen
@arcball = ArcBall.new(0, 0, min(width - 20, height - 20) / 2.0)
@c = []
@p = []
FACE_COUNT.times do |i|

# calc some random triangles in 3 space
val = Vec3D.new(rand(-width/2 .. width/2), rand(-width/2 .. width/2), rand(-width/2 .. width/2))
v0 = Vec3D.new(rand(-val.x .. -val.x + 100), rand(-val.y .. -val.y + 100), rand(-val.z .. -val.z + 100))
v1 = Vec3D.new(rand(-val.x .. -val.x + 100), rand(-val.y .. -val.y + 100), rand(-val.z .. -val.z + 100))
v2 = Vec3D.new(rand(-val.x .. -val.x + 100), rand(-val.y .. -val.y + 100), rand(-val.z .. -val.z + 100))
p << Plane.new([v0, v1, v2])

# build some cute little cylinders
c << Cylinder.new(Vec3D.new(150, 5, 5), 12)

# Using each Triangle normal (N),
# One of the Triangle's edges as a tangent (T)
# Calculate a bi-normal (B) using the cross-product between each N and T
# Note caps represent constants in ruby so we used N = nn, T = tt and B = bb in the ruby code below

#
# A picture helps
# nice, sweet orthogonal axes

# N   B
# |  /
# | /
# |/____T

#
# N, T, B together give you a Frame of Reference (cute little local coordinate system), based on each triangle.
# You can then take the cylinder (or any vertices) and transform them using a 3 x 3 matrix to this coordinate system.
# (In the matrix each column is based on N, T and B respectivley.)
# The transform will handle any rotations and scaling, but not the translation,
# but we can add another dimenson to the matrix to hold the translation values.
# Here's what all this confusing description looks like:

#
# Matrix :                               Vector :
# |  N.x  T.x  B.x  translation.x  |      |  x  |
# |  N.y  T.y  B.y  translation.y  |      |  y  |
# |  N.z  T.z  B.z  translation.z  |      |  z  |
# |  0    0    0    1              |      |  1  |

# We add the extra row in the matrix and the 1 to each vector
# so the math works. We describe the Matrix as 4 rows by 4 columns
# and the vector now as a Matrix with 4 rows and 1 column.
# When you multiply matrices the inner numbers MUST match, so:
# [4 x 4] [4 x 1] is OK, but [4 x 4] [1 x 4] is NOT COOL.

# (Please note there is also row vector approach that you can use,
# Google about; it simply puts the vector on left side of matrix and treats
# it as a 1 row and 4 column matrix. However, you'll also need to shift
# the translation terms to the bottom of the matrix for the math to grock.)

# The Matrix multiplication looks like this (sorry it's a little tedious looking.)
# n.x * x + t.x *y + B.x * z + translation.x * 1  =  new transformed x
# n.y * x + t.y *y + B.y * z + translation.y * 1  =  new transformed y
# n.z * x + t.z *y + B.z * z + translation.z * 1  =  new transformed z
# 0 * x + 0 *y + 0 * z + 1 * 1   =   disregard this crap.
#

nn = p[i].n
tt = Vec3D.new(p[i].vecs.x - p[i].vecs.x, p[i].vecs.y - p[i].vecs.y, p[i].vecs.z - p[i].vecs.z)
nn.normalize!
tt.normalize!
bb = nn.cross(tt)
bb.normalize! # not really needed

# build matrix with frame and translation (to centroid of each triangle)
m4 = Mat4.new(nn, tt, bb, p[i].c)

# transform each cylinder to align with each triangle
c[i].vecs = m4.mult(c[i].vecs)
end
fill(187)
stroke(50, 20)
end

def draw
background(0)
lights
# update the arcball rotation
update
FACE_COUNT.times do |i|
p[i].display
c[i].display
end
end

def update
theta, x, y, z = arcball.update
rotate(theta, x, y, z)
end

def mouse_pressed
arcball.mouse_pressed(mouse_x, mouse_y)
end

def mouse_dragged
arcball.mouse_dragged(mouse_x, mouse_y)
end

def key_pressed
case key
when '+'
@zoom -= 0.1  # closer is bigger
when '-'
@zoom += 0.1
end
camera(0, 0, (height * zoom / 2.0) / tan(PI*30.0 / 180.0), 0, 0, 0, 0, -1, 0)
end

```

Here is the Plane class
```NORM_LEN = 225.0

class Plane
include Processing::Proxy

def initialize(vecs)
@vecs = vecs
init
end

def init
v1 = vecs.dup
v2 = vecs.dup
v1 -= vecs
v2 -= vecs

@c = Vec3D.new(
(vecs.x+vecs.x+vecs.x) / 3,
(vecs.y+vecs.y+vecs.y) / 3,
(vecs.z+vecs.z+vecs.z) / 3
)

@n = v1.cross(v2)
n.normalize!
end

def display
begin_shape(TRIANGLES)
vecs.each do |vec|
vertex(vec.x, vec.y, vec.z)
end
end_shape

#normal
stroke(200, 160, 30)
begin_shape(LINES)
vertex(c.x, c.y, c.z)
vertex(c.x + n.x * NORM_LEN, c.y + n.y * NORM_LEN, c.z + n.z * NORM_LEN)
end_shape

#binormal
stroke(160, 200, 30)
begin_shape(LINES)
vertex(c.x, c.y, c.z)
# tangent
v = vecs.dup
#v.set(vecs)
v -= vecs
v.normalize!
vertex(c.x + v.x * NORM_LEN, c.y + v.y * NORM_LEN, c.z + v.z * NORM_LEN)
end_shape

stroke(30, 200, 160)
begin_shape(LINES)
vertex(c.x, c.y, c.z)
b = v.cross(n)
vertex(c.x + b.x * NORM_LEN, c.y + b.y * NORM_LEN, c.z + b.z * NORM_LEN)
end_shape
stroke(0, 75)
end
end

```

Here is the Mat4 class
```# uber simple Homogeneous 4 x 4 matrix

class Mat4

def initialize(axisX, axisY, axisZ, trans)
@mat = [
[axisX.x, axisY.x, axisZ.x, trans.x],
[axisX.y, axisY.y, axisZ.y, trans.y],
[axisX.z, axisY.z, axisZ.z, trans.z],
[0, 0, 0,  1]
]
end

# The processing version changes the input 'array', here we return
# a new array with transformed values (which we then assign to the input)
# see line 91 Frame_of_Reference.rb

def mult(array)
temp = []
array.each do |arr|
xt = mat * arr.x + mat * arr.y + mat * arr.z + mat * 1
yt = mat * arr.x + mat * arr.y + mat * arr.z + mat * 1
zt = mat * arr.x + mat * arr.y + mat * arr.z + mat * 1
temp << Vec3D.new(xt, yt, zt)
end
return temp
end
end

```

Here is the Cylinder class
```class Cylinder
include Processing::Proxy
attr_accessor :vecs

def initialize(dim, detail)
@dim = dim
@detail = detail
init
end

def init
theta = 0.0
#    created around x-axis
#    y = Math.cos
#    z = Math.sin
veca = []
vecb = []
detail.times do
veca << Vec3D.new(0, Math.cos(theta)*dim.y, Math.sin(theta)*dim.z)
vecb << Vec3D.new(dim.x, Math.cos(theta)*dim.y, Math.sin(theta)*dim.z)
theta += Math::PI * 2/detail
end
@vecs = veca.concat(vecb)
end

def display
detail.times do |i|
if (i<detail-1)
vertex(vecs[i].x, vecs[i].y, vecs[i].z)
vertex(vecs[i+1].x, vecs[i+1].y, vecs[i+1].z)
vertex(vecs[detail+i+1].x, vecs[detail+i+1].y, vecs[detail+i+1].z)
vertex(vecs[detail+i].x, vecs[detail+i].y, vecs[detail+i].z)
else
vertex(vecs[i].x, vecs[i].y, vecs[i].z)
vertex(vecs.x, vecs.y, vecs.z)
vertex(vecs[detail].x, vecs[detail].y, vecs[detail].z)
vertex(vecs[detail+i].x, vecs[detail+i].y, vecs[detail+i].z)
end
end
end_shape
end
end

```

## Thursday, 9 January 2014

### Penrose tiling generator (after Shiffman)

Previously I have experimented with cfdg and lsystems to generate penrose tiling, this version by Dan Shiffman seems very similar to the cfdg version. Interestingly Dan ported this somewhat from python, and here we end with a ruby version. Dan unsurprisingly makes use of the PVector class to do the vector math here we use my Vec2D from the ruby-processing vecmath library, which is far more concise (designed to use arithmetic operators from the outset). Here is the sketch, somewhat drier than the original with choosable options via control panel (it is necessary to press "reset" to change seed/triangle type):-
```# Penrose Tile Generator
# Using a variant of the "ArrayList" recursion technique: http://natureofcode.com/book/chapter-8-fractals/chapter08_section4
# Penrose Algorithm from: http://preshing.com/20110831/penrose-tiling-explained
# Daniel Shiffman May 2013
# Translated (and refactored) to ruby-processing Jan 2014 by Martin Prout

attr_reader :tris, :s, :panel, :hide, :acute

def setup
size(1024, 576)
control_panel do |c|
c.title = "Tiler Control"
c.look_feel "Nimbus"
c.checkbox  :seed
c.checkbox  :acute
c.button    :generate
c.button    :reset!
@panel = c
end
@hide = false
init false # defaults to regular penrose
end

def draw
# only make control_panel visible once, or again when hide is false
unless hide
@hide = true
panel.setVisible(hide)
end
background(255)
translate(width/2, height/2)
tris.each do |t|
t.display
end
end

def generate
next_level = []
tris.each do |t|
more = t.subdivide
more.each do |m|
next_level << m
end
end
@tris = next_level
end

def reset!
Tiler.acute(acute)  # set the Tiler first
init @seed
java.lang.System.gc # but does it do any good?
end

def init alt_seed
@tris = []
10.times do |i|     # create 36 degree segments
a = Vec2D.new
b = Vec2D.from_angle((2 * i - 1) * PI / 10)
c = Vec2D.from_angle((2 * i + 1) * PI / 10)
b *= 370
c *= 370
if alt_seed
tile = (i % 2 == 0)? Tiler.tile(b, a, c) : Tiler.tile(c, a, b)
tris << tile
else
tile = (i % 2 == 0)? Tiler.tile(a, b, c) : Tiler.tile(a, c, b)
tris << tile
end
end
end
```

Here is the tile library:-
```module Tiler
@@acute = false

def self.acute(x)
@@acute = x
end

# setup the initial tiling with all red tiles
def self.tile(a, b, c)
tile = (@@acute)? ATile.new(0, a, b, c) : Tile.new(0, a, b, c)
end
end

class Tile
include Processing::Proxy
PHI = (1.0 + Math.sqrt(5)) / 2.0 # golden ratio
RED = [255, 0, 0]
BLUE = [0, 0, 255]
COLORS = [RED, BLUE]

def initialize(col,  a,  b,  c)
@col, @a, @b, @c = col, a, b, c
end

def display
no_stroke
fill(*COLORS[col])
triangle(a.x, a.y, b.x, b.y, c.x, c.y)
#fill(0,0,255)
#ellipse(a.x,a.y,4,4)
#ellipse(b.x,b.y,4,4)
#ellipse(c.x,c.y,4,4)
end

def subdivide
result = []
if (col == 0)
# Subdivide red triangle
p = b - a
p /= PHI
p += a
result << Tile.new(0, c, p, b)
result << Tile.new(1, p, c, a)
else
# Subdivide blue triangle
q = a - b
q /= PHI
q += b
r = c - b
r /= PHI
r += b
result << Tile.new(1, r, c, a)
result << Tile.new(1, q, r, b)
result << Tile.new(0, r, q, a)
end
return result
end
end

class ATile < Tile
def subdivide
result = []
if (col == 0)
# Subdivide red (half kite) triangle
q = b - a
q /= PHI
q += a
r = c - b
r /= PHI
r += b
result << ATile.new(1, r, q, b)
result << ATile.new(0, q, a, r)
result << ATile.new(0, c, a, r)
else
# Subdivide blue (half dart) triangle
p = a - c
p /= PHI
p += c
result << ATile.new(1, b, p, a)
result << ATile.new(0, p, c, b)
end
return result
end
end
```

Here is a screenshot, before I added second checkbox

## Wednesday, 8 January 2014

### More keyword argument experiments with JRubyArt

Here we create a factory module, that allows us to create a RubyStruct (entry order important) using a keyword hash (order not important).
```#
# Esfera
# by David Pena.
# Somewhat re-factored for ruby-processing
# by Martin Prout
# Distribucion aleatoria uniforme sobre la superficie de una esfera.
#

QUANTITY = 16000

# signature-specific aliases for overloaded methods
java_alias :fill_int, :fill, [Java::int]
java_alias :stroke_int, :stroke, [Java::int]
java_alias :stroke_float_float, :stroke, [Java::float, Java::float]

module HairFactory
Hair = Struct.new(:z, :phi, :len, :theta)
def self.hair(args)
Hair.new(
args[:z],
args[:phi],
args[:len],
args[:theta]
)
end
end

def setup
size(800, 600, P3D)
@rx = 0
@ry = 0
no_smooth
@orb = []
# lets define some ruby lambdas
asine = ->(zl, rl){Math.asin(zl / rl)}
rl = ->(a, b){rand(a .. b)}
rpi = -> {rand * Math::PI * 2}

# lets populate the orb with hairs
QUANTITY.times do
z = rnd.call(radius) # needed twice so need to define outside constructor
orb << HairFactory.hair(  # note by using keyword arguments their order is not important
z: z,
phi: rpi.call,
len: rl.call(1.15, 1.2),
)
end
noise_detail(3)
end

def draw
off = ->(a, b, c){a + ((noise(c * 0.0005, Math.sin(b)) - 0.5) * 0.3)}
offb = ->(a, b, c){a + ((noise(c * 0.0007, Math.sin(b) * 0.01) - 0.5) * 0.3)}
background(0)
translate(width/2,height/2)
rxp = ((mouse_x - (width/2))*0.005)
ryp = ((mouse_y - (height/2))*0.005)
@rx = (rx*0.9)+(rxp*0.1)
@ry = (ry*0.9)+(ryp*0.1)
rotate_y(rx)
rotate_x(ry)
fill_int 0
no_stroke
orb.each do |hair|
thetaff = off.call(hair.theta, hair.phi, millis())
costhetaff = Math.cos(thetaff)
coshairtheta = Math.cos(hair.theta)
phff = offb.call(hair.phi, hair.z, millis())
xa = radius * coshairtheta * Math.cos(hair.phi)
ya = radius * coshairtheta * Math.sin(hair.phi)
xo = radius * costhetaff * Math.cos(phff)
yo = radius * costhetaff * Math.sin(phff)
xb, yb, zb = xo * hair.len, yo * hair.len, zo * hair.len
stroke_weight(1)
begin_shape(LINES)
stroke_int(0)
vertex(xa, ya, za)
stroke_float_float(200, 150)
vertex(xb, yb, zb)
end_shape()
end
if (frame_count % 10 == 0)
puts(frame_rate)
end
end
```

## Tuesday, 7 January 2014

### JRubyArt (ruby-processing development) and processing-2.0

It is highly likely that by the time jruby-9000 gets released ruby-2.0 if not ruby-2.1 will be standard. For that reason it is interesting to look at least at ruby-2.0 if not ruby-2.1 to see if the new features would be useful in ruby-processing. The new keyword argument syntax in particular looks quite interesting and I have included it in a revised version of the boids library (rboids in JRubyArt).
```#####
# Original version
#####
def update(opts={}) # Just flutter, little boids ... just flutter away.
options = {
shuffled: true, # Shuffling keeps things flowing smooth.
cohesion: 100.0,
separation: 10.0,
alignment: 5.0,
goal: 20.0,
limit: 30.0
}
options.merge! opts
# .... use options[var] in method body
# ....
end

#####
#  Updated version
#  Featuring ruby 2.0 syntax
#####

def update(goal: 20.0, limit: 30.0, **opts) # Just flutter, little boids ... just flutter away.

shuffled = opts.fetch(:shuffled, true) # Shuffling keeps things flowing smooth.
cohesion = opts.fetch(:cohesion, 100.0)
separation = opts.fetch(:separation, 10.0)
alignment = opts.fetch(:alignment, 5.0)
# .... use variable directly in method body
# ....
end
```

Previously opts = {} was used as the argument, and default goal and limit needed to be defined in a separate options hash, that was merged.
It occurs to me it could possibly be useful more generally where java methods are heavily overloaded eg camera (and entry order is annoyingly important under java). I do not anticipate using it for background, fill etc however. NB: remember to run in ruby-2.0 mode (eg by setting compat.version=2.0 in .jrubyrc).

## Sunday, 5 January 2014

### Revisiting yaml load and save for jruby-2.0

In the previous post I inclube the "bubble" library which I re-use here in my re-factored load_and_save_yaml.rb sketch (JRubyArt the development branch of ruby-processing). Hereis the yaml:-
```---
bubbles:
- :x: 160
:y: 103
:diameter: 43.19838
:label: Happy
- :x: 372
:y: 137
:diameter: 52.42526
- :x: 273
:y: 235
:diameter: 61.14072
:label: Joyous
- :x: 121
:y: 179
:diameter: 44.758068
:label: Melancholy
```

Here is the JRubyArt sketch:-
```######################################
# Yet another examples of reading and
# writing to some form of markup,
# appropriately yaml.
# by Martin Prout after Dan Shiffman
# updated for ruby-2.0 (Struct to Hash)
# ###################################

def setup()
size(640, 360)
@bubble_data = BubbleData.new "bubbles"
end

def draw
background 255
bubble_data.display mouse_x, mouse_y
end

def mouse_pressed
# create a new bubble instance, where mouse was clicked
@bubble_data.create_new_bubble(mouse_x, mouse_y)
end

class BubbleData
include Enumerable

MAX_BUBBLE = 10

def initialize key
@key = key
@bubbles = []
end

def each &block
bubbles.each &block
end

def create_new_bubble x, y
self << Bubble.new(x, y, rand(40 .. 80), "new label")
save_data
end

def display x, y
self.each do |bubble|
bubble.display
bubble.rollover(x, y)
end
end

# @param path to yaml file

@path = path
data = yaml[key]
bubbles.clear
# iterate the bubble_data array, and create an array of bubbles
data.each do |point|
self << Bubble.new(
point[:x],
point[:y],
point[:diameter],
point[:label])
end
end

def << bubble
bubbles << bubble
bubbles.shift if bubbles.size > MAX_BUBBLE
end

private

def save_data
hash = { key => self.map{ |point| point.to_h} }
yaml = hash.to_yaml
# overwite existing 'data.yaml'
open("data/data.yml", 'w:UTF-8') {|f| f.write(yaml) }
end

end

```

### Revisiting json load and save for ruby-2.0

Since ruby-2.0 Struct to Hash is supported (to_h) so I thought I would give it a go with JRubyArt. The first thing you need to do is to set jruby to run in 2.0 mode, this is easily achieved by setting the compatibility version in your .jrubyrc:-
```compat.version=2.0
```
But there are other ways to do it!
The json:-
```{
"bubbles": [
{
"x": 160,
"y": 103,
"diameter": 43.19838,
"label": "Happy"
},
{
"x": 372,
"y": 137,
"diameter": 52.42526,
},
{
"x": 273,
"y": 235,
"diameter": 61.14072,
"label": "Joyous"
},
{
"x": 121,
"y": 179,
"diameter": 44.758068,
"label": "Melancholy"
}
]
}
```

The sketch:-
```# This example demonstrates how easily "sketch data" can be retrieved from a json file
# in ruby-processing. Note this sketch re-uses the Bubble class from the bubble library.
# The BubbleData class, can load, store and create instances of Bubble (and request them
# to display and/or show their label, when 'mouse over').
# @author Martin Prout, after Daniel Shiffmans version for processing, updated for ruby-2.0
#
require "json"

def setup()
size(640, 360)
# initialize bubble_data with 'key' and read data from 'file path'
@bubble_data = BubbleData.new "bubbles"
end

def draw
background 255
# draw the bubbles and display a bubbles label whilst mouse over
bubble_data.display mouse_x, mouse_y
end

def mouse_pressed
# create a new instance of bubble, where mouse was clicked
bubble_data.create_new_bubble(mouse_x, mouse_y)
end

class BubbleData
include Enumerable

MAX_BUBBLE = 10

# @param key String for top level hash

def initialize key
@key = key
@bubbles = []
end

def each &block
bubbles.each &block
end

def create_new_bubble x, y
self << Bubble.new(x, y, rand(40 .. 80), "new label")
save_data
end

def display x, y
self.each do |bubble|
bubble.display
bubble.rollover(x, y)
end
end

# @param path to json file

@path = path
source_string = open(path, "r"){ |file| file.read }
data = JSON.parse(source_string)[key]
bubbles.clear
# iterate the bubble_data array, and create an array of bubbles
data.each do |point|
self << Bubble.new(
point["x"],
point["y"],
point["diameter"],
point["label"])
end
end

def << bubble
bubbles << bubble
bubbles.shift if bubbles.size > MAX_BUBBLE
end

private

def save_data
hash = { key => self.map{ |point| point.to_h } }
json = JSON.pretty_generate(hash)      # generate pretty output
open(path, 'w') { |f| f.write(json) }
end
end
```

The bubble library:-
```# The bubble library, include BubbleStruct

class Bubble
include Processing::Proxy

# Create  the Bubble
def initialize(x, y, diameter, label)
@data = BubbleStruct.new(x, y, diameter, label)
@over = false
end

# Checking if mouse is over the Bubble
def rollover(px, py)
d = dist(px,py,data.x,data.y)
@over = (d < data.diameter/2)? true : false
end

# Display the Bubble
def display
stroke(0)
stroke_weight(2)
no_fill
ellipse(data.x, data.y, data.diameter, data.diameter)
if (over)
fill(0)
text_align(CENTER)
text(data.label, data.x, data.y + data.diameter/2 + 20)
end
end

def to_h
data.to_h # Struct to Hash since ruby-2.0
end

end

BubbleStruct = Struct.new(:x, :y, :diameter, :label)
```

## Saturday, 4 January 2014

### Watch mode working again with P3D sketches in JRubyArt

Since this commit to vanilla processing the watch mode is now working in P3D. This was the result of a pull request from Jonathan Feinberg (processing.py), who realised which resources were not being released from such sketches (it was probably even more important for processing.py). It was Jonathans pull request that alerted me to the fact that it would be worth retesting with the latest development version of processing (which pulls his requested fix). Anyway the upshot is this will be automatically fixed for Linux and Windows users when the next version of processing gets released (Mac users if sticking with version 2.0.3 will not be so lucky). There exists a big opportunity for any Mac user brave enough to test the development versions of both processing and JRubyArt (next version of ruby-processing) to test if it can also work for them.
Anyway following my preliminary testing of "watch mode" with JRubyARt I can make the following suggestions to make your sketches more watch friendly:-
1. Don't have no_loop in your sketch (it might not get updated)
2. If using constants use ||= to define them (that way you avoid complaint of already defined constant)
Anyway if you did not remember to do it before, you can (of course) do it in your first "edit" of the sketch.