G-coordinatorで3Dプリント#3

:::note alert
この記事内のコード等は最新版のG-coordinator ver3系列と互換性がありません．
ご注意ください
:::

# G-coordinator ver2.0.0に関して

G-coordinatorの最新バージョンとしてver2.0.0をリリースしました．

https://github.com/tomohiron907/G-coordinator/releases/tag/2.0.0

バージョン1系列に比べて，造形のためのコードがかなり簡単になり，また，拡張性が大きく向上しました．しかし，そのために，今までの造形コードとは互換性がなくなっています．ver2系列に対応した造形サンプルコードもgithubにアップしているので，そちらを参照しながらだと読みやすいです．
今回の記事では，G-coordinatorのver2で造形を行うための基本的な考え方と，変更点についても述べていきます．造形のための解説とリリースノートの中間的な記事です．

# G-coordinatorのインストール
今までと同じように，githubのreleaseページに最新版の実行ファイル（windows:exeのzip, mac:dmg)をアップしています．そこから，インストールをしてください．

以前，ver1.2.0の時に，インストール方法を示した動画を作成しています．インストール方法は同じなので，そちらをご参照ください．


# G-coordinator ver2のメインのアップデート
一番大きなアップデートは，造形のための記述方法です．
![スクリーンショット 2023-04-21 0.32.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126722/42909f13-ff7c-a378-aa86-f55ed15397f4.png)


左側のエディタのpythonコードを以下に示します．

```ruby:default_cylinder.py
import numpy as np
import math
import print_settings 
from path_generator import *

LAYER =50


def object_modeling():
    full_object=[]
    for height in range(LAYER):
        arg = np.linspace(0, np.pi*2,100)
        rad = 10
        x = rad*np.cos(arg)
        y = rad*np.sin(arg)
        z = np.full_like(arg, height*0.2+0.2)
        wall = Path(x, y, z)
        outer_wall = Transform.offset(wall, 0.4)
        full_object.append(wall)
        full_object.append(outer_wall)
        
        if height <2 :
            bottom = Transform.fill(wall, infill_distance = 0.4, offset = -0.4)
            bottom = Transform.rotate(bottom, np.pi/2*height)
            full_object.append(bottom)
            
    return full_object
```



印刷したい造形に関して，ｘ座表，ｙ座表，ｚ座表のリストをそれぞれ用意します．（np.arrayでも、listでも問題ないです）
```ruby
x = rad*np.cos(arg)
y = rad*np.sin(arg)
z = np.full_like(arg, height*0.2+0.2)
```

それらのリストを用いて，Path()というクラスのインスタンスを生成します．
```ruby
wall = Path(x, y, z)
```
後述しますが，Pathというクラスには，印刷設定のための変数などがメンバ変数として入っています．
現段階では，一旦，指定されたｘ，ｙ，ｚの座標を順番に樹脂を出していった結果の，一本の樹脂の塊の名前にwallという名前を付けているという認識で大丈夫です．

つまり，以下の写真において，白い線で示された部分は，ノズルから樹脂が切れ間なく出てきて一本の曲線になります．それの名前がwallというわけです．

![スクリーンショット 2023-04-21 0.22.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126722/21ea3662-c8bf-5d5f-397b-c81e3fe13990.png)


このPathクラスのインスタンスを引数として関数に渡し，返り値には，変形したPathクラスのインスタンスが渡されます．
```ruby
outer_wall = Transform.offset(wall, 0.4)
```
__もう少し，簡単に，具体的にいうと，例えばオフセットする関数に入力として先ほどのwallという曲線を入力します．すると，出力としてそれをオフセットした曲線が得られます．__


wallという曲線を0.4外側にオフセットしてねという指示です．-0.4にすれば，内側にオフセットしてくれます．
そして，オフセットした曲線には，outer_wallという名前をつけています．


他にも，Transform.rotate()に入力した曲線は，指定した角度だけ回転した曲線になって出力されたりします．他の関数は，後述します．

そして，出来上がった曲線，wallとouter_wallの二つをfull_objectに追加します．
```ruby
full_object.append(wall)
full_object.append(outer_wall)
```
すなわち，full_objectには，作成したインスタンス（曲線）がリストとなって格納されている状態です．G-coordinator ver1系列では，座表の多次元リストをfull_objectに挿入していたので，そこも大きな違いです．


