Math-BSpline-Basis
view release on metacpan or search on metacpan
lib/Math/BSpline/Basis.pm view on Meta::CPAN
return $class->$orig($munged_args);
};
has 'degree' => (
is => 'ro',
required => 1,
);
has 'knot_vector' => (
is => 'lazy',
builder => sub {
my ($self) = @_;
my $p = $self->degree;
return [
(map { 0 } (0..$p)),
(map { 1 } (0..$p)),
]
}
);
# I use the same variable names as in the NURBS book, although some
# of them are very generic. The use of $p, $U, $P, and $n is
# consistent throughout the relevant chapters of the book.
sub find_knot_span {
my ($self, $u) = @_;
my $p = $self->degree;
my $U = $self->knot_vector;
my $n = (@$U - 1) - $p - 1;
# We expect $u in [$U->[$p], $U->[$n+1]]. We only support
# values outside this range for rounding errors, do not assume
# that the result makes sense otherwise.
return $n if ($u >= $U->[$n+1]);
return $p if ($u <= $U->[$p]);
# binary search
my $low = $p;
my $high = $n + 1;
my $mid = int(($low + $high) / 2);
while ($u < $U->[$mid] or $u >= $U->[$mid+1]) {
if ($u < $U->[$mid]) { $high = $mid }
else { $low = $mid }
$mid = int(($low + $high) / 2);
}
return $mid;
}
# The variable names are inspired by the theory as laid out in the
# NURBS book. We want to calculate N_{i,p}, that inspires $N and
# $p. U is the knot vector, left and right are inspired by the
# terms in the formulas used in the theoretical derivation.
sub evaluate_basis_functions {
my ($self, $i, $u) = @_;
my $p = $self->degree;
my $U = $self->knot_vector;
my $n = (@$U - 1) - $p - 1;
if ($u < $U->[$p] or $u > $U->[$n+1]) {
return [map { 0 } (0..$p)];
}
my $N = [1];
my $left = [];
my $right = [];
for (my $j=1;$j<=$p;$j++) {
$left->[$j] = $u - $U->[$i+1-$j];
$right->[$j] = $U->[$i+$j] - $u;
my $saved = 0;
for (my $r=0;$r<$j;$r++) {
my $temp = $N->[$r] / ($right->[$r+1] + $left->[$j-$r]);
$N->[$r] = $saved + $right->[$r+1] * $temp;
$saved = $left->[$j-$r] * $temp;
}
$N->[$j] = $saved;
}
return $N;
}
sub evaluate_basis_derivatives {
my ($self, $i, $u, $d) = @_;
my $p = $self->degree;
my $U = $self->knot_vector;
my $n = (@$U - 1) - $p - 1;
my $result = [];
$d = min($d, $p);
if ($u < $U->[$p] or $u > $U->[$n+1]) {
for (my $k=0;$k<=$d;$k++) {
push(@$result, [map { 0 } (0..$p)]);
}
return $result;
}
my $ndu = [[1]];
my $left = [];
my $right = [];
for (my $j=1;$j<=$p;$j++) {
$left->[$j] = $u - $U->[$i+1-$j];
$right->[$j] = $U->[$i+$j] - $u;
my $saved = 0;
for (my $r=0;$r<$j;$r++) {
$ndu->[$j]->[$r] = $right->[$r+1] + $left->[$j-$r];
my $temp = $ndu->[$r]->[$j-1] / $ndu->[$j]->[$r];
$ndu->[$r]->[$j] = $saved + $right->[$r+1] * $temp;
$saved = $left->[$j-$r] * $temp;
}
$ndu->[$j]->[$j] = $saved;
( run in 2.689 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )