首页 文章

从旋转的矩形计算边界框坐标

提问于
浏览
60

我有一个矩形左上角的坐标,以及从0到180和-0到-180的宽度,高度和旋转 .

我试图获取矩形周围的实际框的边界坐标 .

什么是计算边界框坐标的简单方法

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

A点并不总是在最小值上,它可以在任何地方 .

如果需要,我可以在as3中使用矩阵变换工具包 .

11 回答

  • 1
    • 变换所有四个角的坐标

    • 找到所有四个x中最小的x为 min_x

    • 找到所有四个x中最大的一个并称之为 max_x

    • 与y同上

    • 你的边界框是 (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)是您旋转的中心 . 您可能需要修改这个,具体取决于您的三角函数(他们希望度数或弧度)坐标系的感觉/符号与指定角度的方式等 .

  • 1

    我意识到你要求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)
    }
    
  • 3

    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在顺时针方向上增加,因为图中旋转了30度的矩形似乎表明你正在使用,我不确定右边的部分是什么意思 . 如果这是错误的方法,那么只需交换对称子句以及st术语的符号 .

  • 0
    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];
            }
    
  • 2

    如果你正在使用GDI,你可以创建一个新的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
    

    结束子

  • 6

    虽然Code 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()

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

    将旋转矩阵应用于角点 . 然后分别使用获得的x,y坐标的最小值/最大值来定义新的边界框 .

  • 8

    以下是我的开源库中的三个函数 . 这些函数在Java中经过全面测试,但公式可以很容易地翻译成任何语言 .

    签名是:

    public static float getAngleFromPoint(最终Point centerPoint,最终Point touchPoint)

    public static float getTwoFingerDistance(float firstTouchX,float firstTouchY,float secondTouchX,float secondTouchY)

    点getPointFromAngle(最终双角,最终双半径)

    该解决方案假设像素密度均匀分布 . 在旋转对象之前,请执行以下操作:

    • 使用getAngleFromPoint计算从中心到右上角的角度(假设它返回20度),这意味着upp左角是-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/hgdialrepohttps://bitbucket.org/warwick/hacergestov2 . 一个是Android的手势库,另一个是Android的拨号控件 . 拨号控件还有一个OpenGLES 2.0实现:https://bitbucket.org/warwick/hggldial

  • 2

    我不确定我理解,但是复合变换矩阵会为所有相关点提供新的坐标 . 如果您认为矩形可能会在转换后溢出可成像区域,则应用剪切路径 .

    如果您不熟悉矩阵的确切定义,请查看here .

  • 24

    我使用Region for First旋转矩形然后使用该旋转区域来检测该矩形

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

相关问题