I have a system at the moment where I create 'things' based on a base class SceneObject, these 'things' have a Proc associated with them that gets called to produce a different representation of the 'thing' (in my case data to be passed to a 3D renderer). At the moment it all works like this...
class SceneObject def initialize(&generate) @generate = generate end def render self.instance_eval(&@generate) end end
class MyObject < SceneObject def initialize super() do # Render some objects end end end
a = MyObject.new a.render
This all works, but I'd like to 'hide' the class definition stuff, the reason being that the data files passed through this system will be defined outside, and don't need to look like Ruby code, they 'use' Ruby code to do the procedural stuff, but other than that I want them to appear as simple as possible, so hiding classes, inheritance, initialisation functions etc. would be a big benefit. Ideally I'd like to do something like this, but can't...
def defineClass(name,&generate) eval <<-"end_eval" class #{name} < SceneObject def initialize super(&#{generate}) end end end_eval end
Then in my data files use this function like so...
defineClass("MyObject") do # Render some objects end
a = MyObject.new a.render
Can anyone offer any advice on how to achieve something like this?
>>>>> "P" == Paul Gregory <pgreg...@aqsis.com> writes:
P> Next question: is it possible to specify arguments to the initialize P> method when defining it with this code?
Well, you can write
pigeon% cat b.rb #!./ruby
class SceneObject def initialize(scale, &generate) @scale = scale @generate = generate end alias init initialize
def render instance_eval(&@generate) end end
def defineClass(name, &block) Object.const_set(name, Class.new(SceneObject)).instance_eval do define_method(:initialize) {|scale| init(scale, &block) } end end
defineClass("MyObject") do puts "render some object #{self.inspect}" end
a = MyObject.new(1.5) a.render pigeon%
pigeon% b.rb render some object #<MyObject:0x401abc2c @generate=#<Proc:0x401abd30@./b.rb:21>, @scale=1.5> pigeon%
class SceneObject def initialize(params, &generate) params.each do |k, v| class << self; self end.send(:attr_accessor, k) send("#{k}=", v) end @generate = generate end alias init initialize
def render instance_eval(&@generate) end end
def defineClass(name, var, &block) Object.const_set(name, Class.new(SceneObject)).instance_eval do define_method(:initialize) {|scale| init(var => scale, &block) } end end
defineClass("MyObject", "scale") do puts "render some object #{self.inspect}" p self.scale end
a = MyObject.new(1.5) a.render pigeon%
pigeon% b.rb render some object #<MyObject:0x401ab7e0 @scale=1.5, @generate=#<Proc:0x401ab8f8@./b.rb:24>> 1.5 pigeon%
but you must prefix `scale' with `self.' otherwise ruby interpret it as a local variable.
t> class << self; self end.send(:attr_accessor, k)
This is stupid, there is a better way
pigeon% cat b.rb #!./ruby
class SceneObject def initialize(params, &generate) params.each do |k, v| k.each_with_index do |name, i| send("#{name}=", v[i]) end end @generate = generate end alias init initialize
def render instance_eval(&@generate) end end
def defineClass(name, *var, &block) Object.const_set(name, Class.new(SceneObject)).instance_eval do var.each {|v| send(:attr_accessor, v)} define_method(:initialize) {|*scale| init(var => scale, &block) } end end
defineClass("MyObject", "scale") do puts "render some object #{self.inspect}" end
a = MyObject.new(1.5) a.render
defineClass("MyObject2", "scale1", "scale2") do puts "render another object #{self.inspect}" end
a = MyObject2.new(1.5, 3.0) a.render pigeon%
pigeon% b.rb render some object #<MyObject:0x401ab31c @generate=#<Proc:0x401ab498@./b.rb:26>, @scale=1.5> render another object #<MyObject2:0x401aafd4 @scale1=1.5, @generate=#<Proc:0x401ab2a4@./b.rb:33>, @scale2=3.0> pigeon%
> t> class << self; self end.send(:attr_accessor, k)
> This is stupid, there is a better way
Spot on! Thanks, that is exactly the sort of thing I'm after.
In case you are interested in what I am trying to achieve, I am trying to make a procedural animation system based around Renderman. Similar in principle to WavesWorld by Michael B. Johnson, and AL my Steve May.
Basincally you define your objects in terms of code segments, which (when I get round to this bit) have links to avars (animated variables) which can be altered over time by various methods, then by adjusting these avars, and regenerating the scene sections which rely on them, you get a powerful animation system.
Long way off yet tho' still just planning and throwing ideas around.
Oh my word! YES, I am interested!!! I have always wanted to do this. I loved WavesWorld and attempted to do the same thing in Perl, but didn't put enough time into it. I'm the author of the RenderMan binding for Perl, by the way, and have now seen the light with Ruby after 10+ years programming in Perl.
Please keep us (or at least me) posted on your progress, Paul! Thanks! -- Glenn Lewis
Paul Gregory wrote:
> In case you are interested in what I am trying to achieve, I am trying > to make a procedural animation system based around Renderman. Similar in > principle to WavesWorld by Michael B. Johnson, and AL my Steve May. > > Basincally you define your objects in terms of code segments, which > (when I get round to this bit) have links to avars (animated variables) > which can be altered over time by various methods, then by adjusting > these avars, and regenerating the scene sections which rely on them, you > get a powerful animation system. > > Long way off yet tho' still just planning and throwing ideas around. > > cheers > > PaulG