曲线艺术编程codingcurves第十章螺旋曲线(SPIRALS)

博客 动态
0 163
优雅殿下
优雅殿下 2023-06-14 14:10:47
悬赏:0 积分 收藏

曲线艺术编程 coding curves 第十章 螺旋曲线(SPIRALS)

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

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

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

第十章 螺旋(SPIRALS)

曲线艺术编程系列第 10 章

来聊聊螺旋线。

螺旋非常像圆,它是一组点到定圆中心点的距离关系。与圆不同的是,圆的点与中心点距离固定不变,而螺旋上的点与中心点距离是变化的。给定点与中心点的距离通常是由一个基于两点之间的角度计算得到。所以你需要有函数传入角度返回半径。然后你可以通过半径与角度找到那个角度上对应的点。有很多不同的螺旋公式,会得到不同形态的螺旋。让我们从最基础的一个开始。

注:圆有时候也被称为“退化”的旋转。“退化”这词在这里没有贬义,只是说圆遵循螺旋的“规则”但又不是通常我们想象中的螺旋。就像一个三角形其中某一边的长度为 0。所有三角形数学计算通常都能正常工作,但在我们眼中它却成了一条线。

阿基米德螺旋

image

如你所见,每个圆的半径都会定量增加。下面是这个螺旋线的公式:

r = a * t

此处,t 是弧度 a 是某个常量,上图中 a 设为 5。 两者相乘的结果即是这个 t 角度上的半径值。如果将 6 增加到 10,我们会得到如下 :

image

现在如你看到的这样,常量 a 决定了螺旋内每个圆之间的间距。在这个例子中,螺旋都旋到 canvas 边界外了。

当我们绘制螺旋时,我们首先需要决定绘制多少个圈。要绕多少圈?如果 t 从 0 到 2 * PI,我们得到的是一个圈:

image

你可以看到这个螺旋有一个圈,螺旋曲线从中心开始向外扩展。三个圈表示 t 从 0 到 2 * PI * 3。

image

有了上面这些,我们可以将它们合在一起创建一个螺旋线的调式器(playground),像这样:

width = 800
height = 800
canvas(width, height)
translate(width / 2, height / 2)
 
cycles = 10
res = 0.01
 
for (t = res; t < 2 * PI * cycles; t += res) {
  r = archimedean(5, t)
  x = cos(t) * r
  y = sin(t) * r
  lineTo(x, y)
}
stroke()
 
function archimedean(a, t) {
  r = a * t
  return r
}

比如我们希望有 10 个圈, 循环结束点就是 2 * PI * cycles。调用 archimedean 螺旋函数,传入 5 作为它的常量, t 作为弧度。它会返回给我们一个半径值,我们可以用它和弧度计算出对应点的 x, y 坐标并用线连接。

image

我使用的绘图 api , 角度正向增加是顺时针方向。所以这个螺旋从中心顺时针绕出来。这可能与你正在使用的编程平台内表现有所不同。这取决于你使用的绘图 api 如何处理角度方向。为了翻转旋转方向,有几种方式:

A. 用 scale 翻转 canvas

scale(1, -1)

B. 改变代码创建点的方式,使其中一个轴为负 例如:

x = cos(t) * r
y = -sin(t) * r

C. 将循环的方向变反