最後に，底面の処理です．
ここの記述を簡単にしたくて，ver2系列を開発したといっても過言ではないくらいです．

```ruby
if height <2 :
    bottom = Transform.fill(wall, infill_distance = 0.4, offset = -0.4)
    bottom = Transform.rotate(bottom, np.pi/2*height)
    full_object.append(bottom)

```
ここまで，読んでもらっていたら，なんとなくわかるかもしれません．
最初の二層に関して底をつけています．wallという先ほどの曲線を入力として，Transform.fill()という関数に入力しています．すると，返ってくるのは，与えられた曲線の内部を塗りつぶすように動いてくれる曲線です．

赤の円を引数にすると，白のラインが得られるイメージです．
![スクリーンショット 2023-04-21 0.48.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126722/5d29ea43-9fd2-2858-27dd-59da5fb89342.png)
また，Transform.fill()関数は，オプションとして，
infill_distance:インフィルの間隔，
offset: wallの曲線からどれだけオフセットした範囲で塗りつぶしを行うか，
angle: 塗りつぶしの線の角度
を指定することが可能です．
ここでは，インフィルの間隔にノズルの大きさを指定しています．つまり，隙間なく，全部塗りつぶすという意味です．

(2023/4/21現在，このTransform.fill()関数は，閉じた凸曲線にしか対応できていないです．凹曲線を入力すると，返ってくる曲線は求めているものにはなりません．これは，今後のアップデートで対応できるようにするつもりですが，アルゴリズムの構築が難しいことが原因です．もし，何か，良いアイデアや他のスライサーで使っているアルゴリズム等の情報があれば，ぜひ，コメントなどいただけると幸いです．）

最後に，得られたbottomという曲線を回転させます．Transform.rotate()の第一引数はPathのインスタンス，第二引数が回転角です．
第0層目（height = 0)の時には，回転なし（fill()関数から返ってきたものそのまま）
第1層目(height = 1)の時には，90度回転する

最後に，作ったbottom をfull_objectに追加します．

以上で，default_cylinderの解説は以上です．
なんとなく，G-coordinator ver2系列でのモデリングの仕方が伝わったかなと思っています．

まとめると，
① 動いてほしいx, y, zの座標リスト
② それをPath(x, y, z)として曲線のインスタンスを生成
③ その曲線をTransform.{関数}に引き渡してやることで，変形した曲線が得られる．
④ 曲線のインスタンスをfull_object.append()で追加していく．


大まかには，この流れを繰り返していきます．
1系列に比べて，記述とモデリングがシンプルになったのが伝われば幸いです．





# 印刷設定のインポート
G-coordinator　ver2では，左側のエディタから，右側の印刷設定の値にアクセスすることが可能です．これにより，印刷設定変数を左側に一括でまとめることが可能です．
具体的には，エディタの中で，print_settings.nozzle_diameterを入力すると，その値は，印刷設定の右側で設定している値(0.4など)になります．

これにより，印刷設定を変更する際にも，書き換える箇所が最低限に抑えられます．

例えば，
```ruby
bottom = Transform.fill(wall, infill_distance = 0.4, offset = -0.4)
```

こうなっていた箇所の0.4というオフセット値やインフィルの間隔はノズルの直径からきているので，
```ruby
import print_settings
nozzle = print_settings.nozzle_diameter
bottom = Transform.fill(wall, infill_distance = nozzle, offset = -nozzle)
```
このように表記しておけば，右側でノズルの直径を変更したときには，自動的に，そのノズルに合うように，インフィルの間隔が変更されます．


例えば，以下の写真では，ノズルの直径を2.0に設定しています．
![スクリーンショット 2023-04-28 9.22.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126722/703b88c1-b2c8-b707-1e45-746e7bc1320f.png)

ここで，ノズルの直径を0．８に変更すると，
![スクリーンショット 2023-04-28 9.22.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126722/7d3c3f15-71da-00f2-5288-08ae2e1c5dde.png)

ノズルがさっきより小さく，より密に面が埋められているのがわかります．

右側の設定欄にある値は，どれも同じようにアクセスすることが可能です．
print_settings.{変数名}の表記にすれば同じように値を取得できます．

# パスへの個別印刷設定
先ほど述べたように，樹脂が連続的に出ている一連の樹脂の曲線をパスと呼びます．

