883d3420790e601bdbdf632548dd0f12b9f8d432
[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; # Padding
20
21 my $out = shift or die "No output file specified.\n";
22 my @rows = @ARGV;
23 die "No rows.\n" unless (@rows);
24
25 my $image = new Image::Imlib2->new(1,1);
26 $image->add_font_path ("/usr/share/fonts/truetype/msttcorefonts");
27 $image->load_font($font);
28 ($fh, $fh) = $image->get_text_size("(Ay)");
29 $image = new Image::Imlib2->new($maxwidth, ($#rows+1)*($fh*2+$pd*12+2));
30 $image->add_font_path ("/usr/share/fonts/truetype/msttcorefonts");
31 $image->load_font($font);
32 $image->set_color(255,255,255,255);
33 #$image->fill_rectangle(0,0,$maxwidth,($#rows+1)*($fh*2+$pd*12+2));
34
35 sub draw_boxed_text {
36     my ($image, $text, $x, $y, $underline)=@_;
37     $image->load_font($font);
38     my ($w, $h) = $image->get_text_size($text);
39     $image->set_color(255,255,255,255);
40     $image->fill_rectangle($x,$y,$w+2*$pd+2,$fh+2*$pd+2);
41     $image->set_color(0,0,0,255);
42     $image->draw_rectangle($x,$y,$w+2*$pd+2,$fh+2*$pd+2);
43     $image->draw_text($x+$pd,$y+$pd,$text);
44     if(($underline||0)==1) {
45         $image->draw_line($x+$pd,$y+$pd+$fh,$x+$w+$pd,$y+$pd+$fh);
46     } elsif(($underline||0)==2) {
47         for(my $i=0;$i<$w;$i+=5) {
48             $image->draw_line($x+$pd+$i,$y+$pd+$fh,
49                 $x+$pd+($i+2>$w?$w$i+2),$y+$pd+$fh);
50         }
51     }
52
53     return $w+2*$pd+1;
54 }
55
56 my $line=0;
57
58 foreach my $row (@rows) {
59     my ($title,$element,@depends) = split (':',$row);
60     my @elements = split (',',$element);
61     my %elcenters = ();
62     my (@in,@out);
63     my ($toffset)=(2*$pd);
64     while ($title =~ s/^>//) {
65         $toffset += 16*$pd;
66     }
67     my ($offset) = ($toffset);
68     foreach $element (@elements) {
69         my $under=0;
70         if ($element =~ s/^_//) {
71             $under=1;
72             push @out,($element);
73         } elsif ($element =~ s/^\.//) {
74             $under=2;
75             push @in,($element);
76         } else {
77             push @in,($element);
78             warn "Element $element in $title not marked as ".
79             "primary or foreign key.\n" if ($element=~/_ID$/);
80         }
81         my $o=draw_boxed_text($image,$element,$offset,$line+$fh+5*$pd,$under);
82         $elcenters{$element}=($offset+$o/2);
83         $offset+=$o;
84     }
85     my $updown=0;
86     my $shifttitle=0;
87     if((($#depends>=0 and $depends[0] eq 'auto') or $#depends=-1)
88             and $#in>=0 and $#out>=0) {
89         $depends[0]=(join(",",@out)."/".join(",",@in));
90     } elsif($#depends>=0 and $depends[0] eq 'auto') {
91         shift @depends;
92     }
93     foreach my $depend (@depends) {
94         unless ($depend) {
95             $updown++;
96             next;
97         }
98         $image->set_color(0,(32*($updown>>1))%128,(64*($updown>>1))%256,255);
99         my ($out,$in) = split('/',$depend);
100         my ($near, $far);
101         my @out = split(',', $out);
102         my @in  = split(',', $in );
103         my @temp = sort { $elcenters{$a} <=> $elcenters{$b} } (@out, @in);
104         if($updown%2==1) {
105             $shifttitle=1;
106             $near=$line+$fh+5*$pd-1;
107             $far=$line+$fh+1*$pd-1;
108         } else {
109             $near=$line+2*$fh+7*$pd+2;
110             $far=$line+2*$fh+11*$pd+2;
111         }
112         $image->draw_line($elcenters{$temp[0]}+$pd*($updown>>1),
113             $far,$elcenters{$temp[$#temp]}+$pd*($updown>>1),$far);
114         foreach $out (@out) {
115             $image->draw_line($elcenters{$out}+$pd*($updown>>1),
116                 $far,$elcenters{$out}+$pd*($updown>>1),$near);
117         }
118         foreach $in (@in) {
119             my $arrow = Image::Imlib2::Polygon->new();
120             $arrow->add_point($elcenters{$in}+$pd*($updown>>1),$near);
121             $arrow->add_point($elcenters{$in}+$pd+$pd*($updown>>1),
122                 ($near+$far)/2);
123             $arrow->add_point($elcenters{$in}-$pd+$pd*($updown>>1),
124                 ($near+$far)/2);
125             $arrow->add_point($elcenters{$in},$near);
126             $arrow->fill();
127             $image->draw_line($elcenters{$in}+$pd*($updown>>1),
128                 $far,$elcenters{$in}+$pd*($updown>>1),$near);
129             $image->draw_polygon($arrow,1);
130         }
131         $updown++;
132     }
133     $image->set_color(0,0,0,255);
134     $image->draw_text($toffset+$pd*2,$line+4*$pd-$shifttitle*(7*$pd/2),$title);
135     $line+=2*$fh+12*$pd+2;
136 }
137
138
139 $image->save($out);