たまには新規プロジェクトをおこそう

久しぶりに cocoa プロジェクトを新規に起こしたのだが、序盤でビルドしたところ

Undefined symbols for architecture arm64:
“_OBJC_CLASS_$_MTKView”

のエラーが・・・。

なんのことはない Metal のフレームワーク入れ忘れてた。

 

cocoa Core Graphics 関係

MacOS の新しい画像ライブラリは Metal だが、2次元に限定されてしまうが、使いやすいのは Core Graphics だろう。

なのだが、情報が乏しいのなんのって。

iOS 系のサンプルや公式ドキュメントを参考に MacOS 向けの簡単なサンプルコードを書いてみた。

簡単な描画サンプル


- (void)drawRect:(CGRect)dirtyRect {
    
    //[super drawRect:dirtyRect];//不要のようだ
     NSGraphicsContext* nsgc = [NSGraphicsContext currentContext];
     CGContextRef context = [nsgc CGContext];
    
     CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    double col[4],col2[4];
     col[0] = 1.0;col[1] = 1.0;col[2] = 0.5;col[3] = 1.0;
    col2[0] = 1.0;col2[1] = 0.0;col2[2] = 0.0;col2[3] = 0.8;
     CGColorRef color = CGColorCreate(space,col);
    CGColorRef color2 = CGColorCreate(space,col2);
     //透明レイヤー開始
     //CGContextBeginTransparencyLayer(context, nil);

     //パスの描画を開始
     CGContextBeginPath(context);
     float startAngle = -M_PI/2;
     float endAngle = startAngle + (M_PI * 2.0) * 0.5;
     CGContextMoveToPoint(context, 100, 100);
     CGContextAddArc(context, 100, 100, 50.0f, startAngle, endAngle, clockwise);
     CGContextClosePath(context);
     CGContextSetFillColorWithColor(context, color);
     CGContextDrawPath(context, kCGPathFill);
    
    CGContextBeginPath(context);
    CGContextMoveToPoint(context, 0, 0);
    //CGContextSetStrokeColor(context, col2);
    CGContextSetLineWidth(context,2.0);
    CGContextAddLineToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 150, 50);
    CGContextDrawPath(context, kCGPathStroke);

     //透明レイヤー終了
     //CGContextEndTransparencyLayer(context);
    
    CGColorRelease(color);

}

Metal に比べると実に簡単。
点を打ってラインで繋ぎ・・・というふうに本当に直感的。
これでアイキャッチのような図形が描画できる。

なお、cocoa 系の Core Graphics の座標系は以下の通り。

clockwise は 1 と定義してます。

Core Graphics が Quartz???

ところで、Core Graphics は Quartz の枠組みを踏襲しているようだが、その実装は CPU レンダリング主体、少なくとも OpenGL 絡みは使っていないはずで、今後も安心して使えると思うのだが、そこらへん、曖昧に説明しているサイトがあったりして「は?」となっている。

追記:ソースコードはこちらの CoreGraphics 特集?に収載されてます。

Core Graphics + Metal

Core Graphics のお手軽さと Metal の速さを組み合わせて両方を活かして使いたいという欲求はあるようで、以下のような記事があった。

Combine the power of CoreGraphics and Metal by sharing resource memory 

サンプルが動画になっている。

見事。

 

Xocde エラー関係

よくあるエラーに関して。

The document “***.h” could not be saved. You don’t have permission.

おそらく、システムのヘッダを書き換えるように fix しちゃったんだと思う。
キャッシュを消したいので、Xcode 自体を強制終了しましょう。再インストールまでは必要ないです。

 

Metal 関係リソース物色中

あまり勉強したことがない Metal 関係のリソースを物色中。

公式サンプル

おお!と思ったのは公式のサンプル紹介記事にあったこのプロジェクト(群)。

C++ と書いてあるが、「どーせ、これ Objective-C++ でしょ?」と思っていたが、れっきとした C++ だった。
例えば、Xcode で GUI アプリを作成した際、自動で生成される main.m に相当する部分は

int main( int argc, char* argv[] )
{
    NS::AutoreleasePool* pAutoreleasePool = NS::AutoreleasePool::alloc()->init();

    MyAppDelegate del;

    NS::Application* pSharedApplication = NS::Application::sharedApplication();
    pSharedApplication->setDelegate( &del );
    pSharedApplication->run();

    pAutoreleasePool->release();

    return 0;
}

となっている。
言われてみれば Objective-C++  と C++ との対応はこうですね、って感じ。

三角形ばかり書いていてもつまらん

そんな方はこちらなんかも。

ただし、これは Swift の iOS プロジェクトなので、MacOS に移植する際にはいくらか(↓)修正が必要。

cocoa アプリでの Metal Viewer の取り扱い

手動で追加する。

NSObect のサブクラスとして適当な名前のクラスを作成した後に、MTKViewDelegate を書き加える。

また、以下の二つのメソッドは MTKViewDelegate を継承するには必須のようなので、付け加える。

 

参考(追記)

MacでObjective-cを使ってMetalを初期化

のシリーズを使って勉強しよう・・・と思っていたのだが、これは Objective-C で Metal を扱う際にはイマイチのやり方のようだ。

もともとアップル公式のサンプルは Objective-C で書かれており、これを Swift に移植したものが、上記シリーズ(というかネットに落ちている情報全般)っぽい。

この記事にならって丹念にアップル公式のサンプル眺めていくしかないかな。

 

 

AppDelegate さて Xib では?

MacOS のデスクトップアプリを Xcode で作成する際、UI として storyboard を選べば、自動で appdelegate は生成されるので、簡単なアプリならば、ここで初期化処理すればいい。

が、レガシーなプロジェクトでは xib が使われている場合も多い。

ちょっと調べますかね。

Xcode を起動し、UI を xib でプロジェクトを作成。

MainMenu.xib が自動で生成されるが、この Objects を管理しているのが AppDelegate という構成になっているようだ。

AppDelegate も自動生成されるが、このクラスは以下のように NSApplicationDelegate を継承している。

なるほど。

ただ、レガシーなプロジェクトではこの継承が明示されていないこともあるようだ。
AppController のような名前になっていて初見でなんのことかわからなかった。
もちろん、
– (void)applicationDidFinishLaunching:
– (void)applicationWillTerminate:
は、実装されていたが。
さらに言っておくと WindowController から AppController のイニシャライズをする、というなんとも風変わりな構成になっていた。

こういうのもあるんですね。