---
layout: post
title: "picrate1, 2"
keywords: library, boids, control_panel
permalink: libraries/picrate.html
---
PiCrate provides a number of libraries that you can use _out of the box_, but which still need to be loaded to use them in you sketches see examples below:-
### Boids library ###
1A built in pure ruby library
The [original boids library][original] was created by Jeremy Ashkenas to demonstrate a pure 'ruby' library for ruby processing, this updated version has the same goal. However it is updated to make use of [Vec3D][vec3d] and [Vec2D][vec2d] classes (picrate features) and keyword arguments (ruby-2.1). It also use [forwardable][forwardable]. To see usage of now correctly implemented `angle` (heading) see [simple example][example].
See the full library code below:-
```ruby
# frozen_string_literal: true
# Boids -- after Tom de Smedt.
# See his Python version: http://nodebox.net/code/index.php/Boids
# This is an example of how a pure-Ruby library can work. Original for
# ruby-processing Jeremy Ashkenas. Reworked, re-factored for picrate
# by Martin Prout, features forwardable, keyword args, Vec3D and Vec2D.
class Boid
attr_reader :boids
attr_accessor :vel, :pos, :is_perching, :perch_time
def initialize(boids, pos)
@boids, @flock = boids, boids
@pos = pos
@vel = Vec3D.new
@is_perching = false
@perch_time = 0.0
end
def cohesion(d:)
# Boids gravitate towards the center of the flock,
# Which is the averaged position of the rest of the boids.
vect = Vec3D.new
@boids.each do |boid|
vect += boid.pos unless boid == self
end
count = @boids.length - 1.0
vect /= count
(vect - pos) / d
end
def separation(radius:)
# Boids don't like to cuddle.
vect = Vec3D.new
@boids.each do |boid|
if boid != self
dv = pos - boid.pos
vect += dv if dv.mag < radius
end
end
vect
end
def alignment(d:)
# Boids like to fly at the speed of traffic.
vect = Vec3D.new
@boids.each do |boid|
vect += boid.vel if boid != self
end
count = @boids.length - 1.0
vect /= count
(vect - vel) / d
end
def limit(max:)
# Tweet, Tweet! The boid police will bust you for breaking the speed limit.
most = [vel.x.abs, vel.y.abs, vel.z.abs].max
return if most < max
scale = max / most.to_f
@vel *= scale
end
def angle
Vec2D.new(vel.x, vel.y).heading
end
def goal(target, d = 50.0)
# Them boids is hungry.
(target - pos) / d
end
end
require 'forwardable'
# The Boids class
class Boids
include Enumerable
extend Forwardable
def_delegators(:@boids, :reject, :<<, :each, :shuffle!, :length, :next)
attr_reader :has_goal, :perch, :perch_tm, :perch_y
def initialize
@boids = []
end
def self.flock(n:, x:, y:, w:, h:)
flock = Boids.new.setup(n, x, y, w, h)
flock.reset_goal(Vec3D.new(w / 2, h / 2, 0))
end
def setup(n, x, y, w, h)
n.times do
dx, dy = rand(w), rand(h)
z = rand(200.0)
self << Boid.new(self, Vec3D.new(x + dx, y + dy, z))
end
@x, @y, @w, @h = x, y, w, h
@scattered = false
@scatter = 0.005
@scatter_time = 50.0
@scatter_i = 0.0
@perch = 1.0 # Lower this number to divebomb.
@perch_y = h
@perch_tm = -> { 25.0 + rand(50.0) }
@has_goal = false
@flee = false
@goal = Vec3D.new
self
end
def scatter(chance = 0.005, frames = 50.0)
@scatter = chance
@scatter_time = frames
end
def no_scatter
@scatter = 0.0
end
def perch(ground = nil, chance = 1.0, frames = nil)
@perch_tm = frames.nil? ? -> { 25.0 + rand(50.0) } : frames
@perch_y = ground.nil? ? @h : ground
@perch = chance
end
def no_perch
@perch = 0.0
end
def reset_goal(target)
@has_goal = true
@flee = false
@goal = target
self
end
def goal(target:, flee:)
@has_goal = true
@flee = flee
@goal = target
self
end
def no_goal
@has_goal = false
end
def constrain
# Put them boids in a cage.
dx, dy = @w * 0.1, @h * 0.1
each do |b|
b.vel.x += rand(dx) if b.pos.x < @x - dx
b.vel.y += rand(dy) if b.pos.y < @y - dy
b.vel.x -= rand(dx) if b.pos.x > @x + @w + dx
b.vel.y -= rand(dy) if b.pos.y > @y + @h + dy
b.vel.z += 10.0 if b.pos.z < 0.0
b.vel.z -= 10.0 if b.pos.z > 100.0
next unless b.pos.y > perch_y && rand < perch
b.pos.y = perch_y
b.vel.y = b.vel.y.abs * -0.2
b.is_perching = true
b.perch_time = perch_tm.respond_to?(:call) ? perch_tm.call : perch_tm
end
end
def update(goal: 20.0, limit: 30.0, **args)
shuffled = args.fetch(:shuffled, true)
cohesion = args.fetch(:cohesion, 100)
separation = args.fetch(:separation, 10)
alignment = args.fetch(:alignment, 5.0)
# Just flutter, little boids ... just flutter away.
# Shuffling keeps things flowing smooth.
shuffle! if shuffled
m1 = 1.0 # cohesion
m2 = 1.0 # separation
m3 = 1.0 # alignment
m4 = 1.0 # goal
@scattered = true if !@scattered && rand < @scatter
if @scattered
m1 = -m1
m3 *= 0.25
@scatter_i += 1.0
end
if @scatter_i >= @scatter_time
@scattered = false
@scatter_i = 0.0
end
m4 = 0.0 unless has_goal
m4 = -m4 if @flee
each do |b|
if b.is_perching
if b.perch_time > 0.0
b.perch_time -= 1.0
next
else
b.is_perching = false
end
end
v1 = b.cohesion(d: cohesion)
v2 = b.separation(radius: separation)
v3 = b.alignment(d: alignment)
v4 = b.goal(@goal, goal)
# NB: vector must precede scalar in '*' operation below
b.vel += (v1 * m1 + v2 * m2 + v3 * m3 + v4 * m4)
b.limit(max: limit)
b.pos += b.vel
end
constrain
end
end
```
Here is the re-factored Flight Patterns Sketch:-
```ruby
#!/usr/bin/env jruby -v -w
# Description:
# Flight Patterns is that ol' Euruko 2008 demo.
# Reworked version for PiCrate
# Usage:
# Drag mouse to steer 'invisible' flock attractor, use 'f' key to toggle flee
# Mouse 'click' to toggle 'sphere' or 'circle' display
require 'picrate'
class FlightPatterns < Processing::App
load_library :boids
attr_reader :flee, :radius
def settings
size 1024, 768, P3D
end
def setup
sketch_title 'Flight Patterns'
sphere_detail 8
color_mode RGB, 1.0
no_stroke
shininess 1.0
specular 0.3, 0.1, 0.1
emissive 0.03, 0.03, 0.1
@radius = 0.02 * height
@click = false
@flee = false
@flocks = (0..3).map { Boids.flock(n: 20, x: 0, y: 0, w: width, h: height) }
end
def mouse_pressed
@click = !@click
end
def key_pressed
return unless key == 'f'
@flee = !@flee
end
def draw
background 0.05
ambient_light 0.01, 0.01, 0.01
light_specular 0.4, 0.2, 0.2
point_light 1.0, 1.0, 1.0, mouse_x, mouse_y, 190
@flocks.each_with_index do |flock, i|
flock.goal(target: Vec3D.new(mouse_x, mouse_y, 0), flee: @flee)
flock.update(goal: 185, limit: 13.5)
flock.each do |boid|
r = (0.15 * boid.pos.z) + radius
case i
when 0 then fill 0.85, 0.65, 0.65
when 1 then fill 0.65, 0.85, 0.65
when 2 then fill 0.65, 0.65, 0.85
end
push_matrix
point_array = (boid.pos.to_a).map { |p| p - (r / 2.0) }
translate(*point_array)
@click ? sphere(r / 2) : ellipse(0, 0, r, r)
@click ? hint(ENABLE_DEPTH_TEST) : hint(DISABLE_DEPTH_TEST)
pop_matrix
end
end
end
end
FlightPatterns.new
```
### Control Panel library
2A built in hybrid ruby/java library
Start by loading in the control_panel library, and then define your panel in setup like so:
```ruby
#!/usr/bin/env jruby -v -W2
# frozen_string_literal: true
require 'picrate'
# Iconic ruby-processing example for PiCrate
class JWishy < Processing::App
load_library :control_panel
attr_reader :alpha, :back_color, :bluish, :hide, :magnitude, :panel
attr_reader :x_wiggle, :y_wiggle, :go_big, :shape
def settings
size 600, 600
end
def setup
sketch_title 'Wishy Worm'
control_panel do |c|
c.title 'Control Panel'
c.look_feel 'Nimbus'
c.slider :bluish, 0.0..1.0, 0.5
c.slider :alpha, 0.0..1.0, 0.5
c.checkbox :go_big, false
c.button :reset
c.menu :shape, %w[oval square triangle], 'oval'
@panel = c
end
@hide = false
@x_wiggle, @y_wiggle = 10.0, 0
@magnitude = 8.15
@back_color = [0.06, 0.03, 0.18]
color_mode RGB, 1
ellipse_mode CORNER
smooth
end
#....rest of code
def draw
# only make control_panel visible once, or again when hide is false
unless hide
@hide = true
panel.set_visible(hide)
end
#.... rest of draw
JWishy.new
```
![JWishy]({{site.github.url}}/assets/jwishy.png)
See also [penrose](https://github.com/ruby-processing/picrate-examples/blob/master/library/vecmath/vec2d/penrose.rb) and [bezier playground](https://github.com/ruby-processing/picrate-examples/blob/master/contributed/bezier_playground.rb) sketches. See ruby code [here](https://github.com/ruby-processing/picrate/blob/master/library/control_panel/control_panel.rb).
### Video Event Library ###
2A built in hybrid ruby/java library
The video library should be installed using `picrate --install video`
The purpose of the `video_event` library is to allow you to use the vanilla processing reflection methods `captureEvent` and `movieEvent` from the processing `video` library. _It is almost impossible to use vanilla processing reflection methods without this sort of wrapper_.
A movie example:-
```ruby
#!/usr/bin/env jruby -w
require 'picrate'
# Loop.
#
# Shows how to load and play a QuickTime movie file.
class Loop < Processing::App
load_libraries :video, :video_event
include_package 'processing.video'
attr_reader :movie
def setup
sketch_title 'Loop'
background(0)
# Load and play the video in a loop
@movie = Movie.new(self, data_path('transit.mov'))
movie.loop
end
def draw
image(movie, 0, 0, width, height)
end
# use camel case to match java reflect method
def movieEvent(m)
m.read
end
def settings
size 640, 360
end
end
Loop.new
```
A capture example-
```ruby
#!/usr/bin/env jruby -w
require 'picrate'
class TestCapture < Processing::App
load_libraries :video, :video_event
include_package 'processing.video'
attr_reader :cam
def setup
sketch_title 'Test Capture'
cameras = Capture.list
fail 'There are no cameras available for capture.' if (cameras.length == 0)
p 'Matching cameras available:'
size_pattern = Regexp.new(format('%dx%d', width, height))
select = cameras.grep size_pattern # filter available cameras
select.uniq.map { |cam| p cam.strip }
fail 'There are no matching cameras.' if (select.length == 0)
start_capture(select[0])
end
def start_capture(cam_string)
# The camera can be initialized directly using an
# element from the array returned by list:
@cam = Capture.new(self, cam_string)
p format('Using camera %s', cam_string)
cam.start
end
def draw
image(cam, 0, 0, width, height)
# The following does the same, and is faster when just drawing the image
# without any additional resizing, transformations, or tint.
# set(0, 0, cam)
end
def captureEvent(c)
c.read
end
def settings
size 1280, 720, P2D
end
end
TestCapture.new
```
### File Chooser Library ###
2A built in hybrid ruby/java library
Start by loading in the chooser library, the purpose of this library is to allow you to use the vanilla processing interface to the `native file chooser` (it is almost impossible to use vanilla processing reflection methods without this sort of wrapper)
```ruby
#!/usr/bin/env jruby -v -W2
require 'picrate'
###########
# Example Native File Chooser using vanilla processing
# select_input, and file_selected
###########
class SelectFile < Processing::App
load_library :file_chooser
def settings
size 200, 100
end
def setup
sketch_title 'Select File, native chooser'
# java_signature 'void selectInput(String, String)'
select_input('Select a File', 'file_selected')
end
# signature 'void file_selected(java.io.File file)'
def file_selected(file)
puts file.get_absolute_path unless file.nil?
end
end
SelectFile.new
```
See also [these examples](https://github.com/ruby-processing/picrate-examples/tree/master/processing_app/library/file_chooser)
[178]:https://github.com/processing/processing-video/blob/master/src/processing/video/Video.java
[original]:https://github.com/jashkenas/ruby-processing/blob/8865c934318e05e62cbfa2603e661275b1cffd31/library/boids/boids.rb
[vec3d]:https://ruby-processing.github.io/classes/vec3d/
[vec2d]:https://ruby-processing.github.io/classes/vec2d/
[forwardable]:https://ruby-doc.org/stdlib-2.0.0/libdoc/forwardable/rdoc/Forwardable.html
[example]:https://github.com/ruby-processing/picrate-examples/blob/master/library/boids/boids_example.rb