我有一个矩形左上角的坐标,以及它的宽度、高度和从 0 到 180 和 -0 到 -180 的旋转。
我正在尝试获取矩形周围实际框的边界坐标。
计算边界框坐标的简单方法是什么
- 最小 y,最大 y,最小 x,最大 x?
A 点并不总是在最小界限上,它可以在任何地方。
如果需要,我可以在 as3 中使用矩阵转换工具包。
我有一个矩形左上角的坐标,以及它的宽度、高度和从 0 到 180 和 -0 到 -180 的旋转。
我正在尝试获取矩形周围实际框的边界坐标。
计算边界框坐标的简单方法是什么
A 点并不总是在最小界限上,它可以在任何地方。
如果需要,我可以在 as3 中使用矩阵转换工具包。
min_x
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) 是您旋转的中心。您可能需要根据您的三角函数(他们期望度数或弧度)、坐标系的感觉/符号与您指定角度的方式等来修改这个。
我知道您要求的是 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 点,则不需要转换其他三个角。
另一种更有效的方法是测试您的旋转角度在哪个象限,然后简单地直接计算答案。这更有效,因为您只有两个 if 语句(检查角度)的最坏情况,而另一种方法的最坏情况是 12 个(在检查其他三个角以查看它们是否大于当前时,每个组件 6 个)最大值或小于当前最小值)我认为。
基本算法仅使用毕达哥拉斯定理的一系列应用,如下所示。我用 theta 表示旋转角度,并用度数表示检查,因为它是伪代码。
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] 范围内的 theta 值。我还假设 theta 沿顺时针方向增加,因为这就是图表中旋转 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];
}
如果您使用的是 GDI+ ,您可以创建一个新的 GrpaphicsPath -> 向其添加任何点或形状 -> 应用旋转变换 -> 使用 GraphicsPath.GetBounds() ,它将返回一个限制旋转形状的矩形。
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
结束子
尽管 Code Guru 说明了 GetBounds() 方法,但我注意到问题被标记为 as3, 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);
}
将旋转矩阵应用于您的角点。然后分别使用获得的 x,y 坐标的最小值/最大值来定义新的边界框。
这是我的开源库中的三个函数。这些函数在 Java 中经过全面测试,但公式可以很容易地翻译成任何语言。
签名是:
public static float getAngleFromPoint(final Point centerPoint, final Point touchPoint)
公共静态浮动 getTwoFingerDistance(浮动 firstTouchX,浮动 firstTouchY,浮动 secondTouchX,浮动 secondTouchY)
Point getPointFromAngle(最终双角,最终双半径)
该解决方案假设像素密度是均匀分布的。在旋转对象之前,请执行以下操作:
使用 getAngleFromPoint 计算从中心到右上角的角度(假设返回 20 度),这意味着左上角是 -20 度或 340 度。
使用 getTwoFingerDistance 返回中心点和右上角之间的对角线距离(这个距离显然应该对所有角都相同,这个距离将在下一次计算中使用)。
现在假设我们将对象顺时针旋转 30 度。我们现在知道右上角必须是 50 度,左上角是 10 度。
您现在应该可以使用左上角和右上角的 getPointFromAngle 函数了。使用从步骤 2 返回的半径。从右上角乘以 2 的 X 位置应该给你新的宽度,从左上角的 Y 位置乘以 2 应该给你新的高度。
上述 4 个步骤应根据您旋转对象的距离来设置,否则您可以将高度作为宽度返回,将宽度作为高度返回。
请记住,角度函数以 0-1 而不是 0-360 的因数表示(只需在适当的情况下乘以或除以 360):
//从两点获取角度,表示为 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;
}
//测量两点之间的对角线距离
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)
}
//从给定半径的角度获取XY坐标(角度以0-1 0表示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。一个是安卓的手势库,另一个是安卓的拨号控件。还有一个拨号控件的 OpenGLES 2.0 实现:https ://bitbucket.org/warwick/hggldial
我不确定我是否理解,但复合变换矩阵将为您提供所有相关点的新坐标。如果您认为矩形可能会溢出变换后的可成像区域,请应用剪切路径。
如果您不熟悉矩阵的确切定义,请查看此处。
我使用区域首先旋转矩形,然后使用该旋转区域来检测该矩形
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();
现在检测那个矩形
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;
}
}