Pregunta

Tengo las coordenadas del punto superior izquierdo de un rectángulo, así como su anchura, altura y rotación de 0 a 180 y de -0 a -180.

Estoy tratando de obtener las coordenadas límite del cuadro real alrededor del rectángulo.

¿Qué es una forma simple de calcular las coordenadas del cuadro delimitador?

  • Min y, max y, min x, max x?

El punto A no siempre está en el límite mínimo, puede estar en cualquier lugar.

Puedo usar la matriz del kit de herramientas de transformación en as3 si es necesario.

¿Fue útil?

Solución

  • Transforma las coordenadas de las cuatro esquinas
  • Encuentra la más pequeña de las cuatro x como min_x
  • Encuentre la mayor de las cuatro x y llámela max_x
  • Lo mismo ocurre con la y
  • El cuadro delimitador es (min_x, min_y), (min_x, max_y), (max_x, max_y), (max_x, min_y)

AFAIK, no hay ningún camino real que te lleve allí mucho más rápido.

Si se está preguntando cómo transformar las coordenadas, intente:

x2 = x0+(x-x0)*cos(theta)+(y-y0)*sin(theta)
y2 = y0-(x-x0)*sin(theta)+(y-y0)*cos(theta)

donde (x0, y0) es el centro alrededor del cual estás girando. Es posible que deba modificar esto dependiendo de sus funciones trigonométricas (¿esperan grados o radianes?), El sentido / signo de su sistema de coordenadas frente a cómo está especificando ángulos, etc.

Otros consejos

Me doy cuenta de que estás pidiendo ActionScript, pero, en caso de que alguien venga a buscar la respuesta de iOS o OS-X, es esto:

+ (CGRect) boundingRectAfterRotatingRect: (CGRect) rect toAngle: (float) radians
{
    CGAffineTransform xfrm = CGAffineTransformMakeRotation(radians);
    CGRect result = CGRectApplyAffineTransform (rect, xfrm);

    return result;
}

Si su sistema operativo le ofrece hacer todo el trabajo duro por usted, ¡déjelo! :)

Swift:

func boundingRectAfterRotatingRect(rect: CGRect, toAngle radians: CGFloat) -> CGRect {
    let xfrm = CGAffineTransformMakeRotation(radians)
    return CGRectApplyAffineTransform (rect, xfrm)
}

El método descrito por MarkusQ funciona perfectamente pero tenga en cuenta que no necesita transformar las otras tres esquinas si ya tiene el punto A.

Un método alternativo, que es más eficiente, es probar en qué cuadrante se encuentra su ángulo de rotación y luego simplemente calcular la respuesta directamente. Esto es más eficiente ya que solo tiene un peor caso de dos afirmaciones if (verificando el ángulo) mientras que el otro enfoque tiene un peor caso de doce (6 para cada componente al verificar las otras tres esquinas para ver si son mayores que las actuales) Máx o menos que el mínimo actual, creo.

El algoritmo básico, que usa nada más que una serie de aplicaciones del teorema de Pitágoras, se muestra a continuación. Denoté el ángulo de rotación con theta y expresé el control allí en grados, ya que es un pseudocódigo.

ct = cos( theta );
st = sin( theta );

hct = h * ct;
wct = w * ct;
hst = h * st;
wst = w * st;

if ( theta > 0 )
{
    if ( theta < 90 )
    {
        // 0 < theta < 90
        y_min = A_y;
        y_max = A_y + hct + wst;
        x_min = A_x - hst;
        x_max = A_x + wct;
    }
    else
    {
        // 90 <= theta <= 180
        y_min = A_y + hct;
        y_max = A_y + wst;
        x_min = A_x - hst + wct;
        x_max = A_x;
    }
}
else
{
    if ( theta > -90 )
    {
        // -90 < theta <= 0
        y_min = A_y + wst;
        y_max = A_y + hct;
        x_min = A_x;
        x_max = A_x + wct - hst;
    }
    else
    {
        // -180 <= theta <= -90
        y_min = A_y + wst + hct;
        y_max = A_y;
        x_min = A_x + wct;
        x_max = A_x - hst;
    }
}

