Claude Media
Claude CodeでCadQueryの3Dモデルを作る — 寸法アサーションでSTEP出力を確かめる

Claude CodeでCadQueryの3Dモデルを作る — 寸法アサーションでSTEP出力を確かめる

CadQueryはPythonで3D CADモデルを書けます。バウンディングボックスと体積をpytestで検証し、通ったものだけSTEPに書き出す反復を、Claude Codeの設定とともに実機の出力付きで示します。

CadQueryは、Pythonのコードで3D CADのモデルを組み立てるライブラリです。成果物がPythonのファイルなので、Claude Codeに書かせやすく、テストも付けられます。

ここで効くのが、見た目ではなく数値で形を確かめる方法です。形状から寸法と体積を取り出してassertし、通ったものだけをSTEPに書き出します。穴の深さを間違えても、バウンディングボックスは変わりません。体積なら差が出ます。この記事では、その実例を手元で動かした出力つきで示します。

CadQueryでClaude Codeに書かせるときの前提

CadQueryの目的は、「できるだけ人に説明するのに近い形のスクリプトでモデルを作ること」と「STEP、AMF、3MFといった高品質なCAD形式をSTLに加えて出力すること」などです。パラメトリックなモデルを、普通のプログラミング言語で書くわけです。

OpenSCADとの違いも、同じ文書の「Why CadQuery instead of OpenSCAD?」に書かれています。CadQueryの土台はOpenCascadeで、NURBSやスプライン、STEPの入出力をネイティブに扱えます。OpenSCADの出力で一般的なSTLは、情報が落ちる形式だとも説明されています。OpenSCADを使う場合のPNGによる検証はOpenSCADの記事にまとめています。

画像を見せて直させるOpenSCADの流れに対し、CadQueryは数値を取り出す方が自然です。形状オブジェクトが体積や寸法を返すからです。

インストールと動作確認

インストール方法にはcondaとpipがあり、condaの方がよくテストされた成熟した方法とされています。pipは、Python 3.9以降が対象です。仮想環境の利用が強く勧められています。

python3 -m venv .venv
.venv/bin/pip install cadquery pytest

手元の検証では、新しい仮想環境へのpipインストールでCadQuery 2.8.0が入り、インストール手順の確認と同じimport cadqueryが通りました。インストール手順が示す動作確認は、cadquery.Workplane('XY').box(1,2,3).toSvg()がSVGの文字列を返すかどうかです。Claude Codeに環境を作らせるなら、この1行を最初に実行させると、導入の失敗とモデルのバグを切り分けられます。

最新のPythonではCadQueryの依存パッケージが追いつかない場合がある、との注意書きもあります。pipで入らないときは、condaの手順に切り替えます。

検証対象のモデルを関数にする

テストから呼べるよう、モデルは「パラメータを定数で持ち、build()が形状を返す」形にします。スクリプトを直接実行したときだけSTEPを書き出します。

# bracket.py
import cadquery as cq
 
LENGTH, WIDTH, THICK = 60.0, 40.0, 5.0
HOLE_D = 6.0
PAD = 8.0
 
 
def build() -> cq.Workplane:
    return (
        cq.Workplane("XY")
        .box(LENGTH, WIDTH, THICK)
        .faces(">Z").workplane()
        .rect(LENGTH - 2 * PAD, WIDTH - 2 * PAD, forConstruction=True)
        .vertices()
        .hole(HOLE_D)
        .edges("|Z").fillet(3.0)
    )
 
 
if __name__ == "__main__":
    build().export("out/bracket.step")

60×40×5mmの板に、四隅から8mm内側に直径6mmの貫通穴を4つ開け、縦方向の4辺に半径3mmの丸みを付けたものです。build()を単独の関数にしておくと、Claude Codeが形を変えるたびにテストを呼び直せます。

バウンディングボックスと体積をpytestで検証する

CadQueryのShapeには、BoundingBox()、Volume()、isValid()があります。クラスリファレンスの説明では、BoundingBox()はBoundBoxオブジェクトを返し、Volume()は形状の体積、isValid()は欠陥が見つからないときに真を返します。

寸法はBoundBoxのxlen、ylen、zlenで取れます。手元でbuild()の結果を調べると、xlenが60.0、ylenが40.0、zlenが5.0、xminが-30.0でした。boxは既定で原点を中心に置くため、最小値が長さの半分のマイナスになります。

