第十二章 玑镂(扭索)纹

原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/

译者:池中物王二狗(sheldon)

源码:github: https://github.com/willian12345/coding-curves

曲线艺术编程系列 第 12 章

玑镂纹是一种错综复杂且非常迷人的图案。它经常被绘制在银行钞票和官方文档上,你也可以在手表表盘或其它复杂机械上找到它们的身影。正因为它们如此复杂难以理解,所以它们经常被镌刻在金属表面,这通常是由机器来完成。想象一下一台高端螺旋仪用金属刻蚀工具代替原本用于绘图的圆珠笔。“玑镂纹” 这词相当模糊,它可以代称所有这一类纹理。我将要讲解的是如下图案中的玑镂纹理:

这个纹理你可能已经在某种证书或钞票上见过了,一旦你知道生成原理,你就可以以调整代码的方式生成另外相似的纹理图案。

第一步:创建一个单独的圆

我们先从创建单个简单的圆开始,它看起来应该像下面这样:

它非常像一个摆线或玫瑰曲线。事实上你可以通过它们的公式创建它,但我将改动一点点,以便后面处理更复杂的纹理图案。

本质上是一个正弦波包住一个圆。注意它有一个内半径和一个外半径。 这个正弦波有 80 个 nodes,它自身波纹会重叠。为了更好的解析,我将增大内半径让它不再重叠:

现在可以看的更清楚了。正如我所说的那样,一个正弦波包住一个圆。现在我要再加一点点重叠纹理:

图中虽然有重叠部分你仍然可以观察到正弦波。第一张图像用的是同样的原理,只是内半径更小且重叠部分更多。现在我让我们看看是如何绘制的。

我们先设内半径 inner 与外半径 outer 开始。通过简单的数学运算得到 mid 半径。它将是正弦波的零点。我们还需要一个范围,它决定了正弦波在内外半径扩展的范围。你也可以称它为这个正弦波的振幅。

width = 600
height = 600
canvas(width, height) translate(width / 2, height / 2) inner = 50
outer = 250
range = (outer - inner) * 0.5
mid = inner + range

下一步,我们需要知道要多少个波形周期,还有多少次重叠。我将把它们称作 nodes 和 div, 它们应该是整数且两个数不能被整除。 这非常像上一章玫瑰曲线中遇到的 n 和 d 参数, 但为了不混淆我会用另外的变量名。

nodes = 80
div = 11

现在我们可以循环 t 到 2 * PI * div 并绘制一些线段。 下一个线段坐标点角度简单的用 t 和 半径 radius 计算得出。我们需要将循环结束条件 2 * PI 再乘以 div 以确保足够绕一圈,首尾可以相连。

for (t = 0; t < 2 * PI * div; t += 0.01) {
radius = mid + sin(t * nodes / div) * range
x = cos(t) * radius
y = sin(t) * radius
lineTo(x, y)
}
stroke()

如果你回头看玫瑰曲线那一章, 你会发现它们都是用相似的方法计算半径值的, 但相比使用正弦和单一的乘法,我们使用 mid 和 range 调整半径至内半径与外半径之间。

你可以多玩玩。试试改动不同的参数。为了生成一个比较好看的玑镂纹,你可能需要将 nodes 设高一点,div 低一点。最重要的是它们的值不能被整除。想要得到比较得体的图案,一个比较简单的方法是让 div 是一个小的质数, nodes 不能是 div 的倍数。

举个例子,如果 div 是 17,你不应该将 nodes 设为 170,否则你将会得到如下图:

但将 nodes 增加到 171 则会得到很好的图案:

如果你想玩的话,我几年前做了一个可交互的版本:

https://bit101.github.io/lab/dailies/170120.html

接下来,我们在混合当中再添加一些复杂性。相比在固定的内外半径来回绘制正弦波,我们在内外半径各自添加正弦波变化!结果如下图:

为此我们需要更多的参数。为了计算出最终外半径上任意一点,我们需要基于外半径,计算有多少正弦波周期和最终外半径与基础外半径的距离差。那么内半径同样需要3个值,nodes 与 div 也依然要。上图中我使用的参数值如下:

inner = 100.0
n0 = 7.0
h0 = 10.0 outer = 250.0
n1 = 17.0
h1 = 20.0 nodes = 142.0
div = 89.0

内半径有 7 nodes, 半径范围在 90 - 110 之间,它是基于100 加减 10。然后,同样,外半径有 17 个 nodes ,半径范围在 230 - 270 之间。

在 for 循环迭代内之前计算 "mid radius" 的相关代码用这些值重写。

