GNU Smalltalk でお絵かき ―GNU Smalltak で cairo のお勉強 その3

結局今回なんで cairo で遊んでみようと思ったかと言うと、Squeak のお気に入りのお絵かきスクリプト

| pen |
pen := Pen new roundNib: 8;
               color: Color red.
                
[Sensor yellowButtonPressed not] whileTrue: [
    Sensor redButtonPressed
        ifTrue: [pen goto: Sensor cursorPoint]
        ifFalse: [pen place: Sensor cursorPoint]]

みたいなことをやるのに、GNU Smalltalk GTK では どれくらいになるのかな?・・と思ってしまったからです。やりたかったのはそれだけだったのですが、なかなかスムースにいかなくて。いつものことですが...orz

#!/usr/bin/env gst

PackageLoader fileInPackage: 'GTK'.
PackageLoader fileInPackage: 'Cairo'.

GTK.GtkWidget extend [
    getPointer [
        | x y |
        x := CInt value: 0.
        y := CInt value: 0.
        self getPointer:  x y: y.
        ^x value @ y value
    ]
]

Object subclass: MainWindow [
    | window penDown pathList |

    pathList [
        ^pathList ifNil:[pathList := OrderedCollection new]
    ]

    addPath: selector point: point [
        "selector = #moveTo:, #lineTo: ..etc"
        ((self pathList notEmpty) and:
        [point = self pathList last argument]) 
            ifFalse: [
                self pathList add: 
                    (Message selector: selector argument: point).
                ^true
            ].
        ^false
    ]

    delete: aWiget event: aGdkEvent [
        GTK.Gtk mainQuit.
        ^false
    ]

    penDown [
        ^penDown ifNil:[penDown := false]
    ]

    penDown: aWidget event: aGdkEvent [
        penDown := true.
        'penDown!' printNl.
        (self addPath: #moveTo: point: aWidget getPointer) 
        ifTrue: [
            aWidget getWindow withContextDo: [:cairoCtx|
                self pathList last sendTo: cairoCtx.
                    cairoCtx stroke
            ]
        ]
    ]

    penUp: aWidget event: aGdkEvent [
        penDown := false.
        'penUp!' printNl.
    ]

    mouseMove: aWidget event: aGdkEvent [
        self penDown ifTrue: [
            (self addPath: #lineTo: point: aWidget getPointer)
            ifTrue: [
              aWidget getWindow withContextDo: [:cairoCtx |
                 (self pathList at: self pathList size -1) sendTo: cairoCtx.
                 self pathList last sendTo: cairoCtx.
                 cairoCtx stroke
              ]
            ]
        ]
    ]

    expose: aWidget event: aGdkEvent [
        aWidget getWindow withContextDo: [:cairoCtx |
            cairoCtx lineCap: #round; lineWidth: 3.0.

            self pathList notEmpty ifTrue: [
                self pathList do: [ :msg |
                    Transcript show: 'exec:'; showCr: msg printString.
                    msg sendTo: cairoCtx
                ].
                cairoCtx stroke
            ]
        ].
        ^false
   ]

    show [
        | box canvas gdkDrawable cairoSf cairoCtx |
        window := GTK.GtkWindow new: GTK.Gtk gtkWindowToplevel.
        window setTitle: 'Cario Lean'.
        window connectSignal: 'delete_event' 
            to: self selector: #delete:event: userData: nil.
        window setDefaultSize: 200 height: 200.


        canvas := GTK.GtkDrawingArea new.
        window add: canvas.

        canvas connectSignal: 'expose-event'
            to: self selector: #expose:event: userData: nil.

        canvas setEvents: 
            (GTK.Gdk gdkButtonPressMask bitOr:
                 (GTK.Gdk gdkButtonReleaseMask bitOr:
                     GTK.Gdk gdkPointerMotionMask)).
        canvas connectSignal: 'button_press_event'
            to: self selector: #penDown:event: userData: nil.
        canvas connectSignal: 'button_release_event'
            to: self selector: #penUp:event: userData: nil.
        canvas connectSignal: 'motion_notify_event'
            to: self selector: #mouseMove:event: userData: nil.

        window showAll.
    ]   
]


Eval [
    window := MainWindow new.
    window show.
    GTK.Gtk main
]


実行したところはこんな感じです。

職場だったんで、トラボでお絵かきしたのですが、、、無謀が身に染みまくりです。(絵になりませぬ...orz)*1

expose event の再描画ときにペンの太さをかえているので、window を最小化したりサイズかえたりするとこんなのになります。

ベクターグラフィックだよ、という主張のつもりでしたが、たんに滲んだだけにしか見えませぬ。失敗(T△T

まぁ、入力されたデータをそのまま極小直線にしたのでは、ドット絵とかわらないですよね。そこらへんをよろしくやるあたりとか面白そうなお題かもかも*2。それ以前に、ぶっちゃけ何も考えないで作っているので、 データ的にも動作的にもかなり重いので、そこからですね。


 * * *


というわけで、GW 前の 仕事のキリの悪いことをいいわけに、ちょっとサボちゃった風味なエントリーはこれにておしまいでし。

*1:でも、そういえば利き手じゃない手で描いたんだったっけ、と思い出して、トラボって結構すごいかも!と思い直したり

*2:スプライン関数で補完とかでいけるのかな?よくわかりませんけれど..