Plotting Euler Integration using Polyline(), C++ - c++

So I'm trying to plot the output of this Euler integration function:
typedef double F(double,double);
using std::vector;
void euler(F f, double y0, double a, double b, double h,vector<POINT> Points)
{
POINT Pt;
double y_n = y0;
double t = a;
for (double t = a; t != b; t += h )
{
y_n += h * f(t, y_n);
Pt.x = t; // assign the x value of the point to t.
Pt.y = y_n; // assign the y value of the point to y_n.
Points.push_back(Pt);
}
}
// Example: Newton's cooling law
double newtonCoolingLaw(double, double t)
{
return t; // return statement ends the function; here, it gives the time derivative y' = -0.07 * (t - 20)
}
I'm trying to use the Polyline() function in a Win32 application, so I do this under the case WM_PAINT:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
//Draw lines to screen.
hPen = CreatePen(PS_SOLID, 1, RGB(255, 25, 5));
SelectObject(hdc, hPen);
using std::vector;
vector<POINT> Points(0);
euler(newtonCoolingLaw, 1, 0, 20, 1,Points);
POINT tmp = Points.at(0);
const POINT* elementPoints[1] = { &tmp };
int numberpoints = (int) Points.size() - 1 ;
Polyline(hdc,elementPoints[1],numberpoints);
When I reroute my I/O to console, here are the outputs for the variables:
I'm able to draw the expected lines to the screen using MovetoEx(hdc,0,0,NULL) and LineTo(hdc,20,20), but for some reason none of these functions will work with my vector<POINT> Points. Any suggestions?

There are multiple things that seem erroneous to me:
1) You should pass the vector by reference or as a return value:
void euler(/*...*/,vector<POINT>& Points)
Currently you are only passing a copy into the function, so the original vector will not be modified.
2) Don't compare doubles for (in-)equality in your for-loop header. Doubles have a limited precision, so if b is much bigger than h, your loop might never terminate, as t might never exactly match b. Compare for "smaller" instead:
for (double t = a; t < b; t += h )
3) Why are you declaring elementPoints as an array of pointers of size 1? Wouldn't a simple pointer do:
const POINT* elementPoints = &tmp ; //EDIT: see point 5)
4) You have an of-by-one error when calling Polyline. If you want to stick with the array at all use.
Polyline(hdc,elementPoints[0],numberpoints);
EDIT: Sorry, I forgot an important one:
5) In your code, elementPoints[0] points to a single double (tmp) and not to the array inside of the vector. This would probably work, if you declared tmpas a reference:
POINT& tmp = Points.at(0); //I'm wondering why this doesn't throw an exception, as the vector should actually be empty here
However, I think what you actually want to do is to get rid of tmp and elementPoints altogether and write in the last line:
Polyline(hdc,&Points[0],(int) Points.size()-1);
//Or probably rather:
Polyline(hdc,&Points[0],(int) Points.size());
Btw.: What is the purpose of the -1?

Related

Initializing a box with N particles arranged in a specific pattern

I'm new to C++, and as an exercise I'm trying to reproduce what was done by Metropolis et al. (Metropolis Monte Carlo).
What I have done thus far - Made 2 classes: Vector and Atom
class Vector {
public:
double x;
double y;
Vector() {
}
Vector (double x_, double y_) {
x = x_;
y = y_;
}
double len() {
return sqrt(x*x + y*y);
}
double lenSqr() {
return x*x + y*y;
}
};
class Atom {
public:
Vector pos;
Vector vel;
Vector force;
Atom (double x_, double y_) {
pos = Vector(x_, y_);
vel = Vector(0, 0);
force = Vector(0, 0);
}
double KE() {
return .5 * vel.lenSqr();
}
};
I am not certain that the way I have defined the class Atom is... the best way to go about things since I will not be using a random number generator to place the atoms in the box.
My problem:
I need to initialize a box of length L (in my case L=1) and load it with 224 atoms/particles in an offset lattice (I have included a picture). I have done some reading and I was wondering if maybe an array would be appropriate here.
One thing that I am confused about is how I could normalize the array to get the appropriate distance between the particles and what would happen to the array once the particles begin to move. I am also not sure how an array could give me the x and y position of each and every atom in the box.
Metropolis offset (hexagonal) lattice
Well, It seems, that generally you don't need to use array to represent the lattice. In practice most often it may sense to represent lattice as array only if your atoms can naturally move only on the cells (for example as figures in chess). But seems that your atoms can move in any direction (already not practicle to use such rigid structure as array, because it has naturally 4 or 8 directions for move in 2D) by any step (it is bad for arrays too, because in this case you need almost countless cells in array to represent minimal distance step).
So basically what do you need is just use array as storage for your 224 atoms and set particular position in lattice via pos parameter.
std::vector<Atom> atoms;
// initialize atoms to be in trigonal lattice
const double x_shift = 1. / 14;
const double y_shift = 1. / 16;
double x_offset = 0;
for (double y = 0; y < 1; y += y_shift){
for (double x = x_offset; x < 1; x += x_shift){
// create atom in position (x, y)
// and store it in array of atoms
atoms.push_back(Atom(x, y));
}
// every new row flip offset 0 -> 1/28 -> 0 -> 1/28...
if (x_offset == 0){
x_offset = x_shift / 2;
}
else{
x_offset = 0;
}
}
Afterwards you just need to process this array of atoms and change their positions, velocities and what you need else according to algorithm.

