be914f806ab1d8b82817b6683f20e231b8e874c4
[tpope-extra.git] / perl / relational-schema
1 #!/usr/bin/perl -w
2 # $Id$
3 # -*- perl -*- vim:set ft=perl sw=4 sts=4:
4
5 # Usage: relational-schema out.png RELATION:_Primary_Key,.Foreign_Key,Etc
6 # To clarifiy, specifiy the relation name, followed by a colon, followed by
7 # the attributes seperated by commas.  Primary keys should be prefixed with
8 # an underscore, and foreign keys with a period.
9 # It is also possible to override or augment the functional dependencies by
10 # adding a second colon and additional specifications.  Since this was
11 # unnecessary for the assignment, it is not documented here.
12
13 use strict;
14 use Image::Imlib2;
15
16 use vars qw($font $maxwidth $pd $fh);
17 $font = "Verdana/11";
18 $maxwidth = 550;
19 $pd = 4;
20
21 my $out = shift or die "No output file specified.\n";
22
23 my @rows = @ARGV;
24 die "No rows.\n" unless (@rows);
25
26 my $image = new Image::Imlib2->new(1,1);
27 $image->add_font_path ("/usr/share/fonts/truetype/msttcorefonts");
28 $image->load_font($font);
29 ($fh, $fh) = $image->get_text_size("(Ay)");
30 $image = new Image::Imlib2->new($maxwidth, ($#rows+1)*($fh*2+$pd*12+2));
31 $image->add_font_path ("/usr/share/fonts/truetype/msttcorefonts");
32 $image->load_font($font);
33 $image->set_color(255,255,255,255);
34 #$image->fill_rectangle(0,0,$maxwidth,($#rows+1)*($fh*2+$pd*12+2));
35
36 sub draw_boxed_text {
37     my ($image, $text, $x, $y, $underline)=@_;
38     $image->load_font($font);
39     my ($w, $h) = $image->get_text_size($text);
40     $image->set_color(255,255,255,255);
41     $image->fill_rectangle($x,$y,$w+2*$pd+2,$fh+2*$pd+2);
42     $image->set_color(0,0,0,255);
43     $image->draw_rectangle($x,$y,$w+2*$pd+2,$fh+2*$pd+2);
44     $image->draw_text($x+$pd,$y+$pd,$text);
45     if(($underline||0)==1) {
46         $image->draw_line($x+$pd,$y+$pd+$fh,$x+$w+$pd,$y+$pd+$fh);
47     } elsif(($underline||0)==2) {
48         for(my $i=0;$i<$w;$i+=5) {
49             $image->draw_line($x+$pd+$i,$y+$pd+$fh,
50                 $x+$pd+($i+2>$w?$w$i+2),$y+$pd+$fh);
51         }
52     }
53
54     return $w+2*$pd+1;
55 }
56
57 my $line=0;
58
59 foreach my $row (@rows) {
60     my ($title,$element,@depends) = split (':',$row);
61     my @elements = split (',',$element);
62     my %elcenters = ();
63     my (@in,@out);
64     my ($toffset)=(2*$pd);
65     while ($title =~ s/^>//) {
66         $toffset += 16*$pd;
67     }
68     my ($offset) = ($toffset);
69     foreach $element (@elements) {
70         my $under=0;
71         if ($element =~ s/^_//) {
72             $under=1;
73             push @out,($element);
74         } elsif ($element =~ s/^\.//) {
75             $under=2;
76             push @in,($element);
77         } else {
78             push @in,($element);
79             warn "Element $element in $title not marked as ".
80             "primary or foreign key.\n" if ($element=~/_ID$/);
81         }
82         my $o=draw_boxed_text($image,$element,$offset,$line+$fh+5*$pd,$under);
83         $elcenters{$element}=($offset+$o/2);
84         $offset+=$o;
85     }
86     my $updown=0;
87     my $shifttitle=0;
88     if((($#depends>=0 and $depends[0] eq 'auto') or $#depends=-1)
89             and $#in>=0 and $#out>=0) {
90         $depends[0]=(join(",",@out)."/".join(",",@in));
91     } elsif($#depends>=0 and $depends[0] eq 'auto') {
92         shift @depends;
93     }
94     foreach my $depend (@depends) {
95         unless ($depend) {
96             $updown++;
97             next;
98         }
99         $image->set_color(0,(32*($updown>>1))%128,(64*($updown>>1))%256,255);
100         my ($out,$in) = split('/',$depend);
101         my ($near, $far);
102         my @out = split(',', $out);
103         my @in  = split(',', $in );
104         my @temp = sort { $elcenters{$a} <=> $elcenters{$b} } (@out, @in);
105         if($updown%2==1) {
106             $shifttitle=1;
107             $near=$line+$fh+5*$pd-1;
108             $far=$line+$fh+1*$pd-1;
109         } else {
110             $near=$line+2*$fh+7*$pd+2;
111             $far=$line+2*$fh+11*$pd+2;
112         }
113         $image->draw_line($elcenters{$temp[0]}+$pd*($updown>>1),
114             $far,$elcenters{$temp[$#temp]}+$pd*($updown>>1),$far);
115         foreach $out (@out) {
116             $image->draw_line($elcenters{$out}+$pd*($updown>>1),
117                 $far,$elcenters{$out}+$pd*($updown>>1),$near);
118         }
119         foreach $in (@in) {
120             my $arrow = Image::Imlib2::Polygon->new();
121             $arrow->add_point($elcenters{$in}+$pd*($updown>>1),$near);
122             $arrow->add_point($elcenters{$in}+$pd+$pd*($updown>>1),
123                 ($near+$far)/2);
124             $arrow->add_point($elcenters{$in}-$pd+$pd*($updown>>1),
125                 ($near+$far)/2);
126             $arrow->add_point($elcenters{$in},$near);
127             $arrow->fill();
128             $image->draw_line($elcenters{$in}+$pd*($updown>>1),
129                 $far,$elcenters{$in}+$pd*($updown>>1),$near);
130             $image->draw_polygon($arrow,1);
131         }
132         $updown++;
133     }
134     $image->set_color(0,0,0,255);
135     $image->draw_text($toffset+$pd*2,$line+4*$pd-$shifttitle*(7*$pd/2),$title);
136     $line+=2*$fh+12*$pd+2;
137 }
138
139
140 $image->save($out);