说明

【跟月影学可视化】学习笔记。

如何用向量描述曲线?

用向量绘制折线的方法来绘制正多边形

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>如何用向量描述曲线</title><style>canvas {border: 1px dashed salmon;}</style>
</head>
<body><canvas width="512" height="512"></canvas><script type="module">import { Vector2D } from './common/lib/vector2d.js';const canvas = document.querySelector('canvas');const ctx = canvas.getContext('2d');const {width, height} = canvas;ctx.translate(0.5 * width, 0.5 * height);ctx.scale(1, -1);/*** 边数 edges* 起点 x, y* 一条边的长度 step* */function regularShape(edges = 3, x, y, step) {const ret = [];const delta = Math.PI * (1 - (edges - 2) / edges);let p = new Vector2D(x, y);const dir = new Vector2D(step, 0);ret.push(p);for(let i = 0; i < edges; i++) {p = p.copy().add(dir.rotate(delta));ret.push(p);}return ret;}function draw(points, strokeStyle = 'salmon', fillStyle = null) {ctx.strokeStyle = strokeStyle;ctx.beginPath();ctx.moveTo(...points[0]);for(let i = 1; i < points.length; i++) {ctx.lineTo(...points[i]);}ctx.closePath();if(fillStyle) {ctx.fillStyle = fillStyle;ctx.fill();}ctx.stroke();}draw(regularShape(3, 128, 128, 100));  // 绘制三角形draw(regularShape(6, -64, 128, 50));  // 绘制六边形draw(regularShape(11, -64, -64, 30));  // 绘制十一边形draw(regularShape(60, 128, -64, 6));  // 绘制六十边形</script>
</body>
</html>

如何用参数方程描述曲线?

1. 画圆

圆可以用一组参数方程来定义。定义了一个圆心在(x0,y0),半径为 r 的圆。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>画圆</title><style>canvas {border: 1px dashed salmon;}</style>
</head>
<body><canvas width="512" height="512"></canvas><script>const canvas = document.querySelector('canvas');const ctx = canvas.getContext('2d');const {width, height} = canvas;ctx.translate(0.5 * width, 0.5 * height);ctx.scale(1, -1);const TAU_SEGMENTS = 60;const TAU = Math.PI * 2;function arc(x0, y0, radius, startAng = 0, endAng = Math.PI * 2) {const ang = Math.min(TAU, endAng - startAng);const ret = ang === TAU ? [] : [[x0, y0]];const segments = Math.round(TAU_SEGMENTS * ang / TAU);for(let i = 0; i <= segments; i++) {const x = x0 + radius * Math.cos(startAng + ang * i / segments);const y = y0 + radius * Math.sin(startAng + ang * i / segments);ret.push([x, y]);}return ret;}function draw(points, strokeStyle = 'salmon', fillStyle = null) {ctx.strokeStyle = strokeStyle;ctx.beginPath();ctx.moveTo(...points[0]);for(let i = 1; i < points.length; i++) {ctx.lineTo(...points[i]);}ctx.closePath();if(fillStyle) {ctx.fillStyle = fillStyle;ctx.fill();}ctx.stroke();}draw(arc(0, 0, 100));</script>
</body>
</html>

2. 画圆锥曲线

椭圆

a、b 分别是椭圆的长轴和短轴,当 a = b = r 时,这个方程是就圆的方程式。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>椭圆</title><style>canvas {border: 1px dashed salmon;}</style>
</head>
<body><canvas width="512" height="512"></canvas><script>const canvas = document.querySelector('canvas');const ctx = canvas.getContext('2d');const {width, height} = canvas;ctx.translate(0.5 * width, 0.5 * height);ctx.scale(1, -1);const TAU_SEGMENTS = 60;const TAU = Math.PI * 2;function ellipse(x0, y0, radiusX, radiusY, startAng = 0, endAng = Math.PI * 2) {const ang = Math.min(TAU, endAng - startAng);const ret = ang === TAU ? [] : [[x0, y0]];const segments = Math.round(TAU_SEGMENTS * ang / TAU);for(let i = 0; i <= segments; i++) {const x = x0 + radiusX * Math.cos(startAng + ang * i / segments);const y = y0 + radiusY * Math.sin(startAng + ang * i / segments);ret.push([x, y]);}return ret;}function draw(points, strokeStyle = 'salmon', fillStyle = null) {ctx.strokeStyle = strokeStyle;ctx.beginPath();ctx.moveTo(...points[0]);for(let i = 1; i < points.length; i++) {ctx.lineTo(...points[i]);}ctx.closePath();if(fillStyle) {ctx.fillStyle = fillStyle;ctx.fill();}ctx.stroke();}draw(ellipse(0, 0, 100, 50));</script>
</body>
</html>

