Drawing a triangle with rounded corners - c#

So here's my code to draw a triangle:
Graphics y = CreateGraphics();
Pen yy = new Pen(Color.Red);
pictureBox1.Refresh();
Point x = new Point(Convert.ToInt32(textBox1.Text)*10, Convert.ToInt32(textBox2.Text)*10);
Point xx = new Point(Convert.ToInt32(textBox3.Text)*10, Convert.ToInt32(textBox4.Text)*10);
Point xxx = new Point(Convert.ToInt32(textBox5.Text)*10, Convert.ToInt32(textBox6.Text)*10);
Point[] u = { x, xx, xxx };
y = pictureBox1.CreateGraphics();
y.DrawPolygon(yy, u);
Is there any way to round the corners on this? I read about it on google and it seems there's only a way to round rectangles, but not triangles.
Is there any command to do that for me or I have to do it manually?
Thanks for replies :)

It is not possible out of the box.
You can create rounded polygons like this:
Change each line by making it shorter (on both ends) by your chosen number of pixels, now line AB is line A1B1..
Create a GraphicsPath which consists of these lines and in between each pair of lines a curve that connects those new endpoints
To better control the curves it would help to add a point in the middle between the original corners and the connection of the new endpoints
For a Triangle ABC this would mean to
Create A1, A2, B1, B2, C1, C2
Then calculate the middle points A3, B3, C3
and finally adding to a GraphicsPath:
Line A1B1, Curve B1B3B2, Line B2C1, Curve C1C3C2, Line C2A2 and Curve A2A3A1.
Here is a piece of code that uses this method:
A GraphicsPaths to be used in the Paint event:
GraphicsPath GP = null;
A test method with a list of points, making up a triangle - (*) we overdraw by two points to make things easier; one could add the final lines and curves by ading a few lines of code instead..
private void TestButton_Click(object sender, EventArgs e)
{
Point A = new Point(5, 50);
Point B = new Point(250, 100);
Point C = new Point(50, 250);
List<Point> points = new List<Point>();
points.Add(A);
points.Add(B);
points.Add(C);
points.Add(A); // *
points.Add(B); // *
GP = roundedPolygon(points.ToArray(), 20);
panel1.Invalidate();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (GP == null) return;
using (Pen pen = new Pen(Brushes.BlueViolet, 3f))
e.Graphics.DrawPath(pen, GP);
}
GraphicsPath roundedPolygon(Point[] points, int rounding)
{
GraphicsPath GP = new GraphicsPath();
List<Line> lines = new List<Line>();
for (int p = 0; p < points.Length - 1; p++)
lines.Add( shortenLine(new Line(points[p], points[p+1]), rounding) );
for (int l = 0; l < lines.Count - 1; l++)
{
GP.AddLine(lines[l].P1, lines[l].P2);
GP.AddCurve(new Point[] { lines[l].P2,
supPoint(lines[l].P2,points[l+1], lines[l+1].P1),
lines[l+1].P1 });
}
return GP;
}
// a simple structure
struct Line
{
public Point P1; public Point P2;
public Line(Point p1, Point p2){P1 = p1; P2 = p2;}
}
// routine to make a line shorter on both ends
Line shortenLine(Line line, int byPixels)
{
float len = (float)Math.Sqrt(Math.Pow(line.P1.X - line.P2.X, 2)
+ Math.Pow(line.P1.Y - line.P2.Y, 2));
float prop = (len - byPixels) / len;
Point p2 = pointBetween(line.P1, line.P2, prop);
Point p1 = pointBetween(line.P1, line.P2, 1 - prop);
return new Line(p1,p2);
}
// with a proportion of 0.5 the point sits in the middle
Point pointBetween(Point p1, Point p2, float prop)
{
return new Point((int)(p1.X + (p2.X - p1.X) * prop),
(int)(p1.Y + (p2.Y - p1.Y) * prop));
}
// a supporting point, change the second prop (**) to change the rounding shape!
Point supPoint(Point p1, Point p2, Point p0)
{
Point p12 = pointBetween(p1, p2, 0.5f);
return pointBetween(p12, p0, 0.5f); // **
}
Example:

I think if you adjust your coordinates appropriately and specify a Width for your Pen greater than 1 and set the LineJoin property to Rounded, you should obtain a triangle with rounded corners.

