From: ijackson Date: Wed, 21 Jan 2004 23:46:10 +0000 (+0000) Subject: parses things and draws a bit of rail X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ijackson/git?a=commitdiff_plain;h=b19112009d75635d1247ded6deda1f1c267ec99e;p=trains.git parses things and draws a bit of rail --- diff --git a/layout/layout b/layout/layout index 8d4f276..a1d5165 100755 --- a/layout/layout +++ b/layout/layout @@ -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);