API find the CW/CCW orientation of a selected sketch profile to use for an "always inside" offset

API find the CW/CCW orientation of a selected sketch profile to use for an "always inside" offset

keonfrey
Explorer Explorer
251 Views
2 Replies
Message 1 of 3

API find the CW/CCW orientation of a selected sketch profile to use for an "always inside" offset

keonfrey
Explorer
Explorer

I'm trying to find a consistent and accurate API method of finding the orientation of profiles similar to the profiles visible in the attached photo. 

 

I read that closed single curve profiles such as circles and ellipses always are CCW but I'm struggling to find a method that can return the correct direction of  other multi-curve sketch profiles for me to use it with the correct negative or positive offset value to always insure the result is inside the selected profile?

 

Any ideas on how I might achieve that? 

0 Likes
252 Views
2 Replies
Replies (2)
Message 2 of 3

SaeedHamza
Advisor
Advisor

Offseting a closed sketch in Fusion360 is always negative to the inside and positive to the outside, and that applies to API as well. Looking at the sketches you've attached, they are all closed sketches, so you should always have a consistant offset to the inside if the value is negative, and to the outside if it's positive.

The real challange is offseting an openned sketch, like a polyline for example, in that case you'll always need to provide a direction point for Fusion API to understand which dircetion you're offseting to when the value is positive, and if you think about it, it is a MUST, because if you create a single line manually in Fusion sketch workspace, you'll see that it's hard to predict which side is positive and which is negative, meaning in API you'll have to come up with a CONSISTANT logic that applies to the case you're dealing with.

I've been using Fusion 360 API for over 5 years, and each case is a unique one when it comes to the direction of offset, as I always define a criteria for the direction point based on the project at hand.

Saeed Hamza
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature

0 Likes
Message 3 of 3

keonfrey
Explorer
Explorer

The offset direction is not always inside on a negative offset value in my experience as seen in this screen grab. The script I used is below and has created an inside offset for circle and an outside offset for just a regular 2 point rectangle. The arrows in the constraint indicate which way the offset was created. The sketch profiles have been created in advance manually as will be the case in my application, if that makes any difference. In further testing it seems to flip the direction of offset if the 2 point rectangle is drawn from bottom left to top right vs top left to bottom right etc. That indicates that the rectangle orientation flips (reversed normal) when drawn from different corners. Maybe you have always drawn the profiles with the same inherent orientation but I recommend you have a play since it is the same result if done manually.

 

keonfrey_0-1741809447722.png

import adsk.core, adsk.fusion, adsk.cam, traceback

def isGroupClosed(curve_list, tol=1e-6😞
    if len(curve_list) == 1:
        geom = curve_list[0].geometry
        if isinstance(geom, adsk.core.Circle3D😞
            return True
 
 
    endpoints = []
    for curve in curve_list:
        try:
            geom = curve.geometry
            sp = geom.startPoint
            ep = geom.endPoint
            pt1 = (round(sp.x, 6), round(sp.y, 6), round(sp.z, 6))
            pt2 = (round(ep.x, 6), round(ep.y, 6), round(ep.z, 6))
            endpoints.append(pt1)
            endpoints.append(pt2)
        except:
            return False

    if not endpoints:
        return False
 
    freq = {}
    for pt in endpoints:
        freq[pt] = freq.get(pt, 0) + 1
 
    for count in freq.values():
        if count % 2 != 0:
            return False

    return True

def run(context😞
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        design = adsk.fusion.Design.cast(app.activeProduct)
        if not design:
            ui.messageBox('No active Fusion design.', 'Offset Connected Curves')
            return
 
        offset_value = -1.0

        all_comps = design.allComponents

        for comp in all_comps:
            for sketch in comp.sketches:
                # Access the constraints object: try sketchConstraints first, else geometricConstraints.
                constraints = getattr(sketch, 'sketchConstraints', None)
                if not constraints:
                    constraints = sketch.geometricConstraints
 
                allCurves = []
                curves = sketch.sketchCurves
                for i in range(curves.count😞
                    try:
                        curve = curves.item(i)
                        allCurves.append(curve)
                    except:
                        continue
 
                visited = set()
                groups = []
                for curve in allCurves:
                    if id(curve) in visited:
                        continue
                    try:
                        connectedCurves = sketch.findConnectedCurves(curve)
                    except:
                        continue

                    group = []
                    for j in range(connectedCurves.count😞
                        try:
                            c = connectedCurves.item(j)
                            group.append(c)
                            visited.add(id(c))
                        except:
                            continue
                    groups.append(group)
 
                for group in groups:
                    if not isGroupClosed(group😞
                        continue 

                    try:
                        curve_list = group
                        val_input = adsk.core.ValueInput.createByReal(offset_value)
                        offInput = constraints.createOffsetInput(curve_list, val_input)
                        offInput.isTopologyMatched = False
                        constraints.addOffset2(offInput)
                    except:
                        continue

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