Topological data scripting/jp

はじめに
FreeCADのpythonインタプリタや外部のスクリプトから、直接、Partモジュールを操作する方法を説明します. まずスクリプティングを参照してください. FreeCADでのpythonスクリプトの方法についてより詳しい情報が必要な場合はFreeCADスクリプトの基本ページを参照してください.

Partモジュールの機能を使用するには、まずインタプリタにPartモジュールをロードする必要があります：

クラス・ダイアグラム
これはPartモジュールの最も重要なクラスに関するUMLの概要図です：

ジオメトリ
ジオメトリオブジェクトは、トポロジーオブジェクトを構成するための基本要素です：
 * GEOM ジオメトリオブジェクトの基底クラスです.
 * LINE 始点と終点で定義された3次元の直線です.
 * CIRCLE 中心と始点と終点で定義される円や円の断片です.
 * ...... じきに増えるでしょう;-)

トポロジー
次の種類のトポロジーのデータが利用できます：
 * COMPOUND 任意の種類のトポロジーオブジェクトのグループです.
 * COMPSOLID ソリッドの合成とは、フェイスで接続するソリッドの集合です. ワイヤとシェルの概念をソリッドへ拡張したものです.
 * SOLID（ソリッド） シェルで区切られた空間です. 3次元です.
 * SELL（シェル） エッジで接続しているフェイスの集合です. シェルは開閉できます.
 * FACE（フェイス） 2次元では平面の一部分、3次元では表面の一部分です. 形状は輪郭線で律則されています（切り取られています）. ２次元です.
 * WIRE（ワイヤ） 頂点で接続するエッジの集合です. エッジの接続に応じて、輪郭線は開閉します.
 * EDGE（エッジ） 制約された曲線に対応する位相要素です. エッジは一般的に頂点によって律則されます. 次元は1次元です.
 * VERTEX（頂点） 点に対応するトポロジーの要素です. 次元は0です.
 * SHAPE（シェイプ） 上記全ての一般的な総称です.

簡単な説明:
Partモジュールの"make ..."メソッドを使って、簡単に基本的なトポロジーオブジェクトを作成することができます. b=Part.makeBox(100,100,100) Part.show(b)

使用可能な他のmake...メソッドです：
 * makeBox(l,w,h,[p,d]) -- 寸法が(l,w,h)で、pからd方向を指す箱を作ります. デフォルトのpはVector(0,0,0)で、dはVector(0,0,1)です.
 * makeCircle(radius,[p,d,angle1,angle2]) -- 指定した半径の円を作ります. 初期値は、p=Vector(0,0,0)、d=Vector(0,0,1)、angle1=0、angle2=360です.
 * makeCompound(list) -- シェイプのリストを合成します.
 * makeCone(radius1,radius2,height,[p,d,angle]) -- 指定した半径と高さの円錐を作成します. 初期値はp=Vector(0,0,0)、d=Vector(0,0,1)、angle=360です.
 * makeCylinder(radius,height,[p,d,angle]) -- 指定した半径と高さを持つ円柱を作成します. 初期値はp=Vector(0,0,0)、d=Vector(0,0,1)、angle=360です.
 * makeLine((x1,y1,z1),(x2,y2,z2)) -- 二点間を結ぶ線を作成します.
 * makePlane(length,width,[p,d]) -- 指定した幅と長さの平面を作成します. 初期値は、p=Vector(0,0,0)、d=Vector(0,0,1)です.
 * makePolygon(list) -- 点のリストからポリゴンを作成します.
 * makeSphere(radius,[p,d,angle1,angle2,angle3]) -- 指定された半径の球を作成します. 初期値は、p=Vector(0,0,0)、d=Vector(0,0,1)、angle1=0、angle2=90、angle3=360です.

初期値は、p=Vector(0,0,0)、d=Vector(0,0,1)、angle1=0、angle2=360、angle3=360です.
 * makeTorus(radius1,radius2,[p,d,angle1,angle2,angle3]) -- 指定した半径のトーラスを作ります.

