Как рассчитать угловые позиции / отметки повернутого / наклоненного прямоугольника?

17

У меня есть два элемента, 2D точка и прямоугольная область. Точка представляет середину этой области. Я также знаю ширину и высоту этой области. И область наклонена на 40 ° относительно сетки.

Теперь я хотел бы рассчитать абсолютные позиции каждой угловой метки этой наклонной области, используя только эти данные. Это возможно?

Stacky
источник

Ответы:

30
X = x*cos(θ) - y*sin(θ)
Y = x*sin(θ) + y*cos(θ)

Это даст вам местоположение точки, повернутой на θ градусов вокруг начала координат. Поскольку углы квадрата вращаются вокруг центра квадрата, а не начала координат, необходимо добавить пару шагов, чтобы использовать эту формулу. Сначала вам нужно установить точку относительно начала координат. Тогда вы можете использовать формулу вращения. После поворота вам нужно переместить его обратно относительно центра квадрата.

// cx, cy - center of square coordinates
// x, y - coordinates of a corner point of the square
// theta is the angle of rotation

// translate point to origin
float tempX = x - cx;
float tempY = y - cy;

// now apply rotation
float rotatedX = tempX*cos(theta) - tempY*sin(theta);
float rotatedY = tempX*sin(theta) + tempY*cos(theta);

// translate back
x = rotatedX + cx;
y = rotatedY + cy;

Примените это ко всем 4 углам, и все готово!

Aholio
источник
4

Это обычная техника - вращать точку вокруг оси, переводя ее в систему координат, где точка - это начало координат, затем поворачивая вокруг этой точки, затем переводя обратно в мировые координаты. (Очень хорошее объяснение этого подхода доступно на Академии Хана )

Однако вы не сохраняете свои прямоугольные углы в мировых координатах, поэтому мы можем адаптировать подход к имеющимся у вас данным.

Cx, Cy // the coordinates of your center point in world coordinates
W      // the width of your rectangle
H      // the height of your rectangle
θ      // the angle you wish to rotate

//The offset of a corner in local coordinates (i.e. relative to the pivot point)
//(which corner will depend on the coordinate reference system used in your environment)
Ox = W / 2
Oy = H / 2

//The rotated position of this corner in world coordinates    
Rx = Cx + (Ox  * cos(θ)) - (Oy * sin(θ))
Ry = Cy + (Ox  * sin(θ)) + (Oy * cos(θ))

Этот подход может быть легко применен к остальным трем углам.

Келли Томас
источник
2

Основываясь на других ответах и ​​дополняя их, мне удалось создать пример с P5 здесь .

Вот код, если вы хотите получить к нему прямой доступ:

function setup() {
createCanvas(400, 400);
}

var count = 0;

function draw() {
  background(250);
  rectMode(CENTER);
  stroke(0,0,255);
  fill(0,0,255);
  count += 1;

  var box1X = 100;
  var box1Y = 100;
  var box2X = 160;
  var box2Y = 100;
  var box1R = count;
  var box2R = -60-count;
  var box1W = 50;
  var box1H = 50;
  var box2W = 50;
  var box2H = 50;

  translate(box1X, box1Y);
  rotate(radians(box1R));
  rect(0, 0, box1W, box1H);
  rotate(radians(-box1R));
  translate(-box1X, -box1Y);

  translate(box2X, box2Y);
  rotate(radians(box2R));
  rect(0, 0, box2W, box2H);
  rotate(radians(-box2R));
  translate(-box2X, -box2Y);

  stroke(255,0,0);
  fill(255,0,0);

  var pointRotated = [];
  pointRotated.push(GetPointRotated(box1X, box1Y, box1R, -box1W/2, box1H/2));  // Dot1
  pointRotated.push(GetPointRotated(box1X, box1Y, box1R, box1W/2, box1H/2));   // Dot2
  pointRotated.push(GetPointRotated(box1X, box1Y, box1R, -box1W/2, -box1H/2)); // Dot3
  pointRotated.push(GetPointRotated(box1X, box1Y, box1R, box1W/2, -box1H/2));  // Dot4
  pointRotated.push(createVector(box1X, box1Y)); // Dot5

  for (var i=0;i<pointRotated.length;i++){
ellipse(pointRotated[i].x,pointRotated[i].y,3,3);
  }
}

function GetPointRotated(X, Y, R, Xos, Yos){
// Xos, Yos // the coordinates of your center point of rect
// R      // the angle you wish to rotate

//The rotated position of this corner in world coordinates    
var rotatedX = X + (Xos  * cos(radians(R))) - (Yos * sin(radians(R)))
var rotatedY = Y + (Xos  * sin(radians(R))) + (Yos * cos(radians(R)))

return createVector(rotatedX, rotatedY)
}
<script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.3.3/p5.min.js"></script>

Danfus
источник
1

Рефакторинг приведенного выше кода дает очищенную форму, которая также подчеркивает простой факт, что каждый угол в основном center + height/2 + width/2, со знаками, подходящими для каждого угла. Это также имеет место, если вы рассматриваете height/2и width/2как повернутые векторы.

Доверие переводчику встроить помощников, это должно быть довольно эффективно, если мы попытаемся это сравнить.

function addPoints(p1, p2) {
    return { x: p1.x + p2.x, y: p1.y + p2.y }
}

function subPoints(p1, p2 ) {
    return { x: p1.x - p2.x, y: p1.y - p2.y }
}

function multPoints(p1, p2 ) {
    return { x: p1.x * p2.x, y: p1.y * p2.y }
}

