C語言實(shí)現(xiàn)繪制LoveBeat愛心曲線的示例代碼
心形曲線
給出心形曲線參數(shù)方程如下:
x = sin^3(θ)
y = (13 * cos(θ) - 5 * cos(2θ) - 3 * cos(3θ) - cos(4θ)) / 16
對(duì) x, y 同時(shí)乘以半徑 R,即可對(duì)其放大
通過上述方程可得到若干在曲線上的點(diǎn),記為集合 S
曲線內(nèi)點(diǎn)的生成
對(duì)于內(nèi)部的點(diǎn),我們則將S向內(nèi)擴(kuò)散,使其符合指數(shù)分布,得到S'
令 e ∈ [m, n] 且 e ~ E(λ)
對(duì) P (x, y) ∈ S 作向內(nèi)擴(kuò)散得到點(diǎn) P' ∈ S':
P' = (x, y) * e
擴(kuò)散程度取決于參數(shù) m, n, λ
曲線外點(diǎn)的生成
對(duì)于外部的點(diǎn),我們則將S向外擴(kuò)散,使其符合均勻分布,得到S''
令 u ~ U [1, 1 + b]
對(duì) P (x, y) ∈ S 作向外擴(kuò)散得到點(diǎn) P'' ∈ S'':
P'' = (x, y) * u
擴(kuò)散程度取決于參數(shù) b
制作動(dòng)畫
有了上述三個(gè)點(diǎn)集合后,就可以描述一個(gè)靜態(tài)的心形圖案
現(xiàn)在,我們?yōu)槠涮砑又芷趧?dòng)畫效果
首先引入時(shí)間參數(shù) t 和周期函數(shù) T(t) = sin^2(t) (可以是其他周期函數(shù),視效果而定)
對(duì)于 P ∈ S U S'
其周期縮放程度與其到原點(diǎn)的距離 d 成反比,可用 R/d 來衡量 (R為心形曲線的半徑)
為其添加階數(shù) i 來擴(kuò)大距離對(duì)縮放程度的影響 (R/d)^i
我們可以得到如下函數(shù)
d' = d * (1 + a * T(t) * (R/d)^i)
外部的點(diǎn)在內(nèi)部點(diǎn)收縮到最小值時(shí),到達(dá)最大值,所以和上式相差一個(gè)相位
我們可以還做一些額外的處理:
- 對(duì)內(nèi)部點(diǎn)和外部點(diǎn)添加隨機(jī)擾動(dòng)
- 繪制時(shí),隨機(jī)點(diǎn)的大小
- 繪制時(shí),隨機(jī)點(diǎn)的顏色、亮度
示例代碼
// 環(huán)境: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; }
到此這篇關(guān)于C語言實(shí)現(xiàn)繪制LoveBeat愛心曲線的示例代碼的文章就介紹到這了,更多相關(guān)C語言繪制愛心曲線內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Unity3D實(shí)現(xiàn)經(jīng)典小游戲Pacman
這篇文章主要介紹了基于Unity3D制作一做個(gè)經(jīng)典小游戲Pacman,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Unity3D有一定的幫助,感興趣的小伙伴可以了解一下2021-12-12C++使用TinyXML2實(shí)現(xiàn)解析和生成XML數(shù)據(jù)
TinyXML2是一個(gè)輕量級(jí)的、開源的C++庫,專門用于解析和生成XML文檔,本文主要為大家介紹了如何使用TinyXML2實(shí)現(xiàn)解析和生成XML數(shù)據(jù),需要的可以參考下2024-04-04C語言使用sizeof和strlen計(jì)算數(shù)組和指針大小
sizeof()一般是用來求取?變量?或者?類型?所占內(nèi)存空間的大小,strlen()是一個(gè)庫函數(shù)是專門用來計(jì)算?字符串?長度的,下面我們就來看看C語言如何使用sizeof和strlen計(jì)算數(shù)組和指針大小吧2023-11-11C語言實(shí)現(xiàn)strlen的三種方法小結(jié)
本文主要介紹了C語言實(shí)現(xiàn)strlen的三種方法小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06C++中l(wèi)ist的使用與模擬實(shí)現(xiàn)
list相較于vector來說會(huì)顯得復(fù)雜,它的好處是在任意位置插入,刪除都是一個(gè)O(1)的時(shí)間復(fù)雜度,下面這篇文章主要給大家介紹了關(guān)于C++中l(wèi)ist的使用與模擬實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2022-05-05C++獲取文件哈希值(hash)和獲取torrent(bt種子)磁力鏈接哈希值
這二個(gè)代碼一個(gè)是獲取文件哈希值的,另外一個(gè)是獲取torrent文件磁力鏈接的哈希值2013-11-11C++中double浮點(diǎn)數(shù)精度丟失的深入分析
這篇文章主要給大家介紹了關(guān)于C++中double浮點(diǎn)數(shù)精度丟失的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01