mod bezier;
mod cutting;
mod elevation;
mod removal;
use bezier::{Walker, bezier_pieces};
pub(crate) use cutting::{
check_piece, check_split, cut, is_clamped_to, reversed_knots, reversed_slot,
};
pub(crate) use elevation::Elevation;
pub(crate) use removal::{check_removal, remove_knots};
use super::bspline::KnotVector;
use crate::Point;
use crate::curves::KnotInsertionError;
pub(crate) trait Pole: Copy + Default {
fn combine(self, a: f64, other: Self, b: f64) -> Self;
fn scale(self, a: f64) -> Self;
fn parts(poles: &[Self]) -> (Vec<Point>, Option<Vec<f64>>);
}
impl Pole for Point {
#[inline]
fn combine(self, a: f64, other: Self, b: f64) -> Self {
Point::new(
a * self.x + b * other.x,
a * self.y + b * other.y,
a * self.z + b * other.z,
)
}
#[inline]
fn scale(self, a: f64) -> Self {
Point::new(a * self.x, a * self.y, a * self.z)
}
fn parts(poles: &[Self]) -> (Vec<Point>, Option<Vec<f64>>) {
(poles.to_vec(), None)
}
}
impl Pole for [f64; 4] {
#[inline]
fn combine(self, a: f64, other: Self, b: f64) -> Self {
std::array::from_fn(|c| a * self[c] + b * other[c])
}
#[inline]
fn scale(self, a: f64) -> Self {
self.map(|c| a * c)
}
fn parts(poles: &[Self]) -> (Vec<Point>, Option<Vec<f64>>) {
let points = poles
.iter()
.map(|&[x, y, z, w]| {
let inverse = 1.0 / w;
Point::new(x * inverse, y * inverse, z * inverse)
})
.collect();
(points, Some(poles.iter().map(|h| h[3]).collect()))
}
}
pub(crate) trait Source: Copy {
type Pole: Pole;
const CONVERTS: bool;
fn len(self) -> usize;
fn get(self, i: usize) -> Self::Pole;
fn slice(self, range: std::ops::Range<usize>) -> Self;
#[inline]
fn copy_to(self, start: usize, out: &mut [Self::Pole]) {
for (k, out) in out.iter_mut().enumerate() {
*out = self.get(start + k);
}
}
#[inline]
fn extend_to(self, range: std::ops::Range<usize>, out: &mut Vec<Self::Pole>) {
out.extend(range.map(|i| self.get(i)));
}
}
impl<P: Pole> Source for &[P] {
type Pole = P;
const CONVERTS: bool = false;
#[inline]
fn len(self) -> usize {
<[P]>::len(self)
}
#[inline]
fn get(self, i: usize) -> P {
self[i]
}
#[inline]
fn slice(self, range: std::ops::Range<usize>) -> Self {
&self[range]
}
#[inline]
fn copy_to(self, start: usize, out: &mut [P]) {
let poles = &self[start..start + out.len()];
if out.len() <= 4 {
for (out, &pole) in out.iter_mut().zip(poles) {
*out = pole;
}
} else {
out.copy_from_slice(poles);
}
}
#[inline]
fn extend_to(self, range: std::ops::Range<usize>, out: &mut Vec<P>) {
out.extend_from_slice(&self[range]);
}
}
#[derive(Clone, Copy)]
struct Weighted<'a> {
points: &'a [Point],
weights: &'a [f64],
}
impl Source for Weighted<'_> {
type Pole = [f64; 4];
const CONVERTS: bool = true;
#[inline]
fn len(self) -> usize {
self.points.len()
}
#[inline]
fn get(self, i: usize) -> [f64; 4] {
let (p, w) = (self.points[i], self.weights[i]);
[w * p.x, w * p.y, w * p.z, w]
}
#[inline]
fn slice(self, range: std::ops::Range<usize>) -> Self {
Weighted {
points: &self.points[range.clone()],
weights: &self.weights[range],
}
}
#[inline]
fn copy_to(self, start: usize, out: &mut [[f64; 4]]) {
let range = start..start + out.len();
let poles = self.points[range.clone()].iter().zip(&self.weights[range]);
for (out, (p, &w)) in out.iter_mut().zip(poles) {
*out = [w * p.x, w * p.y, w * p.z, w];
}
}
#[inline]
fn extend_to(self, range: std::ops::Range<usize>, out: &mut Vec<[f64; 4]>) {
let poles = self.points[range.clone()].iter().zip(&self.weights[range]);
out.extend(poles.map(|(p, &w)| [w * p.x, w * p.y, w * p.z, w]));
}
}
fn from_homogeneous(
edited: &Edited<[f64; 4]>,
points: &[Point],
weights: &[f64],
lanes: Lanes,
) -> (Vec<Point>, Vec<f64>) {
let (rows, width) = lanes.rows_and_width();
let (old_row, new_row) = (points.len() / rows, edited.knots.pole_count() * width);
let (leading, trailing) = (edited.kept.leading * width, edited.kept.trailing * width);
let changed_count = new_row - leading - trailing;
let stride = edited.poles.len() / rows;
let mut new_points = Vec::with_capacity(new_row * rows);
let mut new_weights = Vec::with_capacity(new_row * rows);
for row in 0..rows {
let old = row * old_row..(row + 1) * old_row;
let old_end = old.end - trailing;
new_points.extend_from_slice(&points[old.start..old.start + leading]);
new_weights.extend_from_slice(&weights[old.start..old.start + leading]);
let first = row * stride + leading - edited.start * width;
let changed = &edited.poles[first..first + changed_count];
new_points.extend(changed.iter().map(|&[x, y, z, w]| {
let inverse = 1.0 / w;
Point::new(x * inverse, y * inverse, z * inverse)
}));
new_weights.extend(changed.iter().map(|h| h[3]));
new_points.extend_from_slice(&points[old_end..old.end]);
new_weights.extend_from_slice(&weights[old_end..old.end]);
}
(new_points, new_weights)
}
pub(crate) struct Edited<P> {
pub(crate) knots: KnotVector,
pub(crate) poles: Vec<P>,
pub(crate) kept: Kept,
pub(crate) start: usize,
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct Kept {
pub(crate) leading: usize,
pub(crate) trailing: usize,
}
pub(crate) trait Edit {
type Error;
fn apply<S: Source>(
&self,
direction: &KnotVector,
poles: S,
lanes: Lanes,
) -> Result<Edited<S::Pole>, Self::Error>;
#[allow(clippy::type_complexity)] fn apply_rational(
&self,
direction: &KnotVector,
points: &[Point],
weights: &[f64],
lanes: Lanes,
) -> Result<(KnotVector, Vec<Point>, Vec<f64>), Self::Error> {
let edited = self.apply(direction, Weighted { points, weights }, lanes)?;
let (rows, width) = lanes.rows_and_width();
let new_row = edited.knots.pole_count() * width;
if edited.start == 0 && edited.poles.len() == rows * new_row {
let mut new_weights: Vec<f64> = edited.poles.iter().map(|h| h[3]).collect();
let mut new_points: Vec<Point> = (edited.poles.into_iter())
.map(|[x, y, z, w]| {
let inverse = 1.0 / w;
Point::new(x * inverse, y * inverse, z * inverse)
})
.collect();
new_points.shrink_to_fit();
let old_row = points.len() / rows;
let (leading, trailing) = (edited.kept.leading * width, edited.kept.trailing * width);
for row in 0..rows {
let (new, old) = (row * new_row, row * old_row);
for (new, old) in [
(new..new + leading, old..old + leading),
(
new + new_row - trailing..new + new_row,
old + old_row - trailing..old + old_row,
),
] {
new_points[new.clone()].copy_from_slice(&points[old.clone()]);
new_weights[new].copy_from_slice(&weights[old]);
}
}
return Ok((edited.knots, new_points, new_weights));
}
let (points, weights) = from_homogeneous(&edited, points, weights, lanes);
Ok((edited.knots, points, weights))
}
}
#[allow(clippy::type_complexity)] pub(crate) fn edit<E: Edit>(
edit: &E,
direction: &KnotVector,
points: &[Point],
weights: Option<&[f64]>,
lanes: Lanes,
) -> Result<(KnotVector, Vec<Point>, Option<Vec<f64>>), E::Error> {
match weights {
None => {
let edited = edit.apply(direction, points, lanes)?;
Ok((edited.knots, edited.poles, None))
}
Some(weights) => {
let (knots, points, weights) =
edit.apply_rational(direction, points, weights, lanes)?;
Ok((knots, points, Some(weights)))
}
}
}
pub(crate) fn curve_pieces(
direction: &KnotVector,
points: &[Point],
weights: Option<&[f64]>,
mut visit: impl FnMut(f64, f64, Vec<Point>, Option<Vec<f64>>),
) {
fn run<S: Source>(
direction: &KnotVector,
poles: S,
visit: &mut impl FnMut(f64, f64, Vec<Point>, Option<Vec<f64>>),
) {
Walker::new(direction, 1).for_each_piece(poles, One, |_, x, y, piece| {
let (points, weights) = S::Pole::parts(piece);
visit(x, y, points, weights);
});
}
match weights {
None => run(direction, points, &mut visit),
Some(weights) => run(direction, Weighted { points, weights }, &mut visit),
}
}
pub(crate) fn surface_patches(
u: &KnotVector,
v: &KnotVector,
points: &[Point],
weights: Option<&[f64]>,
mut visit: impl FnMut(usize, (f64, f64), (f64, f64), Vec<Point>, Option<Vec<f64>>),
) {
#[allow(clippy::type_complexity)] fn run<S: Source>(
u: &KnotVector,
v: &KnotVector,
poles: S,
visit: &mut impl FnMut(usize, (f64, f64), (f64, f64), Vec<Point>, Option<Vec<f64>>),
) {
let (v_breaks, strips) = bezier_pieces(v, poles, Lanes::rows(u.pole_count()));
let width = v.degree() + 1;
let mut walker = Walker::new(u, width);
for (strip, span) in strips
.chunks_exact(u.pole_count() * width)
.zip(v_breaks.windows(2))
{
walker.for_each_piece(strip, width, |i, x, y, patch| {
let (points, weights) = S::Pole::parts(patch);
visit(i, (x, y), (span[0], span[1]), points, weights);
});
}
}
match weights {
None => run(u, v, points, &mut visit),
Some(weights) => run(u, v, Weighted { points, weights }, &mut visit),
}
}
pub(crate) struct Insertion<'a>(pub(crate) &'a [f64]);
impl Edit for Insertion<'_> {
type Error = KnotInsertionError;
fn apply<S: Source>(
&self,
direction: &KnotVector,
poles: S,
lanes: Lanes,
) -> Result<Edited<S::Pole>, KnotInsertionError> {
insert_knots(direction, poles, lanes, self.0)
}
fn apply_rational(
&self,
direction: &KnotVector,
points: &[Point],
weights: &[f64],
lanes: Lanes,
) -> Result<(KnotVector, Vec<Point>, Vec<f64>), KnotInsertionError> {
if direction.is_periodic() || self.0.is_empty() {
let edited = insert_knots(direction, Weighted { points, weights }, lanes, self.0)?;
let (points, weights) = from_homogeneous(&edited, points, weights, lanes);
return Ok((edited.knots, points, weights));
}
insert_rational(direction, points, weights, lanes, self.0)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Lanes {
pub(crate) count: usize,
consecutive: bool,
}
impl Lanes {
pub(crate) const CURVE: Lanes = Lanes {
count: 1,
consecutive: true,
};
pub(crate) fn columns(v_count: usize) -> Lanes {
Lanes {
count: v_count,
consecutive: false,
}
}
pub(crate) fn rows(u_count: usize) -> Lanes {
Lanes {
count: u_count,
consecutive: true,
}
}
fn rows_and_width(self) -> (usize, usize) {
if self.consecutive {
(self.count, 1)
} else {
(1, self.count)
}
}
#[inline]
fn index(self, lane: usize, i: usize, poles: usize) -> usize {
if self.consecutive {
lane * poles + i
} else {
i * self.count + lane
}
}
}
fn insert_knots<S: Source>(
knots: &KnotVector,
poles: S,
lanes: Lanes,
insert: &[f64],
) -> Result<Edited<S::Pole>, KnotInsertionError> {
check_insertion(knots, insert)?;
if insert.is_empty() {
let kept = Kept {
leading: knots.pole_count(),
trailing: 0,
};
let poles = (0..poles.len()).map(|i| poles.get(i)).collect();
return Ok(Edited {
knots: knots.clone(),
poles,
kept,
start: 0,
});
}
let (degree, periodic) = (knots.degree(), knots.is_periodic());
let Merged {
flat: new_flat,
seam_count,
interior,
spans: (first_span, last_span),
} = merged_knots(knots, insert)?;
let interior = &insert[interior];
let (poles, kept, start) = if periodic {
let mut tiled_insert = vec![knots.bounds().0; seam_count];
tiled_insert.extend_from_slice(interior);
let poles = refine_periodic(
degree,
knots.flat(),
&new_flat,
knots.multiplicities()[0] as usize,
&tiled_insert,
poles,
lanes,
);
(poles, Kept::default(), 0)
} else {
let trimmed = S::CONVERTS;
let (out, start) = refine(
degree,
knots.flat(),
&new_flat,
(insert, (first_span, last_span)),
poles,
lanes,
trimmed,
);
let kept = Kept {
leading: first_span - degree + 1,
trailing: knots.pole_count() - last_span,
};
(out, kept, start)
};
let knots = KnotVector::from_flat(degree, new_flat, periodic);
Ok(Edited {
knots,
poles,
kept,
start,
})
}
fn check_insertion(knots: &KnotVector, insert: &[f64]) -> Result<(), KnotInsertionError> {
let (first, last) = knots.bounds();
if insert.iter().any(|x| !x.is_finite()) {
return Err(KnotInsertionError::NonFiniteKnot);
}
if insert.windows(2).any(|w| w[1] < w[0]) {
return Err(KnotInsertionError::KnotsNotSorted);
}
if insert
.first()
.zip(insert.last())
.is_some_and(|(&lo, &hi)| lo < first || hi > last)
{
return Err(KnotInsertionError::KnotOutOfRange);
}
Ok(())
}
struct Merged {
flat: Vec<f64>,
seam_count: usize,
interior: std::ops::Range<usize>,
spans: (usize, usize),
}
fn merged_knots(knots: &KnotVector, insert: &[f64]) -> Result<Merged, KnotInsertionError> {
let (first, last) = knots.bounds();
let (degree, periodic) = (knots.degree(), knots.is_periodic());
let seam = |x: f64| periodic && (x == first || x == last);
let seam_count = insert.iter().filter(|&&x| seam(x)).count();
let interior = if seam_count == 0 {
0..insert.len()
} else {
let leading = insert.iter().take_while(|&&x| x == first).count();
leading..leading + insert.len() - seam_count
};
if seam_count > 0 && knots.multiplicities()[0] as usize + seam_count > degree {
return Err(KnotInsertionError::MultiplicityTooLarge);
}
let old = knots.flat();
let ends = (old[0], old[old.len() - 1]);
let total = old.len() + insert.len() + seam_count;
let mut flat = Vec::with_capacity(total);
flat.resize(total, 0.0);
let out = &mut flat[..];
out[..seam_count].fill(first);
let (mut written, mut copied) = (seam_count, 0);
for &x in &insert[interior.clone()] {
while copied < old.len() && old[copied] <= x {
out[written] = old[copied];
(written, copied) = (written + 1, copied + 1);
}
out[written] = x;
written += 1;
let copies = out[..written].iter().rev().take_while(|&&k| k == x).count();
let clamped_end = !periodic && (x == ends.0 || x == ends.1);
if copies > degree + usize::from(clamped_end) {
return Err(KnotInsertionError::MultiplicityTooLarge);
}
}
let pole_count = old.len().saturating_sub(degree + 1);
let span = |at_most: usize| at_most.min(pole_count).saturating_sub(1);
let last_span = span(copied);
let spans = match insert[interior.clone()] {
[] | [_] => (last_span, last_span),
[x, ..] => (span(old.partition_point(|&k| k <= x)), last_span),
};
let rest = old.len() - copied;
out[written..written + rest].copy_from_slice(&old[copied..]);
out[written + rest..].fill(last);
debug_assert!(periodic || insert.is_empty() || spans == self::spans(degree, old, insert));
Ok(Merged {
flat,
seam_count,
interior,
spans,
})
}
fn refine<S: Source>(
degree: usize,
old: &[f64],
new: &[f64],
(x, x_spans): (&[f64], (usize, usize)),
poles: S,
lanes: Lanes,
trimmed: bool,
) -> (Vec<S::Pole>, usize) {
let (p, s) = (degree, x.len());
let n = old.len() - p - 1;
let (rows, width) = lanes.rows_and_width();
if s == 0 {
let mut out = Vec::with_capacity(poles.len());
poles.extend_to(0..poles.len(), &mut out);
return (out, 0);
}
let (a, last) = x_spans;
let b = last + 1;
let slots = refined_slots(p, n, s, x_spans, trimmed);
let mut out = Vec::with_capacity(slots.len() * lanes.count);
for row in 0..rows {
let row_start = row * n * width;
poles.extend_to(
row_start + slots.start * width..row_start + (a - p + 1) * width,
&mut out,
);
out.resize(out.len() + (b + s - a + p - 2) * width, S::Pole::default());
poles.extend_to(
row_start + (b - 1) * width..row_start + (slots.end - s) * width,
&mut out,
);
}
let row_len = slots.len() * width;
for (row, row_out) in out.chunks_exact_mut(row_len).enumerate() {
let row_poles = poles.slice(row * n * width..(row + 1) * n * width);
refine_row(
p,
old,
new,
(x, x_spans),
row_poles,
lanes,
slots.start,
row_out,
);
}
(out, slots.start)
}
fn refined_slots(
degree: usize,
n: usize,
s: usize,
(first_span, last_span): (usize, usize),
trimmed: bool,
) -> std::ops::Range<usize> {
if trimmed {
first_span - degree..(last_span + 1 + degree + s).min(n + s)
} else {
0..n + s
}
}
fn spans(degree: usize, old: &[f64], x: &[f64]) -> (usize, usize) {
let n = old.len() - degree - 1;
let span = |u: f64| degree + old[degree..n].partition_point(|&k| k <= u) - 1;
(span(x[0]), span(x[x.len() - 1]))
}
trait Count: Copy {
fn get(self) -> usize;
}
impl Count for usize {
#[inline]
fn get(self) -> usize {
self
}
}
#[derive(Clone, Copy)]
struct One;
impl Count for One {
#[inline]
fn get(self) -> usize {
1
}
}
#[derive(Clone, Copy)]
struct Fixed<const N: usize>;
impl<const N: usize> Count for Fixed<N> {
#[inline]
fn get(self) -> usize {
N
}
}
#[allow(clippy::type_complexity)] fn insert_rational(
knots: &KnotVector,
points: &[Point],
weights: &[f64],
lanes: Lanes,
insert: &[f64],
) -> Result<(KnotVector, Vec<Point>, Vec<f64>), KnotInsertionError> {
const SMALL: usize = 16;
const MEDIUM: usize = 48;
const STACK: usize = 128;
check_insertion(knots, insert)?;
let Merged {
flat: new_flat,
spans: (a, last),
..
} = merged_knots(knots, insert)?;
let (p, old, s) = (knots.degree(), knots.flat(), insert.len());
let n = knots.pole_count();
let (rows, width) = lanes.rows_and_width();
let b = last + 1;
let slots = refined_slots(p, n, s, (a, last), true);
let (leading, trailing_start) = (a - p + 1, b - 1 + s);
let source = Weighted { points, weights };
let window_row = slots.len() * width;
let size = rows * (n + s) * width;
let (mut new_points, mut new_weights) = (Vec::with_capacity(size), Vec::with_capacity(size));
let (mut small, mut medium, mut stack);
let mut heap = Vec::new();
let window = if rows * window_row <= SMALL {
small = [[0.0; 4]; SMALL];
&mut small[..rows * window_row]
} else if rows * window_row <= MEDIUM {
medium = [[0.0; 4]; MEDIUM];
&mut medium[..rows * window_row]
} else if rows * window_row <= STACK {
stack = [[0.0; 4]; STACK];
&mut stack[..rows * window_row]
} else {
heap.resize(rows * window_row, [0.0; 4]);
&mut heap[..]
};
for (row, out) in window.chunks_exact_mut(window_row).enumerate() {
let at = |i: usize| (i - slots.start) * width;
let old_slot = |i: usize| (row * n + i) * width;
source.copy_to(old_slot(b - 1), &mut out[at(trailing_start)..]);
let row_poles = source.slice(old_slot(0)..old_slot(n));
refine_row(
p,
old,
&new_flat,
(insert, (a, last)),
row_poles,
lanes,
slots.start,
out,
);
new_points.extend_from_slice(&points[old_slot(0)..old_slot(leading)]);
new_weights.extend_from_slice(&weights[old_slot(0)..old_slot(leading)]);
let changed = &out[at(leading)..at(trailing_start)];
new_points.extend(changed.iter().map(|&[x, y, z, w]| {
let inverse = 1.0 / w;
Point::new(x * inverse, y * inverse, z * inverse)
}));
new_weights.extend(changed.iter().map(|h| h[3]));
new_points.extend_from_slice(&points[old_slot(b - 1)..old_slot(n)]);
new_weights.extend_from_slice(&weights[old_slot(b - 1)..old_slot(n)]);
}
let knots = KnotVector::from_flat(p, new_flat, false);
Ok((knots, new_points, new_weights))
}
#[allow(clippy::too_many_arguments)] fn refine_row<S: Source>(
degree: usize,
old: &[f64],
new: &[f64],
x_spans: (&[f64], (usize, usize)),
poles: S,
lanes: Lanes,
start: usize,
out: &mut [S::Pole],
) {
#[allow(clippy::too_many_arguments)] fn of_width<S: Source>(
degree: usize,
old: &[f64],
new: &[f64],
x_spans: (&[f64], (usize, usize)),
poles: S,
width: impl Count,
start: usize,
out: &mut [S::Pole],
) {
match degree {
1 => refine_slots(Fixed::<1>, old, new, x_spans, poles, width, start, out),
2 => refine_slots(Fixed::<2>, old, new, x_spans, poles, width, start, out),
3 => refine_slots(Fixed::<3>, old, new, x_spans, poles, width, start, out),
_ => refine_slots(degree, old, new, x_spans, poles, width, start, out),
}
}
if lanes.consecutive {
of_width(degree, old, new, x_spans, poles, One, start, out);
} else {
let (_, width) = lanes.rows_and_width();
of_width(degree, old, new, x_spans, poles, width, start, out);
}
}
#[allow(clippy::too_many_arguments)] fn refine_slots<S: Source>(
degree: impl Count,
old: &[f64],
new: &[f64],
(x, (a, last)): (&[f64], (usize, usize)),
poles: S,
width: impl Count,
start: usize,
out: &mut [S::Pole],
) {
let (width, p) = (width.get(), degree.get());
let s = x.len();
debug_assert_eq!(new.len(), old.len() + s);
debug_assert_eq!(poles.len(), width * (old.len() - p - 1));
let b = last + 1;
let at = |i: usize| (i - start) * width;
let mut i = b + p - 1;
let mut k = b + p + s - 1;
for &xj in x.iter().rev() {
let count = old[a + 1..=i]
.iter()
.rev()
.take_while(|&&k| xj <= k)
.count();
let moved = i - count;
poles.copy_to((moved - p) * width, &mut out[at(k - p - count)..at(k - p)]);
(i, k) = (moved, k - count);
let slots = &mut out[at(k - p - 1)..at(k + 1)];
let (head, tail) = slots.split_at_mut(width);
head.copy_from_slice(&tail[..width]);
let knots = new[k + 1..k + p + 1].iter().zip(&old[i - p + 1..i + 1]);
for (index, (&new_knot, &old_knot)) in knots.enumerate() {
let pair = &mut slots[(index + 1) * width..(index + 3) * width];
let (left, right) = pair.split_at_mut(width);
let alpha = new_knot - xj;
if alpha == 0.0 {
left.copy_from_slice(right);
} else {
let alpha = alpha / (new_knot - old_knot);
for (left, &right) in left.iter_mut().zip(&*right) {
*left = left.combine(alpha, right, 1.0 - alpha);
}
}
}
k -= 1;
}
}
fn refine_periodic<S: Source>(
degree: usize,
flat: &[f64],
new_flat: &[f64],
seam: usize,
x: &[f64],
poles: S,
lanes: Lanes,
) -> Vec<S::Pole> {
let (first, period) = (flat[0], flat[flat.len() - 1] - flat[0]);
let new_seam = seam + x.iter().take_while(|&&k| k == first).count();
let (n, new_n) = (flat.len() - seam, new_flat.len() - new_seam);
let (block, new_block) = (&flat[..n], &new_flat[..new_n]);
let tiled = |block: &[f64], k: isize| {
let len = block.len() as isize;
block[k.rem_euclid(len) as usize] + period * k.div_euclid(len) as f64
};
let p = degree as isize;
let new_start = new_seam as isize - p - 1;
let (index, tile) = (
(new_start + 1).rem_euclid(new_n as isize) as usize,
(new_start + 1).div_euclid(new_n as isize),
);
let old_index = block.partition_point(|&k| k <= new_block[index]) - 1;
let anchor = old_index as isize + tile * n as isize;
let anchor_knot = tiled(block, anchor);
let window_start = anchor - p;
let window_end = (n + seam) as isize - 1;
let old_knots: Vec<f64> = (window_start..=window_end + p)
.map(|k| tiled(block, k))
.collect();
let mut inserted = Vec::with_capacity(x.len() * (1 - tile) as usize + new_seam - seam);
for t in tile..=0 {
inserted.extend(
x.iter()
.map(|&k| k + period * t as f64)
.filter(|&k| k >= anchor_knot),
);
}
inserted.extend(std::iter::repeat_n(
tiled(block, n as isize),
new_seam - seam,
));
let mut new_knots = Vec::with_capacity(old_knots.len() + inserted.len());
let (mut i, mut j) = (0, 0);
while i < old_knots.len() || j < inserted.len() {
if j == inserted.len() || (i < old_knots.len() && old_knots[i] <= inserted[j]) {
new_knots.push(old_knots[i]);
i += 1;
} else {
new_knots.push(inserted[j]);
j += 1;
}
}
let count = old_knots.len() - p as usize - 1;
let offset = seam as isize - p - 1;
let window = Lanes::rows(lanes.count);
let mut window_poles = vec![S::Pole::default(); lanes.count * count];
for l in 0..lanes.count {
for w in 0..count {
let pole = (window_start + w as isize - offset).rem_euclid(n as isize) as usize;
window_poles[window.index(l, w, count)] = poles.get(lanes.index(l, pole, n));
}
}
let new_count = count + inserted.len();
debug_assert!(!inserted.is_empty());
let (refined, _) = refine(
degree,
&old_knots,
&new_knots,
(&inserted, spans(degree, &old_knots, &inserted)),
window_poles.as_slice(),
window,
false,
);
let anchor_last = (new_block.partition_point(|&k| k <= block[old_index]) - 1) as isize
+ tile * new_n as isize;
let shift = anchor_last - (new_knots.partition_point(|&k| k <= anchor_knot) as isize - 1);
let start = (new_start - shift) as usize;
debug_assert!(start + new_n <= new_count);
let mut out = vec![S::Pole::default(); lanes.count * new_n];
for l in 0..lanes.count {
for w in 0..new_n {
out[lanes.index(l, w, new_n)] = refined[window.index(l, start + w, new_count)];
}
}
out
}