Mesh to Part/ja: Difference between revisions

From FreeCAD Documentation
(Created page with "== Partオブジェクトをメッシュに変換する ==")
No edit summary
Line 1: Line 1:
<languages/>
<languages/>
== Partオブジェクトをメッシュに変換する ==

== Partオブジェクトをメッシュに変換する ==
== Partオブジェクトをメッシュに変換する ==


Line 19: Line 17:
m = Mesh.Mesh(faces)
m = Mesh.Mesh(faces)
Mesh.show(m)
Mesh.show(m)

ときたまOpenCascadeが出力する特定の面の三角形が非常にいびつな場合があります。もし面に長方形パラメータ空間があり、穴やトリム曲線が無ければ独自にメッシュを作成することもできます:

import Mesh
def makeMeshFromFace(u,v,face):
(a,b,c,d)=face.ParameterRange
pts=[]
for j in range(v):
for i in range(u):
s=1.0/(u-1)*(i*b+(u-1-i)*a)
t=1.0/(v-1)*(j*d+(v-1-j)*c)
pts.append(face.valueAt(s,t))
mesh=Mesh.Mesh()
for j in range(v-1):
for i in range(u-1):
mesh.addFacet(pts[u*j+i],pts[u*j+i+1],pts[u*(j+1)+i])
mesh.addFacet(pts[u*(j+1)+i],pts[u*j+i+1],pts[u*(j+1)+i+1])
return mesh

== メッシュをPartオブジェクトに変換する ==

メッシュからPartオブジェクトへの変換はCADでの作業でも非常に重要な操作です。他の人たちからメッシュ形式の3Dデータを受け取ったり、他のアプリケーションからメッシュ形式の3Dデータを出力するというのはよくあることだからです。自由形状や大きな描画シーンを表現する場合にはメッシュは非常に実用的です。とても軽量だからです。しかしCADでの用途を考えた場合、一般的には三角形ではなく曲線で作成された面やソリッドなど、もっと多くの情報を保持できるより上位のオブジェクトの方が好まれます。

メッシュからこういった(FreeCADの[[Part Module/jp|パートモジュール]]で扱われる様な )上位オブジェクトへの変換は簡単な処理ではありません。メッシュは数千もの三角形で構成されている場合もありますし(例えば3Dスキャナーによって生成されている場合)、面と同じだけの数のソリッドを持った場合には処理がとんでもなく重くなることも考えられます。従って多くの場合、変換時に最適化を施す必要があるのです。

FreeCADでは現在、メッシュをPartオブジェクトに変換する二つのメソッドが用意されています。一つ目は単純なもので何の最適化も施さずに直接変換を行います:

import Mesh,Part
mesh = Mesh.createTorus()
shape = Part.Shape()
shape.makeShapeFromMesh(mesh.Topology,0.05) # 二つ目の引数は縫い合わせの許容誤差です
solid = Part.makeSolid(shape)
Part.show(solid)

二つ目のメソッドではメッシュの二つのファセットが作る角度が特定の値を下回る場合にその二つを同一面とみなします。これによってより単純な形状が作成できるのです:

