How to determine endpoints of Arcs in GraphicsPath

2019-06-06 05:29发布

问题:

I have the following PathPoints and PathTypes arrays (format: X, Y, Type):

-177.477900,  11021.670000, 1
-614.447200,  11091.820000, 3
-1039.798000, 10842.280000, 3
-1191.761000, 10426.620000, 3
-1591.569000, 10493.590000, 3
-1969.963000, 10223.770000, 3
-2036.929000, 9823.960000,  3
-2055.820000, 9711.180000,  3
-2048.098000, 9595.546000,  3
-2014.380000, 9486.278000,  3

Here is what this GraphicsPath physically looks like. The 2 Arcs are very distinguishable:

I know that this GraphicsPath.PathData array was created by 2 AddArc commands. Stepping through the code in the debugger, I saw that the first 4 PathData values were added by the first AddArc command, and the remaining 6 points were added by the 2nd AddArc command.

By examining the raw pathpoints/pathtype arrays (without previously knowing that it was 2 AddArc commands so I would know that I have 2 start and end points), I would like to determine to start and end point of each arc.

I have tried several Bezier calculations to 'recreate' the points in the array, but am at a loss to determine how to determine the separate start and end points. It appears that GDI+ is combining the start/end point between the arcs (they are the same point and the arcs are connected), and I am losing the fact that one arc is ending and other one is starting.

Any ideas?

回答1:

Use the GraphicsPathIterator class in combination with the GraphicsPath.SetMarkers method.

For example:

dim gp as new GraphicsPath
gp.AddArc(-50, 0, 100, 50, 270, 90) 'Arc1
gp.SetMarkers()
gp.AddArc(0, 25, 100, 50, 270, 90) 'Arc2
Dim iterator as New GraphicsPathIterator(gp)
Dim i as Integer = 0
Dim MyPts(3) As PointF
Dim temp as New GraphicsPath
Do until i > 2
   iterator.NextMarker(temp)
   MyPts(i) = temp.PathPoints(0)
   MyPts(i + 1) = temp.GetLastPoint()
  i += 2
Loop

'Free system resources...
iterator.Dispose()

temp.Dispose()

Arc1 -> start: MyPts(0); end: MyPts(1)
Arc2 -> start: MyPts(2); end: MyPts(3)

Hope this helps!



回答2:

Take a look at the PathPointType Enum (System.Drawing.Drawing2D). Value Meaning 0 Start (path) 1 Line 3 Bezier/Bezier3 7 PathType Mask 16 Dash Mode 32 Path Marker 128 Close Subpath



回答3:

This one was bugging me a lot too! I had path created beyond my control without markers and couldn't figure out curve endpoints.

In this case you'd expect that the curve starts at [i + 1] but it is not! It turns out that GDI combines path points probably to make the points array shorter. In this case the curve points are: [0], [1], [2], [3].

It seems that if PathPointType.Start or PathPointType.Line is followed by PathPointType.Bezier, then you have to treat the PathPontType.Start or Path.PointType.Line as a first point of your Bezier curve, so in your example it should be like this:

 -177.47, 11021.67, 1  // Draw line to this point AND use it as a Bezier start!
 -614.44, 11091.82, 3  // Second Bezier point
-1039.79, 10842.28, 3  // Third Bezier point
-1191.76, 10426.62, 3  // Fourth Bezier point AND first point of the next Bezier!
-1591.56, 10493.59, 3  // Second Bezier point
-1969.96, 10223.77, 3  // Third Bezier point
-2036.92,  9823.96, 3  // Fourth Bezier point AND first point of the next Bezier!
-2055.82,  9711.18, 3  // Second Bezier point
-2048.09,  9595.54, 3  // Third Bezier point
-2014.38,  9486.27, 3  // Fourth Bezier point

So, when analysing PathPoints array point by point, you have to also check current and following indices. The docs on PatPointType might come in handy. In most cases you can probably ignore additional data stored on bits other than the first three (these three define Start, Line and Bezier). The only exception is CloseSubpath but it's irrelevant if you consider the next advice.

It's also worth noting that if you have a complex path that consists of huge number of PathPoints then it might be handy to split the path into chunks using GraphicsPathIterator. This simplifies the whole procedure as PathPointType.CloseSubpath can be ignored - it will be always the last point of your GraphicsPath chunk.

A quick look into Reflector or here might be helpful if you want to better understand PointTypes array.