如何在外旋矩形的边界内夹住一个矩形

gor*_*dyr 17 javascript math

我正在使用WebGL(使用2d画布后备照片编辑器).

我决定将旋转直接合并到裁剪工具中,其方式类似于iOS 8照片裁剪器.即,在旋转照片时,照片的大小和位置会动态变化,以便裁剪区域始终包含在照片本身中.

但是,我正在努力学习一些数学知识.

我有两个矩形,照片和裁剪区域.

两者都定义为:

var rect = {
     x : x,
     y : y,
     w : width,
     h : height
}
Run Code Online (Sandbox Code Playgroud)

定义照片本身的矩形也具有rotation弧度属性,当然也描述了照片的角度(以弧度表示).

应该注意的是,照片矩形(而不是裁剪矩形)的坐标xy坐标实际上定义了照片的中心,而不是左上角的点.虽然这看起来很奇怪,但它使我们在其他地方的计算更容易,并且不应该影响这个问题.为了完整起见,我提到它.

照片的变换原点始终设置为croparea 的中心.

当照片的角度0和裁剪区域与照片的大小相同时,矩形对齐如下:

在此输入图像描述

当我旋转照片时,比例因子应用于照片矩形以确保croparea保持在照片边界内:

在此输入图像描述

这是通过计算croparea的边界框来实现的,并从中计算出应用于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));
Run Code Online (Sandbox Code Playgroud)

这当前完全按照预期工作,并且无论croparea(和产生的旋转原点)恰好在哪里,都可以正确缩放照片矩形的宽度.

此外,由于这是一个照片裁剪工具,当然可以修改croparea的位置.请注意,在修改croparea位置时,我们正在移动photo矩形本身.(croparea始终保持居中,因为我们认为这对于基于触摸的设备来说更加自然,并且当与鼠标一起使用时仍然可以接受......这在iOS和Windows 8中都是相同的).

因此,我们目前将矩形xy坐标夹photo在croparea的边缘,如下所示:

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));
Run Code Online (Sandbox Code Playgroud)

这就是我遇到麻烦的地方.

当croparea不在照片中居中时,我们最终将croparea移动到照片的边界之外,如下所示:

在此输入图像描述

请记住,旋转的原点始终是croparea的中心

在上面的照片中,您可以看到正确计算所需宽度并正确缩放照片以包含croparea.

然而,由于我们的夹紧功能仅检查croparea的边缘,我们最终得到了照片边界外的croparea.

我们想修改我们的夹紧功能,以便考虑photo矩形的旋转.因此,应正确地钳制xy坐标(在上面的屏幕截图中)以确保最终结果如下所示:

在此输入图像描述

不幸的是,我不知道从哪里开始数学,可以真正使用一些帮助.

我们目前使用相同的夹紧功能,同时旋转和移动photo矩形,并希望尽可能继续这样做.

Tom*_*uwé 4

怎么了?

您不需要将照片坐标固定在原始坐标系中,而是需要将它们固定在旋转的坐标系中。在旋转坐标系中使用裁剪区域角点的坐标(计算minXmaxXminYmaxY)没有任何意义。

夹紧照片坐标

您需要在旋转坐标系中构造一个轴对齐的裁剪区域边界框,可用于夹紧。就像这样:

旋转坐标系中的边界框

由于我们围绕裁剪区域的中心旋转,因此中心坐标保持不变。但是宽度和高度会增加。

请注意,您已经计算了这些数量以适当缩放照片。简而言之,您可以将缩放时使用的宽度crop.w和高度替换crop.h为计算边界框(bbox.wbbox.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))
};
Run Code Online (Sandbox Code Playgroud)

请记住,最后两行获得的坐标仅适用于旋转坐标系。如果您需要它们在原始坐标系中,您仍然需要将它们旋转回来:

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);
Run Code Online (Sandbox Code Playgroud)