actionscript-3 math geometry rotation bounding-box

actionscript 3 - Calcular las coordenadas del cuadro delimitador de un rectángulo girado



actionscript-3 math (11)

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

Estoy tratando de obtener las coordenadas de delimitación de la caja real alrededor del rectángulo.

¿Cuál 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 matrix the transform toolkit en as3 si es necesario.


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 se prueban completamente en Java, pero las fórmulas se pueden traducir fácilmente a cualquier idioma.

Las firmas son:

flotador estático público getAngleFromPoint (punto final centerPointPoint, punto final touchPoint)

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

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

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

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

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

  3. Ahora digamos que rotamos el objeto en el sentido de las agujas del reloj 30 grados. Ahora sabemos que la esquina superior derecha debe estar a 50 grados y la esquina superior izquierda a 10 grados.

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

Estos 4 pasos anteriores se deben poner en condiciones en función de qué tan lejos haya girado su objeto; de lo contrario, puede devolver la altura como el ancho y el ancho como la altura.

Sin tener en cuenta, las funciones de ángulo se expresan en factores de 0-1 en lugar de 0-360 (simplemente multiplique o divida por 360 según corresponda):

// 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) }

// Obtener coordenadas XY desde 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 . Una es una biblioteca de gestos para Android y la otra es un control de marcación para Android. También hay una implementación OpenGLES 2.0 del control de marcado en: https://bitbucket.org/warwick/hggldial


Aunque Code Guru indicó el método GetBounds (), me he dado cuenta de que la pregunta está etiquetada como 3, flex, por lo que aquí hay un fragmento de código 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 ()


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 sentencias if (verificando el ángulo), mientras que el otro enfoque tiene el peor caso de doce (6 por cada componente al verificar las otras tres esquinas para ver si son mayores que el actual max o menos que el mínimo actual), creo.

El algoritmo básico, que utiliza nada más que una serie de aplicaciones del teorema de Pitágoras, se muestra a continuación. He indicado el ángulo de rotación por theta y he expresado la verificación 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 degrees ) { // 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 la dirección de las manecillas del reloj, ya que es lo que el rectángulo que se ha girado 30 grados en el diagrama parece indicar que estás usando, no estaba seguro de lo que la parte de la derecha intentaba denotar. Si este es el camino equivocado, simplemente cambie las cláusulas simétricas y también el signo de los términos st.


Me doy cuenta de que está pidiendo ActionScript, pero, por si alguien llega aquí buscando la respuesta de iOS o OS-X, es este:

+ (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! :)

Rápido:

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


No estoy seguro de entender, pero una matriz de transformación compuesta le dará las nuevas coordenadas para todos los puntos en cuestión. Si crees que el rectángulo puede derramarse sobre la transformación de la zona de imagen que se puede pintar, aplica un trazado de recorte.

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


Utilicé Region for First girando el rectángulo y luego uso 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; } }


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

(editar) Muestra de VB.Net

Public Shared Sub RotateImage(ByRef img As Bitmap, degrees As Integer) '' http://.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

End Sub


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]; }


/** * 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); }


  • Transforma las coordenadas de las cuatro esquinas
  • Encuentra el más pequeño de los cuatro x como min_x
  • Encuentra la más grande de las cuatro x y max_x
  • Lo mismo con los y
  • Su 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 pregunta 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á rotando. Puede necesitar retocar con 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.