fortran66のブログ

fortran について書きます。

【メモ帳】fpm で intel fortran

fpm で起動されるコンパイラintel fortran

fpm のプロトタイプは gfortran 専用でしたが、いつの間にかコンパイラを指定できるようになっていました。コマンドラインのヘルプを出したら書いてありました。

fpm build --compiler ifort
fpm run --compiler ifort

で行けます。

xplot を ifort で

gfortran と intel fortran では、デフォールト・コンストラクタの挙動が違うため、両方でコンパイルできるようにコンストラクタを使わずに、allocate したあと初期値を代入するように修正しました。

xplot は c 言語を使っているため、fpm で intel fortan を使うのは単純にはいきませんでした。(gfortran は コンパイラドライバとして gcc も起動してくれるますが、ifort は拡張子が .c のファイルを受け付けないようです。)

ちょっと toml ファイルの書き方がよく分からないので、足りないと叱られるメッセージをよく見て、コンパイル済みのオブジェクトファイルを足りないファイル名に直してコピーしてやることで誤魔化しました。コンパイラicc でなくても gfortran/gcc で大丈夫です。

【メモ帳】ブラウザからも阿部さん

canvas 形式での出力

f:id:fortran66:20210530130610p:plain

postscript や HTML 形式での出力にはファイル名が必要になるので、インターフェースの改良が必要だがシンプルさが減る。昔作ったインターフェースでは考えてあった。

fortran66.hatenablog.com

ソース・プログラム

module htmlplot
    use device
    implicit none
    private
    public :: fig_t
    type, extends(device_t) :: fig_t
        private
        character(len = :), allocatable, public :: fn
        integer, allocatable :: iw 
    contains 
        procedure :: init  
        procedure :: point 
        procedure :: show
        procedure :: filename
        final :: off  
    end type fig_t
