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: 

Specify internal rails and/or points for PatchFeature using the API

19 REPLIES 19
SOLVED
Reply
Message 1 of 20
rarora7777
1462 Views, 19 Replies

Specify internal rails and/or points for PatchFeature using the API

I want to build a PatchFeature by specifying a set of boundary curves and a set of interior curves (preferred) or points which the patch should pass through. I can achieve this using the interface but can't find a way to do it via the API.

 

Create patch UI (in Patch workspace)Create patch UI (in Patch workspace)I can specify the boundary using the PatchFeature.boundaryCurves property or when calling PatchFeatures.createInput(), but can't find the relevant properties for the internal points/rails. Is this property not exposed in the API so far?

 

 

 

19 REPLIES 19
Message 2 of 20
BrianEkins
in reply to: rarora7777

Support for interior points and rails was added to the feature after it was initially supported by the API. Unfortunately, the API hasn't been updated to expose this capability.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 3 of 20
kandennti
in reply to: BrianEkins

I was also looking for this feature, it's a shame.

 

Since it seems that you can not create NurbsSurface with API,
I want you to support this feature.

Message 4 of 20
sonstwienix
in reply to: rarora7777

2 years later, and i am still suffering from incomplete feature exposure in the API.

I need the Patch feature to support the interior rails stuff. This thread is what google finds.

 

I spent days to generate the rails (a minimum surface area transformation solution for a surface loft)  and now this??


Is there any workaround after 2 years of product updates?

Message 5 of 20
kandennti
in reply to: sonstwienix

Hi @sonstwienix .

 

I'm not sure if it's available, but here's a sample that uses text commands.

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core


def run(context):
    ui: adsk.core.UserInterface = None
    try:
        app: adsk.core.Application = adsk.core.Application.get()
        app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
        ui = app.userInterface
        des: adsk.fusion.Design = app.activeProduct
        root: adsk.fusion.Component = des.rootComponent

        # sketch
        skt: adsk.fusion.Sketch = initSketch(root)

        # support face
        prof: adsk.fusion.Profile = skt.profiles[0]
        extFeat: adsk.fusion.ExtrudeFeature = initExtrude(prof)

        # create Patch
        initPatch(
            extFeat.bodies[0].edges[1],
            skt.sketchPoints[-1]
        )

        # fit
        app.activeViewport.fit()

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


def initPatch(
    boundaryEdge: adsk.fusion.BRepEdge,
    interiorPoint: adsk.fusion.SketchPoint):

    app: adsk.core.Application = adsk.core.Application.get()
    ui = app.userInterface
    sels: adsk.core.Selections=  ui.activeSelections
    sels.clear()

    # profile - preselection
    sels.add(boundaryEdge)

    # command dialog
    app.executeTextCommand(u'Commands.Start FusionSurfacePatchCommand')

    # InteriorRailsAndPoints
    app.executeTextCommand(u'UI.EnableCommandInput InteriorRailsAndPoints')
    sels.clear()
    sels.add(interiorPoint)

    # command comit
    app.executeTextCommand(u'NuCommands.CommitCmd')

    return boundaryEdge.body.parentComponent.features.patchFeatures[-1]


def initExtrude(
    prof: adsk.fusion.Profile) -> adsk.fusion.ExtrudeFeature:

    skt: adsk.fusion.Sketch = prof.parentSketch
    comp: adsk.fusion.Component = skt.parentComponent
    extrudes: adsk.fusion.ExtrudeFeatures = comp.features.extrudeFeatures

    extFeatIpt: adsk.fusion.ExtrudeFeatureInput = extrudes.createInput(
        prof,
        adsk.fusion.FeatureOperations.NewBodyFeatureOperation
    )
    extFeatIpt.isSolid = False
    extFeatIpt.setDistanceExtent(
        False, 
        adsk.core.ValueInput.createByReal(-1)
    )
    extFeat: adsk.fusion.ExtrudeFeature = extrudes.add(extFeatIpt)

    return extFeat


