Pave.jsというパス弄りライブラリを作っている
映像作家の橋本麦(はしもと・ばく)です。普段はモーショングラフィックスや3DCG、コマ撮りやインスタレーション制作などをしています。そのなかでジェネラティブな表現やグラフィック制作のために、p5.jsといったクリエイティブコーディング環境をよく利用していたりします。
ノリでProcessing Advent Calendar 2024に登録したものの、テクニック面で有用な記事は書けそうにないので、手前味噌ながらも最近ぼくが開発しているパス操作ライブラリ Pave.js について紹介させてください。
早い話、こんな感じの造形が楽しめます。
TL;DR
- かたちを操作するためのライブラリ
- 関数型API: p5のAPIが仮想的なペンをどう動かすかを
moveTo,lineToといった描画コマンドで逐次的に指示するのに対し、Pave.jsでは、データとしてのパスを組み合わせたりフィルターを重ねていくことで欲しいパスを得る - 関数の例
- パスを作り出す関数
- arcByPoints: 3点を通る円弧を返す
- nBezier: N次ベジェを返す
- formula: 媒介変数表示された関数をベジェ補間しながら返す
- パスの性質を得る関数
- パスを組み合わせる関数
- パスを変形する関数
- パスを作り出す関数
- 描画コマンドベースのAPIでは思いつきづらい、絶妙なニュアンスを持った図形をジェネラティブに描くことができる
- p5.jsに限らず、Canvas APIやPaper.jsといったライブラリで使用可能
簡単な例
例えば、p5.jsで「角丸の正五角形」を描こうとすると、beginShapeやquadraticVertexを用いて、角丸によってオフセットした頂点や円弧を書き順に沿ってプログラムにする必要があります。面倒くさいのが目に浮かびます。
Pave.jsの場合、パスのジェネレーターやフィルターを組み合わせて、以下のように2行で表現することが可能です。
import {Path} from 'https://cdn.jsdelivr.net/npm/@baku89/pave/+esm'
import {vec2} from 'https://cdn.jsdelivr.net/npm/linearly/+esm'
window.setup = () => {
createCanvas(400, 400)
background('white')
// Paveにおけるベクトルは配列として表現される
// p5.Vectorの代わりにglMatrix.jsやLinearlyなどの線形代数ライブラリを用いる
const center = vec2.scale([width, height], .5)
// 正五角形のパスを生成
const pentagon = Path.ngon(center, 200, 5)
// 角を丸める
const roundPentagon = Path.fillet(pentagon, 20)
strokeWeight(5)
strokeJoin('round')
noFill()
Path.drawToP5(roundPentagon)
}

注意すべき点は、Pave.jsの関数が返すのはあくまでパスを表すデータであって、Path.drawToP5(path)を呼び出して初めてキャンバス上に描画されるということです。パスデータはすべてイミュータブル(変更不可)で、各種関数は常に効果を適用した新しいパスデータを返します。また、頂点などの2次元ベクトルは[20, 30]のような配列として表現されます。p5.Vectorには現状対応していません。
インポートの仕方
現状、Pave.jsはES Moduleしか対応していないため、p5.js Web EditorやOpenProcessingで使うためにはコツが必要です。
- p5のスケッチが書かれた
<script>タグにtype="module"を追加する - p5をグローバルモードで動かしている場合、以下のように書き換える
function setup()→window.setup = function ()function draw()→window.draw = function ()
- CDNから
import文でPave.jsの必要なモジュールを読み込むimport {Path} from 'https://cdn.jsdelivr.net/npm/@baku89/pave@0.5.1/+esm'
文章だけだと分かりづらいので、p5.js Web Editorにおけるセットアップ例を以下にスクリーンキャプチャ込みでポストしています。
Now pavejs supports p5js. You can call
Path.drawToP5(path, p5Scope = window)
to draw the path onto your sketch anywhere.
Pave.jsで何がしたいのか ― マクロなジェネラティブアート
そういえば先日、robamotoさんとGenerative DesignについてSpacesで話していました。山本さんが10年前に書いていた記事『Generative designと解像度』は「なぜ我々はジェネに惹かれるのか」を明快に言語化した記事で、ぼくもウンウンと頷きながら読んだのを覚えています。記事のなかで繰り返し強調されているのが、「人の手では描き得ないキメの細かさ」の快楽です。OpenGLやPostScriptのline関数で書かれるパリッとしたスプラインや点群は、ジェネラティブ・アートの醍醐味なのかもしれません。
こうした微細さを突き詰めていく方向性を「ミクロなジェネラティブアート」と仮に呼ぶとすれば、ぼくが最近気になっているのは「マクロなジェネラティブアート」ともいえる表現です。つまり、ストロークや点といった、かたちを構成する要素ではなく、全体的な輪郭や造作が醸し出すヘンテコリンさ、「おやっ?」感のようなものです。
ここからはだたの個人的な好みにはなりますが、Aphex Twinや攻殻機動隊SACの笑い男事件のロゴ(Winnyのアイコンでも有名ですね)を手掛けたPaul Nicholsenのグラフィックは、その珍妙な佇まいにどこか目を奪われる感覚があります。
Number 3 - artstation
TDC賞にも度々入選し、BjorkやLOEWEのデザインで有名なM/M (Paris)が作り出す造形も ― 彼らは伝統的なタイポグラフィー教育を受けているものの ― ジェネラティブ・アートという視点からも魅力的に映ります。
Letters from M/M (Paris) - TOKYOTDC
作字文化やグラフィックイラストレーションにも近いこうした表現は、反復性や複雑さ、解像度に強度を置く伝統的なジェネラティブ・アートのスタイルに比べると、まだまだ掘られていないのではないでしょうか。
「そのかたちがどのようにできているか」をモジュラーに構成するPave.jsは、このようなマクロなジェネラティブ表現を試しやすいように設計されています。現状はPaper.jsやBezier.jsをまるっとラップしただけの拙さのあるライブラリですが、「どう描くか」を逐次的に書き下すことができるp5.jsのAPIを補完するサードパーティープラグインへと今後発展していき、クリエイティブ・コーディングにおける図形描画に異なるメンタルモデルをもたらす存在になってくれたらと願っています。ご興味のある方は、是非開発に参加いただけたら嬉しいです。