曲线艺术编程 coding curves 第九章 旋轮曲线(ROULETTE CURVES)
第九章 旋轮曲线(ROULETTE CURVES)
原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/
译者:池中物王二狗(sheldon)
曲线艺术编程系列第 9 章
一开始我本章标题我打算使用“次摆线与摆线(旋轮线)”。我想它们是两种不同类型的曲线,虽然有点儿联系。但随着了解的深入,我有点儿凌乱了。事实上摆线是一类非常特别的次摆线。我会原谅我自己的。这是维基百科上它们的定义:
在几何上,次摆线(trochoid 原自古希腊语 trochos ‘wheel’)是一个圆延一条直线滚动一圈形成的旋转曲线。
在几何上, 摆线追踪在圆上的一个点,圆延直线滚动后形成的曲线。
它们不仅不是两个不同的东西,从描述上看它们几乎是同一个东西。细节决定成败。让我们开始探索吧。
有三种不同的次摆线:
- 普通次摆线(也称摆线)
- Prolate trochoids 长幅摆线
- Curtate trochoids 短幅摆线
除此之外,还有一些相关的曲线,大概覆盖以下几种:
Epitrochoids 长短辐外摆线
Hypotrochoids 长短辐内摆线
Epicycloids 外摆线
Hypocycloids 内摆线
Involutes 渐伸线
它们组合在一起就是旋轮曲线家族了。在这章除 Involutes 渐伸线外其它都将涉及到。
讲了了大堆有的没的,先从次摆线开始,让我们一一把它们搞清楚。
Trochoids 次摆线
就像上面描述的,一条次摆线就是一个圆在直线上滚动形成的旋转曲线。在编码之前先把它可视化看看:

如果我们追踪圆周上的那个黑点...

... 新的曲线就是次摆线。事实上由于是绘制点依赖于圆周,所以也称为普通摆线或圆滚线。
如果将圆周上的点延伸超过圆周,那么我们就得到长幅次摆线(Prolate trochoid)。

如果黑点在圆内部,它就是短幅次摆线(Curtate trochoids)。

为了做出这些动画,我让圆从左向右运动,计算出每个位置上圆的旋转角度,用 sine 和 cosine 基于圆的位置与旋转角度计算出点的位置,然后用这些点画出线。但对于次摆线其实有更直接的公式
x = a * t - b * sin(t)
y = a - b * cos(t)
t 是圆的旋转角度,它可以无限增长, a 是圆的半径, b 是圆心点至绘制点的距离 - 可以说是那个点的半径。让我们把它实现出来:
width = 800
height = 300
canvas(width, height)
translate(0, height/2)
scale(1, -1)
moveTo(0, 0)
lineTo(width, 0)
stroke()
a = 20.0
b = 20.0
res = 0.05
for (t = 0.0; t < width; t += res) {
x = a * t - b * sin(t)
y = a - b * cos(t)
lineTo(x, y)
}
stroke()
公式是基于笛卡尔坐标的,我们要将 y 轴移至 canvas 中心,并将 y 轴翻转。
然后画一条直线从 y 轴中心穿过用于表示圆滚动时的“地面”。你可以画也可以选择不画。
我们将 a 和 b 都设为 20, 这样就可以得到摆线(译者者:普通旋转线)了。我们循环将从 0 至 canvas 的宽度用于变量 t, 应用公式连接至计算出的结果点。

如果将 b 提高到 60, 我们就得到了长幅摆线

如果将 b 减小到 10, 我们就得到了短幅摆线。

我不知道长幅摆线和短幅摆线的用词是否准确,但听起来挺酷。
这就是全部关于次摆线的内容。试试给 a 和 b 设不同的值,或者你自己改一下其它值,我就不再再多讲了。因为下面还留有很多旋轮曲线需要探讨。
中心次摆线
接下来我们要聚焦于被称为中心次摆线的四种曲线。与延直线滚动不同,它是延另一个圆滚动,可能是延那个圆圆内部滚动也可能是延那个圆的外部滚动。
四类分别是:
- Epicycloids 外摆线 - 延一个圆外部滚动的圆,圆周上某一点轨迹形成的曲线。
- Epitrochoids 长短辐外摆线 - 与上面一个一样,但这个点是不在圆周上,而是在圆外或圆内,与短幅次摆线与长幅次摆线一样。
- Hypocycloids 内摆线 - 延一个圆内部滚动的圆,圆周上某一点轨迹形成的曲线。
- Hypotrochoids 长短辐内摆线 - 与上面那个一样,但这个点是不在圆周上,而是在圆外或圆内
除此之外,还有一些曲线也是属于上面曲线的一种变体,只是形成曲线的两个圆半径有特殊的比例关系。我们稍后会看介绍一些。
Epitrochoids 长短辐外摆线
首先,让我们将一个圆延另一个圆外滚动可视化

