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

Showing posts with label Vec2D. Show all posts
Showing posts with label Vec2D. Show all posts

Saturday, 25 October 2014

Sketch featuring Vec2D to_curve_vertex (JRubyArt)

########
# Soft Body by Ira Greenberg
# Softbody dynamic simulation using curve_vertex
# and curve_tightness (and new Vec2D to_curve_vertex)
########
require 'jruby_art'

# SoftBody class wrapped JRubyArt sketch features Vec2D :to_curve_vertex
class SoftBody < Processing::App
  attr_reader :accel, :center, :frequency, :radius, :rot_angle
  attr_reader :organic_constant, :nodes, :renderer, :angle, :node_start
  SPRINGING = 0.0009
  DAMPING = 0.98
  NODES = 5

  def setup
    size 640, 360
    @renderer = AppRender.new(self)
    init_node
    no_stroke
    frame_rate 30
  end

  def init_node
    @accel = Vec2D.new
    @center = Vec2D.new(width / 2, height / 2)
    @radius = 45
    @organic_constant = 1
    @rot_angle = -90
    @nodes = (0...NODES).map { Vec2D.new }
    @frequency = (0...NODES).map { rand(5..12) }
    @angle = Array.new(NODES, 0)
  end

  def draw
    fill(0, 100)
    rect(0, 0, width, height)
    update
    draw_shape
    move_shape
  end

  def draw_shape
    curve_tightness(organic_constant)
    fill 255
    begin_shape
    nodes.each { |vec| vec.to_curve_vertex(renderer) }
    nodes.take(NODES - 1).each { |vec| vec.to_curve_vertex(renderer) }
    end_shape(Java::ProcessingCore::PConstants::CLOSE)
  end

  def update
    delta = Vec2D.new(mouse_x - center.x, mouse_y - center.y)
    delta *= SPRINGING
    @accel += delta
    @center += accel
    @accel *= DAMPING
    @organic_constant = 1 - (((accel.x).abs + (accel.y).abs) * 0.1)
    @node_start = create_start
  end

  def create_start
    (0...NODES).map do |n|
      Vec2D.new(
        center.x + DegLut.cos(n * (360 / NODES)) * radius,
        center.y + DegLut.sin(n * (360 / NODES)) * radius
        )
    end
  end

  def move_shape
    (0...NODES).each do |i|
      nodes[i] = Vec2D.new(
      node_start[i].x + DegLut.sin(angle[i]) * (accel.x * 2),
      node_start[i].y + DegLut.sin(angle[i]) * (accel.y * 2)
      )
      angle[i] = frequency[i] + angle[i]
    end
  end
end

SoftBody.new(title: 'Soft Body')

Wednesday, 17 September 2014

Using the Range class in ruby processing

Since ruby-processing-2.6.3 the processing constrain function is actually implemented using the clip function of Range (this method has been added to Range in ruby-processing). However in ruby processing we should probably prefer to use the clip method directly, as I have here in my World class:-
# Class provides an OO way constraining a Mover in a 2D space
# use
# world = World.new((0..width), (0..height))
# world.constrain_mover(mover)
class World
  attr_reader :xrange, :yrange
  def initialize(xrange, yrange)
    @xrange, @yrange = xrange, yrange
  end

  # @param mover is expected respond to loc, vel
  # that in turn respond to x and y getter/setters (Vec2D does this)

  def constrain_mover(mover)
    # Note clip functionality, extends Range in ruby-processing
    unless xrange.cover? mover.loc.x
      mover.vel.x *= -1
      mover.loc.x = xrange.clip mover.loc.x
    end
    return if yrange.cover? mover.loc.y
    mover.vel.y *= -1
    mover.loc.y = yrange.clip mover.loc.y
  end
end

For the sketch using this code see my fork Dan Shiffmans The Nature of Code Examples.

Thursday, 9 January 2014

Penrose tiling generator (after Shiffman)

