曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

这篇具有很好参考价值的文章主要介绍了曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

第三章 弧,圆,椭圆(TRIG CURVES)

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

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

blog: http://cnblogs.com/willian/

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

曲线艺术编程系列第三章

这一篇中我们将关注如何绘制圆弧,圆和椭圆。(结束前再聊聊正切相关的)

很可能你使用的编程平台已内建了一些这样的 api。举个例子,虽然 HTML Canvas api 没有直接画圆形和椭圆的函数,但它有一个 arc 函数,可以用它来间接实现。如何自己动手实现这些功能很有用。某时某刻总会用到的。

首先,我们先聚焦于圆弧与圆。可以说圆弧是圆的一部分,也可以说圆是圆弧延展 360 度而成。从哪个方向开始探索都可以,但对我更钟意从圆开始再进入圆弧部分。

统一度量衡

我使用的绘图 api 内 y 轴与标准笛卡尔坐标系是相反的。负的向上,正的下向。

有别于数学和科学中使用的笛卡尔坐标系, 这在图形绘制 api 中很常见。它与 Processing, HTML Canvas, Cairographics, .net graphics 以及其它很多图形库一样。

有些 api 确实使用笛卡尔坐标系,当角度值为正向增长时代表逆时针方向。

但有些如 pygame, 是混用,y 是为正时是向下,但正向角度旋转时却是逆时针的。

这会影响角度的测量。0 度指向东方。在笛卡尔坐标系中,正向角度转动是逆时针,负向角度转动移动是顺时针。在 Y 轴反转的系统中,正好相反,在这章中我不会再强调这里的差别。我们这里会重新实现一些已内建的绘图 api 函数。

在你学完圆弧这一章节后,如果你想在笛卡尔坐标系下创建圆弧,圆和椭圆的函数也会很简单。

定义

圆的定义一般来说会像这样:“与给定的一个中心点等距的一堆点的集合” ,但当你真的想画一个圆的时候,发现这并没什么卵用。我并不需要一堆无限的点。我们仅需要足够多的点用短线串起来形成圆。

你也见过“圆方程”类似 x2+y2=r^2(译者注:这里是x平方+y平方= r 平方,可表示圆周上任意一点)。 当你尝试想画圆,这好好像也没啥用。

然后你得到了下面这样的参数方程

x = a + r * cos(t)
y = b + r * sin(t)

这里 a 和 b 是圆的中心点, r 是半径, t 是范围参数变量从 0 到 2 * PI。

这里才开始有点儿用。我们可以定义一个圆心点和半径然后跑一个循环,从 0 至 2 * PI 从而得到一堆点,然后用线将这些点连接起来。

还是得提醒你,这里展示的是伪代码。关于伪代码的问题请参考第一章内的说明内容。

width = 600
height = 600
canvas(width, height)
 
cx = width / 2
cy = height / 2
radius = 250
for (t = 0; t < PI * 2; t += 0.01) {
  lineTo(cx + cos(t) * radius, cy + sin(t) * radius)
}
closePath()
stroke()

取决于你所使用的绘图 api, 在你使用 lineTo 之前,你很可能需要用 moveTo 来开头。我信你自己能搞定。这很简单。cx, cy 和 radius 就是上面公式中的 a, b 和 r 。 t 是用于循环的弧度。

注意,最后需要用 closePath 闭合一下。大多数绘图 api 都有这一特性。它会将最后一点与路径最起始点相连,将圆闭合起来。可能在你的平台上有一丢丢不一样,但大概应该如下图所示:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

有一个问题,循环中 0.01 是我猜的一个大概值。如果你定的太大,比如 0.2 ,那么相当于你大踏步绕圆跳一圈,得到的结果将会是下面这样很粗糙的圆,看起来可不怎么润:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

但你如果将增长值设的太小,那么系统将会做太多无用的绘制。圆周越大你就需要更多的线段让它看起来丝滑,半径越小需要的线段就越少。如果你用 0.01 这个常量用于递增,你将在每个圆上绘制 628 条线段。这在小圆上面可就太浪费了。

我上下而求索,找到了一个可用的方案,大至是 4.0 / radius。 在半径 5 至 200 范围内,比直接使用 0.01 这个递增值绘制时的线断少了一半,但看起来依然不错。

可能因系统而异,你自己尝试一下不同值看看。

封装成函数

有了这些, 我们可以把绘制圆封装成一个函数:

function circle(x, y, r) {
  res = 4 / r
  for (t = 0; t < PI * 2; t += res) {
    lineTo(x + cos(t) * r, y + sin(t) *r)
  }
  closePath()
}

注意:我将 stroke 方法移在函数内移徐掉了,这样你可以用函数创建圆,可以选择描边或填充,或两者都用。如果你愿意,你可以进一步封装 strokeCircle 函数和 fillCircle 函数。下面是函数使用演示

width = 600
height = 600
canvas(width, height)
 
circle(width / 2, height / 2, 200)
stroke()

圆弧

已经完成圆这部分了,现在我们可以在此基础上创建 arc 函数。你的编程语言可能有现成的 api ,但这不重要,我们再实现一遍。很简单和圆函数一样,但我们用 start 代表开始位置 0 和 end 代替结束位置的 2 * PI, 我们让调用者决定开始与结束位置。

不再解释了因为过于简单我直接抛出伪代码吧

function arc(x, y, r, start, end) {
  res = 4 / r
  for (t = start; t < end; t += res) {
    lineTo(x + cos(t) * r, y + sin(t) *r)
  }
  lineTo(x + cos(end) * r, y + sin(end) *r)
}

你看简单吧,仅仅是将硬编码的开始与结束角度替换成参数传入的形式。当然,我移除了 closePath() 调用,取而代之的是最终 lineTo,这样更精确一点。

像下面一样使用它:

width = 600
height = 600
canvas(width, height)
 
arc(width / 2, height / 2, 250, 0.5, 3.5)
stroke()

结果会是:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

有一丢丢问题。如果我将输入的开始与结束对调呢?

arc(width / 2, height / 2, 250, 3.5, 0.5)

它会立即结束循环,因为 3.5 已经大于 0.5了。啥也不会画出来。 我想要的是开始在 3.5 度,又绕回到 0.5 度像下面这样:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

一种方式是我们只要保证结束的度数大于开始度数。我们可以判断如果结束度数小于开始度数,那么直接加上 2*PI,直到结束度数大于开始度数。

function arc(x, y, r, start, end) {
  while (end < start) {
    end += 2 * PI
  }
  res = 4 / r
  for (t = start; t < end; t += res) {
    lineTo(x + cos(t) * r, y + sin(t) *r)
  }
  lineTo(x + cos(e) * r, y + sin(e) *r)
}

现在应该可以正常展示成上面期望的那样了。

还有一件事儿,我们总是假定用户绘制是顺时针的。我们应该让用户自己决定。

幸运地是这很容易实现,我们只需要传另一个参数 anticlockwise,如果值为 true,我们只需要交替 start 与 end 就可以了。

function arc(x, y, r, start, end, anticlockwise) {
  if (anticlockwise) {
    start, end = end, start
  }
  while (end < start) {
    end += 2 * PI
  }
  res = 4 / r
  for (t = start; t < end; t += res) {
    lineTo(x + cos(t) * r, y + sin(t) *r)
  }
  lineTo(x + cos(e) * r, y + sin(e) *r)
}

如果你足够幸运,你使用的编程语言支持像这样变量交换:

start, end = end, start

如果不能直接像上面这样交替,那就只能用老方法了:

temp = start
start = end
end = temp

这样调用

arc(width / 2, height / 2, 250, 3.5, 0.5, false)
stroke()

会给你期望的圆弧了:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

下面这段代码

arc(width / 2, height / 2, 250, 3.5, 0.5, true)
stroke()

则给你这样的圆弧:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

两者都是开始于 3.5 度结束到 0.5,一条正的,一条按反的方式画。

正如开始时在圆形处提到过,这里我选择了正向角度顺时针为默认,这与笛卡尔坐标系不同。现在你知道在不同方向上如何绘制圆弧,你可以选择你一个喜欢的作为默认方向。

现在我们有了一个强大的圆弧函数,我们其实可以用它替换原来圆函数内的一些重复代码,像下面这样

function circle(x, y, r) {
  arc(x, y, r, 0, 2 * PI, true)
}

从 0- 2*PI 可不就是一个圆么。

片段与扇区

这里还有几个可以创建的函数如果你觉得它们有用的话。将圆弧首尾相连起(一条弦)形成的一个圆弧片。我们可以这样实现它,在绘制圆弧完毕时直接调用 closePath 函数,你使用的编程语言中肯定也有类似 closePath 的函数。

function segment(x, y, r, start, end, anticlockwise) {
  arc(x, y, r, start, end, anticlockwise)
  closePath()
}

这个圆弧片,就是从 2.5 度至 4.5 度

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

一个扇形就是将圆弧用线段从中心点连接起来,我们可以调用 lineTo 至中心点,然后再 closePath

function sector(x, y, r, start, end, anticlockwise) {
  arc(x, y, r, start, end, anticlockwise)
  lineTo(x, y)
  closePath()
}

用上面圆弧片一样的参数画的扇形:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

现在你可以自己画饼图了。

多边形

在介绍椭圆之前,我想先奖励个正多边型。 这倒不是我认为多边形是曲线,但数学上来讲它可能真的是。 无论如何,反正来都来了,把它学了吧。

