svg 路径上的样本点

问题描述

给定一个 SVG(包含单个路径对象),我想将路径分成 n 个等长的段。返回的值应该是光栅化 svg 中的 (n+1) 个点 (x,y) 坐标列表,指示每段的结束

示例:

给定 SVG :(这个例子只有直线,但解决方案必须适用于所有类型的路径,包括三次和二次贝塞尔曲线)

<svg height="210" width="400">
  <path d="M150 0 L75 200 L225 200 Z" fill="none" stroke="black" stroke-width="4" />
</svg>

呈现为

given-svg

预期输出:下面每个红点的坐标,以左下角为原点。 SVG 始终以 width=100px 呈现

expected-output

我正在使用 python 和 svgpathtools,我该如何实现?

我的方法

  1. 看看 SVG 光栅化器。看看他们如何处理路径。我查看了 this,但遗憾的是无法很好地理解代码,无法自己实现它
  2. 在我的路径中添加一个 stroke-dasharraystroke-dasharray自动将连续笔画“分解”成大小相等的段。然后光栅化这个修改过的 SVG,然后使用类似 OpenCV 的东西来聚类渲染的笔划。然后找到这些簇的中点来得到答案。由于涉及多个缓慢的过程(光栅化 + 聚类),这种方法非常慢。如果路径与自身相交(无穷大符号),它也不是很灵活,因为 stroke-dasharray 也会相交,给出一个奇怪的交点
  3. 我查看了 this question,但我不确定在我的 SVG 始终以 width=100px(根据 SVG 的纵横比自动计算的高度)呈现的情况下这将如何工作,此外还提到了上述方法这里据说是“慢”。此外,我不确定这个链接的问题是否让我指向了路径或 svg 路径的控制点

虽然方法 2 适用于许多图像,但我当然不想那样做,因为它非常慢。有没有更好的方法

解决方法

给定一个 SVG(包含单个路径对象),我想划分 将路径分成 n 个等长的段。

我使用了满足两个条件时出现圆点的效果:

  1. 属性 stroke-dasharray = "(0,N)" 中的笔划(破折号)长度必须为零
  2. stroke-lineCap="round"

我使用 getTotalLength() JS 方法获取 SVG 元素的完整行长

在控制台中可以看到,结果是 ~= 577.2px

假设我们需要将线的长度分成 11 个相等的段:

577,2 / 11 = 52.47 在这种情况下,stroke-dasharray =" 0,52.47 "

<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="210" width="400">
  <path stroke-dasharray="0,52.47"  stroke-lineCap="round" id="p1" d="M150 0 L75 200 L225 200 Z" fill="none" stroke="black" stroke-width="4" />
</svg>
<script>
console.log(p1.getTotalLength() / 11); 
</script>

对于 22 个相等的线段:

<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="210" width="400">
  <path stroke-dasharray="0,26.23"  stroke-lineCap="round" id="p1" d="M150 0 L75 200 L225 200 Z" fill="none" stroke="black" stroke-width="4" />
</svg>
<script>
console.log(p1.getTotalLength() /22); 
</script>

你可以增加stroke-width,点的直径会成比例的增加,但点的中心之间的距离保持不变。

stroke-width="8"

<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="210" width="400">
  <path stroke-dasharray="0,26.23"  stroke-lineCap="round" id="p1" d="M150 0 L75 200 L225 200 Z" fill="none" stroke="black" stroke-width="8" />
</svg>
<script>
console.log(p1.getTotalLength() / 22); 
</script>

更新

评论@ashutoshbsathe

在接受它之前,我会尝试使用更多的 SVG 最终答案

测试这种将 SVG 不同图形的周长分成 N 个相等部分的技术的通用性

#1。将正方形分成4份

