我有一个应用程序在UIView
中绘制一条bezier曲线,当我为Y设置一个值时,我需要找到X相交。首先,据我所知,没有办法找到一个直接指向UIBezierPath
,但您可以找到CGPath
的点。
首先,如果我“中风”我的UIBezierPath
(正如我在我的代码中所做的那样)这实际上是在创建一个CGPath,还是我需要采取进一步措施将其实际转换为CGPath
?
其次,我希望通过提供Y的值来找到在X处相交的曲线。
我的意图是当用户移动滑块(分别向左或向右移动曲线)时,自动为给定的Y值计算X.
我的开始展示。
当前我调整滑块时会发生什么。
我想要的显示效果如何。
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
答案 0 :(得分: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
从0
到1
。
因此,要计算给定Y值的X,首先必须计算a
参数值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)
所以你必须求解T
的三次方程(1)并将该值代入(2)。
立方方程可以明确地求解(例如参见http://en.wikipedia.org/wiki/Cubic_function)或迭代求解(例如使用http://en.wikipedia.org/wiki/Bisection_method)。
通常,三次方程最多可以有三种不同的解。在具体案例中,我们有
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]
的严格递减函数,因此不难看出(3)只有{{1} }}。
将该溶液代入(2)得到所需的X值。
答案 1 :(得分:0)
我花了一段时间,但下面的代码是我如何解决在贝松曲线上找到一个点。数学只找到一个潜在的3个值,所以我怀疑是否会有多个它会失败,但在我的情况下,我的bezier应该只有一个解决方案,因为我的曲线不应该多次穿过同一个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