Este enfoque supone que tienes lo que dices que tienes, es decir, el punto A y un valor para theta que se encuentra en el rango [-180, 180]. También asumí que theta aumenta en el sentido de las agujas del reloj, ya que eso es lo que el rectángulo que ha girado 30 grados en su diagrama parece indicar que está usando, no estaba seguro de lo que la parte de la derecha estaba tratando de denotar. Si esta es la forma incorrecta, simplemente cambie las cláusulas simétricas y también el signo de los términos st.

    fitRect: function( rw,rh,radians ){
            var x1 = -rw/2,
                x2 = rw/2,
                x3 = rw/2,
                x4 = -rw/2,
                y1 = rh/2,
                y2 = rh/2,
                y3 = -rh/2,
                y4 = -rh/2;

            var x11 = x1 * Math.cos(radians) + y1 * Math.sin(radians),
                y11 = -x1 * Math.sin(radians) + y1 * Math.cos(radians),
                x21 = x2 * Math.cos(radians) + y2 * Math.sin(radians),
                y21 = -x2 * Math.sin(radians) + y2 * Math.cos(radians), 
                x31 = x3 * Math.cos(radians) + y3 * Math.sin(radians),
                y31 = -x3 * Math.sin(radians) + y3 * Math.cos(radians),
                x41 = x4 * Math.cos(radians) + y4 * Math.sin(radians),
                y41 = -x4 * Math.sin(radians) + y4 * Math.cos(radians);

            var x_min = Math.min(x11,x21,x31,x41),
                x_max = Math.max(x11,x21,x31,x41);

            var y_min = Math.min(y11,y21,y31,y41);
                y_max = Math.max(y11,y21,y31,y41);

            return [x_max-x_min,y_max-y_min];
        }

si está utilizando GDI +, puede crear un nuevo GrpaphicsPath - > Agregue cualquier punto o forma - > Aplicar transformación de rotación - > use GraphicsPath.GetBounds () y devolverá un rectángulo que limita su forma girada.

(editar) VB.Net Sample

Public Shared Sub RotateImage(ByRef img As Bitmap, degrees As Integer)
' http://stackoverflow.com/questions/622140/calculate-bounding-box-coordinates-from-a-rotated-rectangle-picture-inside#680877
'
Using gp As New GraphicsPath
  gp.AddRectangle(New Rectangle(0, 0, img.Width, img.Height))

  Dim translateMatrix As New Matrix
  translateMatrix.RotateAt(degrees, New PointF(img.Width \ 2, img.Height \ 2))
  gp.Transform(translateMatrix)

  Dim gpb = gp.GetBounds

  Dim newwidth = CInt(gpb.Width)
  Dim newheight = CInt(gpb.Height)

  ' http://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations
  '
  Dim rotatedBmp As New Bitmap(newwidth, newheight)

  rotatedBmp.SetResolution(img.HorizontalResolution, img.VerticalResolution)

  Using g As Graphics = Graphics.FromImage(rotatedBmp)
    g.Clear(Color.White)
    translateMatrix = New Matrix
    translateMatrix.Translate(newwidth \ 2, newheight \ 2)
    translateMatrix.Rotate(degrees)
    translateMatrix.Translate(-img.Width \ 2, -img.Height \ 2)
    g.Transform = translateMatrix
    g.DrawImage(img, New PointF(0, 0))
  End Using
  img.Dispose()
  img = rotatedBmp
End Using

Fin Sub

Aunque Code Guru estableció el método GetBounds (), noté que la pregunta está etiquetada como3, flex, por lo que aquí hay un fragmento de as3 que ilustra la idea.

var box:Shape = new Shape();
box.graphics.beginFill(0,.5);
box.graphics.drawRect(0,0,100,50);
box.graphics.endFill();
box.rotation = 20;
box.x = box.y = 100;
addChild(box);

