Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

Showing posts with label arcball. Show all posts
Showing posts with label arcball. Show all posts

Friday, 6 March 2015

Bezier patch sketch in JRubyArt

Occasionally I see people trawling over really old sketches in this blog, and I wonder what are they thinking? when program versions (ie newer ruby-processing versions) change or there are new opportunities such as JRubyArt, why not look at the "hot" stuff. Anyway I revised this sketch for JRubyArt (no need to import :vecmath it is built in)....
Sketch also makes use of AppRender to make for direct conversion from Vec3D to vertex/normal.
# bezier patch By Marius Watz:
# http://www.openprocessing.org/sketch/57709
# Normal calculation added by Andres Colubri
# Direct port of sample code by Paul Bourke.
# Original code: http://paulbourke.net/geometry/bezier/
#
# hit "spacebar" to generate a new shape and save current
#

NI = 4
NJ = 5
RESI = NI * 10
RESJ = NJ * 10

attr_accessor :outp, :inp, :normp, :auto_normals, :renderer

def setup
  sketch_title 'Bezier Patch'
  ArcBall.init(self)
  @auto_normals = false
  @renderer = AppRender.new(self)
  build
end

def draw
  background(255)
  smooth(8)
  lights
  no_stroke
  fill(192, 192, 192)
  define_lights
  lights
  (0...RESI - 1).each do |i|
    begin_shape(QUAD_STRIP)
    (0...RESJ).each do |j|
      normp[i][j].to_normal(renderer) unless auto_normals
      outp[i][j].to_vertex(renderer)
      outp[i + 1][j].to_vertex(renderer)
    end
    end_shape
  end
end

def settings
  size(1024, 768, P3D)
end

def define_lights
  ambient_light(60, 60, 60)
  point_light(30, 30, 30, 0, 0, 0)
  directional_light(40, 40, 50, 1, 0, 0)
  spot_light(30, 30, 30, 0, 40, 200, 0, -0.5, 0.5, PI / 2, 2)
end


def key_pressed
  return unless key == ' '
  save_frame('bez_patch.png')
  build
end

def build
  @outp = []
  @normp = []
  @inp = []
  uitang = Vec3D.new
  ujtang = Vec3D.new
  (0...NI).each do |i|
    row = []
    (0...NJ).each do |j|
      row << Vec3D.new(i, j, (rand * 6) - 3)
    end
    inp << row
  end
  (0...RESI).each do |i|
    mui = i.fdiv(RESI - 1)
    row = []
    row_n = []
    (0...RESJ).each do |j|
      muj = j.fdiv(RESJ - 1)
      vect = Vec3D.new
      uitang = Vec3D.new
      ujtang = Vec3D.new
      (0...NI).each do |ki|
        bi = bezier_blend(ki, mui, NI)
        dbi = d_bezier_blend(ki, mui, NI)
        (0...NJ).each do |kj|
          bj = bezier_blend(kj, muj, NJ)
          dbj = d_bezier_blend(kj, muj, NJ)
          vect.x += (inp[ki][kj].x * bi * bj)
          vect.y += (inp[ki][kj].y * bi * bj)
          vect.z += (inp[ki][kj].z * bi * bj)
          uitang.x += (inp[ki][kj].x * dbi * bj)
          uitang.y += (inp[ki][kj].y * dbi * bj)
          uitang.z += (inp[ki][kj].z * dbi * bj)
          ujtang.x += (inp[ki][kj].x * bi * dbj)
          ujtang.y += (inp[ki][kj].y * bi * dbj)
          ujtang.z += (inp[ki][kj].z * bi * dbj)
        end
      end
      vect += Vec3D.new(-NI / 2, -NJ / 2, 0)
      vect *= 100
      row << vect
      uitang.normalize!
      row_n << uitang.cross(ujtang.normalize)
    end
    @outp << row
    @normp << row_n
  end
end

def bezier_blend(k, mu,  n)
  blend = 1.0
  nn = n
  kn = k
  nkn = n - k
  while (nn >= 1)
    blend *= nn
    nn -= 1
    if kn > 1
      blend = blend.fdiv(kn)
      kn -= 1
    end
    if nkn > 1
      blend = blend.fdiv(nkn)
      nkn -= 1
    end
  end
  blend *= mu**k.to_f if k > 0
  return blend * (1 - mu)**(n - k).to_f if n - k > 0
  blend
