Как я могу закрепить прямоугольник внутри границ внешнего повернутого прямоугольника

Я работаю над WebGL (с 2d файловым редактором).

Я решил включить поворот непосредственно в инструмент обрезки, подобный фотосъемке iOS 8. то есть размер и положение фотографии изменяются динамически при повороте фотографии, чтобы область обрезки всегда содержалась в самой фотографии.

Однако я борюсь с некоторыми из математики.

У меня есть два прямоугольника, фотография и область обрезки.

Оба определены как:

var rect = {
     x : x,
     y : y,
     w : width,
     h : height
}

Прямоугольник, который сам определяет фотографию, также имеет свойство rotation в радианах, что, конечно, описывает угол фотографии (в радианах).

Следует отметить, что координаты x и y прямоугольника фотографии ( не прямоугольник обрезки) фактически определяют центр фотографии и не верхняя левая точка. Хотя это может показаться странным, это упрощает расчеты для нас в другом месте и не должно влиять на этот вопрос. В интересах полноты я упоминаю это.

Происхождение трансформирования фотографии всегда задается как центр для урожая.

Когда угол фотографии 0, а область обрезки - того же размера, что и фотография, прямоугольники выравниваются так:

enter image description here

Когда я поворачиваю фотографию, масштаб масштабирования применяется к прямоугольнику фотографий, чтобы гарантировать, что область посева остается в пределах границ фотографий:

enter image description here

Это достигается путем вычисления ограничивающего прямоугольника области урожая и из этого, выработки требуемого масштабного коэффициента для применения к прямоугольнику photo:

TC.UI.PhotoCropper.prototype._GetBoundingBox = function(w,h,rads){
    var c = Math.abs(Math.cos(rads));
    var s = Math.abs(Math.sin(rads));
    return({  w: h * s + w * c,  h: h * c + w * s });
}

var bbox = this._GetBoundingBox(crop.w, crop.h, photo.rotation);
var scale = Math.max(1, Math.max(bbox.w/photo.w, bbox.h/photo.h));

В настоящее время он работает точно так, как предполагалось, и ширина прямоугольника фотографии правильно масштабируется везде, где имеет место croparea (и результирующее начало вращения).

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

Поэтому мы в настоящее время зажимаем координаты x и y прямоугольника photo к краям области посева следующим образом:

var maxX = crop.x + (crop.w/2) - (photo.w/2);
var maxY = crop.y + (crop.h/2) - (photo.w/2);
var minX = crop.x - (crop.w/2) + (photo.w/2);
var minY = crop.y - (crop.h/2) + (photo.h/2);

photo.x = Math.min(minX, Math.max(maxX, photo.x));
photo.y = Math.min(minY, Math.max(maxY, photo.y));

И вот, где я испытываю проблемы.

Когда область урожая не центрируется внутри фотографии, мы оказываемся в области croparea, движущейся за пределами фотографии, например:

enter image description here

Помните, что начало вращения всегда точно совпадает с центром области crop

На фотографии выше вы можете увидеть, что требуемая ширина правильно рассчитана, и фото правильно масштабируется, чтобы охватить область crop.

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

Мы хотели бы изменить нашу функцию зажима так, чтобы она учитывала поворот прямоугольника photo. Поэтому координаты x и y (на скриншоте выше) должны быть правильно зажаты, чтобы убедиться, что конечный результат выглядит следующим образом:

enter image description here

К сожалению, я понятия не имею, с чего начать с математики и действительно может использовать некоторую помощь.

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

Ответы

Ответ 1

Что не так?

Вместо того, чтобы зажимать координаты фотографии в исходной системе координат, вам нужно зажать их в повернутой системе координат. Используя координаты углов области посева (при расчете minX, maxX, minY и maxY) в повернутой системе координат нет смысла.

Закрепление фотокодов

Вам нужно построить выровненный по оси ограничивающий прямоугольник области обрезки в повернутой системе координат, которую вы можете использовать для зажима. Например:

Bounding box in the rotated coordinate system

Поскольку мы вращаемся вокруг центра области посева, координаты центра остаются неизменными. Однако ширина и высота будут увеличиваться.

