我正在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
我想允许用户更改上面曲线的EndPoint,并根据新的End point,使画布无效。
但问题是,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
在回答这个问题时,任何参考链接也都很感激。
答案 0 :(得分:12)
更改端点意味着两件事,即沿P1旋转和缩放因子。
比例因子(让我们称之为s)是len(p1 - p0)/ len(p2 - p0)
对于旋转因子(让我们称之为r)我将你推迟到Calculating the angle between three points in 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()(rotation 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,
答案 1 :(得分:6)
首先,我会请您查看以下文章:
您要实现的是分段复合Bézier曲线。从n个控制点的摘要页面(包括开始/结束),您可以获得(n - 1)/ 3个分段贝塞尔曲线。
控制点塑造曲线字面。如果没有给出具有新点的适当控制点,则无法创建平滑连接的贝塞尔曲线。生成它们是行不通的,因为它太复杂而且没有普遍接受的方式。
如果你没有/想要提供额外的控制点,你应该使用Catmull-Rom样条,它通过所有控制点并且将是C1连续的(导数在曲线上的任何点都是连续的)。
Java / android中的Catmull Rom Spline的链接:
底线是如果你没有控制点则不使用立方贝塞尔曲线。生成它们是一个问题,而不是解决方案。
答案 2 :(得分: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);
答案 3 :(得分: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]);