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