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

Tuesday, 12 July 2011

Rod Hilbert in Ruby-Processing (using homebaked libraries)

Here is sketch where I have created two small libraries, one in java that I have archived as jar, and one in ruby that is library just because I put in the library folder. The java library provides fast lookup tables for sin and cos, and the ruby library provides some code that is re-used a lot when I create lystems in ruby-processing.  Se the vanilla processing animation on my other blog. For your convenience I have provided these libaries here as a gist (NB: raw view of gist is required google has done something to the formatted code), as well as the sketch code:-

/**
* Copyright (c) 2011 Martin Prout
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* http://creativecommons.org/licenses/LGPL/2.1/
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package lut;
/**
* A very restricted lookup table for fast sine & cosine computations. The table
* currently has a fixed precision of 1.0 degrees. Thus should be as accurate as
* Math.sin when using integer input. However with a float input, values are
* cast to integer, and there will be errors. Note the reduced lookup up table,
* is restricted to the first quadrant of sine. Conditional rules are used to
* map to other quadrants and cos. Based on ideas from:-
* http://en.wikipedia.org/wiki/Lookup_table
* One annoyance of java is the behaviour of % wrt negative values cf python for
* example. A kludge is required to return the complement of 360, which would
* not otherwise be required. Compile this and jar library as lut.jar
*/
public class LUT {
/**
* Lookup table for degree cosine/sine, has a fixed precision 1.0 degrees
* @author Martin Prout <martin_p@lineone.net>
*/
public static float[] sinLUT = new float[91];
/**
* Message to display on console processing ide
*/
public static final String message = "Sine/Cosine lookup tables initialized"
+ " with a fixed\nprecision of 1.0 degrees. NB: degree input. Use\n"
+ "LUT2 for greater precision (of ca. 0.25 degrees)\n";
/**
* Initialise sin table with values (first quadrant only)
*/
public static void initialize() {
for (int i = 0; i <= 90; i++) {
sinLUT[i] = (float) Math.sin(Math.toRadians(i));
}
System.out.print(message);
}
/**
* Look up sine for the passed angle in degrees.
*
* @param thet degree int
* @return sine value for theta
*/
public static float sin(int thet) {
while (thet < 0) {
thet += 360; // Needed because negative modulus plays badly in java
}
int theta = thet % 360;
int y = theta % 90;
float result = (theta < 90) ? sinLUT[y] : (theta < 180)
? sinLUT[90 - y] : (theta < 270)
? -sinLUT[y] : -sinLUT[90 - y];
return result;
}
/**
* Look up sin for the passed angle in degrees. NB lacks precision unless
* float is round number (needed to work with pen and turtle interface)
* Casting to int rather than rounding is deliberate, use LUT2 instead for
* greater precision with a decimal float input
* @param thet degree float
* @return sin value for theta
*/
public static float sin(float thet) {
return LUT.sin((int) thet);
}
/**
* Look up cos for the passed angle in degrees.
*
* @param thet degree int
* @return sine value for theta
*/
public static float cos(int thet) {
while (thet < 0) {
thet += 360; // Needed because negative modulus plays badly in java
}
int theta = thet % 360;
int y = theta % 90;
float result = (theta < 90) ? sinLUT[90 - y] : (theta < 180)
? -sinLUT[y] : (theta < 270)
? -sinLUT[90 - y] : sinLUT[y];
return result;
}
/**
* Look up cos for the passed angle in degrees. NB lacks precision unless
* float is round number (needed to work with pen and turtle interface)
* Casting to int rather than rounding is deliberate, use LUT2 instead for
* greater precision with a decimal float input
* @param thet degree float
* @return sine value for theta
*/
public static float cos(float thet) {
return LUT.cos((int) thet);
}
}
view raw lut.LUT.java hosted with ❤ by GitHub


