首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GraphicsPath.IsClockWise()扩展方法

GraphicsPath.IsClockWise()扩展方法
EN

Stack Overflow用户
提问于 2010-11-18 04:21:18
回答 3查看 1.5K关注 0票数 4

我需要为GraphicsPath对象创建一个扩展方法,以确定GraphicsPath是顺时针还是逆时针缠绕。

如下所示:

代码语言:javascript
复制
    public static bool IsClockwise(GraphicsPath gp)
    {
        bool bClockWise = false;
        PointF[] pts = gp.PathPoints;

        foreach (PointF pt in pts)
        {
            //??
        }

        return bClockWise;
    }

注意:我只是假设我们在这里的GraphicsPath中需要一个点的foreach,但是如果有更好的方法,请随时提供。

这样做的原因是因为GraphicsPathFillMode确定相邻GraphicsPaths的重叠部分是‘填充’(Winding)还是‘空洞’(Alternate)。但是,只有当相邻的GraphicsPaths在同一方向上缠绕时,才会出现这种情况。

如果两条路径在相反的方向上缠绕,即使将FillMode设置为Winding也会产生(不需要的)孔。

有趣的是,GraphicsPath确实有一个.Reverse()方法,允许更改路径的方向(这确实解决了问题),但是如果不知道路径的方向,就不可能知道GraphicsPath何时需要为.Reversed。

你能帮我解决这个扩展方法吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-11-18 07:22:36

我之所以抛出这个问题,主要是因为这个问题引起了我的兴趣,这是我没有专业知识的东西,我想引发讨论。

我拿了Point in Polygon pseudo-code,试着让它适合你的情况。看起来你只对判断什么东西是顺时针还是逆时针旋转感兴趣。这是一个简单的测试和一个非常简单(边缘粗糙)的C#实现。

代码语言:javascript
复制
[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应该回绕,以便

代码语言:javascript
复制
if (i + 1 == points.Count)
    // use points[0] instead

这还没有优化,它只是潜在地回答了你的问题。也许你已经想出了这一点。

这里希望有建设性的批评。:)

票数 4
EN

Stack Overflow用户

发布于 2010-11-18 04:34:46

不需要,您必须在这些点上循环以确定缠绕顺序。网络上有许多算法,例如。

http://www.engr.colostate.edu/~dga/dga/papers/point_in_polygon.pdf

我想当我需要实现它的时候,我使用了Graphics Gems书籍中的一个算法,针对地球表面(一个地理空间应用程序)进行了修改。在我的脑海中,我认为它将分量向量相乘,然后求和。总数的符号告诉你发条顺序。

票数 2
EN

Stack Overflow用户

发布于 2010-11-18 11:43:26

编辑:

忽略下面的所有这些。我找到问题了。

以上代码的最后一行应从以下内容更改:

代码语言:javascript
复制
return w > 0 ? WindingDirection.CounterClockWise : WindingDirection.Clockwise;

代码语言:javascript
复制
return w > 0 ? WindingDirection.ClockWise : WindingDirection.CounterClockwise;

除此之外,代码运行得很好,我已经接受了这一点作为答案(但我确实想给@winwaed提供支持,他用这个算法找到了这篇文章)。

-忽略-(仅用于历史目的)

嗨,戴夫,

我还不确定发生了什么,但对于逆时针的轮廓,我从函数中得到了不正确的“顺时针”响应。

这是Inkscape 0.48中显示的轮廓之一,它有一个(新的)选项,可以用微小的半箭头来显示路径的方向。正如您所看到的,外部路径是逆时针旋转的(即使函数是顺时针返回的)。

我很快就会调查这件事...

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4208795

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档