end

def d_bezier_blend(k, mu,  n)
  dblendf = 1.0
  nn = n
  kn = k
  nkn = n - k
  while (nn >= 1)
    dblendf *= nn
    nn -= 1
    if kn > 1
      dblendf = dblendf.fdiv(kn)
      kn -= 1
    end
    if nkn > 1
      dblendf = dblendf.fdiv(nkn)
      nkn -= 1
    end
  end
  fk = 1
  dk = 0
  fnk = 1
  dnk = 0
  if k > 0
    fk = mu**k.to_f
    dk = k * mu**(k - 1).to_f
  end
  if n - k > 0
    fnk = (1 - mu)**(n - k).to_f
    dnk = (k - n) * (1 - mu)**(n - k - 1).to_f
  end
  dblendf * (dk * fnk + fk * dnk)
end

Tuesday, 23 December 2014

Lorenz Attractor Ruby-Processing

Here is a processing sketch that used a LinkedList (not needed in ruby version, an array is sufficient)
LENGTH = 10_000
SPEED = 10

load_library 'vecmath'

attr_accessor :particles, :a, :b, :c, :d

def setup
  size 512, 512, P3D
  ArcBall.init(self)
  frame_rate 30
  stroke color(0, 0, 0, 33)
  stroke_weight 2
  @a = 10
  @b = 28
  @c = 8 / 3
  @d = 0.75
  init_particles
end

def draw
  background(255)
  advance_particles(SPEED)
  draw_particles
end

def init_particles
  @particles = []
  LENGTH.times do
    add_particle(Vec3D.new 1.5, -1.5, 1.5) # need to start somewhere
  end
end

def add_particle(p)
  step = Vec3D.new(a * (p.y - p.x), p.x * (b - p.z) - p.y, p.x * p.y - c * p.z)
  step *= (d / step.mag)
  particles << p + step
end

def advance_particles(count)
  (1..count).each do
    particles.shift
    add_particle(particles.last)
  end
end

def draw_particles
  scale(8)
  particles.each_cons(2) do |a, b|
    stroke(color((b.x - a.x) * 255, (b.y - a.y) * 255, (b.z - a.z) * 255, 88))
    line(a.x, a.y, a.z - 30, b.x, b.y, b.z - 30)
  end
end

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:-
  1. vecmath, as direct replacement for PVector and incorporation arcball functionality (uses jafama under hood)
  2. 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.

Sunday, 12 January 2014

Frame of Reference Example Sketch (Original by Ira Greenberg)

Here's another processing sketch that I've translated to ruby-processing, where I replace PVector with Vec3D from the ruby processing vecmath library. It also makes use of the ArcBall functionality built into the vecamth library.
###############
# Frame of Reference example by Ira Greenberg
# https://github.com/irajgreenberg/ProcessingTips
# Translated to ruby-processing by Martin Prout January 2014
# Now use mouse drag for ArcBall manipulation, and  +/- keys for zoom
###############

load_library :vecmath
load_library :geometry

FACE_COUNT = 50

attr_reader :arcball, :c, :p, :zoom

