こちらの続きです。
板状に分割したい3 - C#ATIA
どうもイマイチの様で・・・、完璧は難しいのですが、もう少し
打率を上げたいです。
データがあれば試せるのですが、こちらでそれっぽいものを
作りました。
球(ソリッド) → メッシュ化 → BRep化(ソリッド)→ シェル実行
で、ローポリゴンな形状のこんな感じです。
この形状に対して、少し改良を加えたスクリプトを実行します。
# Fusion360API Python script # ver0.0.3 import traceback import adsk.fusion import adsk.core DEBUG = False def run(context): ui = adsk.core.UserInterface.cast(None) try: app: adsk.core.Application = adsk.core.Application.get() ui = app.userInterface msg: str = 'ボディを選択してください' selFilter: str = 'Bodies' sel: adsk.core.Selection = selectEnt(msg, selFilter) if not sel: return targetBody: adsk.fusion.BRepBody = sel.entity execDivided(targetBody) except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) def execDivided( body: adsk.fusion.BRepBody) -> list: app: adsk.core.Application = adsk.core.Application.get() comp: adsk.fusion.Component = body.parentComponent ngFaces = [] notFind = [] # ****** def execLoft(pairFaces: list) -> list: bodies = [] for faces in pairFaces: loftFeatIpt: adsk.fusion.LoftFeatureInput = loftFeats.createInput( # adsk.fusion.FeatureOperations.NewBodyFeatureOperation adsk.fusion.FeatureOperations.NewComponentFeatureOperation ) loftFeatIpt.isSolid = True loftFeatIpt.isClosed = False loftSections: adsk.fusion.LoftSections = loftFeatIpt.loftSections [loftSections.add(f) for f in faces] app.executeTextCommand(u'Transaction.Start loft') try: # エラーが出るので苦肉の策・・・ loftFeat: adsk.fusion.LoftFeature = loftFeats.add(loftFeatIpt) bodies.append(loftFeat.bodies[0]) app.executeTextCommand(u'Transaction.Commit') except: ngFaces.extend(faces) adsk.core.Application.get().log( 'Failed:\n{}'.format(traceback.format_exc()) ) app.executeTextCommand(u'Transaction.Abort') return bodies def getVector( geo: adsk.core.Surface, point: adsk.core.Point3D) -> adsk.core.Vector3D: if hasattr(geo, 'normal'): return geo.normal eva: adsk.core.SurfaceEvaluator = geo.evaluator _, vec = eva.getNormalAtPoint(point) return vec def getPairFaces(shell: adsk.fusion.BRepShell): registeredList = [] # 組み合わせ済み pairFaces = [] hitPoints: adsk.core.ObjectCollection = adsk.core.ObjectCollection.create() face: adsk.fusion.BRepFace for face in shell.faces: # 既に組み合わせ済みか? # if face.entityToken in registeredList: # continue pnt: adsk.core.Point3D = face.pointOnFace geo: adsk.core.Plane = face.geometry vec: adsk.core.Vector3D = getVector(geo, pnt) revVec: adsk.core.Vector3D = vec.copy() revVec.scaleBy(-1) vecs = [ revVec, vec, ] # 候補の面取得 tempFaces = [] for vec in vecs: hitPoints.clear() res = comp.findBRepUsingRay( pnt, vec, adsk.fusion.BRepEntityTypes.BRepFaceEntityType, -1.0, True, hitPoints ) if res.count < 1: continue # 面と距離 - 向きが平行出ないものは除外 tempFaces.extend( [(f, pnt.distanceTo(p)) for f, p in zip(res, hitPoints) if vec.isParallelTo(getVector(f.geometry, p))] ) # 距離0以下は除外 tempFaces = [(f, l) for f, l in tempFaces if l > 0.001] if len(tempFaces) < 1: notFind.append(face) continue # 近いものが組み合わせ相手 同じボディか?ってチェック入れるべき? nearFace: adsk.fusion.BRepFace = min(tempFaces, key=lambda x: x[1])[0] # if nearFace.entityToken in registeredList: # continue pairFaces.append( ( face, nearFace ) ) # 登録 registeredList.extend( [ face.entityToken, nearFace.entityToken ] ) return pairFaces def isAllPlanarFace(body: adsk.fusion.BRepBody) -> bool: planeType = adsk.core.Plane.classType() if all([f.geometry.classType() == planeType for f in body.faces]): return True return False # ******* # 全て平面かチェック if not isAllPlanarFace(body): return # 完全な中空形状チェック # if not body.shells.count == 2: # return # 外側 これ要らないかも・・・ outer: adsk.fusion.BRepShell = max(body.shells, key=lambda x: x.area) # 外内面の組み合わせ pairFaces = getPairFaces(outer) # LoftFeatures loftFeats: adsk.fusion.LoftFeatures = comp.features.loftFeatures # 外内面でロフト bodies = execLoft(pairFaces) # debug 失敗面 dumpFaces(notFind, 'NotFind', True) dumpFaces(ngFaces, 'LoopErr') return bodies def selectEnt( msg: str, filterStr: str) -> adsk.core.Selection: try: app: adsk.core.Application = adsk.core.Application.get() ui: adsk.core.UserInterface = app.userInterface sel = ui.selectEntity(msg, filterStr) return sel except: return None def dumpFaces(faces, name, pointFg = False): if not DEBUG: return if len(faces) < 1: return app: adsk.core.Application = adsk.core.Application.get() des: adsk.fusion.Design = app.activeProduct comp: adsk.fusion.Component = faces[0].body.parentComponent if pointFg: skt: adsk.fusion.Sketch = comp.sketches.add( comp.xYConstructionPlane ) sktPnts: adsk.fusion.SketchPoints = skt.sketchPoints face: adsk.fusion.BRepFace = None for face in faces: sktPnts.add(face.pointOnFace) tmpMgr: adsk.fusion.TemporaryBRepManager = adsk.fusion.TemporaryBRepManager.get() clones = [tmpMgr.copy(f) for f in faces] baseFeat: adsk.fusion.BaseFeature = None if des.designType == adsk.fusion.DesignTypes.ParametricDesignType: baseFeat = comp.features.baseFeatures.add() baseFeat.name = name bodies: adsk.fusion.BRepBodies = comp.bRepBodies if baseFeat: baseFeat.startEdit() for body in clones: try: bodies.add(body, baseFeat) except: pass baseFeat.finishEdit() else: for body in clones: try: bodies.add(body) except: pass
実行してみると、多くの物は満足出来るレベルなのです。
正直、ん?と思う部分はあるのですが。
中にはダメなやつもあります。
わかりにくいのですが、三角形と四角形をロフトした形状です。
これは元の形状で見ると、シェルで処理する際
外側2枚を内側1枚にして処理されています。
これは・・・GUIでやっても無理じゃないかな?
と言う事で諦めます。
もう一つ、どうしても抜ける部分が出てしまいました。
色々と調べていたら、どうやら内側の面を見つけだす事が
出来ていないようです。
DEBUG = True
にして実行すると、上手く行かない部分の面等も出力します。
相手の面を探す際に使用している点を表示させると
想像以上に端っこでした。
てっきり常に重心付近に出来ると思っていたのですが、違いました。
原因これだな。