SVG/EPS(矢量图)如何通过另一个路径裁剪路径?

3

我试图理解像浏览器这样的SVG程序如何按照给定的路径绘制形状。我很难理解路径是如何被绘制以及如何剪切一个形状的一部分。

例如,考虑带有顶部环的字母ÅA。SVG代码如下:

<svg viewBox="0 0 1800 1800" xmlns="http://www.w3.org/2000/svg">
<path 
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M426 822q0 -47 -24 -71.5t-80 -24.5q-104 0 -104 96q0 46 25.5 71t78.5 25q56 0 80 -25t24 -71z
M319 515h-1l-85 -264h169z
M374 822q0 51 -52 51q-50 0 -50 -51q0 -49 50 -49q52 0 52 49z
" />
</svg>

JSFIDDLE

的意思是:该代码用于绘制SVG图形,其中第一行绘制了一个A的身体,第二行绘制了一个圆形,第三行从第一行中切割了一个三角形,第四行从第二行中切割了一个小圆形。问题是,SVG程序如何理解在第二行通过绘制一个形状来理解,而在现有形状上裁剪形状呢?显然答案是如果路径在另一个路径之内,则进行裁剪,否则进行绘制。但是这并不是整个情况,因为首先需要进行大量计算才能确定路径是否在另一个路径之内,其次路径的顺序也不重要(裁剪路径不一定在绘制路径之后)。这当然不仅限于SVG,其他矢量格式例如EPS也是如此。为了增加实用性,请阅读问题时将其视为:如何解析(使用任何编程语言)上述d元素以找出哪个路径在给定的SVG中绘制(黑色),哪个路径在裁剪(白色)?

3个回答

3

一般而言,不需要解析路径。

相反,你需要将每个路径在当前分辨率下“扫描转换”为一系列矩形。每个矩形可能只有一个像素高,并且在给定的y值上可能有多个矩形。

对于要填充或描边的路径以及作为剪辑应用的路径都要这样做,然后取交集得到一系列矩形。这显然是一个更简单的任务。接着填充这些矩形。

当然,这样会产生仅在特定分辨率下正确的结果。改变分辨率会得到不同的一系列矩形。但它能以合理的速度产生正确的输出。为了绘制结果,相互交叉产生新的任意路径显然是一个更复杂的任务,但不是我们需要执行的任务。


扫描转换的概念非常有帮助,但我仍然不理解顺序独立性。在给定的分辨率下,程序会找到第一条路径内的像素。然后,将第二条路径进行扫描转换:如果像素在之前的路径内,则会删除这些像素;否则,会添加它们。在这种情况下,路径的顺序应该很重要。 - Googlebot
是的,路径的顺序很重要。路径在渲染时被当前裁剪路径所裁剪。如果您稍后更新裁剪路径,则不会更改已渲染的路径。新的裁剪仅适用于后续操作。这对于EPS绝对正确,我认为对于SVG也是如此。 - KenS

2
在下面的例子中,我使用的是你的例子中字母A的路径。
在第一个SVG元素中,字母A从右向左绘制,而孔则相反:你得到了"裁剪"效果。
在第二个例子中,我颠倒了绘制孔的部分。现在这部分与字母A的主要部分绘制方向相同。现在你不会得到"裁剪"效果。
在第三个例子中,我像之前一样使用了颠倒的路径,但我添加了fill-rule="evenodd"。现在该孔被裁剪了,因为"fill-rule属性是一个表示属性,定义用于确定形状内部部分的算法"

svg{width:30%;border:solid}
<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path 
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319 515h-1l-85 -264h169z
" />
</svg>
<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path 
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319,515L402,251L233,251L318,515L319,515z
" />
</svg>

<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319,515L402,251L233,251L318,515L319,515z
" />
</svg>


我明白你所说的方向对图形产生的影响。但是,我没有理解绘制图形的通用算法。它真的由方向决定吗?那么,标准是什么? - Googlebot

1
给定路径是“填充”还是“裁剪”取决于所使用的“绕组”算法,这由SVG fill-rule属性确定,默认为nonzero
对于nonzero模式:
引用: 该规则通过从该点向任何方向绘制到无限远处的射线,然后检查形状段穿过射线的位置来确定画布上某一点的“内部”。从零开始计数,每次路径段从左到右穿过射线时加一,并每次路径段从右到左穿过射线时减一。计数交叉后,如果结果为零,则该点在路径外部。否则,它在内部。以下图示说明了非零规则:

a visualization of the nonzero rule

“nonzero”是默认模式,这就是为什么你经常会听到旋转方向很重要的原因,因为顺时针方向创建了填充,而逆时针方向创建了剪辑。(在GeoJSON中也是这样工作的。)
对于“evenodd”模式:
该规则通过从该点向任意方向绘制一条射线并计算穿过给定形状路径段数来确定画布上该点的“内部性”。如果此数字为奇数,则该点在内部;如果为偶数,则该点在外部。以下图示说明了evenodd规则:

a visualization of the evenodd rule

“evenodd”模式更适用于理解带有孔的基本形状,但对于剪切可能不完全隔离为孤岛的任意块不够灵活。
这里有一篇名为了解SVG fill-rule属性的优秀文章,其中提供了代码示例来进一步解释它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接