从UIBezierPath获取积分

时间:2014-11-11 05:03:53

标签: ios uibezierpath bezier

enter image description here

我通过以下方式绘制了上面的BezierPath:     //位置是用户触摸屏幕的位置。     //位置将是图表的最大值     CGPoint origin = CGPointMake(xStart,620.0);     CGPoint endpt = CGPointMake(xEnd,620.0);     CGPoint midpt1 = midPointForPoints(原点,位置);     CGPoint midpt2 = midPointForPoints(location,endpt);

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:origin];
[path addQuadCurveToPoint:location controlPoint:CGPointMake(midpt1.x, midpt1.y+50)];
[path addQuadCurveToPoint:endpt controlPoint:CGPointMake(midpt2.x, midpt2.y+50)];

[shapeLayer setPath:path.CGPath];

现在,我想检索位于路径上的某些x坐标的y坐标。例如,给定x = 0.0,我想得到y = 0.0,或者给定x = 300.0,y = 50.0。

我查看了一些像questionsample code这样的引用,我仍然不确定。 更新:基本上,我想做这样的事情。 enter image description here

更新 遵循@ Fang的建议:

给出等式

X = (1-t)^2*X0 + 2*t*(1-t)*X1 + t^2 *X2

我解决了t

t = ((2.0 * x0 - x1) + sqrt(((-2.0 * x0 + x1) ** 2.0) 
- ((4 * (x0 - 2.0 * x1 + x2)) * (x0 - x)))) / (2.0 * (x0 - 2.0 * x1 + x2))

t = ((2.0 * x0 - x1) - sqrt(((-2.0 * x0 + x1) ** 2.0) 
- ((4 * (x0 - 2.0 * x1 + x2)) * (x0 - x)))) / (2.0 * (x0 - 2.0 * x1 + x2))

使用此值,找到对应于X的Y(我们使用X来查找上面的t值)

Y = (1-t)^2*Y0 + 2*t*(1-t)*Y1 + t^2 *Y2

按照上面的等式,我应该得到Bezier曲线上的点的y值,但是我得到一个远离右边的点。任何进一步的帮助将非常感谢..

关注:我认为一个可能的问题是我用两个控制点调用addQuadCurveToPoint()两次,而不是用两个控制点调用一次。这是否意味着我绘制两条Bezier路径并将它们组合起来?我也在查看this以查看我的计算有什么问题,唯一的区别似乎是他在调用addQuadCurveToPoint()时使用了两个控制点。

在Fang的强烈咨询后更新:

- (float)getYFromBezierPath:(float)x location:(CGPoint)location ctrlpt1:(CGPoint)ctrlpt1 ctrlpt2:(CGPoint)ctrlpt2 endpt:(CGPoint)endpt {
    float yVal;
    float tVal;
    if (x <= location.x) {
        tVal = [self getTvalFromBezierPath:x x0Val:0.0 x1Val:ctrlpt1.x x2Val:location.x];
        yVal = [self getCoordFromBezierPath:tVal origin:0.0 p1Val:ctrlpt1.y p2Val:location.y];
    } else {
        // THIS PART IS THE PROBLEM //
        tVal = [self getTvalFromBezierPath:x x0Val:location.x x1Val:ctrlpt2.x x2Val:endpt.x];
        yVal = [self getCoordFromBezierPath:tVal origin:location.y p1Val:ctrlpt2.y p2Val:endpt.y];
    }
    return yVal;
}

- (float)getTvalFromBezierPath:(float)x x0Val:(float)x0 x1Val:(float)x1 x2Val:(float)x2 {
    float tVal = (x-x0)/(2*(x1-x0));
    return tVal;
}

- (float)getCoordFromBezierPath:(float)t origin: (float)origin p1Val: (float)p1 p2Val: (float)p2 {
// tVal = (sqrt((-2.0 * x * x1) + (x * x0) + (x * x2) + pow(x1, 2) - (x0 * x2)) + x0 - x1) / (x0 - (2.0 * x1) + x2);
    return (pow((1-t),2) * origin) + (2 * t * (1-t) * p1) + (pow(t,2) * p2);
}

上一个问题: 对于第二个Bezeir路径,y值应随着t值的增加而减小。现在,y值不断增加。我该怎么解决这个问题?经过深入调试后,我还没有发现为什么会发生这种情况,因为一切都符合文档。

2 个答案:

答案 0 :(得分:4)

应该可以沿Bezier路径获取点,因为addQuadCurveToPoint是将二次贝塞尔曲线段添加到路径中。那么,你的第一个二次贝塞尔曲线的三个控制点是(参考原始帖子中的代码片段)

P(0)=原点
P(1)=(midpt1.x,midpt1.y + 50)
P(2)=位置

您可以根据需要计算此二次贝塞尔曲线上的多个点,方法是将参数t从0改为1,任意小的增量值为

C(t)=(1-t)^ 2 * P(0)+ 2 * t *(1-t)* P(1)+ t ^ 2 * P(2)

要从给定的X值中获取Y值,您必须从t的二次多项式给定的X值求解t值:

X =(1-t)^ 2 * X0 + 2 * t *(1-t)* X1 + t ^ 2 * X2

其中X0,X1和X2是P(0),P(1)和P(2)的X坐标,这意味着X0 = origin.x,X1 = midpt1.x和X2 = location.x。 / p>

由此,我们可以得到二次方程

(X0-2 * X1 + X2) t ^ 2 + 2 (X1-X0)* t +(X0-X)= 0

您可以使用二次公式求解t。如果你的X0,X1和X2值恰好使t ^ 2项的系数为零,你可以直接求t为t =(X-X0)/(2 *(X1-X0))。

获得t值后,您可以轻松评估相应的Y值。

答案 1 :(得分:1)

CGPath是不透明的数据类型,即。在这种情况下,我们只能获得我们在创建时定义的点,例如。您创建的图表,只能获得三个点。

与示例代码一样,您可以使用CGPathApply获取这些点。如果您在代码后面附加代码,它将输出3分。

    ...
    [shapeLayer setPath:path.CGPath];
    NSMutableArray *keyPoints = [NSMutableArray array];
    CGPathApply(path.CGPath, (__bridge void *)keyPoints, getPointsFromBezier);

    NSLog(@"Points = %@", points);
}

// copied from the sample code.
void getPointsFromBezier(void *info, const CGPathElement *element)
{
    NSMutableArray *bezierPoints = (__bridge NSMutableArray *)info;
    CGPathElementType type = element->type;
    CGPoint *points = element->points;
    if (type != kCGPathElementCloseSubpath)
    {
        if ((type == kCGPathElementAddLineToPoint) ||
            (type == kCGPathElementMoveToPoint))
            [bezierPoints addObject:VALUE(0)];
        else if (type == kCGPathElementAddQuadCurveToPoint)
            [bezierPoints addObject:VALUE(1)];
        else if (type == kCGPathElementAddCurveToPoint)
            [bezierPoints addObject:VALUE(2)];
    }
}

因此,简而言之,您无法获得该图表上的每个坐标,就像您需要x / y对应的那样。