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

时间:2011-11-06 02:02:27

标签: iphone objective-c ios quartz-graphics bezier

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

我见过的最接近的答案是:how can i trace the finger movement on touch for drawing smooth curves?

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

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

     [path addQuadCurveToPoint:nextPoint controlPoint:WTF];
}

2 个答案:

答案 0 :(得分:12)

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

下面的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];

结果:

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

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

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

Annotated curve

这看起来像是:

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

cp1和cp2可以通过选择一些常量线长度并做一些几何来计算(我现在忘记了所有的线方程,但它们是easily googleable

使用# - &gt;#指定一个段,# - &gt;#(cp#)指定该段的曲线调用的控制点。

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

答案 1 :(得分:4)

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

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

以下是我为此编写的代码:

//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);
}
祝你好运!