我有一个根据以下代码片段绘制分形的方法:
public static void DrawFractal(int x, int y, int len, double angle, PaintEventArgs e,Panel panel1)
{
Graphics g = e.Graphics;
double x1, y1;
x1 = x + len * Math.Sin(angle * Math.PI * 2 / 360.0);
y1 = y + len * Math.Cos(angle * Math.PI * 2 / 360.0);
g.DrawLine(new Pen(Color.Black), x, panel1.Height - y, (int)x1, panel1.Height - (int)y1);
if (len > 2)
{
DrawFractal((int)x1, (int)y1, (int)(len / 1.5), angle + 30, e,panel1);
DrawFractal((int)x1, (int)y1, (int)(len / 1.5), angle - 15, e,panel1);
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
FractalTree.DrawFractal(panel1.Width / 2, panel1.Height / 2, 80, 0, e, panel1);
}
现在这个方法在窗口打开时进行绘制。我希望它在单击按钮时绘制。
private void button2_Click(object sender, EventArgs e)
{
}
我还想添加稍后停止渲染的功能。因此,如果您能提出使用与渲染相关的处理程序和事件的想法,我将很高兴。
[这就是它的样子][1]:https://i.sstatic.net/OGcC0.png [2]:https://i.sstatic.net/mQeHc.png
这里遇到的主要问题是 CPU 密集型操作。在 UI 线程中执行这种冗长的递归例程会冻结它,直到执行完成。因此,正如您所提到的,单击
Stop
按钮不会执行任何操作。
为了保持 UI 响应能力,请创建方法的
Task
版本以在工作线程中运行它们,以便可以等待它们完成或取消。我这里有两个例子可以建议。
绘制位图
在
Bitmap
上绘制形状,然后在 Paint
事件中绘制它。
public class FractalTree
{
public static async Task<Bitmap> DrawFractalAsync(
Rectangle canvas, double len, double angle,
Color backColor, Color treeColor,
CancellationToken token)
{
var bmp = new Bitmap(canvas.Width, canvas.Height);
using (var g = Graphics.FromImage(bmp))
using (var pn = new Pen(treeColor))
{
g.Clear(backColor);
await Task.Run(async () => await DrawLocal(
canvas.Width / 2, canvas.Top, len, angle), token);
Task DrawLocal(double nx, double ny, double newLen, double newAngle)
{
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
double x1 = nx + newLen * Math.Sin(newAngle * Math.PI * 2 / 360.0);
double y1 = ny + newLen * Math.Cos(newAngle * Math.PI * 2 / 360.0);
g.DrawLine(pn,
(float)nx, canvas.Height - (float)ny,
(float)x1, canvas.Height - (float)y1);
if (newLen > 2)
{
DrawLocal(x1, y1, newLen / 1.5, newAngle + 30);
DrawLocal(x1, y1, newLen / 1.5, newAngle - 15);
//DrawLocal(x1, y1, newLen / 1.5, newAngle + 15);
//DrawLocal(x1, y1, newLen / 1.5, newAngle - 15);
}
return Task.CompletedTask;
}
}
return bmp;
}
}
实现示例...
public partial class YourForm : Form
{
private Bitmap bmp;
private CancellationTokenSource cts;
public YourForm()
{
InitializeComponent();
// Input controls...
cmbTemplates.SelectedIndexChanged += (s, e) => Draw();
nudLen.ValueChanged += (s, e) => Draw();
nudAngle.ValueChanged += (s, e) => Draw();
nudOffsetX.ValueChanged += (s, e) => Draw();
nudOffsetY.ValueChanged += (s, e) => Draw();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
bmp?.Dispose();
cts?.Dispose();
}
private void pnlCanvas_Paint(object sender, PaintEventArgs e)
{
if (bmp != null)
e.Graphics.DrawImage(bmp, Point.Empty);
}
private void btnDraw_Click(object sender, EventArgs e) => Draw();
private void btnCancel_Click(object sender, EventArgs e)
{
cts?.Cancel();
}
private async void Draw()
{
if (cts != null) return;
btnDraw.Enabled = false;
var canvas = pnlCanvas.ClientRectangle;
Bitmap newBmp = null;
try
{
using (var cancelTS = new CancellationTokenSource())
{
cancelTS.Token.ThrowIfCancellationRequested();
cts = cancelTS;
if (tcMain.SelectedTab == tpFractal)
{
switch (cmbTemplates.SelectedIndex)
{
case 0:
var len = (int)nudLen.Value;
var angle = (double)nudAngle.Value;
var xo = (int)nudOffsetX.Value;
var yo = (int)nudOffsetY.Value;
canvas.Offset(xo, yo);
newBmp = await FractalTree
.DrawFractalAsync(
canvas, len, angle,
pnlCanvas.BackColor,
Color.Back, cts.Token);
break;
case 1:
// Call another FractalTree algorithm...
break;
}
}
else if (tcMain.SelectedTab == tpPyramid)
{
// Pyramid algorithms
}
}
bmp?.Dispose();
bmp = newBmp;
}
catch (OperationCanceledException)
{
Console.WriteLine("Canceld...!");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
btnDraw.Enabled = true;
pnlCanvas.Invalidate();
cts = null;
}
}
}
演示
绘制形状
使任务计算并返回定义形状的结构列表,并将它们传递给相关的
Graphics.Draw...
或 Graphics.Fill...
方法。
public class FractalTree
{
public static async Task<IEnumerable<PointF[]>> GetFractalPointsAsync(
Rectangle camvas, double len, double angle, CancellationToken token)
{
var points = new ConcurrentBag<PointF[]>();
await Task.Run(async () => await Generate(
camvas.Width / 2, 1, len, angle), token);
Task Generate(double x, double y, double newLen, double newAngle)
{
double nx = x + newLen * Math.Sin(newAngle * Math.PI * 2 / 360.0);
double ny = y + newLen * Math.Cos(newAngle * Math.PI * 2 / 360.0);
var pt1 = new PointF((float)x, camvas.Height - (float)y);
var pt2 = new PointF((float)nx, camvas.Height - (float)ny);
points.Add(new[] { pt1, pt2 });
if (newLen > 2)
{
Generate(nx, ny, newLen / 1.5, newAngle + 30);
Generate(nx, ny, newLen / 1.5, newAngle - 15);
//GenerateLocal(nx, ny, newLen / 1.5, newAngle + 15);
//GenerateLocal(nx, ny, newLen / 1.5, newAngle - 15);
}
return Task.CompletedTask;
}
return points;
}
}
...并编辑实现示例如下...
public partial class YourForm : Form
{
private CancellationTokenSource cts;
private IList<PointF[]> treePoints;
public YourForm()
{
InitializeComponent();
cmbTemplates.SelectedIndexChanged += (s, e) => Draw();
nudLen.ValueChanged += (s, e) => Draw();
nudAngle.ValueChanged += (s, e) => Draw();
nudOffsetX.ValueChanged += (s, e) => Draw();
nudOffsetY.ValueChanged += (s, e) => Draw();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
cts?.Dispose();
}
private void pnlCanvas_Paint(object sender, PaintEventArgs e)
{
if (treePoints != null)
{
foreach (var pts in treePoints)
e.Graphics.DrawCurve(Pens.Black, pts);
}
}
private void btnDraw_Click(object sender, EventArgs e) => Draw();
private void btnCancel_Click(object sender, EventArgs e)
{
cts?.Cancel();
}
private async void Draw()
{
if (cts != null) return;
btnDraw.Enabled = false;
var canvas = pnlCanvas.ClientRectangle;
try
{
using (var cancelTS = new CancellationTokenSource())
{
cts = cancelTS;
if (tcMain.SelectedTab == tpFractal)
{
switch (cmbTemplates.SelectedIndex)
{
case 0:
var len = (int)nudLen.Value;
var angle = (double)nudAngle.Value;
var xo = (int)nudOffsetX.Value;
var yo = (int)nudOffsetY.Value;
canvas.Offset(xo, yo);
var points = await FractalTree
.GetFractalPointsAsync(
canvas, len, angle, cts.Token);
treePoints = points.ToList();
break;
case 1:
// Call another FractalTree algorithm...
break;
}
}
else if (tcMain.SelectedTab == tpPyramid)
{
// Pyramid algorithms
}
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Canceld...!");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
btnDraw.Enabled = true;
pnlCanvas.Invalidate();
cts = null;
}
}
}
这会产生一些漂亮而简单的动画: