WinForms: Drawing A Path In The Right Place - c#

This is a follow-on to a question I asked here: WinForms: Measure Text With No Padding. The question is, given this code ...
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
DrawIt(e.Graphics);
}
private void DrawIt(Graphics graphics) {
var text = "123";
var font = new Font("Arial", 72);
// Build a path containing the text in the desired font, and get its bounds.
GraphicsPath path = new GraphicsPath();
path.AddString(text, font.FontFamily, (int)font.Style, font.Size, new Point(0, 0), StringFormat.GenericDefault);
var bounds = path.GetBounds();
// Find center of the form.
var cx = this.ClientRectangle.Left + this.ClientRectangle.Width / 2;
var cy = this.ClientRectangle.Top + this.ClientRectangle.Height / 2;
// Move it where I want it.
var xlate = new Matrix();
xlate.Translate(cx - bounds.Width / 2, cy - bounds.Height / 2);
path.Transform(xlate);
// Draw the path (and a bounding rectangle).
graphics.DrawPath(Pens.Black, path);
bounds = path.GetBounds();
graphics.DrawRectangle(Pens.Blue, bounds.Left, bounds.Top, bounds.Width, bounds.Height);
// This rectangle doesn't use the positioning from Translate but does use the same size.
graphics.DrawRectangle(Pens.Red, cx - bounds.Width / 2, cy - bounds.Height / 2, bounds.Width, bounds.Height);
}
... why don't the rectangles overlap?
Clearly, when I'm translating the path, I'm not translating by the same units that I'm later drawing them in, but I'm at a loss how to fix it. Any ideas?

You have a wrong assumption about Bounds of the path. Since you added the string to the path, starting from point (0,0), you are assuming the location of the bounds of the path is (0,0). That's not correct.
The following illustration shows the relation between the origin that you added string, (0,0) and bounds of the path, blue rectangle:
To fix it, after adding the string and getting its bounds, store the location of the bounds:
var p = bounds.Location;
And then after applying the transformations, draw the rectangle this way:
graphics.DrawRectangle(Pens.Red,
p.X + cx - bounds.Width / 2, p.Y + cy - bounds.Height / 2,
bounds.Width, bounds.Height);

Related

How to rotate 2d object in C#

