Created
January 8, 2015 15:35
-
-
Save monkstone/b3ca7a0eb31fcf59069c to your computer and use it in GitHub Desktop.
refinements
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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_relative '../ruby-processing/helper_methods' | |
require_relative '../ruby-processing/helpers/string' | |
require_relative '../ruby-processing/library_loader' | |
require_relative '../ruby-processing/config' | |
Dir["#{Processing::RP_CONFIG['PROCESSING_ROOT']}/core/library/\*.jar"].each do | |
|jar| | |
require jar | |
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. | |
using StringUtil | |
# Include some core processing classes that we'd like to use: | |
include_package 'processing.core' | |
# 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 | |
} | |
# All sketches extend this class | |
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: | |
if METHODS_TO_ALIAS.key?(method_name) | |
alias_method METHODS_TO_ALIAS[method_name], method_name | |
end | |
end | |
# Handy getters and setters on the class go here: | |
class << self | |
attr_accessor :sketch_class | |
end | |
def sketch_class | |
self.class.sketch_class | |
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 | |
@@library_loader = LibraryLoader.new | |
class << self | |
def load_libraries(*args) | |
@@library_loader.load_library(*args) | |
end | |
alias_method :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 | |
# It is 'NOT' usually necessary to directly pass options to a sketch, it | |
# gets done automatically for you. Since processing-2.0 you should prefer | |
# setting the sketch width and height and renderer using the size method, | |
# in the sketch (as with vanilla processing), which should be the first | |
# argument in setup. Sensible options to pass are x and y to locate sketch | |
# on the screen, or full_screen: true (prefer new hash syntax) | |
def initialize(options = {}) | |
super() | |
post_initialize(options) | |
$app = self | |
proxy_java_fields | |
set_sketch_path # unless Processing.online? | |
mix_proxy_into_inner_classes | |
java.lang.Thread.default_uncaught_exception_handler = proc do | |
|_thread_, exception| | |
puts(exception.class.to_s) | |
puts(exception.message) | |
puts(exception.backtrace.map { |trace| "\t#{trace}" }) | |
close | |
end | |
run_sketch(options) | |
end | |
def size(*args) | |
w, h, mode = *args | |
@width ||= w | |
@height ||= h | |
@render_mode ||= mode | |
import_opengl if /opengl/ =~ mode | |
super(*args) | |
end | |
def post_initialize(_args) | |
nil | |
end | |
# Set the size if we set it before we start the animation thread. | |
def start | |
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) | |
dispose | |
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 | |
def import_opengl | |
# Include processing opengl classes that we'd like to use: | |
%w(FontTexture FrameBuffer LinePath LineStroker PGL | |
PGraphics2D PGraphics3D PGraphicsOpenGL PShader | |
PShapeOpenGL Texture).each do |klass| | |
java_import "processing.opengl.#{klass}" | |
end | |
end | |
def run_sketch(options = {}) | |
args = [] | |
@width, @height = options[:width], options[:height] | |
if options[:full_screen] | |
present = true | |
args << '--full-screen' | |
args << "--bgcolor=#{options[:bgcolor]}" if options[:bgcolor] | |
end | |
xc = Processing::RP_CONFIG['X_OFF'] ||= 0 | |
yc = Processing::RP_CONFIG['Y_OFF'] ||= 0 | |
x = options.fetch(:x, xc) | |
y = options.fetch(:y, yc) | |
args << "--location=#{x},#{y}" # important no spaces here | |
title = options.fetch( | |
:title, | |
File.basename(SKETCH_PATH).sub(/(\.rb)$/, '').titleize | |
) | |
args << title | |
PApplet.run_sketch(args.to_java(:string), self) | |
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 a list of method names 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 -= %w(width height cursor create_image background size resize) | |
methods = Processing::App.public_instance_methods | |
methods.reject do |m| | |
unwanted.include?(m) || | |
bad_method.match(m) || | |
inner_class.method_defined?(m) | |
end | |
end | |
# Proxy methods through to the sketch. | |
def self.proxy_methods(inner_class) | |
code = desired_method_names(inner_class).reduce('') do |rcode, method| | |
rcode << <<-EOS | |
def #{method}(*args, &block) # def rect(*args, &block) | |
if block_given? # if block_given? | |
$app.send :'#{method}', *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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module StringUtil | |
refine String do | |
def titleize | |
underscore.humanize.gsub(/\b([a-z])/) { $1.capitalize } | |
end | |
def humanize | |
gsub(/_id$/, '').gsub(/_/, ' ').capitalize | |
end | |
def camelize(first_letter_in_uppercase = true) | |
if first_letter_in_uppercase | |
gsub(/\/(.?)/) { '::' + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase } | |
else | |
first + camelize[1..-1] | |
end | |
end | |
def underscore | |
gsub(/::/, '/') | |
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') | |
.gsub(/([a-z\d])([A-Z])/, '\1_\2') | |
.tr('-', '_') | |
.downcase | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment