swifty-creatives (Swiftライブラリ解説) その1

## swifty-creatives
- Swift製のクリエイティブコーディングフレームワーク
- SwiftUIのViewやUIViewなどとして簡単にアプリに導入することができる
- 3D描画やアニメーションなど、一通りのことはできる
- Processingライクな書き方ができる
- https://github.com/yukiny0811/swifty-creatives

### 続き
- [swifty-creatives(Swiftライブラリ解説)その2](https://qiita.com/yukiny/items/a47575886406796ed0a2)

![](https://user-images.githubusercontent.com/28947703/208063423-3ad00c20-1d1c-48b8-8996-2d43e1365fe4.gif)

## 基本的な描画方法
```Sketch1.swift
import SwiftyCreatives

//スケッチプログラムを作ります
class Sketch1: Sketch {
    override func draw(encoder: SCEncoder) {
        color(1, 1, 1, 1) //RGBAで色を指定
        box(3, 3, 3) //Boxを幅3・高さ3・奥行き3の大きさで描画
    }
}

//SwiftUIのViewとしてこのように記述します
struct ContentView: View {
    var body: some View {
        ZStack {
            SketchView(Sketch1())
        }
        .background(.black) //背景は黒が映える
    }
}
```
![ForQiita 2023年-02月-28日 23.06.14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567236/e4a83e42-6c98-2c1b-33ba-c3a66f96d85a.gif)

このように白いBoxが表示されました。デフォルトでカメラ操作がONになっているので、自由に視点を移動することができます。今回はMacOSで実行していますが、iOSやMacCatalystでも同様に動作します。

|現時点(v1.11.2)で対応している図形一覧|
|-|
| Box |
| Rectangle |
| Triangle |
| Circle |
| Line |
| 3D Model (objのみ) |
| Img (画像) |
| Text |
| UIViewObject (UIViewのxibをそのまま3D空間に配置できます。ボタンもタップ可能。)

## Push/Popを用いた描画
```Sketch2.swift
class Sketch2: Sketch {
    let count = 20
    override func draw(encoder: SCEncoder) {
        for i in 0..<count {
            color(1, Float(i) / 20, 0, 1) //色を設定
            pushMatrix()
            rotateY(Float.pi * 2 / Float(count) * Float(i)) //座標系をY軸を中心に回転させる
            translate(10, 0, 0) //現在の座標系でx軸方向に10移動する
            box(1, 1, 1) //サイズ1の立方体を描画する
            popMatrix()
        }
    }
}
```
![](https://user-images.githubusercontent.com/28947703/216469226-3f32ccee-c045-48c3-8fc0-0044ef7da891.png)

このようにリング状にBoxを配置することができました。

## 画像の描画
```Sketch3.swift
class Sketch3: Sketch {
    // 画像を読み込みます
    let image = Img().load(
        image: NSImage(named: "image")!.cgImage(
            forProposedRect: nil,
            context: nil,
            hints: nil
        )!
    ).multiplyScale(12) //サイズを縦横比を保ったまま12倍します
    override func draw(encoder: SCEncoder) {
        image.draw(encoder) //描画します(encoderは気にしなくて大丈夫です)
    }
}
```
![ForQiita 2023年-02月-28日 23.26.27.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567236/b4ed5db1-c8ac-e1ab-6c72-ecfe6fda9b6d.gif)
このように画像も3D空間上に配置することができました。

## ホバーしたら色が変わる長方形
```Sketch4.swift
class Sketch4: Sketch {
    let rect = HitTestableRect().setColor(f4(1, 1, 1, 1)).setScale(f3(5, 5, 5))
    override func draw(encoder: SCEncoder) {
        rect.drawWithCache(encoder: encoder, customMatrix: getCustomMatrix()) //当たり判定を含んで描画する場合にはdraw()ではなくdrawWithCache()を用います
    }
    override func mouseMoved(with event: NSEvent, camera: some MainCameraBase, viewFrame: CGRect) {
        let mousePos = mousePos(event: event, viewFrame: viewFrame) //マウスの座標を取得する関数が用意されています
        let ray = camera.screenToWorldDirection(
            screenPos: mousePos,
            width: Float(viewFrame.width),
            height: Float(viewFrame.height)) //カメラから飛ばすrayを作成します
        //rayを使ってrectの当たり判定計算を行います
        //もし当たっていたらそのワールド座標を、当たっていなかったらnilが帰ってきます
        if let hitPos = rect.hitTestGetPos(origin: ray.origin, direction: ray.direction) {
            rect.setColor(f4(0, 1, 0, 1)) //緑色に
        } else {
            rect.setColor(f4(1, 1, 1, 1)) //白色に
        }
    }
}
```
![ForQiita 2023年-02月-28日 23.33.33.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567236/6e583952-95fa-3d7e-2135-4b9f3b33f393.gif)
このようにマウスがホバーすると色が変わるようになりました。当たり判定はRect・UIViewObject・Box・Imgにつけることができます。

## クリックすると回転する長方形
```Sketch5.swift
class RotatingRect: HitTestableRect {
    @SCAnimatable var rotation: Float = 0.0 //SCAnimatableラッパーをつけると簡単にアニメーションができます
}

class Sketch5: Sketch {
    let rect = RotatingRect().setColor(f4(1, 1, 1, 1)).setScale(f3(5, 5, 5))

    //update()は毎フレームdraw()の前に呼ばれます
    override func update(camera: some MainCameraBase) {

        //$rotation.update()を呼ぶことによりアニメーションする値を更新しています
        //deltaTimeはその名の通り前回のフレームからの時間です。固定値でもいいですが、deltaTimeを用いるとマシンスペックによる挙動の差が少なく抑えられます
        rect.$rotation.update(multiplier: deltaTime * 5) 
    }
    override func draw(encoder: SCEncoder) {

        //$rotation.animationValueに現在のアニメーション用の値が入っているので、それを用いてY軸回転させます
        rotateY(rect.$rotation.animationValue)

        //getCustomMatrix()はとりあえずつけておいてください
        rect.drawWithCache(encoder: encoder, customMatrix: getCustomMatrix())
    }
    override func mouseDown(with event: NSEvent, camera: some MainCameraBase, viewFrame: CGRect) {
        let mousePos = mousePos(event: event, viewFrame: viewFrame)
        let ray = camera.screenToWorldDirection(
            screenPos: mousePos,
            width: Float(viewFrame.width),
            height: Float(viewFrame.height))
        if let _ = rect.hitTestGetPos(origin: ray.origin, direction: ray.direction) {
            rect.rotation += Float.pi
        }
    }
}
```
![ForQiita 2023年-02月-28日 23.43.36.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567236/c47dacbf-d553-f8fc-962a-f64bbe0007ab.gif)
このように簡単にease-outでアニメーションすることができました。

## その他
長くなってしまうので別記事でまた続きを解説します。
|まだ紹介していない機能|
|-|
|テキストの描画|
|TextFactory|
|自作クラスでSketchの関数を使う方法(SCPacket)|
|PostProcessingと自作シェーダーの読み込み|
|PostProcessingプリセット(角丸やBloom)|
|3Dモデル|
|Cameraのコンフィグ(透視投影・平行投影など)|
|描画のコンフィグ(フレームレートなど)|
|ブレンドモード|
|UIViewObjectとxibとIBAction|
|Lighting|
|Fog|

## swifty-creativesで作れるものの例
|3Dグラフ|L-System|
|-|-|
|![](https://user-images.githubusercontent.com/28947703/221126530-c362018e-325c-4747-8e57-c5e18ab7085d.gif)|![](https://user-images.githubusercontent.com/28947703/217897685-7a83bedf-5624-45e2-b566-9a05aab7c103.gif)|

|Boxいろいろ|Bloomエフェクト|
|-|-|
|![ExampleMacOSApp 2023年-03月-01日 0.06.07.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567236/663b3dba-d0a0-bf93-b1ae-de084c5c9191.gif)|![CheckMacOS 2023年-03月-01日 6.46.57.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567236/f82f8cf9-16d4-43f0-cc48-0de2187d2d06.gif)
|
## 関連
https://qiita.com/yukiny/items/43ff5ded25ecbe6caf22
https://qiita.com/yukiny/items/7b5692b503fc7db921d4
https://github.com/yukiny0811/swifty-creatives