Calculate angle while rotating rectangle - c#

I have a program in C# (Windows Forms) which has a rectangle on a Picture Box. They can be drawn at an angle too (rotated). I want to rotate that rectangle using my mouse movements.
I have the code for moving that rectangle
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Bool dragging = false;
Point ptOld = new Point(0, 0);
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.DrawRectangle(rectPen, areaRect);
}
protected override void OnMouseDown(MouseEventArgs e)
{
ptOld = new Point(e.X, e.Y);
dragging = true;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if(dragging = true)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy); // This one moves the rectangle
ptOld = ptNew;
this.Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
dragging = false;
}
Now My requirement is to rotate this rectangle, Any idea, how that can be achieved.

I think you want to calculate angle between two points on X-axis. If so, try the following code:
const double Rad2Deg = 180.0 / Math.PI;
return Math.Atan2(ptOld.Y - e.Y, e.X - ptOld.X) * Rad2Deg;
Also check out this article on calculating angle between two points

When rotating rectangle with mouse, you define the center of rotation (centerXY), in you case it will be the center of the rectangle maybe.
On mouse down record mouse coordinates, mouse_downXY. These two points define a base line. When moving mouse you'll define another line, formed by current mouse coordinates and the rectangle center.
So you'll need to compute the angle between line (centerXY, mouse_downXY) and (centerXY, current_mouseXY). Computing angle between 2 lines with knowing 3 points coordinates is simple trigonometry, so I won't write code for you :) However this post has the answer.

You can calculate the angle using the difference between the old and the new x mouse coordinate (dx in your example). You can use the RotateTransform method of the Graphics object to rotate the rectangle.
I modified your code to do the rotation in addition to the translation. You can move the rectangle with the left mouse button and you can rotate it using the right mouse button.
Rectangle areaRect = new Rectangle(100, 100, 300, 300);
bool dragging = false;
bool rotating = false;
Point ptOld = new Point(0, 0);
float angle = 0;
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.RotateTransform(angle);
dcPaint.DrawRectangle(Pens.Black, areaRect);
dcPaint.RotateTransform(-angle);
}
protected override void OnMouseDown(MouseEventArgs e)
{
ptOld = new Point(e.X, e.Y);
if (e.Button == MouseButtons.Left)
{
dragging = true;
}
if (e.Button == MouseButtons.Right)
{
rotating = true;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (dragging == true)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy); // This one moves the rectangle
ptOld = ptNew;
this.Invalidate();
}
if (rotating == true)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
angle = angle + dx / 10f;
ptOld = ptNew;
this.Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
dragging = false;
rotating = false;
}
Right now, the rectangle is rotated around its top left corner. If you apply a translation before the rotation, you can get it to rotate around its middle.

Related

PictureBox that moves when it should not move

I have a problem with a custom class that inherits from PictureBox. On the OnMouseDown event I center the location of my cursor, and on the OnMouseMove event I move the pictureBox.
The problem is that when I center the location of the cursor, it also moves the pictureBox.
I need first to locate the cursor on the center of the pictureBox and then move the pictureBox when I move the cursor.
I try to put a bool that controls the Click event, but it is doing all the same way.
Method that puts the pictureBox at a correct Point:
public void Colocar(Control control, Unidad unidad, Point p)
{
unidad.Location = p;
control.Controls.Add(unidad);
}
OnMouseDown event handler:
bool clickPerformed = false;
bool clickMove = false; //Para saber si hay que moverlo
private Point MouseDownLocation;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(this.Location.X + this.Size.Width / 2, this.Location.Y + this.Size.Height / 2);
clickPerformed = true;
Control tempSender = this.Parent;
tempSender.Invalidate();
MouseDownLocation = e.Location;
clickMove = true;
}
OnMouseUp event handler:
protected override void OnMouseUp(MouseEventArgs e)
{
this.Parent.Invalidate();
clickMove = false;
base.OnMouseDown(e);
}
Method to draw a circle arround the pictureBox:
public void DrawCircle(Graphics g, Pen pen, float centerX, float centerY, float radius)
{
g.DrawEllipse(pen, centerX - radius, centerY - radius, radius + radius, radius + radius);
}
OnMouseMove event handler:
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (clickMove)
{
Left = e.X + Left - MouseDownLocation.X;
Top = e.Y + Top - MouseDownLocation.Y;
}
}

