end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

FreeCAD + python による 部品形状の同一性判定

https://wiki.freecad.org/TopoShape_API

上記にあるfreecadのdocumentによれば、 部品形状の同一性判定には、isSame()、isEqual()、isPartner()の メソッドがあるようですが、 私の環境?ではなぜか False ばかりになってしまいます。

なので、以下のように自作

#!/usr/bin/python
# -*- coding: utf-8 -*-
import FreeCAD

tolerance = 0.0005 # 許容差

def main():
    doc  = FreeCAD.ActiveDocument
    objs = doc.findObjects('Part::FeaturePython')
    chk_result = is_same_shape(objs[0], objs[1] )
    print( "is_same_shape()=",chk_result )

def is_same_shape(obj_1,obj_2):
    # 元のobjectを壊さない様、copy
    shp_1 = obj_1.Shape.copy()
    shp_2 = obj_2.Shape.copy()
     
    # 体積と表面積を比較
    for (val_1,val_2) in [(shp_1.Volume,shp_2.Volume),
                          (shp_1.Area,  shp_2.Area  )]:
         if abs(val_1 - val_2) > tolerance:
              return False
         
    # boundary boxの中心を原点へ移動
    move_to_origin(shp_1)
    move_to_origin(shp_2)

    # 同じ向きに回転
    rot_same_dir(shp_1, shp_2)

    # 互いをboolean減算し、差がなければ、同一形状と判定
    ret_obj = shp_1.cut(shp_2 )
    if ret_obj.Volume < tolerance:
         return True
    return False

def rot_same_dir(shp_1, shp_2):

    x_1 = shp_1.CenterOfMass.x
    y_1 = shp_1.CenterOfMass.y
    z_1 = shp_1.CenterOfMass.z
    x_2 = shp_2.CenterOfMass.x
    y_2 = shp_2.CenterOfMass.y
    z_2 = shp_2.CenterOfMass.z
    # 回転角算出。が、判定式は怪しい。TODO
    pitch = 0
    if abs(z_1 - z_2) < tolerance:
        pitch = 0
    elif abs(z_1 + z_2) < tolerance:
        pitch = 180
    elif abs(z_1 + x_2) < tolerance:
        pitch = 90
    elif abs(z_1 - x_2) < tolerance:
        pitch = -90
    yaw = 0
    if abs(x_1 - x_2) < tolerance:
        yaw = 0
    elif abs(x_1 + x_2) < tolerance:
        yaw = 180
    elif abs(x_1 + y_2) < tolerance:
        yaw = 180
    elif abs(x_1 - y_2) < tolerance:
        yaw = 180

    # https://blog.freecad.org/2023/01/16/the-rotation-api-in-freecad/
    shp_2.Placement = App.Placement(shp_2.Placement.Base,
                                    App.Rotation(yaw, pitch, 0),
                                    shp_2.Placement.Base.negative())

def move_to_origin(shp):
    shp.Placement = App.Placement(App.Vector(0,0,0),
                                  App.Rotation(App.Vector(1,0,0),0))
    shp.Placement = App.Placement(shp.BoundBox.Center.negative(),
                                  App.Rotation(App.Vector(1,0,0),0))
    return shp

if __name__ == '__main__':
    main()

↑こう書くと、以下のようにA2PLUSでアセンブルしたパーツに対し、True が返ります