def setup
  size(800, 800, P3D)
  @zoom = 1.0
  camera(0, 0, (height/2.0) / tan(PI*30.0 / 180.0), 0, 0, 0, 0, -1, 0) # point camera at origin
  # create an Arcball at centre that pretty much fills the screen
  @arcball = ArcBall.new(0, 0, min(width - 20, height - 20) / 2.0)
  @c = []
  @p = []
  FACE_COUNT.times do |i|

    # calc some random triangles in 3 space
    val = Vec3D.new(rand(-width/2 .. width/2), rand(-width/2 .. width/2), rand(-width/2 .. width/2))
    v0 = Vec3D.new(rand(-val.x .. -val.x + 100), rand(-val.y .. -val.y + 100), rand(-val.z .. -val.z + 100))
    v1 = Vec3D.new(rand(-val.x .. -val.x + 100), rand(-val.y .. -val.y + 100), rand(-val.z .. -val.z + 100))
    v2 = Vec3D.new(rand(-val.x .. -val.x + 100), rand(-val.y .. -val.y + 100), rand(-val.z .. -val.z + 100))
    p << Plane.new([v0, v1, v2])

    # build some cute little cylinders
    c << Cylinder.new(Vec3D.new(150, 5, 5), 12)

    # Using each Triangle normal (N), 
    # One of the Triangle's edges as a tangent (T)  
    # Calculate a bi-normal (B) using the cross-product between each N and T
    # Note caps represent constants in ruby so we used N = nn, T = tt and B = bb in the ruby code below

    #
    # A picture helps
    # nice, sweet orthogonal axes 

    # N   B
    # |  /
    # | /
    # |/____T

    #
    # N, T, B together give you a Frame of Reference (cute little local coordinate system), based on each triangle. 
    # You can then take the cylinder (or any vertices) and transform them using a 3 x 3 matrix to this coordinate system.
    # (In the matrix each column is based on N, T and B respectivley.) 
    # The transform will handle any rotations and scaling, but not the translation, 
    # but we can add another dimenson to the matrix to hold the translation values.  
    # Here's what all this confusing description looks like:

    #
    # Matrix :                               Vector :
    # |  N.x  T.x  B.x  translation.x  |      |  x  |
    # |  N.y  T.y  B.y  translation.y  |      |  y  |
    # |  N.z  T.z  B.z  translation.z  |      |  z  |
    # |  0    0    0    1              |      |  1  |

    # We add the extra row in the matrix and the 1 to each vector 
    # so the math works. We describe the Matrix as 4 rows by 4 columns
    # and the vector now as a Matrix with 4 rows and 1 column. 
    # When you multiply matrices the inner numbers MUST match, so: 
    # [4 x 4] [4 x 1] is OK, but [4 x 4] [1 x 4] is NOT COOL.

    # (Please note there is also row vector approach that you can use,
    # Google about; it simply puts the vector on left side of matrix and treats
    # it as a 1 row and 4 column matrix. However, you'll also need to shift
    # the translation terms to the bottom of the matrix for the math to grock.)

    # The Matrix multiplication looks like this (sorry it's a little tedious looking.)
    # n.x * x + t.x *y + B.x * z + translation.x * 1  =  new transformed x
    # n.y * x + t.y *y + B.y * z + translation.y * 1  =  new transformed y
    # n.z * x + t.z *y + B.z * z + translation.z * 1  =  new transformed z
    # 0 * x + 0 *y + 0 * z + 1 * 1   =   disregard this crap.
    #

    nn = p[i].n
    tt = Vec3D.new(p[i].vecs[1].x - p[i].vecs[0].x, p[i].vecs[1].y - p[i].vecs[0].y, p[i].vecs[1].z - p[i].vecs[0].z)
    nn.normalize!
    tt.normalize!
    bb = nn.cross(tt)
    bb.normalize! # not really needed

    # build matrix with frame and translation (to centroid of each triangle)
    m4 = Mat4.new(nn, tt, bb, p[i].c)

    # transform each cylinder to align with each triangle
    c[i].vecs = m4.mult(c[i].vecs)
  end
  fill(187)
  stroke(50, 20)
end

def draw
  background(0)
  lights
  # update the arcball rotation
  update
  FACE_COUNT.times do |i|
    p[i].display
    c[i].display
  end
end

def update
  theta, x, y, z = arcball.update
  rotate(theta, x, y, z)
end

def mouse_pressed
  arcball.mouse_pressed(mouse_x, mouse_y)
end

def mouse_dragged
  arcball.mouse_dragged(mouse_x, mouse_y)
end

def key_pressed
  case key
  when '+'
    @zoom -= 0.1  # closer is bigger
  when '-'
    @zoom += 0.1
  end
  camera(0, 0, (height * zoom / 2.0) / tan(PI*30.0 / 180.0), 0, 0, 0, 0, -1, 0)
end


Here is the Plane class
NORM_LEN = 225.0

