Let's have a triangle with sides AB = 80, BC = 50, CA = 40.
It is necessary to draw a triangle whose sides would be equal to these values.Tried to do it via AddPolygon, but I think it's not quite the right solution
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (index == 1)
{
Point[] myArray =
{
new Point(80, 50),
new Point(50, 40),
new Point(40, 80),
};
GraphicsPath Path = new GraphicsPath();
Path.AddPolygon(myArray);
Pen P = new Pen(Color.Black, 5);
e.Graphics.DrawPath(P, Path);
}
}
Since numbers represent lengths, not coordinates, they cannot be used in the API directly: you need to do some math before you start drawing.
First, let's decide on the location and orientation of our triangle: vertex A would be at (0, 0), and side AB would lie on the X axis, so point B would be at (80, 0). Now it's all about figuring out the location of point C. You can figure it out by solving two equations together:
(x-80)2 + y2 = 402
x2 + y2 = 502
The math is easy enough for a seventh grader who's good at math, so I wouldn't bore you with the solution. It yields x=45.62 and y=20.45. Plug these numbers into your program to draw your triangle.
Obviously, this draws the triangle in only one, easy-to-do, orientation. To move the triangle, adjust coordinates of its vertices by the same number. Rotating is a lot trickier; you would need to look it up in your favorite book on analytic geometry.
Related
I'm drawing coordinate axes in picturebox
void draw_cor()
{
int w = pictureBox1.ClientSize.Width / 2;
int h = pictureBox1.ClientSize.Height / 2;
Refresh();
Graphics e = pictureBox1.CreateGraphics();
e.TranslateTransform(w, h);
DrawXAxis(new Point(-w, 0), new Point(w, 0), e);
DrawYAxis(new Point(0, h), new Point(0, -h), e);
DrawZAxis(new Point(-pictureBox1.ClientSize.Width , pictureBox1.ClientSize.Height), new Point(pictureBox1.ClientSize.Width, -pictureBox1.ClientSize.Height ), e);
}
markup and text for the x axis as an example
private void DrawXAxis(Point start, Point end, Graphics g)
{
for (int i = Step; i < end.X; i += Step)
{
g.DrawLine(Pens.Black, i, -5, i, 5);
DrawText(new Point(i, 5), (i / Step).ToString(), g, false);
}
for (int i = -Step; i > start.X; i -= Step)
{
g.DrawLine(Pens.Black, i, -5, i, 5);
DrawText(new Point(i, 5), (i / Step).ToString(), g, false);
}
g.DrawLine(Pens.Black, start, end);
g.DrawString("X", new Font(Font.FontFamily, 10, FontStyle.Bold), Brushes.Black, new Point(end.X - 15, end.Y));
}
private void DrawText(Point point, string text, Graphics g, bool isYAxis)
{
var f = new Font(Font.FontFamily, 6);
var size = g.MeasureString(text, f);
var pt = isYAxis
? new PointF(point.X + 1, point.Y - size.Height / 2)
: new PointF(point.X - size.Width / 2, point.Y + 1);
var rect = new RectangleF(pt, size);
g.DrawString(text, f, Brushes.Black, rect);
}
can someone explain how to make a method for marking the z axis?
I understand that the shift should be diagonal in both x and y, but nothing worked out for me and no markup appears on the screen.(so far I have managed to draw only a straight line diagonally )
upd:
private void DrawZAxis(Point start, Point end, Graphics g)
{
for (int i = -Step, j=Step ; i > start.X; i -= Step,j += Step)
{
g.DrawLine(Pens.Black, new Point(i-5, j), new Point(i+5, j));
DrawText(new Point(i, j), (i / -Step).ToString(), g, false);
}
...
}
I seem to have succeeded, but I ran into such a problem:
that is, the markup is not always on the coordinate axis. How to avoid this? It is necessary that the numbers are always on the axis (I suppose I should calculate the coefficient when the window is scaled, but only where to add it or by what to multiply?)
You are dealing with 3D data, so you should use 3D tools to transform your axes, and your data for that matter.
So you need to define a projection from 3D space to 2D space. This is usually done by defining a projection matrix. There are multiple projections to chose from, it looks like your projection is Oblique, but orthographic and perspective projections are also common. The System.Numerics.Vectors library has classes for Matrix4x4, vector2/3/4, with methods to create your projection and transform your vectors.
After transforming a vector you can simply keep the x/y values and discard the z-value to get your image coordinates. Note that if using a perspective transform you need a vector4 and divide the x/y/z elements by W.
Armed with these tools it should be a fairly simple thing to generate start/end points for each axis, and create tick-marks in 3D, before projecting everything to 2D for drawing.
Another option would be to just do everything in Wpf3D to start with, this will likely make some functionality like rotating the camera simpler.
I have two Points I want to connect with an Arc in the C# Graphics Class when my main form is painted. I also have the radius that arc should have and the direction the arc should turn from starting point to the next. I dont see how I should do that with the drawArc overloads provided. Can anybody help me with maybe a function that takes a starting point, a end point, an arc radius and a direction (Clockwise or counter-Clockwise) and then draws that arc? Or Rather I am trying to parse this from how .gcode files specify arc movements and if the radius is negative its a clockwise roation and vice versa.
Am looking forward to some input
I figured it out for you.
Here is the sample code. The key here is the function DrawArcBetweenTwoPoints(). For testing it also draws the center points and the two spokes. You should remove those lines and focus on the DrawArc() part.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TranslateTransform(ClientSize.Width/2, ClientSize.Height/2);
PointF A = new PointF(0, -40);
PointF B = new PointF(100, 40);
e.Graphics.DrawLine(Pens.DarkBlue, A, B);
DrawPoint(e.Graphics, Brushes.Black, A);
DrawPoint(e.Graphics, Brushes.Black, B);
DrawArcBetweenTwoPoints(e.Graphics, Pens.Red, A, B, 100);
}
public void DrawPoint(Graphics g, Brush brush, PointF A, float size = 8f)
{
g.FillEllipse(brush, A.X-size/2, A.Y-size/2, size, size);
}
public void DrawArcBetweenTwoPoints(Graphics g, Pen pen, PointF a, PointF b, float radius, bool flip = false)
{
if (flip)
{
PointF temp = b;
b =a;
a = temp;
}
// get distance components
double x = b.X-a.X, y = b.Y-a.Y;
// get orientation angle
var θ = Math.Atan2(y, x);
// length between A and B
var l = Math.Sqrt(x*x+y*y);
if (2*radius>=l)
{
// find the sweep angle (actually half the sweep angle)
var φ = Math.Asin(l/(2*radius));
// triangle height from the chord to the center
var h = radius*Math.Cos(φ);
// get center point.
// Use sin(θ)=y/l and cos(θ)=x/l
PointF C = new PointF(
(float)(a.X + x/2 - h*(y/l)),
(float)(a.Y + y/2 + h*(x/l)));
g.DrawLine(Pens.DarkGray, C, a);
g.DrawLine(Pens.DarkGray, C, b);
DrawPoint(g, Brushes.Orange, C);
// Conversion factor between radians and degrees
const double to_deg = 180/Math.PI;
// Draw arc based on square around center and start/sweep angles
g.DrawArc(pen, C.X-radius, C.Y-radius, 2*radius, 2*radius,
(float)((θ-φ)*to_deg)-90, (float)(2*φ*to_deg));
}
}
private void Form1_Resize(object sender, EventArgs e)
{
this.Refresh();
}
}
I tested the flip arc with the following code
DrawArcBetweenTwoPoints(e.Graphics, Pens.Red, A, B, 100);
DrawArcBetweenTwoPoints(e.Graphics, Pens.Red, A, B, 100, true);
Welcome to Stackoverflow!
I recommend using a Bezier Curve.
An implementation is shown below which you can adapt for your arc.
The BezierCurve (check Microsoft documentation) is a curve described fully by 4 points.
The start/end points should be clear.
The control points define the curvature of the arc.
You will need to calculate the control points yourself. I recommend trying Code Sample 1 to see how it functions and then adapting the second code sample for your specific purposes. Change the values of the ComputedRotationAngleA/B, flip them around. Change the ControlPointDistance amount. You should be able to get a good idea of how the C# implentation of it works.
Then calculate your own values and plug them into the Code provided in Code Sample 2.
Note:
ComputedRotationAngleA/B control the direction of the arc.
ControlPointDistance controls the point where the curvature is added to the arc.
Good luck!
Code Sample 1:
internal void DrawBezierCurveTest(Pen pen, PaintEventArgs e)
{
int ComputedRotationAngleA = 20, ComputedRotationAngleB = -20;
int ControlPointDistance = 5;
LinePath = new GraphicsPath();
var p1 = new Point(StartX, StartY);
var p2 = new Point(StartX + ControlPointDistance , StartY + ControlPointDistance );
p2.Offset(ComputedRotationAngleA, ComputedRotationAngleA);
var p3 = new Point(EndX - ControlPointDistance , EndY - ControlPointDistance );
p3.Offset(ComputedRotationAngleB, ComputedRotationAngleB);
var p4 = new Point(EndX , EndY);
LinePath.AddBezier(p1, p2, p3, p4);
e.Graphics.DrawPath(pen, LinePath);
}
Code Sample 2:
internal void DrawBezierCurve(Pen pen, PaintEventArgs e)
{
LinePath = new GraphicsPath();
var p1 = new Point(StartX, StartY); //starting point
var p2 = new Point(FirstDipX, FirstDipY) //first control point
var p3 = new Point(SecondDipX, SecondDipY); //second control point
var p4 = new Point(EndX, EndY); //ending point
LinePath.AddBezier(p1, p2, p3, p4);
e.Graphics.DrawPath(pen, LinePath);
}
I want to crop from an image using user-drawn rectangles on a canvas. The rectangles can be moved, re-sized, and rotated.
When the user selects "Get Cropped Image", the area inside the rectangle should be saved in a second image location on the page, which I can do perfectly well, so long as the rectangle is not rotated. (Straight-forward use of CroppedBitmap.) However, when the rectangle is at an angle I do not know how to perform the crop.
This is what I want to do (forgive my poor MS Paint skills):
My questions are:
1) How do I correctly track or calculate the points of the rectangle?
and,
2) Once I have the points, how do I crop the rotated rectangle?
EDIT:
Thanks to user Rotem, I believe that I have the answer to the second question. Using code modified from the following answers: Answer 1, Answer 2, I am seeing good results. Unfortunately, I am still unable to track the correct location points for the rectangle, so I cannot fully test this as of yet.
public static Bitmap CropRotatedRect(Bitmap source, System.Drawing.Rectangle rect, float angle, bool HighQuality)
{
Bitmap result = new Bitmap((int)rect.Width, (int)rect.Height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = HighQuality ? InterpolationMode.HighQualityBicubic : InterpolationMode.Default;
using (Matrix mat = new Matrix())
{
mat.Translate(-rect.Location.X, -rect.Location.Y);
mat.RotateAt(-(angle), rect.Location);
g.Transform = mat;
g.DrawImage(source, new System.Drawing.Point(0, 0));
}
}
return result;
}
EDIT:
The answer to the first point is much easier than I had originally thought. You can always get the top-left corner of the rectangle by calling—
double top = Canvas.GetTop(rect);
double left = Canvas.GetLeft(rect);
You can then calculate the rest of the points using the width and the height—
Point topLeft = new Point(left, top);
Point topRight = new Point(left + rect.Width, top);
Point bottomLeft = new Point(left, top + rect.Height);
Point bottomRight = new Point(left + rect.Width, top + rect.Height);
Point centerPoint = new Point(left + (rect.Width / 2), top + (rect.Height / 2));
If your rectangle is rotated, then you have to translate these points to determine where they truly lie on the canvas—
public Point TranslatePoint(Point center, Point p, double angle)
{
// get the point relative to (0, 0) by subtracting the center of the rotated shape.
Point relToOrig = new Point(p.X - center.X, p.Y - center.Y);
double angleInRadians = angle * Math.PI / 180;
double sinOfA = Math.Sin(angleInRadians);
double cosOfA = Math.Cos(angleInRadians);
Point translatedPoint = new Point(relToOrig.X * cosOfA - relToOrig.Y * sinOfA,
relToOrig.X * sinOfA + relToOrig.Y * cosOfA);
return new Point(translatedPoint.X + center.X, translatedPoint.Y + center.Y);
}
Once you are able to translate the top-left corner, you can use Rotem's cropping method. You can also calculate the position of the rest of the rectangle, so you are able to determine if the rectangle is within the bounds of the image, if it is touching an edge, or any other thing that you might want to do in regards to the position.
I discovered the answer to my own question(s), and made the appropriate edits along the way. Please see above for the answer.
I have several points, and I try to draw Bezier curve using code below
PathFigure pf = new PathFigure(points.From, ps, false); //ps - list of Bezier segments
PathFigureCollection pfc = new PathFigureCollection();
pfc.Add(pf);
var pge = new PathGeometry();
pge.Figures = pfc;
Path p = new Path();
p.Data = pge;
p.Stroke = new SolidColorBrush(Color.FromRgb(244, 111, 011));
My Bezier segments look like this
1,2,3 points - first segment
3,4,5 points - second
5,6,7.. ..
But I got this strange curve (here is 3 big (Nodes) and 7 small ellipse (is my points)):
The line you're getting is the union of three distinct Bezier curves - one for each group of three points. (One for each "Bezier segment"?)
If you want a single smooth curve, you need to pass your 9 (or more) points as a single collection of points (single "Bezier segment"?), not as groups of three points.
Edit: Apparently BezierSegment only supports three points, so no wonder this doesn't work. Even 'PolyBezierSegment' just gives a collection of Bezier segments rather than a single smooth Bezier...
So since WPF doesn't give you anything useful, I knocked something together using the maths here. It's a numeric solution, but it seems to be pretty performant even with enough points to look nice and smooth:
PolyLineSegment GetBezierApproximation(Point[] controlPoints, int outputSegmentCount)
{
Point[] points = new Point[outputSegmentCount + 1];
for (int i = 0; i <= outputSegmentCount; i++)
{
double t = (double)i / outputSegmentCount;
points[i] = GetBezierPoint(t, controlPoints, 0, controlPoints.Length);
}
return new PolyLineSegment(points, true);
}
Point GetBezierPoint(double t, Point[] controlPoints, int index, int count)
{
if (count == 1)
return controlPoints[index];
var P0 = GetBezierPoint(t, controlPoints, index, count - 1);
var P1 = GetBezierPoint(t, controlPoints, index + 1, count - 1);
return new Point((1 - t) * P0.X + t * P1.X, (1 - t) * P0.Y + t * P1.Y);
}
Using this,
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Point[] points = new[] {
new Point(0, 200),
new Point(0, 0),
new Point(300, 0),
new Point(350, 200),
new Point(400, 0)
};
var b = GetBezierApproximation(points, 256);
PathFigure pf = new PathFigure(b.Points[0], new[] { b }, false);
PathFigureCollection pfc = new PathFigureCollection();
pfc.Add(pf);
var pge = new PathGeometry();
pge.Figures = pfc;
Path p = new Path();
p.Data = pge;
p.Stroke = new SolidColorBrush(Color.FromRgb(255, 0, 0));
((Grid)sender).Children.Add(p);
}
gives
Since each of your curves has one control point (a point that influences the curve but isn't necessarily on the curve), you're using quadratic Bézier curves.
If you want to draw two quadratic curves that share an endpoint, and you want the joint to appear smooth, the control points on each side of the shared endpoint must be collinear with the endpoint. That is, the two control points and the endpoint between them must all lie on a straight line. Example:
The solid black discs are the endpoints. The hollow circles are the control points. The solid black line is the curve. The dotted lines show that each endpoint is collinear (on a straight line with) the control point on either side.
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.