def initSketch(
    comp: adsk.fusion.Component) -> adsk.fusion.Sketch:

    skt: adsk.fusion.Sketch = comp.sketches.add(
        comp.xYConstructionPlane
    )

    # profile
    skt.sketchCurves.sketchCircles.addByCenterRadius(
        adsk.core.Point3D.create(2,1,0),
        2
    )

    # interior point
    skt.sketchPoints.add(
        adsk.core.Point3D.create(1,2,1)
    )

    return skt

 

If you only need the shape, you may be able to use the BRepBodyDefinition object.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-5FB440FE-A44D-4204-B37C-06CC2767D727 
It is too complicated for me to understand.

Message 6 of 20
sonstwienix
in reply to: rarora7777

I havent tried it yet, but it looks promising enough that i feel immediately compelled to thank you very much!!

 

Do you have any idea where to find information about the Text commands?

Commands.Start and  UI.EnableCommandInput seem pretty powerful things, if you knew what to write afterwards for a certain feature........... is there documentation that says "InteriorRailsAndPoints" is a valid input?

Message 7 of 20
kandennti
in reply to: sonstwienix

@sonstwienix .

 

In the past, I have written two articles on text commands.

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/use-textcommands/m-p/9645688

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/use-textcommands2/m-p/9937161

 

I believe they can control many, but not all, of the command dialogs.

 

There is very little information about text commands.
What we can do is to try many of them and write them down.

 

However, the use of text commands is deprecated by Autodesk and is at your own risk.

 

Message 8 of 20
sonstwienix
in reply to: rarora7777

If this is deprecates it hopefully gets replaced by something feature complete and maintained 😄

 

Meanwhile, i am struggling with the interior rail selection..

    # InteriorRailsAndPoints
    app.executeTextCommand(u'UI.EnableCommandInput InteriorRailsAndPoints')
    sels.clear()
    sels.add(interiorPoint)

 For me, the variable "interiorPoint" are all Sketch.SketchCurves.sketchFittedSplines.
sels.add() fails with the message "invalid argument entity".

I tried adding the splines one by one and as list. Nothing works. As if the sketch-splines are no "entities" in regards to the expected type. But they have an entityToken and everything.

 

Edit: Btw, i stuggle to select anything from my sketch in general. I also cannot use sketch lines as exterior boundary. sels.add() simply fails with anything coming from a sketch! Why??

I have no idea how to fix this, since in the example script and others that i found, people can add sketch-entities successfully to the selection.

Any ideas?

Message 9 of 20
sonstwienix
in reply to: sonstwienix

Here is a minimal example. Selecting Sektch features somehow works in this example, but it does not have the effect that i wish.

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
    
        app.userInterface.activeSelections.clear()
        NewSketch: adsk.fusion.Sketch = app.activeProduct.rootComponent.sketches.add(app.activeProduct.rootComponent.xYConstructionPlane)
        Circle = NewSketch.sketchCurves.sketchCircles.addByCenterRadius(adsk.core.Point3D.create(0, 0, 0), 5)
        SplinePoints = adsk.core.ObjectCollection.create()
        SplinePoints.add(adsk.core.Point3D.create(-5, 0, 0))
        SplinePoints.add(adsk.core.Point3D.create(0, 0, 5))        
        SplinePoints.add(adsk.core.Point3D.create(5, 0, 0))
        SplineArc = NewSketch.sketchCurves.sketchFittedSplines.add(SplinePoints)
        Point = NewSketch.sketchPoints.add(adsk.core.Point3D.create(0, 0, 5))
        #works:
        app.userInterface.activeSelections.add(Circle)        
        app.userInterface.activeSelections.add(SplineArc)
        app.userInterface.activeSelections.clear()

        #Now its interesting:
        app.userInterface.activeSelections.add(Circle)  
        app.executeTextCommand(u'Commands.Start FusionSurfacePatchCommand')
        app.executeTextCommand(u'UI.EnableCommandInput InteriorRailsAndPoints') 
        app.userInterface.activeSelections.add(Point)  #works
        app.executeTextCommand(u'UI.EnableCommandInput InteriorRailsAndPoints')        
        app.userInterface.activeSelections.add(SplineArc)  # has no effect, but can be selected manually
        app.executeTextCommand(u'NuCommands.CommitCmd')

    except:
        print('Failed:\n{}'.format(traceback.format_exc()))

 

 

 

