テキストから升目状の図を描く

このエントリーをはてなブックマークに追加

裁ち合わせの問題の図を描くために,以下のようなテキスト入力で,同じ文字の箇所を同じ色にした升目状の図を描くプログラムを作成してみた.

  AAAAABBBBBFFF
  AMAEABDBDBDJF
  AAAEABDBDBDJF
  HHHEEEDDDDDJF
  GGHECECCCKIJF
  GHHECECLCKIJI
  GGGGCCCLCKIII

プログラム char2svg.pl

SVG (Scalable Vector Graphics)用のPerlライブラリ libsvg-perlを利用した.
Ubuntuでは,「apt-get install libsvg-perl 」でインストールできる.マニュアルは「man SVG::Manual」で参照できる.

#!/usr/bin/perl
use strict;
use SVG;
use Getopt::Std;
$| = 1;

use vars qw($opt_h $opt_w $opt_c);
&getopts("hw:c");
if ($opt_h) {
    &usage();
}
my @colors = (
    "#ff0000", "#00ff00", "#0000ff", # A B C
    "#ffff00", "#ff00ff", "#00ffff", # D E F
    "#c08000", "#c00080", "#80c000", # G H I
    "#00c080", "#8000c0", "#0080c0", # J K L
    "#c04040", "#40c040", "#4040c0", # M N O
    "#808000", "#800080", "#008080", # P Q R
    "#800000", "#008000", "#000080", # S T U
    );

my ($rows, $cols, @data);
my (%region);
&read_data();
my (@hline, @vline);
foreach my $i (1 .. $rows-2) {
    foreach my $j (1 .. $cols-2) {
	my $c = $data[$i][$j];
	next if &empty($c);
	if ($data[$i][$j] ne $data[$i-1][$j]) {
	    $hline[$i][$j]{$c}++;
	}
	if ($data[$i][$j] ne $data[$i+1][$j]) {
	    $hline[$i+1][$j]{$c}++;
	}
	if ($data[$i][$j] ne $data[$i][$j-1]) {
	    $vline[$i][$j]{$c}++;
	}
	if ($data[$i][$j] ne $data[$i][$j+1]) {
	    $vline[$i][$j+1]{$c}++;
	}
    }
}

my $width = $opt_w || 640;
my $size = int($width / $cols);
$width = $size * $cols;
my $height = $size * $rows;
my %color;

my $k = 0;
foreach my $c (sort keys %region) {
    $color{$c} = $colors[$k % scalar(@colors)];
    $k++;
}
&write_svg();
exit 0;

sub usage {
    print "Usage: $0 [options]\n";
    print "-h        : Help\n";
    exit(1);
}

sub read_data {
    @data = ();
    $rows = 0;
    $cols = 0;
    while (<>) {
	chomp;
	my @x = split(//, $_);
	if (@x > $cols) {
	    $cols = @x;
	}
	foreach my $c (@x) {
	    if (! &empty($c)) {
		$region{$c}++;
	    }
	}
	$data[$rows] = \@x;
	$rows++;
    }
    if (! &empty_row(\@data, 0, $cols)) {
	unshift(@data, []);
	$rows++;
    }
    if (! &empty_row(\@data, $rows-1, $cols)) {
	push(@data, []);
	$rows++;
    }
    if (! &empty_col(\@data, $rows, 0)) {
	foreach my $i (0 .. $rows-1) {
	    unshift(@{$data[$i]}, " ");
	}
	$cols++;
    }
    if (! &empty_col(\@data, $rows, $cols-1)) {
	foreach my $i (0 .. $rows-1) {
	    push(@{$data[$i]}, " ");
	}
	$cols++;
    }
    foreach my $i (0 .. $rows-1) {
	foreach my $j (0 .. $cols-1) {
	    if (&empty($data[$i][$j])) {
		$data[$i][$j] = " ";
	    }
	}
    }
}

sub write_svg {
    my $svg = SVG->new("width"=>$width, "height"=>$height, "-encoding"=>"UTF-8");
    if ($opt_c) {
	foreach my $i (0 .. $rows-1) {
	    foreach my $j (0 .. $cols-1) {
		my $c = $data[$i][$j];
		next if &empty($c);
		my $x = $size * $j;
		my $y = $size * $i;
		my $color = $color{$c};
		$svg->rect(x=>$x, y=>$y, width=>$size, height=>$size,
		       style=>{fill=>$color, stroke=>"none",});
	    }
	}
    }
    foreach my $i (0 .. $rows-1) {
	foreach my $j (0 .. $cols-1) {
	    my $c = $data[$i][$j];
	    my $x1 = $size * $j;
	    my $y1 = $size * $i;
	    my ($x2, $y2) = ($x1+$size, $y1);
	    if ($hline[$i][$j]) {
		$svg->line(x1=>$x1, y1=>$y1, x2=>$x2, y2=>$y2,
			   style=>"fill:none; stroke:#000000; stroke-width:1");
	    } elsif (! &empty($c)) {
		$svg->line(x1=>$x1+1, y1=>$y1, x2=>$x2-1, y2=>$y2,
			   style=>"fill:none; stroke:#c0c0c0; stroke-width:1");
	    }
	    ($x2, $y2) = ($x1, $y1+$size);
	    if ($vline[$i][$j]) {
		$svg->line(x1=>$x1, y1=>$y1, x2=>$x2, y2=>$y2,
			   style=>"fill:none; stroke:#000000; stroke-width:1");
	    } elsif (! &empty($c)) {
		$svg->line(x1=>$x1, y1=>$y1+1, x2=>$x2, y2=>$y2-1,
			   style=>"fill:none; stroke:#c0c0c0; stroke-width:1");
	    }
	}
    }
    print $svg->xmlify, "\n";
}

sub empty_row {
    my ($d, $i, $cols) = @_;
    foreach my $j (0 .. $cols-1) {
	if (! &empty($d->[$i][$j])) {
	    return 0;
	}
    }
    return 1;
}

sub empty_col {
    my ($d, $rows, $j) = @_;
    foreach my $i (0 .. $rows-1) {
	if (! &empty($d->[$i][$j])) {
	    return 0;
	}
    }
    return 1;
}

sub empty {
    my ($c) = @_;
    return ! $c || $c eq " ";
}

使い方

以下の内容のテキストファイル file.in を作成する.

AAAAABBBBBFFF
AMAEABDBDBDJF
AAAEABDBDBDJF
HHHEEEDDDDDJF
GGHECECCCKIJF
GHHECECLCKIJI
GGGGCCCLCKIII

J  HHH  BBB  E E  CCC  AAA  FFF  GGG  DDD
J    H    B  E E  C    A      F  G G  M D
J  KHH  BBB  EEE  CCC  AAA    F  GII  DDD
J  K      B    E    C  A A    F  G I    D
J  KLL  BBB    E  CCC  AAA    F  III  DDD

char2svg.plのプログラムを上の入力に対して実行する.

  $ char2svg.pl -c file.in >file.svg

作成されたSVGファイルをInkscape等で開けば,編集できる.
また,以下のようにすればPNGファイルに変換できる.

  $ inkscape -z -e file.png file.svg