for (t = 0; t < 2 * PI * div; t += 0.01) {
r0 = inner + sin(t * n0) * h0
r1 = outer + sin(t * n1) * h1 range = (r1 - r0) * 0.5
mid = r0 + range radius = mid + sin(t * nodes / div) * range
x = cos(t) * radius
y = sin(t) * radius
lineTo(x, y)
}
stroke()

当然你可以对以上代码进行简化,但为了清晰起见我把每步都写出来了。

你可以继续改动这些参数,看看能创造出什么样的图形。我们还有更复杂的一级要处理。

https://bit101.github.io/lab/dailies/170121.html

第三步 多环

下一步是创造多环,这些环相互配合在一起, 就像章节开头的那张图:

它的实现比看起来人简单的多。如果你想绘制小环外围绕大环,你仅需要将小环的外半径作为大环的内半径。由于我们会多次调用相同的代码,最好把它变成可复用的函数。基本上就是把 for 循环和 stroke 调用 放入一个可传所有参数函数中。我还添加了 x, y 参数, 这样你就可以把图绘制在 canvas 的任意位置了。

function guilloche(x, y, ir, n0, h0, or, n1, h1, nodes, div) {
for (t = 0; t < 2 * PI * div; t += 0.01) {
r0 = ir + sin(t * n0) * h0
r1 = or + sin(t * n1) * h1 range = (r1 - r0) * 0.5
mid = r0 + range radius = mid + sin(t * nodes / div) * range
lineTo(x +cos(t) * radius, y +sin(t) * radius)
}
stroke()
}

我把 canvas 宽高调整为 800x800 并且像下面这样调用代码:

guillloche(400, 400, 50, 6, 10, 120, 12, 20, 137, 37)
guillloche(400, 400, 120, 12, 20, 220, 18, 30, 141, 41)
guillloche(400, 400, 220, 18, 30, 350, 24, 20, 164, 53)

This resulted in the following image:

结果如图:

注意第一行调用,外半径参数是 120, 12,和 20。 这些作为下一个调用的内半径参数。第二个调用的外半径参数 220, 18 和 30, 作为最后一个调用的内半径参数。像这样做后,绘制出的环简直完美。

像往常一样你应该多调调参数,多试试。你可以想加多少环就加多少环。你也许希望把所有半径参数封装成自定义类型结构体让这部分变的更有复用性。我将把这部分工作留给你。

另外你可能想给各个一觉不同的颜色:

