2011-01-31 6 views
9

¿Cómo muevo una bola a lo largo de un UiBezierPath específico? ¿Es eso posible?¿Cómo detecto un toque en un UIBezierPath y muevo una pelota a lo largo de eso?

He intentado todo incluido para hacer una prueba de posicionamiento usando

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

¿Cómo puedo detectar un toque a lo largo de un camino?

+1

Un bezierpath es una clase que sólo dibuja cosas. No podrá responder a ningún evento. –

+0

Cierto, pero CGContextPathContainsPoint es una obra de ingeniería increíble, Antwan. ¡Está ahí para ti en cualquier camino bez! – Fattie

+0

¿Alguna suerte? ¿Tienes solución para esto? –

Respuesta

3

Este es un problema realmente difícil. Deberá determinar el punto en la ruta más cercana al punto tocado, que es un problema matemático complicado. Esto es lo mejor que pude encontrar usando una búsqueda rápida en Google. (Atención:. Es el código en BASIC)

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

lo admito, me siento tentado a escribir esto mismo. Sería muy útil, y me gusta un desafío.

+2

+1 * tentar tentar tentar * –

+0

Cory! Es mucho, mucho más fácil que eso en una Mac. Simplemente usa CGContextPathContainsPoint. Después de eso son tres líneas de código para iterar y encontrar dónde tocó. – Fattie

+0

Estaba asumiendo que quería colocar la pelota en el camino más cercano a donde estaba el dedo. Quizás estaba equivocado. –

14

En primer lugar ... es un hecho absoluto que:

hay, sin duda, ningún método, proporcionado por Apple,
para extraer puntos de un UIBezierPath
.

Eso es un hecho absoluto a partir de febrero de 2011, en el último sistema operativo, y todo en el horizonte. He hablado con los ingenieros relevantes de Apple sobre el problema. Es molesto en muchos contextos de programación de juegos, pero ese es el hecho. Entonces, ¿podría ser que responda tu pregunta?

En segundo lugar: no olvides que es tan fácil como un pastel animar algo junto con a UIBezierPath. Hay numerosas respuestas en SO, por ejemplo How can I animate the movement of a view or image along a curved path?

En tercer lugar: en cuanto a averiguar dónde ocurrieron los toques, si eso es lo que estás preguntando, como dijo Cory, ¡este es un problema difícil! Pero puede usar CGContextPathContainsPoint para averiguar si un punto (o contacto) estaba en una ruta de un grosor determinado.

Después de eso, suponiendo que estamos hablando de cúbicos, debe encontrar los puntos y las velocidades probables (tandas a.k.a.) a lo largo de un bezier.

Aquí es el código exacto, el total de hacer eso:Find the tangent of a point on a cubic bezier curve (on an iPhone). Lo pegué en la parte inferior, también.

Tendrá que implementar su propio MCsim o iteración para encontrar dónde golpeó, según su situación. Eso no es tan difícil.

(cuarto lugar - como una nota al pie, hay que cosa nueva donde se puede dibujar progresivamente un bezpath, probablemente no es relevante a su pregunta, pero sólo una nota.)


Para la comodidad de cualquiera que lea en el futuro, aquí está el resumen de la cuestión relacionada de las dos rutinas "prácticas" ...

Por último, aquí de la manera más sencilla posible son las dos rutinas para calcular los puntos equidistantes (de hecho, a la misma distancia) y las tangentes de ellos, a lo largo de Bezier:

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); 
    } 

Los cuatro los valores precalculados, C1 C2 C3 C4, a veces se llaman los coeficientes del bezier. (Recuerde que a b c d se suele llamar los cuatro puntos de control.) Por supuesto, t va de 0 a 1, tal vez por ejemplo cada 0.05. Simplemente llame a estas rutinas una vez para X y por separado una vez para Y. Espero que ayude a alguien!

+0

Donde los toques ocurrieron en la ruta es un problema. Pero si conocemos todos los puntos en la ruta, es posible presionar el punto en la ruta y usar detrás la animación invisible para comenzar a mover punto por punto. –

+0

Sí, gracias Joe! –

+0

eres el REY! :) –

2

Matemáticamente, no se puede determinar si un punto dentro de una curva, una matemática pura, como una curva no tiene superficie. Lo que puede hacer, es probar si el punto está dentro de la superficie definida por los límites de la curva cerrada creada por CGPathCreateCopyByStrokingPath con CGPathContainsPoint. Crea la curva que se llena para representar su curva en la pantalla.

Si se llama a CGPathCreateCopyByStrokingPath con su parámetro: CGFloat lineWidth menor o igual que el radio mínimo de curvatura de la curva original, no se intersectará automáticamente. Además, la curva original no debe auto intersecarse.

Si CGPathContainsPoint devuelve verdadero, y se cumplen ambas condiciones que mencioné, tiene la garantía de encontrar un único punto en la curva que está a una distancia de lineWidth. No hay una solución algebraica para esto, por lo que debe encontrar un punto aproximado en la curva que esté cerca del punto tocado con el método descrito aquí: Find a point, a given distance, along a simple cubic bezier curve. (On an iPhone!)

7

hay un método en la clase bezierpath llamado containsPoint: .. referir : http://developer.apple.com/library/ios/#documentation/2ddrawing/conceptual/drawingprintingios/BezierPaths/BezierPaths.html

y puede detectar el tiempo en que el punto de contacto se encuentra en el objeto Bezier Path o no. Lo he usado con mi propio método por el cual un usuario puede detectar fácilmente un toque en la ruta bezier (no dentro ni fuera si hay un círculo o una ruta cercana).

Este código permite al usuario seleccionar un objeto de dibujo de camino más blando al tocarlo y aparece una línea punteada con animación. Espero que ayude a alguien. Aquí es el código de mi propio proyecto:

-(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"]; 
} 
+0

de trazado Bézier no quiere decir que el trazado Bézier detecta falsos positivos cuando se toca el interior de la curva de Bezier lejos de la línea circunferencial – amar

+0

de código anterior se detectará un toque correcto en línea circunferencial – Rakesh

0

Swit: de respuesta de @ 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) 
} 
Cuestiones relacionadas