how to handle unclamped BSplineSurface

Need help, or want to share a macro? Post here!
silent_missile
Posts: 16
Joined: Mon Jul 29, 2019 11:07 pm

how to handle unclamped BSplineSurface

Postby silent_missile » Tue Feb 11, 2020 7:46 am

normally we use clamped BSplineCurve and BSplineSurface because the end points locate on the end of the curve/surface.

sometimes we have un-normal application, I need to model unclampled BSplineCurve/Surface, but I meet terrible mistake in FreeCAD.

Code: Select all

import FreeCAD
import Part
FreeCAD.newDocument("Unnamed")
FreeCAD.setActiveDocument("Unnamed")
FreeCAD.ActiveDocument = FreeCAD.getDocument("Unnamed")
# NURBS surface from a Part.BSplineSurface().
# len(knot_u) := nNodes_u + degree_u + 1
# len(knot_v) := nNodes_v + degree_v + 1
poles = [[[9.494737213863035, -0.9949774728396593, 16.27075208109207, 1], [9.549312971732164, 1.2818618094027012, 13.022870257590448, 1], [9.64027660298673, 3.1372910396495417, 8.179986557934111, 1], [13.792828778843486, 7.071431473132651, 2.263030148758937, 1], [18.66382094445385, 13.644095627042061, 0.036666641937649666, 1], [19.95755978150579, 19.260043001655117, -0.18577137390929857, 1], [20.223967211664096, 23.91769736938115, -0.1600081712557669, 1]], [[10.54959252475379, -1.0666044160973747, 16.115361475172744, 1], [10.601569914483544, 1.3552581734317022, 13.000374, 1], [10.66521270155646, 3.297194680340562, 8.392674348292681, 1], [14.45029893750934, 7.228940419325408, 2.62216288948566, 1], [19.02990983203672, 13.658120069707275, 0.38582693574532617, 1], [20.19982941775947, 19.15599130260965, 0.1885200641698277, 1], [20.370150929694518, 23.71914118728892, 0.20572820686804458, 1]], [[12.06322577978621, -0.9969688981643671, 15.888887030570354, 1], [12.107944401245575, 1.4931947244903796, 12.974443, 1], [12.047176263183664, 3.6155461388062426, 8.673813677394223, 1], [15.3082064772923, 7.624687379600255, 3.1455850617688315, 1], [19.37431472068055, 13.929706757034074, 0.9457335577523813, 1], [20.431969920642167, 19.10595922283073, 0.7207913919251576, 1], [20.65284339025161, 23.365573644773345, 0.7320976936595526, 1]], [[14.460502217815266, -1.0006247990229749, 15.533093944119651, 1], [14.531637597792901, 1.488354871063465, 12.924358554845536, 1], [14.33024057532181, 3.9584120614673877, 9.129797391104137, 1], [16.75096586934408, 8.051448354581666, 3.9610032761021405, 1], [20.057965361625392, 14.161066473792669, 1.7871886138158974, 1], [20.845385690978247, 18.985794576116085, 1.5706914182832277, 1], [20.98106749543859, 22.89613272950157, 1.5705642804901863, 1]], [[17.220877854657303, -1.1175357758177094, 15.12423196872025, 1], [17.2920050002584, 1.6686068728447736, 12.868429293751374, 1], [16.898035656679237, 4.482854588794428, 9.640086429627052, 1], [18.341594352637085, 8.691156664361761, 4.9373963105824465, 1], [20.75679229218072, 14.531684628370385, 2.7923453226395663, 1], [21.227643362854163, 18.939505311892784, 2.5458793816560132, 1], [21.255406587089244, 22.43847665757206, 2.531582002124996, 1]], [[19.09251922901625, -1.234709967628515, 14.851229459264252, 1], [19.15970628056484, 1.86385144367135, 12.823885367180173, 1], [18.59844405447844, 4.9697369859564775, 9.974781370512387, 1], [19.342350578774802, 9.221773507445143, 5.5913265392967695, 1], [21.148714998747696, 14.862250240082751, 3.458637780205138, 1], [21.435120022950155, 18.953404465956762, 3.2074948626717026, 1], [21.44896901314071, 22.11569504583196, 3.1821971256879915, 1]], [[20.463206020968265, -1.3019345938575937, 14.64588199741722, 1], [20.512438146506284, 1.9741873335457711, 12.801756427173878, 1], [19.883960134319846, 5.281697285054774, 10.226353395032435, 1], [20.170113184750946, 9.647903782343015, 6.175280005370259, 1], [21.428714098865385, 15.149052359131858, 4.017473027346178, 1], [21.568795215057207, 18.994806697093512, 3.6902837591817383, 1], [21.57654872001159, 21.883578692513357, 3.6565252517244136, 1]]]
degree_u=3
degree_v=3
nNodes_u=7
nNodes_v=7


#knot_u = [0, 0.17684564570425632, 0.45231972770598194, 0.7254117267159598, 1]
#knot_v = [0, 0.24854808618458937, 0.4882656272717224, 0.7322021526243429, 1]
#knot_u_mults = [4, 1, 1, 1, 4]
#knot_v_mults = [4, 1, 1, 1, 4]
knot_u=[0.0, 0.1153846153846154, 0.2514197274648126, 0.46332286746614004, 0.6733936359353538, 0.8846153846153848, 1.0]
knot_u_mults=[3, 1, 1, 1, 1, 1, 3]
knot_v=[0.0, 0.1153846153846154, 0.3065754509112226, 0.4909735594397865, 0.6786170404802637, 0.8846153846153848, 1.0]
knot_v_mults=[3, 1, 1, 1, 1, 1, 3]
NURBS_Cubic_surf=Part.BSplineSurface()
NURBS_Cubic_surf.increaseDegree(degree_u,degree_v)

for i in range(0,len(knot_u)):    #-1):
	NURBS_Cubic_surf.insertUKnot(knot_u[i],knot_u_mults[i],0)

for i in range(0,len(knot_v)):    #-1):
	NURBS_Cubic_surf.insertVKnot(knot_v[i],knot_v_mults[i],0)

poles_xyz=[]
for ii in range(0, len(poles)):
	poles_xyz.append([])
	for jj in range(0, len(poles[ii])):
		poles_xyz[ii].append(FreeCAD.Base.Vector(poles[ii][jj][0], poles[ii][jj][1], poles[ii][jj][2]))
		#tmp=FreeCAD.Base.Vector(poles[ii][jj][0], poles[ii][jj][1], poles[ii][jj][2])
		NURBS_Cubic_surf.setPole(ii+1, jj+1, poles_xyz[ii][jj], poles[ii][jj][3])


Part.show(NURBS_Cubic_surf.toShape())

here the order degree_u and degree_v of the NURBS is 3

as we all know in NURBS theory, if the mults of the end pole is degree_u+1, the end pole would locate at the BSplineCurve/Surface, this is called clamped.

If the mults of the end pole is fewer than degree_u+1, the end pole would not locate at the curve/shape, this is called unclamped.

if it's clamped, FreeCAD works fine; if it's not clamped, the result is completely wrong.
1.jpg
1.jpg (55.99 KiB) Viewed 329 times

is this a bug or a feature? can anybody fix it?

or am I wrong? should I use some other method to build unclamped curve/surface?
User avatar
microelly2
Posts: 4626
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: how to handle unclamped BSplineSurface

Postby microelly2 » Tue Feb 11, 2020 7:58 am

I always use clamped Surfaces and then reduce them with the segment function.
Then endpoles are added but the resulting figure is the same as you expect.
silent_missile
Posts: 16
Joined: Mon Jul 29, 2019 11:07 pm

Re: how to handle unclamped BSplineSurface

Postby silent_missile » Tue Feb 11, 2020 10:16 am

I need to extend the curve/surface, and the extended part is unclamped, this means I have to handle unclamped curve/surface

there is a "mults" parameter for knots, I thought OCCT/FreeCAD can handle it, but I'm disappointed.
wmayer
Site Admin
Posts: 15487
Joined: Thu Feb 19, 2009 10:32 am

Re: how to handle unclamped BSplineSurface

Postby wmayer » Tue Feb 11, 2020 10:36 am

Your creation method is probably wrong. For some reason when inserting knots then number of control points will be increased for an unclamped surface.
After creating the surface you can compare its properties with your input data.

Your method for a clamped surface:

Code: Select all

