拖拽角点缩放旋转/skew斜切图像 斜切场景功能异常求解
拖拽角点调整图像尺寸斜切场景异常解决方案
问题背景
开发支持拖拽角点调整图像尺寸的功能,要求兼容旋转、斜切(skew) 状态的图像。当前实现在标准矩形、仅添加旋转效果的矩形上运行正常,但添加斜切效果后功能出现异常。
现有实现代码如下:
//_originPosition is the position of the mouse before the drag //_originVector is the position of every corner before the drag //In this case: //0 = bottom left corner, 1 = top left, 2 = top right, 3 = bottom right Vector3 positionOffset = _originPosition - mousePosition; var xDirection = (_originVector[3] - _originVector[0]).normalized; var yDirection = (_originVector[1] - _originVector[0]).normalized; //The distance in a specific direction var xDifference = Vector3.Dot(xDirection, positionOffset); var yDifference = Vector3.Dot(yDirection, positionOffset); //The new position var xPosition = -xDifference * xDirection; var yPosition = -yDifference * yDirection; ImageDrawer.SelectedImage.Corner[0] = _originVector[0] - positionOffset; ImageDrawer.SelectedImage.Corner[1] = _originVector[1] + xPosition; ImageDrawer.SelectedImage.Corner[3] = _originVector[3] + yPosition;
各场景运行效果
- 普通矩形场景:功能正常

- 旋转图像场景:功能正常

- 斜切图像场景:功能异常

问题根因
现有逻辑默认图像相邻边互相垂直,该假设仅在普通矩形、旋转矩形场景下成立。斜切后的图像为邻边不垂直的平行四边形,此时将鼠标偏移量单独投影到两个邻边方向、再分别更新对应邻边角点的逻辑,会破坏平行四边形对边平行的几何约束,导致形变异常。
另外当前逻辑没有利用平行四边形对角线中点重合的特性,角点位置计算没有锚定对角固定点,也是斜切场景下计算错误的原因。
修复方案
旋转、斜切后的图像本质始终是平行四边形,拖拽角点调整尺寸时只要满足三个约束即可兼容所有仿射变换场景:
- 被拖拽角点的对角点作为固定锚点,全程位置不变
- 所有边的方向与拖拽开始时的原始边方向保持一致,保证旋转角度、斜切角度不发生偏移
- 始终满足平行四边形几何规则:对边平行、对角线中点重合
修复后代码(以拖拽左下角点0为例)
// 拖拽开始时记录对角锚点位置(角点0的对角是右上角点2) Vector3 anchorPoint = _originVector[2]; // 1. 更新被拖拽的角点位置,这部分逻辑和原有实现一致 Vector3 newCorner0 = _originVector[0] - positionOffset; // 2. 取拖拽开始时的原始边方向,拖拽过程中方向固定不变,避免角度偏移 Vector3 dir01 = (_originVector[1] - _originVector[0]).normalized; // 左下角到左上角的原始边方向 Vector3 dir03 = (_originVector[3] - _originVector[0]).normalized; // 左下角到右下角的原始边方向 Vector3 dir21 = (_originVector[1] - _originVector[2]).normalized; // 右上角到左上角的原始边方向 Vector3 dir23 = (_originVector[3] - _originVector[2]).normalized; // 右上角到右下角的原始边方向 // 3. 直线求交得到另外两个角点的新位置 // 角点1是「过新角点0、沿dir01方向的直线」和「过锚点2、沿dir21方向的直线」的交点 Vector3 newCorner1 = GetLineIntersection(newCorner0, dir01, anchorPoint, dir21); // 角点3是「过新角点0、沿dir03方向的直线」和「过锚点2、沿dir23方向的直线」的交点 Vector3 newCorner3 = GetLineIntersection(newCorner0, dir03, anchorPoint, dir23); // 4. 更新角点位置,锚点2保持原始位置不需要修改 ImageDrawer.SelectedImage.Corner[0] = newCorner0; ImageDrawer.SelectedImage.Corner[1] = newCorner1; ImageDrawer.SelectedImage.Corner[3] = newCorner3;
需要补充直线求交工具函数:
/// <summary> /// 计算两条直线的交点 /// </summary> /// <param name="p1">直线1上的任意一点</param> /// <param name="dir1">直线1的方向向量</param> /// <param name="p2">直线2上的任意一点</param> /// <param name="dir2">直线2的方向向量</param> Vector3 GetLineIntersection(Vector3 p1, Vector3 dir1, Vector3 p2, Vector3 dir2) { Vector3 connectVec = p2 - p1; float crossDir = Vector3.Cross(dir1, dir2).z; float t = Vector3.Cross(connectVec, dir2).z / crossDir; return p1 + t * dir1; }
适配其他角点拖拽
如果需要拖拽其他三个角点,只需要替换对应的对角锚点、边方向即可:
- 拖拽左上角点1时,锚点为右下角点3
- 拖拽右上角点2时,锚点为左下角点0
- 拖拽右下角点3时,锚点为左上角点1
注意:所有边方向、锚点位置、鼠标初始位置都要在拖拽开始的瞬间记录,拖拽过程中不要每帧重新计算,避免出现形变累积误差。
内容的提问来源于stack exchange,提问作者mrxan
相关产品推荐
相关产品推荐

