"""One-dimensional Bezier curves used by CST geometry helpers."""
from __future__ import annotations
from collections.abc import Sequence
from buffalo_core.typing import FloatArray, FloatInput, FloatScalar
from .bezier_common import (
BezierDemotionContinuity,
bezier_degree,
demote_bezier_coefficients,
evaluate_bernstein,
evaluate_bernstein_u,
evaluate_bernstein_uu,
promote_bezier_coefficients,
validate_bezier_coefficients,
)
type BezierCurve1DInput = Sequence[FloatScalar] | FloatArray
[docs]
class BezierCurve1D:
"""One-dimensional Bezier curve."""
def __init__(self, *, coefficients: BezierCurve1DInput) -> None:
"""
Initialize one one-dimensional Bezier curve.
Parameters
----------
coefficients : Sequence[FloatScalar] | FloatArray
Bernstein coefficients ordered from ``u = 0`` to ``u = 1``.
Raises
------
ValueError
If ``coefficients`` is empty or does not have shape ``(n,)``.
"""
coefficient_array = validate_bezier_coefficients(
coefficients,
name="BezierCurve1D coefficients",
)
if coefficient_array.size == 0:
msg = "BezierCurve1D requires at least one coefficient."
raise ValueError(msg)
if coefficient_array.ndim != 1:
msg = "BezierCurve1D coefficients must have shape (n,)."
raise ValueError(msg)
self._coefficients = coefficient_array
@property
def coefficients(self) -> FloatArray:
"""
Return the stored Bernstein coefficients.
This property exposes the stored read-only ``(n,)`` Bernstein
coefficient array.
"""
return self._coefficients
@property
def degree(self) -> int:
"""
Return the Bezier degree.
This property reports the polynomial degree of the
one-dimensional Bezier curve.
"""
return bezier_degree(self.coefficients)
[docs]
def evaluate(self, u: FloatInput) -> FloatArray:
"""
Evaluate the one-dimensional Bezier curve.
Parameters
----------
u : FloatInput
Parameter values, typically in ``[0, 1]``.
Returns
-------
FloatArray
Curve values at ``u`` with the same broadcasted shape as the
normalized input.
"""
return evaluate_bernstein(self.coefficients, u)
[docs]
def evaluate_u(self, u: FloatInput) -> FloatArray:
"""
Evaluate the first derivative of the one-dimensional curve.
Parameters
----------
u : FloatInput
Parameter values, typically in ``[0, 1]``.
Returns
-------
FloatArray
First derivative values at ``u`` with the same broadcasted
shape as the normalized input.
"""
return evaluate_bernstein_u(self.coefficients, u)
[docs]
def evaluate_uu(self, u: FloatInput) -> FloatArray:
"""
Evaluate the second derivative of the one-dimensional curve.
Parameters
----------
u : FloatInput
Parameter values, typically in ``[0, 1]``.
Returns
-------
FloatArray
Second derivative values at ``u`` with the same broadcasted
shape as the normalized input.
"""
return evaluate_bernstein_uu(self.coefficients, u)
[docs]
def demote_degree(
self,
*,
count: int = 1,
continuity: BezierDemotionContinuity = "NOT_CONNECTED",
) -> BezierCurve1D:
"""
Lower the Bezier degree with constrained least-squares demotion.
This operation is intentionally approximate.
It solves a constrained least-squares degree-reduction problem and
does not guarantee exact preservation of the original curve, except in
the special cases where the original curve is exactly reducible to the
requested lower degree.
Parameters
----------
count : int, default=1
Number of degree-reduction steps to apply.
continuity : {"NOT_CONNECTED", "C0", "C1", "C2"},
default="NOT_CONNECTED"
Symmetric endpoint continuity to preserve during each demotion
step.
``"NOT_CONNECTED"`` leaves the endpoints unconstrained.
``"C0"``, ``"C1"``, and ``"C2"`` preserve endpoint value,
value-plus-first-derivative, and value-plus-first-two-derivatives,
respectively, when the current degree allows it.
Returns
-------
BezierCurve1D
Reduced-degree one-dimensional Bezier curve produced by a
constrained least-squares approximate demotion.
"""
coefficients = demote_bezier_coefficients(
self.coefficients,
count=count,
continuity=continuity,
)
return BezierCurve1D(coefficients=coefficients)