如何处理可以调整大小的选择矩形的旋转?
How to handle rotation of selection rectangle that can be resized?
在我的应用程序中,用户可以 select 图像区域。
用户在 Canvas
中拖动一个矩形区域。看起来像下面这样:
离子区域上方的圆圈 (Ellipse
) 是旋转区域的手柄。见下图:
当 selection 区域 未 旋转,并且用户使用方形手柄调整区域大小时,它起作用了。
但是当用户使用椭圆旋转 selection 区域,然后使用方形手柄调整该区域的大小时,它会在两个相反的方向上调整大小(在旋转坐标系的上下文中)。
我的元素结构如下:
Canvas
+--- Image (covers whole canvas)
+--- Canvas (Background white with opacity as shown in images above, and contains the elements to draw the
selection area. I'll call this the 'area canvas')
+--- Rectangle (left handle)
+--- Rectangle (top handle)
+--- Rectangle (right handle)
+--- Rectangle (bottom handle)
+--- Ellipse (rotation handle)
我使用 RotateTransform
进行旋转。变换应用于select离子区域的Canvas。
canvas 的 RenderTransformOrigin 是 (0.5, 0.5)。所以 Canvas 将始终围绕它的中心旋转。
假设该区域顺时针旋转 10 度。然后使用右侧的手柄调整它的大小。现在该区域应该只扩展到右侧。换句话说:区域 canvas 只有 2 个角应该移动,
发生了什么,该区域的左侧也移动了。因此,左侧的角也移动(向左)。
我认为这是由于区域canvas的width/height的变化也改变了中心点造成的。
但是如何解决呢? selection 区域的行为应该与 MS Word 等程序中的 selected 元素完全相同。
Link 到 Github 包含相关代码的存储库: https://github.com/websitetest/selection
解决方案是添加一个平移,取消旋转后canvas中心的移动:所以我对你的方法UpdateResizeArea做了一些修改(数学解决方案)
private void UpdateResizeArea(Point currentMousePoint)
{
:
:
switch (_activeResizeAreaSide)
{
case SelectionArea.ResizeSide.LEFT:
{
_activeResizeArea.OffsetX = _activeResizeAreaXbeforeStart + deltaX;
_activeResizeArea.AreaWidth = _activeResizeAreaWidthbeforeStart - deltaX;
var tg = new TransformGroup();
var rad = _activeResizeArea.Rotation * Math.PI / 180;
var cx = _activeResizeArea.CalculateCenterPoint().X - _activeRotateAreaCenterPointBeforeStart.X;
var cy = _activeResizeArea.CalculateCenterPoint().Y - _activeRotateAreaCenterPointBeforeStart.Y;
var tx = cx * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d + cy * Math.Sin(rad);
var ty = cx * Math.Sin(rad) - cy * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d;
var t = new TranslateTransform(-tx, ty);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.TOP:
{
_activeResizeArea.OffsetY = _activeResizeAreaYbeforeStart + deltaY;
_activeResizeArea.AreaHeight = _activeResizeAreaHeightbeforeStart - deltaY;
var tg = new TransformGroup();
var rad = _activeResizeArea.Rotation * Math.PI / 180;
var cx = _activeResizeArea.CalculateCenterPoint().X - _activeRotateAreaCenterPointBeforeStart.X;
var cy = _activeResizeArea.CalculateCenterPoint().Y - _activeRotateAreaCenterPointBeforeStart.Y;
var tx = cy * Math.Sin(rad) + cx * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d;
var ty = cy * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d - cx * Math.Sin(rad);
var t = new TranslateTransform(-tx, -ty);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.RIGHT:
{
_activeResizeArea.AreaWidth = _activeResizeAreaWidthbeforeStart + deltaX;
var tg = new TransformGroup();
var rad = _activeResizeArea.Rotation * Math.PI / 180;
var cx = _activeResizeArea.CalculateCenterPoint().X - _activeRotateAreaCenterPointBeforeStart.X;
var cy = _activeResizeArea.CalculateCenterPoint().Y - _activeRotateAreaCenterPointBeforeStart.Y;
var tx = cx * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d + cy * Math.Sin(rad);
var ty = cx * Math.Sin(rad) - cy * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d;
var t = new TranslateTransform(-tx, ty);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.BOTTOM:
{
_activeResizeArea.AreaHeight = _activeResizeAreaHeightbeforeStart + deltaY;
var tg = new TransformGroup();
var rad = _activeResizeArea.Rotation * Math.PI / 180;
var cx = _activeResizeArea.CalculateCenterPoint().X - _activeRotateAreaCenterPointBeforeStart.X;
var cy = _activeResizeArea.CalculateCenterPoint().Y - _activeRotateAreaCenterPointBeforeStart.Y;
var tx = cy * Math.Sin(rad) + cx * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d;
var ty = cy * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d - cx * Math.Sin(rad);
var t = new TranslateTransform(-tx, -ty);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
}
您可以使解决方案适应您的项目..因为我刚刚添加了一些代码行而没有改变您程序的逻辑
__________________________________________________________________________________
另一种解决方案(没有数学微积分)是预先计算canvas的一个点与初始旋转中心的位置与新中心的同一点的新位置之间的差异:
switch (_activeResizeAreaSide)
{
case SelectionArea.ResizeSide.LEFT:
{
_activeResizeArea.OffsetX = _activeResizeAreaXbeforeStart + deltaX;
_activeResizeArea.AreaWidth = _activeResizeAreaWidthbeforeStart - deltaX;
var ir = new RotateTransform(_activeResizeArea.Rotation, _activeRotateAreaCenterPointBeforeStart.X, _activeRotateAreaCenterPointBeforeStart.Y);
var fr = new RotateTransform(_activeResizeArea.Rotation, _activeResizeArea.CalculateCenterPoint().X, _activeResizeArea.CalculateCenterPoint().Y);
var ip = ir.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var fp = fr.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var tg = new TransformGroup();
var txx = ip.X - fp.X;
var tyy = ip.Y - fp.Y;
var t = new TranslateTransform(txx, tyy);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.TOP:
{
_activeResizeArea.OffsetY = _activeResizeAreaYbeforeStart + deltaY;
_activeResizeArea.AreaHeight = _activeResizeAreaHeightbeforeStart - deltaY;
var ir = new RotateTransform(_activeResizeArea.Rotation, _activeRotateAreaCenterPointBeforeStart.X, _activeRotateAreaCenterPointBeforeStart.Y);
var fr = new RotateTransform(_activeResizeArea.Rotation, _activeResizeArea.CalculateCenterPoint().X, _activeResizeArea.CalculateCenterPoint().Y);
var ip = ir.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var fp = fr.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var tg = new TransformGroup();
var txx = ip.X - fp.X;
var tyy = ip.Y - fp.Y;
var t = new TranslateTransform(txx, tyy);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.RIGHT:
{
_activeResizeArea.AreaWidth = _activeResizeAreaWidthbeforeStart + deltaX;
var ir = new RotateTransform(_activeResizeArea.Rotation, _activeRotateAreaCenterPointBeforeStart.X, _activeRotateAreaCenterPointBeforeStart.Y);
var fr = new RotateTransform(_activeResizeArea.Rotation, _activeResizeArea.CalculateCenterPoint().X, _activeResizeArea.CalculateCenterPoint().Y);
var ip = ir.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var fp = fr.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var tg = new TransformGroup();
var txx = ip.X - fp.X;
var tyy = ip.Y - fp.Y;
var t = new TranslateTransform(txx, tyy);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.BOTTOM:
{
_activeResizeArea.AreaHeight = _activeResizeAreaHeightbeforeStart + deltaY;
var ir = new RotateTransform(_activeResizeArea.Rotation, _activeRotateAreaCenterPointBeforeStart.X, _activeRotateAreaCenterPointBeforeStart.Y);
var fr = new RotateTransform(_activeResizeArea.Rotation, _activeResizeArea.CalculateCenterPoint().X, _activeResizeArea.CalculateCenterPoint().Y);
var ip = ir.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var fp = fr.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var tg = new TransformGroup();
var txx = ip.X - fp.X;
var tyy = ip.Y - fp.Y;
var t = new TranslateTransform(txx, tyy);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
}
结果:
在我的应用程序中,用户可以 select 图像区域。
用户在 Canvas
中拖动一个矩形区域。看起来像下面这样:
离子区域上方的圆圈 (Ellipse
) 是旋转区域的手柄。见下图:
当 selection 区域 未 旋转,并且用户使用方形手柄调整区域大小时,它起作用了。 但是当用户使用椭圆旋转 selection 区域,然后使用方形手柄调整该区域的大小时,它会在两个相反的方向上调整大小(在旋转坐标系的上下文中)。
我的元素结构如下:
Canvas
+--- Image (covers whole canvas)
+--- Canvas (Background white with opacity as shown in images above, and contains the elements to draw the
selection area. I'll call this the 'area canvas')
+--- Rectangle (left handle)
+--- Rectangle (top handle)
+--- Rectangle (right handle)
+--- Rectangle (bottom handle)
+--- Ellipse (rotation handle)
我使用 RotateTransform
进行旋转。变换应用于select离子区域的Canvas。
canvas 的 RenderTransformOrigin 是 (0.5, 0.5)。所以 Canvas 将始终围绕它的中心旋转。
假设该区域顺时针旋转 10 度。然后使用右侧的手柄调整它的大小。现在该区域应该只扩展到右侧。换句话说:区域 canvas 只有 2 个角应该移动, 发生了什么,该区域的左侧也移动了。因此,左侧的角也移动(向左)。 我认为这是由于区域canvas的width/height的变化也改变了中心点造成的。 但是如何解决呢? selection 区域的行为应该与 MS Word 等程序中的 selected 元素完全相同。
Link 到 Github 包含相关代码的存储库: https://github.com/websitetest/selection
解决方案是添加一个平移,取消旋转后canvas中心的移动:所以我对你的方法UpdateResizeArea做了一些修改(数学解决方案)
private void UpdateResizeArea(Point currentMousePoint)
{
:
:
switch (_activeResizeAreaSide)
{
case SelectionArea.ResizeSide.LEFT:
{
_activeResizeArea.OffsetX = _activeResizeAreaXbeforeStart + deltaX;
_activeResizeArea.AreaWidth = _activeResizeAreaWidthbeforeStart - deltaX;
var tg = new TransformGroup();
var rad = _activeResizeArea.Rotation * Math.PI / 180;
var cx = _activeResizeArea.CalculateCenterPoint().X - _activeRotateAreaCenterPointBeforeStart.X;
var cy = _activeResizeArea.CalculateCenterPoint().Y - _activeRotateAreaCenterPointBeforeStart.Y;
var tx = cx * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d + cy * Math.Sin(rad);
var ty = cx * Math.Sin(rad) - cy * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d;
var t = new TranslateTransform(-tx, ty);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.TOP:
{
_activeResizeArea.OffsetY = _activeResizeAreaYbeforeStart + deltaY;
_activeResizeArea.AreaHeight = _activeResizeAreaHeightbeforeStart - deltaY;
var tg = new TransformGroup();
var rad = _activeResizeArea.Rotation * Math.PI / 180;
var cx = _activeResizeArea.CalculateCenterPoint().X - _activeRotateAreaCenterPointBeforeStart.X;
var cy = _activeResizeArea.CalculateCenterPoint().Y - _activeRotateAreaCenterPointBeforeStart.Y;
var tx = cy * Math.Sin(rad) + cx * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d;
var ty = cy * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d - cx * Math.Sin(rad);
var t = new TranslateTransform(-tx, -ty);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.RIGHT:
{
_activeResizeArea.AreaWidth = _activeResizeAreaWidthbeforeStart + deltaX;
var tg = new TransformGroup();
var rad = _activeResizeArea.Rotation * Math.PI / 180;
var cx = _activeResizeArea.CalculateCenterPoint().X - _activeRotateAreaCenterPointBeforeStart.X;
var cy = _activeResizeArea.CalculateCenterPoint().Y - _activeRotateAreaCenterPointBeforeStart.Y;
var tx = cx * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d + cy * Math.Sin(rad);
var ty = cx * Math.Sin(rad) - cy * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d;
var t = new TranslateTransform(-tx, ty);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.BOTTOM:
{
_activeResizeArea.AreaHeight = _activeResizeAreaHeightbeforeStart + deltaY;
var tg = new TransformGroup();
var rad = _activeResizeArea.Rotation * Math.PI / 180;
var cx = _activeResizeArea.CalculateCenterPoint().X - _activeRotateAreaCenterPointBeforeStart.X;
var cy = _activeResizeArea.CalculateCenterPoint().Y - _activeRotateAreaCenterPointBeforeStart.Y;
var tx = cy * Math.Sin(rad) + cx * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d;
var ty = cy * Math.Sin(rad / 2d) * Math.Sin(rad / 2d) * 2d - cx * Math.Sin(rad);
var t = new TranslateTransform(-tx, -ty);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
}
您可以使解决方案适应您的项目..因为我刚刚添加了一些代码行而没有改变您程序的逻辑
__________________________________________________________________________________
另一种解决方案(没有数学微积分)是预先计算canvas的一个点与初始旋转中心的位置与新中心的同一点的新位置之间的差异:
switch (_activeResizeAreaSide)
{
case SelectionArea.ResizeSide.LEFT:
{
_activeResizeArea.OffsetX = _activeResizeAreaXbeforeStart + deltaX;
_activeResizeArea.AreaWidth = _activeResizeAreaWidthbeforeStart - deltaX;
var ir = new RotateTransform(_activeResizeArea.Rotation, _activeRotateAreaCenterPointBeforeStart.X, _activeRotateAreaCenterPointBeforeStart.Y);
var fr = new RotateTransform(_activeResizeArea.Rotation, _activeResizeArea.CalculateCenterPoint().X, _activeResizeArea.CalculateCenterPoint().Y);
var ip = ir.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var fp = fr.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var tg = new TransformGroup();
var txx = ip.X - fp.X;
var tyy = ip.Y - fp.Y;
var t = new TranslateTransform(txx, tyy);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.TOP:
{
_activeResizeArea.OffsetY = _activeResizeAreaYbeforeStart + deltaY;
_activeResizeArea.AreaHeight = _activeResizeAreaHeightbeforeStart - deltaY;
var ir = new RotateTransform(_activeResizeArea.Rotation, _activeRotateAreaCenterPointBeforeStart.X, _activeRotateAreaCenterPointBeforeStart.Y);
var fr = new RotateTransform(_activeResizeArea.Rotation, _activeResizeArea.CalculateCenterPoint().X, _activeResizeArea.CalculateCenterPoint().Y);
var ip = ir.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var fp = fr.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var tg = new TransformGroup();
var txx = ip.X - fp.X;
var tyy = ip.Y - fp.Y;
var t = new TranslateTransform(txx, tyy);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.RIGHT:
{
_activeResizeArea.AreaWidth = _activeResizeAreaWidthbeforeStart + deltaX;
var ir = new RotateTransform(_activeResizeArea.Rotation, _activeRotateAreaCenterPointBeforeStart.X, _activeRotateAreaCenterPointBeforeStart.Y);
var fr = new RotateTransform(_activeResizeArea.Rotation, _activeResizeArea.CalculateCenterPoint().X, _activeResizeArea.CalculateCenterPoint().Y);
var ip = ir.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var fp = fr.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var tg = new TransformGroup();
var txx = ip.X - fp.X;
var tyy = ip.Y - fp.Y;
var t = new TranslateTransform(txx, tyy);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
case SelectionArea.ResizeSide.BOTTOM:
{
_activeResizeArea.AreaHeight = _activeResizeAreaHeightbeforeStart + deltaY;
var ir = new RotateTransform(_activeResizeArea.Rotation, _activeRotateAreaCenterPointBeforeStart.X, _activeRotateAreaCenterPointBeforeStart.Y);
var fr = new RotateTransform(_activeResizeArea.Rotation, _activeResizeArea.CalculateCenterPoint().X, _activeResizeArea.CalculateCenterPoint().Y);
var ip = ir.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var fp = fr.Transform(new Point(_activeResizeArea.OffsetX, _activeResizeArea.OffsetY));
var tg = new TransformGroup();
var txx = ip.X - fp.X;
var tyy = ip.Y - fp.Y;
var t = new TranslateTransform(txx, tyy);
tg.Children.Add(_activeResizeArea.RotateTransform);
tg.Children.Add(t);
_activeResizeArea.AreaCanvas.RenderTransform = tg;
}
break;
}
结果: