BrowserUk has asked for the wisdom of the Perl Monks concerning the following question:

I posted Re^3: How to draw a curved arrow in perl Tk canvas ? as an answer, but I've since discovered that about 1 or 2 times in 20 randomly generated uses, something goes wrong and it draws a 300° arc instead of a 60° arc.

Look as long as I have, I still cannot see why. It is obviously a math error, but I can't tell if it is mine or atan or Tk::Canvas that is the root of the problem. Can any of you mathematicians see the mistake?

Update:The problem arises when the atan2 calculations are around the vertical. I know that tan gets weird around 90°, but I thought the whole point of atan2 was to compensate for that?

Here is a version of the code that generates 10 random pairs of points and connects with a pair of arcs. The bad ones stand out like a sore thumb, but I cannot see the pattern to them:

#! perl -slw use strict; use Tk; use constant PI => 3.1415926535897932384626433832795; use constant RAD => 180 / PI; sub curvedArrow { my( $cnv, $x1, $y1, $x2, $y2, $color ) = @_; my $rad = sqrt( abs( $x1 - $x2 )**2 + abs( $y1 - $y2 )**2 ); my $q = sqrt( ( $x2 - $x1 )**2 + ( $y2 - $y1 )**2 ); my( $x3, $y3 ) = ( ( $x1 + $x2 ) / 2, ( $y1 + $y2 ) / 2 ); my $xc = $x3 + sqrt( $rad**2 - ( $q / 2 )**2 ) * ( $y1 - $y2 ) / $ +q; my $yc = $y3 + sqrt( $rad**2 - ( $q / 2 )**2 ) * ( $x2 - $x1 ) / $ +q; my $a1 = atan2( ( $yc - $y1 ) , -( $xc - $x1 ) ) * RAD; my $a2 = atan2( ( $yc - $y2 ) , -( $xc - $x2 ) ) * RAD; $cnv->createArc( $xc - $rad, $yc - $rad, $xc + $rad, $yc + $rad, -style => 'arc', -start => $a1, -extent => $a2 - $a1, -outline=> $color ); my $r2 = $rad / 15; $cnv->createArc( $x1-$r2, $y1-$r2, $x1+$r2, $y1+$r2, -start=>$a1-77, -extent=>($a2-$a1)/2, -fill=> $color ); $cnv->createArc( $x2-$r2, $y2-$r2, $x2+$r2, $y2+$r2, -start=> ( $a2+107 ) %360, -extent=>($a2-$a1)/2, -fill=> $colo +r ); return $xc, $yc; } our $W //= 1000; our $H //= 800; my $mw = new MainWindow(-title => 'Test'); my $canvas = $mw->Canvas(-width => $W, -height => $H )->pack; for( 1 .. 10 ) { my( $x1, $y1 ) = ( int( rand( $W ) ), int( rand( $H ) ) ); $canvas->createLine( $x1-5, $y1, $x1+5, $y1, -fill => 'blue' ); $canvas->createLine( $x1, $y1-5, $x1, $y1+5, -fill => 'blue' ); my( $x2, $y2 ) = ( int( rand $W ), int( rand $H ) ); $canvas->createLine( $x2-5, $y2, $x2+5, $y2, -fill => 'green' ); $canvas->createLine( $x2, $y2-5, $x2, $y2+5, -fill => 'green' ); my( $xc, $yc ) = curvedArrow( $canvas, $x1, $y1, $x2, $y2, 'black' + ); $canvas->createLine( $xc-5, $yc, $xc+5, $yc, -fill => 'red' ); $canvas->createLine( $xc, $yc-5, $xc, $yc+5, -fill => 'red' ); ( $xc, $yc ) = curvedArrow( $canvas, $x2, $y2, $x1, $y1, 'red' ) +; $canvas->createLine( $xc-5, $yc, $xc+5, $yc, -fill => 'red' ); $canvas->createLine( $xc, $yc-5, $xc, $yc+5, -fill => 'red' ); } MainLoop;

With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

The start of some sanity?

