繁体   English   中英

如何检测UIBezierPath上的触摸并沿着它移动球?

[英]How do I detect a touch on a UIBezierPath and move a ball along that?

如何沿特定的UiBezierPath移动球? 那可能吗?

我已经尝试了一切,包括使用命中测试

-(BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath*)path inFillArea:(BOOL)inFill

如何检测路径上的触摸?

首先......这绝对是一个事实:

肯定没有方法,由Apple提供,
从UIBezierPath中提取点

截至2011年2月,在最新的操作系统中,这是绝对的事实,一切都在即将到来。 我已经和Apple的相关工程师讨论了这个问题。 它在很多游戏编程环境中都很烦人,但这就是事实。 那么,它可能会回答你的问题?

第二:不要忘记它是那么容易,因为馅饼动画沿 UIBezierPath东西。 在SO上有很多答案,例如, 我如何设置沿弯曲路径的视图或图像的移动动画?

第三:关于找出触摸发生的地方,如果这就是你所要求的,正如科里所说,这是一个难题! 但是您可以使用CGContextPathContainsPoint来确定点(或触摸) 是否在给定厚度的路径上。

在那之后,假设我们正在谈论立方体,你需要找到沿着贝塞尔曲线的点和可能的速度(也就是切线)。

以下是完成此操作的完整代码: 在三次贝塞尔曲线上找到一个点的切线(在iPhone上) 我也把它贴在下面。

根据您的具体情况,您必须实施自己的MCsim或迭代以找到它所在的位置。 那不是那么难。

(第四 - 作为一个脚注,有一个新的东西,你可以逐步绘制一个bezpath,可能与你的问题无关,只是一个注释。)


为了方便将来阅读,以下是两个“方便”例程的链接问题的摘要......

最后,这里以最简单的方式计算等距点(事实上,大致等距)和沿着贝塞尔曲线立方的切线的两个例程:

CGFloat bezierPoint(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
    {
    CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
    CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
    CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
    CGFloat C4 = ( a );

    return ( C1*t*t*t + C2*t*t + C3*t + C4  );
    }

CGFloat bezierTangent(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
    {
    CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
    CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
    CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
    CGFloat C4 = ( a );

    return ( ( 3.0 * C1 * t* t ) + ( 2.0 * C2 * t ) + C3 );
    }

四个预先计算的值C1 C2 C3 C4有时被称为贝塞尔曲线的系数。 (回想一下,abcd通常被称为四个控制点。)当然,t从0到1运行,也许例如每0.05。 只需为X调用一次这些例程,为Y单独调用一次 希望它可以帮到某人

bezierpath类中有一个名为containsPoint的方法:..参考: http//developer.apple.com/library/ios/#documentation/2ddrawing/conceptual/drawingprintingios/BezierPaths/BezierPaths.html

并且您可以检测触摸点是否在贝塞尔路径对象中的天气。 我已经用自己的方法使用了这个方法,用户可以通过这种方法轻松检测贝塞尔曲线路径上的触摸(如果有圆圈或关闭路径,则不在内部或外部)。

此代码允许用户在触摸时选择贝塞尔曲线路径绘图对象,并在其上显示带动画的虚线。 希望它可以帮到某人。 这是我自己项目的代码:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

    if([[self tapTargetForPath:path] containsPoint:startPoint])  // 'path' is bezier path object
          {

                [self selectShape:currentSelectedPath];// now select a new/same shape

                NSLog(@"selectedShapeIndex: %d", selectedShapeIndex);

                break;
            }
}

// this method will let you easily select a bezier path ( 15 px up and down of a path drawing)
- (UIBezierPath *)tapTargetForPath:(UIBezierPath *)path
{
    if (path == nil) {
        return nil;
    }

    CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, fmaxf(35.0f, path.lineWidth), path.lineCapStyle, path.lineJoinStyle, path.miterLimit);
    if (tapTargetPath == NULL) {
        return nil;
    }

    UIBezierPath *tapTarget = [UIBezierPath bezierPathWithCGPath:tapTargetPath];
    CGPathRelease(tapTargetPath);
    return tapTarget;
}

-(void)selectShape:(UIBezierPath *)pathToSelect
{
    centerline = [CAShapeLayer layer];
    centerline.path = pathToSelect.CGPath;
    centerline.strokeColor = [UIColor whiteColor].CGColor;
    centerline.fillColor = [UIColor clearColor].CGColor;
    centerline.lineWidth = 1.0;
    centerline.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:10], [NSNumber numberWithInt:5], nil];
    [self.layer addSublayer:centerline];

    // showing animation on line
    CABasicAnimation *dashAnimation;
    dashAnimation = [CABasicAnimation animationWithKeyPath:@"lineDashPhase"];

    [dashAnimation setFromValue:[NSNumber numberWithFloat:0.0f]];
    [dashAnimation setToValue:[NSNumber numberWithFloat:30.0f]];
    [dashAnimation setDuration:1.0f];
    [dashAnimation setRepeatCount:10000];

    [centerline addAnimation:dashAnimation forKey:@"linePhase"];
}

这是一个非常难的问题。 您需要确定最接近触摸点的路径上的点,这是一个复杂的数学问题。 这是我使用快速Google搜索找到的最好的东西。 (注意:代码在BASIC中。)

http://www.tinaja.com/glib/bezdist.pdf

我承认,我很想自己写这个。 它非常有用,我喜欢挑战。

在数学上,您无法确定曲线内的点(纯数学曲线)是否没有曲面。 您可以做的是测试点是否在由CGPathCreateCopyByStrokingPathCGPathContainsPoint创建的闭合曲线的边界定义的曲面内。 它会创建填充的曲线,以在屏幕上渲染曲线。

如果使用其参数调用CGPathCreateCopyByStrokingPathCGFloat lineWidth小于或等于原始曲线的最小曲率半径,则它将不会自相交。 原始曲线也不应该自相交。

如果CGPathContainsPoint返回true,并且满足我提到的两个条件,则可以保证在曲线上找到一个并且仅在lineWidth距离处的点。 没有代数解决方案,因此您必须使用此处描述的方法在曲线上找到接近触摸点的近似点: 沿着简单的三次贝塞尔曲线找到一个点,一个给定的距离。 (在iPhone上!)

Swit:对于@ rakesh的回答

 func tapTargetForPath(_ path: CGPath) -> UIBezierPath? {
    let path = UIBezierPath(cgPath: path)
   guard let tapTargetPath = CGPath(__byStroking: path.cgPath,
           transform: nil,
           lineWidth: CGFloat(fmaxf(35.0, Float(path.lineWidth))),
           lineCap: path.lineCapStyle,
           lineJoin: path.lineJoinStyle,
           miterLimit: path.miterLimit) else {
            return nil
    }

    return UIBezierPath(cgPath: tapTargetPath)
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM