C语言实现绘制LoveBeat爱心曲线的示例代码

 更新时间:2023年03月08日 09:59:42   作者:编程小鱼六六六  
这篇文章主要为大家详细介绍了如何溧阳C语言实现绘制LoveBeat爱心曲线,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

心形曲线

给出心形曲线参数方程如下:

x = sin^3(θ)

y = (13 * cos(θ) - 5 * cos(2θ) - 3 * cos(3θ) - cos(4θ)) / 16

对 x, y 同时乘以半径 R,即可对其放大

通过上述方程可得到若干在曲线上的点,记为集合 S

曲线内点的生成

对于内部的点,我们则将S向内扩散,使其符合指数分布,得到S'

令 e ∈ [m, n] 且 e ~ E(λ)

对 P (x, y) ∈ S 作向内扩散得到点 P' ∈ S':

P' = (x, y) * e

扩散程度取决于参数 m, n, λ

曲线外点的生成

对于外部的点,我们则将S向外扩散,使其符合均匀分布,得到S''

令 u ~ U [1, 1 + b]

对 P (x, y) ∈ S 作向外扩散得到点 P'' ∈ S'':

P'' = (x, y) * u

扩散程度取决于参数 b

制作动画

有了上述三个点集合后,就可以描述一个静态的心形图案

现在,我们为其添加周期动画效果

首先引入时间参数 t 和周期函数 T(t) = sin^2(t) (可以是其他周期函数,视效果而定)

对于 P ∈ S U S'

其周期缩放程度与其到原点的距离 d 成反比,可用 R/d 来衡量 (R为心形曲线的半径)

为其添加阶数 i 来扩大距离对缩放程度的影响 (R/d)^i

我们可以得到如下函数

d' = d * (1 + a * T(t) * (R/d)^i)

外部的点在内部点收缩到最小值时,到达最大值,所以和上式相差一个相位

我们可以还做一些额外的处理:

  • 对内部点和外部点添加随机扰动
  • 绘制时,随机点的大小
  • 绘制时,随机点的颜色、亮度

示例代码

// 环境:Visual Studio 2022 C++ 20
// EasyX版本:EasyX 2022-9-1
 
#include <random>
#include <unordered_set>
#include <graphics.h>
#include <cmath>
 
#define PINK LIGHTRED | 0x6055ff
#define LIGHTPINK LIGHTRED | 0x6655ff
#define DRAW(vecs, color) for(auto& vec : vecs)Draw(vec, color, distribution(engine))
 
using namespace std;
 
constexpr float Pi = 3.1416f;
constexpr float Rad = Pi / 180;
constexpr int ScreenWidth = 800;
constexpr int ScreenHeight = 600;
constexpr int OX = ScreenWidth / 2;
constexpr int OY = ScreenHeight / 2;
 
static default_random_engine engine;
 
struct Vec2
{
	float X = .0f;
	float Y = .0f;
 
	Vec2() {}
	Vec2(float x, float y) { X = x; Y = y; }
 
	int GetX() const { return static_cast<int>(X); }
	int GetY() const { return static_cast<int>(Y); }
	
	float Magnitude() const { return sqrt(X * X + Y * Y); }
	Vec2 operator*(float num) const{ return Vec2(X * num, Y * num); }
 
	struct VecHash
	{
		size_t operator()(const Vec2& vec) const noexcept
		{
			return std::hash<float>()(vec.X) ^ std::hash<float>()(vec.Y);
		}
	};
 
