我需要为GraphicsPath
对象创建一个扩展方法,以确定GraphicsPath
是顺时针还是逆时针缠绕。
这样的事情:
public static bool IsClockwise(GraphicsPath gp)
{
bool bClockWise = false;
PointF[] pts = gp.PathPoints;
foreach (PointF pt in pts)
{
//??
}
return bClockWise;
}
注意:我假设我们需要foreach
点GraphicsPath
,但如果有更好的方式,请随时提供。
原因是FillMode
的{{1}}确定相邻GraphicsPath的重叠部分是否已填充(GraphicsPath
)或'漏洞'(Winding
)。但是,仅当相邻的GraphicsPath以相同方向缠绕时才会出现这种情况。
如果两个路径以相反的方向缠绕,即使将Alternate
设置为FillMode
也会导致(不需要的)孔。
有趣的是,GraphicsPath DOES有一个Winding
方法,允许人们改变路径的方向(这可以解决问题),但是不可能知道GraphicsPath需要什么时候.Reversed不知道路径的方向。
你能帮忙解决这个扩展方法吗?
答案 0 :(得分:4)
我主要把它扔到那里因为这个问题引起了我的兴趣,这是我没有专业知识的,我想引发讨论。
我拿了Point in Polygon pseudo-code并试图让它适合你的情况。似乎你只对确定是顺时针还是逆时针缠绕的东西感兴趣。这是一个简单的测试,非常简单(粗略的边缘)C#实现。
[Test]
public void Test_DetermineWindingDirection()
{
GraphicsPath path = new GraphicsPath();
// Set up points collection
PointF[] pts = new[] {new PointF(10, 60),
new PointF(50, 110),
new PointF(90, 60)};
path.AddLines(pts);
foreach(var point in path.PathPoints)
{
Console.WriteLine("X: {0}, Y: {1}",point.X, point.Y);
}
WindingDirection windingVal = DetermineWindingDirection(path.PathPoints);
Console.WriteLine("Winding value: {0}", windingVal);
Assert.AreEqual(WindingDirection.Clockwise, windingVal);
path.Reverse();
foreach(var point in path.PathPoints)
{
Console.WriteLine("X: {0}, Y: {1}",point.X, point.Y);
}
windingVal = DetermineWindingDirection(path.PathPoints);
Console.WriteLine("Winding value: {0}", windingVal);
Assert.AreEqual(WindingDirection.CounterClockWise, windingVal);
}
public enum WindingDirection
{
Clockwise,
CounterClockWise
}
public static WindingDirection DetermineWindingDirection(PointF[] polygon)
{
// find a point in the middle
float middleX = polygon.Average(p => p.X);
float middleY = polygon.Average(p => p.Y);
var pointInPolygon = new PointF(middleX, middleY);
Console.WriteLine("MiddlePoint = {0}", pointInPolygon);
double w = 0;
var points = polygon.Select(point =>
{
var newPoint = new PointF(point.X - pointInPolygon.X, point.Y - pointInPolygon.Y);
Console.WriteLine("New Point: {0}", newPoint);
return newPoint;
}).ToList();
for (int i = 0; i < points.Count; i++)
{
var secondPoint = i + 1 == points.Count ? 0 : i + 1;
double X = points[i].X;
double Xp1 = points[secondPoint].X;
double Y = points[i].Y;
double Yp1 = points[secondPoint].Y;
if (Y * Yp1 < 0)
{
double r = X + ((Y * (Xp1 - X)) / (Y - Yp1));
if (r > 0)
if (Y < 0)
w = w + 1;
else
w = w - 1;
}
else if ((Y == 0) && (X > 0))
{
if (Yp1 > 0)
w = w + .5;
else
w = w - .5;
}
else if ((Yp1 == 0) && (Xp1 > 0))
{
if (Y < 0)
w = w + .5;
else
w = w - .5;
}
}
return w > 0 ? WindingDirection.ClockWise : WindingDirection.CounterClockwise;
}
我所提出的差异使得这个问题有点特殊,因为我已经计算了所有点的X和Y的平均值,并将其用作“多边形点”值。所以,传递一个点数组,它会在中间找到一些东西。
我还假设V i + 1在到达点数组的边界时应该环绕,以便
if (i + 1 == points.Count)
// use points[0] instead
这还没有得到优化,只是可以回答你的问题。也许你自己已经想出了这个。
这里希望有建设性的批评。 :)
答案 1 :(得分:2)
不,你必须循环点以确定绕线顺序。网上有许多算法,例如
http://www.engr.colostate.edu/~dga/dga/papers/point_in_polygon.pdf
我认为当我需要实现它时,我使用了一个来自The Graphics Gems书籍的算法,这些算法针对地球表面(地理空间应用程序)进行了修改。在我的脑海中,我认为它将成分矢量相乘并将它们相加。总计的标志给你了蜿蜒的命令。
答案 2 :(得分:0)
修改强>
忽略以下所有内容。我发现了这个问题。
上面代码的最后一行应该改为:
return w > 0 ? WindingDirection.CounterClockWise : WindingDirection.Clockwise;
到
return w > 0 ? WindingDirection.ClockWise : WindingDirection.CounterClockwise;
否则,代码效果很好,我接受了这个作为答案(但我确实想给@winwaed提供道具,他们找到了带算法的文章)。
---忽略---(仅供历史使用)
嗨,戴夫,
我不确定发生了什么(还),但我从逆时针轮廓的函数中获得了“顺时针”的错误响应。
这是其中一个轮廓,显示在Inkscape 0.48中,它有一个(新)选项,用于显示带有微小半箭头的路径方向。如您所见,外部路径逆时针旋转(即使函数顺时针返回)。
我将很快调查这个......