poles = [[[9.494737213863035, -0.9949774728396593, 16.27075208109207, 1], [9.549312971732164, 1.2818618094027012, 13.022870257590448, 1], [9.64027660298673, 3.1372910396495417, 8.179986557934111, 1], [13.792828778843486, 7.071431473132651, 2.263030148758937, 1], [18.66382094445385, 13.644095627042061, 0.036666641937649666, 1], [19.95755978150579, 19.260043001655117, -0.18577137390929857, 1], [20.223967211664096, 23.91769736938115, -0.1600081712557669, 1]], [[10.54959252475379, -1.0666044160973747, 16.115361475172744, 1], [10.601569914483544, 1.3552581734317022, 13.000374, 1], [10.66521270155646, 3.297194680340562, 8.392674348292681, 1], [14.45029893750934, 7.228940419325408, 2.62216288948566, 1], [19.02990983203672, 13.658120069707275, 0.38582693574532617, 1], [20.19982941775947, 19.15599130260965, 0.1885200641698277, 1], [20.370150929694518, 23.71914118728892, 0.20572820686804458, 1]], [[12.06322577978621, -0.9969688981643671, 15.888887030570354, 1], [12.107944401245575, 1.4931947244903796, 12.974443, 1], [12.047176263183664, 3.6155461388062426, 8.673813677394223, 1], [15.3082064772923, 7.624687379600255, 3.1455850617688315, 1], [19.37431472068055, 13.929706757034074, 0.9457335577523813, 1], [20.431969920642167, 19.10595922283073, 0.7207913919251576, 1], [20.65284339025161, 23.365573644773345, 0.7320976936595526, 1]], [[14.460502217815266, -1.0006247990229749, 15.533093944119651, 1], [14.531637597792901, 1.488354871063465, 12.924358554845536, 1], [14.33024057532181, 3.9584120614673877, 9.129797391104137, 1], [16.75096586934408, 8.051448354581666, 3.9610032761021405, 1], [20.057965361625392, 14.161066473792669, 1.7871886138158974, 1], [20.845385690978247, 18.985794576116085, 1.5706914182832277, 1], [20.98106749543859, 22.89613272950157, 1.5705642804901863, 1]], [[17.220877854657303, -1.1175357758177094, 15.12423196872025, 1], [17.2920050002584, 1.6686068728447736, 12.868429293751374, 1], [16.898035656679237, 4.482854588794428, 9.640086429627052, 1], [18.341594352637085, 8.691156664361761, 4.9373963105824465, 1], [20.75679229218072, 14.531684628370385, 2.7923453226395663, 1], [21.227643362854163, 18.939505311892784, 2.5458793816560132, 1], [21.255406587089244, 22.43847665757206, 2.531582002124996, 1]], [[19.09251922901625, -1.234709967628515, 14.851229459264252, 1], [19.15970628056484, 1.86385144367135, 12.823885367180173, 1], [18.59844405447844, 4.9697369859564775, 9.974781370512387, 1], [19.342350578774802, 9.221773507445143, 5.5913265392967695, 1], [21.148714998747696, 14.862250240082751, 3.458637780205138, 1], [21.435120022950155, 18.953404465956762, 3.2074948626717026, 1], [21.44896901314071, 22.11569504583196, 3.1821971256879915, 1]], [[20.463206020968265, -1.3019345938575937, 14.64588199741722, 1], [20.512438146506284, 1.9741873335457711, 12.801756427173878, 1], [19.883960134319846, 5.281697285054774, 10.226353395032435, 1], [20.170113184750946, 9.647903782343015, 6.175280005370259, 1], [21.428714098865385, 15.149052359131858, 4.017473027346178, 1], [21.568795215057207, 18.994806697093512, 3.6902837591817383, 1], [21.57654872001159, 21.883578692513357, 3.6565252517244136, 1]]]
degree_u=3
degree_v=3
nNodes_u=7
nNodes_v=7


knot_u = [0, 0.17684564570425632, 0.45231972770598194, 0.7254117267159598, 1]
knot_v = [0, 0.24854808618458937, 0.4882656272717224, 0.7322021526243429, 1]
knot_u_mults = [4, 1, 1, 1, 4]
knot_v_mults = [4, 1, 1, 1, 4]

NURBS_Cubic_surf=Part.BSplineSurface()
NURBS_Cubic_surf.increaseDegree(degree_u,degree_v)

for i in range(0,len(knot_u)):    #-1):
	NURBS_Cubic_surf.insertUKnot(knot_u[i],knot_u_mults[i],0)

for i in range(0,len(knot_v)):    #-1):
	NURBS_Cubic_surf.insertVKnot(knot_v[i],knot_v_mults[i],0)

poles_xyz=[]
for ii in range(0, len(poles)):
	poles_xyz.append([])
	for jj in range(0, len(poles[ii])):
		poles_xyz[ii].append(FreeCAD.Base.Vector(poles[ii][jj][0], poles[ii][jj][1], poles[ii][jj][2]))
		#tmp=FreeCAD.Base.Vector(poles[ii][jj][0], poles[ii][jj][1], poles[ii][jj][2])
		NURBS_Cubic_surf.setPole(ii+1, jj+1, poles_xyz[ii][jj], poles[ii][jj][3])


# evaluate the computed NURBS
NURBS_Cubic_surf.UKnotSequence
NURBS_Cubic_surf.VKnotSequence
for ii in range(0, len(poles)):
    for jj in range(0, len(poles[ii])):
        print (NURBS_Cubic_surf.getPole(ii+1,jj+1).distanceToPoint(App.Vector(poles[ii][jj])))

NURBS_Cubic_surf.NbUPoles
NURBS_Cubic_surf.NbVPoles
Your method for an unclamped surface:

Code: Select all

poles = [[[9.494737213863035, -0.9949774728396593, 16.27075208109207, 1], [9.549312971732164, 1.2818618094027012, 13.022870257590448, 1], [9.64027660298673, 3.1372910396495417, 8.179986557934111, 1], [13.792828778843486, 7.071431473132651, 2.263030148758937, 1], [18.66382094445385, 13.644095627042061, 0.036666641937649666, 1], [19.95755978150579, 19.260043001655117, -0.18577137390929857, 1], [20.223967211664096, 23.91769736938115, -0.1600081712557669, 1]], [[10.54959252475379, -1.0666044160973747, 16.115361475172744, 1], [10.601569914483544, 1.3552581734317022, 13.000374, 1], [10.66521270155646, 3.297194680340562, 8.392674348292681, 1], [14.45029893750934, 7.228940419325408, 2.62216288948566, 1], [19.02990983203672, 13.658120069707275, 0.38582693574532617, 1], [20.19982941775947, 19.15599130260965, 0.1885200641698277, 1], [20.370150929694518, 23.71914118728892, 0.20572820686804458, 1]], [[12.06322577978621, -0.9969688981643671, 15.888887030570354, 1], [12.107944401245575, 1.4931947244903796, 12.974443, 1], [12.047176263183664, 3.6155461388062426, 8.673813677394223, 1], [15.3082064772923, 7.624687379600255, 3.1455850617688315, 1], [19.37431472068055, 13.929706757034074, 0.9457335577523813, 1], [20.431969920642167, 19.10595922283073, 0.7207913919251576, 1], [20.65284339025161, 23.365573644773345, 0.7320976936595526, 1]], [[14.460502217815266, -1.0006247990229749, 15.533093944119651, 1], [14.531637597792901, 1.488354871063465, 12.924358554845536, 1], [14.33024057532181, 3.9584120614673877, 9.129797391104137, 1], [16.75096586934408, 8.051448354581666, 3.9610032761021405, 1], [20.057965361625392, 14.161066473792669, 1.7871886138158974, 1], [20.845385690978247, 18.985794576116085, 1.5706914182832277, 1], [20.98106749543859, 22.89613272950157, 1.5705642804901863, 1]], [[17.220877854657303, -1.1175357758177094, 15.12423196872025, 1], [17.2920050002584, 1.6686068728447736, 12.868429293751374, 1], [16.898035656679237, 4.482854588794428, 9.640086429627052, 1], [18.341594352637085, 8.691156664361761, 4.9373963105824465, 1], [20.75679229218072, 14.531684628370385, 2.7923453226395663, 1], [21.227643362854163, 18.939505311892784, 2.5458793816560132, 1], [21.255406587089244, 22.43847665757206, 2.531582002124996, 1]], [[19.09251922901625, -1.234709967628515, 14.851229459264252, 1], [19.15970628056484, 1.86385144367135, 12.823885367180173, 1], [18.59844405447844, 4.9697369859564775, 9.974781370512387, 1], [19.342350578774802, 9.221773507445143, 5.5913265392967695, 1], [21.148714998747696, 14.862250240082751, 3.458637780205138, 1], [21.435120022950155, 18.953404465956762, 3.2074948626717026, 1], [21.44896901314071, 22.11569504583196, 3.1821971256879915, 1]], [[20.463206020968265, -1.3019345938575937, 14.64588199741722, 1], [20.512438146506284, 1.9741873335457711, 12.801756427173878, 1], [19.883960134319846, 5.281697285054774, 10.226353395032435, 1], [20.170113184750946, 9.647903782343015, 6.175280005370259, 1], [21.428714098865385, 15.149052359131858, 4.017473027346178, 1], [21.568795215057207, 18.994806697093512, 3.6902837591817383, 1], [21.57654872001159, 21.883578692513357, 3.6565252517244136, 1]]]
degree_u=3
degree_v=3
nNodes_u=7
nNodes_v=7