var bounds:Rectangle = box.getBounds(this);

var boundingBox:Shape = new Shape();
boundingBox.graphics.lineStyle(1);
boundingBox.graphics.drawRect(bounds.x,bounds.y,bounds.width,bounds.height);
addChild(boundingBox);

Noté que hay dos métodos que parecen hacer lo mismo: getBounds () y getRect ()

/**
     * Applies the given transformation matrix to the rectangle and returns
     * a new bounding box to the transformed rectangle.
     */
    public static function getBoundsAfterTransformation(bounds:Rectangle, m:Matrix):Rectangle {
        if (m == null) return bounds;

        var topLeft:Point = m.transformPoint(bounds.topLeft);
        var topRight:Point = m.transformPoint(new Point(bounds.right, bounds.top));
        var bottomRight:Point = m.transformPoint(bounds.bottomRight);
        var bottomLeft:Point = m.transformPoint(new Point(bounds.left, bounds.bottom));

        var left:Number = Math.min(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x);
        var top:Number = Math.min(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y);
        var right:Number = Math.max(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x);
        var bottom:Number = Math.max(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y);
        return new Rectangle(left, top, right - left, bottom - top);
    }

Aplica la matriz de rotación a tus puntos de esquina. Luego use el mínimo / máximo respectivamente de las coordenadas x, y obtenidas para definir su nuevo cuadro delimitador.

Aquí hay tres funciones de mis bibliotecas de código abierto. Las funciones están totalmente probadas en Java, pero las fórmulas se pueden traducir fácilmente a cualquier idioma.

Las firmas son:

flotante estática pública getAngleFromPoint (Punto central final Punto, Punto final táctil)

flotador estático público getTwoFingerDistance (float firstTouchX, float firstTouchY, float secondTouchX, float secondTouchY)

Punto getPointFromAngle (ángulo doble final, radio doble final)

Esta solución supone que la densidad de píxeles está espaciada uniformemente. Antes de rotar el objeto haga lo siguiente:

  1. Use getAngleFromPoint para calcular el ángulo desde el centro hasta la esquina superior derecha (digamos que esto devuelve 20 grados) lo que significa que la esquina superior izquierda es -20 grados o 340 grados.

  2. Use getTwoFingerDistance para devolver la distancia diagonal entre el punto central y la esquina superior derecha (esta distancia debería ser la misma en todas las esquinas, esta distancia se usará en el siguiente cálculo).

  3. Ahora digamos que giramos el objeto en sentido horario 30 grados. Ahora sabemos que la esquina superior derecha debe estar a 50 grados y la esquina superior izquierda está a 10 grados.

  4. Ahora debería poder usar la función getPointFromAngle en la esquina superior izquierda y superior derecha. utilizando el radio devuelto desde el paso 2. La posición X multiplicada por 2 desde la esquina superior derecha le dará el nuevo ancho y la posición Y por 2 desde la esquina superior izquierda le dará la nueva altura.

Estos 4 pasos anteriores se deben poner en condiciones según la distancia con la que haya girado su objeto; de lo contrario, puede devolver la altura como el ancho y la anchura como el alto.

Tenga en cuenta que las funciones de ángulo se expresan en factores de 0-1 en lugar de 0-360 (simplemente multiplique o divida por 360 cuando sea apropiado):

// Obtiene un ángulo de dos puntos expresados ??como un factor de 0 -1 (0 es 0/360, 0.25 es 90 grados, etc.)

public float getAngleFromPoint(final Point centerPoint, final Point touchPoint) {

    float returnVal = 0;

    //+0 - 0.5
    if(touchPoint.x > centerPoint.x) {

        returnVal = (float) (Math.atan2((touchPoint.x - centerPoint.x), (centerPoint.y - touchPoint.y)) * 0.5 / Math.PI);

    }
    //+0.5
    else if(touchPoint.x < centerPoint.x) {

        returnVal = (float) (1 - (Math.atan2((centerPoint.x - touchPoint.x), (centerPoint.y - touchPoint.y)) * 0.5 / Math.PI));

    }//End if(touchPoint.x > centerPoint.x)

    return returnVal;

}

