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