この記事は高専Advent Calender 4日目の記事らしいです。 前日はykbrさんのGoでTwitter Botを改良していく記事でした。僕もそろそろコピーを作らないとですね。
Clojure始めました。 キーボードの記事という事になっていましたが、殆どOpenSCADをClojureで書く記事になります。
はじめに
皆さんEndgameしてますか?僕はしていません。 自作キーボードの界隈では至高のキーボードを手に入れることをEndgameと言います。 市販の入力デバイスの多くはテキスト入力かポインティングデバイスとしての機能に特化していることが多く、 どちらか片方ならば素晴らしいものは多いものの両方を高いレベルで満足させてくれる製品は数少ないのが現状です。 当然自作する流れになる訳ですが、ここで設計データの管理手法という問題が発生します。 3Dデータを直接GUIで構築するタイプのCADはCADベンダーが独自に提供するツールを使うかGitでバイナリとして扱うかになりがちです。 ならテキストで3Dデータを設計してやりましょう。
OpenSCAD
OpenSCAD - The Programmers Solid 3D CAD Modeller
幾何学計算にCGALを、GUIにQtを使ったテキストベースの3DCADです。部分的に翻訳された日本語のWikiやチュートリアルなどがあります。 スクリプトを書いて3Dデータを作るためGitで管理しやすく複雑な形状も自動生成出来ます、がそこそこ辛いです。 トラックボールポチポチで出来ないのはトレードオフなので仕方ないですが変数が全てコンパイル時に計算される、 つまり最終的に格納された値で展開される為、スクリプトっぽい見た目に反して手でゴリゴリ書いていく羽目になります。 反復処理の抽象化すらかなり限定されてしまいます。つら...
scad.clj
なのでOpenSCADをより強力で汎用的な言語でラップするプロジェクトが幾つかあります。 紹介するのはclojureでの者ですが、RubyとJavaScriptはありました。探したら他にもあるかもしれません。
プロジェクトの準備
僕はWindowsが苦手でMacOSが動くマシンも所持してないのでArch Linuxでの作業になります。 デスクトップOSのシェアはLinuxが7割以上ある(?)ので(?)多分大丈夫でしょう(???)
leiningen
clojureのパッケージマネージャです。 READMEにはstableからスクリプト落としてきてパス通せば良いと書いていますが、それやってリリースされてないパッケージダウンロードしようとしていたのでリポジトリをクローンしてリリースされたバージョンのスクリプトを使ったほうが良いです。 JREを入れていないなら先に入れておきましょう。leiningen 2.8.1だとopenjdk-8が良いらしいです。
git clone https://technomancy/leiningen && cd leiningen git tag # 最新版を調べる git checkout <latest version> # <latest version>は先程調べたバージョンで置換 sudo cp./bin/lein /usr/local/bin lein # 起動してみる
プロジェクトを作る
lein new app scad-playground && cd scad-playground
プロジェクト名を変えたい場合はscad-playgroundを任意に置換してください。 project.cljを以下のように編集します。ライブラリのバージョンは適宜読み替えてください。
6,7c6 < :dependencies [[org.clojure/clojure "1.8.0"] < [scad-clj "0.5.3"]] --- > :dependencies [[org.clojure/clojure "1.8.0"]]
これで準備は完了です。
小さな立方体
./src/scad_playground/core.clj
を以下のように編集します。
(ns scad-playground.core (:require [scad-clj.scad] [scad-clj.model]) (:gen-class)) (def simple-cube (scad-clj.model/cube 10 10 10)) (defn -main "I don't do a whole lot ... yet." [& args] (spit "simple-cube.scad" (scad-clj.scad/write-scad simple-cube)))
nsは名前空間を作るマクロで、:require
キーワードで外部ライブラリを読み込むことが出来ます。
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def simple-cube (model/cube 10 10 10)) (defn -main "I don't do a whole lot ... yet." [& args] (spit "simple-cube.scad" (scad/write-scad simple-cube)))
asキーワードを使うことでパッケージを短縮することが出来ます。以降やります。
openscad ./simple-cube.scad
プリミティブ図形達
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def simple-cube (model/cube 10 20 30)) (def simple-sphere (model/sphere 10)) (def simple-cylinder (model/cylinder 10 30)) (defn -main "I don't do a whole lot ... yet." [& args] (spit "simple-cube.scad" (scad/write-scad simple-cube)) (spit "simple-sphere.scad" (scad/write-scad simple-sphere)) (spit "simple-cylinder.scad" (scad/write-scad simple-cylinder)))
ブーリアン演算
OR
union関数を使います。引数に渡された図形の論理和を取ります。
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def primitives (model/union (model/cylinder 10 50) (model/sphere 20))) (defn -main "I don't do a whole lot ... yet." [& args] (spit "primitives.scad" (scad/write-scad primitives)))
AND
intersection関数を使います。引数に渡された図形の論理積を取ります。
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def primitives (model/intersection (model/cube 15 15 50) (model/sphere 20))) (defn -main "I don't do a whole lot ... yet." [& args] (spit "primitives.scad" (scad/write-scad primitives)))
差分
difference関数です。ネジ穴等を開けるときに重宝します。 第一引数から以降の引数の領域を除去した図形が出力されます。
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def primitives (model/difference (model/cube 15 15 50) (model/sphere 20))) (defn -main "I don't do a whole lot ... yet." [& args] (spit "primitives.scad" (scad/write-scad primitives)))
基点変更
scad-cljではデフォルトでcenter=trueとなっています。これは図形の中心と現在の座標が一致するように図形を配置するという意味で、 これがtrueだと座標計算が面倒になる場合があります。
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def simple-cube (model/cube 10 10 10 :center false)) (defn -main "I don't do a whole lot ... yet." [& args] (spit "simple-cube.scad" (scad/write-scad simple-cube)))
基点が変更されていますね。ちなみに球だと何も変わりません。
なめらかさ
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model]) (:gen-class)) (def simple-cube (model/union (->> (binding [model/*fn* 100] (model/sphere 10)) (model/translate [ 20 0 0])) (->> (binding [model/*fn* 10] (model/sphere 10)) (model/translate [-20 0 0])))) (defn -main "I don't do a whole lot ... yet." [& args] (spit "simple-cube.scad" (scad/write-scad simple-cube)))
bindingを使いスコープを越えて変数を書き換えることで円や球の滑らかさを変更できます。 仕様として妙な気はしますがOpenSCAD自体がこうなっている為それに合わせたのでしょう。
移動
水平移動
translate関数を使います。
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def primitives (model/union (model/translate [20 0 0] (model/cube 15 15 50)) (model/translate [-20 0 0] (model/sphere 20)))) (defn -main "I don't do a whole lot ... yet." [& args] (spit "primitives.scad" (scad/write-scad primitives)))
回転移動
rotate関数を使います
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def primitives (model/union (model/rotate [20 0 0] (model/cube 5 5 100)) (model/rotate [0 20 0] (model/cube 5 5 100)))) (defn -main "I don't do a whole lot ... yet." [& args] (spit "primitives.scad" (scad/write-scad primitives)))
マクロ
clojureの->>
を使った書き方もあります。
水平移動を例に取ると、
(ns scad-scad.core (:require [scad-clj.scad :as scad] [scad-clj.model :as model])) (def primitives (model/union (->> (model/cube 15 15 50) (model/translate [20 0 0])) (->> (model/sphere 20) (model/translate [-20 0 0] )))) (defn -main "I don't do a whole lot ... yet." [& args] (spit "primitives.scad" (scad/write-scad primitives)))
最後に
全然キーボード作ってないですね。登録詐欺か? Adc書かずにCivをしていました大変申し訳ありません。Clojure+OpenSCADで設計をする記事は今後も書きたいです。
明日はhrt9092さんの画像処理に関する記事です。アドベントカレンダーを読むのを楽しみに日々を生きている。