// Filename: bearing.cpp
// Author: Joshua Boyce (aka Chazwazza)
// Purpose: Program used to find the bearing between two arbitrary points (from 0 degrees, rotating clockwise)
// Important: Program assumes that positive 'x' on our plane is 0 deg (+x = 0, -y = 90, -x = 180, +y = 270)
// Include declarations
#include <iostream>
#include <iomanip>
#include <ios>
#include <string>
#include <vector>
#include <sstream>
#include <cmath>
#include <stdexcept>
// Definitions
#define PI 3.14159265358979323846
// Using declarations
using std::cin; using std::cout;
using std::vector; using std::endl;
using std::string; using std::istringstream;
using std::setprecision; using std::fixed;
using std::runtime_error; using std::range_error;
using std::domain_error;
// Function declarations
void split_string(string& str, string& delim, vector<double>& results);
double string_to_double(const string& s);
double calculate_bearing(vector<double>& point_one, vector<double>& point_two, unsigned int& deg_rad);
int main(){
// Variables
vector<double> point_one;
vector<double> point_two;
string temp_one;
string temp_two;
string delimiter = ",";
double result = 0;
unsigned int deg_rad;
// Greet the user and get the points to be used
cout << endl << "Welcome to Chazwazza's bearing calculator." << endl;
cout << "Important: Program assumes that positive 'x' on our plane is 0 deg (+x = 0, -y = 90, -x = 180, +y = 270)" << endl;
cout << "Please enter the coordinates of your first point, delimited by a comma: ";
cin >> temp_one;
cout << "Please enter the coordinates of your second point, delimited by a comma: ";
cin >> temp_two;
cout << "Would you like the answer to be displayed in degrees or radians? (0 for degrees, 1 for radians): ";
cin >> deg_rad;
// Split the strings and push back each coordinate into the associated vector
try{
split_string(temp_one,delimiter,point_one);
split_string(temp_two,delimiter,point_two);
} catch (runtime_error e){
cout << e.what() << "Software will now abort.";
return 1;
}
// Calculate the bearing between the two points
try{
result = calculate_bearing(point_one,point_two,deg_rad);
} catch (range_error e) {
cout << e.what();
return 1;
} catch (domain_error e) {
cout << e.what();
return 1;
}
// Get and set the precision from the user and display the answer
int d;
cout << "Please choose how many decimal places you would like the answer to be given to: ";
if (!(cin >> d)){
cout << "Invalid amount of decimal places! Software will now abort.";
return 1;
}
cout << fixed << "The bearing between the given points is " << setprecision(d) << result << endl;
return 0;
}
// Function used to split a string based on a delimiter and push it into a vector
void split_string(string& str, string& delim, vector<double>& results){
try{
int i;
while ((i = str.find_first_of(delim)) != str.npos){
if (i > 0)
results.push_back(string_to_double(str.substr(0,i)));
str = str.substr(i+1);
}
if (str.length() > 0)
results.push_back(string_to_double(str));
} catch (runtime_error e){
throw e;
}
}
// Function used to convert a string to a double
double string_to_double(const string& s){
istringstream i(s);
double x;
if (!(i >> x))
throw runtime_error("Conversion of \"" + s + "\" from string to double failed. ");
return x;
}
// Function to calculate the bearing between the two points
double calculate_bearing(vector<double>& point_one, vector<double>& point_two, unsigned int& deg_rad){
double result = 0;
if (point_one.size() != 2 || point_two.size() != 2) {
throw range_error("Null or invalid dimension amount, you must be using exactly two dimensions!");
} else {
try {
if (deg_rad == 0) {
if (point_one[1] > point_two[1]){
result = acos((pow((point_one[0] - point_two[0]),2) + sqrt(pow((point_one[1] - point_two[1]),2) + pow((point_one[0] - point_two[0]),2)) - pow((point_one[1] - point_two[1]),2)) / (2 * fabs(point_one[0] - point_two[0]) * sqrt(pow((point_one[1] - point_two[1]),2) + pow((point_one[0] - point_two[0]),2)))) * 180 / PI;
} else if (point_one[1] < point_two[1]){
result = (acos((pow((point_one[0] - point_two[0]),2) + sqrt(pow((point_one[1] - point_two[1]),2) + pow((point_one[0] - point_two[0]),2)) - pow((point_one[1] - point_two[1]),2)) / (2 * fabs(point_one[0] - point_two[0]) * sqrt(pow((point_one[1] - point_two[1]),2) + pow((point_one[0] - point_two[0]),2)))) * 180 / PI) + 180;
} else if (point_one[1] == point_two[1]){
result = 180;
}
} else if (deg_rad == 1) {
if (point_one[1] > point_two[1]){
result = acos((pow((point_one[0] - point_two[0]),2) + sqrt(pow((point_one[1] - point_two[1]),2) + pow((point_one[0] - point_two[0]),2)) - pow((point_one[1] - point_two[1]),2)) / (2 * fabs(point_one[0] - point_two[0]) * sqrt(pow((point_one[1] - point_two[1]),2) + pow((point_one[0] - point_two[0]),2))));
} else if (point_one[1] < point_two[1]){
result = acos((pow((point_one[0] - point_two[0]),2) + sqrt(pow((point_one[1] - point_two[1]),2) + pow((point_one[0] - point_two[0]),2)) - pow((point_one[1] - point_two[1]),2)) / (2 * fabs(point_one[0] - point_two[0]) * sqrt(pow((point_one[1] - point_two[1]),2) + pow((point_one[0] - point_two[0]),2))));
} else if (point_one[1] == point_two[1]){
result = PI;
}
} else {
throw range_error("Invalid selection of degrees or radians, value must be zero or one.");
}
} catch (domain_error e) {
throw e;
}
}
return result;
}