将SVG路径转换为多边形以在Javascript Clipper中使用

5

我正在尝试使用JS Clipper对包含二次和三次贝塞尔曲线的SVG路径执行布尔运算。

JS Clipper首先处理多边形,然后执行操作,最后似乎将它们转换回SVG路径。

下面的函数给出了一个SVG路径,但下面的示例从2个多边形开始。

一个示例函数:

// Polygon Arrays are expanded for better readability

function clip() {
  var subj_polygons = [
    [{
      X: 10,
      Y: 10
    }, {
      X: 110,
      Y: 10
    }, {
      X: 110,
      Y: 110
    }, {
      X: 10,
      Y: 110
    }],
    [{
      X: 20,
      Y: 20
    }, {
      X: 20,
      Y: 100
    }, {
      X: 100,
      Y: 100
    }, {
      X: 100,
      Y: 20
    }]
  ];

  var clip_polygons = [
    [{
      X: 50,
      Y: 50
    }, {
      X: 150,
      Y: 50
    }, {
      X: 150,
      Y: 150
    }, {
      X: 50,
      Y: 150
    }],
    [{
      X: 60,
      Y: 60
    }, {
      X: 60,
      Y: 140
    }, {
      X: 140,
      Y: 140
    }, {
      X: 140,
      Y: 60
    }]
  ];

  var scale = 100;
  subj_polygons = scaleup(subj_polygons, scale);
  clip_polygons = scaleup(clip_polygons, scale);

  var cpr = new ClipperLib.Clipper();
  cpr.AddPolygons(subj_polygons, ClipperLib.PolyType.ptSubject);
  cpr.AddPolygons(clip_polygons, ClipperLib.PolyType.ptClip);

  var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
  var clip_fillType = ClipperLib.PolyFillType.pftNonZero;
  var clipTypes = [ClipperLib.ClipType.ctUnion];
  var clipTypesTexts = "Union";
  var solution_polygons, svg, cont = document.getElementById('svgcontainer');

  var i;
  for (i = 0; i < clipTypes.length; i++) {
    solution_polygons = new ClipperLib.Polygons();
    cpr.Execute(clipTypes[i], solution_polygons, subject_fillType, clip_fillType);
    console.log(polys2path(solution_polygons, scale));
  }

}

// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
  var i, j;
  if (!scale) scale = 1;
  for (i = 0; i < poly.length; i++) {
    for (j = 0; j < poly[i].length; j++) {
      poly[i][j].X *= scale;
      poly[i][j].Y *= scale;
    }
  }
  return poly;
}

// converts polygons to SVG path string
function polys2path(poly, scale) {
  var path = "",
    i, j;
  if (!scale) scale = 1;
  for (i = 0; i < poly.length; i++) {
    for (j = 0; j < poly[i].length; j++) {
      if (!j) path += "M";
      else path += "L";
      path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
    }
    path += "Z";
  }
  return path;

}
2个回答

6
我猜你是想将某种SVG路径转换为多边形。
我搜索了很多,但没有找到可靠的、开箱即用的解决方案。
SVG路径可以由十个不同的线段组成,如果考虑相对和绝对坐标,则有20个。它们被表示为path元素的d属性中的字母:相对的是mhvlcqastz,绝对的是MHVLCQASTZ。每个都有不同的属性,其中最复杂的是a(椭圆弧)。类型中最实用、最灵活的是c(立方贝塞尔曲线),因为它可以在高精度下代表所有其他类型,正如这些示例所示:http://jsbin.com/oqojan/32http://jsbin.com/oqojan/42
Raphael JS库有一个Path2Curve函数,可以将所有路径段转换为立方曲线,并且它还可以处理复杂的椭圆弧到立方体的转换。不幸的是,它有一个错误,不能处理所有可能的路径段组合,但幸运的是,有一个修复版本的库可用:http://jsbin.com/oqojan/32/edit(查看Javascript窗口)。
当所有路径段都转换为立方曲线后,它们可以转换为单独的线段。有几种方法,最好的方法似乎是一个自适应递归细分方法,它在曲线的转弯处产生更多的线段,在曲线的其他部分产生较少的线段,以实现曲线保真度和低线段计数的平衡,但不幸的是,它不能处理所有共线的情况。我成功地将AntiGrain的方法转换为Javascript,并添加了预分裂功能,它将曲线分割为本地极值(一阶导数根),之后AntiGrain方法还可以处理所有可能的共线情况:
共线水平:http://jsbin.com/ivomiq/6
不同情况集合:http://jsbin.com/ivomiq/7
随机:http://jsbin.com/ivomiq/8
共线旋转:http://jsbin.com/ivomiq/9

所有上述示例都有两条重叠的路径,以显示适应算法中可能出现的错误:红色曲线使用非常缓慢的暴力方法进行分割,绿色曲线使用AntiGrain方法进行分割。如果你没有看到任何红色的部分,那么AntiGrain的approximate()-function函数正在按预期工作。
好的,现在我们已经修复了Raphael和AntiGrain。如果我们结合这两种方法,我们可以创建一个将ANY svg路径元素转换为多边形(单个或多个子多边形)的函数。我不能100%确定这是最佳或最快的方法,但它应该是可用的。当然,最好的方法是原生浏览器实现...

这个解决方案适用于任何形状吗?由于路径到多边形的转换,我已经放弃使用你的库。实际上,我成功地使用了Phrogz的这种方法:https://gist.github.com/Phrogz/845901将曲线转换为多边形,但存在一个大问题。我的用户应该绘制非常复杂的曲线形状,并且必须进行布尔运算,而不会对任何曲线失真。这需要超过800个采样来进行多边形化,而Phrogz的解决方案在大多数情况下都无法正常工作。您认为Raphael+AG是否能更好地解决这个问题? - nicholaswmin
我的解决方案仅适用于svg路径,因此您需要先将其他形状转换为路径(请参见:https://github.com/johan/svg-js-utils/blob/master/paths.js,pathify()-function)。我认为使用我建议的解决方案可以获得非常好的转换质量(因为您可以使用角度阈值和整体缩放参数来调整精度)。我还测试了Phrogz的解决方案,这是一种典型的暴力方法,当形状复杂时速度很慢,并且容易产生过多或过少的点。Antigrain解决方案可以产生最佳数量的点。AG是什么? - Timo Kähkönen
如果我看到足够的你的代码,我可以尝试帮助你。你可以通过timo.roskapostiREMOVETHIS@hotmail.com联系我。也许最好我们通过电子邮件继续这个讨论,当解决方案被找到时,在此发布所需的步骤或函数。 - Timo Kähkönen
这只是一个打字错误,我想应该是形状的意思是路径。我的意思是我的用户应该绘制非常复杂的曲线路径而不是形状。关于Phrogz的暴力破解,它确实非常慢。 - nicholaswmin
我刚刚给你发送了一封电子邮件,我使用的地址是正确的吗? - nicholaswmin
是的,地址正确,我收到了你的电子邮件。让我们看看能否找到解决方案。 - Timo Kähkönen

0

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