问题描述
给定一个 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>
呈现为
预期输出:下面每个红点的坐标,以左下角为原点。 SVG 始终以 width=100px 呈现
我正在使用 python 和 svgpathtools
,我该如何实现?
我的方法:
- 看看 SVG 光栅化器。看看他们如何处理路径。我查看了 this,但遗憾的是无法很好地理解代码,无法自己实现它
- 在我的路径中添加一个
stroke-dasharray
。stroke-dasharray
会自动将连续笔画“分解”成大小相等的段。然后光栅化这个修改过的 SVG,然后使用类似 OpenCV 的东西来聚类渲染的笔划。然后找到这些簇的中点来得到答案。由于涉及多个缓慢的过程(光栅化 + 聚类),这种方法非常慢。如果路径与自身相交(无穷大符号),它也不是很灵活,因为stroke-dasharray
也会相交,给出一个奇怪的交点 - 我查看了 this question,但我不确定在我的 SVG 始终以 width=100px(根据 SVG 的纵横比自动计算的高度)呈现的情况下这将如何工作,此外还提到了上述方法这里据说是“慢”。此外,我不确定这个链接的问题是否让我指向了路径或 svg 路径的控制点
虽然方法 2 适用于许多图像,但我当然不想那样做,因为它非常慢。有没有更好的方法?
解决方法
给定一个 SVG(包含单个路径对象),我想划分 将路径分成 n 个等长的段。
我使用了满足两个条件时出现圆点的效果:
- 属性
stroke-dasharray = "(0,N)"
中的笔划(破折号)长度必须为零 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>