皆様こんにちは。
AMDlabの齋藤です。
今回はMarionette Mesh[1]と呼ばれる高速に平面四角形のMeshが作成できるものを作って触ってみようかと思います。
今回のデータはこちらに挙げております。
https://github.com/Saito40/TechBlog-Marionette-Mesh
Marionette Meshとは
Marionette Meshとは、高速に平面四角形のMesh(PQ mesh)を作ることができるものです。
各四角形を平板として製作できるため、ガラス等の平板材料で面を張ることができ、
三角形メッシュよりパネル数や接合部を減らしやすくもあります。
基本的なMarionette MeshはXY平面にグリッド、XZ平面、YZ平面にそれぞれ曲線を定めてMeshを作成します。
ここでグリッド上の以下に示した点を点A~Dとします。
点Aを 平面PYZ もしくは 平面PXZ に投影し、その投影点から平面PXY に対して垂線を引きます。
この垂線と曲線の交点をとり、そして平面PXY との高さを ZA とします。
同じように点B、Dについても ZB 、 ZD を取ります。
そして点A、B、Dをそれぞれ ZA 、 ZB 、 ZD 分だけZ方向に移動して、点A’、B’、D’を取ります。
平面は3点で決まるため、Cから 平面PXY に垂線を引き、点A’、B’、D’で構成される平面との交点を点C’とします。
あとはこれを繰り返してMarionette Meshを生成します。
適当に作成した四角形メッシュから平面となる形状を探索する方法とは違い、
このように初めから平面となる点で構成するため、簡単に平面メッシュが作成できます。
さらに、グリッド、2つの曲線と少ない入力で、多様な自由曲面形状が作成できます。
Marionette Meshを作ってみる
それではMarionette Meshを作ってみます。
今回は曲線の入力をRhinoから調整できるようRhinoで取得させるようにしてみます。
グリッドの入力も4つの曲線から構成する方法にします。
ここで今回は簡単のために、原点(0, 0, 0)がXZ平面上の曲線、YZ平面の曲線の上に乗るようにします。
次にグリッドを構成します。
今回はSweep2というコンポーネントでSurfaceを作成してグリッドを構成します。
R1, R2はSurfaceの両サイドを入れ、Sはその反対側を入れます。
詳しい使い方はGrasshopper Indexを確認してください。
https://www.applicraft.com/ghcp_surface/#:~:text=%E6%A9%9F%E8%83%BD%E3%82%92%E8%A6%8B%E3%82%8B-,Sweep2,-*
作成したSurfaceにUV座標を入れてグリッドの点を出します。
SurfaceはそのままではUVの範囲が0~1となっていないため、SurfaceをReparameterizeで0~1の範囲となるようにします。
そしてUV座標を入れてグリッドを作成します。
最後にMeshを構成していきます。
今回はPythonでMeshを作成します。
Python 3でライブラリを使うほどではないため、IronPython 2 Scriptで構成しますが、Python 3 Scriptでも同じことができます。
入力は上から順に
- base_pt , Type Hint: Point3d, Tree Access, グリッド点
- curve_x , Type Hint: Curve, Item Access, XZ平面上の曲線
- curve_y , Type Hint: Curve, Item Access, YZ平面上の曲線
としています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
from Rhino.Geometry import Point3d, Line, Plane, Mesh from Rhino.Geometry.Intersect import Intersection from Grasshopper import DataTree from Grasshopper.Kernel.Data import GH_Path from ghpythonlib.treehelpers import tree_to_list, list_to_tree tol = 1e-3 result = tree_to_list(base_pt, lambda x : x[0][0]) ny = len(result) nx = len(result[0]) ### 1. X方向のガイド bbox = curve_x.GetBoundingBox(True) bbox.Inflate(1000) for i in range(nx): base = result[0][i] line = Line( Point3d(base.X, 0.0, bbox.Min.Z), Point3d(base.X, 0.0, bbox.Max.Z) ) events = Intersection.CurveLine( curve_x, line, tol, tol) z = events[0].PointA.Z result[0][i] = Point3d( base.X, base.Y, z) ### 2. Y方向のガイド bbox = curve_y.GetBoundingBox(True) bbox.Inflate(1000) for j in range(ny): base = result[j][0] line = Line( Point3d(0.0, base.Y, bbox.Min.Z), Point3d(0.0, base.Y, bbox.Max.Z) ) events = Intersection.CurveLine( curve_y, line, tol, tol) z = events[0].PointA.Z result[j][0] = Point3d( base.X, base.Y, z) ### 3. Meshを構成 for j in range(1, ny): for i in range(1, nx): a = result[j - 1][i - 1] b = result[j - 1][i] d = result[j][i - 1] base = result[j][i] plane = Plane(a, b, d) line = Line( base, base + Point3d(0, 0, 1)) _, l_param = Intersection.LinePlane(line, plane) result[j][i] = line.PointAt(l_param) ### 4. RhinoのMeshに直す mesh = Mesh() vertex_index = [] for j in range(ny): ids = [] for i in range(nx): index = mesh.Vertices.Add( result[j][i] ) ids.append(index) vertex_index.append(ids) for j in range(ny - 1): for i in range(nx - 1): a = vertex_index[j][i] b = vertex_index[j][i + 1] c = vertex_index[j + 1][i + 1] d = vertex_index[j + 1][i] mesh.Faces.AddFace( a, b, c, d) mesh.Normals.ComputeNormals() mesh.Compact() |
まず、
curve_x 、
curve_y のガイドを作成します。
交点の取得は、
Intersection.CurveLine で取るようにしました。
|
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
### 1. X方向のガイド bbox = curve_x.GetBoundingBox(True) bbox.Inflate(1000) for i in range(nx): base = result[0][i] line = Line( Point3d(base.X, 0.0, bbox.Min.Z), Point3d(base.X, 0.0, bbox.Max.Z) ) events = Intersection.CurveLine( curve_x, line, tol, tol) z = events[0].PointA.Z result[0][i] = Point3d( base.X, base.Y, z) ### 2. Y方向のガイド bbox = curve_y.GetBoundingBox(True) bbox.Inflate(1000) for j in range(ny): base = result[j][0] line = Line( Point3d(0.0, base.Y, bbox.Min.Z), Point3d(0.0, base.Y, bbox.Max.Z) ) events = Intersection.CurveLine( curve_y, line, tol, tol) z = events[0].PointA.Z result[j][0] = Point3d( base.X, base.Y, z) |
その後、Meshを構成します。
論文のアルゴリズムでは計算で算出する方法を取っていましたが、RhinoCommonが使えるため、
Intersection.LinePlane で面と線の交点を出すようにしました。
|
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
### 3. Meshを構成 for j in range(1, ny): for i in range(1, nx): a = result[j - 1][i - 1] b = result[j - 1][i] d = result[j][i - 1] base = result[j][i] plane = Plane(a, b, d) line = Line( base, base + Point3d(0, 0, 1)) _, l_param = Intersection.LinePlane(line, plane) result[j][i] = line.PointAt(l_param) |
あとは後処理でMeshを作成し、Meshの外形を表示させて完成です。
工夫すれば円状の閉じたMarionette Meshも作成することができます。
おわりに
今回はMarionette Meshを構成してみました。
簡単に平面四角形のMeshが構成できたと思います。
皆さんもぜひ試してみてください。
参考文献
[1] Mesnil, Romain, et al. “Marionette Meshes: Modelling free-form architecture with planar facets.” International Journal of Space Structures 32.3-4 (2017): 184-198.
AMDlabでは、開発に力を貸していただけるエンジニアさんを大募集しております。少しでもご興味をお持ちいただけましたら、カジュアルにお話するだけでも大丈夫ですのでお気軽にご連絡ください!
中途求人ページ: https://www.amd-lab.com/recruit-list/mid-career
カジュアル面談がエントリーフォームからできるようになりました。
採用種別を「カジュアル面談(オンライン)」にして必要事項を記載の上送信してください!
エントリーフォーム: https://www.amd-lab.com/entry
AMDlabのSNSアカウントです!ぜひフォローお願いします✨
■ X(旧Twitter):https://x.com/amdlabinc
■ Instagram:https://www.instagram.com/amdlabinc/
■ Facebook:https://www.facebook.com/amdlab.lnc/





















COMMENTS