3次元で可視化したい
構造解析モデルをはじめ、複雑なデータは、分析するために3次元での可視化を行いたい場合があります。そこで今回は、以前ご紹介したPlotlyを用いた3次元描画をしてみたいと思います。なお、今回はJavaScriptではなく、PythonからPlotlyを利用してみます。 Plotlyに関する前回記事はこちら環境構築
以下のコマンドによりインストールするだけです。pip install plotly
簡単なサンプル
モデルデータ
簡単な解析モデルデータを描画してみます。今回用意したデータはjson形式であり、節点情報(節点名と座標)および部材情報(部材名と両端節点名)のみが定義されているものとします。{
"Node": [
{
"Name": "NODE1",
"X": "0.0",
"Y": "0.0",
"Z": "0.0"
},
{
"Name": "NODE2",
"X": "6.0",
"Y": "0.0",
"Z": "0.0"
},
{
"Name": "NODE3",
"X": "0.0",
"Y": "6.0",
"Z": "0.0"
},
{
"Name": "NODE4",
"X": "6.0",
"Y": "6.0",
"Z": "0.0"
},
{
"Name": "NODE5",
"X": "0.0",
"Y": "0.0",
"Z": "6.0"
},
{
"Name": "NODE6",
"X": "6.0",
"Y": "0.0",
"Z": "6.0"
},
{
"Name": "NODE7",
"X": "0.0",
"Y": "6.0",
"Z": "6.0"
},
{
"Name": "NODE8",
"X": "6.0",
"Y": "6.0",
"Z": "6.0"
}
],
"Beam": [
{
"Name": "BEAM1",
"INode": "NODE1",
"JNode": "NODE5"
},
{
"Name": "BEAM2",
"INode": "NODE2",
"JNode": "NODE6"
},
{
"Name": "BEAM3",
"INode": "NODE3",
"JNode": "NODE7"
},
{
"Name": "BEAM4",
"INode": "NODE4",
"JNode": "NODE8"
},
{
"Name": "BEAM5",
"INode": "NODE5",
"JNode": "NODE6"
},
{
"Name": "BEAM6",
"INode": "NODE5",
"JNode": "NODE7"
},
{
"Name": "BEAM7",
"INode": "NODE7",
"JNode": "NODE8"
},
{
"Name": "BEAM8",
"INode": "NODE6",
"JNode": "NODE8"
},
{
"Name": "BEAM9",
"INode": "NODE1",
"JNode": "NODE8"
}
]
}
描画結果
※マウスで視点を操作できます。サンプルコード
上記のサンプルコードを以下に示します。今回のサンプルデータで3D描画するだけであればもっと簡易に記述することは可能ですが、拡張性や保守性をふまえて、クラス利用や関数の細分化をして記述しています。import plotly.offline as po
import plotly.graph_objs as go
import sys, os, json
from docopt import docopt
# モデルクラス
class Model:
def __init__(self, nodes, beams):
self.Nodes = nodes
self.Beams = beams
# 節点クラス
class Node:
def __init__(self, name, x, y, z):
self.Name = name
self.X = float(x)
self.Y = float(y)
self.Z = float(z)
# 部材クラス
class Beam:
def __init__(self, name, node_i, node_j):
self.Name = name
self.INode = node_i
self.JNode = node_j
# モデルを生成する
def create_model(path):
nodes = []
beams = []
dic_nodes= {}
with open(path) as f:
df = json.load(f)
# 節点
for item in df['Node']:
node = Node(item['Name'], item['X'], item['Y'], item['Z'])
nodes.append(node)
dic_nodes[node.Name] = node
# 部材
for item in df['Beam']:
name = item['Name']
node_i = dic_nodes[item['INode']]
node_j = dic_nodes[item['JNode']]
beam = Beam(item['Name'], node_i, node_j)
beams.append(beam)
return Model(nodes, beams)
# 部材の描画用オブジェクトを取得する
def get_trace_beam(beam):
pair_x = [beam.INode.X, beam.JNode.X]
pair_y = [beam.INode.Y, beam.JNode.Y]
pair_z = [beam.INode.Z, beam.JNode.Z]
return go.Scatter3d(x=pair_x, y=pair_y, z=pair_z, name=beam.Name, mode='lines', line_color='blue', showlegend=True)
# 節点の描画用オブジェクトを取得する
def get_trace_node(node):
x = node.X
y = node.Y
z = node.Z
return go.Scatter3d(x=[x], y=[y], z=[z], mode='markers', marker_color='blue', marker_size=2, name=node.Name, showlegend=True)
# 描画用オブジェクトを取得する
def get_traces(model):
traces = []
# Beam
for beam in model.Beams:
traces.append(get_trace_beam(beam))
# Node
for node in model.Nodes:
traces.append(get_trace_node(node))
return traces
# モデルを描画する
def view_model(model, out_file_name):
traces = get_traces(model)
fig = go.Figure(data=traces)
fig['layout']['scene'].update(go.layout.Scene(aspectmode='data'))
po.plot(fig, filename=out_file_name, auto_open=True)
# メイン
def main(path, out_file_name):
model = create_model(path)
view_model(model, out_file_name)
if __name__ == "__main__":
__doc__ = """
Usage:
model_viewer.py
model_viewer.py -h | --help
Options:
-h --help show this help message and exit
"""
args = docopt(__doc__)
model_data_file_path = args['']
out_file_name = os.path.splitext(os.path.basename(model_data_file_path))[0]
main(model_data_file_path, out_file_name)
おまけ
以下の記事でご紹介している複雑なモデルを描画してみました。Pythonのテンプレートエンジンを使って簡単に解析用テキストファイルを作成する
まとめ
今回はPythonからPlotlyを活用した3次元描画のご紹介でした。採用情報
構造計画研究所 RESPチームでは、いっしょに働いていただけるエンジニアを募集しています。構造設計・構造解析だけでなく、プログラミング技術を活かして新しいものを生み出したいと思っている方、ぜひご応募ください。 採用HPはこちら→https://www.kke.co.jp/recruit/