Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Re^2: Code refactoring: simple trig, but it's been so long. (no trig!)

by tye (Sage)
on Dec 30, 2004 at 05:51 UTC ( [id://418192]=note: print w/replies, xml ) Need Help??


in reply to Re: Code refactoring: simple trig, but it's been so long.
in thread Code refactoring: simple trig, but it's been so long.

Just tangent?? I don't need no stinking tangent!

Since you beat me to the subject of vector algebra I'll skip much of my introduction about how trigonometry sucks for this. Why would anyone want to approximate a bunch of transcendental functions just to approximate another bunch of transcendental functions that are their inverses? Plus you have to special-case horizontal (or vertical, depending) lines.

With vector algebra you only need to do a few simple subtractions, multiplications, additions, exactly one division (where the denominator is never 0), and take exactly one square root.

I'll call our points A and B. Let's let V = ( xv, yv ) be the vector from A to B. Then -V = ( -xv, -yv ) points the other direction. But ( -yv, xv ) and ( yv, -xv ) point perpendicular. So scale these to be the right length and add them to A and B and you get the 4 points of your box that defines your "think line".

And you can do that without having to repeat yourself much if you let the code do the repeating for you:

#!/usr/bin/perl -w use strict; # This takes two 2-dimensional points (A and B) # and a width, and returns 4 points that are the # counter-clockwise perimeter of a "thick line" # (box) of the specified width around the line. sub line2box { my $w= pop @_; die 'Usage: line2box($ax,$ay,$bx,$by,$width)' if $w <= 0 || 4 != @_; # We just keep our 4 coordinates in a single array # so we don't have to repeat ourselves much below. $w /= 2; # Length of offset vector is $width/2 my @d; # Differences between X and Y coord.s my $l= 0; for( 0, 1 ) { my $d= $_[$_] - $_[2+$_]; $l += $d*$d; push @d, $d; } # Now $l is length**2, so we want to scale our # perpendicular vectors by $width / $length so: $l= sqrt( $w*$w / $l ); $_ *= $l for @d; my @box; my @s= ( -1, 1 ); # Sign to apply (- or +) for my $s ( 0, 1 ) { # Index into @s # $p==0 for A, $p==1 for B for my $p ( $s, !$s ) { # Which point for my $c ( 0, 1 ) { # Which coordinate # Push the selected coordinate push @box, $_[2*$p+$c] + $s[$s^$c]*$d[!$c]; } } } return @box; } while( <DATA> ) { last if !/\S/; my @line= split ' '; $line[-1] **= 0.5; my @box= line2box( @line ); printf qq[A line from A( %5.1f, %5.1f )$/] . qq[ to B( %5.1f, %5.1f )$/] . qq[of width sqrt( %5.1f )$/] . qq[is a box, counter-clock-wise:$/] . qq[ W( %5.1f, %5.1f )$/] . qq[ X( %5.1f, %5.1f )$/] . qq[ Y( %5.1f, %5.1f )$/] . qq[ Z( %5.1f, %5.1f )$/] . $/ , @line[0..3], $line[4]**2, , @box; }

And here is some sample input, sample output, and some ASCII-art drawings thrown in to make sense of the numbers.

__END__ 1 0 1 5 4 1 1 6 1 16 -1 -2 2 1 8 A line from A( 1.0, 0.0 ) to B( 1.0, 5.0 ) of width sqrt( 4.0 ) is a box, counter-clock-wise: W( 2.0, 0.0 ) X( 2.0, 5.0 ) Y( 0.0, 5.0 ) Z( 0.0, 0.0 ) w=2 |<--->| | (Y)(B)(X) | # 4+ # | # 3+ # | # 2+ # | # 1+ # | # --+--+-(Z)(A)(W)-+-- -2 -1 0 1 2 A line from A( 1.0, 1.0 ) to B( 6.0, 1.0 ) of width sqrt( 16.0 ) is a box, counter-clock-wise: W( 1.0, -1.0 ) X( 6.0, -1.0 ) Y( 6.0, 3.0 ) Z( 1.0, 3.0 ) | 4+ | 3+ (Z) (Y) --- | ^ 2+ | | | 1+ (A)############(B) w=4 | | --+--+--+--+--+--+--+--+--+- | -1 0 1 2 3 4 5 6 7 v -1+ (W) (X) --- | -2+ | A line from A( -1.0, -2.0 ) to B( 2.0, 1.0 ) of width sqrt( 8.0 ) is a box, counter-clock-wise: W( 0.0, -3.0 ) X( 3.0, 0.0 ) Y( 1.0, 2.0 ) Z( -2.0, -1.0 ) | 3+ / | \ 2+ (Y) w=2*(2**.5) | \ 1+ (B) / | // --+--+--+--+--+--+-(X)-+-- -3 -2 -1 0//1 2 3 4 (Z) -1+ //| (A) -2 | -3(W) | -4+ |

Note that the line:

for my $p ( $s, !$s ) { # Which point

is a tricky way of saying "the first time through (using a negative sign) do A first then B; the second time through (using a positive sign) do B first then A". This gives us the dots in a perimeter order (so you could just connect them if you wanted the outline of the thick line).

And I didn't have to memorize any rules about cos() vs. sin() or even worry about which coordinate was x or y. I just do all of the combinations and I get all of the points needed.

- tye        

Replies are listed 'Best First'.
Re^3: Code refactoring: simple trig, but it's been so long. (no trig!)
by revdiablo (Prior) on Dec 30, 2004 at 17:56 UTC

    I know I'm dense, but this seems like a lot more work than memorizing (or looking up, or figuring out by experimentation) a few rules about cos() and sin(). Perhaps you can explain it to me in a way that makes sense? If using vector algebra truly is better/simpler/etc, I'd at least be curious to understand why...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://418192]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (6)
As of 2024-04-19 11:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found