C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

同一半径限定で球体同士の交差を作る

同一半径限定で、表示された球体同士の交差を作ります。
・・・同一半径のみ欲しいので、同一半径のみなんです。

#Fusion360API Python script
#Author-kantoku
#Description-表示された球体を見つけ、交差作成(同一半径のみ)

import adsk.core, adsk.fusion, traceback
import math, itertools

_tolerance = 0

def run(context):
    ui = None
    try:
        app  :adsk.core.Application = adsk.core.Application.get()
        ui   :adsk.core.UserInterface = app.userInterface
        des  :adsk.fusion.Design = app.activeProduct

        global _tolerance
        um = des.unitsManager
        um.defaultLengthUnits
        _tolerance = um.convert(0.01, um.defaultLengthUnits, um.internalUnits)

        # 拡張
        adsk.fusion.BRepBody.isSphere = isSphere
        adsk.fusion.BRepBody.tryIntersect = tryIntersect
        adsk.core.Point3D.midPoint = midPoint3D

        adsk.core.Point3D.drawSkt = drawSketchPoint
        adsk.core.Circle3D.drawSkt = drawSketchCircle

        # 表示された球体を取得
        showBodies = getShowBody(des)
        spheres = [bd for bd in showBodies if bd.isSphere()]

        # 交差を取得
        ints = []
        for idx, sp1 in enumerate(spheres):
            for sp2 in spheres[idx + 1:]:
                ints.append(sp1.tryIntersect(sp2))
        if len(ints) < 1:
            msg  = '交差はありません!'
            ui.messageBox(msg)
            return
        
        # 可視化
        skt = initRootSketch()
        skt.arePointsShown = False
        skt.isComputeDeferred = True
        [i[0].drawSkt(skt) for i in  ints if len(i) > 0]
        skt.isComputeDeferred = False
        
        # おしまい
        app.activeViewport.refresh()
        ui.messageBox('Done')

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

def drawSketchCircle(
    self :adsk.core.ObjectCollection,
    skt :adsk.fusion.Sketch):

    skt.sketchCurves.sketchFittedSplines.addByNurbsCurve(self.asNurbsCurve)

def drawSketchPoint(
    self :adsk.core.Point3D,
    skt :adsk.fusion.Sketch):

    skt.sketchPoints.add(self)

def initRootSketch() -> adsk.fusion.Sketch:
    app  :adsk.core.Application = adsk.core.Application.get()
    des  :adsk.fusion.Design = app.activeProduct
    root :adsk.fusion.Component = des.rootComponent

    return root.sketches.add(root.xYConstructionPlane)

def getShowBody(
    des :adsk.fusion.Design):

    return [bBody
        for comp in des.allComponents if comp.isBodiesFolderLightBulbOn
        for bBody in comp.bRepBodies if bBody.isLightBulbOn & bBody.isVisible]

def isSphere(
    self :adsk.fusion.BRepBody) -> bool:

    # すべて球面か?
    sr = adsk.core.SurfaceTypes.SphereSurfaceType
    if len([fc for fc in self.faces if fc.geometry.surfaceType != sr]) > 0:
        return False

    # 半径は一致しているか?
    if len(set([round(fc.geometry.radius, 4) for fc in self.faces])) > 1:
        return False

    # 面積は正しいか?
    r = self.faces.item(0).geometry.radius
    if abs(4 * math.pi * r * r - self.area) > 0.001:
        return False

    return True

def tryIntersect(
    self :adsk.fusion.BRepBody,
    tgt :adsk.fusion.BRepBody):

    tgtSurfs = tgt.faces
    spSurfs = self.faces

    app  :adsk.core.Application = adsk.core.Application.get()

    ints = []
    for tgtSurf in tgtSurfs:
        tgtGeo = adsk.core.Sphere.cast(tgtSurf.geometry)
        tgtOri :adsk.core.Point3D = tgtGeo.origin
        tgtRad = tgtGeo.radius

        for spSurf in spSurfs:
            spGeo = adsk.core.Sphere.cast(spSurf.geometry)
            if abs(spGeo.radius - tgtRad) > _tolerance:
                return None
            
            spOri = spGeo.origin
            minLength = tgtOri.distanceTo(spOri)
            if minLength > tgtRad * 2:
                # 干渉なし
                continue
            
            if minLength < _tolerance:
                # 同一
                continue

            if abs(minLength - tgtRad * 2) < _tolerance:
                # 接触
                ints.append(spOri.midPoint(tgtOri))
            else:
                # 干渉
                vec = spOri.asVector()
                vec.subtract(tgtOri.asVector())
                ori = spOri.midPoint(tgtOri)
                pln = adsk.core.Plane.create(ori, vec)

                interLst = pln.intersectWithSurface(spGeo)
                [ints.append(c) for c in interLst] 

    return ints

def midPoint3D(
    self :adsk.core.Point3D,
    pnt :adsk.core.Point3D) -> adsk.core.Point3D:

    p = self.copy()
    p.setWithArray([(x + y) * 0.5 for (x, y) in zip(self.asArray(), pnt.asArray())])
    return p

イマイチな部分が多々有り。

こんなデータで実行すると
f:id:kandennti:20191224145435p:plain

こんな感じで
f:id:kandennti:20191224145528p:plain