PerlでフラクタルをSVGで出力する
これを作る時に、コッホ曲線を描くスクリプトをいじってSVGで出力したので、
その手順を紹介します。
use v5.14; use strict; use warnings; #use Imager; use constant N => 5; use constant WIDTH => 1200; use constant HEIGHT => 1200; use constant TRI_W => 800; # レイアウトはSVGを読み込んでから行えば良い my $margin = (WIDTH - TRI_W) / 2; my ( $x0, $y0 ) = ( $margin, HEIGHT - $margin - $margin ); # コッホ曲線 my @gen = ( # [ 0.0, 0.0 ] は不要 [ 1/3, 0 ], [ 0.5, -sqrt(3.0)/6 ], [ 2/3, 0 ], [ 1.0, 0 ] ); my $points = [ [ 0, 0 ], [ TRI_W, 0 ], [ (TRI_W / 2), ((TRI_W / 2) * sqrt(3.0))], [ 0, 0 ] ]; #my $img = Imager->new( # xsize => WIDTH, ysize => HEIGHT ); #$img->box( filled => 1, color => 'black' ); foreach my $n ( 0..N ) { #$img->box( filled => 1, color => 'black' ); if ( 0 < $n ) { $points = generate( $points ); } my @tmp = map { [ ($x0 + $_->[0]), ($y0 - $_->[1]) ]; } @{$points}; #$img->polyline( points => \@tmp, color => 'white' ); my $dst_file = ($0 =~ s/\.pl//r) . "_${n}.png"; #$img->write( file => $dst_file ) or die $img->errstr; my $points_data = join(' ', map { sprintf('%f,%f', $_->[0], $_->[1]); } @tmp); my ( $width, $height ) = ( WIDTH, HEIGHT ); my $text = <<EOF; <?xml version="1.0" encoding="utf-8"?> <!-- Generator: Adobe Illustrator 16.0.5, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd"> <svg version="1.1" baseProfile="tiny" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="${width}px" height="${height}px" viewBox="0 0 ${width} ${height}" xml:space="preserve"> <polygon fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" points="$points_data "/> </svg> EOF $dst_file = ($0 =~ s/\.pl//r) . "_${n}.svg"; open( my $fh, '>', $dst_file ) or die "cannot opne \"$dst_file\""; print $fh $text; close( $fh ); } sub generate { my $points = shift; my $cnt = scalar( @{$points} ); my @result = ( $points->[0] ); for (my $i=1; $i<$cnt; $i++) { my ( $st, $en ) = ( $points->[$i-1], $points->[$i] ); my $dx = $en->[0] - $st->[0]; my $dy = $en->[1] - $st->[1]; push @result, map { my ( $x, $y ) = ( $_->[0], $_->[1] ); # x2 = $x * cos(a) - $y * sin(a) + st.x; # y2 = $x * sin(a) - $y * cos(a) + st.y; # sin(a) = dy / 1.0, cos(a) = dx / 1.0 [ ($x * $dx) - ($y * $dy) + $st->[0], ($x * $dy) + ($y * $dx) + $st->[1] ]; } @gen; } return \@result; }
Imager
を使って画像を出力するコードは残しておきました。
画像も出力したい方は、コメントアウトしてるところを解除して頂ければと思います。
作業手順は、以下の通りです。
1. テンプレートの用意
SVGの扱えるドローソフト(イラストレーターとか)で、
適当なポリゴン、またはポリラインを追加してSVGで保存する。
この作業の注意点としては、うっかりベジェ曲線を描かないこと。
あと、最低でも3点以上追加するのと、長方形にならないように気をつけること。(*1)
2. SVGをヒアドキュメントとしてPerlスクリプトに追加
ヒアドキュメントとして追加した文字列のうち、
幅と高さに関する箇所と、頂点データを差し替える。
頂点データは、xとyをカンマでつないで、スペース区切りの文字列を生成するだけ。
こうして完成したのが、上記のスクリプトです。
必要に応じて、polygon
とpolyline
を切り替える必要があって、
塗りつぶす必要があるならpolygon
、そうでないならpolyline
を使います。
使うというのは、ヒアドキュメントの一部分を書き換えるという意味です。
あとは適当なパラメータで、SVGを出力するだけですね!
おしまい。
(*1) Rect(長方形), Line(線分)として扱われる
Leave a Comment