class Plane
  include Processing::Proxy

  attr_reader :vecs, :c, :n


  def initialize(vecs)
    @vecs = vecs
    init
  end

  def init
    v1 = vecs[1].dup
    v2 = vecs[2].dup
    v1 -= vecs[0]
    v2 -= vecs[0]

    @c = Vec3D.new(
      (vecs[0].x+vecs[1].x+vecs[2].x) / 3,
      (vecs[0].y+vecs[1].y+vecs[2].y) / 3,
      (vecs[0].z+vecs[1].z+vecs[2].z) / 3
      )

    @n = v1.cross(v2)
    n.normalize!
  end

  def display
    begin_shape(TRIANGLES)
    vecs.each do |vec|
      vertex(vec.x, vec.y, vec.z)
    end
    end_shape

    #normal
    stroke(200, 160, 30)
    begin_shape(LINES)
    vertex(c.x, c.y, c.z)
    vertex(c.x + n.x * NORM_LEN, c.y + n.y * NORM_LEN, c.z + n.z * NORM_LEN)
    end_shape

    #binormal
    stroke(160, 200, 30)
    begin_shape(LINES)
    vertex(c.x, c.y, c.z)
    # tangent
    v = vecs[1].dup
    #v.set(vecs[1])
    v -= vecs[0]
    v.normalize!
    vertex(c.x + v.x * NORM_LEN, c.y + v.y * NORM_LEN, c.z + v.z * NORM_LEN)
    end_shape

    stroke(30, 200, 160)
    begin_shape(LINES)
    vertex(c.x, c.y, c.z)
    b = v.cross(n)
    vertex(c.x + b.x * NORM_LEN, c.y + b.y * NORM_LEN, c.z + b.z * NORM_LEN)
    end_shape
    stroke(0, 75)
  end
end



Here is the Mat4 class
# uber simple Homogeneous 4 x 4 matrix

class Mat4
  attr_reader :mat

  def initialize(axisX, axisY, axisZ, trans)
    @mat = [
    [axisX.x, axisY.x, axisZ.x, trans.x],
    [axisX.y, axisY.y, axisZ.y, trans.y],
    [axisX.z, axisY.z, axisZ.z, trans.z],
    [0, 0, 0,  1]
    ]
  end

  # The processing version changes the input 'array', here we return
  # a new array with transformed values (which we then assign to the input)
  # see line 91 Frame_of_Reference.rb

  def mult(array)
    temp = []
    array.each do |arr|
      xt = mat[0][0] * arr.x + mat[0][1] * arr.y + mat[0][2] * arr.z + mat[0][3] * 1
      yt = mat[1][0] * arr.x + mat[1][1] * arr.y + mat[1][2] * arr.z + mat[1][3] * 1
      zt = mat[2][0] * arr.x + mat[2][1] * arr.y + mat[2][2] * arr.z + mat[2][3] * 1
      temp << Vec3D.new(xt, yt, zt)
    end
    return temp
  end
end


Here is the Cylinder class
class Cylinder
  include Processing::Proxy
  attr_accessor :vecs
  attr_reader :detail, :dim



  def initialize(dim, detail)
    @dim = dim
    @detail = detail
    init
  end

  def init
    theta = 0.0
    #    created around x-axis
    #    y = Math.cos
    #    z = Math.sin
    veca = []
    vecb = []
    detail.times do
      veca << Vec3D.new(0, Math.cos(theta)*dim.y, Math.sin(theta)*dim.z)
      vecb << Vec3D.new(dim.x, Math.cos(theta)*dim.y, Math.sin(theta)*dim.z)
      theta += Math::PI * 2/detail
    end
    @vecs = veca.concat(vecb)
  end

  def display
    begin_shape(QUADS)
    detail.times do |i|
      if (i<detail-1)
        vertex(vecs[i].x, vecs[i].y, vecs[i].z)
        vertex(vecs[i+1].x, vecs[i+1].y, vecs[i+1].z)
        vertex(vecs[detail+i+1].x, vecs[detail+i+1].y, vecs[detail+i+1].z)
        vertex(vecs[detail+i].x, vecs[detail+i].y, vecs[detail+i].z)
      else
        vertex(vecs[i].x, vecs[i].y, vecs[i].z)
        vertex(vecs[0].x, vecs[0].y, vecs[0].z)
        vertex(vecs[detail].x, vecs[detail].y, vecs[detail].z)
        vertex(vecs[detail+i].x, vecs[detail+i].y, vecs[detail+i].z)
      end
    end
    end_shape
  end
end

Wednesday, 13 March 2013

ArcBall library example under the Hood

