How can i draw a trail/trace after a moving line? - c#

My code is drawing a circle then a line from the middle of the circle to the radius size of the circle and the line is moving by 1 angle.
Now i want to make that the line will leave some trail/trace after it like a radar effect.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
anglecounter += 1;
double x = pictureBox1.Size.Width / 2 + 256 *Math.Cos(anglecounter * Math.PI / 180);
double y = pictureBox1.Size.Height / 2 +256 * Math.Sin(anglecounter * Math.PI / 180);
CloudEnteringAlert.Paint(e.Graphics, factor, distance);
e.Graphics.DrawLine(
new Pen(Color.Red, 2f),
new Point(pictureBox1.Size.Width / 2, pictureBox1.Size.Height/2),
new Point((int)x, (int)y));
e.Graphics.DrawEllipse(
new Pen(Color.Red, 2f),
0, 0, pictureBox1.Size.Width, pictureBox1.Size.Height);
}
How can I do this?
EDIT**
This is what i did now in the top of the form i added:
PointF _pt = new PointF(0F, 0F);
PointF _pt2 = new PointF(1F, 1F);
PointF _pt3 = new PointF(2F, 2F);
Color _lineColor = Color.FromArgb(0, 255, 0);
private double anglecounter1;
Then the paint event is now look like this:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
anglecounter += 1;
anglecounter1 += 0.5;
double x = pictureBox1.Size.Width / 2 + 256 *Math.Cos(anglecounter * Math.PI / 180);
double y = pictureBox1.Size.Height / 2 +256 * Math.Sin(anglecounter * Math.PI / 180);
double x1 = pictureBox1.Size.Width / 2 + 256 * Math.Cos(anglecounter1 * Math.PI / 180);
double y1 = pictureBox1.Size.Height / 2 + 256 * Math.Sin(anglecounter1 * Math.PI / 180);
CloudEnteringAlert.Paint(e.Graphics, factor, distance);
e.Graphics.DrawLine(
new Pen(Color.Red, 2f),
new Point(pictureBox1.Size.Width / 2, pictureBox1.Size.Height/2),
new Point((int)x, (int)y));
e.Graphics.DrawEllipse(
new Pen(Color.Red, 2f),
0, 0, pictureBox1.Size.Width, pictureBox1.Size.Height);
// create the fade path and gradient
GraphicsPath gp = new GraphicsPath(FillMode.Winding);
gp.AddLine(new PointF((float)(pictureBox1.Size.Width / 2), (float)(pictureBox1.Size.Height / 2)),new PointF( (float)x1,(float)y1));
gp.AddCurve(new PointF[] { _pt2, _pt3, _pt });
gp.AddLine(new PointF((float)x, (float)y), new PointF((float)(pictureBox1.Size.Width / 2), (float)(pictureBox1.Size.Height / 2)));
PathGradientBrush pgb = new PathGradientBrush(gp);
pgb.CenterPoint = new PointF((float)x1, (float)y1);
pgb.CenterColor = Color.FromArgb(128, _lineColor);
pgb.SurroundColors = new Color[] { Color.Empty };
// draw the fade path
e.Graphics.FillPath(pgb, gp);
}
But if im not wrong in this case the trail/trace is slower then the line is getting faster then it. Also the trail/trace is also behind the line but also in front of the line . Im not sure but thats what i see. So what is wrong ?

Another option is to not erase the image each time. Instead, draw a semi-transparent circle over the previous image:
// Initialize some dimensions
int x = pictureBox1.Bounds.X;
int y = pictureBox1.Bounds.Y;
int w = Math.Min(pictureBox1.Bounds.Width, pictureBox1.Bounds.Height);
int h = w; // Force square
int centerX = w / 2;
int centerY = h / 2;
float radius = w - centerX;
Graphics g = pictureBox1.CreateGraphics();
// First time draw a solid background then
// each successive time cover with semi-transparent background
Brush backGround = firstTime ? new SolidBrush(Color.FromArgb(255, 0, 0, 0)) : new SolidBrush(Color.FromArgb(10, 0, 0, 0));
firstTime = false;
g.FillEllipse(backGround, 0, 0, w, h);
float lineX = (float)(centerX + (radius * Math.Sin(anglecounter * (Math.PI / 180))));
float lineY = (float)(centerX + (radius * Math.Cos(anglecounter * (Math.PI / 180))));
anglecounter -= 1;
g.DrawLine(new Pen(Color.Green, 3), centerX, centerY, lineX, lineY);
g.DrawArc(new Pen(Color.Red, 4), new Rectangle(0, 0, w - 1, h - 1), 0, 360);
Produces this result:

Related

Increasing the height of Panel according to wrapped string Label in Panel

i am trying to develop a win form for chatting purpose. I am developing Chat bubbles using pure inbuilt functions of .Net Framework, No fancy UI, No third party libraries.
Now let's have a look on how every thing is being done.
My following function is responsible for generating a Panel dynamically for each chat message received, the Pain event is used to draw rounded rectangle and color is transparent. A picture box is used to show static avatar.
private void SetRemoteMessage(string msg)
{
PictureBox pb = new PictureBox();
pb.Bounds = new Rectangle(0, 0, 72, 72);
pb.Image = Base64ToImage(avatar_his);
Panel p = new Panel();
Label lb = new Label();
lb.BackColor = Color.Transparent;
lb.ForeColor = Color.Blue;
lb.Text = msg;
lb.Font = new Font("Arial", 14, FontStyle.Bold, GraphicsUnit.Point);
p.Bounds = new Rectangle(rX, rY, (Width / 2) - 25, pb.Height);
p.BackColor = Color.Transparent;
lb.Size = new Size(p.Width - pb.Width, p.Height);
lb.Paint += _control_Paint;
lb.Location = new Point(pb.Width + 5, 0);
p.Controls.Add(pb);
p.Controls.Add(lb);
SetPanel(p);
rY += p.Height + 20;
mY += p.Height + 20;
}
following is the Paint event binded to the parent "Container" Panel so that a simple rounded rectangle is shown for each bubble
private void _control_Paint(object sender, PaintEventArgs e)
{
Control c = (Control)sender;
if(!c.Name.Equals("lb"))
{
Graphics v = e.Graphics;
DrawRoundRect(v, Pens.Blue, e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1, 10);
}
else
{
using (Font font1 = new Font("Arial", 12, FontStyle.Bold, GraphicsUnit.Point))
{
e.Graphics.DrawString(c.Text, font1, Brushes.Blue, c.Bounds);
}
}
base.OnPaint(e);
}
and the following function to actually generate rounded rectangles
private void DrawRoundRect(Graphics g, Pen p, float X, float Y, float width, float height, float radius)
{
GraphicsPath gp = new GraphicsPath();
gp.AddLine(X + radius, Y, X + width - (radius * 2), Y);
gp.AddArc(X + width - (radius * 2), Y, radius * 2, radius * 2, 270, 90);
gp.AddLine(X + width, Y + radius, X + width, Y + height - (radius * 2));
gp.AddArc(X + width - (radius * 2), Y + height - (radius * 2), radius * 2, radius * 2, 0, 90);
gp.AddLine(X + width - (radius * 2), Y + height, X + radius, Y + height);
gp.AddArc(X, Y + height - (radius * 2), radius * 2, radius * 2, 90, 90);
gp.AddLine(X, Y + height - (radius * 2), X, Y + radius);
gp.AddArc(X, Y, radius * 2, radius * 2, 180, 90);
gp.CloseFigure();
g.DrawPath(p, gp);
}
The chat message is not wrapped automatically so to wrap it, the Pain method is used as it can be seen in Paint event . .the output is good as expected instead of one thing
The Problem can be seen easily the Panel height is not increasing according when a large message is given to display in bubble.
What i tried already is
Measuring string length with Graphics class, but i was not able to implement it with success
Enabling Scrollbars, Yes this approach worked but i am not interested to use this behavior
Counting string length and increasing height, This works but not efficient, specially when form is resized all calculations of measurement then become invalid.

