[SOLVED] How do I make a solid from a B-Spline surface?

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
sph
Posts: 19
Joined: Sun Jul 25, 2021 6:18 pm

[SOLVED] How do I make a solid from a B-Spline surface?

Post by sph »

I don't know, if this is the right place for my problem, because it concerns opencascade/pythonocc directly. I tried to register on opencascade's forum, but never got any reply …

Please point me to the right place, if this topic is inappropriate for this forum. Thanks!

I use pythonocc directly with

Code: Select all

from OCC.core import GeomAPI, GeomFill, GeomConvert
I generated the following (green) B-Spline surface:
2021-07-25-Note-20-34-1.resized.png
2021-07-25-Note-20-34-1.resized.png (33.67 KiB) Viewed 2598 times
with

Code: Select all

surface = GeomAPI.GeomAPI_PointsToBSplineSurface()
How can I make a solid out of it?

I managed to add the long (light blue) surface to it:
2021-07-25-Note-20-34-2.resized.png
2021-07-25-Note-20-34-2.resized.png (33.61 KiB) Viewed 2598 times
using

Code: Select all

_, _, v_face, v_back = surface.Bounds()
face = surface.VIso(v_face)
back = surface.VIso(v_back)

strip_surface = GeomFill.GeomFill_Surface(face, back)
or alternatively

Code: Select all

strip_surface = GeomFill.GeomFill_BSplineCurves(
    GeomConvert.GeomConvert_CurveToBSplineCurve(le_face),
    GeomConvert.GeomConvert_CurveToBSplineCurve(te_face),
    GeomFill.GeomConvert_FillingStyle.GeomFill_StretchStyle)
How could I add the (red) top and bottom faces to close the object?
2021-07-25-Note-20-34-3.resized.png
2021-07-25-Note-20-34-3.resized.png (31.07 KiB) Viewed 2598 times
Last edited by sph on Wed Aug 04, 2021 8:12 pm, edited 1 time in total.
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: How do I make a solid from a B-Spline surface?

Post by onekk »

Opencascade forum is not very friendly.

But Python OCC is a completely different thing from FreeCAD.

So I doubt that posting Python OCC code here is the best oprion.

There is no a Python OCC forum?

Said so I'm facing same problem, and for Now I haven't found a proper answer.

obtaining a face from the red curves, seems not to difficult, the main concern is to assemble the solid as a BREP Shell that have all edges "watertight", there are some interesting discussions on the forum:

This is the discussion I have opened some to solve my problem that is still unsolved:

https://forum.freecadweb.org/viewtopic.php?f=22&t=60349

Some other interesting discussions:

https://forum.freecadweb.org/viewtopic. ... e774086c85

https://forum.freecadweb.org/viewtopic.php?f=3&t=16473

https://forum.freecadweb.org/viewtopic.php?t=22675


Hope it helps

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/
sph
Posts: 19
Joined: Sun Jul 25, 2021 6:18 pm

Re: How do I make a solid from a B-Spline surface?

Post by sph »

Thanks Carlo.

I thought, FreeCAD is using pythonocc and occ under its hood, so I was hoping, that someone can tell me, which of the million of occ functions I can use to close my part.

Officially there is this PythonOCC forum: http://groups.google.com/group/pythonocc/about, but whenever I try to open it, I get an error.

I already have the problem to add that read surface. I had those two ideas (note, that to my knowledge, <code>GeomFill_Surface</code> needs at least two curves to generate the surface):
  1. Get the top line of the green and blue surfaces and use them
  2. Get the top line of the green and blue surfaces and use them
The problems are:
Re 1.: The top curve can have concave/convex sections.
Re 2.: The top curve must be split at the right extrema, but this is not at v=0.5.

How would you generate the red curve? That might give me an idea …

Then I will look at making a shell and solid.

Thanks.
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: How do I make a solid from a B-Spline surface?

Post by onekk »

You could try to use "curves" workbench, that is an addon, or maybe try to code in "FreeCAD Python" the whole thing.

If you post some "usable code" here maybe someone, (maybe me maybe other people more skilled that me) wil help you with a "concise" answer in form of some lines of code.

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/
sph
Posts: 19
Joined: Sun Jul 25, 2021 6:18 pm

Re: How do I make a solid from a B-Spline surface?

Post by sph »

Here is an example:

Code: Select all

#!/usr/bin/env python3

import math

from OCC.Core import gp
from OCC.Core import TColgp
from OCC.Core import GeomAPI
from OCC.Core import GeomFill
from OCC.Core import BRepTools
from OCC.Core import BRepBuilderAPI

from OCC.Display.SimpleGui import init_display


NO_X = 10
NO_Z = 10

X = 1
Y = 0.1
Z = 2