Replies are listed 'Best First'.
Re: My math error or Perl's? (Solved:mine)
by BrowserUk (Patriarch) on Aug 01, 2012 at 08:38 UTC

    Seems I was misunderstanding the interactions between atan2's +-Pi returns and Tk::Canvas' +-360° inputs. I was trying too hard.

    The results of the simplification look like this

    I'll update the original post, but here is the modified code and updated test

    #! perl -slw use strict; use Tk; use constant PI => 3.1415926535897932384626433832795; use constant RAD => 180 / PI; sub curvedArrow { my( $cnv, $x1, $y1, $x2, $y2, $color ) = @_; ## set the radius to the distance between p1 & p2 my $rad = sqrt( abs( $x1 - $x2 )**2 + abs( $y1 - $y2 )**2 ); my $q = sqrt( ( $x2 - $x1 )**2 + ( $y2 - $y1 )**2 ); my( $x3, $y3 ) = ( ( $x1 + $x2 ) / 2, ( $y1 + $y2 ) / 2 ); my $xc = $x3 + sqrt( $rad**2 - ( $q / 2 )**2 ) * ( $y1 - $y2 ) / $ +q; my $yc = $y3 + sqrt( $rad**2 - ( $q / 2 )**2 ) * ( $x2 - $x1 ) / $ +q; my $a1 = atan2( ( $yc - $y1 ), -( $xc - $x1 ) ) * RAD; my $a2 = atan2( ( $yc - $y2 ), -( $xc - $x2 ) ) * RAD; $cnv->createArc( $xc - $rad, $yc - $rad, $xc + $rad, $yc + $rad, -style => 'arc', -start => $a1, -extent => -60, -outline=> $color ); my $r2 = $rad / 15; $cnv->createArc( $x1-$r2, $y1-$r2, $x1+$r2, $y1+$r2, -start=>$a1-77, -extent=> -30, -fill=> $color ); $cnv->createArc( $x2-$r2, $y2-$r2, $x2+$r2, $y2+$r2, -start=> ( $a2+107 ) %360, -extent=> -30, -fill=> $color ); return $xc, $yc; } our $W //= 1000; our $H //= 1000; my $mw = new MainWindow(-title => 'Test'); my $canvas = $mw->Canvas(-width => $W, -height => $H )->pack; for my $radius ( 500, 100 ) { for( my $a = 0; $a < 180; $a += 10 ) { my( $x, $y ) = ( sin( $a / RAD )*$radius, cos( $a / RAD )*$rad +ius ); my( $x1, $y1, $x2, $y2 ) = ( 500-$x, 500-$y, 500+$x, 500+$y, +); $canvas->createLine( $x1-5, $y1, $x1+5, $y1, -fill => 'blue' ) +; $canvas->createLine( $x1, $y1-5, $x1, $y1+5, -fill => 'blue' ) +; $canvas->createLine( $x2-5, $y2, $x2+5, $y2, -fill => 'green' +); $canvas->createLine( $x2, $y2-5, $x2, $y2+5, -fill => 'green' +); my( $xc, $yc ) = curvedArrow( $canvas, $x1, $y1, $x2, $y2, 'bl +ack' ); $canvas->createLine( $xc-5, $yc, $xc+5, $yc, -fill => 'red' ); $canvas->createLine( $xc, $yc-5, $xc, $yc+5, -fill => 'red' ); ( $xc, $yc ) = curvedArrow( $canvas, $x2, $y2, $x1, $y1, 're +d' ); $canvas->createLine( $xc-5, $yc, $xc+5, $yc, -fill => 'red' ); $canvas->createLine( $xc, $yc-5, $xc, $yc+5, -fill => 'red' ); } } MainLoop;

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Re: My math error or Perl's?
by Anonymous Monk on Aug 01, 2012 at 02:14 UTC

    I don't know math, but you can identify the rogues with tagging -- its usually the red "one"s for me, so I didn't expand on tags two and three

    #! perl -slw use strict; use Tk; our $W = our $H = 600; ## 2012-07-31-18:49:09 use constant PI => 3.1415926535897932384626433832795; use constant RAD => 180 / PI; sub curvedArrow { my $tag = 'thetag my( $cnv, $x1, $y1, $x2, $y2, $color ) = ( ' . +join(', ', @_ ).')'; my( $cnv, $x1, $y1, $x2, $y2, $color ) = @_; my $rad = sqrt( abs( $x1 - $x2 )**2 + abs( $y1 - $y2 )**2 ); my $q = sqrt( ( $x2 - $x1 )**2 + ( $y2 - $y1 )**2 ); my( $x3, $y3 ) = ( ( $x1 + $x2 ) / 2, ( $y1 + $y2 ) / 2 ); my $xc = $x3 + sqrt( $rad**2 - ( $q / 2 )**2 ) * ( $y1 - $y2 ) / $ +q; my $yc = $y3 + sqrt( $rad**2 - ( $q / 2 )**2 ) * ( $x2 - $x1 ) / $ +q; my $a1 = atan2( ( $yc - $y1 ) , -( $xc - $x1 ) ) * RAD; my $a2 = atan2( ( $yc - $y2 ) , -( $xc - $x2 ) ) * RAD; my @one = ( $xc - $rad, $yc - $rad, $xc + $rad, $yc + $rad, -style => 'arc', -start => $a1, -extent => $a2 - $a1, ); $cnv->createArc( @one, -outline=> $color, -tags => ["one $tag ", "one @one"], ); my $r2 = $rad / 15; $cnv->createArc( $x1-$r2, $y1-$r2, $x1+$r2, $y1+$r2, -start=>$a1-77, -extent=>($a2-$a1)/2, -fill=> $color, -tags => ["two $tag "], ); $cnv->createArc( $x2-$r2, $y2-$r2, $x2+$r2, $y2+$r2, -start=> ( $a2+107 ) %360, -extent=>($a2-$a1)/2, -fill=> $colo +r, -tags => ["three $tag "], ); return $xc, $yc; } our $W //= 1000; our $H //= 800; my $mw = new MainWindow(-title => 'Test'); my $canvas = $mw->Canvas(-width => $W, -height => $H )->pack; for( 1 .. 10 ) { my( $x1, $y1 ) = ( int( rand( $W ) ), int( rand( $H ) ) ); $canvas->createLine( $x1-5, $y1, $x1+5, $y1, -fill => 'blue' ); $canvas->createLine( $x1, $y1-5, $x1, $y1+5, -fill => 'blue' ); my( $x2, $y2 ) = ( int( rand $W ), int( rand $H ) ); $canvas->createLine( $x2-5, $y2, $x2+5, $y2, -fill => 'green' ); $canvas->createLine( $x2, $y2-5, $x2, $y2+5, -fill => 'green' ); my( $xc, $yc ) = curvedArrow( $canvas, $x1, $y1, $x2, $y2, 'black' + ); $canvas->createLine( $xc-5, $yc, $xc+5, $yc, -fill => 'red' ); $canvas->createLine( $xc, $yc-5, $xc, $yc+5, -fill => 'red' ); ( $xc, $yc ) = curvedArrow( $canvas, $x2, $y2, $x1, $y1, 'red' ) +; $canvas->createLine( $xc-5, $yc, $xc+5, $yc, -fill => 'red' ); $canvas->createLine( $xc, $yc-5, $xc, $yc+5, -fill => 'red' ); } #~ $canvas->bind( '<<1>>', \&tagsUnderMouse ); #~ $canvas->bind( '<Button-1>', \&tagsUnderMouse ); #~ $canvas->bind( '<ButtonPress-1>' , \&tagsUnderMouse ); $mw->bind( ref( $canvas ), '<ButtonPress-1>' , \&tagsUnderMouse ); MainLoop; sub tagsUnderMouse { warn "@_"; #~ my $canvas = $Tk::event->W; my $x = $Tk::event->X; my $y = $Tk::event->Y; my( $canvas ) = @_; use Data::Dump; my @closest = $canvas->find( closest => $x, $y ) ; #~ my %ccget = map { $_ => [ $canvas->itemconfigure($_) ] } @clos +est; my %ccget = map { $_ => [ $canvas->itemcget($_, '-tags') ] } @clo +sest; dd [ "$canvas", \@closest , \%ccget ]; }

      Thanks for the reply and code.

      Even picking out which ones produced the affect didn't make things clear to me; but thinking about selection forced me to come up with a better -- ordered rather than random -- testcase. And that crystalised things a brought me to a solution. Thanks.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      The start of some sanity?