詳細な説明
最初に、次のインポートを行います：

>>> import Part >>> from FreeCAD import Base

頂点を作るには？
vertexはx=1,y=0,z=0に位置する点です. 与えられた頂点オブジェクトの座標を調べることもできます：

エッジを作るには？
エッジとは2つの頂点からなる線に他なりません： 注意：頂点オブジェクトを指定してエッジを作成することはできません. 代わりにタプルで指定します. また、エッジオブジェクトでは、長さ、および中央の位置を知ることができます：

ワイヤを作るには？
ワイヤはエッジのリスト、またはワイヤのリストから作成することができます： Part.show(wire3)は正方形をなす4つの線を表示します：

フェイスを作るには？
正しいフェイスは閉じたワイヤのみから作成することができます. この例では、wire3は閉じたワイヤですが、wire2（上記参照）は閉じていません.

フェイスは面積を持ちますが、ワイヤやエッジは面積を持ちません.

円を作るには？
circle = Part.makeCircle(radius,[center,dir_normal,angle1,angle2]) -- 指定した半径の円を作成します.

初期値はcenter=Vector(0,0,0)、dir_normal=Vector(0,0,1)、angle1=0、angle2=360です. 円はこのようにシンプルに作成することができます： 位置と方向をもった円を作るにはこのように書きます. ccircleは、xの原点からの距離が10で、x軸を向くように作成されています. 注：makeCircleは、位置と法線として、タプルではなくBase.Vectorのみを受けとります. また開始角と終了角を与えることによって、円の断片を作成することができます： arc1とarc2の両方を合わせると円になります. 角度は度単位で指定する必要があります. ラジアンが必要な場合は次の様に変換することができます. degrees = radians * 180/PI またはpythonの数学モジュールを使用します（import mathが必要です）： degrees = math.degrees(radians)

点に通る弧を作るには？
残念ながらmakeArc関数はありませんが、三点を通る弧を作成するPart.Arc関数があります. この関数は、基本的には、中間点を通って開始点と終了点を結ぶ弧を仮定しています. Part.Arcは弧オブジェクトを作成します. 弧オブジェクトからエッジオブジェクトを作成するにはtoShapeを呼びます. エッジオブジェクトは、一般には、makeLineやmakeCircleで作成されるものです. 注意：Arcは点としてタプルではなくBase.Vectorのみを受け取ります. arc_edgeはPart.show(arc_edge)によって表示することができます. 円弧として円の一部を使用することも可能です：

ポリゴンや、点を通るラインを作るには？
複数の点を通る線は、複数のエッジからなるワイヤを作成することに他なりません. makePolygon関数は、点のリストを受け取り、その点を通る線を作成します：

平面を作るには？
平面は平坦な表面であり、2次元のフェイスです. makePlane(length,width,[start_pnt,dir_normal]) -- 平面を作成します. 初期値はstart_pnt=Vector(0,0,0)とdir_normal=Vector(0,0,1)です. dir_normal=Vector(0,0,1)はz軸に面した平面を作成します. dir_normal=Vector(1,0,0)はx軸に面した平面を作成します. BoundBoxは、その対角線が（3,0,0）から始まり（5,0,2)で終わる平面を囲む直方体です. このBoundBoxのy軸の厚さはゼロです. 注：makePlaneは、start_pntとdir_normalとしてタプルではなくBase.Vectorのみを受け取ります.

楕円を作るには？
楕円の作成にはいくつかの方法があります： Part.Ellipse

中心が(0,0,0)で、大半径2と小半径1の楕円を作成します.

Part.Ellipse(Ellipse)

楕円のコピーを作成します.

Part.Ellipse(S1,S2,Center)

点Centerを中心とする楕円を作成します. 楕円平面は、Center、S1とS2で定義されています. 主軸はCenterとS1で定義されています. 長径はCenterS1の間の距離です 短径はS2と主軸との距離です.

Part.Ellipse(Center,MajorRadius,MinorRadius)

