言葉でつくるCAD
OpenSCADのPython版、PythonSCADと、Cadqueryから派生したbuild123dを合わせて、コード作成と、画面viewer確認ができます。3D CADをPythonで行う新しいアプローチです。これまで、PythonSCADからbuild123dのコードを呼び出し、サンプルをまとめてみました。実際に実行してみてください。
検証したサンプルを紹介します。
線 (Edge/Wire/Curve)をつかった、circleを三つ繋げたサンプル
線の描画は、円が一番分かりやすい確認になります。2種類用意しました。
平面上で3つの円をつなぐ(Builder モード)
from b123dview import *
with BuildLine() as line:
for i in range(3):
CenterArc(center=(i * 30, 0), radius=20, start_angle=0, arc_size=360)
Line((0, 20), (60, 20)) # 上端を1本で結ぶ
Line((0, -20), (60, -20)) # 下端
show(line) # line.line でも可
BuildLine では円は Circle ではなく CenterArc(..., arc_size=360) で描きます(Circle は BuildSketch 用の「面」です)。半径 20 を 30 間隔で並べているので互いに重なり、上下を直線でつないでいます。
直交する3平面に円を置く(代数モード)
from b123dview import *
c1 = Plane.XY * Edge.make_circle(20)
c2 = Plane.XZ * Edge.make_circle(20)
c3 = Plane.YZ * Edge.make_circle(20)
show(c1, c2, c3) # 複数渡せる
ジャイロのような形になり、線が 3D 空間の任意の向きでも描けていることが一目で分かります。
見た目の調整は、線の太さが wire_r、曲線の分割数が segs です。
show(line, wire_r=0.5, segs=32)
show(c1, c2, c3,wire_r=0.75, segs=50) # 複数渡せる
円1つが segs 個のカプセルになるので、segs=32 で円3つなら 96 個の hull が走ります。プレビューが重く感じたら segs を下げてください。直線(GeomType.LINE)は分割せず1本のカプセルで済むよう分岐してあります。
build123dでの回転体作成
PythonSCADの2D表示は、厚みがなく、renderすると赤い線がでて
2Dとわかるようにしていましたが、build123dからの変換で、ポリゴンで
渡しているのですね。
PythonSCADの回転体作成は、+xy平面で、2D描画、-x領域はエラーになる。
rotate_extrudeでしたが、build123dでも同じ考え方でしょうか。
1. 2D の扱い
今の b123dview は Sketch を薄く押し出した 3D polyhedron として渡しています。PythonSCAD から見ると本物の 2D ではないので、赤い線の表示にはならず、linear_extrude や rotate_extrude にも掛けられません。
XY 平面上のスケッチに限れば、頂点を拾って PythonSCAD の polygon() に変換すれば、本物の 2D にできます。穴は paths で渡します。
def _loop(wire, segs):
pts = []
for e in wire.order_edges():
n = 1 if e.geom_type == GeomType.LINE else segs
seg = [e @ (i / n) for i in range(n + 1)]
if pts and (pts[-1] - seg[0]).length > (pts[-1] - seg[-1]).length:
seg.reverse() # 辺の向きをそろえる
pts += seg[:-1] # 終点は次の辺の始点と重なるので落とす
return [[p.X, p.Y] for p in pts]
def to_polygon(obj, segs=16):
"""XY 平面上の Face / Sketch を PythonSCAD の 2D polygon にする"""
if isinstance(obj, Builder):
obj = obj._obj
polys = []
for f in obj.faces():
pts, paths = [], []
for w in [f.outer_wire(), *f.inner_wires()]:
loop = _loop(w, segs)
paths.append(list(range(len(pts), len(pts) + len(loop))))
pts += loop
polys.append(polygon(pts, paths))
return union(polys) if len(polys) > 1 else polys[0]
これで show(to_polygon(sketch)) は赤線付きの 2D として出ますし、to_polygon(sketch).rotate_extrude() のように PythonSCAD 側の押し出しにも渡せます。from pythonscad import polygon の追加が必要です。傾いたスケッチは Z を捨てているので正しく出ません。
2. 回転体の考え方は少し違います
OpenSCAD の rotate_extrude は「XY の 2D 図形を暗黙に XZ に立てて、X を半径として Z 軸回りに回す」ので、−X 側があるとエラーになります。
build123d の revolve は「描いた平面のまま、明示した軸の回りに回す」です。なので断面は最初から Plane.XZ に描き、axis=Axis.Z で回します。断面が軸をまたいではいけない点は同じです(自己交差になるためエラーになります)。
from b123dview import *
with BuildPart() as vase:
with BuildSketch(Plane.XZ): # ローカル x = X(半径), ローカル y = Z(高さ)
with BuildLine():
Polyline((5, 0), (20, 0), (15, 30), (8, 40), (5, 40), close=True)
make_face()
revolve(axis=Axis.Z, revolution_arc=360)
show(vase)
違いを整理すると、OpenSCAD は「+X 側の XY 図形を Z 軸で回す」一択なのに対し、build123d は断面をどの平面に置くか、どの軸で回すか(Axis.X や任意の Axis)、何度回すか(revolution_arc)を全部明示します。授業では「build123d は暗黙のルールが少ない代わりに、平面と軸を自分で言葉にする」という対比で説明できると思います。
描画したpolylineを安全のために、マイナス部分をカット、または+部分とintersectionする方法
make_face() のあとに +x 側だけを覆う矩形を Mode.INTERSECT で重ねる方法です。BuildSketch 内はローカル座標(x が半径方向)で書けるので、Plane.XZ に置いても考え方は変わりません。
from b123dview import *
with BuildPart() as vase:
with BuildSketch(Plane.XZ):
with BuildLine():
Polyline((-5, 0), (20, 0), (15, 30), (8, 40), (-5, 40), close=True) # わざと −x にはみ出す
make_face()
Rectangle(1000, 1000, align=(Align.MIN, Align.CENTER), mode=Mode.INTERSECT) # x ≥ 0 だけ残す
revolve(axis=Axis.Z)
show(vase)
align=(Align.MIN, Align.CENTER) で矩形の左端を x=0 に置き、上下は中央揃えなので ±500 をカバーします。断面が軸にちょうど接する形になりますが、軸上の辺は許されるので(花瓶の底のような中実部分になります)revolve は通ります。
代数モードなら、split で平面の片側を残す書き方もあります。
from b123dview import *
prof = make_face(Polyline((-5, 0), (20, 0), (15, 30), (8, 40), (-5, 40), close=True))
prof = split(prof, bisect_by=Plane.YZ, keep=Keep.TOP) # Plane.YZ の法線 +X 側を残す
vase = revolve(Plane.XZ * prof, axis=Axis.Z)
show(vase)
Plane.YZ の法線は +X なので Keep.TOP が +X 側です。Builder の中で split を使う場合はローカル座標との対応を意識する必要があるので、生徒向けには矩形の INTERSECT のほうが「+x 側を切り抜く」という意図がそのまま読めて説明しやすいと思います。
いずれも build123d の中で完結するので、変換前に断面が保証され、revolve のエラーで止まることがなくなります。教材なら「断面を描く → 安全のために +x で切り抜く → 回す」を定型にしておくとよいですね。
- PythonSCADのGUIでbuild123dを動かす





