笛卡尔函数——笛卡尔心型函数

本文讲述了笛卡尔与瑞典公主克里斯汀的爱情故事,通过心形线的数学方程,展现了数学与情感的交融。同时,展示了如何使用HTML和JavaScript动态绘制不同风格的心形线图形。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这次来看一个数学知识点:笛卡尔心型函数

先贴出网上搜来的 心形线的平面直角坐标系方程表达式(反正我也不会手画这个)
分别为 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 坐标交换下,又会改变方向,所以会有两个方程表达式。

  1650年,斯德哥尔摩的街头,52岁的笛卡尔邂逅了18岁的瑞典公主克里斯汀。那时,落魄、一文不名的笛卡尔过着乞讨的生活,全部的财产只有身上穿的破破烂烂的衣服和随身所带的几本数学书籍。生性清高的笛卡尔从来不开口请求路人施舍,他只是默默地低头在纸上写写画画,潜心于他的数学世界。

其实,这个方程式的背后是一个悲伤的故事:(慢慢看,看完了再去看代码)

  一个宁静的午后,笛卡尔照例坐在街头,沐浴在阳光中研究数学问题。他如此沉溺于数学世界,身边过往的人群,喧闹的车马队伍。都无法对他造成干扰。

  突然,有人来到他旁边,拍了拍他的肩膀,“你在干什么呢?”扭过头,笛卡尔看到一张年轻秀丽的睑庞,一双清澈的眼睛如湛蓝的湖水,楚楚动人,长长的睫毛一眨一眨的,期待着他的回应。她就是瑞典的小公主,国王最宠爱的女儿克里斯汀。

  她蹲下身,拿过笛卡尔的数学书和草稿纸,和他交谈起来。言谈中,他发现,这个小女孩思维敏捷,对数学有着浓厚的兴趣。

  和女孩道别后,笛卡尔渐渐忘却了这件事,依旧每天坐在街头写写画画。

  几天后,他意外地接到通知,国王聘请他做小公主的数学老师。满心疑惑的笛卡尔跟随前来通知的侍卫一起来到皇宫,在会客厅等候的时候,他听到了从远处传来的银铃般的笑声。转过身,他看到了前几天在街头偶遇的女孩子。慌忙中,他赶紧低头行礼。

  从此,他当上了公主的数学老师。

  公主的数学在笛卡尔的悉心指导下突飞猛进,他们之间也开始变得亲密起来。笛卡尔向她介绍了他研究的新领域———直角坐标系。通过它,代数与几何可以结合起来,也就是日后笛卡尔创立的解析几何学的雏形。

  在笛卡尔的带领下,克里斯汀走进了奇妙的坐标世界,她对曲线着了迷。每天的形影不离也使他们彼此产生了爱慕之心。

  在瑞典这个浪漫的国度里,一段纯粹、美好的爱情悄然萌发。

  然而,没过多久,他们的恋情传到了国王的耳朵里。国王大怒,下令马上将笛卡尔处死。在克里斯汀的苦苦哀求下,国王将他放逐回国,公主被软禁在宫中。

  当时,欧洲大陆正在流行黑死病。身体孱弱的笛卡尔回到法国后不久,便染上重病。

  在生命进入倒计时的那段日子,他日夜思念的还是街头偶遇的那张温暖的笑脸。他每天坚持给她写信,盼望着她的回音。然而,这些信都被国王拦截下来,公主一直没有收到他的任何消息。

  在笛卡尔给克里斯汀寄出第十三封信后,他永远地离开了这个世界。此时,被软禁在宫中的小公主依然徘徊在皇宫的走廊里,思念着远方的情人。

  这最后一封信上没有写一句话,只有一个方程:r=a(1-sinθ)。

  国王看不懂,以为这个方程里隐藏着两个人不可告人的秘密,便把全城的数学家召集到皇宫,但是没有人能解开这个函数式。他不忍看着心爱的女儿每天闷闷不乐,便把这封信给了她。

  拿到信的克里斯汀欣喜若狂,她立即明白了恋人的意图,找来纸和笔,着手把方程图形画了出来,一颗心形图案出现在眼前,克里斯汀不禁流下感动的泪水,这条曲线就是著名的“心形线”。

好了,看完了吧(收起你的泪水,蟹蟹),那我们现在来看如何用html来实现一个笛卡尔新型函数的图像。

<!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>
这是用这段代码实现的图像

 可能你会觉得这样的心形并不好看,所以我们还有另一种画法:

<!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>
这效果还挺不错的,对吧

 也许……我们可以把他变得更好看一些:

