3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

IFCのモデルをplotlyで表示する

Last updated at Posted at 2024-02-13

plotly_axis.JPG

はじめに

IFCファイルを読み込む手段の一つにIfcOpenShellがあります。形状情報も取得することができるのですが、取得してもPythonでは表示する方法がありません。いえ、pythonoccを使用すれば表示できるそうなのですが、pythonoccを使用するにはcondaを使用するか、ソースコードからビルドする必要があるようです。宗教上の理由で(?)condaが使えない人もいると思いますし、ビルドは難易度が高いです。

そんなわけでもっと手軽にIFCのモデルを表示したいので、IfcOpenShellで読み込んだ形状情報をplotlyで表示します。

できました

これで表示できるよ!やったね!!

import plotly.graph_objects as go
import numpy as np
import ifcopenshell.geom

# 世界座標系で取得する設定
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_WORLD_COORDS, True)

# IFCファイル読み込み
model = ifcopenshell.open(path)

# すべての形状情報を取得する
meshes = []
for element in model.by_type("IfcProduct"):
    # 部屋の情報などや壁の開口部などは除く
    if  element.is_a('IfcOpeningElement') or element.is_a('IfcSpace'):
        continue

    try:
        # 形状の取得
        shape = ifcopenshell.geom.create_shape(settings, element)
    except:
        continue

    # 頂点と面の情報を取得
    verts = shape.geometry.verts
    faces = shape.geometry.faces
    verts = np.array(verts).reshape(-1, 3)
    faces = np.array(faces).reshape(-1, 3)

    # 色情報の取得
    materials = shape.geometry.materials
    material_ids = shape.geometry.material_ids
    if len(materials) == 0:
        facecolors = ['lightgray'] * len(faces)
    else:
        colors = [np.array(material.diffuse) for material in materials]
        transparencies = [1 - material.transparency for material in materials]
        colors = np.hstack([np.array(colors), np.array(transparencies).reshape(-1, 1)])
        facecolors = [colors[material_id] for material_id in material_ids]

    # plotlyのメッシュを作成
    mesh = go.Mesh3d(
        x=verts[:, 0],
        y=verts[:, 1],
        z=verts[:, 2],
        i=faces[:, 0],
        j=faces[:, 1],
        k=faces[:, 2],
        facecolor=facecolors,
        flatshading=True,
        lighting=dict(
            ambient=1,
            diffuse=0,
        ),
    )
    meshes.append(mesh)

# plotlyで表示
fig = go.Figure(data=meshes)

# 軸を表示しない設定
noaxis = dict(
    showbackground=False,
    showgrid=False,
    showline=False,
    showticklabels=False,
    ticks="",
    title="",
    zeroline=False,
)

# レイアウト設定
fig.update_layout(
    scene=dict(
        xaxis = noaxis,
        yaxis = noaxis,
        zaxis = noaxis,
    ),
    scene_aspectmode='data',
)

# HTMLファイルに保存
fig.write_html(
    "file.html",
    include_plotlyjs='cdn',
    full_html=True,
)

解説

コードの解説します。

世界座標系の設定

まず最初の設定の部分。

settings = ifcopenshell.geom.settings()
settings.set(settings.USE_WORLD_COORDS, True)

IFCのデータは形状の情報(Representation)と位置情報(ObjectPlacement)が別れています(IfcProductのドキュメント参照)。そのため、特に設定を行わずに形状情報を取得すると、位置情報がないので以下のように原点付近にメッシュが表示されます。

plotly_local.JPG

そのため、正しい位置を取得するには、世界座標系で形状を取得するように設定するか、別途位置情報を取得して変換をかける必要があります。
今回は、こちらの記事を参考にして世界座標系で取得しました。

形状を持つデータの取得

すべての形状データの取得を行います。

for element in model.by_type("IfcProduct"):
    # 部屋の情報などや壁の開口部などは除く
    if  element.is_a('IfcOpeningElement') or element.is_a('IfcSpace'):
        continue

    try:
        # 形状の取得
        shape = ifcopenshell.geom.create_shape(settings, element)
    except:
        continue

