好きなエディタでこまめに結果確認しながらjruby + processingする

jirbインタラクティブに書き換えるのも楽しいけどある程度構造考えながらだとエディタで編集しながらのほうがよくて、でも普段emacs使っていてprocessingのIDEは使いたくなかったのでfcwrap(現rascut)からファイル監視の処理をパクってかつ、いつも書く起動まわりの処理をまとめてみた。

require 'pathname'
require 'logger'
require 'thread'
require 'java'
include_class 'processing.core.PApplet'
include_class 'processing.core.PMatrix'
include_class "processing.core.PImage"
include_class "processing.core.PFont"

class SketchBook
  JFrame = javax.swing.JFrame
  DEFAULT_OPTIONS = {
    :interval => 2,
    :target_file => Time.now,
  }

  def initialize(filename, options = {})
    @options = DEFAULT_OPTIONS.merge(options)
    @target_file = Pathname.new(filename)
    @timestamp = Time.now
    @logger = Logger.new(STDOUT)
    @logger.progname = 'SketchBook'
    create_sketch
    open_sketch Sketch.new
  end

  def logger
    @logger
  end
  
  def create_sketch
    p5_program = ''
    @mutex = Mutex.new
    @mutex.synchronize do
      file = open(@target_file)
      file.each do |line|
        p5_program += line
      end
    end
  
    p5_program = [
      'class Sketch < PApplet',
       p5_program,
      'end'
    ].join("\n")

    def p5_environment
      binding
    end

    result = false
    begin
      eval(p5_program, p5_environment)
      result = true
    rescue
      logger.error "#{@target_file.basename.to_s} has SyntaxError!"
      result = false
    end
    result
  end
  
  def open_sketch(applet)
    @frame.visible = false if @frame
    @frame = JFrame.new applet.class.to_s
    @frame.content_pane.add applet
    @frame.default_close_operation = JFrame::EXIT_ON_CLOSE
    applet.init
    @frame.pack
    @frame.visible = true
  end
  
  def file_updated?
    if @target_file.mtime > @timestamp
      @timestamp = @target_file.mtime
      logger.info "#{@target_file.basename.to_s} was updated."
      true
    else
      false
    end
  end
  
  def run
    loop do
      if file_updated?
        if create_sketch
          open_sketch Sketch.new
        end
      end
      sleep [@options[:interval], 1].max
    end
  end
end

sketchbook = SketchBook.new(ARGV[0])
sketchbook.run

コマンドラインから以下のようにするとfoo.rbが監視対象になるので好きなエディタで編集できる。

$ jruby sketchbook.rb foo.rb

foo.rbはこんな感じでrubyであるという点以外は普通のprocessingに近く書ける

def setup
  size 100, 100
  background 255
  no_stroke
  @img_megane = load_image("utamaru160_meagne_only.PNG")
  smooth
end

def draw
  fill 255, random(100), random(100)
#  background 128
  4.times do
    tint 255, 255, 255, 30
    sz = random(20, 40)
#    image(@img_megane, random(width), random(height), sz, sz)
    ellipse(random(width), random(height), sz, sz)
  end
end


デモ映像ではwindowsコマンドラインからjruby + sketchbook.rb起動して同じマシン上のcoLinux内のemacsで編集してるfoo.rbを編集して保存されるたびにapplet窓にfoo.rbの内容が反映されている。PAppletのstaticメソッドなんかはあいかわらずPApplet.radiansとかアクセスしないとダメなんだけど普段使ってるエディタが使えるので大部楽しい。