It is possible to create a NURBS surface using the API. However, usually, having the NURBS surface isn't enough, and you want to create a face in Fusion whose shape is defined by that surface. Below is some code that does both. It asks you to select a planar face, checks that the face has four linear edges and then uses that information to create a bulged NURBS surface from that face. It then uses that NURBS definition to create a body that contains a single face and finally uses that to create a body in a base feature in the active design. Here's an example of what it creates where the top face on the body is selected, and the yellow face is the result.

import adsk.core, adsk.fusion, traceback
from typing import Tuple
_app = adsk.core.Application.get()
_ui = _app.userInterface
def run(context):
try:
face: adsk.fusion.BRepFace = _ui.selectEntity('Select planar face with four linear sides.', 'PlanarFaces').entity
if face.edges.count != 4:
_ui.messageBox('The selected face must have 4 linear sides.')
return
for edge in face.edges:
if edge.geometry.objectType != adsk.core.Line3D.classType():
_ui.messageBox('The selected face must have 4 linear sides.')
return
bulgeFace = buildBulgeSurfaceBody(face, 5)
# Add the face to the model using a base feature.
des: adsk.fusion.Design = _app.activeProduct
root = des.rootComponent
bf = root.features.baseFeatures.add()
bf.startEdit()
root.bRepBodies.add(bulgeFace[0], bf)
bf.finishEdit()
except:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def buildBulgeSurfaceBody(face: adsk.fusion.BRepFace, bulgeDistance: float) -> adsk.fusion.BRepBody:
try:
# Define a 3x3 control point surface that fits over the face where
# the outer bounds follow the four lines and the middle control
# point is at the center and offset by the bulge distance.
loop : adsk.fusion.BRepLoop = face.loops.item(0)
firstCoedge = loop.coEdges.item(0)
oppositeCoedge = loop.coEdges.item(2)
# Define the four corner points. The others are midpoints of these.
# M4 E2
# 2 +----+----+ 3
# | |
# M1 + M2+ + M3
# E3 | | E1
# 0 +----+----+ 1
# M0 E0
#
# Get the end points of the first edge.
(pnt0, pnt1) = getCoedgeEndPoints(firstCoedge)
mid0 = midPoint(pnt0, pnt1)
# Get the end points of the opposite edge.
(pnt3, pnt2) = getCoedgeEndPoints(oppositeCoedge)
mid4 = midPoint(pnt2, pnt3)
# Calculate the midline points.
mid1 = midPoint(pnt0, pnt2)
mid2 = midPoint(mid0, mid4)
mid3 = midPoint(pnt1, pnt3)
curve0ControlPoints = [pnt0, mid0, pnt1]
curve0 = adsk.core.NurbsCurve3D.createNonRational(curve0ControlPoints, 2, [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], False)
curve1ControlPoints = [pnt1, mid3, pnt3]
curve1 = adsk.core.NurbsCurve3D.createNonRational(curve1ControlPoints, 2, [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], False)
curve2ControlPoints = [pnt3, mid4, pnt2]
curve2 = adsk.core.NurbsCurve3D.createNonRational(curve2ControlPoints, 2, [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], False)
curve3ControlPoints = [pnt2, mid1, pnt0]
curve3 = adsk.core.NurbsCurve3D.createNonRational(curve3ControlPoints, 2, [0.0, 0.0, 0.0, 1.0, 1.0, 1.0], False)
linePoint = mid2.copy()
(_, normal) = face.evaluator.getNormalAtPoint(mid2)
normal.scaleBy(bulgeDistance * 2)
mid2.translateBy(normal)
(_, normal) = face.evaluator.getNormalAtPoint(mid2)
normal.scaleBy(bulgeDistance)
linePoint.translateBy(normal)
surfaceControlPoints = [pnt0, mid0, pnt1, mid1, mid2, mid3, pnt2, mid4, pnt3]
surf = adsk.core.NurbsSurface.create(2, 2, 3, 3, surfaceControlPoints,
[0.0, 0.0, 0.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 1.0, 1.0, 1.0],
[], adsk.core.NurbsSurfaceProperties.OpenNurbsSurface,
adsk.core.NurbsSurfaceProperties.OpenNurbsSurface)
bodyDef = adsk.fusion.BRepBodyDefinition.create()
bodyDef.createVertexDefinition
vertDefs = []
vertDefs.append(bodyDef.createVertexDefinition(pnt0))
vertDefs.append(bodyDef.createVertexDefinition(pnt1))
vertDefs.append(bodyDef.createVertexDefinition(pnt2))
vertDefs.append(bodyDef.createVertexDefinition(pnt3))
edgeDefs = []
edgeDefs.append(bodyDef.createEdgeDefinitionByCurve(vertDefs[0], vertDefs[1], curve0))
edgeDefs.append(bodyDef.createEdgeDefinitionByCurve(vertDefs[1], vertDefs[2], curve1))
edgeDefs.append(bodyDef.createEdgeDefinitionByCurve(vertDefs[2], vertDefs[3], curve2))
edgeDefs.append(bodyDef.createEdgeDefinitionByCurve(vertDefs[3], vertDefs[0], curve3))
lumpDef = bodyDef.lumpDefinitions.add()
shellDef = lumpDef.shellDefinitions.add()
faceDef = shellDef.faceDefinitions.add(surf, False)
faceDef.associativeID = 1
loopDef = faceDef.loopDefinitions.add()
for edgeDef in edgeDefs:
loopDef.bRepCoEdgeDefinitions.add(edgeDef, False)
newBody = bodyDef.createBody()
if not newBody:
_ui.messageBox(bodyDef.outcomeInfo)
points = [pnt0, pnt1, pnt2, pnt3, linePoint]
return (newBody, points)
except:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
# Return the coordinate that represents the midpoint of the two input points.
def midPoint(pnt1 : adsk.core.Point3D, pnt2 : adsk.core.Point3D) -> adsk.core.Point3D:
return adsk.core.Point3D.create((pnt1.x + pnt2.x) / 2,
(pnt1.y + pnt2.y) / 2,
(pnt1.z + pnt2.z) / 2)
# Returns the vertices that correspond with the start and end points of a coedge.
def getCoedgeEndPoints(coEdge : adsk.fusion.BRepCoEdge) -> Tuple[adsk.core.Point3D, adsk.core.Point3D]:
if not coEdge.isOpposedToEdge:
return (coEdge.edge.startVertex.geometry, coEdge.edge.endVertex.geometry)
else:
return (coEdge.edge.endVertex.geometry, coEdge.edge.startVertex.geometry)
---------------------------------------------------------------
Brian EkinsInventor and Fusion 360 API Expert
Website/Blog:
https://EkinsSolutions.com