# test_bracket.py
import math
from bracket import build, LENGTH, WIDTH, THICK, HOLE_D
 
 
def test_shape_is_valid_single_solid():
    part = build()
    assert part.val().isValid()
    assert part.solids().size() == 1
 
 
def test_bounding_box():
    bb = build().val().BoundingBox()
    assert math.isclose(bb.xlen, LENGTH, abs_tol=1e-3)
    assert math.isclose(bb.ylen, WIDTH, abs_tol=1e-3)
    assert math.isclose(bb.zlen, THICK, abs_tol=1e-3)
 
 
def test_volume_is_less_than_plain_plate():
    plate = LENGTH * WIDTH * THICK
    holes = 4 * math.pi * (HOLE_D / 2) ** 2 * THICK
    v = build().val().Volume()
    assert v < plate
    assert v > plate - holes - 500  # 丸みで削れる分の余裕
.venv/bin/python -m pytest -q

手元の実行結果は3 passed in 2.58sでした。許容誤差abs_tolは、浮動小数点の誤差を吸収するための値です。1e-3mmは例で、用途に合わせて決めます。

体積は閉じた式で出せる

寸法が合っても、中身が違う形はあり得ます。そこで体積を、手計算できる式と比べます。板の体積から、貫通穴4つの円柱と、4隅の丸みで削れた分を引きます。丸みの断面積は、半径rの四分円を正方形から引いた(1 - π/4) × r²です。

# test_exact.py
import math
from bracket import build, LENGTH, WIDTH, THICK, HOLE_D
 
 
def test_volume_exact():
    hole = math.pi * (HOLE_D / 2) ** 2 * THICK
    fillet = (1 - math.pi / 4) * 3.0 ** 2 * THICK
    expected = LENGTH * WIDTH * THICK - 4 * hole - 4 * fillet
    assert math.isclose(build().val().Volume(), expected, rel_tol=1e-4)

手元では、この式の値とVolume()が11395.88で一致し、1件が通りました。相対誤差1e-4は例です。式で出せる形なら厳しく、複雑な曲面なら緩めます。

寸法は合うのに体積で落ちる例

穴を貫通でなく深さ2mmの止まり穴に書き換えた場合を試しました。.hole(HOLE_D)を.hole(HOLE_D, 2.0)に変えただけです。

手元の結果では、バウンディングボックスと有効性のテストは3件とも通りました。外形は60×40×5のままだからです。一方、体積の式のテストは落ち、Volume()が11735.18で、期待の11395.88とは約340立方mmずれていました。

寸法だけのアサーションは、この種の取り違えを見逃します。体積、あるいは穴の数や面の数のように、内部の構造を映す値を1つ組み合わせると、見逃しが減ります。

検証を通ったものだけSTEPに書き出す

STEPは、他のCADや解析ソフトと形状をやり取りする形式です。STEPは、物理的な試作用のSTLやDXFとは別に、他のCADツールで後加工するための出力としても使えます。

書き出しはWorkplane.export()です。拡張子.stepなら形式を指定せずに済みます。拡張子を.stpにしたい場合は、ExportTypes.STEPか文字列"STEP"を渡さないとエラーになる、とインポート・エクスポートの節に書かれています。

box.export("out/box.step")
box.export("out/box.stp", "STEP")

単位にも注意が必要です。CadQueryは既定でミリメートルのSTEPを書き出します。ヘッダに書く単位はunitとoutputUnitで制御します。ミリメートルで作ったモデルをメートル宣言で書くなら、unit="MM"のままoutputUnit="M"を指定します。

書き出したSTEPを読み戻す

書き出しの成功と、中身が正しいことは別です。importers.importStep()で読み戻し、元と体積を比べます。

import cadquery as cq
 
r = cq.importers.importStep("out/bracket.step").val()
print(round(r.Volume(), 2), r.isValid())

手元では11395.88 Trueと表示され、書き出し前の体積と一致しました。読み戻しでisValid()が偽になる場合は、書き出し前の形に問題がある可能性があります。

STEPファイルの先頭には、ISO-10303-21;で始まるヘッダが書かれます。手元で出力したファイルでは、FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'))と、Open CASCADE 7.9のSTEPプロセッサ名が見えました。ヘッダの文字列で出力元を判別できます。

Claude Codeに反復を任せる設定

書いて、テストして、直すループを、毎回の指示ではなく設定に持たせます。

CLAUDE.mdに検証の手順を書く

