If length R specified, draws an arc of radius R; R +ve curves to the
right; R -ve to the left.
- join F T arcs two circular arcs of equal minimised radius
- join F T arcsline R straight line between two arcs radius R
- join F T arcline straight line and arc of minimised radius
+ join F T R [S ...]
Joins one existing loc, F, to another, T. F's direction points to
the new track; T's away - ie the added track leaves F in F's
- direction and arrives at T in T's direction. In each case solution
- is chosen with minimum total added length.
+ direction and arrives at T in T's direction. R is the minimum curve
+ radius allowed. S selects from the available solutions, and may be
+ any of the following
+ long prefer longer length solution
+ short prefer shorter length solution
+ right|left prefer mostly bending to the left resp. right
+ beginright|beginleft prefer first bend to the right
+ endright|endleft prefer final bend to the right
+ [!]twoarcs prefer [not] two circular arcs of equal radius
+ [!]arcline prefer [not] one line and an arc of max radius
+ [!]arcsline prefer [not] line between two arcs of min radius
+ if this doesn't resolve, will pick the shortest.
defobj O
[commands]
}
sub cmd_join {
- my ($from,$to,$how,$radius);
+ my ($from,$to,$how,$minradius);
$from= can(\&cva_idex);
$to= can(\&cva_idex);
- $how= can(cvam_enum(qw(arcs arcsline arcline)));
- if ($how eq 'arcsline') {
- $radius= can(\&cva_len);
- }
- my (@paths);
- if ($how eq 'arcs') {
- my ($sigma,$distfact, $theta,$phi, $a,$b,$c,$d, $m,$r);
+ $minradius= can(\&cva_len);
+ my (@paths,@solkinds);
+ do {
+ my ($sigma,$distfact, $theta,$phi, $a,$b,$c,$d, $m,$r, $radius);
my ($cvec,$cfrom,$cto,$midpt, $delta1,$delta2, $path,$reverse);
$sigma= ev_bearing($from,$to);
$distfact= v_dist($from,$to);
$c= -1;
$d= sqrt($b*$b - 4*$a*$c);
foreach $m (qw(-1 1)) {
+ next if $a < 1e-6;
$r= -0.5 * (-$b + $m*$d) / $a;
$radius= -$r * $distfact;
+ next if abs($radius) < $minradius;
$cfrom= ev_compose({}, $from, { X=>0, Y=>-$radius, A=>-0.5*$pi });
$cto= ev_compose({}, $to, { X=>0, Y=> $radius, A=> 0.5*$pi });
$midpt= ev_lincomb({}, $cfrom, $cto, 0.5);
$path= [{ T=>Arc, F=>$from, C=>$cfrom, R=> $radius, D=>$delta1 },
{ T=>Arc, F=>$to, C=>$cto, R=>-$radius, D=>$delta2 }];
push @paths, $path;
+ push @solkinds, 'twoarcs';
}
- }
- my ($path,$segment,$bestpath,$len,$bestlen);
+ } while 0;
+ my ($path,$segment,$bestpath,$len,$scores,$bestscores,@bends,$sk);
+ my ($crit,$cs,$i,$cmp);
foreach $path (@paths) {
- o("% possible path $path\n");
+ $sk= shift @solkinds;
+ o("% possible path $sk $path\n");
$len= 0;
+ @bends= ();
foreach $segment (@$path) {
if ($segment->{T} eq Arc) {
o("% Arc C ".loc2dbg($segment->{C}).
" R $segment->{R} D ".ang2deg($segment->{D})."\n");
- $len += abs($radius * $segment->{D});
+ $len += abs($segment->{R} * $segment->{D});
+ push @bends, signum($segment->{R} * $segment->{D}); # right +ve
} else {
die "unknown segment $segment->{T}";
}
}
- o("% length $len\n");
- if (!defined($bestpath) || $len < $bestlen) {
- $bestpath= $path;
- $bestlen= $len;
+ o("% length $len\n");
+ $scores= [];
+ foreach $crit (@al, 'short') {
+ if ($crit eq 'long') { $cs= $len; }
+ elsif ($crit eq 'short') { $cs= -$len; }
+ elsif ($crit =~ m/^(begin|end|)(left|right)$/) {
+ if ($1 eq 'begin') { $cs= $bends[0]; }
+ elsif ($1 eq 'end') { $cs= $bends[$#bends]; }
+ else { $cs=0; map { $cs += $_ } @bends; }
+ $cs= -$cs if $2 eq 'left';
+ } elsif ($crit =~ m/^(\!?)(twoarcs|arcline|arcsline)$/) {
+ $cs= ($2 eq $sk) != ($1 eq '!');
+ }
+ push @$scores, $cs;
+ }
+ o("% scores @$scores\n");
+ if (defined $bestpath) {
+ for ($i=0,$cmp=0; !$cmp && $i<@$scores; $i++) {
+ $cmp= $scores->[$i] <=> $bestscores->[$i];
+ }
+ next if $cmp < 0;
}
+ $bestpath= $path;
+ $bestscores= $scores;
}
- die unless defined $bestpath;
- o("% chose path $bestpath\n");
+ die "no solution" unless defined $bestpath;
+ o("% chose path $bestpath @al\n");
+ @al= ();
foreach $segment (@$bestpath) {
if ($segment->{T} eq 'Arc') {
arc({}, $segment->{C},$segment->{F},$segment->{R},$segment->{D});