2012-03-02 16 views
9

W iOS próbuję określić punkt na prostokącie przeciętym przez wyobrażoną linię od punktu środkowego do obwodu prostokąt o ustalonym kącie.Znaleźć punkt CGPoint na prostokącie UIView przeciętym przez linię prostą pod danym kątem od punktu środkowego

Powiedz, że znam punkt środkowy, rozmiar prostokąta i kąt (zaczynając od 0 stopni dla Wschodu i przechodząc w kierunku przeciwnym do ruchu wskazówek zegara przez 90 dla Północy i 180 dla Zachodu i 270 dla Południowego do 360 stopni dla Wschodu) . Muszę znać współrzędne punktu przecięcia.

Nieco mylące (dla mnie) matematyczne, ale prawdopodobnie dokładne odpowiedzi na Finding points on a rectangle at a given angle doprowadziły mnie do wypróbowania następującego kodu, ale nie działa poprawnie. To pytanie jest podobne do tego, ale szukam poprawionej metody Objective-C/iOS, a nie ogólnej odpowiedzi matematycznej.

Myślę, że część problemu z kodem dotyczy pojedynczego wejścia 0 do 360 stopni (w radianach bez możliwości wprowadzenia ujemnej liczby), ale prawdopodobnie wystąpią inne problemy. Poniższy kod w większości wykorzystuje notację zdefiniowaną w the answer from belisarius, w tym moją próbę obliczenia punktów przecięcia dla każdego z czterech zdefiniowanych tam regionów.

Ten kod jest w moim UIImageView podklasy:

- (CGPoint) startingPointGivenAngleInDegrees:(double)angle { 
    double angleInRads = angle/180.0*M_PI; 
    float height = self.frame.size.height; 
    float width = self.frame.size.width; 
    float x0 = self.center.x; 
    float y0 = self.center.y; 
    // region 1 
    if (angleInRads >= -atan2(height, width) && angleInRads <= atan2(height, width)) { 
     return CGPointMake(x0 + width/2, y0 + width/2 * tan(angleInRads)); 
    } 
    // region 2 
    if (angleInRads >= atan2(height, width) && angleInRads <= M_PI - atan2(height, width)) { 
     return CGPointMake(x0 + height/(2*tan(angleInRads)),y0+height/2); 
    } 
    // region 3 
    if (angleInRads >= M_PI - atan2(height, width) && angleInRads <= M_PI + atan2(height, width)) { 
     return CGPointMake(x0 - width/2, y0 + width/2 * tan(angleInRads)); 
    } 
    // region 4 
    return CGPointMake(x0 + height/(2*tan(angleInRads)),y0-height/2);  
} 

Odpowiedz

17

Zamiast debugowania kodu, ja po prostu wyjaśnić w jaki sposób to zrobić.

Po pierwsze, kilka terminów. Zdefiniujmy xRadius jako połowę szerokości ramki, a yRadius jako połowę wysokości ramki.

Rozważ teraz cztery krawędzie ramki i rozciągnij je jako nieskończone linie. Na szczycie tych czterech linii, położyć linię, która przechodzi przez środek ramki na swojej określonym kątem:

diagram of rectangle with overlaid line

Powiedzmy, że rama jest wyśrodkowany na pochodzenie - w centrum kadru jest na współrzędnych (0,0). Możemy łatwo obliczyć, gdzie linia przekątna przecina prawą krawędź ramki: współrzędne są (xRadius, xRadius * tan(angle)). I możemy łatwo obliczyć, gdzie linia ukośna przecina górną krawędź ramki: współrzędne są (-yRadius/tan(angle), -yRadius).

(Dlaczego negować współrzędne punktu przecięcia górnej krawędzi? Ponieważ UIView układu współrzędnych jest odwrócony od normalnego matematyczny układu współrzędnych. W matematyce, y współrzędne wzrost ku górze strony. W UIView, y Współrzędne zwiększają się w kierunku dolnej części widoku.)

Możemy więc po prostu obliczyć punkt przecięcia linii z prawą krawędzią ramki. Jeśli to przecięcie znajduje się poza ramką, wiemy, że linia musi przecinać górną krawędź, aby przecinała prawą krawędź. Jak stwierdzić, czy skrzyżowanie z prawą krawędzią jest poza zasięgiem? Jeśli jego współrzędna y (xRadius * tan(angle)) jest większa niż yRadius (lub mniejsza niż -yRadius), jest poza zakresem.

tak, aby je połączyć w sposób, że zaczynają się poprzez obliczenie xRadius i yRadius:

- (CGPoint)radialIntersectionWithConstrainedRadians:(CGFloat)radians { 
    // This method requires 0 <= radians < 2 * π. 

    CGRect frame = self.frame; 
    CGFloat xRadius = frame.size.width/2; 
    CGFloat yRadius = frame.size.height/2; 

Następnie obliczyć współrzędną y przecięcia z prawego boku:

CGPoint pointRelativeToCenter; 
    CGFloat tangent = tanf(radians); 
    CGFloat y = xRadius * tangent; 

Sprawdzamy, czy punkt przecięcia znajduje się w ramce:

if (fabsf(y) <= yRadius) { 

Gdy wiemy, że jest w kadrze, musimy dowiedzieć się, czy chcemy przecięcia z prawą krawędzią lub lewą krawędzią. Jeśli kąt jest mniejszy niż π/2 (90 °) lub większy niż 3π/2 (270 °), chcemy prawą krawędź. W przeciwnym razie chcemy lewą krawędź.

 if (radians < (CGFloat)M_PI_2 || radians > (CGFloat)(M_PI + M_PI_2)) { 
      pointRelativeToCenter = CGPointMake(xRadius, y); 
     } else { 
      pointRelativeToCenter = CGPointMake(-xRadius, -y); 
     } 

Jeśli współrzędną y prawej krawędzi przecięcia • Czy * poza aut obliczamy współrzędną x przecięcia z dolnej krawędzi.

} else { 
     CGFloat x = yRadius/tangent; 

Następnie zastanawiamy się, czy chcemy górnej czy dolnej krawędzi. Jeśli kąt jest mniejszy niż π (180 °), chcemy dolną krawędź. W przeciwnym razie chcemy górnej krawędzi.

 if (radians < (CGFloat)M_PI) { 
      pointRelativeToCenter = CGPointMake(x, yRadius); 
     } else { 
      pointRelativeToCenter = CGPointMake(-x, -yRadius); 
     } 
    } 

Na koniec, obliczamy punkt obliczeniowy przez rzeczywiste centrum ramki i zwracamy. Projekt

return CGPointMake(pointRelativeToCenter.x + CGRectGetMidX(frame), 
     pointRelativeToCenter.y + CGRectGetMidY(frame)); 
} 

test tutaj: https://github.com/mayoff/stackoverflow-radial-intersection

wygląda następująco:

edgepoint screen shot

+0

wspaniałe! Dzięki. Miałem z tym trochę kłopotu, ponieważ w moim przypadku nie zdawałem sobie sprawy, że musiałem [self.superview convertPoint: thePoint toView: self] w mojej podklasie UIImageview (dla innych nowicjuszy, którzy tu przybyli). –

+0

Fantastyczna odpowiedź! – mrvincenzo

+0

Czytam twój kod, który jest doskonały, próbując zrozumieć, co zrobić, jeśli chcę obrócić obraz i nadal utrzymywać wykrywanie krawędzi? jakieś pomysły? – flooie

Powiązane problemy