EGE示例程序——分形
EGE专栏:EGE专栏
EGE示例——分形
目录
- 一、分形
- 二、康托尔集(Cantor Set)
- 1. Koch雪花曲线
- 三、曼德布罗特集(Mandelbrot)
- 可局部放大的曼德布洛特集
- 四、谢尔宾斯基三角形(Sierpinski triangle)
- 1. 迭代法
- 2. 随机法
一、分形
可以看看知乎这篇关于分形的文章
https://www.zhihu.com/question/265983000/answer/301235097
二、康托尔集(Cantor Set)
1. Koch雪花曲线
Koch雪花曲线是由最开始的一个三角形,经过对边不断进行弯折吼形成的一个形似雪花的一个图形。
如下图,从单独的一条边来看,将一条边三等分,中间的一段拱起,变成原来的两倍长度,尖角为60°,这样就将一条边变形为4条小边。再对每条小边进行同样的处理,经过多次之后,就形成了最终的形态。
可以通过控制迭代次数来控制图形精度。三角形三条边进行同样的操作,就形成了雪花。
#include <graphics.h>
#include <math.h>//分别对应60度的倍数
double cosx[6], sinx[6];//koch雪花曲线
//将一条由p1到p2的线()
void koch(ege_point p1, ege_point p2, int dir, int n) {if (n <= 0) {ege_line(p1.x, p1.y, p2.x, p2.y);}else { ege_point mid1 = { p1.x + (p2.x-p1.x) / 3, p1.y + (p2.y - p1.y) / 3}; ege_point mid3 = { p2.x + (p1.x - p2.x) / 3, p2.y + (p1.y - p2.y) / 3 };float len = sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y)* (p2.y - p1.y)) / 3;ege_point mid2 = { mid1.x + len * cosx[(dir + 1) % 6], mid1.y + len * sinx[(dir + 1) % 6] };//递归绘制四条边koch(p1, mid1, dir, n - 1);koch(mid1, mid2, (dir + 1) % 6, n - 1);koch(mid2, mid3, (dir + 5) % 6, n - 1);koch(mid3, p2, dir, n - 1);}
}int main()
{initgraph(600, 600, INIT_RENDERMANUAL);setbkcolor(WHITE); //设置背景颜色setcolor(EGEACOLOR(0xFF, BLUE)); //设置线条颜色ege_enable_aa(true); //开启抗锯齿//计算60°倍角的cos和sin值for (int i = 0; i < 6; i++) {cosx[i] = cos(i * PI / 3);sinx[i] = sin(i * PI / 3);}//计算正三角形的三个顶点ege_point triangle[3] = {{300 + 200 * cos(PI / 6), 300 + 200 * sin(PI / 6)}, {300 + 200 * cos(- PI / 2), 300 + 200 * sin(- PI / 2)},{300 + 200 * cos(5 * PI / 6), 300 + 200 * sin(5 * PI / 6)},};for (int i = 0; i <= 6;delay_ms(800),i++) {cleardevice();//三角形三条边koch(triangle[0], triangle[1], 4, i);koch(triangle[1], triangle[2], 2, i);koch(triangle[2], triangle[0], 0, i);}getch();closegraph();return 0;
}
三、曼德布罗特集(Mandelbrot)
也称 “上帝的指纹”。
描述如下:
就是将窗口上的像素点映射到一个复数c\ c c, 初始z=0\ z=0 z=0, 再由公式
z=z2+cz = z^2+cz=z2+c 对zzz进行多次迭代,如果最终zzz收敛,那么该点就属于集合。
对于判别是否收敛,可以在多次迭代后,判断zzz的模是否大于某个值,若大于则为发散,这里可以取2值,则模的平方为4,判别4即可,避免开方操作。
关于窗口上的像素点坐标映射成的复数c\ c c,可以通过标准坐标系的图形来观察(即中心为(0,0),坐标轴为小数)
,然后选取部分区域,将窗口内的像素坐标映射到这个区域即可。通过改变窗口映射的区域,可以实现放大缩小。
假设窗口大小为 (width, height),对应的区域为 (fromx, tox), (fromy, toy),那么坐标(x,y)\ (x, y) (x,y)对应的复数c\ c c 公式为:
实部real=fromx+(tox−fromx)xwidth实部real\,\,=\,\,fromx\,\,+\,\,\left( tox-fromx \right) \frac{x}{width}实部real=fromx+(tox−fromx)widthx虚部imag=fromy+(toy−fromy)yheight虚部imag\,\,=\,\,fromy\,\,+\,\,\left( toy-fromy \right) \frac{y}{height}虚部imag=fromy+(toy−fromy)heighty 下面的程序则是将窗口区域映射到标准坐标系中 x范围(-2, 1), y轴范围(-1.2, 1.2) 的矩形区域。
颜色则是根据超出阈值时的迭代次数转成HSL颜色,因为迭代次数很靠近,会形成颜色变化过慢,所以进行放大,如果颜色不同,还可以通过增加值来改变色相。
这里取
HSVtoRGB((float)(((i+10) << 4) % 360), 1.0, 1.0)
如果你想后期可以改变颜色,那么只需要存储每个像素超出阈值时的迭代次数即可。
#include <graphics.h>//复数
struct Complex
{double re, im;Complex() :re(0.0), im(0.0) {}Complex(double real, double imag) : re(real), im(imag) {}//重载乘法运算符和加法运算符Complex operator * (Complex c) { return Complex(re * c.re - im * c.im, im * c.re + re * c.im);}Complex operator + (Complex c) { return Complex(re + c.re, im + c.im); }
};int main()
{const int SCR_WIDTH = 640, SCR_HEIGHT = 480;initgraph(SCR_WIDTH, SCR_HEIGHT, INIT_RENDERMANUAL); //初始化图形窗口//映射的区域范围float fromX = -2, toX = 1;float fromY = -1.2, toY = 1.2;Complex c;for (int x = 0; x < 640; x++){c.re = fromX + (toX - fromX) * ((float)x / SCR_WIDTH);for (int y = 0; y < 480; y++){c.im = fromY + (toY - fromY) * ((float)y / SCR_HEIGHT);Complex z;const int ITOR_NUM = 180;int i;for (i = 0; i < ITOR_NUM; i++){if (z.re * z.re + z.im * z.im > 4)break;z = z * z + c;}//设置颜色,这里使用HSV颜色模型,这样可以根据迭代层数,颜色渐变color_t color = (i == ITOR_NUM) ? BLACK : HSVtoRGB((float)(((i+10) << 4) % 360), 1.0, 1.0);putpixel(x, y, color);}}getch();closegraph();return 0;
}
下图映射区域: x∈(-2, 1), y∈(-1.2, 1.2)
下图映射区域: x∈(-2, 2), y∈(-2, 2)
如果你对应到很小的地方,那么会看到细节部分,当然,迭代次数需要更大,这样才能更精细。
x范围(-0.5, 0.5), y轴范围(0.6, 1.2)
对应区域即上图中最下面的一个枝条(y轴向下为正)
通过修改生成颜色公式,可以得到不同的颜色,如
HSVtoRGB((float)((i << 4) % 360), 1.0, 1.0);
可局部放大的曼德布洛特集
(修改自easyx官网分形学示例)
(鼠标左键框选区域放大显示)
由鼠标选取区域重新确定映射区域,然后重新进行计算绘制。
#include <graphics.h>const int NUM_ITER = 1000; // 迭代次数#ifndef SWAP
#define SWAP(a, b, t) {t = a; a = b; b = t;}
#endif // ! SWAPstruct Complex
{double re, im;Complex() :re(0.0), im(0.0) {}Complex(double real, double imag) : re(real), im(imag) {}//重载乘法运算符和加法运算符Complex operator * (Complex c) { return Complex(re * c.re - im * c.im, im * c.re + re * c.im); }Complex operator + (Complex c) { return Complex(re + c.re, im + c.im); }
};#define MAX_COLOR_NUM 64 // 颜色数
int Color[MAX_COLOR_NUM];// 初始化颜色
void initColdeTable()
{for (int i = 0; i < MAX_COLOR_NUM / 2; i++){Color[i] = HSLtoRGB(240, 1.0, i * 2.0 / MAX_COLOR_NUM);Color[MAX_COLOR_NUM - 1 - i] = HSLtoRGB(30, 1.0, i * 2.0 / MAX_COLOR_NUM);}
}// 绘制曼德布洛特集 (Mandelbrot Set)
void draw(double fromx, double fromy, double tox, double toy)
{Complex c;for (int x = 0; x < 640; x++){c.re = fromx + (tox - fromx) * (x / 640.0);for (int y = 0; y < 480; y++){c.im = fromy + (toy - fromy) * (y / 480.0);Complex z;int i = 0;for ( ; i < NUM_ITER; i++){if (z.re * z.re + z.im * z.im > 4.0) break;z = z * z + c;}putpixel(x, y, (i == NUM_ITER) ? 0 : Color[i % MAX_COLOR_NUM]);}}
}int main()
{initgraph(640, 480, INIT_RENDERMANUAL);//初始化颜色表initColdeTable();// 初始化 Mandelbrot Set(曼德布洛特集)坐标系范围const double INIT_FROM_X = -2, INIT_TO_X = 1;const double INIT_FROM_Y = -1.2, INIT_TO_Y = 1.2;double fromx = INIT_FROM_X, tox = INIT_TO_X;double fromy = INIT_FROM_Y, toy = INIT_TO_Y;draw(fromx, fromy, tox, toy);bool isPress = false; //鼠标左键按下标志位bool redraw = true;int areaLeft = 0, areaTop = 0, areaRight = 0, areaBottom = 0; // 定义选区while (1) {mouse_msg msg = getmouse();// 鼠标中键按下时重置图形if (msg.is_mid() && msg.is_down()) {fromx = INIT_FROM_X;tox = INIT_TO_X;fromy = INIT_FROM_Y;toy = INIT_TO_Y;redraw = true;}//鼠标左键点击,选取范围else if (msg.is_left()) {if (msg.is_down()) {isPress = true;setcolor(WHITE);setwritemode(R2_XORPEN);areaLeft = areaRight = msg.x;areaTop = areaBottom = msg.y;}else { // 鼠标左键松开时确定选区isPress = false;redraw = true;//消除选框rectangle(areaLeft, areaTop, areaRight, areaBottom);setwritemode(R2_COPYPEN);areaRight = msg.x;areaBottom = msg.y;if (areaLeft != areaRight && areaTop != areaBottom) {// 修正选区为 4:3int temp;if (areaLeft > areaRight)SWAP(areaLeft, areaRight, temp);if (areaTop > areaBottom)SWAP(areaTop, areaBottom, temp);if ((areaRight - areaLeft) * 0.75 < (areaBottom - areaTop)){areaBottom += (3 - (areaBottom - areaTop) % 3);areaLeft -= (areaBottom - areaTop) / 3 * 4 / 2 - (areaRight - areaLeft) / 2;areaRight = areaLeft + (areaBottom - areaTop) / 3 * 4;}else{areaRight += (4 - (areaRight - areaLeft) % 4);areaTop -= (areaRight - areaLeft) * 3 / 4 / 2 - (areaBottom - areaTop) / 2;areaBottom = areaTop + (areaRight - areaLeft) * 3 / 4;}// 更新坐标系double from = fromx, to = tox;fromx = from + (to - from) * areaLeft / 640;tox = from + (tox - from) * areaRight / 640;from = fromy;to = toy;fromy = from + (to - from) * areaTop / 480;toy = from + (to - from) * areaBottom / 480;}}}else if (msg.is_move() && isPress) {//消除选框rectangle(areaLeft, areaTop, areaRight, areaBottom);areaRight = msg.x;areaBottom = msg.y;//绘制选框rectangle(areaLeft, areaTop, areaRight, areaBottom);} //重绘if (redraw) {redraw = false;draw(fromx, fromy, tox, toy);}}getch();closegraph();return 0;
}
四、谢尔宾斯基三角形(Sierpinski triangle)
1. 迭代法
方法是先取一个实心三角形,将三条边的三个中点相连,分成四个小三角形,将中间的三角形去掉,形成镂空状(即只绘制其它三个三角形)
。然后对剩余的三个三角形重复进行操作。
递归层数太多会很耗时,并且因为镂空得太多,三角形会变得很淡,由于屏幕分辨率限制,也无法太细,所以7以内即可。
可以控制图形精细程度,比较美观,并且可以用抗锯齿函数绘制。
一种是先绘制实心三角形,递归时绘制背景色中心三角形将其镂空,另一种是先不绘制,递归到一定层数时再绘制各个实心小三角形。这里取第二种。
#include <graphics.h>
#include <math.h>void sierpinskiTriangle(ege_point points[3], int n) {if (n == 0) {ege_fillpoly(3, points);return;}ege_point midPoint[3] = {{(points[0].x + points[1].x) / 2, (points[0].y + points[1].y) / 2},{(points[1].x + points[2].x) / 2, (points[1].y + points[2].y) / 2},{(points[0].x + points[2].x) / 2, (points[0].y + points[2].y) / 2}};ege_point tri1[3] = { points[0], midPoint[0], midPoint[2] };ege_point tri2[3] = { midPoint[0], points[1], midPoint[1] };ege_point tri3[3] = { midPoint[2], midPoint[1], points[2] };sierpinskiTriangle(tri1, n - 1);sierpinskiTriangle(tri2, n - 1);sierpinskiTriangle(tri3, n - 1);
}int main()
{initgraph(600, 600, INIT_RENDERMANUAL);setbkcolor(WHITE);delay_ms(0);setfillcolor(EGEARGB(0xFF, 0x50, 0x80, 0xFF));ege_enable_aa(true);ege_point triangle[3] = { {300, 50}, {50, 500}, {550, 500} }; //三角形三个顶点for (int i = 0; i <= 7; delay_ms(1000),i++) {cleardevice();sierpinskiTriangle(triangle, i);}getch();closegraph();return 0;
}
递归1层,递归3层,递归5层和递归7层
2. 随机法
取平面上三点A,B,C,组成一三角形
任意取三角形ABC内的一点P(因为最后都会形成图形,位置不重要)
重复下面步骤:
- 计算出点P与三角形随机一个顶点的中点,并画出该点
- 点P变为该中点
程序比较简单,但是需要大量的随机点,否则图形会看到很多噪点,并且无法控制图形的精细程度。因为是随机生成的,比较粗糙。
初始点不在三角形内部的话,可能会在外部产生几个点,但最后点会进入三角形内部。
下面是代码。
#include <graphics.h>
#include <stdlib.h>
#include <time.h>int main()
{ege_point triangle[3] = { {300, 50}, {50, 500}, {550, 500} }; //三角形三个顶点ege_point p = { 300, rand() % 300 }; //三角形内部任意点initgraph(600, 600, INIT_RENDERMANUAL);setbkcolor(WHITE);srand((unsigned)time(0));for (int i = 0; i <= 60000; i++) {int r = rand() % 3;p.x = (p.x + triangle[r].x) / 2;p.y = (p.y + triangle[r].y) / 2;putpixel(p.x, p.y, BLUE);}// 按任意键退出getch();closegraph();return 0;
}
左边为迭代5000次, 右边为迭代50000次。
EGE专栏:EGE专栏
EGE示例程序——分形相关推荐
- EGE示例程序——花火闪烁的夜晚
专栏:EGE专栏 专栏:EGE示例程序 示例程序下载 花火闪烁的夜晚 站点 链接 百度网盘 示例一 花火闪烁的夜晚 CSDN 示例一 花火闪烁的夜晚 (无需积分) 一.烟花 在做烟花特效前,先来看 ...
- MindSpore部署图像分割示例程序
MindSpore部署图像分割示例程序 本端侧图像分割Android示例程序使用Java实现,Java层主要通过Android Camera 2 API实现摄像头获取图像帧,进行相应的图像处理,之后调 ...
- BizTalk 2006 简单入门示例程序(附源项目文件下载)
BizTalk 2006 简单入门示例程序(附源项目文件下载) 为初学BizTalk Server 2006的开发人员,提供一个简单入门的示例程序,包括一个Receive Port.Send Port ...
- 基于Struts2.3.x+Spring3.2.x+Hibernate4.2.x+EasyUI1.3.4+Maven架构的示例程序
基于Struts2.3.x+Spring3.2.x+Hibernate4.2.x+EasyUI1.3.4+Maven架构的示例程序 不知道为什么,保存的时候显示有一个连接为违禁内容,可能是----. ...
- 如何编译ReactNative示例程序Examples
通过示例程序可以看到一些基本组件的使用,对于学习ReactNative是很有帮助的. 编译示例程序需要将整个项目导入到androidStudio中,androidStudio导入项目时选择react- ...
- ASP.NET AJAX示例程序:实现IDragSource和IDropTarget接口将商品拖放至购物车中
本文来自<ASP.NET AJAX程序设计--第II卷:客户端Microsoft AJAX Library相关>第9章第3节. 9.3 示例程序:实现IDragSource和IDropTa ...
- Linux下的示例程序
linux下的示例程序 #if 0 /* * 1. 遍历目录-1 */ #include <stdio.h> #include <dirent.h> #include &l ...
- python推荐系统-用python写个简单的推荐系统示例程序
用python写个简单的推荐系统示例程序 作者:阿俊 发布于:2011-11-26 16:03 Saturday 分类:推荐系统 python这门语言写程序代码量非常少,短短几行就可以把程序写的很清楚 ...
- OpenCV的示例程序在哪里?
示例程序在各版本的源代码中,以OpenCV – 4.1.2为例,其源代码的下载位置如下: 官网(https://opencv.org/)→Library→Releases→找到OpenCV – 4.1 ...
最新文章
- 2800:垂直直方图
- 网摘精灵教程:网摘自动提交工具。
- JDK动态代理和CGLIB代理的区别
- 从view 得到图片
- RHEL7OSP-6.0的openstack云主机发放
- [How TO]-ubuntu下安装selenium
- Excel中配置VBA的工作环境
- CTF Geek Challenge——第十一届极客大挑战Re Write Up
- 编译bluez-utils-3.36,死活找不到bluez D-bus的解决方法
- [ASP.NET MVC2 系列] ASP.NET MVC 之如何创建自定义路由约束
- 探索 Pexpect
- Halcon算子学习:sample_object_model_3d
- 重新想象 Windows 8.1 Store Apps (75) - 新增控件: Hub, Hyperlink
- java aop xml配置_spring AOP使用 xml配置
- spring+struts2+mybatis
- 男人有钱还是没钱,只需要关注他这三点,就明白了
- Linux查看、处理文件方法
- C语言和Python语言有什么区别呢?
- matlab求定积分
- 王道考研系列 计算机考研 ——机试指南(第二版) 笔记(一)