# ドキュメントには一つだけMeshオブジェクトが入っているとしましょう
import Mesh,Part,MeshPart
faces = []
mesh = App.ActiveDocument.ActiveObject.Mesh
segments = mesh.getPlanes(0.00001) # ここではより厳しい許容誤差を使用します
for i in segments:
if len(i) > 0:
# 線分は内部に穴を持つことができます
wires = MeshPart.wireFromSegment(mesh, i)
# 外部境界が最大のバウンディングボックスを持つものであると仮定します
if len(wires) > 0:
ext=None
max_length=0
for i in wires:
if i.BoundBox.DiagonalLength > max_length:
max_length = i.BoundBox.DiagonalLength
ext = i
wires.remove(ext)
# 内部のワイヤーが全てマークされ、向きが反転されなければなりません。さもなければPart.Faceが失敗します
for i in wires:
i.reverse()
# 外部のワイヤーがリストの先頭になっていることを確認してください
wires.insert(0, ext)
faces.append(Part.Face(wires))
shell=Part.Compound(faces)
Part.show(shell)
#solid = Part.Solid(Part.Shell(faces))
#Part.show(solid)
{{Code|code=
{{Code|code=
#let's assume our document contains one part object
#let's assume our document contains one part object

Revision as of 12:30, 7 January 2019

Partオブジェクトをメッシュに変換する

Part形状のような上位レベルのオブジェクトを メッシュのような単純なオブジェクトに変換するのは比較的簡単な処理です。Partオブジェクトの全ての面を三角形に変換して、結果の三角形(モザイク)をメッシュ構築に使用すればいいのです:

# ドキュメントには一つだけPartオブジェクトが入っているとしましょう
import Mesh
faces = []
shape = FreeCAD.ActiveDocument.ActiveObject.Shape
triangles = shape.tessellate(1) # この数値がモザイクの精度を表します
for tri in triangles[1]:
    face = []
    for i in range(3):
        vindex = tri[i]
        face.append(triangles[0][vindex])
    faces.append(face)
m = Mesh.Mesh(faces)
Mesh.show(m)
#let's assume our document contains one part object
import Mesh
faces = []
shape = FreeCAD.ActiveDocument.ActiveObject.Shape
triangles = shape.tessellate(1) # the number represents the precision of the tessellation)
for tri in triangles[1]:
    face = []
    for i in range(3):
        vindex = tri[i]
        face.append(triangles[0][vindex])
    faces.append(face)
m = Mesh.Mesh(faces)
Mesh.show(m)

Sometimes the triangulation of certain faces offered by OpenCascade is quite ugly. If the face has a rectangular parameter space and doesn't contain any holes or other trimming curves you can also create a mesh on your own:

import Mesh
def makeMeshFromFace(u,v,face):
	(a,b,c,d)=face.ParameterRange
	pts=[]
	for j in range(v):
		for i in range(u):
			s=1.0/(u-1)*(i*b+(u-1-i)*a)
			t=1.0/(v-1)*(j*d+(v-1-j)*c)
			pts.append(face.valueAt(s,t))

	mesh=Mesh.Mesh()
	for j in range(v-1):
		for i in range(u-1):
			mesh.addFacet(pts[u*j+i],pts[u*j+i+1],pts[u*(j+1)+i])
			mesh.addFacet(pts[u*(j+1)+i],pts[u*j+i+1],pts[u*(j+1)+i+1])

	return mesh

Converting Meshes to Part objects

Converting Meshes to Part objects is an extremely important operation in CAD work, because very often you receive 3D data in mesh format from other people or outputted from other applications. Meshes are very practical to represent free-form geometry and big visual scenes, as it is very lightweight, but for CAD we generally prefer higher-level objects that carry much more information, such as the idea of solid, or faces made of curves instead of triangles.

Converting meshes to those higher-level objects (handled by the Part Module in FreeCAD) is not an easy operation. Meshes can be made of thousands of triangles (for example when generated by a 3D scanner), and having solids made of the same number of faces would be extremely heavy to manipulate. So you generally want to optimize the object when converting.

FreeCAD currently offers two methods to convert Meshes to Part objects. The first method is a simple, direct conversion, without any optimization:

import Mesh,Part
mesh = Mesh.createTorus()
shape = Part.Shape()
shape.makeShapeFromMesh(mesh.Topology,0.05) # the second arg is the tolerance for sewing
solid = Part.makeSolid(shape)
Part.show(solid)

The second method offers the possibility to consider mesh facets coplanar when the angle between them is under a certain value. This allows to build much simpler shapes: (let's assume our document contains one Mesh object)

# let's assume our document contains one Mesh object
import Mesh,Part,MeshPart
faces = []
mesh = App.ActiveDocument.ActiveObject.Mesh
segments = mesh.getPlanes(0.00001) # use rather strict tolerance here
 
for i in segments:
  if len(i) > 0:
     # a segment can have inner holes
     wires = MeshPart.wireFromSegment(mesh, i)
     # we assume that the exterior boundary is that one with the biggest bounding box
     if len(wires) > 0:
        ext=None
        max_length=0
        for i in wires:
           if i.BoundBox.DiagonalLength > max_length:
              max_length = i.BoundBox.DiagonalLength
              ext = i

        wires.remove(ext)
        # all interior wires mark a hole and must reverse their orientation, otherwise Part.Face fails
        for i in wires:
           i.reverse()

        # make sure that the exterior wires comes as first in the lsit
        wires.insert(0, ext)
        faces.append(Part.Face(wires))

shell=Part.Compound(faces)
Part.show(shell)
#solid = Part.Solid(Part.Shell(faces))
#Part.show(solid)
Topological data scripting
Scenegraph