问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

趣味编程:心形曲线绘制详解

创作时间:
作者:
@小白创作中心

趣味编程:心形曲线绘制详解

引用
CSDN
1.
https://m.blog.csdn.net/2401_87194328/article/details/144916236

本文将介绍如何通过编程绘制心形曲线。文章将展示完整的代码实现,并对关键部分进行详细解析,帮助读者理解其背后的数学原理和图形绘制技术。

代码展示

#define _CRT_SECURE_NO_WARNINGS
#include <graphics.h>
#include <conio.h>
#include <math.h>
#define WIDTH	640					// 窗口宽度
#define HEIGHT	480					// 窗口高度
#define PI		3.14159265			// π
#define DISPLAY 3					// 展示出来动圆与定圆的交点及心脏线当前所在点的尺寸
#define ARROW	5					// 箭头的尺寸
#define COPIES	600					// 份数,看要获得心形线上的多少个点
#define SECONDS 5					// 跑完一圈的秒数
using namespace std;

// 画坐标抽
void drawCoordinateAxis()
{
    setlinecolor(DARKGRAY);
    line(WIDTH / 2, HEIGHT / 10, WIDTH / 2, HEIGHT / 10 * 9);
    line(WIDTH / 2, HEIGHT / 10, WIDTH / 2 + ARROW, HEIGHT / 10 + ARROW);
    line(WIDTH / 2, HEIGHT / 10, WIDTH / 2 - ARROW, HEIGHT / 10 + ARROW);
    line(WIDTH / 5, HEIGHT / 2, WIDTH / 5 * 4, HEIGHT / 2);
    line(WIDTH / 5 * 4, HEIGHT / 2, WIDTH / 5 * 4 - ARROW, HEIGHT / 2 - ARROW);
    line(WIDTH / 5 * 4, HEIGHT / 2, WIDTH / 5 * 4 - ARROW, HEIGHT / 2 + ARROW);
}

int main()
{
    initgraph(WIDTH, HEIGHT);
    BeginBatchDraw();
    setlinecolor(BLUE);
    // 画坐标轴,定圆
    double r = min(WIDTH, HEIGHT) / 9;
    circle(WIDTH / 2, HEIGHT / 2, r);
    drawCoordinateAxis();
    setrop2(R2_XORPEN);
    double lastX = WIDTH / 2.0, lastY = HEIGHT / 2 - r;					// 上一个心形线的点的 x,y 值,初始值为 y 轴正方向上距原点 a 个单位长度的点
    for (double a = 0; !_kbhit(); a += PI / COPIES * 2)					// a 为当前弧度
    {
        double x = cos(3.0 / 2.0 * PI + a) * 2 * r + WIDTH / 2;			// 动圆这一个循环的圆心的 x 值
        double y = sin(3.0 / 2.0 * PI + a) * 2 * r + HEIGHT / 2;		// 动圆这一个循环的圆心的 y 值
        double FixedPoint_X = cos(PI / 2.0 + a * 2) * r + x;			// 当前循环动圆的定点对应的 x 值
        double FixedPoint_Y = sin(PI / 2.0 + a * 2) * r + y;			// 当前循环动圆的定点对应的 y 值
        double Contact_X = cos(PI / 2.0 + a) * r + x;					// 当前循环两圆切点在动圆上对应的 x 值
        double Contact_Y = sin(PI / 2.0 + a) * r + y;					// 当前循环两圆切点在动圆上对应的 y 值
        // 画出心形线,只用画这一个循环的点和上一个循环的点的线就行
        setrop2(R2_COPYPEN);
        setlinecolor(YELLOW);
        line(lastX, lastY, FixedPoint_X, FixedPoint_Y);
        setrop2(R2_XORPEN);
        lastX = FixedPoint_X;
        lastY = FixedPoint_Y;
        // 动圆与定圆的切点
        setfillcolor(GREEN);
        solidcircle(Contact_X, Contact_Y, DISPLAY);
        // 心形线当前点
        setfillcolor(LIGHTRED);
        solidcircle(lastX, lastY, DISPLAY);
        // 动圆
        setlinecolor(BLUE);
        circle(x, y, r);
        FlushBatchDraw();
        Sleep((double)(1000 * SECONDS) / (double)COPIES + 0.5);
        // 消除动圆
        setlinecolor(BLUE);
        circle(x, y, r);
        // 消除动圆与定圆的交点
        setfillcolor(GREEN);
        solidcircle(Contact_X, Contact_Y, DISPLAY);
        // 消除心形线当前所在点
        setfillcolor(LIGHTRED);
        solidcircle(lastX, lastY, DISPLAY);
    }
    _getch();
    EndBatchDraw();
    return 0;
}

代码详解

头文件包含

  • 包含了<graphic.h>头文件,这是用于图形绘制相关操作的库
  • <conio.h>头文件提供了一些控制台输入输出相关的函数,例如:_kbit_getch等,用于检测键盘输入以及获取字符等操作。
  • <math.h>头文件包含数学相关的函数,像 sin ,cos 等三角函数在这里都会被用到。

绘制坐标轴函数

