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

Showing posts with label JRubyArt. Show all posts
Showing posts with label JRubyArt. Show all posts

Monday, 12 October 2015

Bye Bye Blogger Hello Github

It was nice to be gothic but I've moved onto github.io for blogging about JRubyArt:-

Personal JRubyArt blog



Monkstone

Ruby-Processing group blog



Ruby Processing Group

Wednesday, 16 September 2015

Fraction Sums

# fraction_sums.rb, by Martin Prout
attr_reader :f, :two, :three, :four, :five, :six

def setup
  sketch_title 'Math Blackboard'
  @f = createFont('Arial', 24, true)
  half = 1 / 2r     # since ruby 2.1.0 (and jruby-9.0.0.0)
  third = 1 / 3r
  quarter = 1 / 4r
  fifth = 1 / 5r
  sixth = 1 / 6r
  seventh = 1 / 7r
  format2 = '%s + %s                                     = %s'
  format3 = '%s + %s + %s                            = %s'
  format4 = '%s + %s + %s + %s                   = %s'
  format5 = '%s + %s + %s + %s + %s          = %s'
  format6 = '%s + %s + %s + %s + %s + %s = %s'
  sum2 =  half + third
  result2 = numeric_to_mixed_number(sum2)
  @two = format(format2, half, third, result2)
  sum3 =  half + third + quarter
  result3 = numeric_to_mixed_number(sum3)
  @three = format(format3, half, third, quarter, result3)
  sum4 =  half + third + quarter + fifth
  result4 = numeric_to_mixed_number(sum4)
  @four = format(format4, half, third, quarter, fifth, result4)
  sum5 =  half + third + quarter + fifth + sixth
  result5 = numeric_to_mixed_number(sum5)
  @five = format(format5, half, third, quarter, fifth, sixth, result5)
  sum6 =  half + third + quarter + fifth + sixth + seventh
  result6 = numeric_to_mixed_number(sum6)
  @six = format(format6, half, third, quarter, fifth, sixth, seventh, result6)
end

def draw
  background 10
  text_font(f, 24)
  fill(220)
  text('Math Blackboard JRubyArt', 110, 50)
  text(two, 100, 80)
  text(three, 100, 105)
  text(four, 100, 130)
  text(five, 100, 155)
  text(six, 100, 180)
end

def numeric_to_mixed_number(amount)
  amount_as_integer = amount.to_i
  if (amount_as_integer != amount.to_f) && (amount_as_integer > 0)
    fraction = amount - amount_as_integer
    format('%s %s', amount_as_integer, fraction)
  else
    amount.to_s
  end
end

def settings
  size 640, 250
  smooth 4
end

Tuesday, 15 September 2015

Bresenham's Algorithm in JRubyArt

After ruby-processing version, published by norioc on qiita
# ruby-processing sketch
# http://qiita.com/norioc/items/99514cb659aad03ab7d7
# http://aidiary.hatenablog.com/entry/20050402/1251514618
N = 15
attr_reader :rows, :cols

def setup
  sketch_title 'Bresenham`s Algorithm'
  @rows = height / N
  @cols = width / N
end

def settings
  size 400, 400
end

def build_line(from:, to:)
  next_x = from.x
  next_y = from.y
  delta_x = to.x - from.x
  delta_y = to.y - from.y
  step_x = delta_x < 0 ? -1 : 1
  step_y = delta_y < 0 ? -1 : 1
  delta_x = (delta_x * 2).abs
  delta_y = (delta_y * 2).abs
  b_line = Array.new(1, Vec2D.new(next_x, next_y))
  if delta_x > delta_y
    fraction = delta_y - delta_x / 2
    while next_x != to.x
      if fraction >= 0
        next_y += step_y
        fraction -= delta_x
      end
      next_x += step_x
      fraction += delta_y
      b_line << Vec2D.new(next_x, next_y)
    end
  else
    fraction = delta_x - delta_y / 2
    while next_y != to.y
      if fraction >= 0
        next_x += step_x
        fraction -= delta_y
      end
      next_y += step_y
      fraction += delta_x
      b_line << Vec2D.new(next_x, next_y)
    end
  end
  b_line
end

