HN 日本語サマリー

← 一覧へ戻る
その他

Florilegium: LaTeXによるプロシージャルな花

Florilegium: Procedural Flowers in LaTeX (github.com)

108 pointsby nanna20 コメント

要約

Florilegiumは、LaTeXでプロシージャルに生成されるデルフトブルー風の花柄のページ余白を作成するプロジェクトです。画像や外部アセットは一切使用せず、すべての花びら、葉、茎はLuaで計算されるパラメトリック曲線で、水彩画のように描画され、PDFに直接書き込まれます。テキストの配置に合わせて庭園が変化し、ページごとに異なるデザインが生成されます。初期設定、セクション見出し、特定の行、季節の変化、さらには訪問者(蝶、蜂、鳥など)まで、柔軟に対応可能です。

全文翻訳

florilegium

起源

Redditの投稿で、LaTeXでこのような花を作る方法が尋ねられました。元の作成方法は不明であり、このプロジェクトもそれを再現しようとはしていません。これは可能な方法の一つであり、私が採用した方法です:手作業で作成したプリミティブにランダムなパラメータを適用し、Luaでペイントし、1998年の装飾文献から借用した「未使用スペースを埋める」部分を持つプロシージャルレイアウトで配置します。これにより、似たような外観が得られます。

何か

プロシージャルに生成されるデルフトブルー風の花柄のページ余白をLaTeXで作成します。画像、インポートされたパス、外部アセットは一切使用しません。すべての花びら、葉、茎は、ページが書かれている間にLuaで計算されるパラメトリック曲線であり、水彩画のようにペイントされ、直接PDFに書き込まれます。各ページには独自の庭園が生成されます。

[画像: 6つのシード]

成長した構成の6つのシード。同じコードでも、シードが異なれば庭園はテキストが空けているスペースを埋めます。

2つのドキュメントが1つのパッケージを共有し、3つ目のドキュメントは他の機能を示しています:

florilegium.texflorilegium-grow.tex
構成手作業で作成:茎、葉、花を手で配置し、すべてのパラメータをサンプリング成長:貪欲なスペース充填ループによってページに合うように成長した植物
適応先なし(A4、68mmマージンに合わせて調整)ページレイアウト、および最後のページの下の空きスペース
プレビュー[画像: 手作業で作成][画像: 成長]

どちらのPDFも自己説明的です。本文は余白がどのように描画されるかの説明です。

[画像: 成長したドキュメントの最後のページ]

成長したドキュメントの最後のページ:テキストが早く終わるため、枝が下のスペースにアーチを描いています。

[画像: 詳細]

300 dpiのクロップ。白い筋が引き出されたまだらなウォッシュ、白い葉脈を持つ葉、忘れな草の集散花序、茎の後ろにある淡いゴーストフラワー。

成長を見る

シード6、空の余白から完成したページまで。茎が最初に立ち上がり、葉はその先端が節を通過すると開きます。その後、貪欲なループが引き継がれます。右側では、破線円はそれぞれ、それが発見した最大の空きディスクであり、その直前に枝がそれに成長します。大きな穴が先に取られ、ゆっくりと、花やユリが早く着地します。スプレーや葉が中間を埋め、ループは小葉、つぼみ、小花を茎に詰め込んで終了します。テキストの終わりは同じパスで\pagetotalを使用して測定されるため、短い最後のページは1回のコンパイルで下のスペースに枝を伸ばします。make grow-6はこの方法で1つのシードをレンダリングします(build/grow-6.gifと.mp4、約45秒)。EXPLAIN=1を追加すると破線円が表示されます。

余白はテキストを読む

[画像: showcase.texの3ページ]

showcase.tex、「余白の一年」:最初のページに春、夏、最後のページに秋。各セクションは、それが示す特徴を説明しています。

成長した余白は、周囲のドキュメントに応答できます:

  • イニシャル。\florilegiuminitial[5]{E}は、段落の最初の文字を5行分のタイルに設定します。文字のアウトラインは(デフォルトではLatin Modern Boldの)OpenTypeフォントから読み取られ、花びらのようにペイントされます。ツタがそのストロークの上や下を交互に通過し、葉は収まる場所に、花は隙間に配置されます。文字は目に見えないようにタイプセットされるため、段落は「Every」としてコピーされます。
  • 見出しの花。sectionsオプションを使用すると、番号付きのセクションとサブセクションごとに、かつて見出しが赤で際立っていたように、マージンにその見出しの横に花が描かれます。
  • テキストが要求する場所の花。\florilegiumbloomは、それが立つ行の横に花を配置します。\florilegiumbloom[lily]は庭園の任意のモチーフを要求します。マークはページが出力されるときにページボックスで見つけられるため、これは1回の実行で機能します。
  • 季節。agingオプションを使用すると、庭園は最初のページの春(主に蕾、小さな淡い葉)から最後のページの秋(種子、散る花びら、いくつかの銅色の葉)へと変化します。これには2回の実行が必要です(ページ数が必要)。\florilegiumage{0.8}は代わりに手動で年齢を設定します。