(这个代码的折线是随机生成的)

 代码如下:

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

    <script>
        function heart() {
      var canvas = document.querySelector('canvas');
      var context = canvas.getContext('2d');
      // 画布的宽度
      var width = parseInt(window.getComputedStyle(canvas).width);
      // 画布的高度
      var height = parseInt(window.getComputedStyle(canvas).height);
      // 线开始的颜色
      var startColor = 'rgba(255,255,255,0.7)';
      // 线结束的颜色
      var endColor = 'rgba(0,0,255,1)';
      // 线阴影的颜色
      var shadowColor = 'rgba(255,255,255,0.5)';
      // 保存所有点的坐标的数组
      var pointArr = [];
      // 设置 画布的宽度
      canvas.width = width;
      // 设置 画布的高度
      canvas.height = height;
      context.lineWidth = 3;
      // 将画布的原点(0,0),移动到中间
      // 移动原点是为了能让整个心形显示出来
      context.translate(width / 2, height / 2);

      // 得到心形线上的所有点的坐标的函数
      function heartArr() {
        // t 代表弧度
        var t = Math.PI + 0.5;
        // maxt 代表 t 的最大值
        var maxt = 2 * Math.PI - 1;
        // vt 代表t的增量
        var vt = 0.1;
        // 需要循环的次数
        var maxi = Math.ceil(maxt / vt);
        // x 用来暂时保存每次循环得到的 x 坐标
        var x = 0;
        // y 用来暂时保存每次循环得到的 y 坐标
        var y = 0;
        // 控制心形大小
        var size = 10;
        for (var i = 0; i <= maxi; i++) {
          // x=16 * (sin(t)) ^ 3;
          x = 16 * Math.pow(Math.sin(t), 3);
          // y=13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)
          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]);
        }
      }

      // lineArr 暂时保存折线上的点的坐标
      var lineArr = [];
      // brokenLine 函数用来得到折线上所有点的坐标
      // x 表示折线上的点 每次改变的x坐标
      // maxX 表示 x 的最大值
      // y 表示 折线的 y坐标
      // startX 和 endX 表示会在这两个数范围内产生折线
      function brokenLine(x, maxX, y, startX, endX) {
        if (x > maxX) {
          return;
        }

        if (x > startX && x < endX) {
          if (Math.random() > 0.8) {
            lineArr.push([x, randomNum(y + 60, y - 90)]);
          } else {
            lineArr.push([x, y]);
          }
        } else {
          lineArr.push([x, y]);
        }

        x += randomNum(5, 10);
        brokenLine(x, maxX, y, startX, endX);
      }

      // 返回 from 和 to 之间的一个随机数
      function randomNum(from, to) {
        var Range = to - from;
        var num = from + Math.round(Math.random() * Range);
        return num;
      }


      // 获取所有点的坐标,包括心形线 和 折线
      function getpointArr() {
        // 得到心形线上的所有点的坐标
        heartArr();

        // 获取左边线折线的坐标
        var x = -width / 2;
        var maxX = pointArr[0][0];
        var y = pointArr[0][1];
        var startX = maxX - (maxX - x) * 0.8;
        var endX = maxX - (maxX - x) * 0.2;
        brokenLine(x, maxX, y, startX, endX);
        pointArr.unshift(...lineArr);

        // 获取右边线折线的坐标
        x = pointArr[pointArr.length - 1][0];
        maxX = width / 2;
        y = pointArr[pointArr.length - 1][1];
        startX = maxX - (maxX - x) * 0.8;
        endX = maxX - (maxX - x) * 0.2;
        lineArr = [];
        brokenLine(x, maxX, y, startX, endX);
        pointArr.push(...lineArr);
      }
      getpointArr();

      var index = 0;
      var index2 = 0;
      // 控制两种颜色的间隔,值越大,第一种颜色越明显
      var interval = 3;
      // 画一条线的函数
      function drawLine(index) {
        var x1 = pointArr[index][0];
        var y1 = pointArr[index][1];
        var x2 = pointArr[index + 1][0];
        var y2 = pointArr[index + 1][1];
        context.moveTo(x1, y1);
        context.lineTo(x2, y2);
        context.stroke();
      }
      // 根据点的坐标,画出心形线,产生动画效果
      function draw() {
        /* index2 > pointArr.length - 2 + interval 时
        说明两种颜色的线都画完了,结束函数
        */
        if (index2 > pointArr.length - 2 + interval) {
          return;
        }
        context.lineJoin = "round";
        context.lineCap = "round";
        context.shadowBlur = 20;
        context.shadowColor = shadowColor;

        // 当index2 >= interval 时开始画第二种颜色的线
        if (index2 >= interval) {
          context.beginPath();
          context.strokeStyle = endColor;
          drawLine(index2 - interval);
        }

        /*
        index > pointArr.length - 2 时
        说明第一种颜色的线已经画完了,
        之后只画第二种颜色的线
        */
        if (index > pointArr.length - 2) {
          index2++;
          window.requestAnimationFrame(draw);
          return;
        }

        context.beginPath();
        context.strokeStyle = startColor;
        drawLine(index);

        index++;
        index2++;
        window.requestAnimationFrame(draw);
      }
      window.requestAnimationFrame(draw);
    }
    heart();
    </script>
 </body>
</html>

 有点长,但是效果非常好(是动态的,但我不怎么会打动图,所以你们自己去看)

这里给大家推荐一个可以画函数图像的网站:点这里直接跳转

网页截图

(这个网站会显示404,但其实你直接使用中间的那个小网页就行了)

好了,这就是今天的所有内容,记得带点个关注和咱哟~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值