如何在c#位图中高效地创建圆圈或形状?



(文字墙,如果信息不够请告诉我!)

我正在制作一个程序,它可以制作很多圆圈(少到10个,但可以呈指数级扩展)。我正在寻找一种方法来提高这个过程的性能。一个圆被用作"基数",其他圆被"减法"。从基地开始。例如,想象一下饼干或其他圆形物体,边缘被咬掉了一小口。

最初,我使用Vector2数组或列表来定义每个圆的面积和周长,然后找到在基圆和圆的面积上匹配的向量进行相减,然后将它们从基中移除。当圆圈数到100甚至1000时,这个过程会非常快,但到10000时,它会变得非常慢(几分钟),超过这个数可能需要一个多小时。

周长定义方法:

public override void DefinePerimeter()
{
perimeter = new List<Vector2>();
//Find one quadrant worth of perimeter
//Mirror x,y values to form full circle
for(float i = 0; i < 90; i += resolutionFactor)
{
float cos = radius * MathF.Cos(i);
float sin = radius * MathF.Sin(i);
perimeter.Add(new Vector2((int)(center.X + cos), (int)(center.Y - sin)));
perimeter.Add(new Vector2((int)(center.X - cos), (int)(center.Y - sin)));
perimeter.Add(new Vector2((int)(center.X - cos), (int)(center.Y + sin)));
perimeter.Add(new Vector2((int)(center.X + cos), (int)(center.Y + sin)));
}
perimeter = RemoveDuplicates(perimeter);//Removes duplicate Vector2

区域定义:

if (perimeterDefined == true && perimeter.Count>1)
{
Vector2 yBounds = Bounding.GetYBounds(perimeter);
if(yBounds.Y-yBounds.X>0)
{
List<Vector2> yLevel = new List<Vector2>();
for (int i = (int)yBounds.X; i < (int)yBounds.Y; i++)
{
yLevel.Clear();
yLevel = perimeter.FindAll(x => x.Y == i);
//Instead, define perimeter and area simultaneously in one method
//Define perimeter already finds bounds for each y level, just add area between them each loop
if (yLevel.Count > 0)
{
Vector2 xBounds = Bounding.GetXBounds(yLevel);
for (int j = (int)xBounds.X; j < (int)xBounds.Y; j++)
{
area.Add(new Vector2(j, i));
}
}
}
areaDefined = true;
}
else if(yBounds.Y-yBounds.X==0)
{
area.Add(new Vector2(yBounds.Y, yBounds.Y));
}
}
else if (perimeterDefined == true && perimeter.Count == 1)
{
area.Add(perimeter[0]);
}
else
{
Console.WriteLine("Error: Perimeter not defined!");
}

YBounds和XBounds方法是相同的,只是分别将x交换为y:

public static Vector2 GetYBounds(List<Vector2> range)
{
return new Vector2(range.Min(x => (int)x.Y), range.Max(x => (int)x.Y));
}

所以你可以看到,区域定义是相当沉重的性能方面。我尝试了一种新的方法,我只是计算要减去的圆的半径,并使用它们的位置确定基底区域中的哪些向量需要被删除。不幸的是,这并没有快多少。欢迎任何性能建议。如果有任何问题,请告诉我!

谢谢!:)

先试试Parallel

这就是我要尝试的:

public override void DefinePerimeter()
{
perimeter = new List<Vector2>();
//Find one quadrant worth of perimeter
//Mirror x,y values to form full circle
// this helps to get the value to the stack wich is faster to call
int CenterX = center.x;
int CenterY = center.y;

Parallel.ForEach(Iterate(0,90,resolutionFactor).ToArray(), (value, state,index) => 
{
float cos = radius * MathF.Cos(value);
float sin = radius * MathF.Sin(value);
// do the calculations once then just callit
int CenterXPluszCos = (int)(CenterX + cos);
int CenterXMinusCos = (int)(CenterX - cos);
int CenterYMinusSin = (int)(CenterY - sin);
int CenterYPluszSin = (int)(CenterY + sin);
perimeter.Add(new Vector2(CenterXPluszCos, CenterYMinusSin));
perimeter.Add(new Vector2(CenterXMinusCos , CenterYMinusSin));
perimeter.Add(new Vector2(CenterXMinusCos, CenterYPluszSin));
perimeter.Add(new Vector2(CenterXPluszCos , CenterYPluszSin));
});
perimeter = RemoveDuplicates(perimeter);//Removes duplicate Vector2
}
private static IEnumerable<double> Iterate(double FromInclusive, double toExclusive, double step)
{
for(double d = fromInclusive; d < toExclusive; d+= step yield return d;
}

A如果你想,你可以尝试创建一个周长数组列表,并通过使用索引添加计算向量。如果Add方法减慢了循环的速度,这可能会很好。但是,如果将数组转换回列表,则很难做到这一点,并且从长远来看速度会提高100%。

我要尝试的第二件事是这个。

if (perimeterDefined == true && perimeter.Count>1)
{
Vector2 yBounds = Bounding.GetYBounds(perimeter);
// here you can use somthing else as a variable if you dont want to Order the perimeter
perimeter = perimeter.OrderBy(x => x.Y);
if(yBounds.Y-yBounds.X>0)
{
List<Vector2> yLevel = new List<Vector2>();
int perimeterIndex = (int)yBounds.X;
for (int i = (int)yBounds.X; i < (int)yBounds.Y; i++)
{
yLevel.Clear(); 
// this way the FindAll is eliminated 
// this is faster because  we don't check all the perimeter only what is next in the list so it is O(n)
while(perimeter[perimeterIndex] == i)
{
yLevel.Add(perimeter[perimeterIndex])
perimeterIndex++;
}
//yLevel = perimeter.FindAll(x => x.Y == i);
//Instead, define perimeter and area simultaneously in one method
//Define perimeter already finds bounds for each y level, just add area between them each loop
if (yLevel.Count > 0)
{
Vector2 xBounds = Bounding.GetXBounds(yLevel);
for (int j = (int)xBounds.X; j < (int)xBounds.Y; j++)
{
area.Add(new Vector2(j, i));
}
}
}
areaDefined = true;
}
else if(yBounds.Y-yBounds.X==0)
{
area.Add(new Vector2(yBounds.Y, yBounds.Y));
}
}
else if (perimeterDefined == true && perimeter.Count == 1)
{
area.Add(perimeter[0]);
}
else
{
Console.WriteLine("Error: Perimeter not defined!");
}

如果第二个不工作,那么你可以做的事情是删除yLevel.Clear();因为如果没有找到元素,findAll将返回空列表

最新更新