Android 三次贝塞尔曲线端点变化时寻找新的控制点

Android 三次贝塞尔曲线端点变化时寻找新的控制点,android,android-canvas,bezier,cubic,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(

我正在我的一个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
我想允许用户更改上述曲线的端点,并基于新的端点,使画布无效

但问题是,曲线由两个控制点保持,需要在端点发生变化时重新计算

比如,我只想在端点从(300200)变为(250250)时找到新的控制点

如下图所示:

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

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


在回答这个问题时,任何参考链接都是值得赞赏的。

更改端点意味着两件事,沿P1的旋转和比例因子

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

对于旋转因子(让我们称之为r),我建议您使用它,它也提供了特定于平台的实现,但是您可以通过缩放/旋转p1相对于p0来检查正确性,结果应该是p2

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

d1 = rot(c1 - p0, factor) * s + p0
d2 = rot(c2 - p0, factor) * s + p0
为rot()定义一些伪代码(旋转)


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

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

  • 您试图实现的是一条分段复合贝塞尔曲线。从n个控制点(包括起点/终点)的摘要页面,您可以得到(n-1)/3条分段Bézier曲线

    控制点按字面上的方式塑造曲线。如果不使用新点指定适当的控制点,将无法创建平滑连接的bezier曲线。生成它们将不起作用,因为它太复杂,并且没有普遍接受的方法

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

    java/android中Catmull Rom样条线的链接:

    • (与你的问题类似)

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

    似乎您在这里旋转和缩放一个正方形,其中您知道底部两点,需要计算其他两点。这两个已知点和另外两个形成两个三角形,所以我们只需要找到三角形中的第三个点。假设终点为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);
    

    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]);
    

    您如何计算前两个控制点?你是否试图根据用户运动事件进行绘制?@ArunCThomas:我用选择默认控制点的逻辑更新了问题我很确定我在=)你能举例说明你想要实现什么,以及你希望曲线在端点改变后如何看,因为我真的不明白吗?我猜这并不难。我会使用matrix.mapPoints(p)。我自己也想到了这一点,但只有当他想使用控制点做其他事情或存储它们以供以后使用时,他才需要这样做,因为它们总是可以从p0、p1和p3中推导出来。我的答案中的“因子”是一个比例因子,因此它量化了hoe的长度,比p1-p3的长度长/短很多倍,然后是p1-p2的长度。好的,系数仅用于根据新点的比例增加或减少曲线面积。系数、比例。。。归根结底是同一件事。假设P1在(0,0)处,P2在(0,1)处,曲线长度为2;乘以系数2,得到(0,2)处的p2,曲线长度为4。它基本上是相同的图像,比例为1:2
    // 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]);