找回密码
立即注册
搜索
发回帖 发新帖

6702

积分

0

好友

852

主题
发表于 16 小时前 | 查看: 4| 回复: 0

起因是 Reddit 上的一个帖子:有人贴了张图,问这种花边到底怎么用 LaTeX 做出来。

问题挂在排版论坛里好些天,没人给出源码。过了些日子,一个叫 Kevin Raymond 的人回了一帖,大意是:我也不确定原作者怎么实现的,但这是其中一种做法。

随后他把代码开源了。项目叫 florilegium,10 月 30 号上线,现在 1173 星。

我蹲了一晚上,最想说的是:这里面一张图片都没有。

LaTeX程序化装饰页面截图,展示左侧蓝色植物插画与正文内容

不是「没用 PNG」那种程度。连一个像素都不是画出来的。每一片花瓣、每一根枝条、每一片叶子后头那点淡淡的影子,都是 LaTeX 排版那会儿用 Lua 现算出来的曲线,直接写进 PDF。

它到底画了什么

先说说长什么样。截图这东西,传不到位。

A4 纸,左边留 68 毫米的页边距。这 68 毫米本来是空的,作者把它变成了园子。

底下是土,上面长出来两三根主枝。枝条从小往上抽,每长到一个节点,就往旁边开一片叶子;枝头最后开一朵花。周围有空的地方,再长藤、再长碎花,塞满为止。

然后是点缀:开放到一半的地方会停一只蝴蝶或者蜜蜂,一根主枝上会有一只蜗牛往上爬,大概五页里有一页,会有一只鸟落在自己那根小树枝上。

程序化生成的花园细节放大图,展示蓝色鸟与植物插画的水彩质感

这是 300 dpi 的局部放大。你能看到花瓣上那种斑驳的水彩感,中间还有提白的白丝;叶子是有裂片的,叶脉是白的;枝条后面还藏着浅浅一层「幽灵花」,就像水彩干了以后再叠上去的一层。

这种质感如果是一张手绘扫描图,那一点都不奇怪。问题是它是一堆数学函数。

为什么每页都不一样

因为整座园子是长出来的,不是摆出来的。

它的工作流程大概这样:先有一颗种子(seed),种子定了这一次生长所有的随机数。主枝抽多高、往哪边偏、叶子在哪个节点张开、花开几瓣、花瓣什么角度,全是这颗种子摇出来的。

同一个文档,改一个 seed,就是另一个花园。

书籍页面展示seed 1生成的花园,蓝色植物从页角生长蔓延

同一套代码,六个种子,六种长相。作者在 README 里那句话挺有意思:换个种子,园子会填满正文留出来的任意一块空间。

这话不是修辞。这个包真会读你的排版:正文写到哪、还剩多少空白、最后一页的正文是不是提前结束了。园子就照着这块空间长。它不预设「现在该开花了」,只干一件事:看看还有多大空当,再决定往那儿长什么。

1998 年的一篇装饰学

藤蔓是自动填的。这一步最容易做砸:要么空着,要么堆成一团。

作者用的办法挺老派。他说,这部分「填满没用上的空间」的思路,借的是 1998 年装饰学文献里的一个想法。

具体是这样:程序反复去找当前最大的那块空白圆,然后长一根枝过去,让枝头正好占住这个圆。大洞先补,补得慢;小洞后补,补得快。补到没有一个能塞下的洞为止。

作者把生长过程录成了 GIF,旁边还放了一版带虚线圈的。每个虚线圆,就是那一帧找到的最大的空洞,下一根枝就奔着这个圆去。

这套逻辑的妙处在于:它不用知道「这里该画什么」,只要知道「这里还有多大」。空大的地方长主枝,空小的地方塞碎花,看上去就是匀的。

整页排版也顺手读了一遍。园子不只是填边距,它还会看正文长什么样:

段首 —— 首字下沉能当花盆用。\florilegiuminitial[5]{E} 把段首那个字母圈进一个五行高的方块里,字母的轮廓是从 OpenType 字体里读出来的,然后像花瓣一样上色。一根藤从笔画上面绕过去、再从下面绕回来,有空当的地方就长叶子、开花,花经常落在字母封口里的那个洞。

标题 —— 每个章节旁边开一朵。打开 sections 选项,每个带编号的章节标题旁边都长一朵花。作者说这是照着古书用红墨点标题的老规矩来的。

正文 —— 想让哪儿开花就在哪儿写一句。\florilegiumbloom 或者 \florilegiumbloom[lily],它就长在那一行的旁边。

翻页 —— 从春开到秋。打开 aging,第一页春天,大多是花苞、小叶、颜色浅;翻到最后是秋天,结籽、落瓣,夹几片铜色的叶子。

同一本书的三张内页展示春夏秋三季变化,蓝色花卉装饰边框风格

这三页是同一个种子,往下翻就是春天、夏天、秋天。作者强调了一句:季节改变的是「长什么」,不是「长在哪儿」。所以翻页的时候,位置是连贯的,只是东西在变老。

换个墨色,换套画法

整套配色只由一个颜色决定,叫 ink(墨)。

给蓝色,就是代尔夫特蓝;给棕黄,是乌贼墨;给绿、给绛红、给紫、给青,都能出。从最重的一笔到最淡的一层洗色,是在 OKLCH 里按同一个色相推出来的。作者特意点了一句:这样最浅的那头不会发灰。

六张不同颜色的植物插画设计图排列成两行三列,展示同一seed的不同配色效果

六个墨色,同一个种子。这一排放在一起看,能感觉到「换个颜色 = 换个整套气氛」这个事。

还有一块我觉得挺疯:主题。

