SVG-VCD
view release on metacpan or search on metacpan
bin/VertFigure view on Meta::CPAN
#!/usr/local/bin/perl -w
use strict;
use warnings;
use Getopt::Std;
use Data::Dumper;
use SVG;
use SVG::VCD;
my %options;
if (!getopts('d', \%options) || @ARGV != 1) {
print STDERR "\
Usage: $0 [options] <VCD-file>
-d Dump information about parsed VCD-file.
";
exit 1;
}
if (@ARGV != 1) {
exit 1;
}
my $vcd = new SVG::VCD($ARGV[0]);
if ($options{d}) {
print Dumper($vcd);
exit;
}
my $n = $vcd->ntaxa();
my $th = $vcd->config("taxon-height");
my $cw = $vcd->config("cervical-width");
my $ch = $vcd->config("cervical-height");
my $dw = $vcd->config("dorsal-width");
my $dh = $vcd->config("dorsal-height");
my $dhg = $vcd->config("dorsal-height-gradient");
my $tyo = $vcd->config("text-y-offset") || 0;
my $cdo = $vcd->config("cervico-dorsal-offset");
my $cdc = $vcd->config("cervico-dorsal-color");
my $width = $vcd->config("width");
my $height = $vcd->config("height");
my $svg = SVG->new(width => $width, height => $height);
my $background = $vcd->config("background");
if ($background) {
$svg->rectangle(x => 0, y => 0, width => $width, height => $height,
style => { 'fill' => $background });
}
my $st = $svg->group(style => { 'font-family' => $vcd->config("font-family"),
'font-size' => $vcd->config("font-size"),
fill => 'black',
});
foreach my $i (1 .. $n) {
my $taxon = $vcd->taxon($i-1);
my $data = $taxon->field("data");
my($cdata, $ddata) = split('\|', $data);
$ddata = "" if !defined $ddata;
my $nc = length($cdata);
my $nd = length($ddata);
my $text = $st->text(x => 10, 'y' => $th * $i + $tyo);
my $tname = $taxon->field("taxon");
my %args = ( style => { 'font-style' => "italic" } );
if ($tname =~ s/^\///) {
%args = ();
}
$text->tspan(%args)->cdata($tname);
my $specimen = $taxon->field("specimen");
# I don't know why, but some versions of InkScape (e.g. v0.48 on
# Ubuntu 12.04.2) do not render an ordinary space in this context
# -- hence use of a non-breaking space.
$text->tspan()->cdata(" $specimen") if defined $specimen;
# Cervicals
foreach (my $j = 0; $j < $nc; $j++) {
vertebra($svg, $vcd, $i, $j-$nc, $cw, $ch, substr($cdata, $j, 1));
}
# Dorsals
my $dheight = $dh;
foreach (my $j = 0; $j < $nd; $j++) {
vertebra($svg, $vcd, $i, $j, $dw, $dheight, substr($ddata, $j, 1));
$dheight += $dhg if defined $dhg;
}
}
if ($cdc) {
my $maxh = $ch > $dh ? $ch : $dh;
my $margin = $th - $maxh;
$svg->line(x1 => $cdo, y1 => $margin/2,
x2 => $cdo, y2 => $n * $th + $margin/2,
style => { stroke => $cdc } );
}
print $svg->xmlify();
sub vertebra {
my($svg, $vcd, $yoff, $xoff, $width, $height, $char) = @_;
$char = lc($char);
my $cdo = $vcd->config("cervico-dorsal-offset");
my $th = $vcd->config("taxon-height");
my $bc = $vcd->config("box-color") || "grey";
my $x = $cdo + $xoff * $width;
my $y = $th * $yoff - $height;
$svg->rectangle(x => $x, 'y' => $y, width => $width, height => $height,
style => { stroke => $bc, fill => 'white' });
if (my $polygon = $vcd->config("state-$char-polygon")) {
$svg->polygon(style => { fill => $vcd->config("state-$char-color") },
points => compile_points($x, $y, $width, $height,
0.2, 0,
split(/[, ]/, $polygon)));
} elsif ($char eq "-") {
# Unknown state (e.g. vertebra missing): nothing to draw
} else {
die "unrecognised bifurcation state '$char'";
}
}
# Returns a string of the form '0,0 0,1 1,1 1,0'
sub compile_points {
my $xoff = shift();
my $yoff = shift();
my $xscale = shift();
my $yscale = shift();
my $pMargin = shift();
my $aMargin = shift();
my $text = "";
while (@_) {
my $x = shift();
my $y = shift();
( run in 2.296 seconds using v1.01-cache-2.11-cpan-995e09ba956 )