[分形学] 查看 Mandelbrot 集上任意一点的 Julia 集
easyx吧
全部回复
仅看楼主
level 5
邹汇东 楼主
基于村长的代码修改而来
2025年06月09日 10点06分 1
level 5
邹汇东 楼主
2025年06月09日 10点06分 2
level 5
邹汇东 楼主
右键 Mandelbrot 集上任意一点,以观察该点的 Julia 集长什么样子。发现在 M 集边缘的 J 集比较艳丽,在中心黑区域的 J 集一般般。
2025年06月09日 10点06分 3
观察到,Julia 集的图形似乎和Mandelbrot集芽孢的形状有关。在复杂、精细、微小的芽孢中,Julia 集就复杂;在简单、平滑、粗大的芽孢中,Julia 集也相应地比较简单。
2025年06月09日 11点06分
level 5
邹汇东 楼主
// 程序名称:分形学 - 可以无穷放大的 Mandelbrot Set (曼德布洛特集)
// 编译环境:Visual Studio 2022 | EasyX 20240601
// 最后更新:2025-06-09(汇东更新)
//
#include <graphics.h>
#include <conio.h>
// 定义常量
#define MAXCOLOR 64 // 颜色数
int iterations = 100; // 迭代次数,越高,图像越精细(村长原设为 1000,此处为了提速而修改之)
/////////////////////////////////////////////////
// 定义复数及乘、加运算
/////////////////////////////////////////////////
// 定义复数
struct COMPLEX
{
double re;
double im;
};
// 定义复数“乘”运算
COMPLEX operator * (COMPLEX a, COMPLEX b)
{
COMPLEX c;
c.re = a.re * b.re - a.im * b.im;
c.im = a.im * b.re + a.re * b.im;
return c;
}
// 定义复数“加”运算
COMPLEX operator + (COMPLEX a, COMPLEX b)
{
COMPLEX c;
c.re = a.re + b.re;
c.im = a.im + b.im;
return c;
}
/////////////////////////////////////////////////
// 定义颜色及初始化颜色(针对 Mandelbrot Set)
/////////////////////////////////////////////////
// 定义颜色
int Color[MAXCOLOR];
// 初始化颜色
void InitColor()
{
// 使用 HSL 颜色模式产生角度 h1 到 h2 的渐变色
int h1 = 240, h2 = 30;
for (int i = 0; i < MAXCOLOR / 2; i++)
{
Color[i] = HSLtoRGB((float)h1, 1.0f, i * 2.0f / MAXCOLOR);
Color[MAXCOLOR - 1 - i] = HSLtoRGB((float)h2, 1.0f, i * 2.0f / MAXCOLOR);
}
}
2025年06月09日 11点06分 6
level 5
邹汇东 楼主
/////////////////////////////////////////////////
// 绘制 Mandelbrot Set (曼德布洛特集)
/////////////////////////////////////////////////
void DrawMandelbrot(double fromx, double fromy, double tox, double toy)
{
COMPLEX z, c;
int x, y, k; // 定义循环变量
for (x = 0; x < 640; x++)
{
c.re = fromx + (tox - fromx) * (x / 640.0);
for (y = 0; y < 480; y++)
{
c.im = fromy + (toy - fromy) * (y / 480.0);
z.re = z.im = 0;
for (k = 0; k < iterations; k++)
{
if (z.re * z.re + z.im * z.im > 4.0) break;
z = z * z + c;
}
putpixel(x, y, (k >= iterations) ? 0 : Color[k % MAXCOLOR]);
}
}
}
/////////////////////////////////////////////////
// 绘制 Julia Set (茱莉亚集)
/////////////////////////////////////////////////
void DrawJulia(double fromx, double fromy, double tox, double toy, COMPLEX c)
{
COMPLEX z;
int x, y, k; // 定义循环变量
for (x = 0; x < 640; x++)
{
for (y = 0; y < 480; y++)
{
z.re = -1.6 + 3.2 * (x / 640.0);
z.im = -1.2 + 2.4 * (y / 480.0);
for (k = 0; k < iterations; k++)
{
if (z.re * z.re + z.im * z.im > 4.0) break;
z = z * z + c;
}
putpixel(x, y, (k >= iterations) ? 0 : HSLtoRGB((float)((k << 5) % 360), 1.0, 0.5));
}
}
}
/////////////////////////////////////////////////
// 绘制选中区域的圆形
/////////////////////////////////////////////////
void DrawSelCircle(int x, int y, int radius)
{
setrop2(R2_XORPEN);
circle(x, y, radius);
setrop2(R2_COPYPEN);
}
/////////////////////////////////////////////////
// 绘制帮助按钮
/////////////////////////////////////////////////
void DrawHelpButton(RECT rct)
{
setfillcolor(BLUE);
fillrectangle(rct.left, rct.top, rct.right, rct.bottom);
setbkcolor(BLUE);
drawtext(L"Help", &rct, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
setbkcolor(BLACK);
}
/////////////////////////////////////////////////
// 显示帮助信息
/////////////////////////////////////////////////
void HelpMsgBox()
{
// 显示提示信息
MessageBox(GetHWnd(),
L"分形学 - 可以无穷放大的 Mandelbrot Set (曼德布洛特集)\n\n"
L"使用鼠标左键选择区域,按中键恢复原图形座标系,按右键查看此处的 Julia Set(茱莉亚集)。按任意键退出。\n\n"
L"备注:颜色越深,表示这一点收敛性越好。\n\n"
L"该程序由 huidong 据慢羊羊的代码修改而来。2025-06-09 汇东识。\n\n", L"提示", MB_OK | MB_ICONINFORMATION);
}
2025年06月09日 11点06分 7
level 5
邹汇东 楼主
/////////////////////////////////////////////////
// 主函数
/////////////////////////////////////////////////
int main()
{
//////// 窗口配置
// 初始化绘图窗口及颜色
initgraph(640, 480);
InitColor();
// 主窗口画布
IMAGE* imgWnd = GetWorkingImage();
//////// 数学参数
// 初始化 Mandelbrot Set(曼德布洛特集)座标系
double fromx, fromy, tox, toy;
fromx = -2.1; tox = 1.1;
fromy = -1.2; toy = 1.2;
//////// 界面 UI
RECT rctHelpBtn = { 2, 480 - 20, 50, 478 }; // 帮助按钮区域
//////// 鼠标消息
// 捕获鼠标操作,实现放大鼠标选中区域
ExMessage m;
bool isLDown = false;
int selfx, selfy, seltx, selty; // 定义选区
// 计算鼠标位置对应的 Mandelbrot Set 座标
COMPLEX z = { 0, 0 };
// 计算对比模式的 C 值换算到屏幕座标的位置
POINT comparePt;
//////// 对比模式
bool isInCompareMode = false; // 是否在对比模式中
COMPLEX compareC = { 0, 0 }; // 用于存储 Julia Set 的 c 值
//////// 窗口绘制
// 窗口图像记录,用于刷新背景,快速绘制不同的表层内容
IMAGE imgLeftWnd(640, 480);
IMAGE imgRightWnd(640, 480);
// 重绘左侧窗口的函数
auto RedrawLeftWnd = [&]()
{
DrawMandelbrot(fromx, fromy, tox, toy);
DrawHelpButton(rctHelpBtn);
WCHAR buf[128] = L"Mandelbrot Set";
outtextxy(0, 0, buf);
getimage(&imgLeftWnd, 0, 0, 640, 480);
};
// 重绘右侧窗口的函数
auto RedrawRightWnd = [&]()
{
// 绘制 Julia Set
if (isInCompareMode)
{
// 绘制 Julia Set (茱莉亚集)
double fromx, fromy, tox, toy;
fromx = -2.1; tox = 1.1;
fromy = -1.2; toy = 1.2;
setorigin(640, 0); // 设置原点到右侧
DrawJulia(fromx, fromy, tox, toy, compareC);
setorigin(0, 0); // 恢复原点到左侧
flushmessage(EX_MOUSE); // 清空在绘制过程中缓冲的鼠标消息
}
else
{
// 未定义行为
}
WCHAR buf[128] = { 0 };
swprintf(buf, 127, L"Julia Set | c = %.3f %+.3fi", compareC.re, compareC.im);
outtextxy(640, 0, buf);
getimage(&imgRightWnd, 640, 0, 640 * 2, 480);
};
RedrawLeftWnd();
//////// 主循环
do
{
//////// 获取鼠标消息,进行一些坐标的计算
getmessage(&m, EM_MOUSE | EM_KEY); // 获取一条鼠标消息
z.re = fromx + (tox - fromx) * (m.x / 640.0);
z.im = fromy + (toy - fromy) * (m.y / 480.0);
comparePt.x = (int)((compareC.re - fromx) * 640 / (tox - fromx));
comparePt.y = (int)((compareC.im - fromy) * 480 / (toy - fromy));
switch (m.message)
{
case WM_KEYDOWN:
break;
// 按鼠标中键恢复原图形座标系
case WM_MBUTTONUP:
// 在 Juila 集右键,退出对比模式
if (m.x > 640)
{
Resize(nullptr, 640, 480); // 恢复窗口大小
isInCompareMode = false; // 退出对比模式
}
else
{
fromx = -2.1; tox = 1.1;
fromy = -1.2; toy = 1.2;
RedrawLeftWnd(); // 重绘左侧窗口
}
break;
// 按鼠标左键并拖动,选择区域
case WM_MOUSEMOVE:
{
putimage(0, 0, &imgLeftWnd);
if (isInCompareMode)
{
putimage(640, 0, &imgRightWnd);
DrawSelCircle(comparePt.x, comparePt.y, 5);
}
// 只在左侧 Mandelbrot 区域显示复平面坐标
if (m.x < 640)
{
WCHAR buf[64] = { 0 };
swprintf(buf, 63, L"%.3f + %.3fi", z.re, z.im);
outtextxy(m.x, m.y - 10, buf);
}
if (isLDown)
{
seltx = m.x;
selty = m.y;
rectangle(selfx, selfy, seltx, selty);
}
}
break;
// 按鼠标左键并拖动,选择区域
case WM_LBUTTONDOWN:
setlinecolor(WHITE);
//setrop2(R2_XORPEN);
isLDown = true;
selfx = seltx = m.x;
selfy = selty = m.y;
rectangle(selfx, selfy, seltx, selty);
break;
// 按鼠标左键并拖动,选择区域
case WM_LBUTTONUP:
isLDown = false;
// 检测帮助按钮
if (m.x >= rctHelpBtn.left && m.x <= rctHelpBtn.right &&
m.y >= rctHelpBtn.top && m.y <= rctHelpBtn.bottom)
{
HelpMsgBox();
break;
}
// 选择区域
//rectangle(selfx, selfy, seltx, selty);
//setrop2(R2_COPYPEN);
seltx = m.x;
selty = m.y;
if (selfx == seltx || selfy == selty) break;
// 修正选区为 4:3
int tmp;
if (selfx > seltx) { tmp = selfx; selfx = seltx; seltx = tmp; }
if (selfy > selty) { tmp = selfy; selfy = selty; selty = tmp; }
if ((seltx - selfx) * 0.75 < (selty - selfy))
{
selty += (3 - (selty - selfy) % 3);
selfx -= (selty - selfy) / 3 * 4 / 2 - (seltx - selfx) / 2;
seltx = selfx + (selty - selfy) / 3 * 4;
}
else
{
seltx += (4 - (seltx - selfx) % 4);
selfy -= (seltx - selfx) * 3 / 4 / 2 - (selty - selfy) / 2;
selty = selfy + (seltx - selfx) * 3 / 4;
}
// 更新座标系
double f, t;
f = fromx + (tox - fromx) * selfx / 640;
t = fromx + (tox - fromx) * seltx / 640;
fromx = f;
tox = t;
f = fromy + (toy - fromy) * selfy / 480;
t = fromy + (toy - fromy) * selty / 480;
fromy = f;
toy = t;
// 画图形
RedrawLeftWnd();
break;
// 保持按下右键时,只有按下瞬间会触发 WM_RBUTTONDOWN 事件,因此作为进入对比模式的入口
case WM_RBUTTONDOWN:
if (m.x < 640)
{
isInCompareMode = true;
Resize(nullptr, 640 * 2, 480); // 将窗口宽度扩展到 1280 像素
}
break;
}
// 按鼠标右键,显示 Julia Set(茱莉亚集)
// 按住右键可以连续查看不同位置的 Julia Set
if (isInCompareMode && m.rbutton && m.x < 640)
{
// 设置对比模式的 c 值为当前鼠标位置对应的 Mandelbrot Set 座标
compareC = z;
RedrawRightWnd();
}
} while (m.message != WM_KEYDOWN);
closegraph();
return 0;
}
2025年06月09日 11点06分 8
level 5
邹汇东 楼主
2025年06月09日 12点06分 9
level 1
优秀
2025年06月18日 23点06分 10
吧务
level 11
优秀
2025年07月08日 14点07分 11
1