The comments offer the right direction. If you are going to write your minimum distance finder is C++, you should start with a simple 2d point class and then derive a class to handle 3d points from that class by adding a 3rd coordinate. If you are simply going to use separate x, y, z
coordinate and three separate arrays of double
-- you might as well write the program in C.
Writing a base class for a 2d point isn't difficult at all. The only thing you need to be mindful of in order to then derive a 3d class from it is to declare your coordinate members as protected:
so that all protected members of the 2d point class are available as protected member in the 3d class (class members are private by default and private members of the base are never accessible unless friended)
So what would a minimum 2d point base class look like? Well you would need x, y
coordinates, you would need a default constructor to set x
and y
to 0.0 when the class is constructed, a constructor to take
xand
yvalues, and then a couple of accessor functions to get the
xand
y` values for use in your distance function.
A minimum 2d point class could be:
/* 2D Cartesian Coordinate Point */
class point2_t {
protected: /* allows derived class access to x, y when inherited */
double x, y; /* private members would not be accessible */
public:
point2_t () { x = 0.0, y = 0.0; } /* constructors */
point2_t (const double a, const double b) : x{a}, y{b} { }
const double& getx () const { return x; } /* access functions */
const double& gety () const { return y; }
double dist (const point2_t& p) { /* distance function */
return sqrt ((x-p.getx()) * (x-p.getx()) +
(y-p.gety()) * (y-p.gety()));
}
};
That will allow you to initialize a 2d point with values, get the values currently set and then calculate the distance from some other 2d point. While that will work great, it would still require reading the x
and y
values from the file and then creating a point by passing the coordinates to the constructor. (your could also write a setx(double x)
and corresponding sety()
to allow you to change the x, y
values)
It would be really nice to be able to just cin >> point;
and have it set the x, y
values automatically and to be able to cout << point;
to output the coordinates. You can do so by overloading the <<
and >>
operators. That makes it really convenient to read and output the coordinate data. To do so you can add the following as member functions:
/* overload output and input operators */
friend std::ostream& operator << (std::ostream& os, const point2_t& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
friend std::istream& operator >> (std::istream& is, point2_t& p) {
is >> p.x >> p.y;
return is;
}
Once you have your 2d point class written, all you need to do is derive a 3d point class from it and add a z
coordinate and the corresponding functions to handle all three coordinates instead of two. The basic form to derive a class from a base class including the protected members of the base class is:
class derived : public base {
/* additions */
};
A simple derivation from your 2d point class for a 3d point class (including the overloading <<
and >>
operators) could be:
/* 3D Cartesian Coordinate Point derived from 2D point class */
class point_t: public point2_t {
protected:
double z; /* add z coordinate */
public:
point_t () { point2_t (0.0, 0.0); z = 0.0; }; /* default construct */
/* construct with initializer list */
point_t (const double a, const double b, const double c) :
point2_t (a, b), z{c} {}
const double& getz () const { return z; } /* add getz accessor */
double dist (const point_t& p) { /* extend distance */
return sqrt ((x-p.getx()) * (x-p.getx()) +
(y-p.gety()) * (y-p.gety()) +
(z-p.getz()) * (z-p.getz()));
}
/* extend operators */
friend std::ostream& operator << (std::ostream& os, const point_t& p) {
os << "(" << p.x << ", " << p.y << ", " << p.z << ")";
return os;
}
friend std::istream& operator >> (std::istream& is, point_t& p) {
is >> p.x >> p.y >> p.z;
return is;
}
};
Now you have a 3d point class that can calculate the distance between points. All that remains is creating an instance of the class for your 1st point, and a second temporary instance to read additional points from your file allowing you to compute the distance between the two. (a 3rd instance is handy if you want to save the coordinates for the closest point)
The only caveat with your data file is you need to discard the first line containing the x y z
heading. While you can read a the line into a string
with getline
and simply ignore it, C++ also provides a stream function .ignore()
which allows you to ignore up to the maximum number of readable characters until a delimiter is reached (the newline in this case). Simply include the limits
header and you can then use:
std::ifstream f (argv[1]); /* open file stream */
...
/* discard 1st line in file */
f.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
(either way works)
There is no need to read all the points in the file into a container to process later just to find the minimum of the distance between the first point and the rest. All you need to do is store the first point (p1
below) and then compute the distance between it and the remaining points, saving the minimum distance (distmin
below) found for each subsequent comparison. (you can also save the coordinate of the closest point if you like)
Putting that together in a short main()
could look like:
int main (int argc, char **argv) {
if (argc < 2) { /* validate argument available for filename */
std::cerr << "error: insufficient input.\n";
return 1;
}
std::ifstream f (argv[1]); /* open file stream */
point_t p1, min, tmp; /* 1st, mininum & temporary points */
/* initialize minimum distance to maximum allowable */
double distmin = std::numeric_limits<double>::max();
/* discard 1st line in file */
f.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
if (!(f >> p1)) { /* read 1st point */
std::cerr << "error: failed read of p1\n";
return 1;
}
while (f >> tmp) { /* read remaining points */
double dist = tmp.dist (p1); /* get distance from p1 */
if (dist < distmin) { /* check less than distmin? */
distmin = dist; /* set new distmin */
min = tmp; /* set new closest point */
}
}
/* output results */
std::cout << "\nclosest point to " << p1 << "\n\n" << min <<
"\n\ndistance: " << distmin << '\n';
}
The complete example would then be:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cmath>
#include <limits>
/* 2D Cartesian Coordinate Point */
class point2_t {
protected: /* allows derived class access to x, y when inherited */
double x, y; /* private members would not be accessible */
public:
point2_t () { x = 0.0, y = 0.0; } /* constructors */
point2_t (const double a, const double b) : x{a}, y{b} { }
const double& getx () const { return x; } /* access functions */
const double& gety () const { return y; }
double dist (const point2_t& p) { /* distance function */
return sqrt ((x-p.getx()) * (x-p.getx()) +
(y-p.gety()) * (y-p.gety()));
}
/* overload output and input operators */
friend std::ostream& operator << (std::ostream& os, const point2_t& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
friend std::istream& operator >> (std::istream& is, point2_t& p) {
is >> p.x >> p.y;
return is;
}
};
/* 3D Cartesian Coordinate Point derived from 2D point class */
class point_t: public point2_t {
protected:
double z; /* add z coordinate */
public:
point_t () { point2_t (0.0, 0.0); z = 0.0; }; /* default construct */
/* construct with initializer list */
point_t (const double a, const double b, const double c) :
point2_t (a, b), z{c} {}
const double& getz () const { return z; } /* add getz accessor */
double dist (const point_t& p) { /* extend distance */
return sqrt ((x-p.getx()) * (x-p.getx()) +
(y-p.gety()) * (y-p.gety()) +
(z-p.getz()) * (z-p.getz()));
}
/* extend operators */
friend std::ostream& operator << (std::ostream& os, const point_t& p) {
os << "(" << p.x << ", " << p.y << ", " << p.z << ")";
return os;
}
friend std::istream& operator >> (std::istream& is, point_t& p) {
is >> p.x >> p.y >> p.z;
return is;
}
};
int main (int argc, char **argv) {
if (argc < 2) { /* validate argument available for filename */
std::cerr << "error: insufficient input.\n";
return 1;
}
std::ifstream f (argv[1]); /* open file stream */
point_t p1, min, tmp; /* 1st, mininum & temporary points */
/* initialize minimum distance to maximum allowable */
double distmin = std::numeric_limits<double>::max();
/* discard 1st line in file */
f.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
if (!(f >> p1)) { /* read 1st point */
std::cerr << "error: failed read of p1\n";
return 1;
}
while (f >> tmp) { /* read remaining points */
double dist = tmp.dist (p1); /* get distance from p1 */
if (dist < distmin) { /* check less than distmin? */
distmin = dist; /* set new distmin */
min = tmp; /* set new closest point */
}
}
/* output results */
std::cout << "\nclosest point to " << p1 << "\n\n" << min <<
"\n\ndistance: " << distmin << '\n';
}
Example Input File
Generating a few additional random points in the same range as your values would give you a data file with 10 total points to use to validate the program, e.g.
$ cat dat/3dpoints-10.txt
x y z
0.068472 -0.024941 0.028884
-0.023238 0.028574 -0.021372
0.015325 -0.086100 0.011980
-0.028137 -0.025350 0.021614
-0.013860 0.015710 -0.022659
0.026026 -0.093600 0.019175
0.010445 -0.098790 0.023332
-0.021594 0.017428 -0.025986
0.021800 -0.027678 0.017078
-0.016704 0.017951 0.011059
Example Use/Output
Running the program will then locate the closest point to your first point (p1
) providing the following answer:
$ ./bin/point_distmin dat/3dpoints-10.txt
closest point to (0.068472, -0.024941, 0.028884)
(0.0218, -0.027678, 0.017078)
distance: 0.0482198
Look things over and let me know if you have questions. cpprefernce.com is one of the best references (aside from the standard itself). Keep that bookmark handy and take some time to get to know the language and the site.