Previously I have experimented with cfdg and lsystems to generate penrose tiling, this version by Dan Shiffman seems very similar to the cfdg version. Interestingly Dan ported this somewhat from python, and here we end with a ruby version. Dan unsurprisingly makes use of the PVector class to do the vector math here we use my Vec2D from the ruby-processing vecmath library, which is far more concise (designed to use arithmetic operators from the outset). Here is the sketch, somewhat drier than the original with choosable options via control panel (it is necessary to press "reset" to change seed/triangle type):-
# Penrose Tile Generator
# Using a variant of the "ArrayList" recursion technique: http://natureofcode.com/book/chapter-8-fractals/chapter08_section4
# Penrose Algorithm from: http://preshing.com/20110831/penrose-tiling-explained
# Daniel Shiffman May 2013
# Translated (and refactored) to ruby-processing Jan 2014 by Martin Prout

load_libraries :vecmath, :tile, :control_panel
attr_reader :tris, :s, :panel, :hide, :acute

def setup
  size(1024, 576)
  control_panel do |c|
    c.title = "Tiler Control"
    c.look_feel "Nimbus"
    c.checkbox  :seed
    c.checkbox  :acute
    c.button    :generate
    c.button    :reset!
    @panel = c
  end
  @hide = false
  init false # defaults to regular penrose
end

def draw
  # only make control_panel visible once, or again when hide is false
  unless hide
    @hide = true
    panel.setVisible(hide)
  end
  background(255)
  translate(width/2, height/2)
  tris.each do |t|
    t.display
  end
end

def generate
  next_level = []
  tris.each do |t|
    more = t.subdivide
    more.each do |m|
      next_level << m
    end
  end
  @tris = next_level
end

def reset!
  Tiler.acute(acute)  # set the Tiler first
  init @seed
  java.lang.System.gc # but does it do any good?
end

def init alt_seed
  @tris = []
  10.times do |i|     # create 36 degree segments
    a = Vec2D.new
    b = Vec2D.from_angle((2 * i - 1) * PI / 10)
    c = Vec2D.from_angle((2 * i + 1) * PI / 10)
    b *= 370
    c *= 370
    if alt_seed
      tile = (i % 2 == 0)? Tiler.tile(b, a, c) : Tiler.tile(c, a, b)
      tris << tile
    else
      tile = (i % 2 == 0)? Tiler.tile(a, b, c) : Tiler.tile(a, c, b)
      tris << tile
    end
  end
end

Here is the tile library:-
module Tiler
  @@acute = false

  def self.acute(x)
    @@acute = x
  end

  # setup the initial tiling with all red tiles
  def self.tile(a, b, c)
    tile = (@@acute)? ATile.new(0, a, b, c) : Tile.new(0, a, b, c)
  end
end

class Tile
  include Processing::Proxy
  PHI = (1.0 + Math.sqrt(5)) / 2.0 # golden ratio
  RED = [255, 0, 0]
  BLUE = [0, 0, 255]
  COLORS = [RED, BLUE]
  attr_reader :a, :b, :c, :col

  def initialize(col,  a,  b,  c)
    @col, @a, @b, @c = col, a, b, c
  end

  def display
    no_stroke
    fill(*COLORS[col])
    triangle(a.x, a.y, b.x, b.y, c.x, c.y)
    #fill(0,0,255)
    #ellipse(a.x,a.y,4,4)
    #ellipse(b.x,b.y,4,4)
    #ellipse(c.x,c.y,4,4)
  end

  def subdivide
    result = []
    if (col == 0)
      # Subdivide red triangle
      p = b - a
      p /= PHI
      p += a
      result << Tile.new(0, c, p, b)
      result << Tile.new(1, p, c, a)
    else
      # Subdivide blue triangle
      q = a - b
      q /= PHI
      q += b
      r = c - b
      r /= PHI
      r += b
      result << Tile.new(1, r, c, a)
      result << Tile.new(1, q, r, b)
      result << Tile.new(0, r, q, a)
    end
    return result
  end
end

class ATile < Tile
  def subdivide
    result = []
    if (col == 0)
      # Subdivide red (half kite) triangle
      q = b - a
      q /= PHI
      q += a
      r = c - b
      r /= PHI
      r += b
      result << ATile.new(1, r, q, b)
      result << ATile.new(0, q, a, r)
      result << ATile.new(0, c, a, r)
    else
      # Subdivide blue (half dart) triangle
      p = a - c
      p /= PHI
      p += c
      result << ATile.new(1, b, p, a)
      result << ATile.new(0, p, c, b)
    end
    return result
  end
end

Here is a screenshot, before I added second checkbox

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