IFCのデータ構造を可視化したい
操作感とか表示の見た目に関して、やりたいことメモ
- 属性表示欄のサイズをマウスで変更できるように
- 属性表示欄の見た目とか表示の仕方とか
- 特に逆属性がわかるように
- ノードの表示が多くなったときに表示をどうにかする
- いい感じに自動で整列するとかできたらいいな
- 重なっているノードを縦横で一定間隔で整列する
- undo, redo
- Shift押しながらのノード移動で水平垂直移動
- 表示の保存
- ノードの逆属性の順番がランダムなのをどうにかする(setで取得している関係でそうなっている)
- ファイル読み込み処理をしていることがわかるように(ファイルが大きいと時間がかかる)
- アプリ化したときのログ出力
- ノードのグループ化(UEにもあるようなやつ)
- ノード右クリックからメニューを開いて色々できるように?
- プロパティセットをすべて表示とか?
- 関係するノード展開?
前のスクラップでIFCのデータ構造がグラフ構造っぽいということがわかったので、それを可視化する試み。
ちなみに3Dモデルでの可視化という話ではなくて、データ構造そのものを可視化する。どちらかといえばBIMのModelingではなく、Informationの要素を可視化するイメージ。
直接それを使ってなにかする、というよりも「IFCのデータ構造特殊でよくわかんないからデータいじって解析するにしてもハードル高すぎない!?」という問題の手助けができたらいいなーというモチベ。
まず前提として、IFCファイルは結構特殊なファイル形式である。そのため、既存の可視化ライブラリを流用してもおそらくそのまま使うことはできず、拡張して独自実装する必要があると思われる。そのため、ある程度自由度のあるライブラリを使う必要がある。
データ構造的にあえて近いものを挙げるとER図かなーとも思うけれども、拡張することは必須になるだろうからあまり考えないことにする。
というわけで可視化するにあたってライブラリを調べた。ネットワークグラフ描画ライブラリ7個まとめの記事を参考に。
上記記事の7個のうち Cytoscape.js
のサンプルにこれが近いかなーというものもあったけど、D3.js
のほうが自由度高さそうなのと Plotly
で若干馴染みがあるので ひとまず D3.js
を使っていこうと思う。
↓
結局、グラフライブラリは使用せずにVueを使用してHTML,CSS,SVGで書いている。
というのもChatGPTが強すぎて、ライブラリの学習コストを考えるとそっちのほうが楽そうっていうのが理由。
グラフ構造を表示するにしても、すべてを表示することはノード数が多すぎてできない。
IFC.jsのGitHubにある一番小さそうな TESTED_Simple_project_01.ifc
で、ノード数は 14,119 ある。
つまり、まずは表示方法を検討する必要がある。どうしようか。
ちなみに IfcProduct
の要素だけプロットしたら下記のようになる。これでノード数300くらい。
実際にはここに各ノードの情報を載せないといけないので、多くて50くらいが限界だと思う。
最初にIfcProjectを表示させてそこから展開するような感じにすればどうにか...?
表示非表示を自由に切り替えたり、表示情報を色々工夫してなんかいい感じにできないかな。
データに合わせてこんな感じにノード追加して行く感じで作成中
ノード追加はできたけどノード移動しても線は追従しないので、次はそこらへんを実装する。
ノードのデザインを考えないと。デザインと言うか属性の値をそのままノードの表示させると場所を取りすぎる。属性の値は別途表示させる形のほうが良さそう。あと値のない属性の表示はいらないね。
シンプルにした。
ここから追加する
- IDの場合のみエッジの接続点を配置するように
- 逆属性は左に接続点を置く
- 接続点を引っ張ると、ノードが追加されるように
まずは上2つの見た目部分をやる。
ノードの見た目を調整。接続点があれば別のIFCのEntityが紐づいている。左側に接続点があれば逆属性。接続点がなければ純粋な値が入っている属性。
次は接続点から別のノードを追加する。
接続点をドラッグしてノードを追加できるように。画像はOwnerHistory
からエッジを引っ張った図。
エッジ表示できてないけど。
あと一つの属性から複数のノードがぶら下がっているのにはまだ対応してない。
次はエッジを表示する。
なんとかエッジを繋いだ。エッジがノードの上に来てるのどうにかしたいけど。若干エッジの位置がずれるのもどうにかしたい。
やることメモ
- ゴリ押しした部分があるので、そのうち修正する
- 複数ノードぶら下がっている場合に対応する
- エッジの表示をノードの下に来るようにする
- ...
いろいろあるなー...ひとまず次はエッジをノードの下に表示する。
単にノードが透明なせいだったので、エッジをノードの下に表示するのはすぐできた。
複数ノードぶら下がっている場合に対応したいけど、どういう仕様にするか...全部展開する?選択した一つを展開する?
選択する場合、追加する前に選択画面を表示して -> 選択して -> ノード追加 という手順を踏む必要がある(選択する表示の追加が必要)し、ひとまず全部展開しますか。
実際に動かしているもの
複数ノードを展開できるように。画像はノードをきれいに並べ直したあと。
展開直後は全部のノードが重なって表示される。
ノードが多くなってくると表示がごちゃっとするのをどうにかしないと。
- ホイールコロコロで表示の拡大縮小
- ノードを消す処理
- 表示全体の移動
このあたりかな。まずは拡大縮小しますか。
拡大縮小と全体の移動処理の追加をした。座標計算に手こずりました。
次はノードを削除できるようにします。
ノードを選択して削除できるようにした。もうちょっと選択を目立つデザインにしてもいいかもだけどひとまず。
エッジの表示をどうにかしたい。現状まっすぐ引いているだけなので、接続点の逆側に行くとどこと繋がっているのかわかりにくい。それにノードの横幅が大きいと位置がおかしい。
イメージ的にはRete.js。接続点から少し横に出てから接続しに行く感じ。
(ちなみに最初このライブラリを使って実装しようかと思ったけど、書き方に癖があって習得に時間がかかりそうだったから見送っている)
その前にバグとか直すか。
- 属性が単純な値か他のノードに繋がっているかの判定を、データが数値化か文字かで判定してしまっているので、座標などの数値データでエラーになる
- すでに表示されているエッジでも、ノード追加処理をすると同じエッジが複数描画されてしまう
- ノードを画面外に移動できてしまう(拡大縮小移動処理で対応できるので問題ないといえば問題ない)
表示を調整。ノードを半透明にしてエッジが裏にあっても見えるように。Rete.jsを参考にしました。
あと、ノードの幅は固定にしてはみ出した文字は省略する感じに。
というかChatGPT便利すぎて困る。切り詰め方法。
次は数値データに対応します。
座標とかの数値データのもエラーにならないようには修正完了。
そう思って色々触ってたらまたバグ見つけた。
#374からノード追加するとおかしなことに。直します。
エッジについては直したけど、新たなバグと言うかを。IfcInteger
って何?
数値データなんだけどIFCのエンティティ要素っぽくって???なんでそうなってるの?
ifcopenshellで取得するとid=0で取得される。
item = model.by_id(446)
item.NominalValue.get_info()
# {'id': 0, 'type': 'IfcInteger', 'wrappedValue': 2}
IFCファイル上だと直接エンティティ指定しているみたいな感じ。
#446= IFCPROPERTYSINGLEVALUE('NumberOfStoreys',$,IFCINTEGER(2),$);
ひとまずID:0のデータはそのまま値として扱うことに。
次は属性の値を表示させたい。ただ、ノード上に表示させると画像みたいにちょっと無理がある。
だから属性を表示させる欄を画面右端とかに追加して、そこに表示させる感じかな。
右に表示欄作って表示させた。ひとまず最低限の機能はできたと思うんだよね。表示の仕方とか操作感とか改善点は色々あるけど。
現状だとIfcProjectからしかデータを辿れないから、特定のエンティティについてって言うことができない状態。
次は指定したのエンティティのノードを表示できるようにする。
現状こんな感じに触れる
一応、検索して追加自体はできたけど...
- 指定のIFCエンティティに対して、データが複数ある場合どうするのか
- ノードの追加位置が現状左上固定
複数あるのもID込みで一覧に表示させるか...
ノードの追加位置も右クリックでメニュー表示させてとかやりたいけど...
IDはサブ階層にして表示した。
元の一覧でID全部を表示しようとすると処理速度的に不可能だったので。IFCファイル数十万行とか普通にあるから当たり前だった。
右クリックで検索窓を左側に表示して、右クリックした場所にノードを追加する形に。
ここからどうしようか。3Dモデルも表示して、モデルをクリックしたらそこに関係あるノード表示したり、逆にノード選択すると3Dモデルのどの部分なのかをモデル上でハイライトしたり、とかやろうと思えば色々あるけど。
ひとまずは上でメモった操作上の改善点とかをちまちま修正しますか。
ノード追加中のエッジを表示するように(スクショだだとマウス表示されないからわかりにくいけど)。
エッジ接続位置とずれるのはどうしようかな...ノード追加時にどの属性から追加されたかを判断して位置を調整すればいいんだけど...
ノード追加時のノード位置をエッジ接続点に合わせるようにした。
前の「マウスを離した位置にノードの左上がくる」のとどっちがいいのかはわかんないけど、修正後のほうが自然かな?
(スクショだだとマウス表示されなくてわからないので画像なし)
複数ノードが追加されるときに少しずらして追加するように。
接続点基準でずらしてるからなんか変だけど。まあよし。
エッジを曲線にした。
Shiftドラッグで範囲選択できるように。
選択処理の仕様を変えるか。現状だとノードを次々クリックすると複数選択されるから、最後にクリックしたものだけ選択されるようにする。そしてShift押しながらクリックで複数選択されるようにする。多分一般的にはこっちだと思う。
選択処理色々修正
- 複数選択の仕様変更
- ノード移動時に選択されたノードすべてが移動するように変更
(キャプチャだとわからないので画像はなし...)
ノードの整列処理追加。いい感じのアイコンが見つからなかったのでFigmaで自作した。
整列処理作るよりアイコン作るのが大変だったよ...
使ってて思うのが、この壁のエンティティってどこ壁だ?となってBIMvisoinとかで確認しに行ってるから、やっぱりモデルの表示はほしい。
そんなわけでモデルの表示を実装します。
モデルの表示についてはIfcOpenShell
ではなくてweb-ifc
を使う。なぜなら処理速度が圧倒的に速いので。
そして表示はBabylon.jsでやります。なぜなら趣味だからです。
いや、IFC.js(Three.js)でやろうにも、結局エンティティとモデルの紐づけとかやるには、内部処理を解析しないといけないから、だったら最初から自分で位置から書けばいいかなって。それなら慣れてるBabylon.jsのほうがいいかなって。つまり趣味です。
Babylon.jsで表示はできるんだけど重い。メモリバカ食いしてる。
インスタンスとかも使わずにそのまま表示しているせいだとは思うけど...WebGLに詳しいわけじゃないんだよね...
とりあえず軽いモデルで試してるからいいけど、どうにかしないとな。
IFC.js
でどうやってるのか調べるか。
ThatOpen/engine_components になってからIFC.jsほとんど触れてないんだけど、調べても記事まったく出てこないな?
別に公式ドキュメントを見ればいいんだけど、半分くらいThree.jsの内容なんだよね...あとなんかなんとなくとっつきにくくなってる。英語しか情報がないからかもだけど。
ちゃんとこっちも調べますか。