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:-
- 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.
load_libraries :vecmath, :fastmath
attr_reader :pg, :trefoil
def setup
size(1024, 768, P3D)
texture_mode(NORMAL)
noStroke
@pg = create_graphics(32, 512, P3D)
pg.begin_draw
pg.background(0, 0)
pg.noStroke
pg.fill(255, 0, 0, 200)
pg.end_draw
@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
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)
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)
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
def eval_normal(u, v)
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
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.