【问题标题】:Polygon with rounded corners带圆角的多边形
【发布时间】:2015-01-15 17:36:56
【问题描述】:

我正在使用处理来尝试绘制类似于下图的内容:

我已经看到一些使用bezierVertex 用于常规形状的示例,例如this one,但我想构建一个简单的绘图方法,我可以指定我的形状的顶点,然后是角半径。类似myRoundedShape(x1, y1, x2, y2, x3, y3... xn, yn, cornerRadius) 有什么想法吗?

【问题讨论】:

  • 是未圆角多边形顶点的(xn, yn)坐标吗?如果是,这意味着圆角多边形不会穿过所有 (xn, yn) 点,对吧?
  • 是的,他们是。圆角多边形不会通过 (xn, yn) 点。
  • 找到这个可能有用的链接 (stackoverflow.com/questions/24771828/…)

标签: java processing rounded-corners bezier


【解决方案1】:

如果您需要任意角度的圆角,贝塞尔曲线几乎是最糟糕的选择,因为数学非常复杂。相反,让我们使用 Catmull-Rom 曲线,因为它们被定义为“曲线通过以下点:...”,而不是简单地由点控制

对于 3 个点 p1、p2 和 p3 之间的任意 2 条边,我们可以在 p2 的左右沿边 p1--p2 和 p2--p3 以固定距离创建新点 p2l 和 p2r p2的“半径”:

dx = p2.x-p1.x
dy = p2.y-p1.y
p2l = {x: p2.x - radius * dx, y: p2.y - radius * dy}
p2l_guide = {x: p2.x - 3 * radius * dx, y: p2.y - 3 * radius * dy}

dx = p3.x-p2.x
dy = p3.y-p2.y
p2r = {x: p2.x + radius * dx, y: p2.y + radius * dy}
p2r_guide = {x: p2.x + 3 * radius * dx, y: p2.y + 3 * radius * dy}

现在我们的边看起来像 p1--p2l 和 p2r--p3,诀窍是以一种漂亮的弧形方式“连接”p2l 和 p2r。所以让我们使用curveVertex

beginShape();
curveVertex(p2l_guide.x,p2l_guide.y);
curveVertex(p2l.x,p2l.y);
curveVertex(p2r.x,p2r.y)
curveVertex(p2r_guide.x,p2r_guide.y);
endShape();

当然,您要处理大量边对,因此您需要预处理坐标列表以改为 {p2l_guide,p2l,p2r,p2r_guide} 列表,然后将它们连接为直线p2r(n)--p2l(n+1) 然后添加由 ..._guide 点引导的从 p2l 到 p2r 的连接曲线。在代码中:http://jsfiddle.net/drzp6L0g/3

这个解决方案确实给我们留下了控制引导强度的神秘变量f,因此为了解决我们需要贝塞尔曲线,我们需要确定相同的 p2l 和 p2r点,然后应用some trigonometry 来确定我们的控制点需要是什么才能实现圆弧的近似。它会更准确,但也需要更多工作。

当然,理想的解决方案是简单地使用 arcTo 命令,但 Processing 实际上没有。它有一个arc() 命令,与创建形状无关(所以如果你只需要轮廓......这对你来说可能很好!)但是如果你需要一个填充的形状,那就没有运气了。

【讨论】:

  • 看起来很有希望,非常感谢!稍后我会对其进行更深入的研究,然后再将您的答案标记为正确。
  • 我以后也可以用更多代码来扩展“真实”三次贝塞尔答案的答案。涉及数学,但众所周知,因此它主要是“在确保我们正确复制所需的函数后插入正确的值”。
  • 谢谢,如果您想扩展答案,请继续,但我认为它已经足够接近了。现在我没有时间深入研究它,这就是为什么我在寻找一个更直接、更神奇的解决方案。但它似乎还不存在。
  • 不仅仅是“还”,在 beginShape/endShape 命令集中没有弧函数,处理根本无法“正确”执行此操作。
  • 实际上,这个答案是相当愚蠢的,现在我想了很久,因为我们不想要“圆”角,我们只想要圆角。 “四舍五入”等腰三角形的贝塞尔曲线实际上非常简单,所以我要给你第二个答案,这将比这个简单得多 =)
【解决方案2】:

重新审视这一点,当我们想要带有“半径”的圆角时,我们实际上并不想要圆形连接,我们只希望等腰三角形“尖端”被四舍五入。使用贝塞尔曲线实际上很容易做到这一点,所以就这样吧。

我们仍然需要我们的偏移点 p2l 和 p2r,因为我们要对三角形 p2l--p2--p2r 进行四舍五入:

对于 3 个点 p1、p2 和 p3 之间的任意 2 条边,我们可以在 p2 的左右沿边 p1--p2 和 p2--p3 以固定距离创建新点 p2l 和 p2r p2的“半径”:

dx = p2.x-p1.x
dy = p2.y-p1.y
p2l = {x: p2.x - radius * dx, y: p2.y - radius * dy}

dx = p3.x-p2.x
dy = p3.y-p2.y
p2r = {x: p2.x + radius * dx, y: p2.y + radius * dy}

(请注意,我们不需要任何额外的指导点)。我们现在可以将舍入操作定义为:

start = p2l
c1 = point somewhere on line p2l--p2, ratio distance `t` from p2l and `1-t` from p2
c2 = point somewhere on line p2r--p2, using same ratio distance
end = p2r

如果我们选择比率距离为 0,那么 c1 == p2lc2 == p2r,我们得到一条直线。如果我们选择比率距离 1,那么c1 == c2 == p2,我们就有可能的最尖的曲线。对于看起来不错的曲线,0.5 或 0.75 的值就可以了。所以首先,让我们定义 c1/c2 抽象函数:

float[] roundIsosceles(Point p1, Point p2, Point p3, float t) {
  float mt = 1-t,
        c1x = (mt*p1.x + t*p2.x),
        c1y = (mt*p1.y + t*p2.y),
        c2x = (mt*p3.x + t*p2.x),  
        c2y = (mt*p3.y + t*p2.y);
  return new float[]{ c1x, c1y, c2x, c2y };
} 

现在我们可以根据closed 点列表创建合适的形状:

beginShape();
Point p1,p2,p3;
for(int i=0, last=closed.size(); i<last; i+=3) {
  p1 = closed.get(i);
  p2 = closed.get(i+1);
  p3 = closed.get(i+2);

  // rounded isosceles triangle connector values:
  float[] c = roundIsosceles(p1, p2, p3, 0.75);

  // tell Processing that we have points to add to our shape:
  vertex(p1.x,p1.y);
  bezierVertex(c[0], c[1], c[2], c[3], p3.x, p3.y); 
}
endShape(CLOSE);

完成了。结果如何?运行代码:http://jsfiddle.net/drzp6L0g/13,如图:

以及比率 0、0.25、0.5、0.75 和 1 之间差异的简单演示:

【讨论】:

  • 这是使用 Catmull-Rom 曲线在给定的“未圆角”多边形上实现圆角效果的一种非常聪明的方法。但请注意,圆角不是指定半径的圆弧。
  • 这些是真正的 Bezier 曲线,不是 Catmull-Rom 曲线(都是 Hermite 曲线,但表达和抽象方法不同)。你是对的,这只会产生很好的圆角,而不是基于给定半径的真正的圆形圆角。我可能会稍后第三次修改这个答案=)
猜你喜欢
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
  • 1970-01-01
  • 2016-09-28
  • 1970-01-01
  • 2014-09-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多