← 返回信息流
AI 资讯Hacker News·1 天前

Lil 语言引入矢量图形支持

原标题:Vector Graphics in Lil

速览

Lil 语言近期更新中引入了对矢量图形的原生支持。这一特性扩展了该语言在图形处理和可视化领域的应用能力。

AI 深度解读

Vector Graphics in Lil:Decker 生态中的矢量图形编程范式

背景

矢量图形(Vector Graphics)的历史可以追溯到数字字体的早期探索。Dr. Allen Vincent Hershey 开发了首批数字字体,这些字体由简单的直线段序列描述,并于 1967 年在报告《Calligraphy for Computers》中首次发表。

在 Decker 生态系统中,hershey 模块负责使用这些字体进行文本布局。Decker 的脚本语言 Lil 具有独特的设计,深受 APL(一种数组编程语言)的影响。本文旨在结合二维矢量图形来探讨 Lil 语言的特性,并扩展了交互式文档中讨论的相关示例。Lil 的设计哲学强调集体操作(collective operations),即对大型数据结构整体应用操作,而非逐个元素处理,这极大地简化了图形编程的复杂性。

核心内容

1. 基础数据结构:点、笔画与路径

在 Decker 的画布(Canvas)坐标系中,图形由以下层级结构定义:

  • 点(Point):描述画布上矩形坐标的 (x, y) 数值对。在 Lil 中,点表示为长度为 2 的列表: point:(3,-5)
  • 笔画(Stroke):代表一系列要在画布上绘制的线条,即点的序列。如果有 N 个点,笔画描述 N-1 条连接的线段。笔画也可以被称为“多段线”(polyline)。在 Lil 中,它是点的列表: stroke:((list 3,-5),(list 2,5),(list 0,3))
  • 路径(Path):笔画的列表,即点的列表的列表。路径可以表示构成单个形状(如字母“A”)的笔画集合,也可以表示整个单词或句子的字母序列。路径中的笔画长度可以不一致。

2. 文本渲染与集体操作

Lil 提供了 hershey.textpath[] 函数,它根据字体字形将字符串组装成复杂的路径: p:hershey.textpath[hf_futura "ABC"]

渲染这些路径到画布 c 时,可以使用显式的 each 循环,或者更简洁的 @ 操作符(Shorthand):

each stroke in p
  c.line[stroke]
end
c.line @ p

Decker 的 API 设计倾向于集体操作。例如,canvas.line[] 函数可以直接处理包含多个笔画的路径列表,而不是仅绘制单条线段。如果 API 仅支持单线段绘制,程序员必须手动管理 N 个点对应 N-1 条线段的边界情况(off-by-one error),这会引入不必要的复杂性并掩盖代码的真实意图。

3. 路径变换:缩放、平移与剪切

Lil 利用其算术运算符在嵌套列表结构上的隐式适配(conforming)行为,实现了简洁的路径变换。

  • 均匀缩放:标量乘法会自动递归应用到列表的每个元素。 ((list 3,-5),(list 2,5),(list 0,3))*2 结果为 ((6,-10),(4,10),(0,6))
  • 非均匀缩放:当需要分别对 x 和 y 坐标应用不同缩放因子时,直接使用 (1,2,3,4)*(10,100) 会导致右侧列表被截断或重复以匹配左侧长度。为了正确地将缩放因子“推送”到最内层的点对,需要使用 list 操作符进行封装:
    • 对路径进行非均匀缩放:p * list list scalex,scaley
    • 负数缩放可实现水平或垂直镜像:p * list list -1,1
  • 平移:使用加法运算符,原理与缩放相同: p + list list transx,transy
  • 剪切(Shear)
    • 水平剪切:需要提取路径中所有点的 y 坐标,将其乘以剪切因子后加回 x 坐标。Lil 提供了 last 原语来提取嵌套列表的最后一层元素,配合 @ 操作符可以深入嵌套结构。作者将这种从路径点派生的标量列表称为 peelp + (last @ @ p)*list list shearx,0
    • 垂直剪切:类似地,使用 first 提取 x 坐标: p + (first @ @ p)*list list 0,sheary
  • 转置与旋转
    • 转置可以通过 rev @ @ p 直接交换每个点的元素实现。
    • 90 度旋转可通过结合镜像和转置实现。

4. 基于极坐标的通用旋转

对于更通用的旋转需求,Lil 提供了 mag(模长)、heading(方位角)和 unit(单位化)原语:

  • 分解为极坐标mag 计算点到原点的欧几里得距离,heading 计算角度(弧度)。两者结合可将矩形坐标路径分解为极坐标: p ~ (mag p)*unit heading p
  • 旋转:在角度上加上旋转角 angle,再转换回笛卡尔坐标: (mag p)*unit angle+heading p

5. 高级特效与变形

  • 透视失真:如果路径以原点为中心,可以将每个点乘以其自身的 mag 值,产生透视效果。结合插值参数 t 和常数,可实现动态变形: p*.3+(mag p)*.01*t
  • 随机抖动:生成随机偏移量并添加到路径中,可创建基础的线条沸腾(line-boil)效果。

关键要点

  • Lil 的 APL 风格:Lil 语言深受 APL 影响,强调数组编程和隐式适配(conforming)。算术运算符能自动递归处理嵌套列表,这是实现高效矢量图形变换的核心机制。
  • 集体操作优于循环:Decker 的 API 设计鼓励对数据结构整体进行操作(如 c.line @ p),避免了手动管理索引和边界条件的繁琐,提高了代码的可读性和简洁性。
  • 路径的层级结构:图形由 路径(Path) -> 笔画(Stroke) -> 点(Point) 的嵌套列表构成。理解这一结构是掌握 Lil 图形编程的基础。
  • 变换的通用模式
    • 缩放和平移通过 list list 封装标量因子,利用 *+ 运算符实现。
    • 剪切和坐标提取利用 lastfirst@ 操作符深入嵌套结构提取特定坐标分量(Peel)。
    • 旋转利用 magheading 将笛卡尔坐标转换为极坐标,进行角度运算后再转换回来。
  • Hershey 字体的应用:通过 hershey.textpath[] 将文本转换为矢量路径,使得文本可以像其他图形一样进行缩放、旋转和变形处理。

意义与影响

这篇文章展示了 Decker 生态系统中 Lil 语言在图形编程领域的独特优势。通过借鉴 APL 的数组处理哲学,Lil 提供了一种声明式且高度抽象的方式来处理二维矢量图形。

  1. 简化图形算法实现:传统的图形编程往往需要大量的循环和索引操作来处理顶点变换。Lil 通过隐式适配和原语(如 mag, heading),将复杂的几何变换简化为几行代数表达式,显著降低了实现透视、旋转、剪切等效果的代码复杂度。
  2. 提升开发效率与代码可读性:集体操作的设计使得代码意图更加清晰。程序员无需关心底层数据结构的遍历细节,只需关注数学逻辑本身。这种范式特别适用于需要频繁进行几何变换的交互式图形应用。
  3. 历史与技术的结合:文章将 1960 年代的 Hershey 矢量字体与现代脚本语言结合,证明了经典
查看原文 →beyondloom.com