knot_u=[0.0, 0.1153846153846154, 0.2514197274648126, 0.46332286746614004, 0.6733936359353538, 0.8846153846153848, 1.0]
knot_u_mults=[3, 1, 1, 1, 1, 1, 3]
knot_v=[0.0, 0.1153846153846154, 0.3065754509112226, 0.4909735594397865, 0.6786170404802637, 0.8846153846153848, 1.0]
knot_v_mults=[3, 1, 1, 1, 1, 1, 3]
NURBS_Cubic_surf=Part.BSplineSurface()
NURBS_Cubic_surf.increaseDegree(degree_u,degree_v)

for i in range(0,len(knot_u)):    #-1):
	NURBS_Cubic_surf.insertUKnot(knot_u[i],knot_u_mults[i],0)

for i in range(0,len(knot_v)):    #-1):
	NURBS_Cubic_surf.insertVKnot(knot_v[i],knot_v_mults[i],0)

poles_xyz=[]
for ii in range(0, len(poles)):
	poles_xyz.append([])
	for jj in range(0, len(poles[ii])):
		poles_xyz[ii].append(FreeCAD.Base.Vector(poles[ii][jj][0], poles[ii][jj][1], poles[ii][jj][2]))
		#tmp=FreeCAD.Base.Vector(poles[ii][jj][0], poles[ii][jj][1], poles[ii][jj][2])
		NURBS_Cubic_surf.setPole(ii+1, jj+1, poles_xyz[ii][jj], poles[ii][jj][3])


# evaluate the computed NURBS
NURBS_Cubic_surf.UKnotSequence
NURBS_Cubic_surf.VKnotSequence
for ii in range(0, len(poles)):
    for jj in range(0, len(poles[ii])):
        print (NURBS_Cubic_surf.getPole(ii+1,jj+1).distanceToPoint(App.Vector(poles[ii][jj])))

NURBS_Cubic_surf.NbUPoles
NURBS_Cubic_surf.NbVPoles
Almost everything is OK but not the number of poles. You requested 7x7 but it has 9x9. So, for some of them you don't set its coordinates and that's why the surface is broken. And the multiplicities are also wrong because they are [4,1,1,1,1,1,4] instead of [3,1,1,1,1,1,3]

But note, there is a method buildFromPolesMultsKnots() to directly create a surface.

Method for a clamped surface:

Code: Select all

poles = [[[9.494737213863035, -0.9949774728396593, 16.27075208109207, 1], [9.549312971732164, 1.2818618094027012, 13.022870257590448, 1], [9.64027660298673, 3.1372910396495417, 8.179986557934111, 1], [13.792828778843486, 7.071431473132651, 2.263030148758937, 1], [18.66382094445385, 13.644095627042061, 0.036666641937649666, 1], [19.95755978150579, 19.260043001655117, -0.18577137390929857, 1], [20.223967211664096, 23.91769736938115, -0.1600081712557669, 1]], [[10.54959252475379, -1.0666044160973747, 16.115361475172744, 1], [10.601569914483544, 1.3552581734317022, 13.000374, 1], [10.66521270155646, 3.297194680340562, 8.392674348292681, 1], [14.45029893750934, 7.228940419325408, 2.62216288948566, 1], [19.02990983203672, 13.658120069707275, 0.38582693574532617, 1], [20.19982941775947, 19.15599130260965, 0.1885200641698277, 1], [20.370150929694518, 23.71914118728892, 0.20572820686804458, 1]], [[12.06322577978621, -0.9969688981643671, 15.888887030570354, 1], [12.107944401245575, 1.4931947244903796, 12.974443, 1], [12.047176263183664, 3.6155461388062426, 8.673813677394223, 1], [15.3082064772923, 7.624687379600255, 3.1455850617688315, 1], [19.37431472068055, 13.929706757034074, 0.9457335577523813, 1], [20.431969920642167, 19.10595922283073, 0.7207913919251576, 1], [20.65284339025161, 23.365573644773345, 0.7320976936595526, 1]], [[14.460502217815266, -1.0006247990229749, 15.533093944119651, 1], [14.531637597792901, 1.488354871063465, 12.924358554845536, 1], [14.33024057532181, 3.9584120614673877, 9.129797391104137, 1], [16.75096586934408, 8.051448354581666, 3.9610032761021405, 1], [20.057965361625392, 14.161066473792669, 1.7871886138158974, 1], [20.845385690978247, 18.985794576116085, 1.5706914182832277, 1], [20.98106749543859, 22.89613272950157, 1.5705642804901863, 1]], [[17.220877854657303, -1.1175357758177094, 15.12423196872025, 1], [17.2920050002584, 1.6686068728447736, 12.868429293751374, 1], [16.898035656679237, 4.482854588794428, 9.640086429627052, 1], [18.341594352637085, 8.691156664361761, 4.9373963105824465, 1], [20.75679229218072, 14.531684628370385, 2.7923453226395663, 1], [21.227643362854163, 18.939505311892784, 2.5458793816560132, 1], [21.255406587089244, 22.43847665757206, 2.531582002124996, 1]], [[19.09251922901625, -1.234709967628515, 14.851229459264252, 1], [19.15970628056484, 1.86385144367135, 12.823885367180173, 1], [18.59844405447844, 4.9697369859564775, 9.974781370512387, 1], [19.342350578774802, 9.221773507445143, 5.5913265392967695, 1], [21.148714998747696, 14.862250240082751, 3.458637780205138, 1], [21.435120022950155, 18.953404465956762, 3.2074948626717026, 1], [21.44896901314071, 22.11569504583196, 3.1821971256879915, 1]], [[20.463206020968265, -1.3019345938575937, 14.64588199741722, 1], [20.512438146506284, 1.9741873335457711, 12.801756427173878, 1], [19.883960134319846, 5.281697285054774, 10.226353395032435, 1], [20.170113184750946, 9.647903782343015, 6.175280005370259, 1], [21.428714098865385, 15.149052359131858, 4.017473027346178, 1], [21.568795215057207, 18.994806697093512, 3.6902837591817383, 1], [21.57654872001159, 21.883578692513357, 3.6565252517244136, 1]]]

knot_u = [0, 0.17684564570425632, 0.45231972770598194, 0.7254117267159598, 1]
knot_v = [0, 0.24854808618458937, 0.4882656272717224, 0.7322021526243429, 1]
knot_u_mults=[4, 1, 1, 1, 4]
knot_v_mults=[4, 1, 1, 1, 4]

degree_u=3
degree_v=3
nNodes_u=7
nNodes_v=7

poles_xyz=[]
for ii in range(0, len(poles)):
	poles_xyz.append([])
	for jj in range(0, len(poles[ii])):
		poles_xyz[ii].append(FreeCAD.Base.Vector(poles[ii][jj][0], poles[ii][jj][1], poles[ii][jj][2]))

surf=Part.BSplineSurface()
surf.buildFromPolesMultsKnots(poles=poles_xyz, umults=knot_u_mults, vmults=knot_v_mults, uknots=knot_u, vknots=knot_v, udegree=degree_u, vdegree=degree_v)

