以下是2003年我在看分形算法之后写的一个关于IFS(迭代函数系)类分形实现程序,ifs是分形中最为简单的一类,用途很广。该类利用信息的自相似特性,可以描述很多自然界中存在的食物,如海岸,山脉,树木,楼宇,其数学抽象为仿射坐标变换,旋转,扭曲,平移三种效果的迭加。
特别的,对于二维图像只要事先给定a,b,c,d,e,f系数,就可以确定五花八门的图像了。
x(n+1) = a*x(n) + b*y(n) + c
y(n+1) = d*x(n) + e*y(n) + f
对于其应用印象最深的应该是Maya paint effect screensaver (其算法没有看到,只是根据界面做了一些简单推测). 刚开始接触这个分形系统的时候,我觉得非常神奇,每个mel文件对应一个分形图案,每个图案也非常漂亮,而且绘制非常迅速。开始,我把神奇归结到了那些mel脚本里面,而且差点就去学习mel编程了,很快发现,情况并非如此,因为所有的mel文件中并没有实质上的可执行的语句,只是大量的常数定义,当把多个mel文件打开的时候,发现其关键字全部相同,这才明白,这些数据只是给出了绘制这些分形图形,并进行渲染所需要的参数,真正生成图形的算法应该是在二进制文件里面,其最巧妙的地方就是对一种分形算法重复利用,太阳花,菊花,蒲公英,竹子,杂草均使用一个分形系统,在做渲染的时候,则提取事先定义好的二维图形贴片进行贴图,就生成了各种各样的绚丽图案。
void CIfsDlg::OnFile()
{
while(nValidate == 2) {
nShouldStop = 1;
Sleep(10);
}
flag = 0;
XMax = 0; YMax = 0;
XMin = 0; YMin = 0;
CFileDialog dlg(TRUE, NULL, NULL,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
"ifs文件 (*.ifs)|*.ifs||");
if(dlg.DoModal() == IDOK) {
CString str;
nNum = 0;
CStdioFile file(dlg.GetFileName(), CFile::modeRead);
if(! file.ReadString(str)) return;
nNum = atoi(str);
for(int i=0; i<nNum; i++) {
if(! file.ReadString(str)) return;
sscanf(str, "%f %f %f %f %f %f %f",
&a[i][0], &a[i][1], &a[i][2], &a[i][3],
&a[i][4], &a[i][5], &a[i][6]);
}
file.Close();
p_sum = 0.f;
for(i=0; i<nNum; i++) {
p_sum += a[i][6];
p_cum[i] = int(p_sum*RAND_MAX+.5);
}
}
OnButton1();
}
void CIfsDlg::OnButton1()
{
DWORD id;
while(nValidate == 2) {
nShouldStop = 1;
Sleep(10);
}
nShouldStop = 0;
CreateThread(NULL, 0, PaintIfs, this, NULL, &id);
}
void CIfsDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if(m_Ifs) {
nShouldStop = 1;
if(m_FullScreen) {
XS = cx;
YS = cy;
}
else
{
m_Ifs.SetWindowPos(NULL, 0, 0, cx-130,cy-30, SWP_NOZORDER|SWP_NOMOVE);
XS = cx-140;
YS = cy-40;
}
flag = 0;
XMax = 0; YMax = 0;
XMin = 0; YMin = 0;
OnButton1();
}
}
DWORD WINAPI CIfsDlg::PaintIfs(LPVOID lpvThreadParm)
{
CIfsDlg * dlg = (CIfsDlg *) lpvThreadParm;
dlg->nValidate = 2;
CDC *dc1;
if(dlg->m_FullScreen){
dc1 = dlg->GetWindowDC();
//dc1 = GetDesktopWindow()->GetDC();
}
else
{
dc1 = dlg->m_Ifs.GetDC();
}
int i, temp, k;
RECT rec = {0,0,dlg->XS+10,dlg->YS+10};
dc1->FillRect(&rec, &CBrush(RGB(0, 0, 0)));
COLORREF col = RGB(
dlg->MyRand(128)+128,
dlg->MyRand(128)+128,
dlg->MyRand(128)+128);
for(;;)
{
if(dlg->m_FullScreen && (dlg->MyRand(1000)== 0))
{
dlg->nValidate = 0;
dlg->ReadFile();
dc1->DeleteDC();
return 0;
}
Sleep(5);
for(i=0; i<256; i++)
{
temp = dlg->MyRand();
for(k=0; k<dlg->nNum-1; k++)
{
if(temp<dlg->p_cum[k]) break;
}
dlg->newx = (dlg->a[k][0]*dlg->x+dlg->a[k][1]*dlg->y+dlg->a[k][4]);
dlg->y = (dlg->a[k][2]*dlg->x+dlg->a[k][3]*dlg->y+dlg->a[k][5]);
dlg->x = dlg->newx;
if(dlg->flag == 0 && i>16)
{
dlg->XMax = max(dlg->x, dlg->XMax); dlg->XMin = min(dlg->x, dlg->XMin);
dlg->YMax = max(dlg->y, dlg->YMax); dlg->YMin = min(dlg->y, dlg->YMin);
}
else if(dlg->flag == 1)
{
dlg->px = dlg->x*dlg->xscale + dlg->xoffset;
dlg->py = dlg->y*dlg->yscale + dlg->yoffset;
if((dlg->px >=0 ) && (dlg->px <= dlg->XS + 10)
&& (dlg->py >= 0) && (dlg->py <= dlg->YS+10))
{
dc1->SetPixel(dlg->px+2, dlg->YS - dlg->py+10, col);
}
if(dlg->nShouldStop) {
dlg->nValidate = 0;
dc1->DeleteDC();
return 1;
}
}
}
if(dlg->flag == 0)
{
dlg->xscale = dlg->XS/(dlg->XMax-dlg->XMin);
dlg->yscale = min(dlg->YS/(dlg->YMax-dlg->YMin), dlg->xscale);
if(dlg->yscale < dlg->xscale) dlg->xscale = dlg->yscale;
dlg->xoffset = dlg->XS/2 - (dlg->XMax+dlg->XMin)*dlg->xscale/2-1;
dlg->yoffset = dlg->YS/2 - (dlg->YMax+dlg->YMin)*dlg->yscale/2-1;
dlg->flag = 1;
}
}
dc1->DeleteDC();
dlg->nValidate = 0;
return 0;
}
void CIfsDlg::ReadFile()
{
while(nValidate == 2) {
nShouldStop = 1;
Sleep(10);
}
flag = 0;
XMax = 0; YMax = 0;
XMin = 0; YMin = 0;
CString str;
nNum = 0;
char *fileName[]={"circle.ifs","fern.ifs","ktree.ifs","mleaf.ifs","sierpink.ifs","tree.ifs"};
int n = rand();
int i;
/* str = "";
char ch[8] = {0};
for(i=0; i<100; i++)
{
sprintf(ch, "%d ", MyRand());
str += ch;
if(!(i%10)) str += "\r\n";
}
MessageBox(str);
*/
n=MyRand(6);
char *p = *(fileName+n);
CStdioFile file(p, CFile::modeRead);
if(! file.ReadString(str)) return;
nNum = atoi(str);
for(i=0; i<nNum; i++) {
if(! file.ReadString(str)) return;
sscanf(str, "%f %f %f %f %f %f %f",
&a[i][0], &a[i][1], &a[i][2], &a[i][3],
&a[i][4], &a[i][5], &a[i][6]);
}
file.Close();
p_sum = 0.f;
for(i=0; i<nNum; i++) {
p_sum += a[i][6];
p_cum[i] = int(p_sum*RAND_MAX+.5);
}
OnButton1();
}
int CIfsDlg::MyRand(int nMax)
{
LARGE_INTEGER litmp;
LONGLONG QPart1;
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;
QPart1 &= 0x7fff;
QPart1 = QPart1*QPart1;
int n2 = QPart1&0xff;
QPart1 >>= 8;
int n1 = QPart1 & 0x7fff;
n1 = n1*nMax/0x7fff;
if(n1>=nMax) n1 = nMax-1;
return n1;
}