I have been chipping away at trying to draw a line with a mouse that adheres to a grid. I'm using Bresenhams line algorithm.
However I need it to preserve the order, so if the user is drawing to the left it will give me the points in that order.
WorldTile is a class that acts as a node on the grid.
WorldBoard is an array of WorldTiles.
private static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
public static IEnumerable<WorldTile> GetWorldTilesOnLine(int x0, int y0, int x1, int y1)
{
bool steep = Mathf.Abs(y1 - y0) > Mathf.Abs(x1 - x0);
if (steep)
{
Swap<int>(ref x0, ref y0); // find out how this works
Swap<int>(ref x1, ref y1);
}
if (x0 > x1)
{
Swap<int>(ref x0, ref x1);
Swap<int>(ref y0, ref y1);
}
int dx = (x1 - x0);
int dy = Mathf.Abs(y1 - y0);
int error = (dx / 2);
int ystep = (y0 < y1 ? 1 : -1);
int y = y0;
for (int x = x0; x <= x1; x++)
{
yield return worldBoard[(steep ? y : x), (steep ? x : y)];
error = error - dy;
if (error < 0)
{
y += ystep;
error += dx;
}
}
yield break;
}
Here is a version in which I tried getting it to work (it gets stuck in a while loop occasionally, quite hard to pin point why)
public static IEnumerable<WorldTile> GetWorldTilesOnLine(int x0, int y0, int x1, int y1)
{
int dy = (int)(y1-y0);
int dx = (int)(x1-x0);
int xstep = 1;
int ystep = 1;
if (dy < 0) {dy = -dy; xstep = -1;}
else {ystep = 1;}
if (dx < 0) {dx = -dx; ystep = -1;}
else {xstep = 1;}
dy <<= 1;
dx <<= 1;
float fraction = 0;
//Debug.Log (xstep);
if (x0 >= 0 && x0 < worldBoard.GetLength(0) && y0 >= 0 && y0 < worldBoard.GetLength(1))
{
yield return worldBoard[x0, y0];
}
if (dx > dy) {
fraction = dy - (dx >> 1);
while (Mathf.Abs(x0 - x1) > 1) {
if (fraction >= 0) {
y0 += ystep;
fraction -= dx;
}
x0 += xstep;
fraction += dy;
if (x0 >= 0 && x0 < worldBoard.GetLength(0) && y0 >= 0 && y0 < worldBoard.GetLength(1))
{
yield return worldBoard[x0, y0];
}
}
}
else {
fraction = dx - (dy >> 1);
while (Mathf.Abs(y0 - y1) > 1) {
if (fraction >= 0) {
x0 += xstep;
fraction -= dy;
}
y0 += ystep;
fraction += dx;
if (x0 >= 0 && x0 < worldBoard.GetLength(0) && y0 >= 0 && y0 < worldBoard.GetLength(1))
{
yield return worldBoard[x0, y0];
}
}
}
yield break;
}
Thanks
Jim
With a great deal of help from a friend here is the final solution.
WorldBoard is an array of WorldTiles.
WorldTiles are essentially nodes on a grid.
private static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
public static IEnumerable<WorldTile> GetWorldTilesOnLine(int x0, int y0, int x1, int y1)
{
bool steep = Mathf.Abs(y1 - y0) > Mathf.Abs(x1 - x0);
if (steep)
{
Swap<int>(ref x0, ref y0);
Swap<int>(ref x1, ref y1);
}
int dx = Mathf.Abs(x1 - x0);
int dy = Mathf.Abs(y1 - y0);
int error = (dx / 2);
int ystep = (y0 < y1 ? 1 : -1);
int xstep = (x0 < x1 ? 1 : -1);
int y = y0;
for (int x = x0; x != (x1 + xstep); x += xstep)
{
yield return worldBoard[(steep ? y : x), (steep ? x : y)];
error = error - dy;
if (error < 0)
{
y += ystep;
error += dx;
}
}
yield break;
}
I'm trying to implement an N body simulation in C# using either Runge Kutta 4 or Velocity Verlet integration algorithms.
Before I move to a bigger number of particles, I wanted to test the simulation by modeling the earth's orbit around the sun, however, instead of the elliptical orbit, I get a weird spiral for some reason.
I can't figure out the problem since I made a simpler simulation of the solar system using the same algorithms where the sun was fixed in position and everything worked perfectly. The integrators work perfectly because it doesn't matter which one I use, I get the spiral with both.
Any help would be appreciated.
Here's the code:
class NBODY
{
public static double G = 4 * Math.PI * Math.PI;
class Particle
{
public double[] r; // position vector
public double[] v; // velocity vector
public double mass;
//constructor
public Particle() {}
public Particle(double x, double y, double z, double vx, double vy, double vz, double m)
{
this.r = new double[3];
this.v = new double[3];
this.r[0] = x;
this.r[1] = y;
this.r[2] = z;
this.v[0] = vx;
this.v[1] = vy;
this.v[2] = vz;
this.mass = m;
}
public void Update(Particle[] particles, double t, double h, int particleNumber)
{
RungeKutta4(particles, t, h, particleNumber);
}
private double acc(double r, Particle[] particles, int particleNumber, double[] r_temp, int l)
{
// dv/dt = f(x) = -G * m_i * (x - x_i) / [(x - x_i)^2 + (y - y_i)^2 + (z - z_i)^2]^(3/2)
double sum = 0;
switch (l)
{
case 0:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow( Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[1] - particles[i].r[1], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 1:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 2:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[1] - particles[i].r[1], 2), 1.5);
break;
}
return -G * sum;
}
private void RungeKutta4(Particle[] particles, double t, double h, int particleNumber)
{
//current position of the particle is saved in a vector
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
double[,] k = new double[4, 2];
k[0, 0] = this.v[l]; //k1_r
k[0, 1] = acc(this.r[l], particles, particleNumber, r_temp, l); //k1_v
k[1, 0] = this.v[l] + k[0, 1] * 0.5 * h; //k2_r
k[1, 1] = acc(this.r[l] + k[0, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k2_v
k[2, 0] = this.v[l] + k[1, 1] * 0.5 * h; //k3_r
k[2, 1] = acc(this.r[l] + k[1, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k3_v
k[3, 0] = this.v[l] + k[2, 1] * h; //k4_r
k[3, 1] = acc(this.r[l] + k[2, 0] * h, particles, particleNumber, r_temp, l); //k4_v
this.r[l] += (h / 6.0) * (k[0, 0] + 2 * k[1, 0] + 2 * k[2, 0] + k[3, 0]);
this.v[l] += (h / 6.0) * (k[0, 1] + 2 * k[1, 1] + 2 * k[2, 1] + k[3, 1]);
}
}
/*
Velocity Verlet algorithm:
1. Calculate y(t+h) = y(t) + v(t)h + 0.5a(t)h*h
2. Derive a(t+h) from dv/dt = -y using y(t+h)
3. Calculate v(t+h) = v(t) + 0.5*(a(t) + a(t+h))*h
*/
private void VelocityVerlet(Particle[] particles, double t, double h, int particleNumber)
{
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
//position
this.r[l] += h * this.v[l] + 0.5 * h * h * acc(this.r[l], particles, particleNumber, r_temp, l);
//velocity
this.v[l] += 0.5 * h * (acc(r_temp[l], particles, particleNumber, r_temp,l)
+ acc(this.r[l], particles, particleNumber, r_temp,l));
}
}
}
static void Main(string[] args)
{
//output file
TextWriter output = new StreamWriter("ispis.txt");
// declarations of variables
Particle[] particles = new Particle[2];
particles[0] = new Particle(0, 0, 0, 0, 0, 0, 1); //sun
particles[1] = new Particle(1, 0, 0, 0, 6.28, 0, 3.003467E-06); //earth
int N = 200;
double h, t, tmax;
double[,,] x = new double[particles.Length, N, 3]; //output
// setting initial values, step size and max time tmax
h = 0.01; // the step size in years
tmax = h * N;
// initial time
t = 0;
int i = 0;
while (t <= tmax) {
//updates position of all particles
for (int z = 1; z < particles.Length; z++)
particles[z].Update(particles, t, h, z);
//saves the position for output
for (int j = 1; j < particles.Length ; j++)
for (int z = 0; z < 3; z++ )
x[j,i,z] = particles[j].r[z];
t += h;
i++;
}
//output to file
for (int k = 0; k < particles.Length; k++ )
{
for (int f = 0; f < 3; f++)
{
for (int l = 0; l < N; l++)
output.Write(string.Format("{0,-15:0.########},", x[k,l,f]));
output.Write(string.Format("\n\n"));
}
output.Write(string.Format("\n\n\n\n"));
}
output.Close();
}
}
And here's the plot of the output data for earth's orbit:
Your model calculates the gravity force between two particles twice: for the first particle the force is based on their original coordinates, and for the second particle it is based on an updated position of the first one. This is a clear violation of the Newton's 3rd law. You must precompute all the forces before any update.
Your problem with the orbital of Earth is because the Center of Gravity of the System Earth-Sun, if you want to see the Orbital stay in loops you need to set center Of Gravity In (x,y,z)=(0,0,0) ;
I have a C# code based on your code Above so :
public partial class Form1 : Form
{
static
int
x1,y1,x2,y2, x3, y3;//for the 3th particule
private void timer1_Tick_1(object sender, EventArgs e)
{Moveu();
Invalidate();
}
private void button1_Click_1(object sender, EventArgs e)
{
timer1.Enabled = !timer1.Enabled;
}
public Form1()
{
InitializeComponent();
Paint += new PaintEventHandler(paint);
MouseDown += new MouseEventHandler(mouse_Click);
MouseUp += new MouseEventHandler(mouse_up);
MouseMove += new MouseEventHandler(mouse_move);
// x y z vx vy vz m
particles[0] = new Particle( 0, 0, 0, 0, 0, 0, 1 ) ; //sun
particles[1] = new Particle( 1, 0, 0, 0, 6, 0, 0.03 ); //earth
// particles[2] = new Particle( 0, 2, 0, 0, 0, 0, 1 ); //planet
x1 = (int)(100 * particles[0].r[0] + 300);
y1 = (int)(100 * particles[0].r[1] + 300);
x2 = (int)(100 * particles[1].r[0] + 300);
y2 = (int)(100 * particles[1].r[1] + 300);
}
Particle[] particles = new Particle[2];
void Moveu()
{
double h, t;
// setting initial values, step size and max time tmax
h = 0.005; // the step size in years
// initial time
t = 0;
//updates position of --all-- particles ( z=0 not z=1 )
for (int z = 0; z < particles.Length; z++)
particles[z].RungeKutta4(particles, t, h, z);
x1 = (int)(100 * particles[0].r[0] + 300); // +300 just for render it in centre
y1 = (int)(100 * particles[0].r[1] + 300);
x2 = (int)(100 * particles[1].r[0] + 300);
y2 = (int)(100 * particles[1].r[1] + 300);
// x3 = (int)(100 * particles[2].r[0] + 300);
// y3 = (int)(100 * particles[2].r[1] + 300);
}
void paint(object s, PaintEventArgs e)
{
Graphics graf;
graf = CreateGraphics();
graf.FillEllipse(new SolidBrush(Color.AntiqueWhite), x1 + move.X, y1 + move.Y, 50, 50);
graf.FillEllipse(new SolidBrush(Color.Blue), x2 + move.X, y2 + move.Y, 10, 10);
// graf.FillEllipse(new SolidBrush(Color.Yellow), x3, y3, 20, 20);
}
class Particle
{
public double[] r; // position vector
public double[] v; // velocity vector
public double mass;
//constructor
public Particle() { }
public Particle(double x, double y, double z, double vx, double vy, double vz, double m)
{
this.r = new double[3];
this.v = new double[3];
this.r[0] = x;
this.r[1] = y;
this.r[2] = z;
this.v[0] = vx;
this.v[1] = vy;
this.v[2] = vz;
this.mass = m;
}
private double acc(double r, Particle[] particles, int particleNumber, double[] r_temp, int l)
{
// dv/dt = f(x) = -G * m_i * (x - x_i) / [(x - x_i)^2 + (y - y_i)^2 + (z - z_i)^2]^(3/2)
double sum = 0;
switch (l)
{
case 0:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[1] - particles[i].r[1], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 1:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[2] - particles[i].r[2], 2), 1.5);
break;
case 2:
for (int i = 0; i < particles.Length; i++)
if (i != particleNumber)
sum += particles[i].mass * (r - particles[i].r[l]) / Math.Pow(Math.Pow(r - particles[i].r[l], 2)
+ Math.Pow(r_temp[0] - particles[i].r[0], 2) + Math.Pow(r_temp[1] - particles[i].r[1], 2), 1.5);
break;
}
return -G * sum;
}
public void RungeKutta4(Particle[] particles, double t, double h, int particleNumber)
{
//current position of the particle is saved in a vector
double[] r_temp = new double[3];
for (int j = 0; j < 3; j++)
r_temp[j] = this.r[j];
//loop going over all the coordinates and updating each using RK4 algorithm
for (int l = 0; l < 3; l++)
{
double[,] k = new double[4, 2];
k[0, 0] = this.v[l]; //k1_r
k[0, 1] = acc(this.r[l], particles, particleNumber, r_temp, l); //k1_v
k[1, 0] = this.v[l] + k[0, 1] * 0.5 * h; //k2_r
k[1, 1] = acc(this.r[l] + k[0, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k2_v
k[2, 0] = this.v[l] + k[1, 1] * 0.5 * h; //k3_r
k[2, 1] = acc(this.r[l] + k[1, 0] * 0.5 * h, particles, particleNumber, r_temp, l); //k3_v
k[3, 0] = this.v[l] + k[2, 1] * h; //k4_r
k[3, 1] = acc(this.r[l] + k[2, 0] * h, particles, particleNumber, r_temp, l); //k4_v
this.r[l] += (h / 6.0) * (k[0, 0] + 2 * k[1, 0] + 2 * k[2, 0] + k[3, 0]);
this.v[l] += (h / 6.0) * (k[0, 1] + 2 * k[1, 1] + 2 * k[2, 1] + k[3, 1]);
}
}
}
public static double G = 4 * Math.PI * Math.PI; //then time unite in years and length unite = distance between Earth and Sun and masse is the sun masse unite
void mouse_Click(object o, MouseEventArgs e)
{
dwn = new Point(e.X, e.Y);
if ("" + e.Button == "Left")
{
pos = move;
clicked = true;
}
}
void mouse_move(object o, MouseEventArgs e)
{
if (clicked)
{
move = new Point(e.X + pos.X - dwn.X, e.Y + pos.Y - dwn.Y);
Invalidate();
}
}
void mouse_up(object o, MouseEventArgs e)
{
clicked = false;
}
Point dwn, pos,move;
bool clicked;
}`you need to create a Timer and a button [![enter image description here][1]][1]
So I am trying to translate the algorith found here for concave hulls: http://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf
(Page 65)
Ive read through the entire thing but I cant figure out how to implement sortByAngle and angle, im not to sure what method I should do inside of them. This is what I have so far:
//Main method
public static Vertex[] ConcaveHull(Vertex[] points, int k = 3)
{
if (k < 3)
throw new ArgumentException("K is required to be 3 or more", "k");
List<Vertex> hull = new List<Vertex>();
//Clean first, may have lots of duplicates
Vertex[] clean = RemoveDuplicates(points);
if (clean.Length < 3)
throw new ArgumentException("At least 3 dissimilar points reqired", "points");
if (clean.Length == 3)//This is the hull, its already as small as it can be.
return clean;
if (clean.Length < k)
throw new ArgumentException("K must be equal to or smaller then the amount of dissimilar points", "points");
Vertex firstPoint = clean[0]; //TODO find mid point
hull.Add(firstPoint);
Vertex currentPoint = firstPoint;
Vertex[] dataset = RemoveIndex(clean, 0);
double previousAngle = 0;
int step = 2;
int i;
while (((currentPoint != firstPoint) || (step == 2)) && (dataset.Length > 0))
{
if (step == 5)
dataset = Add(dataset, firstPoint);
Vertex[] kNearestPoints = nearestPoints(dataset, currentPoint, k);
Vertex[] cPoints = sortByAngle(kNearestPoints, currentPoint, previousAngle);
bool its = true;
i = 0;
while ((its) && (i < cPoints.Length))
{
i++;
int lastPoint = 0;
if (cPoints[0] == firstPoint)
lastPoint = 1;
int j = 2;
its = false;
while ((!its) && (j < hull.Count - lastPoint))
{
its = intersectsQ(hull[step - 1 - 1], cPoints[0], hull[step - i - j - 1], hull[step - j - 1]);
j++;
}
}
if (its)
{
return ConcaveHull(points, k + 1);
}
currentPoint = cPoints[0];
hull.Add(currentPoint);
previousAngle = angle(hull[step - 1], hull[step - 2]);
dataset = RemoveIndex(dataset, 0);
step++;
}
bool allInside = true;
i = dataset.Length;
while (allInside && i > 0)
{
allInside = new Polygon(dataset).Contains(currentPoint); //TODO havent finished ray casting yet.
i--;
}
if (!allInside)
return ConcaveHull(points, k + 1);
return hull.ToArray();
}
private static Vertex[] Add(Vertex[] vs, Vertex v)
{
List<Vertex> n = new List<Vertex>(vs);
n.Add(v);
return n.ToArray();
}
private static Vertex[] RemoveIndex(Vertex[] vs, int index)
{
List<Vertex> removed = new List<Vertex>();
for (int i = 0; i < vs.Length; i++)
if (i != index)
removed.Add(vs[i]);
return removed.ToArray();
}
private static Vertex[] RemoveDuplicates(Vertex[] vs)
{
List<Vertex> clean = new List<Vertex>();
VertexComparer vc = new VertexComparer();
foreach (Vertex v in vs)
{
if (!clean.Contains(v, vc))
clean.Add(v);
}
return clean.ToArray();
}
private static Vertex[] nearestPoints(Vertex[] vs, Vertex v, int k)
{
Dictionary<double, Vertex> lengths = new Dictionary<double, Vertex>();
List<Vertex> n = new List<Vertex>();
double[] sorted = lengths.Keys.OrderBy(d => d).ToArray();
for (int i = 0; i < k; i++)
{
n.Add(lengths[sorted[i]]);
}
return n.ToArray();
}
private static Vertex[] sortByAngle(Vertex[] vs, Vertex v, double angle)
{
//TODO
return new Vertex[]{};
}
private static bool intersectsQ(Vertex v1, Vertex v2, Vertex v3, Vertex v4)
{
return intersectsQ(new Edge(v1, v2), new Edge(v3, v4));
}
private static bool intersectsQ(Edge e1, Edge e2)
{
double x1 = e1.A.X;
double x2 = e1.B.X;
double x3 = e2.A.X;
double x4 = e2.B.X;
double y1 = e1.A.Y;
double y2 = e1.B.Y;
double y3 = e2.A.Y;
double y4 = e2.B.Y;
var x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
var y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
if (double.IsNaN(x) || double.IsNaN(y))
{
return false;
}
else
{
if (x1 >= x2)
{
if (!(x2 <= x && x <= x1)) { return false; }
}
else
{
if (!(x1 <= x && x <= x2)) { return false; }
}
if (y1 >= y2)
{
if (!(y2 <= y && y <= y1)) { return false; }
}
else
{
if (!(y1 <= y && y <= y2)) { return false; }
}
if (x3 >= x4)
{
if (!(x4 <= x && x <= x3)) { return false; }
}
else
{
if (!(x3 <= x && x <= x4)) { return false; }
}
if (y3 >= y4)
{
if (!(y4 <= y && y <= y3)) { return false; }
}
else
{
if (!(y3 <= y && y <= y4)) { return false; }
}
}
return true;
}
private static double angle(Vertex v1, Vertex v2)
{
// TODO fix
Vertex v3 = new Vertex(v1.X, 0);
if (Orientation(v3, v1, v2) == 0)
return 180;
double b = EuclideanDistance(v3, v1);
double a = EuclideanDistance(v1, v2);
double c = EuclideanDistance(v3, v2);
double angle = Math.Acos((Math.Pow(a, 2) + Math.Pow(b, 2) - Math.Pow(c, 2)) / (2 * a * b));
if (Orientation(v3, v1, v2) < 0)
angle = 360 - angle;
return angle;
}
private static double EuclideanDistance(Vertex v1, Vertex v2)
{
return Math.Sqrt(Math.Pow((v1.X - v2.X), 2) + Math.Pow((v1.Y - v2.Y), 2));
}
public static double Orientation(Vertex p1, Vertex p2, Vertex p)
{
double Orin = (p2.X - p1.X) * (p.Y - p1.Y) - (p.X - p1.X) * (p2.Y - p1.Y);
if (Orin > 0)
return -1;//Left
if (Orin < 0)
return 1;//Right
return 0;//Colinier
}
I know that there is a load of code here. But im not sure if I can show the context and what I have without it.
Other classes:
public class Polygon
{
private Vertex[] vs;
public Polygon(Vertex[] Vertexes)
{
vs = Vertexes;
}
public Polygon(Bounds bounds)
{
vs = bounds.ToArray();
}
public Vertex[] ToArray()
{
return vs;
}
public IEnumerable<Edge> Edges()
{
if (vs.Length > 1)
{
Vertex P = vs[0];
for (int i = 1; i < vs.Length; i++)
{
yield return new Edge(P, vs[i]);
P = vs[i];
}
yield return new Edge(P, vs[0]);
}
}
public bool Contains(Vertex v)
{
return RayCasting.RayCast(this, v);
}
}
public class Edge
{
public Vertex A = new Vertex(0, 0);
public Vertex B = new Vertex(0, 0);
public Edge() { }
public Edge(Vertex a, Vertex b)
{
A = a;
B = b;
}
public Edge(double ax, double ay, double bx, double by)
{
A = new Vertex(ax, ay);
B = new Vertex(bx, by);
}
}
public class Bounds
{
public Vertex TopLeft;
public Vertex TopRight;
public Vertex BottomLeft;
public Vertex BottomRight;
public Bounds() { }
public Bounds(Vertex TL, Vertex TR, Vertex BL, Vertex BR)
{
TopLeft = TL;
TopRight = TR;
BottomLeft = BL;
BottomRight = BR;
}
public Vertex[] ToArray()
{
return new Vertex[] { TopLeft, TopRight, BottomRight, BottomLeft };
}
}
public class Vertex
{
public double X = 0;
public double Y = 0;
public Vertex() { }
public Vertex(double x, double y)
{
X = x;
Y = y;
}
public static Vertex[] Convert(string vs)
{
vs = vs.Replace("[", "");
vs = vs.Replace("]", "");
string[] spl = vs.Split(';');
List<Vertex> nvs = new List<Vertex>();
foreach (string s in spl)
{
try
{
nvs.Add(new Vertex(s));
}
catch
{
}
}
return nvs.ToArray();
}
public static string Stringify(Vertex[] vs)
{
string res = "[";
foreach (Vertex v in vs)
{
res += v.ToString();
res += ";";
}
res = res.RemoveLastCharacter();
res += "]";
return res;
}
public static string ToString(Vertex[] array)
{
string res = "[";
foreach (Vertex v in array)
res += v.ToString() + ",";
return res.RemoveLastCharacter() + "]";
}
/*
//When x < y return -1
//When x == y return 0
//When x > y return 1
public static int Compare(Vertex x, Vertex y)
{
//To find lowest
if (x.X < y.X)
{
return -1;
}
else if (x.X == y.X)
{
if (x.Y < y.Y)
{
return -1;
}
else if (x.Y == y.Y)
{
return 0;
}
else
{
return 1;
}
}
else
{
return 1;
}
}
*/
public static int CompareY(Vertex a, Vertex b)
{
if (a.Y < b.Y)
return -1;
if (a.Y == b.Y)
return 0;
return 1;
}
public static int CompareX(Vertex a, Vertex b)
{
if (a.X < b.X)
return -1;
if (a.X == b.X)
return 0;
return 1;
}
public double distance (Vertex b){
double dX = b.X - this.X;
double dY = b.Y - this.Y;
return Math.Sqrt((dX*dX) + (dY*dY));
}
public double slope (Vertex b){
double dX = b.X - this.X;
double dY = b.Y - this.Y;
return dY / dX;
}
public static int Compare(Vertex u, Vertex a, Vertex b)
{
if (a.X == b.X && a.Y == b.Y) return 0;
Vertex upper = new Vertex();
Vertex p1 = new Vertex();
Vertex p2 = new Vertex();
upper.X = (u.X + 180) * 360;
upper.Y = (u.Y + 90) * 180;
p1.X = (a.X + 180) * 360;
p1.Y = (a.Y + 90) * 180;
p2.X = (b.X + 180) * 360;
p2.Y = (b.Y + 90) * 180;
if(p1 == upper) return -1;
if(p2 == upper) return 1;
double m1 = upper.slope(p1);
double m2 = upper.slope(p2);
if (m1 == m2)
{
return p1.distance(upper) < p2.distance(upper) ? -1 : 1;
}
if (m1 <= 0 && m2 > 0) return -1;
if (m1 > 0 && m2 <= 0) return -1;
return m1 > m2 ? -1 : 1;
}
public static Vertex UpperLeft(Vertex[] vs)
{
Vertex top = vs[0];
for (int i = 1; i < vs.Length; i++)
{
Vertex temp = vs[i];
if (temp.Y > top.Y || (temp.Y == top.Y && temp.X < top.X))
{
top = temp;
}
}
return top;
}
}
Just a note on convention: you should start function names with upper case, and variables with lower case. In the function sortByAngle, you have a reference to the parameter angle and the function angle simultaneously.
Assuming Angle(...) is simply meant to calculate the angle between two points:
private static double Angle(Vertex v1, Vertex v2)
{
return Math.Atan2(v2.Y - v1.Y, v2.X - v1.X);
}
will give you the angle from v1 to v2, in radians between -pi and +pi. Do not mix degrees and radians. My suggestion is to always use radians, and only convert to degrees if necessary for human-readable output.
private static Vertex[] SortByAngle(Vertex[] vs, Vertex v, double angle)
{
List<Vertex> vertList = new List<Vertex>(vs);
vertList.Sort((v1, v2) => AngleDifference(angle, Angle(v, v1)).CompareTo(AngleDifference(angle, Angle(v, v2))));
return vertList.ToArray();
}
uses List.Sort to sort the vertices from greatest to least angle difference between the vertices point and itself, and angle. The order of v1 and v2 are swapped in the input tuple to sort descending, that is, greatest difference first. The difference between angles is calculated like so:
private static double AngleDifference(double a, double b)
{
while (a < b - Math.PI) a += Math.PI * 2;
while (b < a - Math.PI) b += Math.PI * 2;
return Math.Abs(a - b);
}
The first two lines ensure that the angles are not more than 180 degrees apart.
You have error in
private static Vertex[] nearestPoints(Vertex[] vs, Vertex v, int k)
{
Dictionary<double, Vertex> lengths = new Dictionary<double, Vertex>();
List<Vertex> n = new List<Vertex>();
double[] sorted = lengths.Keys.OrderBy(d => d).ToArray();
for (int i = 0; i < k; i++)
{
n.Add(lengths[sorted[i]]);
}
return n.ToArray();
}
according to code if you have several vertexes at the same distance, function returns only one. Since Dictionary uses unique keys.
BTW, did anyone finish this?
I don't have the time right now to read the paper, but I assume from my knowledge of conVEX hull algorithms that you're going around the points in a particular direction looking for the next point to link to.
If that's the case, "angle" would be the angle of the most recent line segment of the hull, and you want to sort the points by their angle from that line. Therefore you want to calculate the angles between a line (on the hull) and a set of lines (from the current point to each other point being considered). Whether the angles calculated are positive or negative depends upon whether you're going clockwise or anticlockwise. To calculate the angles, look at something like this:
Calculating the angle between two lines without having to calculate the slope? (Java)
Then just sort by the angles.
What about that?
private List<Vector> sortClockwiseFromCentroid(List<Vector> points, Vector center)
{
points = points.OrderBy(x => Math.Atan2(x.X - center.X, x.Y - center.Y)).ToList();
return points;
}