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

Showing posts with label toxiclibs. Show all posts
Showing posts with label toxiclibs. Show all posts

Wednesday, 17 June 2015

Watch Mode Working in Ruby-Processing-3.0

Hey not only can I run sketches in ruby-processing-3.0 using my toxiclibs gem but I can also run them in watch mode, my only problem is can't dispose of the frame of the old sketch (has black background, which is frozen so probably not eating resources). Here I am using jEdit as my ruby-processing ide, which has advantages if you you want test different java versions etc...:-

# A ruby processing-3.0 sketch
#
#
# This example implements a custom VolumetricSpace using an implicit function
# to calculate each voxel. This is slower than the default array or HashMap
# based implementations, but also has much less memory requirements and so might
# be an interesting and more viable approach for very highres voxel spaces
# (e.g. >32 million voxels). This implementation here also demonstrates how to
# achieve an upper boundary on the iso value (in addition to the one given and
# acting as lower threshold when computing the iso surface)
#
# Usage:
# move mouse to rotate camera
# w: toggle wireframe on/off
# -/=: zoom in/out
# l: apply laplacian mesh smooth
#
#

#
# Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012
# This sketch relies on a custom toxiclibscore library for PovRAY export
#
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http://creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

require 'toxiclibs'
load_library :vecmath
RES = 64
ISO = 0.2
MAX_ISO = 0.66
attr_reader :mesh, :gfx, :curr_zoom, :is_wire_frame

def setup
  sketch_title 'Isosurface'
  ArcBall.init(self)
  @gfx = Gfx::ToxiclibsSupport.new(self)
  vol = EvaluatingVolume.new(TVec3D.new(400, 400, 400), RES, RES, RES, MAX_ISO)
  surface = Volume::HashIsoSurface.new(vol)
  @mesh = WETriangleMesh.new
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
end

def settings
  size(720, 720, P3D)
end

def draw
  background(200, 0, 200)
  if is_wire_frame
    no_fill
    stroke(255)
  else
    fill(255)
    no_stroke
    define_lights
    lights
  end
  @gfx.mesh(mesh, true)
end

def key_pressed
  case key
  when 'w', 'W'
    @is_wire_frame = !is_wire_frame
  when 'l', 'L'
    LaplacianSmooth.new.filter(mesh, 1)
  when 's', 'S'
    save_frame('implicit.png')
  end
end

def define_lights
  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

# Creating a volumetric space class
#
class EvaluatingVolume < Volume::VolumetricSpace
  include Processing::Proxy
  attr_reader :upper_bound
  FREQ = PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
  end

  def clear
    # nothing to do here
  end

  def getVoxelAt(i)
    getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes)
  end

  def getVoxel(x, y, z)  # can't overload so we renamed
    val = 0
    if x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1
      xx = x * 1.0 / resX - 0.5  # NB: careful about integer division !!!
      yy = y * 1.0 / resY - 0.5
      zz = z * 1.0 / resZ - 0.5
      val = cos(xx * FREQ) * sin(yy * FREQ) + cos(yy * FREQ) * sin(zz* FREQ) + cos(zz * FREQ) * sin(xx * FREQ)
      # val = sin(xx * FREQ) + cos(yy * FREQ) + sin(zz * FREQ)
      # val = sin(xx * FREQ) * (xx * FREQ) + sin(yy * FREQ) * (yy * FREQ) + sin(zz * FREQ) * (zz * FREQ)
      val = 0 if val > upper_bound
    end
    val
  end
end

Wednesday, 25 March 2015

Implicit isosurface sketch re-worked for JRubyArt

Since the release of the toxiclibs gem (example below use gem version 0.3.0.pre), it is now quite easy to work with the toxiclibs libraries in JRubyArt see sketch below. This being a bare sketch should be run with 'k9 run implicit.rb'

#
# This example implements a custom VolumetricSpace uMath.sing an implicit function
# to calculate each voxel. This is slower than the default array or HashMap
# based implementations, but also has much less memory requirements and so might
# be an interesting and more viable approach for very highres voxel spaces
# (e.g. >32 million voxels). This implementation here also demonstrates how to
# achieve an upper boundary on the iso value (in addition to the one given and
# acting as lower threshold when computing the iso surface)
#
# Usage:
# drag mouse to rotate camera
# mouse wheel zoom in/out
# l: apply laplacian mesh smooth
# 
#

