Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

NURBS cylinder not stitched right

1 REPLY 1
SOLVED
Reply
Message 1 of 2
ginavenolia
137 Views, 1 Reply

NURBS cylinder not stitched right

I'm trying to create a cylinder-like shape from a NURBS surface. It's mostly doing what I want, but the seam in the cylinder looks ragged:

 

ginavenolia_0-1712194262698.png

I've verified that first column of the surface's control points, its last column, and the seam's control points are all identical:

 

First column of the surface:

 
 

 

0: (4.851, 0.000, 0.000)
1: (4.962, 0.000, 1.000)
2: (5.180, 0.000, 2.000)
3: (4.851, 0.000, 3.000)
4: (4.938, 0.000, 4.000)

 

 

Last column of the surface:

 

60: (4.851, 0.000, 0.000)
61: (4.962, 0.000, 1.000)
62: (5.180, 0.000, 2.000)
63: (4.851, 0.000, 3.000)
64: (4.938, 0.000, 4.000)

 

 

Seam:

 

0: (4.851, 0.000, 0.000)
1: (4.962, 0.000, 1.000)
2: (5.180, 0.000, 2.000)
3: (4.851, 0.000, 3.000)
4: (4.938, 0.000, 4.000)

 

 

I'm at my wits' end here. Can anyone tell what's going on here?

 

PS. Turning isPeriodicU=True is a stunning disaster.

 

Here's the code:

 

import math
import random
import traceback
from adsk.core import *
from adsk.fusion import *


