在路径上找到一个点

6
我有一个应用程序,在UIView中绘制一条贝塞尔曲线,并且当我设置Y值时,需要找到X相交点。首先,据我所知,没有办法直接找到UIBezierPath的点,但您可以定位CGPath的点。
首先,如果我“描边”我的UIBezierPath(就像我在我的代码中所做的那样),这是否会实际创建一个CGPath,或者我需要采取进一步的步骤来将其转换为CGPath
第二,我想通过提供Y的值来查找曲线在X处的交点。
我的意图是在用户移动滑块时(向左或向右移动曲线),自动计算给定Y值的X。 我的起始显示。 My starting display 当前调整滑块时会发生什么。 What happens when I currently adjust slider 我希望我的显示看起来像什么。 What I want my display too look like GraphView.h
#import <UIKit/UIKit.h>

@interface GraphView : UIView
{
    float adjust;
    int x;
    int y;
}

- (IBAction)sliderChanged:(id)sender;
- (IBAction)yChanged:(id)sender;

@property (weak, nonatomic) IBOutlet UISlider *sliderValue;
@property (weak, nonatomic) IBOutlet UITextField *xValue;
@property (weak, nonatomic) IBOutlet UITextField *yValue;

@end

GraphView.m

#import "GraphView.h"

@interface GraphView ()


@end

@implementation GraphView

@synthesize sliderValue, xValue, yValue;

- (id)initWithCoder:(NSCoder *)graphView
{
    self = [super initWithCoder:graphView];
    if (self) {
        adjust = 194;
        y = 100;
    }
    return self;
}

- (IBAction)sliderChanged:(id)sender
{
    adjust = sliderValue.value;
    // Calcualtion of the X Value and setting of xValue.text textField goes here
    [self setNeedsDisplay];
}

- (IBAction)yChanged:(id)sender
{
    y = yValue.text.intValue;
    [self setNeedsDisplay];
    [self resignFirstResponder];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch * touch = [touches anyObject];
    if(touch.phase == UITouchPhaseBegan) {
        y = yValue.text.intValue;
        [self setNeedsDisplay];
        [yValue resignFirstResponder];
    }
}

- (void)drawRect:(CGRect)rect
{
    UIBezierPath *lines = [[UIBezierPath alloc] init];
    [lines moveToPoint:CGPointMake(0, y)];
    [lines addLineToPoint:CGPointMake(200, y)];
    [lines addLineToPoint:CGPointMake(200, 280)];
    [lines setLineWidth:1];
    [[UIColor redColor] setStroke];
    float dashPattern[] = {2, 2};
    [lines setLineDash:dashPattern count:2 phase:0.0];
    [lines stroke];

    UIBezierPath *curve = [[UIBezierPath alloc] init];
    [curve moveToPoint:CGPointMake(0, 280)];
    [curve addCurveToPoint:CGPointMake(280, 0) controlPoint1:CGPointMake(adjust, 280) controlPoint2:CGPointMake(adjust, 0)];
    [curve setLineWidth:2];
    [[UIColor blueColor] setStroke];
    [curve stroke];

}

@end

经过进一步思考,我认为如果我知道控制点如何调整贝塞尔路径的曲线(使用的计算或百分比),我就可以计算相交点,而不必让我的应用程序返回相交点。我相信这是一个标准的计算/百分比,有人知道这个计算是什么或在哪里找到它吗? - Old Name
因此,计算相交的解决方案应该是De Casteljau算法。我现在没有时间实现它,但当我有时间时,我会发布我的解决方案。一旦我有了解决方案,我就可以通过提供Y的值来创建自己的计算相交方法。 - Old Name
有一篇已被删除的帖子建议使用勾股定理... 我对此的回应在下一条评论中。 - Old Name
是的,如果我已经知道三个点中的两个点的坐标,这个方法就可以使用;然而,在这种情况下,我只知道一个点的值,即 Y (0, 100),它是由我明确设置的。我只知道另外两个点的一半坐标,即 X (280, ?),我正在尝试计算交点 (?, 100) 的位置。De Casteljau 算法应该为我提供计算交点的方法,因为我知道起点、终点和两个控制点的坐标。顺便说一下,我的 UIView(图形区域)大小为 280x280 像素,这也是我知道 Y 值的一半的原因。 - Old Name
2个回答

8
一个立方贝塞尔曲线由4个点定义。
P0 = (x0, y0) = start point,
P1 = (x1, y1) = first control point,
P2 = (x2, y2) = second control point,
P3 = (x3, y3) = end point,

并且包括所有的要点。

x(t) = (1-t)^3 * x0 + 3*t*(1-t)^2 * x1 + 3*t^2*(1-t) * x2 + t^3 * x3
y(t) = (1-t)^3 * y0 + 3*t*(1-t)^2 * y1 + 3*t^2*(1-t) * y2 + t^3 * y3

其中,t的值从01

因此,要计算给定YX值,您首先需要计算一个参数值T,使得0 <= T <= 1并且

 Y = (1-T)^3 * y0 + 3*T*(1-T)^2 * y1 + 3*T^2*(1-T) * y2 + T^3 * y3      (1)

然后使用以下方法计算 X 坐标:

 X = (1-T)^3 * x0 + 3*T*(1-T)^2 * x1 + 3*T^2*(1-T) * x2 + T^3 * x3      (2)

所以你需要解决三次方程(1)并将值代入(2)。

三次方程可以通过显式求解(例如参见 http://en.wikipedia.org/wiki/Cubic_function)或迭代求解(例如使用二分法)来解决。

一般地,三次方程最多有三个不同的解。在你的具体情况中,我们有:

P0 = (0, 280), P1 = (adjust, 280), P3 = (adjust, 0), P4 = (280, 0)

这样方程(1)就变成了:

Y = (1-T)^3 * 280 + 3*T*(1-T)^2 * 280

简化后变为

Y/280 = 1 - 3*T^2 + 2*T^3    (3)

式子(3)的右边是一个关于T在区间[0, 1]内严格递减的函数,因此很容易看出当0 <= Y <= 280时,(3)有唯一解。

将这个解代入式子(2)中可得所需的X值。


非常令人印象深刻!回答很棒!迫不及待想要实现它! - Old Name
我使用了参数计算,但是遇到了一些问题。如果我将Y值设置为100像素的Y轴上的75,然后将T值设置为75%,它会返回在线条上75%处的坐标,而不是在Y值为75的位置上的值。有什么建议吗? - Old Name
经过一段时间的离开和从不同角度的观察,我认为我的解决方案是计算Y的T值,然后计算出T的值来得到X。 - Old Name
是的,我现在明白了。我想你已经提供了答案……只是我没有注意到!再次感谢! - Old Name
@ShannonBuckland:不客气。- 那么你的新问题http://stackoverflow.com/questions/16779924/parametric-expression-of-a-bezier-curve已经过时了吗? - Martin R
显示剩余12条评论

0

我花了一段时间,但以下代码是我解决在贝塞尔曲线上找到一个点的方法。这个数学公式只能找到可能的三个值中的一个,所以如果有多个解决方案,它可能会失败,但是在我的情况下,我的贝塞尔曲线只应该有一个解决方案,因为我的曲线不应该在同一X或Y平面上交叉超过一次。我想分享我拥有的内容,并欢迎任何问题、评论或建议。

#import "Calculation.h"

@implementation Calculation

@synthesize a, b, c, d, xy;

- (float) calc
{

    float squareRootCalc =
    sqrt(
    6*pow(xy,2)*b*d
    +4*a*pow(c,3)
    -3*pow(b,2)*pow(c,2)
    +9*pow(xy,2)*pow(c,2)
    -6*a*c*b*d
    +6*a*xy*c*b
    -18*pow(xy,2)*b*c
    +6*a*pow(xy,2)*c
    -12*a*xy*pow(c,2)
    -2*pow(a,2)*xy*d
    +pow(a,2)*pow(d,2)
    +4*pow(b,3)*d
    +pow(xy,2)*pow(d,2)
    -4*pow(b,3)*xy
    -4*pow(c,3)*xy
    +pow(a,2)*pow(xy,2)
    +6*c*b*d*xy
    +6*a*c*d*xy
    +6*a*b*d*xy
    -12*pow(b,2)*d*xy
    +6*xy*c*pow(b,2)
    +6*xy*b*pow(c,2)
    -2*a*pow(xy,2)*d
    -2*a*xy*pow(d,2)
    -6*c*d*pow(xy,2)
    +9*pow(xy,2)*pow(b,2)
    -6*a*pow(xy,2)*b)
    ;

    float aCalc = 24*c*d*xy + 24*a*pow(c,2) - 36*xy*pow(c,2) + 4 * squareRootCalc * a;

    float bCalc = -12 * squareRootCalc * b;

    float cCalc = 12 * squareRootCalc * c;

    float dCalc = -4 * squareRootCalc * d;


    float xyCalc =
    24*xy*a*b
    -24*xy*b*d
    -12*b*a*d
    -12*c*a*d
    -12*c*b*d
    +8*xy*a*d
    +8*pow(b,3)
    +8*pow(c,3)
    +4*pow(a,2)*d
    +24*pow(b,2)*d
    -4*xy*pow(a,2)
    -4*xy*pow(d,2)
    +4*a*pow(d,2)
    -12*c*pow(b,2)
    -12*b*pow(c,2)
    -12*a*b*c
    -24*xy*a*c
    +72*xy*c*b
    -36*xy*pow(b,2)
    ;

    float cubeRootCalc = cbrt(aCalc + bCalc + cCalc + dCalc + xyCalc);

    float denomCalc = (a-3*b+3*c-d);

    float secOneCalc = 0.5 * cubeRootCalc / denomCalc;

    float secTwoCalc = -2 * ((a*c - a*d - pow(b,2) + c*b + b*d - pow(c,2)) / (denomCalc * cubeRootCalc));

    float secThreeCalc = (a - 2*b + c) / denomCalc;

    return secOneCalc + secTwoCalc + secThreeCalc;


}

- (Calculation *) initWithA:(float)p0 andB:(float)p1 andC:(float)p2 andD:(float)p3 andXy:(float)xyValue
{
    self = [super init];

    if (self) {
        [self setA:p0];
        [self setB:p1];
        [self setC:p2];
        [self setD:p3];
        [self setXy:xyValue];
    }
    return self;
}

- (void) setA:(float)p0 andB:(float)p1 andC:(float)p2 andD:(float)p3 andXy:(float)xyValue
{
    [self setA:p0];
    [self setB:p1];
    [self setC:p2];
    [self setD:p3];
    [self setXy:xyValue];
}

@end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接