def ellipse(x, a, b):
    """Calculate the y-coordinate of an ellipse with the one apex at (0,0) and one at (2a,0)."""
    return b/a * math.sqrt(a**2 - (x-a)**2)


def parabola(x, a, b):
    """Calculate the y-coordinate of an parabola through the points (0,0), (a,b) and (2a,0)."""
    return b * (1 - ((x-a)/a)**2)


def points():
    """Generate the points of our surface."""
    array = TColgp.TColgp_Array2OfPnt(1, 2*NO_X-1, 1, NO_Z)
    for j in range(NO_Z):
        z = (j+10)/NO_Z * Z
        b = Z/10 - 0.02*z

        # First side (back)
        for i in range(1, NO_X):
            x = i/NO_X * X
            y = parabola(x, X/2, 2*b) + ellipse(x, X/2, b)
            point = gp.gp_Pnt(x, y, z)
            array.SetValue(i, j+1, point)

        # Second side (face)
        k = i
        for i in range(NO_X, 0, -1):
            x = i/NO_X * X
            y = parabola(x, X/2, 2*b) - ellipse(x, X/2, b)
            point = gp.gp_Pnt(x, y, z)
            k += 1
            array.SetValue(k, j+1, point)

    return array


# Fit a surface to our points
pts = points()
surface = GeomAPI.GeomAPI_PointsToBSplineSurface()
surface.Interpolate(pts, False)
surface = surface.Surface()

# Boundary lines to our surface
u_back, u_face, v_bottom, v_top = surface.Bounds()
bl_back = surface.UIso(u_back)
bl_face = surface.UIso(u_face)
bl_bottom = surface.VIso(v_bottom)
bl_top = surface.VIso(v_top)

# Fit a surface to the open aft end of our surface
surface_aft = GeomFill.geomfill_Surface(bl_back, bl_face)

# Boundary lines to the aft surface
u_bottom2, u_top2, v_face2, v_back2 = surface_aft.Bounds()
bl_bottom2 = surface_aft.UIso(u_bottom2)
bl_top2 = surface_aft.UIso(u_top2)

# Fit a surface to the open bottom of our surface
l_bottom = surface_aft.UIso(u_bottom2)
surface_bottom = GeomFill.geomfill_Surface(bl_bottom, bl_bottom2)

# Fit a surface to the open top of our surface
surface_top = GeomFill.geomfill_Surface(bl_top, bl_top2)

# Display the surfaces
display, start_display, _, _ = init_display()
display.DisplayShape(surface, update=True)
display.DisplayShape(surface_aft, update=True)
display.DisplayShape(surface_bottom, update=True)
display.DisplayShape(surface_top, update=True)
display.FitAll()
start_display()
The surfaces generated are:
Screenshot from 2021-08-01 18-06-47.png
Screenshot from 2021-08-01 18-06-47.png (45.57 KiB) Viewed 2270 times
As can be seen, the bottom and top surfaces ‘overlap’. This is due to the using the whole bottom and the line, which closes the it at the end, when generating the bottom surface with `GeomFill.geomfill_Surface(bl_bottom, bl_bottom2)`. The same at the top.

This approach should work, if I split the bottom section in two at the forward extrema and using these two lines to make a filled surface. NB: This extrema is generally not at u=1/2!

How do I close the bottom and top ‘gaps’?

Any hint (FreeCAD, C++, PythonOCC, …) is welcome!

Please also note, that the two surfaces in the example are located in a plane. Generally they are not located in a plane!
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: How do I make a solid from a B-Spline surface?

Post by onekk »

Some FreeCAD python code to start:

Code: Select all

import FreeCAD
import Part

import math

from FreeCAD import Vector, Rotation


DOC_NAME = "surface_test"

FreeCAD.newDocument(DOC_NAME)
FreeCAD.setActiveDocument(DOC_NAME)
DOC = FreeCAD.activeDocument()
VIEW = FreeCAD.Gui.ActiveDocument.ActiveView

def show_point(pt, c_idx, p_idx, p_size = 3, p_color=(1.0, 0.33, 0.0 )):
    vtx = DOC.addObject("Part::Vertex", "curv{}_vx{}".format(c_idx,p_idx))
    vtx.X = pt.x
    vtx.Y = pt.y
    vtx.Z = pt.z
    vtx.ViewObject.PointSize = p_size
    vtx.ViewObject.PointColor = p_color

def show_curve(curve, c_name, psize=1, pcolor=(0.00, 1.0, 0.0 )):
    """Show the curve with the appropriate view approximation"""
    print("curve ti: ", curve.TypeId)
    if curve.TypeId in ("Part::GeomBSplineCurve", "Part::GeomCircle", "Part::GeomArcOfCircle"):
        shape = curve.toShape()
    else:
        shape = curve
    scur = DOC.addObject("Part::Feature", c_name)
    scur.Shape = shape
    scur.ViewObject.Deviation = 0.010
    scur.ViewObject.AngularDeflection = '2.00 deg'


