16

我正在我的一个 Android 应用程序中实现三次贝塞尔曲线逻辑。

我已经在自定义视图的 onDraw 的画布上实现了三次贝塞尔曲线代码。

// Path to draw cubic bezier curve
Path cubePath = new Path();

// Move to startPoint(200,200) (P0)
cubePath.moveTo(200,200);

// Cubic to with ControlPoint1(200,100) (C1), ControlPoint2(300,100) (C2) , EndPoint(300,200) (P1)
cubePath.cubicTo(200,100,300,100,300,200);

// Draw on Canvas
canvas.drawPath(cubePath, paint);

我在下图中可视化了上面的代码。

上述代码的输出

[更新]

Logic for selecting first control points, I've taken ,
baseX = 200 , baseY = 200 and curve_size = X of Endpoint - X of Start Point

Start Point     : x = baseX and y = baseY
Control Point 1 : x = baseX and y =  baseY - curve_size
Control Point 2 : x = baseX + curve_size and y =  baseY - curve_size
End Point       : x = baseX + curve_size and y = baseY

我想允许用户更改上面曲线的端点,并根据新的端点,使画布无效。

但问题是,Curve 由两个控制点维护,需要根据 EndPoint 的变化重新计算。

就像,我只想在 EndPoint 从 (300,200) 变为 (250,250) 时找到新的控制点

如下图所示:

新图片

请帮助我根据新的端点计算两个新的控制点,曲线形状将保持与前一个端点相同

我在搜索过程中参考以下参考链接:

http://pomax.github.io/bezierinfo/

http://jsfiddle.net/hitesh24by365/jHbVE/3/

http://en.wikipedia.org/wiki/B%C3%A9zier_curve

http://cubic-bezier.com/

在回答这个问题时,也感谢任何参考链接。

4

4 回答 4

12

更改端点意味着两件事,沿 P1 的旋转和缩放因子。

比例因子(我们称之为 s)是 len(p1 - p0) / len(p2 - p0)

对于旋转因子(我们称之为 r),我让你去计算 android 中三点之间的角度,这也提供了一个特定于平台的实现,但你可以通过缩放/旋转 p1 相对于 p0 来检查正确性,你应该得到p2 结果。

接下来,将相对于 p0 的缩放和旋转应用到 c1 和 c2。为方便起见,我将新的 c1 称为“d1”和新的 d2。

d1 = rot(c1 - p0, factor) * s + p0
d2 = rot(c2 - p0, factor) * s + p0

为 rot() 定义一些伪代码(旋转http://en.wikipedia.org/wiki/Rotation_%28mathematics%29

rot(point p, double angle){
  point q;
  q.x = p.x * cos(angle) - p.y * sin(angle);
  q.y = p.x * sin(angle) + p.y * cos(angle);
}

您的贝塞尔曲线现在相对于 p0 进行缩放和旋转,p1 更改为 p2,

于 2013-04-19T10:40:41.327 回答
6

首先,我请您查看以下文章:

  1. 贝塞尔曲线
  2. 为什么选择 B 样条曲线
  3. B 样条曲线摘要

您要实现的是分段复合贝塞尔曲线。从 n 个控制点(包括开始/结束)的摘要页面中,您可以获得 (n - 1)/3 个分段贝塞尔曲线。

控制点从字面上塑造曲线。如果您没有给新点提供适当的控制点,您将无法创建平滑连接的贝塞尔曲线。生成它们是行不通的,因为它太复杂并且没有普遍接受的方法。

如果您没有/不想提供额外的控制点,您应该使用 Catmull-Rom 样条,它通过所有控制点并且是 C1 连续的(导数在曲线上的任何点都是连续的)。

java/android 中 Catmull Rom Spline 的链接:

底线是如果您没有控制点,请不要使用三次贝塞尔曲线。生成它们是一个问题而不是解决方案。

于 2013-04-20T06:59:04.923 回答
6

似乎您在这里旋转和缩放一个正方形,您知道底部的两个点并且需要计算另外两个点。这两个已知点与另外两个形成两个三角形,所以我们只需要找到三角形中的第三个点。假设终点是 x1, y1:

PointF c1 = calculateTriangle(x0, y0, x1, y1, true); //find left third point
PointF c2 = calculateTriangle(x0, y0, x1, y1, false); //find right third point

cubePath.reset();
cubePath.moveTo(x0, y0);
cubePath.cubicTo(c1.x, c1.y, c2.x, c2.y, x1, y1);


private PointF calculateTriangle(float x1, float y1, float x2, float y2, boolean left) {
                PointF result = new PointF(0,0);
                float dy = y2 - y1;
                float dx = x2 - x1;
                float dangle = (float) (Math.atan2(dy, dx) - Math.PI /2f);
                float sideDist = (float) Math.sqrt(dx * dx + dy * dy); //square
                if (left){
                    result.x = (int) (Math.cos(dangle) * sideDist + x1);
                    result.y = (int) (Math.sin(dangle) * sideDist + y1);                    
                }else{
                    result.x = (int) (Math.cos(dangle) * sideDist + x2);
                    result.y = (int) (Math.sin(dangle) * sideDist + y2);
                }
                return result;
            }

...

还有其他方法可以做到这一点,无论您在路径中的第一个点和最后一个点之间有多少点或事件其形状都无关紧要。

//Find scale
Float oldDist = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
Float newDist = (float) Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0)); 
Float scale = newDist/oldDist;

//find angle
Float oldAngle = (float) (Math.atan2(y1 - y0, x1 - x0) - Math.PI /2f);
Float newAngle = (float) (Math.atan2(y2 - y0, x2 - x0) - Math.PI /2f);
Float angle = newAngle - oldAngle;

//set matrix
Matrix matrix = new Matrix();
matrix.postScale(scale, scale, x0, y0);
matrix.postRotate(angle, x0, y0);

//transform the path
cubePath.transform(matrix);
于 2013-04-24T17:38:25.117 回答
4

Lumis 建议的一个小变种

// Find scale
Float oldDist = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
Float newDist = (float) Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0)); 
Float scale = newDist/oldDist;

// Find angle
Float oldAngle = (float) (Math.atan2(y1 - y0, x1 - x0));
Float newAngle = (float) (Math.atan2(y2 - y0, x2 - x0));
Float angle = newAngle - oldAngle;

Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
matrix.postRotate(angle);

float[] p = { c1.x, c1.y, c2.x, c2.y };
matrix.mapVectors(p);
PointF newC1 = new PointF(p[0], p[1]);
PointF newC2 = new PointF(p[2], p[3]);
于 2013-04-25T20:35:51.320 回答