2019年5月19日日曜日

CadQueryでSTLファイルを出力する方法


背景

CadQueryとはプログラムで3Dモデルを作れるpythonのライブラリです。
STLファイルとは、3Dモデルを扱うファイル形式の1つです。

CadQueryはFreeCADという3D CADとpython-occという3D CAD、それぞれで動くものが開発されています。

 FreeCAD上で動かすCadQueryで生成した3Dモデルは、FreeCADのGUI(画面)操作でSTLファイルとして出力できます。(FreeCADでstlファイルを出力する方法)
しかしながら、GUIの操作でいちいちSTLファイルを出力するのは、頻度が高いと手間です。
モデルの作成をプログラムで行う流れでSTLファイルの出力もして欲しくなったので、やり方を調べて出力させてみました。

備忘録を兼ねて方法を共有します。

使ったもの

FreeCAD版CadQueryを使う場合

FreeCAD


記事を書いている時点で最新の安定版である1.8.1を利用しました。

インストール手順はどちらかが参考になると思います。

Install on Unix
Download

CadQuery


CadQueryをFreeCADで使えるようにするモジュールであるcadquery-freecad-moduleをFreeCADに設定します。
手順は下記の説明を参考にしました。

cadquery-freecad-module/docs/installation.md

操作方法や記述内容は本家の説明サンプルスクリプト集以前自分が書いた記事などが参考になると思います。

python-occ版CadQueryを使う場合

FreeCADに依存しないCadQueryが開発されているので、そちらも試してみました。

conda

condaとはpythonの実行環境を整備してくれるプログラムです。
FreeCADの代わりに利用されているpython-occというCADプログラムがcondaに依存している関係で、これをインストールする必要があります。

Regular installation - Conda

anacondaの3系をインストールしました。


CadQuery

occ版のcadqueryのREADMEにcondaを利用したインストールコマンドが書かれていますが、不具合があるらしく、cadquery-occが無いという下記のエラーが発生してインストールできません。
PackagesNotFoundError: The following packages are not available from current channels:

  - cadquery-occ

READMEは更新待ちなので、これについてやりとりされていたIssueにあったコマンドでインストールできました。
コードが更新されたら状況が変わると思いますが、READMEのコマンドが動かないときは下記のコマンドでインストールできるかもしれません。
conda install -c conda-forge -c cadquery pythonocc-core=0.18.2 pyparsing python=3.6
pip install git+https://github.com/CadQuery/cadquery.git

STLファイルの出力方法

exportShapeという関数がFreeCAD版にもpython-occ版にもあるため、それを利用しました。
~/freecad_projects/box_stl_sampleディレクトリに、大きさが1x2x3で、Z軸と並行でX座標が最大の角が0.5で丸められた箱のSTLファイルを出力するbox.pyというファイルを例として作ってみます。
box.py
import cadquery as cq

box = cq.Workplane('XY').box(1,2,3).edges('|Z and >X').fillet(0.5)

filepath = './box.stl'

with open(filepath, 'w') as f:
  cq.exporters.exportShape(box, 'STL', f)

CadQueryをFreeCADに設定していれば、freecadcmdコマンドで実行できます。
freecadcmd box.py

box.pyと同じディレクトリにbox.stlファイルができました。



FreeCADのGUIで実行するファイルのディレクトリのパスを取得する

先ほど作ったbox.pyをFreeCADのGUI環境でCadQueryのスクリプトとして開いて実行してみると、許可がないというエラーが出て処理が止まります。
[Errno 13] 許可がありません: './box.stl'


何の許可が無いかと言うと、./box.stlというパスの指定ではFreeCADがインストールされている管理者権限でないと変更できないディレクトリにSTLファイルを保存しようとしているため、そのディレクトリを変更する許可がないことを示しています。

FreeCADのGUIで実行する場合、__file__ では実行するファイルのパスを取り出せません。
そのため、moduleの説明に書いてある MYSCRIPT_DIRを利用するとパスを取得できます。
(MYSCRIPT_DIRではパスを取得できない不具合がありましたが、githubで報告したら直してもらえました。)

