How to shear rectangle without moving it? - c#

I'd like to ask whether it is possible to shear object without actually moving it. Here's the code I'm doing it right now
Matrix matrix = new Matrix();
matrix.Shear(2, 0);
g.Transform = matrix ;
g.DrawRectangle(Pens.Black, new Rectangle(200, 200, 100, 100));
g.ResetTransform();

The Shear factors are relative to the origin of the coordinate system.
So to keep the shear result at the 'same' location you need to add a translation matrix.
Create a translation that moves the point that should be static to the origin of the coordinate system. In the example the static point is (200,300) so the translation is (-200, -300)
apply the translation
Shear
create the inverse of the translation. In the example: (200, 300)
apply the inverse translation
BTW: Make sure you Dispose the matrices and other GDI+ objects!!
Matrix matrix = new Matrix();
matrix.Translate(200, 300);
matrix.Shear(2, 0);
matrix.Translate(-200, -300);
g.Transform = matrix ;
g.DrawRectangle(Pens.Black, new Rectangle(200, 200, 100, 100));
g.ResetTransform();
Or
using (var g = this.CreateGraphics())
{
using(Matrix translation = new Matrix(),
reverseTranslation = translation.Clone(),
sheer = new Matrix()
combination = new Matrix())
{
translation.Translate(200, 300);
reverseTranslation.Invert();
sheer.Shear(2, 0);
combination.Multiply(translation);
combination.Multiply(sheer);
combination.Multiply(reverseTranslation);
g.Transform = combination;
g.DrawRectangle(Pens.Black, new Rectangle(200, 200, 100, 100));
g.ResetTransform();
g.DrawRectangle(Pens.Black, new Rectangle(200, 200, 100, 100));
}
}

This is what you need to do:
Translate the lower-left corner of your rectangle to the origin.
Let the offset be (-dX, -dY).
Apply your shear transform.
Apply the inverse translation to your rectangle, i.e., (dX, dY).

Related

Arc graphic quality

