Implementing a feature similar to Coil

Implementing a feature similar to Coil

adonno
Enthusiast Enthusiast
1,237 Views
8 Replies
Message 1 of 9

Implementing a feature similar to Coil

adonno
Enthusiast
Enthusiast

I have a unique requirement in an automated feature that needs to start the same way the Coil feature starts - i.e. select a plane or planar face, draw a circle and then pop a dialog to get more parameters. The problem is I cannot figure out how to "start" the process with the "sketch plane selection" piece. I can have a selector that selects a plane\planar face and then use that to create a new sketch (on which I believe I know how to get the user to "create" the circle) but this is subtly different to what Coil does. Coil starts the same way Create Sketch starts. How do you start a new sketch "interactively" from the API?

Said differently how do you make this segment from the CircleByCenterRadius_Sample into something that is "user driven" as opposed to hardcoded to the xy plane?

 

        # Create a new sketch on the xy plane.
        sketches = rootComp.sketches
        xyPlane = rootComp.xYConstructionPlane
        sketch = sketches.add(xyPlane)

Yes I know I could add a selectEntity in my dialog but I really want to simulate what the Coil feature does. Pointers to code samples would be greatly appreciated.

 

Thanks

Tony

0 Likes
Accepted solutions (1)
1,238 Views
8 Replies
Replies (8)
Message 2 of 9

kandennti
Mentor
Mentor

Hi @adonno .

 

If you want to do it from the plane selection, you could do it here.

    app.executeTextCommand(u'Commands.Start PrimitiveCoil')

 

If you want to use a circle from a sketch that has already been created, you can do this to display the command dialog.

        ui.activeSelections.add(sktCircle)
        app.executeTextCommand(u'Commands.Start Coil')

 

This is useful because the ID of the command actually called is displayed when you start this add-in and operate it with the GUI.

https://github.com/kantoku-code/Fusion360_Small_Tools_for_Developers/tree/master/CommandLogger 

0 Likes
Message 3 of 9

adonno
Enthusiast
Enthusiast

Interesting but won't that simply start the actual Coil feature process? I want to do the same as Coil to start with but for the rest of the operation, after the sketch and circle have been created, I want to do something completely different i.e. the Coil feature dialog should not appear but instead my own dialog with completely different parameters should take control.

0 Likes
Message 4 of 9

kandennti
Mentor
Mentor

With my current knowledge, I can only understand this kind of operation.

 

#Fusion360API Python script
import adsk.core, adsk.fusion, traceback
import math

_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)

def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        # Coil parameters
        CoilDiameter = 10
        CoilRevolutions = 4
        CoilHeight = 15
        CoilTaperAngleDeg = 20 # degree
        SectionSize = 1

        # wait CommandIDs
        waitLst = ('PrimitiveCoil','CircleCenterRadius')

        # call Coil command
        _app.executeTextCommand(u'Commands.Start PrimitiveCoil')

        # wait
        while True:
            actCmd = _ui.activeCommand
            adsk.doEvents()
            if actCmd in waitLst:
                continue
            break

        # Cancel check
        if _ui.activeCommand != 'Coil':
            return

        # setting parameters
        cmds = [
            u'Commands.SetDouble SectionSize {}'.format(SectionSize),
            # u'Commands.SetDouble CoilDiameter {}'.format(CoilDiameter),
            u'Commands.SetDouble CoilRevolutions {}'.format(CoilRevolutions),
            u'Commands.SetDouble CoilHeight {}'.format(CoilHeight),
            u'Commands.SetDouble CoilTaperAngle {}'.format(math.radians(CoilTaperAngleDeg))
        ]

        [_app.executeTextCommand(cmd) for cmd in cmds]

        # command commit
        _app.executeTextCommand(u'NuCommands.CommitCmd')

    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
0 Likes
Message 5 of 9

adonno
Enthusiast
Enthusiast

I don't think I'm explaining myself well enough. I appreciate the help but I am not trying to automate the coil command. In fact, I do not want a coil at all. What I need to create is a 3D spline that includes a specific mathematical equation that happens to need a circle on a sketch as a reference to start. That is the same as how a coil starts - it asks you to select a plane or planar face, then it asks you to draw a circle on that sketch (and then it prompts you for details of the coil in the dialog). I want the part in italics outside the parenthesis but then I need to display my own dialog that will prompt for things like the equation the spline should follow, how many segments to divide the circle into and other things pertinent to the object I want to create.

0 Likes
Message 6 of 9

adonno
Enthusiast
Enthusiast
Accepted solution