再追踪一下那个黑色的点的绘制结果

这就是你长短辐外摆线!
事实上,由于那个点是在圆周位置上,所以这也就是外摆线!
如果将点往外移动一点...

再我们往内移动一点。

再一次,我创建这些动画是通过计算这些圆的位置,绘制这些圆,再计算这些圆的旋转角度并绘制对应的点, 然后再连接每一帧所绘制的点路径形成曲线。为了展示动画像这么做是值得的,但是其实是有更简单的公式,你可以直接应用:
长短辐外摆线公式:
x = (r0 + r1) * cos(t) - d * cos(((r0 + r1) * t) / r1)
y = (r0 + r1) * sin(t) - d * sin(((r0 + r1) * t) / r1)
参数解析:
- r0 是定圆的半径
- r1 是动圆的半径
- t 是增长的弧度
- d 是动圆中心点与绘制点之间的距离 (如果是外摆线则 r1 与 d 相同)
我们用这个公式瞬间就可以用些代码实现:
width = 800
height = 800
canvas(width, height)
translate(width/2, height/2)
r0 = 180
r1 = 60
d = 60
res = 0.01
circle(0, 0, r0)
stroke()
for (t = 0.0; t < PI * 2; t += res) {
x = (r0 + r1) * cos(t) - d * cos(((r0 + r1) * t) / r1)
y = (r0 + r1) * sin(t) - d * sin(((r0 + r1) * t) / r1)
lineTo(x, y)
}
stroke()
我们设置变量 r0, r1, d 然后绘制定圆仅仅是为了引用变量
接着循环 t 至 2 * PI, 得到 x, y 点,绘制一条线至那个点。
得到结果:

值得注意的一点, ro 到 r1 的比例。 180:60 或 3:1。 我们得到3个结点。如果将 r1 改为 45,比例即变为 4:1 , 我们将得到 4 个结点。(我将 d 变为 45 也是为了适配 r1)

下面是 12:1 :

如果比例的第二个数字不是1,会如何?让我们将 r0 设置为 150,r1 为 100。现在,ratio 为 3:2。

毫无意外,我们得到了一个半的结点(nodes)。为了完成这个曲线,我们不得不再绕一圈,需要将 t 的范围值变为 0 到 PI * 4。然后得到结果:

如果你使用整数,循环总是会完成的。在下一个例子中,比如是 11:7 ,所以不得不将 t 结束值调整为 PI * 14:

如果是 111:70, 意味着我需要 t 结束范围调整至 140 * PI:
手动算这个有点痛苦,你可以创建个函数简化比例计算并用分母 * PI * 2 作为循环次数。 下面有两个有用的函数
//(译者注: gcd 即 greatest common divisor 求最大公约数)
function gcd(x, y) {
result min(x, y)
while (result > 0) {
if (x % result == 0 && y % result == 0) {
break
}
result--
}
return result
}
function simplify(x, y) {
g = gcd(x, y)
return x / g, y / g
}
仅需要将 r0 和 r1 传入 simplify 函数,得到简化后的比例。将结果的第二个数字 * 2 * PI 做为循环的结束条件。下面是例子...
(译得注:简化比例式,最后将分母 * 2 * PI)
width = 800
height = 800
canvas(width, height)
translate(width/2, height/2)
r0 = 111
r1 = 70
d = 70
res = 0.01
num, den = simplify(r0, r1)
circle(0, 0, r0)
stroke()
for (t = 0.0; t < PI * 2 * den; t += res) {
x = (r0 + r1) * cos(t) - d * cos(((r0 + r1) * t) / r1)
y = (r0 + r1) * sin(t) - d * sin(((r0 + r1) * t) / r1)
lineTo(x, y)
}
stroke()
用化简后的分数的分母保证循环足够多闪以形成完整的曲线。
到目前为止,全部是外摆线(圆外旋轮线)。 让我们改变参数 d 来创建一些其它的长短幅外摆线。
让 d 大于 r1:

d 小于 r1:

在这些例子中我没有再把内圆画出来。
你无需让我再提供更多的例子了。只需要改变数字看看能得到啥结果。
特殊长短幅外摆线
蚶线是长短幅摆线的一种,生成它的两个圆半径相等。如果两个点在圆内,则它是特殊的蚶线被称为 Cardioid 心形曲线。
下面是一个蚶线,两个半径都是 80 , d 值设为 160。

