我正在开发一个WebGL (带有2d画布后备图片编辑器)。
我已经决定将旋转直接纳入作物工具,以类似于iOS 8照片裁剪器的方式。也就是说,照片的大小和位置会随着照片的旋转而发生动态变化,以便使作物区域始终包含在照片本身中。
然而,我正在努力解决一些数学问题。
我有两个长方形,照片和作物面积。
两者都被定义为:
var rect = {
x : x,
y : y,
w : width,
h : height
}
定义照片本身的矩形还具有一个以弧度表示的rotation
属性,当然,该属性描述照片的角度(以弧度表示)。
应该注意的是,照片矩形的x
和y
坐标(而不是,即作物矩形)实际上定义了照片的中心,而不是左上角。虽然这似乎很奇怪,但它使我们在其他地方的计算更容易,不应影响这一问题。为了完整起见,我要提一提。
照片的变换原点总是被设置为耕地的中心。
当照片的角度为0
,作物面积与照片大小相同时,矩形排列如下:
当我旋转照片时,会对照片矩形应用缩放因子,以确保裁剪区域保持在照片边界内:
这是通过计算作物面积的边框来实现的,并由此计算出适用于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));
目前,这完全按照预期的工作,照片矩形的宽度是正确的缩放,无论哪里的作物面积(以及由此产生的旋转起源)恰巧。
另外,由于这是一个光刻工具,当然可以修改种植面积的位置。请注意,当修改裁剪区域位置时,我们正在移动photo
矩形本身。(作物区始终保持中心,因为我们认为这是更自然的触摸设备,仍然可以接受时,与鼠标.这在iOS和windows 8中都是一样的。
因此,我们目前将x
和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));
和这个,是我遇到麻烦的地方。。
当作物区域不在照片中的中心位置时,我们最终会看到作物区域在照片的边界外移动,如下所示:
记住,旋转的起源总是种植区的中心。
在上面的照片中,您可以看到所需的宽度被正确地计算出来,并且照片被正确地缩放,以包括作物面积。
然而,由于我们的夹持功能只检查作物区域的边缘,我们结束了我们的作物区域以外的照片边界。
我们想修改我们的夹紧函数,以便它考虑到photo
矩形的旋转。因此,应该正确地夹紧x
和y
坐标(在上面的屏幕快照中),以确保最终结果如下所示:
不幸的是,我不知道从哪里开始的数学,并真正需要一些帮助。
我们目前使用相同的夹紧功能,同时旋转和移动photo
矩形,并希望继续这样做,如果可能的话。
发布于 2014-11-17 01:09:00
怎么了?
您需要在旋转坐标系中夹紧照片坐标,而不是夹紧原始坐标系中的照片坐标。在旋转坐标系中使用作物面积角的坐标(计算minX
、maxX
、minY
和maxY
)是没有意义的。
夹紧照片坐标
您需要在旋转坐标系中构造一个轴对齐的作物区域的包围盒,您可以使用它来夹紧。就像这样:
由于我们是围绕作物区域的中心旋转,中心坐标保持不变。然而,宽度和高度将增加。
请注意,您已经计算了这些量来适当地缩放照片。因此,简而言之,您只需替换计算边界框( crop.w
和bbox.h
)时得到的缩放时使用的宽度crop.h
和高度crop.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);
发布于 2014-11-10 16:34:53
正如DARK_DUCK所说,有一些方法(非平凡的方法)可以编写一个clamp
函数来解决您的问题。但是,当您说“钳子”时,我会自动假设您的解决方案必须限制用户到达某些无效状态,而IMHO不是一个好的解决方案。也许用户会旋转图像通过一些无效的状态,只是为了达到一个有效的状态。
我提出另一种解决办法:
状态将由图像原点(或位置)、旋转和比例组成。
您可以激活从无效状态到持久有效状态的转换。当处于无效状态时,可以通过将作物区边框改为红色通知用户:
有效状态是当作物区域的所有四个角都在图像矩形内时。以下函数将帮助您测试此条件:
//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));
}
你必须:
编辑:下面是一个例子:JSFiddle
这两个矩形有不同的起源。外(pictureArea
)矩形的旋转原点是内部(cropArea
)的原点。
祝好运!
发布于 2014-11-10 09:25:08
实际上,你不能“夹紧”x和y值,因为x和y是相互依赖的。我想说的是,你可以通过改变图像的x值或y值或x和y值的无限线性组合来解决问题(图像超出界限)。
因此,钳位函数将返回一组无限的正确值(如果没有解决方案,则不返回任何值)。如果您想要解决您的问题,您可能应该添加更多的条件,以规范的钳子功能。例如,您可以说箝位函数必须最小化max(displacementX, displacementY)
。如果添加此条件,则钳位函数将返回单个或没有解决方案。
https://stackoverflow.com/questions/26724327
复制相似问题