# 
# Copyright (c) 2010 Karsten Schmidt & ruby-procesMath.sing version Martin Prout 2013
# This sketch relies on a custom ruby-procesMath.sing mesh_to_vbo library
# 
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
# 
# http://creativecommons.org/licenses/LGPL/2.1/
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

require 'toxiclibs'

load_library :mesh_to_vbo

RES = 64
ISO = 0.2
MAX_ISO = 0.66

attr_reader :mesh, :vbo, :curr_zoom, :implicit

def setup
  size(720,720, P3D)
  Processing::ArcBall.init(self)
  @vbo = MeshToVBO.new(self)
  @curr_zoom = 1
  vol = EvaluatingVolume.new(TVec3D.new(400,400,400), RES, RES, RES, MAX_ISO)
  surface = Volume::HashIsoSurface.new(vol)
  @mesh = Toxi::WETriangleMesh.new
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
  no_stroke
  @implicit = vbo.meshToVBO(mesh, true)
  implicit.setFill(color(222, 222, 222))
  implicit.setAmbient(color(50, 50, 50))
  implicit.setShininess(color(10, 10, 10))
  implicit.setSpecular(color(50, 50, 50))
end

def draw
  background(0)
  lights
  define_lights
  shape(implicit)
end

def key_pressed
  case key
  when 'l', 'L'
    Toxi::LaplacianSmooth.new.filter(mesh, 1)
    @implicit = vbo.meshToVBO(mesh, true)
    # new mesh so need to set finish
    implicit.setFill(color(222, 222, 222))
    implicit.setAmbient(color(50, 50, 50))
    implicit.setShininess(color(10, 10, 10))
    implicit.setSpecular(color(50, 50, 50))
  when 's', 'S'
    save_frame("implicit.png")
  end
end

def define_lights
  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

class EvaluatingVolume < Volume::VolumetricSpace

  attr_reader :upper_bound, :lut
  FREQ = Math::PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
  end

  def clear
    # nothing to do here
  end

  def getVoxelAt(i)
    getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes)
  end

  def getVoxel(x, y, z)  # can't overload so we renamed
    val = 0
    if (x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1)
      xx = x * 1.0 / resX - 0.5  # NB: careful about integer division !!!
      yy = y * 1.0 / resY - 0.5
      zz = z * 1.0 / resZ - 0.5
      #val = Math.sin(xx * FREQ) + Math.cos(yy * FREQ) + Math.sin(zz * FREQ)
      val = Math.cos(xx * FREQ) * Math.sin(yy* FREQ) + Math.cos(yy* FREQ) * Math.sin(zz* FREQ) + Math.cos(zz* FREQ)* Math.sin(xx* FREQ)
      if (val > upper_bound)
        val = 0
      end
    end
    return val
  end
end


The library code:-
############################################
# mesh_to_vbo.rb
# a ruby library to convert toxi.mesh object
# to vbo (PShape) written by Martin Prout
############################################
class MeshToVBO
  PShape = Java::ProcessingCore::PShape
  attr_reader :parent

  def initialize(parent)
    @parent = parent
  end

  def meshToVBO(mesh, smth)
    retained = parent.create_shape
    retained.begin_shape(PShape::TRIANGLES)
    if smth
      mesh.compute_vertex_normals
      mesh.getFaces.each do |f|
        retained.normal(f.a.normal.x, f.a.normal.y, f.a.normal.z)
        retained.vertex(f.a.x, f.a.y, f.a.z)
        retained.normal(f.b.normal.x, f.b.normal.y, f.b.normal.z)
        retained.vertex(f.b.x, f.b.y, f.b.z)
        retained.normal(f.c.normal.x, f.c.normal.y, f.c.normal.z)
        retained.vertex(f.c.x, f.c.y, f.c.z)
      end
    else
      mesh.get_faces.each do |f|
        retained.normal(f.normal.x, f.normal.y, f.normal.z)
        retained.vertex(f.a.x, f.a.y, f.a.z)
        retained.vertex(f.b.x, f.b.y, f.b.z)
        retained.vertex(f.c.x, f.c.y, f.c.z)
      end
    end
    retained.end_shape
    retained
  end

  # variant
  # input array of meshes, output an array of shapes
  def meshToRetained(mesh, smth)
    mesh.map { |m| meshToVBO(m, smth) }
  end
