private void panel1_Paint(object sender, PaintEventArgs e)
{
Pen myPen = new Pen(Color.Blue, 1);
Pen myPen2 = new Pen(Color.Red, 1);
Point[] array = { new Point(639, 75), new Point(606, 124), new oint(664, 123) };
matrix.TransformPoints(array);
e.Graphics.Transform = matrix;
e.Graphics.RotateTransform(ang, MatrixOrder.Append);
myPen2.RotateTransform(ang, MatrixOrder.Append);
e.Graphics.DrawPolygon(myPen2, array);
}
I'm using Visual Studio 2010. The above code should be for drawing a triangle in C#, but I can't rotate it without drawn triangle location. How can I achieve that?
This will do the rotation:
private void panel1_Paint(object sender, PaintEventArgs e)
{
Pen myPen = new Pen(Color.Blue, 1);
Pen myPen2 = new Pen(Color.Red, 1);
Point[] array = { new Point(239, 75), new Point(206, 124), new Point(264, 123) };
float x = array.Select(_ => _.X).Sum() / array.Length;
float y = array.Select(_ => _.Y).Sum() / array.Length;
e.Graphics.DrawPolygon(myPen, array);
e.Graphics.TranslateTransform(x,y);
e.Graphics.RotateTransform(ang);
e.Graphics.TranslateTransform(-x, -y);
e.Graphics.DrawPolygon(myPen2, array);
}
No need to rotate a simple Pen although for more adavanced pens this may well be called for..
I have calculated the rotation center coordinates and then move the Graphics' origin there, rotate it and then move back. Then we can draw.
To test you can use a trackbar:
float ang = 0f;
private void trackBar1_Scroll(object sender, EventArgs e)
{
ang = (float) trackBar1.Value;
panel1.Invalidate();
}
You are using multiple methods mixed up. Don't rotate the pen/e.Graphics/matrix all together. When you call e.Graphics.RotateTransform you are manipulating the e.Graphics.Transform
You need to setup the matrix and assign it to the transform. In your case, your triangle has world coordinates, so you need to translate them before you can rotate them.
This will help:
public partial class Form1 : Form
{
// define triangle points ('world coords :'-( ?????????
private Point[] _triangle = { new Point(639, 75), new Point(606, 124), new Point(664, 123) };
// cache the angle
private float _angle = 0;
// cache the GObjects!!
private Pen _myPen = new Pen(Color.Blue, 1);
private Pen _myPen2 = new Pen(Color.Red, 1);
// The rotation origin position
private Point _origin = new Point(637, 110);
public Form1()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
// draw the origin triangle
e.Graphics.DrawPolygon(_myPen, _triangle);
// create a matrix
Matrix matrix = new Matrix();
// first translate the triangle coordinates with the negative origin coords (translate coords to origin coordinate system)
// This translation is only needed, because your triangle is in world coordinates..
matrix.Translate(-_origin.X, -_origin.Y, MatrixOrder.Append);
// do the rotation..
matrix.Rotate(_angle, MatrixOrder.Append);
// translate the triangle coordinates back to the 'world' coordinate system
matrix.Translate(_origin.X, _origin.Y, MatrixOrder.Append);
// assign the matrix to the Graphics
e.Graphics.Transform = matrix;
// Draw the polygon
e.Graphics.DrawPolygon(_myPen2, _triangle);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
float.TryParse(textBox1.Text, NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out _angle);
panel1.Refresh();
}
}
I would define the triangle with it's own origin, this way you can draw/rotate it anywhere on the panel.
Related
I have pictureBox click event. I get coordinates of click and try t draw circle:
private void pictureMain_Click(object sender, EventArgs e){
MouseEventArgs me = (MouseEventArgs)e;
Point coordinates = me.Location;
int x = coordinates.X;
int y = coordinates.Y;
// Create pen.
Pen blackPen = new Pen(Color.Red, 2);
// Create rectangle for ellipse.
Rectangle rect = new Rectangle(x, y, 50, 50);
g.DrawEllipse(blackPen, rect);
}
But it draws circle not in coordinates(x,y) of picturebox. It places circle in another place.
Try this:
private void pictureBox1_Click (object sender, EventArgs e)
{
Point ellipseCenter = ((MouseEventArgs) e).Location;
Size ellipseSize = new Size (50, 50);
Point rectPosition = new Point (ellipseCenter.X - ellipseSize.Width / 2, ellipseCenter.Y - ellipseSize.Height / 2);
Rectangle rect = new Rectangle (rectPosition, ellipseSize);
using (Graphics grp = Graphics.FromImage (pictureBox1.Image))
{
grp.DrawEllipse (Pens.Red, rect);
}
pictureBox1.Refresh ();
}
Imagine I have a line which is colored Gradient with three color : dark red, red and light red. I want to change position of these colors in that line . My purpose is showing something is moving along line.
I don't know How I can create animation for changing position of each colors in a line which is colored gradient.
I have found this : https://learn.microsoft.com/en-us/dotnet/framework/wpf/graphics-multimedia/how-to-animate-the-position-or-color-of-a-gradient-stop
but It isn't too clear.
Here is an example:
It uses a LineraGradientBrush, moving the starting point of the defining rectangle top left and painting a rotated rectangle onto a PictureBox:
Point p1 = Point.Empty;
private void timer1_Tick(object sender, EventArgs e)
{
int deltaX = -3;
int deltaY = -3;
p1 = new Point(p1.X + deltaX , p1.Y + deltaY); // roll..
if (p1.X < deltaX * 1000) p1 = Point.Empty; // ..around
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
float angle = 33f;
if (!timer1.Enabled) return;
Rectangle rectG = new Rectangle(p1.X, p1.Y, 122, 22);
Rectangle rectR = new Rectangle(22, 22, 222, 22);
LinearGradientBrush lBrush = new LinearGradientBrush(rectG,
Color.Red, Color.Red, angle, false);
ColorBlend cblend = new ColorBlend(5);
cblend.Colors = new Color[5]
{ Color.Red, Color.Pink, Color.MistyRose, Color.LightCoral, Color.White };
cblend.Positions = new float[5] { 0f, 0.2f, 0.5f, 0.8f, 1f };
lBrush.InterpolationColors = cblend;
lBrush.WrapMode = WrapMode.TileFlipXY;
e.Graphics.RotateTransform(angle);
e.Graphics.TranslateTransform(22,11);
e.Graphics.FillRectangle(lBrush, rectR);
}
Note that this being Winforms you can't get real smooth animations but if the control/form you paint on is DoubleBufered at least it won't flicker..
I have a list of Points that have been drawn on pictureBox1.
pictureBox1 has been transformed.
Now, I want to get XY coordinates of the point that was drawn as I hover over any drawn point.
When I hover over the pictureBox1, I am getting the XY of the pictureBox -- not a transformed XY.
Can you help me get to the transformed XY?
Thanks
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
int height = pictureBox1.ClientSize.Height / 2;
int width = pictureBox1.ClientSize.Width / 2;
//=====
//scale
//=====
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TranslateTransform(-width, -height);
e.Graphics.ScaleTransform(2f, 2f);
//===========
//draw center
//===========
e.Graphics.DrawLine(new Pen(Color.Black, 0.5f), new Point(width - 2, height), new Point(width + 2, height));
e.Graphics.DrawLine(new Pen(Color.Black, 0.5f), new Point(width, height - 2), new Point(width, height + 2));
//===========
//draw points
//===========
foreach (var p in Points)
{
Point[] pts = new Point[] { new Point(p.X, p.Y) };
Rectangle rc = new Rectangle(pts[0], new Size(1, 1));
e.Graphics.DrawRectangle(Pens.Red, rc);
}
}
As a variation to #Vitaly's answer you can do this:
After transforming the Graphics object you can save its transformation matrix e.Graphics.Transform in a variable:
Matrix matrix = null;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
int height = pictureBox1.ClientSize.Height / 2;
int width = pictureBox1.ClientSize.Width / 2;
//=====
//scale
//=====
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TranslateTransform(-width, -height);
e.Graphics.ScaleTransform(2f, 2f);
matrix = e.Graphics.Transform; // save the transformation matrix!
...
This is necessary as the transfomation data are lost after the Paint event!
Note that the GraphicsState graphics.Save()&Restore() functions can't be used very well for this purpose, as it only puts the state on the stack for using it once, meaning it doesn't save these data in a persistent way.
Later you can use the Matrix and this function to either transform Points with the same matrix or reverse the transformation, e.g. for mouse coordinates:
PointF transformed(Point p0, bool forward)
{
Matrix m = matrix.Clone();
if (!forward) m.Invert();
var pt = new Point[] { p0 };
m.TransformPoints(pt);
return pt[0];
}
Now my MouseMove event shows the location both raw and re-transformed:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
label1.Text = e.Location + " <-> " + transformed(e.Location, false) ;
}
And to test the forward transformation you could add this to the end of the Paint event:
e.Graphics.ResetTransform();
for (int i = 0; i < Points.Count; i++)
{
Point[] pts = new Point[] { Point.Round(transformed(Points[i], true)) };
Rectangle rc = new Rectangle(pts[0], new Size(19, 19));
e.Graphics.DrawRectangle(Pens.Red, rc);
}
This first clears all the transformations and then paints larger Rectangles at the same locations by calling the transformed function.
Note that this will also work with a rotated Graphics object. (Although the last test does not draw the larger rectangles rotated, just moved to the right locations.)
Also note that I return PointF for better precision when scaling with fractions. You can use Point.Round (or Point.Truncate) to get Point.
Do have a look the the Matrix.Elements: They contain the numbers you have used:
float scaleX = matrix.Elements[0];
float scaleY = matrix.Elements[3];
float transX = matrix.Elements[4];
float transY = matrix.Elements[5];
Finally: It is well worth studying the many methods of Matrix..!
You can create a Matrix with necessary transformations and apply it in pictureBox1_Paint(...) via MultiplyTransform(...):
https://msdn.microsoft.com/en-us/library/bt34tx5d(v=vs.110).aspx
Then you can use Matrix::TransformPoints(...) to get transformed XY
I have this code
Graphics g;
g.FillRectangle(new SolidBrush(Color.Red), _Location.X - 2, _Location.Y - 2, 10, 10);
and the rectangle is shot at an angle to some direction, how can I get the rectangle to rotate while moving or rotate at all.
This should rotate a rectangle moving across the screen.
private int _angle = 0;
private Point _location = new Point(0, 0);
private void _timer_Tick(object sender, System.EventArgs e)
{
// nothing interesting here, moving the top left co-ordinate of the
// rectangle at constant rate
_location = new System.Drawing.Point(_location.X + 2, _location.Y + 2);
_angle += 5; // our current rotation angle
this.Invalidate();
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
// rebase the co-ordinate system so our current x,y is 0, 0 and that is
// the center of the rotation
g.TranslateTransform(_location.X, _location.Y, MatrixOrder.Append);
g.RotateTransform(_angle); // do the rotation
// make sure the centre of the rectangle is the centre of rotation (0, 0)
g.FillRectangle(new SolidBrush(Color.Red), -5, -5, 10, 10);
}
I've figured it out right before I saw #steve16351 's response, but his code was still useful. What I did was switch from using PointF to Rectangle, because the Rectangle has a property which holds the value of the upper left corner's coordinates, so I can increment/decrement that at a stable rate, in my game loop, to make it rotate.
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.