C# WPF Draw equilateral polygons programatically - c#

I'm working on a method to programatically draw equilateral polygon shapes in C#, WPF. But I have been stuck and I can't solve calculating the angles. Where is the problem? How should I correct this? I have given the public int, R(radius) a value of 100.
private Path EquilateralPolygon(int sides)
{
//Centering
Point center = new Point(canvasSize.Width / 2, canvasSize.Height / 2);
PathFigure myPathFigure = new PathFigure();
int alfa = 360 / sides;
int[] angles = new int[6];
for (int i = 0; i < sides; i++)
angles[i] = 360 - alfa * i;
MessageBox.Show(angles.Sum().ToString());
Point A = new Point(center.X, center.Y - R);
myPathFigure.StartPoint = A;
PolyLineSegment myLineSegment = new PolyLineSegment();
for (int i = 1; i < sides; i++)
{
myLineSegment.Points.Add(new Point(center.X + Math.Cos(angles[i]) * R, center.Y + Math.Sin(angles[i]) * R));
}
myLineSegment.Points.Add(A);
PathSegmentCollection myPathSegmentCollection = new PathSegmentCollection();
myPathSegmentCollection.Add(myLineSegment);
myPathFigure.Segments = myPathSegmentCollection;
PathFigureCollection myPathFigureCollection = new PathFigureCollection();
myPathFigureCollection.Add(myPathFigure);
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures = myPathFigureCollection;
Path myPath = new Path();
myPath.Stroke = Brushes.Red;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;
return myPath;
}

You've posted a lot of code and were not specific about how it's not working, so there may be more than one issue with your code. However, one big issue is that the Math.Cos (and related trig methods) take the angle in the form of radians, not degrees as you have them.
Parameters
d
Type: System.Double An angle, measured in radians.
You will need to convert them to radians. To convert, multiply by π (available via Math.PI) then divide by 180 degrees.
myLineSegment.Points.Add(
new Point(center.X + Math.Cos(angles[i] * Math.PI / 180.0) * R,
center.Y + Math.Sin(angles[i] * Math.PI / 180) * R));
EDIT: In addition to the radians/degrees issue, I can see you may be experiencing integer truncation, both in the use of your angles array and your calculation of alfa. I would suggest you try changing your use of integers to double so that your code works fine with fractions of a degree.

Related

Getting NaN for image angle in emgucv with c#

I have referenced following link to develop following csharp code to detect angle of Image. https://stackoverflow.com/a/34285205/7805023
Image<Gray, byte> imgout = imgInput.Convert<Gray, byte>().Not().ThresholdBinary(new
Gray(50), new Gray(255));
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Mat hier = new Mat();
CvInvoke.FindContours(imgout, contours, hier, Emgu.CV.CvEnum.RetrType.External, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
for (int i = 0; i <= 1; i++)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
RotatedRect box = CvInvoke.MinAreaRect(contours[i]);
PointF[] Vertices = box.GetVertices();
PointF point = box.Center;
PointF edge1 = new PointF(Vertices[1].X - Vertices[0].X,
Vertices[1].Y - Vertices[0].Y);
PointF edge2 = new PointF(Vertices[2].X - Vertices[1].X,Vertices[2].Y - Vertices[1].Y);
double edge1Magnitude = Math.Sqrt(Math.Pow(edge1.X, 2) + Math.Pow(edge1.Y, 2));
double edge2Magnitude = Math.Sqrt(Math.Pow(edge2.X, 2) + Math.Pow(edge2.Y, 2));
PointF primaryEdge = edge1Magnitude > edge2Magnitude ? edge1 : edge2;
PointF reference = new PointF(Vertices[1].X, Vertices[0].Y);
double thetaRads = Math.Acos((primaryEdge.X * reference.X) + (primaryEdge.Y * reference.Y))/(edge1Magnitude* edge2Magnitude);
double thetaDeg = thetaRads * 180 / Math.PI;
}
I am getting NaN as output for angle. Please go through the code let me know what wrong I have done?
I have used emgucv for image processing.
Any help will be highly appreciated.
NaN, Not a Number, is returned by Math.Acos() when the provided value is not between -1 and 1.
Your calculation of thetaRads is wrong, you need to calculate the Acos of the vectors' dot product divided by the product of the vectors' magnitudes, as per:
double thetaRads = Math.Acos(((primaryEdge.X * reference.X) + (primaryEdge.Y * reference.Y)) / (primaryMagnitude * refMagnitude));

Plot/Draw Circle in WindowsForms Chart