def draw
  background 255
  translate width / 2, height / 2
  rect_mode CENTER
  no_fill
  stroke 60
  (0..rows).each do |i|
    (0..cols).each do |j|
      rect((j - cols / 2) * N, (i - rows / 2) * N, N, N)
    end
  end
  x = mouse_x - width / 2 + N / 2
  y = mouse_y - height / 2 + N / 2
  b_line = build_line(from: Vec2D.new, to: Vec2D.new(x / N, y / N))
  unless b_line.empty?
    fill color('#C7B097')
    b_line.each { |v| rect(v.x * N, v.y * N, N, N) }
  end
  stroke color('#0000FF')
  stroke_width 2
  line 0, 0, mouse_x - width / 2, mouse_y - height / 2
  stroke 0
  stroke_width 1
end

Saturday, 29 August 2015

Changing window / surface dynamically in JRubyArt

Since the changes to processing-3.0 we are able to resize our sketches dynamically:-
def settings
  size(400, 400)
end

def setup
  sketch_title 'Resizable Surface'
  surface.set_resizable(true)
end

def draw
  background(255)
  stroke_weight 4
  line(100, 100, width - 100, height - 100)
end

def key_pressed
  surface.set_size(rand(200..500).floor, rand(200..500).floor)
end

In this sketch "sketch_title" actually calls surface under the hood with another 'set' method but we don't want these ugly get set prefixed methods in ruby do we. I guess I could similary "fix" resizable and convert set_size to sketch_size? And since true is given as default argument to resizable we don't need that either...
def settings
  size(400, 400)
end

def setup
  sketch_title 'Resizable Surface'
  resizable(true)
end

def draw
  background(255)
  stroke_weight 4
  line(100, 100, width - 100, height - 100)
end

def key_pressed
  sketch_size(rand(200..500).floor, rand(200..500).floor)
end
OK a bit more experimentation also works with FX2D, blows up with P2D, but that's the same with vanilla processing (threading error).

Thursday, 27 August 2015

Advanced shader sketches in JRubyArt

