D言語からObjective-CとCocoaAPIを使いたい

バーンズリーのシダ (Barnsley fern)

点の形状をovalからrectに変えることで半分ほどの時間になりました。

Barnsley fern

サンプルコード

Powered by Prism

fern/dub.json

{
    "authors": ["hidhok"],
    "description": "A D/Objective-C Cocoa application.",
    "name": "fern",
    "dflags": ["-L-framework", "-LCocoa"],
    "sourceFiles": ["../cocoa/window",
                    "../cocoa/control",
                    "../cocoa/menus"]
}               
fern/source/app.d

/**
 *	  fern/source/app.d       Version 0.0.1
 */
module fern.main;
import core.attribute : optional, selector;
import std.stdio;
import cocoa.window;
import cocoa.control;
import cocoa.menus;
import fern.draw;

string appName;
string versionNo = "0.0.1";
string helpMessage =  "CocoaAPIを使用して、\n"
        ~ "フラクタル図形によるシダを\nD言語で表示してみました。";

DrawView drView;
ViewController vCtrlr;

void main() {
    auto delegate_ = AppDelegate.alloc.init;
    NSApp.delegate_ = cast(void*) delegate_;
    NSApp.activationPolicy = NSAppActivationPolicy.regular;
    NSApp.run;
}

extern (Objective-C)
class AppDelegate : NSObject {
    override static AppDelegate alloc() @selector("alloc");
    override typeof(this) init() @selector("init");
    bool applicationShouldTerminateAfterLastWindowClosed(
            NSNotification notification) @selector(
            "applicationShouldTerminateAfterLastWindowClosed:") {
        return true;
    }
    void applicationDidFinishLaunching(NSNotification notification) 
                @selector("applicationDidFinishLaunching:") {
        buildWindow;
    }
    void aboutPanel() @selector("aboutPanel") {
        NSMutableDictionary options = NSMutableDictionary.dictionary;
        // NSImage img = NSImage.name("AppIcon.icns".toNs);
        // options.setObjKey(img, "ApplicationIcon".toNs);
        options.setObjKey(versionNo.toNs, "Version".toNs);
        options.setObjKey("horkew.la.coocan.jp".toNs, "Copyright".toNs);
        options.setObjKey(versionNo.toNs, "ApplicationVersion".toNs);
        NSApplication.shared_.aboutPanelOptions(options.copy);
    }
    void showHelp() @selector("showHelp") {
        dialog("Help", helpMessage);
    }
} 

void buildWindow() {
    NSWindow win;
    appName = NSProcessInfo.processInfo.processName.toDs;
    appName ~= "(シダ)";
    NSApp.menu = miniMenu(appName);
    NSRect w_rect = nRect(150, 450, 300, 400);
    immutable w_style = NSWindowStyleMask.titled |
                        NSWindowStyleMask.closable |
                        NSWindowStyleMask.resizable |
                        NSWindowStyleMask.miniaturizable;
    win = NSWindow.alloc.init(w_rect, w_style, backing.buffered, false);
    win.title = (appName ~ " (D言語)").toNs;
    win.bgColor = NSColor.colorRGB(0.2, 0.2, 0.2, 1.0);
    vCtrlr = ViewController.alloc.init;
    vCtrlr.view.subview = win.contentView;
    drView = DrawView.alloc.init;
    win.contentView = drView;
    win.makeKeyAndOrderFront = null ;
    NSApp.activateIgnoringOtherApps = true;
}

extern (Objective-C) 
class ViewController : NSViewController {
    override static ViewController alloc() @selector("alloc");
    override ViewController init() @selector("init");
    override void loadView() @selector("loadView") {
        writeln("loadView    " ~ appName ~ "... running Now");
    }
    override void viewDidLoad() @selector("viewDidLoad") {
        printf("viewDidLoad\n");
    }
}               
fern/source/draw.d

/** 
 *    fern/source/draw.d
 */
module fern.draw;
import core.attribute : optional, selector;
import std.stdio;
import std.conv;
import std.math;
import std.random : uniform;
import std.datetime.stopwatch;
import cocoa.window;
import cocoa.control;

double w, h;
StopWatch sw;

extern (Objective-C)
class DrawView : NSView {
    override static DrawView alloc() @selector("alloc");
    override DrawView init() @selector("init");
    override DrawView init(NSRect) @selector("initWithFrame:");
    override void release() @selector("release");
    override void drawRect(NSRect) @selector("drawRect:") {
        NSColor.green.fill;
        w = this.frame.size.width;
        h = this.frame.size.height;
        int m = 35;
        double mx = m * w / 300;
        double my = m * h / 400;
        float r, x = 0.0, y = 0.0, xn, yn;

        assert(!sw.running);
        sw.start();
        assert(sw.running);
        
        foreach (i; 0..100_000) {
            plot(x * mx, y * my);
            r = 100.0 * uniform(0.0f, 1.0f);
            xn = x;
            yn = y;
            if (r < 1) {
                x = 0;  // 茎
                y = 0.16 * yn;
            } else if (r < 86) {
                x =  0.85 * xn + 0.04 * yn;
                y = -0.04 * xn + 0.85 * yn + 1.6;
            } else if (r < 93) {
                x =  0.20 * xn - 0.26 * yn;  // 左葉
                y =  0.23 * xn + 0.22 * yn + 1.6;
            } else {
                x = -0.15 * xn + 0.28 * yn;  // 右葉
                y =  0.26 * xn + 0.24 * yn + 0.44;
            }
        }

        auto t1 = sw.peek();
        sw.stop();
        assert(!sw.running);
        writeln(t1);
        sw.reset();
    }
}

void plot(float x,float y) {
    x += w / 2;        // offset
    y += 20 * h / 400; // offset
    // NSBezierPath dotPath = NSBezierPath.oval(nRect(x, y, 1, 1));
    NSBezierPath dotPath = NSBezierPath.rect(nRect(x, y, 1, 1));
    dotPath.fill;
}                
cocoa/window.d
cocoa/control.d
cocoa/menus.d