Here is my simple ruby-processing example under the hood
puts self.inspect
#<Processing::App::Sketch>
puts self.class.ancestors

Sketch
Processing::App
Processing::HelperMethods
Math
Java::ProcessingCore::PApplet
Java::ProcessingCore::PConstants
Java::JavaLang::Runnable
Java::JavaAwtEvent::MouseListener
Java::JavaAwtEvent::MouseWheelListener
Java::JavaAwtEvent::MouseMotionListener
Java::JavaAwtEvent::KeyListener
Java::JavaAwtEvent::FocusListener
Java::JavaUtil::EventListener
Java::JavaApplet::Applet
Java::JavaAwt::Panel
Java::JavaxAccessibility::Accessible
Java::JavaAwt::Container
Java::JavaAwt::Component
Java::JavaAwtImage::ImageObserver
Java::JavaAwt::MenuContainer
Java::JavaIo::Serializable
Java::JavaLang::Object
ConcreteJavaProxy
JavaProxy
JavaProxyMethods
Object
Kernel
BasicObject

puts self.class.methods

java_interfaces
java_proxy_class=
const_missing
java_proxy_class
library_loaded?
has_slider
method_added
sketch_class
load_java_library
inherited
load_library
load_ruby_library
load_libraries
full_screen
url_encode
show_depth_warning_xyz
show_method_warning
java_send
dist
exec
splice
main
print
blendColor
createInput
load_strings
split
parse_int
nf
lerp
run_sketch
show_missing_warning
reverse
createReader
lerp_color
lerpColor
acos
sort
floor
sqrt
parseInt
nfs
hour
atan
get_extension
unhex
selectOutput
min
nfp
str
match_pattern
trim
create_path
unbinary
nfc
url_decode
parseFloat
urlDecode
expand
debug
match
urlEncode
round
minute
arraycopy
cos
tan
radians
ceil
atan2
parseChar
save_bytes
matchAll
loadStrings
parse_char
match_all
create_writer
array_copy
append
create_reader
parse_byte
month
map
parse_float
degrees
saveStream
println
asin
show_variation_warning
save_stream
select_impl
showDepthWarningXYZ
shorten
exp
select_input
join
saveBytes
createOutput
max
second
createPath
getExtension
save_strings
parseByte
sq
select_folder
showVariationWarning
binary
loadBytes
create_input
blend_color
save_stream?
hex
subset
norm
log
parseBoolean
open
parse_boolean?
year
mag
show_depth_warning
showMissingWarning
selectFolder
day
selectImpl
matchPattern
split_tokens
access$000
showMethodWarning
sin
arrayCopy
splitTokens
select_output
java_method
saveStrings
parse_boolean
constrain
runSketch
pow
selectInput
create_output
abs
concat
createWriter
showDepthWarning
load_bytes
newAudioClip
new_audio_clip
access$100
access$500
access$002
instance_of
access$500?
is_instance_of
request_focus_controller=
set_request_focus_controller
is_instance_of?
instanceOf
access$400
setRequestFocusController
isInstanceOf
requestFocusController=
new
singleton_class
java_class
field_writer
new_array
__persistent__=
__persistent__
[]
field_accessor
field_reader
java_class=
yaml_tag
allocate
superclass
const_get
private_constant
public_instance_methods
autoload?
freeze
class_variable_get
included_modules
==
class_exec
psych_yaml_as
public_class_method
ancestors
instance_method
<
yaml_as
protected_method_defined?
>
===
private_instance_methods
hash
class_variables
public_constant
<=
method_defined?
instance_methods
class_variable_defined?
name
private_method_defined?
const_set
autoload
include?
protected_instance_methods
module_exec
module_eval
<=>
constants
private_class_method
class_variable_set
to_s
public_method_defined?
class_eval
>=
remove_class_variable
const_defined?
handle_different_imports
psych_to_yaml
include_class
to_yaml
java_kind_of?
to_yaml_properties
java_signature
methods
define_singleton_method
initialize_clone
extend
nil?
tainted?
method
is_a?
instance_variable_defined?
instance_variable_get
instance_variable_set
public_method
display
send
private_methods
enum_for
com
to_java
public_send
instance_of?
taint
class
java_annotation
instance_variables
!~
org
untrust
=~
protected_methods
trust
inspect
java_implements
tap
frozen?
initialize_dup
java
respond_to?
java_package
untaint
respond_to_missing?
clone
java_name
to_enum
singleton_methods
untrusted?
eql?
kind_of?
dup
java_require
javax
public_methods
instance_exec
__send__
instance_eval
equal?
object_id
__id__
!
!=

