当前位置: 首页 > 知识库问答 >
问题:

如何在外部旋转矩形的边界内夹紧矩形

太叔正文
2023-03-14

我正在做一个WebGL(带有2d画布后备照片编辑器)。

我决定将旋转直接纳入裁剪工具,以类似于iOS8照片裁剪器的方式。也就是说,当您旋转照片时,照片的大小和位置会动态变化,以便裁剪区域始终包含在照片本身中。

但是,我正在为一些数学而苦苦挣扎。

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

两者都被定义为:

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

定义照片本身的矩形也有一个以弧度表示的< code>rotation属性,它当然描述了照片的角度(以弧度表示)。

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

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

当照片的角度为 0 且裁剪区域与照片大小相同时,矩形将按如下方式对齐:

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

这是通过计算croparea的边界框来实现的,并由此计算出应用于<code>照片

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的位置当然可以修改。请注意,当修改croparea位置时,我们正在移动< code>photo矩形本身。(croparea总是保持居中,因为我们觉得这对于基于触摸的设备来说更加自然,并且在使用鼠标时仍然可以接受...这在iOS和windows 8中都是一样的)。

因此,我们目前将< code>photo矩形的< code>x和< code>y坐标固定到十字区域的边缘,如下所示:

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未在照片中居中时,我们最终会看到croparea移动到照片边界之外,如下所示:

记住,旋转的原点总是正好在十字中心

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

然而,由于我们的夹持功能只检查作物区域的边缘,我们最终将作物区域置于照片边界之外。

我们希望修改我们的夹紧功能,以便将<code>照片</code>矩形的旋转考虑在内。因此,应正确夹紧xy(在上面的屏幕截图中)坐标,以确保最终结果如下所示:

不幸的是,我不知道从哪里开始学习数学,真的需要一些帮助。

我们目前在旋转和移动< code>photo矩形时使用相同的箝位功能,如果可能的话,我们希望继续这样做。

共有3个答案

师曦
2023-03-14

实际上你不能“钳制”x和y值,因为x和y是相互依赖的。我想说的是,你可以通过改变图像的x值或y值或x和y值的无限线性组合来解决问题(图像越界)。

因此,箝位函数将返回一组无限的正确值(如果没有解决方案,则不返回任何值)。如果您想解决您的问题,您可能应该在箝位函数的规范中添加更多条件。例如,您可以说箝位函数必须最小化<code>max(位移x,位移y)。若添加此条件,则箝位函数将返回单个或无解决方案。

岳玉书
2023-03-14

正如DARK_DUCK所说,有一些方法(非平凡的方法)可以编写一个clamp函数来解决您的问题。但是,当您说“夹”时,我自动假设您的解决方案必须限制用户达到一些无效状态,恕我直言这不是一个好的解决方案。也许用户会旋转通过一些无效状态的图像只是为了达到有效状态。

我提出了一个替代解决方案:

  1. 存储每次更改(旋转、平移、缩放)的最后一个有效状态
  2. 当用户完成其更改时,如果当前状态有效,则不执行任何操作,或者还原上一个有效状态。

有效状态是指裁剪区域的所有 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 位于矩形内,如果

    点pos(a,b,x)==-1

    编辑:这里有一个示例:JSIDLE

    这两个矩形有不同的原点。外部(< code>pictureArea)矩形的旋转原点是内部(< code>cropArea)矩形的原点。

    祝你好运!

  • 李敏学
    2023-03-14

    您需要在旋转坐标系中夹紧照片坐标,而不是在原始坐标系中夹紧照片坐标。在旋转坐标系中使用裁剪区域拐角的坐标(计算 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))
    };
    

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

    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);
    
     类似资料:
    • 我刚开始使用JavaFX,有一个问题。在我的项目中,我想使用旋转矩形。但矩形只围绕其中心旋转,我希望它围绕其左上角旋转。 就像这张照片(从这里开始): 下面是我的项目中的一些代码: 在这种情况下,如果按下箭头键,矩形会旋转。

    • 我需要能够计算出矩形2的大小 为了说明我的问题,这里有一个图表: 我知道矩形的和 1 我知道的纵横比以及始终大于矩形1的最小和 我知道的起源,它始终是矩形1的中心 我知道弧度旋转角度 矩形 1 必须始终完全位于矩形 2 内 给定上述变量,我需要计算矩形2的最小尺寸,同时保持其纵横比和旋转原点。 这个优秀的函数计算旋转外矩形中最大的可能矩形。 计算旋转矩形中的最大矩形 我试图用它作为基础来实现我所要

    • 我正在为我的播放器和bullet类创建一些围绕精灵的矩形,以使用LibGDX的Intersector类中的重叠方法检测碰撞。 我有一个问题: 当我实例化Player和Bullet时,我使用sprite.getBoundingRectangle()在精灵周围创建一个边界框,它返回一个Rectangle对象。我在主类的其他地方更新这些的移动。 当我更新子弹/玩家精灵的移动时,我是否也需要更新子弹/玩家

    • 问题内容: 我到处搜索,但找不到答案。 如何在Java中旋转矩形? 这是我的一些代码: 我尝试了g2d.rotate(100D); 但它没有用。提前致谢。 这是我编辑的代码: 问题答案: 对于图像,必须将Graphics2D的drawImage方法与相对的AffineTransform一起使用。 对于形状,您可以旋转Graphics2D本身: 顺便说一句,您应该重写paintComponent方法

    • 问题内容: 我需要创建围绕其中心旋转的矩形(因此它们不必平行于坐标系的轴)。因此,基本上每个矩形都可以由 center-X , center-Y , width , height 和 angle定义 。然后,我要做的是对这些矩形中是否包含某些点进行计算(因此不会涉及任何绘图)。我想我不能使用该类,因为这些矩形将始终与坐标系的x和y轴平行。是通过编写自己的矩形类来获得此功能的唯一方法,还是可以使用任

    • 我有两个矩形,其中一个是另一个的剪裁。现在我想围绕剪切矩形的中心旋转较大的矩形并调整x / y值。 如何计算旋转后的新x/y值? 我只想绕着小盒子的中心旋转大盒子的x/y。因此,较大盒子的x/y点相对于较小盒子的顶部/左侧点。我有小盒子的宽度和高度,所以我可以计算大盒子相对于小盒子中心的x/y点。旋转的角度以度为单位。旋转可以是任何角度,例如10度。