I am creating a circle that is about to be patched. Over the circle i create a spline arc. I also put a vertex right at the top - just for fun.
The interior rails accept and respects the vertex as input. However the spline is just accepted as input, but is not respected when the feature is created.

Patching the circle manually and choosing the spline via mouse user interface as rail, works as expected.

Message 10 of 20
kandennti
in reply to: sonstwienix

@sonstwienix .

 

I was able to confirm that the spline could not be done correctly.

I tried everything I could think of, but I couldn't do it.

 

Since only sketch points work, I tried to use many points.

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()

        app.userInterface.activeSelections.clear()
        NewSketch: adsk.fusion.Sketch = app.activeProduct.rootComponent.sketches.add(
            app.activeProduct.rootComponent.xYConstructionPlane)
        Circle = NewSketch.sketchCurves.sketchCircles.addByCenterRadius(
            adsk.core.Point3D.create(0, 0, 0), 5)
        SplinePoints = adsk.core.ObjectCollection.create()
        SplinePoints.add(adsk.core.Point3D.create(-5, 0, 0))
        SplinePoints.add(adsk.core.Point3D.create(0, 0, 5))
        SplinePoints.add(adsk.core.Point3D.create(5, 0, 0))
        SplineArc = NewSketch.sketchCurves.sketchFittedSplines.add(
            SplinePoints)
        Point = NewSketch.sketchPoints.add(adsk.core.Point3D.create(0, 0, 5))
        # works:
        # app.userInterface.activeSelections.add(Circle)
        # app.userInterface.activeSelections.add(SplineArc)
        # app.userInterface.activeSelections.clear()

        # get points on SplineArc
        points = getPointsOnCurve(SplineArc, 0.01)
        sktPnts = NewSketch.sketchPoints
        NewSketch.arePointsShown = False
        NewSketch.isComputeDeferred = True
        pointsOnCurve = [sktPnts.add(p) for p in points]
        NewSketch.isComputeDeferred = False

        # Now its interesting:
        app.userInterface.activeSelections.add(Circle)
        app.executeTextCommand(u'Commands.Start FusionSurfacePatchCommand')
        app.executeTextCommand(u'UI.EnableCommandInput InteriorRailsAndPoints')
        app.userInterface.activeSelections.add(Point)  # works
        # app.executeTextCommand(u'UI.EnableCommandInput InteriorRailsAndPoints')
        # # has no effect, but can be selected manually
        # app.userInterface.activeSelections.add(SplineArc)
        sels: adsk.core.Selections = app.userInterface.activeSelections
        for p in pointsOnCurve:
            app.executeTextCommand(
                u'UI.EnableCommandInput InteriorRailsAndPoints')
            sels.add(p)

        app.executeTextCommand(u'NuCommands.CommitCmd')

    except:
        print('Failed:\n{}'.format(traceback.format_exc()))


def getPointsOnCurve(
        curve: adsk.fusion.SketchFittedSpline,
        tolerance: float = 0.1) -> list:

    eva: adsk.core.CurveEvaluator3D = curve.geometry.evaluator
    _, startPrm, endPrm = eva.getParameterExtents()
    _, vertexCoordinates = eva.getStrokes(startPrm, endPrm, tolerance)
    return vertexCoordinates

 

Even with text commands, there's no guarantee that I'll get to the goal.

Message 11 of 20
sonstwienix
in reply to: rarora7777