# evaluate the computed NURBS
surf.UKnotSequence
surf.VKnotSequence
for ii in range(0, len(poles)):
    for jj in range(0, len(poles[ii])):
        print (surf.getPole(ii+1,jj+1).distanceToPoint(App.Vector(poles[ii][jj])))

surf.NbUPoles
surf.NbVPoles
Method for an unclamped surface:

Code: Select all

poles = [[[9.494737213863035, -0.9949774728396593, 16.27075208109207, 1], [9.549312971732164, 1.2818618094027012, 13.022870257590448, 1], [9.64027660298673, 3.1372910396495417, 8.179986557934111, 1], [13.792828778843486, 7.071431473132651, 2.263030148758937, 1], [18.66382094445385, 13.644095627042061, 0.036666641937649666, 1], [19.95755978150579, 19.260043001655117, -0.18577137390929857, 1], [20.223967211664096, 23.91769736938115, -0.1600081712557669, 1]], [[10.54959252475379, -1.0666044160973747, 16.115361475172744, 1], [10.601569914483544, 1.3552581734317022, 13.000374, 1], [10.66521270155646, 3.297194680340562, 8.392674348292681, 1], [14.45029893750934, 7.228940419325408, 2.62216288948566, 1], [19.02990983203672, 13.658120069707275, 0.38582693574532617, 1], [20.19982941775947, 19.15599130260965, 0.1885200641698277, 1], [20.370150929694518, 23.71914118728892, 0.20572820686804458, 1]], [[12.06322577978621, -0.9969688981643671, 15.888887030570354, 1], [12.107944401245575, 1.4931947244903796, 12.974443, 1], [12.047176263183664, 3.6155461388062426, 8.673813677394223, 1], [15.3082064772923, 7.624687379600255, 3.1455850617688315, 1], [19.37431472068055, 13.929706757034074, 0.9457335577523813, 1], [20.431969920642167, 19.10595922283073, 0.7207913919251576, 1], [20.65284339025161, 23.365573644773345, 0.7320976936595526, 1]], [[14.460502217815266, -1.0006247990229749, 15.533093944119651, 1], [14.531637597792901, 1.488354871063465, 12.924358554845536, 1], [14.33024057532181, 3.9584120614673877, 9.129797391104137, 1], [16.75096586934408, 8.051448354581666, 3.9610032761021405, 1], [20.057965361625392, 14.161066473792669, 1.7871886138158974, 1], [20.845385690978247, 18.985794576116085, 1.5706914182832277, 1], [20.98106749543859, 22.89613272950157, 1.5705642804901863, 1]], [[17.220877854657303, -1.1175357758177094, 15.12423196872025, 1], [17.2920050002584, 1.6686068728447736, 12.868429293751374, 1], [16.898035656679237, 4.482854588794428, 9.640086429627052, 1], [18.341594352637085, 8.691156664361761, 4.9373963105824465, 1], [20.75679229218072, 14.531684628370385, 2.7923453226395663, 1], [21.227643362854163, 18.939505311892784, 2.5458793816560132, 1], [21.255406587089244, 22.43847665757206, 2.531582002124996, 1]], [[19.09251922901625, -1.234709967628515, 14.851229459264252, 1], [19.15970628056484, 1.86385144367135, 12.823885367180173, 1], [18.59844405447844, 4.9697369859564775, 9.974781370512387, 1], [19.342350578774802, 9.221773507445143, 5.5913265392967695, 1], [21.148714998747696, 14.862250240082751, 3.458637780205138, 1], [21.435120022950155, 18.953404465956762, 3.2074948626717026, 1], [21.44896901314071, 22.11569504583196, 3.1821971256879915, 1]], [[20.463206020968265, -1.3019345938575937, 14.64588199741722, 1], [20.512438146506284, 1.9741873335457711, 12.801756427173878, 1], [19.883960134319846, 5.281697285054774, 10.226353395032435, 1], [20.170113184750946, 9.647903782343015, 6.175280005370259, 1], [21.428714098865385, 15.149052359131858, 4.017473027346178, 1], [21.568795215057207, 18.994806697093512, 3.6902837591817383, 1], [21.57654872001159, 21.883578692513357, 3.6565252517244136, 1]]]

knot_u=[0.0, 0.1153846153846154, 0.2514197274648126, 0.46332286746614004, 0.6733936359353538, 0.8846153846153848, 1.0]
knot_u_mults=[3, 1, 1, 1, 1, 1, 3]
knot_v=[0.0, 0.1153846153846154, 0.3065754509112226, 0.4909735594397865, 0.6786170404802637, 0.8846153846153848, 1.0]
knot_v_mults=[3, 1, 1, 1, 1, 1, 3]

degree_u=3
degree_v=3
nNodes_u=7
nNodes_v=7

poles_xyz=[]
for ii in range(0, len(poles)):
	poles_xyz.append([])
	for jj in range(0, len(poles[ii])):
		poles_xyz[ii].append(FreeCAD.Base.Vector(poles[ii][jj][0], poles[ii][jj][1], poles[ii][jj][2]))

surf=Part.BSplineSurface()
surf.buildFromPolesMultsKnots(poles=poles_xyz, umults=knot_u_mults, vmults=knot_v_mults, uknots=knot_u, vknots=knot_v, udegree=degree_u, vdegree=degree_v)

# evaluate the computed NURBS
surf.UKnotSequence
surf.VKnotSequence
for ii in range(0, len(poles)):
    for jj in range(0, len(poles[ii])):
        print (surf.getPole(ii+1,jj+1).distanceToPoint(App.Vector(poles[ii][jj])))

surf.NbUPoles
surf.NbVPoles
Now everything is fine and the surface looks good.

Code: Select all

Part.show(surf.toShape())
wmayer
Site Admin
Posts: 15487
Joined: Thu Feb 19, 2009 10:32 am

Re: how to handle unclamped BSplineSurface

Postby wmayer » Tue Feb 11, 2020 12:06 pm

silent_missile wrote:
Tue Feb 11, 2020 10:16 am
I need to extend the curve/surface, and the extended part is unclamped, this means I have to handle unclamped curve/surface

there is a "mults" parameter for knots, I thought OCCT/FreeCAD can handle it, but I'm disappointed.
When you create a surface then its degrees are 1 and 1 and the mults are [2, 2]. After increasing the degrees to 3 the mults are [4, 4]. Because the knot vector already has the values 0.0 and 1.0 the for loop to insert further values won't reduce the multiplicity to 3.
There is a method removeUKnot() that should be able to modify the multiplicity of a knot but unfortunately it only seems to work for inner knots but not for the two outer knots.

So your only option seems to be the method buildFromPolesMultsKnots()
silent_missile
Posts: 16
Joined: Mon Jul 29, 2019 11:07 pm

Re: how to handle unclamped BSplineSurface

Postby silent_missile » Fri Feb 14, 2020 9:18 am

everybody, please help me more.

I tried wmayer's method, and I got a surface of the first set of points.

In that set, the knots have some mults at the end point, but fewer than order+1, because I used A12.1 in page 577 in The NURBS Book 2nd, I need to convert it to unclamped data structure in application

in my test, if the mults happen at end point, if it's order+1, it works very well, if the mults of the end point is fewer than order+1, it also works fine

Code: Select all

surf = Part.BSplineSurface()
surf.buildFromPolesMultsKnots(
        poles=poles_xyz,
        umults=knot_u_mults,
        vmults=knot_v_mults,
        uknots=knot_u,
        vknots=knot_v,
        udegree=p,
        vdegree=q,
        weights=poles_w,
)
In another application, it's more difficult

by this paper
https://www.sciencedirect.com/science/a ... 859190046Y

I use A5.1 in page 151 in The NURBS Book 2nd to insert knot of the extension target, then mirror the inserted knots and corresponding control points at the end plane.

In my own test code, the curve/surface points can be computed, because I developed that code by unclamped algorithm. But I can't convert it to a CAD curve/surface. As we all know, OpenCASCADE/FreeCAD have a lot of CAD functions, if I want to use those function, I have to import the data to OpenCASCADE/FreeCAD, I don't want to develop a new CAD system especially by JavaScript

So the knot vector looks like
[-target, -0.5*target, -0.25*target, 0, 0, 0, 0, k1, k2...1, 1, 1, 1, 1+0.25*target, 1 + 0.5*target, 1+target]
I keep the original control points and add extra points at the end of curve, so k1, k2, ...kn are the save as previous.