Back here. Is there any way to improve the quality of the Arc?
I'm using e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
This is the piece of code that creates the arc:
using (GraphicsPath gp = new GraphicsPath())
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
gp.Reset();
gp.AddPie(_OuterRectangle, (float)_Properties.Origin, (float)_Properties.GaugeType);
gp.Reverse();
gp.AddPie(_InnerRectangle, (float)_Properties.Origin, (float)_Properties.GaugeType);
gp.Reverse();
pArea.SetClip(gp);
using (Pen oPen = new Pen(this.ForeColor, 2f))
{
e.Graphics.DrawPath(oPen, gp);
}
e.Graphics.SetClip(ClientRectangle);
}
Thanks in advance.
EDIT:
I've did what LarsTech proposed and now the quality is perfect, but I'm not having the figure I need:
OuterRectangle: is the ClientRectangle area, that I'm manipulating it to make Width and Height the same lenght;
InnerRectangle: is 2/3ths of the ClientRectangle area, ergo, of the OuterRectangle;
Properties.Origin: is the angle where the arc starts. I have it in an enumerator as Cardinal Points, where North is 270, East is 0,
and so. In case of the figure, is SouthWest, 135 degrees;
Properties.GaugeType: is another enumerator that says if is Complete = 360, Half = 180, Quarter = 90, so with that I can determine the sweep angle. In case of the figure is ThreeQuarter, 270 degrees.
The problem:
When clipping a region of the current Graphics (Graphics.SetClip method), the resulting drawing loses quality, because the antialiasing effect generated by Graphics.SmoothingMode = SmoothingMode.AntiAlias is lost.
A possible solution is to avoid clipping the region defined by the GraphicsPath used to design the arcs (GraphicsPath.AddPie method); this, however, leaves the lines of the Pie visible, compromising the shape.
Another solution is to draw an ellipsis in the center of the arcs using the background color of the Canvas. Since the arcs are drawn using two rectangles, we can use the inner rectagle, inflate it (Rectangle.Inflate method) as needed (a fraction - Pen.Width / 2 - of the Pen size used for the ouline, usually).
This allows to delete the artifacts generated by the GraphicsPath shapes and to draw some other graphics content in the center of the shapes.
For example, using different Brushes:
LinearGradientBrush HatchBrush TextureBrush
Of course there are other methods to achieve the same result. We could draw the Arcs using the GraphicsPath.AddArc method, extract or calculate the first and last points of the Arcs and use them to draw two lines (GraphicsPath.AddLine) that will close the figures.
But, since we want to draw different graphics objects in the center of the arcs, these objects will cover the center area anyway.
How to use this code:
In a Form, add a TrackBar (named tbarSpeed, here)
Add a PictureBox (named Canvas), with Size (200, 200).
Wire up the TrackBar tbarSpeed_Scroll event and the Panel Canvas_Paint event.
using System.Drawing;
using System.Drawing.Drawing2D;
float GaugeValue = 88.0f;
float GaugeSweepAngle = 270.0f;
float GaugeStartAngle = 135.0F;
private void Canvas_Paint(object sender, PaintEventArgs e)
{
var canvas = sender as Control;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var outerRectangle = new Rectangle(10, 10, 180, 180);
var innerRectangle = new Rectangle(30, 30, 140, 140);
var blendRectangle = new Rectangle(10, 10, 180, 160);
var innerCenter = new PointF(outerRectangle.Left + (outerRectangle.Width / 2),
outerRectangle.Top + (outerRectangle.Height / 2));
float gaugeLength = (outerRectangle.Width / 2) - 2;
using (var path = new GraphicsPath())
{
path.AddPie(outerRectangle, GaugeStartAngle, GaugeSweepAngle);
path.AddPie(innerRectangle, GaugeStartAngle, GaugeSweepAngle);
innerRectangle.Inflate(-1, -1);
using (var pen = new Pen(Color.White, 3f))
using (var backgroundbrush = new SolidBrush(canvas.BackColor))
using (var gradientBrush = new LinearGradientBrush(blendRectangle,
Color.Green, Color.Red, LinearGradientMode.ForwardDiagonal))
{
var blend = new Blend()
{
Factors = new[] { 0.0f, 0.0f, 0.1f, 0.3f, 0.7f, 1.0f },
Positions = new[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f }
};
gradientBrush.Blend = blend;
e.Graphics.FillPath(gradientBrush, path);
e.Graphics.DrawPath(pen, path);
e.Graphics.FillEllipse(backgroundbrush, innerRectangle);
using (var format = new StringFormat())
{
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
innerRectangle.Location = new Point(innerRectangle.X, innerRectangle.Y + canvas.Font.Height);
e.Graphics.DrawString(GaugeValue.ToString() + "%", canvas.Font, Brushes.White, innerRectangle, format);
}
using (var mx = new Matrix())
{
mx.RotateAt(GaugeStartAngle + 90 + (GaugeValue * (GaugeSweepAngle / 100)), innerCenter);
e.Graphics.Transform = mx;
e.Graphics.DrawLine(pen, innerCenter, new PointF(innerCenter.X, innerCenter.Y - gaugeLength));
e.Graphics.ResetTransform();
}
}
}
}
private void tbarSpeed_Scroll(object sender, EventArgs e)
{
GaugeValue = tbarSpeed.Value;
Canvas.Invalidate();
}
Sample code on PasteBin

Flip text vertically using Drawstring