How to draw a circular progressbar pie using GraphicsPath in WinForm?

I want my custom circular progress bar in WinForm. But the result doesnt fit to what I think. How can I draw the shape as the same in this picture?. I uploaded two image to be clear in my problem.
My code to do this:
void Form1_Paint(object sender, PaintEventArgs e)
{
int angle = 120;
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
Rectangle outerRect = new Rectangle(50, 50, 100, 100);
Rectangle innerRect = new Rectangle(70, 70, 60, 60);
int innerRadius = innerRect.Width / 2;
int outerRadius = outerRect.Width / 2;
Point innerCenter = new Point(innerRect.X + innerRadius, innerRect.Y + innerRadius);
Point outerCenter = new Point(outerRect.X + outerRadius, outerRect.Y + outerRadius);
GraphicsPath outerCircle = new GraphicsPath();
outerCircle.AddEllipse(outerRect);
GraphicsPath innerCircle = new GraphicsPath();
innerCircle.AddEllipse(innerRect);
GraphicsPath progPath = new GraphicsPath();
Point p1 = new Point(outerRect.X + outerRadius, outerRect.Y);
Point p2 = new Point(innerRect.X + innerRadius, innerRect.Y);
Point inner = new Point((int)(innerRadius * Math.Cos(angle * Math.PI / 180) + innerCenter.X),
(int)(innerRadius * Math.Sin(angle * Math.PI / 180) + innerCenter.Y));
Point outer = new Point((int)(outerRadius * Math.Cos(angle * Math.PI / 180) + outerCenter.X),
(int)(outerRadius * Math.Sin(angle * Math.PI / 180) + outerCenter.Y));
progPath.AddLine(p1, p2);
progPath.AddArc(innerRect, -90, angle);
progPath.AddLine(inner, outer);
progPath.AddArc(outerRect, angle - 90,-angle);
progPath.Widen(Pens.Black);
e.Graphics.DrawPath(Pens.Black, progPath);
}
You can create a GraphicsPath, then add 2 arcs to the path using AddArc method:
Outer arc from start angle 270 and sweep angle 120 degree.
Inner arc in opposite direction, from start angle 270 + 120 and sweep angle -120 degree
Then close the path using GraphicsPath.CloseFigure.
This way the you will have a thick arc as path.
You can fill the path, using Graphics.FillPath method. And also you can draw the borders using GraphicsPath.DrawPath method.
Result
Code
private void Form1_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var center = new Point(100, 100);
var innerR = 30;
var thickness = 20;
var startAngle = 270;
var arcLength = 120;
var outerR = innerR + thickness;
var outerRect = new Rectangle
(center.X - outerR, center.Y - outerR, 2 * outerR, 2 * outerR);
var innerRect = new Rectangle
(center.X - innerR, center.Y - innerR, 2 * innerR, 2 * innerR);
using (var p = new GraphicsPath())
{
p.AddArc(outerRect, startAngle, arcLength);
p.AddArc(innerRect, startAngle + arcLength, -arcLength);
p.CloseFigure();
e.Graphics.FillPath(Brushes.Green, p);
e.Graphics.DrawPath(Pens.Black, p);
}
}

Transcribing a polygon on a circle

