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

Friday, 26 June 2015

When to use Processing::Proxy

Just in case it has passed you by, processing uses java inner classes to give your own classes access to the PApplet methods/variables. For ruby-processing you can include the Processing::Proxy module to give you a similar level of access, note the width and height variables are not available (you can use $app.width and $app.height to get round that). This all very non OO so you should use it only when you need it:-

Wednesday, 17 June 2015

This Hemesh Sketch Forced me to use JRuby Edges

New improved, updated and dried since 22 September 2015

load_library :hemesh

# Module used to include java packages
module MS
  include_package 'wblut.math'
  include_package 'wblut.processing'
  include_package 'wblut.hemesh'
  include_package 'wblut.geom'
end

NUM = 50
attr_reader :mesh, :render, :points

def setup
  sketch_title 'Sponge'
  ArcBall.init(self)
  create_mesh
  @render = MS::WB_Render3D.new(self)
end

def draw
  background(255)
  directional_light(255, 255, 255, 1, 1, -1)
  directional_light(127, 127, 127, -1, -1, 1)
  stroke(0)
  render.draw_edges(mesh)
  no_stroke
  render.draw_faces(mesh)
end

def mouse_pressed
  create_mesh
end

def create_mesh
  rs = MS::WB_RandomOnSphere.new
  creator = MS::HEC_ConvexHull.new
  @points = (0..NUM).map { rs.next_point.mul_self(300.0) }
  creator.set_points(points)
  creator.setN(NUM)
  @mesh = MS::HE_Mesh.new(creator)
  @mesh = MS::HE_Mesh.new(MS::HEC_Dual.new(mesh))
  ext = MS::HEM_Extrude.new.set_chamfer(25).set_relative(false)
  mesh.modify(ext)
  sel = ext.extruded
  ext = MS::HEM_Extrude.new.set_distance(-40)
  mesh.modify_selected(ext, sel)
  mesh.smooth(2)
end

def settings
  size(800, 800, P3D)
  smooth(8)
end

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

Sunday, 14 June 2015

Full Screen Sketch in Ruby-Processing-3.0

Nice clean interface for fullscreen in processing-3.0a10, lets hope they keep it. PS: works a treat on Mint linux ie properly fullscreen no nasty menu-bar
# Description:
# This is a full-screen demo 
# Since processing-3.0a10 set in settings

class FullScreen < Processing::App
  def setup
    sketch_title 'Full Screen'
    no_stroke
  end

  def draw
    lights
    background 0
    fill 120, 160, 220
    (width/100).times do |x|
      (height/100).times do |y|
        new_x, new_y = x * 100, y * 100
        push_matrix
        translate new_x + 50, new_y + 50
        rotate_y(((mouse_x.to_f + new_x) / width) * Math::PI)
        rotate_x(((mouse_y.to_f + new_y) / height) * Math::PI)
        box 90
        pop_matrix
      end
    end
  end

  def settings
    full_screen P3D
  end
end

PS: does not need to be a class wrapped sketch any-more, we are not passing any parameters at runtime.

FX2D works better than JAVA2D on ruby-processing-3.0

Interesting pre-processing of pde sketches to move "size" from "setup" to "settings" does not seem to be implemented for FX2D sketches see sketch:-
FX2D sketch run from processing ide



















Possibly because of a Ben Fry kludge JAVA2D isn't playing well in ruby-processing-3.0 (but then I tried FX2D) and happiness ensued shame about the opengl sketches jumping around
def setup
  sketch_title 'FX2D'
end

def draw
  background 0
  fill 200, 0, 0
  ellipse width / 2, height / 2, 300, 200
end

def settings
  size 400, 300, FX2D
end


Sketch run from ruby-processing-3.0

Saturday, 6 June 2015

Prototyping glsl shader sketches in ruby-processing

Well actually there's an argument for sticking with ruby-processing since there is not much of a performance improvement to be gained by using vanilla processing. I have now included some modififications to ruby-processing that give it the "winning edge" for developing shader sketches in the since ruby-processing-2.6.11 the watch mode has been extended to monitor changes to the glsl shader code (in addition to the ruby code).
                                                        
rp5 watch edge_detect_capture.rb                                                                                                                                               

                                                        
vim data/edge.glsl                                                                                                                                               


// Original shader by Ken Slade
// https://www.shadertoy.com/view/ldsSWr

// Ported to Processing by Raphaël de Courville <twitter: @sableRaph>

#ifdef GL_ES
precision highp float;
#endif

uniform sampler2D texture; // iChannel0 in Shadertoy
uniform vec2 sketchSize; // iResolution in Shadertoy

//options are edge, colorEdge, or trueColorEdge
#define EDGE_FUNC edge


//options are KAYYALI_NESW, KAYYALI_SENW, PREWITT, ROBERTSCROSS, SCHARR, or SOBEL
#define SOBEL

