File Coverage

blib/lib/Math/BSpline/Curve.pm
Criterion Covered Total %
statement 79 91 86.8
branch 9 18 50.0
condition n/a
subroutine 11 12 91.6
pod 3 3 100.0
total 102 124 82.2


line stmt bran cond sub pod time code
1             package Math::BSpline::Curve;
2             $Math::BSpline::Curve::VERSION = '0.001';
3 4     4   2575 use 5.014;
  4         14  
4 4     4   17 use warnings;
  4         8  
  4         118  
5              
6             # ABSTRACT: B-spline curves
7              
8 4     4   1880 use Moo 2.002005;
  4         39436  
  4         21  
9 4     4   5145 use List::Util 1.26 ('min');
  4         82  
  4         371  
10             use Ref::Util 0.010 (
11 4         263 'is_plain_arrayref',
12 4     4   1723 );
  4         5477  
13 4     4   1761 use Math::BSpline::Basis 0.001;
  4         91418  
  4         179  
14 4     4   1772 use Math::Matrix::Banded 0.004;
  4         72373  
  4         3299  
15              
16              
17             has '_degree' => (
18             is => 'ro',
19             required => 1,
20             init_arg => 'degree',
21             );
22              
23              
24              
25             has '_knot_vector' => (
26             is => 'ro',
27             init_arg => 'knot_vector',
28             predicate => 1,
29             );
30              
31              
32              
33             has 'control_points' => (
34             is => 'lazy',
35 0     0   0 builder => sub { return [] },
36             );
37              
38              
39              
40             has 'basis' => (
41             is => 'lazy',
42             handles => [
43             'degree',
44             'knot_vector',
45             ],
46             builder => sub {
47 26     26   42675 my ($self) = @_;
48              
49 26 100       440 return Math::BSpline::Basis->new(
50             degree => $self->_degree,
51             (
52             $self->_has_knot_vector
53             ? (knot_vector => $self->_knot_vector)
54             : (),
55             ),
56             )
57             }
58             );
59              
60              
61              
62             sub evaluate {
63 54     54 1 13305 my ($self, $u) = @_;
64 54         1046 my $basis = $self->basis;
65              
66 54         2158 my $p = $self->degree;
67 54         2035 my $P = $self->control_points;
68 54         401 my $s = $basis->find_knot_span($u);
69 54         1908 my $Nip = $basis->evaluate_basis_functions($s, $u);
70              
71 54 50       4007 return undef if (!@$P);
72 54         84 my $value;
73 54 50       123 if (is_plain_arrayref($P->[0])) {
74             # The control points are plain arrayrefs, hence we have no
75             # overloaded scalar multiplication at our disposal and have
76             # to manipulate the components individually.
77 54         70 my $dim = scalar(@{$P->[0]});
  54         91  
78 54         141 $value = [map { 0 } (1..$dim)];
  108         193  
79 54         130 for (my $i=0;$i<=$p;$i++) {
80 192         276 my $c = $Nip->[$i];
81 192         283 my $this_P = $P->[$s-$p+$i];
82 192         362 for (my $j=0;$j<$dim;$j++) {
83 384         816 $value->[$j] += $c * $this_P->[$j];
84             }
85             }
86             }
87             else {
88             # We use the first control point to initialize the value in
89             # order to support all objects that overload addition and
90             # scalar multiplication.
91 0         0 $value = 0 * $P->[0];
92 0         0 for (my $i=0;$i<=$p;$i++) {
93 0         0 $value += $Nip->[$i] * $P->[$s-$p+$i];
94             }
95             }
96              
97 54         146 return $value;
98             }
99              
100              
101              
102             sub evaluate_derivatives {
103 13     13 1 72578 my ($self, $u, $d) = @_;
104 13         326 my $basis = $self->basis;
105              
106 13         1537 my $p = $self->degree;
107 13         546 my $P = $self->control_points;
108 13         124 my $s = $basis->find_knot_span($u);
109 13         585 my $D = $basis->evaluate_basis_derivatives($s, $u, min($d, $p));
110              
111 13 50       5676 return undef if (!@$P);
112 13         39 my $value = [];
113 13 50       62 if (is_plain_arrayref($P->[0])) {
114             # The control points are plain arrayrefs, hence we have no
115             # overloaded scalar multiplication at our disposal and have
116             # to manipulate the components individually.
117 13         25 my $dim = scalar(@{$P->[0]});
  13         30  
118 13         38 for (my $k=0;$k<=$d;$k++) {
119 50         110 $value->[$k] = [map { 0 } (1..$dim)];
  100         190  
120              
121 50 50       105 if ($k <= $p) {
122 50         100 for (my $i=0;$i<=$p;$i++) {
123 246         339 my $c = $D->[$k]->[$i];
124 246         355 my $this_P = $P->[$s-$p+$i];
125 246         417 for (my $j=0;$j<$dim;$j++) {
126 492         1143 $value->[$k]->[$j] += $c * $this_P->[$j];
127             }
128             }
129             }
130             }
131             }
132             else {
133             # We use the first control point to initialize the value in
134             # order to support all objects that overload addition and
135             # scalar multiplication.
136 0         0 for (my $k=0;$k<=$d;$k++) {
137 0         0 $value->[$k] = 0 * $P->[0];
138              
139 0 0       0 if ($k <= $p) {
140 0         0 for (my $i=0;$i<=$p;$i++) {
141 0         0 $value->[$k] += $D->[$k]->[$i] * $P->[$s-$p+$i];
142             }
143             }
144             }
145             }
146              
147 13         51 return $value;
148             }
149              
150              
151             sub derivative {
152 6     6 1 12959 my ($self) = @_;
153 6         127 my $p = $self->degree;
154 6         443 my $P = $self->control_points;
155 6         126 my $U = $self->knot_vector;
156              
157 6 50       256 return undef if (!@$P);
158              
159 6         13 my $q = $p - 1;
160 6         24 my $V = [@$U[1..($#$U-1)]];
161 6         13 my $Q = [];
162 6 50       20 if (is_plain_arrayref($P->[0])) {
163             # The control points are plain arrayrefs, hence we have no
164             # overloaded scalar multiplication at our disposal and have
165             # to manipulate the components individually.
166 6         10 my $dim = scalar(@{$P->[0]});
  6         10  
167 6         23 for (my $i=0;$i<@$P-1;$i++) {
168 33         68 my $c = $p / ($U->[$i+$p+1] - $U->[$i+1]);
169 33         59 $Q->[$i] = [];
170 33         60 for (my $j=0;$j<$dim;$j++) {
171 66         187 $Q->[$i]->[$j] = $c * ($P->[$i+1]->[$j] - $P->[$i]->[$j]);
172             }
173             }
174             }
175             else {
176 0         0 for (my $i=0;$i<@$P-1;$i++) {
177 0         0 my $c = $p / ($U->[$i+$p+1] - $U->[$i+1]);
178 0         0 $Q->[$i] = $c * ($P->[$i+1] - $P->[$i]);
179             }
180             }
181              
182 6         110 return Math::BSpline::Curve->new(
183             degree => $q,
184             knot_vector => $V,
185             control_points => $Q,
186             );
187             }
188              
189              
190             1;
191              
192             __END__