I'm using a picturebox to create a visual of an instance of my truss class. I'm creating the visual by drawing directly onto the picture box in the paint event. The method looks like this
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (isDraw)
{
//Preparing to draw
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.Bicubic;
RunEntry entry = this.passedHistory.SelectedItem as RunEntry;
AnsFile objToDraw = entry.FileRead;
Pen pen = new Pen(Color.Black);
//Getting size of bitmap
int maxWidth = 0, maxHeight = 0;
foreach (AnsJoint joint in objToDraw.AnsJoints)
{
if (joint.Location.X.Length > maxWidth)
{
maxWidth = (int)joint.Location.X.Length;
}
if (joint.Location.Y.Length > maxHeight)
{
maxHeight = (int)joint.Location.Y.Length;
}
}
//Drawing joints
foreach (AnsJoint joint in objToDraw.AnsJoints)
{
PointF jointPoint = this.ToCartesian(new PointF((float)joint.Location.X.Length - 4f, (float)joint.Location.Y.Length + 10f), maxHeight);
e.Graphics.DrawString(joint.JointID.ToString(), new Font(FontFamily.GenericMonospace, 6f, FontStyle.Regular, GraphicsUnit.Point, 1, false), Brushes.Black, jointPoint);
}
//Draw the panels and links
foreach (AnsMember member in objToDraw.AnsMembers)
{
List<AnsPanel> panels = member.Panels; //Drawing the panels
foreach (AnsPanel pan in panels)
{
pen.Color = Color.Red;
PointF p1 = this.ToCartesian(new PointF((float)pan.I.Location.X.Length, (float)pan.I.Location.Y.Length), maxHeight);
PointF p2 = this.ToCartesian(new PointF((float)pan.J.Location.X.Length, (float)pan.J.Location.Y.Length), maxHeight);
g.DrawEllipse(pen, p1.X - 2.5f, p1.Y - 2.5f, 5, 5);
g.DrawEllipse(pen, p2.X - 2.5f, p2.Y - 2.5f, 5, 5);
g.DrawEllipse(pen, p1.X - 3, p1.Y - 3.3f, 5, 5);
g.DrawEllipse(pen, p2.X - 3, p2.Y - 3.3f, 5, 5);
pen.Color = Color.Black;
g.DrawLine(pen, p1, p2);
}
List<AnsLink> links = member.Links; //Drawing the links
foreach (AnsLink link in links)
{
PointF p1 = this.ToCartesian(new PointF((float)link.I.Location.X.Length, (float)link.I.Location.Y.Length), maxHeight);
PointF p2 = this.ToCartesian(new PointF((float)link.J.Location.X.Length, (float)link.J.Location.Y.Length), maxHeight);
g.FillEllipse(Brushes.Green, p1.X - 1.5f, p1.Y - 1.5f, 3, 3);
g.FillEllipse(Brushes.Green, p2.X - 1.5f, p2.Y - 1.5f, 3, 3);
g.DrawLine(pen, p1, p2);
}
}
g.ScaleTransform(.5f, .5f);
pictureBox1.Tag = entry.FileName;
}
}
Which yields the result I'm expecting
except that I want the truss image to scale to fill the picturebox more. I added the ScaleTransform call to the end of the paint event method but that doesn't seem to have any impact as the picture is the same size with or without the call. How can I scale what I draw on the picturebox to the size of the picturebox?
Call ScaleTransform before you do your drawing. You can calculate the desired scale factor like this:
// place this code after the calculation of maxWidth and maxHeight
// but before the drawing code
PictureBox p = (PictureBox)sender;
float scaleFactor = Math.Min(
((float)p.Width) / maxWidth,
((float)p.Height) / maxHeight
);
g.ScaleTransform(scaleFactor, scaleFactor);
Related
How to I rotate an image without it showing like this?
Here's my Rotation Method:
public static Bitmap RotateImageN(Bitmap bmp, float angle)
{
Bitmap rotatedImage = new Bitmap(bmp.Width, bmp.Height);
using (Graphics g = Graphics.FromImage(rotatedImage))
{
// Set the rotation point to the center in the matrix
g.TranslateTransform(bmp.Width / 2, bmp.Height / 2);
// Rotate
g.RotateTransform(angle);
// Restore rotation point in the matrix
g.TranslateTransform(-bmp.Width / 2, -bmp.Height / 2);
// Draw the image on the bitmap
g.DrawImage(bmp, new Point(0, 0));
}
return rotatedImage;
}
Edit: After trying Loocid's Code
Your rotatedImage Bitmap needs to be big enough to accommodate the rotated image.
Say you rotated your original image by 30° you need to get the size of the bounding box like so:
Using some basic trig:
x = L*cos(30 * π / 180) + w*cos(60 * π / 180)
y = L*sin(30 * π / 180) + w*sin(60 * π / 180)
Therefore change the start of your code to:
var x = bmp.Width * Math.Cos(angle * Math.PI / 180) + bmp.Height * Math.Cos((90-angle) * Math.PI / 180)
var y = bmp.Width * Math.Sin(angle * Math.PI / 180) + bmp.Height * Math.Sin((90-angle) * Math.PI / 180)
Bitmap rotatedImage = new Bitmap(x, y);
The issue occurs in the rotating is related to the bounding box. It is clipping the edge because of the image you provided does not fit into the area that you have given.
I also faced this issue. So I tried a solution from here.
Adding the code that works for me.
public static Bitmap RotateImageN(Bitmap bitmap, float angle)
{
Matrix matrix = new Matrix();
matrix.Translate(bitmap.Width / -2, bitmap.Height / -2, MatrixOrder.Append);
matrix.RotateAt(angle, new System.Drawing.Point(0, 0), MatrixOrder.Append);
using (GraphicsPath graphicsPath = new GraphicsPath())
{
graphicsPath.AddPolygon(new System.Drawing.Point[] { new System.Drawing.Point(0, 0), new System.Drawing.Point(bitmap.Width, 0), new System.Drawing.Point(0, bitmap.Height) });
graphicsPath.Transform(matrix);
System.Drawing.PointF[] points = graphicsPath.PathPoints;
Rectangle rectangle = boundingBox(bitmap, matrix);
Bitmap resultBitmap = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics gDest = Graphics.FromImage(resultBitmap))
{
Matrix mDest = new Matrix();
mDest.Translate(resultBitmap.Width / 2, resultBitmap.Height / 2, MatrixOrder.Append);
gDest.Transform = mDest;
gDest.DrawImage(bitmap, points);
return resultBitmap;
}
}
}
private static Rectangle boundingBox(Image image, Matrix matrix)
{
GraphicsUnit graphicsUnit = new GraphicsUnit();
Rectangle boundingRectangle = Rectangle.Round(image.GetBounds(ref graphicsUnit));
Point topLeft = new Point(boundingRectangle.Left, boundingRectangle.Top);
Point topRight = new Point(boundingRectangle.Right, boundingRectangle.Top);
Point bottomRight = new Point(boundingRectangle.Right, boundingRectangle.Bottom);
Point bottomLeft = new Point(boundingRectangle.Left, boundingRectangle.Bottom);
Point[] points = new Point[] { topLeft, topRight, bottomRight, bottomLeft };
GraphicsPath graphicsPath = new GraphicsPath(points, new byte[] { (byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line });
graphicsPath.Transform(matrix);
return Rectangle.Round(graphicsPath.GetBounds());
}
I have a C# bitmap object, and i am able to draw a line from point A to point B.
I have the 2 points on the edges of the diagram, and I would like to draw an ellipse from A to B. The basic g.DrawEllipse() only draws ellipses either perfectly horizontally or vertically, however I need the ellipse to be kind of diagonal from the one end of the image to the other.
My bitmap: 200 tall by 500 wide
Point A: Column 0, Row 20 (0,20)
Point B: Column 499, Row 60 (499, 60)
Widest Point: 30 - Narrow Radius of the ellipse
Here is what I have so far, the draw ellipse doesnt have the overload I need, so help there please:
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawLine(pen, new Point(20,0), new Point(499,60));
g.DrawEllipse(pen, 20, 0, someWidth, someHeight);
}
Here is how to use the DrawEllipse method from a rotation, the minor axis and two vertices.
First we calculate the Size of the bounding Rectangle:
Given the Points A and B sitting on the short sides of length smallSize we get the long side with a little Pythagoras:
int longSide = (int)(Math.Sqrt((A.Y - B.Y) * (A.Y - B.Y) + (B.X - A.X) * (B.X - A.X)));
So :
Size size = new System.Drawing.Size(longSide, smallSize);
Next we need the rotation angle:
float angle = -(float)(Math.Atan2(A.Y - B.Y, B.X - A.X) * 180f / Math.PI);
And it will make things easier to also get the center Point C:
Point C = new Point((A.X + B.X)/ 2, (A.Y + B.Y)/ 2);
The last thing we want is a routine that draws an ellipse of a given Size, rotated around C at an angle:
void DrawEllipse(Graphics G, Pen pen, Point center, Size size, float angle)
{
int h2 = size.Height / 2;
int w2 = size.Width / 2;
Rectangle rect = new Rectangle( new Point(center.X - w2, center.Y - h2), size );
G.TranslateTransform(center.X, center.Y);
G.RotateTransform(angle);
G.TranslateTransform(-center.X, -center.Y);
G.DrawEllipse(pen, rect);
G.ResetTransform();
}
Here is a little testbed that brings it all together:
Point A = new Point(200, 200); // *
Point B = new Point(500, 250);
int smallSize = 50;
void doTheDraw(PictureBox pb)
{
Bitmap bmp = new Bitmap(pb.Width, pb.Height);
float angle = -(float)(Math.Atan2(A.Y - B.Y, B.X - A.X) * 180f / Math.PI);
int longSide = (int)(Math.Sqrt((A.Y - B.Y) * (A.Y - B.Y) + (B.X - A.X) * (B.X - A.X)));
Point C = new Point((A.X + B.X) / 2, (A.Y + B.Y) / 2);
Size size = new System.Drawing.Size((int)longSide, smallSize);
using (Pen pen = new Pen(Color.Orange, 3f))
using (Graphics g = Graphics.FromImage(bmp))
{
// a nice background grid (optional):
DrawGrid(g, 0, 0, 100, 50, 10,
Color.LightSlateGray, Color.DarkGray, Color.Gainsboro);
// show the points we use (optional):
g.FillEllipse(Brushes.Red, A.X - 4, A.Y - 4, 8, 8);
g.FillRectangle(Brushes.Red, B.X - 3, B.Y - 3, 7, 7);
g.FillEllipse(Brushes.Red, C.X - 5, C.Y - 5, 11, 11);
// show the connection line (optional):
g.DrawLine(Pens.Orange, A, B);
// here comes the ellipse:
DrawEllipse(g, pen, C, size, angle);
}
pb.Image = bmp;
}
The grid is a nice helper:
void DrawGrid(Graphics G, int ox, int oy,
int major, int medium, int minor, Color c1, Color c2, Color c3)
{
using (Pen pen1 = new Pen(c1, 1f))
using (Pen pen2 = new Pen(c2, 1f))
using (Pen pen3 = new Pen(c3, 1f))
{
pen2.DashStyle = DashStyle.Dash;
pen3.DashStyle = DashStyle.Dot;
for (int x = ox; x < G.VisibleClipBounds.Width; x += major)
G.DrawLine(pen1, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += major)
G.DrawLine(pen1, 0, y, G.VisibleClipBounds.Width, y);
for (int x = ox; x < G.VisibleClipBounds.Width; x += medium)
G.DrawLine(pen2, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += medium)
G.DrawLine(pen2, 0, y, G.VisibleClipBounds.Width, y);
for (int x = ox; x < G.VisibleClipBounds.Width; x += minor)
G.DrawLine(pen3, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += minor)
G.DrawLine(pen3, 0, y, G.VisibleClipBounds.Width, y);
}
}
Note that I made A, B, smallSide class level variables so I can modify them during my tests, (and I did *)..
As you can see I have added a TrackBar to make the smallside dynamic; for even more fun I have added this MouseClick event:
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left)) A = e.Location;
else B = e.Location;
doTheDraw(pictureBox1);
}
Note that I didn't care for disposing of the old Bitmap; you should, of course..!
If you wish to use Graphics to create a diagonal ellipse, perhaps you can use DrawBezier() method.
Here is some code that does it:
// Draws an ellipse using 2 beziers.
private void DrawEllipse(Graphics g, PointF center, float width, float height, double rotation)
{
// Unrotated ellipse frame
float left = center.X - width / 2;
float right = center.X + width / 2;
float top = center.Y - height / 2;
float bottom = center.Y + height / 2;
PointF p1 = new PointF(left, center.Y);
PointF p2 = new PointF(left, top);
PointF p3 = new PointF(right, top);
PointF p4 = new PointF(right, center.Y);
PointF p5 = new PointF(right, bottom);
PointF p6 = new PointF(left, bottom);
// Draw ellipse with rotated points.
g.DrawBezier(Pens.Black, Rotate(p1, center, rotation), Rotate(p2, center, rotation), Rotate(p3, center, rotation), Rotate(p4, center, rotation));
g.DrawBezier(Pens.Black, Rotate(p4, center, rotation), Rotate(p5, center, rotation), Rotate(p6, center, rotation), Rotate(p1, center, rotation));
}
// Rotating a given point by given angel around a given pivot.
private PointF Rotate(PointF point, PointF pivot, double angle)
{
float x = point.X - pivot.X;
float y = point.Y - pivot.Y;
double a = Math.Atan(y / x);
if (x < 0)
{
a += Math.PI;
}
float size = (float)Math.Sqrt(x * x + y * y);
double newAngel = a + angle;
float newX = ((float)Math.Cos(newAngel) * size);
float newY = ((float)Math.Sin(newAngel) * size);
return pivot + new SizeF(newX, newY);
}
The above code computes the frame of the ellipse (proir to the rotation) at points p1, p2, ..., p6. And then, draws the ellipse as 2 beziers with the ellipse frame rotated points.
Ive got a panel and im drawing a heart on that panel..
But i dont want to draw the heart i want to draw everything except the heart so the heart is transparent.
Can i invert the Region selected out of the Path?
System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
path.AddArc(0, 0, (this.Width / 2), (this.Height / 2), 135, 195);
path.AddArc((this.Width / 2), 0, (this.Width / 2), (this.Height / 2), 210, 195);
path.AddLine((this.Width / 2), this.Height, (this.Width / 2), this.Height);
this.Region = new Region(path);
this.BackColor = Color.Black;
What it looks like(white = transparent):
What i want it to look like(white = transparent):
I think you can just add 2 graphics paths together.
You could try this code out:
private void panel1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddArc(0, 0, (this.Width / 2), (this.Height / 2), 135, 195);
path.AddArc((this.Width / 2), 0, (this.Width / 2), (this.Height / 2), 210, 195);
path.AddLine((this.Width / 2), this.Height, (this.Width / 2), this.Height);
GraphicsPath path2 = new GraphicsPath();
path2.AddRectangle(new Rectangle(new Point(0, 0), panel1.Size));
path2.AddPath(path, false);
e.Graphics.FillPath(Brushes.Black, path2);
}
Result is:
You can try excluding a region from from the Graphic object of your panel's Paint event:
GraphicsPath path = new GraphicsPath();
path.AddArc(0, 0, (this.Width / 2), (this.Height / 2), 135, 195);
path.AddArc((this.Width / 2), 0, (this.Width / 2), (this.Height / 2), 210, 195);
path.AddLine((this.Width / 2), this.Height, (this.Width / 2), this.Height);
using (Region r = new Region(path)) {
e.Graphics.ExcludeClip(r);
}
// continue drawing...
e.Graphics.Clear(Color.Yellow);
or if trying to modify the control's region, then just use the Region's Exclude property:
GraphicsPath path = new GraphicsPath();
path.AddArc(0, 0, (this.Width / 2), (this.Height / 2), 135, 195);
path.AddArc((this.Width / 2), 0, (this.Width / 2), (this.Height / 2), 210, 195);
path.AddLine((this.Width / 2), this.Height, (this.Width / 2), this.Height);
Region r = new Region(new Rectangle(Point.Empty, this.ClientSize));
r.Exclude(path);
this.Region = r;
I don't know of a way to invert the selection, but you can always draw a heart in the background color, then change the background color to black.
this.BackColor = Form.BackColor;
Form.BackColor = Color.Black;
Original solution here. I've modified that for your needs
this.Region = InvertRegion(new Region(path), this.Width, this.Height);
and
private Region InvertRegion(Region region, int width, int height)
{
Bitmap mask = new Bitmap(width, height);
Graphics.FromImage(mask).FillRegion(Brushes.Black, region);
int matchColor = Color.Black.ToArgb();
Region inverted = new System.Drawing.Region();
inverted.MakeEmpty();
Rectangle rc = new Rectangle(0, 0, 0, 0);
bool inimage = false;
for (int y = 0; y < mask.Height; y++)
{
for (int x = 0; x < mask.Width; x++)
{
if (!inimage)
{
if (mask.GetPixel(x, y).ToArgb() != matchColor)
{
inimage = true;
rc.X = x;
rc.Y = y;
rc.Height = 1;
}
}
else
{
if (mask.GetPixel(x, y).ToArgb() == matchColor)
{
inimage = false;
rc.Width = x - rc.X;
inverted.Union(rc);
}
}
}
if (inimage)
{
inimage = false;
rc.Width = mask.Width - rc.X;
inverted.Union(rc);
}
}
return inverted;
}
Having a bit of a drawing complication you would call it. My math is a bit rusty when it comes to Matrices and drawing rotations on shapes. Here is a bit of code:
private void Form1_Paint(object sender, PaintEventArgs e)
{
g = e.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality;
DoRotation(e);
g.DrawRectangle(new Pen(Color.Black), r1);
g.DrawRectangle(new Pen(Color.Black), r2);
// draw a line (PEN, CenterOfObject(X, Y), endpoint(X,Y) )
g.DrawLine(new Pen(Color.Black), new Point((r1.X + 50), (r1.Y + 75)), new Point((/*r1.X + */50), (/*r1.Y - */25)));
this.lblPoint.Text = "X-pos: " + r1.X + " Y-pos: " + r1.Y;
//this.Invalidate();
}
public void DoRotation(PaintEventArgs e)
{
// move the rotation point to the center of object
e.Graphics.TranslateTransform((r1.X + 50), (r1.Y + 75));
//rotate
e.Graphics.RotateTransform((float)rotAngle);
//move back to the top left corner of the object
e.Graphics.TranslateTransform(-(r1.X + 50), -(r1.Y + 75));
}
public void Form1_KeyDown(object sender, KeyEventArgs e)
{
case Keys.T:
rotAngle += 1.0f;
}
when I rotate (what I think should be r1) both r1 and r2 rotate. I need to be able to rotate each shape individually as I add more shapes.
I would use a function similar to this:
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();
}
}
It uses a matrix to perform the rotation at a certain point, which should be the middle of each rectangle.
Then in your paint method, use it to draw your rectangles:
g.SmoothingMode = SmoothingMode.HighQuality;
//g.DrawRectangle(new Pen(Color.Black), r1);
//DoRotation(e);
//g.DrawRectangle(new Pen(Color.Black), r2);
RotateRectangle(g, r1, 45);
RotateRectangle(g, r2, 65);
Also, here is the line to connect the two rectangles:
g.DrawLine(Pens.Black, new Point(r1.Left + r1.Width / 2, r1.Top + r1.Height / 2),
new Point(r2.Left + r2.Width / 2, r2.Top + r2.Height / 2));
Using these settings:
private Rectangle r1 = new Rectangle(100, 60, 32, 32);
private Rectangle r2 = new Rectangle(160, 100, 32, 32);
resulted in:
hello how to fill lineargradientbrush on the graphicspath..
i have this code and solidbrush can only fill it.. i dont know how to fill it with lineargradientbrush.. any help please..
i have this code..
class KamoteButton : Button
{
protected override void OnPaint(PaintEventArgs pevent)
{
int width = this.Width-1;
int height = this.Height-1;
Color gradColor_a = Color.FromArgb(162, 177, 183);
Color gradColor_b = Color.FromArgb(104, 111, 114);
int radius = this.Width / 8;
Graphics gFx = pevent.Graphics;
gFx.SmoothingMode = SmoothingMode.AntiAlias;
GraphicsPath gp = new GraphicsPath();
gp.AddArc(0, 0, radius, radius, 180, 90);
gp.AddLine(width / 8, 0, width - width / 8, 0);
gp.AddArc(width - radius, 0, radius, radius, 270, 90);
gp.AddLine(width, width / 8, width, height - width / 8);
gp.AddArc(width - radius, height - radius, radius, radius, 0, 90);
gp.AddLine(width - width / 8, height, width / 8, height);
gp.AddArc(0, height - radius, radius, radius, 90, 90);
gp.AddLine(0, height - width / 8, 0, width / 8);
Rectangle r = new Rectangle(0, 0, 100, 100);
LinearGradientBrush lbg = new LinearGradientBrush(gp, gradColor_a, gradColor_b, LinearGradientMode.Vertical);
SolidBrush sb = new SolidBrush(Color.Red);
gFx.FillPath(lbg, gp);
this.Region = new Region(gp);
}
}
Your code to create the GradientBrushh won't compile:
LinearGradientBrush lbg = new LinearGradientBrush(gp,
gradColor_a, gradColor_b, LinearGradientMode.Vertical);
There is no constructor with a Path as parameter. Replace it with 2 points or 1 Rect