IFCで形状情報を持つのはIfcProduct(を継承しているエンティティ)です。そのため IfcProduct を取得すればすべての形状を持つデータを取得できるのですが、IfcSpace(部屋の情報など)や IfcOpeningElement(壁の開口部など)も含まれてしまいます。そのため、それらのエンティティは除いて取得する必要があります。

ちなみに除かないで取得すると以下のようになります(わかりやすくするために色を変えています)。

plotly_opening.JPG

また、IfcProductのドキュメントを見てみるとわかるのですが、形状情報はオプションです。つまり形状のないデータもあります。さらには形状情報があってもIfcAnnotationのようにIfcOpenShellが形状取得に対応していないものもあります。そのため、形状取得するときにエラーが発生しても例外処理で無視するようにしています。

頂点と面と色の取得

メッシュを作成するための、頂点、面、色の情報を取得します。

    # 頂点と面の情報を取得
    verts = shape.geometry.verts
    faces = shape.geometry.faces
    verts = np.array(verts).reshape(-1, 3)
    faces = np.array(faces).reshape(-1, 3)

    # 色情報の取得
    materials = shape.geometry.materials
    material_ids = shape.geometry.material_ids
    if len(materials) == 0:
        facecolors = ['lightgray'] * len(faces)
    else:
        colors = [np.array(material.diffuse) for material in materials]
        transparencies = [1 - material.transparency for material in materials]
        colors = np.hstack([np.array(colors), np.array(transparencies).reshape(-1, 1)])
        facecolors = [colors[material_id] for material_id in material_ids]

頂点と面は公式ドキュメントのコードそのままです(numpyを使ってはいますが)。

色情報についてはplotlyで処理するために RGBA の形式にしています。このときmaterial.transparencyが透明度なのですが、IFCでは 1=透明, 0=不透明 のなので逆転させています。また、色情報がないデータもあるので、その場合はlightgrayとしました。

plotlyのメッシュ作成

plotlyでの3Dメッシュを作成します。

    mesh = go.Mesh3d(
        x=verts[:, 0],
        y=verts[:, 1],
        z=verts[:, 2],
        i=faces[:, 0],
        j=faces[:, 1],
        k=faces[:, 2],
        facecolor=facecolors,
        flatshading=True,
        lighting=dict(
            ambient=1,
            diffuse=0,
        ),
    )

メッシュ作成時はflatshadinglightingを指定して光による表現を消して、単純に色を表示するようにします。この設定がないと以下のように変な影が表示されます。

plotly_model.JPG

なぜこうなるのかはわかりません。ただ、そもそもplotlyはグラフを描画するライブラリであって、3Dモデルを表示するものではない(可能ではあるけれども)ので、光の処理はそこまで得意ではないのかもしれません。

メッシュの表示

作成したメッシュをplotlyで表示します。

fig = go.Figure(data=meshes)

# 軸を表示しない設定
noaxis = dict(
    showbackground=False,
    showgrid=False,
    showline=False,
    showticklabels=False,
    ticks="",
    title="",
    zeroline=False,
)

# レイアウト設定
fig.update_layout(
    scene=dict(
        xaxis = noaxis,
        yaxis = noaxis,
        zaxis = noaxis,
    ),
    scene_aspectmode='data',
)

レイアウトの設定でのscene_aspectmode='data'の指定はアスペクト比の設定です。これがないと見た目が歪みます。また、noaxisの指定をすることで、軸の表示を消してモデルのみ表示させることができます。

できあがり

最終的に表示されたものが以下です。

plotly_flatshading.JPG

まとめ

IFCのモデルをIfcOpenShellで読み込んでplotlyで表示しました。plotlyだとHTMLファイルに出力することもできるので、出力したHTMLをブラウザで開くだけで手軽にモデルを表示することができます。
ただ、かなり表示が重いです。数十MB程度のIFCファイルから作成したHTMLでも開くのに数分かかりました。大きめのモデルは素直にIFCを表示するソフトウェアやIFC.jsなどを使用しましょう。

参考

今回使用したモデル

IfcOpenShellでのジオメトリ取得方法

plotlyで3Dメッシュの表示

web-ifcでの開口部などを除く処理のソースコード

pythonoccを使用しての描画

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?