// Use these parameters to fiddle with settings
#ifdef SCHARR
#define STEP 0.15
#else
#define STEP 1.0
#endif


#ifdef KAYYALI_NESW
const mat3 kayyali_NESW = mat3(-6.0, 0.0, 6.0,
                               0.0, 0.0, 0.0,
                               6.0, 0.0, -6.0);
#endif
#ifdef KAYYALI_SENW
const mat3 kayyali_SENW = mat3(6.0, 0.0, -6.0,
                               0.0, 0.0, 0.0,
                               -6.0, 0.0, 6.0);
#endif
#ifdef PREWITT
// Prewitt masks (see http://en.wikipedia.org/wiki/Prewitt_operator)
const mat3 prewittKernelX = mat3(-1.0, 0.0, 1.0,
                                 -1.0, 0.0, 1.0,
                                 -1.0, 0.0, 1.0);

const mat3 prewittKernelY = mat3(1.0, 1.0, 1.0,
                                 0.0, 0.0, 0.0,
                                 -1.0, -1.0, -1.0);
#endif
#ifdef ROBERTSCROSS
// Roberts Cross masks (see http://en.wikipedia.org/wiki/Roberts_cross)
const mat3 robertsCrossKernelX = mat3(1.0, 0.0, 0.0,
                                      0.0, -1.0, 0.0,
                                      0.0, 0.0, 0.0);
const mat3 robertsCrossKernelY = mat3(0.0, 1.0, 0.0,
                                      -1.0, 0.0, 0.0,
                                      0.0, 0.0, 0.0);
#endif
#ifdef SCHARR
// Scharr masks (see http://en.wikipedia.org/wiki/Sobel_operator#Alternative_operators)
const mat3 scharrKernelX = mat3(3.0, 10.0, 3.0,
                                0.0, 0.0, 0.0,
                                -3.0, -10.0, -3.0);

const mat3 scharrKernelY = mat3(3.0, 0.0, -3.0,
                                10.0, 0.0, -10.0,
                                3.0, 0.0, -3.0);
#endif
#ifdef SOBEL
// Sobel masks (see http://en.wikipedia.org/wiki/Sobel_operator)
const mat3 sobelKernelX = mat3(1.0, 0.0, -1.0,
                               2.0, 0.0, -2.0,
                               1.0, 0.0, -1.0);

const mat3 sobelKernelY = mat3(-1.0, -2.0, -1.0,
                               0.0, 0.0, 0.0,
                               1.0, 2.0, 1.0);
#endif

//performs a convolution on an image with the given kernel
float convolve(mat3 kernel, mat3 image) {
    float result = 0.0;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            result += kernel[i][j]*image[i][j];
        }
    }
    return result;
}

//helper function for colorEdge()
float convolveComponent(mat3 kernelX, mat3 kernelY, mat3 image) {
    vec2 result;
    result.x = convolve(kernelX, image);
    result.y = convolve(kernelY, image);
    return clamp(length(result), 0.0, 255.0);
}

//returns color edges using the separated color components for the measure of intensity
//for each color component instead of using the same intensity for all three.  This results
//in false color edges when transitioning from one color to another, but true colors when
//the transition is from black to color (or color to black).
vec4 colorEdge(float stepx, float stepy, vec2 center, mat3 kernelX, mat3 kernelY) {
    //get samples around pixel
    vec4 colors[9];
    colors[0] = texture2D(texture,center + vec2(-stepx,stepy));
    colors[1] = texture2D(texture,center + vec2(0,stepy));
    colors[2] = texture2D(texture,center + vec2(stepx,stepy));
    colors[3] = texture2D(texture,center + vec2(-stepx,0));
    colors[4] = texture2D(texture,center);
    colors[5] = texture2D(texture,center + vec2(stepx,0));
    colors[6] = texture2D(texture,center + vec2(-stepx,-stepy));
    colors[7] = texture2D(texture,center + vec2(0,-stepy));
    colors[8] = texture2D(texture,center + vec2(stepx,-stepy));

    mat3 imageR, imageG, imageB, imageA;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            imageR[i][j] = colors[i*3+j].r;
            imageG[i][j] = colors[i*3+j].g;
            imageB[i][j] = colors[i*3+j].b;
            imageA[i][j] = colors[i*3+j].a;
        }
    }

    vec4 color;
    color.r = convolveComponent(kernelX, kernelY, imageR);
    color.g = convolveComponent(kernelX, kernelY, imageG);
    color.b = convolveComponent(kernelX, kernelY, imageB);
    color.a = convolveComponent(kernelX, kernelY, imageA);

    return color;
}