i am currently try to inscribe diagonals of a decagon inside a circle
like this
in c# my approach would be creating a circle
e.Graphics.DrawEllipse(myPen, 0, 0, 100, 100);
and draw lines inside using
e.Graphics.DrawLine(myPen, 20, 5, 50, 50);
after that i would draw a decagon polygon.
currently im stuck at how to divide the circle into 10 parts/ finding the correct coordiantes of the points on the circumference of the circles because im not good in math,
i want to know how would i know the next point in a circumference of the circle the size of my circle is indicated above.
and also i want also to ask a better approach for my problem.
Thank you :)
Just for grits and shins, here's a generic implementation that will inscribe an X-sided polygon into the Rectangle you pass it. Note that in this approach I'm not actually calculating any absolute points. Instead, I am translating the origin, rotating the surface, and drawing the lines only with respect to the origin using a fixed length and an angle. This is repeated in a loop to achieve the end result below, and is very similar to commanding the Turtle in Logo:
public partial class Form1 : Form
{
PictureBox pb = new PictureBox();
NumericUpDown nud = new NumericUpDown();
public Form1()
{
InitializeComponent();
this.Text = "Inscribed Polygon Demo";
TableLayoutPanel tlp = new TableLayoutPanel();
tlp.RowCount = 2;
tlp.RowStyles.Clear();
tlp.RowStyles.Add(new RowStyle(SizeType.AutoSize));
tlp.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
tlp.ColumnCount = 2;
tlp.ColumnStyles.Clear();
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
tlp.Dock = DockStyle.Fill;
this.Controls.Add(tlp);
Label lbl = new Label();
lbl.Text = "Number of Sides:";
lbl.TextAlign = ContentAlignment.MiddleRight;
tlp.Controls.Add(lbl, 0, 0);
nud.Minimum = 3;
nud.Maximum = 20;
nud.AutoSize = true;
nud.ValueChanged += new EventHandler(nud_ValueChanged);
tlp.Controls.Add(nud, 1, 0);
pb.Dock = DockStyle.Fill;
pb.Paint += new PaintEventHandler(pb_Paint);
pb.SizeChanged += new EventHandler(pb_SizeChanged);
tlp.SetColumnSpan(pb, 2);
tlp.Controls.Add(pb, 0, 1);
}
void nud_ValueChanged(object sender, EventArgs e)
{
pb.Refresh();
}
void pb_SizeChanged(object sender, EventArgs e)
{
pb.Refresh();
}
void pb_Paint(object sender, PaintEventArgs e)
{
// make circle centered and 90% of PictureBox size:
int Radius = (int)((double)Math.Min(pb.ClientRectangle.Width, pb.ClientRectangle.Height) / (double)2.0 * (double).9);
Point Center = new Point((int)((double)pb.ClientRectangle.Width / (double)2.0), (int)((double)pb.ClientRectangle.Height / (double)2.0));
Rectangle rc = new Rectangle(Center, new Size(1, 1));
rc.Inflate(Radius, Radius);
InscribePolygon(e.Graphics, rc, (int)nud.Value);
}
private void InscribePolygon(Graphics G, Rectangle rc, int numSides)
{
if (numSides < 3)
throw new Exception("Number of sides must be greater than or equal to 3!");
float Radius = (float)((double)Math.Min(rc.Width, rc.Height) / 2.0);
PointF Center = new PointF((float)(rc.Location.X + rc.Width / 2.0), (float)(rc.Location.Y + rc.Height / 2.0));
RectangleF rcF = new RectangleF(Center, new SizeF(1, 1));
rcF.Inflate(Radius, Radius);
G.DrawEllipse(Pens.Black, rcF);
float Sides = (float)numSides;
float ExteriorAngle = (float)360 / Sides;
float InteriorAngle = (Sides - (float)2) / Sides * (float)180;
float SideLength = (float)2 * Radius * (float)Math.Sin(Math.PI / (double)Sides);
for (int i = 1; i <= Sides; i++)
{
G.ResetTransform();
G.TranslateTransform(Center.X, Center.Y);
G.RotateTransform((i - 1) * ExteriorAngle);
G.DrawLine(Pens.Black, new PointF(0, 0), new PointF(0, -Radius));
G.TranslateTransform(0, -Radius);
G.RotateTransform(180 - InteriorAngle / 2);
G.DrawLine(Pens.Black, new PointF(0, 0), new PointF(0, -SideLength));
}
}
}
I got the formula for the length of the side here at Regular Polygon Calculator.
One way of dealing with this is using trigonometric functions sin and cos. Pass them the desired angle, in radians, in a loop (you need a multiple of 2*π/10, i.e. a = i*π/5 for i between 0 and 9, inclusive). R*sin(a) will give you the vertical offset from the origin; R*cos(a) will give you the horizontal offset.
Note that sin and cos are in the range from -1 to 1, so you will see both positive and negative results. You will need to add an offset for the center of your circle to make the points appear at the right spots.
Once you've generated a list of points, connect point i to point i+1. When you reach the ninth point, connect it to the initial point to complete the polygon.
I don't test it, but i think it is ok.
#define DegreeToRadian(d) d * (Pi / 180)
float r = 1; // radius
float cX = 0; // centerX
float cY = 0; // centerY
int numSegment = 10;
float angleOffset = 360.0 / numSegment;
float currentAngle = 0;
for (int i = 0; i < numSegment; i++)
{
float startAngle = DegreeToRadian(currentAngle);
float endAngle = DegreeToRadian(fmod(currentAngle + angleOffset, 360));
float x1 = r * cos(startAngle) + cX;
float y1 = r * sin(startAngle) + cY;
float x2 = r * cos(endAngle) + cX;
float y2 = r * sin(endAngle) + cY;
currentAngle += angleOffset;
// [cX, cY][x1, y1][x2, y2]
}
(fmod is c++ function equals to floatNumber % floatNumber)

