I used NURBS-python and found an interesting problem, and not sure this is done like this intentionally or it is simply a bug. I would like to introduce this with 2 codes.
The first one should output the same as the second one, while it is not. The first one successfully updated the control points of the NURBS curve and drawed a new curve,
The way to change the control points list influence the results.
import math
from geomdl import BSpline
from geomdl import NURBS
from geomdl import fitting
from geomdl import convert
from geomdl.visualization import VisMPL
P1=[[0, 0], [0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [0, 0]]
Degree=3
CPN=5
P2=[[0, 0], [0, 1], [2, 1], [2, 0], [2, -1], [0, -1], [0, 0]]
CP=[[0, 0], [1, 2], [2, 0], [1, -2], [0, 0]]
c1=NURBS.Curve()
c1.degree=Degree
c1.ctrlpts=P1
c1.weights=[1, 1, 1, 1, 1, 1, 1]
c1.knotvector=[0, 0, 0, 0, 0.25, 0.5, 0.75, 1, 1, 1, 1]
c1.vis=VisMPL.VisCurve2D()
c1.render()
c1.ctrlpts=P2
c1.vis=VisMPL.VisCurve2D()
c1.render()
c2=convert.bspline_to_nurbs(fitting.approximate_curve(P1, Degree, ctrlpts_size=CPN))
c2.vis=VisMPL.VisCurve2D()
c2.render()
c2.ctrlpts=CP
c2.vis=VisMPL.VisCurve2D()
c2.render()
while the second one simply update the control points, the curve itself didn't change at all.
import math
from geomdl import BSpline
from geomdl import NURBS
from geomdl import fitting
from geomdl import convert
from geomdl.visualization import VisMPL
P1=[[0, 0], [0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [0, 0]]
Degree=3
CPN=5
P2=[[0, 0], [0, 1], [2, 1], [2, 0], [2, -1], [0, -1], [0, 0]]
CP=[[0, 0], [1, 2], [2, 0], [1, -2], [0, 0]]
c1=NURBS.Curve()
c1.degree=Degree
c1.ctrlpts=P1
c1.weights=[1, 1, 1, 1, 1, 1, 1]
c1.knotvector=[0, 0, 0, 0, 0.25, 0.5, 0.75, 1, 1, 1, 1]
c1.vis=VisMPL.VisCurve2D()
c1.render()
for i in range(len(P2)):
c1.ctrlpts[i]=P2[i]
c1.vis=VisMPL.VisCurve2D()
c1.render()
c2=convert.bspline_to_nurbs(fitting.approximate_curve(P1, Degree, ctrlpts_size=CPN))
c2.vis=VisMPL.VisCurve2D()
c2.render()
for i in range(len(CP)):
c2.ctrlpts[i]=CP[i]
c2.vis=VisMPL.VisCurve2D()
c2.render()
Please help out, thanks.
Curve.ctrlpts
is a property. When you access the getter, it returns the list of control points. Accessing the setter does a lot more, mostly consistency checking and necessary cleanups.
When cpts = c1.ctrlpts
is called, __get__
method of the property is called, reference of the list object that stores the control points inside the Curve
instance is returned and assigned to cpts
variable:
>>> cpts = c1.ctrlpts
>>> type(cpts)
<class 'list'>
When you loop through the list cpts
, you actually loop through its reference in the Curve
class instance. Since you are interacting with the list object directly, you don't access the ctrlpts
setter, which actually does the cleanups by calling Curve.reset()
method.
Unfortunately, it is not possible to override methods of the built-in classes (the classes defined on the API level).
>>> list.__getitem__ = my_getter_method
TypeError: can't set attributes of built-in/extension type 'list'
As a result, triggering the Curve.reset()
method when the underlying list
object changes becomes a little bit complicated (but not impossible).
The best and the safest way to set control points is using the setter method of the property:
my_ctrlpts = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
c1.ctrlpts = my_ctrlpts
If you would like to use the for loop, then you need to call the reset method manually:
for i in range(len(P2)):
c1.ctrlpts[i]=P2[i]
c1.reset(evalpts=True)
Note: I am the author of NURBS-Python (geomdl)