end

Tuesday, 24 March 2015

Toxiclibs gem released

I've just released a toxiclibs gem that can be used with JRubyArt or ruby-processing (examples uses gem version 0.3.0.pre))
# A ruby processing sketch (needs re-factoring for jruby_art)
#
#
# This example implements a custom VolumetricSpace using an implicit function
# to calculate each voxel. This is slower than the default array or HashMap
# based implementations, but also has much less memory requirements and so might
# be an interesting and more viable approach for very highres voxel spaces
# (e.g. >32 million voxels). This implementation here also demonstrates how to
# achieve an upper boundary on the iso value (in addition to the one given and
# acting as lower threshold when computing the iso surface)
#
# Usage:
# drag mouse to rotate camera
# w: toggle wireframe on/off
# mouse wheel to zoom in/out
# l: apply laplacian mesh smooth
#
#

#
# Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012
# This sketch relies on a custom toxiclibscore library for PovRAY export
#
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http://creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

require 'toxiclibs'
load_library 'vecmath' # uncomment this line for ruby-processing
RES = 64
ISO = 0.2
MAX_ISO = 0.66
attr_reader :mesh, :gfx, :curr_zoom, :is_wire_frame

def setup
  size(720, 720, P3D)
  ArcBall.init(self)
  @gfx = Gfx::ToxiclibsSupport.new(self)
  vol = EvaluatingVolume.new(Toxi::Vec3D.new(400, 400, 400), RES, RES, RES, MAX_ISO)
  surface = Volume::HashIsoSurface.new(vol)
  @mesh = Toxi::WETriangleMesh.new
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
end

def draw
  background(0)
  if is_wire_frame
    no_fill
    stroke(255)
  else
    fill(255)
    no_stroke
    define_lights
    lights
  end
  @gfx.mesh(mesh, true)
end

def key_pressed
  case key
  when 'w', 'W'
    @is_wire_frame = !is_wire_frame
  when 'l', 'L'
    Toxi::LaplacianSmooth.new.filter(mesh, 1)
  when 's', 'S'
    save_frame('implicit.png')
  end
end

def define_lights
  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

# Creating a volumetric space class
#
class EvaluatingVolume < Volume::VolumetricSpace
  include Processing::Proxy
  attr_reader :upper_bound
  FREQ = PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
  end

  def clear
    # nothing to do here
  end

  def getVoxelAt(i)
    getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes)
  end

  def getVoxel(x, y, z)  # can't overload so we renamed
    val = 0
    if x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1
      xx = x * 1.0 / resX - 0.5  # NB: careful about integer division !!!
      yy = y * 1.0 / resY - 0.5
      zz = z * 1.0 / resZ - 0.5
      val = cos(xx * FREQ) * sin(yy * FREQ) + cos(yy * FREQ) * sin(zz* FREQ) + cos(zz * FREQ) * sin(xx * FREQ)
      # val = sin(xx * FREQ) + cos(yy * FREQ) + sin(zz * FREQ)
      # val = sin(xx * FREQ) * (xx * FREQ) + sin(yy * FREQ) * (yy * FREQ) + sin(zz * FREQ) * (zz * FREQ)
      val = 0 if val > upper_bound
    end
    val
  end
end

Wednesday, 29 October 2014

Toxiclibs (or any other processing library) as a rubygem

As a proof of principle I have created gem that wraps some of Karsten Schmidts (aka toxi @toxi, aka postspectacular k@thi.ng) toxiclibs as a ruby-gem. This gem has been shown to usable with ruby-processing (or its development version JRubyArt) but if yokolet is right it can also be used with clojure, and other jvm languages (by using JRuby Embed, formerly RedBridge). Here is more on RedBridge and clojure.
require 'toxiclibs'

include Toxi

#
# <p>GrayScottToneMap shows how to use the ColorGradient & ToneMap classes of the
# colorutils package to create a tone map for rendering the results of
# the Gray-Scott reaction-diffusion.</p>
#
# <p><strong>Usage:</strong><ul>
# <li>click + drag mouse to draw dots used as simulation seed</li>
# <li>press any key to reset</li>
# </ul></p>
#