function getRulerCorners() {
    const sin = Math.sin(ruler.angle);
    const cos = Math.cos(ruler.angle);
    const height = { x: sin * ruler.height/2, y: cos * ruler.height/2 };
    const heightUp = addPoints(ruler, multPoints({x: 1, y :-1}, height));
    const heightDown = addPoints(ruler, multPoints({x: -1, y: 1}, height));
    const width = { x: cos * ruler.width/2, y: sin * ruler.width/2 };
    ruler.nw = subPoints(heightUp, width);
    ruler.ne = addPoints(heightUp, width );
    ruler.sw = subPoints(heightDown, width);
    ruler.se = addPoints(heightDown, width);
}
Леамас
источник
0

Смотрите статью в Википедии о ротации . Суть такова:

(1) Если c - центральная точка, то углами являются c + ( L / 2, W / 2), +/- и т. Д., Где L и W - длина и ширина прямоугольника.

(2) Переведите прямоугольник так, чтобы центр c находился в начале координат, вычитая c из всех четырех углов.

(3) Поверните прямоугольник на 40 градусов с помощью приведенных формул триггера.

(4) Переведите обратно, добавив c к каждой координате.

Джозеф О'Рурк
источник
Спасибо за ваш ответ, но я боюсь, что не понимаю. Как я должен вычесть центр (известный) из углов (неизвестно), если они неизвестны? Я имею в виду, что координаты углов - это то, что я пытаюсь выяснить.
Stacky
Я пытался уточнить.
Джозеф О'Рурк
0

Возможно, есть некоторая оптимизация, доступная делением проблемы на две части:

  • вычислить центр верхней и нижней стороны, т. е. центр + повернутая высота / 2.
  • вычислить углы относительно этих центральных точек, используя повернутую ширину / 2
  • Рассчитать фактические синус и косинус раз и навсегда.

Код ниже, здесь прямоугольник называется линейкой. ruler.x, ruler, y - центр прямоугольника.

/** Middle point on rulers's top side. */
function getRulerTopMiddle(cos, sin) {
    return {
        x : ruler.x + sin * ruler.height/2,
        y : ruler.y - cos * ruler.height/2
    }
 }

/** Middle point on rulers's bottom side. */
function getRulerBottomMiddle(cos, sin) {
    return {
        x : ruler.x - sin * ruler.height/2,
        y : ruler.y + cos * ruler.height/2
    }
 }

/** Update ruler's four corner coordinates. */
function getRulerCorners() {
    const sin = Math.sin(ruler.angle);
    const cos = Math.cos(ruler.angle);
    const topMiddle = getRulerTopMiddle(cos, sin);
    const bottomMiddle = getRulerBottomMiddle(cos, sin);

    ruler.nw = {
        x: topMiddle.x - (cos * ruler.width/2),
        y: topMiddle.y - (sin * ruler.width/2)
    }   
    ruler.ne = {
        x: topMiddle.x + (cos * ruler.width/2),
        y: topMiddle.y + (sin * ruler.width/2)
    }   
    ruler.sw = {
        x: bottomMiddle.x - (cos * ruler.width/2),
        y: bottomMiddle.y - (sin * ruler.width/2)
    }   
    ruler.se = {
        x: bottomMiddle.x + (cos * ruler.width/2),
        y: bottomMiddle.y + (sin * ruler.width/2)
    }
}
Алек Леамас
источник
0

Немного поздно, но вот компактная функция, которую я использовал. Он вычисляет верхнюю и левую точки, а затем просто переворачивает их для противоположных углов.

rotatedRect(float x, float y, float halfWidth, float halfHeight, float angle)
{
    float c = cos(angle);
    float s = sin(angle);
    float r1x = -halfWidth * c - halfHeight * s;
    float r1y = -halfWidth * s + halfHeight * c;
    float r2x =  halfWidth * c - halfHeight * s;
    float r2y =  halfWidth * s + halfHeight * c;

    // Returns four points in clockwise order starting from the top left.
    return
        (x + r1x, y + r1y),
        (x + r2x, y + r2y),
        (x - r1x, y - r1y),
        (x - r2x, y - r2y);
}
cmann
источник
0

Старый пост, но вот еще один способ сделать это:

public static Point[] GetRotatedCorners(Rectangle rectangleToRotate, float angle)
{

    // Calculate the center of rectangle.
    Point center = new Point(rectangleToRotate.Left + (rectangleToRotate.Left + rectangleToRotate.Right) / 2, rectangleToRotate.Top + (rectangleToRotate.Top + rectangleToRotate.Bottom) / 2);

    Matrix m = new Matrix();
    // Rotate the center.
    m.RotateAt(360.0f - angle, center);

    // Create an array with rectangle's corners, starting with top-left corner and going clock-wise.
    Point[] corners = new Point[]
        {
            new Point(rectangleToRotate.Left, rectangleToRotate.Top), // Top-left corner.
            new Point(rectangleToRotate.Right, rectangleToRotate.Top),    // Top-right corner.
            new Point(rectangleToRotate.Right, rectangleToRotate.Bottom), // Bottom-right corner.
            new Point(rectangleToRotate.Left, rectangleToRotate.Bottom),  // Botton-left corner
        };

    // Now apply the matrix to every corner of the rectangle.
    m.TransformPoints(corners);

    // Return the corners of rectangle rotated by the provided angle.
    return corners;
}

Надеюсь, это поможет!

DiligentKarma
источник