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

发布于 2024-12-13 15:49:00 字数 583 浏览 2 评论 0原文

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

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

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

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

     [path addQuadCurveToPoint:nextPoint controlPoint:WTF];
}

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 ).

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

时光礼记 2024-12-20 15:49:00

您链接到的图像是一个不使用二次曲线的示例,所以我要运行
使用图像而不是代码。

ios(和 os x)上的贝塞尔曲线路径基本上是绘图命令和点的列表。例如:

[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) 控制曲线离开前一个点的方向和速率,第二个 cp 控制您要弯曲到的点的方向和速率。对于二次曲线(使用 addQuadCurveToPoint:controlPoint: 得到的结果),这两个点是相同的,正如您在方法 此处

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

Annotated curve

这看起来像:

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

cp1 和 cp2 可以通过选择一些恒定的线长度并进行一些几何计算来计算(i现在忘记了我所有的直线方程,但它们很容易在谷歌上搜索

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

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

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:

[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.

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.

Annotated curve

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 )

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

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.

冷情妓 2024-12-20 15:49:00

我不确定这会有多大帮助,但我必须做一些类似的事情来实现一个弯曲的路径,以便在此应用程序中遵循笔记(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);
}

祝你好运!

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.

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.

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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文