最开始我们讨论过分辨率,我们看到过低分辨率的圆边上看起来是一段一段的。你能看到圆是由单独一条条线段组成的。我们可以把这个 bug 点转化成一个可用的特征。如果我们将分辨率降低到足够低直到只有6个片段组成一个圆,我们就得到了一个六边形, 5 条线段就是 五边形,4 条就是方形,3 条就是三角形。 我们仅需要特别处理我们希望有多少条边,除以 2*PI 当作分辨率就可以成形了。

实现如下:

function polygon(x, y, radius, sides) {
  res = PI * 2 / sides
  for (i = 0; i < PI * 2; i+= res) {
    lineTo(x + cos(i) * radius, y + sin(i) * radius)
  }
  closePath()
}

像下面这样调用:

polygon(300, 300, 250, 5)
stroke()

得到一个五边形:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

也许你想为这个多边形初始化一个特别的角度,你可以这样做

function polygon(x, y, radius, sides, rotation) {
  res = PI * 2 / sides
  for (i = 0; i < PI * 2; i+= res) {
    lineTo(x + cos(i + rotation) * radius, y + sin(i + rotation) * radius)
  }
  closePath()
}

现在你可以这样使用

polygon(300, 300, 250, 5, 0.5)
stroke()

得到了一个转了一点角度的多边形

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

试试传入不同的边数。

一个有趣的效果是创建一系列大小不同的多边形,每个多边形相应旋转一丢丢的角度:

angle = 0
for (r = 5; r <= 255; r += 10) {
  polygon(300, 300, r, 5, angle)
  stroke()
  angle += 0.05
}

效果如下:

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

也许有一点点离题,但你看图形里突然形成了5条新的曲线,能接受,能接受。

椭圆

本文最后一部分,椭圆。

好的让我们来看看椭圆在维基百科中的定义...

环绕两个焦点的平面曲线,对于曲线上的所有点,到焦点的两个距离之和为常数
https://en.wikipedia.org/wiki/Ellipse

Em... 完全搞不懂,太数学化了。再看看这条解释...

椭圆是圆锥截面的封闭类型:沿圆锥与平面相交的平面曲线
https://en.wikipedia.org/wiki/Ellipse

还是一样不好理解,好吧,继续...

一个椭圆也可以用一个焦点和椭圆外一条叫做准线的线来定义:对于椭圆上的所有点,到焦点的距离和到准线的距离之比是一个常数。
https://en.wikipedia.org/wiki/Ellipse

好吧,还是不好懂,但就如之前那样,最终我们可以找到可用的参数方程,和之前的圆参数方程差不多

x = a + rx * cos(t)
y = b + ry * sin(t)

这里,除了用 a 和 b 表示圆心点之外,还有 rx 和 ry 最简单就是把它们想象成 “radius x” 和“radius y”, 尽管这些名字可能让数学家鄙视。但对于一个未旋转的椭圆 rx 就是等于一半的椭圆宽,ry 等于一半的椭圆高。

所以我们可以编写下面这样一个函数

function ellipse(x, y, rx, ry) {
  res = 4.0 / max(rx, ry)
  for (t = 0; t < 2 * PI; t += res) {
    lineTo(x + cos(t) * rx, y + sin(t) * ry)
  }
  closePath()
}

值得提醒的一点,是分辨率值, 我将 4.0 除以 rx 和 ry 中的最大值。你可以想想有没有更好的,但这对于我来说足够用了。现在你可以像下面这样调用它

ellipse(300, 300, 250, 150)
stroke()

And get:

得到

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

小奖励

有时候我写了就停不下来。接下来的这部分与创建曲线关系不大…,或者说也有一定相关。你看完后再看想想是不是有关系吧。 比起在圆周(或圆弧,多边形,椭圆)上每个点之间用线段相连,我们可以在这些点上画一些其它的形状。我们将增加曲线点之间的间隔以拥有足够空间容纳其它形状,不至于挤在一起,不然看起来太乱。事实上,多边形函数正好适用在这里。它可以让我们画一个由多个圆组成的圆形环。对于代码我就不解释了,你应该可以理解。

width = 600
height = 600
canvas(width, height)
 
cx = width / 2
cy = height / 2
 
res = PI * 2 / 20 // to draw 20 circles
for (t = 0; t < PI * 2; t += res) {
  x = cx + cos(t) * 200
  y = cy + sin(t) * 200
  circle(x, y, 20)
  stroke()
}

曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)

总结

有想过要不要写这一部分,跑题已经跑的够远的了,这一篇也足够的长了。

到目前为止都很基础,但希望足够有趣。从这章之后,我们将慢慢接触一点复杂的东西希望内容更加的有趣。

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