Assigning the function output to a variable

I have a function which returns the address of a 4x2 matrix whose name is 'a'.
This function computes the elements of 'a' matrix inside and returns the address of the matrix. When I use that function, I want to assign its output to a matrix called 'a1' but when I do so, 'a1' becomes a zero matrix. However, when I assign the output to the same 'a' matrix, everything works fine. Can anyone help me? The code is written on Arduino IDE.
double a[4][2], a1[4][2];
double T0E[4][4]={
{0.1632, -0.3420, 0.9254, 297.9772},
{0.0594, 0.9397, 0.3368, 108.4548},
{-0.9848, 0, 0.1736, -280.5472},
{0, 0, 0, 1}
};
const int axis_limits[4][2]=
{
{ -160, 160 },
{ -135, 60 },
{ -135, 135 },
{ -90, 90 }
};
const unsigned int basex = 50, basez = 100, link1 = 200, link2 = 200, link3=30, endeff=link3+50;
double *inversekinematic(double target[4][4])
{
// angle 1
a[0][0] = -asin(target[0][1]);
a[0][1] = a[0][0];
if (a[0][0]<axis_limits[0][0] || a[0][0]>axis_limits[0][1] || isnan(a[0][0]))
{
bool error=true;
}
// angle 2
double A = sqrt(pow(target[0][3]-cos(a[0][0])*endeff*target[2][2], 2) + pow(target[1][3]-sin(a[0][0])*endeff*target[2][2], 2));
double N = (A - basex) / link1;
double M = -(target[2][3]-endeff*target[2][0] - basez) / link2;
double theta = acos(N / sqrt(pow(N, 2) + pow(M, 2)));
a[1][0] = theta + acos(sqrt(pow(N, 2) + pow(M, 2)) / 2);
a[1][1] = theta - acos(sqrt(pow(N, 2) + pow(M, 2)) / 2);
// angle 3
for (int i = 0; i <= 1; i++)
{
a[2][i] = {asin(-(target[2][3]-endeff*target[2][0]-basez)/link2-sin(a[1][i]))-a[1][i]};
}
// angle 4
for(int i = 0; i <=1; i++)
{
a[3][i] = {-asin(target[2][0])-a[1][i]-a[2][i]};
}
return &a[4][2];
}
void setup(){
Serial.begin(9600);
}
void loop() {
a1[4][2]={*inversekinematic(T0E)};
}
When you type return &a[4][2]; you are returning the address of the 3rd element of the 5th row. This is out of bounds, since C++ uses zero-based indexing and the array was declared as double a[4][2];. I think what you want to do is just return a; to return the address of the entire matrix.
Also, you're doing lots of strange things like declaring the parameter double target[4][4] with a size and using initializer lists to assign single elements, which look unusual to me.
I'll try to be a little more detailed. In C/C++, arrays are nothing more than pointers. So, when you assign one array to another array you are making them literally point to the same data in memory. What you will have to do is copy the elements with loops, or perhaps use memcpy(dest, src, size). For example, if you want to copy the contents of double a[4][2] to double b[4][2], you would use something like memcpy(b, a, sizeof(double) * 8);. If you use a = b; then a and b are pointing to same locations in memory.
Two points:
1. your code says the function inversekinematic() returns a pointer to a double, not an array.
2. you return a pointer to a double, but it's always the same address.
Maybe typedefs will help simplify the code?
typedef double Mat42[4][2];
Mat42 a, a1;
Mat42 *inversekinematic(double target[4][4])
{
// ...
return &a;
}
But, for the code you've shown, I don't see why you need to return the address of a fixed global value. Perhaps your real code might return the address of 'a' or 'a1', but if it doesn't ...

