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示例程序——分形相关推荐

  1. EGE示例程序——花火闪烁的夜晚

    专栏:EGE专栏 专栏:EGE示例程序 示例程序下载 花火闪烁的夜晚 站点 链接 百度网盘 示例一 花火闪烁的夜晚 CSDN 示例一 花火闪烁的夜晚 (无需积分) 一.烟花   在做烟花特效前,先来看 ...

  2. MindSpore部署图像分割示例程序

    MindSpore部署图像分割示例程序 本端侧图像分割Android示例程序使用Java实现,Java层主要通过Android Camera 2 API实现摄像头获取图像帧,进行相应的图像处理,之后调 ...

  3. BizTalk 2006 简单入门示例程序(附源项目文件下载)

    BizTalk 2006 简单入门示例程序(附源项目文件下载) 为初学BizTalk Server 2006的开发人员,提供一个简单入门的示例程序,包括一个Receive Port.Send Port ...

  4. 基于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架构的示例程序 不知道为什么,保存的时候显示有一个连接为违禁内容,可能是----. ...

  5. 如何编译ReactNative示例程序Examples

    通过示例程序可以看到一些基本组件的使用,对于学习ReactNative是很有帮助的. 编译示例程序需要将整个项目导入到androidStudio中,androidStudio导入项目时选择react- ...

  6. ASP.NET AJAX示例程序:实现IDragSource和IDropTarget接口将商品拖放至购物车中

    本文来自<ASP.NET AJAX程序设计--第II卷:客户端Microsoft AJAX Library相关>第9章第3节. 9.3 示例程序:实现IDragSource和IDropTa ...

  7. Linux下的示例程序

    linux下的示例程序 #if 0 /*  * 1. 遍历目录-1  */ #include <stdio.h> #include <dirent.h> #include &l ...

  8. python推荐系统-用python写个简单的推荐系统示例程序

    用python写个简单的推荐系统示例程序 作者:阿俊 发布于:2011-11-26 16:03 Saturday 分类:推荐系统 python这门语言写程序代码量非常少,短短几行就可以把程序写的很清楚 ...

  9. OpenCV的示例程序在哪里?

    示例程序在各版本的源代码中,以OpenCV – 4.1.2为例,其源代码的下载位置如下: 官网(https://opencv.org/)→Library→Releases→找到OpenCV – 4.1 ...

最新文章

  1. 2800:垂直直方图
  2. 网摘精灵教程:网摘自动提交工具。
  3. JDK动态代理和CGLIB代理的区别
  4. 从view 得到图片
  5. RHEL7OSP-6.0的openstack云主机发放
  6. [How TO]-ubuntu下安装selenium
  7. Excel中配置VBA的工作环境
  8. CTF Geek Challenge——第十一届极客大挑战Re Write Up
  9. 编译bluez-utils-3.36,死活找不到bluez D-bus的解决方法
  10. [ASP.NET MVC2 系列] ASP.NET MVC 之如何创建自定义路由约束
  11. 探索 Pexpect
  12. Halcon算子学习:sample_object_model_3d
  13. 重新想象 Windows 8.1 Store Apps (75) - 新增控件: Hub, Hyperlink
  14. java aop xml配置_spring AOP使用 xml配置
  15. spring+struts2+mybatis
  16. 男人有钱还是没钱,只需要关注他这三点,就明白了
  17. Linux查看、处理文件方法
  18. C语言和Python语言有什么区别呢?
  19. matlab求定积分
  20. 王道考研系列 计算机考研 ——机试指南(第二版) 笔记(一)

热门文章

  1. 知识图谱应用——煤矿安全知识图谱构建技术研究学习
  2. Spring中StopWatch的使用
  3. centos7 清理缓存
  4. PyQt5曲线图绘制
  5. 如何在自己的电脑上复现开源论文里的代码
  6. wampserv设置虚拟主机
  7. 一、求相关函数 corrcoef+协方差矩阵cov
  8. python的对象数组_Python当中的array数组对象实例详解
  9. Nginx应用环境 三
  10. 全球与中国环氧固化剂市场现状及未来发展趋势