如何在给定一组点的情况下计算平滑路径的控制点?

时间:2022-12-10 11:35:29

I am using UIBezierPath, but this question concerns control points for the paths, not the drawing. Given a set of points, I can render a path. However, I have not been able to figure out how to calculate the control points to have a smooth line like in a photo curves editor ( How to implement a Photoshop Curves editor in UIKit ).

我正在使用UIBezierPath,但这个问题涉及路径的控制点,而不是绘图。给定一组点,我可以渲染一条路径。但是,我无法弄清楚如何计算控制点以获得平滑线,就像在照片曲线编辑器中一样(如何在UIKit中实现Photoshop曲线编辑器)。

The closest answer I've seen is here: how can i trace the finger movement on touch for drawing smooth curves?

我见过的最接近的答案是:如何在触摸时追踪手指的移动以绘制平滑的曲线?

However, I still cannot grasp the proper calculation. To sum it up in code:

但是,我仍然无法掌握正确的计算方法。在代码中总结一下:

for (int i = 0; i< points; i++) 
{
     ...

     [path addQuadCurveToPoint:nextPoint controlPoint:WTF];
}

2 个解决方案

#1


12  

The image you linked to is an example that doesn't use quadratic curves, so I'm going to run with the image and not the code.

您链接的图像是一个不使用二次曲线的示例,因此我将使用图像而不是代码运行。

A bezier path on ios (and os x) underneath is basically a list of drawing commands and points. eg:

ios(和os x)下面的bezier路径基本上是绘图命令和点的列表。例如:

[path moveTo:CGMakePoint(1,1)];
[path curveToPoint:(10,10) controPoint1:(3,7) controlPoint2:(4,1)];
[path curveToPoint:(10,10) controPoint1:(15,17) controlPoint2:(21,11)];
[path closePath];

Results in:

结果是:

moveto (1,1)      
curveto (10,10) (3,7) (4,1) 
curveto (20,0) (15,17) (21,11)    
closepath 

Control points on a bezier path control the direction and rate of curve out of a point. The first control point(cp) controls the direction and rate of curve exiting the previous point and the second cp controls the same for the point you're curving to. For a quadratic curve (what you get using addQuadCurveToPoint:controlPoint: ), both of these points are the same, as you can see in the docs for the method here.

贝塞尔曲线路径上的控制点控制曲线的方向和速率。第一个控制点(cp)控制退出前一个点的曲线的方向和速率,第二个控制点控制与您弯曲的点相同的曲线。对于二次曲线(使用addQuadCurveToPoint:controlPoint:得到的),这两个点都是相同的,正如您在此处的方法文档中所看到的那样。

Getting a smooth curve along a set of points involves making cp1 and cp2 be colinear with each other and that line be parallel to the points at either end of that segment.

沿着一组点获得平滑曲线涉及使cp1和cp2彼此共线,并且该线平行于该段的任一端的点。

如何在给定一组点的情况下计算平滑路径的控制点?

This would look something like:

这看起来像是这样的:

[path moveTo:2];
[path curveTo:3 controlPoint1:cp1 controlPoint2:cp2];

