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.
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Why is this surface a NurbsSurface and not an EllipticalCone?

Message 1 of 5
275 Views, 4 Replies

Why is this surface a NurbsSurface and not an EllipticalCone?

The script below generates two sketch circles and lofts between them. The product is a NURBS surface, as you can tell by the presence of a seam:

Screen Shot 2021-02-06 at 2.58.23 PM.png

Both circles have identical normals, but they have slightly different radii and X/Y locations.


I am actually trying pretty hard to avoid seams, so I investigated this in some detail. The exact result is exquisitely dependent on the inputs. You might receive a Cylinder, a Cone, an EllipticalCone, or a NurbsSurface depending on the values.


The odd thing is that worse mismatches in values generate better geometry. You nearly always get a seamless surface unless the values vary just in the 6th or 7th decimal places (as shown in the script). Given that the normals of the circles are exactly equal, shouldn't it always be possible to receive at least an elliptical curve?


import adsk.core, adsk.fusion,, traceback
import adsk.core as core, adsk.fusion as fusion
from adsk.core import Vector3D, Point3D, Matrix3D

def run(context):
        ui = None
                app = adsk.core.Application.get()
                ui  = app.userInterface
                design = app.activeProduct
                root = design.rootComponent
                sketch = root.sketches.add(root.xYConstructionPlane)
                circles = sketch.sketchCurves.sketchCircles

                origins = [
                    (0.0438284, 0, -0.6),
                    (0.0438277, 0, -0.2)

                radii = [0.1700008, 0.1700004]

                xyzCoords = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
                xAxis, yAxis, zAxis = (Vector3D.create(*coords) for coords in xyzCoords)
                origin = Point3D.create(0, 0, 0)

                def moveTransform(x, y, z):
                    newOrigin = Point3D.create(x, y, z)
                    matrix = Matrix3D.create()
                    matrix.setToAlignCoordinateSystems(origin, xAxis, yAxis, zAxis, 
                        newOrigin, xAxis, yAxis, zAxis)
                    return matrix

                def makeCircle(loc, radius):
                    moveMatrix = moveTransform(*loc)
                    circle = circles.addByCenterRadius(origin, radius)
                    collection = adsk.core.ObjectCollection.create()
                    sketch.move(collection, moveMatrix)
                    return circle

                def createLoftFeature(profiles):
                    option = fusion.ChainedCurveOptions.noChainedCurves
                    profilePaths = [ fusion.Path.create(curve, option) for curve in profiles ]
                    loftFeatures = root.features.loftFeatures
                    newBodyOption = fusion.FeatureOperations.NewBodyFeatureOperation
                    loftInput = loftFeatures.createInput(newBodyOption)
                    for path in profilePaths:
                    loftInput.isSolid = False
                    return root.features.loftFeatures.add(loftInput)

                sketchCircles = [makeCircle(loc, radius) for loc, radius in zip(origins, radii)]
                loft = createLoftFeature(sketchCircles)
                surface = loft.bodies[0].faces[0].geometry
                ui.messageBox(f"Surface type is {str(type(surface))}")

                if ui:
Message 2 of 5
in reply to: GRSnyder



It does appear that you are falling into a tolerance well here. The default modeling absolute tolerance used is 1e-6, which explains the trouble right around those values. I agree that in principle this case should always fall into either cylinder, cone, elliptical cylinder, or elliptical cone. This determination is made down in the ASM kernel, I can't easily say exactly why, but apparently it is trigger a fall through into the more general wire-wire loft which results in the spline.


If you are trying to avoid the nurbs surface, I can only suggest that you transform the curves to avoid this 'nearly perpendicular' projection. Project the center point from one curve onto the plane of the second. and translate the second curve such that the distance between its center point is greater than or less than 1e-6 by an order of magnitude (>1e-5 or <1e-7).



Kris Kaplan
Message 3 of 5
in reply to: KrisKaplan

Thanks! This is helpful and actionable information.


I had seen oblique references to "the modeling tolerance" elsewhere in the docs, but I had been assuming it was linked to numeric precision.


Just to be sure I'm understanding this correctly: The modeling kernel has a defined precision that is specific (1E-6 in kernel units or one one hundred thousandth of a millimeter) and significantly less precise than the doubles that are used for representation. Coordinates or parameters that are within 1E-6 of each other are considered the same number from a geometric perspective, even if they may be stored at higher precision. So you have to be careful around differences of 1E-6 because you may (or may not, depending on the exact situation) encounter a discontinuity in behavior there.


Is that correct?


Is this the reason that, e.g., the vertex coordinates reported by BRepEdge often do not exactly match the coordinates reported by the underlying Curve3D?

Message 4 of 5
in reply to: GRSnyder

Correct. Modeling precision is not the same as floating point numerical limits precision. Modeling kernels use various tolerances (normally way above numerical limits). In Fusion, the absolute point equality ('resabs') tolerance is set to 1e-6. That means that at least for the purposes of topology, two points are considered coincident if they are within this tolerance distance of each other. This tolerance value is in the 'database length units', which for Fusion is in centimeters. (All of the API geometry APIs are always in cm (db units), so you shouldn't need to worry about any sort of units conversion at this level.)


Because of historical reasons, there isn't any public documentation for the ShapeManager, or ACIS which it derived from. But with a google search I did find this part of a paper that partially describes the tolerances involved.


There are several scenarios where an edge vertex may not be exactly identical to the edge curves endpoints, so it's a bit hard to say which you might be referring to. But they should be considered coincident (topologically speaking) if they are within that resabs tolerance distance. One of perhaps the most common mechanism that could result in this 'fuzziness' would be procedural geometry. The edge could be defined procedurally as the intersection of two faces, and the vertex could be defined as the intersection of these two procedural edge curves. When asked to return curve geometry for one of these intersection edges, an approximation curve has to be generated to represent it. Because these are subsetted approximations of the intersection, the endpoints of both edge curve geometry is not always going to be coincident up to numberical limits, but it will be coincident within resabs tolerance.



Kris Kaplan
Message 5 of 5
in reply to: GRSnyder

Excellent information (and reference) - thank you!

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

Post to forums  

Autodesk DevCon in Munich May 28-29th

Autodesk Design & Make Report