G-coordinator ver2.0では，一つ一つのパスに対して個別の印刷設定を指定することが可能です．
例えば，プリントの最初の層だけ、定着のために印刷速度を遅くしたいという要望があったとします．

そのときには，
```ruby
def object_modeling():
    full_object=[]
    for height in range(LAYER):
        x = np.array([100,-100,-100,100,100], dtype = float)
        y = np.array([100,100,-100,-100,100], dtype = float)
        z = np.full_like(x, (height+1)*thickness)
        wall = Path(x, y, z)
        infill = Transform.fill(wall, offset = - nozzle , infill_distance = nozzle , angle = np.pi/4 + np.pi/2 *height)
        if height == 0:
            wall.print_speed = 500
            infill.print_speed = 500
        full_object.append(wall)
        full_object.append(infill)
```
height == 0 をif分で分岐させてwallというパスに対してwall.print_speedで速度が設定できます．

__各パスに対して，印刷設定の指定があればそちらを採用し，特に指定のない場合には，右側のデフォルト印刷設定が適用されるという認識です．__

これを先ほどの印刷設定のインポートと合わせると，
```ruby
if height == 0:
    wall.print_speed = print_settings.print_speed/2
    infill.print_speed = print_settings.print_speed/2
```
のような表記をすることも可能です．
第0層目だけは，印刷速度をデフォルトの半分にするという設定です．

他に，個別に制御できる印刷設定として，以下のものがあります．
```ruby
wall.extrusion_multiplier #押し出し率に掛ける係数（スカラー）
wall.extrusion_multiplier_array #押し出し率の係数列（指定する座標リストと同じ要素数のリスト）
wall.print_speed #印刷速度(スカラー）
wall.print_speed_array #印刷速度の列（指定する座標リストと同じ要素数のリスト）
wall.retraction #リトラクションの有無（True/False)
wall.z_hop #zホップの有無(True/False)
wall.before_gcode #パスの印刷の前に挿入されるコード
wall.after_gcode #パスの印刷の後に挿入されるコード
```
押し出し率と印刷速度に関しては，パス全体に対して適用されるものと，セグメント単位で押し出し率を設定する配列のものの二種類を用意しています．

# その他のアップデート
G-coordinatorのver2からの他のアップデートに関してです．

まず，エディタの画面の比率を動かすことが可能です．エディタ画面と
グラフィック画面の境にある灰色の点をドラッグすることにより，画面比率を調整できます．
それにより，エラーログを確認したり，プレビューを大きく表示したりがやりやすくなります．

https://twitter.com/tamutamu3D/status/1646804720378601474

他に大きなアップデートとしては，エディタが大きく進化しています．
インデントをするにあたり，tabキーでスペース４つが挿入されますが，複数行の一括インデントとアンインデントができるようになっています．複数行を選択した状態で，tabでインデント，shift+tabでアンインデントです．

また，エディタにサジェスト機能もつけました．

![スクリーンショット 2023-04-28 12.04.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126722/63fcfa8f-39ae-38a8-c54b-1b14335bc9e5.png)
途中まで打った文字に応じてnumpyの関数などをサジェストしてくれます．

print_settingsやTransform　もサジェストしてくれます．
ただ，vscodeのように高機能なサジェストはまだ実装できていないので，今後のアップデートで対応していきたいと思っています．


後，エディタに行数が表示されるようになっていますが，日本語でのコメントを打つと，行数が若干ずれてしまうというバグが確認されています．これも今後のアップデートで修正する予定です．


# 最後に
G-coordinator　ver2.0.0は進化したところが多く，まだどこにバグが発生するか把握しきれていない箇所が多いので，コメント，リプライ，DM等で連絡していただけると幸いです．

また，以前twitterにも書きましたが，自分はG-coordinatorをProcessingのようなジェネラティブアートのツールとしていくことをイメージしています．しかし，造形のためのコードや数学と3Dプリンタの両方に興味のある人が少ないこともあり，なかなか自分で1から造形をおこなうのは難しく，ユーザも限られてしまいます．そこで，G-coordinatorで造形をおこなったり，改造して印刷をおこなったりしたものを積極的に　#Gcoordinator（ハイフンなしに注意）でツイートしてもらえると，よりコミュニティの活性化につながると思っています．