File Coverage

blib/lib/Math/Bezier.pm
Criterion Covered Total %
statement 62 62 100.0
branch 13 14 92.8
condition n/a
subroutine 9 9 100.0
pod 0 3 0.0
total 84 88 95.4


line stmt bran cond sub pod time code
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
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 1     1   4193 use strict;
  1         2  
  1         36  
20 1     1   5 use vars qw( $VERSION );
  1         2  
  1         89  
21              
22             $VERSION = '0.01';
23              
24 1     1   7 use constant X => 0;
  1         5  
  1         74  
25 1     1   5 use constant Y => 1;
  1         2  
  1         248  
26 1     1   5 use constant CX => 2;
  1         2  
  1         43  
27 1     1   5 use constant CY => 3;
  1         1  
  1         1046  
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 2     2 0 53 my $class = shift;
38 2 100       8 my @points = ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_;
  1         4  
39 2         4 my $size = scalar @points;
40 2         2 my @ctrl;
41              
42 2 50       5 die "invalid control points, expects (x1, y1, x2, y2, ..., xn, yn)\n"
43             if $size % 2;
44              
45 2         7 while (@points) {
46 8         20 push(@ctrl, [ splice(@points, 0, 2) ]);
47             }
48 2         4 $size = scalar @ctrl;
49              
50 2         3 my $n = $size - 1;
51 2         2 my $choose;
52              
53 2         5 for (my $k = 0; $k <= $n; $k++) {
54 8 100       17 if ($k == 0) {
    100          
55 2         2 $choose = 1;
56             }
57             elsif ($k == 1) {
58 2         3 $choose = $n;
59             }
60             else {
61 4         7 $choose *= ($n - $k + 1) / $k;
62             }
63 8         14 $ctrl[$k]->[CX] = $ctrl[$k]->[X] * $choose;
64 8         20 $ctrl[$k]->[CY] = $ctrl[$k]->[Y] * $choose;
65             }
66              
67 2         8 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 62     62 0 227 my ($self, $t) = @_;
81 62         60 my $size = scalar @$self;
82 62         58 my (@points, $point);
83              
84 62         58 my $n = $size - 1;
85 62         50 my $u = $t;
86              
87 62         109 push(@points, [ $self->[0]->[CX], $self->[0]->[CY] ]);
88              
89 62         106 for (my $k = 1; $k <= $n; $k++) {
90 186         298 push(@points, [ $self->[$k]->[CX] * $u, $self->[$k]->[CY] * $u ]);
91 186         292 $u *= $t;
92             }
93              
94 62         51 $point = [ @{ $points[$n] } ];
  62         86  
95 62         74 my $t1 = 1 - $t;
96 62         55 my $tt = $t1;
97              
98 62         105 for (my $k = $n - 1; $k >= 0; $k--) {
99 186         209 $point->[X] += $points[$k]->[X] * $tt;
100 186         193 $point->[Y] += $points[$k]->[Y] * $tt;
101 186         278 $tt = $tt * $t1;
102             }
103              
104 62 100       234 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 2     2 0 17 my ($self, $npoints) = @_;
118 2 100       6 $npoints = 20 unless defined $npoints;
119 2         3 my @points;
120 2         1 $npoints--;
121 2         6 foreach (my $t = 0; $t <= $npoints; $t++) {
122 40         69 push(@points, ($self->point($t / $npoints)));
123             }
124 2 100       24 return wantarray ? (@points) : \@points;
125             }
126              
127             1;
128              
129             __END__