長径MajorRadius、短径MinorRadiusの楕円を作成し、 Centerと法線(0,0,1)で定義された平面上に配置します.

上記のコードでは、S1、S2、および中心を渡しています. Arcと同様に、Ellipseも楕円オブジェクトを作りますが、エッジは作成しません. このため、楕円を表示するためにはtoShapeを使い、エッジに変換する必要があります.

注意：Arcはタプルではなく、Base.Vectorのみを受け取ります.

上記のEllipseのコンストラクタでは、中心座標、MajorRadius、MinorRadiusを渡しています.

トーラスを作るには？
makeTorus(radius1,radius2,[pnt,dir,angle1,angle2,angle]) --指定した半径と角度のトーラスを作成します. 初期値は、pnt=Vector(0,0,0)、dir=Vector(0,0,1)、angle1=0、angle1=360、angle=360です.

トーラスは、小さな円が、大きな円に沿ってスイープしているものと考えます：

radius1は大きい円の半径、radius2は小さい円の半径です. pntはトーラスの中心、dirは法線方向です. angle1とangle2は、小さな円のラジアンの角度で、円弧を作成します. 最後のパラメータangleは、トーラスの断片を作成します： 上記のコードは、直径20（半径10）と厚さ4（小さい円の半径2）のトーラスを作成します. 上記のコードは、トーラスのスライスを作成します. 上記のコードは半分のトーラスを作成します. このコードでは最後のパラメータが変更されただけで、あとの角度は初期値のままです.

角度へ180与えた結果、0から180、つまり半分のトーラスを作成します.

ボックスまたは直方体を作るには？
makeBox(length,width,height,[pnt,dir]) -- pntに位置し、寸法(length,width,height)を持ったボックスを作成します.

初期値は、pnt=Vector(0,0,0)、dir=Vector(0,0,1)です.

球を作るには？
makeSphere(radius,[pnt, dir, angle1,angle2,angle3]) -- 指定した半径を持つ球を作成します. 初期値は、pnt=Vector(0,0,0)、dir=Vector(0,0,1)、angle1=-90、angle2=90、angle3=360です. angle1とangle2は、球の垂直方向の最小値と最大値です. angle3は球の直径です.

円柱を作るには？
makeCylinder(radius,height,[pnt,dir,angle]) -- 与えられた半径と高さを持つ円柱を作成します.

初期値は、pnt=Vector(0,0,0)、dir=Vector(0,0,1)、angle=360です.

円錐を作るには？
makeCone(radius1,radius2,height,[pnt,dir,angle]) -- 指定した半径と高さの円錐を作ります.

初期値は、pnt=Vector(0,0,0)、dir=Vector(0,0,1)、angle=360=ベクトルです.

あるシェイプを他のシェイプからカットするには？
cut(...) 与えられた位相形状との差をとります.

2つのシェイプ間の積を得るには？
common(...) 与えられた位相形状との積をとります.

２つのシェイプ間の和をとるには？
fuse(...) オブジェクトと与えられた位相形状との和をとります.

ソリッドを与えられたシェイプで切断するには？
section(...) 与えられた位相形状でオブジェクトを切り取ります.

交差曲線（エッジの合成物）を返します.

シェイプを調査する
トポロジーのデータ構造は簡単に調べることができます： import Part b = Part.makeBox(100,100,100) b.Wires w = b.Wires[0] w w.Wires w.Vertexes Part.show(w) w.Edges e = w.Edges[0] e.Vertexes v = e.Vertexes[0] v.Point

上記の内容をPythonインタープリタに入力することで、Partオブジェクトの構造をよく理解を得ることができます. ここでmakeBoxコマンドはソリッド形状を作成します. このソリッドは、すべてのPartソリッドと同様に、複数のフェイスを含んでいます. フェイスは常に、その境界となる、エッジのリストであるワイヤを持ちます. 各フェイスは正確に1つの閉じたワイヤを持ちます. ワイヤでは、各エッジを別々に調べることができ、またそのエッジでは各頂点を調べることができます. 直線のエッジは当然2つの頂点のみを持っています. Part VertexesはOCCのshapeですが、Point属性を持ちFreeCAD Vectorを返します.