Обратите внимание, что вы уже рассчитали эти количества, чтобы соответствующим образом масштабировать фотографию. Короче говоря, вы можете просто заменить ширину crop.w и height crop.h, используемую при масштабировании, полученную при вычислении ограничивающей рамки (bbox.w и bbox.h):

var maxX = crop.x + (bbox.w/2) - (photo.w/2);
var maxY = crop.y + (bbox.h/2) - (photo.w/2);
var minX = crop.x - (bbox.w/2) + (photo.w/2);
var minY = crop.y - (bbox.h/2) + (photo.h/2);

center = {
    x: Math.min(minX, Math.max(maxX, photo.x)),
    y: Math.min(minY, Math.max(maxY, photo.y))
};

Имейте в виду, что координаты, полученные на последних двух линиях, работают только во вращающейся системе координат. Если вам нужны они в исходной системе координат, вам все равно придется повернуть их назад:

var sin = Math.sin(-rotation);
var cos = Math.cos(-rotation);

photo.x = crop.x + cos*(center.x - crop.x) - sin*(center.y - crop.y);
photo.y = crop.y + sin*(center.x - crop.x) + cos*(center.y - crop.y);

Ответ 2

Как DARK_DUCK сказал, что есть способы (нетривиальные способы) написать функцию clamp, которая решает вашу проблему. Однако, когда вы говорите "зажим", я автоматически предполагаю, что ваше решение должно ограничивать пользователей от доступа к некоторым недействительным состояниям, которые IMHO не являются хорошим решением. Возможно, пользователь будет вращать изображение, проходящее через некоторые недопустимые состояния, чтобы достичь допустимого состояния.

Я предлагаю альтернативное решение:

  • Сохранять последнее действительное состояние при каждом изменении (вращение, перевод, масштаб)
  • Когда пользователь заканчивает свое изменение, вы ничего не делаете, если текущее состояние действительно или вы восстанавливаете последнее действительное состояние.

Состояние будет состоять из начала изображения (или положения), вращения и масштаба.

Вы можете анимировать переход из недействительного состояния в последнее действующее состояние. Если вы находитесь в недопустимом состоянии, вы можете сообщить об этом пользователю, сделав красную область области обрезки: example

A допустимое состояние - это когда все 4 угла области обрезки находятся внутри прямоугольника изображения. Следующие функции помогут вам проверить это условие:

//rotate a point around a origin
function RotatePoint(point, angle, origin)
{
    var a = angle * Math.PI / 180.0;
    var sina = Math.sin(a);
    var cosa = Math.cos(a);

    return
    {
        x : origin.x + cosa * (point.x - origin.x) - sina * (point.y - origin.y),
        y : origin.y + sina * (point.x - origin.x) + cosa * (point.y - origin.y)
    }

}

//position of point relative to line defined by a and b
function PointPos(a, b, point)
{
    return Math.sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x));
}

Вы должны:

  • Вычислить положение углов прямоугольника изображения после применения поворотов и переводов
  • Проверьте, находятся ли прямоугольники прямоугольника внутри прямоугольника изображения, используя вторую функцию, указанную выше. Точка x находится внутри прямоугольника, если

    PointPos (a, b, x) == -1 && & PointPos (b, c, x) == -1 && & PointPos (c, d, x) == -1 && & PointPos (d, a, x) == -1;

Изменить: Вот пример: JSFiddle

Два прямоугольника имеют различное происхождение. Начало вращения внешнего (pictureArea) прямоугольника является началом внутреннего (cropArea).

Удачи!

Ответ 3

На самом деле вы не можете "зажимать" значения x и y, так как x и y являются взаимозависимыми. Я хочу сказать, что вы можете решить проблему (изображение из привязки), изменив значение x изображения или значение y ИЛИ бесконечную линейную комбинацию значений x и y.

Таким образом, функция clamp вернет бесконечный набор правильных значений (или не будет, если нет решения). Если вы хотите решить свою проблему, вы должны добавить дополнительные условия для спецификации функции зажима. Например, можно сказать, что функция зажима должна минимизировать max(displacementX, displacementY). Если вы добавите это условие, то функция clamp вернет одно или никакое решение.