Предположим, что сценарий выглядит следующим образом:
, где нижний левый угол изображения (сплошная линия) имеет координаты (x_i, y_i)
и нижний левый угол контейнера (пунктирная линия) имеет координаты (X_c, Y_c)
.Кроме того, изображение (шириной w
и высотой h
) поворачивается против часовой стрелки на угол beta
относительно корпуса лаборатории, тогда как сам контейнер поворачивается (также против часовой стрелки) на угол alpha
..
Теперь давайте сосредоточимся, например, на верхнем правом углу P
.Что касается лабораторного кадра (глобальный холст), его координаты могут быть выражены как:
R(beta) . ( w, h ) + ( x_i, y_i )
, где .
обозначает умножение матрицы, а R
- матрица вращения против часовой стрелки
R(beta) = [ cos(beta) -sin(beta) ]
[ sin(beta) cos(beta) ]
Теперь нам нужно преобразовать это в координатную рамку относительно контейнера.Формально это означает, что нам нужно сначала вычесть смещение, а затем повернуть на -alpha
(или alpha
по часовой стрелке).Таким образом, со всем вместе:
R(-alpha).( R(beta) . (w, h) + (x_i, y_i) - (X_c, Y_c) )
Другие углы можно обрабатывать аналогично, просто заменив (w, h)
на правильные координаты ...
С точки зрения кода, можно реализовать этиформулы как:
//counter-clock-wise rotation by given angle in degrees
function rotateCCWBy(angle, {x, y}) {
const angle_rad = angle * Math.PI / 180;
const cos_a = Math.cos(angle_rad),
sin_a = Math.sin(angle_rad);
return {
x: cos_a * x - sin_a * y,
y: sin_a * x + cos_a * y
};
}
//shift by a multiple fac of an offset {xref, yref}
function offsetBy(fac, {x:xref, y:yref}, {x, y}) {
return {
x: fac*xref + x,
y: fac*yref + y
};
}
const image = {
coords: {x: 200, y: 0}, //lab-frame coordinates
angle: 45, //lab-frame rotation angle
width: 50,
height: 10
};
const container = {
coords: {x: 100, y: 100}, //lab-frame coordinates
angle: 45 //lab-frame rotation angle
};
//calculate the coordinates of the image's top-right corner
//with respect to the container
const corner = rotateCCWBy(-container.angle,
offsetBy(
-1, container.coords,
offsetBy(
+1, image.coords,
rotateCCWBy(image.angle,
{x: image.width, y: image.height}
)
)
)
);
console.log(corner);
РЕДАКТИРОВАТЬ:
В случае, если ось Y должна указывать «вниз», вышеприведенные формулы также работают, нужно просто интерпретировать углы как часы-вместо против часовой стрелки (поэтому в принципе функцию rotateCCWBy
следует переименовать в rotateCWBy
).В качестве примера рассмотрим этот сценарий:
Здесь верхний левый угол контейнера расположен в позиции (2,1) иСам контейнер поворачивается на 15 градусов.Изображение (черный прямоугольник) шириной 4 и высотой 2 поворачивается на 30 градусов, а его верхний левый угол находится в положении (3, 3).Теперь мы хотим вычислить координаты (x, y)
точки P относительно контейнера.
Используя:
const image = {
coords: {x: 3, y: 3}, //lab-frame coordinates
angle: 30, //lab-frame rotation angle
width: 4,
height: 2
};
const container = {
coords: {x: 2, y: 1}, //lab-frame coordinates
angle: 15 //lab-frame rotation angle
};
//calculate the coordinates of the image's top-left corner
//with respect to the container
const corner = rotateCCWBy(-container.angle,
offsetBy(
-1, container.coords,
offsetBy(
+1, image.coords,
rotateCCWBy(image.angle,
{x: image.width, y: image.height}
)
)
)
);
console.log(corner);
, получаем
{ x: 4.8296291314453415, y: 4.640160440463835 }
, которые могут быть(приблизительно) визуально проверено на прилагаемом рисунке.
EDIT2:
После дополнительного уточнения координаты изображения не должны быть "лабораторными рамками" (т.е. относительно холста), но относительно уже повернутого контейнера.Таким образом, преобразование должно быть адаптировано как:
const corner =
offsetBy(
+1, container.coords,
rotateCCWBy(container.angle,
offsetBy(
+1, image.coords,
rotateCCWBy(image.angle,
{x: image.width, y: image.height}
)
)
)
);
function rotateCCWBy(angle, {x, y}) {
const angle_rad = angle * Math.PI / 180;
const cos_a = Math.cos(angle_rad),
sin_a = Math.sin(angle_rad);
return {
x: cos_a * x - sin_a * y,
y: sin_a * x + cos_a * y
};
}