エッジを調査する
エッジの場合、エッジは任意の曲線であるので、おそらく離散化したいと思うでしょう. FreeCADでは、エッジはその長さによってパラメータ化されています. これはつまり、その長さでエッジや曲線を伸ばすことです： import Part anEdge = Part.makeBox(100,100,100).Edges[0] # 100mmのエッジ長をもつボックスを作成し、その最初のエッジを取り出します print anEdge.Length # エッジの長さ（mm、設計単位）を得ます この長さを位置として使用して、多くのエッジのプロパティにアクセスすることができます. これはつまり エッジ長が100mmであれば、エッジの開始位置は0で終了位置は100であるということです. anEdge.tangentAt(0.0) # 初期の接線方向です anEdge.valueAt(0.0) # エッジの開始点です anEdge.valueAt(100.0) # エッジの終点です anEdge.derivative1At(50.0) #　中点での曲線の一次導関数です anEdge.derivative2At(50.0) # 中点での曲線の二次導関数です anEdge.derivative3At(50.0) # 中点での曲線の三次導関数です anEdge.centerOfCurvatureAt(50) # この位置の曲率中心です anEdge.curvatureAt（50.0）# 曲率です anEdge.normalAt(50) # この位置の法線です（定義されている場合）

選択を使う
ここでは、ユーザがビューア上で行う選択操作を使用する方法についてみていきます. まず最初に、ボックスを作成し、それをビューアで表示します

import Part Part.show(Part.makeBox(100,100,100)) Gui.SendMsgToActiveView("ViewFit") フェイスかエッジを選択します. このスクリプトを使うと 全ての選択したオブジェクトとその子要素を反復することができます. for o in Gui.Selection.getSelectionEx: print o.ObjectName for s in o.SubElementNames: print "name: ",s for s in o.SubObjects: print "object: ",s

エッジを選択し、このスクリプトでその長さを計算します: length = 0.0 for o in Gui.Selection.getSelectionEx: for s in o.SubObjects: length += s.Length print "Length of the selected edges:" ,length

単純なトポロジを作成する
簡単な幾何形状を用いてトポロジーを作成していきます. 例として、図に示した 4つの頂点と2つの円と2つの線からなる部品を用いていきます.

ジオメトリを作成する
まず、このワイヤを構成する幾何学的な要素を作成する必要があります. 幾何学的な要素の頂点が 同じ位置 になるものに注意します. そうでない場合は、後で 幾何学的要素をトポロジーに接続できなくなっています！

そのため、まず点を最初に作成します： from FreeCAD import Base V1 = Base.Vector(0,10,0) V2 = Base.Vector(30,10,0) V3 = Base.Vector(30,-10,0) V4 = Base.Vector(0,-10,0)

円弧
円弧を作成するには、まず補助点を作り、それから三点を通る円弧を作成します.

VC1 = Base.Vector(-10,0,0) C1 = Part.Arc(V1,VC1,V4) VC2 = Base.Vector(40,0,0) C2 = Part.Arc(V2,VC2,V3)
 * 1) 二つ目です

線
線は点を使ってとても簡単につくることができます： L1 = Part.Line(V1,V2) L2 = Part.Line(V4,V3)
 * 1) 二つ目です

全て一緒にまとめる
最後のステップは、幾何学的基本要素を一緒にまとめ、 位相形状を作り上げます: S1 = Part.Shape([C1,C2,L1,L2])

プリズムを作る
ワイヤをある方向へ押し出し、3Dの実体を作成します： W = Part.Wire(S1.Edges) P = W.extrude(Base.Vector(0,0,10))

OCCボトル
OpenCasCadeをはじめようにはボトルの作成方法についての典型的な例があります. これはFreeCADの教材としても最適です. 実際に以下のページの例とOCCのページの内容とを同時に追うことによって、OCCの構造がどのようにFreeCADの中に実装されているか理解できるでしょう.