博客园: http://cnblogs.com/willian/
github: https://github.com/willian12345/文章来源地址https://www.toymoban.com/news/detail-468708.html

到了这里,关于曲线艺术编程 coding curves 第三章 弧,圆,椭圆(ARCS, CIRCLES, ELLIPSES)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 曲线艺术编程 coding curves 第八章 贝赛尔曲线(Bézier Curves)

    原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/ 译者:池中物王二狗(sheldon) blog: http://cnblogs.com/willian/ 源码:github: https://github.com/willian12345/coding-curves 曲线艺术编程系列第8章 贝赛尔曲线 让我们回到真正的曲线上来。贝赛尔曲线编程就非常有趣领人止不住的想探索一翻

    2024年02月08日
    浏览(56)
  • 曲线艺术编程 coding curves 第六章 平托图 (Pintographs)

    原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/ 译者:池中物王二狗(sheldon) blog: http://cnblogs.com/willian/ 源码:github: https://github.com/willian12345/coding-curves 曲线艺术编程系列第 6 章 另一个可用于模拟绘制复杂曲线的物理装置叫平托图(Pintograph), 事实上我真的自己弄了一个。

    2024年02月08日
    浏览(43)
  • 曲线艺术编程 coding curves 第七章 抛物线(Parabolas)

    原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/ 译者:池中物王二狗(sheldon) blog: http://cnblogs.com/willian/ 源码:github: https://github.com/willian12345/coding-curves 曲线艺术编程系列第7章 我承认这一章脑暴时,再三考虑过是否要将抛物线包含进来。此篇覆盖的抛物线比起之前三章

    2024年02月08日
    浏览(42)
  • 曲线艺术编程 coding curves 第五章 谐波图形(谐振图形) HARMONOGRAPHS

    原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/ 译者:池中物王二狗(sheldon) blog: http://cnblogs.com/willian/ 源码:github: https://github.com/willian12345/coding-curves 曲线艺术编程系列第 5 章 这一篇幅建立在对第四章利萨茹曲线的讨论之上。事实上谐波图形并不是一类曲线,它是一

    2024年02月08日
    浏览(37)
  • 编程的实践理论 第三章 函数理论

    如果我们解释规则的使用,我们总是允许发明新的语法。新的语法的 一个好的来源是名称(标识符),通过公理给出了规则是如何使用的。 通常,当我们介绍名称和公理时,我们要它们实现当前的目的。读者 被假定理解它们的作用域,就是它们的应用范围,在它的范围外不

    2024年02月07日
    浏览(86)
  • 《TCP IP网络编程》第三章

    3.1 分配给套接字的IP地址与端口号         IP 是 Internet Protocol(网络协议)的简写,是为收发网络数据而分配给计算机的值。端口号并非赋予计算机的值,而是为了区分程序中创建的套接字而分配给套接字的序号。              为使计算机连接到网络并收发数据,必

    2024年02月13日
    浏览(39)
  • CMD与DOS脚本编程【第三章】

    预计更新 第一章. 简介和基础命令 1.1 介绍cmd/dos脚本语言的概念和基本语法 1.2 讲解常用的基础命令和参数,如echo、dir、cd等 第二章. 变量和运算符 2.1 讲解变量和常量的定义和使用方法 2.2 介绍不同类型的运算符和运算规则 第三章. 控制流程和条件语句 3.1 介绍if、else、for、

    2024年02月06日
    浏览(47)
  • 第三章 MATLAB的分支语句和编程设计

            在前面的章节中,我们开发了几个完全运转的 MATLAB 程序。但是这些程序都十分 简单,包括一系列的 MATLAB 语句,这些语句按照固定的顺序一个接一个的执行。像这 样的程序我们称之顺序结构程序。它首先读取输入,然后运算得到所需结果,打印出结 果,并退出。至

    2024年02月05日
    浏览(40)
  • 《Opencv3编程入门》学习笔记—第三章

    记录一下在学习《Opencv3编程入门》这本书时遇到的问题或重要的知识点。 一、图像的载入、显示和输出到文件 (一)OpenCV的命名空间 简单的OpenCV程序标配: (二)Mat类简析 表示从指定路径下把名为dota.jpg的图像载入到Mat类型的srcImage 变量中。 (三)图像的载入与显示概述

    2024年02月08日
    浏览(56)
  • 第三章:R语言编程 第六节:模块模式

    模块是一种结构,由无法从外部访问的数据以及操纵这些数据的函数组成,是标准化代码编写技术 模块模式的优点 1.因为无法从外部访问数据,所以很好的隐藏了内部实现,说人话就是无需考虑数据在模块内部是如何保存的 2.其次,因为只能借助实现定义的函数访问模块内部

    2024年03月11日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包