C# Adobe Illustrator中的多段线简化是如何工作的?

C# Adobe Illustrator中的多段线简化是如何工作的?,c#,adobe-illustrator,polyline,C#,Adobe Illustrator,Polyline,我正在开发一个记录笔划的应用程序,你可以用定点设备绘制笔划 在上图中,我画了一个笔划,其中包含453个数据点。我的目标是在保持原始笔划形状的同时,大幅减少数据点的数量 对于那些感兴趣的人来说,上面所示的笔划的坐标可以作为参考 事实上,Adobe Illustrator实现了我想要实现的目标。如果我在Illustrator中画一个类似的笔画(用书法笔刷),得到的形状将简化为我们下面看到的形状。在绘制笔划时,它看起来与我的应用程序中的笔划非常相似。释放鼠标按钮后,曲线将简化为我们在此处看到的曲线:

我正在开发一个记录笔划的应用程序,你可以用定点设备绘制笔划

在上图中,我画了一个笔划,其中包含453个数据点。我的目标是在保持原始笔划形状的同时,大幅减少数据点的数量

对于那些感兴趣的人来说,上面所示的笔划的坐标可以作为参考

事实上,Adobe Illustrator实现了我想要实现的目标。如果我在Illustrator中画一个类似的笔画(用书法笔刷),得到的形状将简化为我们下面看到的形状。在绘制笔划时,它看起来与我的应用程序中的笔划非常相似。释放鼠标按钮后,曲线将简化为我们在此处看到的曲线:

我们可以看到,笔划只有14个数据点。尽管还有其他控制点定义贝塞尔样条曲线(或他们使用的任何样条曲线)的倾斜度。这里我们可以看到一些控制点:

我看过类似的算法,但这些算法似乎只从输入集中删除点。如果我没有弄错的话,我正在寻找的方法还必须在集合中引入新的点,以获得所需的曲线


我遇到过这样的问题,它们似乎是相关的。但这些似乎专注于从一小部分输入点创建平滑曲线。我觉得我的情况正好相反。

我遇到了一个问题(这个问题实际上可能是一个骗局),这个问题的答案是建议使用Ramer Douglas Peucker,然后根据需要应用曲线拟合

将提供的示例代码快速改编为我的绘图方法会产生以下曲线:

问题的输入数据已减少到28个点(使用贝塞尔样条线绘制)

我不确定Adobe到底在使用哪种方法,但到目前为止,这种方法非常适合我

适应 所以,写WPF并在这方面做了一些假设。为了处理我的案例(因为我不想调整他的代码),我编写了以下代码片段:

private List<Point> OptimizeCurve( List<Point> curve ) {
  const float tolerance = 1.5f;
  const double error    = 100.0;

  // Remember the first point in the series.
  Point startPoint = curve.First();
  // Simplify the input curve.
  List<Point> simplified = Douglas.DouglasPeuckerReduction( curve, tolerance ).ToList();
  // Create a new curve from the simplified one.
  List<System.Windows.Point> fitted = FitCurves.FitCurve( simplified.Select( p => new System.Windows.Point( p.X, p.Y ) ).ToArray(), error );
  // Convert the points back to our desired type.
  List<Point> fittedPoints = fitted.Select( p => new Point( (int)p.X, (int)p.Y ) ).ToList();
  // Add back our first point.
  fittedPoints.Insert( 0, startPoint );
  return fittedPoints;
}
私有列表曲线(列表曲线){
常数浮动公差=1.5f;
常数双误差=100.0;
//记住本系列的第一点。
点开始点=曲线。第一个();
//简化输入曲线。
简化列表=Douglas.DouglasPeuckerReduction(曲线,公差).ToList();
//从简化曲线创建新曲线。
List fitted=FitCurves.FitCurve(simplified.Select(p=>newsystem.Windows.Point(p.X,p.Y)).ToArray(),错误);
//将点转换回所需类型。
列出fittedPoints=fitted.Select(p=>newpoint((int)p.X,(int)p.Y)).ToList();
//补充我们的第一点。
装配点。插入(0,起始点);
返回装配点;
}

结果列表的格式为起始点、控制点1、控制点2、结束点。

为了复制Illustrator的path>simplify,我广泛使用了bezier simplify。最有效和最像Illustrator的是Philip J.Schneider的图形宝石简化,但还有一个附加步骤。该步骤排除了路径上的锐角点

使用贝塞尔路径:

在每个锐利的贝塞尔分段处分割路径。因此,其贝塞尔控制柄不平滑/共线或相对于线段的两条相邻曲线创建“尖锐点”的任何线段。当某个段被视为“尖锐”时,您可以设置自己定义的阈值。180度是平滑的,179.99度或170度或更低的任何角度都被视为尖锐部分的阈值

当这些路径中的每一条都在其尖锐的分段处从原始路径拆分时,将曲线拟合算法应用于每条路径,然后重新连接它们

这将保留锐利的边,但会平滑多余的线段,沿路径的其余部分拟合曲线

我的实现在paper.js中,但使用fitcurve算法可以利用相同的技术:

C:

JS:

您的数据点是以序列(保留绘制顺序)还是集合(只是点云,没有定义顺序)的形式存储的?@mbeckish:前者。它实际上是一个
列表
,按照记录的顺序填充坐标。这可能无法回答您的问题,因为它依赖于MatLab库,但它可能提供了一些见解:。另请参见。听起来您知道B样条曲线是一种方法,你只是想知道如何计算结应该是多少?我会考虑使用切比雪夫多项式。