I tried to use points, but it does not work in terms of "shaping the surface". In between the points, the patch bulges out.. it kind of sucks.

 

This whole thing is a result of the fundamentally broken nature of Fusion 360s code base. Autodesk would need a feature freeze for the next 2 years to get the rewrites done that are necessary. It seems that way to little resources are allocated to go through the backlog of bugs. Feature expansion on top of this dumpster fire seems to be the priority. When money speaks and the engineers have to shut up...

The issue that we have right here, is likely a side effect of the broken Patch-dialog. Just try to select multiple rails, you will see that after every selection the rail-selection-button becomes inactive. You need to click, select, click, select..
Alternatively you can click the rail selection button and box-select multiple rails! Then multiple entities will show up as "1 selected" - good job. 
Funny enough, this only works when you edit the patch, not on patch creation.
Crazy. It is completely ridicules.

This inconsistent behavior surely trickles though into the text command and displays its weirdness there. Together with the lack of care for the API completeness for new features gives this situation.
I have everything together to give fusion 360 minimum-surface patches in contrast to the pure curvature interpolation. But i am just not able to complete the job.

The alternative is to use lofts. But lofts are also just broken!!  They do not produce a consistently continuous surface even with continuous rails. The surfaces get split up and what not. It will give me a surface, but it wont give me a good solution.

 

I am so frustrated, sorry guys. It is all so good, but everything is bad at the same time. Needlessly.
Educate the investors and team-leads what "technical debt" means, please.

Message 12 of 20
sonstwienix
in reply to: rarora7777

Another great bug to report: you cannot make a sketch outside of the root component and expect anything to work.
If you want to keep your model clean and create sketches where they belong (activeComponent), then you will get nice error messages when using the text commands for the patch.

In detail: the selection process for the sketch-items (like points and lines) fails.
You can make lines and everything in a sketch in the root component and select them via API. But if the sketch is in a sub-component, you get a type-error that the lines of the sketch are not an entity. So app.userInterface.activeSelection.add(EntityOfSketchThatIsNotInRoot) fails.  WHAT.


I have not found a way to move sketches to components. But i cant even do it manually. It is unclear why. It should not have any dependencies. Not sure if there is a cause or just another bug in the UI?

All those small neglected problems (i am sure i am not the first one to notice) translate into huge real-world problems. Thank you Autodesk, the time and money you saved rushing things, 1000 users have to put in 2-fold. Great way of caring.

Message 13 of 20
kandennti
in reply to: sonstwienix

@sonstwienix .

 

I haven't checked in detail, but I think the cause of the error for elements other than the root component is the proxy described at the end of this link.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-88A4DB43-CFDD-4CFF-B124-7EE67915A07A 

 

The first description in this link explains how to select "Bodies" other than the root component.

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/use-textcommands2/m-p/9937161#M12118 

 

Of course I would like to see the "internal points/rails" feature added to the API.

 

Message 14 of 20
sonstwienix
in reply to: kandennti

Not to mention that app.userInterface.activeSelections.add() should support ObjectColletion as input.

Selecting each item one by one is a performance killer.

 

As to your Link, i sadly have not identified the part that handles selections elements drawn in a non-root sketch. And to be honest, this should never be different from a root-sketch -.-

 

I also had more problems coming up. The sketch i create, 3d-includes the outline-edges of another patch (to replicate its boundary shape). Funny enough the included lines sometimes do not give a closed loop that the patch command recognizes (although it must be closed due to where it is included from). Basically, based on the boundary of another shape/patch/face, i cannot create another patch with certainty.. So many bugs in the details. crazy. This is unfixable for me.

 

Message 15 of 20
MichaelT_123
in reply to: rarora7777