Beginner having an issue with classes and functions

I am a beginner programmer working on a program in c++ visual studio 2015 that takes an instance of a class titled rect and passes it to a function within rect that sets a rectangle of random size and position somewhere on a imaginary board in a console window. At the bottom of the code there are full instructions on what the code needs to do. The problem I am having is when the program prints the rectangles, the rectangle of "0's" is not printing but the rectangle of "1's" is. The rectangle rect0 is being passed by reference and the rect1 is being passed by pointer.
/*
iLab2: rectangles
*/
#define NOMINMAX // prevent Windows API from conflicting with "min" and "max"
#include <stdio.h> // C-style output. printf(char*,...), putchar(int)
#include <windows.h> // SetConsoleCursorPosition(HANDLE,COORD)
#include <conio.h> // _getch()
#include <time.h>
/**
* moves the console cursor to the given x/y coordinate
* 0, 0 is the upper-left hand coordinate. Standard consoles are 80x24.
* #param x
* #param y
*/
void moveCursor(int x, int y)
{
COORD c = { x,y };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
struct Vec2
{
short x, y; // variables x and y for storing rectangle coordinates
Vec2() : x(0), y(0) { } // default constructor for vect2 if no parameters are specified
Vec2(int x, int y) : x(x), y(y) { } // default constructor for vect2 if parameters are given
void operator+=(Vec2 v) // function for adding or subtracting (if v is negative) to move the rectangle
{
x += v.x;
y += v.y;
}
};
class Rect
{
Vec2 min, max;
public:
Rect(int minx, int miny, int maxx, int maxy)
:min(minx, miny), max(maxx, maxy)
{}
Rect() {}
void draw(const char letter) const
{
for (int row = min.y; row < max.y; row++)
{
for (int col = min.x; col < max.x; col++)
{
if (row >= 0 && col >= 0)
{
moveCursor(col, row);
putchar(letter);
}
}
}
}
void setMax(int maxx, int maxy)
{
this->max.x = maxx;
this->max.y = maxy;
}
void setMin(int minx, int miny)
{
this->min.x = minx;
this->min.y = miny;
}
bool isOverlapping(Rect const & r) const
{
return !(min.x >= r.max.x || max.x <= r.min.x
|| min.y >= r.max.y || max.y <= r.min.y);
}
void translate(Vec2 const & delta)
{
min+=(delta);
max+=(delta);
}
void setRandom(Rect & r);
void setRandom(Rect* r);
};
void Rect::setRandom(Rect & r)
{
srand(time(NULL)); // added to make the random placement and size of the rect different each time program runs
int pos_x, pos_y, height, width;
pos_x = rand() % 51;
pos_y = rand() % 21;
height = 2 + rand() % 11;
width = 2 + rand() % 11;
height = height / 2;
width = width / 2;
r.min.x = pos_x - width;
r.min.y = pos_y - height;
r.max.x = pos_x + width;
r.max.y = pos_y + height;
}
void Rect::setRandom(Rect * r)
{
srand(time(NULL)); // added to make the random placement and size of the rect different each time program runs
int posX, posY, heightPoint, widthPoint;
posX = rand() % 51;
posY = rand() % 21;
heightPoint = 2 + rand() % 11;
widthPoint = 2 + rand() % 11;
heightPoint = heightPoint / 2;
widthPoint = widthPoint / 2;
this->min.x = posX - widthPoint;
this->min.y = posY - heightPoint;
this->max.x = posX + widthPoint;
this->max.y = posY + heightPoint;
}
int main()
{
// initialization
//Rect userRect(7, 5, 10, 9); // (x-min, y-min, x-max, y-max) x-min how far left the rectange can be
//Rect rect0(10, 2, 14, 4); // (x-min, y-min, x-max, y-max)
//Rect rect1(1, 6, 5, 15); // (x-min, y-min, x-max, y-max)
//Rect userRect;
Rect * userRect;
Rect rect0;
Rect rect1;
const int rectSize = 5;
Rect rect[rectSize];
userRect = new Rect();
// set
rect[0].setRandom(rect[0]);
rect[1].setRandom(& rect[1]);
userRect->setMin(7, 5);
userRect->setMax(10, 9);
//rect0.setMin(10, 2);
//rect0.setMax(14, 4);
//rect1.setMin(1, 6);
//rect1.setMax(5, 15);
int userInput;
do
{
// draw
rect[0].draw('0'); // drawing the 0 rectangle with an x width of 4 and a y height of 2
rect[1].draw('1'); // drawing the 1 rectangle with a x width of 4 and a y height of 9
moveCursor(0, 0); // re-print instructions
printf("move with 'w', 'a', 's', and 'd'");
userRect->draw('#'); // drawing the user rectangle in its starting location with a x width of 3 and a y height of 4
// user input
userInput = _getch();
// update
Vec2 move;
switch (userInput)
{
case 'w': move = Vec2(0, -1); break; // Moves the user Rectangle -y or up on the screen
case 'a': move = Vec2(-1, 0); break; // Moves the user Rectangle -x or left on the screen
case 's': move = Vec2(0, +1); break; // Moves the user Rectangle +y or down on the screen
case 'd': move = Vec2(+1, 0); break; // Moves the user Rectangle +x or right on the screen
}
userRect->draw(' '); // un-draw before moving
userRect->translate(move); // moves the user rectangle to the new location
} while (userInput != 27); // escape key
delete userRect; // delete dynamic object to release memory
return 0;
}
// INSTRUCTIONS
// ------------
// 3) Random rectangles, by reference and by pointer
// a) create a method with the method signature "void setRandom(Rect & r)".
// This function will give the passed-in Rect object a random location.
// The random x should be between 0 and 50 x. The random y should be
// between 0 and 20. Limit the possible width and height to a minimum of 2
// and a maximum of 10.
// b) test "void setRandom(Rect & r)" on the local Rect object "rect0".
// c) create a method with the method signature
// "void setRandomByPointer(Rect * r)", which functions the same as
// "void setRandom(Rect & r)", except that the argument is
// passed-by-pointer.
// d) test "void setRandomByPointer(Rect * r)" on the local Rect object
// "rect1".
// 4) Test and show overlap
// a) Using the existing function "isOverlapping(Rect const &)", test to see
// if userRect collides with any other Rect objects. If userRect is
// overlapping, draw it with '+' instead '#'.
// b) Create a Rect * pointer that points to the address if the Rect object
// that userRect collides with. It should point at NULL if userRect is
// colliding with no other Rect objects.
// c) Print to the screen the width and height of a Rect object that userRect
// collides with. If no collision is happening, print "no collision"
// instead.
// 5) Array of objects
// a) Replace the Rect objects rect0 and rect1 with an array of 2 Rect
// objects, "rect[2]".
// b) Make sure you replace every remaining "rect0" with "rect[0]", and every
// "rect1" with "rect[1]".
// c) Increase the size of the "rect" array to 5. Make sure all 5 Rect
// objects are randomized, drawn to the screen, and tested for collision.
// d) If you have not already done so, replace
// duplicate-code-using-array-elements with a for-loop. For example:
// If you have:
// rect[0].draw('0');
// rect[1].draw('1');
// rect[2].draw('2');
// rect[3].draw('3');
// rect[4].draw('4');
// Replace it with:
// for(int i = 0; i < NUMBER_OF_RECTS; i++)
// {
// rect[i].draw('0'+i);
// }
// Do this where objects are randomized, drawn, and tested for collision
You have two different setRandom() methods with three problems.
Each time either setRandom() gets called, srand() also gets called. srand() should only be called once, when the program starts -- read the first answer to that question, carefully.
Code duplication. The code in both setRandom() is nearly identical. Code duplication is bad. Duplicated code means that if the algorithm needs to be changed in some way, you will have to remember to do it in two places. Or three places. Or four places. Or however many duplicate chunks of code exist in the code. You have to remember them all, and find them. If you miss one, bugs galore.
Same problem as #2, but for the "nearly identical" part. The difference is: the first version of setRandom() takes a reference to another object and modifies another object that's passed by reference. The second version of setRandom() takes a pointer to another object instead of a reference, but ignores it completely, and instead initializes this, instead of the pointed object.
And, as a result of these bugs, we get the results you're seeing.
rect[0].setRandom(rect0);
This ends up initializing rect0. rect[0] is ignored completely, and not initialized at all.
rect[1].setRandom(& rect1);
This ends up initializing rect[1]. rect1 is ignored completely, and not initialized at all.
And that's why the rest of the code fails to draw rect[0]. It does not get initialized at all.
The shown code is completely confused because it has four, and not two, objects. rect0, rect1, and the rect[] array containing two more objects. After they are declared, rect0 and rect1 are completely ignored, except for the misfired initialization, and they serve apparently no purpose whatsoever.
Neither is there any real reason here for setRandom() to take either a pointer or a reference to some other object. The apparent purpose of setRandom() is to initialize an object's dimensions randomly.
So it should simply initialize this's dimensions randomly. Passing some other object, by pointer or reference, makes no sense at all.
Then, after getting rid of rect0 and rect1, and simply calling a single setRandom() method...
rect[0].setRandom();
rect[1].setRandom();
... the rest of the code will proceed and properly draw two randomly-initialized objects.
the code Rect rect[ rectSize ] will create 5 rects to array rect and all of those rects are with min(0,0) max(0,0)(initial state). when you call rect[ 0 ].setRandom( rect0 ) which will update rect0(you pass it by reference) and do nothing to rect[0].when you call rect[ 1 ].setRandom( &rect1 ) you update rect[1] (by this->min.x = posX - some value).so you get difference between rect[0] and rect[1].

Find intersection points for vector construct

So in my software I have two vectors. The first vector matrix stores the information of the shape of a given 3D model. So I got a vector of arrays to store the x,y,z coordinates of points.
std::vector<std::array<double, 3>> matrix;
This vector is already sorted, so that I get the contour of the model.
In the second vector boundingbox I store the information of a bounding box.
std::vector<std::array<double, 3>> boundingbox;
In this vector the first four elements describe the bounding box around the contour. To fill the outline I have placed a grid on it. The grid is in this case defined by the software based on a variable. The variable infill is set by the user at run-time. So currently my program creats the following image.
Now the next step would be to find the intersection points between the grid and the contour. My approach to this would be a typical mathematical approach.
I would use two for-loops. The first loop would be used to iterate over the grid so that each line of the grid is called once.
The second loop would be used the vector to undergo matrix. I developed a pseudo code, in which I describe my procedure.
int fillingStart; //first element of boundingbox to contain information about the grid
int n; //number of lines in the Grid.
for(size_t i=fillingStart; i<(n-1); i+2)
{
double A_x=boundingbox[j][0];
double A_y=boundingbox[j][1];
double B_x=boundingbox[j+1][0];
double B_y=boundingbox[j+1][0];
double AB_x=B_x-A_x;
double AB_y=B_y-A_y;
double intersectionpoint_y = DBL_MAX;
double intersectionpoint_x = DBL_MAX;
double intersectionpoint2_y = DBL_MAX;
double intersectionpoint2_x = DBL_MAX;
for(size_t j=0; j<(matrix.size()-1); j++)
{
double C_x = matrix[j][0];
double C_y = matrix[j][1];
double D_x = matrix[j+1][0];
double D_y = matrix[j+1][1];
double CD_x = D_x-C_x;
double CD_y = D_y-C_y;
double s = (((C_x-A_x)*(-CD_y))-((-CD_x)*(C_y-A_y)))/((AB_x*(-CD_y))-((-CD_x)*AB_y));//Cramer's rule
double t = ((AB_x*(C_y-A_y))-((C_x-A_x)*AB_y)) / ((AB_x * (-CD_y))-((-CD_x)*AB_y));//Cramer's rule
double point_x = A_x+s*AB_x;
double point_y = A_y*s*AB_y;
if(point_x < intersectionpoint_x && point_y < intersectionpoint_y)
{
intersectionpoint_x = point_x;
intersectionpoint_y = point_y;
}
else if(point_x < intersectionpoint2_x && point_y < intersectionpoint2_y)
{
intersectionpoint2_x = point_x;
intersectionpoint2_y = point_y;
}
}
intersects.push_back(std::array<double, 3>());
double q = boundingbox.size()-1;
intersects[q][0] = intersectionpoint_x;
intersects[q][1] = intersectionpoint_y;
intersects.push_back(std::array<double, 3>());
double q = boundingbox.size()-1;
intersects[q][0] = intersectionpoint2_x;
intersects[q][1] = intersectionpoint2_y;
}
With this two loops I would find the intersection points for each line of the grid and each vector (between two points) of the contour. Then I would have to find the two intersection points, closest to the grid line and store these points. The special case would be, if there is something in the contoure, like a hole. In this case I would find four points.
EDIT: Why I want to use intersection points is shown in the following figures
Here we have the contour of a rectangle. As you can see there are just a few points to describe the figure.
The next image shows the filling of the model
Because of the few points of the contour I have to calculate the intersection points of the contour and the grid.
EDIT2: I now got the code working and updated the code here, but the problem is that it saves always the same point in intersectionpoint. Thats because of the if-statement, but I cant figure out how get it working.
You could iterate over the contour, and for each two consecutive points, check if there is a line between, and if there is one, compute the intersection point.
std::vector<std::array<double, 3>> intersects;
auto it = matrix.begin();
while (it != matrix.end() - 1) {
auto &p1 = *it;
auto &p2 = *(++it);
double x;
// Check if there is a vertical line between p1 and p2
if (has_vertical_line(p1, p2, &x)) {
// The equation of the line joining p1 and p2 is:
// (p2[1] - p1[1]) / (p2[0] - p1[0]) * x + p1[0]
double y = (p2[1] - p1[1]) / (p2[0] - p1[0]) * x + p1[0];
intersects.push_back({x, y, 0.0});
}
}
Where has_vertical_line is something like:
bool has_vertical_line (std::array<double, 3> const& p1,
std::array<double, 3> const& p2,
double *px) {
double x1 = p1[0], x2 = p2[0];
if (x2 <= x1) {
std::swap(x1, x2);
}
size_t lx2 = closest_from_below(x2),
lx1 = closest_from_above(x1);
if (lx1 == lx2) {
*px = lines[lx1]; // Assuming abscissa
return true;
}
return false;
}
Where closest_from_below and closest_from_above are simple function that find the line just below / above the current abscissa (trivial since your lines are vertical).

sorting points: concave polygon

I have a set of points that I'm trying to sort in ccw order or cw order from their angle. I want the points to be sorted in a way that they could form a polygon with no splits in its region or intersections. This is difficult because in most cases, it would be a concave polygon.
point centroid;
int main( int argc, char** argv )
{
// I read a set of points into a struct point array: points[n]
// Find centroid
double sx = 0; double sy = 0;
for (int i = 0; i < n; i++)
{
sx += points[i].x;
sy += points[i].y;
}
centroid.x = sx/n;
centroid.y = sy/n;
// sort points using in polar order using centroid as reference
std::qsort(&points, n, sizeof(point), polarOrder);
}
// -1 ccw, 1 cw, 0 collinear
int orientation(point a, point b, point c)
{
double area2 = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
if (area2 < 0) return -1;
else if (area2 > 0) return +1;
else return 0;
}
// compare other points relative to polar angle they make with this point
// (where the polar angle is between 0 and 2pi)
int polarOrder(const void *vp1, const void *vp2)
{
point *p1 = (point *)vp1;
point *p2 = (point *)vp2;
// translation
double dx1 = p1->x - centroid.x;
double dy1 = p1->y - centroid.y;
double dx2 = p2->x - centroid.x;
double dy2 = p2->y - centroid.y;
if (dy1 >= 0 && dy2 < 0) { return -1; } // p1 above and p2 below
else if (dy2 >= 0 && dy1 < 0) { return 1; } // p1 below and p2 above
else if (dy1 == 0 && dy2 ==0) { // 3-collinear and horizontal
if (dx1 >= 0 && dx2 < 0) { return -1; }
else if (dx2 >= 0 && dx1 < 0) { return 1; }
else { return 0; }
}
else return -orientation(centroid,*p1,*p2); // both above or below
}
It looks like the points are sorted accurately(pink) until they "cave" in, in which case the algorithm skips over these points then continues.. Can anyone point me into the right direction to sort the points so that they form the polygon I'm looking for?
Raw Point Plot - Blue, Pink Points - Sorted
Point List: http://pastebin.com/N0Wdn2sm (You can ignore the 3rd component, since all these points lie on the same plane.)
The code below (sorry it's C rather than C++) sorts correctly as you wish with atan2.
The problem with your code may be that it attempts to use the included angle between the two vectors being compared. This is doomed to fail. The array is not circular. It has a first and a final element. With respect to the centroid, sorting an array requires a total polar order: a range of angles such that each point corresponds to a unique angle regardless of the other point. The angles are the total polar order, and comparing them as scalars provides the sort comparison function.
In this manner, the algorithm you proposed is guaranteed to produce a star-shaped polyline. It may oscillate wildly between different radii (...which your data do! Is this what you meant by "caved in"? If so, it's a feature of your algorithm and data, not an implementation error), and points corresponding to exactly the same angle might produce edges that coincide (lie directly on top of each other), but the edges won't cross.
I believe that your choice of centroid as the polar origin is sufficient to guarantee that connecting the ends of the polyline generated as above will produce a full star-shaped polygon, however, I don't have a proof.
Result plotted with Excel
Note you can guess from the nearly radial edges where the centroid is! This is the "star shape" I referred to above.
To illustrate this is really a star-shaped polygon, here is a zoom in to the confusing lower left corner:
If you want a polygon that is "nicer" in some sense, you will need a fancier (probably much fancier) algorithm, e.g. the Delaunay triangulation-based ones others have referred to.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct point {
double x, y;
};
void print(FILE *f, struct point *p) {
fprintf(f, "%f,%f\n", p->x, p->y);
}
// Return polar angle of p with respect to origin o
double to_angle(const struct point *p, const struct point *o) {
return atan2(p->y - o->y, p->x - o->x);
}
void find_centroid(struct point *c, struct point *pts, int n_pts) {
double x = 0, y = 0;
for (int i = 0; i < n_pts; i++) {
x += pts[i].x;
y += pts[i].y;
}
c->x = x / n_pts;
c->y = y / n_pts;
}
static struct point centroid[1];
int by_polar_angle(const void *va, const void *vb) {
double theta_a = to_angle(va, centroid);
double theta_b = to_angle(vb, centroid);
return theta_a < theta_b ? -1 : theta_a > theta_b ? 1 : 0;
}
void sort_by_polar_angle(struct point *pts, int n_pts) {
find_centroid(centroid, pts, n_pts);
qsort(pts, n_pts, sizeof pts[0], by_polar_angle);
}
int main(void) {
FILE *f = fopen("data.txt", "r");
if (!f) return 1;
struct point pts[10000];
int n_pts, n_read;
for (n_pts = 0;
(n_read = fscanf(f, "%lf%lf%*f", &pts[n_pts].x, &pts[n_pts].y)) != EOF;
++n_pts)
if (n_read != 2) return 2;
fclose(f);
sort_by_polar_angle(pts, n_pts);
for (int i = 0; i < n_pts; i++)
print(stdout, pts + i);
return 0;
}
Well, first and foremost, I see centroid declared as a local variable in main. Yet inside polarOrder you are also accessing some centroid variable.
Judging by the code you posted, that second centroid is a file-scope variable that you never initialized to any specific value. Hence the meaningless results from your comparison function.
The second strange detail in your code is that you do return -orientation(centroid,*p1,*p2) if both points are above or below. Since orientation returns -1 for CCW and +1 for CW, it should be just return orientation(centroid,*p1,*p2). Why did you feel the need to negate the result of orientation?
Your original points don't appear form a convex polygon, so simply ordering them by angle around a fixed centroid will not necessarily result in a clean polygon. This is a non-trivial problem, you may want to research Delaunay triangulation and/or gift wrapping algorithms, although both would have to be modified because your polygon is concave. The answer here is an interesting example of a modified gift wrapping algorithm for concave polygons. There is also a C++ library called PCL that may do what you need.
But...if you really do want to do a polar sort, your sorting functions seem more complex than necessary. I would sort using atan2 first, then optimize it later once you get the result you want if necessary. Here is an example using lambda functions:
#include <algorithm>
#include <math.h>
#include <vector>
int main()
{
struct point
{
double x;
double y;
};
std::vector< point > points;
point centroid;
// fill in your data...
auto sort_predicate = [&centroid] (const point& a, const point& b) -> bool {
return atan2 (a.x - centroid.x, a.y - centroid.y) <
atan2 (b.x - centroid.x, b.y - centroid.y);
};
std::sort (points.begin(), points.end(), sort_predicate);
}