Approximating closed BSpline

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Approximating closed BSpline

Post by edwilliams16 »

I think I've tried most combinations of options on bs.approximate() already, but I can't get a sensible closed BSpline around this polygon.
Screenshot 2023-03-17 at 11.32.45 AM.png
Screenshot 2023-03-17 at 11.32.45 AM.png (17.11 KiB) Viewed 806 times
Here's the basic script

Code: Select all

face = App.ActiveDocument.getObject('Face')
pts = [v.Point for v in face.Shape.OuterWire.Vertexes]
ptsclosed = pts + [pts[0]]
bs = Part.BSplineCurve()
bs.approximate(ptsclosed)
Part.show(bs.toShape(), "bsapprox")
What is the magic option? @Chris_G
Attachments
bsplineclosed.FCStd
(5.52 KiB) Downloaded 23 times
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Approximating closed BSpline

Post by jfc4120 »

Edited at top:

Wire to B-spline seems pretty smooth, see image.
wire_to_bspline.png
wire_to_bspline.png (6.17 KiB) Viewed 758 times
Last edited by jfc4120 on Sat Mar 18, 2023 3:19 am, edited 1 time in total.
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Approximating closed BSpline

Post by jfc4120 »

I did points and the shape is different, but the same wire was used:

Code used for test:

Code: Select all

import Draft
doc = App.ActiveDocument
convert = 25.4
select = FreeCADGui.Selection.getSelection()[0]
wire = select.Shape.Wires[0]

KOUNT = 0
for pts in wire.Vertexes:
    KOUNT = KOUNT + 1
    X = pts.Point.x
    Y = pts.Point.y
    Z = pts.Point.z
    if KOUNT == 1:
        multlist = [[X, Y, Z]]
    if KOUNT > 1:
        multlist.append([X, Y, Z])

    # print(pts.Point.x *  .039370078)
    print(pts.Point)

print(multlist)
K = KOUNT
ptss = []
for i in range(0, KOUNT):
    K = K - 1
    print('k', K)
    print(multlist[K][0], multlist[K][1], multlist[K][2])

    ptss.append(App.Vector(multlist[K][0], multlist[K][1], multlist[K][2]))


spline_from_wire = Draft.make_bspline(ptss)


doc.recompute()

EDIT

Using draft select plane so wire is parallel with view gives good results with the points program above (minor dips only): Your shape.
Attachments
bsp2.png
bsp2.png (9.42 KiB) Viewed 702 times
Last edited by jfc4120 on Sat Mar 18, 2023 4:56 pm, edited 1 time in total.
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Approximating closed BSpline

Post by jfc4120 »

However even with object parallel your code gives:

Why the approximate?
bsp3.png
bsp3.png (29.16 KiB) Viewed 689 times
jbi
Posts: 117
Joined: Sun Apr 24, 2016 3:28 pm

Re: Approximating closed BSpline

Post by jbi »

Maybe trying to discretize the wire

Code: Select all

pts=new_wire.discretize(Number=nb)
and run the approximation afterwards.
Maybe you can try also

Code: Select all

bspline=Part.BSplineCurve()
bspline.interpolate(bspline_pts)
bspline_shape=Part.Edge(bspline)
new_wire=Part.Wire([bspline_shape])
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Approximating closed BSpline

Post by edwilliams16 »

jbi wrote: Sat Mar 18, 2023 8:11 am Maybe trying to discretize the wire

Code: Select all

pts=new_wire.discretize(Number=nb)
and run the approximation afterwards.
The points actually came from a discretization in the first place. This was a simplified example.
Maybe you can try also

Code: Select all

bspline=Part.BSplineCurve()
bspline.interpolate(bspline_pts)
bspline_shape=Part.Edge(bspline)
new_wire=Part.Wire([bspline_shape])
I’ve been exploring interpolation. It may be the way to go. Thanks.
jbi
Posts: 117
Joined: Sun Apr 24, 2016 3:28 pm

Re: Approximating closed BSpline

Post by jbi »

