一个浪漫又悲情的爱情故事——笛卡尔心形线

说明

写这篇文章是因为某天看到这样一个公式 r=a(1-cosθ) ,我上网搜了下,原来是笛卡尔心形线的极坐标方程,这个方程里面的确有一个浪漫又悲情的爱情故事,感兴趣的朋友可以点这里看看,而至于这个故事是真是假,这 并不重要。

而这篇文章的目的是要用前端的方式,画出笛卡尔心形线。
本来我想,这么经典的公式,网上应该已经有人实现过了吧。
我搜了搜,不得不佩服网友们,有 Java 实现的有 C# 实现的也有 canvas 实现的还能用 ECharts 画 ,可以学习学习。

好的,开始正文!
先来了解下心形线

心形线,是一个圆上的固定一点在它绕着与其相切且半径相同的另外一个圆周滚动时所形成的轨迹,因其形状像心形而得名。

这里写图片描述

因为 canvas 是直角坐标系的,所以先来看

平面直角坐标系 画法

先贴出网上搜来的 心形线的平面直角坐标系方程表达式
分别为 x^2+y^2+a*x=a*sqrt(x^2+y^2)x^2+y^2-a*x=a*sqrt(x^2+y^2

为什么会有两个方程表达式?
因为心形线的水平方向 和 垂直方向 对应的方程表达式不同,而用相同的方程表达式画的心形线,把每个点的 x 坐标和 y 坐标交换下,又会改变方向,所以会有两个方程表达式。
这里写图片描述

好了,开始画吧,看看这位朋友的做法
思路
根据方程表达式得到所有点的坐标,然后把每个点连接起来,然后填充,最后就行成一个心形了。

参数方程

x=a*(2*sin(t)+sin(2*t))
y=a*(2*cos(t)+cos(2*t))

x,y 分别表示一个点的 x 坐标 和 y 坐标,
a:是一个常数,用来控制心形的大小,
t :代表 弧度
t 的取值范围:-pi<=t<=pi 或 0<=t<=2*pi

代码

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
    <canvas width="400" height="400"></canvas>

    <script>
        var canvas = document.querySelector('canvas');
        var context = canvas.getContext('2d');  
        context.lineWidth = 3;
        // 将画布的原点(0,0),移动到(200,200)
        // 移动原点是为了能让整个心形显示出来
        context.translate(200,200); 

        // t 代表弧度
        var t=0;
        // maxt 代表 t 的最大值
        var maxt = 2*Math.PI;
        // vt 代表 t 的增量
        var vt = 0.01;
        // 需要循环的次数
        var maxi = Math.ceil(maxt/vt);
        // 保存所有点的坐标的数组
        var pointArr=[];
        // x 用来暂时保存每次循环得到的 x 坐标
        var x=0;
        // y 用来暂时保存每次循环得到的 y 坐标
        var y=0;

        // 根据方程得到所有点的坐标
        for(var i=0;i<=maxi;i++){
            // x=a*(2*sin(t)+sin(2*t))
            x=50*(2*Math.sin(t)+Math.sin(2*t));

            // y=a*(2*cos(t)+cos(2*t))
            y=50*(2*Math.cos(t)+Math.cos(2*t));
            t+=vt;
            pointArr.push([x,y]); 
        }

        // 根据点的坐标,画出心形线
		context.moveTo(pointArr[0][0],pointArr[0][1]);
        draw();
        function draw(){
            context.fillStyle='#c00';
            // 把每个点连接起来
            for(var i=1;i<pointArr.length;i++){
                x = pointArr[i][0];
                y = pointArr[i][1];
                context.lineTo(x,y);
            }
            context.fill();
        }
    </script>
 </body>
</html>

效果图
这里写图片描述

平面直角坐标系 画法 (空心心形)

上面的代码是画一个实心的心形,当然我们也可以画空心的,只需要做出一点点的修改就可以。
我们只需要改改 draw() 函数就好,把原来的 fill() 方法,改为 stroke() 方法,并且把 strokeStyle 设置了颜色就行了。

function draw(){
    //context.fillStyle='#c00';
	context.strokeStyle='#c00';
    // 把每个点连接起来
    for(var i=1;i<pointArr.length;i++){
        x = pointArr[i][0];
        y = pointArr[i][1];
        context.lineTo(x,y);
    }
    //context.fill();
	context.stroke();
}

这里写图片描述

极坐标系画法

极坐标系是这样的
这里写图片描述
极坐标系中确定一个点的位置,靠的是极点(图中点O),和 角度 来确定的。
更多关于极坐标系的知识,可以看看这里

看看这位朋友的做法
思路
根据极坐标方程 r=a(1+sinθ) ,得到 r ,以 r 作为半径,根据 r 连续的去画圆弧,画完一圈后,心形就出来了。

心形线 极坐标方程
r=a(1+sinθ)

代码

<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8">
</head>

<body>
  <canvas width="400" height="400"></canvas>

  <script>
    var canvas = document.querySelector('canvas');
    var context = canvas.getContext('2d');
    // 将画布的原点(0,0),移动到(200,100)
    // 移动原点是为了能让整个心形显示出来
    context.translate(200, 100);

	// 画心形
	draw();
    function draw() {
      // 画圆弧时,圆的半径
      var r = 0;
	  //  start 代表画弧线时的 起始角
      var start = 0;
	  //  end 代表画弧线时的 结束角
      var end = 0;
	  //  一个常数,用来控制心形的大小
      var a = 100;

      context.fillStyle = '#e21f27';
	  //连续的画圆弧
      for (var q = 0; q < 500; q++) {
        start += Math.PI * 2 / 500;
		// 当 结束角 是 Math.PI * 2 时也就是已经画了一圈了,心形就出来了
        end = start + Math.PI * 2 / 500;
		// 根据极坐标方程 r=a(1+sinθ),得到 r(半径)
        r = a * (1 + Math.sin(start)); 
		// 画弧线
        context.arc(0, 0, r, start, end, false);
      }
      context.fill();
    }
  </script>
</body>
</html>

效果图
这里写图片描述

极坐标系 画法 (空心心形)

用极坐标系 画法,画空心心形,也是一样的需要改改 draw() 函数,把原来的 fill() 方法,改为 stroke() 方法,并且把 strokeStyle 设置了颜色就行了。

function draw() {
  var r = 0;
  var start = 0;
  var end = 0;
  var a = 100;

  //context.fillStyle = '#e21f27';
  context.strokeStyle = '#e21f27';
  for (var i = 0; i < 500; i++) {
    start += Math.PI * 2 / 500;
    end = start + Math.PI * 2 / 500;
    r = a * (1 + Math.sin(start)); 
    context.arc(0, 0, r, start, end, false);
  }
  //context.fill();
  // 改用 stroke() 方法
  context.stroke();   
}

这里写图片描述

可能你会觉得这样的心形并不好看。
看看这个参数方程吧!

x=16 * (sin(t)) ^ 3;
y=13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)。

根据这个参数方程,用上面说的平面直角坐标系的画法,把代码里的方程换一下,就可以画出这样的心形。
这里写图片描述

代码

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
    <canvas width="400" height="400"></canvas>

	<script>
		var canvas = document.querySelector('canvas');
		var context = canvas.getContext('2d');  
		context.lineWidth = 3;
		// 将画布的原点(0,0),移动到(200,200)
		// 移动原点是为了能让整个心形显示出来
		context.translate(200,200); 

		// t 代表弧度
		var t=0;
		// vt 代表 t 的增量
		var vt = 0.01;
		// maxt 代表 t 的最大值
		var maxt = 2*Math.PI;
		// 需要循环的次数
		var maxi = Math.ceil(maxt/vt);
		// 保存所有点的坐标的数组
		var pointArr=[];
		// 控制心形大小
		var size = 10;
		// x 用来暂时保存每次循环得到的 x 坐标
		var x=0;
		// y 用来暂时保存每次循环得到的 y 坐标
		var y=0;

		// 根据方程得到所有点的坐标
		for(var i=0;i<=maxi;i++){
			// x=16 * (sin(t)) ^ 3;
			var x = 16 * Math.pow(Math.sin(t),3);
			// y=13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)
			var y = 13 * Math.cos(t) - 5 * Math.cos(2 * t) -2 * Math.cos(3 * t)- Math.cos(4 * t);
			t+=vt;
			pointArr.push([x*size,-y*size]); 
		}

		// 根据点的坐标,画出心形线
		context.moveTo(pointArr[0][0],pointArr[0][1]);
		draw();
		function draw(){
			context.fillStyle='#c00';
			// 把每个点连接起来
			for(var i=1;i<pointArr.length;i++){
				x = pointArr[i][0];
				y = pointArr[i][1];
				context.lineTo(x,y);
			}
			context.fill();
		}
	</script>
 </body>
</html>

也许我们还可以再做点什么,比如加点动画,看看下面这个吧。
这里写图片描述
这里下载源码,里面已经加了很详细的注释了。

