2010-10-31 29 views
14

Sto provando a disegnare un gradiente in un oggetto rettangolo, con un angolo dato (Theta), dove le estremità del gradiente stanno toccando il perimetro del rettangolo.Trovare punti su un rettangolo ad un angolo dato

Graph

ho pensato che l'utilizzo di tangenti avrebbe funzionato, ma sto avendo difficoltà a raggiungere i nodi fuori. C'è un algoritmo semplice che mi manca?

Risultato finale

Quindi, questo sta per essere una funzione di (angolo, RectX1, RectX2, RectY1, RectY2). Voglio che venga restituito sotto forma di [x1, x2, y1, y2], in modo che il gradiente possa disegnare attraverso il quadrato. Nel mio problema, se l'origine è 0, allora x2 = -x1 e y2 = -y1. Ma non sarà sempre all'origine.

+3

che cosa l'immagine ha a che fare con il problema? Solo una estremità della linea (presumo che la linea sia l'ipotenusa in questo caso) tocca il confine. la linea passerà sempre (o, come nella foto, inizierà) l'origine? – aaronasterling

+0

@aaronasterling, è la mia comprensione di ciò che sto cercando di ottenere. Ho bisogno sia di X che di Y. Il triangolo cambierà in base all'angolo. – bradlis7

risposta

31

Let un e b i fianchi rettangolo, e (x0, y0) le coordinate del vostro centro rettangolo.

si dispone di quattro regioni da considerare:

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 

Con un po 'di trigonometria-fu, siamo in grado di ottenere le coordinate per il vostro intersezione desiderato in ogni regione.

alt text

Così Z0 è l'espressione per il punto di intersezione tra le regioni 1 e 3
E Z1 è l'espressione per il punto di intersezione tra le regioni 2 e 4

Le linee desiderate passare da (X0, Y0) a Z0 o Z1 a seconda della regione. Quindi, ricordando che 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) 

Basta essere consapevoli dei segni di Tan (φ) in ogni quadrante, e che l'angolo è sempre misurata dalla x POSITIVO asse ANTICLOCKWISE.

HTH!

+0

Bel lavoro! Vedrò cosa posso fare con queste informazioni. – bradlis7

+0

Ottima risposta! Grazie! – BillyBBone

+0

Non capisco quali siano i due angoli φ e θ nella risposta o l'altra risposta - la domanda non specifica solo un angolo? E non dovrebbe esserci una coordinata x diversa per il punto di intersezione/fine nella regione 1 rispetto a 3 (e una diversa coordinata y per il punto di intersezione nella regione 2 rispetto a 4)? –

3

Seguendo la tua immagine, assumerò che il rettangolo sia centrato a (0,0) e che l'angolo in alto a destra sia (w, h). Quindi la linea che collega (0,0) a (w, h) forma un angolo φ con l'asse X, dove tan (φ) = h/w.

Supponendo che θ > φ, stiamo cercando il punto (x, y) in cui la linea che hai disegnato interseca il bordo superiore del rettangolo. Quindi y/x = tan (θ). Sappiamo che y = h quindi, risolvendo x, otteniamo x = h/tan (θ).

Se θ < φ, la linea interseca con il bordo destro del rettangolo (x, y). Questa volta, sappiamo che x = w, quindi y = tan (θ) * w. chiamata

1

C'è una buona (più iOS programmatiche/Objective-C) risposta a questa domanda in Find the CGPoint on a UIView rectangle intersected by a straight line at a given angle from the center point che coinvolge i seguenti passi:

  1. supporre che l'angolo è maggiore o uguale a 0 e meno di 2 * π , andando in senso antiorario da 0 (est).
  2. Ottieni la coordinata y dell'intersezione con il bordo destro del rettangolo [tan (angolo) * larghezza/2].
  3. Verificare se questa coordinata y si trova nel riquadro rettangolare (valore assoluto inferiore o uguale a metà altezza).
  4. Se l'intersezione y è nel rettangolo, se l'angolo è minore di π/2 o maggiore di 3π/2 scegliere il margine destro (larghezza/2, -y coord). Altrimenti scegli il bordo sinistro (-width/2, y coord).
  5. Se la coordinata y dell'intersezione del bordo destro era fuori limite, calcolare la coordinata x dell'intersezione con il bordo inferiore [metà altezza/tan (angolo)].
  6. Successivamente determinare se si desidera il bordo superiore o il bordo inferiore. Se l'angolo è minore di π, vogliamo il bordo inferiore (x, -misura l'altezza). Altrimenti, vogliamo il bordo superiore (-x coord, metà dell'altezza).
  7. Quindi (se il centro del frame non è 0,0), spostare il punto dal centro effettivo del frame.
9

Ok, whew!, ho finalmente ottenuto questo.

NOTA: Ho basato questo sulla splendida risposta di Belisario. Se ti piace, ti piace anche il suo. Tutto ciò che ho fatto è stato trasformare in codice ciò che ha detto.

Ecco come appare in Objective-C. Dovrebbe essere abbastanza semplice da convertire in qualunque sia la tua lingua preferita.

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

Inoltre, ecco un piccolo test di vista che ho creato per verificarne il corretto funzionamento. Crea questa vista e mettila da qualche parte, farà fare un altro piccolo giro attorno al bordo.

@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

Codice fantastico! Ho appena implementato questo in Java. Ho dovuto scambiare le regioni 2 e 4 nei calcoli e ho dovuto usare un fattore y positivo nelle regioni 1 e 3, ma penso che sia perché in Cocoa/Objective-C l'origine è in basso a sinistra. Bravo! Bel lavoro! – Mike

+0

stai usando theta come radianti o "gradi da est" (nell'intervallo da 0 a 180 in senso orario e da 0 a -180 in senso antiorario)? – MiltsInit

6

Javascript versione:

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

Uomo grazie mille, ho appena iniziato a scriverlo, poi ho trovato che qualcuno l'ha già fatto :) –

Problemi correlati