Claude Codeのメモリの解説では、CLAUDE.mdはClaudeへの強制ではなくコンテキストです。確認できる程度に具体的に書くほど従いやすく、1ファイル200行以内が目安とされています。次は、検証の手順を具体的なコマンドで書いた例です。

## CadQuery
- モデルは bracket.py の build() が返す。単位はmm。
- 形を変えたら、先に `.venv/bin/python -m pytest -q` を実行する。
- 3 件すべて通るまで STEP を書き出さない。
- STEP を書き出したら importStep で読み戻し、体積が一致することを確かめる。
- パラメータを変えるときは、テストの期待値の式も同時に直す。

最後の1行は、Claudeがテストを緩めて通そうとするのを避けるための指示です。期待値の式を寸法のパラメータから導く形にしておけば、パラメータを変えても式が追従します。

権限ルールで確認の手間を減らす

テストを回すたびに許可を求められると、反復が止まります。権限ルールは.claude/settings.jsonのallowに書きます。権限の解説にある例のとおり、Bash(npm run *)のように、サブコマンドの後ろに*を置く書き方です。

{
  "permissions": {
    "allow": [
      "Bash(.venv/bin/python -m pytest *)",
      "Bash(.venv/bin/python bracket.py)"
    ]
  }
}

ルールは、拒否、確認、許可の順に評価されます。許可ルールを足しても、既存の拒否ルールの例外にはなりません。pipでの追加インストールのように、環境を変える操作は許可に入れず、毎回確認が入る状態にしておく選択肢があります。

反復の流れ

手順

形を直すたびに回す手順

  1. 1

    変更を依頼する

    寸法や穴の位置を変えたい内容を、Claude Codeに伝えます。

  2. 2

    bracket.pyを直す

    パラメータの定数か、build()の中を直させます。

  3. 3

    pytestを回す

    寸法、有効性、体積の3系統が通るかを見ます。

  4. 4

    STEPを書き出して読み戻す

    通ったらbracket.pyを実行し、importStepで体積を照合します。

落ちたテストの出力をそのままClaude Codeに読ませると、どの値がどれだけずれたかが数値で伝わります。画像より曖昧さが少なく、直す方向が決まりやすくなります。

つまずきやすい点

落とし穴

valが単一のSolidとは限らない

手元で止まり穴の版を試すと、build().val()のオブジェクトはCompoundでした。体積や有効性は問題なく取れましたが、Compoundは複数のソリッドを束ねる型です。部品が離れて2つに分かれていないかは、solids().size() == 1のように個数で見ます。

  • 期待値を実装からコピーしない: Volume()の出力を貼ってアサーションにすると、バグも含めて通ります。穴や丸みの体積のように、設計意図から別経路で出した式と比べます。
  • 許容誤差を決めてから書く: iscloseの許容誤差は、製造の公差とは別の話です。浮動小数点の誤差を吸収する程度に絞り、公差の検証は別のテストにします。
  • STEPの単位を意識する: 既定はミリメートルです。受け取る側のCADがメートル前提なら、unitとoutputUnitを明示します。
  • 画像でも確認したいとき: CadQueryには、可視化とSVGやSTLなどへの書き出しの節があります。形の見た目を目視で見たいときは、数値のテストを通した後に補助として足します。

数値の検証と画像の検証の使い分け

OpenSCADでは、PNGの書き出しコマンドが標準で用意されており、画像による確認が反復の中心になります。CadQueryでは、形状オブジェクトから寸法や体積を直接取れるため、数値のアサーションが中心になります。

観点数値のアサーション画像の確認
向く確認数値のアサーション寸法、体積、ソリッドの個数画像の確認向きの取り違え、全体の見た目
見逃しやすい例数値のアサーション向きは合っているが意図と違う配置画像の確認小さな寸法のずれ
判定の基準数値のアサーション期待値との差画像の確認人または画像を読むモデルの目

どちらか一方で済ませるより、数値で機械的に落とし、見た目は最後に補助で見る分担が現実的です。

まとめ

CadQueryのモデルは、build()を関数にしてpytestに掛けられます。寸法だけでは止まり穴と貫通穴を見分けられないため、体積の閉じた式を1つ加えると、手元の例で約340立方mmのずれを検出できました。STEPは、テストを通った後に書き出し、importStepで読み戻して体積を照合します。

反復の手順はCLAUDE.mdに、テスト実行の許可はsettings.jsonに置くと、毎回の指示が短く済みます。

この記事を共有:XはてブLinkedIn