- Use either of the following methods to create upper and lower surfaces.
- Create a bivariate spline surface that passes through all the input points in Python (Good in a sense that the surface passes through all the input points but high risk of overshoot)
- Create a spline surface using control points (Not ideal but can avoid overshoot completely)
- Trim the surface slightly inside of the boundary.
- Connect the boundary curve defined by spline with the edge curves of surfaces by ruled surfaces.
- Use Part Builder to do "Face from edges", "Shell from faces", and "Solid from shell" to create a solid.
I still have some issues when creating the ruled surfaces to connect the boundary curve and the upper and lower surfaces. A very skewed surface is sometimes created when the boundary curve and the edge curves of upper or lower surfaces are complex. My current workaround is to cut those curves to let FreeCAD know which part of the curves should be joined by a straight line. It would be great if there is more control on the ruled surface.
Anyway, thank you very much for your help. I think I finally got what I wanted.
Code: Select all
import FreeCAD as App
from FreeCAD import Base
import Draft
import Part
import numpy as np
from scipy import interpolate
# Create a sample surface
x=np.linspace(-1,1,10)*np.pi
y=np.linspace(-1,1,10)*np.pi
X, Y=np.meshgrid(x,y)
Z=0.5*(np.sin(X)+2*np.cos(2*Y)+Y)
degree_u=3
degree_v=3
doc = App.newDocument()
#--Forcing the surface to pass through all input points--
# Create spline surface
f=interpolate.RectBivariateSpline(x,y,Z.T)
coeffs=f.get_coeffs()
coeffs=np.reshape(coeffs,Z.T.shape).T
knots=f.get_knots()
# Convert knot vector format
knot_u, mult_u=np.unique(knots[1],return_counts=True)
knot_v, mult_v=np.unique(knots[0],return_counts=True)
# Normalize knot vectors
knot_u=(knot_u-knot_u.min())/(knot_u.max()-knot_u.min())
knot_v=(knot_v-knot_v.min())/(knot_v.max()-knot_v.min())
# Calculate spline coefficients for x and y
fx=interpolate.make_interp_spline(x,x)
coeffx=fx.tck[1]
fy=interpolate.make_interp_spline(y,y)
coeffy=fy.tck[1]
# Create control point vectors
v=Base.Vector
ctrl=[]
for iZ,coeffsi in enumerate(coeffs):
ctrl.append([])
for jZ,_ in enumerate(coeffsi):
ctrl[-1].append(v(coeffx[jZ],coeffy[iZ],coeffs[iZ,jZ]))
# Create spline surface in FreeCAD
periodic = False
bs=Part.BSplineSurface()
bs.buildFromPolesMultsKnots(ctrl, mult_u, mult_v, knot_u, knot_v, periodic, periodic, degree_u, degree_v)
s=bs.toShape()
Part.show(s)
# Show input points and control poins
for iZ, Zi in enumerate(Z):
for jZ, _ in enumerate(Zi):
# Input points
Draft.make_point(v(X[iZ,jZ],Y[iZ,jZ],Z[iZ,jZ]))
# Control points
Draft.make_point(v(coeffx[jZ],coeffy[iZ],coeffs[iZ,jZ]))
#--Using input points as control points--
# Define control points
coeffs2=Z
coeffx2=X[0,:]
coeffy2=Y[:,0]
# Define knot vectors
knot_u2=np.hstack([np.zeros(degree_u),np.linspace(0,1,Z.shape[0]+1-degree_u),np.ones(degree_u)])
knot_v2=np.hstack([np.zeros(degree_v),np.linspace(0,1,Z.shape[1]+1-degree_v),np.ones(degree_v)])
# Convert knot vector format
knot_u2, mult_u2=np.unique(knot_u2,return_counts=True)
knot_v2, mult_v2=np.unique(knot_v2,return_counts=True)
# Normalize knot vectors
knot_u2=(knot_u2-knot_u2.min())/(knot_u2.max()-knot_u2.min())
knot_v2=(knot_v2-knot_v2.min())/(knot_v2.max()-knot_v2.min())
# Create control point vectors
v=Base.Vector
ctrl2=[]
for iZ,coeffsi in enumerate(coeffs2):
ctrl2.append([])
for jZ,_ in enumerate(coeffsi):
ctrl2[-1].append(v(coeffx2[jZ],coeffy2[iZ],coeffs2[iZ,jZ]))
# Create spline surface in FreeCAD
periodic = False
bs2=Part.BSplineSurface()
bs2.buildFromPolesMultsKnots(ctrl2, mult_u2, mult_v2, knot_u2, knot_v2, periodic, periodic, degree_u, degree_v)
s2=bs2.toShape()
Part.show(s2)
doc.recompute()