GraphicsPath.IsClockWise()扩展方法
我需要为GraphicsPath
对象创建扩展方法,以确定GraphicsPath
是顺时针还是逆时针缠绕的。GraphicsPath.IsClockWise()扩展方法
事情是这样的:
public static bool IsClockwise(GraphicsPath gp)
{
bool bClockWise = false;
PointF[] pts = gp.PathPoints;
foreach (PointF pt in pts)
{
//??
}
return bClockWise;
}
注:我只是假设,我们需要在这里GraphicsPath
的点foreach
,但如果有更好的方法,欢迎随时提供。
这样做的原因是因为GraphicsPath
的FillMode
确定是否相邻GraphicsPaths的重叠部分被“填充”(Winding
)或“洞”(Alternate
)。但是,只有当相邻的GraphicsPath被缠绕在相同的方向时,这才是真实的。
如果两个路径缠绕在相对的方向,即使是设置在(不想要的)孔FillMode
到Winding
结果。
有趣的是,GraphicsPath的确实有.Reverse()
方法,它允许一个改变路径的方向(这确实解决该问题),但它不可能知道什么时候需要的GraphicsPath无需知道待.Reversed路径的方向。
你能用这个扩展方法来帮忙吗?
我主要把这件事扔到那里,因为这个问题引起了我的兴趣,这是我没有专门知识,我想讨论。
我拿了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
这并没有经过优化的边界环绕,它只是东西可能会回答你的问题。也许你已经自己想出了这个。
在这里我们希望为有建设性的批评。 :)
不,您将不得不循环点确定收尾顺序。网络上有许多算法,例如。
http://www.engr.colostate.edu/~dga/dga/papers/point_in_polygon.pdf
我想,当我需要实现它,我用从图形宝石本本的算法,修改为地球表面(地理空间应用程序)。关闭我的头顶我认为它乘以分量矢量并将它们相加。总额的标志给了你清盘的顺序。
See my post here。我认为它有一个正是你想要的例子。
谢谢#wageoghe。我卡与.net 3.5,但即使没有.ZIP的东西,也有一些有趣的代码位,我需要更仔细(特别是“GreensTheorem”)。谢谢! – Flipster 2010-11-17 21:53:41
如果你在SO上搜索一下,你应该能够找到你可以在3.5上使用的Zip的实现,因为你没有4.0的Zip的访问权限。我也会看,并尝试发布。现在远离电脑,所以不容易从iPhone发布等。 – wageoghe 2010-11-17 22:09:35
这是Eric Lippert的一个实现。 http://blogs.msdn.com/b/ericlippert/archive/2009/05/07/zip-me-up.aspx – wageoghe 2010-11-17 22:32:26
编辑:
忽略这一切之下。我发现了这个问题。
上述代码的最后一行应改为从:
return w > 0 ? WindingDirection.CounterClockWise : WindingDirection.Clockwise;
到
return w > 0 ? WindingDirection.ClockWise : WindingDirection.CounterClockwise;
否则,代码的伟大工程,我已经接受了这个作为答案(但我想给道具发现文章的@winwaed用算法)。
---忽略---(出于历史目的)
喜戴夫,
我不知道发生了什么(还),但我得到的不正确的响应“顺时针“来自逆时针轮廓的功能。
这里是轮廓,在Inkscape中0.48,其中有一个(新)选项以显示与小的半箭头头部的路径的方向上显示中的一个。如您所见,外部路径逆时针旋转(即使函数顺时针返回)。
我要去看看这个不久...
我觉得这个问题恶化到不得不找一个内点。对于凸多边形来说这很容易。 – 2010-11-17 21:03:10