# Trefoil, by Andres Colubri # A parametric surface is textured procedurally # by drawing on an offscreen PGraphics surface. # somewhat optimised version for ruby-processing (development version) load_libraries :vecmath, :fastmath attr_reader :pg, :trefoil def setup size(1024, 768, P3D) texture_mode(NORMAL) noStroke # Creating offscreen surface for 3D rendering. @pg = create_graphics(32, 512, P3D) pg.begin_draw pg.background(0, 0) pg.noStroke pg.fill(255, 0, 0, 200) pg.end_draw # Saving trefoil surface into a PShape3D object @trefoil = create_trefoil(350, 60, 15, pg) end def draw background(0) pg.begin_draw pg.ellipse(rand(0.0 .. pg.width), rand(0.0 .. pg.height), 4, 4) pg.end_draw ambient(250, 250, 250) pointLight(255, 255, 255, 0, 0, 200) push_matrix translate(width/2, height/2, -200) rotate_x(frame_count * PI / 500) rotate_y(frame_count * PI / 500) shape(trefoil) pop_matrix end # Code to draw a trefoil knot surface, with normals and texture # coordinates. # Adapted from the parametric equations example by Philip Rideout: # http://iphone-3d-programming.labs.oreilly.com/ch03.html # This function draws a trefoil knot surface as a triangle mesh derived # from its parametric equation. def create_trefoil(s, ny, nx, tex) obj = create_shape() obj.begin_shape(TRIANGLES) obj.texture(tex) (0 ... nx).each do |j| u0 = j.to_f / nx u1 = (j + 1).to_f / nx (0 ... ny).each do |i| v0 = i.to_f / ny v1 = (i + 1).to_f / ny p0 = eval_point(u0, v0) n0 = eval_normal(u0, v0) p1 = eval_point(u0, v1) n1 = eval_normal(u0, v1) p2 = eval_point(u1, v1) n2 = eval_normal(u1, v1) # Triangle p0-p1-p2 n0.shape_normal(obj) pa = p0 * s pa.shape_vertex(obj, u0, v0) n1.shape_normal(obj) pb = p1 * s pb.shape_vertex(obj, u0, v1) n2.shape_normal(obj) pc = p2 * s pc.shape_vertex(obj, u1, v1) p1 = eval_point(u1, v0) n1 = eval_normal(u1, v0) # Triangle p0-p2-p1 n0.shape_normal(obj) pa.shape_vertex(obj, u0, v0) n2.shape_normal(obj) pc.shape_vertex(obj, u1, v1) n1.shape_normal(obj) pb = p1 * s pb.shape_vertex(obj, u1, v0) end end obj.end_shape return obj end # Evaluates the surface normal corresponding to normalized # parameters (u, v) def eval_normal(u, v) # Compute the tangents and their cross product. p = eval_point(u, v) tangU = eval_point(u + 0.01, v) tangV = eval_point(u, v + 0.01) tangU -= p tangV -= p tangV.cross(tangU).normalize! # it is easy to chain Vec3D operations end # Evaluates the surface point corresponding to normalized # parameters (u, v) def eval_point(u, v) a = 0.5 b = 0.3 c = 0.5 d = 0.1 s = TWO_PI * u t = (TWO_PI * (1 - v)) * 2 sint = FastMath.sin(t) cost = FastMath.cos(t) sint15 = FastMath.sin(1.5 * t) cost15 = FastMath.cos(1.5 * t) r = a + b * cost15 x = r * cost y = r * sint z = c * sint15 dv = Vec3D.new( -1.5 * b * sint15 * cost - y, -1.5 * b * sint15 * sint + x, 1.5 * c * cost15) q = dv.normalize # regular normalize creates a new Vec3D for us qvn = Vec3D.new(q.y, -q.x, 0).normalize! # chained Vec3D operations ww = q.cross(qvn) coss = FastMath.cos(s) sins = FastMath.sin(s) Vec3D.new( x + d * (qvn.x * coss + ww.x * sins), y + d * (qvn.y * coss + ww.y * sins), z + d * ww.z * sins) end
Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Sunday, 25 May 2014
Somewhat optimised version of trefoil sketch
I knew there was something missing (previous post), I had fully intended to optimise the trefoil sketch, here is my effort, where some values are pre-calculated, and although it look slightly messy putting values directly into new Vec3D instance:-
Labels:
chaining,
optimisation,
pre-calculated values,
trefoil
Saturday, 24 May 2014
Extending ruby-processing with built in jruby extensions
A jruby extension need not be a gem (and in some ways extension as gems can be limiting with ruby-processing, as they do not work too easily with jruby-complete) instead it can become a custom or built in library. In the development branch of ruby-processing (fastmath fork) I have created two built in libraries as jruby extensions:-
For the vecmath I took the advantage of working with java to create arcball functionality using processing reflection calls. I also chose to allow simple Vec3D to vertex and Vec3D to normal (this has got to be most efficient way since it avoids unecessary java to ruby, ruby to java conversions something missing in vanilla processing). Here is a sketch (original by Andrés Colubri) that uses the FastMath sin and cos functions (from jafama) and Vec3D to vertex and Vec3D to normal conversions.
For the curious the current version of ruby-processings jruby extensions are available here.
- vecmath, as direct replacement for PVector and incorporation arcball functionality (uses jafama under hood)
- fastmath, incorporating degree precision deg/cos lookup table and a wrapper for some jafama functions
For the vecmath I took the advantage of working with java to create arcball functionality using processing reflection calls. I also chose to allow simple Vec3D to vertex and Vec3D to normal (this has got to be most efficient way since it avoids unecessary java to ruby, ruby to java conversions something missing in vanilla processing). Here is a sketch (original by Andrés Colubri) that uses the FastMath sin and cos functions (from jafama) and Vec3D to vertex and Vec3D to normal conversions.
# Trefoil, by Andres Colubri # A parametric surface is textured procedurally # by drawing on an offscreen PGraphics surface. load_libraries :vecmath, :fastmath attr_reader :pg, :trefoil def setup size(1024, 768, P3D) texture_mode(NORMAL) noStroke # Creating offscreen surface for 3D rendering. @pg = create_graphics(32, 512, P3D) pg.begin_draw pg.background(0, 0) pg.noStroke pg.fill(255, 0, 0, 200) pg.end_draw # Saving trefoil surface into a PShape3D object @trefoil = create_trefoil(350, 60, 15, pg) end def draw background(0) pg.begin_draw pg.ellipse(rand(0.0 .. pg.width), rand(0.0 .. pg.height), 4, 4) pg.end_draw ambient(250, 250, 250) pointLight(255, 255, 255, 0, 0, 200) push_matrix translate(width/2, height/2, -200) rotate_x(frame_count * PI / 500) rotate_y(frame_count * PI / 500) shape(trefoil) pop_matrix end # Code to draw a trefoil knot surface, with normals and texture # coordinates. # Adapted from the parametric equations example by Philip Rideout: # http://iphone-3d-programming.labs.oreilly.com/ch03.html # This function draws a trefoil knot surface as a triangle mesh derived # from its parametric equation. def create_trefoil(s, ny, nx, tex) obj = create_shape() obj.begin_shape(TRIANGLES) obj.texture(tex) (0 ... nx).each do |j| u0 = j.to_f / nx u1 = (j + 1).to_f / nx (0 ... ny).each do |i| v0 = i.to_f / ny v1 = (i + 1).to_f / ny p0 = eval_point(u0, v0) n0 = eval_normal(u0, v0) p1 = eval_point(u0, v1) n1 = eval_normal(u0, v1) p2 = eval_point(u1, v1) n2 = eval_normal(u1, v1) # Triangle p0-p1-p2 n0.shape_normal(obj) pa = p0 * s pa.shape_vertex(obj, u0, v0) n1.shape_normal(obj) pb = p1 * s pb.shape_vertex(obj, u0, v1) n2.shape_normal(obj) pc = p2 * s pc.shape_vertex(obj, u1, v1) p1 = eval_point(u1, v0) n1 = eval_normal(u1, v0) # Triangle p0-p2-p1 n0.shape_normal(obj) pa.shape_vertex(obj, u0, v0) n2.shape_normal(obj) pc.shape_vertex(obj, u1, v1) n1.shape_normal(obj) pb = p1 * s pb.shape_vertex(obj, u1, v0) end end obj.end_shape return obj end # Evaluates the surface normal corresponding to normalized # parameters (u, v) def eval_normal(u, v) # Compute the tangents and their cross product. p = eval_point(u, v) tangU = eval_point(u + 0.01, v) tangV = eval_point(u, v + 0.01) tangU -= p tangV -= p normUV = tangV.cross(tangU) normUV.normalize! return normUV end # Evaluates the surface point corresponding to normalized # parameters (u, v) def eval_point(u, v) a = 0.5 b = 0.3 c = 0.5 d = 0.1 s = TWO_PI * u t = (TWO_PI * (1 - v)) * 2 r = a + b * FastMath.cos(1.5 * t) x = r * FastMath.cos(t) y = r * FastMath.sin(t) z = c * FastMath.sin(1.5 * t) dv = Vec3D.new dv.x = -1.5 * b * FastMath.sin(1.5 * t) * FastMath.cos(t) - (a + b * FastMath.cos(1.5 * t)) * FastMath.sin(t) dv.y = -1.5 * b * FastMath.sin(1.5 * t) * FastMath.sin(t) + (a + b * FastMath.cos(1.5 * t)) * FastMath.cos(t) dv.z = 1.5 * c * FastMath.cos(1.5 * t) q = dv q.normalize! qvn = Vec3D.new(q.y, -q.x, 0) qvn.normalize! ww = q.cross(qvn) pt = Vec3D.new pt.x = x + d * (qvn.x * FastMath.cos(s) + ww.x * FastMath.sin(s)) pt.y = y + d * (qvn.y * FastMath.cos(s) + ww.y * FastMath.sin(s)) pt.z = z + d * ww.z * FastMath.sin(s) return pt end
For the curious the current version of ruby-processings jruby extensions are available here.
Labels:
arcball,
fastmath,
jruby extension,
normal,
processing,
PVector,
ruby-processing,
Vec3D,
vertex
Wednesday, 21 May 2014
Creating a jruby extension as a gem
What's an extension:-
Many MRI gems use extensions to wrap libraries that are written in C with a ruby wrapper. Examples include nokogiri which wraps libxml2 and libxslt, pg which is an interface to the PostgreSQL database and the mysql and mysql2 gems which provide an interface to the MySQL database. This above was copied from a guide to creating a gem with extensions. In jruby java alternatives are preferred.
A jruby extension can act as a wrapper for a java library
The Apache commons-math library is an example of such a library, here is gem I've created as a jruby extension.
Many MRI gems use extensions to wrap libraries that are written in C with a ruby wrapper. Examples include nokogiri which wraps libxml2 and libxslt, pg which is an interface to the PostgreSQL database and the mysql and mysql2 gems which provide an interface to the MySQL database. This above was copied from a guide to creating a gem with extensions. In jruby java alternatives are preferred.
A jruby extension can act as a wrapper for a java library
Labels:
gem,
jruby extension
Thursday, 15 May 2014
Lambda clock (featuring DegLut tables now only in JRubyArt, soon ruby-processing)
There exists this vanilla processing example, which is ideal for our degree sin/cos lookup tables (and a more functional approach in ruby processing using lambda). Basically if you can do the math then I think you should (rather than hide behind dubious vanilla processing map/normalize utility they are not helping you they are hindering your brain).
# The current time can be read with the second(), minute(), # and hour() functions. In this example, DegLut.sin() and DegLut.cos() values # are used to set the position of the hands, perfect for degree precision Lookup Table. load_library :fastmath def setup size 200, 200 stroke 255 smooth 8 end def draw background 0 fill 80 no_stroke # adj factor to map 0-60 to 0-360 (seconds/minutes) & 0-12 to 0-360 (hours) # since angles for DegLut.sin() and DegLut.cos() start at 3 o'clock we # subtract 90 degrees to make them start at the top. clock_x = ->(val, adj, length){DegLut.cos((val * adj).to_i - 90) * length + width / 2} clock_y = ->(val, adj, length){DegLut.sin((val * adj).to_i - 90) * length + height / 2} ellipse 100, 100, 160, 160 stroke 220 stroke_weight 6 line( 100, 100, clock_x.call(hour % 12 + (minute / 60.0), 30, 50), clock_y.call(hour % 12 + (minute / 60.0), 30, 50) ) stroke_weight 3 line( 100, 100, clock_x.call(minute + (second / 60.0), 6, 60), clock_y.call(minute + (second / 60.0), 6, 60) ) stroke 255, 0, 0 stroke_weight 1 line( 100, 100, clock_x.call(second, 6, 72), clock_y.call(second, 6, 72) ) # Draw the minute ticks stroke_weight 2 stroke 255 (0..360).step(6) do |a| x = 100 + DegLut.cos(a) * 72 y = 100 + DegLut.sin(a) * 72 point x, y end end
Labels:
lambda,
ruby-processing. JRubyArt,
vecmath
Tuesday, 13 May 2014
Latest processing update
Processing has been updated, quite a few fixes, tested on linux with ruby-processing and it seems to make sense to update your version of processing for use with ruby-processing, just make sure you update the processing root in .rp5rc, to ensure you are using the right version.
Processing 2.2.1 fixes all the things I broke at the last minute in 2.2. Changes: https://t.co/f8ZQYywL5R. Download https://t.co/bF4kqfcSGy
— Ben Fry (@ben_fry) May 20, 2014
Labels:
processing,
ruby-processing
Saturday, 10 May 2014
Enhanced exception handling in ruby 2.1.0
This one was promoted by headius (Charles Nutter), so guaranteed to work with jruby-9000? Get access to the cause of the original exception, when re-thrown.
class ExceptionalClass def exceptional_method cause = nil begin raise "Boom!" # RuntimeError raised rescue => e raise StandardError, "Ka-pow!" end end end def setup size 500, 120 background 50, 50, 200 f = createFont("Arial", 16, true) text_font(f, 16) fill(255, 0, 0) begin ExceptionalClass.new.exceptional_method rescue Exception => e exception = "Caught Exception: #{e.message} [#{e.class}]" cause = "Caused by : #{e.cause.message} [#{e.cause.class}]" text(exception, 20, 30) fill(255, 255, 0) text(cause, 20, 60) end end
Labels:
exception handling,
jruby-9000,
ruby-processing
Exploring ruby-2.1.2 syntax in ruby-processing
To run the following sketch you need to be using jruby-9000 (the development version of jruby which supports ruby 2.1.0 syntax out of the box), here I am actually used JRubyArt the development version of ruby-processing (that uses jruby-9000 by default). This sketch uses the very concise syntax for rationals in ruby-2.1.0 (fractions for the masses)
Update 16th November, after a long hiatus the sketch is now working again with jruby 9000.dev-SNAPSHOT (2.1.2p142) 2014-11-08 8a13045 OpenJDK 64-Bit Server VM 24.65-b04 on 1.7.0_65-b32 +jit [linux-amd64].Unfortunately not many others sketches will currently run...
def setup size 640, 250 background 10 f = createFont("Arial", 24, true) third = 1 / 3r # since ruby 2.1.0 quarter = 1 / 4r add = "#{third} + #{quarter} = #{third + quarter}" subtract = "#{third} - #{quarter} = #{third - quarter}" multiply = "#{third} * #{quarter} = #{third * quarter}" text_font(f, 24) fill(220) text("Math blackboard ruby-processing", 80, 50) text(add, 110, 100) text(subtract, 110, 150) text(multiply, 110, 200) end
Update 16th November, after a long hiatus the sketch is now working again with jruby 9000.dev-SNAPSHOT (2.1.2p142) 2014-11-08 8a13045 OpenJDK 64-Bit Server VM 24.65-b04 on 1.7.0_65-b32 +jit [linux-amd64].
Labels:
fractions,
jruby-9000,
rational math,
ruby-processing
Subscribe to:
Posts (Atom)
Followers
Blog Archive
-
▼
2014
(79)
-
▼
May
(7)
- Somewhat optimised version of trefoil sketch
- Extending ruby-processing with built in jruby exte...
- Creating a jruby extension as a gem
- Lambda clock (featuring DegLut tables now only in ...
- Latest processing update
- Enhanced exception handling in ruby 2.1.0
- Exploring ruby-2.1.2 syntax in ruby-processing
-
▼
May
(7)
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