A control disappears from parent UserControl after UserControl's OnRepaint event

I have a UserControl which has a button on it. On the UserControl OnPaint event I draw a rounded corner border (or a simple rectangle if the radius is zero) and then I fill the entire control. After these manipulations my Button (btnClose) disappears.
How do I make my button visible again?
protected override void OnPaint(PaintEventArgs pe)
{
using (System.Drawing.Pen p = new Pen(new SolidBrush(this.BorderColor)))
{
if (borderRadius > 0)
{
DrawRoundRect(pe.Graphics, p, 0, 0, this.Width - 1, this.Height - 1, borderRadius, this.FillColor);
}
else
{
this.BackColor = this.FillColor;
pe.Graphics.DrawRectangle(p, 0, 0, this.Width - 1, this.Height - 1);
}
btnClose.Location = new Point(this.Width - btnClose.Width - BTN_MARGIN_DELTA, BTN_MARGIN_DELTA);
}
base.OnPaint(pe);
}
Just in case, the DrawRoundRect function:
void DrawRoundRect(Graphics g, Pen p, float X, float Y, float width, float height, float radius, Color _fillColor)
{
using (GraphicsPath gp = new GraphicsPath())
{
gp.AddLine(X + radius, Y, X + width - (radius * 2), Y);
gp.AddArc(X + width - (radius * 2), Y, radius * 2, radius * 2, 270, 90);
gp.AddLine(X + width, Y + radius, X + width, Y + height - (radius * 2));
gp.AddArc(X + width - (radius * 2), Y + height - (radius * 2), radius * 2, radius * 2, 0, 90);
gp.AddLine(X + width - (radius * 2), Y + height, X + radius, Y + height);
gp.AddArc(X, Y + height - (radius * 2), radius * 2, radius * 2, 90, 90);
gp.AddLine(X, Y + height - (radius * 2), X, Y + radius);
gp.AddArc(X, Y, radius * 2, radius * 2, 180, 90);
gp.CloseFigure();
using (SolidBrush brush = new SolidBrush(_fillColor))
{
g.FillPath(brush, gp);
g.DrawPath(p, gp);
}
}
}
Try moving the location code to the resize method:
protected override void OnResize(EventArgs e) {
btnClose.Location = new Point(this.Width - btnClose.Width - BTN_MARGIN_DELTA, BTN_MARGIN_DELTA);
}
Moving controls in a paint event could cause recursive calls to the paint event. Only "paint" in a paint event.
I set FillColor = Color.Gray, BorderColor = Color.Black, borderRadius = 5, BTN_MARGIN_DELTA = 2 and it seems to work without any problem. Here is a screenshot:
I think the problem isn't these lines of code.
Well, my mistake. It was a function that deletes all controls from UserControl. So I filter the controls on removal.
void ClearControls()
{
for (int i = 0; i < Items.Count; i++)
{
foreach (Control cc in Controls)
{
if (cc.Name.Contains(LINK_LABEL_FAMILY) || (cc.Name.Contains(LABEL_FAMILY)))
{
Controls.RemoveByKey(cc.Name);
}
}
}
}