Tuesday, 28 August 2012

Control Panel Enhanced ArcBall Sketch

Here is an enhanced arcball sketch, features use of control panel for zoom facility, and drop down menu too choose which or whether rotation is frozen about an axis. See previous post for library.
load_libraries 'arcball', 'opengl', 'control_panel'
import "arcball"
import "opengl"

X = 0
Y = 1
Z = 2

attr_reader :my_ball, :zoom

def setup
  size(600, 600, OPENGL)
  setup_opengl
  @my_ball = ArcBall.new(width/2.0, height/2.0, min(width - 20, height - 20) * 0.5)
  control_panel do |c|
    c.title = 'Controller'
    c.slider  :zoom, 0..2.0, 0.0
    c.menu(:freeze, ['X-axis', 'Y-axis', 'Z-axis', 'free'], 'free') {|m| load_menu_item(m) }
  end
  @zoom = 0
end

def draw
  background(50, 50, 100)
  translate(width/2.0, height/2.0, zoom * width/2.0)
  define_lights
  update
  lights
  stroke(0)
  cube(my_ball.radius / 2.0)
end

def setup_opengl
  hint ENABLE_OPENGL_4X_SMOOTH     # optional
  hint DISABLE_OPENGL_ERROR_REPORT # optional
end

def update
  theta, x, y, z = my_ball.update
  rotate(theta, x, y, z)
end

def mouse_pressed
  my_ball.mouse_pressed(mouse_x, mouse_y)
end

def mouse_dragged
  my_ball.mouse_dragged(mouse_x, mouse_y)
end

def define_lights
  ambient(20, 20, 20)
  ambient_light(50, 50, 50)
  point_light(30, 30, 30, 200, -150, 0)
  directional_light(0, 30, 50, 1, 0, 0)
  spot_light(30, 30, 30, 0, 40, 200, 0, -0.5, -0.5, PI / 2, 2)
end

def load_menu_item(m)
  case(m)
  when 'X-axis':
    my_ball.select_axis(X)
  when 'Y-axis':
    my_ball.select_axis(Y)
  when 'Z-axis':
    my_ball.select_axis(Z)
  when 'free'
    my_ball.select_axis(-1)
  end
end

def cube(sz)
  sz *= 0.5
  fill(200,  200,  200,  255)
  begin_shape(QUADS)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, +sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, -sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    end_shape
end

sketch screenshot using the gimp

Monday, 27 August 2012

A ArcBall library for ruby-processing

Here is my library, NB: three classes in a file named arcball.rb, and in a folder arcball nested in a library folder (NB: AVector is a low fat version of PVector for use here). Note you can put other ruby libraries in that folder. Note the use of :: separators and the Java prefix to call the EPSILON constant from vanilla processing.
class ArcBall
  attr_reader :center_x, :center_y, :v_down, :v_drag, :q_now, :q_drag, :q_down, :axis, :axis_set, :radius

  def initialize(cx, cy, radius)
    @center_x = cx
    @center_y = cy
    @radius = radius
    @v_down = AVector.new
    @v_drag = AVector.new
    @q_now = Quaternion.new
    @q_down = Quaternion.new
    @q_drag = Quaternion.new
    @axis_set = [AVector.new(1.0, 0.0, 0.0), AVector.new(0.0, 1.0, 0.0), AVector.new(0.0, 0.0, 1.0)]
    @axis = -1
  end

  def select_axis(axis)
    @axis = axis
  end

  def mouse2sphere(x, y)
    v = AVector.new((x - center_x) / radius, (y - center_y) / radius, 0)
    mag = v.x * v.x + v.y * v.y
    if (mag > 1.0)
      v.normalize
    else
      v.z = Math.sqrt(1.0 - mag)
    end
    v = constrain(v, axis_set[axis]) unless (axis == -1)
    return v
  end

  def mouse_pressed(x, y)
    @v_down = mouse2sphere(x, y)
    @q_down.copy(q_now)
    @q_drag.reset
  end

  def mouse_dragged(x, y)
    @v_drag = mouse2sphere(x, y)
    @q_drag.set(AVector.dot(v_down, v_drag), v_down.cross(v_drag))
  end


  def constrain(vector, axis)
    res = AVector.sub(vector, AVector.mult(axis, AVector.dot(axis, vector)))
    res.normalize
  end

  def update
    @q_now = Quaternion.mult(q_drag, q_down)
    quat2matrix(q_now)
  end

  def quat2matrix(q)
    q.get_value
  end