ということで、box.stlを下記のように変更すると、FreeCADのGUIで実行できるようになります。
box.py
import cadquery as cq
import os

box = cq.Workplane('XY').box(1,2,3).edges('|Z and >X').fillet(0.5)

dirpath = os.environ.get("MYSCRIPT_DIR")

if dirpath is None:
  dirpath = '.'

filepath = dirpath + '/box.stl'

with open(filepath, 'w') as f:
  cq.exporters.exportShape(box, 'STL', f)
  print('exported', filepath)

それぞれの環境を判別する

Cadqueryの実行環境を、FreeCAD版のGUIとCI、python-occ版のうちどれかを判別したいときは、import済みのモジュールを参照することで判別できました。
import sys

if 'FreeCADGui' in sys.modules:
  print('with FreeCADGui')
elif 'FreeCAD' in sys.modules:
  print('with FreeCAD cmd')

if 'OCC' in sys.modules:
  print('with python-occ')

下記のbox.pyは、上記3種類を判別して別の名前のSTLファイルを出力できます。
box.py
import cadquery as cq
import os
import sys

box = cq.Workplane('XY').box(1,2,3).edges('|Z and >X').fillet(0.5)

dirpath = os.environ.get("MYSCRIPT_DIR")

if dirpath is None and 'FreeCADGui' in sys.modules:
  import Shared
  path = Shared.getActiveCodePane().get_path()
  dirpath = os.path.dirname(os.path.abspath(path))
  print('dirpath', dirpath)

if dirpath is None:
  dirpath = '.'

filepath = None

if 'FreeCADGui' in sys.modules:
  filepath = dirpath + '/box_by_freecadgui_cadquery.stl'
elif 'FreeCAD' in sys.modules:
  filepath = dirpath + '/box_by_freecadcmd_cadquery.stl'

if 'OCC' in sys.modules:
  filepath = dirpath + '/box_by_occ_cadquery.stl'

if filepath is not None:
  with open(filepath, 'w') as f:
    cq.exporters.exportShape(box, 'STL', f)
  print('exported', filepath)

ということで、それぞれ実行してみます。
FreeCADのGUIでbox.pyをCadQueryのプログラムとして開き、プログラムを実行すると「box_by_freecadgui_cadquery.stl」ができます。


下記のようなコマンドでfreecadcmdで実行すると、「box_by_freecadcmd_cadquery.stl」ができます。
freecadcmd box.py


python-occ版のcadqueryをインストールしたconda環境のpythonで下記のようにプログラムを実行すると、「box_by_occ_cadquery.stl」ができます。
python box.py


GUIで実行するときはプレビューを表示する

FreeCADのGUI版を利用する場合、show_objectという関数を利用できます。
この関数を使うと、FreeCAD上でモデルのプレビューが見れるので、モデルを作るときはこれを利用すると便利です。

ファイルの最後の方に下記のような記述をすると、半透明な箱をFreeCADで確認できます。
if 'FreeCADGui' in sys.modules:
 show_object(box, options={'rgba': (204, 204, 204, 0.4)})

ということで、この記事で解説するプログラムの完成版がこちらです。
box.py
import cadquery as cq
import os
import sys

box = cq.Workplane('XY').box(1,2,3).edges('|Z and >X').fillet(0.5)

dirpath = os.environ.get("MYSCRIPT_DIR")

if dirpath is None and 'FreeCADGui' in sys.modules:
  import Shared
  path = Shared.getActiveCodePane().get_path()
  dirpath = os.path.dirname(os.path.abspath(path))
  print('dirpath', dirpath)

if dirpath is None:
  dirpath = '.'

filepath = None

if 'FreeCADGui' in sys.modules:
  filepath = dirpath + '/box_by_freecadgui_cadquery.stl'
elif 'FreeCAD' in sys.modules:
  filepath = dirpath + '/box_by_freecadcmd_cadquery.stl'

