Rust Monthly Topics

3D CADアプリFornjotとRust

Rustのコミュニティの出来事についてまとめている"This Week in Rust"というサイトがあります。その"This Week in Rust"の428号あたりから、464号あたりまで、 Fornjotというプロジェクトの開発状況のアップデートに関するリンクがほぼ毎回掲載されていました。本稿ではそのFornjotについて紹介をします。

Fornjotとは

FornjotはRustで書かれた3D CADアプリ、つまり3次元モデルを設計するためのアプリです。3D CADアプリと聞いて多くの方が思い浮かべるのは、GUIでポインティングデバイスを使って3次元モデルを設計するアプリではないでしょうか。Fornjotによる3次元モデルの設計はGUIでは行いません。Fornjotは「コードファーストCADアプリ」を名乗り、Fornjotではコードによって3次元モデルを生成します。同じようなアプリをご存じない方にはわかりにくいかもしれませんので、簡単なコード例を以下に示します。

#[fj::model] // `model`関数をモデル関数に設定
pub fn model(#[param(default = 10.0)] side: f64) -> fj::Shape {
    #[rustfmt::skip] // 見やすさのため`rustfmt`での整形をスキップ
    let square = fj::Sketch::from_points(vec![ // 4つの点を指定して立方体の底面を生成
        [0.0, 0.0],
        [side, 0.0],
        [side, side],
        [0.0, side],
    ]);
    let cube = fj::Sweep::from_path(square.into(), [0.0, 0.0, side]); // 生成した底面をZ軸方向にスイープして立方体を生成

    cube.into() // `cube`を`fj::Shape`型に変換して戻り値とする
}

上記のコードを実行することによって、図に示す3次元モデルが生成されます。コードの詳細については後述します。

図1 出力されたSTL形式の3次元モデル
図1

コードから3次元モデルを生成する3D CADアプリはFornjotが登場する前から存在しており、OpenSCADが有名です。なぜ新しい3D CADアプリを開発したのか、なぜコードファーストCADアプリなのかといった疑問については、Fornjotの開発者がFornjotのブログFAQでたくさん語っているので、そちらをご参照ください。

本稿ではFornjotの簡単な使い方について紹介していきますが、まずご了承いただきたい点について説明します。Fornjotはまだ開発の初期にあるアプリであり、現時点では実用的に使用できる段階には至っていません。また、開発されて間もないアプリであるがゆえに、今後大規模な変更が加えられる可能性があり、本稿の内容が適用できなくなることもあり得ます。これらの状況をご理解いただいたうえで本稿をお読みください。

なお、この記事は次の環境を前提としています。

macOS Ventura 13.0.1
rustup 1.25.1 (bb60b1e89 2022-07-12)
rustc 1.65.0 (897e37553 2022-11-02)
cargo 1.65.0 (4bc8f24d3 2022-10-20)
Fornjot 0.23.0

Fornjotの使い方〜設計用プロジェクトの作成

Fornjotで3次元モデルを生成するためにはfj-appというプログラムを使います。まず、cargoを使ってfj-appクレートをインストールします。

$ cargo install fj-app