for (t = -res; t > -2 * PI * cycles; t -= res) {

任意一种方式都可以让你的螺旋方向改变。

还有最后一件事...

代码中值得注意的一点是循环起始点是 res 而不是 0

for (t = res; t < 2 * PI * cycles; t += res) {

想知道为我为啥这么做,我们需要从下一种螺旋线寻找答案。

双曲线螺旋 Hyperbolic Spiral

image

这个螺旋与第一个螺旋看起来非常不同。上个螺旋内每个圈是等距的。你可能会说每个圈之间的距离都增长了,还是等等先别下结论。

下面这个函数是这个螺旋准备的:

function hyperbolic(a, t) {
  r = a / t
  return r
}

也没有特别大的不同。我们用除代替了乘。上图我传入了 1000 。如果我将值降到 10, 我们会得到一个很小的螺旋线像这样:

image

这就很明显了 for 循环以 res 开始而不是 0, 如果是 0,那么就会出现除数为 0 的这种情况,这会导致某些问题。也许会崩溃,或者半径为 NaN (not a number) 值, 这没什么用。所以我们将起始值设为非 0。

另一个值得注意的事是螺旋线的旋转方向。上图我画了 20 个圈。如果降为 5(a 变回 1000),将得到:

image

你现在可能看到随着 t 值增长螺旋变的很大但,但增长的速度慢小了。当有 100 个圈,螺旋中心位置开始有些堵了。

image

就如我之前所说,咋一看每个圈半径都在增长,但你现在可以观察到实际上是开始时很大,但是圈与圈之间的半径值逐渐变小了。

费马螺线

image

这种螺旋线很漂亮。这是公式:

function fermat(a, t)  {
    r = a * pow(t, 0.5)
    return r
}

这里我们将 a 乘以 t 的 0.5 平方。假定你的编程语言内置了 pow 函数计算第一个参数的第二个参数次幂。根据你使用的编程语言,它可能是这样:

r = a * (t^0.5)

or

或这样:

r = a * (t**0.5)

上一张图 a 设为了20 绘制了 20 圈。 绘制 10 圈 结果如下:

image

可以看到螺旋线是从内向外绘制的。 一开始圈与圈之间的间距比较大,但越往外面圈的间距越来越小,还超出边界了

圈数改回 20 个圈 a 设为 40:

image

我们能看到间距有所增加。

100 个圈 a 设为 15。

image

最终,每圈之间已很难有间距。而我们得到了一些有趣的摩尔纹图案

连锁螺线 Lituus Spiral

image

它看起来像双曲螺线,但公式却更接近于费马螺线:

function fermat(a, t)  {
    r = a * pow(t, -0.5)
    return r
}

我们公需要将指数中的 0.5 改为 -0.5。上图螺旋线绘制了 20 个圈 a 的值为 500。 下面是 10 个圈

image

你可以看到这是另一种向内绘制的螺旋线。 改回 20 圈, a 设为 50,得到:

image

词 “Lituus” 原意是弯曲的杖,弯曲的魔法棒或角。上图很好的解析了它名字的由来。

将 a 升到 1000, 可以得到:

image

可以这样讲,a 值越低,螺旋吞噬进中心越快。

还有很多类型的螺旋线,来,继续!

对数螺旋 Logarithmic Spiral

image

它有点像反费马螺旋。一开始间隔挺小,越往外越大。公式:

function logarithmic(a, k, t)  {
    r = a * exp(k * t)
    return r
}

公式看起来比之前遇到过的要复杂一点。t 前有两个参数。我们先从 exp 函数开始学习。

我们实际上在谐振波图(Harmonographs) 这一章见过它. 回顾一下,有一个数学常数 e ,也称欧拉数。它的值大概是 2.71828。 当我们在与对数打交道时,常将 e 乘方。所以很多数学程序库会直接提供一个函数, 常被命名为 exp。 举个具体的例子,在 javascript 中,有常用 e, Math.E。为了计算 e 的 2 次幂,就得像下面这样做:

Math.pow(math.E, 2)

但其实已经有一个叫 exp 的函数了,你可以直接用:

Math.exp(2)

代码中的公式也得改成,

r = a * exp(k * t)

我们用 a 乘以 e 的 k * t 次幂。上面的图,我得将 a 设成 0.5 且 将 k 设为 0.05。

如果将 a 变为 1,我们将得到一个相当大的螺旋线,虽然它们看起来很像。

image

a 设为 0.24 后:

image

还是挺像的,但更小一点儿了。

将 a 重置为 0.5 并且将 k 升到 0.1

image

你可以看到扩张的更快了。将 k 设为 0.04 它对结果的影响远超我的预期

image

黄金螺旋(Golden spiral)

image

这类螺旋增长率叫“黄金比例”,大概接近 1.618。它的公式是:

function golden(t) {
    r = pow(PHI, 2 * t / PI)
    return r
}

首先,这个函数除了 t 之外没有其它参数。 PHI 黄金比例值是硬编码在函数内的。很多数学程序库都有黄金比例这个内建常量。如果你没有,将这近似的设置为:

PHI = 1.61803

或者你想更精确一点,你可以像下面这样写得到精确值:

PHI = (1 + sqrt(5) ) / 2

然后放在某个地方按需引用即可。

你很可能已经见过这种螺旋图了:

image

By Romain – Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=114415511

这实际是一个斐波那契螺旋。方块大小是下两个方块之和且每个方块内的曲线是一个角为中心 90 度生成弧线。它不是精确的黄金螺旋,但很接近了。

还有更多

仔细阅读这个螺旋线列表,也许你能找到其它更有趣的

https://en.wikipedia.org/wiki/List_of_spirals

还有:

https://mathworld.wolfram.com/topics/Spirals.html

螺旋角(Spirangles)

image

在写这一章的时候我偶然发现了这个词 (Spirangles)。本质上它是由直线线段组成的螺旋。通过改变每个线段的角度,你可以将它们组成不同的形状。

在已有的基础上实现它非常容易。上面中有个例子使用的是 archimedean 函数,a 为 3,并且 cycles 为 20。 窍门是减低分辨率 res 上图的效果我是将 res 设为了发下值:

res = PI * 2 / 3

Now on each step of the for loop, t will increase by one-third of a circle. Here are some others, dividing by 4 and 5.

现在,循环中每一步,t 会增加 1/3 个圆。 以下是其它, 除 4 和 除 5 的效果:

image

image

窍门你已经知道了。你可能想在其它方法上试试。其中一些通过这样修改后变的让人相当满意。

葵花螺旋(Sunflowers)

若是没有讨论葵花螺旋,那么螺旋这一篇就不算完整。试试用葵花,斐波那契与螺旋的组合搜索,你能得到巨量的阅读材料与漂亮图片。我的意思是,除了绘制葵花螺旋这类有趣的螺旋线外,其它的螺旋我就让你自己去探索了。黄金比例在这个图中是天生的,图是这样:

image

你可以看到不止一条螺旋,有许多螺旋以不同角度进进出出。我常用下面的代码实现(还是以伪代码展示):

width = 800
height = 800
canvas(width, height)
translate(width / 2, height / 2)
 
count = 1000
for (i = 0; i < count; i++) {
  percent = i / count
  size = 14.0 * percent
  r = 380.0 * percent
  t = i * PI * 2 * PHI
  x = cos(t) * r
  y = sin(t) * r
  circle(x, y, size)
  fill()
}

我们用变量 count 代表想要绘制多少“葵花籽”。这里 count 设为了 1000.

然后我们循环 用 i / count 得到一个百分比值。

变量 size 是每颗“种子”的半径。当 i 越接近 count 时,percent 的值会越接近 1,因此 size 的值也会越接近最大值 14。

同样 r 用于表示种子分布的半径。它最高值是 380, 仅比 canvas 宽度的一半小一点点。

我们用 t = i * PI * 2 * PHI 计算出 t , 它就是神奇的葵花斐波那契公式。说真的,你想知道更多,那就认真看一下。有了角度和半径,我们就可以得到 x 和 y 坐标,然后根据种子自己的大小绘制种子了。

到目前为止我想说的螺旋线就这么多了。下回见!

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


博客园: http://cnblogs.com/willian/
github: https://github.com/willian12345/

posted @ 2023-06-14 13:31  池中物王二狗  阅读(7)  评论(0编辑  收藏  举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2012 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员