Bildschirmfoto vom 2023-03-18 10-00-13.png
Bildschirmfoto vom 2023-03-18 10-00-13.png (79.78 KiB) Viewed 614 times

Code: Select all

w=face.Shape.OuterWire
new_wire=Part.Wire(w)
tesselation=2
nb=int(new_wire.Length//tesselation)+1
pts=new_wire.discretize(Number=nb)
nw=Part.makePolygon(pts)

bspline_pts=pts #+[pts[0]]
bspline=Part.BSplineCurve()
bspline.interpolate(bspline_pts) #,PeriodicFlag=True)
bspline_shape=Part.Edge(bspline)
Part.show(bspline_shape)
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Approximating closed BSpline

Post by onekk »

edwilliams16 wrote: Fri Mar 17, 2023 9:40 pm ...

I think not more magics, set degree to 2 so not too much ondulations.

Code: Select all

import FreeCAD
import Part

v3d = FreeCAD.Vector


def show_pts(pts):
    """Show points."""
    for p_idx, pt in enumerate(pts):
        pt_do = Part.show(Part.Vertex(pt), f'pts_{p_idx}')
        pt_do.ViewObject.PointSize = 3
        pt_do.ViewObject.PointColor = (1.0, 0.0, 0.0)


face = FreeCAD.ActiveDocument.getObject('Face')
out_wire_v = face.Shape.OuterWire.Vertexes
pts = [v.Point for v in out_wire_v]

# show_pts(pts)


ptsclosed = pts + [pts[0]]
bs = Part.BSplineCurve()

bs.approximate(ptsclosed, DegMin=2, DegMax=2)
Part.show(bs.toShape(), "bsapprox")

'''
    LengthWeight = float, CurvatureWeight = float, TorsionWeight = float
    If one of these arguments is not null, the functions approximates the
    points using variational smoothing algorithm, which tries to minimize
    additional criterium:
    LengthWeight*CurveLength + CurvatureWeight*Curvature + TorsionWeight*Torsion
    Continuity must be C0, C1(with DegMax >= 3) or C2(with DegMax >= 5).
'''

poly = Part.makePolygon(ptsclosed)
Part.show(poly, "polygon")


bs1 = Part.BSplineCurve()
bs1.approximate(ptsclosed, DegMin=2, DegMax=2, Continuity='C0', LengthWeight=1.0)

Part.show(bs1.toShape(), "bs1_tuned")

ew_bs2.png
ew_bs2.png (7.67 KiB) Viewed 592 times
EDIT: found some refinement, see bs_tuned
ew_bs1.png
ew_bs1.png (7.73 KiB) Viewed 592 times

Hope it helps

Kind Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
jfc4120
Posts: 448
Joined: Sat Jul 02, 2022 11:16 pm

Re: Approximating closed BSpline

Post by jfc4120 »

@onekk and @edwilliams16

Just curious why an approximation routine when using regular points gives a smooth curve?

Where is an approximation routine like that normally used?

See bsp2.png image above, I got a smooth b-spline, no dips.
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Approximating closed BSpline

Post by onekk »

jfc4120 wrote: Sat Mar 18, 2023 4:01 pm @onekk and @edwilliams16

Just curious why an approximation routine when using regular points gives a smooth curve?

Where is an approximation routine like that normally used?
You have to think as a BSpline.

If you see my post I have used two writings one is simply using degree 2 and stop.

Other is using a more tuned way.

Approximate is like to say build a BSPline that is oassing to these points, but what BSPline, what grade (2, 3, 4, 5) periodic, with of without tangency and so on.

Being a curve that is made by a polynomial, you have to tune these things.

You have a method buildFromPoles, but not a method buildFromKnots (that probably would be simply a tename of the approximate method from what I could guess being math impaired :lol: ).

You have another method where you could supply all the four lists:

- Poles
- Weights
- Knots
- Multiplicities

but sadly on mobile I could not check it is something

buildFromPoles.... (multisomething)

Code: Select all

help(Part.BSplineCurve)
will tell you something.

note however that the way to specify some data are following OCCT convention In have around a link where Chris_G probably explains the correct way, but not here on mobile.

Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
Post Reply