IfcOpenShell pythonバインディングのことはじめ

# やりたいこと
表題の通りですが、[IfcOpenShell](http://www.ifcopenshell.org/python.html)というのはIFCを解析できるオープンソースライブラリで、もともとC++で書かれているけれど、Pythonバインディングも出ています。IfcOpenShellのツール群の中には、IfcConvertというものがあって、IFCのジオメトリ抽出が可能になっている。OBJ、IGES、DAE、STLなど様々なフォーマット変換が可能であり、GUIDや名前で特定の形状のみ抽出することもできます。一般的にはこれで十分なのですが、IFCの属性情報と形状を使って、色々なことをしようとすると、どうしても直接ジオメトリやトポロジといった3Dデータをいじりたくなります（ふつうはならないけどね）。

その場合は、[OpenCASCADE](https://www.opencascade.com/)と呼ばれるライブラリ(CADカーネル）を使うのが一般的で、実際ライブラリにも含まれています。このライブラリは、FreeCADなどに用いられる老舗のオープンソースライブラリで、パラメトリックな3Dモデラー機能を提供してくれるようです。

ちなみにIFCの解析ライブラリというのは、オープンソースだと[IFC++](http://ifcquery.com/)や[xBIM Toolkit](https://docs.xbim.net/)、有料だと[IFC Engine](http://www.ifcbrowser.com/)や[apstex IFC Framework](https://www.apstex.com/)（一部は無料）などがあります。


# IfcOpenShellによるIFCの分析とpythonOCCによる描画
## 準備
ライブラリの関係もあって、Python3.5の環境を用意します。私の場合はインストールしてあるAnacondaで、condaを使って必要ライブラリをインストールしました。対応しているPythonは3.5までのようだったので、環境としてはそれを使い、対応したバージョンのOpenCASCADEのPython-bindingである`pythonocc`をインストールします。ネットワーク環境の要因なのか、結構失敗するのでソースコードからビルドしたほうが良いかもしれません。

```shell
conda install -c conda-forge -c oce -c dlr-sc -c ifcopenshell ifcopenshell
conda install -c conda-forge -c dlr-sc -c pythonocc -c oce pythonocc-core==0.17.3
```

あと、適当なIFCファイルを準備します。[ifcOpenHouse](http://blog.ifcopenshell.org/2012/11/say-hi-to-ifcopenhouse.html)などでよいでしょう。

※Python3.6にも対応になったようです（2020/2/15）

## IFCからの形状取得
IFCのジオメトリは、複雑な[`IfcRepresentationItem`](http://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/ifcgeometryresource/lexical/ifcrepresentationitem.htm)に分解されています。具体的にはSurface Model(Tessellatiobなど), SolidModel(CGS,B-reps,sweepなど)などの形状表現やスタイル、テクスチャ表現などです。IfcOpenShellはこの複雑さを隠し、すべての統一された境界表現（B-reps）に変換します。加えて、レンダリングのためのMESH生成も行ってくれます。

ちなみに、実際のBIMでの形状モデリングは「カーネル」と呼ばれるものに依存しているようで、例えばASICやParadsolidといったものです。カットアウトや開口のような複雑な表現も容易にモデリングできます。

## MESHの取得
以下が一番単純なサンプルになります。

```python
import ifcopenshell
from ifcopenshell import geom

def read_geom(ifc_path):
    ifc_file = ifcopenshell.open(ifc_path)
    settings = geom.settings()

    for ifc_entity in ifc_file.by_type("IfcWall"):
        shape = geom.create_shape(settings, ifc_entity)
        # メッシュの取得
        ios_vertices = shape.geometry.verts
        ios_edges = shape.geometry.edges
        ios_faces = shape.geometry.faces

        # 普通にPrintしてみる
        print(ios_vertices)
        print(ios_edges)
        print(ios_faces)
```

`geom.setting()`というのは変換のためのオプションで、デフォルトでは頂点（Vetices）、稜線（Edge）、面（Face）のメッシュを生成します。`geom.create_shape(settings, ifc_entry)`で、ifc_entryで示されたIFC要素の形状を取得することができます。頂点は、x,y,zからなるトリプレットであり、稜線はそれらを結ぶもの、そして面は３つの頂点と稜線から構成されます。


## B-repsを直接扱う。
上記では直接レンダリング可能なメッシュ表現を取得しましたが、境界表現であるB-reps（Boundary representation ）を直接取得することもできます。`geom.setting()`に以下のパラメータを指定します。以下ではついでにファイルに保存を試みています。

```python
settings = geom.settings()
settings.set(settings.USE_BREP_DATA, True)
occ_shape = shape.geometry.brep_data

# IfcOpenShell generate an Open Cascade BREP 
with open("IfcOpenShellSamples/brep_data", "w") as file:
    file.write(occ_shape)
```
出力したフォーマットは、[Open Cascade BREP Format](https://www.opencascade.com/doc/occt-6.7.0/overview/html/occt_brep_format.html)というものらしいです。

pythonOCCを使って描画するのは以下のようにします。色が変な気がしますが、まずはこんなもんかな。

```python
import ifcopenshell
from ifcopenshell import geom

# Specify to return pythonOCC shapes from ifcopenshell.geom.create_shape()
settings = geom.settings()
settings.set(settings.USE_PYTHON_OPENCASCADE, True)

# Initialize a graphical display window
occ_display = geom.utils.initialize_display()
ifc_file = ifcopenshell.open("ifcOpenHouse.ifc")


products = ifc_file.by_type("IfcProduct")
for product in products:
    if product.is_a("IfcOpeningElement"): continue
    if product.Representation:
        shape = geom.create_shape(settings, product).geometry
        display_shape = geom.utils.display_shape(shape)
        if product.is_a("IfcPlate"):
            # Plates are the transparent parts of the window assembly
            # in the IfcOpenHouse model
            geom.utils.set_shape_transparency(display_shape, 0.8)

# すぐ消えるので、ここでループさせる
ifcopenshell.geom.utils.main_loop()
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51703/f2ae77a3-87a2-5daa-8e8e-ae7221cbd783.png)


# 参考
1. [Using IfcOpenShell to parse IFC files with Python](https://thinkmoult.com/using-ifcopenshell-parse-ifc-files-python.html)
2. [IfcOpenShell – Read geom as mesh](https://pythoncvc.net/?p=822)
3. [IfcOpenShell – Read geom as brep](https://pythoncvc.net/?p=839)