I fought with the math for a bit, then I had an idea...overlaid shapes.
<Canvas >
<Polyline Margin="-1 0 0 0"
Fill="{StaticResource Yellow}"
Points="3,14.5 17,14.5 10,2 3,14.5"
StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round"
Stroke="Black"
StrokeThickness="4"/>
<Polyline Margin="-1 0 0 0"
Fill="{StaticResource Yellow}"
Stroke="{StaticResource Yellow}"
Points="3,14.5 17,14.5 10,2 3,14.5"
StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round"
StrokeThickness="3"/>
</Canvas>

public GraphicsPath DrawRoundedTriangle(RectangleF Rect, PointF point_A, PointF point_B, PointF point_C, float Radius)
{
Radius = (Rect.Height / 9f) * (Radius * 0.2f);
float Diameter = Radius * 2f;
float arcSize = (float)((Diameter / 2f) * Math.Sqrt(3.0));
GraphicsPath path = new GraphicsPath();
float sweep_angle = 120f; //Angle of Arc in the corners
if (Radius == 0f)
{
//Drawing of triangle without a rounded corner
path.AddLine(point_A, point_B);
path.AddLine(point_B, point_C);
path.AddLine(point_C, point_A);
}
else
{
PointF[] array = new PointF[6];
array[0] = new PointF(point_A.X + Radius, point_A.Y - arcSize); //Left Vertex point
array[1] = new PointF(point_B.X - Radius, point_B.Y + arcSize); //Top Vertex point
array[2] = new PointF(point_B.X + Radius, point_B.Y + arcSize); //Top Vertex point
array[3] = new PointF(point_C.X - Radius, point_C.Y - arcSize); //Right Vertex point
array[4] = new PointF(point_C.X - arcSize, point_C.Y); //Right Vertex point
array[5] = new PointF(point_A.X + arcSize, point_C.Y); //Left Vertex point
//Path Creation for Drawing
path.AddArc( (array[5].X - Radius), (array[5].Y - Diameter), Diameter, Diameter, 90f, sweep_angle); //Left Vertex
path.AddLine(array[0], array[1]); //Left Side Line
path.AddArc(array[1].X, ((array[1].Y - arcSize) + Radius), Diameter, Diameter, 210f, sweep_angle); //Top Vertex
path.AddLine(array[2], array[3]); //Right Side Line
path.AddArc((array[3].X - arcSize), (array[3].Y - (Diameter - arcSize)), Diameter, Diameter, 330f, sweep_angle); //Right Vertex
path.AddLine(array[4], array[5]); //Bottom Line
}
path.CloseFigure();
return path;
}

Related

How do I rotate a Regular polygon in C#

I need to rotate the regular polygon around a set center by a given degree can somebody help?
Im using this code the generate the regular polygon
private static void DrawRegularPolygon(PointF center, // center Coordinates of circle
int vertexes, // Number of vertices
float radius, // Radius
Graphics graphics)
{
Pen pen;
var angle = Math.PI * 2 / vertexes;
var Rotationangle = (45/180) * Math.PI;
var points = Enumerable.Range(0, vertexes)
.Select(i => PointF.Add(center, new SizeF((float)Math.Sin(i * angle) * radius, (float)Math.Cos(i * angle) * radius )));
if (vertexes%2 == 0)
{
pen = new Pen(Color.Red);
}
else
{
pen = new Pen(Color.Black);
}
graphics.DrawPolygon(pen, points.ToArray());
//graphics.DrawEllipse(Pens.Aqua, new RectangleF(PointF.Subtract(center, new SizeF(radius, radius)), new SizeF(radius * 2, radius * 2)));
}
Try these lines :
graphics.TranslateTransform(center.X, center.Y);
graphics.RotateTransform(180f);
graphics.TranslateTransform(-center.X, -center.Y);
graphics.DrawPolygon(pen, points.ToArray());
I can rotate the polygon 180 degrees, before drawing it.

Creating an Arc between Two Points with Arc Radius and Rotation

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);
}

Fill regular polygons with 2 colors

