You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

拖拽角点缩放旋转/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;

各场景运行效果

  • 普通矩形场景:功能正常
    普通矩形缩放效果
  • 旋转图像场景:功能正常
    旋转图像缩放效果
  • 斜切图像场景:功能异常
    斜切图像缩放异常效果

问题根因

现有逻辑默认图像相邻边互相垂直,该假设仅在普通矩形、旋转矩形场景下成立。斜切后的图像为邻边不垂直的平行四边形,此时将鼠标偏移量单独投影到两个邻边方向、再分别更新对应邻边角点的逻辑,会破坏平行四边形对边平行的几何约束,导致形变异常。
另外当前逻辑没有利用平行四边形对角线中点重合的特性,角点位置计算没有锚定对角固定点,也是斜切场景下计算错误的原因。

修复方案

旋转、斜切后的图像本质始终是平行四边形,拖拽角点调整尺寸时只要满足三个约束即可兼容所有仿射变换场景:

  1. 被拖拽角点的对角点作为固定锚点,全程位置不变
  2. 所有边的方向与拖拽开始时的原始边方向保持一致,保证旋转角度、斜切角度不发生偏移
  3. 始终满足平行四边形几何规则:对边平行、对角线中点重合

修复后代码(以拖拽左下角点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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 04:12:30