Basically i have a windows form that user will be able to draw different shapes(e.g square, circle and triangle), user will be able to highlight any of these shapes after drawing and then control that highlighted shape by moving or rotating it, i don't know how to rotate the shape. any one can help, this is my code (only to draw a square)
PS: user need to click twice on the form to draw the shape between those 2 points as shown below also i know i should be using onPaint method but this is the requirements of the task
Thanks
public Square(Point keyPt, Point oppPt) // constructor
{
this.keyPt = keyPt;
this.oppPt = oppPt;
}
// You will need a different draw method for each kind of shape. Note the square is drawn
// from first principles. All other shapes should similarly be drawn from first principles.
// Ideally no C# standard library class or method should be used to create, draw or transform a shape
// and instead should utilse user-developed code.
public void draw(Graphics g, Pen blackPen)
{
// This method draws the square by calculating the positions of the other 2 corners
double xDiff, yDiff, xMid, yMid; // range and mid points of x & y
// calculate ranges and mid points
xDiff = oppPt.X - keyPt.X;
yDiff = oppPt.Y - keyPt.Y;
xMid = (oppPt.X + keyPt.X) / 2;
yMid = (oppPt.Y + keyPt.Y) / 2;
// draw square
g.DrawLine(blackPen, (int)keyPt.X, (int)keyPt.Y, (int)(xMid + yDiff / 2), (int)(yMid - xDiff / 2));
g.DrawLine(blackPen, (int)(xMid + yDiff / 2), (int)(yMid - xDiff / 2), (int)oppPt.X, (int)oppPt.Y);
g.DrawLine(blackPen, (int)oppPt.X, (int)oppPt.Y, (int)(xMid - yDiff / 2), (int)(yMid + xDiff / 2));
g.DrawLine(blackPen, (int)(xMid - yDiff / 2), (int)(yMid + xDiff / 2), (int)keyPt.X, (int)keyPt.Y);
}
public void fillSquare(Graphics g, Brush redBrush)
{
float xDiff = oppPt.X - keyPt.X;
float yDiff = oppPt.Y - keyPt.Y;
float xMid = (oppPt.X + keyPt.X) / 2;
float yMid = (oppPt.Y + keyPt.Y) / 2;
var path = new GraphicsPath();
path.AddLines(new PointF[] {
keyPt,
new PointF(xMid + yDiff/2, yMid-xDiff/2),
oppPt
});
path.AddLines(new PointF[] {
keyPt,
new PointF(xMid - yDiff/2, yMid + xDiff/2),
oppPt
});
path.CloseFigure();
// Fill Triangle
g.FillPath(redBrush, path);
}
}
}
i have tried this method but something is missing i don't know what is it
private void itemRotation(PaintEventArgs e)
{
Pen blackpen = new Pen(Color.Black);
Graphics g = e.Graphics;
Font myFont = new System.Drawing.Font("Helvetica", 9);
Brush blackwriter = new SolidBrush(System.Drawing.Color.Black);
if (rotateItem)
{
for (int i = 0; i < shapes.Count; i++)
{
if (shapes[i].Selected)
{
if (shapes[i].ShapeType == (int)ShapeTypes.Square)
{
PointF center = new PointF(shapes[i].keyPt.X + (shapes[i].oppPt.X / 2.0F), shapes[i].keyPt.Y + (shapes[i].oppPt.Y / 2.0F));
shapes[i].keyPt = new Point(shapes[i].keyPt.X, shapes[i].keyPt.Y);
shapes[i].oppPt = new Point(shapes[i].oppPt.X, shapes[i].oppPt.Y);
Matrix myMatrix = new Matrix();
myMatrix.Rotate(30);
g.Transform = myMatrix;
((Square)shapes[i]).draw(g, blackpen);
g.DrawString("2nd pos", myFont, blackwriter, shapes[i].keyPt.X, shapes[i].oppPt.X);
}
}
}
}
}
Below is an example of how to draw the same shape (a GraphicsPath) into various locations and rotations.
The key here is the following two commands
e.Graphics.TranslateTransform(x, y);
e.Graphics.RotateTransform(-angle);
See results below:
and the code used to generate it:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// This code defines a graphics shape using a GraphicsPath
// and draws multiple copies along a grid and with various
// rotation angle angles.
e.Graphics.SmoothingMode=SmoothingMode.AntiAlias;
var target = sender as PictureBox;
// Step 1 - Define a rectangle 20 by 12 pixels, center at origin.
var gp = new GraphicsPath();
gp.AddLines(new PointF[] {
new PointF(-10, -6),
new PointF( 10, -6),
new PointF( 10, 6),
new PointF(-10, 6) });
gp.CloseFigure();
// Step 2 - Define a 10×9 grid with two loops
float angle = 0;
for (int i = 0; i<9; i++)
{
// divide the control height into 10 divisions
float y = (i+1)*target.Height/10;
for (int j = 0; j<10; j++)
{
// divide the control width into 11 divisions
float x = (j+1)*target.Width/11;
// Save the default transformation state
var state = e.Graphics.Save();
// Traslate the origin to (x,y), each grid point
e.Graphics.TranslateTransform(x, y);
// Rotate shape by an angle (negative = CCW)
e.Graphics.RotateTransform(-angle);
// Draw the shape
e.Graphics.FillPath(Brushes.LightSeaGreen, gp);
e.Graphics.DrawPath(Pens.Black, gp);
// Restore the default transformation state
e.Graphics.Restore(state);
// Increment the angle by one degree.
// The idea is to show all 90 degrees of rotation in a 10×9 grid.
angle++;
}
}
}

How can I update Graphics.DrawLine() point positions?

