Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Tuesday, 30 November 2010
Saturday, 30 October 2010
Revised Perlin Noise Sketch (to use pixel array)
A somewhat optimized sketch, makes use of processing pixel array functionality, takes a little time to "warm-up". Original used P3D mode, probably should have been P2D (apparently better than default for pixel array type sketches)....
##################################################
# amp_zoom_in.rb
# adapted from a sketch at http://www.biothing.org
##################################################
attr_reader :col, :a, :b, :c, :k, :amp, :time
def setup()
size(640, 480, P2D)
color_mode(RGB, 1.0)
frame_rate(4)
load_pixels()
reset
end
def draw()
@time += 0.1 #step ahead in time with each frame
#now go through each pixel in the window:
(0...width).each do |x|
(0...height).each do |y|
#create values with trig functions, noise, etc acting on x,y and time.
#experiment with how these values are modulating over time.
@a = dist(x, y, width/2, height/2)
@b= (cos(x/360.0 + time)).abs # frequency larger means less
@c = noise(y/180.0*cos(time/20.0)) # inteference, larger means less
@amp = k # amplitude
@col = color((cos(a/120.0 + b + c * 10.0).abs)) * amp
#combine values into a color somehow. again the possibilities are limitless
begin
pixels[y * width + x] = col
rescue
reset
end
end
end
update_pixels()
@k += 1
end
def reset
@k = 0
@time = 0
end
##################################################
# amp_zoom_in.rb
# adapted from a sketch at http://www.biothing.org
##################################################
attr_reader :col, :a, :b, :c, :k, :amp, :time
def setup()
size(640, 480, P2D)
color_mode(RGB, 1.0)
frame_rate(4)
load_pixels()
reset
end
def draw()
@time += 0.1 #step ahead in time with each frame
#now go through each pixel in the window:
(0...width).each do |x|
(0...height).each do |y|
#create values with trig functions, noise, etc acting on x,y and time.
#experiment with how these values are modulating over time.
@a = dist(x, y, width/2, height/2)
@b= (cos(x/360.0 + time)).abs # frequency larger means less
@c = noise(y/180.0*cos(time/20.0)) # inteference, larger means less
@amp = k # amplitude
@col = color((cos(a/120.0 + b + c * 10.0).abs)) * amp
#combine values into a color somehow. again the possibilities are limitless
begin
pixels[y * width + x] = col
rescue
reset
end
end
end
update_pixels()
@k += 1
end
def reset
@k = 0
@time = 0
end
Labels:
perlin noise,
pixel array,
ruby processing
Revisiting My Voronoi Application
I had the idea for sometime to optimize this application. As before the application uses Lee Byrons library which in turn depends on John Lloyds QuickHull3D library (both can be accessed at the Lee Byron link). One day I might re-write the application to use a 3D mesh library directly. The major optimization here is to use processings built in pixels array functionality. The app will work best if the aspect ratio of the image is the same as the frame size (1:1 is probably optimal?). I have introduced an auto-scale feature that seems to work...
Use the view raw feature (below) if you want to copy the code.
Use the view raw feature (below) if you want to copy the code.
Labels:
portrait application,
voronoi mesh
Thursday, 14 October 2010
Creating simple animations using the SunflowAPIAPI library and jruby
Here I explore the animation possibilities of the SunflowAPIAPI library (nothing on the scale of the one by amnon.owed).
It is very simple to use ruby syntax to create different views of a scene by changing the camera position, and use sunflow to render each image. Rather than modify each frame, I had the idea of using 20 incremental views, and crudely converting them to 200 frames by copying each view 10 times. I then used mencoder to stitch the frames together to produce a short movie. Here is the result:-
# hair.rb NB: run this script directly with jruby
require 'library/sunflow_api/library/sunflow_api.jar'
require 'library/sunflow/library/sunflow.jar'
require 'library/sunflow/library/janino.jar'
require 'library/sunflow/library/commons-compiler.jar'
class BasicHair
API = Java::Com::briansteen::SunflowAPIAPI
SMath = Java::Org::sunflow::math
JColor = java.awt.Color
attr_reader :width, :height, :n_particles, :sunflow
def initialize width = 640, height = 480, cam_x = 0
@width = width
@height = height
@n_particles = 20
# create a new API instance
@sunflow = API.new
# set width and height
@sunflow.set_width(width)
@sunflow.set_height(height)
# set background color
@sunflow.set_background(1, 1, 1)
# set camera
@sunflow.set_camera_position(cam_x, 7, 5)
@sunflow.set_camera_target(2, 0.5, 0)
@sunflow.set_thinlens_camera("thinLensCamera", 50, width/height)
# set basic light
@sunflow.set_point_light("myPointLight", SMath::Point3.new(0, 5, 5),
JColor.new(255, 255, 255))
@sunflow.set_directional_light("myDirectionalLight", SMath::Point3.new(-2, 3, 0),
SMath::Vector3.new(0, 0, 0), 3, JColor.new(1, 1, 1))
# @sunflow.setSphereLight("mySphereLight", SMath::Point3.new(0, 30, -5),
# JColor.new(0, 0, 255), 32, 10)
# draw a ground plane
@sunflow.draw_plane("ground", SMath::Point3.new(0, 0, 0), SMath::Vector3.new(0, 1, 0))
# coordinates array
@sunflow.draw_box("boxname", 0, 0, 0, 1)
end
def create_scene
hair_widths = [0.025]
# create particle coordinates
350.times do |j|
# particle start position
particle_x = Math.cos(j * 0.5) * j * 0.0015
particle_y = 0
particle_z = Math.sin(j * 0.5) * j * 0.0015
hair_coordinates = Array.new(n_particles * 3)
array_index = -1
n_particles.times do |i|
particle_x += 0.1 + Math.cos(i * 0.15 + j * 0.05) * 0.13
particle_y -= Math.sin(particle_z * 0.01 + j * 0.05) * 0.125 +
Math.cos(i * 0.5 + particle_y) * 0.125
particle_z += Math.sin(i) * 0.25 + particle_y * 0.01
hair_coordinates[array_index += 1] = particle_x
hair_coordinates[array_index += 1] = particle_y
hair_coordinates[array_index += 1] = particle_z
end
# set ambient occlusion shader
@sunflow.setAmbientOcclusionShader("myAmbientOcclusionShader#{j}", JColor.new(55, 55, 55),
JColor.new(0, 0, 0), 16, 1)
# set glass shader
# @sunflow.setGlassShader("myGlassShader", JColor.new(1, 1, 1), 2.5, 3, JColor.new(1, 1, 1))
# set shiny-diffuse shader
# @sunflow.setShinyDiffuseShader("myShinyShader", JColor.new(55, 55, 55), 0.8)
# draw object
@sunflow.draw_hair("hair#{j}", n_particles - 2, hair_coordinates.to_java(:float),
hair_widths.to_java(:float))
end
end
def render_scene filename = nil
sunflow.setIrradianceCacheGIEngine(32, 0.4, 1, 15, nil)
# render
sunflow.render() unless filename
if filename
begin # test for dodgy filename/path
file = File.new(filename, "w")
file.close
sunflow.render(filename) # save as png image
rescue
puts "Warning #{filename} is not writable"
end
end
end
end
20.times do |i|
hair = BasicHair.new 640, 480, i # preferred render size for vimeo
hair.create_scene
hair.render_scene "/home/tux/ruby_sunflow/frame-#{i}.png" # default is to render in sunflow frame ie not to file
end
It is very simple to use ruby syntax to create different views of a scene by changing the camera position, and use sunflow to render each image. Rather than modify each frame, I had the idea of using 20 incremental views, and crudely converting them to 200 frames by copying each view 10 times. I then used mencoder to stitch the frames together to produce a short movie. Here is the result:-
Changing the camera Sunflow Hair from monkstone on Vimeo.
# hair.rb NB: run this script directly with jruby
require 'library/sunflow_api/library/sunflow_api.jar'
require 'library/sunflow/library/sunflow.jar'
require 'library/sunflow/library/janino.jar'
require 'library/sunflow/library/commons-compiler.jar'
class BasicHair
API = Java::Com::briansteen::SunflowAPIAPI
SMath = Java::Org::sunflow::math
JColor = java.awt.Color
attr_reader :width, :height, :n_particles, :sunflow
def initialize width = 640, height = 480, cam_x = 0
@width = width
@height = height
@n_particles = 20
# create a new API instance
@sunflow = API.new
# set width and height
@sunflow.set_width(width)
@sunflow.set_height(height)
# set background color
@sunflow.set_background(1, 1, 1)
# set camera
@sunflow.set_camera_position(cam_x, 7, 5)
@sunflow.set_camera_target(2, 0.5, 0)
@sunflow.set_thinlens_camera("thinLensCamera", 50, width/height)
# set basic light
@sunflow.set_point_light("myPointLight", SMath::Point3.new(0, 5, 5),
JColor.new(255, 255, 255))
@sunflow.set_directional_light("myDirectionalLight", SMath::Point3.new(-2, 3, 0),
SMath::Vector3.new(0, 0, 0), 3, JColor.new(1, 1, 1))
# @sunflow.setSphereLight("mySphereLight", SMath::Point3.new(0, 30, -5),
# JColor.new(0, 0, 255), 32, 10)
# draw a ground plane
@sunflow.draw_plane("ground", SMath::Point3.new(0, 0, 0), SMath::Vector3.new(0, 1, 0))
# coordinates array
@sunflow.draw_box("boxname", 0, 0, 0, 1)
end
def create_scene
hair_widths = [0.025]
# create particle coordinates
350.times do |j|
# particle start position
particle_x = Math.cos(j * 0.5) * j * 0.0015
particle_y = 0
particle_z = Math.sin(j * 0.5) * j * 0.0015
hair_coordinates = Array.new(n_particles * 3)
array_index = -1
n_particles.times do |i|
particle_x += 0.1 + Math.cos(i * 0.15 + j * 0.05) * 0.13
particle_y -= Math.sin(particle_z * 0.01 + j * 0.05) * 0.125 +
Math.cos(i * 0.5 + particle_y) * 0.125
particle_z += Math.sin(i) * 0.25 + particle_y * 0.01
hair_coordinates[array_index += 1] = particle_x
hair_coordinates[array_index += 1] = particle_y
hair_coordinates[array_index += 1] = particle_z
end
# set ambient occlusion shader
@sunflow.setAmbientOcclusionShader("myAmbientOcclusionShader#{j}", JColor.new(55, 55, 55),
JColor.new(0, 0, 0), 16, 1)
# set glass shader
# @sunflow.setGlassShader("myGlassShader", JColor.new(1, 1, 1), 2.5, 3, JColor.new(1, 1, 1))
# set shiny-diffuse shader
# @sunflow.setShinyDiffuseShader("myShinyShader", JColor.new(55, 55, 55), 0.8)
# draw object
@sunflow.draw_hair("hair#{j}", n_particles - 2, hair_coordinates.to_java(:float),
hair_widths.to_java(:float))
end
end
def render_scene filename = nil
sunflow.setIrradianceCacheGIEngine(32, 0.4, 1, 15, nil)
# render
sunflow.render() unless filename
if filename
begin # test for dodgy filename/path
file = File.new(filename, "w")
file.close
sunflow.render(filename) # save as png image
rescue
puts "Warning #{filename} is not writable"
end
end
end
end
20.times do |i|
hair = BasicHair.new 640, 480, i # preferred render size for vimeo
hair.create_scene
hair.render_scene "/home/tux/ruby_sunflow/frame-#{i}.png" # default is to render in sunflow frame ie not to file
end
Labels:
animation,
jruby,
sunflow raytracing
Sunday, 3 October 2010
Performance tune-ups (or not) for jruby sunflow raytracing
When I realised that sunflow is multi-threaded I thought there may be some performance tune-ups possible.
These are the steps I tried:-
I'm not sure what effect if any updating the janino compiler has but to my mind it makes the application a bit more future-proof (there is the option to upgrade from java 1.4 syntax see janino web site). The windows 0.073 version of sunflow is claimed to be faster on the basis of a native compiler, I doubt it.
Using the server option is a good thing, regular java uses it by default, I'm not sure about jruby. Because --server is a proxy for -J-server, suggest to me that you may need this option since it has been made easier to use?
Using the --fast option ensures pre-compilation (amongst other speed-ups) which must be good thing here?
Allocating increased memory is the only thing the sunflow.sh script does. Running the sunflow.jar directly (java -jar sunflow.jar) requests > 800 mb heap space to run.
The thread pooling is used to prevent wasteful ruby green threads being spawned. Both cores on my dual core linux box were kept running flat out according to my conky readout.
Now the difference between none of these measures, and running with the speed-ups seemed to be noticeable, so I thought I would measure it?
Conclusion
Now I've done a bit of measurement I'm not so sure perhaps it was the janino compiler change after all?
The --server command certainly makes no difference for me, default is --server.
Well it turns out it is all in the imagination when I measured the different compilers with or without any "performance" options performance was much the same...
However I was very impressed with the new StructureSynth built in ray tracing facility, which has apparently been been built with efficiency in mind, the default setting is for 4 processors (2 real, 2 virtual). Apparently povray lags behind in that regard?
These are the steps I tried:-
- I've updated my sunflow to use the latest janino compiler, only a small amount of re-factoring was required essentially deprecating two types of error and revising one because it has been re-factored to the commons-compiler.
- I'm using the --server option
- I'm using the --fast option
- I've increased the heap space -J-Xmx1024m
- I've enabled thread pooling -J-Djruby.thread.pooling=true
I'm not sure what effect if any updating the janino compiler has but to my mind it makes the application a bit more future-proof (there is the option to upgrade from java 1.4 syntax see janino web site). The windows 0.073 version of sunflow is claimed to be faster on the basis of a native compiler, I doubt it.
Using the server option is a good thing, regular java uses it by default, I'm not sure about jruby. Because --server is a proxy for -J-server, suggest to me that you may need this option since it has been made easier to use?
Using the --fast option ensures pre-compilation (amongst other speed-ups) which must be good thing here?
Allocating increased memory is the only thing the sunflow.sh script does. Running the sunflow.jar directly (java -jar sunflow.jar) requests > 800 mb heap space to run.
The thread pooling is used to prevent wasteful ruby green threads being spawned. Both cores on my dual core linux box were kept running flat out according to my conky readout.
Now the difference between none of these measures, and running with the speed-ups seemed to be noticeable, so I thought I would measure it?
Conclusion
Now I've done a bit of measurement I'm not so sure perhaps it was the janino compiler change after all?
The --server command certainly makes no difference for me, default is --server.
Well it turns out it is all in the imagination when I measured the different compilers with or without any "performance" options performance was much the same...
However I was very impressed with the new StructureSynth built in ray tracing facility, which has apparently been been built with efficiency in mind, the default setting is for 4 processors (2 real, 2 virtual). Apparently povray lags behind in that regard?
Labels:
jruby,
sunflow raytracing,
tune ups
Friday, 1 October 2010
SunflowAPIAPI test example using jruby
See previous post for how to setup java libraries. If you are mad like me, and have updated sunflow to use the latest janino compiler you should require the commons-compiler.jar as well.
# sunflow_test.rb NB: run this script directly with jruby
require 'library/sunflow_api/library/sunflow_api.jar'
require 'library/sunflow/library/sunflow.jar'
require 'library/sunflow/library/janino.jar'
class SunflowTest
API = Java::Com::briansteen::SunflowAPIAPI
SMath = Java::Org::sunflow::math
attr_reader :sunflow
def initialize width = 500, height = 500
@sunflow = API.new
sunflow.set_width(width)
sunflow.set_height(height)
sunflow.set_camera_position(0, 2.5, -5)
sunflow.set_camera_target(0, 0, 0)
sunflow.set_thinlens_camera("thinLensCamera", 50, width/height)
end
def create_scene
sunflow.draw_plane("ground", SMath::Point3.new(0,-3.5,0), SMath::Vector3.new(0, 1, 0))
sunflow.draw_sphere_flake("mySphereFlake", 20, SMath::Vector3.new(0, 1, 0), 1)
end
def render_scene filename = nil
sunflow.setPathTracingGIEngine 6
# render
sunflow.render unless filename
if filename
begin # test for dodgy filename/path
file = File.new(filename, "w")
file.close
sunflow.render(filename) # save as png image
rescue
puts "Warning #{filename} is not writable"
end
end
end
end
test = SunflowTest.new
test.create_scene
test.render_scene
# sunflow_test.rb NB: run this script directly with jruby
require 'library/sunflow_api/library/sunflow_api.jar'
require 'library/sunflow/library/sunflow.jar'
require 'library/sunflow/library/janino.jar'
class SunflowTest
API = Java::Com::briansteen::SunflowAPIAPI
SMath = Java::Org::sunflow::math
attr_reader :sunflow
def initialize width = 500, height = 500
@sunflow = API.new
sunflow.set_width(width)
sunflow.set_height(height)
sunflow.set_camera_position(0, 2.5, -5)
sunflow.set_camera_target(0, 0, 0)
sunflow.set_thinlens_camera("thinLensCamera", 50, width/height)
end
def create_scene
sunflow.draw_plane("ground", SMath::Point3.new(0,-3.5,0), SMath::Vector3.new(0, 1, 0))
sunflow.draw_sphere_flake("mySphereFlake", 20, SMath::Vector3.new(0, 1, 0), 1)
end
def render_scene filename = nil
sunflow.setPathTracingGIEngine 6
# render
sunflow.render unless filename
if filename
begin # test for dodgy filename/path
file = File.new(filename, "w")
file.close
sunflow.render(filename) # save as png image
rescue
puts "Warning #{filename} is not writable"
end
end
end
end
test = SunflowTest.new
test.create_scene
test.render_scene
Labels:
jruby,
sunflowAPIAPI
Thursday, 30 September 2010
Basic Hair Example SunflowAPIAPI just using jruby
It occurred to me that all I was getting from using ruby-processing in my previous post was the ruby processing interface to the libraries. So here is the straightforward jruby version....
Screen output very much as before (smaller) hence not included there. For your convenience I present an embedded gist (click on view raw to copy).
See previous post for how to setup java libraries.
Example translated from java version (by Christopher Warnow?) included in the subversion checkout of the sunflowAPIAPI.
Screen output very much as before (smaller) hence not included there. For your convenience I present an embedded gist (click on view raw to copy).
See previous post for how to setup java libraries.
Example translated from java version (by Christopher Warnow?) included in the subversion checkout of the sunflowAPIAPI.
Labels:
jruby,
sunflowAPIAPI
Basic Hair Example SunflowAPIAPI in ruby processing
See previous post for how to setup java libraries.
# basic_hair.rb run with 'rp5 run script.rb'
class BasicHair < Processing::App
load_libraries :sunflow_api, :sunflow
include_package 'com.briansteen'
include_package 'org.sunflow.math'
include_package 'java.awt'
API = Java::Com::briansteen::SunflowAPIAPI
SMath = Java::Org::sunflow::math
JColor = java.awt.Color
WIDTH = 640
HEIGHT = 480
P_AMOUNT = 20
def setup
# create a new API instance
@sunflow = API.new
# set width and height
@sunflow.set_width(WIDTH)
@sunflow.set_height(HEIGHT)
# set background color
@sunflow.set_background(1, 1, 1)
# set camera
@sunflow.set_camera_position(0, 7, 5)
@sunflow.set_camera_target(2, 0.5, 0)
@sunflow.set_thinlens_camera("thinLensCamera", 50, WIDTH/HEIGHT)
# set basic light
@sunflow.set_point_light("myPointLight", SMath::Point3.new(0, 5, 5),
JColor.new(255, 255, 255))
@sunflow.set_directional_light("myDirectionalLight", SMath::Point3.new(-2, 3, 0),
SMath::Vector3.new(0, 0, 0), 3, JColor.new(1, 1, 1))
# @sunflow.setSphereLight("mySphereLight", SMath::Point3.new(0, 30, -5),
# JColor.new(0, 0, 255), 32, 10)
# draw a ground plane
@sunflow.draw_plane("ground", SMath::Point3.new(0, 0, 0), SMath::Vector3.new(0, 1, 0))
# coordinates array
hair_widths = [0.025]
@sunflow.draw_box("boxname", 0, 0, 0, 1)
# create particle coordinates
350.times do |j|
# particle start position
particle_x = Math.cos(j*0.5)*j*0.0015
particle_y = 0
particle_z = Math.sin(j*0.5)*j*0.0015
hair_coordinates = Array.new(P_AMOUNT*3)
array_index = -1
P_AMOUNT.times do |i|
particle_x += 0.1 + Math.cos(i * 0.15 + j*0.05) * 0.13
particle_y -= Math.sin(particle_z*0.01 + j*0.05)*0.125 +
Math.cos(i*0.5 + particle_y)*0.125
particle_z += Math.sin(i)*0.25 + particle_y*0.01
hair_coordinates[array_index += 1] = particle_x
hair_coordinates[array_index += 1] = particle_y
hair_coordinates[array_index += 1] = particle_z
end
# set ambient occlusion shader
@sunflow.setAmbientOcclusionShader("myAmbientOcclusionShader#{j}", JColor.new(55,55,55),
JColor.new(0,0,0), 16, 1)
# set glass shader
# @sunflow.setGlassShader("myGlassShader", JColor.new(1,1,1), 2.5, 3, JColor.new(1,1,1))
# set shiny-diffuse shader
# @sunflow.setShinyDiffuseShader("myShinyShader", JColor.new(55,55,55), 0.8)
# draw object
@sunflow.drawHair("hair#{j}", P_AMOUNT - 2, hair_coordinates.to_java(:float),
hair_widths.to_java(:float))
end
@sunflow.setIrradianceCacheGIEngine(32, 0.4, 1, 15, nil)
# render
@sunflow.render() # display in sunflow window
# @sunflow.render("BasicHair.png") # save as png image
end
def draw
exit
end
end
# basic_hair.rb run with 'rp5 run script.rb'
class BasicHair < Processing::App
load_libraries :sunflow_api, :sunflow
include_package 'com.briansteen'
include_package 'org.sunflow.math'
include_package 'java.awt'
API = Java::Com::briansteen::SunflowAPIAPI
SMath = Java::Org::sunflow::math
JColor = java.awt.Color
WIDTH = 640
HEIGHT = 480
P_AMOUNT = 20
def setup
# create a new API instance
@sunflow = API.new
# set width and height
@sunflow.set_width(WIDTH)
@sunflow.set_height(HEIGHT)
# set background color
@sunflow.set_background(1, 1, 1)
# set camera
@sunflow.set_camera_position(0, 7, 5)
@sunflow.set_camera_target(2, 0.5, 0)
@sunflow.set_thinlens_camera("thinLensCamera", 50, WIDTH/HEIGHT)
# set basic light
@sunflow.set_point_light("myPointLight", SMath::Point3.new(0, 5, 5),
JColor.new(255, 255, 255))
@sunflow.set_directional_light("myDirectionalLight", SMath::Point3.new(-2, 3, 0),
SMath::Vector3.new(0, 0, 0), 3, JColor.new(1, 1, 1))
# @sunflow.setSphereLight("mySphereLight", SMath::Point3.new(0, 30, -5),
# JColor.new(0, 0, 255), 32, 10)
# draw a ground plane
@sunflow.draw_plane("ground", SMath::Point3.new(0, 0, 0), SMath::Vector3.new(0, 1, 0))
# coordinates array
hair_widths = [0.025]
@sunflow.draw_box("boxname", 0, 0, 0, 1)
# create particle coordinates
350.times do |j|
# particle start position
particle_x = Math.cos(j*0.5)*j*0.0015
particle_y = 0
particle_z = Math.sin(j*0.5)*j*0.0015
hair_coordinates = Array.new(P_AMOUNT*3)
array_index = -1
P_AMOUNT.times do |i|
particle_x += 0.1 + Math.cos(i * 0.15 + j*0.05) * 0.13
particle_y -= Math.sin(particle_z*0.01 + j*0.05)*0.125 +
Math.cos(i*0.5 + particle_y)*0.125
particle_z += Math.sin(i)*0.25 + particle_y*0.01
hair_coordinates[array_index += 1] = particle_x
hair_coordinates[array_index += 1] = particle_y
hair_coordinates[array_index += 1] = particle_z
end
# set ambient occlusion shader
@sunflow.setAmbientOcclusionShader("myAmbientOcclusionShader#{j}", JColor.new(55,55,55),
JColor.new(0,0,0), 16, 1)
# set glass shader
# @sunflow.setGlassShader("myGlassShader", JColor.new(1,1,1), 2.5, 3, JColor.new(1,1,1))
# set shiny-diffuse shader
# @sunflow.setShinyDiffuseShader("myShinyShader", JColor.new(55,55,55), 0.8)
# draw object
@sunflow.drawHair("hair#{j}", P_AMOUNT - 2, hair_coordinates.to_java(:float),
hair_widths.to_java(:float))
end
@sunflow.setIrradianceCacheGIEngine(32, 0.4, 1, 15, nil)
# render
@sunflow.render() # display in sunflow window
# @sunflow.render("BasicHair.png") # save as png image
end
def draw
exit
end
end
Labels:
ruby processing,
sunflowAPIAPI
SunflowAPIAPI in ruby processing
# sunflow_test.rb
class SunflowTest < Processing::App
load_libraries :sunflow_api, :sunflow
include_package 'com.briansteen'
include_package "org.sunflow.math"
API = Java::Com::briansteen::SunflowAPIAPI
SMath = Java::Org::sunflow::math
WIDTH = 500
HEIGHT = 500
def setup
@sunflow = API.new
@sunflow.set_width(WIDTH)
@sunflow.set_height(HEIGHT)
@sunflow.set_camera_position(0, 2.5, -5)
@sunflow.set_camera_target(0, 0, 0)
@sunflow.set_thinlens_camera("thinLensCamera", 50, WIDTH/HEIGHT)
@sunflow.draw_plane("ground", SMath::Point3.new(0,-3.5,0), SMath::Vector3.new(0, 1, 0))
@sunflow.draw_sphere_flake("mySphereFlake", 20, SMath::Vector3.new(0, 1, 0), 1)
@sunflow.setPathTracingGIEngine 6
@sunflow.render "SunflowTestRender.png"
end
def draw
exit
end
end
SunflowTest.new :title => "Sunflow Test"
This example was copied from amnon, following his recent posting on the processing discussion boards.
Checkout the sunflowapapi source (created by Christopher Warnow?) as follows:-
svn checkout http://sunflowapiapi.googlecode.com/svn/trunk/ sunflowapiapi-read-only
Get the sunflow libraries here, NB must be version 0.07.3:-
http://sunflow.sourceforge.net
Or possible here for Windows users:-
http://www.polyquark.com/opensource
The usual library setup required compile and jar the sunflowapiapi code into sunflow_api.jar nest that as follows in a folder library, in a folder sunflow_api in the library folder (in the directory where the sketch you want to run is stored). Do similar for the sunflow.jar put the janino.jar in the same folder. The janino.jar provides a fast java compiler, version is 2.~ is supplied with the sunflow download, however with a few minor modifications sunflow can be updated to use the latest version 2.6.1 (basically include the commons-compiler.jar, and remove a couple of deprecated exception types from the code and update the CompileException to the commons compiler version org.codehaus.commons.compiler.CompileException, do this in an ide unless you are crazy).
Labels:
ruby processing,
sunflow,
sunflowAPIAPI
Sunday, 26 September 2010
A Staffordshire Knot Using Ruby Processing and A Context Free Library
A Staffordshire Knot, ruby-processing 3D context-free DSL from monkstone on Vimeo.
Using a slight variation of the previous post we get something similar to the Staffordshire Knot.
segment :rz => i, :ry => Math.sin(i*Math::PI/180)*0.2, :x => Math.sin(i*Math::PI/180)*1.5
Is the main change, although to produce the video, I was saving frames and set the size to 640*480 to match vimeo preferences. Was very jumpy when I saved frames as #####.png ...
I have since uploaded an improved version, this time using the default save_frame (hence tif format). Mencoder doesn't like this format so I batch converted the tif files to png as so:-
mogrify -format png -transparent blue *.tif
See my processing blog for the video recording details.
Saturday, 25 September 2010
An Infinity Loop? With My 3D Context Free Library
# infinity.rb
# An 'infinity loop?'
load_libraries :test_free
full_screen
attr_reader :xrot, :yrot, :zrot, :wheel, :col
def setup_the_spiral
@spiral = ContextFree.define do
rule :infinity do
360.times do |i|
split do
segment :rz => i, :ry => Math.sin(i*Math::PI/180)*0.2, :x => Math.cos(i*Math::PI/180)*3
rewind
end
end
end
rule :segment do
cube :alpha => 0.8
end
rule :segment do
cube :brightness => 1.0, :saturation => 1.0
end
end
end
def setup
render_mode P3D
@wheel = JWheelListener.new(-50, -20) # initialize listener with start_z and maximum values
self.add_mouse_wheel_listener(wheel) # register the mouse listener
smooth
@xrot = 0.01
@yrot = 0
@zrot = 0
@col = 0
setup_the_spiral
end
def draw
background 0
setup_lights col
specular col, 1, 1
emissive 0.05
shininess 10
smooth_rotation(3, 3.2)
smooth_color(6.0)
@spiral.render :infinity, :start_x => 0, :start_y => 0, :start_z => wheel.zoom, :size => height/350,
:stop_size => 0.2, :color => [0, 0.7, 0.7, 0.5], :rx => xrot, :ry => yrot, :rz => zrot
end
##
# Generate a gradient value that changes smoothly over time in the range [ 0, 1 ].
# The Math.sin() function is used to map the current time in milliseconds somewhere
# in the range [ 0, 1 ]. A 'speed' factor is specified by the parameters s1.
#
def smooth_gradient(s1)
mills = millis * 0.00003
gradient = 0.5 * Math.sin(mills * s1) + 0.5
end
##
# Rotate the current coordinate system.
# Uses smooth_gradient() to smoothly animate the rotation.
#
def smooth_rotation(s1, s2)
@yrot = 2.0 * Math::PI * smooth_gradient(s1)
@zrot = 2.0 * Math::PI * smooth_gradient(s2)
end
##
# Generate a 'hue' value which smoothly changes over time.
# The speed of change is controlled by the parameter s1.
#
def smooth_color(s1)
@col = smooth_gradient(s1)
end
def setup_lights(col)
directional_light(col, 0.8, 0.8, -0.5, 0.5, -1)
point_light(col, 0.8, 0.8, -0.5, 0.5, -1)
ambient_light(col, 0.8, 0.8, 1, 1, 1)
end
Labels:
3D context free DSL,
infinity,
loop,
ruby processing
Monday, 13 September 2010
Revised 3D Context Free DSL Sketch, with Mouse Wheel Zoom Utility
I demonstrate the new feature of mouse wheel zoom in this sketch. The logic for the smooth of color and rotation I copied from lazydogs classy rotating ball. PS: if you can't see the embedded code below chances are you are not using either Firefox or Google Chrome browser (doesn't always work for me with Opera, which I also find is awkward to use when copying code).
default.rb from monkstone on Vimeo.
Labels:
context free,
mouse wheel listener,
ruby processing
Sunday, 12 September 2010
A Mouse Wheel Listener in Ruby Processing
For my 3D context free application/library, I would like to be able to zoom using the mouse wheel, I had vaguely thought about using peasycam or proscene to provide that, and other functionality. In the first instance I wanted to create it myself. After a bit of research, this the sort of implementation I would do using the java classes/interfaces in ruby:-
# mouse_listener.rb
class JWheelListener
include java.awt.event.MouseWheelListener
attr_reader :zoom
def initialize(zoom)
@zoom = zoom
end
def mouse_wheel_moved(e)
@zoom += e.get_wheel_rotation * 10
end
end
class Mouse < Processing::App
attr_reader :wheel
def setup
size(1000, 1000)
@wheel = JWheelListener.new(10)
self.add_mouse_wheel_listener(@wheel)
end
def draw
background 0
fill 255, 0, 0
ellipse(width/2, height/2, wheel.zoom, wheel.zoom)
end
end
Mouse.new :title => "Mouse Wheel Listener"
# mouse_listener.rb
class JWheelListener
include java.awt.event.MouseWheelListener
attr_reader :zoom
def initialize(zoom)
@zoom = zoom
end
def mouse_wheel_moved(e)
@zoom += e.get_wheel_rotation * 10
end
end
class Mouse < Processing::App
attr_reader :wheel
def setup
size(1000, 1000)
@wheel = JWheelListener.new(10)
self.add_mouse_wheel_listener(@wheel)
end
def draw
background 0
fill 255, 0, 0
ellipse(width/2, height/2, wheel.zoom, wheel.zoom)
end
end
Mouse.new :title => "Mouse Wheel Listener"
Labels:
mouse_wheel_listener,
ruby processing,
zoom
Saturday, 11 September 2010
3D Context Free DSL Screen Saver?
Here is an animated version of the default.es from StructureSynth in ruby-processing. Mac or windows users might want to experiment with OPENGL rendering, as this sketch exposes errors in processing P3D, I see odd missing pixels from time to time. If you don't like the holes remove the smooth, personally I prefer the holes to the glitchty non-smooth (it is a known issue with current versions of processing with both P2D and P3D renderers, something to do with tessellation algorithms). The test_free library is as in previous post.
Nest the library as follows:-
library/test_free/test_free.rb in the folder containing the sketch
For instructions for installing ruby-processing follow the first link in the blog header.
# full_screen.rb
# An animation of default.es
# the StructureSynth default script
load_libraries :test_free
full_screen
attr_reader :xrot, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do
rule :default do
split do
R1 :brightness => 1
rewind
R2 :brightness => 1
end
end
rule :R1 do
sphere :brightness => 1
R1 :size => 0.99, :x => 0.25, :rz => 6, :ry => 6
end
rule :R2 do
sphere :brightness => 1
R2 :size => 0.99, :x => -0.25, :rz => 6, :ry => 6
end
end
end
def setup
render_mode P3D
smooth
@xrot = 0.01
@yrot = 0
@zrot = 0
setup_the_spiral
end
def draw
background 0
lights
smooth_rotation(6.7, 7.3)
@spiral.render :default, :start_x => 0, :start_y => 0, :start_z => -50, :size => height/350,
:stop_size => 0.2, :color => [0, 0.8, 0.8], :rx => xrot, :ry => yrot, :rz => zrot
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)
mills = millis * 0.00003
y = 0.5 * Math.sin(mills * s1) + 0.5
z = 0.5 * Math.sin(mills * s2) + 0.5
vec = [y, z]
end
##
# Rotate the current coordinate system.
# Uses smooth_vector() to smoothly animate the rotation.
#
def smooth_rotation(s1, s2)
r1 = smooth_vector(s1, s2)
@yrot = (2.0 * Math::PI * r1[0])
@zrot = (2.0 * Math::PI * r1[1])
end
Nest the library as follows:-
library/test_free/test_free.rb in the folder containing the sketch
For instructions for installing ruby-processing follow the first link in the blog header.
# full_screen.rb
# An animation of default.es
# the StructureSynth default script
load_libraries :test_free
full_screen
attr_reader :xrot, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do
rule :default do
split do
R1 :brightness => 1
rewind
R2 :brightness => 1
end
end
rule :R1 do
sphere :brightness => 1
R1 :size => 0.99, :x => 0.25, :rz => 6, :ry => 6
end
rule :R2 do
sphere :brightness => 1
R2 :size => 0.99, :x => -0.25, :rz => 6, :ry => 6
end
end
end
def setup
render_mode P3D
smooth
@xrot = 0.01
@yrot = 0
@zrot = 0
setup_the_spiral
end
def draw
background 0
lights
smooth_rotation(6.7, 7.3)
@spiral.render :default, :start_x => 0, :start_y => 0, :start_z => -50, :size => height/350,
:stop_size => 0.2, :color => [0, 0.8, 0.8], :rx => xrot, :ry => yrot, :rz => zrot
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)
mills = millis * 0.00003
y = 0.5 * Math.sin(mills * s1) + 0.5
z = 0.5 * Math.sin(mills * s2) + 0.5
vec = [y, z]
end
##
# Rotate the current coordinate system.
# Uses smooth_vector() to smoothly animate the rotation.
#
def smooth_rotation(s1, s2)
r1 = smooth_vector(s1, s2)
@yrot = (2.0 * Math::PI * r1[0])
@zrot = (2.0 * Math::PI * r1[1])
end
Labels:
3D,
context free DSL,
ruby processing,
screen saver
Thursday, 9 September 2010
Translating the default.es to ruby processing DSL
It was very constructive to see if I could translate the EisenScript to my ruby processing DSL, I immediately learnt something about the EisenScript (recursion seems to be implicit, something I had not understood before). What I learnt about my library, was I was losing some context by directly altering the attitude (the image expected from the EisenScript appeared briefly but quickly degraded). I have now re-factored the sketch to only adjust the initial angle, the x rotation seems to affect the amplitude of deformation from the plane of the spirals. The y and z rotation give control of the attitude, and need to be used in combination to fully explore the 3 dimensional shape, which is now somewhat like that produced by StrucureSynth.
Here is my revised 3d context free DSL (test_free.rb):-
# A Context-Free library for Ruby-Processing, inspired by
# contextfreeart.org and StructureSynth
# Built on Jeremy Ashkenas context free DSL script
module Processing
class ContextFree
include Processing::Proxy
attr_accessor :rules, :app, :xr, :yr, :zr
AVAILABLE_OPTIONS = [:x, :y, :z, :rx, :ry, :rz, :size, :color, :hue, :saturation, :brightness, :alpha]
HSB_ORDER = {:hue => 0, :saturation => 1, :brightness => 2, :alpha => 3}
# Define a context-free system. Use this method to create a ContextFree
# object. Call render() on it to make it draw.
def self.define(&block)
cf = ContextFree.new
cf.instance_eval &block
cf
end
# Initialize a bare ContextFree object with empty recursion stacks.
def initialize
@app = $app
@graphics = $app.g
@finished = false
@rules = {}
@rewind_stack = []
@matrix_stack = []
@xr = 0
@yr = 0
@zr = 0
end
# Create an accessor for the current value of every option. We use a values
# object so that all the state can be saved and restored as a unit.
AVAILABLE_OPTIONS.each do |option_name|
define_method option_name do
@values[option_name]
end
end
# Here's the first serious method: A Rule has an
# identifying name, a probability, and is associated with
# a block of code. These code blocks are saved, and indexed
# by name in a hash, to be run later, when needed.
# The method then dynamically defines a method of the same
# name here, in order to determine which rule to run.
def rule(rule_name, prob=1, &proc)
@rules[rule_name] ||= {:procs => [], :total => 0}
total = @rules[rule_name][:total]
@rules[rule_name][:procs] << [(total...(prob+total)), proc]
@rules[rule_name][:total] += prob
unless ContextFree.method_defined? rule_name
self.class.class_eval do
eval <<-METH
def #{rule_name}(options)
merge_options(@values, options)
pick = determine_rule(#{rule_name.inspect})
@finished = true if @values[:size] < @values[:stop_size]
unless @finished
get_ready_to_draw
pick[1].call(options)
end
end
METH
end
end
end
# Rule choice is random, based on the assigned probabilities.
def determine_rule(rule_name)
rule = @rules[rule_name]
chance = rand * rule[:total]
pick = @rules[rule_name][:procs].select {|the_proc| the_proc[0].include?(chance) }
return pick.flatten
end
# At each step of the way, any of the options may change, slightly.
# Many of them have different strategies for being merged.
def merge_options(old_ops, new_ops)
return unless new_ops
# Do size first
old_ops[:size] *= new_ops[:size] if new_ops[:size]
new_ops.each do |key, value|
case key
when :size
when :x, :y, :z
old_ops[key] = value * old_ops[:size]
when :rz, :ry, :rx
old_ops[key] = value * (Math::PI / 180.0)
when :hue, :saturation, :brightness, :alpha
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key]] *= value
old_ops[:color] = adjusted
when :width, :height
old_ops[key] *= value
when :color
old_ops[key] = value
else # Used a key that we don't know about or trying to set
merge_unknown_key(key, value, old_ops)
end
end
end
# Using an unknown key let's you set arbitrary values,
# to keep track of for your own ends.
def merge_unknown_key(key, value, old_ops)
key_s = key.to_s
if key_s.match(/^set/)
key_sym = key_s.sub('set_', '').to_sym
if key_s.match(/(brightness|hue|saturation|alpha)/)
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key_sym]] = value
old_ops[:color] = adjusted
else
old_ops[key_sym] = value
end
end
end
# Doing a 'split' saves the context, and proceeds from there,
# allowing you to rewind to where you split from at any moment.
def split(options=nil, &block)
save_context
merge_options(@values, options) if options
yield
restore_context
end
# Saving the context means the values plus the coordinate matrix.
def save_context
@rewind_stack.push @values.dup
@matrix_stack << @graphics.get_matrix
end
# Restore the values and the coordinate matrix as the recursion unwinds.
def restore_context
@values = @rewind_stack.pop
@graphics.set_matrix @matrix_stack.pop
end
# Rewinding goes back one step.
def rewind
@finished = false
restore_context
save_context
end
# Render the is method that kicks it all off, initializing the options
# and calling the first rule.
def render(rule_name, starting_values={})
@values = {:x => 0, :y => 0, :z => 0,
:rz => 0, :ry => 0, :rx => 0,
:size => 1, :width => 1, :height => 1,
:start_x => width/2, :start_y => height/2, :start_z => 0,
:color => [0.5, 0.5, 0.5, 1],
:stop_size => 1.5}
@values.merge!(starting_values)
@finished = false
@app.reset_matrix
@app.no_stroke
@app.color_mode HSB, 1.0
@app.translate @values[:start_x], @values[:start_y], @values[:start_z]
self.send(rule_name, {})
end
def rotate_x rt
@xr = rt
end
def rotate_y rt
@yr = rt
end
def rotate_z rt
@zr = rt
end
# Before actually drawing the next step, we need to move to the appropriate
# location.
def get_ready_to_draw
@app.translate(@values[:x], @values[:y], @values[:z])
@app.rotate_x(@values[:rx] + xr)
@app.rotate_y(@values[:ry] + yr)
@app.rotate_z(@values[:rz] + zr)
end
# Compute the rendering parameters for drawing a shape.
def get_shape_values(some_options)
old_ops = @values.dup
merge_options(old_ops, some_options) if some_options
@app.fill *old_ops[:color]
return old_ops[:size], old_ops
end
# Sphere, cube are the primitive drawing
def cube(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.translate(options[:x] * size, options[:y] * size , options[:z] * size)
@app.box(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an oredered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
def sphere(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.sphere_detail 10
@app.sphere(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an oredered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
end
end
My Translation of default.es to ruby processing
# default.rb
# virtually a direct translation of default.es
# the StructureSynth default script
load_libraries :test_free, :control_panel
attr_reader :amplitude, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do
rule :default do
split do
R1 :brightness => 1
rewind
R2 :brightness => 1
end
end
rule :R1 do
sphere :brightness => 1
R1 :size => 0.99, :x => 0.25, :rz => 6, :ry => 6
end
rule :R2 do
sphere :brightness => 1
R2 :size => 0.99, :x => -0.25, :rz => 6, :ry => 6
end
end
end
def configure_control # setup control panel gui
control_panel do |c|
c.title = "Amplitude+Attitude"
c.slider :amplitude, -0.025..0.015, 0.0
c.slider :yrot, -6.3..6.3, 0.005
c.slider :zrot, -6.3..6.3, 0.005
end
end
def setup
size 800, 800, P3D
configure_control
smooth
setup_the_spiral
end
def draw
background 0
lights
@spiral.render :default, :start_x => 0, :start_y => 0, :start_z => -40, :size => height/400,
:stop_size => 0.2, :color => [0, 0.8, 0.8], :rx => amplitude, :ry => yrot, :rz => zrot
end
Here is my revised 3d context free DSL (test_free.rb):-
# A Context-Free library for Ruby-Processing, inspired by
# contextfreeart.org and StructureSynth
# Built on Jeremy Ashkenas context free DSL script
module Processing
class ContextFree
include Processing::Proxy
attr_accessor :rules, :app, :xr, :yr, :zr
AVAILABLE_OPTIONS = [:x, :y, :z, :rx, :ry, :rz, :size, :color, :hue, :saturation, :brightness, :alpha]
HSB_ORDER = {:hue => 0, :saturation => 1, :brightness => 2, :alpha => 3}
# Define a context-free system. Use this method to create a ContextFree
# object. Call render() on it to make it draw.
def self.define(&block)
cf = ContextFree.new
cf.instance_eval &block
cf
end
# Initialize a bare ContextFree object with empty recursion stacks.
def initialize
@app = $app
@graphics = $app.g
@finished = false
@rules = {}
@rewind_stack = []
@matrix_stack = []
@xr = 0
@yr = 0
@zr = 0
end
# Create an accessor for the current value of every option. We use a values
# object so that all the state can be saved and restored as a unit.
AVAILABLE_OPTIONS.each do |option_name|
define_method option_name do
@values[option_name]
end
end
# Here's the first serious method: A Rule has an
# identifying name, a probability, and is associated with
# a block of code. These code blocks are saved, and indexed
# by name in a hash, to be run later, when needed.
# The method then dynamically defines a method of the same
# name here, in order to determine which rule to run.
def rule(rule_name, prob=1, &proc)
@rules[rule_name] ||= {:procs => [], :total => 0}
total = @rules[rule_name][:total]
@rules[rule_name][:procs] << [(total...(prob+total)), proc]
@rules[rule_name][:total] += prob
unless ContextFree.method_defined? rule_name
self.class.class_eval do
eval <<-METH
def #{rule_name}(options)
merge_options(@values, options)
pick = determine_rule(#{rule_name.inspect})
@finished = true if @values[:size] < @values[:stop_size]
unless @finished
get_ready_to_draw
pick[1].call(options)
end
end
METH
end
end
end
# Rule choice is random, based on the assigned probabilities.
def determine_rule(rule_name)
rule = @rules[rule_name]
chance = rand * rule[:total]
pick = @rules[rule_name][:procs].select {|the_proc| the_proc[0].include?(chance) }
return pick.flatten
end
# At each step of the way, any of the options may change, slightly.
# Many of them have different strategies for being merged.
def merge_options(old_ops, new_ops)
return unless new_ops
# Do size first
old_ops[:size] *= new_ops[:size] if new_ops[:size]
new_ops.each do |key, value|
case key
when :size
when :x, :y, :z
old_ops[key] = value * old_ops[:size]
when :rz, :ry, :rx
old_ops[key] = value * (Math::PI / 180.0)
when :hue, :saturation, :brightness, :alpha
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key]] *= value
old_ops[:color] = adjusted
when :width, :height
old_ops[key] *= value
when :color
old_ops[key] = value
else # Used a key that we don't know about or trying to set
merge_unknown_key(key, value, old_ops)
end
end
end
# Using an unknown key let's you set arbitrary values,
# to keep track of for your own ends.
def merge_unknown_key(key, value, old_ops)
key_s = key.to_s
if key_s.match(/^set/)
key_sym = key_s.sub('set_', '').to_sym
if key_s.match(/(brightness|hue|saturation|alpha)/)
adjusted = old_ops[:color].dup
adjusted[HSB_ORDER[key_sym]] = value
old_ops[:color] = adjusted
else
old_ops[key_sym] = value
end
end
end
# Doing a 'split' saves the context, and proceeds from there,
# allowing you to rewind to where you split from at any moment.
def split(options=nil, &block)
save_context
merge_options(@values, options) if options
yield
restore_context
end
# Saving the context means the values plus the coordinate matrix.
def save_context
@rewind_stack.push @values.dup
@matrix_stack << @graphics.get_matrix
end
# Restore the values and the coordinate matrix as the recursion unwinds.
def restore_context
@values = @rewind_stack.pop
@graphics.set_matrix @matrix_stack.pop
end
# Rewinding goes back one step.
def rewind
@finished = false
restore_context
save_context
end
# Render the is method that kicks it all off, initializing the options
# and calling the first rule.
def render(rule_name, starting_values={})
@values = {:x => 0, :y => 0, :z => 0,
:rz => 0, :ry => 0, :rx => 0,
:size => 1, :width => 1, :height => 1,
:start_x => width/2, :start_y => height/2, :start_z => 0,
:color => [0.5, 0.5, 0.5, 1],
:stop_size => 1.5}
@values.merge!(starting_values)
@finished = false
@app.reset_matrix
@app.no_stroke
@app.color_mode HSB, 1.0
@app.translate @values[:start_x], @values[:start_y], @values[:start_z]
self.send(rule_name, {})
end
def rotate_x rt
@xr = rt
end
def rotate_y rt
@yr = rt
end
def rotate_z rt
@zr = rt
end
# Before actually drawing the next step, we need to move to the appropriate
# location.
def get_ready_to_draw
@app.translate(@values[:x], @values[:y], @values[:z])
@app.rotate_x(@values[:rx] + xr)
@app.rotate_y(@values[:ry] + yr)
@app.rotate_z(@values[:rz] + zr)
end
# Compute the rendering parameters for drawing a shape.
def get_shape_values(some_options)
old_ops = @values.dup
merge_options(old_ops, some_options) if some_options
@app.fill *old_ops[:color]
return old_ops[:size], old_ops
end
# Sphere, cube are the primitive drawing
def cube(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.translate(options[:x] * size, options[:y] * size , options[:z] * size)
@app.box(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an oredered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
def sphere(some_options=nil)
size, options = *get_shape_values(some_options)
rotz = options[:rz]
roty = options[:ry]
rotx = options[:rx]
@app.rotate_x rotx unless rotx.nil?
@app.rotate_y roty unless roty.nil?
@app.rotate_z rotz unless rotz.nil?
@app.sphere_detail 10
@app.sphere(size)
@app.rotate_z(-1 * rotz) unless rotz.nil? # unwind rotations in an oredered way
@app.rotate_y(-1 * roty) unless roty.nil?
@app.rotate_x(-1 * rotx) unless rotx.nil?
end
end
end
My Translation of default.es to ruby processing
# default.rb
# virtually a direct translation of default.es
# the StructureSynth default script
load_libraries :test_free, :control_panel
attr_reader :amplitude, :yrot, :zrot
def setup_the_spiral
@spiral = ContextFree.define do
rule :default do
split do
R1 :brightness => 1
rewind
R2 :brightness => 1
end
end
rule :R1 do
sphere :brightness => 1
R1 :size => 0.99, :x => 0.25, :rz => 6, :ry => 6
end
rule :R2 do
sphere :brightness => 1
R2 :size => 0.99, :x => -0.25, :rz => 6, :ry => 6
end
end
end
def configure_control # setup control panel gui
control_panel do |c|
c.title = "Amplitude+Attitude"
c.slider :amplitude, -0.025..0.015, 0.0
c.slider :yrot, -6.3..6.3, 0.005
c.slider :zrot, -6.3..6.3, 0.005
end
end
def setup
size 800, 800, P3D
configure_control
smooth
setup_the_spiral
end
def draw
background 0
lights
@spiral.render :default, :start_x => 0, :start_y => 0, :start_z => -40, :size => height/400,
:stop_size => 0.2, :color => [0, 0.8, 0.8], :rx => amplitude, :ry => yrot, :rz => zrot
end
Labels:
context free,
ruby processing,
StructureSynth
Subscribe to:
Posts (Atom)
Followers
Blog Archive
-
▼
2010
(73)
-
►
September
(15)
- Basic Hair Example SunflowAPIAPI just using jruby
- Basic Hair Example SunflowAPIAPI in ruby processing
- SunflowAPIAPI in ruby processing
- A Staffordshire Knot Using Ruby Processing and A C...
- An Infinity Loop? With My 3D Context Free Library
- Revised 3D Context Free DSL Sketch, with Mouse Whe...
- A Mouse Wheel Listener in Ruby Processing
- 3D Context Free DSL Screen Saver?
- Translating the default.es to ruby processing DSL
-
►
September
(15)
About Me
- monkstone
- I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2