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

Wednesday, 11 September 2013

Gravitational Attraction (3D) Planetarium Sketch (avoiding reflection with some alternative logic)

Yet another example using Andrés Colubri's planetarium library, hardest to convert so far. Andrés Colubri is concerned that in his sketch some logic would be called too often in the draw loop, this is why he uses the pre and post (methods, only available using reflection, but only recognised if they are part of a java class, this is Catch 22 territory for ruby-processing). I think I've come up with a decent solution (certainly seems to run OK) that avoids reflection, which can only be good thing. In this case I have placed a "guard" in the pre and post methods (ruby methods to be clear). Can't get border to work "too complicated", I reckon I have mad sketch more efficient by replacing PVector with a custom vector (starts at 8 fps, but quickly moves to 60 fps).
The sketch:-
#
# Gravitational Attraction (3D) 
# by Daniel Shiffman.  
#
# Adapted for dome projection by Andres Colubri
# 
# Simulating gravitational attraction 
# G ---> universal gravitational constant
# m1 --> mass of object #1
# m2 --> mass of object #2
# d ---> distance between objects
# F = (G*m1*m2)/(d*d)
#
# For the basics of working with PVector, see
# http://processing.org/learning/pvector/
# as well as examples in Topics/Vectors/
# 
#
load_libraries :planetarium, :solar_system
include_package 'codeanticode.planetarium'

PLANETS = 10
attr_reader :angle, :planets, :sun, :count, :do_rotate
java_alias :background_int, :background, [Java::int] # precast for efficiency
def setup
  size(800, 800, Dome::RENDERER)
  @angle = 0                # Some random planets
  @planets = []
  @count = 10               # warm up time 
  @do_rotate = false
  (0 ... PLANETS).each do
    planets << Planet.new(rand(0.1 .. 2), rand(-width/2 .. width/2), rand(-height/2 .. height/2), rand(-100 .. 100))
  end
  # A single sun
  @sun = Sun.new
end

def pre
  if count == frame_count        # only enter once per frame
    planets.each do |planet|
      # Sun attracts Planets
      force = sun.attract(planet)
      planet.apply_force(force)
      # Update and draw Planets
      planet.update
    end
    @count = frame_count + 1
    frame.set_title("Solar System FPS: #{frame_rate.to_i}")
    @do_rotate = true
  end
end

def draw
  pre
  background_int 0
  # Setup the scene
  lights

  translate(width/2, height/2, 300)

  rotate_y(angle)

  # Display the Sun
  sun.display

  # All the Planets
  planets.each do |planet|
    planet.display
  end
  post
end

# Called after rendering all the faces, but before the dome sphere,
# so it can be used to draw stuff on the corners of the screen.
#def border
#  perspective
#  camera
#  background(255)
#  fill(0)
#  text("FPS: #{frame_rate}", 20, 20)
#end  

def post
  # Rotate around the scene
  if do_rotate
    @angle += 0.003
    @do_rotate = false
  end
end

The solar system library
 # Gravitational Attraction (3D) 
# Daniel Shiffman <http://www.shiffman.net>

# A class for an attractive body in our world

class Sun
  include Processing::Proxy
  G = 0.4       # Universal gravitational constant (arbitrary value)

  attr_reader :sphere, :location, :mass

  def initialize
    @location = Vect.new(0,0)
    @mass = 20
    @sphere = create_shape(SPHERE, mass * 2, 20)
    sphere.set_fill(false)
    sphere.set_stroke(color(255))
  end

  def constrain val, lo, hi                    # override processing overloaded method
    result = (val > hi)? hi : (val < lo)? lo : val
  end

  def attract(m)
    force = Vect.sub(location,m.location)      # Calculate direction of force
    d = constrain(force.mag, 5.0, 25.0)        # Limiting the distance to eliminate "extreme" results for very close or very far objects
    strength = (G * mass * m.mass) / (d * d)   # Calculate gravitional force magnitude
    force.set_mag(strength)                    # Get force vector --> magnitude * direction
    return force
  end

  # Draw Sun
  def display
    push_matrix
    translate(location.x,location.y,location.z)
    shape(sphere)
    pop_matrix
  end
end

# Gravitational Attraction (3D) 
# Daniel Shiffman <http://www.shiffman.net>

# A class for an orbiting Planet

class Planet 
  include Processing::Proxy
  # Basic physics model (location, velocity, acceleration, mass)
  attr_reader :location, :velocity, :acceleration, :mass, :sphere

  def initialize(m, x, y, z)
    @mass = m
    @location = Vect.new(x,y,z)
    @velocity = Vect.new(1,0)   # Arbitrary starting velocity
    @acceleration = Vect.new(0,0)
    @sphere = create_shape(SPHERE, mass * 8, 20)
    sphere.set_stroke(false)
    sphere.set_fill(color(255))
  end

  # Newton's 2nd Law (F = M*A) applied
  def apply_force(force)
    f = Vect.div(force,mass)
    acceleration.add(f)
  end

  # Our motion algorithm (aka Euler Integration)
  def update
    velocity.add(acceleration)  # Velocity changes according to acceleration
    location.add(velocity)      # Location changes according to velocity
    acceleration.zero
  end

  # Draw the Planet
def display
    push_matrix
    translate(location.x,location.y,location.z)
    shape(sphere)
    pop_matrix
  end
end

class Vect
  attr_reader :x, :y, :z
  def initialize(x, y, z = 0)
    @x, @y, @z = x, y, z
  end

  def normalize
    @x, @y, @z = x / mag, y / mag, z / mag  if (mag != 0 && mag != 1)
  end

  def mult a
    @x, @y, @z = x * a, y * a, z * a
  end

  def self.sub(v0, v1)
    Vect.new(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z)
  end

  def add(v)
    @x, @y, @z = x + v.x, y + v.y, z + v.z
  end

  def self.div(v, a)
    Vect.new(v.x / a, v.y / a, v.z / a)
  end

  def mag
    Math.sqrt(x*x + y*y + z*z)
  end

  def set_mag m
    normalize
    @x, @y, @z = x * m, y * m, z * m
  end

  def zero
    @x, @y, @z = 0, 0, 0
  end
end

No comments:

Post a Comment

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