对于大多数人来说,这似乎是一个重复的问题,但事实并非如此。
我遇到了一个非常棘手的问题。我有一个矩形,当它不旋转时可以重新调整大小并且一切正常。但是当它旋转时算法不起作用。代码如下:
internal void resizeToPoint(int tmpX, int tmpY, Rectangle limits)
{
Matrix m = new Matrix();
m.Reset();
m.Translate(mLayoutRectangle.X + mParent.getAbsXOffset(DEPTH_LEVEL.APA) + mLayoutRectangle.Width / 2, mLayoutRectangle.Y + mParent.getAbsYOffset(DEPTH_LEVEL.APA) + mLayoutRectangle.Height / 2);
m.Rotate(-(int)mAngle);
m.Translate(-(mLayoutRectangle.X + mParent.getAbsXOffset(DEPTH_LEVEL.APA) + mLayoutRectangle.Width / 2), -(mLayoutRectangle.Y + mParent.getAbsYOffset(DEPTH_LEVEL.APA) + mLayoutRectangle.Height / 2));
Point pPrime = new Point();
pPrime.X = (int)((m.Elements[0] * tmpX + m.Elements[2] * tmpY) + (int)m.Elements[4]);
pPrime.Y = (int)((m.Elements[1] * tmpX + m.Elements[3] * tmpY) + (int)m.Elements[5]);
Point BottomRight = new Point(mLayoutRectangle.Width + getAbsXOffset(), mLayoutRectangle.Height + getAbsYOffset());
Point TopRight = new Point(mLayoutRectangle.Width + getAbsXOffset(), getAbsYOffset());
Point TopLeft = new Point(getAbsXOffset(), getAbsYOffset());
Point BottomLeft = new Point(getAbsXOffset(), mLayoutRectangle.Height + getAbsYOffset());
Point TopMiddle = new Point((mLayoutRectangle.Width / 2) + getAbsXOffset(), getAbsYOffset());
Point RightMiddle = new Point(mLayoutRectangle.Width + getAbsXOffset(), mLayoutRectangle.Height / 2 + getAbsYOffset());
Point BottomMiddle = new Point(getAbsXOffset() + mLayoutRectangle.Width / 2, mLayoutRectangle.Height + getAbsYOffset());
Point LeftMiddle = new Point(getAbsXOffset(), getAbsYOffset() + mLayoutRectangle.Width / 2);
Rectangle newLocationRectangle = mLayoutRectangle;
Double widthRatioHeight = 1;
if (this is ImageDesignElement)
{
widthRatioHeight = (double)((ImageDesignElement)this).getMonochromeImage().Width / (double)((ImageDesignElement)this).getMonochromeImage().Height;
//widthRatioHeight = (double)mLayoutRectangle.Width / (double)mLayoutRectangle.Height;
}
int rectangleOldWidth = newLocationRectangle.Width;
int rectangleOldHeight = newLocationRectangle.Height;
int rectangleOldX = newLocationRectangle.X;
int rectangleOldY = newLocationRectangle.Y;
switch (mSelectedHandleForScaling)
{
case HANDLE_TYPE.TOP_LEFT:
if (newLocationRectangle.Height - (pPrime.Y - TopLeft.Y) > 0)
{
newLocationRectangle.Height -= (pPrime.Y - TopLeft.Y);
newLocationRectangle.Y += (pPrime.Y - TopLeft.Y);
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
if (newLocationRectangle.Width - (pPrime.X - TopLeft.X) > 0)
{
newLocationRectangle.Width -= (pPrime.X - TopLeft.X);
newLocationRectangle.X += (pPrime.X - TopLeft.X);
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
break;
case HANDLE_TYPE.TOP_MIDDLE:
if (newLocationRectangle.Height - (pPrime.Y - TopMiddle.Y) > 0)
{
newLocationRectangle.Height -= (pPrime.Y - TopMiddle.Y);
newLocationRectangle.Y += (pPrime.Y - TopMiddle.Y);
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
break;
case HANDLE_TYPE.TOP_RIGHT:
if (newLocationRectangle.Height - (pPrime.Y - TopRight.Y) > 0)
{
newLocationRectangle.Height -= (pPrime.Y - TopRight.Y);
newLocationRectangle.Y += (pPrime.Y - TopRight.Y);
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
if (newLocationRectangle.Width + pPrime.X - TopRight.X > 0)
{
newLocationRectangle.Width += pPrime.X - TopRight.X;
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
break;
case HANDLE_TYPE.RIGHT_MIDDLE:
if (newLocationRectangle.Width + pPrime.X - RightMiddle.X > 0)
{
newLocationRectangle.Width += pPrime.X - RightMiddle.X;
}
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
break;
case HANDLE_TYPE.BOTTOM_RIGHT:
if (newLocationRectangle.Height + pPrime.Y - BottomRight.Y > 0)
{
newLocationRectangle.Height += pPrime.Y - BottomRight.Y;
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
if (newLocationRectangle.Width + pPrime.X - BottomRight.X > 0)
{
newLocationRectangle.Width += pPrime.X - BottomRight.X;
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
break;
case HANDLE_TYPE.BOTTOM_MIDDLE:
if (newLocationRectangle.Height + pPrime.Y - BottomMiddle.Y > 0)
{
newLocationRectangle.Height += pPrime.Y - BottomMiddle.Y;
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
break;
case HANDLE_TYPE.BOTTOM_LEFT:
if (newLocationRectangle.Height + pPrime.Y - BottomLeft.Y > 0)
{
newLocationRectangle.Height += pPrime.Y - BottomLeft.Y;
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
if (newLocationRectangle.Width - (pPrime.X - BottomLeft.X) > 0)
{
newLocationRectangle.Width -= (pPrime.X - BottomLeft.X);
newLocationRectangle.X += (pPrime.X - BottomLeft.X);
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
break;
case HANDLE_TYPE.LEFT_MIDDLE:
if (newLocationRectangle.Width - (pPrime.X - BottomLeft.X) > 0)
{
newLocationRectangle.Width -= (pPrime.X - BottomLeft.X);
newLocationRectangle.X += (pPrime.X - BottomLeft.X);
mInitialScalePoint.X = pPrime.X;
mInitialScalePoint.Y = pPrime.Y;
}
break;
}
if (((Control.ModifierKeys & Keys.Shift) != Keys.Shift) && this is ImageDesignElement)
{
int hightChange = newLocationRectangle.Height - rectangleOldHeight;
int widthChange = newLocationRectangle.Width - rectangleOldWidth;
newLocationRectangle.Width = rectangleOldWidth;//reset to the old Width before the call of this method
newLocationRectangle.Width += (int)(((double)hightChange) * widthRatioHeight); //use the difference is width to adjust Width
if (mSelectedHandleForScaling == HANDLE_TYPE.TOP_LEFT || mSelectedHandleForScaling == HANDLE_TYPE.BOTTOM_LEFT)
{
newLocationRectangle.X = rectangleOldX + (rectangleOldWidth - newLocationRectangle.Width);
}
}
if (this is ImageDesignElement)
{
mLayoutRectangle = newLocationRectangle;
//Console.WriteLine("* mLayoutRectangle.Width =" + mLayoutRectangle.Width + " mLayoutRectangle.Height =" + mLayoutRectangle.Height);
}
else
{
mLayoutRectangle = newLocationRectangle;
}
}
现在,我们在这里有一个mLayoutRectangle
矩形对象,其中一些角可以通过鼠标用新位置tmpX
和tmpY
拖动。 mAngle
是旋转度数的角度。
问题是当角度介于0和80之间时,它可以正常工作,但是当角度大于此值时,其行为会完全改变。我不知道我在这里做错了什么。
我使用了以下矩形数据类型:Rectangle Structure from MSDN。我想为此矩形设置left,right,top和bottom值。我可以以某种方式扩展它,以便我可以设置左,右,上,下的值???
问题在这里得到了很好的解释:但我必须以窗口形式而不是WPF来做到这一点。 Exact explanation of the problem
答案 0 :(得分:9)
1:添加空白Windows窗体
2:为Paint
,MouseDown
,MouseUp
,MouseMove
3:添加AnchorPoint.cs:
public enum AnchorPoint
{
TopLeft,
TopRight,
BottomLeft,
BottomRight,
Rotation,
Center
}
4:添加MatrixExtension.cs
public static class MatrixExtension
{
public static PointF TransformPoint(this Matrix @this, PointF point)
{
var points = new[] { point };
@this.TransformPoints(points);
return points[0];
}
}
5:编辑Form1.cs:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private bool _drag;
private SizeF _dragSize;
private PointF _dragStart;
private PointF _dragStartOffset;
private RectangleF _dragRect;
private AnchorPoint _dragAnchor;
private Single _dragRot;
private RectangleF _rect;
private PointF _rectPos;
private Single _rectRotation;
public Form1()
{
InitializeComponent();
//Center location of our item
_rectPos = new PointF(200, 200);
//The rectangle dimensions in _rectPos space
_rect = new RectangleF(-40, -30, 80, 60);
_rectRotation = 0;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
var gc = e.Graphics;
// Move Graphics handler to Rectangle's space
var mat = new Matrix();
mat.Translate(_rectPos.X, _rectPos.Y);
mat.Rotate(_rectRotation);
gc.Transform = mat;
// All out gizmo rectangles are defined in Rectangle Space
var rectTopLeft = new RectangleF(_rect.Left - 5f, _rect.Top - 5f, 10f, 10f);
var rectTopRight = new RectangleF(_rect.Left + _rect.Width - 5f, _rect.Top - 5f, 10f, 10f);
var rectBottomLeft = new RectangleF(_rect.Left - 5f, _rect.Top + _rect.Height - 5f, 10f, 10f);
var rectBottomRight = new RectangleF(_rect.Left + _rect.Width - 5f, _rect.Top + _rect.Height - 5f, 10f, 10f);
var rectRotate = new RectangleF(-5, _rect.Top + -30, 10f, 10f);
var rectCenter = new RectangleF(-5, -5, 10f, 10f);
var backBrush = new SolidBrush(Color.CadetBlue);
var cornerBrush = new SolidBrush(Color.OrangeRed);
// Looks rotated because we've transformed the graphics context
gc.FillRectangle(backBrush, _rect);
gc.FillRectangle(cornerBrush, rectTopLeft);
gc.FillRectangle(cornerBrush, rectTopRight);
gc.FillRectangle(cornerBrush, rectBottomLeft);
gc.FillRectangle(cornerBrush, rectBottomRight);
gc.FillRectangle(cornerBrush, rectRotate);
gc.FillRectangle(cornerBrush, rectCenter);
// Reset Graphics state
gc.ResetTransform();
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
// Compute a Screen to Rectangle transform
var mat = new Matrix();
mat.Translate(_rectPos.X, _rectPos.Y);
mat.Rotate(_rectRotation);
mat.Invert();
// Mouse point in Rectangle's space.
var point = mat.TransformPoint(new PointF(e.X, e.Y));
var rect = _rect;
var rectTopLeft = new RectangleF(_rect.Left - 5f, _rect.Top - 5f, 10f, 10f);
var rectTopRight = new RectangleF(_rect.Left + _rect.Width - 5f, _rect.Top - 5f, 10f, 10f);
var rectBottomLeft = new RectangleF(_rect.Left - 5f, _rect.Top + _rect.Height - 5f, 10f, 10f);
var rectBottomRight = new RectangleF(_rect.Left + _rect.Width - 5f, _rect.Top + _rect.Height - 5f, 10f, 10f);
var rectRotate = new RectangleF(-5, _rect.Top + -30, 10f, 10f);
if (!_drag)
{
//We're in Rectangle space now, so its simple box-point intersection test
if (rectTopLeft.Contains(point))
{
_drag = true;
_dragStart = new PointF(point.X, point.Y);
_dragAnchor = AnchorPoint.TopLeft;
_dragRect = new RectangleF(_rect.Left, _rect.Top, _rect.Width, _rect.Height);
}
else if (rectTopRight.Contains(point))
{
_drag = true;
_dragStart = new PointF(point.X, point.Y);
_dragAnchor = AnchorPoint.TopRight;
_dragRect = new RectangleF(_rect.Left, _rect.Top, _rect.Width, _rect.Height);
}
else if (rectBottomLeft.Contains(point))
{
_drag = true;
_dragStart = new PointF(point.X, point.Y);
_dragAnchor = AnchorPoint.BottomLeft;
_dragRect = new RectangleF(_rect.Left, _rect.Top, _rect.Width, _rect.Height);
}
else if (rectBottomRight.Contains(point))
{
_drag = true;
_dragStart = new PointF(point.X, point.Y);
_dragAnchor = AnchorPoint.BottomRight;
_dragRect = new RectangleF(_rect.Left, _rect.Top, _rect.Width, _rect.Height);
}
else if (rectRotate.Contains(point))
{
_drag = true;
_dragStart = new PointF(point.X, point.Y);
_dragAnchor = AnchorPoint.Rotation;
_dragRect = new RectangleF(_rect.Left, _rect.Top, _rect.Width, _rect.Height);
_dragRot = _rectRotation;
}
else if (rect.Contains(point))
{
_drag = true;
_dragStart = new PointF(point.X, point.Y);
_dragAnchor = AnchorPoint.Center;
_dragRect = new RectangleF(_rect.Left, _rect.Top, _rect.Width, _rect.Height);
_dragStartOffset = new PointF(e.X - _rectPos.X, e.Y - _rectPos.Y);
}
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
_drag = false;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (_drag)
{
var mat = new Matrix();
mat.Translate(_rectPos.X, _rectPos.Y);
mat.Rotate(_rectRotation);
mat.Invert();
var point = mat.TransformPoint(new PointF(e.X, e.Y));
SizeF deltaSize;
PointF clamped;
switch (_dragAnchor)
{
case AnchorPoint.TopLeft:
clamped = new PointF(Math.Min(0, point.X), Math.Min(0, point.Y));
deltaSize = new SizeF(clamped.X - _dragStart.X, clamped.Y - _dragStart.Y);
_rect = new RectangleF(
_dragRect.Left + deltaSize.Width,
_dragRect.Top + deltaSize.Height,
_dragRect.Width - deltaSize.Width,
_dragRect.Height - deltaSize.Height);
break;
case AnchorPoint.TopRight:
clamped = new PointF(Math.Max(0, point.X), Math.Min(0, point.Y));
deltaSize = new SizeF(clamped.X - _dragStart.X, clamped.Y - _dragStart.Y);
_rect = new RectangleF(
_dragRect.Left,
_dragRect.Top + deltaSize.Height,
_dragRect.Width + deltaSize.Width,
_dragRect.Height - deltaSize.Height);
break;
case AnchorPoint.BottomLeft:
clamped = new PointF(Math.Min(0, point.X), Math.Max(0, point.Y));
deltaSize = new SizeF(clamped.X - _dragStart.X, clamped.Y - _dragStart.Y);
_rect = new RectangleF(
_dragRect.Left + deltaSize.Width,
_dragRect.Top,
_dragRect.Width - deltaSize.Width,
_dragRect.Height + deltaSize.Height);
break;
case AnchorPoint.BottomRight:
clamped = new PointF(Math.Max(0, point.X), Math.Max(0, point.Y));
deltaSize = new SizeF(clamped.X - _dragStart.X, clamped.Y - _dragStart.Y);
_rect = new RectangleF(
_dragRect.Left,
_dragRect.Top,
_dragRect.Width + deltaSize.Width,
_dragRect.Height + deltaSize.Height);
break;
case AnchorPoint.Rotation:
var vecX = point.X;
var vecY = -point.Y;
var len = Math.Sqrt(vecX*vecX + vecY*vecY);
var normX = vecX / len;
var normY = vecY / len;
//In rectangles's space,
//compute dot product between,
//Up and mouse-position vector
var dotProduct = (0*normX + 1*normY);
var angle = Math.Acos(dotProduct);
if (point.X < 0)
angle = -angle;
// Add (delta-radians) to rotation as degrees
_rectRotation += (float) ((180/Math.PI)*angle);
break;
case AnchorPoint.Center:
//move this in screen-space
_rectPos = new PointF(e.X - _dragStartOffset.X, e.Y - _dragStartOffset.Y);
break;
}
Invalidate();
}
}
}
}
诀窍是将鼠标点移动到矩形的空间中。这是通过制作一个带有矩形原点,旋转并反转它的矩阵来完成的:
var mat = new Matrix();
mat.Translate(_rectPos.X, _rectPos.Y);
mat.Rotate(_rectRotation);
mat.Invert();
然后将屏幕空间点与反转矩阵相乘,得到矩形空间中的一个点。
var point = mat.TransformPoint(new PointF(e.X, e.Y));
从那里完成所有操作非常简单。即所有交叉点都是点框测试,所有转换和缩放都是在轴对齐的框上完成的。
_rectPos
重置为矩形的中心享受。 :)