Move a rectangle using angles

I need to move a rectangle using angles. Actually I want to change the direction of my moving rectangle when it reaches the location I have given in my code in if statement!
I just need the way I can find out how to move my rectangle at 60, 30, 60, 120, 150, 270 degrees!
Suppose that if
circle.Y>=this.Height-80
See this:
I really actually need to change the direction of rectangle movement using angles! so that at certain location reaches I can change the rectangle direction according to angle of my own choice!
such that:
if(circle.Y>=this.Height-80)
move in the direction of 90 degrees
if(circle.X>=this.Width-80)
move in the direction of 60 degree
as you can see in the screen shot!
What I have been trying is:
public partial class Form1 : Form
{
Rectangle circle;
double dx = 2;
double dy = 2;
public Form1()
{
InitializeComponent();
circle = new Rectangle(10, 10, 40, 40);
}
private void Form1_Load(object sender, EventArgs e)
{
this.Refresh();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillEllipse(new SolidBrush(Color.Red), circle);
}
private void timer_Tick(object sender, EventArgs e)
{
circle.X += (int)dx;
circle.Y += (int)dy;
if (circle.Y>=this.Height-80)
{
dy = -Math.Acos(0) * dy/dy; //here i want to change the direction of circle at 90 degrees so that it should go up vertically straight with same speed
}
this.Refresh();
}
}
The Problem is that I have been trying changing my conditions to:
dy = -Math.Asin(1) * dy;
dx = Math.Acos(0) * dx ;
but in both cases nothing is happening and the direction remains same!
I just want to move the circle in inverted upward direction at 90 degrees when it reach at
circle.Y>=this.Height-80
You need to draw the rectangle again to some image for it to display. I created this code for moving and drawing rectangle on pictureBox1, using your already defined circle-rectangle:
Moving the rectangle:
public void MoveRectangle(ref Rectangle rectangle, double angle, double distance)
{
double angleRadians = (Math.PI * (angle) / 180.0);
rectangle.X = (int)((double)rectangle.X - (Math.Cos(angleRadians) * distance));
rectangle.Y = (int)((double)rectangle.Y - (Math.Sin(angleRadians) * distance));
}
Drawing the rectangle and displaying it in the PictureBox:
public void DrawRectangle(Rectangle rectangle)
{
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.FillEllipse(new SolidBrush(Color.Red), rectangle);
}
pictureBox1.Image = bmp;
}
Demo it with a button click:
private void Button1_Click(object sender, EventArgs e)
{
MoveRectangle(ref circle, 90, 5);
DrawRectangle(circle);
}
Math.Asin(1) * dy is a constant value. Thus, you should use, for example, an instance variable that increments in each Tick of your timer.
...And *dy/dy is irrelevant.
public partial class Form1 : Form
{
Rectangle circle;
double dx = 2;
double dy = 2;
acum=0; //the new variable
...
private void timer_Tick(object sender, EventArgs e)
{
circle.X += (int)dx;
circle.Y += (int)dy;
if (circle.Y>=this.Height-300)
{
dy = -Math.Acos(acum);
acum+=1; //your accumulator
}
this.Refresh();
}
acos and asin are the inverse of sin and cos so the output of those two functions is an angle (usually in radians). This makes the code incorrect.
I strongly suggest that you read up on vector and matrix maths as using Euler angles can get quite messy.
So, you will have a position vector P and a movement vector M and the current position is:
P' = P + M.t
where t is time, P is the original position and P' is the current position.
Then, when you want to change direction, you create a rotation matrix and multiply the movement vector M by this rotation matrix.
The advantage here is that you can step from a 2D system to a 3D system by adding a Z component to your vectors and increasing the size of your matrices.

Draw rectangle with negative coordinates

When i'm trying to draw a rectangle in PictureBox with negative coordinates (-x and -y) the rectangle dissapears, though when it has positive coordinates everything is okay. Here's the code:
Here I get starting coordinates of rectangle
private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
{
start_point.X = e.X;
start_point.Y = e.Y;
}
Here I get the ending coordinates of rectangle:
private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
end_point.X = e.X;
end_point.Y = e.Y;
PictureBox1.Refresh();
}
}
Here I calculate the rectangles width and height:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(sb, start_point.X, start_point.Y, end_point.X - start_point.X, end_point.Y - start_point.Y);
}
If the starting point coordinates are smaller than ending ones, everything works just fine, but when the ending coordinates are smaller than starting ones, the width or height or both values are negative...
How can I solve this problem?
There are 4 possible ways for the user to drag the mouse to make the rectangle. Only one of them you're happy with right now, from upper-left to lower-right. The other 3 ways produce negative values for the rectangle's Width or Height. You deal with all 4 possibilities like this:
var rc = new Rectangle(
Math.Min(startpoint.x, endpoint.x),
Math.Min(startpoint.y, endpoint.y),
Math.Abs(endpoint.x - startpoint.x),
Math.Abs(endpoint.y - startpoint.y));
e.Graphics.FillRectangle(sb, rc);
If the starting X is < the ending X, just swap the values before drawing. Same for the Y coordinates.
if ( start_point.X < end_point.X )
{
var oldX = start_point.X;
start_point.X = end_point.X;
end_point.X = oldX;
}
if ( start_point.Y < end_point.Y )
{
var oldY = start_point.Y;
start_point.Y = end_point.Y;
end_point.Y = oldY;
}

