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.002';
3 5     5   3849 use 5.014;
  5         18  
4 5     5   27 use warnings;
  5         11  
  5         162  
5              
6             # ABSTRACT: B-spline curves
7              
8 5     5   2798 use Moo 2.002005;
  5         58353  
  5         35  
9 5     5   7563 use List::Util 1.26 ('min');
  5         93  
  5         840  
10 5     5   2609 use Ref::Util 0.010 ('is_plain_arrayref');
  5         8214  
  5         385  
11 5     5   2443 use Log::Any 1.044 ('$logger');
  5         42194  
  5         28  
12 5     5   14341 use Math::BSpline::Basis 0.001;
  5         134801  
  5         5192  
13              
14              
15              
16             has '_degree' => (
17             is => 'ro',
18             required => 1,
19             init_arg => 'degree',
20             );
21              
22              
23              
24             has '_knot_vector' => (
25             is => 'ro',
26             init_arg => 'knot_vector',
27             predicate => 1,
28             );
29              
30              
31              
32             has 'control_points' => (
33             is => 'lazy',
34 0     0   0 builder => sub { return [] },
35             );
36              
37              
38              
39             has 'basis' => (
40             is => 'lazy',
41             handles => [
42             'degree',
43             'knot_vector',
44             ],
45             builder => sub {
46 29     29   50126 my ($self) = @_;
47              
48 29 100       562 return Math::BSpline::Basis->new(
49             degree => $self->_degree,
50             (
51             $self->_has_knot_vector
52             ? (knot_vector => $self->_knot_vector)
53             : (),
54             ),
55             )
56             }
57             );
58              
59              
60              
61             sub evaluate {
62 294     294 1 168658 my ($self, $u) = @_;
63 294         6992 my $basis = $self->basis;
64              
65 294         8541 my $p = $self->degree;
66 294         11937 my $P = $self->control_points;
67 294         2513 my $s = $basis->find_knot_span($u);
68 294         14372 my $Nip = $basis->evaluate_basis_functions($s, $u);
69              
70 294 50       25261 return undef if (!@$P);
71 294         472 my $value;
72 294 50       799 if (is_plain_arrayref($P->[0])) {
73             # The control points are plain arrayrefs, hence we have no
74             # overloaded scalar multiplication at our disposal and have
75             # to manipulate the components individually.
76 294         455 my $dim = scalar(@{$P->[0]});
  294         511  
77 294         685 $value = [map { 0 } (1..$dim)];
  588         1164  
78 294         764 for (my $i=0;$i<=$p;$i++) {
79 1152         1721 my $c = $Nip->[$i];
80 1152         1756 my $this_P = $P->[$s-$p+$i];
81 1152         2077 for (my $j=0;$j<$dim;$j++) {
82 2304         5432 $value->[$j] += $c * $this_P->[$j];
83             }
84             }
85             }
86             else {
87             # We use the first control point to initialize the value in
88             # order to support all objects that overload addition and
89             # scalar multiplication.
90 0         0 $value = 0 * $P->[0];
91 0         0 for (my $i=0;$i<=$p;$i++) {
92 0         0 $value += $Nip->[$i] * $P->[$s-$p+$i];
93             }
94             }
95              
96 294         901 return $value;
97             }
98              
99              
100              
101             sub evaluate_derivatives {
102 13     13 1 72702 my ($self, $u, $d) = @_;
103 13         323 my $basis = $self->basis;
104              
105 13         1609 my $p = $self->degree;
106 13         537 my $P = $self->control_points;
107 13         112 my $s = $basis->find_knot_span($u);
108 13         584 my $D = $basis->evaluate_basis_derivatives($s, $u, min($d, $p));
109              
110 13 50       5601 return undef if (!@$P);
111 13         25 my $value = [];
112 13 50       45 if (is_plain_arrayref($P->[0])) {
113             # The control points are plain arrayrefs, hence we have no
114             # overloaded scalar multiplication at our disposal and have
115             # to manipulate the components individually.
116 13         22 my $dim = scalar(@{$P->[0]});
  13         24  
117 13         37 for (my $k=0;$k<=$d;$k++) {
118 50         91 $value->[$k] = [map { 0 } (1..$dim)];
  100         188  
119              
120 50 50       97 if ($k <= $p) {
121 50         102 for (my $i=0;$i<=$p;$i++) {
122 246         338 my $c = $D->[$k]->[$i];
123 246         368 my $this_P = $P->[$s-$p+$i];
124 246         429 for (my $j=0;$j<$dim;$j++) {
125 492         1076 $value->[$k]->[$j] += $c * $this_P->[$j];
126             }
127             }
128             }
129             }
130             }
131             else {
132             # We use the first control point to initialize the value in
133             # order to support all objects that overload addition and
134             # scalar multiplication.
135 0         0 for (my $k=0;$k<=$d;$k++) {
136 0         0 $value->[$k] = 0 * $P->[0];
137              
138 0 0       0 if ($k <= $p) {
139 0         0 for (my $i=0;$i<=$p;$i++) {
140 0         0 $value->[$k] += $D->[$k]->[$i] * $P->[$s-$p+$i];
141             }
142             }
143             }
144             }
145              
146 13         44 return $value;
147             }
148              
149              
150             sub derivative {
151 6     6 1 12926 my ($self) = @_;
152 6         129 my $p = $self->degree;
153 6         443 my $P = $self->control_points;
154 6         122 my $U = $self->knot_vector;
155              
156 6 50       252 return undef if (!@$P);
157              
158 6         12 my $q = $p - 1;
159 6         32 my $V = [@$U[1..($#$U-1)]];
160 6         14 my $Q = [];
161 6 50       20 if (is_plain_arrayref($P->[0])) {
162             # The control points are plain arrayrefs, hence we have no
163             # overloaded scalar multiplication at our disposal and have
164             # to manipulate the components individually.
165 6         8 my $dim = scalar(@{$P->[0]});
  6         13  
166 6         22 for (my $i=0;$i<@$P-1;$i++) {
167 33         69 my $c = $p / ($U->[$i+$p+1] - $U->[$i+1]);
168 33         52 $Q->[$i] = [];
169 33         65 for (my $j=0;$j<$dim;$j++) {
170 66         191 $Q->[$i]->[$j] = $c * ($P->[$i+1]->[$j] - $P->[$i]->[$j]);
171             }
172             }
173             }
174             else {
175 0         0 for (my $i=0;$i<@$P-1;$i++) {
176 0         0 my $c = $p / ($U->[$i+$p+1] - $U->[$i+1]);
177 0         0 $Q->[$i] = $c * ($P->[$i+1] - $P->[$i]);
178             }
179             }
180              
181 6         113 return Math::BSpline::Curve->new(
182             degree => $q,
183             knot_vector => $V,
184             control_points => $Q,
185             );
186             }
187              
188             with ('Math::BSpline::Curve::Role::Approximation');
189              
190              
191             1;
192              
193             __END__