RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 818954
Accepted
Qwertiy
Qwertiy
Asked:2020-04-25 05:43:38 +0000 UTC2020-04-25 05:43:38 +0000 UTC 2020-04-25 05:43:38 +0000 UTC

如何以编程方式获取两个 SVG 路径的交集路径?

  • 772

我们需要使两条 SVG 路径相交并获得一条表示它们相交的路径。

它是在浏览器中运行还是在 Node.js 中运行都没有关系。
你需要的正是路口,使用clip-path不合适。
如果穿越后突然需要它transform,那就不可怕了(我自己去掉)。

我认为已经有一些图书馆,但我只发现

  • svg-intersections - 返回一个交点数组,我需要path
  • path-intersection - 有些东西对我根本不起作用 - 我总是得到一个空数组
  • snap.svg - 似乎提供了一些有用的东西,但我不明白如何使用它

例如,当穿越这些路径时:

M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z
m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z

应该是(大约):

M 43.943359 11.123047 C 40.995759 11.900151 38.742188 12.572266 38.742188 12.572266 L 35.236328 14.996094 C 44.091432999999995 21.21816 55.052161 29.822765 57.455078 37.628906 L 66.939453 33.650391 63.632812 30.410156 C 58.77426 27.95814 52.364322 23.85552 52.214844 19.224609 L 43.943359 11.123047 z

这是一个包含示例路径的交互式片段(忽略颜色 - 它们只是为了清楚起见) - 您需要Пересечение从#path1和获取路径#path2:

svg { width: 10em; width: 100vmin; outline: 1px dotted blue; display: none; }
input { display: none; }
label { width: 10em; float: left; clear: left; cursor: pointer; line-height: 2em; margin: 0 .5em .25em 0; padding: 0 .25em; border: 1px solid; }
:checked + * + * + label { background: antiquewhite; color: blue; }
:checked + * + * + * + * + * + svg { display: inline-block; }
<input type=radio name=svg id=in checked>
<input type=radio name=svg id=out>
<input type=radio name=svg id=cp>

<label for=in>Данные</label>
<label for=out>Пересечение</label>
<label for=cp>Обрезка</label>

<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
  <path id="path1"
    style="fill:rgba(255,0,0,.5); stroke:red;stroke-width:0.26458332px;"
    d="M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z"
  />
  <path id="path2"
    style="fill:rgba(0,255,0,.5);stroke:green;stroke-width:0.26458332px;"
    d="m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z"
  />
</svg>

<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
  <path
    style="fill:rgba(0,0,255,.5);stroke:blue;stroke-width:0.26458332px;"
    d="M 43.943359 11.123047 C 40.995759 11.900151 38.742188 12.572266 38.742188 12.572266 L 35.236328 14.996094 C 44.091432999999995 21.21816 55.052161 29.822765 57.455078 37.628906 L 66.939453 33.650391 63.632812 30.410156 C 58.77426 27.95814 52.364322 23.85552 52.214844 19.224609 L 43.943359 11.123047 z"
  />
</svg>

<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="22 0 76 64">
  <clipPath id="clip2">
    <use xlink:href="#path2" />
  </clipPath>
  <use xlink:href="#path1" clip-path="url(#clip2)" />
</svg>

Snap.svg 示例

var p1 = "M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z"
var p2 = "m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z"

var intersection = Snap.path.intersection(p1, p2)

console.log(intersection)
.as-console-wrapper.as-console-wrapper { max-height: 100vh }
<script src=//cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js></script>

PS:这个问题是英文的。

javascript
  • 4 4 个回答
  • 10 Views

