当立方贝塞尔曲线中的端点变化时找到新的控制点

时间:2013-04-17 07:03:03

标签: android android-canvas bezier cubic

我正在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);

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

Output of above code

[已更新]

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)时找到新的控制点

如下图所示:

New Image

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

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

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 个答案:

答案 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)

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

  1. Bezier Curves
  2. Why B-Spline Curve
  3. B-Spline Curve Summary
  4. 您要实现的是分段复合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]);