曲线艺术编程 coding curves 第十一章 玫瑰花形( ROSES)
第十一章 玫瑰花形 ROSES
原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/
译者:池中物王二狗(sheldon)
曲线艺术编程系列第 11 章
这一篇我们将看到另一种我钟意的曲线类型 -- 玫瑰花形或玫形曲线。 在我看来这类曲线就像是圆形的利萨茹曲线 Lissajous curves,或者非常规则的谐波图。事实上它们是一类特别的长短幅内摆线,特别到足够自成一类。先给你看些例子,下面是玫形曲线:
(译都注:这是玫瑰花 roses 我是不信的...)
就像其它我们关注过的其它曲线一样,我们有参数方程, 用 t 从 0 到 2 * PI 递增,计算得到返回值并用它绘制出曲线。我们先往回看看圆的方程:
function circle(x, y, r) {
for (t = 0; t < 2 * PI; t += 0.01) {
x1 = x + cos(t) * r
y1 = y + sin(t) * r
lineTo(x1, y1)
}
}
你当然可以对 for 循环内的代码简化成一行,但为了解释的更清晰我还是将它展开了。
一个玫瑰花形曲线使用同样的策略,但使用以 t 和其它参数为基础的动态半径取代固定的半径。下面是半径方程:
r = a * cos(n * t)
现在我们有了 2 个新变量。a 是花的覆盖半径, n 控制着花瓣数(当然花瓣数量的计算有点儿复杂,捎后回来解释)。现在我们可以创建一个玫瑰花形函数:
function rose(x, y, a, n) {
for (t = 0; t < 2 * PI; t += 0.01) {
r = a * cos(n * t)
x1 = x + cos(t) * r
y1 = y + sin(t) * r
lineTo(x1, y1)
}
}
当然如果你愿意,可以整理一下代码:
function rose(x, y, a, n) {
for (t = 0; t < 2 * PI; t += 0.01) {
r = a * cos(n * t)
lineTo(x + cos(t) * r, y + sin(t) * r)
}
}
现阶段先假设函数的 n 参数为正整数。当然后面我们也会探索更大的范围。
现在我们就可以绘制我们第一个玫瑰花形:
width = 800
height = 800
canvas(800, 800)
rose(width / 2, height / 2, width * 0.45, 5)
stroke()
在这里我会经常用 width * 0.45。它刚好比 canvas 宽度的一半小一点儿,这让曲线几乎刚好覆盖整个 canvas 但又没能碰到边界。
上面代码会生成一个 5 个花瓣的玫瑰花形:
第一个例子中 n 用的是 7。 而下面这个花 n 用的是 11:
到目前为止我们可以看到 n 与 花瓣数量之间有很好的相关性。前面 n 用的都是奇数。如果我们用的是偶数 n 为 4 会如何呢?
有意思,产生了 8 个花瓣。n 的值遵循 奇数 n 创建 n 个花瓣。 偶数 n 创建 2 * n 个花瓣。把 n 再设大一点看看,设 n = 40,它将产生 80 个花瓣。我不得不提升辨率 -- 将循环上的 t 改为 0.001 以保证花瓣没有锯齿。
相反,如果 n = 1,仅会得到一个单圆
有点怪,但在数学上说得通。你会发现 n 为负值时,玫瑰花看起来与 n 为正值时得到的结果图形一样。 下面左边的是 5 右边的是 -5
毫无意外, n = 0 时会得不到任何图形。并且我们已经覆盖了全部的整数玫瑰花形。如果这是全部了的话就太好了。可惜还有更多的类型。
(译者注:如果你用 javascript 代码实现测试,你会发现 n = 0 时你会画出一个大圆,n = 1 时是右侧一个小圆,
可用源码 https://github.com/willian12345/coding-curves/tree/main/examples/ch11/rose-3.html 测试)
交替玫瑰花形
在我讲完所有 n 值前, 我想先提一下交替玫瑰方程。在半径方程中用正弦 sine 代替余弦 cosine :
r = a * sin(n * t)
它会生成与原来一样的花形,但是旋转了。左侧是用余弦 5 个花瓣的原图, 右侧是用正弦:
8 个花瓣的也是一样的效果 (n = 4)
真实的旋转值是 PI / (2 * n) 弧度, 或 90 / n 度。n 为奇数时,视觉上看起来总是旋转了 90 度 (实际旋转可能不同,但由于旋转对称性,它看起来就像是旋转了90度)。当 n 为偶数时花瓣就会在原版本花瓣之间。
当 n 为分数
当我们给 n 的值是分数时事情就变的更有趣了。我们可以试着用分数生成玫瑰花 :
rose(width/2, height/2, width * 0.45, 5.0 / 4.0)
stroke()
但是结果越令人相当失望
问题在于它需要超过 2 * PI 以获取整圆。那么要超过多少呢? 好吧,我们用编程的方式来解决,我们首先需要确保 n 为有理数。如果它是无理数, 这朵玫瑰花就会无法闭合回起点(译者注:绕多少圈圆也无法与起始点相接)。我们也需要知道分数的分子与分母。我们可以通过调整玫瑰花形生成函数,为其额外添加一个参数,我们将 n 作为分子,d 作为分母。
rose(x, y, a, n, d) {
for (t = 0; t < 2 * PI; t += 0.01) {
r = a * cos(n / d * t)
lineTo(x + cos(t) * r, y + sin(t) * r)
}
}
调整后的函数暂时还没有解决问题,但这是解决问题的第一步。你可以强制 n 和 d 为整数,以确保得到有理数分数(译者注:数学上,有理数是一个整数 a 和一个 正整数 b 的比), 但确保相除前转换它们以保证它返回值为浮点数。
现在需要调整 for 循环 2*PI 的结束条件为我们需要的值。 这个范围值是:
limit = PI * d * m
但是这个新的 m 变量是什么?如果 d * n 结果为奇数则 m 应该为 1。如果 d * n 结果为偶数则 m 应该为 2。 Woo! 有点儿复杂了.. 别担心我们可以简化它。
我们通常将数对2取余来判断数的奇偶性。如果结果为 0 ,则这个数是偶数,如果结果为 1 则原值为奇数。 我们想:
m = 1 when d * n % 2 == 1
并且
m = 2 when d * n % 2 == 0
所以我们可以这样设置:
(译者注: d * n % 2 == 1 奇数 m = 1; d * n % 2 == 0 偶数 m = 2; )
m = 2 - d * n % 2
下面是修改后的函数:
rose(x, y, a, n, d) {
m = 2 - d * n % 2
limit = PI * d * m
for (t = 0; t < limit; t += 0.01) {
r = a * cos(n / d * t)
lineTo(x + cos(t) * r, y + sin(t) * r)
}
}
记住,如果 n 和 d 强制为整数, 为了保证运行正常你可能需要对它们强制转换。我将这部分工作留给你们自己完成 。现在我们可以重新调用:
rose(width/2, height/2, width * 0.45, 5, 4)
stroke()
现在得到的结果就好多了:
(译者注:可用源码 https://github.com/willian12345/coding-curves/tree/main/examples/ch11/rose-6.html 测试)
这回这玫瑰花算是完整了。
现在你可以用不同的分数来测试下效果了。我发现如果分子分母值大但相互很接近会生成非常有趣的数。举个例子, n = 22, d = 21:
甚至是 81 和 80:
当分数小于 1.0 时图形变得完全不同但依然很有趣。举个例子,下图左侧图形 n 和 d 分别为 1,2,中间图形是 1,3, 右侧图形 1,4。
有个找到有趣图案的小技巧是可以对的这对值可以化简,比如 17 / 51 可以化简成 1 / 3, 结果图形就是上图的中间那一幅。当改动一点点其中的某个值时,比如变成 17 / 52:
变动了 1 但造成的结果差异非常大。
有些特别的玫瑰曲线拥有自己的名称。我分享几个给你们。
蚶线三分角 (Limaçon Trisectrix)
n / d 是 1 / 3, 我们在前面已经见过了。
杜勒叶形线 (Dürer Folium)
n / d 是 1 / 2, 之前也见过了
四叶玫瑰线(Quadrifolium)
比例是 2 / 1
三叶玫瑰线(Quadrifolium)
比例是 3 / 1
毛雷尔玫瑰(Maurer Roses)
你以为我们这就结束了?错!还有其它类型的玫瑰形曲线等我们探索呢 -- 毛雷尔玫瑰(Maurer Roses)!
毛雷尔玫瑰 建立在 rose 函数基础上, 与一直绘制曲线不同的是,它绘制的是一系列线段将延玫瑰曲线上的点连接起来。通常使用 360 个特殊角度的线段绘制,虽然这不是必须的。 我们先用比例为 4 / 1 建个玫瑰图, 然后挑一个角度值用于循环内。此例中我使用的是 49。然后我们使用 t 循环 0 到 360度,将 t 乘以这个角度值。 所以 t 会从 0 递增, 到 49, 98, 147, 196 继续往后。我们在我们的玫瑰曲线的下一个点上应用(当然角度要转变成弧度)。下面这个 gif 图 表现的是前 30 次循环时的画面。
换种说法,在普通的玫瑰曲线中,递增间隔非常非常小,所以我们可以得到一个非常平滑的曲线。这里我们递增间隔巨大,这会让图看起来乱糟糟的。但是,我们等它完成 360 次迭代路径绘制完成,将会得到:
Aha! 全部完成后线条竟然不乱了!事实上,看起来还挺不错。上面我在毛雷尔玫瑰上还绘制了标准玫瑰图。但下面这个才是毛雷尔玫瑰的全貌:
我觉得两个图组合起来还挺好看。
所以具体怎么做呢?
我们还是得从基础的 rose 函数开始。 但这次,我们使用单个整数值。所以只有参数 n 没有参数 d 了。我们还要指定每次迭代角度的递增值。为了避免与之前 d 参数混淆,将参数名改成 deg 。这个函数签名是:
function maurer(x, y, a, n, deg)
还有,我们希望将 t 从 0 循环至 360。然后将 t 乘以 deg 。这就是角度了就像之前动画时那样。我们将它设为变量 k ,但在此之前还需要乘以 PI 再除以 180 转换成弧度
function maurer(x, y, a, n, deg) {
for (t = 0; t < 360; t++) {
k = t * deg * PI / 180
r = a * cos(n * k)
lineTo(x + cos(k) * r, y + sin(k) * r)
}
}
我们用 k 代替之前的 t 后执行 rose 算法。
现在我们可以像下面这样调用函数了:
width = 800
height = 800
canvas(800, 800)
maurer(width / 2, height / 2, width * 0.45, 5, 37)
stroke()
// drawing the regular rose is optional
// 标准玫瑰图可画可不画
rose(width / 2, height / 2, width * 0.45, 5, 1)
stroke()
得到:
对 n 和 deg 用不同的值试试。你会发现 n 的作用与前面标准玫瑰图形一样。但是变量 deg 即便是微小的变化都能让图形完全不同。 例如,在这个图形中,n 为 7 且 deg 为 23:
但将 deg 变为 24 时会得到下面这样的结果:
显然没那么好看了。
一般来说,变量 deg 使用奇数会比偶数时要好(当然也有例外)。
任何能被 360 整除的整数都不太行。举个例子, 4, 120:
我还是绘制了完整的图,但毛雷尔图形却仅仅绘制成了右边一个三角形。 于是将值增大到 121, 这回可就好多了:
而且,较小的质数通常都都能得到较漂亮的图形。 我注意到如果 n 值够小那么 deg 质数大一点儿最后的效果也还行。 我还没有完整验证过。我要多尝试测试下。
还有一项你可能想尝试的东西,就是给毛雷尔玫瑰用分数值。你完全不用改动函数。 你就直接传一个分数值给 maurer 函数。 因为我们总是从0 循环至 360, 我们不需要调整循环范围。下面是个传分数值的例子,注意在给 rose 函数传递时两个参数还是分开传的。
maurer(0, 0, width * 0.45, 5.0 / 4.0, 229)
stroke()
rose(0, 0, width * 0.45, 5, 4)
stroke()
看看你能在所有可能的图形变化中找到什么。
本章 Javascript 源码 https://github.com/willian12345/coding-curves/blob/main/examples/ch11
博客园: http://cnblogs.com/willian/
github: https://github.com/willian12345/
曲线艺术编程 coding curves 第十一章 玫瑰花形( ROSES)的更多相关文章
- 《Linux命令行与shell脚本编程大全》第二十一章 sed进阶
本章介绍一些sed编辑器提供的高级特性. 21.1 多行命令 按照之前的知识,所有的sed编辑器命令都是针对单行数据执行操作的. 在sed编辑器读取数据流时,它会基于换行符的位置将数据分成行,一次处理 ...
- Python 编程快速上手 第十一章 Web scrapping
前言 这一章讲了如何在 Web 上抓取相关的信息,工具是三个模块: webbrowser 模块:用于打开浏览器指定页面 requests 模块:用于下载文件 Beautiful Soup 模块:用于解 ...
- 《Java并发编程实战》第十一章 性能与可伸缩性 读书笔记
造成开销的操作包含: 1. 线程之间的协调(比如:锁.触发信号以及内存同步等) 2. 添加�的上下文切换 3. 线程的创建和销毁 4. 线程的调度 一.对性能的思考 1 性能与可伸缩性 执行速度涉及下 ...
- 《Linux命令行与shell脚本编程大全》第十一章 构建基本脚本
11.1使用多个命令 $date;who // 命令列表,加入分号就可以,这样会依次执行.参见5.2.1节 注意区分$(date;who),这个是进程列表,会生成一个子shell来执行 Shel ...
- java并发编程实战:第十一章----性能和可伸缩性
线程的最主要目的是提高程序的运行性能,但性能的提升会导致复杂性的提升,又会导致安全性和活跃性的风险 一.对性能的思考 提升性能意味着用更少的资源做更多地事情.要想通过并发来获得更好的性能,就要更有效地 ...
- JavaScript DOM编程艺术-学习笔记(第十章、第十一章)
第十章 1.动画中,因为js的效率高,所以看不见过渡效果 2.题外话:①国外人写书,总是先感谢一遍亲朋好友,最后感谢自己的家人. 3."除非允许用户'冻结'移动的内容,否则应该避免让内容在页 ...
- [CSAPP笔记][第十一章网络编程]
第十一章 网络编程 我们需要理解基本的客户端-服务端编程模型,以及如何编写使用因特网提供的服务的客户端-服务端程序. 最后,我们将把所有这些概念结合起来,开发一个小的但功能齐全的Web服务器,能够为真 ...
- CSAPP:第十一章 网络编程
CSAPP:第十一章 网络编程 11.1 客户端服务器模型11.2 全球IP因特网11.3 套接字接口 11.1 客户端服务器模型 每个网络应用都是基于客户端-服务器模型.采用这个模型,一个应用是 ...
- 第十一章:Python高级编程-协程和异步IO
第十一章:Python高级编程-协程和异步IO Python3高级核心技术97讲 笔记 目录 第十一章:Python高级编程-协程和异步IO 11.1 并发.并行.同步.异步.阻塞.非阻塞 11.2 ...
- 《汇编语言 基于x86处理器》第十一章 MS-DOS 编程部分的代码 part 2
▶ 书中第十一章的程序,主要讲了 Windows 接口,在小黑框中进行程序交互 ● 在屏幕指定位置输出带自定义属性的文字 INCLUDE Irvine32.inc .data outHandle HA ...
随机推荐
- 企业信息化-3.6 IT资源管理2-系统及应用
笔者从业的主要是App Dev&Ops,对操作系统有些了解,对应用软件了解的更多.以下是总结了以前跟Host&Server Service.Cloud Service.IT Solut ...
- go微服务框架kratos学习笔记二(kratos demo 结构)
目录 api cmd configs dao di model server service 上篇文章go微服务框架kratos学习笔记一(kratos demo)跑了kratos demo 本章来看 ...
- VMware Workstation Pro许可证
永久许可证:ZC10K-8EF57-084QZ-VXYXE-ZF2XF 备用许可证: UF71K-2TW5J-M88QZ-8WMNT-WKUY4 AZ7MK-44Y1J-H819Z-WMYNC-N7A ...
- PyTorch实践模型训练(Torchvision)
模型训练的开发过程可以看作是一套完整的生产流程,这些环节包括: 数据读取.网络设计.优化方法与损失函数的选择以及一些辅助的工具等,TorchVision是一个和PyTorch配合使用的Python包, ...
- Flask 上下文是什么 ?
哈喽大家好,我是咸鱼.今天我们来聊聊什么是 Flask 上下文 咸鱼在刚接触到这个概念的时候脑子里蹦出的第一个词是 CPU 上下文 今天咸鱼希望通过这篇文章,让大家能够对 Flask 上下文设计的 ...
- ROS2的安装与使用(超详细图文教程)
ROS2的安装与使用(超详细图文教程) 如果前面的虚拟机以及Ubuntu22.04镜像都安装好了,根据目录直接跳到ROS2的安装. 资料参考于:古月居 VMware虚拟机的安装 安装地址: 对于不了解 ...
- linux syslog.d日记操作记录-小节
以下记录在学习LDD3时调试处理打印的一些操作 syslog 不同的发行版,不同的脚本文件,如fedora18中为rsyslog的名称 1:配置文件 /etc/syslog.conf(fedora r ...
- 一文掌握ArrayList和LinkedList源码解读
大家好,我是Leo! 今天来看一下ArrayList和LinkedList的源码,主要是看一下常用的方法,包括像add.get.remove方法,大部分都是从源码直接解读的,相信大家读完都会有一定收获 ...
- 沁恒 CH32V208(三): CH32V208 Ubuntu22.04 Makefile VSCode环境配置
目录 沁恒 CH32V208(一): CH32V208WBU6 评估板上手报告和Win10环境配置 沁恒 CH32V208(二): CH32V208的储存结构, 启动模式和时钟 沁恒 CH32V208 ...
- DFS(深度优先搜索) 总是需要重置 visited 的状态吗?
问题来自 P1902 刺杀大使,在最初的实现中 DFS 中一段代码如下: visited[x2][y2] = true; flag = dfs(v, x2, y2); visited[x2][y2] ...