I have drawn regular polygons and divided those into equal parts.
It's like this :
but I want to fill it with 2 colors like this :
How do I implement this?
Code how to draw polygons is below:
Graphics g = e.Graphics;
nPoints = CalculateVertices(sides, radius, angle, center);
g.DrawPolygon(navypen, nPoints);
g.FillPolygon(BlueBrush, nPoints);
Point center = new Point(ClientSize.Width / 2, ClientSize.Height / 2);
for(int i = 0; i < sides; i++) {
g.DrawLine(new Pen(Color.Navy), center.X, center.Y, nPoints[i].X, nPoints[i].Y);
}
private PointF[] CalculateVertices(int sides, int radius, float startingAngle, Point center)
{
if (sides < 3) {
sides = 3;
}
//throw new ArgumentException("Polygon must have 3 sides or more.");
List<PointF> points = new List<PointF>();
float step = 360.0f / sides;
float angle = startingAngle; //starting angle
for (double i = startingAngle; i < startingAngle + 360.0; i += step) //go in a circle
{
points.Add(DegreesToXY(angle, radius, center));
angle += step;
}
return points.ToArray();
}
private PointF DegreesToXY(float degrees, float radius, Point origin)
{
PointF xy = new PointF();
double radians = degrees * Math.PI / 180.0;
xy.X = (int)(Math.Cos(radians) * radius + origin.X);
xy.Y = (int)(Math.Sin(-radians) * radius + origin.Y);
return xy;
}
There are several ways but the most straight-forward is to draw the polygons (triangles) of different colors separately.
Assumig a List<T> for colors:
List<Color> colors = new List<Color> { Color.Yellow, Color.Red };
You can add this before the DrawLine call:
using (SolidBrush brush = new SolidBrush(colors[i%2]))
g.FillPolygon(brush, new[] { center, nPoints[i], nPoints[(i+1)% sides]});
Note how I wrap around both the nPoints and the colors using the % operator!

Drawing triangles on each edge of regular polygon

I've been tried to draw triangles on each edge of regular polygons.
So far I got to make polygons like this:
What I'm trying to make is that small triangle on the each edge of the polygon:
How do I do this?
Code how to draw polygons is below:
int sides = 5;
Graphics g = e.Graphics;
nPoints = CalculateVertices(sides, radius, angle, center);
g.DrawPolygon(navypen, nPoints);
g.FillPolygon(BlueBrush, nPoints);
Point center = new Point(ClientSize.Width / 2, ClientSize.Height / 2);
for(int i = 0; i < sides; i++) {
g.DrawLine(new Pen(Color.Navy), center.X, center.Y, nPoints[i].X, nPoints[i].Y);
}
private PointF[] CalculateVertices(int sides, int radius, float startingAngle, Point center)
{
if (sides < 3) {
sides = 3;
}
//throw new ArgumentException("Polygon must have 3 sides or more.");
List<PointF> points = new List<PointF>();
float step = 360.0f / sides;
float angle = startingAngle; //starting angle
for (double i = startingAngle; i < startingAngle + 360.0; i += step) //go in a circle
{
points.Add(DegreesToXY(angle, radius, center));
angle += step;
}
return points.ToArray();
}
private PointF DegreesToXY(float degrees, float radius, Point origin)
{
PointF xy = new PointF();
double radians = degrees * Math.PI / 180.0;
xy.X = (int)(Math.Cos(radians) * radius + origin.X);
xy.Y = (int)(Math.Sin(-radians) * radius + origin.Y);
return xy;
}
Try this out...instead of calculating absolute points for the triangle, I've instead computed points for a "unit triangle" at the origin (using your function!). Then I simply rotate and move the Graphics surface and draw the unit triangle where I want it:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private PointF[] nPoints;
private PointF[] triangle;
private int sides = 5;
private int angle = 0;
private int radius = 100;
private int triangleLength = 10;
private void Form1_Load(object sender, EventArgs e)
{
triangle = this.CalculateVertices(3, triangleLength, 0, new Point(0, 0)); // this "unit triangle" will get reused!
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point center = new Point(ClientSize.Width / 2, ClientSize.Height / 2);
nPoints = CalculateVertices(sides, radius, angle, center);
// draw the polygon
g.FillPolygon(Brushes.Blue, nPoints);
g.DrawPolygon(Pens.Black, nPoints);
for (int i = 0; i < sides; i++)
{
g.DrawLine(Pens.Black, center.X, center.Y, nPoints[i].X, nPoints[i].Y);
}
// draw small triangles on each edge:
float step = 360.0f / sides;
float curAngle = angle + step / 2; // start in-between the original angles
for (double i = curAngle; i < angle + (step / 2) + 360.0; i += step) //go in a circle
{
// move to the center and rotate:
g.ResetTransform();
g.TranslateTransform(center.X, center.Y);
g.RotateTransform((float)i);
// move out to where the triangle will be drawn and render it
g.TranslateTransform(radius, 0);
g.FillPolygon(Brushes.LightGreen, triangle);
g.DrawPolygon(Pens.Black, triangle);
}
}
// this is your code unchanged
private PointF[] CalculateVertices(int sides, int radius, float startingAngle, Point center)
{
if (sides < 3)
{
sides = 3;
}
//throw new ArgumentException("Polygon must have 3 sides or more.");
List<PointF> points = new List<PointF>();
float step = 360.0f / sides;
float angle = startingAngle; //starting angle
for (double i = startingAngle; i < startingAngle + 360.0; i += step) //go in a circle
{
points.Add(DegreesToXY(angle, radius, center));
angle += step;
}
return points.ToArray();
}
// this is your code unchanged
private PointF DegreesToXY(float degrees, float radius, Point origin)
{
PointF xy = new PointF();
double radians = degrees * Math.PI / 180.0;
xy.X = (int)(Math.Cos(radians) * radius + origin.X);
xy.Y = (int)(Math.Sin(-radians) * radius + origin.Y);
return xy;
}
}