I made a box that I drew using Graphics.DrawLine() for each edge. But I would like the box to be able to move around the screen freely, along with changing the size of the edges. The box is created by using a middle point and width and height, then basic math to find the 4 points which the lines connect to. My question is: How can I update the position of the lines? Do I have to clear the graphics and re-draw the lines each frame? Any answers will be helpful. Thank you.
Here is the code I am using:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Point box_Middle = new Point(300, 300);
int boxWidth = 100;
int boxHeight = 200;
Pen boxPen = new Pen(Color.Red, 3);
DrawBox(e, boxPen, box_Middle, boxWidth, boxHeight);
}
void DrawBox (PaintEventArgs e, Pen pen, Point middle, int width, int height)
{
graphics = e.Graphics;
graphics.TextRenderingHint = System.Drawing.
Text.TextRenderingHint.
SingleBitPerPixelGridFit;
// Draw Box
Point topLeft = new Point(middle.X - width / 2, middle.Y - height / 2);
Point topRight = new Point(topLeft.X + width, topLeft.Y);
Point bottomLeft = new Point(topLeft.X, topLeft.Y + height);
Point bottomRight = new Point(topRight.X, bottomLeft.Y);
graphics.DrawLine(pen, topLeft, topRight);
graphics.DrawLine(pen, topLeft, bottomLeft);
graphics.DrawLine(pen, bottomLeft, bottomRight);
graphics.DrawLine(pen, topRight, bottomRight);
}

Adapt Rectangle's region to a different size