3 extra points are append and prepend in the array, because I need to keep 2nd order continuity which means curvature continuity.

then I need to map this vector to range[0, 1], so it looks like
[0, k1, k2, k3, k3, k3, k3, k4...]
some of internal knot repeat.

if the repeat happens at some position not the end, there would be problem, an exception

Geom.OCCError

would be raised

here is the control points and knots, please help me to try that, you can simply use this data to replace the data in the third reply of this thread, the exception can be raised

control points

Code: Select all

[[[9.068757563189992, -2.6944957245128247, 17.490567855962535, 1], [8.93826943501047, -1.4555105066569196, 16.551231793471505, 1], [8.903305891202592, -0.5990170393119777, 15.735865559207014, 1], [8.893076120383126, -0.08738668080722323, 15.110594625103968, 1], [8.85917492092599, 1.6081432717300113, 13.038462704861793, 1], [9.088668280701501, 2.9624500761025057, 8.05556849032455, 1], [13.395147585080677, 7.041202828732808, 2.064628861818192, 1], [18.42599564039713, 13.725410449092601, -0.10904875905113254, 1], [19.80591507076088, 19.342946012850597, -0.3576913996663066, 1], [20.021262750768603, 22.341471235911392, -0.389206865375026, 1], [20.081573452138517, 23.181244198739183, -0.3980331496462297, 1], [20.112056326905346, 24.44282131479675, -0.399619215788922, 1], [20.027070707421146, 26.114396156084428, -0.36238554956480096, 1]], [[9.554691634598832, -2.883381617409447, 17.3081651437757, 1], [9.434026645353617, -1.5891038136867082, 16.452021580661345, 1], [9.401307711001477, -0.6796829894101543, 15.674941127178476, 1], [9.389237047388155, -0.123379394701022, 15.060969486660772, 1], [9.349235176187234, 1.7201965226664746, 13.026283137030267, 1], [9.504326776874015, 3.180809940544943, 8.154850422854125, 1], [13.71112712680983, 7.071922891237927, 2.224182645847926, 1], [18.613472088365718, 13.66942234334463, 0.013057202491092545, 1], [19.92605666030484, 19.278812349589483, -0.2161272050641137, 1], [20.10677254361049, 22.264739818843516, -0.21445408774461852, 1], [20.157384194827053, 23.100984629579102, -0.21398551116645761, 1], [20.173466566117312, 24.35662543608946, -0.19897775781156268, 1], [20.070028284901166, 26.01702757227097, -0.13535157389624994, 1]], [[9.915803489549713, -2.98161008654386, 17.192682830104943, 1], [9.801534219074009, -1.6614856607494113, 16.38365688587836, 1], [9.77022720171123, -0.723856652140193, 15.629637445130513, 1], [9.757106825341074, -0.1419635570768772, 15.024179687567434, 1], [9.71362623285009, 1.7864153114833141, 13.017708128795224, 1], [9.825125640152711, 3.325971314102241, 8.228791575296022, 1], [13.943695900826066, 7.107574438840525, 2.3454389429665734, 1], [18.747825938644773, 13.647313761392988, 0.11678709750972752, 1], [20.013152453330665, 19.236166662682955, -0.09988417233606635, 1], [20.169780368039692, 22.207705340083916, -0.08462890648598906, 1], [20.213645894660967, 23.039920397034848, -0.08035648626316771, 1], [20.219709308030573, 24.288863688144346, -0.058837896649712285, 1], [20.1036098131345, 25.938206146503383, 0.014898390964687595, 1]], [[10.153846461257803, -3.0118182457126306, 17.133245317109715, 1], [10.043085205164441, -1.6849290140708024, 16.34298706267264, 1], [10.012513604330385, -0.7376654127188789, 15.599719162147556, 1], [9.99893597014986, -0.14587482594081236, 15, 1], [9.953940036943408, 1.8153040776024303, 13.012546, 1], [10.044422647886826, 3.4122296492883084, 8.277596, 1], [14.094563022808646, 7.143717404548399, 2.427847737174211, 1], [18.8318309328357, 13.650531895743912, 0.19690755308181007, 1], [20.06874512515554, 19.212290323710793, -0.01399697076283558, 1], [20.211240533548427, 22.169929095399674, 0, 1], [20.251148082224407, 22.998251316995052, 0.0039200195875364096, 1], [20.251354511988726, 24.240791304478428, 0.02440605087850957, 1], [20.128029755740208, 25.880511180480603, 0.09618623196298172, 1]], [[10.715069278161772, -3.0821803821633993, 16.995559656802264, 1], [10.61263290970905, -1.7400721735949443, 16.247802866381882, 1], [10.583810875217662, -0.770225668464383, 15.529173587847769, 1], [10.569155053667432, -0.15509737084435324, 14.942985700522797, 1], [10.520586035030464, 1.883422110908222, 13.000374, 1], [10.561512260336833, 3.615621795733029, 8.392674, 1], [14.45029893750934, 7.228940419325408, 2.62216288948566, 1], [19.02990983203672, 13.658120069707275, 0.38582693574532617, 1], [20.19982941775947, 19.15599130260965, 0.1885200641698277, 1], [20.30900119675373, 22.080854903620118, 0.1995500481701333, 1], [20.339576063224126, 22.89999804545452, 0.20263912751908894, 1], [20.325993984876188, 24.127421543130247, 0.22069237948900883, 1], [20.18570711656094, 25.744419119294065, 0.2878667707053176, 1]], [[12.137154710171684, -2.8143721913698805, 16.831586168852205, 1], [12.090508906180155, -1.5495205542278456, 16.046425658220272, 1], [12.086800293252542, -0.6377511592161078, 15.340438023110964, 1], [12.080056354192225, -0.059755657538711586, 14.791989015651577, 1], [12.057707112235063, 1.8557066859975366, 12.974443, 1], [11.933305185359648, 3.975281724048325, 8.673814, 1], [15.3082064772923, 7.624687379600255, 3.1455850617688315, 1], [19.37431472068055, 13.929706757034074, 0.9457335577523813, 1], [20.431969920642167, 19.10595922283073, 0.7207913919251576, 1], [20.573544225237903, 21.83626427349529, 0.7280384452196381, 1], [20.61319380774476, 22.60091895913431, 0.7300680694395952, 1], [20.624200178736114, 23.75031968010881, 0.7478143075850376, 1], [20.536714542662523, 25.2735670207947, 0.8155451853330927, 1]], [[14.479222674173496, -2.827309167410598, 16.33325117681234, 1], [14.476892784638942, -1.582246465633871, 15.662451251326782, 1], [14.489333219125799, -0.6635688402946606, 15.04217399113688, 1], [14.487275165337845, -0.06385715804113179, 14.551254306807028, 1], [14.480454827601031, 1.9235720537809313, 12.924359, 1], [14.248213828067437, 4.244200425861611, 9.129797, 1], [16.75096586934408, 8.051448354581666, 3.9610032761021405, 1], [20.057965361625392, 14.161066473792669, 1.7871886138158974, 1], [20.845385690978247, 18.985794576116085, 1.5706914182832277, 1], [20.932354294073644, 21.492222377420617, 1.570609926175237, 1], [20.956710894756117, 22.19417755346109, 1.5705871033327117, 1], [20.955239386721868, 23.25192434095696, 1.5838725706653396, 1], [20.871353386535286, 24.65906203502376, 1.643575006303928, 1]], [[17.093541464703122, -2.5089608137300483, 16.006337529670837, 1], [17.181239781869987, -1.4016018210875516, 15.28819153496406, 1], [17.234262778848375, -0.5932309155181043, 14.699728109619276, 1], [17.247647703039462, -0.0689260552184985, 14.275224250518303, 1], [17.2920050002584, 1.6686068728447736, 12.868429293751374, 1], [16.898035656679237, 4.482854588794428, 9.640086429627052, 1], [18.341594352637085, 8.691156664361761, 4.9373963105824465, 1], [20.75679229218072, 14.531684628370385, 2.7923453226395663, 1], [21.227643362854163, 18.939505311892784, 2.5458793816560132, 1], [21.24543888701271, 21.18225739950589, 2.536715123186111, 1], [21.25042273705098, 21.81036702853898, 2.5341485626555533, 1], [21.231126001147373, 22.760592922003234, 2.544331518689196, 1], [21.145543460982985, 24.031916263603634, 2.600090342310477, 1]], [[18.920392954152447, -2.702406015180803, 15.59430621275493, 1], [19.04243399382607, -1.5289747324705094, 14.986962154566099, 1], [19.105162694236178, -0.6516131873322063, 14.46971763628216, 1], [19.11780615945611, -0.06851640703589716, 14.088205813300071, 1], [19.15970628056484, 1.86385144367135, 12.823885367180173, 1], [18.59844405447844, 4.9697369859564775, 9.974781370512387, 1], [19.342350578774802, 9.221773507445143, 5.5913265392967695, 1], [21.148714998747696, 14.862250240082751, 3.458637780205138, 1], [21.435120022950155, 18.953404465956762, 3.2074948626717026, 1], [21.443996875199762, 20.980352702732763, 3.1912796534496644, 1], [21.44648294417024, 21.548023874282368, 3.1867383895688275, 1], [21.433741819959476, 22.413304146124826, 3.1932638675619693, 1], [21.375969784981002, 23.582827864330998, 3.2429152362895586, 1]], [[19.801195425276696, -2.7735224347570058, 15.405544885135802, 1], [19.92787107954879, -1.5764227632252497, 14.843470039070993, 1], [19.989423425430385, -0.6734791560153905, 14.359213926171739, 1], [19.99988175629054, -0.06877306446664189, 14, 1], [20.03454039789024, 1.935207492314755, 12.80957421492216, 1], [19.429808608926177, 5.171486881684919, 10.137477168220672, 1], [19.87768181273596, 9.497351163990675, 5.968979, 1], [21.329795057383624, 15.047729923812062, 3.820045796436579, 1], [21.52156998516339, 18.980179975016434, 3.5197224435891643, 1], [21.527920082270395, 20.893746838022597, 3.5, 1], [21.529698503149024, 21.429664195581275, 3.4944764930574204, 1], [21.52141988955102, 22.25092482775538, 3.502007252168012, 1], [21.480218478496738, 23.368744067248798, 3.5558631429170746, 1]], [[20.041737734584974, -2.7930285959291217, 15.354520898279466, 1], [20.169706894886687, -1.5894172265141815, 14.804414648830921, 1], [20.23094704931422, -0.6794515438075754, 14.329031361712007, 1], [20.2408085408592, -0.06884316690428656, 13.975907798543668, 1], [20.273489272198262, 1.954697412930263, 12.80566532104802, 1], [19.65688437162301, 5.226592083369846, 10.181915281626553, 1], [20.023900156538122, 9.572621400383966, 6.072129561435096, 1], [21.379254578124506, 15.098391141471959, 3.9187594118913798, 1], [21.545182600110298, 18.987493336054975, 3.605003101385451, 1], [21.550842548896586, 20.870091640458707, 3.584322705097186, 1], [21.552427685458106, 21.39733588164268, 3.5785309120084086, 1], [21.54536451708056, 22.2065824377231, 3.5863286336789777, 1], [21.50867842454652, 23.310307273174786, 3.6413091357610017, 1]], [[20.40594618484721, -2.820708044763034, 15.276080633921255, 1], [20.537515994368214, -1.6078386713016324, 14.744356105357634, 1], [20.599121486387116, -0.6879355619583517, 14.282747851015452, 1], [20.608416011627085, -0.06900364338821018, 13.939148132416937, 1], [20.639217831098936, 1.9821209411339835, 12.800467432800612, 1], [20.001747314891986, 5.306115876062689, 10.249295766542291, 1], [20.237637536163817, 9.681782358766492, 6.22583259808834, 1], [21.449514976805517, 15.174335348746034, 4.065492415269333, 1], [21.57992999614285, 19.001080918442565, 3.735420274134082, 1], [21.584945284175742, 20.834730455741422, 3.7140289073227772, 1], [21.586349875759108, 21.34826600744622, 3.7080379984093184, 1], [21.58112725988918, 22.138895914067902, 3.716388102455463, 1], [21.55106281078853, 23.221327983587734, 3.7731076228529776, 1]], [[20.896553251824667, -2.8606298497253055, 15.166710043124404, 1], [21.03504791144836, -1.6343837078693666, 14.661694804783242, 1], [21.09817213104513, -0.7001533175394733, 14.219665647907943, 1], [21.10708183062173, -0.06923961778824746, 13.889283407596817, 1], [21.13660834766498, 2.021592283822501, 12.794405093747384, 1], [20.466330084363364, 5.412524159275467, 10.34002586650881, 1], [20.513618119919396, 9.823504617693153, 6.429354999881389, 1], [21.536320780802807, 15.276773598392356, 4.259072421756299, 1], [21.624535064611447, 19.023680869073925, 3.9130318011532386, 1], [21.629799999440408, 20.78788466327774, 3.8916246805843233, 1], [21.63127450760409, 21.2819711014696, 3.885629359642044, 1], [21.628813920304687, 22.046709193885192, 3.8948560170113535, 1], [21.607826618267254, 23.10031426673233, 3.953827745987022, 1]]]
u knot vector

Code: Select all

[0.0, 0.028846153846153855, 0.057692307692307696, 0.11538461538461539, 0.11538461538461539, 0.11538461538461539, 0.11538461538461539, 0.2514197274648126, 0.46332286746614004, 0.6733936359353537, 0.8846153846153847, 0.8846153846153847, 0.8846153846153847, 0.8846153846153847, 0.9423076923076923, 0.9711538461538463, 1.0]
you can see the repeats of the knots inside the vector (not at the head of tail)

v knot vector

Code: Select all

[0.0, 0.028846153846153855, 0.057692307692307696, 0.11538461538461539, 0.11538461538461539, 0.11538461538461539, 0.11538461538461539, 0.3065754509112226, 0.49097355943978654, 0.6786170404802638, 0.8846153846153847, 0.8846153846153847, 0.8846153846153847, 0.8846153846153847, 0.9423076923076923, 0.9711538461538463, 1.0]
experts, please help me.
wmayer
Site Admin
Posts: 15487
Joined: Thu Feb 19, 2009 10:32 am

Re: how to handle unclamped BSplineSurface

Postby wmayer » Fri Feb 14, 2020 9:31 am

experts, please help me.
The expert is microelly2. But nevertheless can you post the code you have so far, please? It would be easier to see where the problem is.
User avatar
microelly2
Posts: 4626
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: how to handle unclamped BSplineSurface

Postby microelly2 » Fri Feb 14, 2020 2:27 pm

some experiments with unclamped curves
$dp_011.png
$dp_011.png (6.55 KiB) Viewed 199 times

Code: Select all

pts=[p.Point for p in App.ActiveDocument.Sketch.Shape.Vertexes]

bs=Part.BSplineCurve()

poles=pts

if 1:
	bs.buildFromPolesMultsKnots(poles,[4,1,1,4],[0,1,2,3],False,3)
	Part.show(bs.toShape())

	# this is my favorite to reduce curves and faces
	# its not 
	bs2=bs.copy()
	bs2.segment(1,2)
	Part.show(bs2.toShape())

if 1:
	bs.buildFromPolesMultsKnots(poles,[4,1,1,1,3],[0,1,2,3,4],False,3)
	Part.show(bs.toShape())
	bs.buildFromPolesMultsKnots(poles,[4,1,1,1,1,2],[0,1,2,3,4,5],False,3)
	Part.show(bs.toShape())
	bs.buildFromPolesMultsKnots(poles,[4,1,1,1,2,1],[0,1,2,3,4,5],False,3)
	Part.show(bs.toShape())
	bs.buildFromPolesMultsKnots(poles,[3,2,1,1,2,1],[0,1,2,3,4,5],False,3)
	Part.show(bs.toShape())
	bs.buildFromPolesMultsKnots(poles,[4,1,1,1,1,1,1],[0,1,2,3,4,5,6],False,3)
	Part.show(bs.toShape())
	bs.buildFromPolesMultsKnots(poles,[1,1,1,1,1,1,1,1,1,1],[0,1,2,3,4,5,6,7,8,9],False,3)
	Part.show(bs.toShape())

	bs.buildFromPolesMultsKnots(poles[1:-1],[1,1,1,1,1,1,1,1,],[0,1,2,3,4,5,6,7,],False,3)
	Part.show(bs.toShape())
	Part.show(Part.makePolygon(poles[1:-1]))

there are some constraints:
len(mults) ==len (knots)
sum(mults) == len(polers) + degree + 1
if these are satisfied all should work.
Attachments
unclamped.FCStd
(17.35 KiB) Downloaded 2 times
silent_missile
Posts: 16
Joined: Mon Jul 29, 2019 11:07 pm

Re: how to handle unclamped BSplineSurface

Postby silent_missile » Sat Feb 15, 2020 2:49 am

thanks for your help, but the problem still exist, I need some more help

here are some code, please help me to check

the first function is extend_mirror_curve, this function follows
https://www.sciencedirect.com/science/a ... 859190046Y

I use an individual NURBS package geomdl, its homepage is
https://github.com/orbingol/NURBS-Python

you can install it by
pip install --user geomdl
or
conda install -c orbingol geomdl
if you use Anaconda

this package provides a lot of NURBS functions, and I use insert knot algorithm here

I insert knot of the extend target, then mirror the new control point to the other side to extend the curve

Code: Select all

def list_extension(_l, _min, _max):
    """
    _l: list [x0, x1, x2...xn], its order is not negtive
    this function would extend _l to the range _min to _max
    finally x0=_min, xn=_max
    and the ratio between each x would keep as previous
    """
    res = []
    for i in range(0, len(_l)):
        res.append(_min + (_l[i] - _l[0]) * (_max - _min) / (_l[-1] - _l[0]))
    return res


import copy
from geomdl import BSpline
def extend_mirror_curve(p, U, Pw, x, order=2, direction=0):
    """
    Extend B-Spline curve by mirror algorithm. It's reference to
    "curvature-continuous extensions for rational B-spline curves and surfaces"
    
    p    : degree of unclamp, p==1 means straight line, p>=2 means curve
    U    : knot vector
    P    : control points
    x    : extend ratio of U
    order: continuous order, it must be a positive integer, default is 2 which means curvature continue
    direction: >0 means head, <0 means tail, 0 means both

    return new Pw of new control points and new U of new knots vector for an unclamped B-Spline curve
    """
    if not isinstance(order, int) or not isinstance(p, int):
        raise TypeError(
            'in extend_mirror_curve function, input parameter p and order should be integer')
    if p <= 1:
        raise ValueError(
            'in extend_mirror_curve function, input parameter p and order should be positive')
    if order >= p:
        raise ValueError(
            'in extend_mirror_curve function, input parameter order should be smaller than p')
    if x > 0.4 or x < 0:
        raise ValueError(
            'in extend_mirror_curve function, input parameter x should between 0 to 0.4')
    elif x == 0:
        return copy.deepcopy(Pw), copy.deepcopy(U)
    if p >= 2:  # len(Pw) >= 3
        crv = BSpline.Curve()
        crv.degree = p
        crv.ctrlpts = copy.deepcopy(Pw)
        crv.knotvector = copy.deepcopy(U)
        resPw = copy.deepcopy(Pw)
        resU = copy.deepcopy(U)
        # judge wether there are any existing points to fit the requirement
        if x not in crv.knotvector:
            crv.insert_knot(x)
        if 1 - x not in crv.knotvector:
            crv.insert_knot(1 - x)
        if direction >= 0:
            mirror_plane = {
                'point':
                    crv.ctrlpts[0],
                'direction': [
                    crv.ctrlpts[1][0] - crv.ctrlpts[0][0],
                    crv.ctrlpts[1][1] - crv.ctrlpts[0][1],
                    crv.ctrlpts[1][2] - crv.ctrlpts[0][2],
                ]
            }
            """
            in geomdl library, the BSpline class assumes the line knot vector is clamped at the end point
            this means the start U is 0 and repeats degree+1 times, the end U is 1 and repeats degree+1 times
            now I'll extend that curve, but the new curve would not be clamped
            freecad and OpenCASCADE accepts unclamped curve
            """
            if crv.knotvector[crv.degree + 1 + order] > x:
                # there are fewer than order knots before x
                # we need to insert enough knots before x
                while crv.knotvector[crv.degree + 1 + order] > x:
                    idx = crv.knotvector.index(x, 0, crv.degree + 1 + order)
                    crv.insert_knot((x + crv.knotvector[idx - 1]) / 2)
            for i in range(p + 1, crv.knotvector.index(x) + 1):
                # crv.ctrlpts is a two dimensional list, so Pw[i] is a list
                # we need to deepcopy it, or strange mistake would happen
                # mirror_point can handle [x, y, z], but w is lost, so append it
                # weight value should be calculated, but in geomdl, the insert_knot function doesn't handle weight
                # so I have to assume it's 1 for this case
                tmp = mirror_point(copy.deepcopy(crv.ctrlpts[i - p]), mirror_plane)
                tmp.append(1)
                resPw.insert(0, tmp)
                resU.insert(0, -crv.knotvector[i])
        if direction <= 0:
            mirror_plane = {
                'point':
                    crv.ctrlpts[-1],
                'direction': [
                    crv.ctrlpts[-2][0] - crv.ctrlpts[-1][0],
                    crv.ctrlpts[-2][1] - crv.ctrlpts[-1][1],
                    crv.ctrlpts[-2][2] - crv.ctrlpts[-1][2],
                ]
            }
            if crv.knotvector[-(crv.degree + 1 + order + 1)] < 1 - x:
                while crv.knotvector[-(crv.degree + 1 + order + 1)] < 1 - x:
                    idx = crv.knotvector.index(1 - x)
                    crv.insert_knot((1 - x + crv.knotvector[idx + 1]) / 2)
            for i in range(p + 1, len(crv.knotvector) - crv.knotvector.index(1 - x)):
                tmp = mirror_point(copy.deepcopy(crv.ctrlpts[-(i - p + 1)]), mirror_plane)
                tmp.append(1)
                resPw.append(tmp)
                resU.append(1 + 1 - crv.knotvector[-(i + 1)])
        resU = list_extension(resU, 0.0, 1.0)
    else:  # len(Pw)==2
        resPw = extend_linear(Pw, x, direction)
        # resU = [0, 0, 0, 1, 1, 1]
        resU = copy.deepcopy(U)
    return resPw, resU
extend_linear function is very simple, it's to handle special case, it won't run in this test. I put the code here just because I want to make the code complete

Code: Select all

def extend_linear(Pw, x, direction=0):
    """
    Pw: [[x0, y0, z0, w0], [x1, y1, z1, w1]] two points of a line section
    x: extend ratio
    direction: positive means extend at Pw[0], negtive means extend at Pw[1], 0 means both
    this function would modify Pw
    """
    resPw = copy.deepcopy(Pw)
    if direction >= 0:
        resPw[0][0] = Pw[0][0] - x * (Pw[1][0] - Pw[0][0])
        resPw[0][1] = Pw[0][1] - x * (Pw[1][1] - Pw[0][1])
        resPw[0][2] = Pw[0][2] - x * (Pw[1][2] - Pw[0][2])
    if direction <= 0:
        resPw[1][0] = Pw[1][0] + x * (Pw[1][0] - Pw[0][0])
        resPw[1][1] = Pw[1][1] + x * (Pw[1][1] - Pw[0][1])
        resPw[1][2] = Pw[1][2] + x * (Pw[1][2] - Pw[0][2])
    return resPw
mirror_point function is also very simple geometry algorithm

Code: Select all

def mirror_point(pt, plane):
    """
    pt: [x, y, z] to demonstrate a point
    plane: {'point': [x, y, z], 'direction': [x, y, z]} to demonstrate a plane through point and perpendicular direction
    """
    t = 2 * (plane['direction'][0] * plane['point'][0] + plane['direction'][1] * plane['point'][1] +
             plane['direction'][2] * plane['point'][2] - plane['direction'][0] * pt[0] -
             plane['direction'][1] * pt[1] - plane['direction'][2] * pt[2]) / (
                 plane['direction'][0]**2 + plane['direction'][1]**2 + plane['direction'][2]**2)
    return [
        pt[0] + t * plane['direction'][0], pt[1] + t * plane['direction'][1],
        pt[2] + t * plane['direction'][2]
    ]
then we can develop the function to extend the surface based on the function to extend curve

Code: Select all