4 个回答

  • Voted
  1. Best Answer
    Bharatha
    2020-12-28T11:57:47Z2020-12-28T11:57:47Z

    经过多天的搜索,我仍然找到了一个我根本没想到会看到它的解决方案,即here。这个答案让我想起很久以前我在 PaperJS 的布尔运算中看到了这个。

    PaperJS 有 5 种不同的布尔运算:exclude、subtract、unite、intersect,divide我们将使用其中一种称为intersect. 这些操作也是同名函数,并且都返回一个新item对象,您可以使用该函数exportSVG()从中获取真正的对象SVG Path element,其中包含我们路径相交的路径。

    正确解决方案的示例

    paper.install(window);
    window.onload = function()
    {
        paper.setup('canvas');
    
        var p1 = 'M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z',
            p2 = 'm 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z',
            path1 = new Path(p1),
            path2 = new Path(p2);
    
        path1.fillColor = 'rgba(255,0,0,.5)';
        path1.position = new Point(25, 25);
    
        path2.fillColor = 'rgba(0,255,0,.5)';
        path2.position = new Point(40, 25);
        
        var result = path2.intersect(path1);
        result.selected = true;
        result.fillColor = '#77f';
    
        //exportSVG() docu: http://paperjs.org/reference/item/#exportsvg
        var svgPathElement = result.exportSVG(),
            dPath = svgPathElement.getAttribute('d');
        
        document.querySelector('path').setAttribute('d', dPath);
    
        var output = document.querySelector('#output');
        output.innerHTML = '<pre>' + dPath + '</pre>';
        output.innerHTML += '<xmp>' + svgPathElement.outerHTML + '</xmp>';
    };
    table
    {
        margin-left:14px;
        padding-left:14px;
        border-left:1px solid gray;
        display:inline-block
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-full.min.js"></script>
    
    <canvas id="canvas" width="75" height="75" resize></canvas>
    <table><tr><td><b>Наш путь пересечения в виде отдельного SVG:</b></td></tr>
    <tr><td>
        <svg width="75" height="75" viewBox="0 0 75 75">
        <path fill="rgba(0,0,255,.5)" d=""/>
        </svg>
    </td></tr></table>
    
    <div id="output"></div>

    也可以看看:

    • PaperJS布尔运算示例:排除、减去、合并、相交、除法
    • paperjs 示例
    • PaperJS 参考(文档)
    • 8
  2. Stranger in the Q
    2020-09-27T20:25:41Z2020-09-27T20:25:41Z
    1. 这是jsclipper

    2. 一个可怕的变态,但它有效:对于一个项目,我有一个混合了 2 个颜色区域的位图,选择了一种混合颜色,随后将其回溯到矢量中。

    PS:追踪 - potrace,如果有人感兴趣 - 写

    • 5
  3. Qwertiy
    2020-12-28T06:08:37Z2020-12-28T06:08:37Z

    思路如下:

    • 对于每个相交点,我们将相交的线段分成 2 个片段,每个片段
    • 稍微偏离交点到每一边,我们检查哪一边在另一条路径内
    • 我们选择必要的片段,如果需要,改变地方的开始和结束
    • 不参与交集的段按照谁拥有最后一个片段来添加
    • 有必要以某种方式小心处理段重复相交的情况

    到目前为止,只有一个未完成的尝试来计算第一点:

    var p1 = "M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z"
    var p2 = "m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z"
    
    function binSearch(l, r, f, eps) {
      while (1) {
        var m = (l+r) / 2
        if (f(m) > 0) r = m; else l = m;
        if (r-l < eps) return m
      }
    }
    
    var intersection = Snap.path.intersection(p1, p2)
    var cx = intersection[0].x, cy = intersection[0].y
    var b1 = intersection[0].bez1, b2 = intersection[0].bez2
    
    var pp1 = `M${b1[0]},${b1[1]} C ${b1.slice(2).join(" ")}`
    var pp2 = `M${b2[0]},${b2[1]} C ${b2.slice(2).join(" ")}`
    
    var t1 = intersection[0].t1
    var t2 = intersection[0].t2
    
    function selectFragment(p, b, t, dt, x, y, isSecond) {
      var bp = `M${b[0]},${b[1]} C ${b.slice(2).join(" ")}`
      var len = Snap.path.getTotalLength(bp)
      
      // Почему-то значение intersection[0].t1 неточно работает в findDotsAtSegment
      // Ищем точное бинпоиском, но это только для выбора сегмента
      // Обрезать путь надо по оригинальному значению
      var tt = binSearch(0, 1, t => Snap.path.findDotsAtSegment(...b, t).x - x, dt / 4)
      
      var ptl = Snap.path.findDotsAtSegment(...b, tt - dt)
      var ptr = Snap.path.findDotsAtSegment(...b, tt + dt)
      
      var isl = Snap.path.isPointInside(p, ptl.x, ptl.y)
      var isr = Snap.path.isPointInside(p, ptr.x, ptr.y)
      
      if (isl ^ isr) {
        if (isl) {
          var l = 0, r = t * len   // Берём начало пути b
        } else {
          var l = t * len, r = len // Берём конец пути b
        }
      } else {
        throw new Error("Both points are on the same side")
      }
      
      var pt1 = Snap.path.findDotsAtSegment(...b, l)
      var pt2 = Snap.path.findDotsAtSegment(...b, r)
      
      // не работает
      ////// Для второго фрагмента точка пересечения должна быть в начале, а для первого - в конце
      ////if (isSecond ^ (Math.abs(pt1.x-x)+Math.abs(pt1.y-y) > Math.abs(pt2.x-x)+Math.abs(pt2.y-y))) {
      ////  [l, r] = [r, l] // Нужно перевернуть фрагмент
      ////}
    
      // getSubpath может вернуть начало и конец неточно, поэтому
      // начальную M меняем на L чтобы исправить его начало - пока не готово
      var res = Snap.path.getSubpath(bp, l, r) // .replace('M', 'L')
      
      // Добавляем переход в нужную точку через L чтобы исправить неточности
      return res // isSecond ? `L ${x},${y} ${res}` : `${res}`
    }
    
    // Самый первый фрагмент не должен начинаться с линии - возвращаем M
    var p1i = selectFragment(p1, b2, t2, .001, cx, cy, false).replace('L', 'M')
    var p2i = selectFragment(p2, b1, t1, .001, cx, cy, true)
    
    console.log(p1i)
    console.log(p2i)
    
    document.getElementById('res').setAttribute('d', p1i + p2i)
    
    //[
    //  {
    //    "x": 35.21843116823025,
    //    "y": 15.00935009348013,
    //    "t1": 0.769785747695916,
    //    "t2": 0.25341822203708086,
    //    "segment1": 2,
    //    "segment2": 2,
    //    "bez1": [
    //      23.434524,
    //      23.156249,
    //      23.434524,
    //      23.156249,
    //      38.742559,
    //      12.572916,
    //      38.742559,
    //      12.572916
    //    ],
    //    "bez2": [
    //      24.379464999999996,
    //      7.4702374,
    //      31.561010999999997,
    //      13.139880000000002,
    //      74.650301,
    //      37.519345,
    //      50.837796999999995,
    //      49.803571000000005
    //    ]
    //  },
    //  ...
    //]
    svg { width: 10em; width: 100vmin; outline: 1px dotted blue; display: none; }
    input { display: none; }
    label { width: 10em; float: left; clear: left; cursor: pointer; line-height: 2em; margin: 0 .5em .25em 0; padding: 0 .25em; border: 1px solid; }
    :checked + * + * + * + label { background: antiquewhite; color: blue; }
    :checked + * + * + * + * + * + * + * + svg { display: inline-block; }
    /* svg:last-of-type { display: inline-block; position: absolute; transform: translateX(-100%); } /**/
    <script src=//cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js></script>
    
    <input type=radio name=svg id=in checked>
    <input type=radio name=svg id=out>
    <input type=radio name=svg id=cp>
    <input type=radio name=svg id=int>
    
    <label for=in>Данные</label>
    <label for=out>Пересечение</label>
    <label for=cp>Обрезка</label>
    <label for=int>Вычисленное</label>
    
    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
      <path id="path1"
        style="fill:rgba(255,0,0,.5); stroke:red;stroke-width:0.26458332px;"
        d="M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z"
      />
      <path id="path2"
        style="fill:rgba(0,255,0,.5);stroke:green;stroke-width:0.26458332px;"
        d="m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z"
      />
    </svg>
    
    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
      <path
        style="fill:rgba(0,0,255,.5);stroke:blue;stroke-width:0.26458332px;"
        d="M 43.943359 11.123047 C 40.995759 11.900151 38.742188 12.572266 38.742188 12.572266 L 35.236328 14.996094 C 44.091432999999995 21.21816 55.052161 29.822765 57.455078 37.628906 L 66.939453 33.650391 63.632812 30.410156 C 58.77426 27.95814 52.364322 23.85552 52.214844 19.224609 L 43.943359 11.123047 z"
      />
    </svg>
    
    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="22 0 76 64">
      <clipPath id="clip2">
        <use xlink:href="#path2" />
      </clipPath>
      <use xlink:href="#path1" clip-path="url(#clip2)" />
    </svg>
    
    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
      <path id="res"
        style="fill:none;stroke:black;stroke-width:0.26458332px;"
      />
    </svg>

    • 4
  4. Monkey Mutant
    2020-12-28T07:07:04Z2020-12-28T07:07:04Z

    如果没有插件,那就做一个面具,看看

    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" height="500" width="500">
       
       <defs>
        <style>
    	   #path1{
    		 fill:#fff;
    	   }
    	   #path2{
    		 fill:#000;
    	  }
    	</style>
    	
    	  <mask id="mask">
            <path id="path1" d="M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z" />
         </mask>
       </defs>
    
       <path id="path2" d="m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z" mask="url(#mask)"/>
    
    </svg>

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5