GraphicsPath.IsClockWise () Extension Method

I need to create an extension method for a GraphicsPath object that determines whether GraphicsPath is applied clockwise or counterclockwise.

Something like that:

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

Note. I just assume that we need foreach at points in GraphicsPath here, but if there is a better way, please feel free to suggest.

The reason for this is that the FillMode for GraphicsPath determines whether the overlapping sections of adjacent GraphicsPaths are " Winding " or "Holes" ( Alternate ). However, this is only true if neighboring GraphicsPaths are wound in one direction.

Fillmode Winding vs Alternate

If two paths are wound in opposite directions, even setting FillMode to Winding results in (unwanted) holes.

Interestingly, GraphicsPath DOES has a .Reverse() method that allows you to change the direction of the path (and it SOLVES the problem), but it is impossible to know WHEN GraphicsPath should be. Fixed without knowing the direction of the path.

Can you help with this extension method?

+4
source share
4 answers

I basically give up because this question caught my interest, it was that I had no experience, and I would like to create a discussion.

I took Point in the Polygon pseudo-code and tried to customize it according to your situation. It seemed that you are only interested in the fact that something is wound clockwise or counterclockwise. Here is a simple test and a very simple (rough around the edges) C # implementation.

 [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 => pX); float middleY = polygon.Average(p => pY); 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; } 

The difference I have made makes this a bit specific to your problem, so that I calculated the average values ​​of X and Y from all points and used them as the point-in-polygon value. Thus, passing only an array of points, he will find something in the middle of them.

I also made the assumption that V i + 1 should turn around when it reaches the boundaries of an array of points, so

 if (i + 1 == points.Count) // use points[0] instead 

This is not optimized, it just could potentially answer your question. And perhaps you have already come up with this yourself.

Here they hope for constructive criticism. :)

+4
source

No, you will need to iterate over the points to determine the winding order. There are a number of algorithms on the Internet, for example.

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

I think that when I needed to implement it, I used the algorithm from the books “Graphic Gems”, modified for the Earth’s surface (geospatial application). from the top of my head, I think he multiplied the component vectors and summed them. The total amount sign has given you the winding order.

+2
source

See my post here . I think this is an example of what you want.

+1
source

EDIT:

Ignore it all below. I found a problem.

The last line of code above should be changed from:

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

to

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

Otherwise, the code works fine, and I accepted this as the answer (but I want to give the details of @winwaed, who found the article with the algorithm).

--- ignore --- (for historical purposes only)

Hi Dave

I'm not sure what is happening (for now), but I get the wrong answer “clockwise” from the function for contours counterclockwise.

Here is one of the paths displayed in Inkscape 0.48, which has a (new) option to show the direction of the path with the tiny heads of a half arrow. As you can see, the outer path rotates counterclockwise (even if the function returned clockwise).

Counter clockwise winding

I will do this briefly ...

0
source

All Articles