import statistics
def average_in_matrix(mat, k):
    res = []
    if k == 0:  # average for each row
        for i in range(0, len(mat)):
            res.append(statistics.mean(mat[i]))
        return res
    elif k == 1:  # average for each column
        for i in range(0, len(mat[0])):
            tmp = 0
            for j in range(0, len(mat)):
                tmp += mat[j][i]
            res.append(tmp / len(mat))
        return res


def extend_mirror_surface(Pw, p, U, q, V, du, dv):
    """
    construct a new surface by the new control points to get an extended surface.
    :param pw: control points
    [
        [[x00, y00, z00, w00], [x01, y01, z01, w01],...],
        [[x10, y10, z10, w10], [x11, y11, z11, w11],...],
        ...
    ]
    :param p: order in U direction,
    :param U: U array [u0, u1, u2,...]
    :param q: order in V direction,
    :param V: V array [v0, v1, v2,...]
    :param du:extension information of U direction
    {
        direction: >0 head direction, <0 tail direction, ==0 both direction,
        order: continuity order, 1 for tangential, 2 for curvature, bigger are also ok, but order<=p is must
        value: extension ratio value
    }
    :param dv:extension information of V direction
    :return: extended value of the control points and U, V array
    input variables would not be modified
    """
    # extend in U direction
    mid_tmp_Pw = [[row[i] for row in Pw] for i in range(0, len(Pw[0]))]
    resPw = []
    for i in range(0, len(mid_tmp_Pw)):
        Pw_tmp, resU = extend_mirror_curve(p, U, mid_tmp_Pw[i], du['value'], du['order'],
                                           du['direction'])
        resPw.append(copy.deepcopy(Pw_tmp))
    # extend in V direction
    # transpose the matrix
    mid_tmp_Pw = [[row[i] for row in resPw] for i in range(0, len(resPw[0]))]
    resPw = []
    for i in range(0, len(mid_tmp_Pw)):
        Pw_tmp, resV = extend_mirror_curve(q, V, mid_tmp_Pw[i], dv['value'], du['order'],
                                           dv['direction'])
        resPw.append(copy.deepcopy(Pw_tmp))
    return resPw, resU, resV
all above functions are pure geometry algorithm functions, they are not relative to CAD data structure or visualization

then we can use the calculated data to generate CAD data

Code: Select all

import itertools
def claps_knots(knots_list):
    knot_list = []
    multi_list = []
    tmp_list = itertools.groupby(knots_list)
    for key, value in tmp_list:
        knot_list.append(key)
        multi_list.append(len(list(value)))
    return knot_list, multi_list


def nurbs_surf(xyzw_mat, p, u_list, q, v_list):
    knot_u, knot_u_mults = claps_knots(u_list)
    knot_v, knot_v_mults = claps_knots(v_list)
    poles_xyz = []
    poles_w = []
    for ii in range(0, len(xyzw_mat)):
        poles_xyz.append([])
        poles_w.append([])
        for jj in range(0, len(xyzw_mat[ii])):
            poles_xyz[ii].append(
                FreeCAD.Base.Vector(xyzw_mat[ii][jj][0], xyzw_mat[ii][jj][1], xyzw_mat[ii][jj][2]))
            poles_w[ii].append(xyzw_mat[ii][jj][3])
    surf = Part.BSplineSurface()
    surf.buildFromPolesMultsKnots(
        poles=poles_xyz,
        umults=knot_u_mults,
        vmults=knot_v_mults,
        uknots=knot_u,
        vknots=knot_v,
        udegree=p,
        vdegree=q,
        weights=poles_w,
    )
    return surf
then let's input some data to test

Code: Select all

control_pts = [[[9.99893597014986, -0.14587482594081236, 15, 1], [9.873385145201231, 2.2119034625735043, 13.012546, 1], [9.861329331861858, 3.909977150499094, 8.277596, 1], [14.022422317098352, 7.2843035028007055, 2.427847737174211, 1], [18.8318309328357, 13.650531895743912, 0.19690755308181007, 1], [20.06874512515554, 19.212290323710793, -0.01399697076283558, 1], [20.211240533548427, 22.169929095399674, 0, 1]], [[10.569155053667432, -0.15509737084435324, 14.942985700522797, 1], [10.459357938064695, 2.1981448750660313, 13.000374, 1], [10.408477868747745, 4.035077527237043, 8.392674, 1], [14.377288229207528, 7.3730795563204685, 2.62216288948566, 1], [19.02990983203672, 13.658120069707275, 0.38582693574532617, 1], [20.19982941775947, 19.15599130260965, 0.1885200641698277, 1], [20.30900119675373, 22.080854903620118, 0.1995500481701333, 1]], [[12.080056354192225, -0.059755657538711586, 14.791989015651577, 1], [11.97385375781338, 2.336401997815952, 12.974443, 1], [11.808695037004533, 4.331438439538135, 8.673814, 1], [15.231195470325707, 7.777385661826152, 3.1455850617688315, 1], [19.37431472068055, 13.929706757034074, 0.9457335577523813, 1], [20.431969920642167, 19.10595922283073, 0.7207913919251576, 1], [20.573544225237903, 21.83626427349529, 0.7280384452196381, 1]], [[14.487275165337845, -0.06385715804113179, 14.551254306807028, 1], [14.460495410949084, 2.068278010858668, 12.924359, 1], [14.114495697974233, 4.669672979672439, 9.129797, 1], [16.66961518638569, 8.21855265239845, 3.9610032761021405, 1], [20.057965361625392, 14.161066473792669, 1.7871886138158974, 1], [20.845385690978247, 18.985794576116085, 1.5706914182832277, 1], [20.932354294073644, 21.492222377420617, 1.570609926175237, 1]], [[17.247647703039462, -0.06892605521849851, 14.275224250518303, 1], [17.274454361365194, 1.8414403120210157, 12.868429, 1], [16.755966645199383, 4.987702487482975, 9.640086429627052, 1], [18.253767162437008, 8.874134996759109, 4.9373963105824465, 1], [20.75679229218072, 14.531684628370385, 2.7923453226395663, 1], [21.227643362854163, 18.939505311892784, 2.5458793816560132, 1], [21.24543888701271, 21.18225739950589, 2.536715123186111, 1]], [[19.11780615945611, -0.06851640703589716, 14.088205813300071, 1], [19.15970628056484, 1.86385144367135, 12.823885367180173, 1], [18.441005635563975, 5.525370404438601, 9.974781370512387, 1], [19.249167271185264, 9.414732704690934, 5.5913265392967695, 1], [21.148714998747696, 14.862250240082751, 3.458637780205138, 1], [21.435120022950155, 18.953404465956762, 3.2074948626717026, 1], [21.443996875199762, 20.980352702732763, 3.1912796534496644, 1]], [[19.999881756290538, -0.06877306446664187, 14, 1], [20.03454039789024, 1.935207492314755, 12.80957421492216, 1], [19.265944514981744, 5.751966715184104, 10.137477168220672, 1], [19.781716008171703, 9.69564980558665, 5.968979, 1], [21.329795057383624, 15.047729923812062, 3.820045796436579, 1], [21.52156998516339, 18.980179975016434, 3.5197224435891643, 1], [21.527920082270395, 20.893746838022597, 3.5, 1]]]
u_knots = [0, 0, 0, 0, 0.17684564570425632, 0.45231972770598194, 0.7254117267159598, 1, 1, 1, 1]
v_knots = [0, 0, 0, 0, 0.24854808618458937, 0.4882656272717224, 0.7322021526243429, 1, 1, 1, 1]
u_degree = 3
v_degree = 3

du = {
    'direction': 0,
    'order': min(2, u_degree - 1),
    'value': 0.15
}
dv = {
    'direction': 0,
    'order': min(2, v_degree - 1),
    'value': 0.15
}
xyz_ext, u_knots_ext, v_knots_ext = extend_mirror_surface(control_pts, u_degree, u_knots, v_degree, v_knots, du, dv)
ext_nurbs_surf = nurbs_surf(xyz_ext, u_degree, u_knots_ext, v_degreee, v_knots_ext)
at the final step, the code would raise an exception

Part.OCCError: Geom_BSplineSurface

I checked the data, and I think the data satisfies
len(mults) ==len (knots)
sum(mults) == len(polers) + degree + 1

so I don't know where is the root cause of the error, please help me
User avatar
microelly2
Posts: 4626
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: how to handle unclamped BSplineSurface

Postby microelly2 » Sat Feb 15, 2020 7:58 am

I will have a look at your code this evening.