end

class Quaternion
  attr_reader :w, :x, :y, :z

  def initialize(w = 1.0,  x = 0,  y = 0,  z = 0)
    @w, @x, @y, @z = w,  x,  y,  z
  end

  def reset
    @w = 1.0
    @x = 0.0
    @y = 0.0
    @z = 0.0
  end

  def set(w, v)
    @w = w
    @x = v.x
    @y = v.y
    @z = v.z
  end

  def copy(q)
    @w = q.w
    @x = q.x
    @y = q.y
    @z = q.z
  end

  def self.mult(q1, q2)      # class method   
    x0 = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y
    y0 = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z
    z0 = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x
    w0 = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
    Quaternion.new(w0,  x0,  y0,  z0)
  end

  def get_value
    sa = Math.sqrt(1.0 - w * w)
    sa = 1.0 unless (sa >= Java::processing::core::PConstants::EPSILON)
    [Math.acos(w) * 2, x / sa, y / sa, z / sa]
  end
end

class AVector

  attr_accessor :x, :y, :z

  def initialize(x = 0, y = 0, z = 0)
    @x, @y, @z = x, y, z
  end

  def add(vector)
    AVector.new(vector.x + x, vector.y + y, vector.z + z)
  end

  def normalize
    orig_dist = Math.sqrt(x * x + y * y + z * z)
    @x /= orig_dist
    @y /= orig_dist
    @z /= orig_dist
    self
  end

  def self.dot(v1, v2)
    v1.x * v2.x + v1.y * v2.y + v1.z * v2.z
  end

  def self.mult(v, scalar)
    AVector.new(v.x * scalar, v.y * scalar, v.z * scalar)
  end

  def self.sub(v1, v2)
    AVector.new(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z)
  end

  def cross(v)
    AVector.new(y * v.z - v.y * z,  z * v.x - v.z * x, x * v.y - v.x * y)
  end

end

This is the test class test_arcball.rb:-
load_libraries 'arcball', 'opengl'
import "arcball"
import "opengl"

X = 0
Y = 1
Z = 2

attr_reader :my_ball

def setup
  size(600, 600, OPENGL)
  setup_opengl
  @my_ball = ArcBall.new(width/2.0, height/2.0, min(width - 20, height - 20) * 0.5)
end

def draw
  background(50, 50, 100)
  translate(width/2.0, height/2.0)  # @todo add zoom via z, using control_panel
  define_lights
  update
  lights
  stroke(0)
  cube(my_ball.radius)
end

def setup_opengl
  hint ENABLE_OPENGL_4X_SMOOTH     # optional
  hint DISABLE_OPENGL_ERROR_REPORT # optional
end

def update
  theta, x, y, z = my_ball.update
  rotate(theta, x, y, z)
end

def mouse_pressed
  my_ball.mouse_pressed(mouse_x, mouse_y)
end

def mouse_dragged
  my_ball.mouse_dragged(mouse_x, mouse_y)
end

def define_lights
  ambient(20, 20, 20)
  ambient_light(50, 50, 50)
  point_light(30, 30, 30, 200, -150, 0)
  directional_light(0, 30, 50, 1, 0, 0)
  spot_light(30, 30, 30, 0, 40, 200, 0, -0.5, -0.5, PI / 2, 2)
end

def key_pressed                # @todo select via control_panel instead
  case(key)
  when 'x':
    my_ball.select_axis(X)
  when 'y':
    my_ball.select_axis(Y)
  when 'z':
    my_ball.select_axis(Z)
  end
end

def key_released
  my_ball.select_axis(-1)
end

def cube(sz)
  sz *= 0.5
  fill(200,  200,  200,  255)
  begin_shape(QUADS)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, +sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, -sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    end_shape
end



Followers

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2