心形线三个值都设为 80

肾脏线是长短幅外摆线的一类,它的定圆半径是动圆半径的2倍,且那个点在动圆的圆周上。这是肾脏线的图

你也可以让那个动圆上的点不在动圆圆周上,但却不再是肾脏线了,但依然是很不错的曲线。

现在让我们把视线转向长短幅内摆线!
长短幅内摆线
正如前面提到的,长短幅内摆线其实就是动圆是延定圆内部滚动的。

我们跟踪那个曲线上的绘制点,就得到了长短幅内摆线

事实上,由于绘制点在动圆圆周上,它就是内摆线。
当绘制点移动到动圆外部, 得到的就是:

当绘制点移到动圆内部...

像之前一样,这个动画是用野路子创建的,其实是有简单公式可以实现。
长短幅内摆线公式:
x = (r0 - r1) * cos(t) + d * cos(((r0 - r1) * t) / r1)
y = (r0 - r1) * sin(t) - d * sin(((r0 - r1) * t) / r1)
参数与长短幅外摆线类似,事实上公式也基本相同,仅有几项符号不同。
下面是使用方法。
width = 800
height = 800
canvas(width, height)
translate(width/2, height/2)
r0 = 300
r1 = 50
d = 50
res = 0.01
num, den = simplify(r0, r1)
for (t = 0.0; t < PI * 2 * den; t += res) {
x = (r0 - r1) * cos(t) + d * cos(((r0 - r1) * t) / r1)
y = (r0 - r1) * sin(t) - d * sin(((r0 - r1) * t) / r1)
}
stroke()
代码运行结果:

注意 r0 与 r1 比例是 6:1, 拥有 6 个点。比例的作用原理与之前的长短幅外摆线一样
下面是半径 312 和 76 (译者注:这里 r0 为 312,r1,d 都为 76):

将绘制点 d 值设大一点移出动圆圆周:

移入动圆内...

如果你不断尝试,我保证你能发掘出更多有趣的图形。
特殊长短幅内摆线
我将再介绍两种特殊长短幅内摆线。我们需要再次调整两个圆的大小比例。

这是星状图的比例 4:1

还有一个更有趣的比例 2:1. 被称为图斯双圆,以描述它的13世纪波斯天文学家名字而命名。下面是演示它的动画。

正如你所看到的,形成了一条直线。
如果你多创建几个动圆,并将每个动圆的绘制点相位与前一个相比再往后调整一点,你将会得到如下图:

每个点延直线来回运动。如果你将所有线条和动圆移除,你会得到一个旋转的圆的错觉。事实上,它看起来像是另一个内摆线

螺旋图!
小时候如果你像我一样比较呆,那么你可能也会像我这样拥有下面这些东西(译者注:可能我不够呆,反正我没有过,倒是见过同学有类似的东西--!):

这是几个星期前为写这一章内容我新买的。它很酷,锡盒子包装内包含了一些绘制用的纸还有使用说明和灵感小册子。

大的齿轮固定在纸上。过去用的是小钉子,现在用的是用粘粘的东西(sticky-tack 多伟大的改进!)固定住,还有一些小的多孔的小齿轮。你将笔插进这些小孔中,让它延着大圆旋转一圈。

瞧!一个长短幅内摆线

如果你将小齿轮摆外面滚一轮,它就是长短幅外摆线。相当有趣,说实施,我还是更喜欢用代码实现它。下面是绘制过程中笔滑了,这让线条看起来有点儿乱。笔触也断断续续的。