Is there any possibility to plot a circle in a WindowsForm Chart?
A method-call as follows would be really nice!
Graph.Series["circle"].Circle.Add(centerX, centerY, radius);
Well, I created myself a work around.
Maybe it helps someone
public void DrawCircle(Chart Graph, double centerX, double centerY, double radius, int amountOfEdges)
{
string name = "circle_" + centerX + centerY + radius + amountOfEdges;
// Create new data series
if (Graph.Series.IndexOf(name) == -1)
Graph.Series.Add(name);
// preferences of the line
Graph.Series[name].ChartType = SeriesChartType.Spline;
Graph.Series[name].Color = Color.FromArgb(0, 0, 0);
Graph.Series[name].BorderWidth = 1;
Graph.Series[name].IsVisibleInLegend = false;
// add line segments (first one also as last one)
for (int k = 0; k <= amountOfEdges; k++)
{
double x = centerX + radius * Math.Cos(k * 2 * Math.PI / amountOfEdges);
double y = centerY + radius * Math.Sin(k * 2 * Math.PI / amountOfEdges);
Graph.Series[name].Points.AddXY(x, y);
}
}
You can call it for example via
DrawCircle(Graph, 5, 4, 3, 30);
Around 30 points should be enough to get a nice circle instead of a polygon, but depends on the size of your chart.

Rotate Point around pivot Point repeatedly

For a while now I've been using the following function to rotate a series of Points around a pivot point in various programs of mine.
private Point RotatePoint(Point point, Point pivot, double radians)
{
var cosTheta = Math.Cos(radians);
var sinTheta = Math.Sin(radians);
var x = (cosTheta * (point.X - pivot.X) - sinTheta * (point.Y - pivot.Y) + pivot.X);
var y = (sinTheta * (point.X - pivot.X) + cosTheta * (point.Y - pivot.Y) + pivot.Y);
return new Point((int)x, (int)y);
}
This has always worked great, until I tried to rotate a shape repeatedly by small amounts. For example, this is what I get from calling it for 45° on a rectangular polygon made up of 4 points:
foreach (var point in points)
Rotate(point, center, Math.PI / 180f * 45);
But this is what I get by calling rotate 45 times for 1°:
for (var i = 0; i < 45; ++i)
foreach (var point in points)
Rotate(point, center, Math.PI / 180f * 1)
As long as I call it only once it's fine, and it also seems like it gets gradually worse the lower the rotation degree is. Is there some flaw in the function, or am I misunderstanding something fundamental about what this function does?
How could I rotate repeatedly by small amounts? I could save the base points and use them to update the current points whenever the rotation changes, but is that the only way?
Your Point position measure is off because of the integer rounding generated by the RotatePoint() method.
A simple correction in the method returned value, using float coordinates, will produce the correct measure:
To test it, create a Timer and register its Tick event as RotateTimerTick():
Added a rotation spin increment (see the rotationSpin Field) to emphasize the motion effect.
PointF pivotPoint = new PointF(100F, 100F);
PointF rotatingPoint = new PointF(50F, 100F);
double rotationSpin = 0D;
private PointF RotatePoint(PointF point, PointF pivot, double radians)
{
var cosTheta = Math.Cos(radians);
var sinTheta = Math.Sin(radians);
var x = (cosTheta * (point.X - pivot.X) - sinTheta * (point.Y - pivot.Y) + pivot.X);
var y = (sinTheta * (point.X - pivot.X) + cosTheta * (point.Y - pivot.Y) + pivot.Y);
return new PointF((float)x, (float)y);
}
private void RotateTimerTick(object sender, EventArgs e)
{
rotationSpin += .5;
if (rotationSpin > 90) rotationSpin = 0;
rotatingPoint = RotatePoint(rotatingPoint, pivotPoint, (Math.PI / 180f) * rotationSpin);
Panel1.Invalidate(new Rectangle(new Point(50,50), new Size(110, 110)));
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.White, new RectangleF(100, 100, 8, 8));
e.Graphics.FillEllipse(Brushes.Yellow, new RectangleF(rotatingPoint, new SizeF(8, 8)));
}
This is the result using float values:
And this is what happens using integer values:
If you want you can use the Media3D to only deal with matrix and simplify the coding. Something as simple as this will work.
public Point3D Rotate(Point3D point, Point3D rotationCenter, Vector3D rotation, double degree)
{
// create empty matrix
var matrix = new Matrix3D();
// translate matrix to rotation point
matrix.Translate(rotationCenter - new Point3D());
// rotate it the way we need
matrix.Rotate(new Quaternion(rotation, degree));
// apply the matrix to our point
point = matrix.Transform(point);
return point;
}
Then you simply call the method and specify the rotation. Lets say you work with 2D (like in your example) and lets assume we work with XY plane so the rotation is in Z. You can do something like :
var rotationPoint = new Point3D(0, 0, 0);
var currentPoint = new Point3D(10, 0, 0);
// rotate the current point around the rotation point in Z by 45 degree
var newPoint = Rotate(currentPoint, rotation, new Vector3D(0, 0, 1), 45d);

