CGPath 复制 lineJoin 和 miterLimit 没有明显影响

CGPath copy lineJoin and miterLimit has no apparent affect

我正在使用 copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform‌​:) 抵消 CGPath。问题是偏移路径引入了各种锯齿线,这些锯齿线似乎是斜接连接的结果。将 miterLimit 更改为 0 没有任何效果,使用斜线连接也没有任何区别。

在此图像中有原始路径(在应用 strokingWithWidth 之前)、使用斜接连接的偏移路径和使用斜角连接的偏移路径。为什么使用 bevel join 没有任何影响?

使用斜接的代码(请注意,使用 CGLineJoin.round 会产生相同的结果):

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()

使用斜面的代码:

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()

这是一条由两条线段组成的路径:

这是我用 30 线宽的斜角连接描边时的样子:

如果我用相同的参数制作路径的描边副本,描边副本看起来像这样:

注意到那里的三角形了吗?这似乎是因为 Core Graphics 以一种简单的方式创建了描边副本:它沿着原始路径的每一段进行追踪,创建一个偏移 15 点的复制段。它用直线连接每个这些复制的线段(因为我指定了斜角连接)。在慢动作中,复制操作看起来像这样:

所以在关节的内侧,我们得到一个三角形,而在外侧,我们得到平坦的斜面。

当 Core Graphics 对原始路径进行描边时,那个三角形是无害的,因为 Core Graphics 使用 non-zero winding rule 来填充描边。但是当你抚摸被抚摸过的副本时,三角形就变得可见了。

现在,如果我缩小描边副本时使用的线宽,三角形就会变小。如果我然后增加用于绘制描边副本的线宽,并使用斜接连接绘制描边副本,三角形实际上最终看起来像是被填充了:

现在,假设我将原始路径中的单个关节替换为由一条很短的线连接的两个关节,在底部创建一个(非常小的)平点:

当我制作这条路径的描边副本时,副本有 两个 个内部三角形,如果我描边副本,它看起来像这样:

这就是当您制作路径的描边副本时那些奇怪的星形形状的来源:非常短的线段创建重叠的三角形。

请注意,我使用 bevel 连接制作副本。制作副本时使用斜接也会创建隐藏的三角形,因为连接的选择只会影响关节的 外部 ,而不影响关节的内部。

但是,连接的选择 在描边副本时很重要,因为使用斜接会使星星变大。请参阅 this document 以很好地说明连接样式对锐角外观的影响程度。

所以斜接使三角形的点突出得很远,这使得重叠的三角形看起来像星星。如果我使用斜角连接对描边副本进行描边,结果如下:

星星在这里几乎看不见,因为三角形是用钝角绘制的。

如果你不能接受内部三角形,你将不得不编写自己的函数(或在 Internet 上找到一个函数)来制作没有三角形的路径的描边副本,或者从副本中消除三角形.

如果您的路径完全由平面段组成,最简单的解决方案可能是使用现有的多边形裁剪库。应用于描边副本的“联合”操作应该消除内部三角形。 See this answer for example. 请注意,这些库往往是用 C++ 编写的,因此您可能必须编写一些 Objective-C++ 代码,因为 Swift 无法直接调用 C++ 代码。

如果您想知道我是如何为这个答案生成图形的,我是使用 this Swift playground.

完成的