# ----code begin here ----


a_tol = 0.001

NO_X = 10
NO_Z = 10

X = 1
Y = 0.1
Z = 2


def ellipse(x, a, b):
    """Calculate the y-coordinate of an ellipse with the one apex at (0,0) and one at (2a,0)."""
    return b/a * math.sqrt(a**2 - (x-a)**2)


def parabola(x, a, b):
    """Calculate the y-coordinate of an parabola through the points (0,0), (a,b) and (2a,0)."""
    return b * (1 - ((x-a)/a)**2)

def points():
    """Generate the points of our surface."""
    array = []
    array1 = []
    for j in range(NO_Z):
        z = (j+10)/NO_Z * Z
        b = Z/10 - 0.02*z
        line = []
        # First side (back)
        for i in range(1, NO_X):
            x = i/NO_X * X
            y = parabola(x, X/2, 2*b) + ellipse(x, X/2, b)
            point = Vector(x, y, z)
            line.append(point)

        # Second side (face)
        for i in range(NO_X, 0, -1):
            x = i/NO_X * X
            y = parabola(x, X/2, 2*b) - ellipse(x, X/2, b)
            point = Vector(x, y, z)
            line.append(point)

        array.append(line)

    return array

array = points()

show_points = False

if show_points == True:
    for l_idx,line in enumerate(array):
        for p_idx, pt in enumerate(line):
            show_point(pt, l_idx, p_idx)

DOC.recompute()

bs = Part.BSplineSurface()
bs.approximate(Points=array)

base_surf = bs.toShape()
show_curve(base_surf, "base_surf")

DOC.recompute()

This will create the base surface, now the top, bottom and the side, could be obtained using the first and last "line", let's me work some more on the code.

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/
sph
Posts: 19
Joined: Sun Jul 25, 2021 6:18 pm

Re: How do I make a solid from a B-Spline surface?

Post by sph »

Thanks for converting the PythonOCC code to FreeCAD code!

Now I am really curious, how the top and bottom can be closed …
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: How do I make a solid from a B-Spline surface?

Post by onekk »

I have tried many ways, but there are some quirks,
EDIT: see next post
Regards

Carlo D.
Last edited by onekk on Mon Aug 02, 2021 6:33 pm, edited 1 time in total.
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/
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: How do I make a solid from a B-Spline surface?

Post by onekk »

This code will be more polished and permit to try the two alternatives, see:

Code: Select all

    bs1.exchangeUV()
that will reverse the BSplineSurface, but is working only on open curves, closing them it make FreeCAD crash.

Code: Select all


sections = []

secs_open = True


if secs_open == True:
    for line in array:
        sec = Part.BSplineCurve()
        sec.approximate(line)

        sections.append(sec)
    
    bs1 = Part.BSplineSurface()
    bs1.buildFromNSections(sections)
    bs1.exchangeUV()
    
    alt_surf = bs1.toShape()
    show_curve(alt_surf, "alt_surf")


    
else:
    for line in array:
        sec = Part.BSplineCurve()
        sec.approximate(line)
        sec_sp = sec.StartPoint
        sec_ep = sec.EndPoint
        
        c_ln = Part.LineSegment(sec_ep, sec_sp)
    
        edges = Part.sortEdges([c_ln.toShape(), sec.toShape()])[0]
    
        prof = Part.Wire(edges)
            
        sections.append(prof)
    
    shapes = Part.makeLoft(sections,True)
    show_curve(shapes,"loft")


'''
top_face = Part.Face(sections[0])
show_curve(top_face,"top")

bot_face = Part.Face(sections[-1])
show_curve(bot_face,"bottom")

'''


EDIT: after some research it semm that the makeLoft is working at least using "check geometry" from menu Part, maybe the "black" faces are simply visualization artifacts, see if it works for you.


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/
sph
Posts: 19
Joined: Sun Jul 25, 2021 6:18 pm

Re: How do I make a solid from a B-Spline surface?

Post by sph »

Thanks.

Code: Select all

secs_open = True
does not produce any additional faces for me.

Code: Select all

secs_open = False
produces a closed loft with top and bottom surfaces. The problem with this approach is, that my real life case is a bit more complicated and I cannot use a loft.

But I possibly could make a dummy loft using the the bottom section (and some others), cut out the bottom surface and use this to close my object. Then I could do the same for the top surface. But it strikes me a bit complicated and I wonder, if there isn't a way, to fill a 3d curve with a surface (other CAD packages have something called ‘Boundary Surface’ or ‘Filled Surface’ which does exactly that).
Post Reply