问题
I am offsetting a CGPath using copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:). The problem is the offset path introduces all kinds of jagged lines that seem to be the result of a miter join. Changing the miterLimit
to 0 has no effect, and using a bevel line join also makes no difference.
In this image there is the original path (before applying strokingWithWidth
), an offset path using miter join, and an offset path using bevel join. Why doesn't using bevel join have any affect?
Code using miter (Note that using CGLineJoin.round
produces identical results):
let pathOffset = path.copy(strokingWithWidth: 4.0,
lineCap: CGLineCap.butt,
lineJoin: CGLineJoin.miter,
miterLimit: 20.0)
context.saveGState()
context.setStrokeColor(UIColor.red.cgColor)
context.addPath(pathOffset)
context.strokePath()
context.restoreGState()
Code using bevel:
let pathOffset = path.copy(strokingWithWidth: 4.0,
lineCap: CGLineCap.butt,
lineJoin: CGLineJoin.bevel,
miterLimit: 0.0)
context.saveGState()
context.setStrokeColor(UIColor.red.cgColor)
context.addPath(pathOffset)
context.strokePath()
context.restoreGState()
回答1:
Here is a path consisting of two line segments:
Here's what it looks like if I stroke it with bevel joins at a line width of 30:
If I make a stroked copy of the path with the same parameters, the stroked copy looks like this:
Notice that triangle in there? That appears because Core Graphics creates the stroked copy in a simple way: it traces along the each segment of the original path, creating a copied segment that is offset by 15 points. It joins each of these copied segments with straight lines (because I specified bevel joins). In slow motion, the copy operation looks like this:
So on the inside of the joint, we get a triangle, and on the outside, we get the flat bevel.
When Core Graphics strokes the original path, that triangle is harmless, because Core Graphics uses the non-zero winding rule to fill the stroke. But when you stroke the stroked copy, the triangle becomes visible.
Now, if I scale down the line width used when I make the stroked copy, the triangle becomes smaller. And if I then increase the line width used to draw the stroked copy, and draw the stroked copy with mitered joins, the triangle can actually end up looking like it's filled in:
Now, suppose I replace that single joint in the original path with two joints connected by a very short line, creating a (very small) flat spot on the bottom:
When I make a stroked copy of this path, the copy has two internal triangles, and if I stroke the stroked copy, it looks like this:
So that's where those weird shapes star shapes come from when you make a stroked copy of your paths: very short segments creating overlapping triangles.
Note that I made my copies with bevel joins. Using miter joins when making the copy also creates the hidden triangles, because the choice of join only affects the outside of the joint, not the inside of the joint.
However, the choice of join does matter when stroking the stroked copy, because the use of miter joins makes the stars larger. See this document for a good illustration of how much the join style can affect the appearance of an acute angle.
So the miter joins make the triangles' points stick out quite far, which makes the overlapping triangles look like a star. Here's the result if I stroke the stroked copy using bevel joins instead:
The star is nigh-invisible here because the triangles are drawn with blunted corners.
If the inner triangles are unacceptable to you, you will have to write your own function (or find one on the Internet) to make a stroked copy of the path without the triangles, or to eliminate the triangles from the copy.
If your path consists entirely of flat segments, the easiest solution is probably to use an existing polygon-clipping library. The “union” operation, applied to the stroked copy, should eliminate the inner triangles. See this answer for example. Note that these libraries tend to be written in C++, so you'll probably have to write some Objective-C++ code since Swift cannot call C++ code directly.
In case you're wondering how I generated the graphics for this answer, I did it using this Swift playground.
来源:https://stackoverflow.com/questions/45133957/cgpath-copy-linejoin-and-miterlimit-has-no-apparent-affect