<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="300" width="300">
        <!-- Background rectangle -->
 <path  id="back" d="M50 10 L250 10 L250 210 50 210Z" fill="none" stroke="grey" stroke-width="2" /> 
             <!-- Divide into 4 equal parts using the stroke-dasharray  -->
 <path id="p1" stroke-dasharray="0,200"  stroke-lineCap="round"  d="M50 10 L250 10 L250 210 50 210Z" fill="none" stroke="red" stroke-width="8" />
</svg>
<script>
console.log(p1.getTotalLength() / 4); 
</script>

#2。将正方形分成16份

<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="300" width="300">
        <!-- Background rectangle -->
 <path  id="back" d="M50 10 L250 10 L250 210 50 210Z" fill="none" stroke="grey" stroke-width="2" /> 
             <!-- Divide into 8 equal parts using the stroke-dasharray  -->
 <path id="p1" stroke-dasharray="0,50"  stroke-lineCap="round"  d="M50 10 L250 10 L250 210 50 210Z" fill="none" stroke="red" stroke-width="8" />
</svg>
<script>
console.log(p1.getTotalLength() / 16); 
</script>

#3。将星星分成5份

<style>
#back {
fill:none;
stroke:black;
stroke-width:2;
}
#p1 {
fill:none;
stroke:red;
stroke-width:8;
stroke-dasharray:0,179.95;
stroke-lineCap:round;
}

</style>
<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="300" width="300" viewBox="0 0 300 300">
   <!-- Background star -->
   <polygon id="back" points = "279.1,160.8 195.2,193.3 174.4,280.8   117.6,211.1 27.9,218.3 76.7,142.7 42.1,59.6 129.1,82.7 197.4,24.1 202.3,114 "/>
      <!--  red dots 5 -->
  <polygon id="p1" points = "279.1,114 "/>
</svg>
<script>
console.log(p1.getTotalLength() / 5); 
</script>

#4。将星星分成10份

<style>
#back {
fill:none;
stroke:black;
stroke-width:2;
}
#p1 {
fill:none;
stroke:red;
stroke-width:8;
stroke-dasharray:0,89.978;
stroke-lineCap:round;
}

</style>
<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="300" width="300" viewBox="0 0 300 300">
   <!-- Background star -->
   <polygon id="back" points = "279.1,114 "/>
      <!--  red dots 10 -->
  <polygon id="p1" points = "279.1,114 "/>
</svg>
<script>
console.log(p1.getTotalLength() / 10); 
</script>

#5。将六边形分成6份

<style>
#back {
fill:none;
stroke:black;
stroke-width:1;
}
#p1 {
fill:none;
stroke:red;
stroke-width:4;
stroke-dasharray:0,31.64;
stroke-lineCap:round;
}

</style>
<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="300" width="300" viewBox="-5 0 100 100">
   <!-- Background hexagon -->
   <path id="back"  d="M48.9 60.9 16.9 60.9 1.2 33.4 17.1 6.1 48.7 6.2 64.4 33.6z"/>
      <!-- Six red dots -->
  <path id="p1"  d="M48.9 60.9 16.9 60.9 1.2 33.4 17.1 6.1 48.7 6.2 64.4 33.6z"/>
</svg>
<script>
console.log(p1.getTotalLength() / 6); 
</script>

#6。将六边形分成12份

#back {
fill:none;
stroke:black;
stroke-width:1;
}
#p1 {
fill:none;
stroke:red;
stroke-width:4;
stroke-dasharray:0,15.82;
stroke-lineCap:round;
}
<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink" height="300" width="300" viewBox="-5 0 100 100">
   <!-- Background hexagon -->
   <path id="back"  d="M48.9 60.9 16.9 60.9 1.2 33.4 17.1 6.1 48.7 6.2 64.4 33.6z"/>
      <!--  red dots 12 -->
  <path id="p1"  d="M48.9 60.9 16.9 60.9 1.2 33.4 17.1 6.1 48.7 6.2 64.4 33.6z"/>
</svg>
<script>
console.log(p1.getTotalLength() /12); 
</script>