IFCOpenshellとTrimeshを使ってIFCのデータを可視化した話と設定の落とし穴

### これなんの話？
BIMの標準データ交換フォーマットであるIFCのデータを可視化しようとした。
しかし、PythonのJupyter上で可視化しようとしたところ、IFCのデータを処理するためのライブラリ・IfcOpenShellに落とし穴があってドツボにはまったけど何とか解決したよっていうお話。

# IFCのデータをPythonで可視化する

## まずやらないといけないこと
### IFCのデータのメッシュ化
IFCのデータは曲線やらなにやらのデータが入っていて、座標系は部屋や階ごとの相対座標系になっていたりする。
こいつを座標系展開して、形状をメッシュ化するのは大変。
ただ、IFCのデータを読み込んで、3Dメッシュ化する機能を持つライブラリは世の中に既に存在している。
それが[IfcOpenshell](http://ifcopenshell.org/)である。他にも[IFCEngine](http://rdf.bg/product-list/ifc-engine/)なんてものもあるが、こちらは研究目的なら無償だけどビジネスやらなにやらで使うならお金がかかる。

とりあえず、今回はpythonでコードを書くので使うのならばpythonのバインディングがあるIfcOpenshellでしょうと。


### 必要なもののインストール
ifcopenshellをインストールするにはcondaが必要だけど、Anaconda入れればOK
Anacondaでのcondaの使い方とか知らないっていう人は別途自分で調べてください()

pythonのバージョンは3.8でcondaの仮想環境は構築しました。
そのうえで、とりあえず以下のコマンドを実行すれば必要なものは入る…はず

```
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

pip install trimesh
```


## いざコード作成
jupyterを起動してコードを書きました。
サンプルは[IfcOpenHouse](https://github.com/aothms/IfcOpenHouse)を使いました。
内容は以下の通り

```python
import ifcopenshell
from ifcopenshell import geom
import trimesh

ifc_file = ifcopenshell.open("IfcOpenHouse_IFC2x3.ifc")
settings = geom.settings()

scene = trimesh.Scene()

for ifc_entity in ifc_file.by_type("IfcProduct"):
    if ifc_entity.Representation:
        shape = geom.create_shape(settings, ifc_entity)
        ios_vertices = shape.geometry.verts
        ios_faces = shape.geometry.faces

        vertices = [[ios_vertices[i], ios_vertices[i+1], ios_vertices[i+2]] for i in range(0, len(ios_vertices), 3)]
        faces = [[ios_faces[i], ios_faces[i+1], ios_faces[i+2]] for i in range(0, len(ios_faces), 3)]
        
        mesh = trimesh.Trimesh(vertices=vertices, faces=faces)

        scene.add_geometry(mesh)

scene.show()
```

これで可視化出来る…はずだった

ところが出てきたのはこんなやつだった

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465191/6cf627b3-a052-33c2-2327-e90f271c8479.png)


まあ、こうなってしまうのはIfcOpenshellの方のコードが色々とおかしいんでしょう…ってことで、色々調べてみた結果、どうやら座標系の設定の方に問題があることが分かった。

と、いうわけでその設定を行うようにしたコードだと以下の通りになる。

```python

import ifcopenshell
from ifcopenshell import geom
import trimesh

# 3Dオブジェクトのシーングラフを作成
scene = trimesh.Scene()

def read_geom(ifc_path):
    # ファイル読み込み
    ifc_file = ifcopenshell.open(ifc_path)
    settings = geom.settings()
    # ワールド座標系を設定する（この設定がないとさっきみたいに変な三角形メッシュが出力される)
    settings.set(settings.USE_WORLD_COORDS, True)

    # IfcProductのサブクラスを全部取得する
    for ifc_entity in ifc_file.by_type("IfcProduct"):
        if ifc_entity.is_a("IfcOpeningElement"): continue
        if ifc_entity.Representation:
            # 設定に基づいてメッシュデータを作成
            shape = geom.create_shape(settings, ifc_entity)
            ios_vertices = shape.geometry.verts
            ios_faces = shape.geometry.faces

            # 出力した頂点リストと頂点インデックスのリストを整形
            # どちらも要素3の配列が並んでる２次元配列に
            vertices = [[ios_vertices[i], ios_vertices[i+1], ios_vertices[i+2]] for i in range(0, len(ios_vertices), 3)]
            faces = [[ios_faces[i], ios_faces[i+1], ios_faces[i+2]] for i in range(0, len(ios_faces), 3)]
            
            # trimeshを使って頂点インデックスと頂点リストからメッシュを生成
            mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
            
            # メッシュをシーングラフに追加
            scene.add_geometry(mesh)

read_geom("IfcOpenHouse_IFC2x3.ifc")

# シーングラフを可視化
scene.show()

```

これをjupyter上で実行して得られた画像がこちら

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465191/ff6ea236-fc9a-8901-82f9-ad4929a52e21.png)



### 参考にしたところ
https://community.osarch.org/discussion/185/ifcconvert-from-ifcopenshell-from-python