chiark / gitweb /
parses things and draws a bit of rail
authorijackson <ijackson>
Wed, 21 Jan 2004 23:46:10 +0000 (23:46 +0000)
committerijackson <ijackson>
Wed, 21 Jan 2004 23:46:10 +0000 (23:46 +0000)
layout/layout

index 8d4f2766eacb5d571a07abf9f2481ff65dd30e46..a1d51652c9cbf2b2ee9b3d0b5940d22b0da16015 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/perl -w
 
 use POSIX;
 
@@ -7,46 +7,72 @@ use POSIX;
 #  $loc{$id}{Y}
 #  $loc{$id}{A}  may be undef
 
+open DEBUG, ($debug ? ">&2" : ">/dev/null") or die $!;
+
 sub canf ($$) {
     my ($converter,$defaulter)=@_;
-    my ($spec);
-    &$defaulter unless @al;
+    my ($spec,$v);
+    return &$defaulter unless @al;
     $spec= shift @al;
-    return &$converter($spec);
+    $v= &$converter($spec);
+    dv('canf ','$spec',$spec, '$v',$v);
+    return $v;
 }
-
-sub can ($) { my ($conv)=@_; canf($c, sub { die "too few args"; }); }
-sub cano ($$) { my ($conv,$def)=@_; canf($c, sub { return $def }); }
+sub can ($) { my ($c)=@_; canf($c, sub { die "too few args"; }); }
+sub cano ($$) { my ($c,$def)=@_; canf($c, sub { return $def }); }
 
 $pi= atan2(0,-1);
 
-%units_len= qw(- mm  mm 0  cm 10  m 1000);
-%units_ang= qw(- d   r 1); $units_ang{'d'}= 2*$pi/360;
+%units_len= qw(- mm  mm 1  cm 10  m 1000);
+%units_ang= qw(- d   r 1); $units_ang{'d'}= 2*$pi / 360;
 
 sub cva_len ($) { my ($sp)=@_; cva_units($sp,\%units_len); }
 sub cva_ang ($) { my ($sp)=@_; cva_units($sp,\%units_ang); }
 sub cva_units ($$) {
-    my ($sp,$ua,$n,$u)=@_;
-    $sp =~ s/^([-0-9eE.]+)([A-Za-z]*)$/;
-    ($n,$u)= ($1,$1);
-    $u=$u{'-'} unless length $u;
+    my ($sp,$ua)=@_;
+    my ($n,$u,$r);
+    $sp =~ m/^([-0-9eE.]*[0-9.])([A-Za-z]*)$/
+       or die "lexically invalid quantity";
+    ($n,$u)= ($1,$2);
+    $u=$ua->{'-'} unless length $u;
     defined $ua->{$u} or die "unknown unit $u";
-    return $n * $ua->{$u};
+    $r= $n * $ua->{$u};
+    print DEBUG "cva_units($sp,)=$r ($n $u $ua->{$u})\n";
+    return $r;
+}
+sub cva_idstr ($) {
+    my ($sp)=@_;
+    die "invalid id" unless $sp =~ m/^[-0-9a-z]+$/;
+    return $&;
 }