Drawing Parabola using DrawLine C#

Good evening, I know on the web there are similar questions and a few tutorials about it, but I'd like you to check my code and correct it. I mean, I'd like to know what's wrong with my project.
I have to draw a parabola graph given its equation on my main panel.
I also must include two buttons, zoom in and zoom out, which are used to reduce and enlarge the "view" panel's view (and so the parabola).
I was recommended to use a scale var.
This is my code:
note: x0, y0 are panel_main x center, y center.
I have x, y that are used to determine x,y from the equation.
xpc, ypc are converted for the window scale (so are pixels).
xmin, xmax are the extreme values that, with a certain scale, stay on the panel
I hope you can give me a hint, thanks a lot!
public void DisegnaParabola()
{
Graphics gs = panel_main.CreateGraphics();
pen.Color = Color.Black;
scale = (x0*2) / zoom; //Pixels equivalent to 1x or 1y
n_punti = (x0*2) / scale; //Number of x math points that are visible in window
xmin = -(n_punti / 2);
xmax = n_punti / 2;
precision = 1 / scale; //Increment of x to have 1px
if (asse_parabola.SelectedIndex == 0) //if Y axis
{
for (double i = xmin + precision; i < xmax; i += precision)
{
rifx = i - precision; //Old points
rifxpc = rifx * scale;
rify = (a * Math.Pow(rifx, 2)) + b * rifx + c;
rifypc = y0 - (rify * scale);
x = i; //New points
y = (a * Math.Pow(x, 2)) + b * x + c;
ypc = y0 - (y * scale);
gs.DrawLine(pen, (float)rifxpc, (float)rifypc, (float)xpc, (float)ypc);
}
}
else
{
scale = (y0*2) / zoom; //Pixels for 1y
n_punti = (y0*2) / scale; //Numbers of y in the window
ymin = -(n_punti / 2);
ymax = n_punti / 2;
for(double i=ymin+precision; i<ymax; i+=precision)
{
rify = y - precision;
rifypc = (y0*2) - rify * scale;
rifx = (a * Math.Pow(rify, 2)) + b * rify + c;
rifxpc = x0 + (rifx * scale);
y = i;
x = (a * Math.Pow(y, 2)) + b * y + c;
xpc = x0 + (x * scale);
gs.DrawLine(pen, (float)rifypc, (float)rifxpc, (float)ypc, (float)xpc);
}
}
lbl_canc.Visible = true;
}
Your question actually consists of several tasks and as usual the key is to take and break those apart..
One issue is getting the data, I will leave the details to you but show how to sparate it from the rest.
The next issue is to scale the data. I'll show you how to avoid this one altogether and scale the drawing tool instead.
And the third one is to draw them to a display surface. As you'll see this is really simple once the other issues are taken care of.
Let's start with the most important step: Collecting the data. You try to create and scale and draw them all in the same piece of code. This has many disadvantages..
Let's first collect the data in a suitable structure:
List<PointF> points = new List<PointF>();
List<T> is the collection of choice most of the time; certainly much nicer than arrays! In some method you should fill that list with your data, calculated from some formula.
Here is an example:
List<PointF> getPoints(float start, float end, int count, float ymax)
{
List<PointF> points = new List<PointF>();
float deltaX = (end - start) / count;
for (int i = 0; i < count; i++)
{
float x = i * deltaX;
// insert your own formula(s) here!
float y = ymax + (float)Math.Sin(x * somefactor) * ymax;
points.Add(new PointF(x, y));
}
return points;
}
Now for the second important part: How to scale the data? This can be done either when creating them; but again, separating the two taks makes them both a lot simpler.
So here is a function that, instead of scaling the data scales the Graphics object we will use to plot them:
void ScaleGraphics(Graphics g, List<PointF> data)
{
float xmax = data.Select(x => x.X).Max();
float ymax = data.Select(x => x.Y).Max();
float xmin = data.Select(x => x.X).Min();
float ymin = data.Select(x => x.Y).Min();
float width = Math.Abs(xmax - xmin);
float height = Math.Abs(ymax - ymin);
var vr = g.VisibleClipBounds;
g.ScaleTransform(vr.Width / width, vr.Height / height);
}
This method makes sure that all the data in our list will fit into the drawing surface. If you want to restrict them to a different size you can pass it in and change the code accordingly..
Finally we need to do the actual drawing. We do that where we should, that is in the Paint event of our drawing surface control..:
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (points.Count < 2) return; // no lines to draw, yet
ScaleGraphics(e.Graphics, points);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using ( Pen pen = new Pen(Color.Blue )
{ Width = 1.5f , LineJoin = LineJoin.Round, MiterLimit = 1f} )
e.Graphics.DrawLines(pen, points.ToArray());
}

