Рассчитать координаты ограничительной рамки из повернутого прямоугольника - PullRequest
64 голосов
/ 07 марта 2009

У меня есть координаты верхней левой точки прямоугольника, а также его ширина, высота и поворот от 0 до 180 и от -0 до -180.

Я пытаюсь получить ограничивающие координаты фактического прямоугольника вокруг прямоугольника.

Что такое простой способ вычисления координат ограничительной рамки

  • Мин у, макс у, мин х, макс х?

Точка A не всегда находится на минимальной границе, она может быть где угодно.

Я могу использовать матричный набор инструментов преобразования в as3, если это необходимо.

Ответы [ 11 ]

74 голосов
/ 07 марта 2009
  • Преобразование координат всех четырех углов
  • Найдите наименьшее из всех четырех х как min_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) - центр, вокруг которого вы вращаетесь. Возможно, вам придется повозиться с этим в зависимости от ваших функций триггера (ожидают ли они градусов или радиан), смысла / знака вашей системы координат в зависимости от того, как вы указываете углы и т. Д.

24 голосов
/ 07 февраля 2012

Я понимаю, что вы запрашиваете ActionScript, но на тот случай, если кто-нибудь придет сюда в поисках ответа для iOS или OS-X, это так:

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

    return result;
}

Если ваша ОС предлагает сделать всю тяжелую работу за вас, позвольте ей! :)

Swift:

func boundingRectAfterRotatingRect(rect: CGRect, toAngle radians: CGFloat) -> CGRect {
    let xfrm = CGAffineTransformMakeRotation(radians)
    return CGRectApplyAffineTransform (rect, xfrm)
}
10 голосов
/ 08 марта 2009

Метод, описанный MarkusQ, работает отлично, но имейте в виду, что вам не нужно преобразовывать три других угла, если у вас уже есть точка A.

Альтернативный метод, который является более эффективным, состоит в том, чтобы проверить, в каком квадранте находится ваш угол поворота, а затем просто вычислить ответ напрямую. Это более эффективно, поскольку у вас есть только наихудший случай двух операторов if (проверка угла), тогда как другой подход имеет наихудший случай двенадцати (6 для каждого компонента при проверке трех других углов, чтобы увидеть, больше ли они текущего максимум или меньше текущего мин) думаю.

Основной алгоритм, который использует не более чем ряд применений теоремы Пифагора, показан ниже. Я обозначил угол поворота тэтой и выразил проверку там в градусах как ее псевдокод.

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

Этот подход предполагает, что у вас есть то, что вы говорите, то есть точка А и значение для тета, которое лежит в диапазоне [-180, 180]. Я также предположил, что тета увеличивается в направлении по часовой стрелке, так как это то, что прямоугольник, повернутый на 30 градусов на вашей диаграмме, похоже, указывает на то, что вы используете, я не был уверен, что пыталась обозначить часть справа. Если это не так, просто поменяйте местами симметричные предложения, а также знак слагаемых.

6 голосов
/ 06 октября 2010
    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];
        }
3 голосов
/ 25 марта 2009

если вы используете GDI +, вы можете создать новый GrpaphicsPath -> Добавить любые точки или фигуры к нему -> Применить преобразование поворота -> использовать GraphicsPath.GetBounds (), и он вернет прямоугольник, который ограничивает вашу повернутую фигуру.

(редактировать) VB.Net Sample

Public Shared Sub RotateImage(ByRef img As Bitmap, degrees As Integer)
' /413529/rasschitat-koordinaty-ogranichitelnoi-ramki-iz-povernutogo-pryamougolnika
'
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

2 голосов
/ 21 февраля 2011
/**
     * 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);
    }
2 голосов
/ 18 апреля 2009

Хотя 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 ()

1 голос
/ 19 сентября 2015

Вот три функции из моих библиотек с открытым исходным кодом. Функции полностью протестированы в Java, но формулы могут быть легко переведены на любой язык.

Подписи:

публичный статический метод с плавающей точкой getAngleFromPoint (конечная точка centerPoint, конечная точка touchPoint)

общедоступное статическое значение с плавающей точкой getTwoFingerDistance (сначала с плавающей точкой TouchX, с плавающей точкой firstTouchY, с плавающей точкой secondTouchX, с плавающей точкой secondTouchY)

Точка getPointFromAngle (конечный двойной угол, конечный двойной радиус)

В этом решении предполагается, что плотность пикселей равномерно распределена. Перед вращением объекта сделайте следующее:

  1. Используйте getAngleFromPoint для вычисления угла от центра к верхнему правому углу (допустим, это возвращает 20 градусов), означая, что левый верхний угол составляет -20 градусов или 340 градусов.

  2. Используйте getTwoFingerDistance для возврата диагонального расстояния между центральной точкой и верхним правым углом (это расстояние должно быть одинаково одинаковым для всех углов. Это расстояние будет использовано в следующем расчете).

  3. Теперь допустим, что мы поворачиваем объект по часовой стрелке на 30 градусов. Теперь мы знаем, что верхний правый угол должен находиться под углом 50 градусов, а верхний левый угол - под углом 10 градусов.

  4. Теперь вы сможете использовать функцию getPointFromAngle в верхнем левом и правом верхнем углу. используя радиус, возвращенный из шага 2. Позиция X, умноженная на 2 в верхнем правом углу, должна дать вам новую ширину, а позиция 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. Одна - это библиотека жестов для Android, а другая - управление циферблатом для Android. Существует также реализация OpenGLES 2.0 для управления набором по адресу: https://bitbucket.org/warwick/hggldial

1 голос
/ 07 марта 2009

Примените матрицу вращения к вашим угловым точкам. Затем используйте минимум / максимум соответственно полученных координат x, y для определения вашего нового ограничивающего прямоугольника.

0 голосов
/ 27 апреля 2018

Я использовал 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;
        }

    }
...