# 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 # require "json" load_library :bubble attr_reader :bubble_data def setup() size(640, 360) # initialize bubble_data with 'key' and read data from 'file path' @bubble_data = BubbleData.new "bubbles" bubble_data.load_data "data/data.json" 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 attr_reader :key, :path, :bubbles # @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.add Bubble.new(x, y, rand(40 .. 80), "new label") save_data load_data path end def display x, y self.each do |bubble| bubble.display bubble.rollover(x, y) end end # @param path to json file def load_data path @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.add Bubble.new( point["position"]["x"], point["position"]["y"], point["diameter"], point["label"]) end end def add bubble bubbles << bubble bubbles.shift if bubbles.size > MAX_BUBBLE end private def save_data hash = { key => self.map{ |point| point.to_hash } } json = JSON.pretty_generate(hash) # generate pretty output open(path, 'w') { |f| f.write(json) } end end
Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Saturday, 28 September 2013
Refactored Load Save Json Sketch (To isolate responsibilities and use Enumerable)
Here rather than sub-classing Array (which can be unsafe) we extend Enumerable, to make our BubbleData class iteratable (to do this, we only needed to provide the each method). But it also suited us to create a custom add method, so we can check whether the MAX_SIZE has been reached, and if so to "shift" the oldest bubble off the underlying array. We have also isolated OO responsibilities, in that the Sketch class only needs to know about the BubbleData class directly to "load", "display" and "add" bubbles.
Labels:
Enumerable,
json,
refactor,
ruby-processing
Saturday, 21 September 2013
Reading and writing RubyStructs to yaml ruby processing
Since ruby 1.9.3 it is possible to read and write RubyStructs to YAML (using Psych), this makes for a more efficient way of storing and restoring data, whilst maintaining human readability see included yaml file. It is essential that the Struct is declared, and this is easiest done in our bubble library (see below).
The bubble library "library/bubble/bubble.rb"
The data file "struct_data.yaml"
###################################### # Yet another examples of reading and # writing to some form of markup, # appropriatetly yaml using ruby structs # by Martin Prout after Dan Shiffman # ################################### load_library :bubble attr_reader :bubbles, :bubble_data def setup() size(640, 360) # load data from file load_data end def draw background 255 bubbles.each do|bubble| bubble.display bubble.rollover(mouse_x, mouse_y) end end def load_data yaml = Psych.load_file("data/struct_data.yaml") # we are storing the data as an array of RubyStruct, in a hash with # a symbol as the key (the latter only to show we can, it makes no sense) data = yaml[:bubbles] @bubbles = [] # iterate the bubble_data array, and populate the array of bubbles data.each do |pt| bubbles << Bubble.new(pt.x, pt.y, pt.diameter, pt.label) end end def save_data # demonstrate how easy it is to create yaml object from a hash in ruby yaml = bubble_data.to_hash.to_yaml # overwite existing 'data.yaml' open("data/struct_data.yaml", 'w') {|f| f.write(yaml) } end def mouse_pressed # create a new bubble instance, where mouse was clicked @bubble_data = BubbleData.new bubbles @bubble_data.add_bubble(Bubble.new(mouse_x, mouse_y, rand(40 .. 80), "new label")) save_data # reload the yaml data from the freshly created file load_data end class BubbleData attr_reader :data def initialize data @data = data end def add_bubble bubble data << bubble if (data.size > 10) # Delete the oldest bubble data.shift end end # Using symbol as hash key for a change. Thus demonstrating only to show we can store # more complex data strucures quite readily. def to_hash {bubbles: data.map{|point| point.to_struct}} end end
The bubble library "library/bubble/bubble.rb"
# The bubble library, include BubbleStruct class Bubble include Processing::Proxy attr_reader :x, :y, :diameter, :name, :over # Create the Bubble def initialize(x, y, diameter, name) @x = x @y = y @diameter = diameter @name = name @over = false end # Checking if mouse is over the Bubble def rollover(px, py) d = dist(px,py,x,y) @over = (d < diameter/2)? true : false end # Display the Bubble def display stroke(0) stroke_weight(2) noFill ellipse(x,y,diameter,diameter) if (over) fill(0) text_align(CENTER) text(name, x, y + diameter/2 + 20) end end def to_struct BubbleStruct.new(x, y, diameter, name) end end BubbleStruct = Struct.new(:x, :y, :diameter, :label)
The data file "struct_data.yaml"
--- :bubbles: - !ruby/struct:BubbleStruct x: 160 y: 103 diameter: 43.2 label: Happy - !ruby/struct:BubbleStruct x: 372 y: 137 diameter: 52.4 label: Sad - !ruby/struct:BubbleStruct x: 273 y: 235 diameter: 61.0 label: Joyous - !ruby/struct:BubbleStruct x: 121 y: 179 diameter: 44.7 label: Melancholy
Labels:
bubble library,
daniel shiffman,
ruby-processing,
struct,
yaml
Friday, 20 September 2013
Reading and writing to a yaml file ruby-processing
The processing sketch
The data file "data.yaml"
require "yaml" attr_reader :bubbles, :bubble_data def setup() size(640, 360) # read source_string from file load_data end def draw background 255 bubbles.each do|bubble| bubble.display bubble.rollover(mouse_x, mouse_y) end end def load_data yaml = YAML.load_file("data/data.yaml") # parse the source string @bubble_data = BubbleData.new "bubbles" # get the bubble_data from the top level hash data = bubble_data.extract_data yaml @bubbles = [] # iterate the bubble_data array, and create an array of bubbles data.each do |point| bubbles << Bubble.new( point["position"]["x"], point["position"]["y"], point["diameter"], point["label"]) end end def save_data # demonstrate how easy it is to create json object from a hash in ruby yaml = bubble_data.to_hash.to_yaml # overwite existing 'data.json' open("data/data.yaml", 'w') {|f| f.write(yaml) } end def mouse_pressed # create a new bubble instance, where mouse was clicked @bubble_data.add_bubble(Bubble.new(mouse_x, mouse_y, rand(40 .. 80), "new label")) save_data # reload the json data from the freshly created file load_data end class BubbleData attr_reader :name, :data def initialize name = "bubbles" @name = name @data = [] end def add_bubble bubble data << bubble end def extract_data yaml @data = yaml[name] end def to_hash {name => data.map{|point| point.to_hash}} end end class Bubble attr_reader :x, :y, :diameter, :name, :over def initialize(x, y, diameter, name) @x, @y, @diameter, @name = x, y, diameter, name @over = false end def rollover px, py d = dist px, py, x, y @over = (d < diameter / 2.0) end def display stroke 0 stroke_weight 2 no_fill ellipse x, y, diameter, diameter if over fill 0 text_align CENTER text(name, x, y + diameter / 2.0 + 20) end end def to_hash {"position" => {"x" => x, "y" => y}, "diameter" => diameter, "label" => name} end end
The data file "data.yaml"
--- bubbles: - position: x: 160 y: 103 diameter: 43.19838 label: Happy - position: x: 372 y: 137 diameter: 52.42526 label: Sad - position: x: 273 y: 235 diameter: 61.14072 label: Joyous - position: x: 121 y: 179 diameter: 44.758068 label: MelancholySee more up to date version here.
Labels:
ruby-processing,
yaml
Wednesday, 18 September 2013
Reading and writing to a csv file ruby-processing
Whilst I was on a roll I thought I would rubify the load_table processing example, there really is no need to use processings convenience method load_table, we can do it all in pure ruby:-
# # Loading Tabular Data # after Daniel Shiffman, by Martin Prout. # # This example demonstrates how to use CSV # to retrieve data from a CSV file and make objects # from that data. # # Here is what the CSV looks like: # # x,y,diameter,name # 160,103,43.19838,Happy # 372,137,52.42526,Sad # 273,235,61.14072,Joyous # 121,179,44.758068,Melancholy # require 'csv' load_library 'bubble' attr_reader :bubbles, :data def setup size(640, 360) load_data end def draw background(255) # Display all bubbles bubbles.each do |b| b.display b.rollover(mouse_x, mouse_y) end text_align(LEFT) fill(0) text('Click to add bubbles.', 10, height - 10) end def load_data # Load CSV file into an Array of Hash objects # :headers option indicates the file has a header row @data = CSV.read('data/data.csv', :headers => true).map{|row| row.to_hash} # The size of the array of Bubble objects is determined by the total number of 'rows' in the CSV @bubbles = [] data.each do |row| # You access the values via their column name (set by using headers option above) x = row['x'].to_f y = row['y'].to_f d = row['diameter'].to_f n = row['name'] # Make a Bubble object out of the data read bubbles << Bubble.new(x, y, d, n) end end def mouse_pressed # Create a new 'row' hash row = {'x' => mouse_x.to_s, 'y' => mouse_y.to_s, 'diameter' => rand(40..80).to_s, 'name' => 'Blah'} # add the row to the existing data array data << row # If the table has more than 10 rows data.shift if (data.size > 10) # Delete the oldest row # read column names from data, and generate csv 'string' that can be written to file column_names = data.first.keys s = CSV.generate do |csv| csv << column_names data.each do |row| csv << row.values end end # Writing the csv data back to the same file, (also specify UTF-8 format) File.open('data/data.csv', 'w:UTF-8') { |file| file.write(s)} # And reloading it load_data endNB: For a more upto date version see JRubyArt version
Labels:
csv,
processing,
ruby-processing,
saveTable,
UTF-8
Monday, 16 September 2013
Using json in ruby-processing
It is actually far easier to use rubys json library than processing/java, because all you need is a hash to convert to_json, and vice versa. Here is one of Daniel Shiffmans sketches given the ruby-processing treatment. It might be also possible to do something similar with XML using Nokogiri (ruby hash to XML) and rails supports (XML to ruby hash).
The data "data.json"
require "json" attr_reader :bubbles, :bubble_data def setup() size(640, 360) # read source_string from file load_data end def draw background 255 bubbles.each do|bubble| bubble.display bubble.rollover(mouse_x, mouse_y) end end def load_data source_string = open("data/data.json", "r").read # parse the source string @bubble_data = BubbleData.new # get the bubble_data from the top level hash data = bubble_data.extract_data source_string @bubbles = [] # iterate the bubble_data array, and create an array of bubbles data.each do |point| bubbles << Bubble.new( point["position"]["x"], point["position"]["y"], point["diameter"], point["label"]) end end def save_data # demonstrate how easy it is to create json object from a hash in ruby # json = bubble_data.to_hash.to_json # if you don't require pretty output json = JSON.pretty_generate(bubble_data.to_hash) # pretty output # overwite existing 'data.json' open("data/data.json", 'w') {|f| f.write(json) } end def mouse_pressed # create a new bubble instance, where mouse was clicked @bubble_data.add_bubble(Bubble.new(mouse_x, mouse_y, rand(40 .. 80), "new label")) save_data # reload the json data from the freshly created file load_data end class BubbleData attr_reader :name, :data def initialize name = "bubbles" @name = name @data = [] end def add_bubble bubble data << bubble end def extract_data source_string @data = JSON.parse(source_string)[name] end def to_hash {name => data.map{|point| point.to_hash}} end end class Bubble attr_reader :x, :y, :diameter, :name, :over def initialize(x, y, diameter, name) @x, @y, @diameter, @name = x, y, diameter, name @over = false end def rollover px, py d = dist px, py, x, y @over = (d < diameter / 2.0) end def display stroke 0 stroke_weight 2 no_fill ellipse x, y, diameter, diameter if over fill 0 text_align CENTER text(name, x, y + diameter / 2.0 + 20) end end def to_hash {"position" => {"x" => x, "y" => y}, "diameter" => diameter, "label" => name} end end
The data "data.json"
{ "bubbles": [ { "position": { "x": 160, "y": 103 }, "diameter": 43.19838, "label": "Happy" }, { "position": { "x": 372, "y": 137 }, "diameter": 52.42526, "label": "Sad" }, { "position": { "x": 273, "y": 235 }, "diameter": 61.14072, "label": "Joyous" }, { "position": { "x": 121, "y": 179 }, "diameter": 44.758068, "label": "Melancholy" } ] }
Labels:
daniel shiffman,
hash to json,
json,
pretty output,
processing,
ruby-processing
Sunday, 15 September 2013
Why ruby-processing?
No not _why ruby-processing, but what might make you want to explore ruby-processing?Well if you are coming from a ruby background, the answer is quite obvious you can write processing sketches in ruby (a language you know and love), and thanks to jruby mirroring regular ruby you are pretty much up to date (the same cannot be said for jython on which processing.py is based, which shows no signs of catching up with python any time soon).
Vanilla processing itself is in a bit of time-warp and only really supports language features consistent with java 5 (whereas java 7 is now the de-facto standard).
Perhaps it is the "permissive" nature of the ruby-language that is one of its most attractive features, which is well illustrated in the following sketch. Where you can readily extend the behaviour of a java class (PVector in this case), and what is more it is possible to do the same to an instance of a ruby array, ruby is a language that just lets you get things done (it does not stand in the way). Also it can be extremely elegant and expressive.
Then there is the community, rubyists are generally warm, welcoming and open, and thanks to the ecology (rubygems and the like eg json gem see example use here) you will find it is a lot easier to collaborate in ruby than many other programming environments (we also do not have a huge repo containing legacy code cf vanilla processing 1.38Gb+ they must be joking).
Perhaps it is the "permissive" nature of the ruby-language that is one of its most attractive features, which is well illustrated in the following sketch. Where you can readily extend the behaviour of a java class (PVector in this case), and what is more it is possible to do the same to an instance of a ruby array, ruby is a language that just lets you get things done (it does not stand in the way). Also it can be extremely elegant and expressive.
Then there is the community, rubyists are generally warm, welcoming and open, and thanks to the ecology (rubygems and the like eg json gem see example use here) you will find it is a lot easier to collaborate in ruby than many other programming environments (we also do not have a huge repo containing legacy code cf vanilla processing 1.38Gb+ they must be joking).
# Drawolver: draw 2D & revolve 3D # Example to show how to extend Ruby classes in a useful way and how to # use PVector and the Array is extended to yield one_of_each # pair of pts. See the drawolver library. Also features the use each_cons, # possibly a rare use for this ruby Enumerable method? # 2010-03-22 - fjenett (last revised by monkstone 2013-09-13) attr_reader :drawing_mode, :points, :rot_x, :rot_y, :vertices module ExtendedArray # send one item from each array, expects array to be 2D: # array [[1,2,3], [a,b,c]] sends # [1,a] , [2,b] , [3,c] def one_of_each( &block ) i = 0 one = self[0] two = self[1] mi = one.length > two.length ? two.length : one.length while i < mi do yield( one[i], two[i] ) i += 1 end end end def setup size 1024, 768, P3D frame_rate 30 reset_scene end def draw background 0 if (!drawing_mode) translate(width/2, height/2) rotate_x rot_x rotate_y rot_y @rot_x += 0.01 @rot_y += 0.02 translate(-width/2, -height/2) end no_fill stroke 255 points.each_cons(2) { |ps, pe| line ps.x, ps.y, pe.x, pe.y} if (!drawing_mode) stroke 125 fill 120 lights ambient_light 120, 120, 120 vertices.each_cons(2) do |r1, r2| begin_shape(TRIANGLE_STRIP) ext_array = [r1,r2].extend ExtendedArray # extend an instance of Array ext_array.one_of_each do |v1, v2| vertex v1.x, v1.y, v1.z vertex v2.x, v2.y, v2.z end end_shape end end end def reset_scene @drawing_mode = true @points = [] @rot_x = 0.0 @rot_y = 0.0 end def mouse_pressed reset_scene points << RPVector.new(mouse_x, mouse_y) end def mouse_dragged points << RPVector.new(mouse_x, mouse_y) end def mouse_released points << RPVector.new(mouse_x, mouse_y) recalculate_shape end def recalculate_shape @vertices = [] points.each_cons(2) do |ps, pe| b = points.last - points.first len = b.mag b.normalize a = ps - points.first dot = a.dot b b = b * dot normal = points.first + b c = ps - normal nlen = c.mag vertices << [] (0..TWO_PI).step(PI/15) do |ang| e = normal + c * cos(ang) e.z = c.mag * sin(ang) vertices.last << e end end @drawing_mode = false end # a wrapper around PVector that implements operators methods for +, -, *, / # class RPVector < Java::ProcessingCore::PVector def + (vect) RPVector.new self.x + vect.x, self.y + vect.y, self.z + vect.z end def - (vect) RPVector.new self.x - vect.x, self.y - vect.y, self.z - vect.z end def * (scalar) RPVector.new self.x * scalar, self.y * scalar, self.z * scalar end def / (scalar) RPVector.new(self.x / scalar, self.y / scalar, self.z / scalar) unless scalar == 0 end end
Labels:
ecology,
jruby,
ruby-processing,
rubygems
Wednesday, 11 September 2013
Gravitational Attraction (3D) Planetarium Sketch (avoiding reflection with some alternative logic)
Yet another example using Andrés Colubri's planetarium library, hardest to convert so far.
Andrés Colubri is concerned that in his sketch some logic would be called too often in the draw loop, this is why he uses the pre and post (methods, only available using reflection, but only recognised if they are part of a java class, this is Catch 22 territory for ruby-processing). I think I've come up with a decent solution (certainly seems to run OK) that avoids reflection, which can only be good thing. In this case I have placed a "guard" in the pre and post methods (ruby methods to be clear). Can't get border to work "too complicated", I reckon I have mad sketch more efficient by replacing PVector with a custom vector (starts at 8 fps, but quickly moves to 60 fps).
The sketch:-
The solar system library
The sketch:-
# # Gravitational Attraction (3D) # by Daniel Shiffman. # # Adapted for dome projection by Andres Colubri # # Simulating gravitational attraction # G ---> universal gravitational constant # m1 --> mass of object #1 # m2 --> mass of object #2 # d ---> distance between objects # F = (G*m1*m2)/(d*d) # # For the basics of working with PVector, see # http://processing.org/learning/pvector/ # as well as examples in Topics/Vectors/ # # load_libraries :planetarium, :solar_system include_package 'codeanticode.planetarium' PLANETS = 10 attr_reader :angle, :planets, :sun, :count, :do_rotate java_alias :background_int, :background, [Java::int] # precast for efficiency def setup size(800, 800, Dome::RENDERER) @angle = 0 # Some random planets @planets = [] @count = 10 # warm up time @do_rotate = false (0 ... PLANETS).each do planets << Planet.new(rand(0.1 .. 2), rand(-width/2 .. width/2), rand(-height/2 .. height/2), rand(-100 .. 100)) end # A single sun @sun = Sun.new end def pre if count == frame_count # only enter once per frame planets.each do |planet| # Sun attracts Planets force = sun.attract(planet) planet.apply_force(force) # Update and draw Planets planet.update end @count = frame_count + 1 frame.set_title("Solar System FPS: #{frame_rate.to_i}") @do_rotate = true end end def draw pre background_int 0 # Setup the scene lights translate(width/2, height/2, 300) rotate_y(angle) # Display the Sun sun.display # All the Planets planets.each do |planet| planet.display end post end # Called after rendering all the faces, but before the dome sphere, # so it can be used to draw stuff on the corners of the screen. #def border # perspective # camera # background(255) # fill(0) # text("FPS: #{frame_rate}", 20, 20) #end def post # Rotate around the scene if do_rotate @angle += 0.003 @do_rotate = false end end
The solar system library
# Gravitational Attraction (3D) # Daniel Shiffman <http://www.shiffman.net> # A class for an attractive body in our world class Sun include Processing::Proxy G = 0.4 # Universal gravitational constant (arbitrary value) attr_reader :sphere, :location, :mass def initialize @location = Vect.new(0,0) @mass = 20 @sphere = create_shape(SPHERE, mass * 2, 20) sphere.set_fill(false) sphere.set_stroke(color(255)) end def constrain val, lo, hi # override processing overloaded method result = (val > hi)? hi : (val < lo)? lo : val end def attract(m) force = Vect.sub(location,m.location) # Calculate direction of force d = constrain(force.mag, 5.0, 25.0) # Limiting the distance to eliminate "extreme" results for very close or very far objects strength = (G * mass * m.mass) / (d * d) # Calculate gravitional force magnitude force.set_mag(strength) # Get force vector --> magnitude * direction return force end # Draw Sun def display push_matrix translate(location.x,location.y,location.z) shape(sphere) pop_matrix end end # Gravitational Attraction (3D) # Daniel Shiffman <http://www.shiffman.net> # A class for an orbiting Planet class Planet include Processing::Proxy # Basic physics model (location, velocity, acceleration, mass) attr_reader :location, :velocity, :acceleration, :mass, :sphere def initialize(m, x, y, z) @mass = m @location = Vect.new(x,y,z) @velocity = Vect.new(1,0) # Arbitrary starting velocity @acceleration = Vect.new(0,0) @sphere = create_shape(SPHERE, mass * 8, 20) sphere.set_stroke(false) sphere.set_fill(color(255)) end # Newton's 2nd Law (F = M*A) applied def apply_force(force) f = Vect.div(force,mass) acceleration.add(f) end # Our motion algorithm (aka Euler Integration) def update velocity.add(acceleration) # Velocity changes according to acceleration location.add(velocity) # Location changes according to velocity acceleration.zero end # Draw the Planet def display push_matrix translate(location.x,location.y,location.z) shape(sphere) pop_matrix end end class Vect attr_reader :x, :y, :z def initialize(x, y, z = 0) @x, @y, @z = x, y, z end def normalize @x, @y, @z = x / mag, y / mag, z / mag if (mag != 0 && mag != 1) end def mult a @x, @y, @z = x * a, y * a, z * a end def self.sub(v0, v1) Vect.new(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z) end def add(v) @x, @y, @z = x + v.x, y + v.y, z + v.z end def self.div(v, a) Vect.new(v.x / a, v.y / a, v.z / a) end def mag Math.sqrt(x*x + y*y + z*z) end def set_mag m normalize @x, @y, @z = x * m, y * m, z * m end def zero @x, @y, @z = 0, 0, 0 end end
Monday, 9 September 2013
Planetarium calibrate sketch in ruby-processing
load_libraries :planetarium, :control_panel include_package 'codeanticode.planetarium' attr_reader :cube_x, :cube_y, :panel, :hide, :camera, :grid def setup # For the time being, only use square windows size(600, 600, Dome::RENDERER) @camera = DomeCamera.new(self) @hide = false @grid = true control_panel do |c| c.title = "Control" c.look_feel "Metal" c.slider :cube_x, -width / 2.0 .. width / 2.0, -60.0 c.slider :cube_y, -height / 2.0 .. height / 2.0, 60.0 @panel = c end end def mouse_pressed @hide = false if hide # use mouse click to restore control panel end # Called five times per frame. def draw unless hide @hide = true panel.set_visible hide end face = camera.getFace case(face) when DomeCamera::POSITIVE_X background(240, 59, 31) when DomeCamera::NEGATIVE_X background(240, 146, 31) when DomeCamera::POSITIVE_Y background(30, 245, 0) when DomeCamera::NEGATIVE_Y background(30, 232, 156) when DomeCamera::POSITIVE_Z background(52, 148, 206) when DomeCamera::NEGATIVE_Z background(183, 115, 13) end push_matrix translate(width/2, height/2, 300) stroke(255) noFill push_matrix translate(cube_x, cube_y, 0) sphere_detail(8) sphere(30) pop_matrix lines_amount = 10 (0 ... lines_amount).each do |i| ratio = i/(lines_amount - 1.0) line(0, 0, cos(ratio * TWO_PI) * 50, sin(ratio * TWO_PI) * 50) end pop_matrix end def key_pressed @grid = !grid (grid)? camera.set_mode(Dome::GRID) : camera.set_mode(Dome::NORMAL) end
New Improved Planetarium Basic Sketch
That is more like it decent controls, no need for that reflection nonsense. Another example using Andrés Colubri's planetarium library.
# The planetarium library is designed to create real-time projections on # spherical domes. It is based on the FullDome project by Christopher # Warnow (ch.warnow@gmx.de): # https://github.com/mphasize/FullDome # # A brief descrition on how it works: a 360° view of the scene is generated # by rendering the scene 6 times from each direction: positive x, negative x, # positive y, and so on. The output of each rendering is stored inside a cube map # texture, which is then applied on a sphere representing the dome. # Hence, the library calls the draw method 6 times per frame in order to update # the corresponding side of the cube map texture (in reality, only 5 times since # the bottom side of the cube map is not invisible on the dome). # New improved version using control panel no need for reflection "nastiness" load_libraries :planetarium, :control_panel include_package 'codeanticode.planetarium' attr_reader :cube_x, :cube_y, :cube_z, :panel, :hide def setup # For the time being, only use square windows size(600, 600, Dome::RENDERER) @hide = false control_panel do |c| c.title = "Control" c.look_feel "Metal" c.slider :cube_x, -width / 2.0 .. width / 2.0, 10.0 c.slider :cube_y, -height / 2.0 .. height / 2.0, 10.0 c.slider :cube_z, -width / 2.0 .. 0, -20.0 @panel = c end end def mouse_pressed @hide = false if hide # use mouse click to restore control panel end # Called five times per frame. def draw unless hide @hide = true panel.set_visible(hide) end background(0) push_matrix translate(width/2, height/2, 300) lights stroke(0) fill(150) push_matrix translate(cube_x, cube_y, cube_z) box(50) pop_matrix stroke(255) lines_amount = 10 (0 ... lines_amount).each do |i| ratio = i/(lines_amount - 1.0) line(0, 0, cos(ratio * TWO_PI) * 50, sin(ratio * TWO_PI) * 50) end pop_matrix end
Sunday, 8 September 2013
Getting ready for jruby-1.7.5
Charlie has called for a big push on jruby-1.7.5 this weekend, I've tested current beta release with ruby-processing, and as far as I can I tell it will be good to go. I'm wondering whether there might be something else I could do to boost up the next release? Currently I'm thinking of dropping the perlin noise gem example which seems a bit iffy now. Perhaps another gem to replace it? I've been messing with Andres Colubri planetarium library (but will probably give it miss) can't get java reflection to work in reverse with "pre()".
But then I had brainwave!!!
But then I had brainwave!!!
# The planetarium library is designed to create real-time projections on # spherical domes. It is based on the FullDome project by Christopher # Warnow (ch.warnow@gmx.de): # https://github.com/mphasize/FullDome # # A brief descrition on how it works: a 360° view of the scene is generated # by rendering the scene 6 times from each direction: positive x, negative x, # positive y, and so on. The output of each rendering is stored inside a cube map # texture, which is then applied on a sphere representing the dome. # Hence, the library calls the draw method 6 times per frame in order to update # the corresponding side of the cube map texture (in reality, only 5 times since # the bottom side of the cube map is not invisible on the dome). # I can't get 'pre' to work correctly on ruby processing (reflection in reverse) # so here we use mouse_released instead load_library :planetarium include_package 'codeanticode.planetarium' attr_reader :cube_x, :cube_y, :cube_z def setup # For the time being, only use square windows size(600, 600, Dome::RENDERER) @cube_x, @cube_y, @cube_z = width/2, height/2, -width/4 end # Called one time per frame when pre, or to order via mouse. def mouse_released # The dome projection is centered at (0, 0), so the mouse coordinates # need to be offset by (width/2, height/2) @cube_x += ((mouse_x - width * 0.5) - cube_x) * 0.2 @cube_y += ((mouse_y - height * 0.5) - cube_y) * 0.2 end # Called five times per frame. def draw background(0) push_matrix translate(width/2, height/2, 300) lights stroke(0) fill(150) push_matrix translate(cube_x, cube_y, cube_z) box(50) pop_matrix stroke(255) lines_amount = 10 (0 ... lines_amount).each do |i| ratio = i/(lines_amount - 1.0) line(0, 0, cos(ratio * TWO_PI) * 50, sin(ratio * TWO_PI) * 50) end pop_matrix end def key_pressed if (key == CODED) @cube_z -= 5 if (key_code == UP) @cube_z += 5 if (key_code == DOWN) end end
Labels:
jruby,
planetarium library,
ruby-processing
Wednesday, 4 September 2013
Dome Projection Shader
Latest processing shader example by Andres Colubri, sketch requires features that will be available in ruby-processing-2.1.5, also requires shader files, and to be run with the --nojruby flag:-
# # DomeProjection # # This sketch uses use environmental mapping to render the output # on a full spherical dome. # # Based on the FullDome_template code from Christopher Warnow: # https://github.com/mphasize/FullDome # # attr_reader :fbo, :cube_map_shader, :dome_sphere, :env_map_texture_id ENV_MAP_SIZE = 1024 def setup size(640, 640, P3D) init_cube_map end def draw background(0) draw_cube_map end def draw_scene background(0) stroke(255, 0, 0) stroke_weight(2) (-width ... 2 * width).step(50) do |i| line(i, -height, -100, i, 2 * height, -100) end (-height ... 2 * height).step(50) do |i| line(-width, i, -100, 2 * width, i, -100) end lights no_stroke translate(mouse_x, mouse_y, 200) rotate_x(frame_count * 0.01) rotate_y(frame_count * 0.01) box(100) end java_import "java.nio.IntBuffer" def init_cube_map sphere_detail(50) @dome_sphere = create_shape(SPHERE, height/2.0) dome_sphere.rotate_x(HALF_PI) dome_sphere.set_stroke(false) pgl = beginPGL @env_map_texture_id = IntBuffer.allocate(1) pgl.gen_textures(1, env_map_texture_id) pgl.bind_texture(PGL::TEXTURE_CUBE_MAP, env_map_texture_id.get(0)) pgl.texParameteri(PGL::TEXTURE_CUBE_MAP, PGL::TEXTURE_WRAP_S, PGL::CLAMP_TO_EDGE) pgl.texParameteri(PGL::TEXTURE_CUBE_MAP, PGL::TEXTURE_WRAP_T, PGL::CLAMP_TO_EDGE) pgl.texParameteri(PGL::TEXTURE_CUBE_MAP, PGL::TEXTURE_WRAP_R, PGL::CLAMP_TO_EDGE) pgl.texParameteri(PGL::TEXTURE_CUBE_MAP, PGL::TEXTURE_MIN_FILTER, PGL::NEAREST) pgl.texParameteri(PGL::TEXTURE_CUBE_MAP, PGL::TEXTURE_MAG_FILTER, PGL::NEAREST) (PGL::TEXTURE_CUBE_MAP_POSITIVE_X ... PGL::TEXTURE_CUBE_MAP_POSITIVE_X + 6).each do |i| pgl.texImage2D(i, 0, PGL::RGBA8, ENV_MAP_SIZE, ENV_MAP_SIZE, 0, PGL::RGBA, PGL::UNSIGNED_BYTE, nil) end # Init fbo, rbo @fbo = IntBuffer.allocate(1) rbo = IntBuffer.allocate(1) pgl.genFramebuffers(1, fbo) pgl.bindFramebuffer(PGL::FRAMEBUFFER, fbo.get(0)) pgl.framebufferTexture2D(PGL::FRAMEBUFFER, PGL::COLOR_ATTACHMENT0, PGL::TEXTURE_CUBE_MAP_POSITIVE_X, env_map_texture_id.get(0), 0) pgl.genRenderbuffers(1, rbo) pgl.bindRenderbuffer(PGL::RENDERBUFFER, rbo.get(0)) pgl.renderbufferStorage(PGL::RENDERBUFFER, PGL::DEPTH_COMPONENT24, ENV_MAP_SIZE, ENV_MAP_SIZE) # Attach depth buffer to FBO pgl.framebufferRenderbuffer(PGL::FRAMEBUFFER, PGL::DEPTH_ATTACHMENT, PGL::RENDERBUFFER, rbo.get(0)) pgl.enable(PGL::TEXTURE_CUBE_MAP) pgl.active_texture(PGL::TEXTURE1) pgl.bind_texture(PGL::TEXTURE_CUBE_MAP, env_map_texture_id.get(0)) endPGL # Load cubemap shader. @cube_map_shader = load_shader("cubemapfrag.glsl", "cubemapvert.glsl") cube_map_shader.set("cubemap", 1) end def draw_cube_map regenerateEnvMap drawDomeMaster end def drawDomeMaster ortho reset_matrix shader(cube_map_shader) shape(dome_sphere) reset_shader end # Called to regenerate the envmap def regenerateEnvMap pgl = beginPGL # bind fbo pgl.bindFramebuffer(PGL::FRAMEBUFFER, fbo.get(0)) # generate 6 views from origin(0, 0, 0) pgl.viewport(0, 0, ENV_MAP_SIZE, ENV_MAP_SIZE) perspective(90.0 * DEG_TO_RAD, 1.0, 1.0, 1025.0) (PGL::TEXTURE_CUBE_MAP_POSITIVE_X ... PGL::TEXTURE_CUBE_MAP_NEGATIVE_Z).each do |face| reset_matrix case face when PGL::TEXTURE_CUBE_MAP_POSITIVE_X camera(0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 0.0) when PGL::TEXTURE_CUBE_MAP_NEGATIVE_X camera(0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0, 0.0) when PGL::TEXTURE_CUBE_MAP_POSITIVE_Y camera(0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0) when PGL::TEXTURE_CUBE_MAP_NEGATIVE_Y camera(0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) when PGL::TEXTURE_CUBE_MAP_POSITIVE_Z camera(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0) end scale(-1, 1, -1) translate(-width * 0.5, -height * 0.5, -500) pgl.framebufferTexture2D(PGL::FRAMEBUFFER, PGL::COLOR_ATTACHMENT0, face, env_map_texture_id.get(0), 0) draw_scene# Draw objects in the scene flush# Make sure that the geometry in the scene is pushed to the GPU no_lights # Disabling lights to avoid adding many times pgl.framebufferTexture2D(PGL::FRAMEBUFFER, PGL::COLOR_ATTACHMENT0, face, 0, 0) end endPGL end
Labels:
dome projection,
GLSL shader,
ruby-processing-2.0
Subscribe to:
Posts (Atom)
Followers
Blog Archive
-
▼
2013
(94)
-
▼
September
(11)
- Refactored Load Save Json Sketch (To isolate respo...
- Reading and writing RubyStructs to yaml ruby proce...
- Reading and writing to a yaml file ruby-processing
- Reading and writing to a csv file ruby-processing
- Using json in ruby-processing
- Why ruby-processing?
- Gravitational Attraction (3D) Planetarium Sketch (...
- Planetarium calibrate sketch in ruby-processing
- New Improved Planetarium Basic Sketch
- Getting ready for jruby-1.7.5
- Dome Projection Shader
-
▼
September
(11)
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