複雑曲線と円周のロフト処理
By K.Yoshimi
測定データをCAD化したり、CAEのためのモデル化をしたりする場合に、 しばしば、複雑な曲線と円周の間をロフト処理によって、曲面でつなげたい場合があります。

これを通常のCADソフトウェアで、ロフト処理すると、無理につなげようとしてエラーを出したり、 作られたとしても、面が捩れていたりします。
例えば、ANSYS SpaceClaimにある ブレンド処理で、2つの面間をつなぐと、下図のように捩れた曲面ができてしまいます。

通常、このような捩れをなくすには、複雑曲線と円周の始点が近い位置にくるように調整したり、 各曲線を同数に分割して、お互いをロフト処理でつなげたりする必要があり、やや面倒です。
この記事では、汎用性はやや低くいですが、複雑曲線と円周をロフト処理する別の方法を提案します。
メッシュを利用したロフト処理
本記事で提案する「メッシュを利用したロフト処理」の方法は、下記の手順で行います。
- 2つの曲線を同一面上に射影
- 2つの曲線間に面を作成
- 2つの曲線間の面に三角形メッシュを生成
- 内側の曲線を法線方向に押し出す(中間のメッシュは各曲線からの距離を見ながら押し出す)
それでは、各手順を見ていきましょう。
2つの曲線を同一面上に射影
まず、円周を複雑曲線を含む面上に射影します。
注意:この提案手法では、下図のように円周が複雑曲線の内部にある必要があります。これが、汎用性の低い所以です。

2つの曲線間に面を作成
次に、2つの曲線間に面を作成します。
この処理は、通常のCAD機能があれば可能でしょう(オープンソースのGmshでも可)。

2つの曲線間の面に三角形メッシュを生成
2つの曲線間の面に、適当なサイズの三角形メッシュを生成します。
下図の三角形は、ANSYS ICEM CFDを使用して作成しています。
(Gmshなどのオープンソースのメッシャーでも作成可能)

作成されたメッシュはSTL形式で出力します。 以下のloft1.stlは、メッシュのサンプルデータです。
内側の曲線を法線方向に押し出す
作成した三角形メッシュを、内側の曲線を法線方向に押し出す形で補間します。
この処理には、以下のようなPythonスクリプト(loft.py)で実行できます。
import argparse
import numpy as np
import vtk
def get_cell_locator(connectivity, region_id):
threshold = vtk.vtkThreshold()
threshold.SetInputData(connectivity.GetOutput())
threshold.ThresholdBetween(region_id, region_id)
threshold.SetInputArrayToProcess(
0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS, 'RegionId')
to_poly = vtk.vtkGeometryFilter()
to_poly.SetInputConnection(threshold.GetOutputPort())
to_poly.Update()
cell_locator = vtk.vtkCellLocator()
cell_locator.SetDataSet(to_poly.GetOutput())
cell_locator.BuildLocator()
return cell_locator
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--input-stl', '-i')
parser.add_argument('--output-stl', '-o')
parser.add_argument('--height')
args = parser.parse_args()
input_stl = args.input_stl
output_stl = args.output_stl
height = float(args.height)
reader = vtk.vtkSTLReader()
reader.SetFileName(input_stl)
reader.Update()
input = reader.GetOutput()
output = vtk.vtkPolyData()
output.DeepCopy(input)
feature_edges = vtk.vtkFeatureEdges()
feature_edges.SetInputConnection(reader.GetOutputPort())
feature_edges.BoundaryEdgesOn()
feature_edges.FeatureEdgesOff()
feature_edges.Update()
connectivity = vtk.vtkConnectivityFilter()
connectivity.SetInputConnection(feature_edges.GetOutputPort())
connectivity.SetExtractionModeToAllRegions()
connectivity.ColorRegionsOn()
connectivity.Update()
outer = get_cell_locator(connectivity, 0)
inner = get_cell_locator(connectivity, 1)
normalGenerator = vtk.vtkPolyDataNormals()
normalGenerator.SetInputData(input)
normalGenerator.ComputePointNormalsOn()
normalGenerator.ComputeCellNormalsOff()
normalGenerator.SplittingOff()
normalGenerator.Update()
normals = normalGenerator.GetOutput().GetPointData().GetArray('Normals')
out_points = vtk.vtkPoints()
out_points.SetNumberOfPoints(input.GetNumberOfPoints())
cell_id = vtk.mutable(0)
closest_point = [0.0, 0.0, 0.0]
sub_id = vtk.mutable(0)
closestPointDist2 = vtk.mutable(0.0)
for pt_id in range(input.GetNumberOfPoints()):
point = np.array(input.GetPoint(pt_id))
normal = np.array(normals.GetTuple3(pt_id))
outer.FindClosestPoint(point, closest_point, cell_id, sub_id, closestPointDist2)
d0 = np.sqrt(float(closestPointDist2))
inner.FindClosestPoint(point, closest_point, cell_id, sub_id, closestPointDist2)
d1 = np.sqrt(float(closestPointDist2))
h = d0 * height / (d0 + d1)
out_points.SetPoint(pt_id, point + normal*h)
output.SetPoints(out_points)
writer = vtk.vtkSTLWriter()
writer.SetFileName(output_stl)
writer.SetInputData(output)
writer.Write()
if __name__ == '__main__':
main()
上記スクリプトは、下記のコマンドで実行します。
python loft.py -i loft1.stl -o out.stl --height 5.0
ここで、オプションの意味は、下記です。
- -i: 入力するSTLファイル
- -o: loft処理したSTLファイル
- –height: 内側カーブを法線方向に押し出す高さ(マイナスにすると逆方法に押し出す)
このスクリプトで押し出した結果は、下図のようになります。

三角形メッシュのCAD化(ソリッド化)
最後に、必要であれば、作成された三角形メッシュをCAD化(ソリッド化)します。
例えば、ANSYS SpaceClaimのオートスキン機能を使用すると、 下図のように、三角形から滑らかなCADパッチを作成することができます。