Andres Colubri has published some advanced shader experiments here for processing-3.0, this is one of the examples translated for JRubyArt (requires shader files from the repo. See also Andres write-up on Advanced-OpenGL. Cool sketch of a textured Earth with an independent cloud layer.
# Earth model with bump mapping, specular texture and dynamic cloud layer.
# Adapted from the THREE.js tutorial to processing by Andres Colubri,
# translated to JRubyArt by Martin Prout:
# http://learningthreejs.com/blog/2013/09/16/how-to-make-the-earth-in-webgl/

attr_reader :earth, :clouds, :earth_shader, :cloud_shader, :earth_rotation
attr_reader :clouds_rotation, :target_angle

def setup
  sketch_title 'Blue Marble'
  @earth_rotation = 0
  @clouds_rotation = 0
  earth_tex = load_image('earthmap1k.jpg')
  cloud_tex = load_image('earthcloudmap.jpg')
  alpha_tex = load_image('earthcloudmaptrans.jpg')
  bump_map = load_image('earthbump1k.jpg')
  spec_map = load_image('earthspec1k.jpg')
  @earth_shader = load_shader('EarthFrag.glsl', 'EarthVert.glsl')
  earth_shader.set('texMap', earth_tex)
  earth_shader.set('bumpMap', bump_map)
  earth_shader.set('specularMap', spec_map)
  earth_shader.set('bumpScale', 0.05)
  @cloud_shader = load_shader('CloudFrag.glsl', 'CloudVert.glsl')
  cloud_shader.set('texMap', cloud_tex)
  cloud_shader.set('alphaMap', alpha_tex)
  @earth = create_shape(SPHERE, 200)
  earth.setStroke(false)
  earth.setSpecular(color(125))
  earth.setShininess(10)
  @clouds = create_shape(SPHERE, 201)
  clouds.setStroke(false)
end

def draw
  background(0)
  translate(width / 2, height / 2)
  point_light(255, 255, 255, 300, 0, 500)
  target_angle = map1d(mouse_x, (0..width), (0..TWO_PI))
  @earth_rotation += 0.05 * (target_angle - earth_rotation)
  shader(earth_shader)
  push_matrix
  rotate_y(earth_rotation)
  shape(earth)
  pop_matrix
  shader(cloud_shader)
  push_matrix
  rotate_y(earth_rotation + clouds_rotation)
  shape(clouds)
  pop_matrix
  @clouds_rotation += 0.001
end

def settings
  size(600, 600, P3D)
end

Monday, 17 August 2015

Java-8-lambda and jruby extensions

Should we be getting excited about java-8-lambda for jruby extensions (working in NetBeans I got this suggestion when updating source from java-7 to java-8 having listened to Charlies talk at JVMLS2015 perhaps we should?
// Java seven

public static void createVec2(final Ruby runtime) {
    RubyClass vec2Cls = runtime.defineClass("Vec2D", runtime.getObject(), new ObjectAllocator() {
            @Override
            public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
                return new Vec2(runtime, rubyClass);
            }
    });
    vec2Cls.defineAnnotatedMethods(Vec2.class);

}

// Java eight
public static void createVec2(final Ruby runtime) {
    RubyClass vec2Cls = runtime.defineClass("Vec2D", runtime.getObject(), (Ruby runtime1, RubyClass rubyClass) -> new Vec2(runtime1, rubyClass));
    vec2Cls.defineAnnotatedMethods(Vec2.class);
}
Update 22 August 2015 just released JRubyArt 0.5.0 featuring java8lambda in production...

Friday, 14 August 2015

AABB for JRubyArt using keyword args

UPDATED ENTRY 2 September 2015, to avoid clash with toxicgem AABB is now AaBb since JRubyArt-0.6.0
I'm adding a 2D axis aligned bounding box to JRubyArt, here's what it looks like, position can be conditionally updated (ie if block given, only updates if the block evaluates to true):-
# Axis aligned bounding box class (AABB would clash with Toxicgem)
class AaBb
  attr_reader :center, :extent

  def initialize(center:, extent:)
    @center = center
    @extent = extent
  end

  def self.from_min_max(min:, max:)
    new(center: (min + max) * 0.5, extent: max - min)
  end

  def position(vec)
    return @center = vec unless block_given?
    @center = vec if yield
  end

  def scale(d)
    @extent *= d
  end

  def contains?(vec)
    rad = extent * 0.5
    return false unless (center.x - rad.x..center.x + rad.x).cover? vec.x
    (center.y - rad.y..center.y + rad.y).cover? vec.y
  end
end

Here is an example using it (based on processing Basics/Input/MouseFunctions.pde but vastly superior to my thinking). Two AaBb objects are used, one to define the window bounds so we can stop block going off screen (vanilla processing example fails to do that) and one to define the block area.
# Click on the box and drag it across the screen.

attr_reader :block, :block_locked, :over_block, :renderer, :bounds
BLOCK_WIDTH = 150

def setup
  sketch_title 'AaBb Example'
  @block = Block.new(
    center: Vec2D.new(width / 2, height / 2),
    size: Vec2D.new(BLOCK_WIDTH, BLOCK_WIDTH))
  @locked = false
  @over_block = false
  @bounds = AaBb.new(
    center: Vec2D.new(width / 2, height / 2),
    extent: Vec2D.new(width - BLOCK_WIDTH, height - BLOCK_WIDTH))
  @renderer = AppRender.new(self)
end

def draw
  background 0
  fill 153
  if block.over?(Vec2D.new(mouse_x, mouse_y))
    @over_block = true
    stroke 255
    fill 255 if block_locked?
  else
    @over_block = false
    stroke 153
  end
  # Draw the box as a shape
  begin_shape
  block.points_array.each { |vec| vec.to_vertex(renderer) }
  end_shape(CLOSE)
end

def block_locked?
  block_locked
end

def over_block?
  over_block
end

def mouse_pressed
  if over_block?
    @block_locked = true
    fill 255
  else
    @block_locked = false
  end
end

def mouse_dragged
  return unless block_locked?
  position = Vec2D.new(mouse_x, mouse_y)
  block.new_position(position) { bounds.contains? position }
end

def mouse_released
  @block_locked = false
end

def settings
  size 640, 360
  smooth 4
end

# Use class to contain block behaviour
class Block
  attr_reader :aabb

  def initialize(center:, size:)
    @aabb = AaBb.new(center: center, extent: size)
  end

  def new_position(center, &block)
    @aabb.position(center.dup, &block)
  end

  def over?(vec)
    aabb.contains? vec
  end

  # use for shape
  def points_array
    a = aabb.center - aabb.extent * 0.5
    c = aabb.center + aabb.extent * 0.5
    b = Vec2D.new(c.x, a.y)
    d = Vec2D.new(a.x, c.y)
    [a, b, c, d]
  end

  # use for rect
  def points
    [aabb.center, aabb.extent]
  end
end

Thursday, 13 August 2015

Numeric#step with keyword arguments in JRubyArt (and jruby-9.0.0.0)

Since jruby-9.0.0.0 we can use many syntax features of ruby-2.1, most useful of these for JRubyArt is the ability to make code more readable/reliable with various keyword arguments changes here is one such example:-
# We extend the language of conditionals by adding the
# keyword "elsif". This allows conditionals to ask
# two or more sequential questions, each with a different
# action.

def setup
  background 0
  2.step(by: 2, to: width - 2) do |i|
    # If 'i' divides by 20 with no remainder
    # draw the first line..
    # else if 'i' divides by 10 with no remainder
    # draw second line, else draw third line
    if (i % 20) == 0
      stroke 255
      line i, 80, i, height / 2
    elsif (i % 10) == 0
      stroke 153
      line i, 20, i, 180
    else
      stroke 102
      line i, height / 2, i, height - 20
    end
  end
end

def settings
  size 640, 360
end

Sunday, 26 July 2015

Yet more experiments with JRubyArt and pry

My experience of ruby-processing with pry (for live mode) has not been promising, however things change so I thought I would give it another go, since the live IRB, has not been that wonderful either:-
The live.rb code
# An pry shell for live coding.
# Will start with your sketch.

require_relative 'base'
Processing.load_and_run_sketch

ARGV.clear # So that pry doesn't try to load them as files.

require 'pry'
$app.pry

The sketch k9 create fred 300 300 --wrap
class Fred < Processing::App
  def setup
    sketch_title 'Fred'
  end

  def draw

  end

  def settings
    size 300, 300, FX2D
    # smooth # here
  end
end

k9 live fred.rb

Turns out it was a really lucky choice to experiment with FX2D sketch (as there might be threading or other issues with JAVA2D), another strategy that seemed to work well is to redefine the sketch draw loop from pry (still can't see a serious use case, but it is a bit of fun). IRB can handle a yield block in draw loop (with block_given? guard) but it doesn't play too well with pry.

Tuesday, 14 July 2015

A more refined (ruby-2.2) interface for java classes with mixed arguments

Since ruby-2.0 it has been possible to use keywords as first class arguments, and ruby-2.1 introduced the required keyword argument (these are available to JRubyArt and potentially ruby-processing since jruby-9.0.0.0). So I thought it would be sensible to make use of them to make say the parameters of an ArcBall library more descriptive:-
# experimental ArcBall class
class ArcBall
  attr_reader :applet, :x, :y, :r
  def initialize(app:, center_x: nil, center_y: nil, radius: nil)
    @applet = app
    @x = (center_x.nil?) ? app.width * 0.5 : center_x
    @y = (center_y.nil?) ? app.height * 0.5 : center_y
    @r = (radius.nil?) ? app.width * 0.8 : radius
  end

  def to_s
    format('ArcBall.new(applet: %s, centre_x: %d, centre_y: %d, radius: %d)',
           applet, x, y, r)
  end
end

Applet = Struct.new(:width, :height) do
  def to_s
    'MyApplet'
  end
end

applet = Applet.new(800, 600)
puts ArcBall.new(app: applet).to_s
puts ArcBall.new(app: applet, center_x: 300, center_y: 200, radius: 800).to_s

Output:
ArcBall.new(applet: MyApplet, centre_x: 400, centre_y: 300, radius: 640)
ArcBall.new(applet: MyApplet, centre_x: 300, centre_y: 200, radius: 800)

Testing whether concept works in practice for JRubyArt (using a local ArcBall library in sketchbook for test purposes);-
load_library :arcball

def setup
  sketch_title 'Arcball Box'
  ArcBall.new(app: self)
  fill 180
end

def draw
  background 50
  box 300, 300, 300
end

def settings
  size 600, 600, P3D
  smooth 8
end

# experimental ArcBall class
class ArcBall
  include_package 'arcball'
  attr_reader :applet, :x, :y, :r
  def initialize(app:, center_x: nil, center_y: nil, radius: nil)
    @applet = app
    @x = (center_x.nil?) ? app.width * 0.5 : center_x
    @y = (center_y.nil?) ? app.height * 0.5 : center_y
    @r = (radius.nil?) ? app.width * 0.8 : radius
    app = JarcBall.new applet.to_java, x.to_java(:float), y.to_java(:float), r.to_java(:float)
    app.set_active true
  end

  def to_s
    format('ArcBall.new(applet: %s, centre_x: %d, centre_y: %d, radius: %d)',
           applet, x, y, r)
  end
end

And indeed it does work if you supply suitable java types to the "hidden" java ArcBall class.

Friday, 3 July 2015

JRubyArt-0.3.0 vs ruby-processing-2.6.12

Previously I posted a comparison between a different version of JRubyArt and ruby-processing where both were running using processing-2.2.1 and the the same version, but they had different designs. The current JRubyArt candidate is more similar to ruby-processing, but is targetting processing-3.0 (currently 3.0a10) and jruby-9.0.0.0 (currently jruby-9.0.0.0.rc1). I see ruby-processing as sticking with processing-2.2.1, but remaining compatible with stable releases of jruby, whereas JRubyArt will only be compatible with processing-3.0a10+.

External Dependencies

  • ruby-processing requires an installed version of processing-2.2.1
  • ruby-processing requires an installed version of processing-3.0a10+

Core processing libraries (sound, video etc)

  • ruby-processing has builtin support 
  • jruby_art has the same built in support but sound, video libraries are now external (since processing-3.0)

Contributed processing libraries

  • ruby-processing has builtin support for libraries installed by processing ide 
  • jruby_art has the same builtin support for libraries installed by processing

Vec2D, Vec3D, Arcball and DEGLUT

  • ruby-processing requires load_library 
  • jruby_art these libraries automatically loaded into jruby runtime

Processing::Proxy module

  • in ruby-processing Proxy can be used to mimic processings inner classes, probably v. bad juju convenience isn't everything 
  • in jruby_art a different Proxy can also be used to mimic processings inner classes but has a simpler implementation

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

Tuesday, 28 April 2015

Exploring the latest processing-3.0 with JRubyArt (with some success)

I have created a processing-3.0 branch to explore using the latest processing with JRubyArt and it is looking pretty good so far, JAVA2D was easy to get running but P2D and P3D modes took a bit longer. The key to running in the opengl modes was to putting the native binaries on the java.libary.path:-
jruby -Djava.library.path="/home/tux/lib/linux64" trefoil.rb
I needed to change the build to include the lwjgl jars and app.rb to accommodate the changes to processing but also unlike vanilla processing the sketch was also modified, replacing the applet convention of size(width, height, renderer) in setup with 3 methods (not required to be called in setup):-
# Old
  def setup
    size(1024, 768, P3D)
  end
#New
  def sketch_width
    1024
  end

  def sketch_height
    768
  end

  def sketch_renderer
    P3D
  end

Here is a working 'bare' JAVA2D sketch (run using k9 run keyboard.rb) which was tested with jruby-complete-9.0.0.0.pre2.
def setup
  @num_chars = 26
  @key_scale = 200.0 / @num_chars - 1.0
  @rect_width = width / 4
  no_stroke
  background 0
end

def draw
  return unless key_pressed? && ('A'..'z').include?(key)
  key_index = key.ord - (key <= 'Z' ? 'A'.ord : 'a'.ord)
  fill millis % 255
  begin_rect = map1d(key_index, (0..25), (0..width - @rect_width))
  rect begin_rect, 0, @rect_width, height
end

def sketch_width; 640; end

def sketch_height; 360; end

def sketch_renderer; JAVA2D; end

Rubocop will complain of single line definition, but here I think it makes sense! The default renderer is currently JAVA2D, but it looks like Ben is having some success with JavaFX so that might change v. soon. It should be possible to use the java Class loader to set the java.library.path as we do in load_library, but this will probably require 'k9 run sketch', however there is work to be done to even run anything other than Java2D with k9. So presently I think I will concentrate on jruby-9.0.0.0 work.....
Allowing for Ben / Andrés to progress processing-3.0, before I waste any more time.

Warning this has all changed in favour of settings method, and defining renderer along with size as single line entry ie like processing-2.0 but in settings, not setup... furthermore jruby class loader reverted to original and the design of JRubyArt mirrored ruby-processing

Thursday, 2 April 2015

Using delegators in JRubyArt (where vanilla processing uses inner classes)

Using delegators in JRubyArt (where vanilla processing uses inner classes) and ruby-processing uses 'include Processing::Proxy' to mimic the same behaviour. See node.rb, where we selectively expose PApplets fill, stroke, stroke_weight and ellipse methods in the Node class.


force_directed_graph.rb
require 'jruby_art'
require 'toxiclibs'
require_relative 'cluster'
require_relative 'node'

#
# Copyright (c) 2010 Daniel Shiffman
#
# 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 ForceDirectedGraph < Processing::App
  attr_reader :physics, :clusters, :show_physics, :show_particles, :f

  def setup
    size(640, 360)
    @f = create_font('Georgia', 12, true)
    @show_physics = true
    @show_particles = true
    # Initialize the physics
    @physics = Physics::VerletPhysics2D.new
    physics.set_world_bounds(Toxi::Rect.new(10, 10, width - 20, height - 20))
    # Spawn a new random graph
    new_graph
  end

  # Spawn a new random graph
  def new_graph
    # Clear physics
    physics.clear
    center = TVec2D.new(width / 2, height / 2)
    @clusters = (0..8).map { Cluster.new(physics, rand(3..8), rand(20..100), center) }
    # All clusters connect to all clusters
    clusters.each_with_index do |ci, i|
      clusters[i + 1..clusters.size - 1].each do |cj|
        ci.connect(cj)
      end
    end
  end

  def draw
    # Update the physics world
    physics.update
    background(255)
    # Display all points
    clusters.each(&:display) if show_particles
    # If we want to see the physics
    if show_physics
      clusters.each_with_index do |ci, i|
        ci.internal_connections self
        # Cluster connections to other clusters
        clusters[1 + i..clusters.size - 1].each do |cj|
          ci.show_connections(self, cj)
        end
      end
    end
    # Instructions
    fill(0)
    text_font(f)
    text("'p' to display or hide particles\n'c' to display or hide connections\n'n' for new graph", 10, 20)
  end

  # Key press commands
  def key_pressed
    case key
    when 'c'
      @show_physics = !show_physics
      @show_particles = true unless show_physics
    when 'p'
      @show_particles = !show_particles
      @show_physics = true unless show_particles
    when 'n'
      new_graph
    end
  end
end

ForceDirectedGraph.new(title: 'Force directed graphs')

cluster.rb
#
# Copyright (c) 2010 Daniel Shiffman
#
# 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 Cluster
  attr_reader :nodes, :diameter, :physics

  # We initialize a with a number of nodes, a diameter, and centerpoint
  def initialize(physics, n, d, center)
    @physics = physics
    @diameter = d
    @nodes = (0..n).map { Node.new(center.add(TVec2D.random_vector)) }
    # Connect all the nodes with a Spring
    nodes[1..nodes.size - 1].each_with_index do |pi, i|
      nodes[0..i].each do |pj|
        physics.add_spring(Physics::VerletSpring2D.new(pi, pj, diameter, 0.01))
      end
    end
  end

  def display
    nodes.each(&:display)
  end
  # This functons connects one cluster to another
  # Each point of one cluster connects to each point of the other cluster
  # The connection is a "VerletMinDistanceSpring"
  # A VerletMinDistanceSpring is a spring which only enforces its rest length if the
  # current distance is less than its rest length. This is handy if you just want to
  # ensure objects are at least a certain distance from each other, but don't
  # care if it's bigger than the enforced minimum.

  def connect(other)
    other_nodes = other.nodes
    nodes.each do |pi|
      other_nodes.each do |pj|
        physics.add_spring(Physics::VerletMinDistanceSpring2D.new(pi, pj, (diameter + other.diameter) * 0.5, 0.05))
      end
    end
  end

  # Draw all the internal connections
  def internal_connections(app)
    app.stroke(200, 0, 0, 80)
    nodes[0..nodes.size - 1].each_with_index do |pi, i|
      nodes[i + 1..nodes.size - 1].each do |pj|
        app.line(pi.x, pi.y, pj.x, pj.y)
      end
    end
  end

  # Draw all the connections between this and another Cluster
  def show_connections(app, other)
    app.stroke(200, 200, 0, 20)
    app.stroke_weight(2)
    other_nodes = other.nodes
    nodes.each do |pi|
      other_nodes[0..other_nodes.size - 1].each do |pj|
        app.line(pi.x, pi.y, pj.x, pj.y)
      end
    end
  end
end

node.rb
require 'forwardable'

# The Nature of Code
# Daniel Shiffman
# http://natureofcode.com
# Force directed graph
# Heavily based on: http://code.google.com/p/fidgen/
# Notice how we are using inheritance here!
# We could have just stored a reference to a VerletParticle object
# inside the Node class, but inheritance is a nice alternative
class Node < Physics::VerletParticle2D
  extend Forwardable
  def_delegators(:@app, :fill, :stroke, :stroke_weight, :ellipse)
  def initialize(pos)
    super(pos)
    @app = $app
  end

  # All we're doing really is adding a display function to a VerletParticle
  def display
    fill(50, 200, 200, 150)
    stroke(50, 200, 200)
    stroke_weight(2)
    ellipse(x, y, 16, 16)
  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

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