以下のスクリプトはFreeCADのインストールにも含まれています（Mod/Partフォルダ内）. pythonインタプリタから次のようにタイプすることで呼び出すことができます: import Part import MakeBottle bottle = MakeBottle.makeBottle Part.show(bottle)

完全なスクリプト
MakeBottleスクリプト一式です：

import Part, FreeCAD, math from FreeCAD import Base def makeBottle(myWidth=50.0, myHeight=70.0, myThickness=30.0): aPnt1=Base.Vector(-myWidth/2.,0,0) aPnt2=Base.Vector(-myWidth/2.,-myThickness/4.,0) aPnt3=Base.Vector(0,-myThickness/2.,0) aPnt4=Base.Vector(myWidth/2.,-myThickness/4.,0) aPnt5=Base.Vector(myWidth/2.,0,0) aArcOfCircle = Part.Arc(aPnt2,aPnt3,aPnt4) aSegment1=Part.Line(aPnt1,aPnt2) aSegment2=Part.Line(aPnt4,aPnt5) aEdge1=aSegment1.toShape aEdge2=aArcOfCircle.toShape aEdge3=aSegment2.toShape aWire=Part.Wire([aEdge1,aEdge2,aEdge3]) aTrsf=Base.Matrix aTrsf.rotateZ(math.pi) # z軸周りを回転 aMirroredWire=aWire.transformGeometry(aTrsf) myWireProfile=Part.Wire([aWire,aMirroredWire]) myFaceProfile=Part.Face(myWireProfile) aPrismVec=Base.Vector(0,0,myHeight) myBody=myFaceProfile.extrude(aPrismVec) myBody=myBody.makeFillet(myThickness/12.0,myBody.Edges) neckLocation=Base.Vector(0,0,myHeight) neckNormal=Base.Vector(0,0,1) myNeckRadius = myThickness / 4. myNeckHeight = myHeight / 10 myNeck = Part.makeCylinder(myNeckRadius,myNeckHeight,neckLocation,neckNormal) myBody = myBody.fuse(myNeck) faceToRemove = 0 zMax = -1.0 for xp in myBody.Faces: try: surf = xp.Surface if type(surf) == Part.Plane: z = surf.Position.z               if z > zMax: zMax = z                   faceToRemove = xp        except: continue myBody = myBody.makeThickness([faceToRemove],-myThickness/50, 1.e-3) return myBody

詳細な説明
import Part, FreeCAD, math from FreeCAD import Base Partモジュールはもちろん、FreeCAD.Baseモジュールが必要です. このモジュールには、ベクトルや行列といったFreeCADの基本構造が含まれています. def makeBottle(myWidth=50.0, myHeight=70.0, myThickness=30.0): aPnt1=Base.Vector(-myWidth/2.,0,0) aPnt2=Base.Vector(-myWidth/2.,-myThickness/4.,0) aPnt3=Base.Vector(0,-myThickness/2.,0) aPnt4=Base.Vector(myWidth/2.,-myThickness/4.,0) aPnt5=Base.Vector(myWidth/2.,0,0) Here we define our makeBottle function. This function can be called without arguments, like we did above, in which case default values for width, height, and thickness will be used. Then, we define a couple of points that will be used for building our base profile. aArcOfCircle = Part.Arc(aPnt2,aPnt3,aPnt4) aSegment1=Part.Line(aPnt1,aPnt2) aSegment2=Part.Line(aPnt4,aPnt5)

ここではmakeBottle関数を定義します. この関数は、上記の例のように、引数無しでも呼ぶことができます. その場合には、幅、高さ、厚さの初期値が使用されます. 次に、基本的なプロファイルの構築に使用する点を定義します. aArcOfCircle = Part.Arc(aPnt2,aPnt3,aPnt4) aSegment1=Part.Line(aPnt1,aPnt2) aSegment2=Part.Line(aPnt4,aPnt5) ここでは実際のジオメトリを定義しています. 円弧を一つ、点を三つ、そして二点で構成される線分を二つ作ります. aEdge1=aSegment1.toShape aEdge2=aArcOfCircle.toShape aEdge3=aSegment2.toShape aWire=Part.Wire([aEdge1,aEdge2,aEdge3]) ジオメトリとシェイプの違いを覚えているでしょうか？ここでは、作成したジオメトリを使ってシェイプを作り上げています. 3つのエッジ（直線や曲線です）を作り、それらを使いワイヤを一つ作成します. aTrsf=Base.Matrix aTrsf.rotateZ(math.pi) # rotate around the z-axis aMirroredWire=aWire.transformGeometry(aTrsf) myWireProfile=Part.Wire([aWire,aMirroredWire]) ここまでで、まだプロファイルは半分しか出来上がっていません. 同じ方法でプロファイル全体を作成するよりも、これまで作成したものをミラーして、2つの半分を一つにくっつけるほうが簡単です. それにはまず行列を作成します. 行列は3次元空間のオブジェクトを変換するとても一般的な方法です. 行列を使えば、3Dオブジェクトの厄介な基本的な変換（移動、回転、スケール）が一つで済むのです. ここでは行列を作成し、それを反転します. 作成したワイヤにこの変換行列を適用してコピーを作ります. ここまで、2本のワイヤができました. ワイヤは、実際にはエッジのリストですので、これらのワイヤから3つ目のワイヤを作ることができます. myFaceProfile=Part.Face(myWireProfile) aPrismVec=Base.Vector(0,0,myHeight) myBody=myFaceProfile.extrude(aPrismVec) myBody=myBody.makeFillet(myThickness/12.0,myBody.Edges) すでに閉じたワイヤがあるので、これをフェイスに変換することができます. フェイスを作ってしまえば、それを押し出すことができるようになります. このようにしてソリッドができました. では、次にこのオブジェクトに素敵なフィレットを少し使ってみましょう. デザインには気を使うでしょ？ neckLocation=Base.Vector(0,0,myHeight) neckNormal=Base.Vector(0,0,1) myNeckRadius = myThickness / 4. myNeckHeight = myHeight / 10 myNeck = Part.makeCylinder(myNeckRadius,myNeckHeight,neckLocation,neckNormal) これでボトルの本体はできました. あとはボトルの首を作成する必要があります. そこで、円柱で新らしいソリッドを作成します. myBody = myBody.fuse（myNeck） フューズ操作、他のアプリではユニオンとも呼ばれることもあります、はとても強力な機能です. この操作は、結合処理において、結合すべきものと削除すべきものを管理します. return myBody これで、この関数の結果としてPartソリッドを返します. このPartソリッドは、他のPartシェイプと同様に、FreeCADドキュメント内で属性になります. myObject = FreeCAD.ActiveDocument.addObject("Part::Feature","myObject") myObject.Shape = bottle あるいはもっとシンプルな方法で: Part.show(bottle)

読み込みと保存
Partモジュールの作業内容を保存する方法はいくつかあります. もちろんFreeCADドキュメントを保存することもできますが、PartオブジェクトをBREPやIGS、STEPやSTLなどの共通CADフォーマットに直接保存することもできます.

シェイプをファイルへ保存するのは簡単です. 全てのシェイプオブジェクトで、exportBre、pexportIges、exportStl、およびexportStepメソッドが利用できます. ではやってみましょう： import Part s = Part.makeBox(0,0,0,10,10,10) s.exportStep("test.stp") ここではSTEPファイルへボックスを保存します. BREPやIGESやSTEPファイルを読み込むには、単純に反対の操作を行います： import Part s = Part.Shape s.read（"test.stp"） BREPやIGES、STEPファイルを開いたりインポートは、メニューの「ファイル -> 開く」か「ファイル　-> インポート」からも利用できます. 現時点ではエクスポートはこのやり方ではまだ使用できませんが、すぐに改善されるでしょう.