Home > RubyCocoa で QuartzComposer CustomPatch をつくりたい

RubyCocoa で QuartzComposer CustomPatch をつくりたい

先に今の状態 : できそうな糸口がつかめたところで挫折 :(

SocketReaderPatchApacheLogPatch とQuartzComposerのカスタムパッチをつくってきたのですが、ここらでRubyで書くかと思い立ちました。 バベル案内ホワイの(感動的)Rubyガイド を読んで、こんなにもRubyが世界に受け入れらているのかと感動したことと、いくつかCustomPatchをつくってみて、CustomPatchj作成の概観がつかめてきたから。

どうやるか

RubyCocoaのsvn trunkを使います。 最近のリビジョンでは、QuartzComposer.frameworkが使えるようになっているのです。

>> require 'osx/cocoa'
r=> true
>> OSX.require_framework 'QuartzComposer'
=> true
>> OSX::QCStructure
=> OSX::QCStructure
>> OSX::QCPatch
=> OSX::QCPatch

ひゃっほう。

で、いつもどおりQuartzComposer CustomPatch の Xcode template をつかって、Xcodeプロジェクトをはじめます。

RubyCocoa.frameworkをリンクしておき、RubyPatchPrincipla.m を

#import <rubyCocoa/RubyCocoa.h>
#imporut "RubyPatchPrincipal.h"
 
@implementation RubyPatchPlugin
+ (void)registerNodesWithManager:(GFNodeManager*)fp8
{
  static bool loaded = false;
  if (!loaded) {
    if (RBBundleInit("/tmp/custom_patch.rb", self, nil)) {
      NSLog(@"[RubyPatchPlugin.registerNodesWithManager] RBBundleInit failed");
    } else {
      loaded = true;
    }
  }
 
  Class helperClass = NSClassFromString(@"RubyPatch");
  [fp8 registerNodeWithClass:helperClass];
}
@end

のように書きました。 RubyCocoaのbundle生成用APIを用いています。

loadされるRubyスクリプトはこういうもの。

require 'osx/cocoa'
OSX.require_framework 'QuartzComposer'
 
class RubyPatch < OSX::QCPatch
  #attr_accessor :inputFoo, :outputBar
 
  def RubyPatch.logger=(l)
    @@logger = l
  end
 
  def RubyPatch.executionMode
    @@logger.info("executionMode")
    2
  end
 
  def RubyPatch.allowsSubpatches
    @@logger.info("allowsSubpatches")
    0 # in objc.h, NO is defined as 0
  end
 
  def RubyPatch.timeMode
    @@logger.info("timeMode")
    1
  end
 
 
  def init
    @@logger.info("init")
    @inputFoo = OSX::QCBooleanPort.alloc.init
  end
 
  def initWithIdentifier(fp8)
    @@logger.info("initWithIdentifier")
    init
    self
  end
 
  def setup(fp8)
    @@logger.info("setup %s, %s", fp8, fp8.class)
    fp8
  end
 
  def cleanup(fp8)
    @@logger.info("cleanup")
  end
 
  def enable(fp8)
    @@logger.info("enable")
  end
 
  def disable(fp8)
    @@logger.info("disable")
  end
 
  def execute(fp8, time, arg)
    @@logger.info("execute")
    true;
  end
end
 
OSX.init_for_bundle do |bundle, param, logger|
logger.info("init bundle=%s param=%s", bundle, param)
  RubyPatch.logger = logger
end

templateが生成するObjective-Cのコードを、Rubyに書き下したものですね。

実行

ビルドもすんなり通り、さっそく実行してみます。

qc-exception-rubycocoapatch.png

アウチ。 なんか例外が投げられてしまいます。 出しているログを眺めてみると…

正常なもの (Objective-Cで書いたカスタムパッチ)

2007-07-14 14:35:03.568 Quartz Composer[15580] RubyPatch.plugin (Quartz Composer): init bundle=NSBundle  (loaded) param=
2007-07-14 14:35:03.708 Quartz Composer[15580] initWithIdentifier
2007-07-14 14:35:03.708 Quartz Composer[15580] executionMode
2007-07-14 14:35:03.708 Quartz Composer[15580] timeMode
2007-07-14 14:35:03.710 Quartz Composer[15580] allowsSubpatches
2007-07-14 14:35:03.741 Quartz Composer[15580] allowsSubpatches
2007-07-14 14:35:03.741 Quartz Composer[15580] allowsSubpatches
2007-07-14 14:35:03.741 Quartz Composer[15580] allowsSubpatches
2007-07-14 14:35:03.785 Quartz Composer[15580] setup

だめなもの (RubyCocoaで書いた)

2007-07-14 14:36:11.300 Quartz Composer[15602] RubyPatch.plugin (Quartz Composer): init bundle=NSBundle  (loaded) param=
2007-07-14 14:36:11.301 Quartz Composer[15602] assert passed !
2007-07-14 14:36:11.342 Quartz Composer[15602] RubyPatch.plugin (Quartz Composer): initWithIdentifier
2007-07-14 14:36:11.342 Quartz Composer[15602] RubyPatch.plugin (Quartz Composer): init
2007-07-14 14:36:11.345 Quartz Composer[15602] RubyPatch.plugin (Quartz Composer): allowsSubpatches
2007-07-14 14:36:11.377 Quartz Composer[15602] RubyPatch.plugin (Quartz Composer): allowsSubpatches
2007-07-14 14:36:11.378 Quartz Composer[15602] RubyPatch.plugin (Quartz Composer): allowsSubpatches

よく見てみると、RubyCocoaで書いた方は、initWithIdentifier のあとに executionMode, timeMode が呼ばれることなく allowsSubpatches が呼ばれています。 executionMode, timeMode が分からないので、QuartzComposer としてはこのパッチをどの種類にして良いか分からず、レンダリングできませんよーという例外がきてるっぽい。

問題

ではなぜ executionMode, timeMode が呼ばれないか、考えてみます。

QCPatchのヘッダによると、executionMode, timeMode はそれぞれ int を返すメソッドのようです。 ところが、RubyCocoaで用意した executionMode, timeMode は返り値の型を指定していません。 ちょっと調べたところ、RubyCocoaではRubyのメソッドが整数型を返すとき、Objective-Cの世界にはNSDecimalNumberの型を返すようになっているようです。

その結果、executionMode, timeMode は別のメソッドとして認識されてしまい、QCPatchのメソッドをオーバーライドできず、呼ばれていないのではないかと推測しました。

解決案

  1. intで返すようにRubyCocoaでなんとかする
  2. QCPatchをRubyクラスで継承せずにObjective-Cのクラスで継承させ、そのメンバにRubyクラスのdelegateのオブジェクトをもたせて、実際の処理はdelegateオブジェクトに振る

2が手っ取り早そうですが、いまひとつかっこいくないなぁ。

別の問題

CustomPatchのテンプレートによると、QCPatchを継承したクラスのメンバに QCPortクラスのインスタンス (ex. inputEnable, outputString など) を書くことで、パッチのinput/outputポートを指定できるような仕組みになっています。

じゃあ、

attr_accessor :inputEnable, :outputString
...

みたいにすればいいのか、というとどうもそうでもないみたいで…

やはり解決案2を使うしかないのかもです。

Comments:0

Comment Form
Remember personal info

Trackbacks:0

Trackback URL for this entry
http://blog.deadbeaf.org/2007/07/14/make-quartzcomposer-custompatch-with-rubycocoa/trackback/
Listed below are links to weblogs that reference
RubyCocoa で QuartzComposer CustomPatch をつくりたい from mootoh.log

Home > RubyCocoa で QuartzComposer CustomPatch をつくりたい

Feeds

Return to page top