DrawEllipse from two joint (Point, or rather X and Y coordinates)

I'm looking to show skeleton by ellipse and not by line. I have two Point with coordinates for X and Y.
When i want to draw an ellipse i need
public abstract void DrawEllipse(
Brush brush,
Pen pen,
Point center,
double radiusX,
double radiusY
)
so i have tried with this code but there is some error(don't know radiusY):
double centerX = (jointPoints[jointType0].X + jointPoints[jointType1].X) / 2;
double centerY = (jointPoints[jointType0].Y + jointPoints[jointType1].Y) / 2;
double radiusX =Math.Sqrt( (Math.Pow((jointPoints[jointType1].X - jointPoints[jointType0].X), 2)) + (Math.Pow((jointPoints[jointType1].Y - jointPoints[jointType0].Y), 2)));
drawingContext.DrawEllipse(null, drawPen, new Point(centerX, centerY), radiusX, radiusX/5);
Can anyone help me?
private void DrawBone(IReadOnlyDictionary<JointType, Joint> joints, IDictionary<JointType, Point> jointPoints, JointType jointType0, JointType jointType1, DrawingContext drawingContext, Pen drawingPen,List<JointType> badJoint)
{
Joint joint0 = joints[jointType0];
Joint joint1 = joints[jointType1];
// If we can't find either of these joints, exit
if (joint0.TrackingState == TrackingState.NotTracked ||
joint1.TrackingState == TrackingState.NotTracked)
{
return;
}
// We assume all drawn bones are inferred unless BOTH joints are tracked
Pen drawPen = this.inferredBonePen;
if ((joint0.TrackingState == TrackingState.Tracked) && (joint1.TrackingState == TrackingState.Tracked))
{
drawPen = drawingPen;
}
//If a bone makes parts of an one bad angle respect reference angle
if (badJoint.Contains(jointType0) && badJoint.Contains(jointType0))
drawPen = new Pen(Brushes.Red, 6);
drawingContext.DrawLine(drawPen, jointPoints[jointType0], jointPoints[jointType1]);
You cannot (just) use the DrawEllipse method, because that will always draw horizontal or vertical elipses.
Use this code to implement the rotation: https://stackoverflow.com/a/5298921/1974021 and write a method that takes the following input parameters:
Focalpoint1
FocalPoint2
Radius
As an ellipse can be described by both focal points and a (combined) radius. If you use the focal points, the ellipsis will overlap at the joints to create a circle-like pattern at each joint. Is that about what you want? (It is even easier if you only want them to touch at the joint)
Okay, it's actually not the focal point but the center of the osculating circle. Try this method:
private static void DrawEllipse(Pen pen, Graphics g, PointF pointA, PointF pointB, float radius)
{
var center = new PointF((pointA.X + pointB.X) / 2, (pointA.Y + pointB.Y) / 2);
var distance = GetDistance(pointA, pointB);
// The axis are calculated so that the center of the osculating circles are conincident with the points and has the given radius.
var a = radius + distance / 2; // Semi-major axis
var b = (float)Math.Sqrt(radius * a); // Semi-minor axis
// Angle in degrees
float angle = (float)(Math.Atan2(pointA.Y - pointB.Y, pointA.X - pointB.X) * 180 / Math.PI);
using (Matrix rotate = new Matrix())
{
GraphicsContainer container = g.BeginContainer();
rotate.RotateAt(angle, center);
g.Transform = rotate;
g.DrawEllipse(pen, center.X-a, center.Y-b, 2 * a, 2 * b);
g.EndContainer(container);
}
}
private static float GetDistance(PointF a, PointF b)
{
var dx = a.X - b.X;
var dy = a.Y - b.Y;
return (float)Math.Sqrt(dx * dx + dy * dy);
}

Categories