最近看到埃舍尔那些令人震撼又充满启发的作品。也许不可能楼梯是他最出名的画作之一,但他在密铺上也投入了大量精力。
所谓密铺,就是用重复形状覆盖一个表面。棋盘格是最常见的一种密铺:平面被交替颜色的正方形铺满。
![]()
埃舍尔的密铺之所以吸引人,在于他居然能用鸟、鱼、马或其他动物覆盖整个平面,让原本相当抽象的画面变得鲜活起来。
有意思的是,做出某种形式的密铺并不难,尽管真正找到能互相咬合的有意义形状,确实需要艺术头脑。那么,就用Haskell来重构埃舍尔的一幅作品。
从棋盘格开始理解密铺
先看最基础的密铺:棋盘格。怎样才能修改正方形,使结果仍然能拼合?显然,黑色方块左侧必须贴合白色方块右侧,顶部和底部同理。
所以,如果把左右两侧替换成同一条曲线,上下两侧也替换成同一条曲线,得到的瓦片就能与相邻瓦片很好地拼合,覆盖整个平面。
看下面的例子。一块瓦片的左右边被标成绿色,上下边被标成红色。只要这些线不相交,就能得到密铺。
而且,不必沿用正方形的四个角。只要红色曲线从绿色曲线结束的地方开始,就能闭合边界,得到合适的密铺瓦片。
把想法翻译成Haskell代码
用记录来指定密铺,因为记录便于在一个变量中存储多个不同类型的值。为了让代码更省事,启用 OverloadedRecordDot 扩展,这样就能写 x.field 而不是 field x。
生成PNG图片使用 Rasterific 后端,安装Haskell库 diagrams-rasterific 后即可使用。
代码开头需要隐藏 intersection 函数,避免后面出现命名冲突,并导入 Diagrams.Prelude 和限定的 Diagrams.Backend.Rasterific。
在 diagrams 库中有多种曲线数据类型。这里使用 Trail,因为它最方便处理,并且正好满足需求。
Haskell类型系统的一个好处是允许表达得非常精确。正方形的边会被替换成曲线,但不是任意曲线。需要的是开放曲线,也就是端点松散的曲线。
这一点用类型 Trail' Line V2 a 来表达,其中 V2 指定使用二维空间,a 是坐标类型参数。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.