PerlモジュールAlgorithm::Line::Bresenhamを使ってtgifの円を円状に配置

はじめに

前回は、Algorithm::Line::Bresenhamの紹介をしたんですが、
Perlで、グラフをtgifの形式(objファイル)で生成してepsで出力したい。」
という目的がありました。
問題設定を具体的にして、以下のようにしました。
「完全グラフをPerlを使って生成してeps出力したい。」
ということで、
今回は、Algorithm::Line::Bresenhamを使ってtgifの円を円状に配置する方法を考えたのでご紹介します。
完全グラフを生成するためには、辺を全ての頂点間につなぐ必要があるのですがそれは次回に持ち越します。

予備知識

まず、tgifのobjがどのような形式なのか調べる必要がありました。[1]を参考にしたところtgifの仕様は「tgif.pl」というPrologのプログラムで書かれていることを知りました。ただ、僕はPrologに関する知識がほとんどなかったので、円を描画するのに必要な座標の設定がどのようになされているのかを、tgif.plを見ながら学びました。[2]を参考にしました。以下がtgifのovalオブジェクト(楕円)の定義です。

tgif_oval(Obj) :-
        current_predicate(tgif_file_version,tgif_file_version(_)),
        tgif_file_version(FileVersion),
        FileVersion =< 7, !,
        ( var(Obj) -> OutputObj = true ; OutputObj = false ),
        Obj = oval(_Color,_LeftTopX,_LeftTopY,_RightBotX,_RightBotY,_ObjFill,
                _LineWidth,_PenPat),
        ( OutputObj == true -> call(Obj) ; true ).

コードを書くときに注意した点

「_LeftTopX,_LeftTopY,_RightBotX,_RightBotY」が楕円の座標と、楕円を円にするために必要なパラメータだと判断したので、これらをPerlのヒアドキュメントを用いて座標を円状に配置しました。このときに、Algorithm::Line::Bresenhamで算出した値を用いました。

コード

use strict;
use warnings;
use Algorithm::Line::Bresenham qw/circle/;
use Data::Dumper;

my $y = 10;
my $x = 10;
my $radius = 5;
my @points = circle ($x, $y, $radius);

open(FH, ">test.obj");

my $msg =  <<"EOF";
%TGIF 4.2.1
state(1,37,10.000,0,0,0,16,0,9,1,1,1,0,0,2,1,0,'Times-Roman',0,195840,0,0,1,50,0,0,1,1,0,16,0,0,1,1,1,1,1497,1056,1,17280,2880,0).
%
% @(#)\$Header\$
% %W%
%
unit("1 pixel/pixel").
color_info(13,65535,0,[
	"magenta", 65535, 0, 65535, 65535, 0, 65535, 1,
	"red", 65535, 0, 0, 65535, 0, 0, 1,
	"green", 0, 65535, 0, 0, 65535, 0, 1,
	"blue", 0, 0, 65535, 0, 0, 65535, 1,
	"yellow", 65535, 65535, 0, 65535, 65535, 0, 1,
	"pink", 65535, 49344, 52171, 65535, 49344, 52171, 1,
	"cyan", 0, 65535, 65535, 0, 65535, 65535, 1,
	"CadetBlue", 24415, 40606, 41120, 24415, 40606, 41120, 1,
	"white", 65535, 65535, 65535, 65535, 65535, 65535, 1,
	"black", 0, 0, 0, 0, 0, 0, 1,
	"DarkSlateGray", 12079, 20303, 20303, 12079, 20303, 20303, 1,
	"khaki", 61680, 59110, 35980, 61680, 59110, 35980, 1,
	"NavyBlue", 0, 0, 32896, 0, 0, 32896, 1
]).
script_frac("0.6").
fg_bg_colors('black','white').
dont_reencode("FFDingbests:ZapfDingbats").
objshadow_info('#c0c0c0',2,2).
rotate_pivot(0,0,0,0).
spline_tightness(1).
page(1,"",1,'').
EOF

foreach my $point (@points) {
  my ($px, $py) = ($point->[0] * 50, $point->[1] * 50);
  my $px2 = $px + 49;
  my $py2=  $py + 46;
  $msg.= 
"oval('black','',$px,$py,$px2,$py2,0,2,1,1634,0,0,0,0,0,'2',0,[\n]).\n";
}

$msg.= "\n";

print FH $msg;
close(FH);

exit;

出力されたtest.objの中身

%TGIF 4.2.1
state(1,37,10.000,0,0,0,16,0,9,1,1,1,0,0,2,1,0,'Times-Roman',0,195840,0,0,1,50,0,0,1,1,0,16,0,0,1,1,1,1,1497,1056,1,17280,2880,0).
%
% @(#)$Header$
% %W%
%
unit("1 pixel/pixel").
color_info(13,65535,0,[
	"magenta", 65535, 0, 65535, 65535, 0, 65535, 1,
	"red", 65535, 0, 0, 65535, 0, 0, 1,
	"green", 0, 65535, 0, 0, 65535, 0, 1,
	"blue", 0, 0, 65535, 0, 0, 65535, 1,
	"yellow", 65535, 65535, 0, 65535, 65535, 0, 1,
	"pink", 65535, 49344, 52171, 65535, 49344, 52171, 1,
	"cyan", 0, 65535, 65535, 0, 65535, 65535, 1,
	"CadetBlue", 24415, 40606, 41120, 24415, 40606, 41120, 1,
	"white", 65535, 65535, 65535, 65535, 65535, 65535, 1,
	"black", 0, 0, 0, 0, 0, 0, 1,
	"DarkSlateGray", 12079, 20303, 20303, 12079, 20303, 20303, 1,
	"khaki", 61680, 59110, 35980, 61680, 59110, 35980, 1,
	"NavyBlue", 0, 0, 32896, 0, 0, 32896, 1
]).
script_frac("0.6").
fg_bg_colors('black','white').
dont_reencode("FFDingbests:ZapfDingbats").
objshadow_info('#c0c0c0',2,2).
rotate_pivot(0,0,0,0).
spline_tightness(1).
page(1,"",1,'').
oval('black','',750,500,799,546,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',750,500,799,546,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',250,500,299,546,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',250,500,299,546,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',500,750,549,796,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',500,250,549,296,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',500,750,549,796,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',500,250,549,296,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',750,550,799,596,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',750,450,799,496,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',250,550,299,596,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',250,450,299,496,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',550,750,599,796,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',550,250,599,296,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',450,750,499,796,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',450,250,499,296,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',750,600,799,646,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',750,400,799,446,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',250,600,299,646,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',250,400,299,446,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',600,750,649,796,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',600,250,649,296,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',400,750,449,796,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',400,250,449,296,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',700,650,749,696,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',700,350,749,396,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',300,650,349,696,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',300,350,349,396,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',650,700,699,746,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',650,300,699,346,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',350,700,399,746,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',350,300,399,346,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',650,700,699,746,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',650,300,699,346,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',350,700,399,746,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',350,300,399,346,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',700,650,749,696,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',700,350,749,396,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',300,650,349,696,0,2,1,1634,0,0,0,0,0,'2',0,[
]).
oval('black','',300,350,349,396,0,2,1,1634,0,0,0,0,0,'2',0,[
]).

出力

tgifの画面の出力です。

pngの出力です。

まとめ

きれいな円を描くためには、$x, $y, $radius, $px, $pyのさじ加減が難しいです。
とりあえず、円状の円の配置は成功したので辺を全ての頂点間につなぐステップに入ろうと思います。
また、座標パラメータが操作できるテキストファイルの画像ファイルにおいて、その中のオブジェクトはPerlを使って細かく操作できることを実感しました。