	struct VecCompare
	{
		bool operator()(const Vec2& vec1, const Vec2& vec2) const noexcept
		{
			return fabsf(vec1.X - vec2.X) < 1e-2f && fabsf(vec1.Y - vec2.Y) < 1e-2f;
		}
	};
};
using VecSet = unordered_set<Vec2, Vec2::VecHash, Vec2::VecCompare>;
 
 
float CalculateX(float t){ return powf(sin(t), 3.0f); }
float CalculateY(float t){ return -(13 * cosf(t) - 5 * cosf(2 * t) - 2 * cosf(3 * t) - cosf(4 * t)) / 16.0f; }
 
 
VecSet InitHeart(float startAngle, float endAngle, float radius, size_t count)
{
	VecSet set;
	float rad = startAngle * Rad;
	float step = (endAngle - startAngle) * Rad / count;
	float endRad = endAngle * Rad;
 
	for (; rad < endRad; rad += step)
        set.insert(Vec2(CalculateX(rad) * radius, CalculateY(rad) * radius));
 
	return set;
}
 
 
VecSet BuildInside(const VecSet& set, size_t frequency, float lambda, float range, float min)
{
	VecSet retSet;
	exponential_distribution<float> distribution(lambda);
 
	for (size_t i = 0; i < frequency; i++)
	{
		for (auto& vec : set)
		{
			float pX = distribution(engine);
			float scalarX = (pX < 1.0 ? 1.0f - pX : 1.0f) * range + min;
 
			float pY = distribution(engine);
			float scalarY = (pY < 1.0 ? 1.0f - pY : 1.0f) * range + min;
 
			retSet.insert(Vec2(vec.X * scalarX, vec.Y * scalarY));
		}
	}
 
	return retSet;
}
 
 
VecSet BuildOutside(const VecSet& set, size_t frequency, float max)
{
	VecSet retSet;
	uniform_real_distribution<float> distribution(1.0f, max);
 
	for (size_t i = 0; i < frequency; i++)
	{
		for (auto& vec : set)
            retSet.insert(Vec2(vec.X * distribution(engine), vec.Y *  distribution(engine)));
	}
 
	return retSet;
}
 
 
VecSet Undulate(const VecSet& set, float radius)
{
	VecSet retSet;
	uniform_real_distribution<float> distribution(-radius, radius);
 
	for (auto& vec : set)
		retSet.insert(Vec2(vec.X + distribution(engine), vec.Y + distribution(engine)));
 
	return retSet;
}
 
 
VecSet Zoom(const VecSet& set, float factor, float radius, float t, float idx)
{
	VecSet retSet;
 
	for (auto& vec : set)
        retSet.insert(vec * (1.0f + factor * sin(t * Pi) * powf((radius / vec.Magnitude()), idx)));
 
	return retSet;
}
 
 
void Draw(const Vec2& vec, COLORREF color, int radius)
{
	putpixel(vec.GetX() + OX, vec.GetY() + OY, color);
 
	if(radius >= 2)
		putpixel(vec.GetX() + OX + 1, vec.GetY() + OY, color);
 
	if(radius >= 3)
		putpixel(vec.GetX() + OX, vec.GetY() + OY + 1, color);
}
	
 
int main()
{
	float radius = 160.0f;
	auto border = InitHeart(0, 360, radius, 480);
	auto inside = BuildInside(border, 30, 5.0f, 0.85f, 0.15f);
 
	initgraph(ScreenWidth, ScreenHeight);
	BeginBatchDraw();
 
	float t = .0f;
	float tStep = 0.05f;
	uniform_int_distribution<int> distribution(1, 3);
	ExMessage msg{};
 
	while(!peekmessage(&msg, EX_KEY))
	{
		auto ps1 = Zoom(border, 0.1f, radius, t, 1.3f);
		auto ps2 = Undulate(Zoom(inside, 0.1f, radius, t, 1.3f), 3.0f);
		auto ps3 = Undulate(BuildOutside(border, 10 - static_cast<size_t>(sin(t) * 5), 1.35f - sin(t) * 0.15f), 3.0f);
 
		DRAW(ps1, LIGHTPINK);
		DRAW(ps2, LIGHTPINK);
		DRAW(ps3, PINK);
 
		FlushBatchDraw();
		Sleep(40);
 
		t += tStep;
		if (t > 1.0f)
			t = .0f;
 
		cleardevice();
	}
 
	EndBatchDraw();
	closegraph();
 
	return 0;
}

到此这篇关于C语言实现绘制LoveBeat爱心曲线的示例代码的文章就介绍到这了,更多相关C语言绘制爱心曲线内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Unity3D实现经典小游戏Pacman

    Unity3D实现经典小游戏Pacman

    这篇文章主要介绍了基于Unity3D制作一做个经典小游戏Pacman,文中的示例代码讲解详细,对我们学习Unity3D有一定的帮助,感兴趣的小伙伴可以了解一下
    2021-12-12
  • C++中全局变量的初始化全过程

    C++中全局变量的初始化全过程

    这篇文章主要介绍了C++全局变量的初始化全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • C++使用TinyXML2实现解析和生成XML数据

    C++使用TinyXML2实现解析和生成XML数据

    TinyXML2是一个轻量级的、开源的C++库,专门用于解析和生成XML文档,本文主要为大家介绍了如何使用TinyXML2实现解析和生成XML数据,需要的可以参考下
    2024-04-04
  • C语言使用sizeof和strlen计算数组和指针大小

    C语言使用sizeof和strlen计算数组和指针大小

    sizeof()一般是用来求取 变量 或者 类型 所占内存空间的大小,strlen()是一个库函数是专门用来计算 字符串 长度的,下面我们就来看看C语言如何使用sizeof和strlen计算数组和指针大小吧
    2023-11-11
  • C++两种素数判定方法

    C++两种素数判定方法

    这篇文章主要介绍了C++如何判断一个数是不是素数,提供了两种方法具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • C语言实现停车管理系统

    C语言实现停车管理系统

    这篇文章主要为大家详细介绍了C语言实现停车管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C语言实现strlen的三种方法小结

    C语言实现strlen的三种方法小结

    本文主要介绍了C语言实现strlen的三种方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • C++中list的使用与模拟实现

    C++中list的使用与模拟实现

    list相较于vector来说会显得复杂,它的好处是在任意位置插入,删除都是一个O(1)的时间复杂度,下面这篇文章主要给大家介绍了关于C++中list的使用与模拟实现的相关资料,需要的朋友可以参考下
    2022-05-05
  • C++获取文件哈希值(hash)和获取torrent(bt种子)磁力链接哈希值

    C++获取文件哈希值(hash)和获取torrent(bt种子)磁力链接哈希值

    这二个代码一个是获取文件哈希值的,另外一个是获取torrent文件磁力链接的哈希值
    2013-11-11
  • C++中double浮点数精度丢失的深入分析

    C++中double浮点数精度丢失的深入分析

    这篇文章主要给大家介绍了关于C++中double浮点数精度丢失的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01

最新评论