#############################################################
# library/grammar/grammar.rb
# Non-stochastic grammar
# with unique premise/rules
############################################################
class Grammar
attr_accessor :axiom, :rules
def initialize axiom
@axiom = axiom
@rules = Hash.new
end
def add_rule premise, rule
rules.store(premise, rule)
end
##########################################
# replace each pre char with a unique rule
##########################################
def new_production production
production.gsub!(/./) { |c| (r = @rules[c]) ? r : c }
end
##########################################
# control the number of iterations
# default 0, returns the axiom
##########################################
def generate repeat = 0
prod = axiom
repeat.times do
prod = new_production prod
end
return prod
end
end
view raw grammar.rb hosted with ❤ by GitHub


class Rod_Hilbert < Processing::App
#full_screen # NB: All distances are relative to screen height
load_libraries 'grammar', 'lut'
import 'lut'
attr_reader :grammar, :production, :distance, :depth, :centre_adjust
# some lsystem constants
CENTER_ADJUST = [0, 0.5, 1.5, 3.5, 7.5]
XPOS = 0
YPOS = 1
ANGLE = 2
BEN = Math::PI/720 # use BEN to create a bent Hilbert
THETA = Math::PI/2 # + BEN
PHI = Math::PI/2 #- BEN
AXIOM = "A"
def setup()
size 600, 600, P3D
#render_mode P3D
LUT.initialize
@grammar = Grammar.new(AXIOM)
@grammar.add_rule "A", "B>F<CFC<F>D+F-D>F<1+CFC<F<B1^"
@grammar.add_rule "B", "A+F-CFB-F-D1->F>D-1>F-B1>FC-F-A1^"
@grammar.add_rule "C", "1>D-1>F-B>F<C-F-A1+FA+F-C<F<B-F-D1^"
@grammar.add_rule "D", "1>CFB>F<B1>FA+F-A1+FB>F<B1>FC1^"
@depth = 2
reset(depth)
camera(width/2.0, height/2.0, 600, 0, 0, 0, 0, -1, 0)
noStroke()
end
def reset(depth)
@distance = 380
create_grammar(depth)
end
def create_grammar(gen)
if (gen > 0)
@distance *= 1.0/((2**gen) - 1.0)
@production = grammar.generate gen
end
end
def render(production)
"""
Render evaluates the production string and calls box primitive
uses processing affine transforms (translate/rotate)
"""
lightSpecular(30, 30, 30)
ambient(192, 192, 192)
ambientLight(80, 80, 80)
directionalLight(0, 0, 0, 80, 80, 80)
specular(40, 40, 40)
shininess(0.3)
fill(192, 192, 192)
sphere_detail(10)
sphere(distance/7) # first sphere end cap
repeat = 1
production.scan(/./) do |ch|
case(ch)
when "F"
draw_rod(distance) # NB: translation done in draw_rod
when '+'
rotate_x(THETA * repeat)
repeat = 1
when '-'
rotate_x(-THETA * repeat)
repeat = 1
when '>'
rotate_y(THETA * repeat)
repeat = 1
when '<'
rotate_y(-THETA * repeat)
when '^'
rotateZ(PHI * repeat)
repeat = 1
when '1'
repeat = 2
when 'A', 'B', 'C', 'D'
else
puts("character '#{ch}' not in grammar")
end
end
end
def draw_rod(distance)
sides = 10
radius = distance/7
angle = 0
angle_increment = 360 / sides
translate(0, 0, -distance / 2.0)
begin_shape(QUAD_STRIP)
for i in 0...sides+1
normal(LUT.cos(angle), LUT.sin(angle), 0)
vertex(radius*LUT.cos(angle), radius*LUT.sin(angle), -distance/2)
vertex(radius*LUT.cos(angle), radius*LUT.sin(angle), distance/2)
angle += angle_increment
end
end_shape()
translate(0, 0, -distance/2)
sphere(radius)
end
def draw()
background(10, 10, 200)
lights()
push_matrix()
rotate_x(LUT.sin(frame_count))
rotate_y(LUT.cos(frame_count))
push_matrix()
translate( distance * CENTER_ADJUST[depth], -distance * CENTER_ADJUST[depth], distance * CENTER_ADJUST[depth])
render(production)
pop_matrix()
pop_matrix()
end
end
view raw rod_hilbert.rb hosted with ❤ by GitHub

Followers

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