Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect control points calculation in knot insertion procedure, under certain circumstances #158

Open
portnov opened this issue Nov 4, 2022 · 1 comment
Labels
bug There is a problem with the coding or algorithms

Comments

@portnov
Copy link

portnov commented Nov 4, 2022

Describe the bug
When using curves with unclamped knotvector, if you try to insert a knot near the right end of knotvector (more precisely, try to insert u_bar which is greater than u[-p-1], where u is knotvector, p is curve's degree) - control points are calculated incorrectly.

To Reproduce
As simple example, consider the curve of degree = 2, with control points:

[[0, 0, 0], [1, 1, 0], [2,1,0], [3, 0, 0]]

and knotvector = [0, 1, ..., 6]. Now try to call operations.insert_knot with u_bar = 4. Expected control points of resulting curve are:

[[0.0,  0.0,  0.0 ],
 [1.0,  1.0,  0.0 ],
 [2.0, 1.0,  0.0 ],
 [2.5,  0.5,  0.0 ],
 [3.0,  0.0,  0.0 ]]

but Geomdl calculates:

[[0. 0. 0.]
 [1. 1. 0.]
 [2. 1. 0.]
 [2. 1. 0.]
 [3. 0. 0.]]

Configuration:

  • geomdl install source: PyPI

Additional Details (Optional)

I believe the problem is here: https://github.com/orbingol/NURBS-Python/blob/5.x/geomdl/operations.py#L82
find_span_linear is implemented in such a way that it never returns value greater than num_ctrlpts. Maybe it is good in some situations, but not in this one.
I had similar problem in Sverchok, and for me the solution was

        k = u.searchsorted(u_bar, side='right')-1
@rendetto
Copy link

rendetto commented Jan 7, 2025

As @portnov suggested the problem is with how the span is calculated near the right end of the knot vector. This leads to wrong control points calculation. An adapted version for Geomdl of the proposed solution can be:

span = np.array(obj.knotvector).searchsorted(param[0], side='right') - 1

but since Geomdl originally does not use NumPy the solution can be:

span = -1
for i in range(len(obj.knotvector) - 1):
    if obj.knotvector[i] <= param[0] < obj.knotvector[i + 1]:
        span = i
        break

It's much slower with basic Python list operations, but in practice it shouldn't matter much.

Originally the problem appeared when I tried to construct closed splines with unclamped knot vector by wrapping the control points as explained here:
https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve-closed.html

I'm using Sverchok/Blender as a test ground. The test case is for B-Spline of degree 5.
Here you can see the preparation phase with construction of the control polygon (in yellow) with 9 control points and a knot vector (-1.25, -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25):

prepare_for_wrapping_BSpline5deg
The white control polygon and points are from the clamped version of the same spline.

After wrapping the control points (align points with indices (1,5)(6,2)(7,3)(0,4,8)) the spline looks like this:

WrappingCVs_BSpline5deg

As a result of the span calculation the last control point (from the white control polygon of the clamped spline) does not match the first but the previous control point (the curvature plot (in green) indicated this as well). It is unexpected behavior for this construction since the control polygon of the unclamped spline (in yellow) is closed.

After applying the solution for span calculation the spline looks smoothly closed as expected:

WrappingCVs_BSpline5deg_Correct

This matches the implementations in Sverchok and also Open Cascade.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug There is a problem with the coding or algorithms
Projects
None yet
Development

No branches or pull requests

2 participants