Rotate a individual rectangle and ALL its contents - c#

I'm using the method posted here
Using a matrix to rotate rectangles individually
(I hope this is not a repost, if it is maybe i'm doing something wrong)
I'm using the perfectly defined RotateRectangle method as follows
public void RotateRectangle(Graphics g, Rectangle r, float angle)
{
using (Matrix m = new Matrix())
{
m.RotateAt(angle, new PointF(r.Left + (r.Width / 2),
r.Top + (r.Height / 2)));
g.Transform = m;
g.DrawRectangle(Pens.Black, r);
g.ResetTransform();
}
}
And then on my drawing i create a label, and a rectangle outside of it
g.DrawString("e/10", fnt2, new SolidBrush(Color.Black), (int)(ecobdesenho / 10) / 2 + esp - 15, esp - 25); //15 para tras, 15 para cima
Rectangle r1 = new Rectangle((int)(ecobdesenho / 10) / 2 + esp - 15, esp - 25, 25, 12);
RotateRectangle(g, r1, 40);
(Notice the rectangle in top left corner)
Am I missing something? Or should I go eat bananas?

Your RotateRectangle is not rotating a rectangle of canvas; it's drawing a rotated rectangle.
Everything that's drawn between setting the Transform and the ResetTransform will be drawn rotated.
Therefore, if you want the DrawString to be rotated, you need to put it after you set the Transform, and before you reset it.
using (Matrix m = new Matrix())
{
m.RotateAt(angle, new PointF(r.Left + (r.Width / 2),
r.Top + (r.Height / 2)));
g.Transform = m;
g.DrawString("e/10", fnt2, new SolidBrush(Color.Black), (int)(ecobdesenho / 10) / 2 + esp - 15, esp - 25); //15 para tras, 15 para cima
g.DrawRectangle(Pens.Black, r);
g.ResetTransform();
}

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.

Translate/Rotate previously drawn rectangle, is this possible?

I have a bmp with text, for ease of access the bmp rotates accordingly to the user options. Problem: the text gets inverted whenever the image rotates.
(only strict angles, 90, 180, etc)
Possible solution:
Rotate the text at 180ยบ then use the regular rotation, so that it doesnt get mirrored?
I tried this the following way, I create a rectangle around the text and turn it.
Rectangle r1 = new Rectangle((int)(ecobdesenho / 10) / 2 + esp - 15, esp - 25, 25, 12);
using (Matrix m = new Matrix())
{
m.RotateAt(180, new PointF((int)(ecobdesenho / 10) / 2 + esp - 15 + (25 / 2),
esp - 25 + (12 / 2)));
g.Transform = m;
g.DrawRectangle(new Pen(Color.Black), r1);
g.DrawString("e/10", fnt2, new SolidBrush(Color.Black), (int)(ecobdesenho / 10) / 2 + esp - 15, esp - 25); //15 para tras, 15 para cima
g.ResetTransform();
}
Then, i provide the bmp with the rotation before its drawings:
g.TranslateTransform((float)(xWcorrigido / 2 + esp), (float)(yWcorrigido / 2 + esp));
g.RotateTransform(180);
g.TranslateTransform((float)(-xWcorrigido / 2 - esp), (float)(-yWcorrigido / 2 - esp));
However this combination doesnt affect the previous text. I tried to place it inside the using Matrix brackets, but regardless of it, it doesnt apply the whole transformation process.
I could too, first use the general translation, and then turn the text at very specific boxes, but it would give the same amount of work i'm trying to avoid.
Any hint? My brain already hurts with so many possible combinations
The solution is applying the transformation within the matrix, and for the matrix, like this:
Rectangle r1 = new Rectangle((int)(ecobdesenho / 10) / 2 + esp - 15, esp - 25, 25, 12);
using (Matrix m = new Matrix())
{
m.TranslateTransform((float)(xWcorrigido / 2 + esp), (float)(yWcorrigido / 2 + esp));
m.RotateTransform(180);
m.TranslateTransform((float)(-xWcorrigido / 2 - esp), (float)(-yWcorrigido / 2 - esp));
m.RotateAt(180, new PointF((int)(ecobdesenho / 10) / 2 + esp - 15 + (25 / 2),
esp - 25 + (12 / 2)));
g.Transform = m;
g.DrawRectangle(new Pen(Color.Black), r1);
g.DrawString("e/10", fnt2, new SolidBrush(Color.Black), (int)(ecobdesenho / 10) / 2 + esp - 15, esp - 25); //15 para tras, 15 para cima
g.ResetTransform();
}

Zooming in on quadratic curve line in c#

I am relatively new to c# and i am trying to draw the quadratic curve with an X and Y graph to scale with. The i drew curve although appears at the upper left corner of the screen which is very small and barely noticeable. Is there possible way to enlarge my curve line and align it to the middle so it can be shown properly?
protected override void OnPaint(PaintEventArgs e)
{
float a = 1, b = -3, c = -4;
double x1, x2, x3, y1, y2, y3, delta;
delta = (b * b) - (4 * a * c);
x1 = ((b * (-1)) + Math.Sqrt(delta)) / (2 * a);
y1 = a * (x1 * x1) + b * (x1) + c;
x2 = x1 + 1;
y2 = a * (x2 * x2) + b * (x2) + c;
x3 = x1 - 3;
y3 = a * (x3 * x3) + b * (x3) + c;
int cx1 = Convert.ToInt32(x1);
int cx2 = Convert.ToInt32(x2);
int cx3 = Convert.ToInt32(x3);
int cy1 = Convert.ToInt32(y1);
int cy2 = Convert.ToInt32(y2);
int cy3 = Convert.ToInt32(y3);
Graphics g = e.Graphics;
Pen aPen = new Pen(Color.Blue, 1);
Point point1 = new Point(cx1, cy1);
Point point2 = new Point(cx2, cy2);
Point point3 = new Point(cx3, cy3);
Point[] Points = { point1, point2, point3 };
g.DrawCurve(aPen, Points);
Yes it is possible and even rather simple to both move (Translate) and enlarge (Scale) the Graphics results by using Graphics.TranslateTransform and Matrix and Graphics.MultiplyTransform:
using System.Drawing.Drawing2D;
//..
int deltaX = 100;
int deltaY = 100;
g.TranslateTransform(deltaX, deltaY);
float factor = 2.5f;
Matrix m = new Matrix();
m.Scale(factor, factor);
g.MultiplyTransform(m);
Note that the scaling works like a lens and will enlarge the pixels. So you may want to scale down the Pen.Width when you scale up the Graphics..
Using one before..
g.DrawEllipse(Pens.Blue, 11, 11, 55, 55);
..and two after the transformations..
g.DrawEllipse(Pens.Red, 11, 11, 55, 55);
using (Pen pen = new Pen(Color.Green, 1/factor))
g.DrawEllipse(pen, 11, 11, 44, 44);
..these calls result in this image:
(I have changed the green circle's radius to avoid complete overlaying..)
It will be up to you to find the desired numbers for the moving and scaling; this will probably involve finding the minimum and maximum values for points involved..
I would suggest that you look into Microsoft Chart controls, it has a lots of interesting features regarding how to do this kind of curves with the ability to parameterize them.
A link to a more recent version of it: here

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

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:

Lost trying to draw a line of x length y degrees

I think its my maths that's letting me down!
I'm trying to draw a line of x length at Y degrees on a picturebox control with a North South East west, lines.
the below works but draws the line line at 250 degrees not 290!?
Bitmap bmp = new Bitmap(400, 400);
Graphics g = Graphics.FromImage(bmp);
// smooth graphics
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawLine(new Pen(Color.Black, 2), 205, 20, 205, 385);
g.DrawLine(new Pen(Color.Red, 2), 20, 205, 390, 205);
// let's draw a coordinate equivalent to (20,30) (20 up, 30 across)
g.DrawString("N", new Font("Calibri", 12 , FontStyle.Bold), new SolidBrush(Color.Black), 197, 0);
g.DrawString("S", new Font("Calibri", 12, FontStyle.Bold), new SolidBrush(Color.Black), 197, 385);
g.DrawString("W", new Font("Calibri", 12, FontStyle.Bold), new SolidBrush(Color.Black), 0, 195);
g.DrawString("E", new Font("Calibri", 12, FontStyle.Bold), new SolidBrush(Color.Black), 390, 195);
//Draw Wind line
int x = 205, y = 205;
int wSpeed = 30*3; //length of line
int angle = 290; //angle to draw line at.
int startX = x;
int startY = y;
int endX = Convert.ToInt32(Math.Round(x + wSpeed * Math.Sin(Calcs.Radians(angle))));
int endY = Convert.ToInt32(Math.Round(y + wSpeed * Math.Cos(Calcs.Radians(angle))));
g.DrawLine(new Pen(Color.Blue, 2), startX, startY, endX, endY);
PictureBox display = pictureBox1;
this.Controls.Add(display);
display.Image = bmp;
public static double Radians(double dDegrees)
{
double result = Math.PI * dDegrees / 180.0;
return result;
}
If you look at the results: its -20 degrees from 270, instead of +20 expected, meaning you v got correct Y but wrong X. I suggest check(change) the sign of X coord. Graphics is very often trial-error unless you are expert.
int endX = Convert.ToInt32(Math.Round(x - wSpeed * Math.Sin(Calcs.Radians(angle))));

Categories