2010-10-31 18 views
14

Ich versuche, einen Farbverlauf in einem Rechteckobjekt mit einem bestimmten Winkel (Theta) zu zeichnen, wobei die Enden des Farbverlaufs den Umfang des Rechtecks ​​berühren.Finden von Punkten in einem Rechteck in einem bestimmten Winkel

Graph

Ich dachte, dass Tangens mit funktionieren würde, aber ich habe Probleme die Knicke raus. Gibt es einen einfachen Algorithmus, den ich gerade vermisse?

End Ergebnis

Also, das wird eine Funktion von (einem Winkel, RectX1, RectX2, RectY1, RectY2) sein. Ich möchte, dass es in Form von [x1, x2, y1, y2] zurückgegeben wird, sodass der Gradient über das Quadrat ziehen wird. In meinem Problem, wenn der Ursprung 0 ist, dann x2 = -x1 und y2 = -y1. Aber es wird nicht immer am Ursprung sein.

+3

was hat das Bild hat mit dem Problem zu tun? Nur ein Ende der Linie (ich nehme an, die Linie ist in diesem Fall die Hypotenuse) berührt die Grenze. Wird die Linie immer den Ursprung durchqueren (oder, wie abgebildet, beginnen)? – aaronasterling

+0

@Aaronasterling, Es ist mein Verständnis von dem, was ich erreichen möchte. Ich brauche sowohl X als auch Y. Das Dreieck ändert sich basierend auf dem Winkel. – bradlis7

Antwort

31

Nennen wir ein und b Ihre Rechteckseiten und (x0, y0) die Koordinaten des Rechtecks ​​Mitte.

Sie haben vier Regionen zu berücksichtigen:

alt text

 
    Region from    to     Where 
    ==================================================================== 
     1  -arctan(b/a)  +arctan(b/a)  Right green triangle 
     2  +arctan(b/a)  π-arctan(b/a)  Upper yellow triangle 
     3  π-arctan(b/a)  π+arctan(b/a)  Left green triangle 
     4  π+arctan(b/a)  -arctan(b/a)  Lower yellow triangle 

Mit einem wenig von Trigonometrie-fu, können wir die Koordinaten für die gewünschten Schnittpunkt in jeder Region erhalten.

alt text

So Z0 ist der Ausdruck für den Schnittpunkt für die Bereiche 1 und 3
Und Z1 wird der Ausdruck für den Schnittpunkt für die Bereiche 2 und 4

