├── lib
├── templates
│ ├── application
│ │ ├── Contents
│ │ │ ├── PkgInfo
│ │ │ ├── Resources
│ │ │ │ └── sketch.icns
│ │ │ ├── MacOS
│ │ │ │ └── JavaApplicationStub
│ │ │ └── Info.plist.erb
│ │ ├── lib
│ │ │ ├── MANIFEST.MF
│ │ │ ├── library
│ │ │ │ └── library.txt
│ │ │ └── args.txt.erb
│ │ ├── run.exe
│ │ └── run.erb
│ └── create
│ │ ├── blank_sketch.rb.erb
│ │ └── p3d_sketch.rb.erb
├── ruby-processing
│ ├── version.rb
│ ├── runners
│ │ ├── run_app.rb
│ │ ├── run.rb
│ │ ├── live.rb
│ │ ├── base.rb
│ │ └── watch.rb
│ ├── helpers
│ │ ├── numeric.rb
│ │ ├── range.rb
│ │ ├── camel_string.rb
│ │ └── string_extra.rb
│ ├── config.rb
│ ├── exporters
│ │ ├── application_exporter.rb
│ │ ├── creator.rb
│ │ └── base_exporter.rb
│ ├── library_loader.rb
│ ├── helper_methods.rb
│ ├── app.rb
│ └── runner.rb
└── ruby-processing.rb
├── .yardopts
├── Gemfile
├── .mvn
├── wrapper
│ └── maven-wrapper.properties
└── extensions.xml
├── test
├── sketches
│ ├── graphics.rb
│ ├── setup_ex.rb
│ ├── vector.rb
│ ├── basic.rb
│ ├── arcball.rb
│ ├── p2d.rb
│ ├── p3d.rb
│ ├── pdf.rb
│ └── export_test.rb
├── README.md
├── deglut_spec_test.rb
├── aabb_spec_test.rb
├── helper_methods_test.rb
├── rp5_run_test.rb
├── test_map1d.rb
├── math_tool_test.rb
└── vecmath_spec_test.rb
├── .travis.yml
├── library
├── fastmath
│ └── fastmath.rb
├── video_event
│ └── video_event.rb
├── library_proxy
│ ├── library_proxy.rb
│ └── README.md
├── vecmath
│ └── vecmath.rb
├── file_chooser
│ └── file_chooser.rb
├── control_panel
│ └── control_panel.rb
└── boids
│ └── boids.rb
├── .gitignore
├── bin
└── rp5
├── src
└── monkstone
│ ├── vecmath
│ ├── vec3
│ │ └── Vec3Library.java
│ ├── vec2
│ │ ├── Vec2Library.java
│ │ └── Vec2.java
│ ├── JRender.java
│ ├── AppRender.java
│ └── ShapeRender.java
│ ├── arcball
│ ├── ArcballLibrary.java
│ ├── WheelHandler.java
│ ├── Constrain.java
│ ├── Rarcball.java
│ ├── Quaternion.java
│ ├── Jvector.java
│ └── Arcball.java
│ ├── fastmath
│ ├── DeglutLibrary.java
│ └── Deglut.java
│ ├── videoevent
│ └── VideoInterface.java
│ ├── MathToolLibrary.java
│ ├── ColorUtil.java
│ ├── core
│ └── AbstractLibrary.java
│ └── MathTool.java
├── Rakefile
├── LICENSE.md
├── CONTRIBUTING.md
├── pom.rb
├── ruby-processing.gemspec
├── vendors
└── Rakefile
├── pom.xml
├── README.md
└── CHANGELOG
/lib/templates/application/Contents/PkgInfo:
--------------------------------------------------------------------------------
1 | APPL????
--------------------------------------------------------------------------------
/.yardopts:
--------------------------------------------------------------------------------
1 | --markup markdown
2 | -
3 | CONTRIBUTING.md
4 | LICENSE.md
5 | README.md
6 |
--------------------------------------------------------------------------------
/lib/ruby-processing/version.rb:
--------------------------------------------------------------------------------
1 | module RubyProcessing
2 | VERSION = '2.7.1'
3 | end
4 |
--------------------------------------------------------------------------------
/lib/ruby-processing/runners/run_app.rb:
--------------------------------------------------------------------------------
1 | require_relative 'base'
2 |
3 | Processing.run_app
--------------------------------------------------------------------------------
/lib/ruby-processing/runners/run.rb:
--------------------------------------------------------------------------------
1 | require_relative 'base'
2 |
3 | Processing.load_and_run_sketch
4 |
--------------------------------------------------------------------------------
/lib/templates/application/lib/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Created-By: JRuby
3 | Main-Class: org.jruby.Main
--------------------------------------------------------------------------------
/lib/templates/application/lib/library/library.txt:
--------------------------------------------------------------------------------
1 | All of your included libraries will be safely tucked away in here.
--------------------------------------------------------------------------------
/lib/templates/application/run.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jashkenas/ruby-processing/HEAD/lib/templates/application/run.exe
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in ruby-processing.gemspec
4 | gemspec
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip
--------------------------------------------------------------------------------
/lib/templates/create/blank_sketch.rb.erb:
--------------------------------------------------------------------------------
1 | def setup
2 | <%= "size #{@width}, #{@height}" if @with_size %>
3 | end
4 |
5 | def draw
6 |
7 | end
8 |
--------------------------------------------------------------------------------
/lib/templates/create/p3d_sketch.rb.erb:
--------------------------------------------------------------------------------
1 | def setup
2 | <%= "size #{@width}, #{@height}, P3D" if @with_size %>
3 | end
4 |
5 | def draw
6 |
7 | end
--------------------------------------------------------------------------------
/test/sketches/graphics.rb:
--------------------------------------------------------------------------------
1 | def setup
2 | size(100, 100, P3D)
3 | puts Java::Processing::opengl::PGraphicsOpenGL.OPENGL_VERSION
4 | exit
5 | end
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: ruby
4 |
5 | rvm:
6 | - jruby-19mode
7 | - jruby-head
8 |
9 | jdk:
10 | - openjdk7
11 |
12 |
--------------------------------------------------------------------------------
/lib/templates/application/Contents/Resources/sketch.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jashkenas/ruby-processing/HEAD/lib/templates/application/Contents/Resources/sketch.icns
--------------------------------------------------------------------------------
/lib/templates/application/lib/args.txt.erb:
--------------------------------------------------------------------------------
1 | set EXPORTED='true'
2 | org.jruby.Main lib/ruby-processing/runners/run.rb lib/<%= @main_file %>
3 | <%= @windows_class_path %>
4 |
--------------------------------------------------------------------------------
/lib/templates/application/Contents/MacOS/JavaApplicationStub:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jashkenas/ruby-processing/HEAD/lib/templates/application/Contents/MacOS/JavaApplicationStub
--------------------------------------------------------------------------------
/library/fastmath/fastmath.rb:
--------------------------------------------------------------------------------
1 | require_relative '../../lib/ruby-processing'
2 | require "#{RP5_ROOT}/lib/rpextras"
3 |
4 | Java::MonkstoneFastmath::DeglutLibrary.load(JRuby.runtime)
5 |
--------------------------------------------------------------------------------
/lib/ruby-processing/helpers/numeric.rb:
--------------------------------------------------------------------------------
1 | class Numeric #:nodoc:
2 | def degrees
3 | self * 180 / Math::PI
4 | end
5 |
6 | def radians
7 | self * Math::PI / 180
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/test/sketches/setup_ex.rb:
--------------------------------------------------------------------------------
1 | def setup
2 | size(300, 300)
3 | begin
4 | unknown_method()
5 | rescue NoMethodError => e
6 | puts e
7 | exit
8 | end
9 | end
10 |
11 | def draw
12 | end
13 |
--------------------------------------------------------------------------------
/test/sketches/vector.rb:
--------------------------------------------------------------------------------
1 | load_library :vecmath
2 |
3 | def setup
4 | size(300, 300)
5 | a = Vec2D.new(1.0, 1.0)
6 | b = Vec2D.new(1.0, 1.0)
7 | (a == b) ? puts('ok') : puts('fail')
8 | exit
9 | end
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | syntax: glob
2 | .DS_Store
3 | *.sw*
4 | *~
5 | *.pdf
6 | *.gem
7 | *.tgz
8 | *.jar
9 | *.zip
10 | Gemfile.lock
11 | .ruby-version
12 | .jrubyrc
13 | tmp
14 | vendors/*.tar.gz
15 | target
16 | MANIFEST.MF
17 |
--------------------------------------------------------------------------------
/lib/ruby-processing/helpers/range.rb:
--------------------------------------------------------------------------------
1 | # Extend Range class to include clip (used to implement processing constrain)
2 | class Range #:nodoc:
3 | def clip(n)
4 | return n if cover?(n)
5 | (n < min) ? min : max
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/.mvn/extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | io.takari.polyglot
5 | polyglot-ruby
6 | 0.1.18
7 |
8 |
9 |
--------------------------------------------------------------------------------
/bin/rp5:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | file = __FILE__
4 | if test(?l, file)
5 | require "pathname"
6 | file = Pathname.new(file).realpath
7 | end
8 |
9 | require File.expand_path(File.dirname(file) + "/../lib/ruby-processing")
10 | Processing::Runner.execute
11 |
--------------------------------------------------------------------------------
/test/sketches/basic.rb:
--------------------------------------------------------------------------------
1 | java_alias :background_int, :background, [Java::int]
2 |
3 | def setup
4 | size(300, 300)
5 | frame_rate(10)
6 | end
7 |
8 | def draw
9 | background_int 0
10 | if frame_count == 3
11 | puts 'ok'
12 | exit
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/test/sketches/arcball.rb:
--------------------------------------------------------------------------------
1 | load_library :vecmath
2 |
3 | def setup
4 | size(300, 300, P3D)
5 | ArcBall.init(self)
6 | frame_rate(10)
7 | end
8 |
9 | def draw
10 | background 39, 232, 51
11 | if frame_count == 3
12 | puts 'ok'
13 | exit
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/test/sketches/p2d.rb:
--------------------------------------------------------------------------------
1 | java_alias :background_int, :background, [Java::int]
2 |
3 | def setup
4 | size(300, 300, P2D)
5 | frame_rate(10)
6 | end
7 |
8 | def draw
9 | background_int 255
10 | if frame_count == 3
11 | puts 'ok'
12 | exit
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/templates/application/run.erb:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | <%#
3 | bash script to run exported application on linux
4 | %>
5 | APPDIR=$(dirname "$0")
6 | export EXPORTED="true"
7 | cd "$APPDIR/lib"
8 | java -cp "<%= @linux_class_path %>" org.jruby.Main ruby-processing/runners/run.rb <%= @main_file %>
9 |
--------------------------------------------------------------------------------
/test/sketches/p3d.rb:
--------------------------------------------------------------------------------
1 | java_alias :background_float_float_float, :background, [Java::float, Java::float, Java::float]
2 |
3 | def setup
4 | size(300, 300, P3D)
5 | frame_rate(10)
6 | end
7 |
8 | def draw
9 | background_float_float_float 39, 232, 51
10 | if frame_count == 3
11 | puts 'ok'
12 | exit
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/library/video_event/video_event.rb:
--------------------------------------------------------------------------------
1 | require_relative '../../lib/ruby-processing'
2 | require "#{RP5_ROOT}/lib/rpextras"
3 |
4 | class Processing::App
5 | include Java::MonkstoneVideoevent::VideoInterface
6 | def captureEvent(c)
7 | # satisfy implement abstract class
8 | end
9 |
10 | def movieEvent(m)
11 | # satisfy implement abstract class
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/library/library_proxy/library_proxy.rb:
--------------------------------------------------------------------------------
1 | require_relative '../../lib/ruby-processing'
2 | require "#{RP5_ROOT}/lib/rpextras"
3 |
4 | LibraryProxy = Java::MonkstoneCore::AbstractLibrary
5 |
6 | # classes that inherit from Library are expected to implement
7 | # the abstract methods of processing.core.AbstractLibrary
8 | # def pre...
9 | # def draw...
10 | # def post...
11 | # NOOP is fine...
12 |
--------------------------------------------------------------------------------
/library/vecmath/vecmath.rb:
--------------------------------------------------------------------------------
1 | require_relative '../../lib/ruby-processing'
2 | require "#{RP5_ROOT}/lib/rpextras"
3 |
4 | Java::MonkstoneArcball::ArcballLibrary.load(JRuby.runtime)
5 | Java::MonkstoneVecmathVec2::Vec2Library.load(JRuby.runtime)
6 | Java::MonkstoneVecmathVec3::Vec3Library.load(JRuby.runtime)
7 |
8 | AppRender ||= Java::MonkstoneVecmath::AppRender
9 | ShapeRender ||= Java::MonkstoneVecmath::ShapeRender
10 |
--------------------------------------------------------------------------------
/lib/ruby-processing/runners/live.rb:
--------------------------------------------------------------------------------
1 | # A pry shell for live coding.
2 | # Will start with your sketch.
3 | require_relative 'base'
4 | Processing.load_and_run_sketch
5 |
6 | class PryException < StandardError
7 | end
8 |
9 | MESSAGE = "You need to 'jruby -S gem install pry' for 'live' mode"
10 |
11 | if Gem::Specification.find_all_by_name('pry').any?
12 | require 'pry'
13 | $app.pry
14 | else
15 | fail(PryException.new, MESSAGE)
16 | end
17 |
--------------------------------------------------------------------------------
/lib/ruby-processing/config.rb:
--------------------------------------------------------------------------------
1 | require 'psych'
2 |
3 | module Processing
4 |
5 | if ENV['EXPORTED'].eql?('true')
6 | RP_CONFIG = { 'PROCESSING_ROOT' => RP5_ROOT, 'JRUBY' => 'false' }
7 | end
8 | unless defined? RP_CONFIG
9 | begin
10 | CONFIG_FILE_PATH = File.expand_path('~/.rp5rc')
11 | RP_CONFIG = (Psych.load_file(CONFIG_FILE_PATH))
12 | rescue
13 | warn('WARNING: you need to set PROCESSING_ROOT in ~/.rp5rc')
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/test/sketches/pdf.rb:
--------------------------------------------------------------------------------
1 | # One Frame.
2 | #
3 | # Saves one PDF with the contents of the display window.
4 | # Because this example uses beginRecord, the image is shown
5 | # on the display window and is saved to the file.
6 |
7 | load_library 'pdf'
8 | include_package 'processing.pdf'
9 |
10 | def setup
11 | size(600, 600)
12 | begin_record(PDF, "line.pdf")
13 | background(255)
14 | stroke(0, 20)
15 | strokeWeight(20.0)
16 | line(200, 0, 400, height)
17 | end_record
18 | end
19 |
20 |
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | rbest/vim
2 | :syntax onsketches/sketches/
3 | b
4 | ========
5 | sketches/
6 | OK strictly thesketches/ould dsketches/est objects, rather than the indirect tests on io here. These test make use of minitest capture_io (this seems to require real files rather than temp files?). Also there seems to be some problem with how the built in version minitest run so I've specified gem "minitest". The graphics test is designed to fail if your graphics setup does not supports opengl 3+, this may not be fatal, but as message states is probably suboptimal.
7 | sketches/
8 | [gist]:https://gist.githusketches/kstone/6145906
9 |
--------------------------------------------------------------------------------
/lib/ruby-processing.rb:
--------------------------------------------------------------------------------
1 | # Ruby-Processing is for Code Art.
2 | # Send suggestions, ideas, and hate-mail to jashkenas [at] gmail.com
3 | # Also, send samples and libraries.
4 | unless defined? RP5_ROOT
5 | $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
6 | RP5_ROOT = File.expand_path(File.dirname(__FILE__) + '/../')
7 | end
8 |
9 | SKETCH_ROOT ||= Dir.pwd
10 |
11 | require 'ruby-processing/version'
12 | require 'ruby-processing/helpers/numeric'
13 | require 'ruby-processing/helpers/range'
14 |
15 | # The top-level namespace, a home for all Ruby-Processing classes.
16 | module Processing
17 | require 'ruby-processing/runner'
18 | end
19 |
--------------------------------------------------------------------------------
/lib/ruby-processing/helpers/camel_string.rb:
--------------------------------------------------------------------------------
1 | require 'forwardable'
2 |
3 | # Avoid the monkey patching of String for camelize
4 | class CamelString
5 | extend Forwardable
6 | def_delegators(:@string, *String.public_instance_methods(false))
7 | def initialize(str = 'no_name')
8 | @string = (str.length > 60) ? 'long_name' : str
9 | end
10 |
11 | def camelize(first_letter_in_uppercase = true)
12 | if first_letter_in_uppercase
13 | @string.gsub(%r{\/(.?)}) { '::' + Regexp.last_match[1].upcase }
14 | .gsub(/(^|_)(.)/) { Regexp.last_match[2].upcase }
15 | else
16 | @string[0] + camelize[1..-1]
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/src/monkstone/vecmath/vec3/Vec3Library.java:
--------------------------------------------------------------------------------
1 | package monkstone.vecmath.vec3;
2 |
3 | import java.io.IOException;
4 | import org.jruby.Ruby;
5 | import org.jruby.runtime.load.Library;
6 |
7 | /**
8 | *
9 | * @author Martin Prout
10 | */
11 | public class Vec3Library implements Library {
12 |
13 | /**
14 | *
15 | * @param runtime
16 | */
17 | public static void load(final Ruby runtime) {
18 | Vec3.createVec3(runtime);
19 | }
20 |
21 | /**
22 | *
23 | * @param runtime
24 | * @param wrap
25 | * @throws IOException
26 | */
27 | @Override
28 | public void load(final Ruby runtime, boolean wrap) throws IOException {
29 | load(runtime);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/monkstone/arcball/ArcballLibrary.java:
--------------------------------------------------------------------------------
1 | package monkstone.arcball;
2 |
3 | import java.io.IOException;
4 | import org.jruby.Ruby;
5 | import org.jruby.runtime.load.Library;
6 |
7 | /**
8 | *
9 | * @author Martin Prout
10 | */
11 | public class ArcballLibrary implements Library {
12 |
13 | /**
14 | *
15 | * @param runtime
16 | */
17 |
18 | public static void load(final Ruby runtime) {
19 | Rarcball.createArcBall(runtime);
20 | }
21 |
22 | /**
23 | *
24 | * @param runtime
25 | * @param wrap
26 | * @throws IOException
27 | */
28 | @Override
29 | public void load(final Ruby runtime, boolean wrap) throws IOException {
30 | load(runtime);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/monkstone/vecmath/vec2/Vec2Library.java:
--------------------------------------------------------------------------------
1 | package monkstone.vecmath.vec2;
2 |
3 | import java.io.IOException;
4 | import org.jruby.Ruby;
5 | import org.jruby.runtime.load.Library;
6 |
7 |
8 | /**
9 | *
10 | * @author Martin Prout
11 | */
12 | public class Vec2Library implements Library{
13 |
14 | /**
15 | *
16 | * @param runtime
17 | */
18 | public static void load(final Ruby runtime) {
19 | Vec2.createVec2(runtime);
20 | }
21 |
22 | /**
23 | *
24 | * @param runtime
25 | * @param wrap
26 | * @throws IOException
27 | */
28 | @Override
29 | public void load(final Ruby runtime, boolean wrap) throws IOException {
30 | load(runtime);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/monkstone/fastmath/DeglutLibrary.java:
--------------------------------------------------------------------------------
1 | package monkstone.fastmath;
2 |
3 | import java.io.IOException;
4 | import org.jruby.Ruby;
5 | import org.jruby.runtime.load.Library;
6 |
7 |
8 | /**
9 | *
10 | * @author Martin Prout
11 | */
12 | public class DeglutLibrary implements Library {
13 |
14 | /**
15 | *
16 | * @param runtime
17 | */
18 | public static void load(final Ruby runtime) {
19 | Deglut.createDeglut(runtime);
20 | }
21 |
22 | /**
23 | *
24 | * @param runtime
25 | * @param wrap
26 | * @throws IOException
27 | */
28 | @Override
29 | public void load(final Ruby runtime, boolean wrap) throws IOException {
30 | load(runtime);
31 | }
32 |
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/test/deglut_spec_test.rb:
--------------------------------------------------------------------------------
1 | gem 'minitest' # don't use bundled minitest
2 | require 'java'
3 | require 'minitest/autorun'
4 | require 'minitest/pride'
5 |
6 | require_relative '../lib/rpextras'
7 |
8 | Java::MonkstoneFastmath::DeglutLibrary.new.load(JRuby.runtime, false)
9 |
10 | EPSILON ||= 1.0e-04
11 | TO_RADIAN = Math::PI / 180
12 |
13 | Dir.chdir(File.dirname(__FILE__))
14 |
15 | class DeglutTest < Minitest::Test
16 | def test_cos_sin
17 | (-720..720).step(1) do |deg|
18 | sine = DegLut.sin(deg)
19 | deg_sin = Math.sin(deg * TO_RADIAN)
20 | assert_in_delta(sine, deg_sin, EPSILON)
21 | cosine = DegLut.cos(deg)
22 | deg_cos = Math.cos(deg * TO_RADIAN)
23 | assert_in_delta(cosine, deg_cos, EPSILON)
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/src/monkstone/videoevent/VideoInterface.java:
--------------------------------------------------------------------------------
1 | package monkstone.videoevent;
2 | import processing.video.Movie;
3 | import processing.video.Capture;
4 | /**
5 | * This interface makes it easier/possible to use the reflection methods
6 | * from Movie and Capture classes in Processing::App in ruby-processing
7 | * @author Martin Prout
8 | */
9 | public interface VideoInterface {
10 | /**
11 | * Used to implement reflection method in PApplet
12 | * @see processing.video.Movie
13 | * @param movie Movie
14 | */
15 | public void movieEvent(Movie movie);
16 | /**
17 | * Used to implement reflection method in PApplet
18 | * @see processing.video.Capture
19 | * @param capture Capture
20 | */
21 | public void captureEvent(Capture capture);
22 | }
23 |
--------------------------------------------------------------------------------
/test/sketches/export_test.rb:
--------------------------------------------------------------------------------
1 | gem 'minitest' # don't use bundled minitest
2 | require 'minitest/autorun'
3 |
4 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
5 |
6 | Dir.chdir(File.dirname(__FILE__))
7 |
8 | class Rp5Test < Minitest::Test
9 |
10 | def test_normal
11 | out, _err_ = capture_io do
12 | open('|../bin/rp5 app pdf.rb', 'r') do |io|
13 | while l = io.gets
14 | puts(l.chop)
15 | end
16 | end
17 | end
18 | assert_match(/ok/, out, 'Failed PDF sketch')
19 | end
20 |
21 | def test_p3d
22 | out, _err_ = capture_io do
23 | open('|../bin/rp5 app p3d.rb', 'r') do |io|
24 | while l = io.gets
25 | puts(l.chop)
26 | end
27 | end
28 | end
29 | assert_match(/ok/, out, 'Failed P3D sketch')
30 | end
31 | end
--------------------------------------------------------------------------------
/lib/ruby-processing/helpers/string_extra.rb:
--------------------------------------------------------------------------------
1 | require 'forwardable'
2 |
3 | # Avoid the monkey patching of String for underscore/titleize/humanize
4 | class StringExtra
5 | extend Forwardable
6 | def_delegators(:@string, *String.public_instance_methods(false))
7 | def initialize(str = 'no_name')
8 | @string = (str.length > 60) ? 'long_name' : str
9 | end
10 |
11 | def titleize
12 | gsub(/::/, '/')
13 | .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
14 | .gsub(/([a-z\d])([A-Z])/, '\1_\2')
15 | .tr('-', '_')
16 | .downcase
17 | .gsub(/_id$/, '')
18 | .gsub(/_/, ' ').capitalize
19 | .gsub(/\b([a-z])/) { Regexp.last_match[1].capitalize }
20 | end
21 |
22 | def humanize
23 | gsub(/_id$/, '').gsub(/_/, ' ').capitalize
24 | end
25 |
26 | def underscore
27 | gsub(/::/, '/')
28 | .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
29 | .gsub(/([a-z\d])([A-Z])/, '\1_\2')
30 | .tr('-', '_')
31 | .downcase
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/src/monkstone/vecmath/JRender.java:
--------------------------------------------------------------------------------
1 | package monkstone.vecmath;
2 |
3 | /**
4 | *
5 | * @author Martin Prout
6 | */
7 | public interface JRender {
8 |
9 | /**
10 | *
11 | * @param x
12 | * @param y
13 | */
14 | public void vertex(double x, double y);
15 |
16 | /**
17 | *
18 | * @param x
19 | * @param y
20 | */
21 | public void curveVertex(double x, double y);
22 |
23 | /**
24 | *
25 | * @param x
26 | * @param y
27 | * @param z
28 | */
29 | public void vertex(double x, double y, double z);
30 |
31 | /**
32 | *
33 | * @param x
34 | * @param y
35 | * @param z
36 | * @param u
37 | * @param v
38 | */
39 | public void vertex(double x, double y, double z, double u, double v);
40 |
41 | /**
42 | *
43 | * @param x
44 | * @param y
45 | * @param z
46 | */
47 | public void curveVertex(double x, double y, double z);
48 |
49 | /**
50 | *
51 | * @param x
52 | * @param y
53 | * @param z
54 | */
55 | public void normal(double x, double y, double z);
56 | }
57 |
--------------------------------------------------------------------------------
/test/aabb_spec_test.rb:
--------------------------------------------------------------------------------
1 | gem 'minitest' # don't use bundled minitest
2 | require 'java'
3 | require 'minitest/autorun'
4 | require 'minitest/pride'
5 |
6 | require_relative '../lib/rpextras'
7 | require_relative '../lib/ruby-processing/helpers/aabb'
8 |
9 | Java::MonkstoneVecmathVec2::Vec2Library.new.load(JRuby.runtime, false)
10 | Java::MonkstoneVecmathVec3::Vec3Library.new.load(JRuby.runtime, false)
11 |
12 | EPSILON ||= 1.0e-04
13 |
14 | Dir.chdir(File.dirname(__FILE__))
15 |
16 | class MathToolTest < Minitest::Test
17 | def test_aabb_new
18 | x, y = 1.0000001, 1.01
19 | a = Vec2D.new(x, y)
20 | assert AaBb.new(center: Vec2D.new, extent: a).kind_of? AaBb
21 | x0, y0 = -4, -4
22 | a = Vec2D.new(x0, y0)
23 | b = a *= -1
24 | assert AaBb.from_min_max(min: a, max: b).kind_of? AaBb
25 | x, y = 1.0000001, 1.01
26 | a = AaBb.new(center: Vec2D.new, extent: Vec2D.new(x, y))
27 | a.position(Vec2D.new(4, 6))
28 | assert a.center == Vec2D.new(4, 6)
29 | x, y = 1.0000001, 1.01
30 | a = AaBb.new(center: Vec2D.new, extent: Vec2D.new(x, y))
31 | a.position(Vec2D.new(4, 6)) { false }
32 | assert a.center == Vec2D.new
33 | end
34 | end
35 |
36 |
--------------------------------------------------------------------------------
/src/monkstone/MathToolLibrary.java:
--------------------------------------------------------------------------------
1 | /**
2 | * The purpose of this class is to load the MathTool into ruby-processing runtime
3 | * Copyright (C) 2015-16 Martin Prout. This code is free software; you can
4 | * redistribute it and/or modify it under the terms of the GNU Lesser General
5 | * Public License as published by the Free Software Foundation; either version
6 | * 2.1 of the License, or (at your option) any later version.
7 | *
8 | * Obtain a copy of the license at http://www.gnu.org/licenses/lgpl-2.1.html
9 | */
10 |
11 | package monkstone;
12 |
13 | import java.io.IOException;
14 | import org.jruby.Ruby;
15 | import org.jruby.runtime.load.Library;
16 |
17 |
18 | /**
19 | *
20 | * @author Martin Prout
21 | */
22 | public class MathToolLibrary implements Library{
23 |
24 | /**
25 | *
26 | * @param runtime
27 | */
28 | public static void load(final Ruby runtime) {
29 | MathTool.createMathTool(runtime);
30 | }
31 |
32 | /**
33 | *
34 | * @param runtime
35 | * @param wrap
36 | * @throws java.io.IOException
37 | */
38 | @Override
39 | public void load(final Ruby runtime, boolean wrap) throws IOException {
40 | load(runtime);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/monkstone/arcball/WheelHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Martin Prout
3 | *
4 | * This library is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 2.1 of the License, or (at your option) any later version.
8 | *
9 | * http://creativecommons.org/licenses/LGPL/2.1/
10 | *
11 | * This library is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public
17 | * License along with this library; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 |
21 | package monkstone.arcball;
22 |
23 | /**
24 | * @author Martin Prout
25 | * from a borrowed pattern seen in Jonathan Feinbergs Peasycam
26 | * when I was struggling with non functioning browser applet,
27 | * probably superfluous here.
28 | */
29 | public interface WheelHandler {
30 | /**
31 | *
32 | * @param amount
33 | */
34 |
35 | public void handleWheel(final int amount);
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require_relative 'lib/ruby-processing/version'
2 |
3 | def create_manifest
4 | title = 'Implementation-Title: rpextras (java extension for ruby-processing)'
5 | version = format('Implementation-Version: %s', RubyProcessing::VERSION)
6 | file = File.open('MANIFEST.MF', 'w') do |f|
7 | f.puts(title)
8 | f.puts(version)
9 | end
10 | end
11 |
12 | task :default => [:init, :compile, :test, :gem]
13 |
14 | desc 'Create Manifest'
15 | task :init do
16 | create_manifest
17 | end
18 |
19 | desc 'Build gem'
20 | task :gem do
21 | sh 'gem build ruby-processing.gemspec'
22 | end
23 |
24 | desc 'Compile'
25 | task :compile do
26 | sh 'mvn package'
27 | sh 'mv target/rpextras.jar lib'
28 | end
29 |
30 | desc 'Test'
31 | task :test do
32 | sh 'jruby test/vecmath_spec_test.rb'
33 | sh 'jruby test/deglut_spec_test.rb'
34 | sh 'jruby test/math_tool_test.rb'
35 | home = File.expand_path('~')
36 | config = File.exist?(format('%s/.rp5rc', home))
37 | if config
38 | sh 'jruby test/helper_methods_test.rb'
39 | ruby 'test/rp5_run_test.rb'
40 | else
41 | warn format('You should create %s/.rp5rc to run sketch tests', home)
42 | end
43 | end
44 |
45 | desc 'Clean'
46 | task :clean do
47 | Dir['./**/*.%w{jar gem}'].each do |path|
48 | puts format('Deleting %s ...', path)
49 | File.delete(path)
50 | end
51 | FileUtils.rm_rf('./tmp')
52 | end
53 |
--------------------------------------------------------------------------------
/lib/ruby-processing/runners/base.rb:
--------------------------------------------------------------------------------
1 | # -*- encoding : utf-8 -*-
2 |
3 | SKETCH_PATH ||= ARGV.shift
4 | SKETCH_ROOT ||= File.dirname(SKETCH_PATH)
5 |
6 | # we can safely require app.rb as we are using a jruby runtime
7 | require_relative '../app'
8 |
9 | # More processing module
10 | module Processing
11 | # For use with "bare" sketches that don't want to define a class or methods
12 | BARE_WRAP = <<-EOS
13 | class Sketch < Processing::App
14 | %s
15 | end
16 | EOS
17 |
18 | NAKED_WRAP = <<-EOS
19 | class Sketch < Processing::App
20 | def setup
21 | size(DEFAULT_WIDTH, DEFAULT_HEIGHT)
22 | %s
23 | no_loop
24 | end
25 | end
26 | EOS
27 |
28 | # This method is the common entry point to run a sketch, bare or complete.
29 |
30 | def self.run_app
31 | load SKETCH_PATH
32 | Processing::App.sketch_class.new unless $app
33 | end
34 |
35 | def self.load_and_run_sketch
36 | source = read_sketch_source
37 | wrapped = !source.match(/^[^#]*< Processing::App/).nil?
38 | no_methods = source.match(/^[^#]*(def\s+setup|def\s+draw)/).nil?
39 | if wrapped
40 | run_app
41 | return
42 | end
43 | code = no_methods ? format(NAKED_WRAP, source) : format(BARE_WRAP, source)
44 | Object.class_eval(code, SKETCH_PATH, -1)
45 | Processing::App.sketch_class.new
46 | end
47 |
48 | # Read in the sketch source code. Needs to work both online and offline.
49 | def self.read_sketch_source
50 | File.read(SKETCH_PATH)
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Ruby-Processing is released under the MIT License.
2 | You can do pretty much whatever you'd like with it.
3 |
4 | ___
5 |
6 | Copyright (c) 2008-2014 omygawshkenas
7 |
8 | Permission is hereby granted, free of charge,
9 | to any person obtaining a copy of this software
10 | and associated documentation files (the "Software"),
11 | to deal in the Software without restriction,
12 | including without limitation the rights to use,
13 | copy, modify, merge, publish, distribute, sublicense,
14 | and/or sell copies of the Software, and to permit
15 | persons to whom the Software is furnished to do so,
16 | subject to the following conditions:
17 |
18 | The above copyright notice and this permission
19 | notice shall be included in all copies or substantial
20 | portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
23 | OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
28 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
29 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE
30 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 |
32 | ___
33 |
34 | Ruby-Processing also distributes core components
35 | of both [JRuby][] and [Processing][] both of which are
36 | licensed under the GNU lesser public license
37 |
38 | [jruby]: http://www.jruby.org/
39 | [processing]: http://www.processing.org/
40 |
--------------------------------------------------------------------------------
/src/monkstone/ColorUtil.java:
--------------------------------------------------------------------------------
1 | /**
2 | * The purpose of this utility is to allow ruby-processing users to use an alternative
3 | * to processing.org color their sketches (to cope with ruby FixNumber vs java int)
4 | * Copyright (C) 2015-16 Martin Prout. This tool is free software; you can
5 | * redistribute it and/or modify it under the terms of the GNU Lesser General
6 | * Public License as published by the Free Software Foundation; either version
7 | * 2.1 of the License, or (at your option) any later version.
8 | *
9 | * Obtain a copy of the license at http://www.gnu.org/licenses/lgpl-2.1.html
10 | */
11 | package monkstone;
12 |
13 | /**
14 | *
15 | * @author Martin Prout
16 | */
17 | public class ColorUtil {
18 |
19 | /**
20 | * Returns hex long as a positive int unless greater than Integer.MAX_VALUE
21 | * else return the complement as a negative integer or something like that
22 | */
23 | static final int hexLong(long hexlong) {
24 | long SPLIT = Integer.MAX_VALUE + 1;
25 | if (hexlong < SPLIT) {
26 | return (int) hexlong;
27 | } else {
28 | return (int) (hexlong - SPLIT * 2L);
29 | }
30 | }
31 |
32 | static public int colorString(String hexstring) {
33 | return java.awt.Color.decode(hexstring).getRGB();
34 | }
35 |
36 | static public float colorLong(double hex) {
37 | return (float) hex;
38 | }
39 |
40 | static public int colorLong(long hexlong){
41 | return hexLong(hexlong);
42 | }
43 |
44 | static public float colorDouble(double hex){
45 | return (float)hex;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 | In the spirit of [free software][free-sw], **everyone** is encouraged to help improve this project.
3 |
4 | Here are some ways *you* can contribute:
5 |
6 | * by reporting bugs or problems [here][]
7 | * by closing [issues][]
8 | * by proselytizing [JRubyArt][], it is the future we need more champions
9 | * by supporting [Processing.org][], nothing to do with us but we rely on them
10 | * by figuring out how we could clamp pbox2d and toxiclibs version to 0.4.2 and 0.4.0
11 | * by updating build to match [JRubyArt][]
12 |
13 | ## Submitting an Issue
14 | We use the [GitHub issue tracker][issues] to track bugs and features. Before
15 | submitting a bug report or feature request, check to make sure it has not
16 | already been submitted. When submitting a bug report, ideally include a [Gist][]
17 | that includes a stack trace and any details that may be necessary to reproduce
18 | the bug, including your gem version, Ruby version, and operating system.
19 |
20 | ## Submitting a Pull Request
21 | 1. [Fork the repository.][fork]
22 | 2. [Submit a pull request.][pr]
23 |
24 | [free-sw]: http://www.fsf.org/licensing/essays/free-sw.html
25 | [here]: https://github.com/jashkenas/ruby-processing/issues
26 | [issues]: https://github.com/jashkenas/ruby-processing/issues
27 | [gist]: https://gist.github.com/
28 | [fork]: http://help.github.com/fork-a-repo/
29 | [pr]: http://help.github.com/send-pull-requests/
30 | [processing.org]: http://processing.org/foundation/
31 | [development branch]: https://github.com/ruby-processing/JRubyArt
32 | [contributing examples]: https://github.com/ruby-processing/Example-Sketches/blob/master/CONTRIBUTING.md
33 | [shoes/furoshoki]:https://github.com/shoes/furoshiki
34 | [JRubyArt]:https://github.com/ruby-processing/JRubyArt
35 |
--------------------------------------------------------------------------------
/src/monkstone/arcball/Constrain.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Martin Prout
3 | *
4 | * This library is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 2.1 of the License, or (at your option) any later version.
8 | *
9 | * http://creativecommons.org/licenses/LGPL/2.1/
10 | *
11 | * This library is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public
17 | * License along with this library; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 |
21 | package monkstone.arcball;
22 |
23 | /**
24 | *
25 | * @author Martin Prout
26 | */
27 | public enum Constrain {
28 |
29 | /**
30 | * Used to constrain arc-ball rotation about X axis
31 | */
32 |
33 | XAXIS(0),
34 | /**
35 | * Used to constrain arc-ball rotation about Y axis
36 | */
37 | YAXIS(1),
38 | /**
39 | * Used to constrain arc-ball rotation about Z axis
40 | */
41 | ZAXIS(2),
42 | /**
43 | * Used for default no constrain arc-ball about any axis
44 | */
45 | FREE(-1);
46 | private final int index;
47 |
48 | Constrain(int idx) {
49 | this.index = idx;
50 | }
51 |
52 | /**
53 | * Numeric value of constrained axis
54 | * @return index int
55 | */
56 | public int index() {
57 | return index;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/pom.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 | project 'rp5extras', 'https://github.com/jashkenas/ruby-processing' do
3 | model_version '4.0.0'
4 | id 'ruby-processing:rp5extras', '2.7.1'
5 | packaging 'jar'
6 | description 'rp5extras for ruby-processing'
7 | organization 'ruby-processing', 'https://ruby-processing.github.io'
8 | developer 'monkstone' do
9 | name 'Martin Prout'
10 | email 'mamba2928@yahoo.co.uk'
11 | roles 'developer'
12 | end
13 |
14 | issue_management 'https://github.com/jashkenas/ruby-processing/issues', 'Github'
15 |
16 | source_control(
17 | url: 'https://github.com/jashkenas/ruby-processing',
18 | connection: 'scm:git:git://github.com/jashkenas/ruby-processing.git',
19 | developer_connection: 'scm:git:git@github.com/jashkenas/ruby-processing.git'
20 | )
21 |
22 | properties(
23 | 'maven.compiler.source' => '1.7',
24 | 'project.build.sourceEncoding' => 'UTF-8',
25 | 'maven.compiler.target' => '1.7',
26 | 'polyglot.dump.pom' => 'pom.xml',
27 | 'processing.api' => 'http://processing.github.io/processing-javadocs/core/',
28 | 'jruby.api' => 'http://jruby.org/apidocs/'
29 | )
30 |
31 | pom 'org.jruby:jruby:1.7.25'
32 | jar 'org.processing:core:2.2.1'
33 | jar 'org.processing:video:2.2.1'
34 | plugin_management do
35 | plugin :resources, '2.6'
36 | plugin :dependency, '2.10'
37 | plugin(
38 | :compiler, '3.5.1',
39 | 'source' => '1.7',
40 | 'target' => '1.7'
41 | )
42 | plugin(
43 | :javadoc, '2.10.4',
44 | 'detectOfflineLinks' => 'false',
45 | 'links' => ['${processing.api}', '${jruby.api}']
46 | )
47 | plugin(
48 | :jar, '3.0.2',
49 | 'archive' => {
50 | 'manifestFile' => 'MANIFEST.MF'
51 | }
52 | )
53 | end
54 | build do
55 | default_goal 'package'
56 | source_directory 'src'
57 | final_name 'rpextras'
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/src/monkstone/vecmath/AppRender.java:
--------------------------------------------------------------------------------
1 | package monkstone.vecmath;
2 |
3 | import processing.core.PApplet;
4 |
5 | /**
6 | *
7 | * @author Martin Prout
8 | */
9 | public class AppRender implements JRender {
10 |
11 | final PApplet app;
12 |
13 | /**
14 | *
15 | * @param app
16 | */
17 | public AppRender(final PApplet app) {
18 | this.app = app;
19 | }
20 |
21 | /**
22 | *
23 | * @param x
24 | * @param y
25 | */
26 | @Override
27 | public void vertex(double x, double y) {
28 | app.vertex((float) x, (float) y);
29 | }
30 |
31 | /**
32 | *
33 | * @param x
34 | * @param y
35 | */
36 | @Override
37 | public void curveVertex(double x, double y) {
38 | app.curveVertex((float) x, (float) y);
39 | }
40 |
41 | /**
42 | *
43 | * @param x
44 | * @param y
45 | * @param z
46 | */
47 | @Override
48 | public void vertex(double x, double y, double z) {
49 | app.vertex((float) x, (float) y, (float) z);
50 | }
51 |
52 | /**
53 | *
54 | * @param x
55 | * @param y
56 | * @param z
57 | */
58 | @Override
59 | public void normal(double x, double y, double z) {
60 | app.normal((float) x, (float) y, (float) z);
61 | }
62 |
63 | /**
64 | *
65 | * @param x
66 | * @param y
67 | * @param z
68 | * @param u
69 | * @param v
70 | */
71 | @Override
72 | public void vertex(double x, double y, double z, double u, double v) {
73 | app.vertex((float) x, (float) y, (float) z, (float) u, (float) v);
74 | }
75 |
76 | /**
77 | *
78 | * @param x
79 | * @param y
80 | * @param z
81 | */
82 | @Override
83 | public void curveVertex(double x, double y, double z) {
84 | app.curveVertex((float) x, (float) y, (float) z);
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/lib/templates/application/Contents/Info.plist.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleName
6 | <%= @title %>
7 | CFBundleVersion
8 | 1.0
9 | CFBundleAllowMixedLocalizations
10 | true
11 | CFBundleExecutable
12 | JavaApplicationStub
13 | CFBundleDevelopmentRegion
14 | English
15 | CFBundlePackageType
16 | APPL
17 | CFBundleSignature
18 | ????
19 | CFBundleInfoDictionaryVersion
20 | 6.0
21 | CFBundleIconFile
22 | sketch.icns
23 | CFBundleIdentifier
24 | org.ruby-processing.<%= @class_name %>
25 | Java
26 |
27 | VMOptions
28 | -Xms756M -Xmx756M
29 | MainClass
30 | org.jruby.Main
31 | WorkingDirectory
32 | $JAVAROOT
33 | Arguments
34 | ruby-processing/runners/run.rb <%= @main_file %>
35 | JVMVersion
36 | 1.7+
37 | ClassPath
38 | <%= @class_path %>
39 | Properties
40 |
41 | apple.laf.useScreenMenuBar
42 | true
43 | apple.awt.showGrowBox
44 | false
45 | com.apple.smallTabs
46 | true
47 | apple.awt.Antialiasing
48 | false
49 | apple.awt.TextAntialiasing
50 | true
51 | com.apple.hwaccel
52 | true
53 | apple.awt.use-file-dialog-packages
54 | false
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/monkstone/vecmath/ShapeRender.java:
--------------------------------------------------------------------------------
1 | package monkstone.vecmath;
2 |
3 | import processing.core.PShape;
4 |
5 | /**
6 | *
7 | * @author Martin Prout
8 | */
9 | public class ShapeRender implements JRender {
10 |
11 | final PShape shape;
12 |
13 | /**
14 | *
15 | * @param shape
16 | */
17 | public ShapeRender(final PShape shape) {
18 | this.shape = shape;
19 |
20 | }
21 |
22 | /**
23 | *
24 | * @param x
25 | * @param y
26 | */
27 | @Override
28 | public void vertex(double x, double y) {
29 | shape.vertex((float) x, (float) y);
30 | }
31 |
32 | /**
33 | *
34 | * @param x
35 | * @param y
36 | */
37 | @Override
38 | public void curveVertex(double x, double y) {
39 | throw new UnsupportedOperationException("Not implemented for this renderer");
40 | }
41 |
42 | /**
43 | *
44 | * @param x
45 | * @param y
46 | * @param z
47 | */
48 | @Override
49 | public void vertex(double x, double y, double z) {
50 | shape.vertex((float) x, (float) y, (float) z);
51 | }
52 |
53 | /**
54 | *
55 | * @param x
56 | * @param y
57 | * @param z
58 | */
59 | @Override
60 | public void normal(double x, double y, double z) {
61 | shape.normal((float) x, (float) y, (float) z);
62 | }
63 |
64 | /**
65 | *
66 | * @param x
67 | * @param y
68 | * @param z
69 | * @param u
70 | * @param v
71 | */
72 | @Override
73 | public void vertex(double x, double y, double z, double u, double v) {
74 | shape.vertex((float) x, (float) y, (float) z, (float) u, (float) v);
75 | }
76 |
77 | /**
78 | *
79 | * @param x
80 | * @param y
81 | * @param z
82 | */
83 | @Override
84 | public void curveVertex(double x, double y, double z) {
85 | throw new UnsupportedOperationException("Not implemented for this renderer");
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/test/helper_methods_test.rb:
--------------------------------------------------------------------------------
1 | gem 'minitest' # don't use bundled minitest
2 | require 'java'
3 | require 'minitest/autorun'
4 | require 'minitest/pride'
5 |
6 | require_relative '../lib/ruby-processing/helper_methods'
7 |
8 | include Processing::HelperMethods
9 |
10 | EPSILON ||= 1.0e-04
11 |
12 | Java::Monkstone::MathToolLibrary.new.load(JRuby.runtime, false)
13 |
14 | include Processing::HelperMethods
15 | include Processing::MathTool
16 |
17 | EPSILON ||= 1.0e-04
18 |
19 | Dir.chdir(File.dirname(__FILE__))
20 |
21 | class HelperMethodsTest < Minitest::Test
22 | def test_hex_color
23 | col_double = 0.5
24 | hexcolor = 0xFFCC6600
25 | dodgy_hexstring = '*56666'
26 | hexstring = '#CC6600'
27 | assert hex_color(col_double) == 0.5, 'double as a color'
28 | assert hex_color(hexcolor) == -3381760, 'hexadecimal fixnum color'
29 | assert hex_color(hexstring) == -3381760, 'hexadecimal string color'
30 | assert_raises(StandardError, 'Dodgy Hexstring') do
31 | hex_color(dodgy_hexstring)
32 | end
33 | end
34 |
35 | def test_dist
36 | ax, ay, bx, by = 0, 0, 1.0, 1.0
37 | assert dist(ax, ay, bx, by) == Math.sqrt(2), '2D distance'
38 | by = 0.0
39 | assert dist(ax, ay, bx, by) == 1.0, 'when y dimension is zero'
40 | ax, ay, bx, by = 0, 0, 0.0, 0.0
41 | assert dist(ax, ay, bx, by) == 0.0, 'when x and y dimension are zero'
42 | ax, ay, bx, by = 1, 1, -2.0, -3.0
43 | assert dist(ax, ay, bx, by) == 5, 'classic triangle dimensions'
44 | ax, ay, bx, by, cx, cy = -1, -1, 100, 2.0, 3.0, 100
45 | assert dist(ax, ay, bx, by, cx, cy) == 5, 'classic triangle dimensions'
46 | ax, ay, bx, by, cx, cy = 0, 0, -1.0, -1.0, 0, 0
47 | assert dist(ax, ay, bx, by, cx, cy) == Math.sqrt(2)
48 | ax, ay, bx, by, cx, cy = 0, 0, 0.0, 0.0, 0, 0
49 | assert dist(ax, ay, bx, by, cx, cy) == 0.0
50 | ax, ay, bx, by, cx, cy = 0, 0, 1.0, 0.0, 0, 0
51 | assert dist(ax, ay, bx, by, cx, cy) == 1.0, 'when x and z dimension are zero'
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/ruby-processing.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'ruby-processing/version'
5 | require 'rake'
6 |
7 | Gem::Specification.new do |spec|
8 | spec.name = "ruby-processing"
9 | spec.version = RubyProcessing::VERSION
10 | spec.authors = %w(Jeremy\ Ashkenas Peter\ Gassner\ Martin\ Stannard\ Andrew\ Nanton
11 | Marc\ Chung Peter\ Krenn Florian\ Jenett Andreas\ Haller
12 | Juris\ Galang Guillaume\ Pierronnet Martin\ Prout)
13 | spec.email = "jeremy@ashkenas.com"
14 | spec.description = <<-EOS
15 | Ruby-Processing is a ruby wrapper for the processing-2.0 art framework.
16 | This version supports processing-2.2.1, and uses jruby-complete-1.7.26 or an
17 | installed jruby as the glue between ruby and java. Use both processing
18 | libraries and ruby gems in your sketches. The "watch" mode, provides a
19 | nice REPL-ish way to work on your processing sketches. Features a polyglot
20 | maven build, opening the way to use/test latest jruby.
21 | EOS
22 | spec.summary = %q{Code as Art, Art as Code. Processing and Ruby are meant for each other.}
23 | spec.homepage = "http://wiki.github.com/jashkenas/ruby-processing"
24 | spec.post_install_message = %q{Use 'rp5 setup install' to install jruby-complete, and 'rp5 setup check' to check config.}
25 | spec.license = 'MIT'
26 |
27 | spec.files = FileList['bin/**/*', 'lib/**/*', 'library/**/*', 'samples/**/*', 'vendors/Rakefile'].exclude(/jar/).to_a
28 | spec.files << 'lib/rpextras.jar'
29 |
30 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
31 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
32 | spec.require_paths = ["lib"]
33 | spec.required_ruby_version = '>= 1.9.3'
34 |
35 | spec.add_development_dependency "bundler", "~> 1.10"
36 | spec.add_development_dependency "rake", "~> 10.4"
37 | spec.add_development_dependency "minitest", "~> 5.8"
38 | spec.requirements << 'A decent graphics card'
39 | spec.requirements << 'java runtime >= 1.7+'
40 | spec.requirements << 'processing = 2.2.1'
41 | end
42 |
43 |
--------------------------------------------------------------------------------
/lib/ruby-processing/runners/watch.rb:
--------------------------------------------------------------------------------
1 | require_relative 'base'
2 |
3 | module Processing
4 | # A sketch loader, observer, and reloader, to tighten
5 | # the feedback between code and effect.
6 | class Watcher
7 | # Sic a new Processing::Watcher on the sketch
8 | WATCH_MESSAGE ||= <<-EOS
9 | Warning:
10 | To protect you from running watch mode in a top level
11 | directory with lots of nested ruby or GLSL files we
12 | limit the number of files to watch to %d.
13 | If you really want to watch %d files you should
14 | increase MAX_WATCH in ~/.rp5rc
15 |
16 | EOS
17 | SLEEP_TIME = 0.2
18 | def initialize
19 | reload_files_to_watch
20 | @time = Time.now
21 | start_watching
22 | end
23 |
24 | # Kicks off a thread to watch the sketch, reloading Ruby-Processing
25 | # and restarting the sketch whenever it changes.
26 | def start_watching
27 | start_runner
28 | Kernel.loop do
29 | if @files.find { |file| FileTest.exist?(file) && File.stat(file).mtime > @time }
30 | puts 'reloading sketch...'
31 | $app && $app.close
32 | @time = Time.now
33 | java.lang.System.gc
34 | start_runner
35 | reload_files_to_watch
36 | end
37 | sleep SLEEP_TIME
38 | end
39 | end
40 |
41 | # Convenience function to report errors when loading and running a sketch,
42 | # instead of having them eaten by the thread they are loaded in.
43 | def report_errors
44 | yield
45 | rescue Exception => e
46 | wformat = 'Exception occured while running sketch %s...'
47 | tformat = "Backtrace:\n\t%s"
48 | warn format(wformat, File.basename(SKETCH_PATH))
49 | puts format(tformat, e.backtrace.join("\n\t"))
50 | end
51 |
52 | def start_runner
53 | @runner.kill if @runner && @runner.alive?
54 | @runner = Thread.start do
55 | report_errors do
56 | Processing.load_and_run_sketch
57 | end
58 | end
59 | end
60 |
61 | def reload_files_to_watch
62 | @files = Dir.glob(File.join(SKETCH_ROOT, '**/*.{rb,glsl}'))
63 | count = @files.length
64 | max_watch = RP_CONFIG.fetch('MAX_WATCH', 20)
65 | return unless count > max_watch
66 | warn format(WATCH_MESSAGE, max_watch, count)
67 | abort
68 | end
69 | end
70 | end
71 |
72 | Processing::Watcher.new
73 |
--------------------------------------------------------------------------------
/library/file_chooser/file_chooser.rb:
--------------------------------------------------------------------------------
1 | # Here's a little library for using swing JFileChooser.
2 | # in ruby-processing, borrows heavily from control_panel
3 |
4 | module FileChooser
5 | ##
6 | # FileFilter is abstract, requires accept and getDescription
7 | ##
8 |
9 | require 'pathname'
10 | JXChooser = Java::javax::swing::JFileChooser
11 | JFile = Java::java::io::File
12 | System = Java::JavaLang::System
13 |
14 | class Filter < Java::javax::swing::filechooser::FileFilter
15 | attr_reader :description, :extensions
16 | def define(description, extensions)
17 | @description, @extensions = description, extensions
18 | end
19 |
20 | def accept(fobj)
21 | return true if extensions.include? File.extname(fobj.to_s).downcase
22 | return true if fobj.isDirectory
23 | end
24 |
25 | def getDescription
26 | description
27 | end
28 | end
29 |
30 | class RXChooser
31 | java_import javax.swing.UIManager
32 | require 'rbconfig'
33 | HOST = 'host_os'
34 | UHOME = 'user.home'
35 | UDIR = 'user.dir'
36 | OS = :unix
37 | case RbConfig::CONFIG[HOST]
38 | when /darwin/ then OS = :mac
39 | when /mswin|mingw/ then OS = :windows
40 | end
41 |
42 | def initialize
43 | javax.swing.UIManager.setLookAndFeel(
44 | javax.swing.UIManager.getSystemLookAndFeelClassName)
45 | @chooser = JXChooser.new
46 | end
47 |
48 | def set_filter(description, extensions)
49 | filter = FileChooser::Filter.new
50 | filter.define(description, extensions)
51 | @chooser.setFileFilter(filter)
52 | end
53 |
54 | def display
55 | if :windows == OS
56 | @chooser.setCurrentDirectory(JFile.new(System.getProperty(UDIR)))
57 | else
58 | @chooser.setCurrentDirectory(JFile.new(System.getProperty(UHOME)))
59 | end
60 | success = @chooser.show_open_dialog($app)
61 | if success == JXChooser::APPROVE_OPTION
62 | return Pathname.new(@chooser.get_selected_file.get_absolute_path).to_s
63 | else
64 | nil
65 | end
66 | end
67 |
68 | def dispose
69 | @chooser = nil
70 | end
71 | end
72 |
73 | module InstanceMethods
74 | def file_chooser
75 | @chooser = RXChooser.new
76 | return @chooser unless block_given?
77 | yield(@chooser)
78 | end
79 | end
80 | end
81 |
82 | Processing::App.send :include, FileChooser::InstanceMethods
83 |
--------------------------------------------------------------------------------
/src/monkstone/arcball/Rarcball.java:
--------------------------------------------------------------------------------
1 | package monkstone.arcball;
2 |
3 | import org.jruby.Ruby;
4 | import org.jruby.RubyClass;
5 | import org.jruby.RubyModule;
6 | import org.jruby.RubyObject;
7 | import org.jruby.anno.JRubyClass;
8 | import org.jruby.anno.JRubyMethod;
9 | import org.jruby.runtime.Arity;
10 | import org.jruby.runtime.ThreadContext;
11 | import org.jruby.runtime.builtin.IRubyObject;
12 | import processing.core.PApplet;
13 |
14 | /**
15 | *
16 | * @author Martin Prout
17 | */
18 | @JRubyClass(name = "ArcBall")
19 | public class Rarcball extends RubyObject {
20 |
21 | private static final long serialVersionUID = -8164248008668234947L;
22 |
23 | /**
24 | *
25 | * @param runtime
26 | */
27 | public static void createArcBall(final Ruby runtime) {
28 | RubyModule processing = runtime.defineModule("Processing");
29 | RubyModule arcBallModule = processing.defineModuleUnder("ArcBall");
30 | arcBallModule.defineAnnotatedMethods(Rarcball.class);
31 | }
32 |
33 | /**
34 | *
35 | * @param runtime
36 | * @param metaClass
37 | */
38 | public Rarcball(Ruby runtime, RubyClass metaClass) {
39 | super(runtime, metaClass);
40 | }
41 |
42 | /**
43 | *
44 | * @param context
45 | * @param self
46 | * @param args optional (no args jx = 0, jy = 0)
47 | */
48 | @JRubyMethod(name = "init", meta = true, rest = true, required = 1, optional = 3)
49 |
50 | public static void init(ThreadContext context, IRubyObject self, IRubyObject args[]) {
51 | int count = Arity.checkArgumentCount(context.getRuntime(), args, 1, 4);
52 | if (count == 4) {
53 | PApplet parent = (PApplet) args[0].toJava(PApplet.class);
54 | double cx = (double) args[1].toJava(Double.class);
55 | double cy = (double) args[2].toJava(Double.class);
56 | double radius = (double) args[3].toJava(Double.class);
57 | new Arcball(parent, cx, cy, radius).setActive(true);
58 | }
59 | if (count == 3) {
60 | PApplet parent = (PApplet) args[0].toJava(PApplet.class);
61 | double cx = (double) args[1].toJava(Double.class);
62 | double cy = (double) args[2].toJava(Double.class);
63 | new Arcball(parent, cx, cy, parent.width * 0.8f).setActive(true);
64 | }
65 | if (count == 1) {
66 | PApplet parent = (PApplet) args[0].toJava(PApplet.class);
67 | new Arcball(parent).setActive(true);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/test/rp5_run_test.rb:
--------------------------------------------------------------------------------
1 | gem 'minitest' # don't use bundled minitest
2 | require 'minitest/autorun'
3 | require 'minitest/pride'
4 |
5 | Dir.chdir(File.dirname(__FILE__))
6 |
7 | class Rp5Test < Minitest::Test
8 |
9 | def test_normal
10 | out, _err_ = capture_io do
11 | open('|../bin/rp5 run sketches/basic.rb', 'r') do |io|
12 | while l = io.gets
13 | puts(l.chop)
14 | end
15 | end
16 | end
17 | assert_match(/ok/, out, 'Failed Basic Sketch')
18 | end
19 |
20 | def test_p2d
21 | out, _err_ = capture_io do
22 | open('|../bin/rp5 run sketches/p2d.rb', 'r') do |io|
23 | while l = io.gets
24 | puts(l.chop)
25 | end
26 | end
27 | end
28 | assert_match(/ok/, out, 'Failed P2D sketch')
29 | end
30 |
31 | def test_proc_root
32 | require 'psych'
33 | path = File.expand_path('~/.rp5rc')
34 | config = FileTest.exist?(path)? Psych.load_file(path) : {}
35 | root = config.empty? ? '' : config['PROCESSING_ROOT']
36 | assert root =~ /processing/, 'You need to set your PROCESSING_ROOT in .rp5rc'
37 | end
38 |
39 |
40 | def test_p3d
41 | out, _err_ = capture_io do
42 | open('|../bin/rp5 run sketches/p3d.rb', 'r') do |io|
43 | while l = io.gets
44 | puts(l.chop)
45 | end
46 | end
47 | end
48 | assert_match(/ok/, out, 'Failed P3D sketch')
49 | end
50 |
51 | def test_graphics
52 | out, _err_ = capture_io do
53 | open('|../bin/rp5 run sketches/graphics.rb', 'r') do |io|
54 | while l = io.gets
55 | puts(l.chop)
56 | end
57 | end
58 | end
59 | assert out[0].to_i >= 3, "Graphics capability #{out} may be sub-optimal"
60 | end
61 |
62 | def test_setup_exception
63 | out, _err_ = capture_io do
64 | open('|../bin/rp5 run sketches/setup_ex.rb', 'r') do |io|
65 | while l = io.gets
66 | puts(l.chop)
67 | end
68 | end
69 | end
70 | assert out.index("undefined method `unknown_method'"), 'Failed to raise exception?'
71 | end
72 |
73 | def test_vector
74 | out, _err_ = capture_io do
75 | open('|../bin/rp5 run sketches/vector.rb', 'r') do |io|
76 | while l = io.gets
77 | puts(l.chop)
78 | end
79 | end
80 | end
81 | assert out.index(/ok/), 'Failed vector test'
82 | end
83 |
84 | def test_arcball
85 | out, _err_ = capture_io do
86 | open('|../bin/rp5 run sketches/arcball.rb', 'r') do |io|
87 | while l = io.gets
88 | puts(l.chop)
89 | end
90 | end
91 | end
92 | assert_match(/ok/, out, 'Failed arcball sketch')
93 | end
94 | end
95 |
96 |
97 |
--------------------------------------------------------------------------------
/test/test_map1d.rb:
--------------------------------------------------------------------------------
1 | gem 'minitest' # don't use bundled minitest
2 | require 'java'
3 | require 'minitest/autorun'
4 | require 'minitest/pride'
5 |
6 | require_relative '../lib/rpextras'
7 | require_relative '../lib/ruby-processing/helper_methods'
8 |
9 | Java::Monkstone::MathToolLibrary.new.load(JRuby.runtime, false)
10 |
11 | include Processing::HelperMethods
12 | include Processing::MathTool
13 |
14 | EPSILON ||= 1.0e-04
15 |
16 | Dir.chdir(File.dirname(__FILE__))
17 |
18 | class Rp5Test < Minitest::Test
19 | def test_map1d
20 | x = [0, 5, 7.5, 10]
21 | range1 = (0..10)
22 | range2 = (100..1)
23 | range3 = (0..10)
24 | range4 = (5..105)
25 | assert map1d(x[0], range1, range2) == 100, 'map to first'
26 | assert map1d(x[1], range1, range2) == 50.5, 'map to reversed intermediate'
27 | assert map1d(x[2], range3, range4) == 80.0, 'map to intermediate'
28 | assert map1d(x[3], range1, range2) == 1, 'map to last'
29 | end
30 |
31 | def test_p5map # as map1d except not using range input
32 | x = [0, 5, 7.5, 10]
33 | range1 = (0..10)
34 | range2 = (100..1)
35 | range3 = (0..10)
36 | range4 = (5..105)
37 | assert p5map(x[0], range1.first, range1.last, range2.first, range2.last) == 100
38 | assert p5map(x[1], range1.first, range1.last, range2.first, range2.last) == 50.5
39 | assert p5map(x[2], range3.first, range3.last, range4.first, range4.last) == 80.0
40 | assert p5map(x[3], range1.first, range1.last, range2.first, range2.last) == 1
41 | end
42 |
43 | def test_norm
44 | x = [10, 140, 210]
45 | start0, last0 = 30, 200
46 | start1, last1 = 0, 200
47 | assert norm(x[0], start0, last0) == -0.11764705882352941, 'unclamped map'
48 | assert norm(x[1], start1, last1) == 0.7, 'map to intermediate'
49 | assert norm(x[2], start1, last1) == 1.05, 'unclamped map'
50 | end
51 |
52 | def test_norm_strict
53 | x = [10, 140, 210]
54 | start0, last0 = 30, 200
55 | assert norm_strict(x[0], start0, last0) == 0, 'clamped map to 0..1.0'
56 | end
57 |
58 | def test_lerp # behaviour is deliberately different to processing which is unclamped
59 | x = [0.5, 0.8, 2.0]
60 | start0, last0 = 300, 200
61 | start1, last1 = 0, 200
62 | assert lerp(start0, last0, x[0]) == 250, 'produces a intermediate value of a reversed range'
63 | assert lerp(start1, last1, x[1]) == 160, 'lerps tp an intermediate value'
64 | assert lerp(start1, last1, x[2]) == 200, 'lerps to the last value of a range'
65 | end
66 |
67 | def test_constrain
68 | x = [15, 2_500, -2_500]
69 | start1, last1 = 0, 200
70 | assert constrain(x[0], start1, last1) == 15
71 | assert constrain(x[1], start1, last1) == 200
72 | assert constrain(x[2], start1, last1) == 0
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/vendors/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake/clean'
2 |
3 | WARNING = <<-EOS
4 | WARNING: you may not have wget installed, you could just download
5 | the correct version of jruby-complete to the vendors folder, and
6 | re-run rp5 setup install instead of installing wget. Some systems
7 | may also require 'sudo' access to install, NB: this is untested....
8 |
9 | EOS
10 |
11 | JRUBYC_VERSION = '1.7.26'
12 | EXAMPLES = '1.8'
13 | HOME_DIR = ENV['HOME']
14 | MAC_OR_LINUX = /linux|mac|darwin/ =~ RbConfig::CONFIG['host_os']
15 |
16 | CLOBBER.include("jruby-complete-#{JRUBYC_VERSION}.jar")
17 |
18 | desc "download, and copy to ruby-processing"
19 | task :default => [:download, :copy_ruby]
20 |
21 | desc "download JRuby upstream sources"
22 | task :download => ["jruby-complete-#{JRUBYC_VERSION}.jar"]
23 |
24 | file "jruby-complete-#{JRUBYC_VERSION}.jar" do
25 | begin
26 | sh "wget https://s3.amazonaws.com/jruby.org/downloads/#{JRUBYC_VERSION}/jruby-complete-#{JRUBYC_VERSION}.jar"
27 | rescue
28 | warn(WARNING)
29 | end
30 | check_sha1("jruby-complete-#{JRUBYC_VERSION}.jar", 'c09885af02af34266ed929f94cedcf87cc965f46')
31 | end
32 |
33 | directory "../lib/ruby"
34 |
35 | desc "copy jruby-complete"
36 | task :copy_ruby => ["../lib/ruby"] do
37 | sh "cp -v jruby-complete-#{JRUBYC_VERSION}.jar ../lib/ruby/jruby-complete.jar"
38 | end
39 |
40 | def check_sha1(filename, expected_hash)
41 | require "digest/sha1"
42 | sha1 = Digest::SHA1.new
43 | File.open(filename, "r") do |f|
44 | while buf = f.read(4096)
45 | sha1.update(buf)
46 | end
47 | end
48 | if sha1.hexdigest != expected_hash
49 | raise "bad sha1 checksum for #{filename} (expected #{expected_hash} got #{sha1.hexdigest})"
50 | end
51 | end
52 |
53 | desc "download, and copy to ruby-processing"
54 | task :unpack_samples => [:download_examples, :copy_examples]
55 |
56 | desc 'download and copy examples to user home'
57 | task :download_examples
58 | file_name = (MAC_OR_LINUX.nil?) ? "#{EXAMPLES}.zip" : "#{EXAMPLES}.tar.gz"
59 | file file_name do
60 | begin
61 | if MAC_OR_LINUX.nil?
62 | sh "wget https://github.com/ruby-processing/Example-Sketches/archive/#{EXAMPLES}.zip"
63 | else
64 | sh "wget https://github.com/ruby-processing/Example-Sketches/archive/#{EXAMPLES}.tar.gz"
65 | end
66 | rescue
67 | warn(WARNING)
68 | end
69 | end
70 |
71 | desc "copy examples"
72 | task :copy_examples => file_name do
73 | if MAC_OR_LINUX.nil?
74 | sh "unzip #{EXAMPLES}.zip"
75 | else
76 | sh "tar xzvf #{EXAMPLES}.tar.gz"
77 | end
78 | sh "rm -r #{HOME_DIR}/rp_samples" if File.exist? "#{HOME_DIR}/rp_samples"
79 | sh "cp -r Example-Sketches-#{EXAMPLES} #{HOME_DIR}/rp_samples"
80 | sh "rm -r Example-Sketches-#{EXAMPLES}"
81 | end
82 |
--------------------------------------------------------------------------------
/test/math_tool_test.rb:
--------------------------------------------------------------------------------
1 | gem 'minitest' # don't use bundled minitest
2 | require 'java'
3 | require 'minitest/autorun'
4 | require 'minitest/pride'
5 |
6 | require_relative '../lib/rpextras'
7 | require_relative '../lib/ruby-processing/helper_methods'
8 |
9 | Java::Monkstone::MathToolLibrary.new.load(JRuby.runtime, false)
10 |
11 | include Processing::HelperMethods
12 | include Processing::MathTool
13 |
14 | EPSILON ||= 1.0e-04
15 |
16 | Dir.chdir(File.dirname(__FILE__))
17 |
18 | class MathToolTest < Minitest::Test
19 | def test_map1d
20 | x = [0, 5, 7.5, 10]
21 | range1 = (0..10)
22 | range2 = (100..1)
23 | range3 = (0..10)
24 | range4 = (5..105)
25 | assert map1d(x[0], range1, range2) == 100, 'map to first'
26 | assert map1d(x[1], range1, range2) == 50.5, 'map to reversed intermediate'
27 | assert map1d(x[2], range3, range4) == 80.0, 'map to intermediate'
28 | assert map1d(x[3], range1, range2) == 1, 'map to last'
29 | end
30 |
31 | def test_p5map # as map1d except not using range input
32 | x = [0, 5, 7.5, 10]
33 | range1 = (0..10)
34 | range2 = (100..1)
35 | range3 = (0..10)
36 | range4 = (5..105)
37 | assert p5map(x[0], range1.first, range1.last, range2.first, range2.last) == 100
38 | assert p5map(x[1], range1.first, range1.last, range2.first, range2.last) == 50.5
39 | assert p5map(x[2], range3.first, range3.last, range4.first, range4.last) == 80.0
40 | assert p5map(x[3], range1.first, range1.last, range2.first, range2.last) == 1
41 | end
42 |
43 | def test_norm
44 | x = [10, 140, 210]
45 | start0, last0 = 30, 200
46 | start1, last1 = 0, 200
47 | assert norm(x[0], start0, last0) == -0.11764705882352941, 'unclamped map'
48 | assert norm(x[1], start1, last1) == 0.7, 'map to intermediate'
49 | assert norm(x[2], start1, last1) == 1.05, 'unclamped map'
50 | end
51 |
52 | def test_norm_strict
53 | x = [10, 140, 210]
54 | start0, last0 = 30, 200
55 | assert norm_strict(x[0], start0, last0) == 0, 'clamped map to 0..1.0'
56 | end
57 |
58 | def test_lerp # behaviour is deliberately different to processing which is unclamped
59 | x = [0.5, 0.8, 2.0]
60 | start0, last0 = 300, 200
61 | start1, last1 = 0, 200
62 | assert lerp(start0, last0, x[0]) == 250, 'produces a intermediate value of a reversed range'
63 | assert lerp(start1, last1, x[1]) == 160, 'lerps tp an intermediate value'
64 | assert lerp(start1, last1, x[2]) == 200, 'lerps to the last value of a range'
65 | end
66 |
67 | def test_constrain
68 | x = [15, 2_500, -2_500]
69 | start1, last1 = 0, 200
70 | assert constrain(x[0], start1, last1) == 15
71 | assert constrain(x[1], start1, last1) == 200
72 | assert constrain(x[2], start1, last1) == 0
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/src/monkstone/core/AbstractLibrary.java:
--------------------------------------------------------------------------------
1 | package monkstone.core;
2 |
3 | import static processing.core.PConstants.*;
4 |
5 | /**
6 | * The purpose of this class is to enable
7 | * access to processing pre, draw and post loops in
8 | * ruby-processing as a regular java library class.
9 | * Also included background, fill and stroke methods.
10 | * PConstants should also be available from static import
11 | * @author Martin Prout
12 | */
13 | public abstract class AbstractLibrary {
14 |
15 | private final processing.core.PApplet app;
16 |
17 | /**
18 | * Useful accessors
19 | */
20 | public int width, height;
21 |
22 | /**
23 | *
24 | * @param app PApplet
25 | */
26 | public AbstractLibrary(processing.core.PApplet app) {
27 | this.app = app;
28 | this.width = app.width;
29 | this.height = app.height;
30 | setActive(true);
31 | }
32 |
33 | /**
34 | * Extending classes must implement this gives access to, by reflection,
35 | * processing PApplet pre loop (called before draw)
36 | */
37 | public abstract void pre();
38 |
39 | /**
40 | * Extending classes must implement this gives access to processing PApplet
41 | * draw loop
42 | */
43 | public abstract void draw();
44 |
45 | /**
46 | * Extending classes must implement this gives access to, by reflection,
47 | * processing PApplet post loop (called after draw)
48 | */
49 | public abstract void post();
50 |
51 | private void setActive(boolean active) {
52 | if (active) {
53 | this.app.registerMethod("pre", this);
54 | this.app.registerMethod("draw", this);
55 | this.app.registerMethod("post", this);
56 | this.app.registerMethod("dispose", this);
57 | } else {
58 | this.app.unregisterMethod("pre", this);
59 | this.app.unregisterMethod("draw", this);
60 | this.app.unregisterMethod("post", this);
61 | }
62 | }
63 |
64 | /**
65 | * Simple signature for background hides need to call app
66 | * @param col
67 | */
68 | public void background(int col) {
69 | this.app.background(col);
70 | }
71 |
72 | /**
73 | * Simple signature for fill hides need to call app
74 | * @param col
75 | */
76 | public void fill(int col) {
77 | this.app.fill(col);
78 | }
79 |
80 | /**
81 | * Simple signature for stroke hides need to call app
82 | * @param col
83 | */
84 | public void stroke(int col) {
85 | this.app.stroke(col);
86 | }
87 |
88 | /**
89 | * Access applet if we must
90 | * @return app PApplet
91 | */
92 | public processing.core.PApplet app() {
93 | return this.app;
94 | }
95 |
96 | /**
97 | * required for processing
98 | */
99 | public void dispose() {
100 | setActive(false);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/library/library_proxy/README.md:
--------------------------------------------------------------------------------
1 | ### Using the LibraryProxy in your sketches
2 | In the sketch you should `load_library :library_proxy` and your library class should inherit
3 | from LibraryProxy and implement pre(), draw() and post() methods (can be empty method if not
4 | required). For simplicity initialize your `processing library` in the sketch `setup`.
5 |
6 | ### Example library
7 |
8 | ```ruby
9 | require 'forwardable'
10 |
11 | # A custom Array created using forwardable (that can also access the PApplet pre,
12 | # post and draw loops by extending our new LibraryProxy class. Also has access
13 | # to custom background(int), fill(int) and stroke(int) methods.
14 | class CustomArray < LibraryProxy
15 | extend Forwardable
16 | def_delegators(:@objs, :each, :<<)
17 | include Enumerable
18 |
19 | attr_reader :app
20 |
21 | # We must initialize class with the PApplet instance
22 | def initialize(app)
23 | @app = app
24 | @objs = []
25 | end
26 |
27 | def add_object(mx, my, x, y, speed)
28 | self << Particle.new(x.to_i, y.to_i, mx, my, Sketch::UNIT, speed, 1, 1)
29 | end
30 |
31 | # Access the processing post loop (gets called after draw)
32 | def post
33 | each do |obj|
34 | update_x obj
35 | next unless obj.y >= Sketch::UNIT || obj.x <= 0
36 | obj.ydir *= -1
37 | obj.y += obj.ydir
38 | end
39 | end
40 |
41 | def update_x(obj)
42 | obj.x += obj.speed * obj.xdir
43 | return if (0..Sketch::UNIT).cover? obj.x
44 | obj.xdir *= -1
45 | obj.x += obj.xdir
46 | obj.y += obj.ydir
47 | end
48 |
49 | # We need this to fulfill the contract of implementing abstract methods of
50 | # LibraryProxy which is an alias for Java::ProcessingCore::AbstractLibrary
51 | def pre
52 | end
53 |
54 | # Access the processing draw loop here, using our custom background and fill
55 | # note: use of 'app' to access ellipse functionality as would otherwise be
56 | # required for background and fill
57 | def draw
58 | background(0)
59 | fill(255)
60 | each do |obj|
61 | app.ellipse(obj.mx + obj.x, obj.my + obj.y, 6, 6)
62 | end
63 | end
64 | end
65 |
66 | # The Particle object
67 |
68 | Particle = Struct.new(:x, :y, :mx, :my, :size, :speed, :xdir, :ydir)
69 | ```
70 | ### Example sketch
71 |
72 | ```ruby
73 | # A minimalist sketch that demonstrates a possible approach to creating a custom
74 | # array of objects using forwardable. Also demonstrates how to use LibraryProxy.
75 |
76 | load_library :library_proxy # loads the JRubyArt LibraryProxy abstract class
77 | require_relative 'custom_array' # loads our custom 'library' class
78 |
79 | UNIT = 40
80 |
81 | def setup
82 | size 640, 360
83 | wide_count = width / UNIT
84 | height_count = height / UNIT
85 | custom_array = CustomArray.new(self)
86 | height_count.times do |i|
87 | wide_count.times do |j|
88 | custom_array.add_object(j * UNIT, i * UNIT, UNIT / 2, UNIT / 2, rand(0.05..0.8))
89 | end
90 | end
91 | no_stroke
92 | end
93 |
94 | # does nothing here see custom_array.rb
95 | def draw
96 | end
97 | ```
--------------------------------------------------------------------------------
/src/monkstone/fastmath/Deglut.java:
--------------------------------------------------------------------------------
1 | package monkstone.fastmath;
2 |
3 | import org.jruby.Ruby;
4 | import org.jruby.RubyClass;
5 | import org.jruby.RubyModule;
6 | import org.jruby.RubyObject;
7 | import org.jruby.anno.JRubyClass;
8 | import org.jruby.anno.JRubyMethod;
9 | import org.jruby.runtime.ThreadContext;
10 | import org.jruby.runtime.builtin.IRubyObject;
11 |
12 | /**
13 | *
14 | * @author Martin Prout
15 | */
16 | @JRubyClass(name = "DegLut")
17 | public class Deglut extends RubyObject {
18 |
19 | /**
20 | * Lookup table for degree cosine/sine, has a fixed precision 1.0
21 | * degrees Quite accurate but imprecise
22 | *
23 | * @author Martin Prout
24 | */
25 | static final double[] SIN_DEG_LUT = new double[91];
26 | /**
27 | *
28 | */
29 | public static final double TO_RADIANS = Math.PI / 180;
30 | /**
31 | *
32 | */
33 | private static boolean initialized = false;
34 |
35 | private final static int NINETY = 90;
36 | private final static int FULL = 360;
37 | private static final long serialVersionUID = -1466528933765940101L;
38 |
39 | /**
40 | * Initialise sin table with values (first quadrant only)
41 | */
42 | public static final void initTable() {
43 | if (initialized == false) {
44 | for (int i = 0; i <= NINETY; i++) {
45 | SIN_DEG_LUT[i] = Math.sin(TO_RADIANS * i);
46 | }
47 | initialized = true;
48 | }
49 | }
50 |
51 |
52 | /**
53 | *
54 | * @param runtime
55 | */
56 |
57 | public static void createDeglut(final Ruby runtime){
58 | RubyModule deglutModule = runtime.defineModule("DegLut");
59 | deglutModule.defineAnnotatedMethods(Deglut.class);
60 | Deglut.initTable();
61 | }
62 |
63 |
64 | /**
65 | *
66 | * @param runtime
67 | * @param klass
68 | */
69 | private Deglut(Ruby runtime, RubyClass klass) {
70 | super(runtime, klass);
71 | }
72 |
73 | /**
74 | *
75 | * @param context
76 | * @param klazz
77 | * @param other
78 | * @return sin float
79 | */
80 | @JRubyMethod(name = "sin", meta = true)
81 |
82 | public static IRubyObject sin(ThreadContext context, IRubyObject klazz, IRubyObject other) {
83 | int thet = (Integer) other.toJava(Integer.class);
84 | while (thet < 0) {
85 | thet += FULL; // Needed because negative modulus plays badly in java
86 | }
87 | int theta = thet % FULL;
88 | int y = theta % NINETY;
89 | double result = (theta < NINETY) ? SIN_DEG_LUT[y] : (theta < 180)
90 | ? SIN_DEG_LUT[NINETY - y] : (theta < 270)
91 | ? -SIN_DEG_LUT[y] : -SIN_DEG_LUT[NINETY - y];
92 | return context.getRuntime().newFloat(result);
93 | }
94 |
95 | /**
96 | *
97 | * @param context
98 | * @param klazz
99 | * @param other
100 | * @return cos float
101 | */
102 | @JRubyMethod(name = "cos", meta = true)
103 | public static IRubyObject cos(ThreadContext context, IRubyObject klazz, IRubyObject other) {
104 | int thet = (Integer) other.toJava(Integer.class);
105 | while (thet < 0) {
106 | thet += FULL; // Needed because negative modulus plays badly in java
107 | }
108 | int theta = thet % FULL;
109 | int y = theta % NINETY;
110 | double result = (theta < NINETY) ? SIN_DEG_LUT[NINETY - y] : (theta < 180)
111 | ? -SIN_DEG_LUT[y] : (theta < 270)
112 | ? -SIN_DEG_LUT[NINETY - y] : SIN_DEG_LUT[y];
113 | return context.getRuntime().newFloat(result);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/lib/ruby-processing/exporters/application_exporter.rb:
--------------------------------------------------------------------------------
1 | require 'rbconfig'
2 | require_relative 'base_exporter'
3 |
4 | module Processing
5 |
6 | # A utility class to export Ruby-Processing sketches as
7 | # Mac/Win/Nix Applications.
8 | class ApplicationExporter < BaseExporter
9 |
10 | USAGE = <<-EOS
11 |
12 | The application exporter will generate a Mac application for you.
13 | Usage: script/application
14 | Example: script/applet samples/jwishy.rb
15 | Probably won't work with Oracle Java on Mac
16 |
17 | EOS
18 |
19 | def export!(sketch)
20 | # Check to make sure that the main file exists
21 | @main_file_path, @main_file, @main_folder = *get_main_file(sketch)
22 | usage(@main_file_path && FileTest.exist?(@main_file_path))
23 |
24 | extract_information
25 |
26 | compute_destination_name
27 |
28 | wipe_and_recreate_destination
29 |
30 | copy_over_necessary_files
31 |
32 | calculate_substitutions
33 |
34 | create_executables
35 |
36 | symlink_library_into_place
37 | end
38 |
39 | def compute_destination_name
40 | @dest = "#{@title}.app"
41 | end
42 |
43 | def copy_over_necessary_files
44 | @prefix = 'lib'
45 | cp_r(Dir["#{RP5_ROOT}/lib/templates/application/{*,**}"], @dest)
46 | @necessary_files = [@main_file_path]
47 | @necessary_files += Dir["#{RP_CONFIG['PROCESSING_ROOT']}/core/library/{*,**}"]
48 | @necessary_files += Dir["#{RP5_ROOT}/lib/{*,**}"]
49 | @necessary_files += @real_requires
50 | NECESSARY_FOLDERS.each do |folder|
51 | resource_path = File.join(@main_folder, folder)
52 | @necessary_files << resource_path if FileTest.exist?(resource_path)
53 | end
54 | @necessary_files.uniq!
55 | cp_r(@necessary_files, File.join(@dest, @prefix))
56 | cp_r(@libraries, File.join(@dest, @prefix, 'library')) unless @libraries.empty?
57 | # Then move the icon
58 | potential_icon = Dir.glob(File.join(@dest, @prefix, 'data/*.icns'))[0]
59 | move(potential_icon, File.join(@dest, 'Contents/Resources/sketch.icns'), force: true) if potential_icon
60 | end
61 |
62 | def calculate_substitutions
63 | file_list = ['lib/ruby/jruby-complete.jar']
64 | @class_path = file_list.map { |f| '$JAVAROOT/' + f.sub(@prefix + '/', '') }.join(':')
65 | @linux_class_path = '.:../lib/ruby/*:../lib/*:../lib/library/*'
66 | @windows_class_path = '.;../lib/ruby/*;../lib/*;../lib/library/*'
67 | end
68 |
69 | def create_executables
70 | render_erb_in_path_with_binding(@dest, binding, delete: true)
71 | rm Dir.glob(@dest + "/**/*.java")
72 | runnable = @dest + '/' + File.basename(@main_file, '.rb')
73 | move @dest + '/run', runnable
74 | move @dest + '/run.exe', "#{runnable}.exe"
75 | chmod 0755, runnable
76 | chmod 0755, "#{runnable}.exe"
77 | chmod 0755, File.join(@dest, 'Contents', 'MacOS', 'JavaApplicationStub')
78 | end
79 |
80 | def symlink_library_into_place
81 | cd @dest + '/Contents/Resources'
82 | # Poor ol' windows can't symlink.
83 | # TODO...
84 | win ||= RbConfig::CONFIG['host_os'].match(/mswin/i)
85 | win ||= RbConfig::CONFIG['host_os'].match(/windows/i)
86 | puts "\n[warning] Applications exported from Windows won't run on Macs...\n" if win
87 | ln_s('../../lib', 'Java') unless win
88 | end
89 |
90 | def usage(predicate)
91 | return if predicate
92 | puts USAGE
93 | exit
94 | end
95 | end
96 | end
97 |
--------------------------------------------------------------------------------
/src/monkstone/arcball/Quaternion.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Martin Prout
3 | *
4 | * This library is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 2.1 of the License, or (at your option) any later version.
8 | *
9 | * http://creativecommons.org/licenses/LGPL/2.1/
10 | *
11 | * This library is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public
17 | * License along with this library; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 |
21 | package monkstone.arcball;
22 |
23 | /**
24 | * Based on a original sketch by Ariel Malka
25 | * Arcball quaternion idea by Ken Shoemake
26 | * http://dl.acm.org/citation.cfm?id=325242
27 | * A google of the quaternions term will find a
28 | * freely down-loadable article by Ken Shoemake.
29 | * @author Martin Prout
30 | */
31 | public final class Quaternion {
32 |
33 | private double w, x, y, z;
34 |
35 | /**
36 | *
37 | */
38 | public Quaternion() {
39 | reset();
40 | }
41 |
42 | /**
43 | *
44 | * @param w
45 | * @param x
46 | * @param y
47 | * @param z
48 | */
49 | public Quaternion(double w, double x, double y, double z) {
50 | this.w = w;
51 | this.x = x;
52 | this.y = y;
53 | this.z = z;
54 | }
55 |
56 | /**
57 | *
58 | */
59 | public final void reset() {
60 | w = 1.0f;
61 | x = 0.0f;
62 | y = 0.0f;
63 | z = 0.0f;
64 | }
65 |
66 | /**
67 | *
68 | * @param w scalar
69 | * @param v custom Vector class
70 | */
71 | public void set(double w, Jvector v) {
72 | this.w = w;
73 | x = v.x;
74 | y = v.y;
75 | z = v.z;
76 | }
77 |
78 | /**
79 | *
80 | * @param q
81 | */
82 | public void set(Quaternion q) {
83 | w = q.w;
84 | x = q.x;
85 | y = q.y;
86 | z = q.z;
87 | }
88 |
89 | /**
90 | *
91 | * @param q1
92 | * @param q2
93 | * @return new Quaternion
94 | */
95 | public static Quaternion mult(Quaternion q1, Quaternion q2) {
96 | double w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
97 | double x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
98 | double y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
99 | double z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
100 | return new Quaternion(w, x, y, z);
101 | }
102 |
103 | /**
104 | * Transform this Quaternion into an angle (radians) and an axis vector, about
105 | * which to rotate (avoids NaN by setting sa to 1.0F when sa < epsilon)
106 | * @return a new double[] where a0 = angle and a1 .. a3 are axis vector
107 | */
108 |
109 | public double[] getValue() {
110 | double sa = Math.sqrt(1.0 - w * w);
111 | if (sa < processing.core.PConstants.EPSILON) {
112 | sa = 1.0f;
113 | }
114 | return new double[]{ Math.acos(w) * 2.0f, x / sa, y / sa, z / sa};
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
11 | 4.0.0
12 | ruby-processing
13 | rp5extras
14 | 2.7.1
15 | rp5extras
16 | rp5extras for ruby-processing
17 | https://github.com/jashkenas/ruby-processing
18 |
19 | ruby-processing
20 | https://ruby-processing.github.io
21 |
22 |
23 |
24 | monkstone
25 | Martin Prout
26 | mamba2928@yahoo.co.uk
27 |
28 | developer
29 |
30 |
31 |
32 |
33 | scm:git:git://github.com/jashkenas/ruby-processing.git
34 | scm:git:git@github.com/jashkenas/ruby-processing.git
35 | https://github.com/jashkenas/ruby-processing
36 |
37 |
38 | Github
39 | https://github.com/jashkenas/ruby-processing/issues
40 |
41 |
42 | http://processing.github.io/processing-javadocs/core/
43 | pom.xml
44 | UTF-8
45 | 1.7
46 | http://jruby.org/apidocs/
47 | 1.7
48 |
49 |
50 |
51 | org.jruby
52 | jruby
53 | 1.7.25
54 | pom
55 |
56 |
57 | org.processing
58 | core
59 | 2.2.1
60 |
61 |
62 | org.processing
63 | video
64 | 2.2.1
65 |
66 |
67 |
68 | src
69 | package
70 | rpextras
71 |
72 |
73 |
74 | maven-resources-plugin
75 | 2.6
76 |
77 |
78 | maven-dependency-plugin
79 | 2.10
80 |
81 |
82 | maven-compiler-plugin
83 | 3.5.1
84 |
85 | 1.7
86 | 1.7
87 |
88 |
89 |
90 | maven-javadoc-plugin
91 | 2.10.4
92 |
93 | false
94 |
95 | ${processing.api}
96 | ${jruby.api}
97 |
98 |
99 |
100 |
101 | maven-jar-plugin
102 | 3.0.2
103 |
104 |
105 | MANIFEST.MF
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/lib/ruby-processing/exporters/creator.rb:
--------------------------------------------------------------------------------
1 | BASIC = <<-CODE
2 | def setup
3 | size %s, %s
4 | end
5 |
6 | def draw
7 |
8 | end
9 | CODE
10 |
11 | BASIC_MODE = <<-CODE
12 | def setup
13 | size %s, %s, %s
14 | end
15 |
16 | def draw
17 |
18 | end
19 | CODE
20 |
21 | CLASS_BASIC = <<-CODE
22 | class %s < Processing::App
23 | def setup
24 | size %s, %s
25 | end
26 |
27 | def draw
28 |
29 | end
30 | end
31 | CODE
32 |
33 | CLASS_MODE = <<-CODE
34 | class %s < Processing::App
35 | def setup
36 | size %s, %s, %s
37 | end
38 |
39 | def draw
40 |
41 | end
42 | end
43 | CODE
44 |
45 | INNER = <<-CODE
46 | class %s
47 | include Processing::Proxy
48 |
49 | end
50 | CODE
51 |
52 | module Processing
53 | require_relative '../helpers/string_extra'
54 | require_relative '../helpers/camel_string'
55 | # Write file to disk
56 | class SketchWriter
57 | attr_reader :file
58 | def initialize(path)
59 | underscore = StringExtra.new(path).underscore
60 | @file = "#{File.dirname(path)}/#{underscore}.rb"
61 | end
62 |
63 | def save(template)
64 | File.open(file, 'w+') do |f|
65 | f.write(template)
66 | end
67 | end
68 | end
69 |
70 | # An abstract class providing common methods for real creators
71 | class Creator
72 | ALL_DIGITS = /\A\d+\Z/
73 |
74 | def already_exist(path)
75 | underscore = StringExtra.new(path).underscore
76 | new_file = "#{File.dirname(path)}/#{underscore}.rb"
77 | return if !FileTest.exist?(path) && !FileTest.exist?(new_file)
78 | puts 'That file already exists!'
79 | exit
80 | end
81 |
82 | # Show the help/usage message for create.
83 | def usage
84 | puts <<-USAGE
85 |
86 | Usage: rp5 create
87 | mode can be P2D / P3D.
88 | Use --wrap for a sketch wrapped as a class
89 | Use --inner to generated a ruby version of 'java' Inner class
90 | Examples: rp5 create app 800 600
91 | rp5 create app 800 600 p3d --wrap
92 | rp5 create inner_class --inner
93 |
94 | USAGE
95 | end
96 | end
97 |
98 | # This class creates bare sketches, with an optional render mode
99 | class BasicSketch < Creator
100 | # Create a blank sketch, given a path.
101 | def basic_template
102 | format(BASIC, @width, @height)
103 | end
104 |
105 | def basic_template_mode
106 | format(BASIC_MODE, @width, @height, @mode)
107 | end
108 |
109 | def create!(path, args)
110 | return usage if /\?/ =~ path || /--help/ =~ path
111 | # Check to make sure that the main file doesn't exist already
112 | already_exist(path)
113 | main_file = File.basename(path, '.rb') # allow uneeded extension input
114 | writer = SketchWriter.new(main_file)
115 | @width = args[0]
116 | @height = args[1]
117 | @mode = args[2].upcase unless args[2].nil?
118 | template = @mode.nil? ? basic_template : basic_template_mode
119 | writer.save(template)
120 | end
121 | end
122 |
123 | # This class creates class wrapped sketches, with an optional render mode
124 | class ClassSketch < Creator
125 | def class_template
126 | format(CLASS_BASIC, @name, @width, @height)
127 | end
128 |
129 | def class_template_mode
130 | format(CLASS_MODE, @name, @width, @height, @mode)
131 | end
132 | # Create a class wrapped sketch, given a path.
133 | def create!(path, args)
134 | return usage if /\?/ =~ path || /--help/ =~ path
135 | main_file = File.basename(path, '.rb') # allow uneeded extension input
136 | # Check to make sure that the main file doesn't exist already
137 | already_exist(path)
138 | @name = CamelString.new(main_file).camelize
139 | writer = SketchWriter.new(main_file)
140 | @title = StringExtra.new(main_file).titleize
141 | @width, @height = args[0], args[1]
142 | @mode = args[2].upcase unless args[2].nil?
143 | template = @mode.nil? ? class_template : class_template_mode
144 | writer.save(template)
145 | end
146 | end
147 |
148 | # This class creates a pseudo 'java inner class' of the sketch
149 | class Inner < Creator
150 | def inner_class_template
151 | format(INNER, @name)
152 | end
153 | # Create a pseudo inner class, given a path.
154 | def create!(path, _args_)
155 | return usage if /\?/ =~ path || /--help/ =~ path
156 | main_file = File.basename(path, '.rb') # allow uneeded extension input
157 | # Check to make sure that the main file doesn't exist already
158 | already_exist(path)
159 | @name = main_file.camelize
160 | writer = SketchWriter.new(main_file)
161 | template = inner_class_template
162 | writer.save(template)
163 | end
164 | end
165 | end
166 |
--------------------------------------------------------------------------------
/src/monkstone/arcball/Jvector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015-16 Martin Prout
3 | *
4 | * This library is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU Lesser General Public
6 | * License as published by the Free Software Foundation; either
7 | * version 2.1 of the License, or (at your option) any later version.
8 | *
9 | * http://creativecommons.org/licenses/LGPL/2.1/
10 | *
11 | * This library is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public
17 | * License along with this library; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 | package monkstone.arcball;
21 |
22 | /**
23 | *
24 | * @author Martin Prout
25 | */
26 | public final class Jvector {
27 |
28 | static final double EPSILON = 9.999999747378752E-5f;
29 |
30 | /**
31 | *
32 | */
33 | public double x;
34 |
35 | /**
36 | *
37 | */
38 | public double y;
39 |
40 | /**
41 | *
42 | */
43 | public double z;
44 |
45 | /**
46 | *
47 | * @param x
48 | * @param y
49 | * @param z
50 | */
51 | public Jvector(double x, double y, double z) {
52 | this.x = x;
53 | this.y = y;
54 | this.z = z;
55 | }
56 |
57 | /**
58 | *
59 | */
60 | public Jvector() {
61 | this(0.0f, 0.0f, 0.0f);
62 | }
63 |
64 | /**
65 | *
66 | * @param vect
67 | */
68 | public Jvector(Jvector vect) {
69 | this(vect.x, vect.y, vect.z);
70 | }
71 |
72 | /**
73 | *
74 | * @param other
75 | * @return subtracted vector
76 | */
77 | public Jvector sub(Jvector other) {
78 | return new Jvector(this.x - other.x, this.y - other.y, this.z - other.z);
79 | }
80 |
81 | /**
82 | *
83 | * @param scalar
84 | * @return subtracted vector
85 | */
86 | public Jvector mult(double scalar) {
87 | return new Jvector(this.x * scalar, this.y * scalar, this.z * scalar);
88 | }
89 |
90 | /**
91 | *
92 | * @return magnitude float
93 | */
94 | public double mag() {
95 | return Math.sqrt(x * x + y * y + z * z);
96 | }
97 |
98 | /**
99 | * The usual normalize
100 | *
101 | * @return this Jvector
102 | */
103 | public Jvector normalize() {
104 | double mag = Math.sqrt(x * x + y * y + z * z);
105 | this.x /= mag;
106 | this.y /= mag;
107 | this.z /= mag;
108 | return this;
109 | }
110 |
111 | /**
112 | *
113 | * @param other
114 | * @return new dot product
115 | */
116 | public double dot(Jvector other) {
117 | return x * other.x + y * other.y + z * other.z;
118 | }
119 |
120 | /**
121 | *
122 | * @param other
123 | * @return new cross product
124 | */
125 | public Jvector cross(Jvector other) {
126 | double xc = y * other.z - z * other.y;
127 | double yc = z * other.x - x * other.z;
128 | double zc = x * other.y - y * other.x;
129 | return new Jvector(xc, yc, zc);
130 | }
131 |
132 | /**
133 | *
134 | * @param other
135 | * @return boolean
136 | */
137 | public boolean equals(Jvector other) {
138 | if (other instanceof Jvector) {
139 |
140 | if (Math.abs(this.x - other.x) > EPSILON) {
141 | return false;
142 | }
143 | if (Math.abs(this.y - other.y) > EPSILON) {
144 | return false;
145 | }
146 | return (Math.abs(this.z - other.z) > EPSILON);
147 | }
148 | return false;
149 |
150 | }
151 |
152 | /**
153 | *
154 | * @param obj
155 | * @return boolean
156 | */
157 | @Override
158 | public boolean equals(Object obj) {
159 | if (obj == null) {
160 | return false;
161 | }
162 | if (getClass() != obj.getClass()) {
163 | return false;
164 | }
165 | final Jvector other = (Jvector) obj;
166 | if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) {
167 | return false;
168 | }
169 | if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) {
170 | return false;
171 | }
172 | return (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z));
173 | }
174 |
175 | /**
176 | *
177 | * @return has int
178 | */
179 | @Override
180 | public int hashCode() {
181 | int hash = 7;
182 | hash = 97 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
183 | hash = 97 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
184 | hash = 97 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
185 | return hash;
186 | }
187 | }
188 |
189 |
--------------------------------------------------------------------------------
/lib/ruby-processing/exporters/base_exporter.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 | require 'erb'
3 | require_relative '../library_loader'
4 | require_relative '../helpers/string_extra'
5 |
6 | module Processing
7 | # This base exporter implements some of the common
8 | # code-munging needed to generate apps/ blank sketches.
9 | class BaseExporter
10 | include FileUtils
11 | DEFAULT_DIMENSIONS = { 'width' => '100', 'height' => '100' }
12 | DEFAULT_DESCRIPTION = ''
13 | NECESSARY_FOLDERS = %w(data lib vendor)
14 |
15 | # Returns the filepath, basename, and directory name of the sketch.
16 | def get_main_file(file)
17 | @file = file
18 | return file, File.basename(file), File.dirname(file)
19 | end
20 |
21 | # Centralized method to read the source of the sketch and extract
22 | # all the juicy details.
23 | def extract_information
24 | # Extract information from main file
25 | @info = {}
26 | @info[:source_code] = source = read_source_code
27 | @info[:class_name] = extract_class_name(source)
28 | @info[:title] = extract_title(source)
29 | @info[:width] = extract_dimension(source, 'width')
30 | @info[:height] = extract_dimension(source, 'height')
31 | @info[:description] = extract_description(source)
32 | @info[:libraries] = extract_libraries(source)
33 | @info[:real_requires] = extract_real_requires(source)
34 | hash_to_ivars @info
35 | @info
36 | end
37 |
38 | # Searches the source for a class name.
39 | def extract_class_name(source)
40 | match = source.match(/(\w+)\s*<\s*Processing::App/)
41 | match ? match[1] : 'Sketch'
42 | end
43 |
44 | # Searches the source for a title.
45 | def extract_title(source)
46 | generated_title = StringExtra.new(File.basename(@file, '.rb')).titleize
47 | match = source.match(/#{@info[:class_name]}\.new.*?:title\s=>\s["'](.+?)["']/m)
48 | match ? match[1] : generated_title
49 | end
50 |
51 | # Searches the source for the width and height of the sketch.
52 | def extract_dimension(source, dimension)
53 | filter = /#{@info[:class_name]}\.new.*?:#{dimension}\s?=>\s?(\d+)/m
54 | match = source.match(filter)
55 | sz_match = source.match(/^[^#]*size\(?\s*(\d+)\s*,\s*(\d+)\s*\)?/)
56 | return match[1] if match
57 | return (dimension == 'width' ? sz_match[1] : sz_match[2]) if sz_match
58 | warn 'using default dimensions for export, please use constants integer'\
59 | 'values in size() call instead of computed ones'
60 | DEFAULT_DIMENSIONS[dimension]
61 | end
62 |
63 | # Searches the source for a description of the sketch.
64 | def extract_description(source)
65 | match = source.match(/\A((\s*#(.*?)\n)+)[^#]/m)
66 | match ? match[1].gsub(/\s*#\s*/, "\n") : DEFAULT_DESCRIPTION
67 | end
68 |
69 | # Searches the source for any libraries that have been loaded.
70 | def extract_libraries(source)
71 | lines = source.split("\n")
72 | libs = lines.grep(/^[^#]*load_(?:java_|ruby_)?librar(?:y|ies)\s+(.+)/) do
73 | Regexp.last_match(1).split(/\s*,\s*/).map do |raw_library_name|
74 | raw_library_name.tr("\"':\r\n", '')
75 | end
76 | end.flatten
77 | lib_loader = LibraryLoader.new
78 | libs.map { |lib| lib_loader.get_library_paths(lib) }.flatten.compact
79 | end
80 |
81 | # Looks for all of the codes require or load commands, checks
82 | # to see if the file exists (that it's not a gem, or a standard lib)
83 | # and hands you back all the real ones.
84 | def extract_real_requires(source)
85 | code = source.dup
86 | requirements = []
87 | partial_paths = []
88 | Kernel.loop do
89 | matchdata = code.match(
90 | /^.*[^::\.\w](require_relative|require|load)\b.*$/
91 | )
92 | break unless matchdata
93 | line = matchdata[0].gsub('__FILE__', "'#{@main_file_path}'")
94 | req = /\b(require_relative|require|load)\b/
95 | if req =~ line
96 | ln = line.gsub(req, '')
97 | partial_paths << ln
98 | where = "{#{local_dir}/,}{#{partial_paths.join(',')}}"
99 | where += '.{rb,jar}' unless line =~ /\.[^.]+$/
100 | requirements += Dir[where]
101 | end
102 | code = matchdata.post_match
103 | end
104 | requirements
105 | end
106 |
107 | protected
108 |
109 | def read_source_code
110 | File.read(@main_file_path)
111 | end
112 |
113 | def local_dir
114 | File.dirname(@main_file_path)
115 | end
116 |
117 | def hash_to_ivars(hash)
118 | hash.each { |k, v| instance_variable_set("@#{k}", v) }
119 | end
120 |
121 | def wipe_and_recreate_destination
122 | remove_entry_secure @dest if FileTest.exist?(@dest)
123 | mkdir_p @dest
124 | end
125 |
126 | def render_erb_in_path_with_binding(path, some_binding, opts = {})
127 | erbs = Dir.glob(path + "/**/*.erb") # double quotes required
128 | erbs.each do |erb|
129 | string = File.open(erb) { |f| f.read }
130 | rendered = render_erb_from_string_with_binding(string, some_binding)
131 | File.open(erb.sub('.erb', ''), 'w') { |f| f.print rendered }
132 | rm erb if opts[:delete]
133 | end
134 | end
135 |
136 | def render_erb_from_string_with_binding(erb, some_binding)
137 | ERB.new(erb, nil, '<>', 'rendered').result(some_binding)
138 | end
139 | end
140 | end
141 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### __IMPORTANT: Ruby-processing is deprecated and unsupported__ ###
2 |
3 | Use the updated version [JRubyArt][jruby_art] or the standalone alternative [propane][], which target processing-3.5.3 and processing-4.0 (like jdk11+) respectively, and support ruby-2.5+ syntax. Works on same platforms as vanilla processing (windows, mac, linux) for Android see Yuki Morohoshi [ruboto-processing2][].
4 |
5 | [Processing][] provides a tidy API, with a bunch of handy methods you can call
6 | from Ruby-Processing. Here's a smattering:
7 |
8 | `alpha`, `arc`, `background`, `blend`, `blue`, `ellipse`, `frame_rate`, `hue`, `lerp`, `load_image`, `load_pixels`, `mouse_pressed`, `noise`, `rect`, `saturation`, `shape`, `smooth`, `text_align`, `translate`, `triangle`, `vertex`...
9 |
10 |
11 | ## Installation
12 | We assume you have some version of ruby installed if not, there is a [guide to installing][] ruby on various platforms including windows. Or here is an [alternative install][] guide.
13 |
14 | MacOSX users please try out this new [method](https://github.com/jashkenas/ruby-processing/wiki/Installing-ruby-processing-on-the-mac) or see this [japanese][] guide.
15 |
16 | Ideally you should install [jruby](http://jruby.org/download), at the very least you will have at least ruby-1.9.3 installed. You should [download][] and install vanilla [processing-2.2.1](https://processing.org/download/) prior to installing this version of ruby-processing. You must also set the `PROCESSING_ROOT` in the .rp5rc yaml configuration file, the easiest way to do this is by running the [SetProcessingRoot.pde](https://gist.github.com/monkstone/7438749) sketch in the processing ide.
17 |
18 | Then install ruby-processing (from rubygems-org) in the usual way
19 |
20 | `gem install ruby-processing` some systems eg Ubuntu may require `sudo` access
21 |
22 | To install jruby-complete use our built in tool (relies on `wget` to download [jruby-complete-1.7.26](http://jruby.org/download))
23 |
24 | since ruby-processing-2.5.0 `rp5 setup install` (was `install_jruby_complete`)
25 |
26 | If you haven't got `wget` just download jruby-complete-1.7.26 (for ruby-processing-2.7.1) to the vendors folder (then run above tool)
27 |
28 | The vendored jruby-complete is only required for application export, and running certain sketches (eg shader sketches see [wiki][]).
29 |
30 | ## Documentation
31 |
32 | See [Wiki][]
33 |
34 | See also [FAQs][], [Contributing][] and [Samples][]
35 |
36 | # Usage Example
37 |
38 | ```bash
39 | rp5 run my_sketch.rb
40 | ```
41 |
42 | or if not using system jruby (and not `JRUBY: 'false'` in `~/.rp5rc`)
43 |
44 | ```bash
45 | rp5 --nojruby run my_sketch.rb
46 | ```
47 |
48 | where a simple ``my_sketch.rb`` could look like this
49 |
50 | ```ruby
51 | def setup
52 | size 400, 400
53 | fill 255
54 | end
55 |
56 | def draw
57 | background 0
58 | ellipse mouse_x, mouse_y, 100, 100
59 | end
60 | ```
61 |
62 | or a simple 3D sketch ``cube.rb`` features ArcBall from vecmath library
63 |
64 | ```ruby
65 | load_library :vecmath
66 |
67 | ############################
68 | # Use mouse drag to rotate
69 | # the arcball. Use mousewheel
70 | # to zoom. Hold down x, y, z
71 | # to constrain rotation axis.
72 | ############################
73 |
74 | def setup
75 | size(600, 600, P3D)
76 | smooth(8)
77 | ArcBall.init(self, 300, 300)
78 | fill 180
79 | end
80 |
81 | def draw
82 | background(50)
83 | box(300, 300, 300)
84 | end
85 |
86 | ```
87 | See [samples][] for many more examples
88 | ___
89 |
90 | ### Supported java version
91 |
92 | NB: you can't use jdk/jre installed by processing
93 | * Open jdk8 (latest version preferred, is the default linux install)
94 | * jdk8 from Oracle (latest version preferred, or required by Mac)
95 | * jdk7 should also work (typically ubuntu linux or some other linux distro)
96 |
97 | ### Supported ruby version
98 |
99 | This gem has been tested with the following ruby implementations
100 |
101 | * Ruby 1.9.3
102 | * Ruby 2.0.0
103 | * Ruby 2.1.2
104 | * Ruby 2.2.1
105 | * Ruby 2.3.0
106 | * [JRuby][] preferred use jruby-1.7.XX but also works with jruby-9.1.2.0 release
107 |
108 | ### Supported processing version
109 |
110 | * processing-2.2.1 (required)
111 | * for processing-3.0+ see [JRubyArt][jruby_art] or [propane][propane]
112 |
113 | ____
114 |
115 | ### Ruby alternatives for processing convenience methods
116 |
117 | Many processing (convenience) methods make little sense in ruby (and many are no-longer implemented). See ruby alternatives for [details][].
118 | ____
119 |
120 | [License][]
121 |
122 | [license]:LICENSE.md
123 | [contributing]:CONTRIBUTING.md
124 | [jruby]:http://www.jruby.org/
125 | [processing]: http://www.processing.org/
126 | [download]:https://processing.org/download/
127 | [samples]:https://github.com/ruby-processing/Example-Sketches
128 | [wiki]:http://github.com/jashkenas/ruby-processing/wikis/
129 | [details]:https://github.com/jashkenas/ruby-processing/wiki/Replacing-processing-convenience-methods
130 | [FAQs]:http://github.com/jashkenas/ruby-processing/wikis/FAQs/
131 | [release]:https://github.com/jashkenas/ruby-processing/releases/
132 | [guide to installing]:https://www.ruby-lang.org/en/installation/
133 | [alternative install]:http://tutorials.jumpstartlab.com/topics/environment/environment.html
134 | [fix]:https://github.com/jruby/jruby/issues/1917
135 | [japanese]:http://qiita.com/yohm13/items/f3f82f423b507cec1dcc
136 | [jruby_art]:https://ruby-processing.github.io/JRubyArt/
137 | [ruboto-processing2]:https://github.com/hoshi-sano/ruboto-processing2
138 | [propane]:https://ruby-processing.github.io/propane/
139 |
--------------------------------------------------------------------------------
/library/control_panel/control_panel.rb:
--------------------------------------------------------------------------------
1 | # Here's a little library for quickly hooking up controls to sketches.
2 | # For messing with the parameters and such.
3 | # These controls will set instance variables on the sketches.
4 |
5 | # You can make sliders, checkboxes, buttons, and drop-down menus.
6 | # (optionally) pass the range and default value.
7 |
8 | module ControlPanel
9 | # class used to create slider elements for control_panel
10 | class Slider < javax.swing.JSlider
11 | def initialize(control_panel, name, range, initial_value, proc = nil)
12 | min = range.begin * 100
13 | max = (
14 | (range.exclude_end? && range.begin.respond_to?(:succ)) ?
15 | range.max : range.end) * 100
16 | super(min, max)
17 | set_minor_tick_spacing((max - min).abs / 10)
18 | set_paint_ticks true
19 | # paint_labels = true
20 | set_preferred_size(java.awt.Dimension.new(190, 30))
21 | label = control_panel.add_element(self, name)
22 | add_change_listener do
23 | update_label(label, name, value)
24 | $app.instance_variable_set("@#{name}", value) unless value.nil?
25 | proc.call(value) if proc
26 | end
27 | set_value(initial_value ? initial_value * 100 : min)
28 | fire_state_changed
29 | end
30 |
31 | def value
32 | get_value / 100.0
33 | end
34 |
35 | def update_label(label, name, value)
36 | value = value.to_s
37 | value << '0' if value.length < 4
38 | label.set_text "
#{name}: #{value}"
39 | end
40 | end
41 |
42 | # class used to combo_box menu elements for control_panel
43 | class Menu < javax.swing.JComboBox
44 | def initialize(control_panel, name, elements, initial_value, proc = nil)
45 | super(elements.to_java(:String))
46 | set_preferred_size(java.awt.Dimension.new(190, 30))
47 | control_panel.add_element(self, name)
48 | add_action_listener do
49 | $app.instance_variable_set("@#{name}", value) unless value.nil?
50 | proc.call(value) if proc
51 | end
52 | set_selected_index(initial_value ? elements.index(initial_value) : 0)
53 | end
54 |
55 | def value
56 | get_selected_item
57 | end
58 | end
59 |
60 | # Creates check-box elements for control_panel
61 | class Checkbox < javax.swing.JCheckBox
62 | def initialize(control_panel, name, proc = nil)
63 | @control_panel = control_panel
64 | super(name.to_s)
65 | set_preferred_size(java.awt.Dimension.new(190, 64))
66 | set_horizontal_alignment javax.swing.SwingConstants::CENTER
67 | control_panel.add_element(self, name, false)
68 | add_action_listener do
69 | $app.instance_variable_set("@#{name}", value) unless value.nil?
70 | proc.call(value) if proc
71 | end
72 | end
73 |
74 | def value
75 | is_selected
76 | end
77 | end
78 |
79 | # Creates button elements for control_panel
80 | class Button < javax.swing.JButton
81 | def initialize(control_panel, name, proc = nil)
82 | super(name.to_s)
83 | set_preferred_size(java.awt.Dimension.new(170, 64))
84 | control_panel.add_element(self, name, false, true)
85 | add_action_listener do
86 | $app.send(name.to_s)
87 | proc.call(value) if proc
88 | end
89 | end
90 | end
91 |
92 | # class used to contain control_panel elements
93 | class Panel < javax.swing.JFrame
94 | java_import javax.swing.UIManager
95 |
96 | attr_accessor :elements, :panel
97 |
98 | def initialize
99 | super()
100 | @elements = []
101 | @panel = javax.swing.JPanel.new(java.awt.FlowLayout.new(1, 0, 0))
102 | set_feel
103 | end
104 |
105 | def display
106 | add panel
107 | set_size 200, 30 + (64 * elements.size)
108 | set_default_close_operation javax.swing.JFrame::HIDE_ON_CLOSE
109 | set_resizable false
110 | set_location($app.width + 10, 0) unless $app.width + 10 > $app.displayWidth
111 | panel.visible = true
112 | end
113 |
114 | def add_element(element, name, has_label = true, _button_ = false)
115 | if has_label
116 | label = javax.swing.JLabel.new("
#{name}")
117 | panel.add label
118 | end
119 | elements << element
120 | panel.add element
121 | has_label ? label : nil
122 | end
123 |
124 | def remove
125 | remove_all
126 | dispose
127 | end
128 |
129 | def slider(name, range = 0..100, initial_value = nil, &block)
130 | Slider.new(self, name, range, initial_value, block || nil)
131 | end
132 |
133 | def menu(name, elements, initial_value = nil, &block)
134 | Menu.new(self, name, elements, initial_value, block || nil)
135 | end
136 |
137 | def checkbox(name, initial_value = nil, &block)
138 | checkbox = Checkbox.new(self, name, block || nil)
139 | checkbox.do_click if initial_value == true
140 | end
141 |
142 | def button(name, &block)
143 | Button.new(self, name, block || nil)
144 | end
145 |
146 | def look_feel(lf)
147 | set_feel(lf)
148 | end
149 |
150 | private
151 |
152 | def set_feel(lf = 'metal')
153 | lafinfo = javax.swing.UIManager.getInstalledLookAndFeels
154 | laf = lafinfo.select do |info|
155 | info.getName.eql? lf.capitalize
156 | end
157 | javax.swing.UIManager.setLookAndFeel(laf[0].getClassName)
158 | end
159 | end
160 |
161 | # instance methods module
162 | module InstanceMethods
163 | def control_panel
164 | @control_panel = ControlPanel::Panel.new unless @control_panel
165 | return @control_panel unless block_given?
166 | yield(@control_panel)
167 | @control_panel.display
168 | end
169 | end
170 | end
171 |
172 | Processing::App.send :include, ControlPanel::InstanceMethods
173 |
--------------------------------------------------------------------------------
/lib/ruby-processing/library_loader.rb:
--------------------------------------------------------------------------------
1 | # The processing wrapper module
2 | require_relative '../ruby-processing'
3 |
4 | module Processing
5 |
6 | # Encapsulate library loader functionality as a class
7 | class LibraryLoader
8 | attr_reader :sketchbook_library_path
9 |
10 | def initialize
11 | @sketchbook_library_path = File.join(find_sketchbook_path, 'libraries')
12 | @loaded_libraries = Hash.new(false)
13 | end
14 |
15 | # Detect if a library has been loaded (for conditional loading)
16 | def library_loaded?(library_name)
17 | @loaded_libraries[library_name.to_sym]
18 | end
19 |
20 | # Load a list of Ruby or Java libraries (in that order)
21 | # Usage: load_libraries :opengl, :boids
22 | #
23 | # If a library is put into a 'library' folder next to the sketch it will
24 | # be used instead of the library that ships with Ruby-Processing.
25 | def load_libraries(*args)
26 | message = 'no such file to load -- %s'
27 | args.each do |lib|
28 | loaded = load_ruby_library(lib) || load_java_library(lib)
29 | fail(LoadError.new, format(message, lib)) unless loaded
30 | end
31 | end
32 | alias_method :load_library, :load_libraries
33 |
34 | # For pure ruby libraries.
35 | # The library should have an initialization ruby file
36 | # of the same name as the library folder.
37 | def load_ruby_library(library_name)
38 | library_name = library_name.to_sym
39 | return true if @loaded_libraries.include?(library_name)
40 | if ENV['EXPORTED'].eql?('true')
41 | begin
42 | return @loaded_libraries[library_name] = (require_relative "../library/#{library_name}")
43 | rescue LoadError
44 | return false
45 | end
46 | end
47 | path = get_library_paths(library_name, 'rb').first
48 | return false unless path
49 | @loaded_libraries[library_name] = (require path)
50 | end
51 |
52 | # HACK: For pure java libraries, such as the ones that are available
53 | # on this page: http://processing.org/reference/libraries/index.html
54 | # that include native code, we mess with the 'Java ClassLoader', so that
55 | # you don't have to futz with your PATH. But it's probably bad juju.
56 | def load_java_library(library_name)
57 | library_name = library_name.to_sym
58 | return true if @loaded_libraries.include?(library_name)
59 | jpath = get_library_directory_path(library_name, 'jar')
60 | jars = get_library_paths(library_name, 'jar')
61 | return false if jars.empty?
62 | jars.each { |jar| require jar }
63 | platform_specific_library_paths = get_platform_specific_library_paths(jpath)
64 | platform_specific_library_paths = platform_specific_library_paths.select do |ppath|
65 | FileTest.directory?(ppath) && !Dir.glob(File.join(ppath, '*.{so,dll,jnilib}')).empty?
66 | end
67 | unless platform_specific_library_paths.empty?
68 | platform_specific_library_paths << java.lang.System.getProperty('java.library.path')
69 | new_library_path = platform_specific_library_paths.join(java.io.File.pathSeparator)
70 | java.lang.System.setProperty('java.library.path', new_library_path)
71 | field = java.lang.Class.for_name('java.lang.ClassLoader').get_declared_field('sys_paths')
72 | if field
73 | field.accessible = true
74 | field.set(java.lang.Class.for_name('java.lang.System').get_class_loader, nil)
75 | end
76 | end
77 | @loaded_libraries[library_name] = true
78 | end
79 |
80 | def platform
81 | match = %w(mac linux windows).find do |os|
82 | java.lang.System.getProperty('os.name').downcase.index(os)
83 | end
84 | return 'other' unless match
85 | return match unless match =~ /mac/
86 | 'macosx'
87 | end
88 |
89 | def get_platform_specific_library_paths(basename)
90 | bits = 'universal' # for MacOSX, but does this even work, or does Mac return '64'?
91 | if java.lang.System.getProperty('sun.arch.data.model') == '32' ||
92 | java.lang.System.getProperty('java.vm.name').index('32')
93 | bits = '32'
94 | elsif java.lang.System.getProperty('sun.arch.data.model') == '64' ||
95 | java.lang.System.getProperty('java.vm.name').index('64')
96 | bits = '64' unless platform =~ /macosx/
97 | end
98 | [platform, platform + bits].map { |p| File.join(basename, p) }
99 | end
100 |
101 | def get_library_paths(library_name, extension = nil)
102 | dir = get_library_directory_path(library_name, extension)
103 | Dir.glob("#{dir}/*.{rb,jar}")
104 | end
105 |
106 | protected
107 |
108 | def get_library_directory_path(library_name, extension = nil)
109 | extensions = extension ? [extension] : %w(jar rb)
110 | extensions.each do |ext|
111 | ["#{SKETCH_ROOT}/library/#{library_name}",
112 | "#{Processing::RP_CONFIG['PROCESSING_ROOT']}/modes/java/libraries/#{library_name}/library",
113 | "#{RP5_ROOT}/library/#{library_name}/library",
114 | "#{RP5_ROOT}/library/#{library_name}",
115 | "#{@sketchbook_library_path}/#{library_name}/library"
116 | ].each do |jpath|
117 | if File.exist?(jpath) && !Dir.glob(format('%s/*.%s', jpath, ext)).empty?
118 | return jpath
119 | end
120 | end
121 | end
122 | nil
123 | end
124 |
125 | def find_sketchbook_path
126 | preferences_paths = []
127 | sketchbook_paths = []
128 | sketchbook_path = Processing::RP_CONFIG.fetch('sketchbook_path', false)
129 | return sketchbook_path if sketchbook_path
130 | ["'Application Data/Processing'", 'AppData/Roaming/Processing',
131 | 'Library/Processing', 'Documents/Processing',
132 | '.processing', 'sketchbook'].each do |prefix|
133 | spath = format('%s/%s', ENV['HOME'], prefix)
134 | pref_path = format('%s/preferences.txt', spath)
135 | preferences_paths << pref_path if FileTest.file?(pref_path)
136 | sketchbook_paths << spath if FileTest.directory?(spath)
137 | end
138 | return sketchbook_paths.first if preferences_paths.empty?
139 | lines = IO.readlines(preferences_paths.first)
140 | matchedline = lines.grep(/^sketchbook/).first
141 | matchedline[/=(.+)/].gsub('=', '')
142 | end
143 | end
144 | end
145 |
--------------------------------------------------------------------------------
/library/boids/boids.rb:
--------------------------------------------------------------------------------
1 | # Boids -- after Tom de Smedt.
2 | # See his Python version: http://nodebox.net/code/index.php/Boids
3 | # This is an example of how a pure-Ruby library can work.
4 | # -- omygawshkenas
5 |
6 | class Boid
7 | attr_accessor :boids, :x, :y, :z, :vx, :vy, :vz, :is_perching, :perch_time
8 |
9 | def initialize(boids, x, y, z)
10 | @boids, @flock = boids, boids
11 | @x, @y, @z = x, y, z
12 | @vx, @vy, @vz = 0.0, 0.0, 0.0
13 | @is_perching = false
14 | @perch_time = 0.0
15 | end
16 |
17 | def cohesion(d = 100.0)
18 | # Boids gravitate towards the center of the flock,
19 | # Which is the averaged position of the rest of the boids.
20 | cvx, cvy, cvz = 0.0, 0.0, 0.0
21 | boids.reject { |bd| bd.equal? self }.each do |boid|
22 | cvx, cvy, cvz = cvx + boid.x, cvy + boid.y, cvz + boid.z
23 | end
24 | count = boids.length - 1.0
25 | cvx, cvy, cvz = cvx / count, cvy / count, cvz / count
26 | [(cvx - x) / d, (cvy - y) / d, (cvz - z) / d]
27 | end
28 |
29 | def separation(radius = 10.0)
30 | # Boids don't like to cuddle.
31 | svx, svy, svz = 0.0, 0.0, 0.0
32 | boids.reject { |bd| bd.equal? self }.each do |boid|
33 | dvx, dvy, dvz = x - boid.x, y - boid.y, z - boid.z
34 | svx += dvx if dvx.abs < radius
35 | svy += dvy if dvy.abs < radius
36 | svz += dvz if dvz.abs < radius
37 | end
38 | [svx, svy, svz]
39 | end
40 |
41 | def alignment(d = 5.0)
42 | # Boids like to fly at the speed of traffic.
43 | avx, avy, avz = 0.0, 0.0, 0.0
44 | boids.reject { |bd| bd.equal? self }.each do |boid|
45 | avx, avy, avz = avx + boid.vx, avy + boid.vy, avz + boid.vz
46 | end
47 | count = boids.length - 1.0
48 | avx, avy, avz = avx / count, avy / count, avz / count
49 | [(avx - vx) / d, (avy - vy) / d, (avz - vz) / d]
50 | end
51 |
52 | def limit(max = 30.0)
53 | # Tweet, Tweet! The boid police will bust you for breaking the speed limit.
54 | most = [@vx.abs, @vy.abs, @vz.abs].max
55 | return if most < max
56 | scale = max / most.to_f
57 | @vx *= scale
58 | @vy *= scale
59 | @vz *= scale
60 | end
61 |
62 | def angle
63 | a = (Math.atan(@vy / @vx) * (180.0 / Math::PI)) + 360.0
64 | a += 180.0 if @vx < 0.0
65 | a
66 | end
67 |
68 | def goal(gx, gy, gz, d = 50.0)
69 | # Them boids is hungry.
70 | [(gx - x) / d, (gy - y) / d, (gz - z) / d]
71 | end
72 | end
73 |
74 | require 'forwardable'
75 |
76 | class Boids
77 | include Enumerable
78 | extend Forwardable
79 | def_delegators(:@boids, :reject, :<<, :each, :shuffle!, :length, :next)
80 |
81 | attr_accessor :boids, :x, :y, :w, :h,
82 | :scattered, :has_goal, :flee
83 |
84 | attr_reader :scatter, :scatter_time, :scatter_i,
85 | :perch, :perch_y, :perch_t, :boids,
86 | :goal_x, :goal_y, :goal_z
87 |
88 | def initialize
89 | @boids = []
90 | end
91 |
92 | def self.flock(n, x, y, w, h)
93 | Boids.new.setup(n, x, y, w, h)
94 | end
95 |
96 | def setup(n, x, y, w, h)
97 | n.times do
98 | dx, dy = rand(w), rand(h)
99 | z = rand(200.0)
100 | self << Boid.new(self, x + dx, y + dy, z)
101 | end
102 | @x, @y, @w, @h = x, y, w, h
103 | init
104 | self
105 | end
106 |
107 | def init
108 | @scattered = false
109 | @scatter = 0.005
110 | @scatter_time = 50.0
111 | @scatter_i = 0.0
112 | @perch = 1.0 # Lower this number to divebomb.
113 | @perch_y = h
114 | @perch_t = -> { rand(25..75.0) }
115 | @has_goal = false
116 | @flee = false
117 | @goal_x = @goal_y = @goal_z = 0.0
118 | end
119 |
120 | def scatter(chance = 0.005, frames = 50.0)
121 | @scatter = chance
122 | @scatter_time = frames
123 | end
124 |
125 | def no_scatter
126 | @scatter = 0.0
127 | end
128 |
129 | def perch(ground = nil, chance = 1.0, frames = nil)
130 | frames ||= -> { rand(25..75.0) }
131 | ground ||= h
132 | @perch, @perch_y, @perch_t = chance, ground, frames
133 | end
134 |
135 | def no_perch
136 | @perch = 0.0
137 | end
138 |
139 | def goal(gx, gy, gz, flee = false)
140 | @has_goal = true
141 | @flee = flee
142 | @goal_x, @goal_y, @goal_z = gx, gy, gz
143 | end
144 |
145 | def no_goal
146 | @has_goal = false
147 | end
148 |
149 | def constrain
150 | # Put them boids in a cage.
151 | dx, dy = w * 0.1, h * 0.1
152 | each do |b|
153 | b.vx += rand(dx) if b.x < x - dx
154 | b.vx += rand(dy) if b.y < y - dy
155 | b.vx -= rand(dx) if b.x > x + w + dx
156 | b.vy -= rand(dy) if b.y > y + h + dy
157 | b.vz += 10.0 if b.z < 0.0
158 | b.vz -= 10.0 if b.z > 100.0
159 | next unless b.y > @perch_y && rand < @perch
160 | b.y = @perch_y
161 | b.vy = -(b.vy.abs) * 0.2
162 | b.is_perching = true
163 | @perch_t.respond_to?(:call) ? b.perch_time = @perch_t.call : b.perch_time = @perch_t
164 | end
165 | end
166 |
167 | def update(opts = {}) # Just flutter, little boids ... just flutter away.
168 | options = {
169 | shuffled: true, # Shuffling keeps things flowing smooth.
170 | cohesion: 100.0,
171 | separation: 10.0,
172 | alignment: 5.0,
173 | goal: 20.0,
174 | limit: 30.0
175 | }
176 | options.merge! opts
177 | shuffle! if options[:shuffled]
178 | m1 = 1.0 # cohesion
179 | m2 = 1.0 # separation
180 | m3 = 1.0 # alignment
181 | m4 = 1.0 # goal
182 | @scattered = true if !(@scattered) && rand < @scatter
183 | if @scattered
184 | m1 = -m1
185 | m3 *= 0.25
186 | @scatter_i += 1.0
187 | end
188 | if @scatter_i >= @scatter_time
189 | @scattered = false
190 | @scatter_i = 0.0
191 | end
192 | m4 = 0.0 unless @has_goal
193 | m4 = -m4 if @flee
194 | each do |b|
195 | if b.is_perching
196 | if b.perch_time > 0.0
197 | b.perch_time -= 1.0
198 | next
199 | else
200 | b.is_perching = false
201 | end
202 | end
203 | vx1, vy1, vz1 = *b.cohesion(options[:cohesion])
204 | vx2, vy2, vz2 = *b.separation(options[:separation])
205 | vx3, vy3, vz3 = *b.alignment(options[:alignment])
206 | vx4, vy4, vz4 = b.goal(@goal_x, @goal_y, @goal_z, options[:goal])
207 | b.vx += m1 * vx1 + m2 * vx2 + m3 * vx3 + m4 * vx4
208 | b.vy += m1 * vy1 + m2 * vy2 + m3 * vy3 + m4 * vy4
209 | b.vz += m1 * vz1 + m2 * vz2 + m3 * vz3 + m4 * vz4
210 | b.limit(options[:limit])
211 | b.x += b.vx
212 | b.y += b.vy
213 | b.z += b.vz
214 | end
215 | constrain
216 | end
217 | end
218 |
--------------------------------------------------------------------------------
/lib/ruby-processing/helper_methods.rb:
--------------------------------------------------------------------------------
1 | # processing module wrapper
2 | require_relative '../rpextras'
3 |
4 |
5 | module Processing
6 | # Provides some convenience methods available in vanilla processing
7 | Java::Monkstone::MathToolLibrary.load(JRuby.runtime)
8 | module HelperMethods
9 | # processings epsilon may not be defined yet
10 | EPSILON ||= 1.0e-04
11 | # Nice block method to draw to a buffer.
12 | # You can optionally pass it a width, a height, and a renderer.
13 | # Takes care of starting and ending the draw for you.
14 | def buffer(buf_width = width, buf_height = height, renderer = @render_mode)
15 | buf = create_graphics(buf_width, buf_height, renderer)
16 | buf.begin_draw
17 | yield buf
18 | buf.end_draw
19 | buf
20 | end
21 |
22 | # A nice method to run a given block for a grid.
23 | # Lifted from action_coding/Nodebox.
24 | def grid(cols, rows, col_size = 1, row_size = 1)
25 | (0...cols * rows).map do |i|
26 | x = col_size * (i % cols)
27 | y = row_size * i.div(cols)
28 | yield x, y
29 | end
30 | end
31 |
32 | # lerp_color takes three or four arguments, in Java that's two
33 | # different methods, one regular and one static, so:
34 | def lerp_color(*args)
35 | args.length > 3 ? self.class.lerp_color(*args) : super(*args)
36 | end
37 |
38 | def color(*args)
39 | return super(*args) unless args.length == 1
40 | super(hex_color(args[0]))
41 | end
42 |
43 | # Overrides Processing convenience function thread, which takes a String
44 | # arg (for a function) to more rubylike version, takes a block...
45 | def thread(&block)
46 | if block_given?
47 | Thread.new(&block)
48 | else
49 | fail ArgumentError, 'thread must be called with a block', caller
50 | end
51 | end
52 |
53 | # explicitly provide 'processing.org' min instance method
54 | # to return a float:- a, b and c need to be floats
55 |
56 | def min(*args)
57 | args.min # { |a,b| a <=> b } optional block not reqd
58 | end
59 |
60 | # explicitly provide 'processing.org' max instance method
61 | # to return a float:- a, b and c need to be floats
62 |
63 | def max(*args)
64 | args.max # { |a, b| a <=> b } optional block not reqd
65 | end
66 |
67 | # explicitly provide 'processing.org' dist instance method
68 | def dist(*args)
69 | len = args.length
70 | if len == 4
71 | return dist2d(*args)
72 | elsif len == 6
73 | return dist3d(*args)
74 | end
75 | fail ArgumentError, 'takes 4 or 6 parameters'
76 | end
77 |
78 | # Uses PImage class method under hood
79 | def blend_color(c1, c2, mode)
80 | Java::ProcessingCore::PImage.blendColor(c1, c2, mode)
81 | end
82 |
83 | # There's just so many functions in Processing,
84 | # Here's a convenient way to look for them.
85 | def find_method(method_name)
86 | reg = Regexp.new("#{method_name}", true)
87 | methods.sort.select { |meth| reg.match(meth) }
88 | end
89 |
90 | # Proxy over a list of Java declared fields that have the same name as
91 | # some methods. Add to this list as needed.
92 | def proxy_java_fields
93 | fields = %w(sketchPath key frameRate frame mousePressed keyPressed)
94 | methods = fields.map { |field| java_class.declared_field(field) }
95 | @declared_fields = Hash[fields.zip(methods)]
96 | end
97 |
98 | class VersionError < StandardError
99 | end
100 |
101 | # By default, your sketch path is the folder that your sketch is in.
102 | # If you'd like to do something fancy, feel free.
103 | def set_sketch_path(spath = nil)
104 | field = @declared_fields['sketchPath']
105 | begin
106 | field.set_value(java_self, spath || SKETCH_ROOT)
107 | rescue TypeError
108 | fail VersionError, 'Use JRubyArt for processing-3.0'
109 | end
110 | end
111 |
112 | # Fix java conversion problems getting the last key
113 | # If it's ASCII, return the character, otherwise the integer
114 | def key
115 | int = @declared_fields['key'].value(java_self)
116 | int < 256 ? int.chr : int
117 | end
118 |
119 | # Provide a convenient handle for the Java-space version of self.
120 | def java_self
121 | @java_self ||= to_java(Java::ProcessingCore::PApplet)
122 | end
123 |
124 | # Get the sketch path
125 | def sketch_path
126 | @declared_fields['sketchPath'].value(java_self)
127 | end
128 |
129 | # Fields that should be made accessible as under_scored.
130 | define_method(:mouse_x) { mouseX }
131 |
132 | define_method(:mouse_y) { mouseY }
133 |
134 | define_method(:pmouse_x) { pmouseX }
135 |
136 | define_method(:pmouse_y) { pmouseY }
137 |
138 | define_method(:frame_count) { frameCount }
139 |
140 | define_method(:mouse_button) { mouseButton }
141 |
142 | define_method(:key_code) { keyCode }
143 |
144 | # Ensure that load_strings returns a real Ruby array
145 | def load_strings(file_or_url)
146 | loadStrings(file_or_url).to_a
147 | end
148 |
149 | # Writes an array of strings to a file, one line per string.
150 | # This file is saved to the sketch's data folder
151 | def save_strings(filename, strings)
152 | saveStrings(filename, [strings].flatten.to_java(:String))
153 | end
154 |
155 | # frame_rate needs to support reading and writing
156 | def frame_rate(fps = nil)
157 | return @declared_fields['frameRate'].value(java_self) unless fps
158 | super(fps)
159 | end
160 |
161 | # Is the mouse pressed for this frame?
162 | def mouse_pressed?
163 | @declared_fields['mousePressed'].value(java_self)
164 | end
165 |
166 | # Is a key pressed for this frame?
167 | def key_pressed?
168 | @declared_fields['keyPressed'].value(java_self)
169 | end
170 |
171 | private
172 |
173 | # parse single argument color int/double/String
174 | def hex_color(a)
175 | if a.is_a?(Fixnum)
176 | return Java::Monkstone::ColorUtil.colorLong(a)
177 | elsif a.is_a?(String)
178 | return Java::Monkstone::ColorUtil.colorString(a) if a =~ /#\h+/
179 | fail StandardError, 'Dodgy Hexstring'
180 | end
181 | Java::Monkstone::ColorUtil.colorDouble(a)
182 | end
183 |
184 | def dist2d(*args)
185 | dx = args[0] - args[2]
186 | dy = args[1] - args[3]
187 | return 0 if dx.abs < EPSILON && dy.abs < EPSILON
188 | Math.hypot(dx, dy)
189 | end
190 |
191 | def dist3d(*args)
192 | dx = args[0] - args[3]
193 | dy = args[1] - args[4]
194 | dz = args[2] - args[5]
195 | return 0 if dx.abs < EPSILON && dy.abs < EPSILON && dz.abs < EPSILON
196 | Math.sqrt(dx * dx + dy * dy + dz * dz)
197 | end
198 | end
199 | end
200 |
--------------------------------------------------------------------------------
/src/monkstone/MathTool.java:
--------------------------------------------------------------------------------
1 | /**
2 | * The purpose of this tool is to allow ruby-processing users to use an alternative
3 | * to processing.org map, lerp and norm methods in their sketches
4 | * Copyright (C) 2015-16 Martin Prout. This tool is free software; you can
5 | * redistribute it and/or modify it under the terms of the GNU Lesser General
6 | * Public License as published by the Free Software Foundation; either version
7 | * 2.1 of the License, or (at your option) any later version.
8 | *
9 | * Obtain a copy of the license at http://www.gnu.org/licenses/lgpl-2.1.html
10 | */
11 | package monkstone;
12 |
13 | import org.jruby.Ruby;
14 | import org.jruby.RubyClass;
15 | import org.jruby.RubyFloat;
16 | import org.jruby.RubyModule;
17 | import org.jruby.RubyObject;
18 | import org.jruby.RubyRange;
19 | import org.jruby.anno.JRubyMethod;
20 | import org.jruby.runtime.ThreadContext;
21 | import org.jruby.runtime.builtin.IRubyObject;
22 |
23 | /**
24 | *
25 | * @author MartinProut
26 | */
27 |
28 | public class MathTool extends RubyObject {
29 |
30 | private static final long serialVersionUID = 4427564758225746633L;
31 |
32 |
33 |
34 | /**
35 | *
36 | * @param runtime
37 | */
38 | public static void createMathTool(Ruby runtime) {
39 | RubyModule processing = runtime.defineModule("Processing");
40 | RubyModule module = processing.defineModuleUnder("MathTool");
41 | module.defineAnnotatedMethods(MathTool.class);
42 | }
43 |
44 | /**
45 | *
46 | * @param context JRuby runtime
47 | * @param recv self
48 | * @param args array of RubyRange (must be be numeric)
49 | * @return RubyFloat
50 | */
51 | @JRubyMethod(name = "map1d", rest = true, module = true)
52 | public static IRubyObject mapOneD(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
53 | double value = (Double) args[0].toJava(Double.class);
54 | RubyRange r1 = (RubyRange) args[1];
55 | RubyRange r2 = (RubyRange) args[2];
56 | double first1 = (Double) r1.first(context).toJava(Double.class);
57 | double first2 = (Double) r2.first(context).toJava(Double.class);
58 | double last1 = (Double) r1.last(context).toJava(Double.class);
59 | double last2 = (Double) r2.last(context).toJava(Double.class);
60 | return mapMt(context, value, first1, last1, first2, last2);
61 | }
62 |
63 | /**
64 | *
65 | * @param context JRuby runtime
66 | * @param recv self
67 | * @param args array of RubyRange (must be be numeric)
68 | * @return RubyFloat
69 | */
70 | @JRubyMethod(name = "constrained_map", rest = true, module = true)
71 | public static IRubyObject constrainedMap(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
72 | double value = (Double) args[0].toJava(Double.class);
73 | RubyRange r1 = (RubyRange) args[1];
74 | RubyRange r2 = (RubyRange) args[2];
75 | double first1 = (Double) r1.first(context).toJava(Double.class);
76 | double first2 = (Double) r2.first(context).toJava(Double.class);
77 | double last1 = (Double) r1.last(context).toJava(Double.class);
78 | double last2 = (Double) r2.last(context).toJava(Double.class);
79 | double max = Math.max(first1, last1);
80 | double min = Math.min(first1, last1);
81 | if (value < min) {
82 | value = min;
83 | }
84 | if (value > max) {
85 | value = max;
86 | }
87 | return mapMt(context, value, first1, last1, first2, last2);
88 | }
89 |
90 |
91 | /**
92 | *
93 | * @param context JRuby runtime
94 | * @param recv self
95 | * @param args floats as in processing map function
96 | * @return RubyFloat
97 | */
98 | @JRubyMethod(name = {"p5map", "map"}, rest = true, module = true)
99 | public static IRubyObject mapProcessing(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
100 | double value = (Double) args[0].toJava(Double.class);
101 | double first1 = (Double) args[1].toJava(Double.class);
102 | double first2 = (Double) args[3].toJava(Double.class);
103 | double last1 = (Double) args[2].toJava(Double.class);
104 | double last2 = (Double) args[4].toJava(Double.class);
105 | return mapMt(context, value, first1, last1, first2, last2);
106 | }
107 |
108 |
109 | /**
110 | * A more correct version than processing.org version
111 | * @param context
112 | * @param recv
113 | * @param args args[2] should be between 0 and 1.0 if not returns start or stop
114 | * @return lerp value
115 | */
116 | @JRubyMethod(name = "lerp", rest = true, module = true)
117 | public static IRubyObject lerpP(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
118 | double start = (Double) args[0].toJava(Double.class);
119 | double stop = (Double) args[1].toJava(Double.class);
120 | double amount = (Double) args[2].toJava(Double.class);
121 | if (amount <= 0) return args[0];
122 | if (amount >= 1.0) return args[1];
123 | return context.getRuntime().newFloat((1 - amount) * start + (stop * amount));
124 | }
125 |
126 |
127 | /**
128 | * Identical to p5map(value, low, high, 0, 1).
129 | * Numbers outside of the range are not clamped to 0 and 1,
130 | * because out-of-range values are often intentional and useful.
131 | * @param context
132 | * @param recv
133 | * @param args
134 | * @return norm value
135 | */
136 | @JRubyMethod(name = "norm", rest = true, module = true)
137 | public static IRubyObject normP(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
138 | double value = (Double) args[0].toJava(Double.class);
139 | double start = (Double) args[1].toJava(Double.class);
140 | double stop = (Double) args[2].toJava(Double.class);
141 | return mapMt(context, value, start, stop, 0, 1.0);
142 | }
143 |
144 | /**
145 | * Identical to p5map(value, low, high, 0, 1) but 'clamped'.
146 | * Numbers outside of the range are clamped to 0 and 1,
147 | * @param context
148 | * @param recv
149 | * @param args
150 | * @return strict normalized value ie 0..1.0
151 | */
152 | @JRubyMethod(name = "norm_strict", rest = true, module = true)
153 | public static IRubyObject norm_strict(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
154 | Ruby ruby = context.runtime;
155 | double value = (Double) args[0].toJava(Double.class);
156 | double start = (Double) args[1].toJava(Double.class);
157 | double stop = (Double) args[2].toJava(Double.class);
158 | if (value <= start) {
159 | return new RubyFloat(ruby, 0);
160 | } else if (value >= stop) {
161 | return new RubyFloat(ruby, 1.0);
162 | } else {
163 | return mapMt(context, value, start, stop, 0, 1.0);
164 | }
165 | }
166 |
167 | static final RubyFloat mapMt(ThreadContext context, double value, double first1, double last1, double first2, double last2) {
168 | double result = first2 + (last2 - first2) * ((value - first1) / (last1 - first1));
169 | return context.getRuntime().newFloat(result);
170 | }
171 |
172 | /**
173 | * Provides processing constrain method as a ruby module method
174 | * @param context
175 | * @param recv
176 | * @param args
177 | * @return original or limit values
178 | */
179 | @JRubyMethod(name = "constrain", rest = true, module = true)
180 | public static IRubyObject constrainValue(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
181 | RubyFloat value = args[0].convertToFloat();
182 | RubyFloat start = args[1].convertToFloat();
183 | RubyFloat stop = args[2].convertToFloat();
184 | if (value.op_ge(context, start).isTrue() && value.op_le(context, stop).isTrue()) {
185 | return args[0];
186 | } else if (value.op_ge(context, start).isTrue()) {
187 | return args[2];
188 | } else {
189 | return args[1];
190 | }
191 | }
192 |
193 | /**
194 | *
195 | * @param runtime
196 | * @param metaClass
197 | */
198 | public MathTool(Ruby runtime, RubyClass metaClass) {
199 | super(runtime, metaClass);
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/lib/ruby-processing/app.rb:
--------------------------------------------------------------------------------
1 | # version without embedded or online
2 | # This class is a thin wrapper around Processing's PApplet.
3 | # Most of the code here is for interfacing with Swing,
4 | # web applets, going fullscreen and so on.
5 | require 'java'
6 | require_relative 'helper_methods'
7 | require_relative 'helpers/string_extra'
8 | require_relative 'library_loader'
9 | require_relative 'config'
10 |
11 |
12 |
13 | module Processing
14 | # This is the main Ruby-Processing class, and is what you'll
15 | # inherit from when you create a sketch. This class can call
16 | # all of the methods available in Processing, and has two
17 | # mandatory methods, 'setup' and 'draw', both of which you
18 | # should define in your sketch. 'setup' will be called one
19 | # time when the sketch is first loaded, and 'draw' will be
20 | # called constantly, for every frame.
21 | Dir["#{RP_CONFIG["PROCESSING_ROOT"]}/core/library/\*.jar"].each do |jar|
22 | require jar unless jar =~ /native/
23 | end
24 | Java::Monkstone::MathToolLibrary.load(JRuby.runtime)
25 | # Include some core processing classes that we'd like to use:
26 | include_package 'processing.core'
27 |
28 | # Watch the definition of these methods, to make sure
29 | # that Processing is able to call them during events.
30 | METHODS_TO_ALIAS ||= {
31 | mouse_pressed: :mousePressed,
32 | mouse_dragged: :mouseDragged,
33 | mouse_clicked: :mouseClicked,
34 | mouse_moved: :mouseMoved,
35 | mouse_released: :mouseReleased,
36 | key_pressed: :keyPressed,
37 | key_released: :keyReleased,
38 | key_typed: :keyTyped
39 | }
40 | # All sketches extend this class
41 | class App < PApplet
42 | include Math
43 | include HelperMethods
44 | include MathTool
45 | # Alias some methods for familiarity for Shoes coders.
46 | # attr_accessor :frame, :title
47 | alias_method :oval, :ellipse
48 | alias_method :stroke_width, :stroke_weight
49 | alias_method :rgb, :color
50 | alias_method :gray, :color
51 |
52 | def sketch_class
53 | self.class.sketch_class
54 | end
55 |
56 | # Keep track of what inherits from the Processing::App, because we're going
57 | # to want to instantiate one.
58 | def self.inherited(subclass)
59 | super(subclass)
60 | @sketch_class = subclass
61 | end
62 |
63 | class << self
64 | # Handy getters and setters on the class go here:
65 | attr_accessor :sketch_class, :library_loader
66 |
67 | def load_libraries(*args)
68 | library_loader ||= LibraryLoader.new
69 | library_loader.load_library(*args)
70 | end
71 | alias_method :load_library, :load_libraries
72 |
73 | def library_loaded?(library_name)
74 | library_loader.library_loaded?(library_name)
75 | end
76 |
77 | def load_ruby_library(*args)
78 | library_loader.load_ruby_library(*args)
79 | end
80 |
81 | def load_java_library(*args)
82 | library_loader.load_java_library(*args)
83 | end
84 |
85 | # When certain special methods get added to the sketch, we need to let
86 | # Processing call them by their expected Java names.
87 | def method_added(method_name) #:nodoc:
88 | return unless METHODS_TO_ALIAS.key?(method_name)
89 | alias_method METHODS_TO_ALIAS[method_name], method_name
90 | end
91 | end
92 |
93 | def library_loaded?(library_name)
94 | self.class.library_loaded?(library_name)
95 | end
96 |
97 | # It is 'NOT' usually necessary to directly pass options to a sketch, it
98 | # gets done automatically for you. Since processing-2.0 you should prefer
99 | # setting the sketch width and height and renderer using the size method,
100 | # in the sketch (as with vanilla processing), which should be the first
101 | # argument in setup. Sensible options to pass are x and y to locate sketch
102 | # on the screen, or full_screen: true (prefer new hash syntax)
103 |
104 | def initialize(options = {})
105 | super()
106 | post_initialize(options)
107 | $app = self
108 | proxy_java_fields
109 | set_sketch_path # unless Processing.online?
110 | mix_proxy_into_inner_classes
111 | java.lang.Thread.default_uncaught_exception_handler = proc do
112 | |_thread_, exception|
113 | puts(exception.class.to_s)
114 | puts(exception.message)
115 | puts(exception.backtrace.map { |trace| "\t#{trace}" })
116 | close
117 | end
118 | run_sketch(options)
119 | end
120 |
121 | def size(*args)
122 | w, h, mode = *args
123 | @width ||= w
124 | @height ||= h
125 | @render_mode ||= mode
126 | import_opengl if /opengl/ =~ mode
127 | super(*args)
128 | end
129 |
130 | def post_initialize(_args)
131 | nil
132 | end
133 |
134 | # Set the size if we set it before we start the animation thread.
135 | def start
136 | size(@width, @height) if @width && @height
137 | super()
138 | end
139 |
140 | # Provide a loggable string to represent this sketch.
141 | def inspect
142 | "#"
143 | end
144 |
145 | # Cleanly close and shutter a running sketch.
146 | def close
147 | control_panel.remove if respond_to?(:control_panel)
148 | dispose
149 | frame.dispose
150 | end
151 |
152 | private
153 |
154 | # Mix the Processing::Proxy into any inner classes defined for the
155 | # sketch, attempting to mimic the behavior of Java's inner classes.
156 | def mix_proxy_into_inner_classes
157 | klass = Processing::App.sketch_class
158 | klass.constants.each do |name|
159 | const = klass.const_get name
160 | next if const.class != Class || const.to_s.match(/^Java::/)
161 | const.class_eval('include Processing::Proxy')
162 | end
163 | end
164 |
165 | def import_opengl
166 | # Include processing opengl classes that we'd like to use:
167 | %w(FontTexture FrameBuffer LinePath LineStroker PGL
168 | PGraphics2D PGraphics3D PGraphicsOpenGL PShader
169 | PShapeOpenGL Texture).each do |klass|
170 | java_import "processing.opengl.#{klass}"
171 | end
172 | end
173 |
174 | def run_sketch(options = {})
175 | args = []
176 | @width, @height = options[:width], options[:height]
177 | if options[:full_screen]
178 | present = true
179 | args << '--full-screen'
180 | args << "--bgcolor=#{options[:bgcolor]}" if options[:bgcolor]
181 | end
182 | xc = Processing::RP_CONFIG['X_OFF'] ||= 0
183 | yc = Processing::RP_CONFIG['Y_OFF'] ||= 0
184 | x = options.fetch(:x, xc)
185 | y = options.fetch(:y, yc)
186 | args << "--location=#{x},#{y}" # important no spaces here
187 | string_extra = StringExtra.new(File.basename(SKETCH_PATH).sub(/(\.rb)$/, ''))
188 | title = options.fetch(:title, string_extra.titleize)
189 | args << title
190 | PApplet.run_sketch(args.to_java(:string), self)
191 | end
192 | end # Processing::App
193 |
194 | # This module will get automatically mixed in to any inner class of
195 | # a Processing::App, in order to mimic Java's inner classes, which have
196 | # unfettered access to the methods defined in the surrounding class.
197 | module Proxy
198 | include Math
199 | include MathTool
200 | # Generate a list of method names to proxy for inner classes.
201 | # Nothing camelCased, nothing __internal__, just the Processing API.
202 | def self.desired_method_names(inner_class)
203 | bad_method = /__/ # Internal JRuby methods.
204 | unwanted = PApplet.superclass.instance_methods + Object.instance_methods
205 | unwanted -= %w(width height cursor create_image background size resize)
206 | methods = Processing::App.public_instance_methods
207 | methods.reject do |m|
208 | unwanted.include?(m) || bad_method.match(m) || inner_class.method_defined?(m)
209 | end
210 | end
211 |
212 | # Proxy methods through to the sketch.
213 | def self.proxy_methods(inner_class)
214 | code = desired_method_names(inner_class).reduce('') do |rcode, method|
215 | rcode << <<-EOS
216 | def #{method}(*args, &block) # def rect(*args, &block)
217 | if block_given? # if block_given?
218 | $app.send :'#{method}', *args, &block # ...
219 | else # else
220 | $app.#{method} *args # $app.rect *args
221 | end # end
222 | end # end
223 | EOS
224 | end
225 | inner_class.class_eval(code)
226 | end
227 |
228 | # Proxy the sketch's constants on to the inner classes.
229 | def self.proxy_constants(inner_class)
230 | Processing::App.constants.each do |name|
231 | next if inner_class.const_defined?(name)
232 | inner_class.const_set(name, Processing::App.const_get(name))
233 | end
234 | end
235 |
236 | # Don't do all of the work unless we have an inner class that needs it.
237 | def self.included(inner_class)
238 | proxy_methods(inner_class)
239 | proxy_constants(inner_class)
240 | end
241 | end # Processing::Proxy
242 | end # Processing
243 |
--------------------------------------------------------------------------------
/src/monkstone/arcball/Arcball.java:
--------------------------------------------------------------------------------
1 | /**
2 | * The purpose of this library is to allow users to use ArcBall in processing
3 | * sketches Copyright (C) 2014 Martin Prout This library is free software; you
4 | * can redistribute it and/or modify it under the terms of the GNU Lesser
5 | * General Public License as published by the Free Software Foundation; either
6 | * version 2.1 of the License, or (at your option) any later version.
7 | *
8 | * Obtain a copy of the license at http://www.gnu.org/licenses/lgpl-2.1.html
9 | */
10 |
11 | /*
12 | * CREDITS...Initially I found this arcball in a sketch by Ariel Malka,
13 | * only later did I find the Tom Carden processing tutorial example, so take your pick
14 | *
15 | * 1) Ariel Malka - June 23, 2003 http://www.chronotext.org
16 | *
17 | * 2) Simon Greenwold? 2003 (as reported 2006 by Tom Carden http://wiki.processing.org/w/Arcball)
18 | *
19 | * 3) Arcball concept invented by Ken Shoemake, published in his 1985 SIGGRAPH paper "Animating rotations with quaternion curves".
20 | *
21 | * 4) Somewhat modified by Martin Prout to support callbacks from processing sketch
22 | **/
23 | package monkstone.arcball;
24 |
25 | import java.util.Objects;
26 | import processing.core.PApplet;
27 | import processing.event.KeyEvent;
28 | import processing.event.MouseEvent;
29 |
30 | /**
31 | * Supports the Arcball and MouseWheel zoom manipulation of objects in
32 | * processing
33 | *
34 | * @author Martin Prout
35 | */
36 | public class Arcball {
37 |
38 | private double center_x;
39 | private double center_y;
40 | private double radius;
41 | private Jvector v_down;
42 | private Jvector v_drag;
43 | private Quaternion q_now;
44 | private Quaternion q_down;
45 | private Quaternion q_drag;
46 | private Jvector[] axisSet;
47 | private Constrain axis;
48 | private boolean isActive = false;
49 | private PApplet parent;
50 | private double zoom = 1.0f;
51 | private WheelHandler zoomWheelHandler;
52 | private boolean camera = false;
53 | float DEPTH = (float) (1 / (2 * Math.tan(Math.PI / 6)));
54 |
55 | /**
56 | *
57 | * @param parent PApplet
58 | * @param center_x double x coordinate of arcball center
59 | * @param center_y double y coordinate of arcball center
60 | * @param radius double radius of arcball
61 | */
62 | public Arcball(PApplet parent, double center_x, double center_y, double radius) {
63 | this.zoomWheelHandler = new WheelHandler() {
64 | @Override
65 | public void handleWheel(int delta) {
66 | zoom += delta * 0.05;
67 | }
68 | };
69 | this.parent = parent;
70 | this.center_x = center_x;
71 | this.center_y = center_y;
72 | this.radius = radius;
73 | this.v_down = new Jvector();
74 | this.v_drag = new Jvector();
75 | this.q_now = new Quaternion();
76 | this.q_down = new Quaternion();
77 | this.q_drag = new Quaternion();
78 | this.axisSet = new Jvector[]{new Jvector(1.0f, 0.0f, 0.0f), new Jvector(0.0f, 1.0f, 0.0f), new Jvector(0.0f, 0.0f, 1.0f)};
79 | this.axis = Constrain.FREE; // no constraints...
80 | }
81 |
82 | /**
83 | * Default centered arcball and half width or half height whichever smaller
84 | *
85 | * @param parent
86 | *
87 | */
88 | public Arcball(PApplet parent) {
89 | // this(parent, parent.width / 2.0f, parent.height / 2.0f, Math.min(parent.width, parent.height) * 0.5f);
90 | this(parent, 0f, 0f, Math.min(parent.width, parent.height) * 0.8f);
91 | parent.camera(parent.width / 2.0f, parent.height / 2.0f, (parent.height * DEPTH), 0, 0, 0, 0, 1.0f, 0);
92 | camera = true;
93 | this.axis = Constrain.FREE; // no constraints...
94 | }
95 |
96 | /**
97 | * mouse event to register
98 | *
99 | * @param e
100 | */
101 | public void mouseEvent(MouseEvent e) {
102 | int x = e.getX();
103 | int y = e.getY();
104 | switch (e.getAction()) {
105 | case (MouseEvent.PRESS):
106 | v_down = mouse2sphere(x, y);
107 | q_down.set(q_now);
108 | q_drag.reset();
109 | break;
110 | case (MouseEvent.DRAG):
111 | v_drag = mouse2sphere(x, y);
112 | q_drag.set(v_down.dot(v_drag), v_down.cross(v_drag));
113 | break;
114 | case (MouseEvent.WHEEL):
115 | if (zoomWheelHandler != null) {
116 | zoomWheelHandler.handleWheel(e.getCount());
117 | }
118 | break;
119 | default:
120 | }
121 | }
122 |
123 | /**
124 | * key event to register
125 | *
126 | * @param e
127 | */
128 | public void keyEvent(processing.event.KeyEvent e) {
129 | if (e.getAction() != KeyEvent.PRESS) {
130 | } else {
131 | switch (e.getKey()) {
132 | case 'x':
133 | constrain(Constrain.XAXIS);
134 | break;
135 | case 'y':
136 | constrain(Constrain.YAXIS);
137 | break;
138 | case 'z':
139 | constrain(Constrain.ZAXIS);
140 | break;
141 | }
142 | }
143 | if (e.getAction() == KeyEvent.RELEASE) {
144 | constrain(Constrain.FREE);
145 | }
146 | }
147 |
148 | /**
149 | *
150 | */
151 | public void pre() {
152 | if (!camera) {
153 | parent.translate((float) center_x, (float) center_y);
154 | }
155 | update();
156 | }
157 |
158 | /**
159 | * May or may not be required for use in Web Applet it works so why worry as
160 | * used by Jonathan Feinberg peasycam, and that works OK
161 | *
162 | * @param active
163 | */
164 | public void setActive(boolean active) {
165 | if (active != isActive) {
166 | isActive = active;
167 | if (active) {
168 | this.parent.registerMethod("dispose", this);
169 | this.parent.registerMethod("pre", this);
170 | this.parent.registerMethod("mouseEvent", this);
171 | this.parent.registerMethod("keyEvent", this);
172 |
173 | } else {
174 | this.parent.unregisterMethod("pre", this);
175 | this.parent.unregisterMethod("mouseEvent", this);
176 | this.parent.unregisterMethod("keyEvent", this);
177 | }
178 | }
179 | }
180 |
181 | /**
182 | * Don't call this directly in sketch use reflection to call in eg in pre()
183 | */
184 | private void update() {
185 | q_now = Quaternion.mult(q_drag, q_down);
186 | applyQuaternion2Matrix(q_now);
187 | parent.scale((float) zoom);
188 | }
189 |
190 | /**
191 | * Returns either the Jvector of mouse position mapped to a sphere or the
192 | * constrained version (when constrained to one axis)
193 | *
194 | * @param x
195 | * @param y
196 | * @return mouse coordinate mapped to unit sphere
197 | */
198 | public Jvector mouse2sphere(double x, double y) {
199 | Jvector v = new Jvector((x - center_x) / radius, (y - center_y) / radius, 0);
200 | double mag_sq = v.x * v.x + v.y * v.y;
201 | if (mag_sq > 1.0) {
202 | v.normalize();
203 | } else {
204 | v.z = Math.sqrt(1.0 - mag_sq);
205 | }
206 | if (axis != Constrain.FREE) {
207 | v = constrainVector(v, axisSet[axis.index()]);
208 | }
209 | return v;
210 | }
211 |
212 | /**
213 | * Returns the Jvector if the axis is constrained
214 | *
215 | * @param vector
216 | * @param axis
217 | * @return constrained vector
218 | */
219 | public Jvector constrainVector(Jvector vector, Jvector axis) {
220 | Jvector res = vector.sub(axis.mult(axis.dot(vector)));
221 | return res.normalize(); // like Jvector res is changed
222 | }
223 |
224 | /**
225 | * Constrain rotation to this axis
226 | *
227 | * @param axis
228 | */
229 | public void constrain(Constrain axis) {
230 | this.axis = axis;
231 | }
232 |
233 | /**
234 | * Rotate the parent sketch according to the quaternion
235 | *
236 | * @param q
237 | */
238 | public void applyQuaternion2Matrix(Quaternion q) {
239 | // instead of transforming q into a matrix and applying it...
240 | double[] aa = q.getValue();
241 | parent.rotate((float) aa[0], (float) aa[1], (float) aa[2], (float) aa[3]);
242 | }
243 |
244 | /**
245 | * A recommended inclusion for a processing library
246 | */
247 | public void dispose() {
248 | setActive(false);
249 | }
250 |
251 | /**
252 | *
253 | * @param obj
254 | * @return java boolean
255 | */
256 | @Override
257 | public boolean equals(Object obj) {
258 | if (obj == null) {
259 | return false;
260 | }
261 | if (getClass() != obj.getClass()) {
262 | return false;
263 | }
264 | final Arcball other = (Arcball) obj;
265 | if (Double.doubleToLongBits(this.center_x) != Double.doubleToLongBits(other.center_x)) {
266 | return false;
267 | }
268 | if (Double.doubleToLongBits(this.center_y) != Double.doubleToLongBits(other.center_y)) {
269 | return false;
270 | }
271 | if (Double.doubleToLongBits(this.radius) != Double.doubleToLongBits(other.radius)) {
272 | return false;
273 | }
274 | return Objects.equals(this.parent, other.parent);
275 | }
276 |
277 | /**
278 | *
279 | * @return has code int
280 | */
281 | @Override
282 | public int hashCode() {
283 | long hash = 3;
284 | hash = 59 * hash + Double.doubleToLongBits(this.center_x);
285 | hash = 59 * hash + Double.doubleToLongBits(this.center_y);
286 | hash = 59 * hash + Double.doubleToLongBits(this.radius);
287 | hash = 59 * hash + Objects.hashCode(this.parent);
288 | return (int) hash;
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/lib/ruby-processing/runner.rb:
--------------------------------------------------------------------------------
1 | require 'ostruct'
2 | require 'fileutils'
3 | require 'rbconfig'
4 | require_relative '../ruby-processing/config'
5 | require_relative '../ruby-processing/version'
6 |
7 | module Processing
8 |
9 | # Utility class to handle the different commands that the 'rp5' command
10 | # offers. Able to run, watch, live, create, app, and unpack
11 | class Runner
12 | HELP_MESSAGE ||= <<-EOS
13 | Version: #{RubyProcessing::VERSION}
14 |
15 | Ruby-Processing is a little shim between Processing and JRuby that helps
16 | you create sketches of code art.
17 |
18 | Usage:
19 | rp5 [choice] path/to/sketch
20 |
21 | choice:-
22 | run: run sketch once
23 | watch: watch for changes on the file and relaunch it on the fly
24 | live: launch sketch and give an interactive IRB shell
25 | create [width height][mode][flag]: create a new sketch.
26 | app: create an application version of the sketch
27 | setup: check setup, install jruby-complete, unpack samples
28 |
29 | Common options:
30 | --nojruby: use jruby-complete in place of an installed version of jruby
31 | (Set [JRUBY: 'false'] in .rp5rc to make using jruby-complete default)
32 |
33 | Examples:
34 | rp5 setup unpack_samples
35 | rp5 run rp_samples/samples/contributed/jwishy.rb
36 | rp5 run-app rp_samples/samples/contributed/jwishy.rb
37 | rp5 create some_new_sketch 640 480 p3d (P3D mode example)
38 | rp5 create some_new_sketch 640 480 --wrap (a class wrapped default sketch)
39 | rp5 watch some_new_sketch.rb
40 |
41 | Everything Else:
42 | http://wiki.github.com/jashkenas/ruby-processing
43 |
44 | EOS
45 |
46 | WIN_PATTERNS = [
47 | /bccwin/i,
48 | /cygwin/i,
49 | /djgpp/i,
50 | /ming/i,
51 | /mswin/i,
52 | /wince/i
53 | ]
54 |
55 | attr_reader :os
56 |
57 | # Start running a ruby-processing sketch from the passed-in arguments
58 | def self.execute
59 | runner = new
60 | runner.parse_options(ARGV)
61 | runner.execute!
62 | end
63 |
64 | # Dispatch central.
65 | def execute!
66 | case @options.action
67 | when 'run' then run(@options.path, @options.args)
68 | when 'run-app' then run_app(@options.path, @options.args)
69 | when 'watch' then watch(@options.path, @options.args)
70 | when 'live' then live(@options.path, @options.args)
71 | when 'create' then create(@options.path, @options.args)
72 | when 'app' then app(@options.path)
73 | when 'setup' then setup(@options.path)
74 | when /-v/ then show_version
75 | when /-h/ then show_help
76 | else
77 | show_help
78 | end
79 | end
80 |
81 | # Parse the command-line options. Keep it simple.
82 | def parse_options(args)
83 | @options = OpenStruct.new
84 | @options.wrap = !args.delete('--wrap').nil?
85 | @options.inner = !args.delete('--inner').nil?
86 | @options.jruby = !args.delete('--jruby').nil?
87 | @options.nojruby = !args.delete('--nojruby').nil?
88 | @options.action = args[0] || nil
89 | @options.path = args[1] || File.basename(Dir.pwd + '.rb')
90 | @options.args = args[2..-1] || []
91 | end
92 |
93 | # Create a fresh Ruby-Processing sketch, with the necessary
94 | # boilerplate filled out.
95 | def create(sketch, args)
96 | require_relative '../ruby-processing/exporters/creator'
97 | return Processing::Inner.new.create!(sketch, args) if @options.inner
98 | return Processing::ClassSketch.new.create!(sketch, args) if @options.wrap
99 | Processing::BasicSketch.new.create!(sketch, args)
100 | end
101 |
102 | # Just simply run a ruby-processing sketch.
103 | def run(sketch, args)
104 | ensure_exists(sketch)
105 | spin_up('run.rb', sketch, args)
106 | end
107 |
108 | def run_app(sketch, args)
109 | ensure_exists(sketch)
110 | spin_up('run_app.rb', sketch, args)
111 | end
112 |
113 | # Run a sketch, keeping an eye on it's file, and reloading
114 | # whenever it changes.
115 | def watch(sketch, args)
116 | ensure_exists(sketch)
117 | spin_up('watch.rb', sketch, args)
118 | end
119 |
120 | # Run a sketch, opening its guts to IRB, letting you play with it.
121 | def live(sketch, args)
122 | ensure_exists(sketch)
123 | spin_up('live.rb', sketch, args)
124 | end
125 |
126 | # Generate a cross-platform application of a given Ruby-Processing sketch.
127 | def app(sketch)
128 | require_relative '../ruby-processing/exporters/application_exporter'
129 | Processing::ApplicationExporter.new.export!(sketch)
130 | end
131 |
132 | def setup(choice)
133 | proc_root = FileTest.exist?("#{ENV['HOME']}/.rp5rc")
134 | case choice
135 | when /check/
136 | check(proc_root, FileTest.exist?("#{RP5_ROOT}/lib/ruby/jruby-complete.jar"))
137 | when /install/
138 | install(proc_root)
139 | when /unpack_samples/
140 | system "cd #{RP5_ROOT}/vendors && rake unpack_samples"
141 | else
142 | puts 'Usage: rp5 setup [check | install | unpack_samples]'
143 | end
144 | end
145 |
146 | def install(root_exist)
147 | system "cd #{RP5_ROOT}/vendors && rake"
148 | return if root_exist
149 | set_processing_root
150 | warn 'PROCESSING_ROOT set optimistically, run check to confirm'
151 | end
152 |
153 | def check(root_exist, installed)
154 | show_version
155 | root = ' PROCESSING_ROOT = Not Set!!!' unless root_exist
156 | root ||= " PROCESSING_ROOT = #{Processing::RP_CONFIG['PROCESSING_ROOT']}"
157 | jruby = Processing::RP_CONFIG['JRUBY']
158 | x_off = Processing::RP_CONFIG['X_OFF']
159 | y_off = Processing::RP_CONFIG['Y_OFF']
160 | puts root
161 | puts " JRUBY = #{jruby}" unless jruby.nil?
162 | puts " X_OFF = #{x_off}" unless x_off.nil?
163 | puts " Y_OFF = #{y_off}" unless y_off.nil?
164 | puts " jruby-complete installed = #{installed}"
165 | end
166 |
167 | # Display the current version of Ruby-Processing.
168 | def show_version
169 | puts "Ruby-Processing version #{RubyProcessing::VERSION}"
170 | end
171 |
172 | # Show the standard help/usage message.
173 | def show_help
174 | puts HELP_MESSAGE
175 | end
176 |
177 | private
178 |
179 | # Trade in this Ruby instance for a JRuby instance, loading in a starter
180 | # script and passing it some arguments.Unless '--nojruby' is passed, the
181 | # installed version of jruby is used instead of our vendored jarred one
182 | # (which is required for some sketches eg shaders and for export). To use
183 | # jruby-complete by default set JRUBY: false in ~/.rp5rc config
184 | # (but that will make using other gems in your sketches hard....)
185 | def spin_up(starter_script, sketch, args)
186 | runner = "#{RP5_ROOT}/lib/ruby-processing/runners/#{starter_script}"
187 | warn('The --jruby flag is no longer required') if @options.jruby
188 | @options.nojruby = true if Processing::RP_CONFIG['JRUBY'] == 'false'
189 | java_args = discover_java_args(sketch)
190 | if @options.nojruby
191 | command = ['java',
192 | java_args,
193 | '-cp',
194 | jruby_complete,
195 | 'org.jruby.Main',
196 | runner,
197 | sketch,
198 | args].flatten
199 | else
200 | command = ['jruby',
201 | java_args,
202 | runner,
203 | sketch,
204 | args].flatten
205 | end
206 | exec(*command)
207 | # exec replaces the Ruby process with the JRuby one.
208 | end
209 |
210 | # If you need to pass in arguments to Java, such as the ones on this page:
211 | # http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/java.html
212 | # add them to a java_args.txt in your data directory next to your sketch.
213 | def discover_java_args(sketch)
214 | arg_file = "#{File.dirname(sketch)}/data/java_args.txt"
215 | args = []
216 | args += dock_icon
217 | if FileTest.exist?(arg_file)
218 | args += File.read(arg_file).split(/\s+/)
219 | elsif Processing::RP_CONFIG['java_args']
220 | args += Processing::RP_CONFIG['java_args'].split(/\s+/)
221 | end
222 | args.map! { |arg| "-J#{arg}" } unless @options.nojruby
223 | args
224 | end
225 |
226 | # NB: we really do require 'and' not '&&' to get message returned
227 |
228 | def ensure_exists(sketch)
229 | puts "Couldn't find: #{sketch}" and exit unless FileTest.exist?(sketch)
230 | end
231 |
232 | def jruby_complete
233 | rcomplete = File.join(RP5_ROOT, 'lib/ruby/jruby-complete.jar')
234 | return rcomplete if FileTest.exist?(rcomplete)
235 | warn "#{rcomplete} does not exist\nTry running `rp5 setup install`"
236 | exit
237 | end
238 |
239 | def host_os
240 | detect_os = RbConfig::CONFIG['host_os']
241 | case detect_os
242 | when /mac|darwin/ then :mac
243 | when /linux/ then :linux
244 | when /solaris|bsd/ then :unix
245 | else
246 | WIN_PATTERNS.find { |r| detect_os =~ r }
247 | fail "unknown os: #{detect_os.inspect}" if Regexp.last_match.nil?
248 | :windows
249 | end
250 | end
251 |
252 | # Optimistically set processing root
253 | def set_processing_root
254 | require 'psych'
255 | @os ||= host_os
256 | data = {}
257 | path = File.expand_path("#{ENV['HOME']}/.rp5rc")
258 | if os == :mac
259 | data['PROCESSING_ROOT'] = '/Applications/Processing.app/Contents/Java'
260 | else
261 | root = "#{ENV['HOME']}/processing-2.2.1"
262 | data['PROCESSING_ROOT'] = root
263 | end
264 | data['JRUBY'] = true
265 | open(path, 'w:UTF-8') { |f| f.write(data.to_yaml) }
266 | end
267 |
268 | # On the Mac, we can display a fat, shiny ruby in the Dock.
269 | def dock_icon
270 | @os ||= host_os
271 | icon = []
272 | if os == :mac
273 | icon << '-Xdock:name=Ruby-Processing'
274 | icon << "-Xdock:icon=#{RP5_ROOT}/lib/templates/application/Contents/Resources/sketch.icns"
275 | end
276 | icon
277 | end
278 | end # class Runner
279 | end # module Processing
280 |
--------------------------------------------------------------------------------
/test/vecmath_spec_test.rb:
--------------------------------------------------------------------------------
1 | gem 'minitest' # don't use bundled minitest
2 | require 'java'
3 | require 'minitest/autorun'
4 | require 'minitest/pride'
5 |
6 | require_relative '../lib/rpextras'
7 |
8 | Java::MonkstoneVecmathVec2::Vec2Library.load(JRuby.runtime)
9 | Java::MonkstoneVecmathVec3::Vec3Library.load(JRuby.runtime)
10 |
11 | EPSILON = 1.0e-04
12 |
13 | Dir.chdir(File.dirname(__FILE__))
14 |
15 | class VecmathTest < Minitest::Test
16 | def test_equals
17 | x, y = 1.0000001, 1.01
18 | a = Vec2D.new(x, y)
19 | assert_equal(a.to_a, [x, y], 'Failed to return Vec2D as and Array')
20 | end
21 |
22 | def test_not_equals
23 | a = Vec2D.new(3, 5)
24 | b = Vec2D.new(6, 7)
25 | refute_equal(a, b, 'Failed equals false')
26 | end
27 |
28 | def test_copy_equals
29 | x, y = 1.0000001, 1.01
30 | a = Vec2D.new(x, y)
31 | b = a.copy
32 | assert_equal(a.to_a, b.to_a, 'Failed deep copy')
33 | end
34 |
35 | def test_copy_not_equals
36 | x, y = 1.0000001, 1.01
37 | a = Vec2D.new(x, y)
38 | b = a.copy
39 | b *= 0
40 | refute_equal(a.to_a, b.to_a, 'Failed deep copy')
41 | end
42 |
43 | def test_equals_when_close
44 | a = Vec2D.new(3.0000000, 5.00000)
45 | b = Vec2D.new(3.0000000, 5.000001)
46 | assert_equal(a, b, 'Failed to return equal when v. close')
47 | end
48 |
49 | def test_sum
50 | a = Vec2D.new(3, 5)
51 | b = Vec2D.new(6, 7)
52 | c = Vec2D.new(9, 12)
53 | assert_equal(a + b, c, 'Failed to sum vectors')
54 | end
55 |
56 | def test_subtract
57 | a = Vec2D.new(3, 5)
58 | b = Vec2D.new(6, 7)
59 | c = Vec2D.new(-3, -2)
60 | assert_equal(a - b, c, 'Failed to subtract vectors')
61 | end
62 |
63 | def test_multiply
64 | a = Vec2D.new(3, 5)
65 | b = 2
66 | c = a * b
67 | d = Vec2D.new(6, 10)
68 | assert_equal(c, d, 'Failed to multiply vector by scalar')
69 | end
70 |
71 | def test_divide
72 | a = Vec2D.new(3, 5)
73 | b = 2
74 | c = Vec2D.new(1.5, 2.5)
75 | d = a / b
76 | assert_equal(c, d, 'Failed to divide vector by scalar')
77 | end
78 |
79 | def test_from_angle
80 | a = Vec2D.from_angle(Math::PI * 0.75)
81 | assert_equal(a, Vec2D.new(-1 * Math.sqrt(0.5), Math.sqrt(0.5)), 'Failed to create vector from angle')
82 | end
83 |
84 | def test_random
85 | a = Vec2D.random
86 | assert a.kind_of? Vec2D
87 | assert_in_delta(a.mag, 1.0, EPSILON)
88 | end
89 |
90 | def test_assign_value
91 | a = Vec2D.new(3, 5)
92 | a.x=23
93 | assert_equal(a.x, 23, 'Failed to assign x value')
94 | end
95 |
96 | def test_mag
97 | a = Vec2D.new(-3, -4)
98 | assert_equal(a.mag, 5, 'Failed to return magnitude of vector')
99 | end
100 |
101 | def test_mag_variant
102 | a = Vec2D.new(3.0, 2)
103 | b = Math.sqrt(3.0**2 + 2**2)
104 | assert_in_delta(a.mag, b, EPSILON, 'Failed to return magnitude of vector')
105 | end
106 |
107 | def test_mag_zero_one
108 | a = Vec2D.new(-1, 0)
109 | assert_equal(a.mag, 1, 'Failed to return magnitude of vector')
110 | end
111 |
112 | def test_dist
113 | a = Vec2D.new(3, 5)
114 | b = Vec2D.new(6, 7)
115 | assert_equal(a.dist(b), Math.sqrt(3.0**2 + 2**2), 'Failed to return distance between two vectors')
116 | end
117 |
118 | def test_lerp
119 | a = Vec2D.new(1, 1)
120 | b = Vec2D.new(3, 3)
121 | assert_equal(a.lerp(b, 0.5), Vec2D.new(2, 2), 'Failed to return lerp between two vectors')
122 | end
123 |
124 | def test_lerp_unclamped
125 | a = Vec2D.new(1, 1)
126 | b = Vec2D.new(3, 3)
127 | assert_equal(a.lerp(b, 5), Vec2D.new(11, 11), 'Failed to return lerp between two vectors')
128 | end
129 |
130 | def test_lerp!
131 | a = Vec2D.new(1, 1)
132 | b = Vec2D.new(3, 3)
133 | a.lerp!(b, 0.5)
134 | assert_equal(a, Vec2D.new(2, 2), 'Failed to return lerp! between two vectors')
135 | end
136 |
137 | def test_lerp_unclamped!
138 | a = Vec2D.new(1, 1)
139 | b = Vec2D.new(3, 3)
140 | a.lerp!(b, 5)
141 | assert_equal(a, Vec2D.new(11, 11), 'Failed to return lerp! between two vectors')
142 | end
143 |
144 | def test_set_mag
145 | a = Vec2D.new(1, 1)
146 | assert_equal(a.set_mag(Math.sqrt(32)), Vec2D.new(4, 4), 'Failed to set_mag vector')
147 | end
148 |
149 | def test_set_mag_block
150 | a = Vec2D.new(1, 1)
151 | assert_equal(a.set_mag(Math.sqrt(32)) { true }, Vec2D.new(4, 4), 'Failed to set_mag_block true vector')
152 | end
153 |
154 | def test_set_mag_block_false
155 | a = Vec2D.new(1, 1)
156 | assert_equal(a.set_mag(Math.sqrt(32)) { false }, Vec2D.new(1, 1), 'Failed to set_mag_block true vector')
157 | end
158 |
159 | def test_plus_assign
160 | a = Vec2D.new(3, 5)
161 | b = Vec2D.new(6, 7)
162 | a += b
163 | assert_equal(a, Vec2D.new(9, 12), 'Failed to += assign')
164 | end
165 |
166 | def test_normalize
167 | a = Vec2D.new(3, 5)
168 | b = a.normalize
169 | assert_in_delta(b.mag, 1, EPSILON, 'Failed to return a normalized vector')
170 | end
171 |
172 | def test_normalize!
173 | a = Vec2D.new(3, 5)
174 | a.normalize!
175 | assert_in_delta(a.mag, 1, EPSILON, 'Failed to return a normalized! vector')
176 | end
177 |
178 | def test_heading
179 | a = Vec2D.new(1, 1)
180 | assert_in_delta(a.heading, Math::PI / 4.0, EPSILON, 'Failed to return heading in radians')
181 | end
182 |
183 | def test_rotate
184 | x, y = 20, 10
185 | b = Vec2D.new(x, y)
186 | a = b.rotate(Math::PI / 2)
187 | assert_equal(a, Vec2D.new(-10, 20), 'Failed to rotate vector by scalar radians')
188 | end
189 |
190 |
191 | def test_inspect
192 | a = Vec2D.new(3, 2.000000000000001)
193 | assert_equal(a.inspect, 'Vec2D(x = 3.0000, y = 2.0000)')
194 | end
195 |
196 | def test_array_reduce
197 | array = [Vec2D.new(1, 2), Vec2D.new(10, 2), Vec2D.new(1, 2)]
198 | sum = array.reduce(Vec2D.new) { |c, d| c + d }
199 | assert_equal(sum, Vec2D.new(12, 6))
200 | end
201 |
202 | def test_array_zip
203 | one = [Vec2D.new(1, 2), Vec2D.new(10, 2), Vec2D.new(1, 2)]
204 | two = [Vec2D.new(1, 2), Vec2D.new(10, 2), Vec2D.new(1, 2)]
205 | zipped = one.zip(two).flatten
206 | expected = [Vec2D.new(1, 2), Vec2D.new(1, 2), Vec2D.new(10, 2), Vec2D.new(10, 2), Vec2D.new(1, 2), Vec2D.new(1, 2)]
207 | assert_equal(zipped, expected)
208 | end
209 |
210 | def test_equals
211 | x, y, z = 1.0000001, 1.01, 0.0
212 | a = Vec3D.new(x, y)
213 | assert_equal(a.to_a, [x, y, z], 'Failed to return Vec3D as and Array')
214 | end
215 |
216 | def test_not_equals
217 | a = Vec3D.new(3, 5, 1)
218 | b = Vec3D.new(6, 7, 1)
219 | refute_equal(a, b, 'Failed equals false')
220 | end
221 |
222 | def test_copy_equals
223 | x, y, z = 1.0000001, 1.01, 1
224 | a = Vec3D.new(x, y, z)
225 | b = a.copy
226 | assert_equal(a.to_a, b.to_a, 'Failed deep copy')
227 | end
228 |
229 | def test_copy_not_equals
230 | x, y, z = 1.0000001, 1.01, 6.0
231 | a = Vec3D.new(x, y, z)
232 | b = a.copy
233 | b *= 0
234 | refute_equal(a.to_a, b.to_a, 'Failed deep copy')
235 | end
236 |
237 | def test_equals_when_close
238 | a = Vec3D.new(3.0000000, 5.00000, 2)
239 | b = Vec3D.new(3.0000000, 5.000001, 2)
240 | assert_equal(a, b, 'Failed to return equal when v. close')
241 | end
242 |
243 | def test_sum
244 | a = Vec3D.new(3, 5, 1)
245 | b = Vec3D.new(6, 7, 1)
246 | c = Vec3D.new(9, 12, 2)
247 | assert_equal(a + b, c, 'Failed to sum vectors')
248 | end
249 |
250 | def test_subtract
251 | a = Vec3D.new(3, 5, 0)
252 | b = Vec3D.new(6, 7, 1)
253 | c = Vec3D.new(-3, -2, -1)
254 | assert_equal(a - b, c, 'Failed to subtract vectors')
255 | end
256 |
257 | def test_multiply
258 | a = Vec3D.new(3, 5, 1)
259 | b = 2
260 | c = a * b
261 | d = Vec3D.new(6, 10, 2)
262 | assert_equal(c, d, 'Failed to multiply vector by scalar')
263 | end
264 |
265 | def test_divide
266 | a = Vec3D.new(3, 5, 4)
267 | b = 2
268 | c = Vec3D.new(1.5, 2.5, 2)
269 | d = a / b
270 | assert_equal(c, d, 'Failed to divide vector by scalar')
271 | end
272 |
273 | def test_random
274 | a = Vec3D.random
275 | assert a.kind_of? Vec3D
276 | assert_in_delta(a.mag, 1.0, EPSILON)
277 | end
278 |
279 | def test_assign_value
280 | a = Vec3D.new(3, 5)
281 | a.x=23
282 | assert_equal(a.x, 23, 'Failed to assign x value')
283 | end
284 |
285 | def test_mag
286 | a = Vec3D.new(-3, -4)
287 | assert_equal(a.mag, 5, 'Failed to return magnitude of vector')
288 | end
289 |
290 | def test_mag_variant
291 | a = Vec3D.new(3.0, 2)
292 | b = Math.sqrt(3.0**2 + 2**2)
293 | assert_in_delta(a.mag, b, EPSILON, 'Failed to return magnitude of vector')
294 | end
295 |
296 | def test_mag_zero_one
297 | a = Vec3D.new(-1, 0)
298 | assert_equal(a.mag, 1, 'Failed to return magnitude of vector')
299 | end
300 |
301 | def test_dist
302 | a = Vec3D.new(3, 5, 2)
303 | b = Vec3D.new(6, 7, 1)
304 | message = 'Failed to return distance between two vectors'
305 | assert_equal(a.dist(b), Math.sqrt(3.0**2 + 2**2 + 1), message)
306 | end
307 |
308 | def test_dist_squared
309 | a = Vec3D.new(3, 5, 2)
310 | b = Vec3D.new(6, 7, 1)
311 | message = 'Failed to return distance squared between two vectors'
312 | assert_equal(a.dist_squared(b), 3.0**2 + 2**2 + 1, message)
313 | end
314 |
315 | def test_dot
316 | a = Vec3D.new(10, 20, 0)
317 | b = Vec3D.new(60, 80, 0)
318 | assert_equal(a.dot(b), 2200.0, 'Failed to dot product')
319 | end
320 |
321 | def test_cross
322 | a = Vec3D.new(3, 5, 2)
323 | b = Vec3D.new(6, 7, 1)
324 | c = Vec3D.new(-9.0, 9.0, -9.0)
325 | assert_equal(a.cross(b), c, 'Failed cross product')
326 | end
327 |
328 | def test_set_mag
329 | a = Vec3D.new(1, 1)
330 | assert_equal(a.set_mag(Math.sqrt(32)), Vec3D.new(4, 4), 'Failed to set_mag vector')
331 | end
332 |
333 | def test_set_mag_block
334 | a = Vec3D.new(1, 1)
335 | assert_equal(a.set_mag(Math.sqrt(32)) { true }, Vec3D.new(4, 4), 'Failed to set_mag_block true vector')
336 | end
337 |
338 | def test_set_mag_block_false
339 | a = Vec3D.new(1, 1)
340 | assert_equal(a.set_mag(Math.sqrt(32)) { false }, Vec3D.new(1, 1), 'Failed to set_mag_block true vector')
341 | end
342 |
343 | def test_plus_assign
344 | a = Vec3D.new(3, 5)
345 | b = Vec3D.new(6, 7)
346 | a += b
347 | assert_equal(a, Vec3D.new(9, 12), 'Failed to += assign')
348 | end
349 |
350 | def test_normalize
351 | a = Vec3D.new(3, 5)
352 | b = a.normalize
353 | assert_in_delta(b.mag, 1, EPSILON, 'Failed to return a normalized vector')
354 | end
355 |
356 | def test_normalize!
357 | a = Vec3D.new(3, 5)
358 | a.normalize!
359 | assert_in_delta(a.mag, 1, EPSILON, 'Failed to return a normalized! vector')
360 | end
361 |
362 | def test_inspect
363 | a = Vec3D.new(3, 2.000000000000001, 1)
364 | assert_equal(a.inspect, 'Vec3D(x = 3.0000, y = 2.0000, z = 1.0000)')
365 | end
366 |
367 | def test_array_reduce
368 | array = [Vec3D.new(1, 2), Vec3D.new(10, 2), Vec3D.new(1, 2)]
369 | sum = array.reduce(Vec3D.new) { |c, d| c + d }
370 | assert_equal(sum, Vec3D.new(12, 6))
371 | end
372 |
373 | def test_array_zip
374 | one = [Vec3D.new(1, 2), Vec3D.new(10, 2), Vec3D.new(1, 2)]
375 | two = [Vec3D.new(1, 2), Vec3D.new(10, 2), Vec3D.new(1, 2)]
376 | zipped = one.zip(two).flatten
377 | expected = [Vec3D.new(1, 2), Vec3D.new(1, 2), Vec3D.new(10, 2), Vec3D.new(10, 2), Vec3D.new(1, 2), Vec3D.new(1, 2)]
378 | assert_equal(zipped, expected)
379 | end
380 |
381 | def test_eql?
382 | a = Vec3D.new(3.0, 5.0, 0)
383 | b = Vec3D.new(3.0, 5.0, 0)
384 | assert(a.eql?(b))
385 | end
386 |
387 | def test_not_eql?
388 | a = Vec3D.new(3.0, 5.0, 0)
389 | b = Vec3D.new(3.0, 5.000001, 0)
390 | refute(a.eql?(b))
391 | end
392 |
393 | def test_equal?
394 | a = Vec3D.new(3.0, 5.0, 0)
395 | assert(a.equal?(a))
396 | end
397 |
398 | def test_not_equal?
399 | a = Vec3D.new(3.0, 5.0, 0)
400 | b = Vec3D.new(3.0, 5.0, 0)
401 | refute(a.equal?(b))
402 | end
403 | end
404 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | v2.7.1 update to jruby-complete-1.7.26.
2 |
3 | v2.7.0 bump version in recognition of the 'new' run-app option see wiki Getting-Started
4 |
5 |
6 | v2.6.18 update to jruby-complete-1.7.25, to allow travis testing, anyone wishing to update to jruby-complete-9.0.5.0+ should try out `propane`.
7 |
8 | v2.6.17 move to a polyglot maven build and update to jruby-complete-1.7.24, to allow travis testing, should be easy to update to jruby-complete-9.0+ if required. Updating processing version is unecessary because JRubyArt does that.
9 |
10 | v2.6.16 update to jruby-complete-1.7.23 changed to static load for jruby extensions implement Vec2D.random and Vec3D.random
11 | * get rid of rspec as development requirement (all minitest now) this is expected to be the last release of ruby-processing
12 |
13 | v2.6.15 added guard against running 'watch' in top level directories, rescue error
14 | * when processing-3.0 is specified, and suggest updating to JRubyArt update to
15 | * jruby-complete-1.7.22 (this may be the final release of 1.7.XX series)
16 | * features deprecation of processing map in favor of p5map (or map1d) which
17 | * are now both jruby extensions, along with lerp and norm
18 |
19 | v2.6.14 revert to using 'require to load jars' because everything just worked
20 | * before, and sometimes just doesn't with java classpath loading.
21 | * jruby-9.0.0.0 has been fixed, so now we don't need compability changes...
22 | * use pry-java for live mode
23 |
24 | v2.6.13 update to jruby-complete-1.7.21
25 | * the perfomance of 1.7.XX series still exceeds jruby-9.0.0.0
26 |
27 | v2.6.12 Changes to make jruby and PApplet use same classloader
28 | * to make ruby-processing compatible with JRuby-9.0.0.0.rc1+
29 | * getting ready for JRuby-9.0.0.0 release
30 |
31 | v2.6.11 Update examples sketches to version 1.6
32 | * Enhanced 'watch' mode now monitors '*.glsl' files...
33 | * Two new libraries to make it possible to use java reflection methods
34 | * video_event library makes possible use of 'capturEvent(c)' and 'movieEvent(m)'
35 | * library_proxy possible use of 'pre', 'draw' and 'post' in a ruby library.
36 |
37 | v2.6.10 Update to jruby-complete-1.7.20
38 | * Possibly the last 1.7 series releases prior to jruby-9.0.0.0?
39 |
40 | v2.6.9 Fix wrapped sketches (no-one complained, yet old hands prefer them?)
41 | * simplified the loading of files to monitor, in watch mode
42 |
43 | v2.6.8 Some more refactoring, mainly tidying up library_loader/app interface.
44 | * changes for Vec2D and Vec3D, added eql? (exact match) and dup (alias copy)
45 |
46 | v2.6.7 Update to use jruby-complete-1.7.19, replaced monkey patching of
47 | * String, by creating StringExtra and CamelString classes
48 |
49 | v2.6.6 Update to use jruby-complete-1.7.18
50 |
51 | v2.6.5 Update to use jruby-complete-1.7.16.2
52 | * Some more refactoring including helper_methods, and base runner
53 | * No need for erb when running bare sketches. For class wrapped sketches
54 | * make sure to call new, after all the only point in writing a class wrapped
55 | * sketch is to send runtime parameter (:title, :fullscreen, etc)
56 |
57 | v2.6.4 Update to use jruby-complete-1.7.16.1
58 | * Some general refactoring of app.rb, moved examples to
59 | * their own github repo.
60 |
61 | v2.6.3 Update to use jruby-complete-1.7.16
62 | * Avoid Vec2D hypot NaNN with guards, correct
63 | * --inner template, some more refactoring, full_screen is now only
64 | * available via a runtime arg (there is only one way to do it..)
65 | * now adding Range.clip (like numpy.clip) which is used to implement
66 | * processing constrain. Sometimes it will be better to use clip directly.
67 |
68 | v2.6.2 Update to use jruby-complete-1.7.15
69 | * X and Y screen offset for sketch, can be set in ~/.rp5rc
70 | * A post initialization hook has been included to allow custom parameter setting
71 | * More deprecated methods have been removed anyone wanting pow(x), sq(x),
72 | * radians(x) can easily make their own if they must. Prefer x**2, x * x and
73 | * x.radians in ruby-processing etc..
74 |
75 | v2.6.1 The templates for the improved sketch creator, broke app export
76 | * fix means templates are now hard coded (functionality retained)
77 | * update to use jruby-complete-1.7.14.
78 |
79 | v2.6.0 Removal of eval hack, and deprecating / removing processing
80 | * convenience methods. New improved sketch creator, added templates
81 | * class wrapped --wrap, and "inner class mixin" --inner.
82 |
83 | v2.5.1 Some fixes, and remove eval hack from base_exporter
84 | * Since this release some processing convenience methods are
85 | * deprecated see wiki (includes second, minute, hour in favor
86 | * of ruby alternatives t = Time.now, t.hour, t.sec, t.min
87 | * and theta.radians should be used instead of radians(theta))
88 | * These changes will be seen in 2.6.0 release, with removal of
89 | * yet another eval hack.
90 |
91 | v2.5.0 Some refactoring, and a new install procedure
92 | * Some re-factoring of both code and examples, including
93 | * replacing the overuse of __FILE__ with 'relative_require'
94 | * 'install_jruby_complete', replaced by 'rp5 setup install'
95 | * The vecmath library is now a compiled jruby extension
96 | * which includes an extremely simple ArcBall interface.
97 | * Introducing the fastmath library with DegLut tables for cos/sin.
98 | * Update to JRuby-1.7.13
99 |
100 | v2.4.4 Update to JRuby-1.7.12
101 | * Enhancement to Vec2D & Vec3D (preferred to PVector as
102 | * providing a more ruby-like interface), now provide a
103 | * conditional set_mag method, via optional &block.
104 |
105 | v2.4.3 Update to JRuby-1.7.11
106 | * Added an autorun demo Rakefile to some sample directories
107 | * Support utf-8 in sketches
108 | * Refactor and extend vecmath (updated drawolver to use Vec3D)
109 |
110 | v2.4.2 Update to JRuby-1.7.10
111 | * Revised suggestions for PROCESSING_ROOT on MacOSX
112 |
113 | v2.4.1 First release to return to rubygems since processing 1.0.11
114 | * Features a post-install download of jruby-complete (version 1.7.9)
115 | * Features use of jars from an installed version of vanilla processing,
116 | * on linux and windows use version 2.1.0 (later versions may also work).
117 | * For Mac, especially if you are using Mac "java" stick with version 2.0.3
118 | * Update gemspec to match modern expectations
119 |
120 | v2.4.0 Returning to rubygems distribution, by not including any jars
121 | * Use jars from an installed version of vanilla processing-2.0.3 (or version 2.1.0 linux and windows)
122 | * Require an installed jruby (with an optional jruby-complete-1.7.8 post
123 | * install)
124 |
125 | v2.3.1 Revert to processing-2.0.3 for MacOSX
126 | * Mac users may use Apple jvm java 6
127 | * Windows and Linux users need at least java 7 jre (java 8 does now work)
128 |
129 | v2.2.2 Update to JRuby-1.7.6
130 | * Merge vec.rb, quaternion.rb and arcball.rb into vecmath.rb, stricter path
131 | # requires ruby filename to match that of library
132 |
133 | v2.2.1 Replacing 'arcball' library with 'vecmath' library
134 | * Arcball functionality is retained (in vecmath library), Vec2D and Vec3D
135 | * have been added to 'vecmath' library, they provide a pure ruby alternative
136 | * to processings PVector class, and hence a more ruby like interface
137 |
138 |
139 | v2.2.0 Update to JRuby-1.7.5
140 | * Changed app.rb to only java_import used, core classes thus when mode JAVA2D
141 | * ie default mode do not java_import opengl classes, removed event classes
142 | * since we failed to address the directly
143 |
144 | v2.1.6 In anticipation of JRuby-1.7.5
145 | * Minor release to crystalize changes before JRuby-1.7.5
146 | * Rakefile tries to detect Windows OS & warn possibly missing 'wget'
147 | * Rubified and expanded Shiffmans advanced data examples
148 |
149 | v2.1.5 Update to processing-2.0.3
150 | * Minor changes to control_panel
151 | * Introducing file_chooser, deprecate select_input
152 | * Added display_width and display_height aliases
153 |
154 | v2.1.4 Improved build file
155 | * Build corrected to work on systems with directories containing spaces/etc
156 | * Control panel extra feature to allow setting of look and feel
157 |
158 | v2.1.3 Update to processing-2.0.2
159 | * Minor update to samples
160 |
161 | v2.1.2 Moved JRuby-Complete.jar (avoids classpath conflict)
162 | * Change to using external jruby as default, introduce --nojruby
163 | * flag to use provided jruby-complete
164 | * Tests revised to be more compatible with minitest ethos (capture_io)
165 |
166 | v2.1.1 Added Gemfile
167 | * Support bundler usage
168 |
169 | v2.1.0 gc-pruned ruby-processing-2.0
170 | * Since BFG tool was used for archive pruning
171 | * This repo is not compatible with forks of jashkenas prior to this release
172 |
173 | v2.0.1 First minor revision for ruby-processing-2.0
174 | * Changes for application export on Windows and linux
175 | * Added support for 'require_relative' on export
176 |
177 | v2.0.0 A major revision, now based on processing-2.0 and JRuby 1.7+
178 | * Processing updated to processing-2.0.1 export to applet has disappeared, also P3D is
179 | * the new OPENGL (except Jogl2 instead of Jogl1.1) if you've got an old graphics card or even some new netbook
180 | * with onboard graphics you may have issues
181 | * http://forum.processing.org/one/topic/processing-2-0-won-t-run-on-netbooks-and-older-cheaper-hardware.
182 | * Processing-2.0 has its own event system (replacing java.awt.event), ruby-processing sketches will normally
183 | * use this event system.
184 | * JRuby upgraded to 1.7.4 (default is ruby 1.9 and 2.0 is possible with a switch)
185 | * NB: bare sketches replace class wrapped sketches see samples...
186 | * Samples have been extended to include vanilla processing library examples.
187 | * References to the 'online' variable have been removed (deprecated in processing-2.0 slated for removal)
188 | * test suite now uses MiniTest some old tests have been remove. Others that probably will fail anyway, are
189 | * temporarily marked as skip.
190 | * Samples now rely on ruby 1.9 (almost 2.0) and processing-2.0
191 | * Where possible examples have been 'fixed' to run with new version (backward compability is not possible)
192 |
193 | v1.0.11 Fixing broken stuffs...
194 | * JRuby upgraded to 1.6.5
195 | * applet export fixed
196 | * application export fixed
197 |
198 | v1.0.10 Solidifying before Processing 2.0 ...
199 | * JRuby upgraded to 1.6.4
200 | * Processing upgraded to 1.5.1
201 | * load_library now works for Ruby and Java libraries present in the libraries Processing sketchbook
202 | * test suite created
203 | * removed ruby-processing specific hex() and shape() methods in favor of Processing ones
204 | * added some missing methods from Processing: println(), min(), max(), abs(), binary(), nf*(), etc...
205 | * watcher: watch for *.rb files inside sketch directory
206 | * linux opengl bugs fixed
207 | * samples/peasy_cam/hilbert_fractal example now allow the possibility of changing the fractal depth and to more correctly centre the fractal
208 | * added configuration file in $HOME/.rp5rc to configure java_args and sketchbook_path
209 |
210 | v1.0.9 The Yearly Update...
211 | * JRuby upgraded to 1.4.0 final.
212 | * Fix to allow arguments to be passed to sketches.
213 | * Allow "shape" to be called with a block.
214 | * Added new examples, including Monkstone's 3D Anar library and Hilbert curve.
215 |
216 | v1.0.8 Polishing the Windows...
217 | * Windows Application exporting works again, merely by virtue of
218 | not cluttering up the classpath.
219 | * Safer Ruby Platform detection.
220 |
221 | v1.0.7 Stability...
222 | * Added preliminary support for embedding Ruby-Processing in the Processing
223 | IDE (see the ruby-processing-plugin project).
224 | * Added 'width' and 'height' as methods that should get proxied down
225 | to inner classes and classes that include the Processing::Proxy.
226 | * Fixed a padding bug that put tiny gray margins on Windows and Linux.
227 | * Updated JRuby to 1.2.0 final as well as the Processing libraries.
228 | * Got a little bit better at detecting full-screen support on Linux.
229 | * Fixed some applet and app exporting problems on Windows.
230 | * The Boids library had a speed limit fix that should make 'em less flighty.
231 | * Peter Krenn contributed a simple Pong example.
232 |
233 | v1.0.6 Inner Classes...
234 | * Java-style inner classes. Any inner class of a sketch will now have the
235 | Processing methods and constants proxied down for convenience.
236 | * Sketches with tiny sizes get displayed in a nicer fashion.
237 | * New Blue Logo: Ruby-Processing, literally.
238 | * Moumar contributed a patch for the control_panel library, allowing your
239 | sliders and buttons to have an initial value.
240 |
241 | v1.0.5 Spring Cleaning...
242 | * The "Learning Processing" examples are now a separate project, a
243 | long-merited change. They'll grow up on their own at
244 | http://github.com/jashkenas/learning-processing-with-ruby
245 | * The watcher is now a bit better about catching recoverable exceptions.
246 | * load_strings and save_strings methods have been added to Processing::App.
247 | * Fixing a permissions problem with applet/application exporting.
248 |
249 | v1.0.4 Bare is Beautiful...
250 | * Ruby-Processing now supports "bare" sketches, which are sketches that
251 | consist of only setup and draw methods, or sketches that contain no method
252 | definitions at all (implicitly wrapping them in a 'setup'). This works
253 | by pre-processing the code.
254 | * Initialization heavily tweaked so that size() works as in Processing,
255 | from within setup(), and so that you can call full_screen as a class method,
256 | in your class definition, to avoid the need for explicit sketch instantiation.
257 | * "rp5 create" has a "--bare" option.
258 | * Many samples now use the bare style, and more "Learning Processing" examples
259 | were contributed by Juris Galang.
260 |
261 | v1.0.3 Tweaks and Tuneups...
262 | * "rp5 watch" is now a bit more robust, and tries to reload every
263 | * file, global, and constant that it thinks it needs to.
264 | * Many, many examples have been contributed by Marc Chung,
265 | Peter Krenn, and Florian Jenett.
266 | * Andreas Haller contributed a patch that added Ruby-1.9 compatibility.
267 | * The render mode now defaults to JAVA2D, as does Processing.
268 | * "rp5 create" now informs you of the file it just created.
269 | * "key" now returns a character, if ASCII and the integer value otherwise,
270 | mirroring Processing's behavior.
271 | * Numbers now have the methods 'degrees' and 'radians', for ease.
272 |
273 | v1.0.2 Bugfixes and Java Args...
274 | * Application exporting, long plagued, should now be a little
275 | closer to rock-solid. If you need to pass command-line options
276 | to the JVM, add a java_args.txt file in your sketch's data
277 | folder that sets stack size, memory size, or whatever ails you.
278 |
279 | v1.0.1 Gemmin' it up.
280 | * The smallest version bump is the biggest change:
281 | Ruby-Processing has undergone a great refactor, kicked off by
282 | Peter Gassner's initial efforts to make a gem out of it. Now
283 | available as a real RubyGem.
284 |
285 | * Changes all around: The main interface to Ruby-Processing is now
286 | through the 'rp5' command. Try rp5 --help to get started.
287 |
288 | * has_slider has been superseded by control_panel, a more full-
289 | fledged library for controlling aspects of your sketch. Read
290 | how to use it on the wiki, or check out jwishy.rb
291 |
292 | v1.0. Ruby-Processing goes 1.0 with Processing 1.0
293 | * Processing updated to 1.0.1 (congrats to the Processing team),
294 | and JRuby updated to the latest trunk. Most sketches run a good
295 | bit faster now.
296 |
297 | * Ruby-Processing now comes with many default libraries: Boids, DXF,
298 | Javascript, Minim, Net, OpenGL, PDF, Serial, Slider, and Video
299 | are now included in the download.
300 |
301 | * has_slider moved out into an included ruby library.
302 |
303 | v0.9. Multi-platform Application export, live coding, and more.
304 | * Inspired by NodeBox, Ruby-Processing now sports the ability
305 | to have sliders control numeric variables in your sketches.
306 | If you're using an instance variable, say, @speed, to control
307 | the speed of your sketch.
308 |
309 | has_slider :speed
310 |
311 | Will bring up a panel alongside with a slider that controls
312 | the speed. It can take a range of values as an optional parameter.
313 | Check out and run jwishy.rb for an example.
314 |
315 | * Multi-platform app export! Exporting your Ruby-Processing
316 | apps will now create executable apps for Mac/Windows/Linux.
317 |
318 | * Live coding support. Now you can do script/live path/to/sketch.rb
319 | to open up an interactive session with your sketch available
320 | as $app.
321 |
322 | * Nick Sieger donated an additional sample.
323 |
324 | v0.8. Exporting Applications
325 | * Ruby-Processing can now export Mac applications! Running
326 | script/application my_sketch.rb will create MySketch.app,
327 | complete with all of its data and libraries. If you have
328 | a .icns file inside of your data folder, it will become
329 | the app's icon.
330 |
331 | * Added a mathematical Fern sample. It's a port of Luis
332 | Correia's java original, with algorithms from Wikipedia.
333 |
334 | * Sketches now have a library_loaded? method, so that you can
335 | check if a library has been started successfully, and
336 | conditionally enable things. (Good for OpenGL.)
337 |
338 | * The Boids library is now about 40% faster. It also comes with
339 | an example in library/boids/samples.
340 |
341 | * Specs have been started both for exporting and for Ruby-
342 | Processing itself.
343 |
344 | v0.7. Flocking Boids and OpenGL Applets
345 | * Thanks to MenTaLguY, once again, for work on the JRubyApplet, OpenGL
346 | is now a first-class citizen. If you're using OpenGL in your sketch,
347 | the applet exporter should just work. It has also been moved and
348 | renamed, so now you can use it like:
349 |
350 | script/applet my_sketch.rb
351 |
352 | * An app generator has been added for getting started. It'll give you
353 | a template for an empty Ruby-Processing sketch, with setup and draw
354 | methods and all that. Usage:
355 |
356 | script/generate my_sketch 800 600
357 |
358 | Will create a file called my_sketch.rb, with a title of "My Sketch",
359 | 800 pixels wide and 600 pixels tall. Width and height are optional.
360 |
361 | * Ruby-Processing now includes its first pure-Ruby library, a port
362 | of Tom de Smedt's "Boids", for algorithmic flocking.
363 |
364 | v0.6. Generating Applets
365 | * Now we're baking up some applet pie. The applet_tree script will
366 | take your Ruby-Processing sketch, export it as an applet, and
367 | generate an HTML page for you to post. It's way easier now than it
368 | would have been before. (thanks to MenTaLguY.) Use it like so:
369 |
370 | ./applet_tree my_sketch.rb
371 |
372 | But there are caveats: Applets don't work with native libraries, so
373 | no OpenGL. If you're requiring other files that aren't part of the
374 | standard Ruby distro, you'll need to include them as libraries, which
375 | means: Drop them in a folder inside of "library". Use
376 | load_ruby_library("folder_name") or load_java_library() to load 'em.
377 | These methods replace the previous load_library(). Ruby libs will
378 | load the .rb with the same name as the folder. Java libs will just
379 | load up all of the .jars in the folder.
380 |
381 | Demos — all of the standard samples are available as applets:
382 | http://fiercefrontiers.com/applets/jwishy/
383 | http://fiercefrontiers.com/applets/tree/
384 | http://fiercefrontiers.com/applets/circle_collision/
385 | http://fiercefrontiers.com/applets/reflection/
386 |
387 |
388 | v0.5. With Native Libraries
389 | * Ruby-Processing gets easy native library support. Now you can take
390 | Processing libraries, drop them in the library folder, and load them
391 | up like so (inside your sketch):
392 |
393 | load_library "opengl"
394 |
395 | It works by loading up all of the .jars in that folder, and setting
396 | the java.library.path to that folder, so that the native extensions
397 | can be found.
398 |
399 | * Full Screen OpenGL demo added, but you'll need to copy over the
400 | OpenGL library to use it.
401 |
402 | v0.4. Going Fullscreen
403 | * Ruby-Processing goes fullscreen. Just pass :full_screen => true
404 | into the options when you’re starting up your app. Like so:
405 |
406 | MyApp.new(:title => "MyApp", :full_screen => true)
407 |
408 | * Because Processing has just so many methods, you can now search
409 | through them: find_method "method_name"
410 |
411 | v0.3. First Real Release
412 | * Processing::App.current will give you a handle on the app. (Useful
413 | in jirb).
414 | * samples/jwishy.rb has some new hooks for live coding.
415 | * circle_collision and tree samples added (Joe Holt)
416 |
--------------------------------------------------------------------------------
/src/monkstone/vecmath/vec2/Vec2.java:
--------------------------------------------------------------------------------
1 | package monkstone.vecmath.vec2;
2 | /*
3 | * Copyright (C) 2015-16 Martin Prout
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * http://creativecommons.org/licenses/LGPL/2.1/
11 | *
12 | * This library is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | * Lesser General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public
18 | * License along with this library; if not, write to the Free Software
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 | */
21 |
22 | import org.jruby.Ruby;
23 | import org.jruby.RubyArray;
24 | import org.jruby.RubyBoolean;
25 | import org.jruby.RubyClass;
26 | import org.jruby.RubyObject;
27 | import org.jruby.anno.JRubyClass;
28 | import org.jruby.anno.JRubyMethod;
29 | import org.jruby.runtime.Arity;
30 | import org.jruby.runtime.Block;
31 | import org.jruby.runtime.ObjectAllocator;
32 | import org.jruby.runtime.ThreadContext;
33 | import org.jruby.runtime.builtin.IRubyObject;
34 | import monkstone.vecmath.JRender;
35 |
36 | /**
37 | *
38 | * @author Martin Prout
39 | */
40 | @JRubyClass(name = "Vec2D")
41 | public class Vec2 extends RubyObject {
42 |
43 | static final double EPSILON = 9.999999747378752e-05; // matches processing.org EPSILON
44 | private static final long serialVersionUID = -7013225882277559392L;
45 | private double jx = 0;
46 | private double jy = 0;
47 |
48 | /**
49 | *
50 | * @param runtime
51 | */
52 | public static void createVec2(final Ruby runtime) {
53 | RubyClass vec2Cls = runtime.defineClass("Vec2D", runtime.getObject(), new ObjectAllocator() {
54 | @Override
55 | public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
56 | return new Vec2(runtime, rubyClass);
57 | }
58 | });
59 | vec2Cls.defineAnnotatedMethods(Vec2.class);
60 |
61 | }
62 |
63 | /**
64 | *
65 | * @param context
66 | * @param klazz
67 | * @param args optional (no args jx = 0, jy = 0)
68 | * @return new Vec2 object (ruby)
69 | */
70 | @JRubyMethod(name = "new", meta = true, rest = true)
71 | public static final IRubyObject rbNew(ThreadContext context, IRubyObject klazz, IRubyObject[] args) {
72 | Vec2 vec2 = (Vec2) ((RubyClass) klazz).allocate();
73 | vec2.init(context, args);
74 | return vec2;
75 | }
76 |
77 | /**
78 | *
79 | * @param runtime
80 | * @param klass
81 | */
82 | public Vec2(Ruby runtime, RubyClass klass) {
83 | super(runtime, klass);
84 | }
85 |
86 | void init(ThreadContext context, IRubyObject[] args) {
87 | if (Arity.checkArgumentCount(context.getRuntime(), args, Arity.OPTIONAL.getValue(), 2) == 2) {
88 | jx = (Double) args[0].toJava(Double.class);
89 | jy = (Double) args[1].toJava(Double.class);
90 | }
91 | }
92 |
93 | /**
94 | *
95 | * @param context
96 | * @return jx float
97 | */
98 | @JRubyMethod(name = "x")
99 |
100 | public IRubyObject getX(ThreadContext context) {
101 | return context.getRuntime().newFloat(jx);
102 | }
103 |
104 | /**
105 | *
106 | * @param context
107 | * @return jy float
108 | */
109 | @JRubyMethod(name = "y")
110 |
111 | public IRubyObject getY(ThreadContext context) {
112 | return context.getRuntime().newFloat(jy);
113 | }
114 |
115 | /**
116 | *
117 | * @param context
118 | * @param other
119 | * @return jx float
120 | */
121 | @JRubyMethod(name = "x=")
122 |
123 | public IRubyObject setX(ThreadContext context, IRubyObject other) {
124 | jx = (Double) other.toJava(Double.class);
125 | return other;
126 | }
127 |
128 | /**
129 | *
130 | * @param context
131 | * @param other
132 | * @return jy float
133 | */
134 | @JRubyMethod(name = "y=")
135 |
136 | public IRubyObject setY(ThreadContext context, IRubyObject other) {
137 | jy = (Double) other.toJava(Double.class);
138 | return other;
139 | }
140 |
141 | /**
142 | *
143 | * @param context
144 | * @param other
145 | * @return hypotenuse float
146 | */
147 | @JRubyMethod(name = "dist", required = 1)
148 |
149 | public IRubyObject dist(ThreadContext context, IRubyObject other) {
150 | Vec2 b = null;
151 | Ruby runtime = context.getRuntime();
152 | if (other instanceof Vec2) {
153 | b = (Vec2) other.toJava(Vec2.class);
154 | } else {
155 | throw runtime.newTypeError("argument should be Vec2D");
156 | }
157 | double result = Math.hypot((jx - b.jx), (jy - b.jy));
158 | return runtime.newFloat(result);
159 | }
160 |
161 | /**
162 | *
163 | * @param context
164 | * @param other
165 | * @return cross product as a new Vec3D
166 | */
167 | @JRubyMethod(name = "cross", required = 1)
168 |
169 | public IRubyObject cross(ThreadContext context, IRubyObject other) {
170 | Vec2 b = null;
171 | Ruby runtime = context.getRuntime();
172 | if (other instanceof Vec2) {
173 | b = (Vec2) other.toJava(Vec2.class);
174 | } else {
175 | throw runtime.newTypeError("argument should be Vec2D");
176 | }
177 | return runtime.newFloat(jx * b.jy - jy * b.jx);
178 | }
179 |
180 | /**
181 | *
182 | * @param context
183 | * @param other
184 | * @return do product as a float
185 | */
186 | @JRubyMethod(name = "dot", required = 1)
187 |
188 | public IRubyObject dot(ThreadContext context, IRubyObject other) {
189 | Vec2 b = null;
190 | Ruby runtime = context.getRuntime();
191 | if (other instanceof Vec2) {
192 | b = (Vec2) other.toJava(Vec2.class);
193 | } else {
194 | throw runtime.newTypeError("argument should be Vec2D");
195 | }
196 | return runtime.newFloat(jx * b.jx + jy * b.jy);
197 | }
198 |
199 | /**
200 | *
201 | * @param context
202 | * @param other
203 | * @return new Vec2 object (ruby)
204 | */
205 | @JRubyMethod(name = "+", required = 1)
206 |
207 | public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
208 | Vec2 b = null;
209 | Ruby runtime = context.getRuntime();
210 | if (other instanceof Vec2) {
211 | b = (Vec2) other.toJava(Vec2.class);
212 | } else {
213 | throw runtime.newTypeError("argument should be Vec2D");
214 | }
215 | return Vec2.rbNew(context, other.getMetaClass(), new IRubyObject[]{
216 | runtime.newFloat(jx + b.jx),
217 | runtime.newFloat(jy + b.jy)});
218 | }
219 |
220 | /**
221 | *
222 | * @param context
223 | * @param other
224 | * @return new Vec2 object (ruby)
225 | */
226 | @JRubyMethod(name = "-", required = 1)
227 |
228 | public IRubyObject op_minus(ThreadContext context, IRubyObject other) {
229 | Vec2 b = null;
230 | Ruby runtime = context.getRuntime();
231 | if (other instanceof Vec2) {
232 | b = (Vec2) other.toJava(Vec2.class);
233 | } else {
234 | throw runtime.newTypeError("argument should be Vec2D");
235 | }
236 | return Vec2.rbNew(context, other.getMetaClass(), new IRubyObject[]{
237 | runtime.newFloat(jx - b.jx),
238 | runtime.newFloat(jy - b.jy)});
239 | }
240 |
241 | /**
242 | *
243 | * @param context
244 | * @param other
245 | * @return new Vec2 object (ruby)
246 | */
247 | @JRubyMethod(name = "*")
248 |
249 | public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
250 | Ruby runtime = context.getRuntime();
251 | double scalar = (Double) other.toJava(Double.class);
252 | return Vec2.rbNew(context, this.getMetaClass(),
253 | new IRubyObject[]{runtime.newFloat(jx * scalar),
254 | runtime.newFloat(jy * scalar)});
255 | }
256 |
257 | /**
258 | *
259 | * @param context
260 | * @param other
261 | * @return new Vec2 object (ruby)
262 | */
263 | @JRubyMethod(name = "/", required = 1)
264 |
265 | public IRubyObject op_div(ThreadContext context, IRubyObject other) {
266 | Ruby runtime = context.getRuntime();
267 | double scalar = (Double) other.toJava(Double.class);
268 | if (Math.abs(scalar) < Vec2.EPSILON) {
269 | return this;
270 | }
271 | return Vec2.rbNew(context, this.getMetaClass(), new IRubyObject[]{
272 | runtime.newFloat(jx / scalar),
273 | runtime.newFloat(jy / scalar)});
274 | }
275 |
276 | /**
277 | *
278 | * @param context
279 | * @return angle radians as a float
280 | */
281 | @JRubyMethod(name = "heading")
282 | public IRubyObject heading(ThreadContext context) {
283 | return context.getRuntime().newFloat(Math.atan2(jy, jx));
284 | }
285 |
286 | /**
287 | *
288 | * @param context
289 | * @return magnitude float
290 | */
291 | @JRubyMethod(name = "mag")
292 |
293 | public IRubyObject mag(ThreadContext context) {
294 | double result = 0;
295 | if (Math.abs(jx) > EPSILON && Math.abs(jy) > EPSILON) {
296 | result = Math.hypot(jx, jy);
297 | }
298 | else{
299 | if (Math.abs(jy) > EPSILON) {
300 | result = Math.abs(jy);
301 | }
302 | if (Math.abs(jx) > EPSILON) {
303 | result = Math.abs(jx);
304 | }
305 | }
306 | return context.getRuntime().newFloat(result);
307 | }
308 |
309 | /**
310 | * Call yield if block given, do nothing if yield == false else set_mag to
311 | * given scalar
312 | *
313 | * @param context
314 | * @param scalar double value to set
315 | * @param block should return a boolean (optional)
316 | * @return this Vec2D with the new magnitude
317 | */
318 | @JRubyMethod(name = "set_mag")
319 |
320 | public IRubyObject set_mag(ThreadContext context, IRubyObject scalar, Block block) {
321 | double new_mag = (Double) scalar.toJava(Double.class);
322 | if (block.isGiven()) {
323 | if (!(boolean) block.yield(context, scalar).toJava(Boolean.class)) {
324 | return this;
325 | }
326 | }
327 | double current = 0;
328 | if (Math.abs(jx) > EPSILON && Math.abs(jy) > EPSILON) {
329 | current = Math.hypot(jx, jy);
330 | }
331 | else{
332 | if (Math.abs(jy) > EPSILON) {
333 | current = Math.abs(jy);
334 | }
335 | if (Math.abs(jx) > EPSILON) {
336 | current = Math.abs(jx);
337 | }
338 | }
339 | if (current > 0) {
340 | jx *= new_mag / current;
341 | jy *= new_mag / current;
342 | }
343 | return this;
344 | }
345 |
346 | /**
347 | *
348 | * @param context
349 | * @return this as a ruby object
350 | */
351 | @JRubyMethod(name = "normalize!")
352 |
353 | public IRubyObject normalize_bang(ThreadContext context) {
354 | double mag = 0;
355 | if (Math.abs(jx) > EPSILON && Math.abs(jy) > EPSILON) {
356 | mag = Math.hypot(jx, jy);
357 | }
358 | else{
359 | if (Math.abs(jx) > EPSILON) {
360 | mag = Math.abs(jx);
361 | }
362 | if (Math.abs(jy) > EPSILON) {
363 | mag = Math.abs(jy);
364 | }
365 | }
366 | if (mag > 0) {
367 | jx /= mag;
368 | jy /= mag;
369 | }
370 | return this;
371 | }
372 |
373 | /**
374 | *
375 | * @param context
376 | * @return new Vec2 object (ruby)
377 | */
378 | @JRubyMethod(name = "normalize")
379 |
380 | public IRubyObject normalize(ThreadContext context) {
381 | double mag = 0;
382 | Ruby runtime = context.getRuntime();
383 | if (Math.abs(jx) > EPSILON && Math.abs(jy) > EPSILON) {
384 | mag = Math.hypot(jx, jy);
385 | }
386 | else{
387 | if (Math.abs(jx) > EPSILON) {
388 | mag = jx;
389 | }
390 | if (Math.abs(jy) > EPSILON) {
391 | mag = jy;
392 | }
393 | }
394 | if (mag < EPSILON) {
395 | mag = 1.0;
396 | }
397 | return Vec2.rbNew(context, this.getMetaClass(), new IRubyObject[]{
398 | runtime.newFloat(jx / mag),
399 | runtime.newFloat(jy / mag)});
400 | }
401 |
402 | /**
403 | * Example of a regular ruby class method Use Math rather than RadLut
404 | * here!!!
405 | *
406 | * @param context
407 | * @param klazz
408 | * @param other input angle in radians
409 | * @return new Vec2 object (ruby)
410 | */
411 | @JRubyMethod(name = "from_angle", meta = true)
412 | public static IRubyObject from_angle(ThreadContext context, IRubyObject klazz, IRubyObject other) {
413 | Ruby runtime = context.getRuntime();
414 | double scalar = (Double) other.toJava(Double.class);
415 | return Vec2.rbNew(context, klazz, new IRubyObject[]{
416 | runtime.newFloat(Math.cos(scalar)),
417 | runtime.newFloat(Math.sin(scalar))});
418 | }
419 |
420 | /**
421 | *
422 | * @param context
423 | * @param other
424 | * @return this Vec2 object rotated
425 | */
426 | @JRubyMethod(name = "rotate!")
427 | public IRubyObject rotate_bang(ThreadContext context, IRubyObject other) {
428 | double theta = (Double) other.toJava(Double.class);
429 | double x = (jx * Math.cos(theta) - jy * Math.sin(theta));
430 | double y = (jx * Math.sin(theta) + jy * Math.cos(theta));
431 | jx = x;
432 | jy = y;
433 | return this;
434 | }
435 |
436 |
437 |
438 | /**
439 | *
440 | * @param context
441 | * @param other
442 | * @return a new Vec2 object rotated
443 | */
444 | @JRubyMethod(name = "rotate")
445 | public IRubyObject rotate(ThreadContext context, IRubyObject other) {
446 | Ruby runtime = context.getRuntime();
447 | double theta = (Double) other.toJava(Double.class);
448 | IRubyObject[] ary = new IRubyObject[]{
449 | runtime.newFloat(jx * Math.cos(theta) - jy * Math.sin(theta)),
450 | runtime.newFloat(jx * Math.sin(theta) + jy * Math.cos(theta))};
451 | return Vec2.rbNew(context, this.getMetaClass(), ary);
452 | }
453 |
454 | /**
455 | *
456 | * @param context
457 | * @param args
458 | * @return as a new Vec2 object (ruby)
459 | */
460 | @JRubyMethod(name = "lerp", rest = true)
461 | public IRubyObject lerp(ThreadContext context, IRubyObject[] args) {
462 | Ruby runtime = context.getRuntime();
463 | Arity.checkArgumentCount(runtime, args, 2, 2);
464 | Vec2 vec = (Vec2) args[0].toJava(Vec2.class);
465 | double scalar = (Double) args[1].toJava(Double.class);
466 | assert (scalar >= 0 && scalar < 1.0) :
467 | "Lerp value " + scalar + " out of range 0 .. 1.0";
468 | return Vec2.rbNew(context, this.getMetaClass(), new IRubyObject[]{
469 | runtime.newFloat(jx + (vec.jx - jx) * scalar),
470 | runtime.newFloat(jy + (vec.jy - jy) * scalar)});
471 | }
472 |
473 | /**
474 | *
475 | * @param context
476 | * @param args
477 | * @return this
478 | */
479 | @JRubyMethod(name = "lerp!", rest = true)
480 | public IRubyObject lerp_bang(ThreadContext context, IRubyObject[] args) {
481 | Arity.checkArgumentCount(context.getRuntime(), args, 2, 2);
482 | Vec2 vec = (Vec2) args[0].toJava(Vec2.class);
483 | double scalar = (Double) args[1].toJava(Double.class);
484 | assert (scalar >= 0 && scalar < 1.0) :
485 | "Lerp value " + scalar + " out of range 0 .. 1.0";
486 | jx += (vec.jx - jx) * scalar;
487 | jy += (vec.jy - jy) * scalar;
488 | return this;
489 | }
490 |
491 | /**
492 | *
493 | * @param context
494 | * @param other
495 | * @return theta radians float
496 | */
497 | @JRubyMethod(name = "angle_between")
498 |
499 | public IRubyObject angleBetween(ThreadContext context, IRubyObject other) {
500 | Vec2 vec = null;
501 | Ruby runtime = context.getRuntime();
502 | if (other instanceof Vec2) {
503 | vec = (Vec2) other.toJava(Vec2.class);
504 | } else {
505 | throw runtime.newTypeError("argument should be Vec2D");
506 | }
507 | return runtime.newFloat(Math.atan2(jx - vec.jx, jy - vec.jy));
508 | }
509 |
510 | /**
511 | * Example of a regular ruby class method Use Math rather than RadLut
512 | * here!!!
513 | *
514 | * @param context
515 | * @param klazz
516 | * @return new Vec2 object (ruby)
517 | */
518 | @JRubyMethod(name = "random", meta = true)
519 | public static IRubyObject random_direction(ThreadContext context, IRubyObject klazz) {
520 | Ruby runtime = context.getRuntime();
521 | double angle = Math.random() * Math.PI * 2;
522 | return Vec2.rbNew(context, klazz, new IRubyObject[]{
523 | runtime.newFloat(Math.cos(angle)),
524 | runtime.newFloat(Math.sin(angle))});
525 | }
526 |
527 | /**
528 | *
529 | * @param context
530 | * @return new copy
531 | */
532 | @JRubyMethod(name = {"copy", "dup"})
533 |
534 | public IRubyObject copy(ThreadContext context) {
535 | Ruby runtime = context.runtime;
536 | return Vec2.rbNew(context, this.getMetaClass(), new IRubyObject[]{
537 | runtime.newFloat(jx),
538 | runtime.newFloat(jy)});
539 | }
540 |
541 | /**
542 | *
543 | * @param context
544 | * @return ruby array
545 | */
546 | @JRubyMethod(name = "to_a")
547 |
548 | public IRubyObject toArray(ThreadContext context) {
549 | Ruby runtime = context.runtime;
550 | return RubyArray.newArray(context.getRuntime(), new IRubyObject[]{
551 | runtime.newFloat(jx),
552 | runtime.newFloat(jy)});
553 | }
554 |
555 | /**
556 | *
557 | * @param context
558 | * @param object
559 | */
560 | @JRubyMethod(name = "to_vertex")
561 |
562 | public void toVertex(ThreadContext context, IRubyObject object) {
563 | JRender renderer = (JRender) object.toJava(JRender.class);
564 | renderer.vertex(jx, jy);
565 | }
566 |
567 | /**
568 | *
569 | * @param context
570 | * @param object
571 | */
572 | @JRubyMethod(name = "to_curve_vertex")
573 |
574 | public void toCurveVertex(ThreadContext context, IRubyObject object) {
575 | JRender renderer = (JRender) object.toJava(JRender.class);
576 | renderer.curveVertex(jx, jy);
577 | }
578 |
579 |
580 | /**
581 | * For jruby-9000 we alias to inspect
582 | * @param context
583 | * @return custom to string (inspect)
584 | */
585 | @JRubyMethod(name = {"to_s", "inspect"})
586 |
587 | public IRubyObject to_s(ThreadContext context) {
588 | return context.getRuntime().newString(String.format("Vec2D(x = %4.4f, y = %4.4f)", jx, jy));
589 | }
590 |
591 | /**
592 | *
593 | * @return hash int
594 | */
595 | @Override
596 | public int hashCode() {
597 | int hash = 5;
598 | hash = 53 * hash + (int) (Double.doubleToLongBits(this.jx) ^ (Double.doubleToLongBits(this.jx) >>> 32));
599 | hash = 53 * hash + (int) (Double.doubleToLongBits(this.jy) ^ (Double.doubleToLongBits(this.jy) >>> 32));
600 | return hash;
601 | }
602 |
603 | /**
604 | *
605 | * @param obj
606 | * @return ruby boolean
607 | */
608 | @Override
609 | public boolean equals(Object obj) {
610 | if (obj instanceof Vec2){
611 | final Vec2 other = (Vec2) obj;
612 | if (!((Double)this.jx).equals(other.jx)) {
613 | return false;
614 | }
615 | return ((Double)this.jy).equals(other.jy);
616 | }
617 | return false;
618 | }
619 |
620 | /**
621 | *
622 | * @param context
623 | * @param other
624 | * @return ruby boolean
625 | */
626 | @JRubyMethod(name = "eql?", required = 1)
627 | public IRubyObject eql_p(ThreadContext context, IRubyObject other) {
628 | Ruby runtime = context.getRuntime();
629 | if (other instanceof Vec2){
630 | Vec2 v = (Vec2) other.toJava(Vec2.class);
631 | if (!((Double)this.jx).equals(v.jx)) {
632 | return RubyBoolean.newBoolean(runtime, false);
633 | }
634 | return RubyBoolean.newBoolean(runtime, ((Double)this.jy).equals(v.jy));
635 | }
636 | return RubyBoolean.newBoolean(runtime, false);
637 | }
638 |
639 | /**
640 | *
641 | * @param context
642 | * @param other
643 | * @return ruby boolean
644 | */
645 | @JRubyMethod(name = "==", required = 1)
646 |
647 | @Override
648 | public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
649 | Ruby runtime = context.getRuntime();
650 | if (other instanceof Vec2) {
651 | Vec2 v = (Vec2) other.toJava(Vec2.class);
652 | double diff = jx - v.jx;
653 | if ((diff < 0 ? -diff : diff) > Vec2.EPSILON) {
654 | return RubyBoolean.newBoolean(runtime, false);
655 | }
656 | diff = jy - v.jy;
657 | boolean result = ((diff < 0 ? -diff : diff) < Vec2.EPSILON);
658 | return RubyBoolean.newBoolean(runtime, result);
659 | }
660 | return RubyBoolean.newBoolean(runtime, false);
661 | }
662 | }
663 |
--------------------------------------------------------------------------------