Waypoint generator - Calculating round trip based on rules - c#

I'm trying to work on a piece of code to return a list of waypoints which represent a flight path for a AI raid to fly. Unfortunately maths and trig were never my strong point so I've been googling and not getting very far very fast.
I have the following code
public static Point2d CalculateCoordinate(double Angle, double Distance)
{
Point2d coord = new Point2d(Distance * Math.Cos(Angle), Distance * Math.Sin(Angle));
return coord;
}
public static double GetAngle(Point2d coord1, Point2d coord2, Point2d coord3, Point2d coord4)
{
double result = 0.0;
result = (Math.Atan2(coord2.y - coord1.y, coord2.x - coord1.x) - Math.Atan2(coord4.y - coord3.y, coord4.x - coord3.x)) * (180 / Math.PI);
if(result<0)
{
result = result + 360;
}
return result;
}
public static List<Point2d> GenerateWaypoints(Point2d Startpoint, Point2d Target)
{
List<Point2d> waypoints = new List<Point2d>();
double tempDistance = 0.0;
double tempAngle = 30.0 * ( Math.PI/180);
bool ok = false;
double distancemodifier = 0.8;
waypoints.Add(Startpoint);
tempDistance = Startpoint.distance(ref Target)*distancemodifier;
Console.WriteLine(tempDistance.ToString());
Console.WriteLine(ReturnStringFromP2D(Startpoint));
Console.WriteLine(ReturnStringFromP2D(Target));
Point2d tempPoint2d = CalculateCoordinate(tempAngle, tempDistance);
Console.WriteLine(ReturnStringFromP2D(tempPoint2d));
while (!ok)
{
tempPoint2d = CalculateCoordinate(tempAngle, tempDistance);
if (GetAngle(Startpoint, tempPoint2d, tempPoint2d, Target) > 30.00 || tempPoint2d.distance(ref Target) < 10000)
{
distancemodifier = distancemodifier - 0.05;
tempDistance = Startpoint.distance(ref Target) * distancemodifier;
}
}
ok = false;
waypoints.Add(tempPoint2d);
waypoints.Add(Target);
tempAngle = tempAngle + 30.0;
tempDistance = Target.distance(ref Startpoint) * distancemodifier;
tempPoint2d = CalculateCoordinate(tempAngle, tempDistance);
while (!ok)
{
tempPoint2d = CalculateCoordinate(tempAngle, tempDistance);
if (GetAngle(Target, tempPoint2d, tempPoint2d, Startpoint) > 30.00)
{
distancemodifier = distancemodifier - 0.05;
tempDistance = Target.distance(ref Startpoint) * distancemodifier;
}
}
waypoints.Add(tempPoint2d);
return waypoints;
}
The issue is that I'm not getting anything out of it that makes sense. The rules I want to apply are that no turn in the flightpath should be larger than 30 degrees. I have the start position, the target position. I want to make sure that the first turn is further than 10km from the target position.
EDIT: OK, here's what I am actually trying to achieve.
Using a 1km grid.
Taking Start point to be 0, 10 and target point to be 10,40, I want to generate a set of waypoints that go from start, to an intermediate step between start and target which should generate a no more than 30 degree angle (but not be a straight line) between the two legs and no closer to target than 5km. Then to target, then back to the start point, with the rule that no angle should be greater than 30 degrees. Therefore we might get the following (worked out roughly on paper so there might be an error but it should be indicative of what I'm trying to achieve.)
Start 0,10
Intermediate 22,20
Target 10,40
Intermediate -10,48
Intermediate -25,40
Intermediate -35,28
Intermediate -35,14
Intermediate -20,5
Start 0,10
It seems to me that I need three methods, one to return the bearing between two points (used at the start to get the initial direction from start to target - and then modify away from a straight line), the second to calculate the angle between two lines (used on every step to check the previous angle doesn't exceed 30 degrees), and the third to generate a coordinate given a bearing and a distance from the current location.
From what I understand, my GetAngle() above is correct, and cut down to this will work for bearings between two points??
public static double GetBearing(Point2d coord1, Point2d coord2) {
double result = 0.0;
result = Math.Atan2(coord2.y - coord1.y, coord2.x - coord1.x) * (180 / Math.PI);
if(result<0)
{
result = result + 360;
}
return result;
}

Related

Given two segments (AB and BC) of known length and an angle between, how to calculate coordinates of C point, if coordinates of A and B are known?

I'm creating a C# code which draws a path. The problem is I don't have coordinates of the path vertices. Instead, I know length of each segment and angle between adjacent segments.
Assuming the first point of the path has coordinates (0;0) I want to draw the path calculating every vertex from given segment length and angle. I'm not good in trigonometry, but I hope it is possible.
I try to cycle through the collection of segments to calculate next point coordinate at each step. So at any step I have the following data:
Given first segment AB with length L1, next segment BC with length L2, an angle ABC between segments AB and BC. Coordinates of points A and B are know, because are evaluated on the previous step.
If it is possible, how to calculate coordinates of the point C from the given data?
This is an example of a collection of segments:
public ObservableCollection<SequenceStep> Sequence { get; set; }
where:
public class SequenceStep
{
public double Length { get; set; }
public double Angle { get; set; }
}
I cycle through the sequence like this:
for (var i = 1; i < Sequence.Count; i++)
{
var sequenceStep = Sequence[i];
var angleInRadians = Math.PI * sequenceStep.Angle / 180.0;
// Calculate next point coordinates from (0,0)
var x = Math.Cos(angleInRadians) * sequenceStep.Length;
var y = Math.Sin(angleInRadians) * sequenceStep.Length;
}
// I start from segment[1], because segment[0] has points (0,0) and (segment[0].Length, 0).
But evaluated coordinates are only for the angle between point and axis X. I think I need to rotate those x,y coordinates, to correspond with orientation of the segment BC. But I always get wrong coordinates.
I would appreciate any help, a C# method or a set formulas.
You can accumulate the angle and calculate the positions like you already do:
double angle = 0.0;
double x = Seqence[0].Length;
double y = 0.0;
for (var i = 1; i < Sequence.Count; i++)
{
var sequenceStep = Sequence[i];
var angleInRadians = Math.PI * sequenceStep.Angle / 180.0;
// update the current angle
angle += Math.PI - angleInRadians;
// ^-- might also be -= depending on your definition of orientation
// Calculate next point coordinates
x += Math.Cos(angle) * sequenceStep.Length;
y += Math.Sin(angle) * sequenceStep.Length;
// Now use (x, y)
}

Finding the true anomaly from state vectors

I'm attempting to convert from state vectors (position and speed) into Kepler elements, however I'm running into problems where a negative velocity or position will give me wrong results when trying to calculate true anomaly.
Here are the different ways I'm trying to calculate the True Anomaly:
/// <summary>
/// https://en.wikipedia.org/wiki/True_anomaly#From_state_vectors
/// </summary>
public static double TrueAnomaly(Vector4 eccentVector, Vector4 position, Vector4 velocity)
{
var dotEccPos = Vector4.Dot(eccentVector, position);
var talen = eccentVector.Length() * position.Length();
talen = dotEccPos / talen;
talen = GMath.Clamp(talen, -1, 1);
var trueAnomoly = Math.Acos(talen);
if (Vector4.Dot(position, velocity) < 0)
trueAnomoly = Math.PI * 2 - trueAnomoly;
return trueAnomoly;
}
//sgp = standard gravitational parameter
public static double TrueAnomaly(double sgp, Vector4 position, Vector4 velocity)
{
var H = Vector4.Cross(position, velocity).Length();
var R = position.Length();
var q = Vector4.Dot(position, velocity); // dot product of r*v
var TAx = H * H / (R * sgp) - 1;
var TAy = H * q / (R * sgp);
var TA = Math.Atan2(TAy, TAx);
return TA;
}
public static double TrueAnomalyFromEccentricAnomaly(double eccentricity, double eccentricAnomaly)
{
var x = Math.Sqrt(1 - Math.Pow(eccentricity, 2)) * Math.Sin(eccentricAnomaly);
var y = Math.Cos(eccentricAnomaly) - eccentricity;
return Math.Atan2(x, y);
}
public static double TrueAnomalyFromEccentricAnomaly2(double eccentricity, double eccentricAnomaly)
{
var x = Math.Cos(eccentricAnomaly) - eccentricity;
var y = 1 - eccentricity * Math.Cos(eccentricAnomaly);
return Math.Acos(x / y);
}
Edit: another way of doing it which Spectre pointed out:
public static double TrueAnomaly(Vector4 position, double loP)
{
return Math.Atan2(position.Y, position.X) - loP;
}
Positions are all relative to the parent body.
These functions all agree if position.x, position.y and velocity.y are all positive.
How do I fix these so that I get a consistent results when position and velocity are negitive?
Just to clarify: My angles appear to be sort of correct, just pointing in the wrong quadrant depending on the position and or velocity vectors.
Yeah so I was wrong, the above all do return the correct values after all.
So I found an edge case where most of the above calculations fail.
Given position and velocity:
pos = new Vector4() { X = -0.208994076275941, Y = 0.955838328099748 };
vel = new Vector4() { X = -2.1678187689294E-07, Y = -7.93096769486992E-08 };
I get some odd results, ie ~ -31.1 degrees, when I think it should return ` 31.1 (non negative). one of them returns ~ 328.8.
However testing with this position and velocity the results apear to be ok:
pos = new Vector4() { X = -0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(-25) };
See my answer for extra code on how I'm testing and the math I'm using for some of the other variables.
I'm going around in circles on this one. this is a result of a bug in my existing code that shows up under some conditions but not others.
I guess the real question now is WHY am I getting different results with position/velocity above that don't match to my expectations or each other?
Assuming 2D case... I am doing this differently:
compute radius of semi axises and rotation
so you need to remember whole orbit and find 2 most distant points on it that is major axis a. The minor axis b usually is 90 deg from major axis but to be sure just fins 2 perpendicularly most distant points on your orbit to major axis. So now you got both semi axises. The initial rotation is computed from the major axis by atan2.
compute true anomaly E
so if center is x0,y0 (intersection of a,b or center point of both) initial rotation is ang0 (angle of a) and your point on orbit is x,y then:
E = atan2(y-y0,x-x0) - ang0
However in order to match Newton/D'Alembert physics to Kepler orbital parameters you need to boost the integration precision like I did here:
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
see the [Edit3] Improving Newton D'ALembert integration precision even more in there.
For more info and equations see:
Solving Kepler's equation
[Edit1] so you want to compute V I see it like this:
As you got your coordinates relative to parent you can assume they are already in focal point centered so no need for x0,y0 anymore. Of coarse if you want high precision and have more than 2 bodies (focal mass + object + proximity object(s) like moons) then the parent mass will no longer be in focal point of orbit but close to it ... and to remedy you need to use real focal point position so x0,y0 again... So how to do it:
compute center point (cx,cy) and a,b semi axises
so its the same as in previous text.
compute focal point (x0,y0) in orbit axis aligned coordinates
simple:
x0 = cx + sqrt( a^2 + b^2 );
y0 = cy;
initial angle ang0 of a
let xa,ya be the intersection of orbit and major axis a on the side with bigger speeds (near parent object focus). Then:
ang0 = atan2( ya-cy , xa-cx );
and finally the V fore any of yours x,y
V = atan2( y-y0 , x-x0 ) - ang0;
Ok so on further testing it appears my original calcs do all return the correct values, however when I was looking at the outputs I was not taking the LoP into account and basically not recognizing that 180 is essentially the same angle as -180.
(I was also looking at the output in radians and just didn't see what should have been obvious)
Long story short, I have a bug I thought was in this area of the code and got lost in the weeds.
Seems I was wrong above. see OP for edge case.
Here's some code I used to test these,
I used variations of the following inputs:
pos = new Vector4() { X = 0.25, Y = 0.25 };
vel = new Vector4() { X = Distance.KmToAU(-25), Y = Distance.KmToAU(25) };
And tested them with the following
double parentMass = 1.989e30;
double objMass = 2.2e+15;
double sgp = GameConstants.Science.GravitationalConstant * (parentMass + objMass) / 3.347928976e33;
Vector4 ev = OrbitMath.EccentricityVector(sgp, pos, vel);
double e = ev.Length();
double specificOrbitalEnergy = Math.Pow(vel.Length(), 2) * 0.5 - sgp / pos.Length();
double a = -sgp / (2 * specificOrbitalEnergy);
double ae = e * a;
double aop = Math.Atan2(ev.Y, ev.X);
double eccentricAnomaly = OrbitMath.GetEccentricAnomalyFromStateVectors(pos, a, ae, aop);
double aopD = Angle.ToDegrees(aop);
double directAngle = Math.Atan2(pos.Y, pos.X);
var θ1 = OrbitMath.TrueAnomaly(sgp, pos, vel);
var θ2 = OrbitMath.TrueAnomaly(ev, pos, vel);
var θ3 = OrbitMath.TrueAnomalyFromEccentricAnomaly(e, eccentricAnomaly);
var θ4 = OrbitMath.TrueAnomalyFromEccentricAnomaly2(e, eccentricAnomaly);
var θ5 = OrbitMath.TrueAnomaly(pos, aop);
double angleΔ = 0.0000001; //this is the "acceptable" amount of error, really only the TrueAnomalyFromEccentricAnomaly() calcs needed this.
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ1), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ2), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ3), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ4), angleΔ);
Assert.AreEqual(0, Angle.DifferenceBetweenRadians(directAngle, aop - θ5), angleΔ);
and the following to compare the angles:
public static double DifferenceBetweenRadians(double a1, double a2)
{
return Math.PI - Math.Abs(Math.Abs(a1 - a2) - Math.PI);
}
And eccentricity Vector found thus:
public static Vector4 EccentricityVector(double sgp, Vector4 position, Vector4 velocity)
{
Vector4 angularMomentum = Vector4.Cross(position, velocity);
Vector4 foo1 = Vector4.Cross(velocity, angularMomentum) / sgp;
var foo2 = position / position.Length();
return foo1 - foo2;
}
And EccentricAnomaly:
public static double GetEccentricAnomalyFromStateVectors(Vector4 position, double a, double linierEccentricity, double aop)
{
var x = (position.X * Math.Cos(-aop)) - (position.Y * Math.Sin(-aop));
x = linierEccentricity + x;
double foo = GMath.Clamp(x / a, -1, 1); //because sometimes we were getting a floating point error that resulted in numbers infinatly smaller than -1
return Math.Acos(foo);
}
Thanks to Futurogogist and Spektre for their help.
I am assuming you are working in two dimensions?
Two dimensional vectors of position p and velocity v. The constant K is the the product of the gravitational constant and the mass of the gravity generating body. Calculate the eccentricity vector
eccVector = (dot(v, v)*p - dot(v, p)*v) / K - p / sqrt(dot(p, p));
eccentricity = sqrt(dot(eccVector, eccVector));
eccVector = eccVector / eccentricity;
b = { - eccVector.y, eccVector.x}; //unit vector perpendicular to eccVector
r = sqrt(dot(p, p));
cos_TA = dot(p, eccVector) / r; \\ cosine of true anomaly
sin_TA = dot(p, b) / r; \\ sine of true anomaly
if (sin_TA >= 0) {
trueAnomaly = arccos(cos_TA);
}
else if (sin_TA < 0){
trueAnomaly = 2*pi - arccos(cos_TA);
}

How to choose random float in a square weighted against arbitrary zones?

I have a square, which goes from -1 to 1 in x and y.
Choosing a random point in this square is pretty easy:
Random r = new Random();
float x = (float)Math.Round(r.NextDouble() * 2 - 1, 4);
float y = (float)Math.Round(r.NextDouble() * 2 - 1, 4);
This gives me any point, with equal probability, in my square.
It woud also be pretty easy to just remove a section of the square from the possibilities
Random r = new Random();
float x = (float)Math.Round(r.NextDouble() * 1.5 - 1, 4);
float y = (float)Math.Round(r.NextDouble() * 2 - 1, 4);
But what I'm really struggling to do, is to weight the random towards a certain zone. Specifically, I would like the section highlighted here to be more likely, and everything else (except the red section, which is still off-limits) should have a probability lower depending on the distance from the highligthed line. The furthest point should have 0 chance, and the rest an existing chance which is higher when closer to the line, with points exactly on my line (since I round them to a specific decimal, there are points with are on the line) having the best odds.
Sorry for the ugly pictures. This is the best i could do in paint to show my thoughts.
The "most likely" area is an empty diamond (just the that with the vertices (-1, 0), (0, -0.5), (1, 0), (0, 0.5), with of course the red area override the weighting because it's off limits. The red area is anything with x > 0.5
Does anyone knows how to do this? I'm working in C# but honestly an algorithm in any non-esoteric language would do the trick. I'm completely lost as to how to proceed.
A commenter noted that adding the off-limits zone to the algorithm is an added difficulty with no real use.
You can assume that I'll take care of the off-limit section by myself after running the weighting algorithm. Since it's just 25% of the area, most of the times it wouldn't even make a difference performance-wise if I just made this:
while (x > 0.5)
{
runAlgorithmAgain();
}
So you can safely ignore that part for answers.
Ok, here my thoughts on this matter. I would like to propose algorithm which, with some rejections, might solve your problem. Note, due to need of acceptance-rejection, it might be slower than you expected it to be.
We sample in single quadrant (say, lower left one), then use reflection to put point into any other quadrant, and then reject red zone points.
Basically, sampling in quadrant is two-step process. First, we sample first position on the border line. As soon as we got position on the line, we sample from distribution which is bell-like shape (Gaussian or Laplace for example), and move point in the orthogonal to the border line direction.
Code compiles, but completely untested, so please check everything startign with the numbers
using System;
namespace diamond
{
class Program
{
public const double SQRT_5 = 2.2360679774997896964091736687313;
public static double gaussian((double mu, double sigma) N, Random rng) {
var phi = 2.0 * Math.PI * rng.NextDouble();
var r = Math.Sqrt( -2.0 * Math.Log(1.0 - rng.NextDouble()) );
return N.mu + N.sigma * r * Math.Sin(phi);
}
public static double laplace((double mu, double sigma) L, Random rng) {
var v = - L.sigma * Math.Log(1.0 - rng.NextDouble());
return L.mu + ((rng.NextDouble() < 0.5) ? v : -v );
}
public static double sample_length(double lmax, Random rng) {
return lmax * rng.NextDouble();
}
public static (double, double) move_point((double x, double y) pos, (double wx, double wy) dir, double l) {
return (pos.x + dir.wx * l, pos.y + dir.wy * l);
}
public static (double, double) sample_in_quadrant((double x0, double y0) pos, (double wx, double wy) dir, double lmax, double sigma, Random rng) {
while (true) {
var l = sample_length(lmax, rng);
(double x, double y) = move_point(pos, dir, l);
var dort = (dir.wy, -dir.wx); // orthogonal to the line direction
var s = gaussian((0.0, sigma), rng); // could be laplace instead of gaussian
(x, y) = move_point((x, y), dort, s);
if (x >= -1.0 && x <= 0.0 && y >= 0.0 && y <= 1.0) // acceptance/rejection
return (x, y);
}
}
public static (double, double) sample_in_plane((double x, double y) pos, (double wx, double wy) dir, double lmax, double sigma, Random rng) {
(double x, double y) = sample_in_quadrant(pos, dir, lmax, sigma, rng);
if (rng.NextDouble() < 0.25)
return (x, y);
if (rng.NextDouble() < 0.5) // reflection over X
return (x, -y);
if (rng.NextDouble() < 0.75) // reflection over Y
return (-x, y);
return (-x, -y); // reflection over X&Y
}
static void Main(string[] args) {
var rng = new Random(32345);
var L = 0.5 * SQRT_5 + 0.5 / SQRT_5; // sampling length, BIGGER THAN JUST A SEGMENT IN THE QUADRANT
(double x0, double y0) pos = (-1.0, 0.0); // initial position
(double wx, double wy) dir = (2.0 / SQRT_5, 1.0 / SQRT_5); // directional cosines, wx*wx + wy*wy = 1
double sigma = 0.2; // that's a value to play with
// last rejection stage
(double x, double y) pt;
while(true) {
pt = sample_in_plane(pos, dir, L, sigma, rng);
if (pt.x < 0.5) // reject points in the red area, accept otherwise
break;
}
Console.WriteLine(String.Format("{0} {1}", pt.x, pt.y));
}
}
}

Converting from distance to GeoCoordinate in WP8

I want to find the GeoCoordinate of the place that is 4000km right to my position, name it newLocation.
I need that in order to convert the result into ViewportPoint like this:
System.Windows.Point p1 =
mapControl.ConvertGeoCoordinateToViewportPoint(newLocation);
and assuming my location is defined as:
System.Windows.Point p2 =
radiusMap.ConvertGeoCoordinateToViewportPoint(myLocation);
then I can simply add a circle centered on my location, and the border passes from newLocation that I need to calculate, like this:
double radius = getDistance(p1, p2);
Ellipse circle = new Ellipse();
circle.Width = radius * 2;
circle.Height = radius * 2;
circle.Opacity = 0.4;
circle.Fill = new SolidColorBrush(Colors.Green);
MapOverlay mapOverLay = new MapOverlay();
mapOverLay.PositionOrigin = new System.Windows.Point(0.5, 0.5);
mapOverLay.Content = circle;
mapOverLay.GeoCoordinate = myLocation;
MyLayer.Add(mapOverLay);
radiusMap.Layers.Add(MyLayer);
So, calculating newLocation is all what I need.
Notes:
I tried this solution but there is a limitation.
We can do the following in visual studio:
GeoCoordinate location1 = ..., location2 = ...;
double distance = location1.GetDistanceTo(location2);
I wonder if the inverse is already implemented.
UPDATE: I found the implementation of GetDistanceTo(...) and here it is:
double d = this.Latitude * 0.017453292519943295;
double num3 = this.Longitude * 0.017453292519943295;
double num4 = other.Latitude * 0.017453292519943295;
double num5 = other.Longitude * 0.017453292519943295;
double num6 = num5 - num3;
double num7 = num4 - d;
double num8 = Math.Pow(Math.Sin(num7 / 2.0), 2.0) + ((Math.Cos(d) * Math.Cos(num4)) * Math.Pow(Math.Sin(num6 / 2.0), 2.0));
double num9 = 2.0 * Math.Atan2(Math.Sqrt(num8), Math.Sqrt(1.0 - num8));
return (6376500.0 * num9);
I wonder if getting the inverse is possible or not. i.e. giving it the geocoordinates of me, and the distance from me, so that it returns the geocoordinates of the new location.
There are of course an infinite number of points (around a circle) which are 400km from an initial starting point, you will need to define both the distance and the bearing in order to reduce this to a single point.
If you can assume the Earth to be a sphere, you can travel around a great circle as shown
φ2 = asin( sin(φ1)*cos(d/R) + cos(φ1)*sin(d/R)*cos(θ) )
λ2 = λ1 + atan2( sin(θ)*sin(d/R)*cos(φ1), cos(d/R)−sin(φ1)*sin(φ2) )
where
φ is latitude
λ is longitude
θ is the bearing (in radians, clockwise from north)
d is the distance traveled
R is the earth’s radius 6 378.1 kilometers
d/R is the angular distance, in radians
So plug in your values to calculate the new location φ2, λ2
If by "right to my position" you mean due East, θ will be 90 degrees = Π / 2
Of course there is a hack you could use to find a point due East. One degree of longitude at the Equator = 110.57 km . Hence you could calculate an upper bound and then interate down to a reasonable threshold.
Take your start point - for 400km, add 3.61 degrees.
Check the distance with the distanceTo() method
If it is too big, chop off a part of a degree.
Repeat 2)+ 3) until within an acceptable threshold.

Calculate coordinates of a regular polygon's vertices

I am writing a program in which I need to draw polygons of an arbitrary number of sides, each one being translated by a given formula which changes dynamically. There is some rather interesting mathematics involved but I am stuck on this probelm.
How can I calculate the coordinates of the vertices of a regular polygon (one in which all angles are equal), given only the number of sides, and ideally (but not neccessarily) having the origin at the centre?
For example: a hexagon might have the following points (all are floats):
( 1.5 , 0.5 *Math.Sqrt(3) )
( 0 , 1 *Math.Sqrt(3) )
(-1.5 , 0.5 *Math.Sqrt(3) )
(-1.5 , -0.5 *Math.Sqrt(3) )
( 0 , -1 *Math.Sqrt(3) )
( 1.5 , -0.5 *Math.Sqrt(3) )
My method looks like this:
void InitPolygonVertexCoords(RegularPolygon poly)
and the coordinates need to be added to this (or something similar, like a list):
Point[] _polygonVertexPoints;
I'm interested mainly in the algorithm here but examples in C# would be useful. I don't even know where to start. How should I implement it? Is it even possible?!
Thank you.
for (i = 0; i < n; i++) {
printf("%f %f\n",r * Math.cos(2 * Math.PI * i / n), r * Math.sin(2 * Math.PI * i / n));
}
where r is the radius of the circumsribing circle. Sorry for the wrong language No Habla C#.
Basically the angle between any two vertices is 2 pi / n and all the vertices are at distance r from the origin.
EDIT:
If you want to have the center somewher other than the origin, say at (x,y)
for (i = 0; i < n; i++) {
printf("%f %f\n",x + r * Math.cos(2 * Math.PI * i / n), y + r * Math.sin(2 * Math.PI * i / n));
}
The number of points equals the number of sides.
The angle you need is angle = 2 * pi / numPoints.
Then starting vertically above the origin with the size of the polygon being given by radius:
for (int i = 0; i < numPoints; i++)
{
x = centreX + radius * sin(i * angle);
y = centreY + radius * cos(i * angle);
}
If your centre is the origin then simply ignore the centreX and centreY terms as they'll be 0,0.
Swapping the cos and sin over will point the first point horizontally to the right of the origin.
Sorry, I dont have a full solution at hand right now, but you should try looking for 2D-Rendering of Circles. All classic implementations of circle(x,y,r) use a polygon like you described for drawing (but with 50+ sides).
Say the distance of the vertices to the origin is 1. And say (1, 0) is always a coordinate of the polygon.
Given the number of vertices (say n), the rotation angle required to position the (1, 0) to the next coordinate would be (360/n).
The computation required here is to rotate the coordinates. Here is what it is; Rotation Matrix.
Say theta = 360/n;
[cos(theta) -sin(theta)]
[sin(theta) cos(theta)]
would be your rotation matrix.
If you know linear algebra you already know what i mean. If dont just have a look at Matrix Multiplication
One possible implementation to generate a set of coordinates for regular polygon is to:
Define polygon center, radius and first vertex1. Rotate the vertex n-times2 at an angle of: 360/n.
In this implementation I use a vector to store the generated coordinates and a recursive function to generate them:
void generateRegularPolygon(vector<Point>& v, Point& center, int sidesNumber, int radius){
// converted to radians
double angRads = 2 * PI / double(sidesNumber);
// first vertex
Point initial(center.x, center.y - radius);
rotateCoordinate(v, center, initial, angRads, sidesNumber);
}
where:
void rotateCoordinate(vector<Point>& v, Point& axisOfRotation, Point& initial, double angRads, int numberOfRotations){
// base case: number of transformations < 0
if(numberOfRotations <= 0) return;
else{
// apply rotation to: initial, around pivot point: axisOfRotation
double x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
double y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
// store the result
v.push_back(Point(x, y));
rotateCoordinate(v, axisOfRotation, Point(x,y), angRads, --numberOfRotations);
}
}
Note:
Point is a simple class to wrap the coordinate into single data structure:
class Point{
public:
Point(): x(0), y(0){ }
Point(int xx, int yy): x(xx), y(yy) { }
private:
int x;
int y;
};
1 in terms of (relative to) the center, radius. In my case the first vertex is translated from the centre up horizontally by the radius lenght.
2 n-regular polygon has n vertices.
The simple method is:
Let's take N-gone(number of sides) and length of side L. The angle will be T = 360/N.
Let's say one vertices is located on origin.
* First vertex = (0,0)
* Second vertex = (LcosT,LsinT)
* Third vertex = (LcosT+Lcos2T, LsinT+Lsin2T)
* Fourth vertex = (LcosT+Lcos2T+Lcos3T, LsinT+Lsin2T+Lsin3T)
You can do in for loop
hmm if you test all the versions that are listed here you'll see that the implementation is not good. you can check the distance from the center to each generated point of the polygon with : http://www.movable-type.co.uk/scripts/latlong.html
Now i have searched a lot and i could not find any good implementation for calculating a polyogon using the center and the radius...so i went back to the math book and tried to implement it myself. In the end i came up with this...wich is 100% good:
List<double[]> coordinates = new List<double[]>();
#region create Polygon Coordinates
if (!string.IsNullOrWhiteSpace(bus.Latitude) && !string.IsNullOrWhiteSpace(bus.Longitude) && !string.IsNullOrWhiteSpace(bus.ListingRadius))
{
double lat = DegreeToRadian(Double.Parse(bus.Latitude));
double lon = DegreeToRadian(Double.Parse(bus.Longitude));
double dist = Double.Parse(bus.ListingRadius);
double angle = 36;
for (double i = 0; i <= 360; i += angle)
{
var bearing = DegreeToRadian(i);
var lat2 = Math.Asin(Math.Sin(lat) * Math.Cos(dist / earthRadius) + Math.Cos(lat) * Math.Sin(dist / earthRadius) * Math.Cos(bearing));
var lon2 = lon + Math.Atan2(Math.Sin(bearing) * Math.Sin(dist / earthRadius) * Math.Cos(lat),Math.Cos(dist / earthRadius) - Math.Sin(lat) * Math.Sin(lat2));
coordinates.Add(new double[] { RadianToDegree(lat2), RadianToDegree(lon2) });
}
poly.Coordinates = new[] { coordinates.ToArray() };
}
#endregion
If you test this you'll see that all the points are at the exact distance that you give ( radius ). Also don't forget to declare the earthRadius.
private const double earthRadius = 6371.01;
This calculates the coordinates of a decagon. You see the angle used is 36 degrees. You can split 360 degrees to any number of sides that you want and put the result in the angle variable.
Anyway .. i hope this helps you #rmx!

Categories

Resources