Drawn shape/line fires on MouseEnter

i have PictureBox, how can I draw a shape/line that fires on MouseEnter event and change color or do more.
private void ImgViewer_Paint(object sender, PaintEventArgs e)
{
var graph = e.Graphics;
using (var pen = new Pen(Color.FromArgb(0, 255, 0)))
graph.DrawLine(pen, x1, y1, x2, y2);
}
this code is not enough, i guess
If you know the equation of the shape you could calculate whether the mouse is within or outside the shape area. Note that this is easy if the shape is consisted of the straight lines or circles (ellipses) for which the geometrical equations are relatively simple. For instance if your shape is a triangle with x and y coordinates (10,10), (50,10) and (30,50) than you should derive the equations of the lines using the equation of the line in two points:
y-y1 = ((y2-y1)/(x2-x1))*(x-x1)
the equations of the lines of our triangle would be:
y=1
y=2*x-10
y=-2*x+110
We should draw that triangle on some canvas, let's say on the PictureBox with FixedSingle border. Add the Paint event handler
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Point[] p = new Point[3];
p[0] = new Point(10,10);
p[1] = new Point(50,10);
p[2] = new Point(30,50);
e.Graphics.DrawLines(Pens.Black, p);
e.Graphics.FillPolygon(Brushes.Red, p);
}
We should add the MouseMove event handler for the PictureBox
bool inside = false;
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Y > 10 && e.Y < 2 * e.X - 10 && e.Y < -2 * e.X + 110)
{
if (!inside)
{
inside = true;
HandleMouseEnter();
}
}
else
inside = false;
}
void HandleMouseEnter()
{
MessageBox.Show("Mouse inside");
}
In if statement whether the mouse cursor is within the triangle (note that the coordinate origin in C# is on the top-left corner but it is similar to the real geometry). The HandleMouseEnter is the method that handles the mouse enter.
You could use similar approach for an arbitrary shape but you should have geometry equations that describe it.

Drawing reversible rectangle

I got code from http://support.microsoft.com/kb/314945 to draw a reversible/rubber band rectangle. I added code to it so that when i leave the left mouse button a rectangle is also created on the image and then i use that for cropping the image.
This works superb. the only problem is that the rubberband rectangle doesnot start or end from where the mouse is... there is very little diference but still it is quite notable. i use the same co-ords to draw the rectangle afterwards which is drawn exactly where my mouse started and where is ended. help would be appreciated.
Here is the code: (Got issue fixed - adding code that others can benefit from it)
I have made it a control and i use it wherever i need it!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace CroppingControl
{
public partial class CroppingImage : UserControl
{
Rectangle rc = new Rectangle();
Boolean bHaveMouse;
Point ptOriginal = new Point();
Point ptLast = new Point();
Image Pic;
public CroppingImage()
{
InitializeComponent();
pictureBox1.MouseDown += new MouseEventHandler(MyMouseDown);
pictureBox1.MouseUp += new MouseEventHandler(MyMouseUp);
pictureBox1.MouseMove += new MouseEventHandler(MyMouseMove);
bHaveMouse = false;
}
public Image Image
{
set
{
pictureBox1.Image = value;
Pic = value;
}
get
{
return pictureBox1.Image;
}
}
public void MyMouseDown(Object sender, MouseEventArgs e)
{
pictureBox1.Image = Pic;
// Make a note that we "have the mouse".
bHaveMouse = true;
// Store the "starting point" for this rubber-band rectangle.
ptOriginal.X = e.X;
ptOriginal.Y = e.Y;
// Special value lets us know that no previous
// rectangle needs to be erased.
ptLast.X = -1;
ptLast.Y = -1;
}
// Convert and normalize the points and draw the reversible frame.
private void MyDrawReversibleRectangle(Point p1, Point p2)
{
Point px = p1;
Point py = p2;
// Convert the points to screen coordinates.
p1 = PointToScreen(p1);
p2 = PointToScreen(p2);
// Normalize the rectangle.
if (p1.X < p2.X)
{
rc.X = p1.X;
rc.Width = p2.X - p1.X;
}
else
{
rc.X = p2.X;
rc.Width = p1.X - p2.X;
}
if (p1.Y < p2.Y)
{
rc.Y = p1.Y;
rc.Height = p2.Y - p1.Y;
}
else
{
rc.Y = p2.Y;
rc.Height = p1.Y - p2.Y;
}
// Draw the reversible frame.
ControlPaint.DrawReversibleFrame(rc, Color.Black, FrameStyle.Dashed);
rc.X = px.X;
rc.Y = px.Y;
}
// Called when the left mouse button is released.
public void MyMouseUp(Object sender, MouseEventArgs e)
{
// Set internal flag to know we no longer "have the mouse".
bHaveMouse = false;
// If we have drawn previously, draw again in that spot
// to remove the lines.
if (ptLast.X != -1)
{
Point ptCurrent = new Point(e.X, e.Y);
MyDrawReversibleRectangle(ptOriginal, ptLast);
Graphics graphics = pictureBox1.CreateGraphics();
Pen pen = new Pen(Color.Gray, 2);
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;
graphics.DrawRectangle(pen, rc);
}
// Set flags to know that there is no "previous" line to reverse.
ptLast.X = -1;
ptLast.Y = -1;
ptOriginal.X = -1;
ptOriginal.Y = -1;
}
// Called when the mouse is moved.
public void MyMouseMove(Object sender, MouseEventArgs e)
{
Point ptCurrent = new Point(e.X, e.Y);
// If we "have the mouse", then we draw our lines.
if (bHaveMouse)
{
// If we have drawn previously, draw again in
// that spot to remove the lines.
if (ptLast.X != -1)
{
MyDrawReversibleRectangle(ptOriginal, ptLast);
}
// Update last point.
ptLast = ptCurrent;
// Draw new lines.
MyDrawReversibleRectangle(ptOriginal, ptCurrent);
}
}
}
}
ControlPaint.DrawReversibleFrame uses screen co-ordinates to draw a rectangle on the screen (i.e. without respect to your application's windows) which is useful when acting upon drag mouse actions as the mouse may move outside of the application window. If you are using these very same co-ordinates raw to paint in your application, then they will be out as the co-ordinates on the control upon which you are painting are with respect to the control's origin (usually its top-left corner).
To use the screen co-ordinates, you first need to convert them into control co-ordinates using the PointToClient() or RectangleToClient() methods on the control upon which you are painting, e.g.
// panel`s OnPaint
Rectangle screenRectangle = ...
Rectangle clientRectangle = panel.RectangleToClient(screenRectangle);
graphics.DrawRectangle(Pens.Red, clientRectangle);

Categories