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

Showing posts with label vertex. Show all posts
Showing posts with label vertex. Show all posts

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.

Tuesday, 12 May 2009

Nautilus Shell Using Ruby Processing (no magic numbers)

Here we create a nautilus like shape using vertex and line functions, without any magic numbers, well apart from the A and B constants (which are empirically chosen so no magic really). This now old-style, as we can write applets without explicitly including the Processing::App wrapper. The code is implicitly wrapped in an applet which is more like the way things work in the java processing ide see later post (Interactive Ruby and Ruby Processing), I kind of like this style though.

# nautilus.rb
require 'ruby-processing'
class Nautilus < Processing::App
  A = 0.8  # pitch constant
  B = 1.4  # radius constant
  def setup
    translate 230, 120 # offset x and y from origin
    rotate QUARTER_PI + HALF_PI # initial rotation
    smooth
    background 0
    stroke_weight 1
    stroke 255
    for z in 8 .. 40 do # draw the radial lines first (it looks nicer)
      line(get_x(z*A), get_y(z*A), get_x((z-8)*A), get_y((z-8)*A))
    end
    no_fill
    begin_shape # begin spiral 'shell' shape
    stroke_weight 4
    stroke 255, 0, 0
    for i in 0 .. 40 do
      vertex(get_x(i*A), get_y(i*A))
    end
    end_shape
    save_frame "nautilus.png"
  end
  def get_x theta
    A*Math.cos(theta)*Math.exp(theta/Math.tan(B))
  end
   def get_y theta
    A*Math.sin(theta)*Math.exp(theta/Math.tan(B))
  end    
end

Nautilus.new :width => 360, :height => 300, :title => "Approximate Nautilus"

Here is the result:-

Nautilus shell using vertex function

Here is sketch produced using 'magic numbers' calculated using a spreadsheet, iffy method but I like the result.

# vert.rb
require 'ruby-processing'
class Vert < Processing::App
        def setup  
                no_fill  
                smooth  
                stroke_weight 4 
                stroke 255, 0, 0 
                background 0 
                begin_shape # begin spiral 'shell' shape
                vertex 202.5, 200 
                vertex 202.07, 202.07 
                vertex 200, 203.42 
                vertex 197.17, 202.83 
                vertex 195.31, 200 
                vertex 196.12, 196.12 
                vertex 200, 193.58 
                vertex 205.31, 194.69 
                vertex 208.78, 200 
                vertex 207.27, 207.27 
                vertex 200, 212.03 
                vertex 190.05, 209.95 
                vertex 183.53, 200 
                vertex 186.38, 186.38 
                vertex 200, 177.46 
                vertex 218.65, 181.35 
                vertex 230.86, 200 
                vertex 225.54, 225.54 
                vertex 200, 242.26 
                vertex 165.04, 234.96 
                vertex 142.15, 200 
                vertex 152.13, 152.13 
                vertex 200, 120.79 
                vertex 265.53, 134.47 
                vertex 308.44, 200 
                vertex 289.72, 289.72 
                vertex 200, 348.47 
                vertex 77.16, 322.84 
                end_shape  # end spiral 'shell' shape
                stroke_weight 1 # draw the segment lines
                stroke 255         # in lighter color and lighter weight
                line 77.16, 322.84, 165.04, 234.96 
                line 200, 242.26, 200, 348.47 
                line 289.72, 289.72, 225.54, 225.54 
                line 308.44, 200, 230.86, 200 
                line 265.53, 134.47, 218.65, 181.35 
                line 200, 177.46, 200, 120.79 
                line 186.38, 186.38, 152.13, 152.13 
                line 183.53, 200, 142.15, 200 
                line 190.05, 209.95, 165.04, 234.96 
                line 200, 212.03, 200, 242.26 
                line 207.27, 207.27, 225.54, 225.54 
                line 230.86, 200, 208.78, 200 
                line 205.31, 194.69, 218.65, 181.35 
                line 200, 193.58, 200, 177.46 
                line 196.12, 196.12, 186.38, 186.38 
                line 195.31, 200, 183.53, 200 
                line 197.17, 202.83, 190.05, 209.95 
                save_frame "spiral.png" 
        end
end

Vert.new :width => 350, :height => 350, :title => "Nautilus?"


Here is the image, which I think manages that nautilus shell look:-

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