やっとの思いで進めました。
スケッチプロファイルを選択する事で、自由に動き回れる円の最大直径を表示するスクリプトです。
# Fusion360API Python script
import traceback
import adsk
import adsk.core as core
import adsk.fusion as fusion
import time
def run(context):
ui: core.UserInterface = None
try:
app: core.Application = core.Application.get()
ui = app.userInterface
des: fusion.Design = app.activeProduct
msg: str = 'プロファイルを選択/ESCキーで中止'
selFilter: str = 'Profiles'
sel: core.Selection = select_ent(msg, selFilter)
if not sel:
return
t = time.time()
unitsMgr: core.UnitsManager = des.unitsManager
diameter = unitsMgr.convert(
get_max_diameter(sel.entity, 0.001),
unitsMgr.internalUnits,
unitsMgr.defaultLengthUnits,
)
print(f"{time.time() - t} s")
ui.messageBox(f"φ{diameter}です!", "最大直径")
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def get_max_diameter(
prof: fusion.Profile,
tolerance: float = 0.01,
) -> float:
'''
プロファイル内を加工するための最大直径の取得
'''
skt: fusion.Sketch = prof.parentSketch
loop: fusion.BRepLoop = None
outer = [loop for loop in prof.profileLoops if loop.isOuter][0]
edges = [e.sketchEntity for e in outer.profileCurves]
directionPnt: core.Point3D = get_direction_point(edges)
if not directionPnt:
print("direction point not found")
return -1
outerEdges = core.ObjectCollection.createWithArray(edges)
keepProfCount = skt.profiles.count + 1
app: core.Application = core.Application.get()
endValue = get_first_value(prof)
splitCount = 10
startValue = 0
step = 1
stateValue = 0
while step > tolerance:
step = (endValue - startValue) / splitCount
for idx in range(splitCount):
stateValue = startValue + (step * idx)
try:
app.executeTextCommand(u"Transaction.Start kantoku_offset")
skt.offset(outerEdges, directionPnt, stateValue)
resProfCount = skt.profiles.count
if resProfCount > keepProfCount:
endValue = stateValue
startValue = startValue if idx == 0 else stateValue - step
break
except:
endValue = stateValue
startValue = startValue if idx == 0 else stateValue - step
break
finally:
app.executeTextCommand(u"Transaction.Abort")
return stateValue * 2
def get_first_value(
prof: fusion.Profile,
) -> float:
'''
バウンダリングボックスから最初のオフセット目標値を取得
'''
bBox: core.BoundingBox3D = prof.boundingBox
maxPnt: core.Point3D = bBox.maxPoint
minPnt: core.Point3D = bBox.minPoint
return max(
[
abs(maxPnt.x - minPnt.x),
abs(maxPnt.y - minPnt.y),
abs(maxPnt.z - minPnt.z),
]
)
def get_direction_point(
edges: list[fusion.BRepEdge],
isInner: bool = True,
) -> core.Point3D:
'''
オフセット用参照ポイント
edgesは閉じている状態の必要あり
'''
crvs = [e.geometry for e in edges]
tempMgr: fusion.TemporaryBRepManager = fusion.TemporaryBRepManager.get()
wireBody, _ = tempMgr.createWireFromCurves(crvs)
faceBody: fusion.BRepBody = tempMgr.createFaceFromPlanarWires([wireBody])
crv: core.Curve3D = crvs[0]
eva: core.CurveEvaluator3D = crv.evaluator
stPnt: core.Point3D = None
_, stPnt, _ = eva.getEndPoints()
_, prm = eva.getParameterAtPoint(stPnt)
vec: core.Vector3D = None
_, vec, _ = eva.getCurvature(prm)
vecs = [vec.copy() for _ in range(2)]
[vec.scaleBy(v) for vec, v in zip(vecs, [0.001, -0.001])]
pnts = [stPnt.copy() for _ in range(2)]
[p.translateBy(vec) for vec, p in zip(vecs, pnts)]
if isInner:
expectedValue = fusion.PointContainment.PointOnPointContainment
else:
expectedValue = fusion.PointContainment.PointOutsidePointContainment
for pnt in pnts:
res: fusion.PointContainment = faceBody.pointContainment(pnt)
if res == expectedValue:
return pnt
return None
def select_ent(
msg: str,
filter: str
) -> core.Selection:
try:
app: core.Application = core.Application.get()
ui: core.UserInterface = app.userInterface
sel = ui.selectEntity(msg, filter)
return sel
except:
return None
※もし試される方は、無限ループに陥る可能性が有る為、他のドキュメントは保存し
閉じてから実行して下さい。
最初に添付したf3dファイルで試すとこんな結果です。

最短距離が10.442mmなので、まあまあの精度です。
そもそも、そんなにシビアな直径の工具は特注するしかないので、
"じゃ φ10で加工するか"
となるはずです。
オフセットだけで判断している為、現実的には凹角があった場合は適切か?
凹R以下の直径である必要ではないか?等、これだけでは判断しきれない
気もしてます。難しい。