从一个旋转的矩形计算边界框坐标
我有一个矩形的左上angular的坐标以及它的宽度,高度和旋转从0到180和-0到-180。
我正在尝试获取矩形周围实际框的边界坐标。
计算边界框坐标的简单方法是什么?
- Min y,max y,min x,max x?
A点并不总是在最小范围内,它可以在任何地方。
如果需要,我可以在as3中使用matrix转换工具包。
- 转换所有四个angular的坐标
- find所有四个最小的x作为
min_x
- find所有四个中最大的一个,并将其
max_x
- 与你同上
- 你的边界框是
(min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)
AFAIK,没有任何皇室之路可以让你快得多。
如果您想知道如何转换坐标,请尝试:
x2 = x0+(x-x0)*cos(theta)+(y-y0)*sin(theta) y2 = y0-(x-x0)*sin(theta)+(y-y0)*cos(theta)
其中(x0,y0)是您正在旋转的中心。 您可能需要根据您的三angular函数(他们是否期望度数或弧度)来调整坐标系统的感觉/符号,以及如何指定angular度等。
我意识到你要求的是ActionScript,但为了防止任何人在这里寻找iOS或OS-X的答案,它是这样的:
+ (CGRect) boundingRectAfterRotatingRect: (CGRect) rect toAngle: (float) radians { CGAffineTransform xfrm = CGAffineTransformMakeRotation(radians); CGRect result = CGRectApplyAffineTransform (rect, xfrm); return result; }
如果你的操作系统为你做了所有的辛苦工作,让它! 🙂
迅速:
func boundingRectAfterRotatingRect(rect: CGRect, toAngle radians: CGFloat) -> CGRect { let xfrm = CGAffineTransformMakeRotation(radians) return CGRectApplyAffineTransform (rect, xfrm) }
MarkusQ概述的方法是完美的,但要记住,如果你已经有了A点,你不需要转换其他三个angular落。
另一种更有效的方法是testing你的旋转angular度在哪个象限,然后直接计算答案。 这是更有效的,因为只有两个if语句的最坏情况(检查angular度),而另一个方法的最坏情况是十二个(每个组件在检查其他三个angular时检查是否大于当前值最大或小于当前分钟)我想。
下面显示了基本algorithm,它只使用毕达哥拉斯定理的一系列应用。 我已经用theta表示了旋转angular度,并用伪代码表示了度数。
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; } }
这种方法假定你有你说的你有,即点A和θ值在[-180,180]范围内。 我也认为θ在顺时针方向上增加了,因为这个图中的矩形已经被旋转了30度,似乎表明你正在使用,我不确定右边的部分是用什么来表示的。 如果这是错误的方法,那么交换对称从句和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]; }
尽pipeCode Guru声明了GetBounds()方法,但我注意到这个问题被标记为3,flex,所以这里有一个as3片段来说明这个想法。
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);
我注意到有两个方法似乎做同样的事情:getBounds()和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); }
如果你正在使用GDI +,你可以创build一个新的GrpaphicsPath – >添加任何点或形状 – >应用旋转转换 – >使用GraphicsPath.GetBounds(),它将返回一个矩形,限制你的旋转形状。
(编辑)VB.Net示例
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
结束小组
将旋转matrix应用于angular点。 然后分别使用获得的x,y坐标的最小值/最大值来定义新的边界框。
以下是我的开源库中的三个函数。 这些函数在Java中进行了全面的testing,但公式可以很容易地翻译成任何语言。
签名是:
public static float getAngleFromPoint(final Point centerPoint,final Point touchPoint)
public static float getTwoFingerDistance(float firstTouchX,float firstTouchY,float secondTouchX,float secondTouchY)
点getPointFromAngle(最终双angular度,最终双半径)
该解决scheme假设像素密度均匀分布。 旋转对象之前,请执行以下操作:
-
使用getAngleFromPoint计算从中心到右上angular的angular度(假设这返回20度),这意味着upp左angular是-20度或340度。
-
使用getTwoFingerDistance返回中心点和右上angular之间的对angular距离(这个距离应该与所有的angular相同,这个距离将在下一次计算中使用)。
-
现在让我们说我们顺时针旋转对象30度。 我们现在知道右上angular必须是50度,左上angular是10度。
-
您现在应该可以使用左上angular和右上angular的getPointFromAngle函数。 使用从步骤2返回的半径。从右上angular乘以2的X位置应该给你新的宽度,并且从左上angular的Y位置乘以2应该给出新的高度。
以上4个步骤应该根据你旋转的物体的距离而定,另外,你可以返回高度的宽度和高度。
考虑到angular度函数是用0-1而不是0-360(只要在适当的时候乘以或除以360)的因子表示:
//从两点表示一个angular度,表示为0 -1的因子(0表示0/360,0.25表示90度等)
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; }
//测量两点之间的对angular线距离
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) }
//从给定半径的angular度获取XY坐标(该angular度用0-160为0/360度,0.75为270等表示)
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; }
这些代码片段来自我的开源库: https : //bitbucket.org/warwick/hgdialrepo和https://bitbucket.org/warwick/hacergestov2 。 一个是Android的手势库,另一个是Android的拨号控件。 拨号控制还有一个OpenGLES 2.0实现: https : //bitbucket.org/warwick/hggldial
我不确定我是否理解,但是一个复合变换matrix会给你所有有关点的新坐标。 如果您认为矩形可能溢出了可变区域,则转换后应用剪切path。
如果你不熟悉matrix的确切定义,请看这里 。