//finds edges where fragment intensity changes from a higher value to a lower one (or
//vice versa).
vec4 edge(float stepx, float stepy, vec2 center, mat3 kernelX, mat3 kernelY){
    // get samples around pixel
    mat3 image = mat3(length(texture2D(texture,center + vec2(-stepx,stepy)).rgb),
                      length(texture2D(texture,center + vec2(0,stepy)).rgb),
                      length(texture2D(texture,center + vec2(stepx,stepy)).rgb),
                      length(texture2D(texture,center + vec2(-stepx,0)).rgb),
                      length(texture2D(texture,center).rgb),
                      length(texture2D(texture,center + vec2(stepx,0)).rgb),
                      length(texture2D(texture,center + vec2(-stepx,-stepy)).rgb),
                      length(texture2D(texture,center + vec2(0,-stepy)).rgb),
                      length(texture2D(texture,center + vec2(stepx,-stepy)).rgb));
    vec2 result;
    result.x = convolve(kernelX, image);
    result.y = convolve(kernelY, image);

    float color = clamp(length(result), 0.0, 255.0);
    return vec4(color);
}

//Colors edges using the actual color for the fragment at this location
vec4 trueColorEdge(float stepx, float stepy, vec2 center, mat3 kernelX, mat3 kernelY) {
    vec4 edgeVal = edge(stepx, stepy, center, kernelX, kernelY);
    return edgeVal * texture2D(texture,center);
}

void main( void ){
    vec2 uv = gl_FragCoord.xy / sketchSize.xy;
    vec4 color = texture2D(texture, uv.xy);
#ifdef KAYYALI_NESW
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            kayyali_NESW, kayyali_NESW);
#endif
#ifdef KAYYALI_SENW
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            kayyali_SENW, kayyali_SENW);
#endif
#ifdef PREWITT
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            prewittKernelX, prewittKernelY);
#endif
#ifdef ROBERTSCROSS
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            robertsCrossKernelX, robertsCrossKernelY);
#endif
#ifdef SOBEL
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            sobelKernelX, sobelKernelY);
#endif
#ifdef SCHARR
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            scharrKernelX, scharrKernelY);
#endif
}

Edit the above code to change say "#define EDGE_FUNCTION edge" to edgeTrueColor say... save the changes
                                                        
:w                                                                                                                                               
And the sketch reloads with new EDGE_FUNCTION brilliant....
To get you started checkout my fork of Filters4Processing (by Raphaël de Courville) and Andrés Colubri blog. See also processing tutorial. Find shaders from shadertoy (that Raphaël has shown we can translate for processing) check this link. See also glsl syntax highlighting in vim other versions available, other editors too.

Monday, 1 June 2015

More Experiments with Java Reflection in Ruby-Processing

I think I may have cracked it this time!!! Recently I have created a ruby-processing fork of Filters4Processing, whose examples include running movie sketches. Now in vanilla processing there are these convenience methods void movieEvent(Movie movie) and void captureEvent(Capture capture)that are used to read the movie/capture (presumably a frame at a time prior to draw loop). These are/were not available via ruby-processing, however I have found a way to make it work by creating a java interface (and compiling into my rpextras.jar). Then I reopen the Processing::App class to include the interface and it just works!!!

The Interface

package processing.core;

/**
 * This interface makes it easier/possible to use the reflection methods
 * void captureEvent(Capture capture); and
 * void movieEvent(Movie movie); from ruby-processing
 * @author Martin Prout
 */
public interface VideoInterface {
    void movieEvent(processing.video.Movie movie);
    void captureEvent(processing.video.Capture capture);
}

The video_event video library script

require 'rpextras'

class Processing::App
  include Java::ProcessingCore::VideoInterface
end
Now if those processing guys had only created some decent interfaces in the first place this would have been a bit easier.

Method in Use

#
# Speed. 
#
# Use the Movie.speed method to change
# the playback speed. The video_event library is
# required to use "movieEvent" java reflection method
#

load_libraries :video, :video_event
include_package 'processing.video'

attr_reader :mov

def setup
  size(640, 360)
  background(0)
  @mov = Movie.new(self, "transit.mov")
  mov.loop
end

def draw
  image(mov, 0, 0)
  new_speed = map(mouse_x, 0, width, 0.1, 2)
  mov.speed(new_speed)
  fill(255)
  text("%.2f" % new_speed << "X", 10, 30)
end

# The java reflection method  
def movieEvent(mov)
  mov.read
end

A capture example
load_library :video, :video_event
include_package 'processing.video'
attr_reader :cam, :my_shader

def setup
  size(640, 480, P2D)
  cameras = Capture.list
  @my_shader = load_shader('edge_detect.glsl')
  my_shader.set('sketchSize', width.to_f, height.to_f)
  start_capture(width, height)
end

def start_capture(w, h)
  @cam = Capture.new(self, w, h)
  cam.start
end

def draw
  image(cam, 0, 0)
  return if mouse_pressed?
  filter(my_shader)
end

# The java reflection method  
def captureEvent(cam)
  cam.read
end

Main problem is an introduction of a compile time dependency on processing video.jar, and also seems to be safer to keep camel case for reflection method, paradoxically that may improve in latest processing where the video library becomes an external library.

Followers

Blog Archive

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