生长循环其实根本不知道自己画的是什么。它只读一张表:每个图案占多大一块、权重多少、调哪个函数来画。把表里的函数一换,同样的生长过程就能长出完全不一样的东西。

作者写了两个主题:

几何主题 —— 同一株植物,用平面几何切出来。枝条在节点之间走直线,叶子是折起来的带锯齿多边形,花是菱形堆的或者多层星形,鸟和蝴蝶是低模的。颜色是平涂,没有水彩,花丛里还长水晶。

数学主题 —— 完全不是植物了。数学符号从页边往下淌,铺到页面底部,稍微叠在一起。拐角放一个大的数学图形,中间是「一族」图形慢慢变形。

数学主题值得多说一句。它选的图不是乱挑的:科赫雪花一层一层加上去;多边形一边一边逼近圆;立方体截角变成八面体;弦 $k$ 和 $mk \bmod n$ 连起来,图形从心脏线渐渐变成肾形线;还有 Vogel 圆盘,只有用黄金角排才是均匀的。

底边还跑着几个真正成立的式子:$e^{i\pi} + 1 = 0$、$V - E + F = 2$。

作者说了一句挺有意思的话:生长循环从来不知道自己在画什么。它只认名字。你给它一个叫 flower 的函数,它就长花;给它一个叫 crystal 的函数,它就长晶体。名字一样,过程一样,出来的东西两回事。

浏览器里也能种

这里有个我觉得最值得偷学的工程手法。

浏览器那个预览,你会以为是重写了一遍 JavaScript。不是。

svg.lua 干的是:把包自己的代码原封不动跑一遍,只给 TeX 那几个接口做了个小替身,管页面尺寸、页码、往哪儿画。再把 ink.lua 平时写给 PDF 的绘图指令改写成 SVG。

浏览器里长出来的园子,跟 LuaLaTeX 排出来那个,是同一个,不是模仿的。

README 里写了个可以验证的地方:--seed 7 加上默认页边距,出来的 SVG 和用种子 7 排出来的 PDF 第一页,逐字节一致。

浏览器那版是把 Lua 5.4 编译成 WebAssembly 跑的(项目叫 wasmoon),放在后台线程里,一页大概一秒长完。

那个在线预览页,本身就是控制台。这页我最喜欢的地方,是它没堆一堆滑块。它把页面做成一张印厂打样,旁边配一页「版权页」,就是书末那页写着用什么纸、什么墨、哪家厂印的。版权页上凡是用铜色印的词,点一下都能改:改纸、改墨、改页边距。

然后你把正文末尾往上一拖,排版就多出一页空白,园子自动往那块空白里长。

下面还有一项:红绿色盲模拟。它能按红色盲、绿色盲、蓝黄色盲三种视觉,把这一页分别渲染出来,并且按 OKLab 距离算出「墨色和重点色之间差多远」。差得太近、容易分不清的组合,你选了它会提示。

说一说过程

这个项目最坦白的地方,是它把制作过程也一起开源了。

作者在 PROCESS.md 开头放了个说明:我这些百分之百是人写的话,都放在这种灰框里。也就是说,凡是带框的段落是他本人的评论,其他部分是过程记录。

他自己讲的几条:

  • 从看到 Reddit 那个帖到 v1.0 发布,大约三个小时
  • 规划用的 Claude.ai,写代码用的 Claude Code
  • 选 LaTeX 和 Lua 是因为这两个他都不太熟,而且手上没有任何原始素材可以直接抄
  • 他原话:没有源码本来应该更容易避免照抄,但你会看到 Claude 一上来就是奔着照抄去的

这句我看了两遍。在自己项目的文档里写「AI 的第一反应是抄」,这份坦诚不多见。

他也表了态:不管你站 AI 这条线的哪一边,我尊重你的看法,也谢谢你容我讲我自己的。

还有一条我觉得挺克制的细节:theme=geometric 那一页,体积只有花卉版的十分之一左右。同样的生长过程,换成平涂和直线之后,画笔路径短了太多。这种「换个画法顺带把性能也换了」的情况,只有把「长什么」和「怎么画」拆开才可能。

收个尾

为什么想推这个项目,说白了三条。

一是它把「装饰」当成正经的算法问题。 大多数人做排版,插图就是插图,找一张、裁一下、塞进去。这项目答的是个更难的问题:这页需要一朵花,那这朵花该长在哪儿、多大、朝哪个方向、跟旁边的留白怎么相处。

二是它不追求「一次画对」。 靠种子和约束,让一堆随机的东西互相避让,长出来自己就舒服。这个思路能偷到不少别处去。自动生成封面、自动排海报,都能用。

三是那句「生长循环从来不知道自己在画什么」。 这是整个项目的骨架。过程和内容拆开了,同一个过程就能长出花卉、几何、数学三种完全不同的东西,代码一行没改。

想上手,最省事的就是开它那个网页预览:改几个铜色的词,拖一下正文的位置,看园子怎么重新长。

真要往自己文档里用,一条命令的事。具体用法和参数说明,可以参考项目的文档:

\usepackage[grown,density=0.4,intensity=0.6]{florilegium}

density 是长得密不密,intensity 是墨色淡不淡。两个都拉到最低,它就是一枝带花的藤,配一段文字正好。

项目地址 github.com/kevinraymond/florilegium,MIT 协议。作者是 Kevin Raymond,动手三个小时,种了一片能一直长下去的花园。




上一篇:全球法律塞进一个 Git 仓库:查旧法条可精确到某年某月某日
下一篇:对话亚马逊 CTO Werner Vogels:AI 写代码时代,初级工程师为何仍被大量招聘?
您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-11-2 19:29 , Processed in 0.067312 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表