I want to move a Diamond Shape in the form(for example 2 pixels every 200ms) horizantally.
I used the following code in From_Paint Event.
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point p1 = new Point(5,0);
Point p2 = new Point(10, 5);
Point p3 = new Point(5, 10);
Point p4 = new Point(0, 5);
Point[] ps = { p1, p2, p3, p4, p1 };
g.DrawLines(Pens.Black, ps);
}
I know how to move a picturebox but how to do with shape.
Thanks,
Ani
You'll need to track your current location in a form level variable. If you do this, your Form1_Paint event can change the X pixel location each time it draws.
Just add a Timer to your form, and set it's interval to 200ms. Each 200ms, add 2 to your current X pixel, and invalidate your control (so it redraws).
Edit: Add this to your form:
int xOffset = 0;
Then, in your timer_Tick:
private void timer1_Tick(object sender, EventArgs e)
{
if (xOffset < 500)
xOffset += 2;
else
timer1.Enabled = false; // This will make it only move 500 pixels before stopping.... Change as desired.
this.Invalidate(); // Forces repaint
}
Change your paint event to:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point p1 = new Point(5 + xOffset,0);
Point p2 = new Point(10 + xOffset, 5);
Point p3 = new Point(5 + xOffset, 10);
Point p4 = new Point(0 + xOffset, 5);
Point[] ps = { p1, p2, p3, p4, p1 };
g.DrawLines(Pens.Black, ps);
}
use Timer then redraw on each tick.
Related
I've been trying to solve a problem I'm having with rotating an oval. The code that I'm posting works, but it's mimicking the kind of code I have to work with for real. Meaning, I can't use the OnPaint override and I'm limited to what I can do in the main code. I'm adding an oval to a graphic layer, and I need to be able to rotate, move and resize the oval. Move and resizing work flawlessly, but rotating doesn't work as I need it to.
If you click in the small box at the 9 oclock position,
the oval will rotate as expected:
The required behavior is to be able to click in the small box at the 12 oclock position, and have the oval rotate again. This does not occur. In order to get the oval to rotate again you need to click in the original 9 oclock position. What I'm really, really trying to find out is how to get the coordinates of the box at the 12 oclock position (or what ever position or location that box ends up after rotating) so that I can rotate it again. Thus far, I have been unable to figure it out. Please try and understand, I'm working with old code that was poorly written and I'm not allowed to change very much of it. What I write must integrate with what's already there. Below is the code snippet that demonstrates what I'm doing. I know I'm missing something obvious, just don't know what. Thanks.
public partial class Form1 : Form
{
int theAngle = 0;
Pen pen2 = new Pen(Color.FromArgb(255, 68, 125, 255), 4);
Pen pen3 = new Pen(Color.Green, 4);
Pen smallPen = new Pen(Color.Black, 1);
PointF center = new PointF(0, 0);
Rectangle rectangle = new Rectangle(20, 20, 100, 30);
Rectangle myRect2 = new Rectangle();
bool mouseBtnDown = false;
Graphics gw;
public Form1()
{
InitializeComponent();
this.MouseDown += mouseDown;
gw = this.CreateGraphics();
}
private void mouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (myRect2.Contains(e.Location))
theAngle+=90;
rotate();
}
if (e.Button == MouseButtons.Right)
{
//oval = false;
//mouseBtnDown = false;
theAngle = 0;
rectangle.X = e.X - 50;
rectangle.Y = e.Y - 15;
rectangle.Width = 100;
rectangle.Height = 30;
center.X = rectangle.Left + (0.5f * rectangle.Width);
center.Y = rectangle.Top + (0.5f * rectangle.Height);
myRect2.Size = new Size(15, 15);
myRect2.Location = new Point(rectangle.Left - 15, (rectangle.Top - (rectangle.Height / 2)) + 15);
drawstuff();
// Invalidate();
}
}
public void rotate()
{
var matrix = new Matrix();
matrix.RotateAt(theAngle, center);
gw.Transform = matrix;
drawstuff();
}
public void drawstuff()
{
gw.SmoothingMode = SmoothingMode.AntiAlias; // Creates smooth lines.
gw.DrawEllipse(pen2, rectangle);
gw.DrawRectangle(smallPen, myRect2);
}
}
You can use the Transform matrix to rotate points. So what I've done is get the 4 corner points of myRect2, rotate them using the same Transform matrix, then assign these to a rectangle. You can then use this new rectangle to check the location. I also changed the call to drawstuff() at the end of the right button click to call rotate(), this way the rotated rectangle can get updated when the ellipse is first placed, and the graphics transform angle gets updated to 0.
public partial class Form1 : Form
{
int theAngle = 0;
Pen pen2 = new Pen(Color.FromArgb(255, 68, 125, 255), 4);
Pen pen3 = new Pen(Color.Green, 4);
Pen smallPen = new Pen(Color.Black, 1);
PointF center = new PointF(0, 0);
Rectangle rectangle = new Rectangle(20, 20, 100, 30);
Rectangle myRect2 = new Rectangle();
Rectangle rotatedRect2 = new Rectangle();
bool mouseBtnDown = false;
Graphics gw;
public Form1()
{
InitializeComponent();
this.MouseDown += mouseDown;
gw = this.CreateGraphics();
}
private void mouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (rotatedRect2.Contains(e.Location))
theAngle += 90;
rotate();
}
if (e.Button == MouseButtons.Right)
{
//oval = false;
//mouseBtnDown = false;
theAngle = 0;
rectangle.X = e.X - 50;
rectangle.Y = e.Y - 15;
rectangle.Width = 100;
rectangle.Height = 30;
center.X = rectangle.Left + (0.5f * rectangle.Width);
center.Y = rectangle.Top + (0.5f * rectangle.Height);
myRect2.Size = new Size(15, 15);
myRect2.Location = new Point(rectangle.Left - 15, (rectangle.Top - (rectangle.Height / 2)) + 15);
rotate();
//drawstuff();
// Invalidate();
}
}
public void rotate()
{
var matrix = new Matrix();
matrix.RotateAt(theAngle, center);
gw.Transform = matrix;
// Get the 4 corner points of myRect2.
Point p1 = new Point(myRect2.X, myRect2.Y),
p2 = new Point(myRect2.X + myRect2.Width, myRect2.Y),
p3 = new Point(myRect2.X, myRect2.Y + myRect2.Height),
p4 = new Point(myRect2.X + myRect2.Width, myRect2.Y + myRect2.Height);
Point[] pts = new Point[] { p1, p2, p3, p4 };
// Rotate the 4 points.
gw.Transform.TransformPoints(pts);
// Update rotatedRect2 with those rotated points.
rotatedRect2.X = pts.Min(pt => pt.X);
rotatedRect2.Y = pts.Min(pt => pt.Y);
rotatedRect2.Width = pts.Max(pt => pt.X) - pts.Min(pt => pt.X);
rotatedRect2.Height = pts.Max(pt => pt.Y) - pts.Min(pt => pt.Y);
drawstuff();
}
public void drawstuff()
{
gw.SmoothingMode = SmoothingMode.AntiAlias; // Creates smooth lines.
gw.DrawEllipse(pen2, rectangle);
gw.DrawRectangle(smallPen, myRect2);
}
}
Something to note. Drawing to the screen like this (using the form's created graphics and drawing using your own draw function) means the ellipses/rectangles are only drawn once. i.e. if you draw a few then minimize your form, when you bring it back up the ellipses will be gone. Not sure if this is what you're after or not. One way to fix this would be to draw the ellipses to a Bitmap and then in the form's Paint event this bitmap is draw to the form.
I have two Points I want to connect with an Arc in the C# Graphics Class when my main form is painted. I also have the radius that arc should have and the direction the arc should turn from starting point to the next. I dont see how I should do that with the drawArc overloads provided. Can anybody help me with maybe a function that takes a starting point, a end point, an arc radius and a direction (Clockwise or counter-Clockwise) and then draws that arc? Or Rather I am trying to parse this from how .gcode files specify arc movements and if the radius is negative its a clockwise roation and vice versa.
Am looking forward to some input
I figured it out for you.
Here is the sample code. The key here is the function DrawArcBetweenTwoPoints(). For testing it also draws the center points and the two spokes. You should remove those lines and focus on the DrawArc() part.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TranslateTransform(ClientSize.Width/2, ClientSize.Height/2);
PointF A = new PointF(0, -40);
PointF B = new PointF(100, 40);
e.Graphics.DrawLine(Pens.DarkBlue, A, B);
DrawPoint(e.Graphics, Brushes.Black, A);
DrawPoint(e.Graphics, Brushes.Black, B);
DrawArcBetweenTwoPoints(e.Graphics, Pens.Red, A, B, 100);
}
public void DrawPoint(Graphics g, Brush brush, PointF A, float size = 8f)
{
g.FillEllipse(brush, A.X-size/2, A.Y-size/2, size, size);
}
public void DrawArcBetweenTwoPoints(Graphics g, Pen pen, PointF a, PointF b, float radius, bool flip = false)
{
if (flip)
{
PointF temp = b;
b =a;
a = temp;
}
// get distance components
double x = b.X-a.X, y = b.Y-a.Y;
// get orientation angle
var θ = Math.Atan2(y, x);
// length between A and B
var l = Math.Sqrt(x*x+y*y);
if (2*radius>=l)
{
// find the sweep angle (actually half the sweep angle)
var φ = Math.Asin(l/(2*radius));
// triangle height from the chord to the center
var h = radius*Math.Cos(φ);
// get center point.
// Use sin(θ)=y/l and cos(θ)=x/l
PointF C = new PointF(
(float)(a.X + x/2 - h*(y/l)),
(float)(a.Y + y/2 + h*(x/l)));
g.DrawLine(Pens.DarkGray, C, a);
g.DrawLine(Pens.DarkGray, C, b);
DrawPoint(g, Brushes.Orange, C);
// Conversion factor between radians and degrees
const double to_deg = 180/Math.PI;
// Draw arc based on square around center and start/sweep angles
g.DrawArc(pen, C.X-radius, C.Y-radius, 2*radius, 2*radius,
(float)((θ-φ)*to_deg)-90, (float)(2*φ*to_deg));
}
}
private void Form1_Resize(object sender, EventArgs e)
{
this.Refresh();
}
}
I tested the flip arc with the following code
DrawArcBetweenTwoPoints(e.Graphics, Pens.Red, A, B, 100);
DrawArcBetweenTwoPoints(e.Graphics, Pens.Red, A, B, 100, true);
Welcome to Stackoverflow!
I recommend using a Bezier Curve.
An implementation is shown below which you can adapt for your arc.
The BezierCurve (check Microsoft documentation) is a curve described fully by 4 points.
The start/end points should be clear.
The control points define the curvature of the arc.
You will need to calculate the control points yourself. I recommend trying Code Sample 1 to see how it functions and then adapting the second code sample for your specific purposes. Change the values of the ComputedRotationAngleA/B, flip them around. Change the ControlPointDistance amount. You should be able to get a good idea of how the C# implentation of it works.
Then calculate your own values and plug them into the Code provided in Code Sample 2.
Note:
ComputedRotationAngleA/B control the direction of the arc.
ControlPointDistance controls the point where the curvature is added to the arc.
Good luck!
Code Sample 1:
internal void DrawBezierCurveTest(Pen pen, PaintEventArgs e)
{
int ComputedRotationAngleA = 20, ComputedRotationAngleB = -20;
int ControlPointDistance = 5;
LinePath = new GraphicsPath();
var p1 = new Point(StartX, StartY);
var p2 = new Point(StartX + ControlPointDistance , StartY + ControlPointDistance );
p2.Offset(ComputedRotationAngleA, ComputedRotationAngleA);
var p3 = new Point(EndX - ControlPointDistance , EndY - ControlPointDistance );
p3.Offset(ComputedRotationAngleB, ComputedRotationAngleB);
var p4 = new Point(EndX , EndY);
LinePath.AddBezier(p1, p2, p3, p4);
e.Graphics.DrawPath(pen, LinePath);
}
Code Sample 2:
internal void DrawBezierCurve(Pen pen, PaintEventArgs e)
{
LinePath = new GraphicsPath();
var p1 = new Point(StartX, StartY); //starting point
var p2 = new Point(FirstDipX, FirstDipY) //first control point
var p3 = new Point(SecondDipX, SecondDipY); //second control point
var p4 = new Point(EndX, EndY); //ending point
LinePath.AddBezier(p1, p2, p3, p4);
e.Graphics.DrawPath(pen, LinePath);
}
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 need to be able to draw a polygon using mouse click locations.
Here is my current code:
//the drawshape varible is called when a button is pressed to select use of this tool
if (DrawShape == 4)
{
Point[] pp = new Point[3];
pp[0] = new Point(e.Location.X, e.Location.Y);
pp[1] = new Point(e.Location.X, e.Location.Y);
pp[2] = new Point(e.Location.X, e.Location.Y);
Graphics G = this.CreateGraphics();
G.DrawPolygon(Pens.Black, pp);
}
Thanks
Ok here is some sample code:
private List<Point> polygonPoints = new List<Point>();
private void TestForm_MouseClick(object sender, MouseEventArgs e)
{
switch(e.Button)
{
case MouseButtons.Left:
//draw line
polygonPoints.Add(new Point(e.X, e.Y));
if (polygonPoints.Count > 1)
{
//draw line
this.DrawLine(polygonPoints[polygonPoints.Count - 2], polygonPoints[polygonPoints.Count - 1]);
}
break;
case MouseButtons.Right:
//finish polygon
if (polygonPoints.Count > 2)
{
//draw last line
this.DrawLine(polygonPoints[polygonPoints.Count - 1], polygonPoints[0]);
polygonPoints.Clear();
}
break;
}
}
private void DrawLine(Point p1, Point p2)
{
Graphics G = this.CreateGraphics();
G.DrawLine(Pens.Black, p1, p2);
}
First, add this code:
List<Point> points = new List<Point>();
On the object you are drawing on, capture the OnClick event. One of the arguments should have the X and Y coordinates of the click. Add them to the points array:
points.Add(new Point(xPos, yPos));
And then finally, where you're drawing the lines, use this code:
if (DrawShape == 4)
{
Graphics G = this.CreateGraphics();
G.DrawPolygon(Pens.Black, points.ToArray());
}
EDIT:
Ok, so the above code isn't exactly correct. First of all, its most likely a Click event instead of a OnClick event. Second, To get the mouse position, you need two variables declared up with the points array,
int x = 0, y = 0;
Then have a mouse move event:
private void MouseMove(object sender, MouseEventArgs e)
{
x = e.X;
y = e.Y;
}
Then, in your Click event:
private void Click(object sender, EventArgs e)
{
points.Add(new Point(x, y));
}