总结

这篇文章主要是说用笛卡尔心形线方程画心形,但是想要画出心形的方式绝对是多种多样的,单纯的用CSS也可以,复杂点 用贝塞尔曲线也能画出来,大家不妨去试试,说不定又有什么新发现呢。

  • 174
    点赞
  • 395
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
### 回答1: 要使用两个 for 循环来实现尔心形线,可以将 x 和 y 的取值范围分别拆分成两个循环,然后在内层循环中计算每个点的坐标并输出。 下面是一个示例代码,可以使用两个 for 循环来打印出尔心形线的图形: ```c #include <stdio.h> #include <math.h> int main() { int x, y, r = 16; for (y = r; y >= -r; y--) { for (x = -2 * r; x <= 2 * r; x++) { if (pow(x / 4.0, 2) + pow(y / 4.0, 2) - pow(r, 2) <= 0) { if (pow(x / 4.0, 2) + pow((y - 7 * sin(x / 4.0)) / 4.0, 2) - pow(r, 2) <= 0) { printf("*"); } else { printf(" "); } } else { printf(" "); } } printf("\n"); } return 0; } ``` 在这个示例代码中,我们使用了两个 for 循环分别遍历 y 和 x 的取值范围。在内层循环中,我们使用尔心形线的数学方程来计算出每个点的坐标,然后根据坐标和半径的关系来判断这个点是否在曲线上。如果是,我们输出一个字符表示这个点,否则输出一个空格表示这个点不在曲线上。 需要注意的是,在这个示例代码中我们使用了一个常数 r 来表示曲线的半径。同时,为了让图形显示完整,我们将 y 的取值范围从 r 到 -r,x 的取值范围从 -2r 到 2r。如果你希望调整图形的大小和位置,可以调整这些常数的值。 ### 回答2: 要实现尔心形线,可以借助两个for循环来控制曲线的生成。 首先,我们需要确定心形曲线的数学表达式。一种常用的表达式是: x = 16 * (sin(t))^3 y = 13 * cos(t) - 5 * cos(2t) - 2 * cos(3t) - cos(4t) 接下来,我们可以使用两个for循环来生成曲线上的每个点。首先,我们需要确定t的取值范围,通常取0到2π(或360度)。然后,我们可以通过设定一个步长来控制t的变化。 下面是一个使用两个for循环实现尔心形线的示例代码: ```python import math for t in range(0, 360, 1): t_rad = math.radians(t) # 将角度转换为弧度 x = 16 * (math.sin(t_rad))**3 y = 13 * math.cos(t_rad) - 5 * math.cos(2 * t_rad) - 2 * math.cos(3 * t_rad) - math.cos(4 * t_rad) print(f"({x:.2f}, {y:.2f})") ``` 上述代码中,我们使用了`math.sin()`和`math.cos()`函数来计算正弦和余弦值,`math.radians()`函数将角度转换为弧度。 循环从0到360度(或0到2π)遍历了所有角度值,计算了每一个点的坐标(x,y),并打印出来。 这样,通过两个for循环和尔心形线的数学表达式,我们就实现了尔心形线的生成。 ### 回答3: 要实现尔心形线,可以使用两个for循环来生成坐标点,然后将这些点连接在一起形成心形图案。 首先,我们需要定义一个适当的范围来生成x和y的值。假设x的范围是从-2到2,y的范围是从-3到3。这是一个经验性的范围选择,可以根据实际需要进行调整。 接下来,我们可以使用两个嵌套的for循环来遍历x和y的值。外层的循环控制x的变化,内层的循环控制y的变化。 在每次循环中,我们需要计算该点的距离心形中心的距离,并判断这个距离是否在心形的范围内。如果在范围内,就将这个点添加到心形图案中。 在每次迭代中,我们可以根据当前的x和y的值计算它们与心形中心之间的距离。可以使用勾股定理来计算距离,即sqrt(x^2 + y^2)。 然后,我们可以将这个距离与心形的形状进行比较,来判断该点是否在心形范围内。通常,心形的形状定义为1 - abs(sqrt(x^2 + y^2) - 1)。 如果计算出的距离在这个范围内,我们可以将这个点添加到心形图案中。可以使用符号*或其他字符来表示图案。 完成内外层的循环后,我们就可以得到一个用字符表示的心形图案。 总结起来,使用两个for循环可以遍历心形图案的所有可能的点,判断每个点是否在心形范围内,并将它们连接起来形成心形曲线。根据实际需求,可以调整范围和心形的形状来获得不同的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值