抛物线

抛物线的参数方程。其中 p 是常数,为焦点到准线的距离。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>抛物线</title><style>canvas {border: 1px dashed salmon;}</style>
</head>
<body><canvas width="512" height="512"></canvas><script>const canvas = document.querySelector('canvas');const ctx = canvas.getContext('2d');const {width, height} = canvas;ctx.translate(0.5 * width, 0.5 * height);ctx.scale(1, -1);const LINE_SEGMENTS = 60;function parabola(x0, y0, p, min, max) {const ret = [];for(let i = 0; i <= LINE_SEGMENTS; i++) {const s = i / 60;const t = min * (1 - s) + max * s;const x = x0 + 2 * p * t ** 2;const y = y0 + 2 * p * t;ret.push([x, y]);}return ret;}function draw(points, strokeStyle = 'salmon', fillStyle = null) {ctx.strokeStyle = strokeStyle;ctx.beginPath();ctx.moveTo(...points[0]);for(let i = 1; i < points.length; i++) {ctx.lineTo(...points[i]);}ctx.closePath();if(fillStyle) {ctx.fillStyle = fillStyle;ctx.fill();}ctx.stroke();}draw(parabola(0, 0, 5.5, -10, 10));</script>
</body>
</html>

3. 画其他常见曲线

在 lib 下面新建一个 parametric.js 文件,封装一个更简单的 JavaScript 参数方程绘图模块。

