使用c#

时间:2016-02-02 12:50:13

标签: c# winforms math

对于大多数人来说,这似乎是一个重复的问题,但事实并非如此。

我遇到了一个非常棘手的问题。我有一个矩形,当它不旋转时可以重新调整大小并且一切正常。但是当它旋转时算法不起作用。代码如下:

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矩形对象,其中一些角可以通过鼠标用新位置tmpXtmpY拖动。 mAngle是旋转度数的角度。

问题是当角度介于0和80之间时,它可以正常工作,但是当角度大于此值时,其行为会完全改变。我不知道我在这里做错了什么。

我使用了以下矩形数据类型:Rectangle Structure from MSDN。我想为此矩形设置left,right,top和bottom值。我可以以某种方式扩展它,以便我可以设置左,右,上,下的值???

问题在这里得到了很好的解释:但我必须以窗口形式而不是WPF来做到这一点。 Exact explanation of the problem

1 个答案:

答案 0 :(得分:9)

1:添加空白Windows窗体

2:为PaintMouseDownMouseUpMouseMove

添加鼠标处理程序

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));

从那里完成所有操作非常简单。即所有交叉点都是点框测试,所有转换和缩放都是在轴对齐的框上完成的。

实施例

enter image description here

待办事项

  • 分开代码并制作可重复使用的
  • 停止拖动时,将_rectPos重置为矩形的中心

享受。 :)