I have some code that writes some text to a defined region.
graphics.DrawString(text, goodFont, Brushes.Black, textarea, stringFormat);
There are some cases where I would like to flip the text on the horizontal so that it goes from:
To
I have tried to measure the string width and take the inverse of that:
float w = graphics.MeasureString(text, goodFont).Width;
graphics.DrawString(text, goodFont, Brushes.Black, -w, 0, stringFormat);
but then my issue is that the text extends outside the boundary of the box I wish to draw it in (textarea).
I would like to flip the text on the horizontal while maintaining my box boundary. Can anybody point me in the right direction for how to accomplish my task?
Thanks in advance!
EDIT: I am trying to avoid having to create a bitmap and then do the transformation.
You can use graphics transformation. The easier I see is to use the this Matrix Constructor (Rectangle, Point[]) like this:
Point[] transformPoints =
{
// upper-left:
new Point(textarea.Right - 1, textarea.Top),
// upper-right:
new Point(textarea.Left + 1, textarea.Top),
// lower-left:
new Point(textarea.Right - 1, textarea.Bottom),
};
var oldMatrix = graphics.Transform;
var matrix = new Matrix(textarea, transformPoints);
try
{
graphics.Transform = matrix;
graphics.DrawString(text, goodFont, Brushes.Black, textarea, stringFormat);
}
finally
{
graphics.Transform = oldMatrix;
matrix.Dispose();
}
P.S. Although #serhiyb posted similar answer a few seconds before mine, I think this is easier to understand - you define the transformation by simply specifying a source rectangle and how to transform its upper-left, upper-right and lower-left points.
You can use the Matrix Constructor to transform the graphics and later draw the graphics using the DrawString method.
Try this:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
string text = "This is a Test";
g.DrawString(text, Font, Brushes.Black, 0, 0);
g.MultiplyTransform(new Matrix(-1, 0, 0, 1, 68, 50));
g.DrawString(text, Font, Brushes.Black, 0, 0);
g.ResetTransform();
}
Output:
You can use Transformation Matrix for that
Something like:
float w = graphics.MeasureString(text, goodFont).Width;
graphics.MultiplyTransform(new Matrix(-1, 0, 0, 1, w, 0));
/*
Matrix:
-1 0
0 1
newX -> -x
newY -> y
and dx offset = w (since we need to move image to right because of new negative x)
*/
graphics.DrawString(text, goodFont, Brushes.Black, textarea, stringFormat);
graphics.ResetTransform();
You may need to play with Matrix/area parameters as I'm coding it blindly but I hope you got the idea.

Drawing a Rectangle of Size 2X2 Inches -Inches to Pixel Conversion

