fortran66のブログ

fortran について書きます。

Fortran から X-Window に点を打つ

X-window グラフィック

これまで Win32 用のグラフィックルーチンは作ってきましたが、X-Window 用も出来ないものかと前々から考えておりました。Fortran だけで実現しようとすると、沢山のインターフェースを用意する必要が出てきますが、Linux 系では C コンパイラは必ず入っているので、C 言語も使うことにしてハードルを下げることにします。

Bash on Windows で実行して、とりあえず、点を打てれば良しとします。


参考ページ:
linux - X11/Xlib.h not found in Ubuntu - Stack Overflow

  • X-Window Graphics の基礎

http://www.cc.kyoto-su.ac.jp/~isida/Pdfs/x.pdf

実行結果

モンテカルロ法で円周率計算
f:id:fortran66:20180707233919p:plain

gfortran / intel fortran どちらでも実行できました。

  • 必要ならば
sudo apt install libx11-dev
  • gfortran 実行例
gfortran plot.c plot.f90 -lX11

c コンパイラgnu のものを使います。

gfortran -c -o plot.lib plot.c
ifort plot.f90 plot.lib -lX11

ソース・プログラム

C 言語プログラム plot.c

X window の open/close と 点打ちの3つのルーチンを用意します。

#include <X11/Xlib.h>
#include <X11/Xutil.h>

static Display *d;
static Window  w;
static GC      gc;
static unsigned long white, black;

void X_open(int nx, int ny)
{
  // Open a display.
  d = XOpenDisplay(0);
  if ( !d ) return;
  white = WhitePixel(d, DefaultScreen(d));
  black = BlackPixel(d, DefaultScreen(d));

  // Create the window
  w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, nx, ny, 0, black, white);
  XMapWindow(d, w);
  gc = XCreateGC(d, w, 0, 0);
}

void X_point(int ix, int iy)
{
  XDrawPoint(d, w, gc, ix, iy);
  XFlush(d);
}

void X_close(void)
{
  XFreeGC(d, gc);
  XDestroyWindow(d, w);
  XFlush(d);
  XCloseDisplay(d);
}
Fortran プログラム plot.f90

C 言語の3つのルーチンに対するインターフェースを用意します。そこから先は Fortran だけで話が済みます。

module m_plot
    implicit none
    interface
         subroutine Xopen(nx, ny) bind(c, name = 'X_open')
             integer, value :: nx, ny
         end subroutine Xopen

         subroutine Xpoint(ix, iy) bind(c, name = 'X_point')
             integer, value :: ix, iy
         end subroutine Xpoint

         subroutine Xclose() bind(c, name = 'X_close')
         end subroutine Xclose
     end interface
end module m_plot


program plot
    use m_plot
    implicit none
    integer, parameter :: n = 10000, nx = 500, ny = nx
    real :: x(n), y(n)
    integer :: i, ix, iy

    call random_seed()
    call random_number(x)
    call random_number(y)

    call Xopen(nx, ny)
    do ix = 0, nx
        iy = sqrt(real(ny**2 - ix**2))
        call Xpoint(ix, iy)
    end do
    do iy = 0, ny
        ix = sqrt(real(nx**2 - iy**2))
        call Xpoint(ix, iy)
    end do

    do i = 1, n
        ix = nx * x(i)
        iy = ny * y(i)
        call Xpoint(ix, iy)
    end do
    print *, 'Monte-Carlo Pi=', 4 * count(x**2 + y**2 < 1.0) / real(n)

    print *, 'press enter'
    read *
    call Xclose()
end program plot

補足[H30.7.10]

環境によっては、窓の立ち上がりが遅くて?待たねばならないようです。XFlush も乱用できません。

#include <X11/Xlib.h>
#include <X11/Xutil.h>
static Display* d;
static Window   w;
static GC       gc;
unsigned long white, black;

void X_open(int nx, int ny){
    // Open a display.
    d = XOpenDisplay(0);
    if ( !d ) return;
    //
    white = WhitePixel(d, DefaultScreen(d));
    black = BlackPixel(d, DefaultScreen(d));
    // Create the window
    w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, nx, ny, 0, black, white);
    XMapWindow(d, w);
    gc = XCreateGC(d, w, 0, 0);
    XFlush(d);
}

void X_point(int ix, int iy){
     XDrawPoint(d, w, gc, ix, iy);
     XFlush(d);
}

void X_flush(){
     XFlush(d);
}

void X_close(void){
    XFreeGC(d, gc);
    XDestroyWindow(d, w);
    XFlush(d);
    XCloseDisplay(d);
}
module m_plot
    implicit none
    interface
         subroutine Xopen(nx, ny) bind(c, name = 'X_open')
             integer, value :: nx, ny
         end subroutine Xopen

         subroutine Xpoint(ix, iy) bind(c, name = 'X_point')
             integer, value :: ix, iy
         end subroutine Xpoint

         subroutine Xclose() bind(c, name = 'X_close')
         end subroutine Xclose

         subroutine Xflush() bind(c, name = 'X_flush')
         end subroutine Xflush
     end interface
end module m_plot


program plot
    use m_plot
    implicit none
    integer, parameter :: n = 10000, nx = 500, ny = nx
    real :: x(n), y(n)
    integer :: i, ix, iy

    call random_seed()
    call random_number(x)
    call random_number(y)

    call Xopen(nx, ny)
    call sleep(1)                              ! non-standard
    do ix = 0, nx
        iy = sqrt(real(ny**2 - ix**2))
        call Xpoint(ix, iy)
    end do
    do iy = 0, ny
        ix = sqrt(real(nx**2 - iy**2))
        call Xpoint(ix, iy)
    end do
    call Xflush()

    do i = 1, n
        ix = nx * x(i)
        iy = ny * y(i)
        call Xpoint(ix, iy)
    end do
    call Xflush()
    print *, 'Monte-Carlo Pi=', 4 * count(x**2 + y**2 < 1.0) / real(n)

    print *, 'press enter'
    read *
    call Xclose()
end program plot