2010-10-31 14 views
14

Estoy tratando de dibujar un degradado en un objeto rectangular, con un ángulo dado (Theta), donde los extremos del degradado están tocando el perímetro del rectángulo.Encontrar puntos en un rectángulo en un ángulo determinado

Graph

pensé que el uso de la tangente funcionaría, pero estoy teniendo problemas para conseguir soltarse un poco. ¿Hay algún algoritmo fácil que me pierda?

resultado final

Por lo tanto, esto va a ser una función de (ángulo, RectX1, RectX2, RectY1, RectY2). Quiero que se devuelva en forma de [x1, x2, y1, y2], de modo que el degradado dibuje a través del cuadrado. En mi problema, si el origen es 0, entonces x2 = -x1 y y2 = -y1. Pero no siempre va a ser sobre el origen.

+3

¿qué la imagen tiene que ver con el problema? Solo un extremo de la línea (supongo que la línea es la hipotenusa en este caso) toca el límite. ¿la línea siempre pasará (o, como se representa, comenzará en) el origen? – aaronasterling

+0

@aaronasterling, entiendo lo que intento lograr. Necesito tanto X como Y. El triángulo cambiará según el ángulo. – bradlis7

Respuesta

31

Vamos un y b los lados del rectángulo, y (x0, y0) las coordenadas de su centro de rectángulo.

usted tiene cuatro regiones a considerar:

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 poco de trigonometría-fu, podemos obtener las coordenadas de su intersección deseado en cada región.

alt text

Así Z0 es la expresión para el punto de intersección de las regiones 1 y 3
Y Z1 es la expresión para el punto de intersección de las regiones 2 y 4

Las líneas deseadas pasar de (X0, Y0) a Z0 o Z1 dependiendo de la región. Así recordando que Tan (φ) = sen (φ)/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) 

Sólo ten en cuenta de los signos de Tan (φ) Traducido por cada cuadrante, y que el ángulo siempre se mide desde el x POSITIVO eje ANTICLOCKWISE.

HTH!

+0

¡Buen trabajo! Veré qué puedo hacer con esta información. – bradlis7

+0

¡Excelente respuesta! ¡Gracias! – BillyBBone

+0

No entiendo qué representan los dos ángulos φ y θ en su respuesta o en la otra respuesta: ¿la pregunta no especifica solo un ángulo? ¿Y no debería haber una coordenada x diferente para el punto de intersección/final en la región 1 en comparación con 3 (y una coordenada y diferente para el punto de intersección en la región 2 en comparación con 4)? –

3

Siguiendo su imagen, supongo que el rectángulo está centrado en (0,0), y que la esquina superior derecha es (w, h). Entonces la línea que conecta (0,0) a (w, h) forma un ángulo φ con el eje X, donde tan (φ) = h/w.

Suponiendo que θ > φ, estamos buscando el punto (x, y) donde la línea que ha dibujado se cruza con el borde superior del rectángulo. Entonces y/x = tan (θ). Sabemos que y = h así que, al resolver x, obtenemos x = h/tan (θ).

Si θ < φ, la línea se cruza con el borde derecho del rectángulo en (x, y). Esta vez, sabemos que x = w, entonces y = tan (θ) * w. llamada

1

Hay una buena (más IOS programáticos/Objective-C) respuesta a esta cuestión en Find the CGPoint on a UIView rectangle intersected by a straight line at a given angle from the center point que involucra los siguientes pasos:

  1. suponer que el ángulo es mayor o igual que 0 y menor que 2 * π , yendo en sentido antihorario desde 0 (Este).
  2. Obtenga la coordenada y de la intersección con el borde derecho del rectángulo [tan (ángulo) * ancho/2].
  3. Comprueba si esta coordenada y está en el marco del rectángulo (valor absoluto menor o igual que la mitad de la altura).
  4. Si la intersección y está en el rectángulo, entonces si el ángulo es menor que π/2 o mayor que 3π/2, elija el borde derecho (ancho/2, -y coord). De lo contrario, elija el borde izquierdo (-width/2, y coord).
  5. Si la coordenada y de la intersección del borde derecho estaba fuera de límites, calcule la coordenada x de la intersección con el borde inferior [la mitad de la altura/tan (ángulo)].
  6. A continuación, determine si desea el borde superior o inferior. Si el ángulo es menor que π, queremos el borde inferior (x, la mitad de la altura). De lo contrario, queremos el borde superior (-x coord, la mitad de la altura).
  7. Luego (si el centro del encuadre no es 0,0), desplace el punto por el centro real del encuadre.
9

Ok, ¡oh!, finalmente obtuve este.

NOTA: Me basé en la increíble respuesta de belisarius. Si te gusta esto, por favor, como él también. Todo lo que hice fue convertir lo que él dijo en código.

Esto es lo que parece en Objective-C. Debería ser lo suficientemente simple como para convertirlo en el idioma que prefiera.

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

Además, aquí hay una pequeña vista de prueba que creé para verificar que funciona. Cree esta vista y colóquela en algún lugar, hará que otra pequeña vista se deslice por el borde.

@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

código fantástico! Acabo de implementar esto en Java. Tuve que cambiar las regiones 2 y 4 en los cálculos y tuve que usar un yFactor positivo en las regiones 1 y 3, pero creo que eso se debe a que en Cocoa/Objective-C el origen está en la parte inferior izquierda. ¡Bravo! ¡Buen trabajo! – Mike

+0

¿está utilizando theta como radianes o "grados desde el este" (en el rango de 0 a 180 en el sentido de las agujas del reloj, y de 0 a -180 en el sentido contrario de las agujas del reloj)? – MiltsInit

6

versión Javascript:

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

Hombre muchas gracias, acabo de comenzar a escribirlo, luego encontré que alguien ya lo hizo :) –

Cuestiones relacionadas