I need to draw a rectangle that should be around the size 2X2 Inches when printed on a paper.
I know that i can draw a rectangle using
g.DrawRectangle(pen, 100,100, 100, 200);
This application will only be used in computers.How can i convert the inches to pixels properly so that i can get the desired dimensions when printed.
To make an image print in the right size by default, it needs to have the right combination of dpi and pixels.
Let's look at an example:
// aiming at 150dpi and 4x6 inches:
float dpi = 150;
float width = 4;
float height = 6;
using (Bitmap bmp = new Bitmap((int)(dpi * width), (int)(dpi * height)))
{
// first set the resolution
bmp.SetResolution(dpi, dpi);
// then create a suitable Graphics object:
using (Graphics G = Graphics.FromImage(bmp))
using (Pen pen = new Pen(Color.Orange))
{
pen.Alignment = System.Drawing.Drawing2D.PenAlignment.Center;
G.Clear(Color.FloralWhite);
// using pixels here:
Size sz = new System.Drawing.Size((int)dpi * 2 - 1, (int)dpi * 2 - 1);
G.DrawRectangle(pen, new Rectangle(new Point(0, 0), sz));
G.DrawRectangle(pen, new Rectangle(new Point(0, 300), sz));
G.DrawRectangle(pen, new Rectangle(new Point(0, 600), sz));
G.DrawRectangle(pen, new Rectangle(new Point(300, 0), sz));
G.DrawRectangle(pen, new Rectangle(new Point(300, 300), sz));
G.DrawRectangle(pen, new Rectangle(new Point(300, 600), sz));
// alternative code:
// we can also set the Graphics object to measure stuff in inches;
G.PageUnit = GraphicsUnit.Inch;
// or fractions of it, let's use 10th:
G.PageScale = 0.1f;
using (Pen pen2 = new Pen(Color.MediumPurple, 1f / dpi * G.PageScale))
{
// draw one rectangle offset by an inch:
G.DrawRectangle(pen2, 10f, 10f, 20f, 20f);
}
bmp.Save(#"D:\xxx.jpg", ImageFormat.Jpeg);
}
}
Note that I had to subtract 1 pixel from the drawn size as DrawRectangle overdraws by 1 pixel!
Note that the coordinates I draw at depend on the resolution! Also note how the jpeg format creates a lot of smeared colors. Pngcreates crisper results, especially once you print text..
Also note how I had to scale down the PenWidth in the alternative code!

how to resize polygon in C#?

I draw Polygon with this code:
Graphics surface;
surface = this.CreateGraphics();
SolidBrush brush = new SolidBrush(Color.Olive);
Point[] points = { new Point(50, 50), new Point(250, 50), new Point(50, 250) };
surface.FillPolygon(brush, points);
how to resize polygon Similar to the following?
Pic
Try this:
var g = e.Graphics;
var points=new PointF[] { new PointF(0, 0), new PointF(1, 0), new PointF(0, 1) };
var st=g.SaveState();
g.TranslateTransform(100f, 100f);
g.ScaleTransform(40f, 40f);
g.FillPolygon(Brushes.Olive, points);
g.Transform=mx;
g.TranslateTransform(300f, 100f);
g.ScaleTransform(80f, 80f);
g.FillPolygon(Brushes.MediumOrchid, points);
g.Restore(st);
which draws to polygons of the same shape on different locations with different sizes.
(red annotations added by me)
You have a couple options. A simple, rather silly solution would be to use linq:
double resizeValue = 1.5;
points.Select(x => new Point(x.X*resizeValue, x.Y*resizeValue);
That way is simple to understand, I think. May be better ways, but if this is all you're doing, it's probably sufficient.

C# Rotate a Polygon(Triangle)

I have a method which is to draw a polygon, and then rotate that polygon 90 degrees to the right so that its original top point is now pointing towards the right.
This is the code to draw the polygon(triangle) how ever I'm lost on how to rotate this.
Point[] points = new Point[3];
points[0] = new Point((int)top, (int)top);
points[1] = new Point((int)top - WIDTH / 2, (int)top + HEIGHT);
points[2] = new Point((int)top + WIDTH / 2, (int)top + HEIGHT);
paper.FillPolygon(normalBrush, points);
Thanks in advance.
http://msdn.microsoft.com/en-us/library/s0s56wcf.aspx#Y609
public void RotateExample(PaintEventArgs e)
{
Pen myPen = new Pen(Color.Blue, 1);
Pen myPen2 = new Pen(Color.Red, 1);
// Draw the rectangle to the screen before applying the transform.
e.Graphics.DrawRectangle(myPen, 150, 50, 200, 100);
// Create a matrix and rotate it 45 degrees.
Matrix myMatrix = new Matrix();
myMatrix.Rotate(45, MatrixOrder.Append);
// Draw the rectangle to the screen again after applying the
// transform.
e.Graphics.Transform = myMatrix;
e.Graphics.DrawRectangle(myPen2, 150, 50, 200, 100);
}
You can use TransformPoints method of Matrix class to just rotate the points
See this informative Wikipedia article for a great explanation of rotation matrices. When rotating 90 degrees we note that cos 90 collapses into zero yielding the following simple transformation where x' and y' are your rotated coordinates and x and y are the previous coordinates.
x' = -y
y' = x
Applying this simple replacement on your example yields the following code. I've also used a shorthand collection initializer expression for added readability.
var points = new[]
{
new Point(-(int) top, (int) top),
new Point((int) -(top + HEIGHT), (int) top - WIDTH/2),
new Point((int) -(top + HEIGHT), (int) top + WIDTH/2)
};
paper.FillPolygon(normalBrush, points);
I also recommend reading up on linear algebra using for example Anton Rorres, et al.
You can rotate your polygon if you rotate every point. You also have to find out your rotation center O. Probably you want to use your polygon center as rotation center.

Categories