Hi Mr Sonstwienix,
Congrats 😏...you have started quite an interesting discussion.
Let's extract The main topic of the contention!
What is the selected sketch element?
As you know by the autopsy now, there seem to be two types of sketches; those belonging to root components and others defined in lower-level component objects.
What is the component, though?
It is a recipe to build/instantiate objects which in F360 are called occurrences which in turn represent real&physical bodies/geometries of a design.
The root component is by definition only one; thus, the recipe, as well as its sketches, are selfsame!
We can't say the same about lower-level components. They, in the F360’s model paradigm, exist only as virtual definitions/objects. Obviously, it applies also to their sketch elements (recipe’s ingredients) of such a category of components. They are virtual ..., and as such they, do not belong to the physical design constituency. They can be instantiated as many occurrences at once, so the single selection can’t identify them... as you have tried in your code. They are not entities.
I don't think your experience and code error represent the bug on the F360 side; quite contrary, it means only the logical outcome for the implemented F360 model structure.


How to solve your predicament?
Consider trying the following:
• Include in the selection process an occurrence you want to apply your process to
• Get proxy of a sketch referenced
• Or/and get proxy of a sketch entity of interest


This is only the “theoretical" advice, but with the firm will, you can forge it into the hard code 😉.
It might or might not work 90/10!

 

Regards

MichaelT

 

 

 

MichaelT
Message 16 of 20
sonstwienix
in reply to: MichaelT_123

I am sorry, but i have to tell you that you suffer from Stockholm Syndrome. Please seek advise and bill Autodesk for compensation 😄

 

Look, the point is that this code should work, no matter what:

 

 

def selectAllCurves(Sketch: adsk.fusion.Sketch):
    app: adsk.core.Application = adsk.core.Application.get()
    for curve in Sketch.sketchCurves:
        app.userInterface.activeSelections.add(curve)  

 

 

From a software development standpoint: Do you agree or disagree. Answer as if you were under oath.

Also, if you had an employee, who produces an API where this simple code has a hidden problem which systematically shows when you follow good modelling practices, would you or would you NOT continue to pay him?

🙂

I am not deep enough into the API, to really understand what consequences your explanation has to my code.

Could you transform the above snippet to make it work with all sketches?

Message 17 of 20
kandennti
in reply to: sonstwienix

@sonstwienix .

 

In your code, it is not clear what kind of sketch you are passing as a parameter.

 

I made a simple sample.

 

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core
import math

def run(context):
    ui: adsk.core.UserInterface = None
    try:
        app: adsk.core.Application = adsk.core.Application.get()
        app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
        ui = app.userInterface
        sels: adsk.core.Selections = ui.activeSelections

        # create Occurrence & Sketch & SketchCircle
        occ: adsk.fusion.Occurrence = initOccSketch()

        # get Occurrence Sketch - NativeObject
        skt_native: adsk.fusion.Sketch = occ.component.sketches[0]
        trySelectAllCurves(skt_native)
        app.log(f'Sketch Name:{skt_native.name} - Select Count:{sels.count}')

        # get Occurrence Sketch - Proxy
        # https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-ab459896-998a-4139-90a1-ff6c30d1bffd
        # Check the nativeObject Property or assemblyContext Property.
        skt_proxy: adsk.fusion.Sketch = skt_native.createForAssemblyContext(occ)
        trySelectAllCurves(skt_proxy)
        app.log(f'Sketch Name:{skt_proxy.name} - Select Count:{sels.count}')

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def trySelectAllCurves(Sketch: adsk.fusion.Sketch):
    app: adsk.core.Application = adsk.core.Application.get()
    for curve in Sketch.sketchCurves:
        try:
            app.userInterface.activeSelections.add(curve)
        except:
            pass

def initOccSketch() -> adsk.fusion.Occurrence:
    app: adsk.core.Application = adsk.core.Application.get()
    des: adsk.fusion.Design = app.activeProduct
    root: adsk.fusion.Component = des.rootComponent

    occ: adsk.fusion.Occurrence = root.occurrences.addNewComponent(
        adsk.core.Matrix3D.create()
    )

    comp:adsk.fusion.Component = occ.component

    skt: adsk.fusion.Sketch = comp.sketches.add(comp.xYConstructionPlane)

    skt.sketchCurves.sketchCircles.addByCenterRadius(
        adsk.core.Point3D.create(0, 0, 0),
        1.0
    )

    return occ

 