// 画坐标抽
void drawCoordinateAxis()
{
    setlinecolor(DARKGRAY);
    line(WIDTH / 2, HEIGHT / 10, WIDTH / 2, HEIGHT / 10 * 9);
    line(WIDTH / 2, HEIGHT / 10, WIDTH / 2 + ARROW, HEIGHT / 10 + ARROW);
    line(WIDTH / 2, HEIGHT / 10, WIDTH / 2 - ARROW, HEIGHT / 10 + ARROW);
    line(WIDTH / 5, HEIGHT / 2, WIDTH / 5 * 4, HEIGHT / 2);
    line(WIDTH / 5 * 4, HEIGHT / 2, WIDTH / 5 * 4 - ARROW, HEIGHT / 2 - ARROW);
    line(WIDTH / 5 * 4, HEIGHT / 2, WIDTH / 5 * 4 - ARROW, HEIGHT / 2 + ARROW);
}

这个函数的功能是绘制坐标轴。首先通过setlinecolor(DARKGRAY)设置线条颜色为深灰色,然后绘制纵轴:

  • 从窗口高度的十分之一位置到十分之九位置绘制一条垂直的直线作为纵轴主体(line(WIDTH)/ 2,HEIGHT / 10,HEIGHT / 10 * 9)
  • 接着在纵轴顶端绘制向上和向下的箭头,通过两条斜线来实现

在绘制横轴:

  • 从窗口的五分之一位置到五分之四位置绘制一条水平的直线作为横轴主体。(line(WIDTH / 5,HEIGHT / 2,WIDTH / 5 *4,HEIGHT / 2))
  • 最后在横轴有段绘制向左或向右的箭头

main 函数主体部分

  • 初始化图形窗口与相关设置
int main()
{
    initgraph(WIDTH, HEIGHT);
    BeginBatchDraw();
    setlinecolor(BLUE);

首先通过initgraph(WIDTH,HIGHT)初始化一个指定宽度和高度(由前面宏定义WIDTHHEIGHT确定)。接着调用BeginBatchDraw()开始批量绘图模式,这种模式可以减少图形绘制过程中的闪烁现象,提高显示效果。追后通过setlinecolor(BLUE)设置后续绘制线条的颜色为蓝色。

  • 设置绘图模式与初始化相关变量
    setrop2(R2_XORPEN);
    double lastX = WIDTH / 2.0, lastY = HEIGHT / 2 - r;                  // 上一个心形线的点的 x,y 值,初始值为 y 轴正方向上距原点 a 个单位长度的点

通过setrop2(R2_XORPEN)设置绘图的光栅操作模式后为异或模式(XOR),这种模式在后续绘制图形是可以方便地实现图形的叠加和擦除效果。然后初始化lastXlastY,它们用于记录上一个绘制的心形线点的坐标,初始值设定为在 y 轴正方向上距离原点定圆半径 r 长度的点坐标(也就是心形线起始点在 y轴 正半轴上的情况)。

  • 循环绘制心形线及相关图形
    for (double a = 0;!_kbhit(); a += PI / COPIES * 2)                  // a 为当前弧度
    {
        double x = cos(3.0 / 2.0 * PI + a) * 2 * r + WIDTH / 2;          // 动圆这一个循环的圆心的 x 值
        double y = sin(3.0 / 2.0 * PI + a) * 2 * r + HEIGHT / 2;          // 动圆这一个循环的圆心的 y 值
        double FixedPoint_X = cos(PI / 2.0 + a * 2) * r + x;              // 当前循环动圆的定点对应的 x 值
        double FixedPoint_Y = sin(PI / 2.0 + a * 2) * r + y;              // 当前循环动圆的定点对应的 y 值
        double Contact_X = cos(PI / 2.0 + a) * r + x;                    // 当前循环两圆切点在动圆上对应的 x 值
        double Contact_Y = sin(PI / 2.0 + a) * r + y;                    // 当前循环两圆切点在动圆上对应的 y 值

这是一个关键的循环,循环条件是!_kbhit(),即只要没有键盘按键按下就一直循环,每次循环中a(代表弧度)按一定的增量(PI / COPIES * 2,也就是将整个圆周按照COPIES份进行细分,每次增加对应的弧度值)增加。

在循环内:

  • 首先根据当前弧度a计算动圆在这一时刻的圆心坐标(x, y),这里利用三角函数结合定圆半径r以及窗口中心坐标来计算,动圆的圆心运动轨迹是围绕着一个特定的路径(从代码中的三角函数表达式可以看出是符合一定规律的圆形轨迹,且与最终要绘制的心形线相关)。
  • 接着计算当前循环下动圆的定点坐标(FixedPoint_X, FixedPoint_Y),这个定点是与绘制心形线相关的一个关键位置点,通过特定的三角函数关系结合动圆圆心坐标和定圆半径计算得出。
  • 然后计算当前循环两圆切点在动圆上对应的坐标(Contact_X, Contact_Y),同样是基于三角函数、动圆圆心坐标以及定圆半径来确定。
        // 画出心形线,只用画这一个循环的点和上一个循环的点的线就行
        setrop2(R2_COPYPEN);
        setlinecolor(YELLOW);
        line(lastX, lastY, FixedPoint_X, FixedPoint_Y);
        setrop2(R2_XORPEN);
        lastX = FixedPoint_X;
        lastY = FixedPoint_Y;

先将绘图模式切换为R2_COPYPEN(正常的绘制模式,直接绘制图形),设置线条颜色为黄色,然后通过line函数绘制从上一个心形线点(坐标(lastX, lastY))到当前循环计算出的心形线点(坐标(FixedPoint_X, FixedPoint_Y))的线段,这样逐步绘制线段就可以呈现出心形线的形状。绘制完后再切换回R2_XORPEN模式,并且更新lastXlastY为当前绘制的心形线点坐标,用于下一次循环绘制时作为上一个点的坐标。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号