(译者注:我添加了随机颜色源码在 https://github.com/willian12345/coding-curves/tree/main/examples/ch12/guilloche-colorful.html)

这里有一个可交互的最终版本:

https://bit101.github.io/lab/dailies/170122.html

注意,这里可没有说环与环之间必须要紧密相扣。你可以试着给它们之间留点距离或者让它们重叠一部分看看你能创造出多有趣的图案。下一个例子中,第一个与最后一个环与之前例子中设置的一样, 但中间这个环的参数定义的相当不同:

guillloche(400, 400, 20, 4, 5, 120, 8, 10, 137, 37)
guillloche(400, 400, 160, 5, 24, 160, 11, 24, 80, 17)
guillloche(400, 400, 220, 18, 16, 350, 10, 20, 164, 53)

此处,内外半径值相等,都是 160,生成比简单的离散环更有趣的团状环。

还有,别忘了把它做成动画!

总结

你用 Guilloche patterns 关键词搜索图片,会得到非常多不同样式的图,大部分比我这里讲的要复杂的多。我只是从中挑了一个来讲。

也许你想尝试修改代码绘制其它形状的玑镂纹,也许从椭圆形玑镂是个不错的开始。然后再开始绘制其它形状。但这些内容都超出本章范围了。

本章 Javascript 源码 https://github.com/willian12345/coding-curves/blob/main/examples/ch12


博客园: http://cnblogs.com/willian/

github: https://github.com/willian12345/

曲线艺术编程 coding curves 第十二章 玑镂(扭索)纹的更多相关文章

  1. JavaScript DOM编程艺术-学习笔记(第十二章)

    第十二章 1.本章是综合前面章节的所有东西的,一个综合实例 2.流程:①项目简介:a.获取原始资料(包括文本.图片.音视频等) b.站点结构(文件目录结构) c.页面(文件)结构 ②设计(切图) ③c ...

  2. 《Linux命令行与shell脚本编程大全》 第二十二章 学习笔记

    第二十二章:使用其他shell 什么是dash shell Debian的dash shell是ash shell的直系后代,ash shell是Unix系统上原来地Bourne shell的简化版本 ...

  3. 《Linux命令行与shell脚本编程大全》第二十二章 gawk进阶

    gawk是一门功能丰富的编程语言,你可以通过它所提供的各种特性来编写好几程序处理数据. 22.1 使用变量 gawk编程语言支持两种不同类型的变量: 内建变量和自定义变量 22.1.1 内建变量 ga ...

  4. java并发编程实战:第十二章---并发程序的测试

    并发程序中潜在错误的发生并不具有确定性,而是随机的. 安全性测试:通常会采用测试不变性条件的形式,即判断某个类的行为是否与其规范保持一致 活跃性测试:进展测试和无进展测试两方面,这些都是很难量化的(性 ...

  5. 《Java并发编程实战》第十二章 测试并发程序 读书笔记

    并发测试分为两类:安全性测试(无论错误的行为不会发生)而活性测试(会发生). 安全測试 - 通常採用測试不变性条件的形式,即推断某个类的行为是否与其它规范保持一致. 活跃性測试 - 包含进展測试和无进 ...

  6. 《Linux命令行与shell脚本编程大全》第十二章 使用结构化命令

    许多程序要就对shell脚本中的命令施加一些逻辑控制流程. 结构化命令允许你改变程序执行的顺序.不一定是依次进行的 12.1 使用if-then语句 如下格式: if command then     ...

  7. [CSAPP笔记][第十二章并发编程]

    第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟 ...

  8. 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条

    http://blog.csdn.net/terryzero/article/details/3797782 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条 标签: swing编程 ...

  9. CSAPP:第十二章 并发编程

    CSAPP:第十二章 并发编程 12.1 线程执行模型12.2 多线程之间并发通信12.3 其他并发问题   使用应用级并发的应用程序称为并发程序.现代操作系统提供三种基本的构造并发程序的方法: 进程 ...

  10. 《OpenCL异构并行编程实战》补充笔记散点,第五至十二章

    ▶ 第五章,OpenCL 的并发与执行模型 ● 内存对象与上下文相关而不是与设备相关.设备在不同设备之间的移动如下,如果 kernel 在第二个设备上运行,那么在第一个设备上产生的任何数据结果在第二个 ...

随机推荐

  1. 多精度 simulator 中的 RL:一篇 14 年 ICRA 的古早论文

    目录 全文快读 0 abstract 1 intro 2 related work 3 背景 & 假设 3.1 RL & KWIK(know what it knows)的背景 3.2 ...

  2. Flutter 异步编程指南

    作者:京东物流 王志明 1 Dart 中的事件循环模型 在 App 开发中,经常会遇到处理异步任务的场景,如网络请求.读写文件等.Android.iOS 使用的是多线程,而在 Flutter 中为单线 ...

  3. 一文带你弄懂 Maven 拉包原理

    业务需求开发的时候,我们总是会遇到拉不到依赖包的情况.此时如果不清楚 Maven 拉取依赖包的原理,那么很可能找不到问题所在.今天树哥就带大家了解下 Maven 拉包的原理,让你在遇到问题的时候能快速 ...

  4. 存储系统模拟—R实现

    存储系统 存储问题是人们最熟悉又最需要研究的问题之一.例如企业储存的原材料.在制品等,存储太少,不足以满足生产的需要,将使生产过程中断; 存储太多,超过了生产的需要,将造成资金及资源的积压浪费.商店储 ...

  5. [Excel/Word]常用函数与技巧

    1 Excel case1 同时多列筛选 同时筛选多列: 选中首行(属性行)>筛选>(筛选目标的N列) case2 IF/OR/AND/COUNTIF语句 =IF(condition,co ...

  6. HTML、 input;、accept 属性-规定能够通过文件上传进行提交的文件类型

    定义和用法 文章地址: http://www.w3school.com.cn/tags/att_input_accept.asp accept 属性规定了可通过文件上传提交的服务器接受的文件类型. 注 ...

  7. 解决IDEA创建项目时无法引入依赖问题:Cannot resolve **.**.**(已解决)

    今天在创建SpringBoot整合MyBatis项目时出现报错: Cannot resolve org.springframework:spring-tx:5.3.26 Cannot resolve ...

  8. YOLO2论文中文版

    文章目录 YOLO9000中文版 摘要 1. 引言 2. 更好 3. 更快 4. 更强 5. 结论 参考文献 YOLO9000中文版 摘要 我们引入了一个先进的实时目标检测系统YOLO9000,可以检 ...

  9. Python-pytest-repeat的简单使用

    前言: 一.简介 pytest-repeat是pytest的插件,重复执行单个用例,或多个测试用例,并指定重复次数. 二.安装 1.执行如下命令 pip3 install pytest-repeat ...

  10. Win Airtest + 夜神模拟器 实现APP自动化

    前言: Airtest 是一个跨平台的UI自动化测试框架,适用于游戏和App.目前支持Windows.Android平台和 iOS 平台. 一.下载Airtest 下载地址:https://airte ...