BSplineX-python new wrapper #4
Replies: 4 comments 19 replies
|
Overall, I think the wrapper is a good idea, and the draft shows how necessary it is. One thing I don't like are dummy subclasses class OpenUniform(BSpline):
pass
class OpenNonUniform(BSpline):
passSince they have no actual meaning besides (maybe) helping readability. I had some alternative solutions in mind, for instance, we could get completely rid of the factory functions in the Python interface, and overload the constructors of the subclasses instead. |
|
For example, we could have these constructors: class OpenUniform(BSpline):
@overload
def __init__(self, degree: int) -> None:
"""Create a uniform b-spline with open boundary condition
> NOTE: useful constructor for interpolating points
:param degree: degree of the b-spline
:return: the open uniform b-spline
"""
...
@overload
def __init__(self, degree: int, begin: float, end: float, num_elems: int) -> None:
"""Create a uniform b-spline with open boundary condition
> NOTE: useful constructor for fitting points
:param degree: degree of the b-spline
:param begin: starting knot
:param end: end knot
:param num_elems: number of knots
:return: the open uniform b-spline
"""
...
@overload
def __init__(
self, degree: int, begin: float, end: float, num_elems: int, ctrl_points: npt.NDArray[np.float64] | list[float]
) -> None:
"""Create a uniform b-spline with open boundary condition
:param degree: degree of the b-spline
:param begin: starting knot
:param end: end knot
:param num_elems: number of knots
:param ctrl_points: control points
:return: the open uniform b-spline
"""
...
def __init__(
self,
degree: int,
begin: float | None = None,
end: float | None = None,
num_elems: int | None = None,
ctrl_points: npt.NDArray[np.float64] | list[float] | None = None,
) -> None:
"""Create a uniform b-spline with open boundary condition
This function has three possible overloads:
- make_open_uniform(degree)
- make_open_uniform(degree, begin, end, num_elems)
- make_open_uniform(degree, begin, end, num_elems, ctrl_points)
Note that only `make_open_uniform(degree, begin, end, num_elems, ctrl_points)`
generates a valid b-spline, the other overloads are meant to
be used for fitting (`make_open_uniform(degree, begin, end, num_elems)`)
and interpolation (`make_open_uniform(degree))`
:param degree: degree of the b-spline
:param begin: starting knot
:param end: end knot
:param num_elems: number of knots
:param ctrl_points: control points
:return: the open uniform b-spline
"""
if all((arg is not None for arg in (begin, end, num_elems, ctrl_points))):
super().__init__(_impl.make_open_uniform(degree, begin, end, num_elems, ctrl_points))
return
if all((arg is not None for arg in (begin, end, num_elems))) and ctrl_points is None:
super().__init__(_impl.make_open_uniform(degree, begin, end, num_elems))
return
if all((arg is None for arg in (begin, end, num_elems, ctrl_points))):
super().__init__(_impl.make_open_uniform(degree))
return
error = """
This function has three possible overloads:
- make_open_uniform(degree)
- make_open_uniform(degree, begin, end, num_elems)
- make_open_uniform(degree, begin, end, num_elems, ctrl_points)
"""
raise ValueError(dedent(error))I tested them and the overloads working (now also the docstring per overload). We need to understand if it actually works because it has happened to me before that it randomly stopped showing the docstrings correctly for the overloads. that being said nvim does a good job at showing them and vscode an exceptional one, feels like c++ overloads. Obviously this is to be ironed out, I do not like the triple if in the constructor, but the idea is there I think. I still have no idea what kind of functionality we may add, but this direction feels way more pythonic, I think I like it. Anyway, I am now settled and ready to work on this again 😉 🚀 |
|
How about something like this class BSpline:
_bspline: _impl.OpenUniform | _impl.OpenNonUniform
def __init__(self, bspline: _impl.OpenUniform | _impl.OpenNonUniform) -> None:
self._bspline = bspline
@overload
def evaluate(self, x: float, derivative_order: int = 0) -> float: ...
@overload
def evaluate(self, x: npt.NDArray[np.float64], derivative_order: int = 0) -> npt.NDArray[np.float64]: ...
def evaluate(
self, x: npt.NDArray[np.float64] | float, derivative_order: int = 0
) -> npt.NDArray[np.float64] | float:
"""evaluate the b-spline (derivative) at the given value(s)
:param x: value(s) to evaluate the b-spline at
:param derivative_order: order of the derivative, default 0
:return: the value(s) of the b-spline
"""
return self._bspline.evaluate(x, derivative_order)
@overload
@classmethod
def make_open_uniform(cls, degree: int) -> "BSpline":
"""Create a uniform b-spline with open boundary condition
> NOTE: useful constructor for interpolating points
:param degree: degree of the b-spline
:return: the open uniform b-spline
"""
...
@overload
@classmethod
def make_open_uniform(cls, degree: int, begin: float, end: float, num_elems: int) -> "BSpline":
"""Create a uniform b-spline with open boundary condition
> NOTE: useful constructor for fitting points
:param degree: degree of the b-spline
:param begin: starting knot
:param end: end knot
:param num_elems: number of knots
:return: the open uniform b-spline
"""
...
@overload
@classmethod
def make_open_uniform(
cls, degree: int, begin: float, end: float, num_elems: int, ctrl_points: npt.NDArray[np.float64] | list[float]
) -> "BSpline":
"""Create a uniform b-spline with open boundary condition
:param degree: degree of the b-spline
:param begin: starting knot
:param end: end knot
:param num_elems: number of knots
:param ctrl_points: control points
:return: the open uniform b-spline
"""
...
@classmethod
def make_open_uniform(
cls,
degree: int,
begin: float | None = None,
end: float | None = None,
num_elems: int | None = None,
ctrl_points: npt.NDArray[np.float64] | list[float] | None = None,
) -> "BSpline":
if all((arg is not None for arg in (begin, end, num_elems, ctrl_points))):
return cls(_impl.make_open_uniform(degree, begin, end, num_elems, ctrl_points))
if all((arg is not None for arg in (begin, end, num_elems))) and ctrl_points is None:
return cls(_impl.make_open_uniform(degree, begin, end, num_elems))
if all((arg is None for arg in (begin, end, num_elems, ctrl_points))):
return cls(_impl.make_open_uniform(degree))
error = """
This function has three possible overloads:
- make_open_uniform(degree)
- make_open_uniform(degree, begin, end, num_elems)
- make_open_uniform(degree, begin, end, num_elems, ctrl_points)
"""
raise ValueError(dedent(error)) |
|
Here is hopefully the last draft, let me know what you think @masinag class BSpline:
_bspline: _impl.OpenUniform | _impl.OpenNonUniform
def __init__(self, bspline: _impl.OpenUniform | _impl.OpenNonUniform) -> None:
self._bspline = bspline
def evaluate(self, x: npt.ArrayLike, derivative_order: int = 0) -> npt.NDArray[np.float64]:
"""evaluate the b-spline (derivative) at the given value(s)
:param x: values to evaluate the b-spline at
:param derivative_order: order of the derivative
:return: the values of the b-spline evalutated for each x
"""
return np.asarray(self._bspline.evaluate(x, derivative_order))
@overload
def make_open_uniform(degree: int) -> BSpline:
"""Create a uniform b-spline with open boundary condition
> NOTE: useful constructor for interpolating points
:param degree: degree of the b-spline
:return: the open uniform b-spline
"""
...
@overload
def make_open_uniform(degree: int, begin: float, end: float, num_elems: int) -> BSpline:
"""Create a uniform b-spline with open boundary condition
> NOTE: useful constructor for fitting points
:param degree: degree of the b-spline
:param begin: starting knot
:param end: end knot
:param num_elems: number of knots
:return: the open uniform b-spline
"""
...
@overload
def make_open_uniform(
degree: int, begin: float, end: float, num_elems: int, ctrl_points: npt.NDArray[np.float64] | list[float]
) -> BSpline:
"""Create a uniform b-spline with open boundary condition
:param degree: degree of the b-spline
:param begin: starting knot
:param end: end knot
:param num_elems: number of knots
:param ctrl_points: control points
:return: the open uniform b-spline
"""
...
def make_open_uniform(
degree: int,
begin: float | None = None,
end: float | None = None,
num_elems: int | None = None,
ctrl_points: npt.NDArray[np.float64] | list[float] | None = None,
) -> BSpline:
if all((arg is not None for arg in (begin, end, num_elems, ctrl_points))):
return BSpline(_impl.make_open_uniform(degree, begin, end, num_elems, ctrl_points))
if all((arg is not None for arg in (begin, end, num_elems))) and ctrl_points is None:
return BSpline(_impl.make_open_uniform(degree, begin, end, num_elems))
if all((arg is None for arg in (begin, end, num_elems, ctrl_points))):
return BSpline(_impl.make_open_uniform(degree))
error = """
This function has three possible overloads:
- make_open_uniform(degree)
- make_open_uniform(degree, begin, end, num_elems)
- make_open_uniform(degree, begin, end, num_elems, ctrl_points)
"""
raise ValueError(dedent(error))I quite like this to be honest. I also added a little change to how we handle input, probably having a |

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Let's see if this works as I'd like it to. I hope to be able to link commits and such. Let's use this to discuss the best way to write the new wrapper.
See branch features/wrapper for a taste on the new wrapper. In short I propose the following:
All reactions