die gewünschten Zeilen Übergang von (X0, Y0) zu Z0 oder Z1 in Abhängigkeit von der Region. So erinnert, dass Tan (φ) = Sin (φ)/Cos (φ)

 

    Lines in regions  Start     End 
    ====================================================================== 
     1 and 3   (X0,Y0)  (X0 + a/2 , (a/2 * Tan(φ))+ Y0 
     2 and 4   (X0,Y0)  (X0 + b/(2* Tan(φ)) , b/2 + Y0) 

Just bewusst sein, der Anzeichen von Tan (φ) in jedem Quadranten, und dass der Winkel wird immer von der positiven x gemessen Achse ANTICLOCKWISE.

HTH!

+0

Gute Arbeit! Ich werde sehen, was ich mit dieser Info machen kann. – bradlis7

+0

Ausgezeichnete Antwort! Vielen Dank! – BillyBBone

+0

Ich verstehe nicht, was die beiden Winkel φ und θ in Ihrer Antwort oder der anderen Antwort darstellen - gibt die Frage nicht nur einen Winkel an? Und sollte es nicht eine andere x-Koordinate für den Schnittpunkt/Endpunkt im Bereich 1 im Vergleich zu 3 geben (und eine andere y-Koordinate für den Schnittpunkt im Bereich 2 im Vergleich zu 4)? –

3

Folgen Sie Ihrem Bild, ich gehe davon aus, dass das Rechteck bei (0,0) zentriert ist, und dass die obere rechte Ecke (w, h) ist. Dann bildet die Linie, die (0,0) mit (w, h) verbindet, einen Winkel φ mit der X-Achse, wobei tan (φ) = h/w ist.

Angenommen, θ > φ, suchen wir nach dem Punkt (x, y), wo die Linie, die Sie gezeichnet haben, die obere Kante des Rechtecks ​​schneidet. Dann y/x = tan (θ). Wir wissen, dass y = h, also, für x zu lösen, erhalten wir x = h/tan (θ).

Wenn θ < φ, schneidet die Linie mit der rechten Kante des Rechtecks ​​bei (x, y). Diesmal wissen wir, dass x = w, also y = tan (θ) * w.

1

Es gibt eine gute (mehr programmatische iOS/Objective-C) Antwort auf diese Frage bei Find the CGPoint on a UIView rectangle intersected by a straight line at a given angle from the center point mit folgenden Schritten:

  1. wird angenommen, dass der Winkel größer oder gleich 0 und kleiner als 2 * π , gegen den Uhrzeigersinn von 0 (Osten).
  2. Ermitteln Sie die y-Koordinate des Schnittpunkts mit der rechten Kante des Rechtecks ​​[tan (Winkel) * width/2].
  3. Überprüfen Sie, ob diese Y-Koordinate im Rechteckrahmen liegt (absoluter Wert kleiner oder gleich der halben Höhe).
  4. Wenn sich der y-Schnittpunkt im Rechteck befindet, wählen Sie die rechte Kante (Breite/2, -y coord), wenn der Winkel kleiner als π/2 oder größer als 3π/2 ist. Andernfalls wählen Sie die linke Kante (-width/2, y coord).
  5. Wenn die y-Koordinate des Schnittpunkts der rechten Kante außerhalb der Grenzen liegt, berechnen Sie die x-Koordinate des Schnittpunkts mit der unteren Kante [halbe Höhe/tan (Winkel)].
  6. Als nächstes bestimmen, ob Sie die Oberkante oder die Unterkante wollen. Wenn der Winkel kleiner als π ist, wollen wir die untere Kante (x, -halb der Höhe). Ansonsten wollen wir die obere Kante (-x koord, halbe Höhe).
  7. Dann (wenn die Mitte des Rahmens nicht 0,0 ist), versetzen Sie den Punkt um die tatsächliche Mitte des Rahmens.
9

Ok, pew!, habe ich endlich dieses hier.

HINWEIS: Ich basierte diese off von Belisarius tolle Antwort. Wenn dir das gefällt, mag bitte auch seine. Alles, was ich tat, war, was er sagte, in Code umzuwandeln.

So sieht es in Objective-C aus. Es sollte einfach genug sein, um zu Ihrer Lieblingssprache zu konvertieren.

+ (CGPoint) edgeOfView: (UIView*) view atAngle: (float) theta 
{ 
    // Move theta to range -M_PI .. M_PI 
    const double twoPI = M_PI * 2.; 
    while (theta < -M_PI) 
    { 
     theta += twoPI; 
    } 

    while (theta > M_PI) 
    { 
     theta -= twoPI; 
    } 

    // find edge ofview 
    // Ref: http://stackoverflow.com/questions/4061576/finding-points-on-a-rectangle-at-a-given-angle 
    float aa = view.bounds.size.width;           // "a" in the diagram 
    float bb = view.bounds.size.height;           // "b" 

    // Find our region (diagram) 
    float rectAtan = atan2f(bb, aa); 
    float tanTheta = tan(theta); 

    int region; 
    if ((theta > -rectAtan) 
    && (theta <= rectAtan)) 
    { 
     region = 1; 
    } 
    else if ((theta > rectAtan) 
    &&  (theta <= (M_PI - rectAtan))) 
    { 
     region = 2; 
    } 
    else if ((theta > (M_PI - rectAtan)) 
    ||  (theta <= -(M_PI - rectAtan))) 
    { 
     region = 3; 
    } 
    else 
    { 
     region = 4; 
    } 

    CGPoint edgePoint = view.center; 
    float xFactor = 1; 
    float yFactor = 1; 

    switch (region) 
    { 
     case 1: yFactor = -1;  break; 
     case 2: yFactor = -1;  break; 
     case 3: xFactor = -1;  break; 
     case 4: xFactor = -1;  break; 
    } 

    if ((region == 1) 
    || (region == 3)) 
    { 
     edgePoint.x += xFactor * (aa/2.);          // "Z0" 
     edgePoint.y += yFactor * (aa/2.) * tanTheta; 
    } 
    else                  // region 2 or 4 
    { 
     edgePoint.x += xFactor * (bb/(2. * tanTheta));      // "Z1" 
     edgePoint.y += yFactor * (bb/2.); 
    } 

    return edgePoint; 
} 

Darüber hinaus ist hier eine kleine Testansicht, die ich erstellt habe, um zu überprüfen, dass es funktioniert. Erstellen Sie diese Ansicht und legen Sie sie irgendwo hin, um einen weiteren kleinen Blick auf die Kante zu werfen.

@interface DebugEdgeView() 
{ 
    int degrees; 
    UIView *dotView; 
    NSTimer *timer; 
} 

@end 

@implementation DebugEdgeView 

- (void) dealloc 
{ 
    [timer invalidate]; 
} 


- (id) initWithFrame: (CGRect) frame 
{ 
    self = [super initWithFrame: frame]; 
    if (self) 
    { 
     self.backgroundColor = [[UIColor magentaColor] colorWithAlphaComponent: 0.25]; 
     degrees = 0; 
     self.clipsToBounds = NO; 

     // create subview dot 
     CGRect dotRect = CGRectMake(frame.size.width/2., frame.size.height/2., 20, 20); 
     dotView = [[DotView alloc] initWithFrame: dotRect]; 
     dotView.backgroundColor = [UIColor magentaColor]; 
     [self addSubview: dotView]; 

     // move it around our edges 
     timer = [NSTimer scheduledTimerWithTimeInterval: (5./360.) 
               target: self 
               selector: @selector(timerFired:) 
               userInfo: nil 
               repeats: YES]; 
    } 

    return self; 
} 


- (void) timerFired: (NSTimer*) timer 
{ 
    float radians = ++degrees * M_PI/180.; 
    if (degrees > 360) 
    { 
     degrees -= 360; 
    } 

    dispatch_async(dispatch_get_main_queue(), ^{ 
     CGPoint edgePoint = [MFUtils edgeOfView: self atAngle: radians]; 
     edgePoint.x += (self.bounds.size.width/2.) - self.center.x; 
     edgePoint.y += (self.bounds.size.height/2.) - self.center.y; 
     dotView.center = edgePoint; 
    }); 
} 

@end 
+0

Fantastischer Code! Ich habe das gerade in Java implementiert. Ich musste die Regionen 2 und 4 in die Berechnungen einbeziehen und musste einen positiven yFactor in den Regionen 1 und 3 verwenden, aber ich denke, das liegt daran, dass in Cocoa/Objective-C der Ursprung unten links ist. Bravo! Gute Arbeit! – Mike

+0

verwenden Sie Theta als Radianten oder "Grad von Osten" (im Bereich 0 bis 180 im Uhrzeigersinn und 0 bis -180 gegen den Uhrzeigersinn)? – MiltsInit

6

Javascript Version:

function edgeOfView(rect, deg) { 
 
    var twoPI = Math.PI*2; 
 
    var theta = deg * Math.PI/180; 
 
    
 
    while (theta < -Math.PI) { 
 
    theta += twoPI; 
 
    } 
 
    
 
    while (theta > Math.PI) { 
 
    theta -= twoPI; 
 
    } 
 
    
 
    var rectAtan = Math.atan2(rect.height, rect.width); 
 
    var tanTheta = Math.tan(theta); 
 
    var region; 
 
    
 
    if ((theta > -rectAtan) && (theta <= rectAtan)) { 
 
     region = 1; 
 
    } else if ((theta > rectAtan) && (theta <= (Math.PI - rectAtan))) { 
 
     region = 2; 
 
    } else if ((theta > (Math.PI - rectAtan)) || (theta <= -(Math.PI - rectAtan))) { 
 
     region = 3; 
 
    } else { 
 
     region = 4; 
 
    } 
 
    
 
    var edgePoint = {x: rect.width/2, y: rect.height/2}; 
 
    var xFactor = 1; 
 
    var yFactor = 1; 
 
    
 
    switch (region) { 
 
    case 1: yFactor = -1; break; 
 
    case 2: yFactor = -1; break; 
 
    case 3: xFactor = -1; break; 
 
    case 4: xFactor = -1; break; 
 
    } 
 
    
 
    if ((region === 1) || (region === 3)) { 
 
    edgePoint.x += xFactor * (rect.width/2.);          // "Z0" 
 
    edgePoint.y += yFactor * (rect.width/2.) * tanTheta; 
 
    } else { 
 
    edgePoint.x += xFactor * (rect.height/(2. * tanTheta));      // "Z1" 
 
    edgePoint.y += yFactor * (rect.height/2.); 
 
    } 
 
    
 
    return edgePoint; 
 
};

+0

Mann vielen Dank, ich habe gerade angefangen, es zu schreiben, dann habe ich jemanden gefunden, der es schon gemacht hat :) –

Verwandte Themen