def run(context):
    app = Application.get()
    ui = app.userInterface
    try:
        # Parameters
        uCount = 12  # Number of points around the circumference of the cylinder
        vCount = 5  # Number of points along the height of the cylinder
        isPeriodicU = False  # Use periodic NURBS around the circumference?

        # Set up the grid
        grid = []
        for u in range(uCount):
            strip = []
            for v in range(vCount):
                radius = random.gauss(5.0, 0.1)  # * v / vCount + 1.0
                angle = 2.0 * math.pi * u / uCount
                x = radius * math.cos(angle)
                y = radius * math.sin(angle)
                z = v
                point = Point3D.create(x, y, z)
                strip.append(point)
            grid.append(strip)

        def getPoint(u: int, v: int) -> Point3D:
            return grid[u % uCount][v]

        # Create the NURBS surface for the barrel of the cylinder
        degreeU = 1
        degreeV = 1

        controlPointCountU = uCount + 1  # Add one to repeat the start point
        controlPointCountV = vCount

        controlPoints = [
            getPoint(u, v)
            for u in range(controlPointCountU)
            for v in range(controlPointCountV)
        ]

        for index, controlPoint in enumerate(controlPoints):
            if index % controlPointCountV == 0:
                print()
            print(
                f"{index}: ({controlPoint.x:.3f}, {controlPoint.y:.3f}, {controlPoint.z:.3f})"
            )

        knotCountU = controlPointCountU + degreeU + 1
        knotCountV = controlPointCountV + degreeV + 1

        knotsU = [float(i) for i in range(knotCountU)]
        knotsV = [float(i) for i in range(knotCountV)]

        weights = []

        if isPeriodicU:
            propertiesU = NurbsSurfaceProperties.PeriodicNurbsSurface
        else:
            propertiesU = NurbsSurfaceProperties.ClosedNurbsSurface
        propertiesV = NurbsSurfaceProperties.OpenNurbsSurface

        # https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-9f00de94-1b71-4ae3-80ca-0dde9a4f72d9
        surface = NurbsSurface.create(
            degreeU,
            degreeV,
            controlPointCountU,
            controlPointCountV,
            controlPoints,
            knotsU,
            knotsV,
            weights,
            propertiesU,  # type: ignore
            propertiesV,  # type: ignore
        )

        # Create the NURBS curves for the top, bottom, and seam of the barrel
        if isPeriodicU:
            # The documentation says the number of knots should always be len(points)+degree+1,
            # but the API appears to enforce len(points) when isPeriodic is True
            knotCountU = controlPointCountU

        # https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-f41f31c5-2d11-43ad-9ad4-398ef9184684
        botCurve = NurbsCurve3D.createNonRational(
            controlPoints=[getPoint(u, 0) for u in range(controlPointCountU)],
            degree=degreeU,
            knots=[float(i) for i in range(knotCountU)],
            isPeriodic=isPeriodicU,
        )

        topCurve = NurbsCurve3D.createNonRational(
            controlPoints=[
                getPoint(u, controlPointCountV - 1) for u in range(controlPointCountU)
            ],
            degree=degreeU,
            knots=[float(i) for i in range(knotCountU)],
            isPeriodic=isPeriodicU,
        )

        seamCurve = NurbsCurve3D.createNonRational(
            controlPoints=[getPoint(0, v) for v in range(controlPointCountV)],
            degree=degreeV,
            knots=[float(i) for i in range(knotCountV)],
            isPeriodic=False,
        )

        print()
        print("Seam:")
        for index, controlPoint in enumerate(seamCurve.controlPoints):
            print(
                f"{index}: ({controlPoint.x:.3f}, {controlPoint.y:.3f}, {controlPoint.z:.3f})"
            )

        # Define the vertices
        botPoint = getPoint(0, 0)
        topPoint = getPoint(0, controlPointCountV - 1)

        bodyDefinition = BRepBodyDefinition.create()
        lumpDefinition = bodyDefinition.lumpDefinitions.add()
        shellDefinition = lumpDefinition.shellDefinitions.add()

        botVertex = bodyDefinition.createVertexDefinition(botPoint)
        topVertex = bodyDefinition.createVertexDefinition(topPoint)

        # Define the edges
        botPlane = Plane.create(
            Point3D.create(0.0, 0.0, botPoint.z), Vector3D.create(0.0, 0.0, -1.0)
        )

        topPlane = Plane.create(
            Point3D.create(0.0, 0.0, topPoint.z), Vector3D.create(0.0, 0.0, 1.0)
        )

        botEdge = bodyDefinition.createEdgeDefinitionByCurve(
            botVertex, botVertex, botCurve
        )
        topEdge = bodyDefinition.createEdgeDefinitionByCurve(
            topVertex, topVertex, topCurve
        )
        seamEdge = bodyDefinition.createEdgeDefinitionByCurve(
            topVertex, botVertex, seamCurve
        )

        # Define the faces
        botFace = shellDefinition.faceDefinitions.add(botPlane, False)
        botLoop = botFace.loopDefinitions.add()
        botLoop.bRepCoEdgeDefinitions.add(botEdge, True)

        topFace = shellDefinition.faceDefinitions.add(topPlane, False)
        topLoop = topFace.loopDefinitions.add()
        topLoop.bRepCoEdgeDefinitions.add(topEdge, False)

        barrelFace = shellDefinition.faceDefinitions.add(surface, False)
        barrelLoop = barrelFace.loopDefinitions.add()
        barrelLoop.bRepCoEdgeDefinitions.add(topEdge, False)
        barrelLoop.bRepCoEdgeDefinitions.add(seamEdge, False)
        barrelLoop.bRepCoEdgeDefinitions.add(botEdge, True)
        barrelLoop.bRepCoEdgeDefinitions.add(seamEdge, True)

        # Create the body
        bodyDefinition.doFullHealing = True
        body = bodyDefinition.createBody()

        print(f"Is valid: {bodyDefinition.isValid}")

        for outcome in bodyDefinition.outcomeInfo:
            print(f"Outcome: {outcome}")

        # Add the body to the design
        designProduct = Design.cast(app.activeProduct)
        component = designProduct.activeComponent
        baseFeatures = component.features.baseFeatures

        baseFeature = baseFeatures.add()
        baseFeature.startEdit()
        component.bRepBodies.add(body, baseFeature)
        baseFeature.finishEdit()

        groups = component.customGraphicsGroups
        while groups.count > 0:
            groups.item(0).deleteMe()

        graphics = groups.add()

        for v in range(controlPointCountV):
            assert getPoint(0, v) is getPoint(controlPointCountU - 1, v)
            coords = []
            for u in range(controlPointCountU):
                point = getPoint(u, v)
                coords.append(point.x)
                coords.append(point.y)
                coords.append(point.z)
            coordinates = CustomGraphicsCoordinates.create(coords)
            lines = graphics.addLines(coordinates, [], True)
            lines.weight = 2
            lines.color = CustomGraphicsSolidColorEffect.create(
                Color.create(0, 127, 255, 255)
            )
            lines.depthPriority = 100

        for u in range(controlPointCountU):
            coords = []
            for v in range(controlPointCountV):
                point = getPoint(u, v)
                coords.append(point.x)
                coords.append(point.y)
                coords.append(point.z)
            coordinates = CustomGraphicsCoordinates.create(coords)
            lines = graphics.addLines(coordinates, [], True)
            lines.weight = 2
            lines.color = CustomGraphicsSolidColorEffect.create(
                Color.create(0, 255, 127, 255)
            )
            lines.depthPriority = 200
    except:
        ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

 

 

1 REPLY 1
Message 2 of 2
ginavenolia
in reply to: ginavenolia

Well, it looks like I got the vertices reversed when creating the seam edge. That was horribly embarrassing. 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report