The sample creates an occurrence, creates a sketch, and creates only one sketch circle.

The first time, if you select it as a nativeObject, you will get an error.

The second time, if you use the createForAssemblyContext method to get a proxy, you can select it.

1.png

 

You do not understand the difference.
Don't worry, you're not the only one, many people have trouble with this mechanism.

 

The explanation to understand this is the proxy listed at the end of this link.

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-88A4DB43-CFDD-4CFF-B124-7EE67915A07A 

 

Message 18 of 20
sonstwienix
in reply to: kandennti

Here is the thing:


In your code, it is not clear what kind of sketch you are passing as a parameter.

THERE IS JUST ONE TYPE: adsk.funsion.Sketch. It is completely clear what kind of parameter i am passing and there is not even an alternative 😉

 

I understand the flow of your code but i am unable to adapt it to my purpose. I am very sorry. I will continue to try, but the situation is somewhat complicated.

 

Basically, i need to make a sketch in the currently active component (that may be root or some sub-component) and then select stuff successfully - a truly expert topic i have not mastered yet.


Meaning, the selection process must work with sketches and with sketches that are not sketches but are called sketches and also passed as adsk.fusion.Sketch while not being actual sketches. Especially i must be able to select sketch entities from root-sketches and also sketch entities that are not sketch entities (even if their type says so) that come from a sketch that is not a sketch despite being passed around as adsk.fusion.Sketch object representing a sketch that is not a sketch with entities that are not entities from an object that is not root.

 

In somewhat easier terms, i have no idea how to get the occurance of the activeComponent to pass it to createForAssemblyContext() in the context of the active component being some random component or root.

 

Or maybe even more compact: Is there a way to convert any adsk.fusion.Sketch that to an actual adsk.fusion.Sketch no matter where the original sketch comes from?

 

Edit: this seems to give me a sketch to work with. But i am unsure how generalized it is, since i am only referring to occuranceList.item(0). I do not understand the nuances of that aspect.

occurcanceList  = root.occurrencesByComponent(design.activeComponent)
component: adsk.fusion.Component = root
occ = None
if occurcanceList.count != 0:
    occ = occurcanceList.item(0)#itemByName(design.activeComponent.name)            
    component = occ.component

TargetSketch: adsk.fusion.Sketch = component.sketches.add(root.xYConstructionPlane)
TargetSketch.name = 'MinimumSurfaceSketch'
if occ is not None:
    TargetSketch = TargetSketch.createForAssemblyContext(occ)   

 I can now use the sketch as if it was a sketch. Basically a one-liner as it should have been. *facepalm*

 

adsk.fusion.ThankYou.

Message 19 of 20
MichaelT_123
in reply to: sonstwienix

Hi Mr Sonstwienix,
Demanding a free of charge solution to "so seemingly complicated?" problem from a person with Stockholm Syndrome while boasting of their own ability to manage paying people for software development and at the same time admitting to limited knowledge of the basics of software developed over the years by more than average minds ... that's too much for me ... I'm out of here!

 

All The Best...anyway
MichaelT

 

P.S.
Kandennti-San, your code example couldn't be more clearer. It is of a diamond quality!

MichaelT
Message 20 of 20
sonstwienix
in reply to: MichaelT_123

Dude, it was a joke about how you reasoned about the unreasonable situation, nothing personal nor serious. Just because there are reasons, doesnt mean they are good (but i understand them and it helped).

The point was, that an object of adsk.fusion.Sketch should behave like adsk.fusion.Sketch and not be different under the hood. If it is different, then give it a different type and provide a way to get the underlying Sketch-object. Make it the one-line that it should be and be done.

 

Everything is friendly here. Sorry for the misunderstanding.

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