代码之家  ›  专栏  ›  技术社区  ›  Flipster

GraphicsPath.IsClockWise()扩展方法

  •  4
  • Flipster  · 技术社区  · 14 年前

    我需要为 GraphicsPath 对象,该对象确定 图形空间 顺时针或逆时针缠绕。

    像这样的:

        public static bool IsClockwise(GraphicsPath gp)
        {
            bool bClockWise = false;
            PointF[] pts = gp.PathPoints;
    
            foreach (PointF pt in pts)
            {
                //??
            }
    
            return bClockWise;
        }
    

    注:我只是假设我们需要一个 foreach 关于 图形空间 在这里,但如果有更好的方法,请随时提供。

    原因是 FillMode 图形空间 确定相邻图形的重叠部分是否为“填充”( Winding )或“洞”( Alternate ). 但是,只有在相邻的图形沿同一方向缠绕时,这才是正确的。

    Fillmode Winding vs Alternate

    如果这两条路在相反的方向缠绕,甚至设置 填充模式 弯曲的 导致(不需要的)孔。

    有趣的是,GraphicsPath确实有 .Reverse() 方法,该方法允许更改路径的方向(这确实解决了问题),但不可能知道何时需要更改GraphicsPath。在不知道路径方向的情况下反转。

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

    3 回复  |  直到 14 年前
        1
  •  4
  •   Dave White John Alexiou    14 年前

    我之所以这么说,主要是因为这个问题引起了我的兴趣,这是我没有专业知识的问题,我想引起大家的讨论。

    我拿走了 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
    

    这还没有优化,它只是一些潜在的答案你的问题。也许你已经自己想出来了。

    希望能得到建设性的批评。:)

        2
  •  2
  •   winwaed    14 年前

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

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

    我想当我需要实现它的时候,我使用了一个来自GraphicsGems书籍的算法,为地球表面修改(一个地理空间应用程序)。在我的头顶上,我认为它乘以了分量向量并求和。总数的符号给了你一个盘旋的顺序。

        3
  •  0
  •   Flipster    14 年前

    编辑:

    忽略下面的所有内容。我发现了问题。

    上述代码的最后一行应改为:

    return w > 0 ? WindingDirection.CounterClockWise : WindingDirection.Clockwise;
    

    return w > 0 ? WindingDirection.ClockWise : WindingDirection.CounterClockwise;
    

    否则,代码工作得很好,我已经接受了这个答案(但我确实想给@winwaed一些道具,他用算法找到了这篇文章)。

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

    嗨,戴夫,

    我还不确定发生了什么,但我得到了一个错误的响应“顺时针”的功能,轮廓是逆时针的。

    这是其中一个轮廓,显示在Inkscape0.48中,它有一个(新的)选项,可以用微小的半箭头来显示路径的方向。如您所见,外部路径逆时针缠绕(即使函数返回顺时针方向)。

    Counter Clockwise Winding

    我很快就会调查。。。

    推荐文章