Part Designワークベンチ
先日のオシロスコープのプローブホルダーをFreeCADのPythonスクリプトで作ってみたではPartワークベンチの機能を使いましたが、今回はPart Designワークベンチの機能を使って同様なプローブホルダーを作ってみました。
手順は下記のようにしました。
- 加算直方体で最初の外形を作成
- 厚みツールでくり抜いて箱型を形成
- スケッチの押し出しでプローブ穴の壁部分を形成
- スケッチの押し出しでリブとプローブスリットの壁部分を形成
- ポケットでプローブ穴をくり抜く
- ポケットでプローブスリットをくり抜く
- 外形の壁を楕円形にくり抜く
- 対象のエッジを選んでフィレットを形成
今回も最後にフィレットを形成していますが、Part DesignワークベンチのフィレットはPartワークベンチのそれよりも制約が厳しく、前回の形状では形成できませんでした。そこで、フィレット形成前の形状を少し変更して、フィレットと他のエッジとの干渉を避けるようにしました。
import FreeCAD as App
import Part
import Sketcher
# パラメーター
wt = 1.35 #肉厚
filletR = 1 #フィレット半径
sizeX = 90 #外形(x)
sizeY = 25 #外形(y)
sizeZ = 25 #外形(z)
hPosX = [12, 34, 56, 78] #プローブ穴位置(x)
hPosY = 16 #プローブ穴位置(y)
hDia = 13 #プローブ穴直径
hSlit_w = 5 #プローブスリット幅
hSlitb_w = 10 #プローブスリットの壁部分の幅
hSlit_t = 5 #プローブスリット開口部の厚み
hDepth = 7.5 #プローブ穴の深さ
def box(
body, #追加先ボディ
name = 'Box', #名前
size = (10, 20, 30), #サイズ
):
feature = App.ActiveDocument.addObject('PartDesign::AdditiveBox', name)
feature.Length = size[0]
feature.Width = size[1]
feature.Height = size[2]
b = body.addObject(feature)
doc.recompute()
return b[0]
# 厚みツール
def thickness(
body,
shape, #適用対象図形
face,
name = 'Thickness',
wth = 1 #厚み
):
th = body.newObject('PartDesign::Thickness', name)
th.Base = shape, face
th.Value = wth
return th
#スケッチを作ってアタッチ
def sketch_attach(
shape, #適用対象図形
face,
name = 'Sketch'
):
sk = bd_ph.newObject('Sketcher::SketchObject', name)
sk.AttachmentSupport = shape, face
sk.MapMode = 'FlatFace'
return sk
# スケッチに長方形を追加する
def sketch_square(
sketch,
pos = (0, 0),
size = (10, 10)
):
n_sg0 = len(sketch.Geometry) #作業前のスケッチ内の図形要素数
vt = [ #頂点リスト
App.Vector(pos[0], pos[1], 0)
,App.Vector((pos[0]+size[0]), pos[1], 0)
,App.Vector((pos[0]+size[0]), (pos[1]+size[1]), 0)
,App.Vector(pos[0], (pos[1]+size[1]), 0)
]
#ラインの頂点リスト
l_ln = [[vt[i], vt[(i+1) % len(vt)]] for i in range(len(vt))]
#ラインを追加
for ln in l_ln:
sketch.addGeometry(Part.LineSegment(*ln))
#一致拘束を追加
for i in range(len(l_ln)):
ln0 = n_sg0 + i #基準のライン番号
ln1 = n_sg0 + (i+1) % len(l_ln) #制約を適用することで移動するライン番号
sketch.addConstraint(Sketcher.Constraint("Coincident", ln0, 2, ln1, 1))
# スケッチに円を追加する
def sketch_circle(
sketch,
pos = (0, 0),
r = 10 #半径
):
v_pos = App.Vector(pos[0], pos[1], 0)
v_ax = App.Vector(0, 0, 1)
sketch.addGeometry(Part.Circle(v_pos, v_ax, r))
# スケッチに楕円を追加する
def sketch_ellipse(
sketch,
center = (0, 0), #中心座標
ma_ax = (0, 20), #長軸
mi_ax = (10, 0) #短軸
):
v_c = App.Vector(center[0], center[1], 0)
v_a = App.Vector((center[0]+ma_ax[0]), (center[1]+ma_ax[1]), 0)
v_i = App.Vector((center[0]+mi_ax[0]), (center[1]+mi_ax[1]), 0)
sketch.addGeometry(Part.Ellipse(v_a, v_i, v_c))
# 押し出し
def pad(
body,
profile,
name = 'Pad',
len = 10 #押し出し長
):
pd = body.newObject('PartDesign::Pad', name)
pd.Profile = profile
pd.Length = len
if type(profile) != list:
profile.Visibility = False
return pd
# ポケット作成
def pocket(
body,
profile,
name = 'Pocket',
ptype = 'Dimension',
len = 10
):
pk = body.newObject('PartDesign::Pocket', name)
pk.Profile = profile
pk.Type = ptype
pk.Length = len
if type(profile) != list:
profile.Visibility = False
return pk
doc = App.activeDocument()
if doc == None:
#アクティブドキュメントが存在しなければ新規作成
doc = App.newDocument()
#プローブホルダーのボディ
bd_ph = doc.addObject('PartDesign::Body','probeholder')
#外形の箱
ph = box(bd_ph, 'outline', (sizeX, sizeY, sizeZ))
#肉厚分の箱にくり抜く
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (sizeZ-0.1) < f.CenterOfMass.z < (sizeZ+0.1)]
ph = thickness(bd_ph, ph, bf, 'box_ol', wt)
doc.recompute()
#プローブ穴の壁を作る
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (wt-0.1) < f.CenterOfMass.z < (wt+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_holewall')
for hx in hPosX:
#プローブ穴の壁
sketch_circle(sk, [hx, hPosY], ((hDia / 2) + wt))
ph = pad(bd_ph, sk,'holewall', (hDepth-wt))
doc.recompute()
#リブを作る
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (wt-0.1) < f.CenterOfMass.z < (wt+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_rib')
#リブ(横)
sketch_square(sk, [0, (hPosY-(wt/2))], [sizeX, wt])
for hx in hPosX:
#リブ(縦)
sketch_square(sk, [(hx-(wt/2)), 0], [wt, hPosY])
#スリットの壁
sketch_square(sk, [(hx-(hSlitb_w/2)), (hPosY+(wt/2))], [hSlitb_w, (sizeY-hPosY-(wt/2))])
ph = pad(bd_ph, sk,'rib', (hDepth-wt))
doc.recompute()
#プローブ穴を作る
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (0-0.1) < f.CenterOfMass.z < (0+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_hole')
for hx in hPosX:
#プローブ穴
sketch_circle(sk, [hx, -(hPosY)], (hDia / 2))
ph = pocket(bd_ph, sk,'hole', 'ThroughAll')
doc.recompute()
#プローブスリットを作る
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (0-0.1) < f.CenterOfMass.z < (0+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_slit')
for hx in hPosX:
#スリット
sketch_square(sk, [(hx-(hSlit_w/2)), -(hPosY)], [hSlit_w, -(sizeY-hPosY)])
ph = pocket(bd_ph, sk,'slit', 'ThroughAll')
doc.recompute()
#外形の横壁を楕円形にくり抜く
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (0-0.1) < f.CenterOfMass.x < (0+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_sidewall')
#楕円
center = (-(sizeY), sizeZ) #中心座標
w = sizeY-wt-filletR #幅
h = sizeZ-hSlit_t #高さ
if w > h:
ma_ax = (w, 0) #長軸
mi_ax = (0, h) #短軸
else:
ma_ax = (0, h)
mi_ax = (w, 0)
sketch_ellipse(sk, center, ma_ax, mi_ax)
ph = pocket(bd_ph, sk,'sidewall', 'ThroughAll')
doc.recompute()
#フィレット
edges = ph.Shape.Edges
##外形のエッジのうちフィレット対象を抽出
fil_edges = []
for i, ed in enumerate(edges):
sel = False
for vx in ed.Vertexes:
if (
#リブの接続部
((wt-0.1) < vx.Z < (wt+0.1))
#スリットの外側
or (((sizeY-0.1) < vx.Y < (sizeY+0.1)) and ((hSlit_t-0.1) < vx.Z < (hSlit_t+0.1)))
):
sel = True
break
if sel:
fil_edges.append(f'Edge{i+1}')
fil = bd_ph.newObject('PartDesign::Fillet','Fillet')
fil.Radius = filletR
fil.Base = (ph, fil_edges)
doc.recompute()
ph = fil
参考文献