After a lot of fiddling the hints from @kandennti helped me get the solution to work. This is what I came up with. My approach to finding the latest circle that was drawn seems very clumsy but I could not see another way to get the latest item that was drawn. If anybody can improve on that please let me know.

 

 

def get_new_curves(start, end):
    """
    Find a list of all the new elements that were added between the collections start and end
    :param start: list
    :param end: list
    :return result: a list of differences between start and end
    """
    result = []
    for i in end:
        if i not in start:
            result.append(i)
    return result


def get_circle_profile():
    """
    Sketch, select and return a circle profile
    :return profile: a single selected circle profile
    """
    global root_component, ui

    # Assume no profile is selected
    profile = None

    # Get the starting set of sketch curves
    start_curves = [curve for sketch in root_component.sketches for curve in sketch.sketchCurves if
                    isinstance(curve, adsk.fusion.SketchCircle)]

    # Call CircleCenterRadius command
    app.executeTextCommand(u'Commands.Start CircleCenterRadius')

    # Wait for the user to finish drawing a circle
    while True:
        adsk.doEvents()
        # Check to see if a circle has been added
        end_curves = [curve for sketch in root_component.sketches for curve in sketch.sketchCurves if
                      isinstance(curve, adsk.fusion.SketchCircle)]
        circle_added = get_new_curves(start_curves, end_curves)
        if len(circle_added) != 1:
            continue
        break

    # Get the ending set of sketch curves and extract the SketchCircle that was added
    end_curves = [curve for sketch in root_component.sketches for curve in sketch.sketchCurves if
                  isinstance(curve, adsk.fusion.SketchCircle)]
    circle_added = get_new_curves(start_curves, end_curves)

    # Check that we got 1 and one 1 circle and selected it
    if len(circle_added) == 1:
        profile = circle_added[0]
        ui.activeSelections.add(profile)
    return profile

 

0 Likes
Message 7 of 9

kandennti
Mentor
Mentor

When you prompt the user for a circle drawing operation.

・Do you assume it will be used while the user is in the sketch?

・Do you want to use it before entering the sketch?

・Do you want it to work with both?

0 Likes
Message 8 of 9

adonno
Enthusiast
Enthusiast

I expect the circle to be used while in the sketch. Basically I am going to take the circle as "base" and then build a spline in 3D space from there, where the center and radius of the circle is used to determine the starting point of the spline.

0 Likes
Message 9 of 9

kandennti
Mentor
Mentor

@adonno .

 

I could not make it cleanly.
If you are using an add-ins, you may want to use events.

 

I tried using entityToken, which is a string, so you can easily get the added circles with the 'set' diff.

#Fusion360API Python script
import adsk.core, adsk.fusion, traceback

_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)

_targetCommandID = 'CircleCenterRadius'

# Get the entityToken from the list.
def getTokenList(lst) -> list:
    return [c.entityToken for c in lst]


# Get all entityTokens of sketchCircle.
# {Sketch.entityToken : List(SketchCircles.entityToken, ...)}
def getAllSketchCirclesTokenDict() -> dict:
    des :adsk.fusion.Design = adsk.core.Application.get().activeProduct

    circleDict = {}
    for comp in des.allComponents:
        for skt in comp.sketches:
            tokens = getTokenList(skt.sketchCurves.sketchCircles)
            circleDict[skt.entityToken] = tokens

    return circleDict


def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface
        des :adsk.fusion.Design = _app.activeProduct

        # Before execution, get all entityTokens of sketchCircle as a dictionary.
        circleDict = getAllSketchCirclesTokenDict() 

        # exec textcommands
        _app.executeTextCommand(u'Commands.Start {}'.format(_targetCommandID))

        # Wait for command to finish
        skt = adsk.fusion.Sketch.cast(None)
        while True:
            adsk.doEvents()

            actObj = adsk.fusion.Sketch.cast(des.activeEditObject)
            if actObj:
                skt = des.activeEditObject

            if _ui.activeCommand != _targetCommandID:
                break
        
        if not skt:
            # Cancel
            return

        # Get the difference before and after the execution.
        sktToken = skt.entityToken
        if sktToken in circleDict:
            # Existing sketch
            beforeTokens = circleDict[sktToken]
            afterTokens = getTokenList(skt.sketchCurves.sketchCircles)
            addTokens = set(afterTokens) - set(beforeTokens)
        else:
            # new sketch
            addTokens = getTokenList(skt.sketchCurves.sketchCircles)

        # Obtaining an object from an entityToken
        addCircles = []
        for token in addTokens:
            addCircles.extend(des.findEntityByToken(token))

        # Select the Circles that has been added.
        sels = _ui.activeSelections
        sels.clear()
        [sels.add(c) for c in addCircles]

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