Under Windows Forms, I take a full size screenshot of a specific window that has a specific window size, I save it into a Bitmap object, then, I declared a Rectangle structure to crop a region of that Bitmap, because later I need to manipulate only a very specific part/region of the screenshot...
To make things simpler for this question, lets say the window and bitmap size is 640x480, the Rectangle's X,Y is: 436,150 and the Width,Height is: 146,170, and what I crop from the screenshot (the bitmap) is a balloon image. The window is a videogame.
The problem is that when the window size increase, the balloon image increase too, as obvious, so the x,y and width/height of my rectangle for a window size of 640x480 will not properly capture/crop the entire balloon image when the window of the game has a bigger size...
I need to know how can I calculate the x,y width/height that my rectangle should have to properly crop the balloon image when the window size changes. I need to adapt the rectangle.
So, if this is the predefined size and rectangle I have:
{ new Size(640, 480), new Rectangle(436, 150, 146, 170) }
From that, the approximated adapted values that the rectangle should have to properly crop the same equivalent area in a window size of 800x600 and 1280x768 it would be more or less these:
{ new Size(800, 600), new Rectangle(546, 186, 186, 212) }
{ new Size(1280, 768), new Rectangle(830, 232, 240, 274) }
...are just approximated values, but not perfect, because I did it manually since Im not sure which is the way to calculate and automate this math operation.
I hope my question and problem was understood. Thankyou in advance.
Maybe you're over-thinking it, but all you need to do is capture the percentage change between the original size and the new size (for both X and Y), and then apply that percentage to the properties of the original rectangle to get the new rectangle.
For example:
public static Rectangle GetNewRectangle(Size oldSize, Rectangle oldRectangle,
Size newSize)
{
var percentChangeX = (double)newSize.Width / oldSize.Width;
var percentChangeY = (double)newSize.Height / oldSize.Height;
return new Rectangle
{
X = (int)(oldRectangle.X * percentChangeX),
Y = (int)(oldRectangle.Y * percentChangeY),
Width = (int)(oldRectangle.Width * percentChangeX),
Height = (int)(oldRectangle.Height * percentChangeY)
};
}
Example usage:
// Helper method to display size and rectangle properties
private static string GetDisplayValues(Size size, Rectangle rect)
{
return $" - size: {size.Width} x {size.Height}\n" +
$" - rect: {rect.X}, {rect.Y} : {rect.Width} x {rect.Height}\n";
}
private static void Main()
{
var size = new Size(640, 480);
var rect = new Rectangle(436, 150, 146, 170);
Console.WriteLine($"Original:\n{GetDisplayValues(size, rect)}");
var newSize = new Size(800, 600);
var newRect = GetNewRectangle(size, rect, newSize);
Console.WriteLine($"Resized:\n{GetDisplayValues(newSize, newRect)}");
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
Try this:
if width 640:
X = 436 / 640 = 0.68125 (68.125%)
W = 146 / 640 = 0.22125 (22.125%)
if heigth 480:
Y = 150 / 480 = 0.3125 (31.25%)
H = 170 / 480 = 0.3541666666666666666666666667 (35.41666666666666666666666667%)
Considering the size of the form as this.Width, and the height as this.Height:
decimal pX = 0.68125;
decimal pW = 0.22125;
decimal pY = 0.3125;
decimal pH = 0.3541666666666666666666666667;
Rectangle rect = new Rectangle(this.Width * pX, this.Height * pY, this.Width * pW, this.Height * pH);
Given a source Bitmap and a selection Rectangle inside its boundaries:
RectangleF SourceRect = new Rectangle(Point.Empty, SourceBitmap.Size);
Rectangle SelectionRect = new Rectangle([Point], [Size]);
When the SourceBitmap changes its size, the new size of the selection rectangle is calculated using the scale factor given by the relation between the old size and the new size of the SourceBitmap:
RectangleF DestinationRect = new RectangleF(Point.Empty, InflatedBitmap.Size);
SizeF ScaleFactor = new SizeF(DestinationRect.Width / SourceRect.Width,
DestinationRect.Height / SourceRect.Height);
PointF NewPosition = new PointF(SelectionRect.X * ScaleFactor.Width, SelectionRect.Y * ScaleFactor.Height);
SizeF NewSize = new SizeF(SelectionRect.Width * ScaleFactor.Width, SelectionRect.Height * ScaleFactor.Height);
RectangleF InflatedSelection = new RectangleF(NewPosition, NewSize);
With a SourceBitmap and a selection rectangle sized as:
RectangleF SourceRect = new RectangleF(0, 0, 640, 480);
RectangleF SelectionRect = new RectangleF(436, 150, 146, 170);
If the inflated bitmaps are sized as:
RectangleF DestinationRect1 = new RectangleF(0, 0, 800, 600);
RectangleF DestinationRect2 = new RectangleF(0, 0, 1280, 768);
The Inflated selection with a scale factor of (1.25, 1.25) and (2, 1.6) will be (rounded down):
RectangleF InflatedSelection1 = new RectangleF(545, 187, 182, 212);
RectangleF InflatedSelection2 = new RectangleF(872, 240, 292, 272);

Drawing a polygon according to the input coordinates

How can i draw a polygon according to the input coordinates which are given in C#.
You didn't show any code because based on those coordinate, you are applying some form of scaling to the image.
Using the Paint event of a PictureBox, here is an example using those coordinates on the screen. It fills in the polygon, then draws the border, then it loops through all the points to draw the red circle:
void pictureBox1_Paint(object sender, PaintEventArgs e) {
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.Clear(Color.White);
// draw the shading background:
List<Point> shadePoints = new List<Point>();
shadePoints.Add(new Point(0, pictureBox1.ClientSize.Height));
shadePoints.Add(new Point(pictureBox1.ClientSize.Width, 0));
shadePoints.Add(new Point(pictureBox1.ClientSize.Width,
pictureBox1.ClientSize.Height));
e.Graphics.FillPolygon(Brushes.LightGray, shadePoints.ToArray());
// scale the drawing larger:
using (Matrix m = new Matrix()) {
m.Scale(4, 4);
e.Graphics.Transform = m;
List<Point> polyPoints = new List<Point>();
polyPoints.Add(new Point(10, 10));
polyPoints.Add(new Point(12, 35));
polyPoints.Add(new Point(22, 35));
polyPoints.Add(new Point(24, 22));
// use a semi-transparent background brush:
using (SolidBrush br = new SolidBrush(Color.FromArgb(100, Color.Yellow))) {
e.Graphics.FillPolygon(br, polyPoints.ToArray());
}
e.Graphics.DrawPolygon(Pens.DarkBlue, polyPoints.ToArray());
foreach (Point p in polyPoints) {
e.Graphics.FillEllipse(Brushes.Red,
new Rectangle(p.X - 2, p.Y - 2, 4, 4));
}
}
}
You may use Graphics.DrawPolygon. You can store the coordinates in an array of Point and then you can pass that to DrawPolygon method. You may wanna see:
Drawing with Graphics in WinForms using C#
private System.Drawing.Graphics g;
System.Drawing.Point[] p = new System.Drawing.Point[6];
p[0].X = 0;
p[0].Y = 0;
p[1].X = 53;
p[1].Y = 111;
p[2].X = 114;
p[2].Y = 86;
p[3].X = 34;
p[3].Y = 34;
p[4].X = 165;
p[4].Y = 7;
g = PictureBox1.CreateGraphics();
g.DrawPolygon(pen1, p);
This simple function is able to generate an array of PointF equal to the vertices of the regular polygon to be drawn, where "center" is the center of the polygon, "sides" is its number of sides, "sideLength" is the size of each side in pixels and "offset" is its slope.
public PointF[] GetRegularPolygonScreenVertex(Point center, int sides, int sideLength, float offset)
{
var points = new PointF[sides];
for (int i = 0; i < sides; i++)
{
points[i] = new PointF(
(float)(center.X + sideLength * Math.Cos((i * 360 / sides + offset) * Math.PI / 180f)),
(float)(center.Y + sideLength * Math.Sin((i * 360 / sides + offset) * Math.PI / 180f))
);
}
return points;
}
The result obtained can be used to draw a polygon, e.g. with the function:
GraphicsObject.DrawPolygon(new Pen(Brushes.Black, GetRegularPolygonScreenVertex(new Point(X, Y), 6, 30, 60f));
Which will generate a regular hexagon with a side of 30 pixels inclined by 30°.
hex

c# can you get position from axis without having a form?

I have a WinForm application that I would like to convert to a service. It has two mscharts on it. I don't need to see those charts anymore because the application creates bitmaps out of them. I am drawing a line on one chart depending on data from another chart, like this:
// Get Graphics object from chart
Graphics graph = e.ChartGraphics.Graphics;
// Convert X and Y values to screen position
float pixelYMax = (float)e.ChartGraphics.GetPositionFromAxis("Default", AxisName.Y, CreatininePoint.YValues[0]);
float pixelXMax = (float)e.ChartGraphics.GetPositionFromAxis("Default", AxisName.X, CreatininePoint.XValue);
float pixelYMin = (float)e.ChartGraphics.GetPositionFromAxis("Default", AxisName.Y, CreatininePoint.YValues[0]-20);
float pixelXMin = (float)e.ChartGraphics.GetPositionFromAxis("Default", AxisName.X, CreatininePoint.XValue);
PointF point1 = PointF.Empty;
PointF point2 = PointF.Empty;
// Set Maximum and minimum points
point1.X = pixelXMax;
point1.Y = pixelYMax-10;
point2.X = pixelXMin;
point2.Y = 84;
// Convert relative coordinates to absolute coordinates.
point1 = e.ChartGraphics.GetAbsolutePoint(point1);
point2 = e.ChartGraphics.GetAbsolutePoint(point2);
float[] dashValues = { 4,2 };
Pen blackPen = new Pen(Color.Black, 1);
blackPen.DashPattern = dashValues;
//e.Graphics.DrawLine(blackPen, new Point(5, 5), new Point(405, 5));
//graph.DrawLine(blackPen, point1, point2);
// Draw connection line
graph.DrawLine(new Pen(Color.Black, 2), point2, point1);
// Create string to draw.
//String drawString = creatininept + " ng/mL";
String drawString = "67 ng/mL";
// Create font and brush.
Font drawFont = new Font("Arial", 7);
SolidBrush drawBrush = new SolidBrush(Color.Black);
// Create point for upper-left corner of drawing.
PointF drawPoint = new PointF(point1.X,point1.Y+5);
// Draw string to screen.
graph.DrawString(drawString, drawFont, drawBrush, drawPoint);
is it possible to do this without having a form so that we can have a service running?
Bitmap canvas = new Bitmap(600, 480);
Graphics graph = Graphics.FromImage(canvas);
.. then proceed with drawing on graph.

Categories

Resources