How do I draw an annulus (doughnut) using GDI+?

I have been trying to draw an annulus (ring with thickness) with a transparent hole and a gradient rim in C# with very little success. Does anyone have any suggestions on how to do this?
here's a nice Blend Utility
Here's the Final result - thanks to BlueMonkMN
Rectangle GetSquareRec(double radius, int x, int y)
{
double r = radius;
double side = Math.Sqrt(Math.Pow(r, 2) / 2);
Rectangle rec = new Rectangle(x - ((int)side), y - ((int)side), (int)(side * 2) + x, (int)(side * 2) + y);
return rec;
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics gTarget = e.Graphics;
gTarget.SmoothingMode = SmoothingMode.AntiAlias;
GraphicsPath pTemp = new GraphicsPath();
Rectangle r = GetSquareRec(200, 225, 225);
pTemp.AddEllipse(r);
pTemp.AddEllipse(GetSquareRec(50, 225, 225));
Color[] colors = new Color[5];
colors[0] = Color.FromArgb(192, 192, 192);
colors[1] = Color.FromArgb(105, 0, 0);
colors[2] = Color.FromArgb(169, 169, 169);
colors[3] = Color.FromArgb(0, 0, 0);
colors[4] = Color.FromArgb(0, 0, 0);
float[] positions = new float[5];
positions[0] = 0f;
positions[1] = 0.1f;
positions[2] = 0.35f;
positions[3] = 0.5f;
positions[4] = 1f;
ColorBlend Cb = new ColorBlend();
Cb.Colors = colors;
Cb.Positions = positions;
PathGradientBrush pgb = new PathGradientBrush(pTemp);
pgb.InterpolationColors = Cb;
pgb.CenterPoint = new PointF(r.X + (r.Width / 2), r.Y + (r.Height / 2));
gTarget.FillPath(pgb, pTemp);
}
http://www.freeimagehosting.net/uploads/th.515733e62e.jpg
This is how I did it in the Scrolling Game Development Kit:
pTemp = new GraphicsPath();
pTemp.AddEllipse(Start.X, Start.Y, End.X - Start.X, End.Y - Start.Y);
pTemp.AddEllipse((Start.X * 3 + End.X) / 4f,
(Start.Y * 3 + End.Y) / 4f,
(End.X - Start.X) / 2f,
(End.Y - Start.Y) / 2f);
PathGradientBrush pgb = new PathGradientBrush(pTemp);
Blend b = new Blend();
b.Factors = new float[] { 0, 1, 1 };
b.Positions = new float[] { 0, .5F, 1 };
pgb.Blend = b;
pgb.CenterColor = ((SolidBrush)CurrentBrush).Color;
pgb.SurroundColors = new Color[] {CurrentPen.Color};
gTarget.FillPath(pgb, pTemp);
pgb.Dispose();
pTemp.Dispose();
(source: enigmadream.com)
I edited the original SGDK code for this sample because originally I wasn't smart enough to scale the gradient to exclude the hole, but now I guess I am :).
If you would rather see the gradient like this:
(source: enigmadream.com)
Then change the blend code to look like this:
Blend blend = new Blend();
blend.Factors = new float[] { 0, 1, 0, 0 };
blend.Positions = new float[] { 0, 0.25F, .5F, 1 };
pgb.Blend = blend;
You may use two calls to Graphics.DrawArc combined, drawing the top and bottom or left and right portions of the annulus, one portion at a time.

Categories