1. Trang chủ
  2. » Công Nghệ Thông Tin

Mastering Algorithms with Perl phần 7 ppsx

74 290 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 74
Dung lượng 558,17 KB

Nội dung

} } elsif ( abs( $det_b < epsilon ) ) { # The other cross product is "zero" and # the other vector is also "zero". return 1 if abs( $dx10 ) < epsilon and abs( $dy10 ) < epsilon; } Page 440 return 0; # Default is no intersection. } We'll test line_intersect () with two pairs of lines. The first pair intersects at (3, 4), and the second pair of lines do not intersect at all because they're parallel: print "Intersection\n" if line_intersect( 3, 0, 3, 6, 1, 1, 6, 6 ); print "No intersection\n" unless line_intersect( 1, 1, 6, 6, 4, 2, 7, 5 ); Intersection No intersection Line Intersection: The Horizontal-Vertical Case Often, the general case of line intersection is too general: if the lines obey Manhattan geometry, that is, if they' re strictly horizontal or vertical, a very different solution for finding the intersections is available. The solution is to use binary trees, which were introduced in Chapter 3, Advanced ata Structures. We will slide a horizontal line from bottom to top over our plane, constructing a binary tree of lines as we do so. The resulting binary tree contains vertical lines sorted on their x-coordinate, for this reason, the tree is called an x-tree. The x-tree is constructed as follows: • The points will be processed from bottom to top, vertical lines before horizontal ones and from left to right. This means that both endpoints of a horizontal line will be seen simultaneously, while the endpoints of a vertical line will be seen separately. • Whenever the lower endpoint of a vertical line is seen, that node is added to the binary tree, with its x-coordinate as the value. This divides the points in the tree in a left-right manner: if line a is left of line b, node a will be left of node b in the tree. • Whenever the upper endpoint of a vertical line is seen, the corresponding node is deleted from the binary tree. • Whenever a horizontal line is encountered, the nodes in the tree (the active vertical lines) are checked to determine whether any of them intersect the horizontal line. The horizontal lines are not added to the tree; their only duty is to trigger the intersection checks. Figure 10-9 shows how an x-tree develops as the imaginary line proceeds from the bottom of the picture to the top. The left picture simply identifies the order in which line segments are encountered: first c, then e, and so on. The middle picture shows the x-tree just after e is encountered, and the right picture after a and d are encountered. Note that d is not added to the tree; it serves only to trigger an intersection check.break Page 441 Figure 10-9. Horizontal-vertical line intersection The manhattan_intersection() subroutine implements this algorithm:break # manhattan_intersection ( @lines ) # Find the intersections of strictly horizontal and vertical lines. # Requires basic_tree_add(), basic_tree_del (), and basic_tree_find(), # all defined in Chapter 3, Advanced Data Structures. # sub manhattan_intersection { my @op; # The coordinates are transformed here as operations. while (@_) { my @line = splice @_, 0, 4; if ($line[1] == $line[3]) { # Horizontal. push @op, [ @line, \&range_check_tree ]; } else { # Vertical. # Swap if upside down. @line = @line[0, 3, 2, 1] if $line[1] > $line[3]; push @op, [ @line[0, 1, 2, 1], \&basic_tree_add ]; push @op, [ @line[0, 3, 2, 3], \&basic_tree_del ]; } } my $x_tree; # The range check tree. # The x coordinate comparison routine. my $compare_x = sub { $_[0]->[0] <=> $_[1]->[0] }; my @intersect; # The intersections. foreach my $op (sort { $a->[1] <=> $b->[1] || $a->[4] == \&range_check_tree || $a->[0] <=> $b->[0] } @op) { if ($op->[4] == \&range_check_tree) { push @intersect, $op->[4]->( \$x_tree, $op, $compare_x ); Page 442 } else { # Add or delete. $op->[4]->( \$x_tree, $op, $compare_x ); } } return @intersect, } } # range_check_tree( $tree_link, $horizontal, $compare ) # Returns the list of tree nodes that are within the limits # $horizontal->[0] and $horizontal->[1]. Depends on the binary # trees of Chapter 3, Advanced Data Structures. # sub range_check_tree { my ( $tree, $horizontal, $compare ) = @_; my @range = ( ); # The return value. my $node = $$tree; my $vertical_x = $node->{val}; my $horizontal_lo = [ $horizontal-> [ 0 ] ]; my $horizontal_hi = [ $horizontal-> [ 1 ] ]; return unless defined $$tree; push @range, range_check_tree( \$node->{left}, $horizontal, $compare ) if defined $node->{left}; push @range, $vertical_x->[ 0 ], $horizontal->[ 1 ] if $compare->( $horizontal_lo, $horizontal ) <= 0 && $compare->( $horizontal_hi, $horizontal ) >= 0; push @range, range_check_tree( \$node->{right}, $horizontal, $compare ) if defined $node->{right}; return @range; } manhattan_intersection() runs in O (N log N + k), where k is the number of intersections (which can be no more than (N/2) 2 ). We'll demonstrate manhattan_intersection( ) with the lines in Figure 10-10. The lines in Figure 10-10 are stored in an array and tested for intersections as follows:break @lines = ( 1, 6, 1, 3, 1, 2, 3, 2, 1, 1, 4, 1, 2, 4, 7, 4, 3, 0, 3, 6, 4, 3, 4, 7, 5, 7, 5, 4, 5, 2, 7, 2 ); print join(" ", manhattan_intersection(@lines)), "\n; Page 443 Figure 10-10. Lines for the example of Manhattan algorithm We get: 3 1 3 2 1 4 3 4 4 4 5 4 This tells you the six points of intersection. For example, (3, 1) is the bottommost intersection, and (5, 4) is the upper-rightmost intersection. Inclusion In this section, we are interested in whether a point is inside a polygon. Once we know that, we can conduct more sophisticated operations, such as determining whether a line is partially or completely inside a polygon. Point in Polygon Determining whether a point is inside a polygon is a matter of casting a ''ray" from the point to "infinity" (any point known to be outside the polygon). The algorithm is simple: count the number of times the ray crosses the polygon edges. If the crossing happens an odd number of times (points e, f, h, and J in Figure 10-11), we are inside the polygon; otherwise, we are outside (a, b, c, d, g, and i). There are some tricky special cases (rare is the geometric algorithm without caveats): What if the ray crosses a polygon vertex? (points d, f, g, and j)) Or worse, an edge? (point j) The algorithm we are going to use is guaranteed to return true forcontinue Page 444 truly inside points and false for truly outside points. For the borderline cases, it depends on how the "glancing blows" are counted. Figure 10-11. Is the point in the polygon? Count the edge crossings The point_in_polygon() subroutine returns a true value if the given point (the first two arguments) is inside the polygon (described by the subsequent arguments).break # point_in_polygon ( $x, $y, @xy ) # # Point ($x,$y), polygon ($x0, $y0, $x1, $y1, . . .) in @xy. # Returns 1 for strictly interior points, 0 for strictly exterior # points. For the boundary points the situation is more complex and # beyond the scope of this book. The boundary points are # exact, however: if a plane is divided into several polygons, any # given point belongs to exactly one polygon. # # Derived from the comp.graphics.algorithms FAQ, # courtesy of Wm. Randolph Franklin. # sub point_in_polygon { my ( $x, $y, @xy ) = @_; my $n = @xy / 2; # Number of points in polygon. my @i = map { 2 * $_ } 0 (@xy/2); # The even indices of @xy. my @x = map { $xy[ $_ ] } @i; # Even indices: x-coordinates. my @y = map { $xy[ $_ + 1 ] } @i; # Odd indices: y-coordinates. my ( $i, $j ); # Indices. my $side = 0; # 0 = outside, 1 = inside. for ( $i = 0, $j = $n -1 ; $i < $n; $j = $i++ ) { if ( ( Page 445 # If the y is between the (y-) borders . . . ( ( $y[ $i ] <= $y ) && ( $y < $y[ $j ] ) ) || ( ( $y[ $j ] <= $y ) && ( $y < $y[ $i ] ) ) ) and # . . . the (x,y) to infinity line crosses the edge # from the ith point to the jth point . . . ($x < ( $x[ $j ] - $x[ $i ] ) * ( $y - $y[ $i ] ) / ( $y[ $j ] - $y[ $i ] ) + $x[ $i ] )) { $side = not $side; # Jump the fence. } } return $side ? 1 : 0; } To detect whether the number of intersections is even or odd, we don't actually need to count them. We can do something much faster: simply toggle the Boolean variable $side Using the polygon in Figure 10-12, we can test whether the nine points are inside or outside as follows: @polygon = ( 1, 1, 3, 5, 6, 2, 9, 6, 10, 0, 4,2, 5, -2); print "( 3, 4): ", point_in_polygon( 3, 4, @polygon ), "\n"; print "( 3, 1): ", point_in_polygon( 3, 1, @polygon ), "\n"; print "( 3,-2): ", point_in_polygon( 3,-2, @polygon ), "\n"; print "( 5, 4): ", point_in_polygon( 5, 4, @polygon ), "\n"; print "( 5, 1): ", point_in_polygon( 5, 1, @polygon ), "\n"; print "( 5,-2): ", point_in_polygon( 5,-2, @polygon ), "\n"; print "( 7, 4): ", point_in_polygon( 7, 4, @polygon ), "\n"; print "( 7, 1): ", point_in_polygon( 7, 1, @polygon ), "\n"; print "( 7,-2): ", point_in_polygon( 7,-2, @polygon ), "\n"; The results: ( 3, 4): 1 ( 3, 1): 1 ( 3,-2): 0 ( 5, 4): 0 ( 5, 1): 0 ( 5,-2): 0 ( 7, 4): 0 ( 7, 1): 1 ( 7,-2): 0 This tells us that that the points (3, 4), (3, 1), and (7, 1) are inside the polygon, and the rest are outside.break Page 446 Figure 10-12 12. A sample polygon with some inside and outside points Point in Triangle For simple polygons such as triangles, we can use an alternative algorithm. We start from a corner of the triangle and determine whether we have to look to the left or right to see the point. Then we travel to the next corner and look at the point. If the side we had to look to changed, we know that the point cannot be within the triangle. We visit the final corner and check again; if the side still hasn't changed, we can safely conclude that the point is inside the triangle. Also, if we detect that the point is on an edge, we can immediately return true. In Figure 10-13, we can envision traveling countefclockwise around the vertices of the triangle. Any point inside the triangle will be to our left. If the point is outside the triangle, we'll notice a change from left to right. This algorithm is implemented in the point_in_triangle() subroutine:break # point_in_triangle ( $x, $y, $x0, $y0, $x1, $y1, $x2, $y2 ) returns # true if the point ($x,$y) is inside the triangle defined by # the following points sub point_in_triangle { my ( $x, $y, $x0, $y0, $x1, $y1, $x2, $y2 ) = @_; # clockwise() from earlier in the chapter. my $cw0 = clockwise( $x0, $y0, $x1, $y1, $x, $y ); return 1 if abs( $cw0 ) < epsilon, # On 1st edge. my $cw1 = clockwise( $x1, $y1, $x2, $y2, $x, $y ); Page 447 Figure 10-13. Determining whether a point is inside a triangle return 1 if abs( $cw1 ) < epsilon; # On 2nd edge. # Fail if the sign changed. return 0 if ( $cw0 < 0 and $cwl > 0 ) or ( $cw0 > 0 and $cwl < 0 ); my $cw2 = clockwise( $x2, $y2, $x0, $y0, $x, $y ); return 1 if abs( $cw2 ) < epsilon; # On 3rd edge. # Fail if the sign changed. return 0 if ( $cw0 < 0 and $cw2 > 0 ) or ( $cw0 > 0 and $cw2 < 0 ); # Jubilate! return 1; } Let's define a triangle with vertices at (1, 1), (5, 6), and (9, 3), and test seven points for inclusion: @triangle = ( 1, 1, 5, 6, 9, 3 ); print "(1, 1): ", point_in_triangle( 1, 1, @triangle ), "\n"; print "(1, 2): ", point_in_triangle( 1, 2, @triangle ), "\n"; print "(3, 2): ", point_in_triangle( 3, 2, @triangle ), "\n"; print "(3, 3): ", point_in_triangle( 3, 3, @triangle ), "\n"; print "(3, 4): ", point_in_triangle( 3, 4, @triangle ), "\n"; print "(5, 1): ", point_in_triangle( 5, 1, @triangle ), "\n"; print "(5, 2): ", point_in_triangle( 5, 2, @triangle ), "\n"; The output:break (1, 1): 1 (1, 2): 0 (3, 2): 1 (3, 3): 1 (3, 4): 0 (5, 1): 0 (5, 2): 1 Page 448 This tells us that the points (1, 2), (3, 4), and (5, 1) are outside the triangle and the rest are inside: Point in Quadrangle Any convex quadrangle (a four-sided polygon—all squares and rectangles are quadrangles) can be split into two triangles along any two opposing points. We can combine this observation with the point_in_triangle() subroutine to determine whether a point is in the quadrangle. (Beware of degenerate quadrangles: quadrangles that have overlapping corner points so that they reduce to triangles, lines, or even points. ) A split of a quadrangle into two triangles is illustrated in Figure 10-14. Figure 10-14. Splitting a quadrangle into two triangles The point_in_quadarangle() subroutine simply calls point_in_triangle() twice, one for each triangle resulting from the split: # point_in_quadrangle( $x, $y, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3 ) # Return true if the point ($x,$y) is inside the quadrangle # defined by the points p0 ($x0,$y0), p1, p2, and p3. # Simply uses point_in_triangle. # sub point_in_quadrangle { my ( $x, $y, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3 ) = @_; return point_in_triangle( $x, $y, $x0, $y0, $x1, $y1, $x2, $y2 ) || point_in_triangle( $x, $y, $x0, $y0, $x2, $y2, $x3, $y3 ) } point_in_quadrangle() will be demonstrated with the quadrangle and points shown in Figure 10-15.break Page 449 Figure 10-15. Determining whether a point is in a quadrangle The quadrangle's vertices are at (1, 4), (3, 0), (6, 2), and (5, 5), so that's what we'll provide: @quadrangle = ( 1, 4, 3, 0, 6, 2, 5, 5 ); print "(0, 2): ", point_in_quadrangle( 0, 2, @quadrangle ), "\n"; print "(1, 4): ", point_in_quadrangle( 1, 4, @quadrangle ), "\n"; print "(2, 2): ", point_in_quadrangle( 2, 2, @quadrangle ), "\n"; print "(3, 6): ", point_in_quadrangle( 3, 6, @quadrangle ), "\n"; print "(3, 4): ", point_in_quadrangle( 3, 4, @quadrangle ), "\n"; print "(4, 2): ", point_in_quadrangle( 4, 2, @quadrangle ), "\n"; print "(5, 4): ", point_in_quadrangle( 5, 4, @quadrangle ), "\n"; The output: (0, 2): 0 (1, 4): 1 (2, 2): 1 (3, 6): 0 (3, 4): 1 (4, 2): 1 (5, 4): 1 (6, 2): 1 This means that the points (0, 2) and (3, 6) are outside the quadrangle and the rest are inside. Boundaries In this section, we explore the boundaries of geometric objects, which we can use to determine whether objects seem to overlap. We say "seem" because these boundaries give only the first approximation: concave objects confuse the issue.break Page 450 Bounding Box The bounding box of a geometric object is defined as the smallest d-dimensional box containing the d-dimensional object where the sides align with the axes. The bounding box can be used in video games to determine whether objects just collided. Three bounding boxes are shown in Figure 10-16. [...]... "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; # # # # # # # printf "%3.f", printf "%4.f", printf "%5.f", 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; # prints "1235" # prints "1235" # prints " 1235" printf printf printf printf printf 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; # # # # # "%8.1f", "%8.2f", "%8.3f", "%8.4f", "%8.5f",... -$1234.56 # $5 678 .12 # -$5 678 .12 / / / / # $1234.56 # -$1234. 57 # $5 678 .12 # -$5 678 .13 / / / / 100); 100); 100); 100); 100); 100); 100); 100); # Round to the nearest penny print insert_dollar(round( 1234.5 678 * 100) / 100); print insert_dollar(round(-1234.5 678 * 100) / 100); print insert_dollar(round( 5 678 .1234 * 100) / 100); # $1234. 57 # -$1234.56 # $5 678 .13 # -$5 678 .12 # $1234. 57 # -$1234. 57 # $5 678 .12 print... printf printf printf "%3.e", "%4.e", "%5.e", "%6.e", 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; # # # # prints prints prints prints "le+03" "le+03" "le+03" " le+03" printf printf printf printf printf printf printf "%.0e", "%.le", "%.2e", "%.3e", "%.4e", "%.5e", "%.6e", 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; # # # # # # # prints prints prints prints prints prints... speed speed speed speed speed of of of of of of of of of Venus is 12 675 0 km/h Jupiter is 471 49 km/h Mars is 871 14 km/h Pluto is 21 372 km/h Earth is 1 075 88 km/h Saturn is 348 17 km/h Uranus is 24549 km/h Neptune is 176 32 km/h Mercury is 172 698 km/h Page 472 Precision Mathematics on a computer is different from mathematics on paper With paper, what you see is what you get: when you speak of 3/10, or 1/9,... in Figure 10- 17 We pass bounding_box_of_points() 21 arguments: the dimension 2 and the 10 pairs of coordinates describing the 10 points in Figure 10- 17: @bb = bounding_box_of_points(2, 1, 2, 2, 5, 5, 4, 5, 7, 3, 5, 7, 4, 2, 3, 5, 5, 1, 7, 6, 1), "\n"; print "@bb\n"; The result is the lower-left and upper-right vertices of the square, (1, 1) and (7, 7) :break 1 1 7 7 Page 452 Figure 10- 17 A polygon and... print insert_dollar(int(-5 678 .1234 * * * * # Round down to the penny print insert_dollar(floor( 1234.5 678 print insert_dollar(floor(-1234.5 678 print insert_dollar(floor( 5 678 .1234 print insert_dollar(floor(-5 678 .1234 # Round up to the penny print insert_dollar(ceil( 1234.5 678 print insert_dollar(ceil(-1234.5 678 print insert_dollar(ceil( 5 678 .1234 print insert_dollar(ceil(-5 678 .1234 100) 100) 100) 100)... prints prints "1234.5 678 00" (defaults to %.6f) "1235" "1234.6" "1234. 57" "1234.568" "1234.5 678 " "1234.5 678 0" " 1234.6" " 1234. 57" "1234.568" "1234.5 678 " "1234.5 678 0" (width 8, precision 5) The %e field formats numbers according to exponential notation, as in 1.234e+03 for 1234 On many systems, the %E field does the same thing but displays an uppercase E: 1.234E+03.break Page 477 printf printf printf... directive to countermand an earlier use integer: #!/usr/bin /perl -w use constant pi => 3.14159265358 979 ; use integer; %planets = (Mercury Earth Jupiter Uranus Pluto => => => => => [88, 58.05e6], Venus => [365.25, 150e6], Mars => [4331.9, 78 0e6], Saturn => [30681, 2 877 e6], Neptune => [90582, 73 95e6]), # but [224 .7, 108.45e6], [6 87, 228.6e6], [1 076 0.3, 1431e6], [60266.3, 4059e6], Pluto's orbit varies sub... you the results when various fields are applied to 1234.5 678 We'll start off with %d: printf printf printf printf "%d", "%2d", "%6d", "%.6d", 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; 1234.5 678 ; # # # # prints prints prints prints "1234" "1234" " 1234" (width of 6) "001234" (precision of 6) None of the digits before the decimal point are ever sacrificed with a %d field The same is true for %f, although %.of or... environments, but some have upcoming Windows ports (Gtk, as of mid 1999).break * Perl' s Tk module should not be confused with the Tk toolkit, which was originally written by John Ousterhout for use with his programming language, Tcl The Tk toolkit is language-independent, and that's why it can interface with, for example, Perl The Perl/ Tk module is an interface to the toolkit Page 468 Gnome by Kenneth Albanowski . 2, 3, 1, 7, 2, 5, 5, 7, 7, 4, 5, 5, 6, 1), " "; print "@bb "; The result is the lower-left and upper-right vertices of the square, (1, 1) and (7, 7) :break 1 1 7 7 Page 452 Figure. " "; print "( 7, 4): ", point_in_polygon( 7, 4, @polygon ), " "; print "( 7, 1): ", point_in_polygon( 7, 1, @polygon ), " "; print "( 7, -2): ",. 7, -2): ", point_in_polygon( 7, -2, @polygon ), " "; The results: ( 3, 4): 1 ( 3, 1): 1 ( 3,-2): 0 ( 5, 4): 0 ( 5, 1): 0 ( 5,-2): 0 ( 7, 4): 0 ( 7, 1): 1 ( 7, -2): 0 This tells us that

Ngày đăng: 12/08/2014, 21:20

TỪ KHÓA LIÊN QUAN