home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / Bezier.pm < prev    next >
Encoding:
Perl POD Document  |  2000-10-19  |  5.7 KB  |  221 lines

  1. #========================================================================
  2. # Math::Bezier
  3. #
  4. # Module for the solution of Bezier curves based on the algorithm 
  5. # presented by Robert D. Miller in Graphics Gems V, "Quick and Simple
  6. # Bezier Curve Drawing".
  7. #
  8. # Andy Wardley <abw@kfs.org>
  9. #
  10. # Copyright (C) 2000 Andy Wardley.  All Rights Reserved.
  11. #
  12. # This module is free software; you can redistribute it and/or
  13. # modify it under the same terms as Perl itself.
  14. #
  15. #========================================================================
  16.  
  17. package Math::Bezier;
  18.  
  19. use strict;
  20. use vars qw( $VERSION );
  21.  
  22. $VERSION = '0.01';
  23.  
  24. use constant X  => 0;
  25. use constant Y  => 1;
  26. use constant CX => 2;
  27. use constant CY => 3;
  28.  
  29.  
  30. #------------------------------------------------------------------------
  31. # new($x1, $y1, $x2, $y2, ..., $xn, $yn)
  32. #
  33. # Constructor method to create a new Bezier curve form.
  34. #------------------------------------------------------------------------
  35.  
  36. sub new {
  37.     my $class = shift;
  38.     my @points = ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_;
  39.     my $size = scalar @points;
  40.     my @ctrl;
  41.  
  42.     die "invalid control points, expects (x1, y1, x2, y2, ..., xn, yn)\n"
  43.     if $size % 2;
  44.  
  45.     while (@points) {
  46.     push(@ctrl, [ splice(@points, 0, 2) ]);
  47.     }
  48.     $size = scalar @ctrl;
  49.  
  50.     my $n = $size - 1;
  51.     my $choose;
  52.  
  53.     for (my $k = 0; $k <= $n; $k++) {
  54.     if ($k == 0) {
  55.         $choose = 1;
  56.     }
  57.     elsif ($k == 1) {
  58.         $choose = $n;
  59.     }
  60.     else {
  61.         $choose *= ($n - $k + 1) / $k;
  62.     }
  63.     $ctrl[$k]->[CX] = $ctrl[$k]->[X] * $choose;
  64.     $ctrl[$k]->[CY] = $ctrl[$k]->[Y] * $choose;
  65.     }
  66.  
  67.     bless \@ctrl, $class;
  68. }
  69.  
  70.  
  71. #------------------------------------------------------------------------
  72. # point($theta)
  73. #
  74. # Calculate (x, y) point on curve at position $theta (in the range 0 - 1)
  75. # along the curve.  Returns a list ($x, $y) or reference to a list 
  76. # [$x, $y] when called in list or scalar context respectively.
  77. #------------------------------------------------------------------------
  78.  
  79. sub point {
  80.     my ($self, $t) = @_;
  81.     my $size = scalar @$self;
  82.     my (@points, $point);
  83.  
  84.     my $n = $size - 1;
  85.     my $u = $t;
  86.  
  87.     push(@points, [ $self->[0]->[CX], $self->[0]->[CY] ]);
  88.  
  89.     for (my $k = 1; $k <= $n; $k++) {
  90.     push(@points, [ $self->[$k]->[CX] * $u, $self->[$k]->[CY] * $u ]);
  91.     $u *= $t;
  92.     }
  93.  
  94.     $point = [ @{ $points[$n] } ];
  95.     my $t1 = 1 - $t;
  96.     my $tt = $t1;
  97.  
  98.     for (my $k = $n - 1; $k >= 0; $k--) {
  99.     $point->[X] += $points[$k]->[X] * $tt;
  100.     $point->[Y] += $points[$k]->[Y] * $tt;
  101.     $tt = $tt * $t1;
  102.     }
  103.  
  104.     return wantarray ? (@$point) : $point;
  105. }    
  106.  
  107.  
  108. #------------------------------------------------------------------------
  109. # curve($npoints)
  110. #
  111. # Sample curve at $npoints points.  Returns a list or reference to a list 
  112. # of (x, y) points along the curve, when called in list or scalar context
  113. # respectively.
  114. #------------------------------------------------------------------------
  115.  
  116. sub curve {
  117.     my ($self, $npoints) = @_;
  118.     $npoints = 20 unless defined $npoints;
  119.     my @points;
  120.     $npoints--;
  121.     foreach (my $t = 0; $t <= $npoints; $t++) {
  122.     push(@points, ($self->point($t / $npoints)));
  123.     }
  124.     return wantarray ? (@points) : \@points;
  125. }
  126.  
  127. 1;
  128.  
  129. __END__
  130.  
  131. =head1 NAME
  132.  
  133. Math::Bezier - solution of Bezier Curves
  134.  
  135. =head1 SYNOPSIS
  136.  
  137.     use Math::Bezier;
  138.  
  139.     # create curve passing list of (x, y) control points
  140.     my $bezier = Math::Bezier->new($x1, $y1, $x2, $y2, ..., $xn, $yn);
  141.  
  142.     # or pass reference to list of control points
  143.     my $bezier = Math::Bezier->new([ $x1, $y1, $x2, $y2, ..., $xn, $yn]);
  144.  
  145.     # determine (x, y) at point along curve, range 0 -> 1
  146.     my ($x, $y) = $bezier->point(0.5);
  147.  
  148.     # returns list ref in scalar context
  149.     my $xy = $bezier->point(0.5);
  150.  
  151.     # return list of 20 (x, y) points along curve
  152.     my @curve = $bezier->curve(20);
  153.  
  154.     # returns list ref in scalar context
  155.     my $curve = $bezier->curve(20);
  156.  
  157. =head1 DESCRIPTION
  158.  
  159. This module implements the algorithm for the solution of Bezier curves
  160. as presented by Robert D. Miller in Graphics Gems V, "Quick and Simple
  161. Bezier Curve Drawing".
  162.  
  163. A new Bezier curve is created using the new() constructor, passing a list
  164. of (x, y) control points.
  165.  
  166.     use Math::Bezier;
  167.  
  168.     my @control = ( 0, 0, 10, 20, 30, -20, 40, 0 );
  169.     my $bezier  = Math::Bezier->new(@control);
  170.  
  171. Alternately, a reference to a list of control points may be passed.
  172.  
  173.     my $bezier  = Math::Bezier->new(\@control);
  174.  
  175. The point($theta) method can then be called on the object, passing a
  176. value in the range 0 to 1 which represents the distance along the
  177. curve.  When called in list context, the method returns the x and y
  178. coordinates of that point on the Bezier curve.
  179.  
  180.     my ($x, $y) = $bezier->point(0.5);
  181.     print "x: $x  y: $y\n
  182.  
  183. When called in scalar context, it returns a reference to a list containing
  184. the x and y coordinates.
  185.  
  186.     my $point = $bezier->point(0.5);
  187.     print "x: $point->[0]  y: $point->[1]\n";
  188.  
  189. The curve($n) method can be used to return a set of points sampled
  190. along the length of the curve (i.e. in the range 0 <= $theta <= 1).
  191. The parameter indicates the number of sample points required,
  192. defaulting to 20 if undefined.  The method returns a list of ($x1,
  193. $y1, $x2, $y2, ..., $xn, $yn) points when called in list context, or 
  194. a reference to such an array when called in scalar context.
  195.  
  196.     my @points = $bezier->curve(10);
  197.  
  198.     while (@points) {
  199.     my ($x, $y) = splice(@points, 0, 2);
  200.     print "x: $x  y: $y\n";
  201.     }
  202.  
  203.     my $points = $bezier->curve(10);
  204.  
  205.     while (@$points) {
  206.     my ($x, $y) = splice(@$points, 0, 2);
  207.     print "x: $x  y: $y\n";
  208.     }
  209.  
  210. =head1 AUTHOR
  211.  
  212. Andy Wardley E<lt>abw@kfs.orgE<gt>
  213.  
  214. =head1 SEE ALSO
  215.  
  216. Graphics Gems 5, edited by Alan W. Paeth, Academic Press, 1995,
  217. ISBN 0-12-543455-3.  Section IV.8, 'Quick and Simple Bezier Curve
  218. Drawing' by Robert D. Miller, pages 206-209.
  219.  
  220. =cut
  221.