contains

    subroutine init(fig)
        class(fig_t), intent(in out) :: fig
        allocate(fig%iw)
        fig%line0 => line0
        fig%line  => line
        if (.not. allocated(fig%fn)) fig%fn = 'figure'
        associate (iw => fig%iw, title => fig%fn)
            open(newunit = iw, file = trim(title) // '.html')
            write(iw, '(a)') '<!DOCTYPE html>'
            write(iw, '(a)') '<html>'
            write(iw, '(a)') '<head>'
            write(iw, '(a)') '<meta charset="Shift_JIS">'
            write(iw, '(3a)') '<title>', trim(title), '</title>'
            write(iw, '(a)')  '<script type="text/javascript">'
            write(iw, '(a)') '<!--'
            write(iw, '(a)') 'function plotter() {'
            write(iw, '(3a)') "var canvas = document.getElementById('", trim(title), "');"
            write(iw, '(a)') "var context = canvas.getContext('2d');"
            write(iw, '(a)') '//'
            write(iw, '(a)') 'context.scale(1, 1);'
            write(iw, '(a)') 'context.lineWidth = 1;'                 ! pen default
            write(iw, '(a)') "context.strokeStyle = 'rgb(0, 0, 0)';"  ! pen default
            write(iw, '(a)') 'context.lineCap = "butt";'
            write(iw, '(a)') 'context.beginPath();'
        end associate
    end subroutine init

    subroutine filename(fig, fn)
        class(fig_t), intent(in out) :: fig
        character(*), intent(in) :: fn
        fig%fn = fn
    end subroutine filename

    subroutine off(fig)
        type(fig_t), intent(in) :: fig
        associate (iw => fig%iw, title => fig%fn, nx => fig%nx, ny => fig%ny)
            write(iw, '(a)') 'context.stroke();'
            write(iw, '(a)') '}'
            write(iw, '(a)') '//-->'
            write(iw, '(a)') '</script>'
            write(iw, '(a)') '</head>'
            write(iw, '(a)') '<body onLoad="plotter()">'
            write(iw, '(3a, i6, a, i6, a)') '<canvas id="', trim(title) , '" width="', nx, '" height="', ny, '">'
            write(iw, '(a)') '</canvas>'
            write(iw, '(a)') '</body>'
            write(iw, '(a)') '</html>' 
            close(iw)
        end associate
    end subroutine off

    subroutine point(fig, ix, iy)
        class(fig_t), intent(in out) :: fig 
        integer, intent(in) :: ix, iy
        write(fig%iw, '(a, i7, a, i7, a)') 'context.fillRect(', ix, ',', iy, ', 0.5, 0.5);'
    end subroutine point
 
    subroutine show(fig)
        class(fig_t), intent(in) :: fig
        write(fig%iw, '(a)') 'context.stroke();'
        write(fig%iw, '(a)') 'context.beginPath();'
    end subroutine show

    subroutine line0(fig, ix0, iy0, ix1, iy1)
        class(device_t), intent(in out) :: fig
        integer, intent(in) :: ix0, iy0, ix1, iy1
        select type (fig)
        type is (fig_t)
            write(fig%iw, '(a, i7, a, i7, a)') 'context.moveTo(', ix0, ',', iy0, ');'
            write(fig%iw, '(a, i7, a, i7, a)') 'context.lineTo(', ix1, ',', iy1, ');'
        end select
      end subroutine line0

      subroutine line(fig, x, y, ipen)
        class(device_t), intent(in out) :: fig
        real, intent(in) :: x, y
        integer, intent(in) :: ipen
        real :: xn, yn, fx, fy
        select type (fig)
        type is (fig_t)
            xn = fig%nx / 2.0
            yn = fig%ny / 2.0 * 1.5
            fx = fig%nx / 150.0
            fy = fig%ny / 150.0
            if (ipen == 1) then 
                write(fig%iw, '(a, f10.3, a, f10.3, a)') 'context.lineTo(', fx * x + xn, ',', -fy * y + yn, ');'
            else 
                write(fig%iw, '(a, f10.3, a, f10.3, a)') 'context.moveTo(', fx * x + xn, ',', -fy * y + yn, ');'
            end if 
        end select  
    end subroutine line

  end module htmlplot    

【メモ帳】abstract type の用い方について

abstract type に concrete な routine

以前、下記の様な記事を書きましたが、この手法はけっこう適用範囲が広いのではないかと思えてきましたので、メモっておきます。

fortran66.hatenablog.com

はじめは abstract な interface に concrete な routine を置くのはいかがなものかと思いましたが、悪くない気がしてきました。

如何なる場合によろしいか

以前の例では、CG 法で逆行列を計算する時に、密行列と疎行列では行列とベクトルの積以外は全て共通に出来るので、下部の行列形式と積計算を後から実装すべき abstract interface とし、上部の CG 法の部分はこの段階で具体的に与えられるので、procedure pointer にぶら下げる形で abstract type のメンバーとして与えました。

つまり、下部が個々に後から実装すべきもので、その下部ルーチンを共通インターフェースを通して上部ルーチンが利用する場合、この手法が使えることになります。

これは、グラフィックス・ルーチンにも適用できます。個別のデバイス装置で点を打つ機能だけを abstract interface に則って実装すれば、点の集まりで線を引くところから先は全て共通になるので、はじめから procedure pointer にぶら下げておいてもいいことになります。

また procedure pointer は、point 先の procedure の差し替えが可能なので、デバイス装置固有の線引きルーチンの方が良ければ、付け替えられることになります。

ただ postscript ファイル出力でやってみると、procedure pointer に与えた型が合わないと叱られるので、select type を使ってやらねばならず面倒でした。(改善の余地がありますが、今後の課題とします。)

f:id:fortran66:20210530023052p:plain

例題

ここで、昨日分の記事のルーチンの改訂版を見てみることにします。全体は github に与えてあり、fpm (Fortran package manager) で実行できます。

github.com

abstract interface

pass 指定は明示しなくていいと思いますが、書いておきます。

init, point, show の三つは出力装置毎に実装する必要があるので、abstract interface に deferred 属性で与えておきます。

line, line0 は、出力装置固有の線引きルーチンを作りたい場合があるので、インターフェースを定めておきたいですが、点の連続打ちで実装を済ませる場合も多く、その場合装置に依存しない実装になるので、一か所で共通化しておきたくもあります。ここでのやり方で使い分けが実現できると思われます。

module device
    implicit none
    type, abstract :: device_t
        integer :: nx, ny
        procedure (device_line0), pointer, pass :: line0 => device_line0
        procedure (device_line ), pointer, pass :: line  => device_line
    contains
        procedure (device_init) , deferred, pass :: init     
        procedure (device_point), deferred, pass :: point 
        procedure (device_show) , deferred, pass :: show  
    end type device_t
    
    abstract interface
        subroutine device_init(fig)
            import :: device_t
            class(device_t), intent(in out) :: fig 
        end subroutine device_init  
        
        subroutine device_point(fig, ix, iy)
            import :: device_t
            class(device_t), intent(in out) :: fig 
            integer, intent(in) :: ix, iy
        end subroutine device_point
        
        subroutine device_show(fig)
            import :: device_t
            class(device_t), intent(in) :: fig
        end subroutine device_show    
    end interface

contains
    
    subroutine device_line0(fig, ix0, iy0, ix1, iy1)
        class(device_t), intent(in out) :: fig 
        integer, intent(in) :: ix0, iy0, ix1, iy1
        integer :: nx, ny, i
        real  :: d
        nx = ix1 - ix0
        ny = iy1 - iy0       
        if (nx == 0 .and. ny == 0) then
            call fig%point(ix0, iy0)
        else if (abs(nx) < abs(ny)) then
            d = nx / real(ny)
            do i = 0, ny, sign(1, ny) 
                call fig%point(nint(ix0 + i * d), iy0 + i)
            end do    
        else
            d = ny / real(nx)
            do i = 0, nx, sign(1, nx)
                call fig%point(ix0 + i, nint(iy0 + i * d))
            end do    
        end if
    end subroutine device_line0
   
    subroutine device_line(fig, x, y, ipen)
        class(device_t), intent(in out) :: fig
        real, intent(in) :: x, y
        integer, intent(in) :: ipen
        integer, save :: ix0 = 0, iy0 = 0 
        integer :: ix, iy
        real :: xn, yn, fx, fy
        xn = fig%nx / 2.0
        yn = fig%ny / 2.0 * 1.5
        fx = fig%nx / 150.0
        fy = fig%ny / 150.0
        ix = nint( fx * x + xn)      
        iy = nint(-fy * y + yn)
        if (ipen == 1) call fig%line0(ix0, iy0, ix, iy)
        ix0 = ix
        iy0 = iy
    end subroutine device_line
   
end module device

具体的実装1

unicode 点字

module uniplot
    use device
    implicit none
    private
    public :: fig_t
    type, extends(device_t) :: fig_t
        private
        integer, allocatable :: array(:, :)
    contains
        procedure :: init  
        procedure :: point 
        procedure :: show  
    end type fig_t
contains
    subroutine init(fig)
        class(fig_t), intent(in out) :: fig
        allocate(fig%array(0:(fig%nx+1)/2, 0:(fig%ny+3)/4), source = 0)
    end subroutine init  
  
    subroutine point(fig, ix, iy)
        class(fig_t), intent(in out) :: fig 
        integer, intent(in) :: ix, iy
        integer :: iax, iay
        iax = ix / 2
        iay = iy / 4
        ! clipping
        if (0<=ix .and. ix<fig%nx .and. 0<=iy .and. iy<fig%ny) then
            fig%array(iax, iay) = ior(fig%array(iax, iay), icode(mod(ix, 2), mod(iy, 4)))
        end if     
    end subroutine point
  
    pure elemental integer function icode(kx, ky)
        integer, intent(in) :: kx, ky
        if (ky == 3) then
            icode = 64 + 64 * kx
        else ! 0, 1, 2
            icode = 2**(ky + 3*kx)  
        end if
    end function icode
  
    subroutine show(fig)
        class(fig_t), intent(in) :: fig 
        integer :: iy
        do iy = 0, ubound(fig%array, 2)
            print '(*(a))', reverse_endian(shift_code(fig%array(:, iy)))
        end do
    end subroutine show    
  
    pure elemental integer function shift_code(k)
        integer, intent(in) :: k
        integer, parameter :: n0 = 14852224 ! Z'E2A080' 
        shift_code = n0 + 256 * (k /64) + mod(k, 64)  !E2A180, E2A280, E2A380      
    end function shift_code    
    
    pure elemental character(len = 4) function reverse_endian(i)
        integer, intent(in) :: i
        character:: tmp(4)
        tmp = transfer(i, ' ', size = 4)
        reverse_endian = transfer(tmp(4:1:-1), '    ')  !array 4 to len 4
    end function reverse_endian

end module uniplot

具体的実装2

簡易 X11 window

Finalizer に window 処理の終了処理を入れてみました。Finalizer は処理系によって挙動が異なるので問題になるかもしれません。gfortran ではうまくいっているようです。

#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 a 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 xplot
    use, intrinsic :: iso_c_binding
    use device
    implicit none
    private
    public :: fig_t

    interface
        subroutine Xopen(nx, ny) bind(c, name = 'X_open')
            use, intrinsic :: iso_c_binding , only : c_int
            integer(c_int), value :: nx, ny
        end subroutine Xopen

        subroutine Xpoint(ix, iy) bind(c, name = 'X_point')
            use, intrinsic :: iso_c_binding, only : c_int
            integer(c_int), 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

    type, extends(device_t) :: fig_t
        private
    contains
        procedure :: init     
        procedure :: point 
        procedure :: show  
        final :: x_close
    end type fig_t

contains

    subroutine init(fig)
        class(fig_t), intent(in out) :: fig 
        call Xopen(int(fig%nx, c_int), int(fig%ny, c_int))  
     !   call sleep(1)  ! non-standard : sleeps 1 sec
    end subroutine init  

    subroutine point(fig, ix, iy)
        class(fig_t), intent(in out) :: fig 
        integer, intent(in) :: ix, iy
        call Xpoint(int(ix, c_int), int(iy, c_int))
    end subroutine point

    subroutine show(fig)
       class(fig_t), intent(in) :: fig
       call XFlush() 
    end subroutine show    

    subroutine x_close(fig)
        type(fig_t), intent(in out) :: fig
        print *, 'press ENTER to continue'
        read *
        call Xclose()
    end subroutine x_close

end module xplot

具体的実装 3

postscript ファイル生成

module psplot
    use device
    implicit none
    private
    public :: fig_t
    type, extends(device_t) :: fig_t
        private
        character(len = :), allocatable, public :: fn
        integer, allocatable :: iw 
    contains 
        procedure :: init  
        procedure :: point 
        procedure :: show
        procedure :: filename
        final :: off  
    end type fig_t
contains

    subroutine init(fig)
        class(fig_t), intent(in out) :: fig
        allocate(fig%iw)
        fig%line0 => line0
        fig%line  => line
        if (.not. allocated(fig%fn)) fig%fn = 'figure'
        associate (iw => fig%iw, fn => fig%fn)
            open(newunit = iw, file = trim(fn) // '.ps')
            write(iw, '(a)') '%!PS-Adobe-3.0 EPSF-3.0'
            write(iw, '(a, 2i8)') '%%BoundingBox: 0 0 ', fig%nx, fig%ny
            write(iw, '(2a)') '%%Title: ', trim(fn)
            write(iw, '(a)') '%%EndComments'
            write(iw, '(a)') 'gsave'
            write(iw, '(a)') '1 1 scale'    !'0.8 0.8 scale'
            write(iw, '(a)') '1 setlinewidth'
            write(iw, '(a)') '0.0 0.0 0.0 setrgbcolor'
            write(iw, '(a)') '2 setlinejoin'
            write(iw, '(a, i8, a)') '0 ', fig%ny, ' translate'
            write(iw, '(a)') 'newpath'
        end associate
    end subroutine init

    subroutine filename(fig, fn)
        class(fig_t), intent(in out) :: fig
        character(*), intent(in) :: fn
        fig%fn = fn
    end subroutine filename

    subroutine off(fig)
        type(fig_t), intent(in) :: fig
        write(fig%iw, '(a)') 'stroke'
        write(fig%iw, '(a)') 'showpage'
        write(fig%iw, '(a)') 'grestore'
        write(fig%iw, '(a)') '%%EOF'
        close(fig%iw)
    end subroutine off

    subroutine point(fig, ix, iy)
        class(fig_t), intent(in out) :: fig 
        integer, intent(in) :: ix, iy
        write(fig%iw, '(a)') 'newpath'
        write(fig%iw, '(*(g0))') ix, ' ', -iy, ' 0.5 0 360 arc fill'
    end subroutine point
 
    subroutine show(fig)
        class(fig_t), intent(in) :: fig
        write(fig%iw, '(a)') 'stroke'
        write(fig%iw, '(a)') 'newpath'
    end subroutine show

    subroutine line0(fig, ix0, iy0, ix1, iy1)
        class(device_t), intent(in out) :: fig
        integer, intent(in) :: ix0, iy0, ix1, iy1
        select type (fig)
        type is (fig_t)
            write(fig%iw, '(2i7, a)') ix0, -iy0, ' moveto'
            write(fig%iw, '(2i7, a)') ix1, -iy1, ' lineto'
        end select
      end subroutine line0

    subroutine line(fig, x, y, ipen)
        class(device_t), intent(in out) :: fig
        real, intent(in) :: x, y
        integer, intent(in) :: ipen
        real :: xn, yn, fx, fy
        select type (fig)
        type is (fig_t)
            xn = fig%nx / 2.0
            yn = fig%ny / 2.0 * 1.5
            fx = fig%nx / 150.0
            fy = fig%ny / 150.0
            if (ipen == 1) then 
                write(fig%iw, '(2f10.3, a)') fx * x + xn, fy * y - yn, ' lineto'
            else 
                write(fig%iw, '(2f10.3, a)') fx * x + xn, fy * y - yn, ' moveto'
            end if 
        end select  
    end subroutine line

  end module psplot    

【メモ帳】fpm に X11 用の c ファイル入れる

fpm に c プログラム入れて X window に図

fpm を利用して unicode plot と X-window で同じ図を出すことにします。

fortran66.hatenablog.com

そのためには、fpm に c プログラムを認識させ、X11 のライブラリとリンクさせる必要があります。fpm 公式 gihub に例題集があって、それを見ることで何とかなりました。

github.com

fpm.toml の [build] に link = "X11" を付け加えるだけで解決します。

[build]
auto-executables = true
auto-tests = true
auto-examples = true

link = "X11" 

実行結果

f:id:fortran66:20210529022716p:plain

プログラム

OO 式に abstract type で抽象クラスを書いておきました。これによって fig オブジェクトを allocate する時に、どのデバイスに出すかを決定しています。そこ以外は共通のプログラムで書けます。

改訂版

下に貼ったソースコードを改訂したものを github に上げました。

github.com

git clone https://github.com/f66blog/xplot
cd xplot
fpm run
fpm test

f:id:fortran66:20210529223629p:plain

メインプログラム app/main.f90

program main
    use device
    implicit none  
    class (device_t), allocatable :: fig
    real, allocatable :: x(:), y(:)
    integer :: n = 10**3
    allocate(x(n), y(n))
    call random_seed()
    call random_number(x)
    call random_number(y)  

uplot: block
        use  uniplot
        allocate(fig, source = fig_t(100, 100))
        call monte_carlo(fig, x, y) 
        deallocate(fig)
    end block uplot

xplot: block
        use xplot
        allocate(fig, source = fig_t(450, 450))
        call monte_carlo(fig, x, y) 
        deallocate(fig)
    end block xplot

contains
    subroutine monte_carlo(fig, x, y)
        class(device_t), intent(in out) :: fig
        real, intent(in) :: x(:), y(:)
        integer :: i, ix0, iy0, ix1, iy1, k
        k = fig%nx
        print *
        print *, 'Monte Carlo: estimated pi =', 4.0 * count(x**2 + y**2 < 1.0) / size(x) 
        call fig%init()
        ! draw box
        call fig%line0(0, 0, k-1, 0)
        call fig%line0(0, 0, 0, k-1)
        call fig%line0(0, k-1, k-1, k-1)
        call fig%line0(k-1, 0, k-1, k-1)
        ! draw 1/4 circle
        ix0 = 0
        iy0 = 0
        do ix1 = 0, k - 1
            iy1 = k - 1 - int(sqrt(real((k-1)**2 - ix1**2)))
            call fig%line0(ix0, iy0, ix1, iy1)
            ix0 = ix1
            iy0 = iy1
        end do           
  
        do i = 1, n 
            call fig%point(int(k * x(i)), int(k * y(i)))
        end do 
        call fig%show()
    end subroutine monte_carlo

end program main

抽象クラス定義 src/device.f90

module device
    implicit none
    type, abstract :: device_t
        integer :: nx, ny
    contains
        procedure (device_init) , deferred, pass :: init     
        procedure (device_point), deferred, pass :: point 
        procedure (device_show) , deferred, pass :: show  
        procedure (device_line0), deferred, pass :: line0
        procedure (device_line) , deferred, pass :: line
    end type device_t
    
    abstract interface
        subroutine device_init(fig)
            import :: device_t
            class(device_t), intent(in out) :: fig 
        end subroutine device_init  
        
        subroutine device_point(fig, ix, iy)
            import :: device_t
            class(device_t), intent(in out) :: fig 
            integer, intent(in) :: ix, iy
        end subroutine device_point
        
        subroutine device_show(fig)
            import :: device_t
            class(device_t), intent(in) :: fig
        end subroutine device_show    
        
        subroutine device_line0(fig, ix0, iy0, ix1, iy1)
            import :: device_t
            class(device_t), intent(in out) :: fig 
            integer, intent(in) :: ix0, iy0, ix1, iy1
        end subroutine device_line0
        
        subroutine device_line(fig, x, y, ipen)
            import :: device_t
            class(device_t), intent(in out) :: fig
            real, intent(in) :: x, y
            integer, intent(in) :: ipen
        end subroutine device_line
    end interface
    
end module device

unicode plot 用モジュール src/uniplot.f90

module uniplot
    use device
    implicit none
    private
    public :: fig_t
    type, extends(device_t) :: fig_t
        private
        integer, allocatable :: array(:, :)
    contains
        procedure :: init  
        procedure :: point 
        procedure :: show  
        procedure :: line0
        procedure :: line
    end type fig_t
contains
    subroutine init(fig)
        class(fig_t), intent(in out) :: fig
        allocate(fig%array(0:(fig%nx+1)/2, 0:(fig%ny+3)/4) )
    end subroutine init  
  
    subroutine point(fig, ix, iy)
        class(fig_t), intent(in out) :: fig 
        integer, intent(in) :: ix, iy
        integer :: iax, iay
        iax = ix / 2
        iay = iy / 4
        ! clipping
        if (0<=ix .and. ix<fig%nx .and. 0<=iy .and. iy<fig%ny) then
            fig%array(iax, iay) = ior(fig%array(iax, iay), icode(mod(ix, 2), mod(iy, 4)))
        end if     
    end subroutine point
  
    pure elemental integer function icode(kx, ky)
        integer, intent(in) :: kx, ky
        if (ky == 3) then
            icode = 64 + 64 * kx
        else ! 0, 1, 2
            icode = 2**(ky + 3*kx)  
        end if
    end function icode
  
    subroutine line0(fig, ix0, iy0, ix1, iy1)
        class(fig_t), intent(in out) :: fig 
        integer, intent(in) :: ix0, iy0, ix1, iy1
        integer :: i, ix, iy, nx, ny
        real :: d
        nx = ix1 - ix0
        ny = iy1 - iy0
        if (nx == 0 .and. ny ==0) then
            call fig%point(ix, iy) 
        else if (abs(nx) < abs(ny)) then
            d = nx / real(ny)
            do i = 0, ny, sign(1, ny) 
                call fig%point(nint(ix0 + i * d), iy0 + i)
            end do    
        else
            d = ny / real(nx)
            do i = 0, nx, sign(1, nx)
                call fig%point(ix0 + i, nint(iy0 + i * d))
            end do  
        end if  
    end subroutine line0
  
    subroutine show(fig)
        class(fig_t), intent(in) :: fig 
        integer :: iy
        do iy = 0, ubound(fig%array, 2)
            print '(*(a))', reverse_endian(shift_code(fig%array(:, iy)))
        end do
    end subroutine show    
  
    pure elemental integer function shift_code(k)
        integer, intent(in) :: k
        integer, parameter :: n0 = Z'E2A080' !14852224
        shift_code = n0 + 256 * (k /64) + mod(k, 64)  !E2A180, E2A280, E2A380      
    end function shift_code    
    
    pure elemental character(len = 4) function reverse_endian(i)
        integer, intent(in) :: i
        character:: tmp(4)
        tmp = transfer(i, ' ', size = 4)
        reverse_endian = transfer(tmp(4:1:-1), '    ')  !array 4 to len 4
    end function reverse_endian
    
    
    subroutine line(fig, x, y, ipen)
        class(fig_t), intent(in out) :: fig
        real, intent(in) :: x, y
        integer, intent(in) :: ipen
        integer, save :: ix0 = 0, iy0 = 0 
        integer :: ix, iy
        real, parameter :: xn = 80.0, yn = 100.0, fx = 1.0, fy = 0.85
        ix = nint( fx * x + xn)      
        iy = nint(-fy * y + yn)
        if (ipen == 1) call fig%line0(ix0, iy0, ix, iy)
        ix0 = ix
        iy0 = iy
    end subroutine line
  
end module uniplot

X window 用 plot モジュール src/xplot.f90

module xplot
    use device
    implicit none
    private
    public :: fig_t

    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

    type, extends(device_t) :: fig_t
        private
    contains
        procedure :: init     
        procedure :: point 
        procedure :: show  
        procedure :: line0
        procedure :: line
        final :: x_close
    end type fig_t

contains

    subroutine init(fig)
        class(fig_t), intent(in out) :: fig 
        call Xopen(fig%nx, fig%ny)  
     !   call sleep(1)  ! non-standard
    end subroutine init  

    subroutine point(fig, ix, iy)
        class(fig_t), intent(in out) :: fig 
        integer, intent(in) :: ix, iy
        call Xpoint(ix, iy)
    end subroutine point

    subroutine show(fig)
       class(fig_t), intent(in) :: fig
       call XFlush() 
    end subroutine show    

    subroutine line0(fig, ix0, iy0, ix1, iy1)
        class(fig_t), intent(in out) :: fig 
        integer, intent(in) :: ix0, iy0, ix1, iy1
        integer :: nx, ny, i
        real  :: d
        nx = ix1 - ix0
        ny = iy1 - iy0       
        if (nx == 0 .and. ny == 0) then
            call Xpoint(ix0, iy0)
        else if (abs(nx) < abs(ny)) then
            d = nx / real(ny)
            do i = 0, ny, sign(1, ny) 
                call Xpoint(nint(ix0 + i * d), iy0 + i)
            end do    
        else
            d = ny / real(nx)
            do i = 0, nx, sign(1, nx)
                call Xpoint(ix0 + i, nint(iy0 + i * d))
            end do    
        end if
    end subroutine line0

    subroutine x_close(fig)
        type(fig_t), intent(in out) :: fig
        print *, 'press ENTER to continue'
        read *
        call Xclose()
    end subroutine x_close

  
    subroutine line(fig, x, y, ipen)
        class(fig_t), intent(in out) :: fig
        real, intent(in) :: x, y
        integer, intent(in) :: ipen
        integer, save :: ix0 = 0, iy0 = 0 
        integer :: ix, iy
        real, parameter :: xn = 250.0, yn = 600.0, fx = 4.0, fy = 4.0
        ix = nint( fx * x + xn)      
        iy = nint(-fy * y + yn)
        if (ipen == 1) call fig%line0(ix0, iy0, ix, iy)
        ix0 = ix
        iy0 = iy
    end subroutine line

end module xplot

X11 c 言語インターフェース src/plot.c

#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 a 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);
}

【メモ帳】点字プロットで作図

Logistic map & Lorenz attractor

昔のインターフェースを流用して、作図してみます。

fortran66.hatenablog.com

fortran66.hatenablog.com

f:id:fortran66:20210528032958p:plain 横に縞が入るのが、昔の電送写真のようでいいですね。

プログラム

device.f90

    module device
        implicit none

        type, abstract :: device_t
            integer :: nsize_x = 640, nsize_y = 480
            character(len = 80) :: title = 'Plotter'
            integer :: width = 1, color = 0
        contains
            procedure (device_on)  , deferred, pass :: on
            procedure (device_off) , deferred, pass :: off
            procedure (device_show), deferred, pass :: show
            procedure (device_pen) , deferred, pass :: pen
            procedure (device_line), deferred, pass :: line
            procedure (device_move), deferred, pass :: move
        end type device_t 
  
        abstract interface 
            subroutine device_on(self)
                import :: device_t
                class(device_t), intent(in out) :: self
            end subroutine device_on
      
            subroutine device_off(self, isec)
                import :: device_t
                class(device_t), intent(in out) :: self
                integer, intent(in), optional :: isec 
            end subroutine device_off
    
            subroutine device_show(self)
                import :: device_t
                class(device_t), intent(in) :: self
            end subroutine device_show
        
            subroutine device_pen(self, iwidth, icolor)
                import :: device_t
                class(device_t), intent(in out) :: self
                integer, intent(in), optional :: iwidth, icolor
            end subroutine device_pen
    
            subroutine device_line(self, ix, iy)
                import :: device_t
                class(device_t), intent(in out) :: self
                integer, intent(in) :: ix, iy
            end subroutine device_line
      
            subroutine device_move(self, ix, iy)
                import :: device_t
                class(device_t), intent(in out) :: self
                integer, intent(in) :: ix, iy
            end subroutine device_move
        end interface
  
    end module device

unicode.f90

module unicode
    use device
    implicit none
    private
    public :: unicode_t
    type, extends(device_t) :: unicode_t
        private
        integer :: ix0 = 0, iy0 = 0
        integer, allocatable :: array(:, :)
    contains
        procedure, pass :: on   => unicode_on
        procedure, pass :: off  => unicode_off
        procedure, pass :: show => unicode_show
        procedure, pass :: pen  => unicode_pen
        procedure, pass :: line => unicode_line
        procedure, pass :: move => unicode_move
        procedure, pass :: point => unicode_point
        procedure, pass :: line0       
    end type unicode_t
contains
    subroutine unicode_on(self)
        class(unicode_t), intent(in out) :: self 
        allocate(self%array(0:(self%nsize_x+1)/2, 0:(self%nsize_y+3)/4), source = 0 )
    end subroutine unicode_on  

    subroutine unicode_off(self, isec)
        class(unicode_t), intent(in out) :: self
        integer, intent(in), optional :: isec  
        deallocate(self%array)
    end subroutine unicode_off  

    subroutine unicode_show(self)
        class(unicode_t), intent(in) :: self 
        integer :: iy
        do iy = 0, ubound(self%array, 2)
            print '(*(a))', reverse_endian(shift_code(self%array(:, iy)))
        end do
    end subroutine unicode_show   

    subroutine unicode_pen(self, iwidth, icolor)
        class(unicode_t), intent(in out) :: self
        integer, intent(in), optional :: iwidth, icolor
        return
    end subroutine unicode_pen    

    subroutine unicode_move(self, ix, iy)
        class(unicode_t), intent(in out) :: self
        integer, intent(in) :: ix, iy
        self%ix0 = ix 
        self%iy0 = iy
    end subroutine unicode_move    

    subroutine unicode_line(self, ix, iy)
        class(unicode_t), intent(in out) :: self
        integer, intent(in) :: ix, iy
        call self%line0(self%ix0, self%iy0, ix, iy)
        self%ix0 = ix 
        self%iy0 = iy
    end subroutine unicode_line    



    subroutine unicode_point(self, ix, iy)
        class(unicode_t), intent(in out) :: self 
        integer, intent(in) :: ix, iy
        integer :: iax, iay
        iax = ix / 2
        iay = iy / 4
        ! clipping
        if (0 <= iax .and. iax <= ubound(self%array, 1) .and. &
            0 <= iay .and. iay <= ubound(self%array, 2) ) then
            self%array(iax, iay) = ior(self%array(iax, iay), icode(mod(ix, 2), mod(iy, 4)))
        end if     
    end subroutine unicode_point

    pure elemental integer function icode(kx, ky)
        integer, intent(in) :: kx, ky
        if (ky == 3) then
            icode = 64 + 64 * kx
        else ! 0, 1, 2
            icode = 2**(ky + 3*kx)  
        end if
    end function icode

    subroutine line0(self, ix0, iy0, ix1, iy1)
        class(unicode_t), intent(in out) :: self 
        integer, intent(in) :: ix0, iy0, ix1, iy1
        integer :: i, ix, iy, nx, ny 
        real :: d
        nx = ix1 - ix0
        ny = iy1 - iy0  
        if (nx == 0 .and. ny == 0) then
            call self%point(ix0, iy0)
        else if (abs(nx) < abs(ny)) then
            d = nx / real(ny)
            do i = 0, abs(ny)
                ix = nint(ix0 + d * sign(i, ny))
                iy = iy0 + sign(i, ny)
                call self%point(ix, iy)
            end do  
        else
            d = ny / real(nx)
            do i = 0, abs(nx)
                iy = nint(iy0 + d * sign(i, nx))
                ix = ix0 + sign(i, nx)
                call self%point(ix, iy)
            end do  
        end if  
    end subroutine line0

    pure elemental integer function shift_code(k)
        integer, intent(in) :: k
        integer, parameter :: n0 = Z'E2A080' !14852224
        shift_code = n0 + 256 * (k /64) + mod(k, 64)  !E2A180, E2A280, E2A380      
    end function shift_code    
  
    pure elemental character(len = 4) function reverse_endian(i)
        integer, intent(in) :: i
        character:: tmp(4)
        tmp = transfer(i, ' ', size = 4)
        reverse_endian = transfer(tmp(4:1:-1), '    ')  !array 4 to len 4
    end function reverse_endian
  
  
    subroutine line(self, x, y, ipen)
        class(unicode_t), intent(in out) :: self
        real, intent(in) :: x, y
        integer, intent(in) :: ipen
        integer, save :: ix0 = 0, iy0 = 0 
        integer :: ix, iy
        real, parameter :: xn = 80.0, yn = 100.0, fx = 1.0, fy = 0.85
        ix = nint( fx * x + xn)      
        iy = nint(-fy * y + yn)
        if (ipen == 1) call self%line0(ix0, iy0, ix, iy)
        ix0 = ix
        iy0 = iy
    end subroutine line

end module unicode

main.f90

program test
    use unicode
    implicit none
    class (unicode_t), allocatable :: fig
  
    chaos: block
        integer :: ix, i, nx = 150, ny = 80
        real :: p, x, y
        allocate(fig, source = unicode_t(nx, ny, 'Chaos'))
        print *,  'Chaos'
        call fig%on()
        do ix = 1, nx
            p = 0.3
            x = ix * (3.0 - 1.5) / nx + 1.5 
            do i = 1, 50
                p = p + x * p * (1.0 - p)
            end do
            do i = 51, 100
                y = p / 1.5 * ny
                call fig%move(ix - 1, ny - INT(y))
                call fig%line(ix - 0, ny - INT(y))
                p = p + x * p * (1 - p)
            end do
        end do
        call fig%show()
        call fig%off()
        deallocate(fig)
    end block chaos


    Lorenz_attractor: block
        integer, parameter :: kd = kind(1.0d0)
        real (kd) :: x, y, z, dx, dy, dz, a, b, c, d
        integer :: k, mult = 2, nx = 150, ny = 100
        allocate(fig, source = unicode_t(nx, ny, 'Lorenz attractor'))
        print *,  'Lorenz attractor'
        call fig%on()
        a = 10.0d0
        b = 28.0d0
        c = 8.0d0 / 3.0d0
        d = 0.01d0
        x = 1.0d0
        y = 1.0d0
        z = 1.0d0
        do k = 1, 3000
            dx = a * (y - x)
            dy = x * (b - z) - y
            dz = x * y - c * z
            x = x + d * dx
            y = y + d * dy
            z = z + d * dz
            if (k < 100) then 
                call fig%move( INT(mult * (x + 30)), INT(mult * z) )
            else
                call fig%line( INT(mult * (x + 30)), INT(mult * z) )
            end if 
        end do
        call fig%show()
        deallocate(fig)
    end block Lorenz_attractor 

end program test

【メモ帳】点字 plot で pi

Monte Carlo で素朴に円周率を計算してみます

1000 点での見積もりなので二桁ほどの精度もありません。 でも点数を増やすと図がつぶれてしまいますw

f:id:fortran66:20210528104710p:plain

実行結果

 Monte Carlo: estimated pi =   3.19600010    
⡟⡛⠛⡛⠛⠛⠛⢿⠭⠭⣩⣉⣙⢉⠉⠙⠉⡩⠉⢉⠋⠉⠍⠉⠍⠝⠉⠉⠉⠉⠉⠉⠉⣙⠝⠉⠉⠍⠉⠉⠉⢙⠉⠩⠉⡉⠉⠉⠉⢹⠀
⡧⠄⠈⠂⠂⢐⠁⠡⢄⠁⠖⠀⠈⠉⠛⠒⠤⣒⣀⠀⠀⠁⠀⠊⠀⠐⡂⠌⡀⠀⡀⠀⠀⢀⢀⢐⠑⠀⠈⠐⠀⠁⠢⠀⠀⠈⠁⠀⠀⢸⠀
⡏⡀⠁⠀⠠⠁⠂⠀⠀⠀⠐⢀⠐⠀⡀⠒⠄⠐⠨⠱⡒⠴⣀⠀⠐⠀⡢⠀⠀⠄⡀⠀⠌⠁⡉⠁⠀⡀⠀⠀⠀⠈⠀⠀⠁⡀⠐⠀⠂⣸⠀
⡇⡀⠄⠀⠁⠂⠀⠀⠂⢀⢠⠀⠀⠀⠀⠀⠀⠂⠂⠀⠀⠀⠀⠉⠒⢄⡀⠂⢀⠀⠃⠀⠠⢀⠄⡠⠀⠀⠀⠀⠀⠀⠀⠄⢀⠀⢈⠢⠀⢸⠀
⡧⠀⡈⠂⠤⠄⠈⢐⠂⡐⡠⢤⠈⠀⠀⡀⠀⡀⠀⠑⠠⡀⠄⠁⠀⠈⢘⠖⢄⡘⠠⠂⠊⠀⡀⡠⠠⠀⠠⠐⠀⠀⠂⠀⠂⠈⠑⠂⠀⢸⠀
⡇⠰⢄⢐⠂⠀⠀⠀⠀⠨⠄⠈⠄⠠⣬⠀⠀⠀⢐⠁⠂⠄⡀⡀⠠⠀⠀⠀⠀⠈⠢⢄⠀⠀⡀⠠⠀⠀⠀⠀⠀⠑⠀⠄⠀⠐⠀⠂⠀⢸⠀
⡏⠕⠈⠖⠁⣀⠀⠀⠀⠀⠈⠠⠀⠀⠃⣠⠀⠀⠀⠆⠀⡀⠀⠈⠀⠈⠀⠀⠀⢀⠄⠠⠑⢄⠀⠀⠂⡒⠀⠈⢂⠀⠁⠀⠠⠅⡀⠀⠆⢸⠀
⣇⠀⡀⠀⠀⠀⠠⠀⠄⢀⠀⠘⠁⠄⠀⠀⠉⠈⠠⢠⡀⠠⠂⠀⠀⡀⠀⠁⡈⠀⢠⠀⠀⠀⣑⢺⠀⠀⠀⠀⠄⠈⠁⠀⡔⠀⡀⠀⠂⢸⠀
⡇⠌⠈⠠⠀⢀⢀⠀⢀⠄⡂⠠⢞⠀⠁⠀⠐⠑⠡⠀⠀⠠⠀⢄⠀⠀⠀⡀⠖⠀⡀⠌⠀⠀⠀⠀⠑⣤⠈⠈⠀⠊⠀⢠⠀⠄⢂⠀⡀⣺⠀
⡗⠀⠀⠀⠐⠀⠠⠀⠀⠐⠀⠀⠣⠀⠠⢋⠂⠠⠀⡀⠁⠆⠀⠄⠄⠀⠄⠢⠀⠀⠀⢀⠈⡈⠀⡌⠂⠀⠑⡄⡀⠄⡀⠀⠀⠀⠢⠀⠁⢸⠀
⣇⠂⠐⠀⠄⠠⢀⠈⡰⠀⠀⠀⠅⠀⠁⠀⠀⠀⢀⠠⠀⠐⠄⠃⠁⡈⠀⠀⡠⠀⡄⡒⠀⠠⠀⠀⠉⠀⠀⡈⣢⠀⠁⠠⠀⡢⡀⡠⠀⢸⠀
⡇⢀⠐⠀⢈⠈⠀⠁⢀⠶⢄⢀⠀⠀⠀⠀⡁⠀⠆⠠⠀⠀⠀⠀⠀⠑⡐⠈⠠⠐⠀⠁⠢⡤⠄⠆⠀⠀⠄⠀⠀⠱⣀⠠⠂⠀⢀⠈⠀⢸⠀
⣇⠄⠐⠀⠀⠀⠀⢁⠀⠐⠠⠐⠈⠀⠆⠂⠀⠄⠀⠀⠄⠀⠀⠀⠄⠃⠀⠐⡀⠀⠀⠀⠔⢀⠡⠀⠆⠁⠀⠀⠀⠂⠘⢕⠀⢂⠠⠁⠀⢸⠀
⡇⠀⠁⠐⠆⡘⢄⠄⠀⠀⠈⢠⠂⠢⠀⢀⡄⠀⠁⠀⠀⠀⡄⡀⠀⠀⠁⠀⠀⠀⠀⠄⠀⠀⠐⠈⠀⠀⡆⠂⠀⠁⠁⠈⢦⠀⠀⠀⠀⢸⠀
⡏⠀⠀⠀⠀⠀⠀⠈⠀⢐⠀⢀⠀⠂⠀⠀⠀⠀⠀⠀⠀⠀⠠⠀⠄⢀⠀⠂⠂⠀⢀⢠⠀⠈⠀⠐⠐⠁⠀⠀⠀⠀⢀⠀⠈⡆⡀⠀⠁⢸⠀
⣇⠀⠀⠀⠀⠀⠀⠂⠀⠀⠘⡀⡀⠁⠨⠀⠐⠀⡀⠀⠠⠁⠈⡀⠂⠂⠂⢘⠂⠀⠂⠀⠀⠀⠌⠀⠠⠠⠀⠀⠐⠀⠐⠀⠀⠙⡕⠂⠀⢸⠀
⡧⢀⠠⠀⠀⠱⢄⠀⠀⠐⠄⠤⢀⠀⠀⠀⠠⠈⠀⠡⠀⢀⠀⠄⠃⠀⠒⠄⠈⢂⠡⠀⢀⡈⠃⠈⡀⠀⠀⢀⡰⠑⠀⠈⠀⠁⠱⡀⡀⢸⠀
⡇⠑⠀⢀⠄⠠⠀⠀⠀⠀⡀⠠⠀⠂⠨⡁⡂⠂⠈⠔⠀⠡⠂⠢⠀⠀⠀⢄⠀⡀⡀⠀⣐⠠⠀⠀⠂⠀⠀⠀⠀⠐⠈⠀⠀⡐⠀⢣⠀⢸⠀
⡇⡀⠀⠁⠠⠀⠀⠀⠀⠈⠀⠂⠀⠊⠀⠀⠨⢊⠄⢀⡀⡀⠠⠀⠀⠀⠀⠀⠀⠀⠀⠂⡠⠀⠄⠀⠀⠀⢈⠀⠁⠀⠀⢠⠠⠀⠀⠘⡌⢸⠀
⡇⠄⠀⠐⠀⠀⠀⠀⠔⡀⠃⡠⠀⠀⡈⠀⠱⢐⠐⢄⠀⠄⠀⠀⠐⠀⠑⠤⠀⠀⠀⠈⠀⡈⠀⠀⠀⠁⡀⢁⠁⠀⠀⠈⠀⠀⠠⠠⢫⢸⠀
⡇⡃⠂⡀⠀⠂⠁⠐⠄⢀⠁⠈⠀⠅⠀⠀⠈⠀⠈⠀⡀⠀⣀⠀⠡⠀⠀⠀⠀⠀⠠⢀⠙⠈⢁⠂⢁⢀⠀⢀⠠⢄⠀⠀⡀⠁⠀⠀⠸⣹⠀
⡇⡀⠠⠄⠀⠀⠀⠁⡄⠄⠁⡂⠀⠐⠈⠀⢑⠀⠀⠀⠀⠀⠠⠈⠀⡀⠑⠪⠀⠂⠀⠁⠌⢀⠈⠁⠀⠀⠀⠀⠀⡀⠀⠀⢀⠀⠀⠐⡄⣿⠀
⡇⠀⡂⢀⠀⠀⠀⠀⠀⠂⠀⠄⠀⠄⠀⠀⠀⠀⠂⠀⠄⠀⠀⠜⢀⠉⠀⠁⠀⠀⠐⠀⠈⠀⠂⠉⢀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠁⡘⣿⠀
⡏⠐⠠⠐⠀⠀⠐⠀⠄⠀⠀⡈⠀⠀⢁⡀⠈⠀⠐⠉⠀⠨⠄⠁⠨⠢⠄⠀⠑⠀⠀⢀⣄⠁⠄⠈⠈⡀⠐⠁⠀⠐⠄⠀⠂⠈⠄⠀⢀⢸⠀
⣇⣀⣄⣄⣀⣀⣄⣥⣀⣂⣁⣈⣀⣈⣀⣀⣄⣈⣐⣐⣀⣀⣁⣀⣄⣕⣀⣀⣀⣀⣂⣀⣀⣀⣀⣂⣀⣐⣀⣀⣰⣂⣂⣨⣀⣀⣑⣑⣄⣼⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

プログラム

はみ出しをクリッピングするようにしました。

module uniplot
    implicit none
    private
    public :: fig_t
    type :: fig_t
        private
        integer :: nx, ny
        integer, allocatable :: array(:, :)
  contains
        procedure :: init     
        procedure :: point 
        procedure :: show  
        procedure :: line0
        procedure :: line
    end type fig_t
contains
    subroutine init(fig, nx, ny)
        class(fig_t), intent(out) :: fig 
        integer, intent(in) :: nx, ny
        fig%nx = nx
        fig%ny = ny 
        allocate(fig%array(0:(nx+1)/2, 0:(ny+3)/4) )
    end subroutine init  

  subroutine point(fig, ix, iy)
      class(fig_t), intent(in out) :: fig 
      integer, intent(in) :: ix, iy
      integer :: iax, iay
      iax = ix / 2
      iay = iy / 4
      ! clipping
      if (0<=ix .and. ix<fig%nx .and. 0<=iy .and. iy<fig%ny) then
          fig%array(iax, iay) = ior(fig%array(iax, iay), icode(mod(ix, 2), mod(iy, 4)))
      end if     
  end subroutine point

  pure elemental integer function icode(kx, ky)
      integer, intent(in) :: kx, ky
      if (ky == 3) then
          icode = 64 + 64 * kx
      else ! 0, 1, 2
          icode = 2**(ky + 3*kx)  
      end if
  end function icode

  subroutine line0(fig, ix0, iy0, ix1, iy1)
      class(fig_t), intent(in out) :: fig 
      integer, intent(in) :: ix0, iy0, ix1, iy1
      integer :: i, ix, iy, nx, ny
      real :: d
      nx = ix1 - ix0
      ny = iy1 - iy0
      if (nx == 0 .and. ny ==0) then
          call fig%point(ix, iy) 
      else if (abs(nx) < abs(ny)) then
          d = nx / real(ny)
          do i = 0, abs(ny)
              ix = nint(ix0 + d * sign(i, ny))
              iy = iy0 + sign(i, ny)
              call fig%point(ix, iy)
            end do  
      else
          d = ny / real(nx)
          do i = 0, abs(nx)
              iy = nint(iy0 + d * sign(i, nx))
              ix = ix0 + sign(i, nx)
              call fig%point(ix, iy)
          end do  
      end if  
  end subroutine line0

  subroutine show(fig)
      class(fig_t), intent(in) :: fig 
      integer :: iy
      do iy = 0, ubound(fig%array, 2)
          print '(*(a))', reverse_endian(shift_code(fig%array(:, iy)))
      end do
  end subroutine show    

  pure elemental integer function shift_code(k)
      integer, intent(in) :: k
      integer, parameter :: n0 = Z'E2A080' !14852224
      shift_code = n0 + 256 * (k /64) + mod(k, 64)  !E2A180, E2A280, E2A380      
  end function shift_code    
  
  pure elemental character(len = 4) function reverse_endian(i)
      integer, intent(in) :: i
      character:: tmp(4)
      tmp = transfer(i, ' ', size = 4)
      reverse_endian = transfer(tmp(4:1:-1), '    ')  !array 4 to len 4
  end function reverse_endian
  
  
  subroutine line(fig, x, y, ipen)
      class(fig_t), intent(in out) :: fig
      real, intent(in) :: x, y
      integer, intent(in) :: ipen
      integer, save :: ix0 = 0, iy0 = 0 
      integer :: ix, iy
      real, parameter :: xn = 80.0, yn = 100.0, fx = 1.0, fy = 0.85
      ix = nint( fx * x + xn)      
      iy = nint(-fy * y + yn)
      if (ipen == 1) call fig%line0(ix0, iy0, ix, iy)
      ix0 = ix
      iy0 = iy
  end subroutine line

end module uniplot
program uniplot_main
    use :: uniplot
    implicit none
    real, allocatable :: x(:), y(:)
    integer :: n
    n = 10**3
    allocate(x(n), y(n))
    call random_seed()
    call random_number(x)
    call random_number(y)

plot: block 
        type(fig_t) :: fig1
        integer :: i, ix0, iy0, ix1, iy1, k
        k = 100
        print *
        print *, 'Monte Carlo: estimated pi =', 4.0 * count(x**2 + y**2 < 1.0) / n 
        call fig1%init(k, k)
        ! draw box
        call fig1%line0(0, 0, k-1, 0)
        call fig1%line0(0, 0, 0, k-1)
        call fig1%line0(0, k-1, k-1, k-1)
        call fig1%line0(k-1, 0, k-1, k-1)
        ! draw 1/4 circle
        ix0 = 0
        iy0 = 0
        do ix1 = 0, k - 1
            iy1 = k - 1 - int(sqrt(real((k-1)**2 - ix1**2)))
            call fig1%line0(ix0, iy0, ix1, iy1)
            ix0 = ix1
            iy0 = iy1
        end do     

        do i = 1, n 
            call fig1%point(int(k * x(i)), int(k * y(i)))
        end do 
        call fig1%show()
    end block plot
  end program uniplot_main

【寝言】金星 水星 月食

月食 金星 水星?

男色家阿部さんの次は月食か。

天気が悪くて見えないかと思っていましたが、夕刻にかけて少し晴れ間が出ました。ふと西の空を見ると、金星がまさに沈もうとするところで、少し上の方に水星らしき星もかすかに見えていました。金星と水星がもうすぐ 0.5 度(月の見かけ大きさ程度)まで接近するらしいので天気良くあって欲しいのですが・・・

写真では水星らしきものが見えますが、スマホデジカメのノイズかもしれませんw f:id:fortran66:20210526224252j:plain

東の空を見ると肉眼では月がかなりかけて見えましたが、スマホのカメラ越しにみると結構まだ丸く光って見えました。赤外方向に感度が高いせいでしょうか?薄雲のせいで写真では綺麗に取れませんでした。

もう少し遅く、皆既食の時間帯には、さそり座のアンタレスらしき星も見えました。 月の下の方、電柱のトランスのわきの星がアンタレス、月の右斜め上にサソリの頭の星の内二つが写っているのではないかと思います。 f:id:fortran66:20210526224518j:plain

https://www.honda.co.jp/outdoor/knowledge/constellation/picture-book/scorpius/images/main_full_pc.png

www.honda.co.jp

さそり座の女/お金をちょうだい/おんなの朝

さそり座の女/お金をちょうだい/おんなの朝

  • アーティスト:美川憲一
  • 発売日: 2003/08/21
  • メディア: CD