#
# Copyright (c) 2010 Karsten Schmidt
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http://creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

NUM_ITERATIONS = 10

attr_reader :gs, :tone_map

def setup
  size(256,256)
  @gs= Toxi::GrayScott.new width,height, false
  @gs.set_coefficients 0.021, 0.076, 0.12, 0.06
  # create a color gradient for 256 values
  grad = Toxi::ColorGradient.new
  # NamedColors are preset colors, but any TColor can be added
  # see javadocs for list of names:
  # http://toxiclibs.org/docs/colorutils/toxi/color/NamedColor.html
  # NB: use '::' in place of '.' here for these java constants
  grad.add_color_at(0, Toxi::NamedColor::BLACK)
  grad.add_color_at(128, Toxi::NamedColor::RED)
  grad.add_color_at(192, Toxi::NamedColor::YELLOW)
  grad.add_color_at(255, Toxi::NamedColor::WHITE)
  # this gradient is used to map simulation values to colors
  # the first 2 parameters define the min/max values of the
  # input range (Gray-Scott produces values in the interval of 0.0 - 0.5)
  # setting the max = 0.33 increases the contrast
  @tone_map = Toxi::ToneMap.new 0, 0.33, grad
end

def draw
  @gs.set_rect(mouse_x, mouse_y, 20, 20) if mouse_pressed?
  load_pixels
  # update the simulation a few time steps
  NUM_ITERATIONS.times { @gs.update(1) }
  # read out the V result array
  # and use tone map to render colours
  gs.v.length.times do |i|
    pixels[i]=tone_map.getARGBToneFor(gs.v[i])  # NB: don't camel case convert here
  end
  update_pixels
end

def key_pressed
  @gs.reset
end



Here is another example running a JRubyArt sketch from netbeans.

Friday, 14 December 2012

MeshToVBO conversion ruby-processing

