Skip to content

Instantly share code, notes, and snippets.

@monkstone
Last active December 24, 2015 12:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monkstone/6795611 to your computer and use it in GitHub Desktop.
Save monkstone/6795611 to your computer and use it in GitHub Desktop.
Alternative app.rb to explicitly import java classes
# version without embedded or online
# This class is a thin wrapper around Processing's PApplet.
# Most of the code here is for interfacing with Swing,
# web applets, going fullscreen and so on.
require 'java'
require 'ruby-processing/config'
require 'ruby-processing/helper_methods'
require 'ruby-processing/library_loader'
Dir["#{RP5_ROOT}/lib/core/\*.jar"].each { |jar| require jar }
# Include some processing classes that we'd like to use:
%w(PApplet PConstants PFont PImage PShape PShapeOBJ PShapeSVG PStyle PGraphicsJava2D PGraphics PFont PVector PMatrix2D PMatrix3D).each do |klass|
java_import "processing.core.#{klass}"
end
%w(Event MouseEvent KeyEvent TouchEvent).each do |klass|
java_import "processing.event.#{klass}"
end
module Processing
# This is the main Ruby-Processing class, and is what you'll
# inherit from when you create a sketch. This class can call
# all of the methods available in Processing, and has two
# mandatory methods, 'setup' and 'draw', both of which you
# should define in your sketch. 'setup' will be called one
# time when the sketch is first loaded, and 'draw' will be
# called constantly, for every frame.
class App < PApplet
include Math
include HelperMethods
# Alias some methods for familiarity for Shoes coders.
#attr_accessor :frame, :title
alias_method :oval, :ellipse
alias_method :stroke_width, :stroke_weight
alias_method :rgb, :color
alias_method :gray, :color
# When certain special methods get added to the sketch, we need to let
# Processing call them by their expected Java names.
def self.method_added(method_name) #:nodoc:
# Watch the definition of these methods, to make sure
# that Processing is able to call them during events.
methods_to_alias = {
mouse_pressed: :mousePressed,
mouse_dragged: :mouseDragged,
mouse_clicked: :mouseClicked,
mouse_moved: :mouseMoved,
mouse_released: :mouseReleased,
key_pressed: :keyPressed,
key_released: :keyReleased,
key_typed: :keyTyped
}
if methods_to_alias.keys.include?(method_name)
alias_method methods_to_alias[method_name], method_name
end
end
# Class methods that we should make available in the instance.
[:map, :pow, :norm, :lerp, :second, :minute, :hour, :day, :month, :year,
:sq, :constrain, :dist, :blend_color, :degrees, :radians, :mag, :println,
:hex, :min, :max, :abs, :binary, :ceil, :nf, :nfc, :nfp, :nfs,
:norm, :round, :trim, :unbinary, :unhex ].each do |meth|
method = <<-EOS
def #{meth}(*args)
self.class.#{meth}(*args)
end
EOS
eval method
end
# Handy getters and setters on the class go here:
def self.sketch_class; @sketch_class; end
@@full_screen = false
def self.full_screen; @@full_screen = true; end
def full_screen?; @@full_screen; end
# Keep track of what inherits from the Processing::App, because we're going
# to want to instantiate one.
def self.inherited(subclass)
super(subclass)
@sketch_class = subclass
end
def self.has_slider(*args) #:nodoc:
raise "has_slider has been replaced with a nicer control_panel library. Check it out."
end
@@library_loader = LibraryLoader.new
class << self
def load_libraries(*args)
@@library_loader.load_library(*args)
end
alias :load_library :load_libraries
def library_loaded?(library_name)
@@library_loader.library_loaded?(library_name)
end
def load_ruby_library(*args)
@@library_loader.load_ruby_library(*args)
end
def load_java_library(*args)
@@library_loader.load_java_library(*args)
end
end
def library_loaded?(library_name)
self.class.library_loaded?(library_name)
end
# When you make a new sketch, you pass in (optionally),
# a width, height, x, y, title, and whether or not you want to
# run in full-screen.
#
# This is a little different than Processing where height
# and width are declared inside the setup method instead.
def initialize(options={})
super()
$app = self
proxy_java_fields
set_sketch_path #unless Processing.online?
mix_proxy_into_inner_classes
#@started = false
java.lang.Thread.default_uncaught_exception_handler = proc do |thread, exception|
puts(exception.class.to_s)
puts(exception.message)
puts(exception.backtrace.collect { |trace| "\t" + trace })
close
end
# for the list of all available args, see
# http://processing.org/reference/
args = []
@width, @height = options[:width], options[:height]
if @@full_screen || options[:full_screen]
@@full_screen = true
args << "--present"
end
@render_mode ||= JAVA2D
x = options[:x] || 0
y = options[:y] || 0
args << "--location=#{x}, #{y}"
title = options[:title] || File.basename(SKETCH_PATH).sub(/(\.rb)$/, '').titleize
args << title
PApplet.run_sketch(args, self)
#end
end
def size(*args)
w, h, mode = *args
@width ||= w unless @width
@height ||= h unless @height
@render_mode ||= mode unless @render_mode
if [P3D, P2D].include? @render_mode
%w(FontTexture FrameBuffer LinePath LineStroker PGL PGraphics2D PGraphics3D PGraphicsOpenGL PShader PShapeOpenGL Texture).each do |klass|
java_import "processing.opengl.#{klass}"
end
end
super(*args)
end
# Make sure we set the size if we set it before we start the animation thread.
def start
self.size(@width, @height) if @width && @height
super()
end
# Provide a loggable string to represent this sketch.
def inspect
"#<Processing::App:#{self.class}:#{@title}>"
end
# Cleanly close and shutter a running sketch.
def close
control_panel.remove if respond_to?(:control_panel)
self.dispose
self.frame.dispose
end
private
# Mix the Processing::Proxy into any inner classes defined for the
# sketch, attempting to mimic the behavior of Java's inner classes.
def mix_proxy_into_inner_classes
klass = Processing::App.sketch_class
klass.constants.each do |name|
const = klass.const_get name
next if const.class != Class || const.to_s.match(/^Java::/)
const.class_eval 'include Processing::Proxy'
end
end
end # Processing::App
# This module will get automatically mixed in to any inner class of
# a Processing::App, in order to mimic Java's inner classes, which have
# unfettered access to the methods defined in the surrounding class.
module Proxy
include Math
# Generate the list of method names that we'd like to proxy for inner classes.
# Nothing camelCased, nothing __internal__, just the Processing API.
def self.desired_method_names(inner_class)
bad_method = /__/ # Internal JRuby methods.
unwanted = PApplet.superclass.instance_methods + Object.instance_methods
unwanted -= ['width', 'height', 'cursor', 'create_image', 'background', 'size', 'resize']
methods = Processing::App.public_instance_methods
methods.reject {|m| unwanted.include?(m) || bad_method.match(m) || inner_class.method_defined?(m) }
end
# Proxy methods through to the sketch.
def self.proxy_methods(inner_class)
code = desired_method_names(inner_class).inject('') do |code, method|
code << <<-EOS
def #{method}(*args, &block) # def rect(*args, &block)
if block_given? # if block_given?
$app.send :'#{method}', *args, &block # $app.send(:rect, *args, &block)
else # else
$app.#{method} *args # $app.rect *args
end # end
end # end
EOS
end
inner_class.class_eval(code)
end
# Proxy the sketch's constants on to the inner classes.
def self.proxy_constants(inner_class)
Processing::App.constants.each do |name|
next if inner_class.const_defined?(name)
inner_class.const_set(name, Processing::App.const_get(name))
end
end
# Don't do all of the work unless we have an inner class that needs it.
def self.included(inner_class)
proxy_methods(inner_class)
proxy_constants(inner_class)
end
end # Processing::Proxy
end # Processing
@monkstone
Copy link
Author

Alternative app.rb, has advantage of only loading opengl classes if required

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment