【実装】pythonoccでstepファイルから面の形を検出してみた

 目次

- はじめに
- pythonOCCとはなんぞや？
- 実行環境
- 入力
- 事前設定
- 動かしたコード
- 結果
- 今後の予定
- 最後に
- 参考情報

# はじめに
[LIGTHzアドベントカレンダー 2022](https://qiita.com/advent-calendar/2022/lightz)の18日目の記事です。

サイズの大きなstepファイル（3DCADデータファイル）の幾何情報や寸法情報を
市販ツールを使う事なく簡易的に調べたくなる場面があり、オープンソース
を使って手早く幾何情報や寸法情報を識別する方法がないかを調べています。

python OpenCasCade Technology(通称pythonOCC)を用い
stepファイル（3DCADデータファイル）から面の形を検出したので
手順を残します。（初心者向け）
# PythonOCCとはなんぞや？
・C++実装のOSSであるOpenCascade Technology(OCCT)にpythonのラッパーがかぶさったOSSを指し、
　3次元CADやCAM、その他のための開発プラットフォームとして提供されています。（[wikipedia参照](https://ja.wikipedia.org/wiki/Open_Cascade_%E3%83%86%E3%82%AF%E3%83%8E%E3%83%AD%E3%82%B8%E3%83%BC)）
・OCCTを使う事により、stp/B-repデータから3D形状の寸法や幾何情報などを読み出したり、
　3D形状設計を行う事などができます。
・20年以上、非ソフトウェア分野を含む幅広な技術ドメインで利用されてきた事もあり、
　やや[サグラダファミリア](https://ja.wikipedia.org/wiki/%E3%82%B5%E3%82%B0%E3%83%A9%E3%83%80%E3%83%BB%E3%83%95%E3%82%A1%E3%83%9F%E3%83%AA%E3%82%A2)化しているとの声もある様です。([参照記事](https://qiita.com/_EnumHack/items/577b087c071e4e9cd3b3))
・使い方などの詳細はリンク先をご参照願います。
　　[OCCTのコミュニティサイト](https://dev.opencascade.org/)
　　[開発者による利用方法の説明動画(youtube)](https://www.youtube.com/watch?v=i5zCHArA06E)
# 何に使うのか？
・3D形状設計を簡易的に行いたい時
・step/B-repファイルなどの3DCADデータから寸法情報や表面の形状情報などを自動的に取り出したい時
# 実行環境
　OS:　Ubuntu 20.04 LTS
　python:　3.8.13
　conda:　4.14.0
　numpy:　1.22.4
　IPython          :　8.5.0
　jupyter_core     :　4.11.1
　[pythonOCC：　7.6.1](https://github.com/tpaviot/pythonocc-core/tree/b642c77d84add7c08bcb6e52f04a677d11f412d5)　([GNU GPLv3ライセンス](https://github.com/tpaviot/pythonocc-core/blob/master/LICENSE))
　[ソースコード(pythonocc_core)](https://github.com/tpaviot/pythonocc-core)
　[ソースコード(pythonocc_util)](https://github.com/tpaviot/pythonocc-utils)
　[ソースコード(pythonocc_demos)](https://github.com/tpaviot/pythonocc-demos)
# 入力
　以下オープンソースで提供されている3DCADデータを用いました。
　[stpデータ]("https://github.com/hducg/MFCAD")
　([MITライセンス](https://github.com/hducg/MFCAD/blob/master/LICENSE))

# 事前設定
以下の環境設定を行います。

```sh:
% conda create -n test python=3.8.13
% conda activate test
% conda install -c conda-forge pythonocc-core=7.5.1
% conda install -c numpy
% git clone https://github.com/tpaviot/pythonocc-core.git
% git clone https://github.com/tpaviot/pythonocc-demos.git
% git clone https://github.com/tpaviot/pythonocc-utils.git
```
以下を実行し、実行OKメッセージが出たら環境構築完了です。

```sh:
% cd ./pythonocc-core/test/
% python run_test.py
:
:
test_midpoint (core_extend_shapefactory_unittest.TestExtendShapeFactory) ... ok
test_scale_shape (core_extend_shapefactory_unittest.TestExtendShapeFactory) ... ok
----------------------------------------------------------------------
Ran 117 tests in 5.931s

OK
```
# 動かしたコード

以下のコードは、pythonocc-demos/core_geometry_face_recognition_from_stepfile.py
を基に若干の修正を加えたものとなります。
まず、形状面を識別するためのライブラリ群をimportします。
BRepAdaptor_Surfaceが形状面の種別判定用classとなります。
```py:

import os
import os.path
import sys


from OCC.Core.STEPControl import STEPControl_Reader
from OCC.Core.IFSelect import IFSelect_RetDone, IFSelect_ItemsByEntity
from OCC.Core.GeomAbs import GeomAbs_Plane, GeomAbs_Cylinder
from OCC.Core.TopoDS import topods_Face
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
from OCC.Display.SimpleGui import init_display

from OCC.Extend.TopologyUtils import TopologyExplorer

#pythonOCC library
from OCC.Core.Geom import Geom_ElementarySurface
from OCC.Core.Geom import Geom_CylindricalSurface
from OCC.Core.BRep import BRep_Tool_Surface
```
次に、stp(step)ファイルをロードするための関数を定義します。
```py:

def read_step_file(filename):
    """ read the STEP file and returns a compound
    """
    step_reader = STEPControl_Reader()
    status = step_reader.ReadFile(filename)

    if status == IFSelect_RetDone:  # check status
        failsonly = False
        step_reader.PrintCheckLoad(failsonly, IFSelect_ItemsByEntity)
        step_reader.PrintCheckTransfer(failsonly, IFSelect_ItemsByEntity)
        step_reader.TransferRoot(1)
        a_shape = step_reader.Shape(1)
    else:
        print("Error: can't read file.")
        sys.exit(0)
    return a_shape
```

次に、stepファイルから形状面の名称などを識別する関数を定義します。
BRepAdaptor_Surfaceでa_face(3DCADデータ実体)の形状面を識別して結果毎に表示処理を変えています。
```py:

def recognize_face(a_face):
    """ Takes a TopoDS shape and tries to identify its nature
    whether it is a plane a cylinder a torus etc.
    if a plane, returns the normal
    if a cylinder, returns the radius
    """
    surf = BRepAdaptor_Surface(a_face, True)
    surf_type = surf.GetType()

    if  surf_type == GeomAbs_Plane:
        print("--> plane")
        # look for the properties of the plane
        # first get the related gp_Pln
        gp_pln = surf.Plane()
        location = gp_pln.Location()  # a point of the plane
        normal = gp_pln.Axis().Direction()  # the plane normal
        # then export location and normal to the console output
        print("--> Location (global coordinates)", location.X(), location.Y(), location.Z())
        print("--> Normal (global coordinates)", normal.X(), normal.Y(), normal.Z())
    elif surf_type == GeomAbs_Cylinder:
        print("--> cylinder")
        # look for the properties of the cylinder
        # first get the related gp_Cyl
        gp_cyl = surf.Cylinder()
        location = gp_cyl.Location()  # a point of the axis
        axis = gp_cyl.Axis().Direction()  # the cylinder axis
        # then export location and normal to the console output
        print("--> Location (global coordinates)", location.X(), location.Y(), location.Z())
        print("--> Axis (global coordinates)", axis.X(), axis.Y(), axis.Z())
    else:
        # TODO there are plenty other type that can be checked
        # see documentation for the BRepAdaptor class
        # https://www.opencascade.com/doc/occt-6.9.1/refman/html/class_b_rep_adaptor___surface.html
        print("not implemented")
```

　＞        # TODO there are plenty other type that can be checked
　＞        # see documentation for the BRepAdaptor class
　＞        # https://www.opencascade.com/doc/occt-6.9.1/refman/html/class_b_rep_adaptor___surface.html
C++最新版のOCC(7.6.3)にpythonoccのバージョンアップが追い付いていない(6.9.1)様で上記のコメントがあります。
全機能を本格的に使いこなすにはC++版も確認する必要があるかもしれないですね。

最後に、実際のstepファイル（./sample.stp（各自で動かす時は事前に用意してください））
をrecognize_faceで読み込み、形状面の名称などを識別します。

```py:

path: str = './sample.stp'

shp = read_step_file(path)
t = TopologyExplorer(shp)
# loop over faces only
for f in t.faces():
    # call the recognition function
    recognize_face(f)
```


# 結果

```output.log:
Output exceeds the size limit. Open the full output data in a text editor
--> cylinder
--> Location (global coordinates) 13.5 20.5 5.2
--> Axis (global coordinates) 0.0 0.0 2.0
--> plane
--> Location (global coordinates) 60.5 12.5 3.2
--> Normal (global coordinates) 0.0 -1.0 0.0
:
```
形状面毎の形の識別結果（cylinder、planeなど）や座標位置（Location）などを検出する事が出来ました。

# 今後の予定
- 寸法や形状面同士の接続関係を識別するコードの実装方法を調査し、適宜掲載予定です
- pythonocc-demos以下などのソースコードの読み解き結果を適宜掲載予定です
- OCCT利用方法の調査結果を適宜掲載予定です
# 最後に
弊社では、自動車・機械産業のお客様に向けたソリューション開発エンジニアを絶賛採用中（2022/12時点）です :muscle: :muscle:
https://www.wantedly.com/projects/1181162
# 参考情報
- pythonOCC Q&A
https://github.com/tpaviot/pythonocc-core/issues
- pythonOCC利用例
https://qiita.com/MachineCAT/items/c3e417a44d40890c608c