cp1 and cp2 can be computed by choosing some constant line length and doing some geometry (i forget all my line equations right now but they're easily googleable )

cp1和cp2可以通过选择一些恒定的线长度并做一些几何来计算(我现在忘记了所有的线方程,但它们很容易上网)

Going to use #-># to designate a segment and #->#(cp#) to designate a control point for that segment's curveto call.

要使用# - >#来指定一个段,使用# - >#(cp#)来指定该段的曲线调用的控制点。

The next issue is making the curve smooth from the 2->3 segment going into 3->4 segment. At this point in your code, you should have a control point calculated for 2->3(cp2). Given your constant line length from before (this will control how sharp of a curve you get), you can calculate a cp1 for 3->4 by getting a point colinear with 2->3(cp2) and point 3 in the diagram. Then calculate a 3->4(cp2) that's colinear with 3->4(cp1) and parallel to the line that point 3 and point 4 form. Rinse and repeat through your points array.

下一个问题是使曲线从2-> 3段进入3-> 4段是平滑的。在代码中的这一点上,您应该为2-> 3(cp2)计算一个控制点。给定你之前的恒定线长度(这将控制你得到的曲线的锐度),你可以通过在图中用2-> 3(cp2)和点3获得点共线来计算3-> 4的cp1。然后计算3-> 4(cp2),其与3-> 4(cp1)的共线并且平行于点3和点4的线形成。冲洗并重复点阵。

#2


4  

I'm not sure how much this will help, but I had to do something similar to implement a curved path for notes to follow in this app, (www.app.net/hereboy). Essentially, it is a path with three curves.

我不确定这会有多大帮助,但我不得不做类似的事情来实现一个弯曲的路径,以便在这个应用程序中遵循笔记,(www.app.net/hereboy)。基本上,它是一条有三条曲线的路径。

To do this I created 4 points per curve, a starting point, an ending point, and two control points at the 25% mark and the 75% mark.

为此,我在每条曲线上创建了4个点,一个起点,一个终点,以及25%标记和75%标记处的两个控制点。

Here is the code that I wrote to do this:

这是我写的代码:

//create points along the keypath for curve.
CGMutablePathRef curvedPath = CGPathCreateMutable();
const int TOTAL_POINTS = 3;
int horizontalWiggle = 15;

int stepChangeX = (endPoint.x - viewOrigin.x) / TOTAL_POINTS;
int stepChangeY = (endPoint.y - viewOrigin.y) / TOTAL_POINTS;

for(int i = 0; i < TOTAL_POINTS; i++) {
    int startX = (int)(viewOrigin.x + i * stepChangeX);
    int startY = (int)(viewOrigin.y + i * stepChangeY);

    int endX = (int)(viewOrigin.x + (i+1) * stepChangeX);
    int endY = (int)(viewOrigin.y + (i+1) * stepChangeY);

    int cpX1 = (int)(viewOrigin.x + (i+0.25) * stepChangeX);
    if((i+1)%2) {
        cpX1 -= horizontalWiggle;
    } else {
        cpX1 += horizontalWiggle;
    }
    int cpY1 = (int)(viewOrigin.y + (i+0.25) * stepChangeY);

    int cpX2 = (int)(viewOrigin.x + (i+0.75) * stepChangeX);
    if((i+1)%2) {
        cpX2 -= horizontalWiggle;
    } else {
        cpX2 += horizontalWiggle;
    }
    int cpY2 = (int)(viewOrigin.y + (i+0.75) * stepChangeY);

    CGPathMoveToPoint(curvedPath, NULL, startX, startY);
    CGPathAddCurveToPoint(curvedPath, NULL, cpX1, cpY1, cpX2, cpY2, endX, endY);
}

Good luck!

祝你好运!

#1


12  

The image you linked to is an example that doesn't use quadratic curves, so I'm going to run with the image and not the code.

您链接的图像是一个不使用二次曲线的示例,因此我将使用图像而不是代码运行。

A bezier path on ios (and os x) underneath is basically a list of drawing commands and points. eg:

ios(和os x)下面的bezier路径基本上是绘图命令和点的列表。例如:

[path moveTo:CGMakePoint(1,1)];
[path curveToPoint:(10,10) controPoint1:(3,7) controlPoint2:(4,1)];
[path curveToPoint:(10,10) controPoint1:(15,17) controlPoint2:(21,11)];
[path closePath];

Results in:

结果是:

moveto (1,1)      
curveto (10,10) (3,7) (4,1) 
curveto (20,0) (15,17) (21,11)    
closepath 

Control points on a bezier path control the direction and rate of curve out of a point. The first control point(cp) controls the direction and rate of curve exiting the previous point and the second cp controls the same for the point you're curving to. For a quadratic curve (what you get using addQuadCurveToPoint:controlPoint: ), both of these points are the same, as you can see in the docs for the method here.

贝塞尔曲线路径上的控制点控制曲线的方向和速率。第一个控制点(cp)控制退出前一个点的曲线的方向和速率,第二个控制点控制与您弯曲的点相同的曲线。对于二次曲线(使用addQuadCurveToPoint:controlPoint:得到的),这两个点都是相同的,正如您在此处的方法文档中所看到的那样。

Getting a smooth curve along a set of points involves making cp1 and cp2 be colinear with each other and that line be parallel to the points at either end of that segment.

沿着一组点获得平滑曲线涉及使cp1和cp2彼此共线,并且该线平行于该段的任一端的点。

如何在给定一组点的情况下计算平滑路径的控制点?

This would look something like:

这看起来像是这样的:

[path moveTo:2];
[path curveTo:3 controlPoint1:cp1 controlPoint2:cp2];

cp1 and cp2 can be computed by choosing some constant line length and doing some geometry (i forget all my line equations right now but they're easily googleable )

cp1和cp2可以通过选择一些恒定的线长度并做一些几何来计算(我现在忘记了所有的线方程,但它们很容易上网)

Going to use #-># to designate a segment and #->#(cp#) to designate a control point for that segment's curveto call.

要使用# - >#来指定一个段,使用# - >#(cp#)来指定该段的曲线调用的控制点。

The next issue is making the curve smooth from the 2->3 segment going into 3->4 segment. At this point in your code, you should have a control point calculated for 2->3(cp2). Given your constant line length from before (this will control how sharp of a curve you get), you can calculate a cp1 for 3->4 by getting a point colinear with 2->3(cp2) and point 3 in the diagram. Then calculate a 3->4(cp2) that's colinear with 3->4(cp1) and parallel to the line that point 3 and point 4 form. Rinse and repeat through your points array.

下一个问题是使曲线从2-> 3段进入3-> 4段是平滑的。在代码中的这一点上,您应该为2-> 3(cp2)计算一个控制点。给定你之前的恒定线长度(这将控制你得到的曲线的锐度),你可以通过在图中用2-> 3(cp2)和点3获得点共线来计算3-> 4的cp1。然后计算3-> 4(cp2),其与3-> 4(cp1)的共线并且平行于点3和点4的线形成。冲洗并重复点阵。

#2


4  

I'm not sure how much this will help, but I had to do something similar to implement a curved path for notes to follow in this app, (www.app.net/hereboy). Essentially, it is a path with three curves.

我不确定这会有多大帮助,但我不得不做类似的事情来实现一个弯曲的路径,以便在这个应用程序中遵循笔记,(www.app.net/hereboy)。基本上,它是一条有三条曲线的路径。

To do this I created 4 points per curve, a starting point, an ending point, and two control points at the 25% mark and the 75% mark.

为此,我在每条曲线上创建了4个点,一个起点,一个终点,以及25%标记和75%标记处的两个控制点。

Here is the code that I wrote to do this:

这是我写的代码:

//create points along the keypath for curve.
CGMutablePathRef curvedPath = CGPathCreateMutable();
const int TOTAL_POINTS = 3;
int horizontalWiggle = 15;

int stepChangeX = (endPoint.x - viewOrigin.x) / TOTAL_POINTS;
int stepChangeY = (endPoint.y - viewOrigin.y) / TOTAL_POINTS;

for(int i = 0; i < TOTAL_POINTS; i++) {
    int startX = (int)(viewOrigin.x + i * stepChangeX);
    int startY = (int)(viewOrigin.y + i * stepChangeY);

    int endX = (int)(viewOrigin.x + (i+1) * stepChangeX);
    int endY = (int)(viewOrigin.y + (i+1) * stepChangeY);

    int cpX1 = (int)(viewOrigin.x + (i+0.25) * stepChangeX);
    if((i+1)%2) {
        cpX1 -= horizontalWiggle;
    } else {
        cpX1 += horizontalWiggle;
    }
    int cpY1 = (int)(viewOrigin.y + (i+0.25) * stepChangeY);

    int cpX2 = (int)(viewOrigin.x + (i+0.75) * stepChangeX);
    if((i+1)%2) {
        cpX2 -= horizontalWiggle;
    } else {
        cpX2 += horizontalWiggle;
    }
    int cpY2 = (int)(viewOrigin.y + (i+0.75) * stepChangeY);

    CGPathMoveToPoint(curvedPath, NULL, startX, startY);
    CGPathAddCurveToPoint(curvedPath, NULL, cpX1, cpY1, cpX2, cpY2, endX, endY);
}

Good luck!

祝你好运!