//==================================================================
// Copyright 2002, softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// SoftSurfer makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from it's use.
// Users of this code must verify correctness for their application.
//==================================================================

#include "point.h"
#include "Vector.h"

//==================================================================
// Point Class Methods
//==================================================================

//------------------------------------------------------------------
// Constructors (add more as needed)
//------------------------------------------------------------------

// n-dim Point
Point::Point( int n, int a[]) {
	x = y = z = 0;
	err = Enot;
	switch (dimn = n) {
	case 3: z = a[2];
	case 2: y = a[1];
	case 1: x = a[0];
		break;
	default:
		err=Edim;
	}
}

Point::Point( int n, double a[]) {
	x = y = z = 0.0;
	err = Enot;
	switch (dimn = n) {
	case 3: z = a[2];
	case 2: y = a[1];
	case 1: x = a[0];
		break;
	default:
		err=Edim;
	}
}

//------------------------------------------------------------------
// IO streams
//------------------------------------------------------------------

// Read input Point format: "(%f)", "(%f, %f)", or "(%f, %f, %f)"
istream& operator>>( istream& input, Point& P) {
	char c;
	input >> c;                // skip '('
	input >> P.x;
	input >> c;                
	if (c == ')') {
		P.setdim(1);       // 1D coord
		return input;
	}
	// else                    // skip ','
	input >> P.y;
	input >> c;
	if (c == ')') {
		P.setdim(2);       // 2D coord
		return input;
	}
	// else                    // skip ','
	input >> P.z;
	P.setdim(3);               // 3D coord
	input >> c;                // skip ')'
	return input;
}

// Write output Point in format: "(%f)", "(%f, %f)", or "(%f, %f, %f)"
ostream& operator<<( ostream& output, Point P) {
	switch (P.dim()) {
	case 1:
		output << "(" << P.x << ")";
		break;
	case 2:
		output << "(" << P.x << ", " << P.y << ")";
		break;
	case 3:
		output << "(" << P.x << ", " << P.y << ", " << P.z << ")";
		break;
	default:
		output << "Error: P.dim = " << P.dim();
	}
	return output;
}

//------------------------------------------------------------------
// Assign (set) dimension
//------------------------------------------------------------------

int Point::setdim( int n) {
	switch (n) {
	case 1: y = 0;
	case 2: z = 0;
	case 3:
		return dimn = n;
	default:                      // out of range value
		err = Edim;           // just flag the error
		return ERROR_;
	}
}

//------------------------------------------------------------------
// Comparison (note: dimension must compare)
//------------------------------------------------------------------

int Point::operator==( Point Q)
{
	if (dimn != Q.dim()) return FALSE_;
	switch (dimn) {
	case 1:
		return (x==Q.x);
	case 2:
		return (x==Q.x && y==Q.y);
	case 3:
	default:
		return (x==Q.x && y==Q.y && z==Q.z);
	}
}

int Point::operator!=( Point Q)
{
	if (dimn != Q.dim()) return TRUE_;
	switch (dimn) {
	case 1:
		return (x!=Q.x);
	case 2:
		return (x!=Q.x || y!=Q.y);
	case 3:
	default:
		return (x!=Q.x || y!=Q.y || z!=Q.z);
	}
}

//------------------------------------------------------------------
// Point Vector Operations
//------------------------------------------------------------------

Vector Point::operator-( Point Q)        // Vector diff of Points
{
	Vector v;
	v.x = x - Q.x;
	v.y = y - Q.y;
	v.z = z - Q.z;
	v.dimn = max( dimn, Q.dim());
	return v;
}

Point Point::operator+( Vector v)        // +ve translation
{
	Point P;
	P.x = x + v.x;
	P.y = y + v.y;
	P.z = z + v.z;
	P.dimn = max( dimn, v.dim());
	return P;
}

Point Point::operator-( Vector v)        // -ve translation
{
	Point P;
	P.x = x - v.x;
	P.y = y - v.y;
	P.z = z - v.z;
	P.dimn = max( dimn, v.dim());
	return P;
}

Point& Point::operator+=( Vector v)        // +ve translation
{
	x += v.x;
	y += v.y;
	z += v.z;
	dimn = max( dimn, v.dim());
	return *this;
}

Point& Point::operator-=( Vector v)        // -ve translation
{
	x -= v.x;
	y -= v.y;
	z -= v.z;
	dimn = max( dimn, v.dim());
	return *this;
}

//------------------------------------------------------------------
// Point Scalar Operations (convenient but often illegal)
//        are not valid for points in general,
//        unless they are 'affine' as coeffs of 
//        a sum in which all the coeffs add to 1,
//        such as: the sum (a*P + b*Q) with (a+b == 1).
//        The programmer must enforce this (if they want to).
//------------------------------------------------------------------

Point operator*( int c, Point Q) {
	Point P;
	P.x = c * Q.x;
	P.y = c * Q.y;
	P.z = c * Q.z;
	P.dimn = Q.dim();
	return P;
}

Point operator*( double c, Point Q) {
	Point P;
	P.x = c * Q.x;
	P.y = c * Q.y;
	P.z = c * Q.z;
	P.dimn = Q.dim();
	return P;
}

Point operator*( Point Q, int c) {
	Point P;
	P.x = c * Q.x;
	P.y = c * Q.y;
	P.z = c * Q.z;
	P.dimn = Q.dim();
	return P;
}

Point operator*( Point Q, double c) {
	Point P;
	P.x = c * Q.x;
	P.y = c * Q.y;
	P.z = c * Q.z;
	P.dimn = Q.dim();
	return P;
}

Point operator/( Point Q, int c) {
	Point P;
	P.x = Q.x / c;
	P.y = Q.y / c;
	P.z = Q.z / c;
	P.dimn = Q.dim();
	return P;
}

Point operator/( Point Q, double c) {
	Point P;
	P.x = Q.x / c;
	P.y = Q.y / c;
	P.z = Q.z / c;
	P.dimn = Q.dim();
	return P;
}

//------------------------------------------------------------------
// Point Addition (also convenient but often illegal)
//    is not valid unless part of an affine sum.
//    The programmer must enforce this (if they want to).
//------------------------------------------------------------------

Point operator+( Point Q, Point R)
{
	Point P;
	P.x = Q.x + R.x;
	P.y = Q.y + R.y;
	P.z = Q.z + R.z;
	P.dimn = max( Q.dim(), R.dim());
	return P;
}

//------------------------------------------------------------------
// Affine Sums
// Returns weighted sum, even when not affine, but...
// Tests if coeffs add to 1.  If not, sets: err = Esum.
//------------------------------------------------------------------

Point asum( int n, int c[], Point Q[])
{
	int        maxd = 0;
	int        cs = 0;
	int i;
	Point      P;

	for (i=0; i<n; i++) {
		cs += c[i];
		if (Q[i].dim() > maxd)
			maxd = Q[i].dim();
	}
	if (cs != 1)                 // not an affine sum
		P.err = Esum;        // flag error, but compute sum anyway

	for (i=0; i<n; i++) {
		P.x += c[i] * Q[i].x;
		P.y += c[i] * Q[i].y;
		P.z += c[i] * Q[i].z;
	}
	P.dimn = maxd;
	return P;
}

Point asum( int n, double c[], Point Q[])
{
	int        maxd = 0;
	double     cs = 0.0;
	Point      P;
	int i;

	for (i=0; i<n; i++) {
		cs += c[i];
		if (Q[i].dim() > maxd)
			maxd = Q[i].dim();
	}
	if (cs != 1)                 // not an affine sum
		P.err = Esum;        // flag error, but compute sum anyway

	for (i=0; i<n; i++) {
		P.x += c[i] * Q[i].x;
		P.y += c[i] * Q[i].y;
		P.z += c[i] * Q[i].z;
	}
	P.dimn = maxd;
	return P;
}

//------------------------------------------------------------------
// Distance between Points
//------------------------------------------------------------------

double d( Point P, Point Q) {      // Euclidean distance
	double dx = P.x - Q.x;
	double dy = P.y - Q.y;
	double dz = P.z - Q.z;
	return sqrt(dx*dx + dy*dy + dz*dz);
}

double d2( Point P, Point Q) {     // squared distance (more efficient)
	double dx = P.x - Q.x;
	double dy = P.y - Q.y;
	double dz = P.z - Q.z;
	return (dx*dx + dy*dy + dz*dz);
}

//------------------------------------------------------------------
// Sidedness of a Point wrt a directed line P1->P2
//        - makes sense in 2D only
//------------------------------------------------------------------

double Point::isLeft( Point P1, Point P2) {
	if (dimn != 2 || P1.dim() != 2 || P2.dim() != 2) {
		err = Edim;        // flag error, but compute anyway
	}
	return ((P1.x - x) * (P2.y - y) - (P2.x - x) * (P1.y - y));
}

//------------------------------------------------------------------
// Error Routines
//------------------------------------------------------------------

char* Point::errstr() {            // return error string
	switch (err) {
	case Enot:
		return "no error";
	case Edim:
		return "error: invalid dimension for operation";
	case Esum:
		return "error: Point sum is not affine";
	default:
		return "error: unknown err value";
	}
}
