¿Cómo calcular las posiciones de las esquinas / marcas de un rectángulo rotado / inclinado?

17

Tengo dos elementos, un punto 2D y un área rectangular. El punto representa el centro de esa área. También sé el ancho y la altura de esa área. Y el área está inclinada 40 ° con respecto a la cuadrícula.

Ahora me gustaría calcular las posiciones absolutas de cada marca de esquina de esa área inclinada solo usando estos datos. ¿Es eso posible?

Stacky
fuente

Respuestas:

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

Esto le dará la ubicación de un punto girado θ grados alrededor del origen. Dado que las esquinas del cuadrado giran alrededor del centro del cuadrado y no del origen, se deben agregar un par de pasos para poder usar esta fórmula. Primero necesita establecer el punto relativo al origen. Entonces puedes usar la fórmula de rotación. Después de la rotación, debe moverlo hacia atrás en relación con el centro del cuadrado.

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

¡Aplique esto a las 4 esquinas y listo!

Aholio
fuente
4

Es una técnica común rotar un punto alrededor de un pivote traduciéndolo a un sistema de coordenadas donde el pivote es el origen, luego girando sobre este origen y luego volviendo a las coordenadas mundiales. (Una muy buena explicación de este enfoque está disponible en Khan Academy )

Sin embargo, no está almacenando las esquinas de sus rectángulos en coordenadas mundiales, por lo que podemos adaptar un enfoque para adaptarlo a los datos que tiene disponibles.

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(θ))

Este enfoque se puede aplicar fácilmente a las otras tres esquinas.

Kelly Thomas
fuente
2

Basado en las otras respuestas, y para complementarlas, logré crear un ejemplo con P5 aquí .

Aquí está el código, en caso de que desee acceder a él directamente:

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
fuente
1

Refactorizar el código anterior proporciona un formulario limpio que también resalta el simple hecho de que cada esquina es básicamente center + height/2 + width/2, con signos apropiados para cada esquina. Esto también es válido si se trata height/2y width/2como vectores rotados.

Confiar en el intérprete para que incorpore a los ayudantes, esto debería ser bastante efectivo, si tratamos de compararlo.

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);
}
leamas
fuente
0

Vea el artículo de Wikipedia sobre rotación . La esencia es esta:

(1) Si c es el punto central, entonces las esquinas son c + ( L / 2, W / 2), +/- etc., donde L y W son la longitud y el ancho del rectángulo.

(2) Traslade el rectángulo de modo que el centro c esté en el origen, restando c de las cuatro esquinas.

(3) Gire el rectángulo 40 grados a través de las fórmulas trigonométricas citadas.

(4) Traducir de nuevo agregando c a cada coordenada.

Joseph O'Rourke
fuente
Gracias por su respuesta, pero me temo que no la entiendo. ¿Cómo se supone que debo restar el centro (conocido) de las esquinas (desconocido) si son desconocidos? Quiero decir, las coordenadas de las esquinas son las mismas cosas que estoy tratando de descubrir.
Stacky
Traté de aclarar.
Joseph O'Rourke
0

Posiblemente, hay algunas optimizaciones disponibles al dividir el problema en dos:

  • Calcule el centro de la parte superior e inferior, es decir, centro + altura rotada / 2.
  • Calcule las esquinas relativas a estos puntos centrales utilizando el ancho girado / 2
  • Calcule el seno y el coseno reales de una vez por todas.

Código a continuación, aquí el rectángulo se llama regla. regla.x, regla, y es el centro del rectángulo.

/** 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)
    }
}
Alec Leamas
fuente
0

Un poco tarde, pero aquí hay una función compacta que he usado. Calcula los puntos superior e izquierdo, luego solo los voltea para las esquinas opuestas.

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
fuente
0

Publicación anterior, pero aquí hay otra forma de hacerlo:

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

¡Espero eso ayude!

DiligenteKarma
fuente