当我玩的过程中才发现,它只能画出短幅摆线和内摆线。绘制点只能是在移动的齿轮内。
本章 Javascript 源码 https://github.com/willian12345/coding-curves/blob/main/examples/ch09
博客园: http://cnblogs.com/willian/
github: https://github.com/willian12345/
曲线艺术编程 coding curves 第九章 旋轮曲线(ROULETTE CURVES)的更多相关文章
- 《Linux命令行与shell脚本编程大全》第九章 安装软件程序
包管理系统(PMS):用来进行软件安装.管理和删除的命令行工具 9.1包管理基础 1.主流的Linux发行版都采用了某种形式的包管理系统来控制软件和库的安装 2.PMS用一个数据库来记录:系统上安装了 ...
- Python 编程快速上手 第九章 组织文件
上一章节,主要讲了如何用 Python 进行创建并写入新文件.这一章节,讲了对如何用 Python 对文件进行进一步的操作,包括: 移动,复制,删除文件 改名 压缩文件 [shutil]移动,复制,删 ...
- 《Java并发编程实战》第九章 图形用户界面应用程序界面 读书笔记
一.为什么GUI是单线程化 传统的GUI应用程序通常都是单线程的. 1. 在代码的各个位置都须要调用poll方法来获得输入事件(这样的方式将给代码带来极大的混乱) 2. 通过一个"主事件循环 ...
- JavaScript DOM编程艺术-学习笔记(第八章、第九章)
第八章 1.小知识点: ①某些浏览器要根据DOCTYPE 来决定页面的呈现模式(标准模式 / 怪异模式--也称兼容模式): 兼容模式意味着浏览器要模仿老一辈的浏览器的怪异行为,来让老站点得到运行,并让 ...
- [转]Windows Shell 编程 第九章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987969】
第九章 图标与Windows任务条 如果问一个非程序人员Windows最好的特色是什么,得到的答案应该是系统最有吸引力的图标.无论是Windows98现在支持的通用串行总线(USB)还是WDM(看上去 ...
- 第九章:Python高级编程-Python socket编程
第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...
- Java编程思想 第九章 接口
第九章 接口 抽象类和抽象方法 抽象:从具体事物抽出.概括出它们共同的方面.本质属性与关系等,而将个别的.非本质的方面.属性与关系舍弃,这种思维过程,称为抽象. 这句话概括了抽象的概念,而在Java中 ...
- 精通Web Analytics 2.0 (11) 第九章: 新兴分析—社交,移动和视频
精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第九章: 新兴分析-社交,移动和视频 网络在过去几年中发生了不可思议的发展变化:从单向对话到双向对话的转变; 由视频,Ajax和 ...
- 异步编程系列第05章 Await究竟做了什么?
p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...
- [Effective Java]第九章 异常
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
随机推荐
- 面向Web开发人员的Linux实用入门
从 web 开发的视角说一下在使用 Linux 时遇到的问题,主要是针对操作本身,因为指令在网上都可以查到,不会深入原理,但尽量实用. 基础认知 为什么使用 Linux 最初我使用 Linux 是因为 ...
- ACM-NEFU15届校赛-大二组
A.小林找工作 #include<bits/stdc++.h> using namespace std; const int MAXN=1e5+10; int p[MAXN]; int m ...
- C++库封装JNI接口——实现java调用c++
1. JNI原理概述 通常为了更加灵活高效地实现计算逻辑,我们一般使用C/C++实现,编译为动态库,并为其设置C接口和C++接口.用C++实现的一个库其实是一个或多个类的简单编译链接产物.然后暴露其实 ...
- 【云享专刊】开源遇上华为云,OCP架构变身“云原生框架”
摘要:华为云DTSE团队出品云原生改造指南,助力轻松实践OCP上云. 本文分享自华为云社区<[云享专刊]开源遇上华为云,OCP架构变身"云原生框架">,作者:华为云社区 ...
- linux CentOS 7上安装Chrome浏览器
目录 linux CentOS 7上安装Chrome浏览器 添加Chrome浏览器的官方存储库,使用以下命令: 安装Chrome浏览器: 确认Chrome浏览器是否安装成功: linux CentOS ...
- java.lang.OutOfMemoryError- unable to create new native thread 问题排查
问题描述 最近连续两天大约凌晨3点,线上服务开始异常,出现OOM报错.且服务所在的物理机只能ping通,但是无法登录.报错信息如下: ERROR 04-12 03:01:43,930 [Default ...
- python函数参数与类参数
python关于函数的一些应用 前言 鉴于python3与python2有些不同,看到某些代码时可能会感到疑惑,就稍微记录一下. 一.不限制个数的函数参数 1.*args 以此为参数,会被python ...
- Linux中如何通过yum或者apt下载安装MySQL
一. yum mysql5.7以下 mysql5.7以上 Centos8 可以,但是需要重新配置文件 可以,但是需要重新配置文件 可以,但是需要重新配置文件 Centos7 可以直接yum,但是是 ...
- 最好用的.NET敏捷开发框架-RDIFramework.NET V3.6版全新发布 100%源码授权
RDIFramework.NET,基于.NET的快速信息化系统敏捷开发框架.10年沉淀.历经上千项目检验,致力于企业智能化开发,帮助提升软件开发效率.最好用的.NET开发框架,100%源码授权. 1. ...
- 第7章. 部署到GiteePages
Gitee Pages 是一个免费的静态网页托管服务,您可以使用 Gitee Pages 托管博客.项目官网等静态网页.如果您使用过 Github Pages 那么您会很快上手使用 Gitee 的 P ...