Perlでジュリア集合を描く(中編)

だいぶ時間があいてしまったけど、とりあえず続きを書く。

手順としてはこんな感じ。

  1. ランダムパラメータを使って、低解像度のデータを量産する
  2. 適当なパラメータでレンダリングする
  3. 使えそうなデータを選択して、データの生成範囲を再検討する
  4. パラメータを変更しながらレンダリング

非常に効率が悪いんだけど、
どうすればどういう感じになるのかも分からなければ、
良い感じにレンダリングする術もないので、今は仕方ない。

そんな感じで、パラメータと生成範囲を決めてデータを出力するスクリプトはこちら。

use v5.14;
use strict;
use warnings;

use Imager;
use Time::HiRes qw/time/;

use constant KL => 5000;
use constant KS => 2000;

use constant DST_DIR => './julia';

my @gen_info = (
    {
        a => [ -0.260693184726186,-0.687516709032337 ],
        RS => -1.5,
        RE =>  1.5,
        IS => -1.5,
        IE =>  1.5
    },
    {
        a => [ -0.933335203860999,0.291308479429304 ],
        RS => -1.7,
        RE =>  1.7,
        IS => -1.7,
        IE =>  1.7
    },
    {
        a => [ -0.305360385610307,0.778048402593683 ],
        RS => -1.5,
        RE =>  1.5,
        IS => -1.5,
        IE =>  1.5
    },
    {
        a => [ 0.417568855329243,-0.141460590091484 ],
        RS => -1.2,
        RE =>  1.2,
        IS => -1.2,
        IE =>  1.2
    }
);

if ( not -e DST_DIR ) {
    mkdir DST_DIR;
}

local $| = 1;
my $i = 0;
foreach my $info ( @gen_info ) {
    my ( $width, $height ) = ( KS, KS );

    my $file_name = sprintf( '%03d_%d_%d.dat', $i++, $width, $height );
    my $dst_file = join( '/', DST_DIR, $file_name );

    if ( -e $dst_file ) {
        next;
    }

    my $pixels = generate( $width, $height, $info );
    if ( not $pixels ) {
        next;
    }

    open( my $fh, '>', $dst_file ) or die "cannot open $dst_file : $!";
    binmode( $fh );
    foreach ( @{$pixels} ) {
        print $fh pack('s*', @{$_});
    }
    close( $fh );
}

sub generate {
    my ( $width, $height, $args ) = @_;

    my ( $a_r, $a_i ) = @{$args->{a}};
    my $dr = ( $args->{RE} - $args->{RS} ) / $width;
    my $di = ( $args->{IE} - $args->{IS} ) / $height;

    my $start = time();
    printf( "%4d/%4d", 1, $height );
    my @pixels = ();
    for (my $iy=0; $iy<$height; $iy++) {
        my $start_yloop = time();

        my @buf = ();
        for (my $ix=0; $ix<$width; $ix++) {

            my $z_r = ($ix * $dr) + $args->{RS};
            my $z_i = ($iy * $di) + $args->{IS};

            my $i = -1;
            foreach ( 0..KL ) {
                my $z2_r = ($z_r * $z_r) - ($z_i * $z_i) + $a_r;
                my $z2_i = (2.0 * $z_r * $z_i) + $a_i;

                if ( 4 < (($z2_r * $z2_r) + ($z2_i * $z2_i)) ) {
                    $i = $_;
                    last;
                }

                ( $z_r, $z_i ) = ( $z2_r, $z2_i );
            }

            push @buf, $i;
        }

        push @pixels, \@buf;
        printf( "\r%4d/%4d", $iy + 1, $height );

        if ( 1 < (time() - $start_yloop) ) {
            printf( "\rtime out! %.2fsec\n", (time() - $start) );
            return 0;
        }
    }
    printf( "\rcomplete! %.2fsec\n", (time() - $start) );

    return \@pixels;
}

とは言うものの、これをレンダリングしないといけないんだけどね。

おしまい。

Leave a Comment