Fornjot ではライブラリクレートのプロジェクトを作って、そのプロジェクトの中で3次元のモデルを設計します(以下「設計用プロジェクト」と呼ぶことにします⁠⁠。適当なディレクトリに設計用プロジェクトを作ります。

$ cargo new --lib fj-playground
$ cd fj-playground

設計用プロジェクトでは、Fornjotのエンドユーザ向けAPIを定義しているfjクレートが必要になりますので、Cargo.tomlで依存関係を設定します。Cargo.tomlを以下のように記述します。

コード Cargo.toml
[package]
name = "fj-playground"
version = "0.1.0"
edition = "2021"

[dependencies]
fj = "0.23.0"

なお、Cargo.tomlでは特に触れられていませんが、設計用プロジェクトはクレートタイプcdylibのライブラリクレートとしてビルドされますfj-hostクレートのソースコード⁠。その理由については後述します。

クレートタイプをcdylibに変更すると、ライブラリクレートが動的システムライブラリを出力するようになります。

それでは、src/lib.rsに設計用のコードを書いていきます。先に掲載した立方体を生成するコードを再掲します。このコードからは、1辺が10の立方体が生成されます。

コード src/lib.rs
#[fj::model] // `model`関数をモデル関数に設定
pub fn model(#[param(default = 10.0)] side: f64) -> fj::Shape {
    #[rustfmt::skip] // 見やすさのため`rustfmt`での整形をスキップ
    let square = fj::Sketch::from_points(vec![ // 4つの点を指定して立方体の底面を生成
        [0.0, 0.0],
        [side, 0.0],
        [side, side],
        [0.0, side],
    ]);
    let cube = fj::Sweep::from_path(square.into(), [0.0, 0.0, side]); // 生成した底面をZ軸方向にスイープして立方体を生成

    cube.into() // `cube`を`fj::Shape`型に変換して戻り値とする
}
図2 3次元モデルの説明
図2

src/lib.rsではfj::modelアトリビュートの付いた、戻り値の型がfj::Shapeとなる関数を定義します(以下「モデル関数」と呼ぶことにします⁠⁠。モデル関数の戻り値が最終的に生成される3次元モデルです。この例では、modelという名前の関数がモデル関数にあたります。

モデル関数には、3次元モデルの生成時にfj-appから値を渡すことのできるパラメータを設定できます。モデル関数の引数にparamアトリビュートを付けることで、引数がパラメータとして扱われます。このコード例では、立方体の1辺の長さとして既定値が10のsideというパラメータを使っています。

モデル関数ではまず、fj::Sketch::from_points関数を使って1辺が10の正方形のfj::Sketchを生成して、それがsquareを束縛しています。次に、fj::Sweep::from_path関数を使って、squareをZ軸方向に10だけスイープした形、つまり1辺が10の立方体を生成して、それがcubeを束縛しています。

最後に、into関数でcubefj::Shapeに変換して、これをモデル関数の戻り値としています。

Fornjotの使い方〜3次元モデルの生成

作成した設計用プロジェクトから3次元モデルを生成するには、fj-appを使います。fj-appでは設計用プロジェクトのディレクトリを-mオプションで指定します。

$ fj-app -m .
図3 fj-appのスクリーンショット
図3

これで、3次元モデルの表示用のGUIが起動して、生成された3次元モデルを見ることができます。

また、-eオプションを使うことで3MF形式やSTL形式のファイルに3次元モデルをエクスポートすることもできます。

$ fj-app -e cube.stl -m .

これで、cube.stlというファイルに3次元モデルがエクスポートされます。STL形式は現状多くの3Dプリンタのスライサがサポートしているファイル形式ですので、エクスポートの機能を使うとFornjotで設計した3次元モデルを3Dプリンタで出力するといった使い方も見えてきます。ちなみに、筆者が3D CADアプリに興味を持ったのは、自作キーボードのケースを設計して3Dプリンタで出力したかったからでした。

前述のモデル関数のパラメータについては-pオプションを使って変更できます。sideパラメータの既定値は10ですが、次のようにすると1辺が20の立方体が生成されます。

$ fj-app -m . -p side=20

立方体なので3次元モデルの見た目だけでは変化が分からないかもしれませんが、サイドバーに「Model bounding box size: 20.0 20.0 20.0」と表示されています。

ここまでFornjotの簡単な使い方を紹介しました。本稿で示した例は、Fornjotの説明をするために単純化したものですので、ご自身で書き換えて、どのような3次元モデルが生成できるのか試してみてください。Fornjotで使用できるAPIについては、fjクレートのドキュメントで調べることができます。

また、FornjotのGitHubリポジトリにもいくつかのサンプルがあります。例えば、starモデルは、既定値ではとげの数が5個の星型をベースとした立体を生成します。

$ git clone -b v0.23.0 https://github.com/hannobraun/Fornjot.git
$ cd Fornjot
$ cargo run -- -m star
図4 とげが5個の星型ベースのモデルのスクリーンショット
図4

starモデルのモデル関数にはnum_pointsというパラメータがあります。num_pointsパラメータを変更することで、starモデルからとげの数を変更した星型をベースとした立体を生成できます。次のようにすると、とげの数が10個の星型をベースとした立体が生成されます。

$ cargo run -- -m star -p num_points=10
図5 とげが10個の星型ベースのモデルのスクリーンショット
図5

どのようにしてこのようなことを実現しているのか、興味のある方はstarモデルのモデル関数を見てみてください。

この記事では、2次元モデルを生成して、それをスイープすることで3次元モデルを生成するという3D CADアプリの基本的な操作を紹介しました。さらに複雑な3次元モデルを生成するとなると、複数の3次元モデルを生成して、それらの3次元モデルに対して和、差、積といったブーリアン演算を行うことになるのですが、現時点ではFornjotにはブーリアン演算が実装されていません。もちろん、Fornjotの開発者もブーリアン演算の重要性は理解しており、その実装には高い優先度が設定されていますので、今後に期待したいところです。

コードファーストCADアプリのDSLとしてのRust

現時点で、Fornjotで3次元モデルを設計するために使用できるDSL(Domain-specific Language)はRustのみです。Rustの開発環境はかなり整備されてきており、モデル関数を書くときにもRustの開発環境の恩恵を受けることができます。しかし、コードファーストCADのDSLとしてRustが最適なのかと言われると、若干の疑問を持たれる方もいらっしゃるのではないでしょうか。Fornjotの開発者も、コードファーストCADのDSLの1つの選択肢としてRustは「あり」だとしつつも、基本的にはFornjotをプログラミング言語中立にしておきたいと考えていて、Rust以外のプログラミング言語でも3次元モデルを設計できるようにしたいと考えているようです。

ところで、3次元モデルの生成にかかる時間をできる限り短くすることは、開発体験の向上につながります。また、3次元モデルのコードの修正結果は、できる限り早く、できれば即時に生成される3次元モデルに反映されてほしいところです。Rustはプロジェクトのビルドに時間がかかると言われることがありますが、Fornjotでは設計用プロジェクトからどのようにして3次元モデルを生成しているのでしょうか。

設計用プロジェクトはRustのライブラリクレートのプロジェクトと同じ形式をしていて、設計用プロジェクト単体で何かを実行できるものではありません。前述のとおり、設計用プロジェクトから3次元モデルを生成するにはfj-appを使う必要があります。設計用プロジェクトをバイナリクレートのプロジェクトの形式にして、3D CADエンジン、GUIなどのためのFornjotのクレートを依存関係に設定した上でcargo runで3次元モデルを生成するというしくみもあり得るのですが、そのようなしくみを現状のFornjotは採用していません。

設計用プロジェクトをたくさんのクレートに依存させてしまうと、設計用プロジェクトのビルドに時間がかかってしまい、前述の「3次元モデルの生成にかかる時間をできる限り短くする」という目標から遠ざかってしまいます。そこで現状のFornjotでは、設計用プロジェクトをFornjot本体とのインターフェイスとなるfjクレートのみに依存させて、動的システムライブラリを出力するように設計用プロジェクトをビルドしています。そしてfj-app(正確には、fj-appクレートが依存しているfj-hostクレートでは、libloadingクレートを使って設計用プロジェクトから出力された動的システムライブラリをロードして、3次元モデルを生成します。これが設計用プロジェクトのライブラリクレートのクレートタイプをcdylibにしている理由です。このようなしくみを採用することで、Fornjotでは設計用プロジェクトのビルドの時間を短縮しています。

まとめ

本稿では、3D CADアプリのFornjotについて、簡単な使い方を紹介しました。Fornjotには今後、設計用プロジェクトの出力を動的システムライブラリからWebAssemblyへと変更する計画もあるようで、技術的にも面白いプロジェクトです。興味を持った方は、ぜひFornjotのリポジトリを覗いてみてください。

おすすめ記事

記事・ニュース一覧