I am still struggling to realize what I want to do.
My code shall take user-defined segments (e.g. either a line, a circle, or whatever geometric segment definition I will implement) and chain them together in a vector. However, the order of segment type ("line", "circle",...) is user-defined and may hence vary from execution to execution.
Before I go on: Each segment has different input data needed for its own creation (e.g. a line has no radius, only starting and ending point).
My preferred approach would be to
read user input and identify order of segments
create each segment
Feed these to a function (e.g. member function/method for a class implementing the contour).
This function creates the contour, e.g. by implementing a vector.
My current test code has a hard-coded segment sequence but the trick that I want to achieve is that the order (and number) of segments is not hard-coded. Unfortunately I cannot figure out how.
Here's the code:
#include <iostream>
#include <vector>
struct point
{
double x;
double y;
};
class segment
{
public:
segment()
{
P1.x = 0;
P1.y = 0;
P2.x = 0;
P2.y = 0;
};
virtual ~segment() {};
virtual double get_radius() { return 0; };
virtual double get_length() { return 0; };
virtual double get_angle() { return 0; };
int segment_id = 0;
protected:
point P1;
point P2;
};
class Line : public segment
{
public:
Line() {};
Line(const point pt1, const point pt2)
{
P1.x = pt1.x;
P1.y = pt1.y;
P2.x = pt2.x;
P2.y = pt2.y;
segment_id = 1;
};
~Line() {};
double get_length() { return calc_length(); };
double get_angle() { return calc_angle(); };
private:
double calc_length()
{
// calculate length (here: dummy value)
return 1;
}
double calc_angle()
{
// calculate angle (here: dummy value)
return 0.5;
}
double length = 0;
double angle = 0;
}
;
class circle : public segment
{
public:
circle()
{
center.x = 0;
center.y = 0;
};
circle(const double r, const point c)
{
radius = r;
center.x = c.x;
center.y = c.y;
segment_id = 2;
};
~circle() {};
double get_radius() { return radius; };
point get_center() { return center; };
double get_length() { return 3.14 * radius; }; //returns circumference
private:
double radius = 0;
point center;
};
//-------------------------------------------------------
int main()
{
int nbr = 5;
point start;
start.x = 1;
start.y = 2;
point end;
end.x = 3;
end.y = 4;
point c;
c.x = 0;
c.y = 0;
double r = 9;
auto anotherCircle = std::make_unique<circle>(r, c);
auto anotherLine = std::make_unique<Line>(start, end);
std::unique_ptr<circle> yet_anotherCircle;
circle* myCircle = new circle(r, c);
Line* myLine = new Line(start, end);
//VERSION 1: Does not compile. I get an exception in <memory> line 1762 when trying to delete _Ptr
//std::vector<std::unique_ptr<segment>> v1;
//v1.emplace_back(anotherCircle);
//v1.emplace_back(anotherLine);
//std::cout << v1[0]->get_radius() << std::endl;
//v1.emplace_back(myLine);
//std::cout << v1[1]->segment_id << std::endl;
//VERSION 2: Compiles
std::vector<std::unique_ptr<segment>> v2;
v2.emplace_back(std::make_unique<circle>(r, c));
v2.emplace_back(std::make_unique<Line>(start, end));
}
The straight forward way that I imagine but that does not seem to work would require version 1 to work. I could then probably use template objects that I feed into the vector. Unfortunately this is not the way to go and I have not the slightest idea how to approach this. It would be awesome if somebody could help me here! Thanks!
You need to move items in vector, as your items are no copyable:
v1.emplace_back(std::move(anotherCircle));
I have another problem that I have no idea how to solve. Maybe somebody can help me.
What I want to do:
I have a vector that shall take elements of various class types. In my example code I have two classes (Line, circle) that are both derived from a virtual class segment.
My code shall chain several circle or Line elements and put them in the vector. Each element may be different from the other (different radii, different starting and ending points, etc) and the sequence of elements shall vary from execution to execution. That is for instance for the first execution I have a circle with radius 2 followed by another circle of radius 1, followed by a Line of length 4 and for the second execution I have a Line of length 1 followed by another Line of Length 5 in a different direction, followed by a circle of radius 0.5.
I've already learned how to compose the vector such that it can contain different types but as of now the sequence and definition of each element is hard-coded. Now I want to make this flexible (in the end the sequence and definition shall be file-driven). For this I attempt to implement a template function that takes whatever element is fed into it and adds it to the vector. The current definition also takes the vector as input but I may end up to define this function as a method for the vector.
Unfortunately I cannot figure out a way how to do it that works. I understand that I cannot copy a unique_ptr so I tried with the std::move() method but doesn't work. I get an C2664 error message of the xmemory module in line 671 saying that I cannot convert argument 1 in T2 into a std::nullptr_t.
Can somebody help me here? That'll be so awesome!
Here's my example code that implements the basic idea for my code:
#include <iostream>
#include <vector>
#include <variant>
struct point
{
double x;
double y;
};
class segment
{
public:
segment()
{
P1.x = 0;
P1.y = 0;
P2.x = 0;
P2.y = 0;
};
virtual ~segment() {};
virtual double get_radius() { return 0; };
virtual double get_length() { return 0; };
virtual double get_angle() { return 0; };
int segment_id = 0;
protected:
point P1;
point P2;
};
class Line : public segment
{
public:
Line() {};
Line(const point pt1, const point pt2)
{
P1.x = pt1.x;
P1.y = pt1.y;
P2.x = pt2.x;
P2.y = pt2.y;
segment_id = 1;
};
~Line() {};
double get_length() { return calc_length(); };
double get_angle() { return calc_angle(); };
private:
double calc_length()
{
// calculate length (here: dummy value)
return 1;
}
double calc_angle()
{
// calculate angle (here: dummy value)
return 0.5;
}
double length = 0;
double angle = 0;
}
;
class circle : public segment
{
public:
circle()
{
center.x = 0;
center.y = 0;
};
circle(const double r, const point c)
{
radius = r;
center.x = c.x;
center.y = c.y;
segment_id = 2;
};
~circle() {};
double get_radius() { return radius; };
point get_center() { return center; };
double get_length() { return 3.14 * radius; }; //returns circumference
private:
double radius = 0;
point center;
};
//-------------------------------------------------------
//T1: class type "segment", T2: class object Line or circle
template<typename T1, typename T2>
inline void add_segment(T1 v, T2 line_or_circle)
{
v.emplace_back(line_or_circle);
}
//-------------------------------------------------------
int main()
{
int nbr = 5;
point start;
start.x = 1;
start.y = 2;
point end;
end.x = 3;
end.y = 4;
point c;
c.x = 0;
c.y = 0;
double r = 9;
auto anotherCircle = std::make_unique<circle>(r, c);
auto anotherLine = std::make_unique<Line>(start, end);
circle myCircle(r, c);
//VERSION 1: Does now compile.
std::vector<std::unique_ptr<segment>> v1;
v1.emplace_back(std::move(anotherCircle));
v1.emplace_back(std::move(anotherLine));
std::cout << v1[0]->get_radius() << std::endl;
std::cout << v1[1]->segment_id << std::endl;
//VERSION 2: Compiles
std::vector<std::unique_ptr<segment>> v2;
v2.emplace_back(std::make_unique<circle>(r, c));
v2.emplace_back(std::make_unique<Line>(start, end));
//=================================================================
//now I want to implement this as a function call
//=================================================================
std::vector<std::unique_ptr<segment>> v3;
//VERSION 5:
auto myLine2 = std::make_unique<Line>(start, end);
add_segment(v3, std::move(myLine2)); //shall add object of class Line or circle (derived from virtual segment class, see above) to vector v3. In this example a Line but might be a circle
}
Your function add_segment is taking the vector by value. This fails to compile because the vector is uncopyable, as unique pointers are uncopyable. Even if you used a copyable pointer type, it would be a pointless method as the copy is destroyed at the end of the function.
You will also need to move the line_or_circle parameter in the body of add_segment.
template<typename T1, typename T2>
inline void add_segment(T1 & v, T2 line_or_circle)
{
v.emplace_back(std::move(line_or_circle));
}
I am using OpenCV 2.4.10 with Visual Studio 2013 for my code. But I am getting the following linking error:
1>Pathfinding.obj : error LNK2019: unresolved external symbol "public:
class cv::Vec & __cdecl cv::Mat::at >(int,int)"
(??$at#V?$Vec#E$02#cv###Mat#cv##QEAAAEAV?$Vec#E$02#1#HH#Z) referenced
in function "private: struct Pathfinding::Point2A * __cdecl
Pathfinding::GetNeighbors(struct Pathfinding::Point2A,int &)"
(?GetNeighbors#Pathfinding##AEAAPEAUPoint2A#1#U21#AEAH#Z)
1>C:\Users\ysingh\Documents\DstarLite\OpenCV\Astar\x64\Debug\Astar.exe
: fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Here is the header file (please see struct Point2A in class definition) where the above error is referring to:
#include<opencv2\core\core.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<string>
class Pathfinding
{
private :
//Two dimensional , integer -based point structure , contains additional variables for pathfinding calculation
**struct Point2A**
{
// x, y the coordinates of the point
//dir is the direction from the previous point . see directions coding below
int x, y, dir;
//level: the cost of route from start to this point
//fscore: the essence of the A* algorithm, value is : [level] + [in air distance from destination] * astar_weight
float fScore, level;
//Constructors
Point2A() : x(0), y(0), fScore(0), dir(0){};
Point2A(int _x, int _y, float _level = 0.f, int _dir = 0) :x(_x), y(_y), level(_level), fScore(0), dir(_dir) {};
//== operator overload
bool operator == (const Point2A other);
};
//CompByPos : struct used in the stl map<Point2A, Point2A> during the pathfinding
//it only contains a comparator function
//we need this, because every point is unique by position, but not by fscore
struct CompByPos
{
bool operator()(const Point2A a, const Point2A b) const;
};
//CompByFScore : contains a comparating function, which works by fscore
//it gives priority for the smaller fScore
//used in stl priority_queue<Point2A>
struct CompByFScore
{
bool operator()(const Point2A a, const Point2A b);
};
//mapimg is the map got, pathmap is the same, but the pixels of the path are colored
//pathmap is only valid after calculate path
//blurmap is matimg blurred with opencv function, its used in keeping away from walls
cv::Mat mapimg, pathmap, blurmap;
//astar_weight is the multiplier of A* coefficient
//wall_weight is used in keeping away from walls features
float astar_weight, wall_weight;
//no comment
Point2A start, dest;
//daigonal decides if a pixel (which is a node) has 4 or 8 neighbours
//see direction coding below
//calculated decides if the Pathfinding object has valid path for current map and settings
bool diagonal, calculated;
//mrows and mcols refers to the size of mapimg
//blursize is used in avaoiding wall avoidance feature
int mrows, mcols, blur_size;
//stores the list of directions for the path
std::string dir;
//calculated Eucledian Distance between two points a and b
float Distance(Point2A a, Point2A b);
//returns an array of the points surrounding point p
//the length of the array is not constant, because the function performs
//OnMap checks too. use arraySize ref variable to get the size of the array returned
Point2A* GetNeighbors(Point2A p, int& arraySize);
// Function sets default values
void InitValues();
//Checks if point p is wall
//Class support black and white maps, where black pixels are wall
bool IsWall(Point2A p);
//Function decides if coordinates of this point are on map or not
bool OnMap(int x, int y);
public:
enum ErrorCodes
{
NoError = 0,
NoMap,
StartIsWall,
DestIsWall,
NoPath,
AlreadyCalculated
};
static const int diagonalDirX[];
static const int diagonalDirY[];
static const int nonDiagonalDirX[];
static const int nonDiagonalDirY[];
//constructor :sets default values diagonal = true, astar coefficient 0.3
Pathfinding();
//constructor, argument map is the map on which algorithm is implemented
Pathfinding(cv::Mat map, bool _diagonal = true);
//Set OpenCV Mat image as the map
void SetMap(cv::Mat map);
////sets the A* pathfinding coefficient. 0.f means Dijkstra's algorithm, anything else is A* (positive values recommended).
//The bigger the value, the more the algorithm steers towards the destination
//but setting it too high can result in suboptimal path
//after changing that, have to call CalculatePath again
void SetAWeight(float weight);
//if set to true, each pixel has 8 connected neighbor, else only 4 - see GetDirections() comment
//after changing that, have to call CalculatePath again
void SetDiagonal(bool _diagonal);
//sets the value of how much the algorithm tries to avoid going near walls.
//weight: the amount the walls push away the route. default 10.f
//0.f disables the feature
//avoidZoneLevel: the size of the zone surrounding the walls, in which the upper effect works. default: 5
void SetWallWeight(float weight, int avoidZoneLevel);
//sets the start point. the coordinate system is the OpenCV/image default, the origin is the upper left corner of the image.
//start and destination points have to be set after the map image!
void SetStart(int x, int y);
void SetDestination(int x, int y);
//returns the map, on which the calculated path is marked red
//call this after CalculatePath(), otherwise returns empty map
cv::Mat GetPathMap();
// returns a std::string of numbers, which represent the directions along the path.Direction coding(relative to p) :
//call after CalculatePath()
//if diagonal is set to true if diagonal == false
// [0] [1] [2] [3]
// [3] [p] [4] [2] [p] [0]
// [5] [6] [7] [1]
std::string GetDirections();
//evaluates the algorithm. It's a separate function because it takes some time
//check out the ErrorCodes enum to decode the returned values
ErrorCodes CalculatePath();
};
I am also attaching the .cpp for this class
#include "Pathfinding.h"
bool Pathfinding::Point2A::operator==(const Point2A other) {
return x == other.x && y == other.y;
}
bool Pathfinding::CompByPos::operator()(const Point2A a, const Point2A b) const
{
if (a.x == b.x)
return a.y > b.y;
else
return a.x > b.x;
}
bool Pathfinding::CompByFScore::operator()(const Point2A a, const Point2A b)
{
return a.fScore > b.fScore;
}
float Pathfinding::Distance(Point2A a, Point2A b)
{
float x = static_cast<float>(a.x - b.x);
float y = static_cast<float>(a.y - b.y);
return sqrtf(x*x + y*y);
}
Pathfinding:: Point2A* Pathfinding::GetNeighbors(Point2A p, int& arraySize)
{
arraySize = 0;
uchar size;
if (diagonal)
size = 8;
else
size = 4;
Point2A* ret = new Point2A[size];
for (int i = 0; i < size; i++) {
int x, y;
if (diagonal)
{
x = p.x + diagonalDirX[i];
y = p.y + diagonalDirY[i];
}
else
{
x = p.x + nonDiagonalDirX[i];
y = p.y + nonDiagonalDirY[i];
}
if (!OnMap(x, y))
continue;
float level = p.level + 1.f + (255 - blurmap.at<cv::Vec3b>(y, x)[2]) / 255.f * wall_weight;
Point2A n = Point2A(x, y, level, i);
if (diagonal && (i == 0 || i == 2 || i == 5 || i == 7))
n.level += 0.414213f;
ret[arraySize] = n;
arraySize++;
}
return ret;
}
void Pathfinding::InitValues()
{
astar_weight = 0.3f;
wall_weight = 10.f;
blur_size = 11;
diagonal = true;
calculated = false;
}
bool Pathfinding::IsWall(Point2A p)
{
if (mapimg.at<cv::Vec3b>(p.y, p.x) == cv::Vec3b(0, 0, 0))
return true;
return false;
}
bool Pathfinding::OnMap(int x, int y)
{
if (x >= 0 && y >= 0 && x < mcols && y < mrows)
return true;
return false;
}
const int Pathfinding::diagonalDirX[] = { -1, 0, 1, -1, 1, -1, 0, 1 };
const int Pathfinding::diagonalDirY[] = { -1, -1, -1, 0, 0, 1, 1, 1 };
const int Pathfinding::nonDiagonalDirX[] = { 1, 0, -1, 0 };
const int Pathfinding::nonDiagonalDirY[] = { 0, 1, 0, -1 };
Pathfinding::Pathfinding()
{
InitValues();
}
Pathfinding::Pathfinding(cv::Mat map, bool _diagonal)
{
InitValues();
SetMap(map);
diagonal = _diagonal;
}
void Pathfinding::SetMap(cv::Mat map)
{
if (!map.empty())
{
mapimg = map;
calculated = false;
mrows = map.rows;
mcols = map.cols;
GaussianBlur(mapimg, blurmap, cv::Size(blur_size, blur_size), 0);
}
}
void Pathfinding::SetAWeight(float weight)
{
if (astar_weight != weight)
{
astar_weight = weight;
calculated = false;
}
}
void Pathfinding::SetDiagonal(bool _diagonal)
{
if (diagonal != _diagonal)
{
diagonal = _diagonal;
calculated = false;
}
}
void Pathfinding::SetWallWeight(float weight, int avoidZoneLevel)
{
if (wall_weight == weight && blur_size == 2 * avoidZoneLevel + 1)
return;
wall_weight = weight;
if (avoidZoneLevel >= 0)
blur_size = 2 * avoidZoneLevel + 1;
calculated = false;
}
void Pathfinding::SetStart(int x, int y)
{
if (!mapimg.empty())
{
if (OnMap(x, y))
{
start = Point2A(x, y);
calculated = false;
}
}
}
void Pathfinding::SetDestination(int x, int y)
{
if (!mapimg.empty())
{
if (OnMap(x, y))
{
dest = Point2A(x, y);
calculated = false;
}
}
}
cv::Mat Pathfinding::GetPathMap()
{
if (calculated) return pathmap;
else return cv::Mat();
}
std::string Pathfinding::GetDirections()
{
if (calculated) return dir;
else return std::string();
}
Pathfinding::ErrorCodes Pathfinding::CalculatePath()
{
if (calculated)
return AlreadyCalculated;
if (mapimg.empty())
return NoMap;
if (IsWall(start))
return StartIsWall;
if (IsWall(dest))
return DestIsWall;
dir = std::string();
mapimg.copyTo(pathmap);
int **closedSet = new int*[mrows];
float **openSet = new float*[mrows];
for (int i = 0; i < mrows; i++) {
closedSet[i] = new int[mcols];
openSet[i] = new float[mcols];
for (int j = 0; j < mcols; j++) {
closedSet[i][j] = 0;
openSet[i][j] = -1.0f;
}
}
std::priority_queue<Pathfinding::Point2A, std::vector<Point2A>, CompByFScore> openSetQue[2];
int osq = 0;
std::map <Pathfinding::Point2A, Pathfinding::Point2A, CompByPos> cameFrom;
start.fScore = Distance(start, dest);
openSetQue[osq].push(start);
openSet[start.y][start.x] = 0.0f;
while (openSetQue[osq].size() != 0) {
Point2A current = openSetQue[osq].top();
if (current == dest) {
while (cameFrom.size() != 0) {
pathmap.at<cv::Vec3b>(current.y, current.x) = cv::Vec3b(0, 0, 255);
dir = std::to_string(current.dir) + dir;
auto it = cameFrom.find(current);
Point2A keytmp = current;
if (it == cameFrom.end()) {
for (int i = 0; i < mrows; i++) {
delete openSet[i];
delete closedSet[i];
}
delete openSet;
delete closedSet;
calculated = true;
dir = dir.substr(1, dir.length() - 1);
return NoError;
}
current = cameFrom[current];
cameFrom.erase(keytmp);
}
}
openSetQue[osq].pop();
closedSet[current.y][current.x] = 1;
int arraySize;
Point2A *neighbors = GetNeighbors(current, arraySize);
for (int i = 0; i < arraySize; i++) {
Point2A neighbor = neighbors[i];
if (closedSet[neighbor.y][neighbor.x] == 1)
continue;
if (IsWall(neighbor)) {
closedSet[neighbor.y][neighbor.x] = 1;
continue;
}
float ngScore = neighbor.level;
if (openSet[neighbor.y][neighbor.x] == -1.0f || ngScore < openSet[neighbor.y][neighbor.x]) {
cameFrom[neighbor] = current;
neighbor.fScore = ngScore + Distance(neighbor, dest) * astar_weight;
if (openSet[neighbor.y][neighbor.x] == -1.0f) {
openSet[neighbor.y][neighbor.x] = ngScore;
openSetQue[osq].push(neighbor);
}
else {
openSet[neighbor.y][neighbor.x] = ngScore;
while (!(neighbor == openSetQue[osq].top())) {
openSetQue[1 - osq].push(openSetQue[osq].top());
openSetQue[osq].pop();
}
openSetQue[osq].pop();
if (openSetQue[osq].size() >= openSetQue[1 - osq].size()) {
osq = 1 - osq;
}
while (!openSetQue[osq].empty()) {
openSetQue[1 - osq].push(openSetQue[osq].top());
openSetQue[osq].pop();
}
osq = 1 - osq;
openSetQue[osq].push(neighbor);
}
}
}
delete neighbors;
}
return NoPath;
}
Here is my main file .cpp too :
#include"Pathfinding.h"
#include<opencv2\highgui\highgui.hpp>
#include<iostream>
Pathfinding pathfinding;
cv::Mat mapimg;
void DisplayMap()
{
cv::Mat tmp;
cv::imshow("Path", tmp);
}
int main()
{
//Open and load the map
mapimg = cv::imread("test.png");
pathfinding.SetMap(mapimg);
pathfinding.SetWallWeight(0.f, 0);
pathfinding.SetStart(1, 1);
pathfinding.SetDestination(39, 53);
pathfinding.SetDiagonal(false);
DisplayMap();
}
I think I am using Pathfinding class twice in the definition of the functions in .cpp ( i.e. Line 29 in .cpp file > Pathfinding:: Point2A* Pathfinding::GetNeighbors(Point2A p, int& arraySize)
My intention is not to throw a bunch of codes at the people but to give them an complete picture for the problem so that people can provide me some useful suggestions. My apologies for this.
My deadlines are near and I am constrained with time. Can someone suggest me some solutions.
I assume your project settings are the problem for this (your write you did a working minimalistic example, so you indeed use right libs and includes).
Please check what the include and lib paths evaluate to (check this inside the configuration site). Maybe you see they are relative paths or a makro was set wrong.
Usually, an "UNRESOLVED EXTERNAL" error means you did not link the right lib (32/64 debug/release these are 4 different combinations!) or the path to the lib is wrong.
See this answer,
If you DID explicitly set up linking with all the necessary libraries,
but linking errors still show, you might be mixing up 64/32 bit
libraries and application.
BUILD -> Configration Manager. check 'platform' is 'x64'
Check that your Project -> Properties -> VC++ Directories -> Library Directories, includes the path where the OpenCV libraries are
And Linker -> General -> Aditional Library Directories
C:\opencv\build\x64\vc11\lib
(on a 64-bit machine running VS2012, it will vary on other setups).
I have an std::list that I'm trying to sort based on some calculations. Point2D is a struct with only int no, double x, and double y;
Here's the method that contains my list.sort code:
std::vector<Point2D> GrahamScan::getSortedPointSet(std::vector<Point2D> points) {
Point2D lowest = getLowestPoint(points);
std::list<Point2D> list;
for (int i = 0; i < (int)points.size(); i++) {
list.push_back(points[i]);
}
list.sort(compare_points);
std::vector<Point2D> temp;
for (int i = 0; i < (int)list.size(); i++) {
temp.push_back(list.front());
list.pop_front();
}
return temp;
}
And here's the compare_points method I wrote:
bool GrahamScan::compare_points(const Point2D& a, const Point2D& b) {
if (a.x == b.x && a.y == b.y) {
return false;
}
double thetaA = atan2((long)a.y - lowest.y, (long)a.x - lowest.x);
double thetaB = atan2((long)b.y - lowest.y, (long)b.x - lowest.x);
if (thetaA < thetaB) {
return false;
}
else if (thetaA > thetaB) {
return true;
}
else {
double distanceA = sqrt((((long)lowest.x - a.x) * ((long)lowest.x - a.x)) +
(((long)lowest.y - a.y) * ((long)lowest.y - a.y)));
double distanceB = sqrt((((long)lowest.x - b.x) * ((long)lowest.x - b.x)) +
(((long)lowest.y - b.y) * ((long)lowest.y - b.y)));
if (distanceA < distanceB) {
return false;
}
else {
return true;
}
}
}
The error Visual Studio is spitting out at me is "GrahamScan::compare_points":non-standard syntax; use '&' to create a pointer to member"
I don't have much experience in C++, but I'm trying to convert some Java code that uses a TreeSet to C++ and this is my attempt.
Any assistance would be appreciated.
If you want to keep compare_points in GrahamScan namespace you need to make it static:
static bool GrahamScan::compare_points
The reason the compiler complains is that compare_points is a member function. It needs a GrahamScan object to be applied on. Behind the curtains the real function signature of compare_points is something like bool compare_points(GrahamScan *this, const Point2D& a, const Point2D& b). So either make it static or don't define it as a member function.
Once you make compare_points static, your lowest variable will no longer be accessible to it. Easier way to work around that is to make lowest also static:
class GrahamScan
{
// declaration is inside class
static Point2D lowest;
}
// definition is outside class
Point2D GrahamScan::lowest;
and use it like this:
std::vector<Point2D> GrahamScan::getSortedPointSet(std::vector<Point2D> points)
{
GrahamScan::lowest = getLowestPoint(points);
//...
}
So I've made for myself a point printing class, that is supposed to have the user enter in 2-tuples; that is, x and y, that then prints them back to the user in ^order,^ where order means p1=(x,y)
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
class Point2D {
public:
Point2D();
Point2D(double a, double b);
double getx();
double gety();
void setx(double a);
void sety(double b);
virtual void print();
virtual void print(int a);
double angle();
private:
double x;
double y;
};
bool operator<( Point2D a , Point2D b );
int main() {
double my_x=-999;
double my_y=-999;
string my_color;
double my_weight;
vector<Point2D*> points;
cout << "Welcome to Point Printer! Please insert the x-and y-coordinates for your points and I will print them in sorted order! Just one rule, the point (0,0) is reserved as the terminating point, so when you are done enter (0,0).\n";
while(true)
{
cout << "x = ";
cin>>my_x;
cout << "y = ";
cin>>my_y;
if((my_x == 0)&&(my_y==0))
{
break;
}
points.push_back(new Point2D(my_x, my_y));
}
sort(points.begin(), points.end());
cout << "\n\n";
cout << "Your points are\n\n";
for(int i=0;i<points.size();i++)
{
cout<<i+1<<": ";
(*points[i]).print(); cout<<endl; // this is the printing gadget
}
for(int i=0; i<points.size(); i++)
{
delete points[i];
}
cout << endl << endl;
return 0;
}
double Point2D::angle()
{
double Angle = atan2(y,x);
if(Angle < 0)
{
return Angle + 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
}
return Angle;
}
bool operator< (Point2D a, Point2D b)
{
if (a.getx()*a.getx()+a.gety()*a.gety() < b.getx()*b.getx()+b.gety()*b.gety())
{
return true;
}
else if (a.getx()*a.getx()+a.gety()*a.gety() > b.getx()*b.getx()+b.gety()*b.gety())
{
return false;
}
if (a.getx()*a.getx()+a.gety()*a.gety() ==b.getx()*b.getx()+b.gety()*b.gety())
{
if (a.angle() < b.angle())
{
return true;
}
else if (a.angle() > b.angle())
{
return false;
}
}
return true;
}
Point2D::Point2D() { x = 0; y = 0; return;}
Point2D::Point2D(double a, double b) { x = a; y = b; return;}
double Point2D::getx() { return x;}
double Point2D::gety() { return y;}
void Point2D::setx(double a) { x = a; return; }
void Point2D::sety(double b) { y = b; return; }
void Point2D::print() {
cout<<"("<<x<<","<<y<<")";
return;
}
void Point2D::print(int a) {
print(); cout<<endl;
}
What I'm having trouble with is either one of the following:
sort
angle()
operator<(Point2D a, Point2D b)
Something different entirely...
In particular, the following points:
x = 1
y = 2
x = 2
y = 3
x = 1.1
y = 2.2
x = -10
y = 10
x = -5
y = -3
x = -5
y = 3
x = 5
y = -3
x = 5
y = 3
x = 0
y = 0
are not sorted in the correct order.
Any help would be much appreciated. Thank you.
The problem (or one of them) is the final statement in your comparison function.
return true;
Look at this block:
if (a.getx()*a.getx()+a.gety()*a.gety() ==b.getx()*b.getx()+b.gety()*b.gety())
{
if (a.angle() < b.angle())
{
return true;
}
else if (a.angle() > b.angle())
{
return false;
}
}
First of all, if we've gotten to this point, we've determined that the (x*x + y*y) calculations for both a and b are equal. Now let's assume that the angle is also equal. What happens? The first test fails because a.angle() is not less than b.angle(). Then the second test fails because a.angle() is not greater than b.angle(). Then you return true. In other words, you're saying that it is true that a is less than b, even though by all rights, they should be considered equal, and so you should return false. Instead of multiple tests on the angle, you can just return a.angle() < b.angle();, and that should do the trick. With some additional simplifications, your function should look something like this:
bool operator<(Point2d a, Point2d b)
{
double A = a.getx()*a.getx()+a.gety()*a.gety();
double B = b.getx()*b.getx()+b.gety()*b.gety();
if (A < B) return true;
if (A > B) return false;
return a.angle() < b.angle();
}
The problem is probably that you are storing and sorting pointers, not objects. The points will be compared not with your operator but their addresses. Try change points to vector<Point2d>
First of all just use (if your are just planning to sort 2D points) :
(Edit : See Benjamin Lindley comments below.)
bool operator < ( Point2D a, Point2D b)
{
return a.getx() < b.getx() ||
(a.getx()==b.getx() && a.gety()< b.gety() );
}
Another thing if use use std::cout in operator < ( Point2D a, Point2D b), you will notice it won't be called anytime.
The reason is this:
vector<Point2D*> points; // Vector of Point2D*
but bool operator< (Point2D a, Point2D b) is used for comparision.
Suggested Fixes:
vector<Point2D> points;
points.push_back(Point2D(my_x, my_y));
And accordingly, wherever applicable.
Also you can't define anything like
bool operator<(const Point2D* a, const Point2D* b)
Because of this:
C++03 standard, ยง13.5 [over.oper] p6:
An operator function shall either be a non-static member function or
be a non-member function and have at least one parameter whose type is
a class, a reference to a class, an enumeration, or a reference to an
enumeration.