CFFIでGRを叩く

今日も今日とてKaiの開発。

先日にPlotlyの分のある程度が完成したので現在はバックエンドをGRに拡張するために色々やっている。

今日少しGRに着手するための作業が完了したのでそのメモ。

CFFI

CFFIはC言語へのFFI (Foreign Function Interface)を行うCommon Lispのライブラリ。

これの具体的な使い方はmasatoiさんのこの記事がすごくよくまとまっている。

qiita.com

この記事を参考にGRの共有オブジェクトを触っていく。

実際にやってみる

上記の記事を参考にやってみる。

GRのサンプルコード(C言語)は以下の通りで

#include <gr.h>

int main(void) {
    double x[] = {0, 0.2, 0.4, 0.6, 0.8, 1.0};
    double y[] = {0.3, 0.5, 0.4, 0.2, 0.6, 0.7};
    gr_polyline(6, x, y);
    gr_axes(gr_tick(0, 1), gr_tick(0, 1), 0, 0, 1, 1, -0.01);
    // Press any key to exit
    getc(stdin);
    return 0;
}

今回はこれを動かすのを目標とする。

最初にGRのバイナリをダウンロードしてくる。

リンクはここから。

github.com

準備ができたら、まずQuicklispでCFFIをロードする。

(ql:quickload :cffi)

次にGRの共有オブジェクトをロードする。

(defparameter path-to-gr "~/Downloads/gr")

(cffi:load-foreign-library
  (merge-pathnames "gr/lib/libGR.so" path-to-gr))

そしてウィンドウを立ち上げるための初期化関数などを用意。

(cffi:defcfun ("gr_initgr" initgr) :void)

(cffi:defcfun ("gr_opengks" opengks) :void)

(cffi:defcfun ("gr_closegks" closegks) :void)

このあとサンプルコードをひたすらCFFIの記法へと変換していくが、その前に環境変数を設定しておく(これがめっちゃ大切)

(setf (uiop:getenv "GRDIR") path-to-gr)

(setf (uiop:getenv "GKS_FONTPATH") path-to-gr)

さて、準備はできたのであとは上記のサンプルコードをCFFIの記法に変換していく。

(defparameter *x*
  (cffi:foreign-alloc :double
                      :initial-contents
                      (list 0.0d0 0.2d0 0.4d0 0.6d0 0.8d0 1.0d0)))
(defparameter *y*
  (cffi:foreign-alloc :double
                      :initial-contents
                      (list 0.3d0 0.5d0 0.4d0 0.2d0 0.6d0 0.7d0)))

(cffi:defcfun ("gr_polyline" polyline) :void
  (n :int)
  (x :pointer)
  (y :pointer))

(cffi:defcfun ("gr_tick" tick) :double
  (a :double)
  (b :double))

(cffi:defcfun ("gr_axes" axes) :void
  (x-tick :double)
  (y-tick :double)
  (x-org :double)
  (y-org :double)
  (major-x :int)
  (major-y :int)
  (tick-size :double))

これで準備は終わり。

追記.

実際のコードを書いてみて色々動かしているとき、試しに (polyline '(1 2 3) '(1 2 3)) としてみたところ、ウィンドウは出てくるが何も描画されず焦っていた。

検証をしてみたところ、どうやらGRは0-1の浮動小数点を相対座標として読み込んで画面に描画している ので、1より大きい値は表示されないことに注意。

早速動かしてみる。

(initgr)

(polyline 6 *x* *y*)

これを実行すると以下のようになる。

f:id:komi1230:20200412171735p:plain
線が描画された

次にaxesの部分を実行する。

(axes (tick 0d0 1d0) (tick 0d0 1d0) 1d0 1d0 1 1 -0.01d0)

そうすると以下の通りで追加で描画される。

f:id:komi1230:20200412171909p:plain
後から追加で描画された

まとめ

これにて手元でGRをCommon Lispから動かすことができた。

今後もKaiの開発を進めていこうと思う。

ところで途中で用意したopengksとかclosegksはどういうケースで使うのだろう?

個人的なイメージとしてはopengksを叩くことによってウィンドウが立ち上がるようなイメージだったのだけど、どうやらそうではないらしい。

謎だ。