-sub cva_idstr ($) { my ($sp)=@_; die unless m/^[-0-9a-z]+$; return $&; }
 sub cva_idex ($) {
     my ($sp,$id)=@_;
+    my ($r);
     $id=cva_idstr($sp);
     die "unknown $id" unless defined $loc{$id};
-    return $loc{$id};
+    $r= $loc{$id};
+    printf DEBUG "%s %s %s\n", $id, join("|", keys %$r), $r->{A};
+    return $r;
 }
 sub cva_idnew ($) {
     my ($sp,$id)=@_;
-    $id=cva_idstr($sp); 
-    die "duplicate $id" if defined $loc{$id};
-    keys %{ $loc{$id} };
+    $id=cva_idstr($sp);
+    die "duplicate $id" if exists $loc{$id};
+    exists $loc{$id}{X};
     return $loc{$id};
-}    
+}
+sub cva_cmd ($) {
+    my ($sp)=@_;
+    die "command lexically invalid" if $sp =~ m/[^-0-9a-z]/i;
+    $sp =~ y/-/_/;
+    return $sp;
+}
+sub cva__enum ($$) {
+    my ($sp,$el)=@_;
+    return $sp if grep { $_ eq $sp } @$el;
+    die "invalid option (permitted: @$el)";
+}
+sub cvam_enum { my (@e) = @_; return sub { cva__enum($_[0],\@e); }; }
 
 sub cmd_mark {
     $mark= 1;
@@ -58,18 +84,193 @@ sub cmd_abs {
     $nl->{X}= can(\&cva_len);
     $nl->{Y}= can(\&cva_len);
     $nl->{A}= cano(\&cva_ang, undef);
-}   
+dv('cmd_abs ','$nl',$nl,'\\%loc',\%loc);
+}
+
+sub evreff ($) {
+    my ($pfx) = @_;
+    $pfx . ($pfx =~ m/\}$|\]$/ ? '' : '->');
+}
+sub evr ($) {
+    my ($v) = @_;
+    return $v if $v !~ m/\W/ && $v =~ m/[A-Z]/ && $v =~ m/^[a-z_]/i;
+    return $v if $v eq ($v+0.0);
+    $v =~ s/[\\\']/\\$&/g;
+    return "'$v'";
+}
+sub dv1 ($$$);
+sub dv1_kind ($$$$$$$) {
+    my ($pfx,$expr,$ref,$ref_exp,$ixfmt,$ixesfn,$ixmapfn) = @_;
+    my ($ix,$any);
+    return 0 if $ref ne $ref_exp;
+    $any=0;
+    foreach $ix (&$ixesfn) {
+       $any=1;
+       my ($v)= &$ixmapfn($ix);
+#print STDERR "dv1_kind($pfx,$expr,$ref,$ref_exp,$ixmapfn) ix=$ix v=$v\n";
+       dv1($pfx,$expr.sprintf($ixfmt,evr($ix)),$v);
+    }
+    if (!$any) {
+       printf DEBUG "%s%s= ".$ixfmt, $pfx, $expr, ' ';
+    }
+    1;
+}    
+sub dv1 ($$$) {
+    return unless $debug;
+    my ($pfx,$expr,$v) = @_;
+    $ref= ref $v;
+#print STDERR "dv1 >$pfx|$ref<\n";
+    if (!$ref) {
+       printf DEBUG "%s%s= %s\n", $pfx,$expr, evr($v);
+       return;
+    } elsif ($ref eq 'SCALAR') {
+       dv1($pfx, ($expr =~ m/^\$/ ? "\$$expr" : '${'.$expr.'}'), $$v);
+       return;
+    }
+    $expr.='->' unless $expr =~ m/\]$|\}$/;
+    return if dv1_kind($pfx,$expr,$ref,'ARRAY','[%s]',
+                       sub { ($[ .. $#$v) },
+                       sub { $v->[$_[0]] });
+    return if dv1_kind($pfx,$expr,$ref,'HASH','{%s}',
+                       sub { sort keys %$v },
+                       sub { $v->{$_[0]} });
+    printf DEBUG "%s%s is %s\n", $pfx, $expr, $ref;
+}
+    
+sub dv {
+    my ($pfx,@l) = @_;
+    my ($expr,$v,$ref);
+    while (@l) {
+       ($expr,$v,@l)=@l;
+       dv1($pfx,$expr,$v);
+    }
+}                  
+
+sub loc_lin_comb ($$$) {
+    my ($a,$b,$p) = @_;
+    my ($q,$r) = 1.0-$p;
+    map { $r->{$_} = $q * $a->{$_} + $p * $b->{$_} } qw(X Y A);
+    dv("loc_lin_comb ",'$a',$a,'$b',$b,'$p',$p,'$r',$r);
+    return $r;
+}
+
+$psu_ulen= 4.5;
+$psu_edgelw= 0.5;
+$psu_ticklw= 0.1;
+$psu_ticksperu= 3;
+$psu_ticklen= 3.0;
+$psu_allwidth= 37.0/2;
+$psu_gauge= 9;
+$psu_sleeperlen= 17;
+$psu_sleeperlw= 15;
+$psu_raillw= 1.0;
+
+sub o ($) {
+    # fixme optional marking
+    print "$_[0]" or die $!;
+}
+
+sub o_path_begin () {
+    o("      newpath\n");
+    $o_path_verb= 'moveto';
+}
+sub o_path_point ($) {
+    my ($pt)=@_;
+    o("        $pt $o_path_verb\n");
+    $o_path_verb= 'lineto';
+}
+sub o_path_stroke ($) {
+    my ($width)=@_;
+    o("        $width setlinewidth stroke\n");
+}    
+
+sub o_line ($$$) {
+    my ($a,$b,$width)=@_;
+    o_path_begin();
+    o_path_point($a);
+    o_path_point($b);
+    o_path_stroke($width);
+}
+
+sub psu_coords ($$$) {
+    my ($ends,$inunit,$across)=@_;
+    # $ends->[0]{X} etc.; $inunit 0 to 1 (but go to 1.5);
+    # $across in mm, +ve to right.
+    my (%ea_zo);
+    $ea_zo{X}=$ea_zo{Y}=0;
+    foreach $zo (qw(0 1)) {
+       $prop= $zo ? $inunit : (1.0 - $inunit);
+       $ea_zo{X} += $prop * ($ends->[$zo]{X} - $across * sin($ends->[0]{A}));
+       $ea_zo{Y} += $prop * ($ends->[$zo]{Y} - $across * cos($ends->[0]{A}));
+    }
+#    dv("psu_coords ", '$ends',$ends, '$inunit',$inunit, '$across',$across,
+#       '\\%ea_zo', \%ea_zo);
+    return $ea_zo{X}." ".$ea_zo{Y};
+}
+
+sub parametric_segment ($$$$$) {
+    my ($endstatuses,$p0,$p1,$lenperp,$calcfn) = @_;
+    # makes $p (global) go from $p0 to $p1  ($p1>$p0)
+    # $ends is II, SI, IS, SS (I=actual lineobj end, S=in mid of lineobj)
+    # $lenperp is the length of one unit p, ie the curve
+    # must have a uniform `density' in parameter space
+    # $calcfn is invoked with $p set and should return a loc
+    # (ie, ref to X =>, Y =>, A =>).
+    my ($pa,$pb,@ends,$side,$ppu,$e,$v,$tick);
+    $ppu= $psu_ulen/$lenperp;
+    my ($railctr)=($psu_gauge + $psu_raillw)*0.5;
+    my ($tickend)=($psu_allwidth - $psu_ticklen);
+    my ($tickpitch)=($psu_ulen / $psu_ticksperu);
+    my ($sleeperctr)=($psu_ulen*0.5);
+    my ($sleeperend)=($psu_sleeperlen*0.5);
+print DEBUG "ps $p0 $p1\n";
+    for ($pa= $p0; $pa<$p1; $pa=$pb) {
+       $pb= $pa + $ppu;
+       $p= $pa; $ends[0]= @ends ? $ends[1] : &$calcfn;
+       $p= $pb; $ends[1]= &$calcfn;
+print DEBUG "pa $pa $ends[0]{X} $ends[0]{Y} $ends[0]{A}\n";
+print DEBUG "pb $pb $ends[1]{X} $ends[1]{Y} $ends[1]{A}\n";
+       $e= $pb<=$p1 ? 1.0 : ($p1-$pa)/$ppu;
+       o("    gsave\n");
+       o_path_begin();
+       o_path_point(psu_coords(\@ends,0,-$psu_allwidth));
+       o_path_point(psu_coords(\@ends,0,$psu_allwidth));
+       o_path_point(psu_coords(\@ends,$e,$psu_allwidth));
+       o_path_point(psu_coords(\@ends,$e,-$psu_allwidth));
+       o("        closepath clip\n");
+       foreach $side qw(-1 1) {
+           o_line(psu_coords(\@ends,0,$side*$psu_allwidth),
+                  psu_coords(\@ends,1.5,$side*$psu_allwidth),
+                  $psu_edgelw);
+           o_line(psu_coords(\@ends,0,$side*$railctr),
+                  psu_coords(\@ends,1.5,$side*$railctr),
+                  $psu_raillw);
+           for ($tick=0; $tick<1.5; $tick+=$tickpitch/$psu_ulen) {
+               o_line(psu_coords(\@ends,$tick,$side*$psu_allwidth),
+                      psu_coords(\@ends,$tick,$side*$tickend),
+                      $psu_ticklw);
+           }
+       }
+       o_line(psu_coords(\@ends,$sleeperctr,-$sleeperend),
+              psu_coords(\@ends,$sleeperctr,+$sleeperend),
+              $psu_sleeperlw);
+       o("      grestore\n");
+    }
+}
 
 sub cmd_extend {
+    my ($from,$to);
     $from= can(\&cva_idex);
     $to= can(\&cva_idnew);
+    printf DEBUG "%s %s\n", join("|", keys %$from), join("|", values %$from);
     die "no ang" unless defined $from->{A};
-    $how= can(cva_enum(qw(len upto ang)));
+    $how= can(cvam_enum(qw(len upto ang)));
     if ($how eq 'len') { $len= can(\&cva_len); }
     elsif ($how eq 'ang') { $ang= can(\&cva_ang); }
     elsif ($how eq 'upto') { $upto= can(\&cva_id); }
     $radius= cano(\&cva_ang, 'Inf'); # +ve is right hand bend
     if ($radius eq 'Inf') {
+       print DEBUG "extend inf $len\n";
        if ($how eq 'ang') { die "len of straight spec by angle"; }
        if ($how eq 'upto') {
            $len= ($upto->{X} - $from->{X}) * cos($from->{a})
@@ -78,9 +279,11 @@ sub cmd_extend {
        $to->{X}= $from->{X} + $len * cos($from->{A});
        $to->{Y}= $from->{Y} + $len * sin($from->{A});
        $to->{A}= $from->{A};
-       # fixme mark
+       parametric_segment(II, 0.0, 1.0, $len, sub {
+           loc_lin_comb($from, $to, $p);
+       });
     } else {
-       $signum= $radius / abs($radisu);
+       $signum= $radius / abs($radius);
        $ctr->{X}= $from->{X} + $radius * sin($from->{A});
        $ctr->{Y}= $from->{Y} - $radius * cos($from->{A});
        if ($how eq 'upto') {
@@ -93,19 +296,21 @@ sub cmd_extend {
        $to->{A}= $beta;
        $to->{X}= $ctr->{X} + $radius * cos($beta);
        $to->{Y}= $ctr->{Y} - $radius * sin($beta);
-       
-
+#      parametric_segment(0, II, $radius);
+    }
     die "point only" unless defined $from->{A};
     join($from,$to);
 }
 
 sub cmd__do {
-    
-       
-sub cmd_str_asfar {
-    $from= can(\&cva_idex);
-    $to= can(\&cva_idex);
-    
+    $cmd= can(\&cva_cmd);
+    &{ "cmd_$cmd" };
+}      
+
+$ptscale= 72/25.4 * 0.1;
+
+o("%!\n".
+  "  $ptscale $ptscale scale\n");
 
 while (<>) {
     next if m/^\s*\#/;
@@ -113,7 +318,6 @@ while (<>) {
     @al= split /\s+/, $_;
     next unless @al;
     $mark= 0;
-    $cmd= can(\&cva_cmd);
-shift @al;    $cmd =~ y/-_/_-/;
-    &{ "cmd_$cmd" };
+    cmd__do();
 }
+dv('','\\%loc',\%loc);