Since processing-2.0 there is the possibility of converting 3D meshes to vertical buffer objects (vbo) as instances of PShape. Here I explore a toxiclibs example sketch that instead of using toxis gfx package to render the shapes converts the shapes to vbo using a ruby library (albeit with a Processing::Proxy mixin. Here is the library:-
############################################
# mesh_to_vbo.rb
# a ruby library to convert toxi.mesh object
# to vbo (PShape) written by Martin Prout
# make use of Processing::Proxy mixin
############################################
class MeshToVBO
    include Processing::Proxy

    attr_reader :parent

    def initialize(parent)
        @parent = parent
    end

    def meshToVBO(mesh, smth)
        retained = parent.createShape(PShape::TRIANGLES)
        retained.enableStyle()
        retained.fill(222, 222, 222)
        retained.ambient(50)
        retained.shininess(10)
        retained.specular(50)
        if (smth)
            mesh.computeVertexNormals()
            mesh.getFaces.each do |f|
                retained.normal(f.a.normal.x, f.a.normal.y, f.a.normal.z)
                retained.vertex(f.a.x, f.a.y, f.a.z)
                retained.normal(f.b.normal.x, f.b.normal.y, f.b.normal.z)
                retained.vertex(f.b.x, f.b.y, f.b.z)
                retained.normal(f.c.normal.x, f.c.normal.y, f.c.normal.z)
                retained.vertex(f.c.x, f.c.y, f.c.z)
            end
        else
            mesh.getFaces.each do |f|
                retained.normal(f.normal.x, f.normal.y, f.normal.z)
                retained.vertex(f.a.x, f.a.y, f.a.z)
                retained.vertex(f.b.x, f.b.y, f.b.z)
                retained.vertex(f.c.x, f.c.y, f.c.z)
            end
        end
        retained.end()
        return retained
    end

    # variant
    # input array of meshes, output an array of shapes
    def meshToRetained(mesh, smth)
        rshapes = []
        (0 ... mesh.length).each do |i|
            rshapes.push(meshToVBO(mesh[i], smth))
        end
        return rshapes
    end
end



Here is the example sketch:-
# This example demonstrates how to rotate a number of meshes
# so that each points towards a common & user controlled focal point.
#
# Requires toxiclibs-0020 or newer
#
# (c) 2012 Karsten Schmidt / LGPL2 licensed
#
load_libraries 'toxiclibscore', 'vbo'

include_package 'toxi.geom'
include_package 'toxi.geom.mesh'

# container for mesh positions

attr_reader :positions, :vbo

def setup
  size(640,480,P3D)
  @vbo = MeshToVBO.new(self)
  @positions = []
  # compute mesh positions on circle in XZ plane
  (Circle.new(200).toPolygon2D(8)).each do |p|
    positions.push(p.to3DXZ)
  end
end

def draw
  background(51)
  lights
  no_stroke
  translate(width/2,height/2,0)
  rotate_x(-PI/6)
  # create manual focal point in XY plane
  focus = Vec3D.new((mouse_x - width/2), (mouse_y - height/2), 0)
  # create mesh prototype to draw at all generated positions
  # the mesh is a simple box placed at the world origin
  m = AABB.new(25).to_mesh
  # draw focus
  shape(vbo.meshToVBO(AABB.new(focus, 5).to_mesh, false))
  positions.each do |p|
    # align the positive z-axis of mesh to point at focus
    # mesh needs to be located at world origin for it to work correctly
    # only once rotated, move it to actual position
    shape(vbo.meshToVBO(m.copy.pointTowards(focus.sub(p), Vec3D::Z_AXIS).translate(p), false))
  end
  # draw connections from mesh centers to focal point
  stroke(0,255,255)
  positions.each do |p|
    line(p.x, p.y, p.z, focus.x, focus.y, focus.z)
  end
end

Monday, 12 March 2012

Toxiclibs InflateMesh in ruby-processing (with PovRAY export)

See vanilla processing version here for PovRAY files and a ray-traced image.
#
# Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
# 
# http://creativecommons.org/licenses/LGPL/2.1/
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

load_libraries 'toxiclibscore', 'toxiclibs_p5', 'volumeutils', 'control_panel', 'povmesh', 'verletphysics'
include_package 'povmesh.mesh'
include_package 'toxi.geom'
include_package 'toxi.geom.mesh'
include_package 'toxi.physics'
include_package 'toxi.physics.behaviors'
include_package 'toxi.processing'
include_package 'toxi.physics.constraints'
include_package 'toxi.volume'

attr_reader :mesh, :gfx, :physics, :render, :box, :pm, :inflate

def setup
  size(680, 382, P3D)
  @gfx = ToxiclibsSupport.new(self)
  @pm = POVMesh.new(self)
  @render = true
  init_physics
end

def draw
  physics.update
  box.vertices.values().each { |v| v.set(physics.particles.get(v.id))}
  box.center(nil)
  box.vertices.values().each { |v| physics.particles.get(v.id).set(v)}
  box.compute_face_normals()
  box.face_outwards()
  box.compute_vertex_normals()
  background(51)
  translate(width / 2.0, height / 2.0, 0)
  rotate_x((height / 2 - mouse_x) * 0.01)
  rotate_y((width / 2 - mouse_y) * 0.01)
  no_fill
  lights
  directional_light(255, 255, 255, -200, 1000, 500)
  specular(255)
  shininess(16)
  gfx.origin(Vec3D.new, 50)
  fill(192)
  no_stroke
  if (render)
    gfx.mesh(box, true, 5)
  else
    no_loop
    pm.begin_save(java.io.File.new(sketchPath("box.inc")))
    pm.set_texture(Textures::METAL) # sequential rainbow color 1st is a red?
    pm.saveAsPOV(box)   # default smooth ie normals
    pm.end_save
    exit # no way back
  end
end

def init_physics
  @box = WETriangleMesh.new
  # create a simple start mesh
  #box.addMesh(Cone.new(Vec3D.new(0, 0, 0), Vec3D.new(0, 1, 0), 10, 50, 100).to_mesh(4))
  box.add_mesh(AABB.new(Vec3D.new, 50).to_mesh())
  # then subdivide a few times...
  4.times { box.subdivide }
  @physics = VerletPhysics.new
  physics.set_world_bounds(AABB.new(Vec3D.new, 180))
  # turn mesh vertices into physics particles
  box.vertices.values.each { |v| physics.add_particle(VerletParticle.new(v))}
  # turn mesh edges into springs
  box.edges.values.each do |e|
    a = physics.particles.get((e.a).id)
    b = physics.particles.get((e.b).id)
    physics.add_spring(VerletSpring.new(a, b, a.distance_to(b), 0.005))
  end
end


def key_pressed
  case key
  when 'r'
    init_physics
  when 's'
    @render = false
  end
end

def mouse_pressed
    @inflate = AttractionBehavior.new(Vec3D.new, 400, -0.3, 0.001)
  physics.add_behavior(inflate);
end

def mouse_released
  physics.remove_behavior(inflate)
end

Saturday, 4 February 2012

Using the Control Panel With Toxiclibs

Here is a recent sketch modified to use the ruby processing control panel (doesn't want to work with opengl with current release), next obvious variant to include sliders for resolution and or upper and lower boundary levels.
#
# This example implements a custom VolumetricSpace using an implicit function
# to calculate each voxel. This is slower than the default array or HashMap
# based implementations, but also has much less memory requirements and so might
# be an interesting and more viable approach for very highres voxel spaces
# (e.g. >32 million voxels). This implementation here also demonstrates how to
# achieve an upper boundary on the iso value (in addition to the one given and
# acting as lower threshold when computing the iso surface)
#
# Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012
# This sketch relies on a custom toxiclibscore library for PovRAY export, and
# features the ruby-processing control panel (doesn't seem to work with opengl)
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
# 
# http://creativecommons.org/licenses/LGPL/2.1/
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

load_libraries 'toxiclibscore', 'toxiclibs_p5', 'volumeutils', 'control_panel'
include_package 'toxi.geom'
include_package 'toxi.geom.mesh'
include_package 'toxi.volume'
include_package 'toxi.processing'

RES = 64
ISO = 0.2
MAX_ISO = 0.66

attr_reader :mesh, :gfx, :is_wire_frame, :curr_zoom

def setup()
  size(720,720, P3D)
  @curr_zoom = 1.0
  control_panel do |c|
    c.title = "Implicit Function"
    c.slider :curr_zoom, 0.5..4.0, 1.0
    c.button :wire_frame_view
    c.button :smooth
    c.button :save_sketch_view
    c.button :export_to_povray
    c.button :exit!
  end
  @gfx = ToxiclibsSupport.new(self)
  vol = EvaluatingVolume.new(Vec3D.new(400,400,400), RES, RES, RES, MAX_ISO)
  surface = HashIsoSurface.new(vol)
  @mesh = WETriangleMesh.new()
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
end

def draw()
  background(0)
  translate(width / 2.0, height / 2.0, 0)
  rotate_x(mouse_y * 0.01)
  rotate_y(mouse_x * 0.01)
  scale(curr_zoom)
  if (is_wire_frame)
    no_fill()
    stroke(255)
  else
    fill(255)
    no_stroke()
    lights()
  end
  @gfx.mesh(mesh, true)
end

def wire_frame_view
  @is_wire_frame = !is_wire_frame
end

def smooth
  LaplacianSmooth.new().filter(mesh, 1)
end

def export_to_povray
  file_id = "implicit"
  pw = create_writer(file_id + ".inc")
  mesh.saveAsPOV(pw)
  pw.flush()
  pw.close()
  exit()
end

def save_sketch_view
  save_frame("implicit.png")
end

class EvaluatingVolume < VolumetricSpace
  include Processing::Proxy
  include_package 'toxi.math'
  attr_reader :upper_bound, :lut
  FREQ = PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
    @lut=SinCosLUT.new()
  end

  def clear()
    # nothing to do here
  end

  def getVoxelAt(i)
    getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes)
  end

  def getVoxel(x, y, z)  # can't overload so we renamed
    val = 0
    if (x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1)
      xx = x * 1.0 / resX - 0.5  # NB: careful about integer division !!!
      yy = y * 1.0 / resY - 0.5
      zz = z * 1.0 / resZ - 0.5
      #val = lut.sin(xx * FREQ) + lut.cos(yy * FREQ) + lut.sin(zz * FREQ)
      val = lut.sin(xx * FREQ) * (xx * FREQ) + lut.sin(yy * FREQ) * (yy * FREQ) + lut.sin(zz * FREQ) * (zz * FREQ)
      #val = lut.cos(xx * FREQ) * lut.sin(yy* FREQ) + lut.cos(yy* FREQ) * lut.sin(zz* FREQ) + lut.cos(zz* FREQ)* lut.sin(xx* FREQ);
      if (val > upper_bound)
        val = 0
      end
    end
    return val
  end
end
Screenshot of ruby processing sketch

Using toxiclibs, volumeutils etc in Ruby-Processing

Here is a much more complicated example using toxiclibs in ruby-processing (once more relies on my custom toxiclibscore for the PovRAY export). However you can easily remove that part of the sketch if just want to experiment with using toxiclibs in ruby-processing. This example features the implementation of an abstract java class (namely VolumetricSpace) in jruby.
#
# This example implements a custom VolumetricSpace using an implicit function
# to calculate each voxel. This is slower than the default array or HashMap
# based implementations, but also has much less memory requirements and so might
# be an interesting and more viable approach for very highres voxel spaces
# (e.g. >32 million voxels). This implementation here also demonstrates how to
# achieve an upper boundary on the iso value (in addition to the one given and
# acting as lower threshold when computing the iso surface)
#
# Usage:
# move mouse to rotate camera
# w: toggle wireframe on/off
# -/=: zoom in/out
# l: apply laplacian mesh smooth
# 
#

# 
# Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012
# This sketch relies on a custom toxiclibscore library for PovRAY export
# 
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
# 
# http://creativecommons.org/licenses/LGPL/2.1/
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

load_libraries 'toxiclibscore', 'toxiclibs_p5', 'volumeutils', 'opengl'
include_package 'processing.opengl'
include_package 'toxi.geom'
include_package 'toxi.geom.mesh'
include_package 'toxi.volume'
include_package 'toxi.processing'

RES = 64
ISO = 0.2
MAX_ISO = 0.66

attr_reader :mesh, :gfx, :curr_zoom, :is_wire_frame

def setup()
  size(720,720, (library_loaded?(:opengl) ? OPENGL : P3D))
  @gfx = ToxiclibsSupport.new(self)
  @curr_zoom = 1
  vol = EvaluatingVolume.new(Vec3D.new(400,400,400), RES, RES, RES, MAX_ISO)
  surface = HashIsoSurface.new(vol)
  @mesh = WETriangleMesh.new()
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
end

def draw()
  background(0)
  translate(width / 2.0, height / 2.0, 0)
  rotate_x(mouse_y * 0.01)
  rotate_y(mouse_x * 0.01)
  scale(curr_zoom)
  if (is_wire_frame)
    no_fill()
    stroke(255)
  else
    fill(255)
    no_stroke()
    lights()
  end
  @gfx.mesh(mesh, true)
end

def key_pressed()
  case key
  when 'w', 'W'
    @is_wire_frame = !is_wire_frame
  when '-'
    @curr_zoom -= 0.1
  when'='
    @curr_zoom += 0.1
  when 'l', 'L'
    LaplacianSmooth.new().filter(mesh, 1)
  when 'e', 'E'
    file_id = "implicit"
    pw = create_writer(file_id + ".inc")
    mesh.saveAsPOV(pw)
    pw.flush()
    pw.close()
    exit()
  when 's', 'S'
    save_frame("implicit.png")
  end
end

class EvaluatingVolume < VolumetricSpace
  include Processing::Proxy
  include_package 'toxi.math'
  attr_reader :upper_bound, :lut
  FREQ = PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
    @lut=SinCosLUT.new()
  end

  def clear()
    # nothing to do here
  end

  def getVoxelAt(i)
    getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes)
  end

  def getVoxel(x, y, z)  # can't overload so we renamed
    val = 0
    if (x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1)
      xx = x * 1.0 / resX - 0.5  # NB: careful about integer division !!!
      yy = y * 1.0 / resY - 0.5
      zz = z * 1.0 / resZ - 0.5
      val = lut.sin(xx * FREQ) + lut.cos(yy * FREQ) + lut.sin(zz * FREQ)
      #val = Math.sin(xx * FREQ) * (xx * FREQ) + Math.sin(yy * FREQ) * (yy * FREQ) + Math.sin(zz * FREQ) * (zz * FREQ)
      if (val > upper_bound)
        val = 0
      end
    end
    return val
  end