// Mide la distancia diagonal entre dos puntos

public float getTwoFingerDistance(final float firstTouchX, final float firstTouchY, final float secondTouchX, final float secondTouchY) {

    float pinchDistanceX = 0;
    float pinchDistanceY = 0;

    if(firstTouchX > secondTouchX) {

        pinchDistanceX = Math.abs(secondTouchX - firstTouchX);

    }
    else if(firstTouchX < secondTouchX) {

        pinchDistanceX = Math.abs(firstTouchX - secondTouchX);

    }//End if(firstTouchX > secondTouchX)

    if(firstTouchY > secondTouchY) {

        pinchDistanceY = Math.abs(secondTouchY - firstTouchY);

    }
    else if(firstTouchY < secondTouchY) {

        pinchDistanceY = Math.abs(firstTouchY - secondTouchY);

    }//End if(firstTouchY > secondTouchY)

    if(pinchDistanceX == 0 && pinchDistanceY == 0) {

        return 0;

    }
    else {

        pinchDistanceX = (pinchDistanceX * pinchDistanceX);
        pinchDistanceY = (pinchDistanceY * pinchDistanceY);
        return (float) Math.abs(Math.sqrt(pinchDistanceX + pinchDistanceY));

    }//End if(pinchDistanceX == 0 && pinchDistanceY == 0)

}

// Obtenga las coordenadas XY de un ángulo dado un radio (el ángulo se expresa en un factor de 0-1 0 siendo 0/360 grados y 0.75 siendo 270, etc.)

public Point getPointFromAngle(final double angle, final double radius) {

    final Point coords = new Point();
    coords.x = (int) (radius * Math.sin((angle) * 2 * Math.PI));
    coords.y = (int) -(radius * Math.cos((angle) * 2 * Math.PI));

    return coords;

}

Estos fragmentos de código son de mis bibliotecas de código abierto: https://bitbucket.org/warwick/hgdialrepo y https://bitbucket.org/warwick/hacergestov2 . Uno es una biblioteca de gestos para Android y el otro es un control de marcación para Android. También hay una implementación de OpenGLES 2.0 del control de marcado en: https://bitbucket.org/warwick/hggldial

No estoy seguro de entenderlo, pero una matriz de transformación compuesta le proporcionará las nuevas coordenadas para todos los puntos en cuestión. Si cree que el rectángulo puede extenderse sobre el área imaginable, la transformación posterior aplica un trazado de recorte.

En caso de que no esté familiarizado con la definición exacta de las matrices, eche un vistazo a aquí .

Usé Región para Primero rotar el rectángulo y luego usar esa región girada para detectar ese rectángulo

        r = new Rectangle(new Point(100, 200), new Size(200, 200));         
        Color BorderColor = Color.WhiteSmoke;
        Color FillColor = Color.FromArgb(66, 85, 67);
        int angle = 13;
        Point pt = new Point(r.X, r.Y);
        PointF rectPt = new PointF(r.Left + (r.Width / 2),
                               r.Top + (r.Height / 2));
       //declare myRegion globally 
        myRegion = new Region(r);

        // Create a transform matrix and set it to have a 13 degree

        // rotation.
        Matrix transformMatrix = new Matrix();
        transformMatrix.RotateAt(angle, pt);

        // Apply the transform to the region.
        myRegion.Transform(transformMatrix);
        g.FillRegion(Brushes.Green, myRegion);
        g.ResetTransform();

ahora para detectar ese rectángulo

        private void panel_MouseMove(object sender, MouseEventArgs e)
    {


        Point point = e.Location;
        if (myRegion.IsVisible(point, _graphics))
        {
            // The point is in the region. Use an opaque brush.
            this.Cursor = Cursors.Hand;
        }
        else {
            this.Cursor = Cursors.Cross;
        }

    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top