// 根据点来绘制图形
function draw(points,context,{ strokeStyle = "salmon", fillStyle = null, close = false } = {}
) {context.strokeStyle = strokeStyle;context.beginPath();context.moveTo(...points[0]);for (let i = 1; i < points.length; i++) {context.lineTo(...points[i]);}if (close) context.closePath();if (fillStyle) {context.fillStyle = fillStyle;context.fill();}context.stroke();
}// 导出高阶函数绘图模块
export function parametric(xFunc, yFunc, zFunc) {/*** start、end 表示参数方程中关键参数范围的参数* seg 表示采样点个数的参数,当 seg 默认 100 时,就表示在 start、end 范围内采样 101(seg+1)个点* ...args 后续其他参数是作为常数传给参数方程的数据。* */ return function (start, end, seg = 100, ...args) {const points = [];for (let i = 0; i <= seg; i++) {const p = i / seg;const t = start * (1 - p) + end * p;const x = xFunc(t, ...args); // 计算参数方程组的xconst y = yFunc(t, ...args); // 计算参数方程组的yif (zFunc) {points.push(zFunc(x, y));} else {points.push([x, y]);}}return {draw: draw.bind(null, points),points, // 生成的顶点数据};};
}

下面使用上面封装的实现一下抛物线,阿基米德螺旋线,星形线。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>画其他常见曲线</title><style>canvas {border: 1px dashed salmon;}</style></head><body><canvas width="512" height="512"></canvas><script type="module">import { parametric } from "./common/lib/parametric.js";const canvas = document.querySelector("canvas");const ctx = canvas.getContext("2d");const { width, height } = canvas;const w = 0.5 * width,h = 0.5 * height;ctx.translate(w, h);ctx.scale(1, -1);// 绘制坐标轴function drawAxis() {ctx.save();ctx.strokeStyle = "#ccc";ctx.beginPath();ctx.moveTo(-w, 0);ctx.lineTo(w, 0);ctx.stroke();ctx.beginPath();ctx.moveTo(0, -h);ctx.lineTo(0, h);ctx.stroke();ctx.restore();}drawAxis();// 绘制抛物线const para = parametric((t) => 25 * t,(t) => 25 * t ** 2);para(-5.5, 5.5).draw(ctx);// 绘制阿基米德螺旋线const helical = parametric((t, l) => l * t * Math.cos(t),(t, l) => l * t * Math.sin(t));helical(0, 50, 500, 5).draw(ctx, { strokeStyle: "MediumPurple" });// 绘制星形线const star = parametric((t, l) => l * Math.cos(t) ** 3,(t, l) => l * Math.sin(t) ** 3);star(0, Math.PI * 2, 50, 150).draw(ctx, { strokeStyle: "Orange" });</script></body>
</html>

4. 画贝塞尔曲线

贝塞尔曲线是一种使用数学方法描述的曲线,被广泛用于计算机图形学和动画中。在矢量图中,贝塞尔曲线用于定义可无限放大的光滑曲线。可以用来构建 Catmull–Rom 曲线。

贝塞尔曲线又分为二阶贝塞尔曲线(Quadratic Bezier Curve)和三阶贝塞尔曲线(Qubic Bezier Curve)。

二阶贝塞尔曲线

二阶贝塞尔曲线由三个点确定,P0是起点,P1是控制点,P2是终点


二阶贝塞尔曲线的原理示意图

绘制30条从圆心出发,旋转不同角度的二阶贝塞尔曲线

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>二阶贝塞尔曲线</title><style>canvas {border: 1px dashed salmon;}</style></head><body><canvas width="512" height="512"></canvas><script type="module">import { parametric } from "./common/lib/parametric.js";import { Vector2D } from "./common/lib/vector2d.js";const canvas = document.querySelector("canvas");const ctx = canvas.getContext("2d");const { width, height } = canvas;const w = 0.5 * width,h = 0.5 * height;ctx.translate(w, h);ctx.scale(1, -1);// 绘制坐标轴function drawAxis() {ctx.save();ctx.strokeStyle = "#ccc";ctx.beginPath();ctx.moveTo(-w, 0);ctx.lineTo(w, 0);ctx.stroke();ctx.beginPath();ctx.moveTo(0, -h);ctx.lineTo(0, h);ctx.stroke();ctx.restore();}drawAxis();const quadricBezier = parametric((t, [{ x: x0 }, { x: x1 }, { x: x2 }]) => (1 - t) ** 2 * x0 + 2 * t * (1 - t) * x1 + t ** 2 * x2,(t, [{ y: y0 }, { y: y1 }, { y: y2 }]) => (1 - t) ** 2 * y0 + 2 * t * (1 - t) * y1 + t ** 2 * y2);const p0 = new Vector2D(0, 0);const p1 = new Vector2D(100, 0);p1.rotate(0.75);const p2 = new Vector2D(200, 0);const count = 30;for (let i = 0; i < count; i++) {// 绘制30条从圆心出发,旋转不同角度的二阶贝塞尔曲线p1.rotate((2 / count) * Math.PI);p2.rotate((2 / count) * Math.PI);quadricBezier(0, 1, 100, [p0, p1, p2]).draw(ctx);}</script></body>
</html>

效果如下

三阶贝塞尔曲线

三阶贝塞尔曲线的参数方程为:

三阶贝塞尔曲线的原理示意图:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>三阶贝塞尔曲线</title><style>canvas {border: 1px dashed salmon;}</style></head><body><canvas width="512" height="512"></canvas><script type="module">import { parametric } from "./common/lib/parametric.js";import { Vector2D } from "./common/lib/vector2d.js";const canvas = document.querySelector("canvas");const ctx = canvas.getContext("2d");const { width, height } = canvas;const w = 0.5 * width,h = 0.5 * height;ctx.translate(w, h);ctx.scale(1, -1);// 绘制坐标轴function drawAxis() {ctx.save();ctx.strokeStyle = "#ccc";ctx.beginPath();ctx.moveTo(-w, 0);ctx.lineTo(w, 0);ctx.stroke();ctx.beginPath();ctx.moveTo(0, -h);ctx.lineTo(0, h);ctx.stroke();ctx.restore();}drawAxis();const cubicBezier = parametric((t, [{ x: x0 }, { x: x1 }, { x: x2 }, { x: x3 }]) =>(1 - t) ** 3 * x0 +3 * t * (1 - t) ** 2 * x1 +3 * (1 - t) * t ** 2 * x2 +t ** 3 * x3,(t, [{ y: y0 }, { y: y1 }, { y: y2 }, { y: y3 }]) =>(1 - t) ** 3 * y0 +3 * t * (1 - t) ** 2 * y1 +3 * (1 - t) * t ** 2 * y2 +t ** 3 * y3);const p0 = new Vector2D(0, 0);const p1 = new Vector2D(100, 0);p1.rotate(0.75);const p2 = new Vector2D(150, 0);p2.rotate(-0.75);const p3 = new Vector2D(200, 0);const count = 30;for (let i = 0; i < count; i++) {p1.rotate((2 / count) * Math.PI);p2.rotate((2 / count) * Math.PI);p3.rotate((2 / count) * Math.PI);cubicBezier(0, 1, 100, [p0, p1, p2, p3]).draw(ctx);}</script></body>
</html>

【数学篇】07 # 如何用向量和参数方程描述曲线?相关推荐

  1. 【数学篇】05 # 如何用向量和坐标系描述点和线段?

    说明 [跟月影学可视化]学习笔记. 坐标系与坐标映射 HTML:采用的是窗口坐标系,以参考对象(参考对象通常是最接近图形元素的 position 非 static 的元素)的元素盒子左上角为坐标原点, ...

  2. 再寄小读者之数学篇[2014.07.01-2014.12.31]

    [再寄小读者之数学篇](2014-12-24 乘积型不等式) [再寄小读者之数学篇](2014-12-04 $\left(1+\frac{1}{x}\right)^x>\frac{2ex}{2x ...

  3. matlab用regress方法求ln函数_数学篇|高中数学48条秒杀型公式与方法,一定要掌握!...

    「 致于学教育 」 高中数学48条秒杀型公式 1.适用条件:[直线过焦点],必有ecosA=(x-1)/(x+1),其中A为直线与焦点所在轴夹角,是锐角.x为分离比,必须大于1. 注上述公式适合一切圆 ...

  4. OD使用教程7(上)- 调试篇07|解密系列

    OD使用教程7(上)- 调试篇07 让编程改变世界 Change the world by program   小甲鱼觉得,掌握逆向的思维尤为重要. 所以在咱的OD使用教程中,不单会告诉你怎么去逆向这 ...

  5. 《(数学篇)》 复数运算

    [译文连载]<(数学篇)>--第六章 复数运算 虚数有一个直观化的解释:它把数字"旋转",就像负数把数字做了"镜像"一样.这种深刻的见解使得我们理解 ...

  6. 《北大学科》第一季:数学篇

    1913年,北京大学首开中国数学高等教育先河.蔡元培校长曾说:"大学宗旨凡治哲学文学应用科学者,都要从纯粹科学入手,治纯粹科学者,都要从数学入手.所以各系秩序,列数学系为第一系." ...

  7. C/C++基础讲解(二十六)之数值计算与趣味数学篇(打鱼还是晒网与怎样存钱以获取最大利息)

    C/C++基础讲解(二十六)之数值计算与趣味数学篇(打鱼还是晒网与怎样存钱以获取最大利息) 程序之美 前言 很多时候,特别是刚步入大学的学子们,对于刚刚开展的计算机课程基本上是一团迷雾,想要弄明白其中 ...

  8. 高中数学一对一辅导如何用半年时间数学从60分到130分逆袭诀窍

    距离2019年高考的真的只剩下半年时间了,想要快速逆袭提高数学成绩,如何用半年时间数学从60分,到130分那么一定要掌握科学的数学备考方法,下面肖老师为大家提供高中数学如何从60到130逆袭诀窍. 6 ...

  9. 「GitLab篇」如何用Git平台账号登录建木CI

    介绍 继上一篇博客「Gitee篇」如何用Git平台账号登录建木CI发布后,得到了很多小伙伴们的关注,我趁热打铁推出了如何用Git平台账号登录建木CI 系列的第二篇 「GitLab篇」如何用Git平台账 ...

最新文章

  1. R语言ggplot2可视化:自定义设置X轴上的时间间隔(中断、以年为单位),使用scale_x_date()自定义设置坐标轴间隔和标签、添加标题、副标题、题注信息
  2. 如何修改Web Interface4.5多语言版的版权信息
  3. 函数exit()详解:参数EXIT_FAILURE(是1),EXIT_SUCCESS(是0)
  4. F# 4.0于全平台发布
  5. 15种CSS居中的方式
  6. 012 动态调试smali代码
  7. UDP socket 设置为的非阻塞模式
  8. arcgis矢量之间的空白如何快速填充
  9. 语音识别学习日志 2019-7-17 语音识别基础知识准备6 {维特比算法(Viterbi Algorithm)}
  10. php表白情话,朋友圈唯美表白短句情话 适合发朋友圈的情话
  11. python连接influxdb_python 访问 InfluxDB 数据库
  12. 分子动力学模拟软件_机器学习模拟1亿原子:中美团队获2020「超算诺贝尔奖」戈登贝尔奖...
  13. nssm在服务器上的使用超详细说明
  14. C# 文件查询管理器
  15. php+tcpdf+表格,PHP使用tcpdf类生成PDF文件
  16. python--单例模式
  17. 2022年中式烹调师(初级)考试题库及在线模拟考试
  18. html中图片左右切换,超简单的图片左右切换滑动
  19. 32强鹏城逐战!“共筑梦想、创赢未来” 2021年绿色产业创新创业大赛深圳赛区比赛精彩上演
  20. 吴华伟(帮别人名字作诗)

热门文章

  1. ew 135 G3 森海塞尔无线话筒
  2. TFTP协议详解及TFTP穿越NAT
  3. Mac上Multitouch触控板手势使用技巧
  4. win7 制作linux u盘安装教程,win7系统下u盘安装linux centos7,详细步骤,亲测
  5. [ 汇编语言 (一) ] —— 踩着硬件的鼓点,掌握计算机的精髓
  6. 【每日一题】洛谷--P1996 约瑟夫问题 java题解
  7. gg 修改器游戏被保护_某游戏DLL保护分析,以及偷学一点Unity代码保护思路
  8. 牛客网基础知识强化巩固-周结04
  9. Vue美食节项目______显示美食空间
  10. ES6中的class类详解