end

opengl rendered implicit function

Tuesday, 3 May 2011

Winged Edge Mesh and Physics from Toxiclibs

Here is the toxiclibs InflateMesh verlet physics example demonstrating winged mesh class functionality. Although this was a P3D sketch in the example it looks best using OPENGL. Because there is still an issue with linux and ruby-processing OPENGL at less than full_screen, I have scaled some of the variables in the sketch (mainly annotated I think), anyway you should probably look at the original (included in the library download toxiclibs-complete version 0020).



# <p>This example uses the attraction behavior to inflate a 3D mesh.
# The mesh vertices are re-created as physics particles and connected
# using springs. Upon mouse press the inflation force is applied,
# counteracting the forces created by the springs, causing the mesh to
# expand and deform.</p>
#
# <p>Usage: Click and hold mouse button to inflate mesh</p>
#

############
# Copyright (c) 2010 Karsten Schmidt translated to ruby processing by Martin Prout (Spring 2011)
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http:#creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
class InflateMesh < Processing::App
  load_libraries 'toxiclibs_p5', 'toxiclibscore', 'verletphysics', 'opengl'
  include_package 'processing.opengl'
  include_package 'toxi.geom'
  include_package 'toxi.geom.mesh.subdiv'
  include_package 'toxi.geom.mesh'
  include_package 'toxi.physics'
  include_package 'toxi.physics.behaviors'
  include_package 'toxi.physics.constraints'
  include_package 'toxi.processing'

  attr_reader :physics, :inflate, :box, :gfx
  full_screen

  def setup
    setup_opengl
    @gfx = ToxiclibsSupport.new(self)
    init_physics
  end

  def draw
    @physics.update()
    for vert in box.vertices.values
      vert.set(physics.particles.get(vert.id))
    end
    box.center(nil)              # nil means centre at 0, 0, 0
    for vert in box.vertices.values
      @physics.particles.get(vert.id).set(vert)
    end
    box.compute_face_normals
    box.face_outwards
    box.compute_vertex_normals
    background(51)
    translate(width / 2, height / 2, 0)
    rotate_x((height / 2 - mouse_y) * 0.01)
    rotate_y((width / 2 - mouse_x) * 0.01)
    no_fill
    lights
    directional_light(255, 255, 255, -200, 1000, 500)
    specular(255)
    shininess(16)
    @gfx.origin(Vec3D.new, 300)  # scaled up axes display from 50
    fill(192)
    no_stroke
    @gfx.mesh(box, true, 10)     # scaled up normals display from 5
  end

  def init_physics
    @box = WETriangleMesh.new
    # create a simple start mesh
    #box.add_mesh(Cone.new(Vec3D.new(0, 0, 0), Vec3D.new(0, 1, 0), 10, 50, 100).to_mesh(4))
    @box.add_mesh(AABB.new(Vec3D.new, 150).to_mesh)      # scaled up from 50
    # then subdivide a few times...
    @box.subdivide
    @box.subdivide
    @box.subdivide
    @box.subdivide
    @physics = VerletPhysics.new
    @physics.set_world_bounds(AABB.new(Vec3D.new, 540))  # scaled up from 180
    # turn mesh vertices into physics particles
    for vert in box.vertices.values
      @physics.add_particle(VerletParticle.new(vert))
    end
    # turn mesh edges into springs
    for w_edge in box.edges.values
      vp_a = physics.particles.get(w_edge.a.id)
      vp_b = physics.particles.get(w_edge.b.id)
      @physics.add_spring(VerletSpring.new(vp_a, vp_b, vp_a.distance_to(vp_b), 0.05))
    end
  end

  def key_pressed     # using a switch anticipating more key actions
    case key
    when 'i'
    init_physics
    when 's'
    save_frame "inflate.png"
    end
  end


  def mouse_pressed
    @inflate=AttractionBehavior.new(Vec3D.new, 400, -0.3, 0.001)
    @physics.add_behavior(inflate)
  end

  def mouse_released
    @physics.remove_behavior(inflate)
  end

  def setup_opengl
    render_mode OPENGL
    hint ENABLE_OPENGL_4X_SMOOTH     # optional
    hint DISABLE_OPENGL_ERROR_REPORT # optional
  end
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