This is an example from another forum post of python that inserts splines and then lofts them - this should get you close to where you want to be!!
#Author-pcapjp96
#Description-from forums
import adsk.core, adsk.fusion, adsk.cam, traceback, math
def run(context):
ui = None
try:
app = adsk.core.Application.get()
ui = app.userInterface
#define geometry
PropOD = 17.78 # outer diameter of the whole propeller
numSections = 20 # number of airfoil cross sections defined per blade
heightVal = PropOD*.0486 #define the thickness of the hub as a percent of the Propeller diameter
#roR = radius of cross section/propeller radius
#coD = chord/propeller diameter
#t0oc = max thickness/chord
#f0oc = max camber/chord
roR = [0.0675, 0.1407, 0.2134, 0.2852, 0.3557, 0.4243, 0.4908, 0.5547, 0.6156, 0.6731, 0.7269, 0.7766, 0.8219, 0.8626, 0.8984, 0.929, 0.9544, 0.9742, 0.9885, 0.9971, 1.0]
coD = [0.0409, 0.0622, 0.0846, 0.1019, 0.1116, 0.1151, 0.1138, 0.1088, 0.1015, 0.0927, 0.0834, 0.0745, 0.0662, 0.058, 0.0518, 0.0486, 0.0416, 0.0315, 0.0208, 0.0102, 0.001]
t0oc = [0.1157, 0.081, 0.0719, 0.0711, 0.071, 0.071, 0.0713, 0.0714, 0.0716, 0.0715, 0.0708, 0.0705, 0.0704, 0.0715, 0.0718, 0.0702, 0.072, 0.064, 0.0465, 0.0244, -0.0]
f0oc = [0.3638, 0.2974, 0.1941, 0.1415, 0.1137, 0.098, 0.0888, 0.0838, 0.0817, 0.0815, 0.0825, 0.0838, 0.085, 0.0861, 0.0826, 0.077, 0.0737, 0.0716, 0.0704, 0.0697, 0.0695]
pitch = [37.8052, 36.0376, 28.5569, 23.1935, 19.4709, 16.8137, 14.8683, 13.4188, 12.3222, 11.485, 10.836, 10.3208, 9.9027, 9.5636, 9.1906, 8.8226, 8.5587, 8.3667, 8.2378, 8.1642, 8.1403]
#----------------------------------------------------------------------
design = app.activeProduct # what's open at the time
# Get the root component of the active design.
rootComp = design.rootComponent
#create planes
planes = rootComp.constructionPlanes
planeInput = planes.createInput()
R = PropOD/2 #propeller radius
attackAngleList = pitch
offsetVals = [] #create a list of values for the offset planes
for i in range(0,numSections):
offsetVal = adsk.core.ValueInput.createByReal(roR[i]*R)
offsetVals.append(offsetVal)
xSec0 = .4632*heightVal #define the radius of the initial cross section
chordScale = [] #creat list of chord scaling
attackAngleList_rad = [] #create list of attack angles in radians
for i in range(0,len(roR)):
attackAngleList_rad.append(attackAngleList[i]*math.pi/180) # make the angles into radians
chordScale.append(coD[i]*PropOD)
# Create a new sketch on the xy plane.
sketches = rootComp.sketches #sketches comes from the object model(that big tree)
xyPlane = rootComp.xYConstructionPlane # tell it to use xy plane
sketch = sketches.add(xyPlane) #take sketches object and add it to the xy plane
# add cross section 0 in here (the xy plane) the radius should be adjusted to reflect the height value
circles = sketch.sketchCurves.sketchCircles
circle1 = circles.addByCenterRadius(adsk.core.Point3D.create(0, 0, 0), xSec0)
#get sketch profile
profile = sketch.profiles.item(0)
# Create loft feature input
loftFeats = rootComp.features.loftFeatures
loftInput = loftFeats.createInput(adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
loftSectionsObj = loftInput.loftSections
loftSectionsObj.add(profile)
#create the second profile
planeInput.setByOffset(xyPlane, offsetVals[0])
Plane_1 = planes.add(planeInput)
sketches = rootComp.sketches #sketches comes from the object model(that big tree)
sketch = sketches.add(Plane_1) #take sketches object and add it to the PLane_1
circles = sketch.sketchCurves.sketchCircles
circle2 = circles.addByCenterRadius(adsk.core.Point3D.create(0, 0, 0), xSec0)
profile = sketch.profiles.item(0)
loftSectionsObj.add(profile)
for i in range(2,numSections): #create the cross sections for each point of the radius, starting from the 3rd radial point becasue the first two cross section wer too thin and had too much camber
profile = crossSectionCreator(attackAngleList_rad[i],chordScale[i],PropOD,numSections,offsetVals[i],f0oc[i],t0oc[i])
loftSectionsObj.add(profile)
loftInput.isSolid = True
blade = loftFeats.add(loftInput)
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def crossSectionCreator(attackAngle,chord,PropOD,numSections,offset,f0oc,t0oc):
ui = None
try:
app = adsk.core.Application.get()
ui = app.userInterface
design = app.activeProduct #what's open at the time
# Get the root component of the active design.
rootComp = design.rootComponent #sets up the top level component of the design in fusion
xyPlane = rootComp.xYConstructionPlane # tell it to use xy plane
#create 16 planes from 0 to R
planes = rootComp.constructionPlanes
planeInput = planes.createInput()
planeInput.setByOffset(xyPlane, offset)
Plane_1 = planes.add(planeInput)
# Create a new sketch on the work plane 1.
sketches = rootComp.sketches #sketches comes from the object model(that big tree)
sketch_1 = sketches.add(Plane_1) #take sketches object and add it to the PLane_1
pointsList_1 = [] #a list of all the points for the first cross section
points_1 = adsk.core.ObjectCollection.create() #create the point collection for the first cross section
xCoorList = [] #create a list of all x coordinates
xCoordList = [0,.005,.0075,.0125,.025,.05,.075,.10,.15,.20,.25,.30,.35,.40,.45,.50,.55,.60,.65,.70,.75,.80,.85,.90,.95,1]
yCoorList = []
#toc is the thickness/chord at each x value
toc = [0,.00765,.00928,.01183,.01623,.02182,.0265,.0304,.03658,.04127,.04483,.04742,.04912,.04995,.04983,.04863,.04632,.04304,.03899,.03432,.02912,.02352,.01771,.01188,.00604,.00021]
t0oc_scale = t0oc/max(toc)
toc = [i * t0oc_scale for i in toc]
pointsList = [] # create an empty list to keep track of the points to plot the airfoil
xyCoordList = [0,0]
for i in range(0,len(xCoordList)-1): #run thru the list backwards to create negative y coor
xCoord = xCoordList[len(xCoordList)-1-i]
yCoord = (-(toc[len(toc)-1-i])/2)
xyCoordList[0] = xCoord
xyCoordList[1] = yCoord
pointsList.append(xyCoordList[:])
xyCoordList = [0,0]
pointsList.append(xyCoordList[:])
for i in range(1,len(xCoordList)):#run thru the list forward to create positive y coor
xCoord = xCoordList[i]
yCoord = ((toc[i])/2)
xyCoordList[0] = xCoord
xyCoordList[1] = yCoord
pointsList.append(xyCoordList[:])
#add camber
fof0 = [0,0.0422424851726010,0.0593926061968285,0.0906323084802032,0.158483525134397,0.270931306877788,0.365394143890664,0.447811008675706,0.586552890627142,0.698817437077800,0.789957166562438,0.862944523603016,0.919556283803553,0.960850115191575,0.987381517219326,0.999299100103072,0.996365017088989,0.977907976432830,0.942682218138460,0.888536838448501,0.811548821711142,0.702243798510330,0.542003516481535,0.358366674805168,0.171113059574976,4.65743561188795e-11]
yCoorMeanCamberLine = [] #this is f
#dfof0dxoc is dimensionless slope of the mean camber line
dfof0dxoc = [8.86648212455747,7.14348529861109,6.61217039234403,5.94005607895737,5.01947707446513,4.07954342980629,3.51284803126115,3.09821832429069,2.48609887456089,2.02135629339510,1.63380592532664,1.29146814418333,0.976460919354681,0.677114163075186,0.384630792942005,0.0912942925837331,-0.210851915769909,-0.531483751613063,-0.884477130569965,-1.29366100928359,-1.81194534231248,-2.70919436130699,-3.52064353360441,-3.76526230389796,-3.66538904077040,-3.00042531135620]
for i in range(0,len(fof0)): #loop for upper values
yCoorMeanCamberLine.append(fof0[i]*f0oc)
theta = math.atan(dfof0dxoc[i]*f0oc)
yCoord = yCoorMeanCamberLine[i]+(toc[i]/2)*math.cos(theta)
xCoord = pointsList[len(pointsList)-len(fof0)+i][0]-(toc[i]/2)*math.sin(theta)
xyCoordList = [xCoord,yCoord]
pointsList[len(pointsList)-len(fof0)+i] = (xyCoordList[:])
for i in range(0,len(fof0)):#loop for lower values
theta = math.atan(dfof0dxoc[len(dfof0dxoc)-1-i]*f0oc)
yCoord = yCoorMeanCamberLine[len(yCoorMeanCamberLine)-1-i]-(toc[len(toc)-1-i]/2)*math.cos(theta)
xCoord = pointsList[i][0]+(toc[len(toc)-1-i]/2)*math.sin(theta)
xyCoordList = [xCoord,yCoord]
pointsList[i] = xyCoordList[:]
for i in range(0,len(pointsList)): #this loop will step through and manipulate each point
#get the x and y point as a decimal
xcoord = float(pointsList[i][0])
ycoord = float(pointsList[i][1])
if xcoord == 0 or ycoord == 0:#leave the point at 0,0 alone
xcoord = xcoord
ycoord = ycoord
else:
#scale the chord length of the cross section
xcoord = xcoord*chord
ycoord = ycoord*chord
#change the attack angle of the cross section
s = (xcoord**2+ycoord**2)**(1/2) #length from origin to the point
theta = attackAngle-math.atan(ycoord/xcoord)
xcoord = s*math.cos(theta)
ycoord = -s*math.sin(theta)
# make a list of the x and y coordinates
xCoorList.append(xcoord)
yCoorList.append(ycoord)
#save the coordinate in the list
xy_1 = [xcoord,ycoord]
pointsList_1.append(xy_1)
for i in range(0,len(pointsList_1)): #move the the cross section to the middle now that its been scaled and twisted
xcoord = float(pointsList_1[i][0])
ycoord = float(pointsList_1[i][1])
xcoord = xcoord-max(xCoorList)/2
ycoord = ycoord-min(yCoorList)/2
xy_1 = [xcoord,ycoord]
pointsList_1[i] = xy_1
points_1.add(adsk.core.Point3D.create(xcoord, ycoord, 0)) #add the new point to the collection
lines = sketch_1.sketchCurves.sketchLines;
# connect the end of the spline to complete the sketch
lines.addByTwoPoints(adsk.core.Point3D.create(pointsList_1[0][0], pointsList_1[0][1], 0), adsk.core.Point3D.create(pointsList_1[i][0], pointsList_1[i][1], 0))
sketch_1.sketchCurves.sketchFittedSplines.add(points_1) # create a spline curve connecting the points
profile = sketch_1.profiles.item(0) #creates the profile to be lofted
return profile # allow to profile to be accessed outside this function
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))