[画像: 4つのイニシャル]

[画像: 季節ごとの1つのシード]

春、初夏、晩夏、秋の1つのシード。季節は成長するもの、場所を変えます。

訪問者

[画像: 鳥とカタツムリの詳細]

ほとんどのページには訪問者がいます:開いたスペースには蝶が1〜2匹と蜂、主な茎を這い上がるカタツムリ、そして約5ページに1つは独自の枝に止まった鳥がいます。それらは独自のランダムストリームから来るため、訪問者のいないページは、それがあったであろうページと全く同じです。novisitorsオプションはそれらを省きます。

どんなインクでも

[画像: 6色のインクで描かれた1つのシード]

パレット全体は1つの色、inkから来ています:デルフトブルー、セピア、グリーン、クリムゾン、バイオレット、ティール。最も濃い線から最も淡いウォッシュまでのランプは、OKLCHでその色相から計算されるため、明るい端は決して灰色になりません。

PostScriptで

[画像: postscript/florilegium.psの2ページ、シード7]

postscript/florilegium.ps:純粋なPostScriptで成長した余白、1つの自己完結型ファイル。Ghostscriptまたはプリンターは、ページを解釈しながらすべての花びらを計算し、2ページのテキストがその方法を説明します。

描画コア、すべてのプリミティブ、成長ループはink.lua、flora.lua、grow.luaから手続きごとに移植されています。TeXや他のファイルは必要ありません:

gs -dALLOWPSTRANSPARENCY -sDEVICE=pdfwrite -dSeed=7 -o garden.pdf postscript/florilegium.ps
make postscript             # 同じ結果 -> build/postscript-7.pdf; SEED=12 で別の庭園

-dSeed=Nは庭園を選択します。指定しない場合はクロックが選択するため、印刷ごとに新しい庭園が生成されます。-dAge=0から1は季節を設定し、-dNoVisitorsは蝶、蜂、カタツムリ、鳥を省きます。2ページは約2.5秒かかります。Luaの多くは直接マッピングされます:PostScriptの独自の変換行列が各プリミティブ(移動、回転、スケーリング)を配置し、パスとベジェ曲線はネイティブであり、実行時に構築された手続きはLuaのクロージャ(花びらの半幅はアウトラインビルダーに渡される手続き)の代わりになります。4つのマッピングされない点があります:

  • レイヤー。庭園は1つの順序で成長しますが、背面から前面にペイントされるため、各フィルとストロークはpathforallを使用してレイヤーごとのリストにキャプチャされ、リストは最後に再生されます。
  • 透明度。PostScriptには透明度はありません。Ghostscriptは-dALLOWPSTRANSPARENCYで提供します。このフラグがない場合、またはプリンター上では、各半透明のフィルは、着地する可能性のある色と混合されて不透明としてペイントされます。結果はよりフラットになりますが、庭園は同じです。
  • ランダム性。ジェネレータは32ビット整数演算のPark-Millerであり、PostScriptの浮動小数点数は単精度であるため、ここでのシード7はLaTeXでのシード7とは異なる庭園です。
  • テキスト。ファイルはTimesで独自の2ページをタイプセットします。見出しの場所とテキストの終了場所を知っているため、見出しの銅色の花と最後の行の下の庭園はLaTeXの場合と同様に機能します。イニシャル、アニメーション、手作業での構成はありません。

[画像: florilegium.psの2つの印刷、2つの異なる庭園]

lp postscript/florilegium.psでEpsonインクジェットプリンターに2回送信されたファイル。シードは指定されていません。コンピュータの印刷システムはジョブごとにプログラムを再度実行するため、各印刷はクロックからシードを取得し、異なる庭園を生成します。プリンターは透明度がないため、不透明なバージョンを取得します。

ビルド

LuaLaTeX、1パス、1ページあたり約1秒(showcase.texは2パス、季節のためページ数が必要)。

make                        # 3つのPDF
make seed-7                 # シード7からの1ページ -> build/seed-7.pdf
make gallery                # gallery/seeds.png + seeds.gif, inks.png, seasons.png (python3, Pillow, pdftoppm)
make grow-6                 # シード6の成長アニメーション -> build/grow-6.gif + .mp4 (ffmpeg)
make grow-6 EXPLAIN=1       # 同じものを破線円付きで -> build/grow-6-explain.*

TeXがインストールされていない場合:イメージを一度ビルドし、その中ですべてを実行します。

docker build -t florilegium-tex tools
make RUN=tools/in-docker

または、florilegium.sty、6つの.luaファイル、および.texファイルのいずれかをOverleafプロジェクトに貼り付け、コンパイラをLuaLaTeXに設定します。

独自のドキュメントで使用します:

\usepackage[grown]{florilegium}      % または手作業用には plain \usepackage{florilegium}
% grown用のオプション:sections(見出しに花)、aging(2パス)、
% novisitors(蝶、蜂、カタツムリ、鳥なし)
\definecolor{ink}{HTML}{5B2A1F}      % オプション:パレット全体の色相(これはセピア)
\definecolor{accent}{HTML}{B4662F}   % オプション:ヘアラインの銅色
...
\florilegiuminitial[4]{T}his paragraph starts with a decorated T.
... a flower in the margin beside this line\florilegiumbloom ...
\florilegiumage{0.8}                 % grownのみ:ここから秋になる
\florilegiumtextend                  % grownのみ:テキストの最後の行に

ページ番号の代わりに固定シード:lualatex "\def\florilegiumseed{7}\input{florilegium-grow.tex}"。

任意のプリミティブをTikZピクチャ内で、現在のスコープのシフト、回転、スケールの下で単独で描画できます:

\begin{tikzpicture}
  \begin{scope}[shift={(2,2)},rotate=20]\florilegiumdraw{flower}{1.3}\end{scope}
  \begin{scope}[shift={(5,0)}]\florilegiumdraw{leaf}{3.0, {depth=0.6}}\end{scope}
  \begin{scope}[shift={(8,1)}]\florilegiumdraw{ghost}{"lily", {s=1.4}}\end{scope}
  \begin{scope}[shift={(11,1)}]\florilegiumdraw{butterfly}{1.5}\end{scope}
\end{tikzpicture}

仕組み

描画(ink.lua)。すべてのジオメトリはLuaで計算され、ページごとに1つのリテラルノードとしてPDFパスオペレータとして書き込まれます。TeXは算術演算を行いません。プリミティブは、プリミティブがスコープ内で呼び出されたときにTikZから読み取られるアフィン変換の下でローカルフレームで描画されます。色は、1つのドキュメントカラーinkから派生したトーンランプから来ています:ネイビーからミストへ、inkの色相で、OKLCHで補間されるため、明るい端は灰色にならずに青のままです。テーパー付きストロークは、サンプリングされたリボンではなく、2つのオフセットベジェ曲線です。

プリミティブ(flora.lua)。水彩画家が描く順序でペイントされます:フラットなウォッシュ、その上に掛け合わされた暗い斑点、エッジに集まる顔料、白で引き出された筋、いくつかの細い葉脈、そしてほとんど輪郭はありません。広い花びらと花、傾いて見えるスターリリー、つぼみと小花、蕾と釣鐘草;葉状と全縁の葉;サソリの尾のようにカールする忘れな草の集散花序、分岐した小枝、散形花序、巻きひげ、種子頭;蝶、蜂、カタツムリ、鳥;銅色の輝き、ロゼット、そしてヘッドルールになる茎を持つアイビーの葉のフレア。

手作業での構成(authored.lua)。植物は左下隅の根から成長します:手で選ばれた制御点を通る3本の茎、フッターとしてページの下部沿いの1本、葉は茎の選択された割合の位置に、そして他のすべてのモチーフは茎から接線方向に外れてモチーフの軸に沿って到着する枝に配置されます。これにより、すべての花が根にたどることができます。

成長した構成(grow.lua)、Wong、Zongker、SalesinのComputer-Generated Floral Ornament(SIGGRAPH 1998)の精神で。茎は、その高さの滑らかなノイズとして列を上に揺れ動きます。葉は、スペースがあるノードに掛けられます。次に、繰り返し:茎の届く範囲内で最大の空きディスクを取り、それに合うモチーフを選択し、最も近い茎の点から枝を成長させます。枝は次に茎になります。大きな穴が先に取られるため、花やユリが早く着地し、スプレーや葉が中間を埋め、ループは小葉、つぼみ、小花を茎に詰め込んで終了します。テキストの終わりは同じパスで\pagetotalを使用して測定されるため、短い最後のページは1回のコンパイルで下のスペースに枝を伸ばします。

ページの読み取り(florilegium.lua)。見出しまたは\florilegiumbloomは、テキスト内に目に見えないマーカーノードを残します。ページが出力されるとき、そのボックスはノードごとに歩かれ、設定されたときの高さ、深さ、グルーを合計して、各マーカーのページ上の高さを求めます。その後、庭園はこれらの行に、葉が掛けられる前に花を与えます。年齢はモチーフの重みを再計算します:春はつぼみとつぼみのスプレー、秋は種子頭、そしていくつかの葉はインクではなくアクセントから作られたランプでペイントされます。

イニシャル(initial.lua)。文字のアウトラインは、luaotfloadのOpenTypeリーダーからベジェセグメントとして取得されます。ポリゴンにフラット化されると、偶奇の内部テストが得られ、文字とフレームへの距離のグリッドが、花のための最大の隙間を見つけます。ツタはタイルを横切るCatmull-Rom曲線です。文字を横切るときは交互に上と下に行き、上を通るストランドは紙の薄いハローとともに文字にクリップされて再度描画されます。

プロシージャル ≠ ランダム。シードはページ番号から派生するため、ページnは常に庭園nを取得します。再現性により、ドキュメントはタイプセットするのに十分安定します。

CLAUDE.mdで、コードのハンドオフスタイルのマップ、チューニング可能なパラメータ、およびまだ完了していないアイデアを確認してください。

ライセンス

MIT。