It will seems to most of the guys here that it's a repeated question but it isn't.
I am having a very hard problem. I have a rectangle which can be re-sized when it's not rotated and all work fine. But when it's rotated the algorithm is not working. The code is below:
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;
}
}
Now, here we have a mLayoutRectangle
a rectangle object in which some corners can be dragged with new position tmpX
and tmpY
through mouse. mAngle
is the angle in degree rotated.
The problem is when angle is between 0 and 80 it works fine but when the angle is greater than this its behavior is changed totally. I don't get what I am doing wrong here.
I have used the following rectangle datatype: Rectangle Structure from MSDN. I want to set the left, right, top and bottom values for this rectangle. Can i somehow extend it, so that i can set values for left, right, top and bottom???
The problem is well explained here: but i have to do that in window form not WPF. Exact explanation of the problem
1: Add a blank Windows Form
2: Add Mouse handlers for Paint
, MouseDown
, MouseUp
, MouseMove
3: Add AnchorPoint.cs:
public enum AnchorPoint
{
TopLeft,
TopRight,
BottomLeft,
BottomRight,
Rotation,
Center
}
4: add 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: Edit 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();
}
}
}
}
The trick is to move your mouse points into the rectangle's space. This is done by making a matrix with the rectangles point of origin, its rotation and inverting it:
var mat = new Matrix();
mat.Translate(_rectPos.X, _rectPos.Y);
mat.Rotate(_rectRotation);
mat.Invert();
then you multiply your screen-space points with the inverted matrix to get you a point in the rectangle's space.
var point = mat.TransformPoint(new PointF(e.X, e.Y));
From there is very simple to do all your operations. i.e. all intersections are point-box tests, and all transformations and scaling are done on an axis-aligned box.
_rectPos
to the center of the rectangleEnjoy. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With