簡體   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