How to determine a directionPoint to offset an arbitrary sketch loop inwards?

How to determine a directionPoint to offset an arbitrary sketch loop inwards?

Anonymous
Not applicable
1,546 Views
9 Replies
Message 1 of 10

How to determine a directionPoint to offset an arbitrary sketch loop inwards?

Anonymous
Not applicable

Hi,

 

Am working on a program to offset outside loops in a sketch outward and offset inside loops inwards. I can imagine being able to get the max/min x & y values for the sketch curves that make up the loop and use this info to pick a directionPoint outside the loop pretty easily.

 

I'm confused as to how best find a directionPoint inside an arbitrary loop? Any help much appreciated!

 

Thx,

 

Rod. 

 

0 Likes
1,547 Views
9 Replies
Replies (9)
Message 2 of 10

Anonymous
Not applicable

Any takers? I'm still stuck on finding a point inside an arbitrary sketch profile, or loop.

 

Thx,

 

Rod.

0 Likes
Message 3 of 10

ShirleyShi
Alumni
Alumni
Hi Rod, There is no simple way to do it now. A workaround might be extruding the profile into a body, then find a point inside the top face of the body by modifying the code in the following posts: http://forums.autodesk.com/t5/api-and-scripts/extrude-some-profiles/td-p/6404721 http://modthemachine.typepad.com/my_weblog/2016/05/is-point-on-face.html The projection of that point onto the sketch plane will be the point you want. You might also want to request an API to do it in IdeaStation: http://forums.autodesk.com/t5/fusion-360-ideastation-request-a/idb-p/125

Shirley

Developer for Fusion Electronics

Autodesk, Inc.

Message 4 of 10

Anonymous
Not applicable

Hi Shirley,

 

Thanks for the response, it's helps to know I wasn't missing an easy way to do this. That's is quite the workaround for what should be a simple task, I'll try the workaround and I'll submit an idea station request for sure!

 

Thanks again,

 

Rod.

0 Likes
Message 5 of 10

Anonymous
Not applicable
0 Likes
Message 6 of 10

liujac
Alumni
Alumni

You can get the centroid of a profile by querying AreaProperties. The following script demo how to get the centroid of a profile.

 

 

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

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

        product = app.activeProduct
        design = adsk.fusion.Design.cast(product)

        # Get the root component of the active design
        rootComp = design.rootComponent
        
        # Get one profile
        sketches = rootComp.sketches
        sketch = sketches[0]
        prof = sketch.profiles[0]
        
        # Get area properties from a profile
        areaProps = prof.areaProperties(adsk.fusion.CalculationAccuracy.MediumCalculationAccuracy)
        
        # Get centroid
        centroid = areaProps.centroid
        
        ui.messageBox('{}, {}, {}'.format(centroid.x, centroid.y, centroid.z))

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

 

 

Thanks,

Jack

Message 7 of 10

Anonymous
Not applicable

Hi Jack,

 

I tried your suggestion and it worked well for a profile where the centroid is inside the profile but fails for profiles where the centroid is outside the profile, see the attached profile. Looks like we still need AreaPoperties methods to get points inside and outside a profile?

 

Thanks,

 

Rod.

 

Screen Shot 2016-07-12 at 6.06.14 PM.png

0 Likes
Message 8 of 10

liujac
Alumni
Alumni

Right. The geometric centroid of a convex object always lies in the object. A non-convex object might have a centroid that is outside the figure itself. It's easy to find a point outside the profile as your mentioned. We still need to add method to get a point inside a profile.

 

Thanks,

Jack

Message 9 of 10

ekinsb
Alumni
Alumni

This is a little bit elaborate but in my testing so far, it works in all cases.  I've attached the script too.

 

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

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

        # Get the active sketch.
        sk = adsk.fusion.Sketch.cast(app.activeEditObject)
        
        pnt = None
        if sk:
            # Get all of the curves in the sketch and add them to a collection.
            # This is the same set of curves that will be input to the offset method.
            skCurves = adsk.core.ObjectCollection.create()
            skCurve = adsk.fusion.SketchCurve.cast(None)
            for skCurve in sk.sketchCurves:
                skCurves.add(skCurve)

            # Get a point in the "inside" of the curves.                
            pnt = getPointInClosedCurves(skCurves)
        
        if pnt:
            # Create a sketch point so the result is visible.
            sk.sketchPoints.add(pnt)
            ui.messageBox('Sketch point created in the area of the sketch curves')
        else:
            ui.messageBox('No point was found.')

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


# Function that calculates a point that is "inside" the set of input curves.
def getPointInClosedCurves(skCurves):
    try:
        # Build up list of connected points.
        curve = adsk.fusion.SketchCurve.cast(None)
        pointSets = []
        boundBox = None
        for curve in skCurves:
            if not boundBox:
                boundBox = curve.boundingBox
            else:
                boundBox.combine(curve.boundingBox)            
                
            if isinstance(curve, adsk.fusion.SketchLine):
                skLine = adsk.fusion.SketchLine.cast(curve)
                pointSets.append((skLine.startSketchPoint.geometry, skLine.endSketchPoint.geometry))
            else:
                curveEval = adsk.core.CurveEvaluator3D.cast(curve.geometry.evaluator)
                (retVal, startParam, endParam) = curveEval.getParameterExtents()
                
                (retVal, strokePoints) = curveEval.getStrokes(startParam, endParam, 0.1)
                pointSets.append(strokePoints)
            
        # Create two points that define a line that crosses the entire range.  They're moved
        # to be outside the bounding box so there's not problem with coincident points.
        lineVec = boundBox.minPoint.vectorTo(boundBox.maxPoint)
        lineVec.normalize()
        maxPoint = boundBox.maxPoint.copy()
        maxPoint.translateBy(lineVec)
        lineVec.scaleBy(-1)
        minPoint = boundBox.minPoint.copy()
        minPoint.translateBy(lineVec)
    
        # Iterate through all of the lines and get the intersection between the lines
        # and the long crossing line.
        intPointList = []
        for pointSet in pointSets:
            pnt1 = None
            pnt2 = None
            for point in pointSet:
                if not pnt2:
                    pnt2 = point
                else:
                    pnt1 = pnt2
                    pnt2 = point
    
                    intCoords = getLineIntersection(minPoint.x, minPoint.y, maxPoint.x, maxPoint.y, pnt1.x, pnt1.y, pnt2.x, pnt2.y)
                    if intCoords:
                        intPoint = adsk.core.Point3D.create(intCoords[0], intCoords[1], 0)
                        intPointList.append((intPoint, intPoint.distanceTo(minPoint)))
        
        # Make sure at last two intersection points were found.
        if len(intPointList) >= 2:
            # Sort the points by the distance from the min point.
            sortedPoints = sorted(intPointList, key=getKey) 
        
            # Get the first two points and compute a mid point.  That's a point in the area.                
            pnt1 = sortedPoints[0]
            pnt2 = sortedPoints[1]
            midPoint = adsk.core.Point3D.create((pnt1[0].x + pnt2[0].x)/2, (pnt1[0].y + pnt2[0].y)/2, 0)
            return midPoint
        else:
            return False
    except:
        ui = adsk.core.Application.get().userInterface
        ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
    
                                
# Function that's used to get the value to sort with.
def getKey(item):
    return item[1]

                
# Calculate the intersection between two line segments.  This is based on the JS
# sample in the second answer at:
# http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
def getLineIntersection(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y):
    s1_x = p1_x - p0_x
    s1_y = p1_y - p0_y
    s2_x = p3_x - p2_x
    s2_y = p3_y - p2_y

    s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y)
    t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y)

    if (s >= 0 and s <= 1 and t >= 0 and t <= 1):
        # Collision detected
        i_x = p0_x + (t * s1_x)
        i_y = p0_y + (t * s1_y)
        return (i_x, i_y)

    return False  # No collision

 


Brian Ekins
Inventor and Fusion 360 API Expert
Mod the Machine blog
Message 10 of 10

Anonymous
Not applicable

Thanks Brian!

 

I've been away from this for a while, will check out your workaround and report back.

 

Rod.

0 Likes