この記事は高専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>
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さんの画像処理に関する記事です。アドベントカレンダー を読むのを楽しみに日々を生きている。