"""Classes associated with general curves."""
from abc import ABC, abstractmethod
import numpy as np
from scipy.integrate import quad
from buffalo_wings.internal.numeric import as_float_array, as_float_scalar
from buffalo_wings.type_aliases import FloatArray, FloatInput, FloatScalar
[docs]
class Curve(ABC):
"""
Base class for 1-d curves.
Curves can be interrogated based on their natural parameterization, using
the parameter, t.
"""
#
# Parametric interface
#
[docs]
@abstractmethod
def xy(self, t: FloatInput) -> tuple[FloatArray, FloatArray]:
"""
Calculate the coordinates of geometry at parameter location.
Parameters
----------
t : numpy.ndarray
Parameter for desired locations.
Returns
-------
numpy.ndarray
X-coordinate of point.
numpy.ndarray
Y-coordinate of point.
"""
[docs]
@abstractmethod
def xy_t(self, t: FloatInput) -> tuple[FloatArray, FloatArray]:
"""
Calculate rates of change of the coordinates at parameter location.
Parameters
----------
t : numpy.ndarray
Parameter for desired locations.
Returns
-------
numpy.ndarray
Parametric rate of change of the x-coordinate of point.
numpy.ndarray
Parametric rate of change of the y-coordinate of point.
"""
[docs]
@abstractmethod
def xy_tt(self, t: FloatInput) -> tuple[FloatArray, FloatArray]:
"""
Calculate second derivative of the coordinates at parameter location.
Parameters
----------
t : numpy.ndarray
Parameter for desired locations.
Returns
-------
numpy.ndarray
Parametric second derivative of the x-coordinate of point.
numpy.ndarray
Parametric second derivative of the y-coordinate of point.
"""
[docs]
def normal(self, t: FloatInput) -> tuple[FloatArray, FloatArray]:
"""
Calculate the unit normal at parameter location.
Parameters
----------
t : numpy.ndarray
Parameter for desired locations.
Returns
-------
numpy.ndarray, numpy.ndarray
Unit normal at point.
"""
sx, sy = self.tangent(t)
nx = -sy
ny = sx
return nx, ny
[docs]
def tangent(self, t: FloatInput) -> tuple[FloatArray, FloatArray]:
"""
Calculate the unit tangent at parameter location.
Parameters
----------
t : numpy.ndarray
Parameter for desired locations.
Returns
-------
numpy.ndarray, numpy.ndarray
Unit tangent at point.
"""
sx, sy = self.xy_t(t)
temp = np.sqrt(sx**2 + sy**2)
sx = as_float_array(np.divide(sx, temp))
sy = as_float_array(np.divide(sy, temp))
return sx, sy
[docs]
def k(self, t: FloatInput) -> FloatArray:
"""
Calculate the curvature at parameter location.
Parameters
----------
t : numpy.ndarray
Parameter for desired locations.
Returns
-------
numpy.ndarray
Curvature of surface at point.
"""
xt, yt = self.xy_t(t)
xtt, ytt = self.xy_tt(t)
return (xt * ytt - yt * xtt) / (xt**2 + yt**2) ** (3 / 2)
[docs]
def arc_length(self, t_s: FloatScalar, t_e: FloatInput) -> FloatArray:
"""
Calculate the arc-length distance between two points on surface.
Parameters
----------
t_s : float
Start point of distance calculation.
t_e : numpy.ndarray
End point of distance calculation.
Returns
-------
numpy.ndarray
Distance from start point to end point.
"""
def fun(t: float) -> float:
xt, yt = self.xy_t(t)
return as_float_scalar(np.sqrt(xt**2 + yt**2))
t_begin = float(t_s)
t_e = as_float_array(t_e)
it = np.nditer([t_e, None])
with it:
for ti, alen in it:
segment_ends = [x for x in self.joints() if t_begin < x < ti]
segment_ends.append(float(ti))
segment_start = t_begin
alen[...] = 0.0
for t_end in segment_ends:
alen[...] += quad(fun, segment_start, t_end)[0]
segment_start = t_end
return it.operands[1]
[docs]
@abstractmethod
def joints(self) -> list[float]:
"""
Return the locations of any joints/discontinuities in the curve.
The resulting list needs to contain any parametric locations where
some non-standard discontinuity (slope, curvature, etc.) occurs as
well as the end points for the curve (if they exist).
Returns
-------
List[float]
Parametric coordinates of any discontinuities.
"""