Matrix (?) to Rectangle and vise versa

I'm currently working with files that define rectangular shapes in a way that I'm unfamiliar with. Someone told me it might be a matrix, but knowing that doesn't particularly help me with my problem, converting it to points and back.
For example, I have these values:
0.95, -0.28, -0.28, -0.95, 250.0234, 172.1973, -589.0131, 604.8696
These 8 floats make up a rect with the following coordinates, the center being 0:
{X=-778,Y=838}
{X=-303,Y=698}
{X=-399,Y=372}
{X=-874,Y=512}
To get these points I have used the following function, that someone else wrote for use with these files:
static Point[] GetPoints(double d01, double d02, double d03, double d04, double l01, double l02, double p01, double p02)
{
var points = new Point[4];
double a00 = d01 * l01;
double a01 = d02 * l01;
double a02 = d03 * l02;
double a03 = d04 * l02;
double sx1 = p01 - a00 - a02; if (sx1 < p01) sx1 = Math.Ceiling(sx1);
double sy1 = p02 - a01 - a03; if (sy1 < p02) sy1 = Math.Ceiling(sy1);
double sx2 = p01 + a00 - a02; if (sx2 < p01) sx2 = Math.Ceiling(sx2);
double sy2 = p02 + a01 - a03; if (sy2 < p02) sy2 = Math.Ceiling(sy2);
double sx3 = p01 + a00 + a02; if (sx3 < p01) sx3 = Math.Ceiling(sx3);
double sy3 = p02 + a01 + a03; if (sy3 < p02) sy3 = Math.Ceiling(sy3);
double sx4 = p01 - a00 + a02; if (sx4 < p01) sx4 = Math.Ceiling(sx4);
double sy4 = p02 - a01 + a03; if (sy4 < p02) sy4 = Math.Ceiling(sy4);
if (a02 * a01 > a03 * a00)
{
points[0] = new Point((int)sx1, (int)sy1);
points[1] = new Point((int)sx2, (int)sy2);
points[2] = new Point((int)sx3, (int)sy3);
points[3] = new Point((int)sx4, (int)sy4);
}
else
{
points[0] = new Point((int)sx1, (int)sy1);
points[3] = new Point((int)sx2, (int)sy2);
points[2] = new Point((int)sx3, (int)sy3);
points[1] = new Point((int)sx4, (int)sy4);
}
return points;
}
What I'm looking for now is possibly an explanation what these numbers even are, is this something common that I can read up on or is it custom, and a way to convert the points back to the 8 float values.
Can someone help me with this? Without knowing what this even is, it's really hard to find anything :/
p01 and p02 are center coordinates (CenterX and CenterY)
d01-d04 represent cosine and sine of rotation angle (or dx,dy components of unit-length direction vector)
l01 and l02 are half-width and half-height of initial axis-aligned rectangle.
sxi and syi are X and Y-coordinates of ith vertice.
These coordinates are rounded toward the center coordinate.
Finally the vertices are numerated in certain order - either clockwise or counterclockwise (I did not checked)
To reconstruct initial parameters from vertice set:
You can determine center point as middle of two opposite vertices
p01 = (points[0].X + points[2].X) / 2
p02 = (points[0].Y + points[2].Y) / 2
and rotation angle
Angle = atan2(points[1].Y - points[0].Y, points[1].X - points[0].X)
Then correct Angle by +-Pi/2 or +-Pi to the smallest magnitude value (for example, 3/4*Pi=> Pi/4)
Note that initial parameter set angle may differ by +-Pi/2
Find
d01 = Cos(Angle) etc
Note that angle may differ by +-Pi/2 or +-Pi from initial parameter set.
l01 = Abs((points[1].X - points[0].X) * 0.5 / Cos(Angle))
l02 = Abs((points[2].Y - points[1].Y) * 0.5 / Cos(Angle))
Note that l01, l02 may be interchanged due to ambiguity of angle value.

Categories