分形雪






4.89/5 (38投票s)
介绍了如何使用分形绘制雪花,并包含一个漂亮的雪屏保。
引言
本文介绍了一些分形的基本特征,并演示了如何使用分形绘制雪花。源代码包含一个绘制单个雪花的类和一个用于显示下落雪花的Windows窗体控件。此控件可用于创建屏保应用程序。在屏保应用程序中,雪花是通过随机设置生成的,因此几乎有无限的雪花变化。
分形
分形是几何形状,其中某个模式根据缩放重复出现。也就是说,当你放大对象的某个部分时,它看起来与缩放前的原始对象相同或非常相似。
在程序中,分形是通过递归创建的。相同的(或非常相似的)绘图操作会一直重复,直到递归深度超过指定的次数。在我的应用程序中,绘图操作在连接雪花中心与其边缘的线上执行,然后在第一步创建的新线上执行。
雪花
应用程序使用两种略有不同的算法来制作更多样化的雪花。这两种方法具有相同的输入参数和相同的含义,因此使用这两种方法绘制雪花非常简单。
如果您想了解更多关于雪花是如何生成的以及每个输入参数的作用,请安装屏保并进入设置 - 雪花预览。
第一种类型 (LineType)
绘制雪花
- 从雪花中心向边缘绘制六个分形分支。
- 线段按指定比例分成两部分。
- 在找到的点处,以指定角度绘制两条新线。
- 操作(从第2步到第4步)在两条新创建的线上重复执行。
第二种类型 (PointType)
绘制雪花
- 从雪花中心向边缘创建六个分形分支(此时不进行绘制!)。
- 线段按指定比例分成两部分。
- 在找到的点处,创建两条新线(不绘制),角度与指定角度相同。
- 操作(从第2步到第4步)在两条新创建的线以及从找到的点到边缘点的第三条线上重复执行。
- 超过重复次数后,绘制所有剩余的线。
源代码
UML类图
雪花绘制
以下函数是用于绘制第一种雪花类型的简化递归函数
此代码使用 Rotate
函数。此函数绕指定中心旋转点,并在 SnowFlake.cs 中实现。
// Ratio used for calculating dividing point
private const float fBranching=0.3f;
// Ratio used for decreasing length of lines
private const float fShrinking=0.6f;
// Maximal depth of recursion
private const int iMaxDepth=5;
// Angle between parent and child line (in degrees)
private const int iBranchAngle=35;
// Draw snowflake branch
// output - Graphics to draw on
// start - Starting point of line
// end - Ending point of line
// depth - Depth of recursion
private void DrawBranch(Graphics output,
PointF start,PointF end,int depth)
{
if (depth==iMaxDepth) return;
DrawLine(output,pn,start,end);
// calculate dividing point
PointF cnt=new PointF(start.X+(end.X-start.X)*fBranching,
start.Y+(end.Y-start.Y)*fBranching);
// calculate point used as end (after rotation) of two new lines
PointF nend=new PointF(cnt.X+(end.X-start.X)*fShrinking,
cnt.Y+(end.Y-start.Y)*fShrinking);
// recursion - draw two new lines
DrawBranch(output,cnt,Rotate(nend,cnt,iBranchAngle),depth+1);
DrawBranch(output,cnt,Rotate(nend,cnt,-iBranchAngle),depth+1);
}
使用此代码
使用 SnowFlake
SnowFlake
对象可用于绘制雪花。它有两种绘制方法。第一种方法 Draw(Graphics, PointF)
可用于正常绘制,第二种方法 Draw(Graphics, PointF, int)
允许您为绘制的像素指定 alpha 值。第二种方法用于屏保中的雪花融化。RandomSnowFlake
是派生自 SnowFlake
的类。唯一的区别是,雪花的所有属性都在构造函数中通过随机数生成,因此如果您想创建雪花,则不必设置所有属性。
// Draw snowflakes in Paint event handler
private void Form_Paint(object sender, PaintEventArgs pe)
{
Random rndGen=new Random();
pe.Graphics.Clear(Color.Black);
SnowFlake flake=new SnowFlake(),
random=new RandomSnowFlake(rndGen.Next(Int32.MaxValue));
// draw snowflake
flake.Draw(pe.Graphics,new PointF(70.0f,70.0f));
// draw melting snowflake
flake.Draw(pe.Graphics,new PointF(70.0f,140.0f,128));
// draw random snowflake
random.Draw(pe.Graphics,new PointF(105.0f,105.0f));
}
使用 SnowControl
SnowControl
是用于创建下落雪花的控件。这与填充屏保整个屏幕的控件完全相同。可以使用设计器将此控件添加到 Form
中,或通过类似以下代码的手动添加:
此控件有很多属性,在屏保设置部分有详细说明。
SnowControl snowCtrl;
public Form()
{
// Create control
snowCtrl=new SnowControl();
Controls.Add(snowCtrl);
snowCtrl.Dock=DockStyle.Fill;
}
private void Form_Load(object sender,EventArgs e)
{
// Start animation
snowCtrl.Start();
}
SnowControl 和屏保设置
环境
名为 StopFalling
的属性是从屏幕底部算起的像素长度,下落的雪花将在该位置停止。如果此属性小于零,雪花将落到屏幕外。下落的雪花会受到风的影响。WindForce
指定了风对雪花的影响程度,WindChanging
决定了风向改变的速度。
雪花
您可以更改屏幕上显示的雪花数量 (MaxFlakes
) 和雪花的颜色 (FlakeColor
)。大小和速度属性会在指定的最小值和最大值之间随机生成(MinSpeed
, MaxSpeed
和 MinSize
, MaxSize
)。
当雪花落到屏幕底部时,它会在该位置停留指定的帧数 (MeltingStart
),之后雪花开始融化,融化过程持续的帧数由 MeltingFluency
属性指定。
背景
屏保的背景可以是任何兼容 .NET 的图像、纯色或两种颜色之间的渐变(顶部和底部颜色)。请注意,如果您将背景设置为图像并且雪花数量较多,则屏保可能会非常慢!
感谢...
感谢 Xenik (Adam Abonyi) 提供此想法、有益的建议以及在撰写此英文文章过程中提供的巨大帮助。额外感谢以下文章的作者。
相关文章
Marc 的分形树为本文提供了很大的启发,另外两篇文章包含有关创建屏保的有用信息,包括多显示器支持以及如何在 .NET 中绘制屏保预览。
- 创建分形圣诞树,作者 Marc Clifton。
- Christian 和 James 的 Code Project 屏保,作者 Christian Graus 和 James T. Johnson。
- 如何在 C# 中开发屏保,作者 Rakesh Rajan。