if 'OCC' in sys.modules:
  filepath = dirpath + '/box_by_occ_cadquery.stl'

if filepath is not None:
  with open(filepath, 'w') as f:
    cq.exporters.exportShape(box, 'STL', f)
  print('exported', filepath)

if 'FreeCADGui' in sys.modules:
  show_object(box, options={'rgba': (204, 204, 204, 0.4)})

FreeCADのGUIでプログラムを実行すると、このように半透明の結果を確認できます。
プログラムの編集に戻りたい場合は、画面中央の「box.py」タブをクリックするか.、Ctr+Tabを入力して、プログラム編集タブに戻れます。


現時点(2019-05-19)で気になったふるまい

FreeCAD版とpython-occ版でSTLファイルの粒度とファイルサイズが異なる

FreeCADのGUIとcmdで出力するSTLファイルは同じですが、標準設定で出力したpython-occ版のSTLファイルはFreeCAD版に比べてファイルサイズが小さく、STLをFreeCADで見てみると丸めた部分の角が荒くなっています。

FreeCAD版: tolerance=0.1(標準)、17KB


python-occ版: tolerance=0.1(標準)、9KB


exportShapeにはtoleranceというオプションを渡せるため、0.001を設定して出力してみました。
0.01を飛ばしたのは、それだとFreeCAD版に変化が無かったためです。
cq.exporters.exportShape(box, 'STL', f, tolerance=0.001)

FreeCAD版: tolerance=0.001、39KB


python-occ版: tolerance=0.001、67KB



python-occの方がファイルサイズが大きいのは変わりませんが、toleranceが0.001だと、FreeCAD版よりpython-occ版の方が粒度が高くなるようでした。

プログラムをFreeCAD版で実行するか、python-occ版で実行するかによって出力されるSTLが変わるため、厳密な用途に利用する場合は注意が必要そうです。

STLファイルの出力をFreeCADのGUIで行うと、CadQuery+FreeCADの半分以下のファイルサイズだけど粒度が高い

背景でも言いましたが、FreeCADはGUI上での操作でSTLファイルを出力できます。
「box_by_freecad_manually.stl」というファイルを作って、確認してみました。



FreeCAD手動出力版: tolerance不明、7.7KB


7.7KBとCadQueryでFreeCADを通してtolerent=0.1で出力したフィアルよりも小さいながら、torelent=0.001でFreeCADを通して出力したファイルと同等の細かさになりました。

調べてみた所、CadQueryで出力したファイルは人が読める形式なのに対し、FreeCADのGUI操作で出力したものは人が読めない形式になっていたのが理由のようでした。
人が読めない形式にする代わりに、ファイルサイズを圧縮しているようです。

box_by_freecadgui_cadquery.stlは、意味の分かりそうな単語と数字で構成されています。
box_by_occ_cadquery.stlも同じような構成でした。


一方、box_by_freecad_manually.stlは、これを見ただけでは解読が困難そうな文字列で構成されています。


まとめ

CadQueryのプログラムでSTLファイルを出力できました。
出力されるSTLファイルは条件によって粒度やファイルサイズが変わるため、STLファイルの出力条件を変える場合は、出力されたSTLが要件に見合うか確認するのが良さそうでした。

参考

FreeCAD版CadQueryの関連ページです。
https://github.com/dcowden/cadquery
CadQuery Documentation

python-occ版CadQueryの関連ページです。
https://github.com/CadQuery/cadquery
CadQuery 2 Documentation

変更履歴

2019/06/14
ソースコードのインデントとして使われていたtabがスペース1個に置き換わっていたので、インデントをスペース2個に変更しました。
MYSCRIPT_DIRではパスを取得できない不具合を修正してもらえたので、MYSCRIPT_DIRの実装を自分で再現してパスを取得する方法を削除しました。
MYSCRIPT_DIRと書くべきところをMYPROJECT_DIRと書いていた部分があったので、修正しました。

0 件のコメント :