#include #include #include #include "Vector_3d.hpp" ///////////////////////////////////////////////// // This file contains all function definitions // // - All function declarations are in // // the file Vector_3d.hpp // ///////////////////////////////////////////////// // This example extends the previous Vector_3d project by // introducing operator overloading. Refer to the previous // project for a discussion of the Vector_3d class and the // vector operations themselves. // // The focus here is on how operator overloading allows // expressions involving Vector_3d objects to resemble the // corresponding mathematical expressions. // // For example, if '+' is overloaded for Vector_3d objects, // we can write // // u + v // // instead of // // add( u, v ) // // The expression u + v results in a call to // // operator+( u, v ) // // Operator overloading does not create new operators or // change their precedence or associativity. It defines what // an existing operator does when applied to specified types. ////// // These query the arguments, but do not change them ////// // Calculate the inner product of u and v. double inner( Vector_3d const &u, Vector_3d const &v ) { return u.x_*v.x_ + u.y_*v.y_ + u.z_*v.z_; } // Calculate the norm (length) of u. double norm( Vector_3d const &u ) { return std::sqrt( inner( u, u ) ); } // Calculate u + v. // // This overload allows us to write // // u + v // // instead of calling a function such as add( u, v ). // // Notice that by lining up the values that are initializing // the new vector, it is easy to see that the corresponding // components are being added. Vector_3d operator+( Vector_3d const &u, Vector_3d const &v ) { return Vector_3d{ u.x_ + v.x_, u.y_ + v.y_, u.z_ + v.z_ }; } // Calculate a*u for a scalar 'a'. // // This overload handles expressions in which the scalar // appears on the left: // // 3.0*u Vector_3d operator*( double const a, Vector_3d const &u ) { return Vector_3d{ a*u.x_, a*u.y_, a*u.z_ }; } // Calculate u*a for a scalar 'a'. // // This requires a second overload because the types of the // two arguments appear in the opposite order: // // u*3.0 // // Rather than repeating the calculation, this overload // calls the previous overload as a*u. Vector_3d operator*( Vector_3d const &u, double const a ) { return a*u; } // Apply the unary + operator to u. // // Unary + has only one operand and leaves the vector // unchanged: // // +u Vector_3d operator+( Vector_3d const &u ) { return u; } // Apply the unary - operator to u. // // Unary - returns the additive inverse: // // -u // // This reuses scalar multiplication rather than repeating // the calculation for each component. Vector_3d operator-( Vector_3d const &u ) { return (-1.0)*u; } // Calculate u - v. // // This is the binary - operator, which has two operands. // The compiler distinguishes it from unary - because the // two overloaded functions have different parameter lists. // // Subtraction is implemented using addition and unary -. Vector_3d operator-( Vector_3d const &u, Vector_3d const &v ) { return u + (-v); } // Calculate u/a for a non-zero scalar 'a'. // // Division is implemented as multiplication by 1/a. Vector_3d operator/( Vector_3d const &u, double const a ) { assert( a != 0.0 ); return (1.0/a)*u; } // Calculate the projection of u onto v. // // There is no natural C++ operator for vector projection, // so this remains an ordinary function. Vector_3d proj( Vector_3d const &u, Vector_3d const &v ) { // The inner product of v and itself is 0 if and // only if v is zero, and the projection onto the // zero vector is the zero vector. double inner_v_v{ inner( v, v ) }; if ( inner_v_v == 0.0 ) { return v; } else { return (inner( v, u )/inner_v_v)*v; } } // Calculate the cross product u x v. // // There is no natural C++ operator for the vector cross // product, so this also remains an ordinary function. Vector_3d cross( Vector_3d const &u, Vector_3d const &v ) { return Vector_3d{ u.y_*v.z_ - u.z_*v.y_, u.z_*v.x_ - u.x_*v.z_, u.x_*v.y_ - u.y_*v.x_ }; } // Determine whether u and v are equal. // // Overloading == allows us to write // // u == v // // The result is true only if all three components are equal. bool operator==( Vector_3d const &u, Vector_3d const &v ) { return (u.x_ == v.x_) && (u.y_ == v.y_) && (u.z_ == v.z_); } // Determine whether u and v are unequal. // // Rather than repeating the component comparisons, != is // implemented by negating the result of ==. bool operator!=( Vector_3d const &u, Vector_3d const &v ) { return !(u == v); } // Return a string representation of u. // // This function is unchanged in principle from the previous // Vector_3d project. std::string to_string( Vector_3d const &u ) { return "(" + std::to_string( u.x_ ) + ", " + std::to_string( u.y_ ) + ", " + std::to_string( u.z_ ) + ")"; } // Insert u into an output stream. // // Overloading << allows a Vector_3d object to be used with // an output stream in the same manner as built-in types: // // std::cout << u; // // The output stream is returned by reference. Consequently, // the result can immediately be used as the left operand of // another << operator, allowing expressions such as // // std::cout << u << std::endl; std::ostream &operator<<( std::ostream &out, Vector_3d const &u ) { out << "(" << u.x_ << ", " << u.y_ << ", " << u.z_ << ")"; return out; } ////// // These modify the first vector passed ////// // Normalize u if it is not the zero vector. // // The overloaded /= operator allows the calculation to be // expressed directly as division of the vector by its norm. void normalize( Vector_3d &u ) { double norm_u{ norm( u ) }; if ( norm_u != 0.0 ) { u /= norm_u; } } ////////////////////////////////// // Compound assignment operators ////////////////////////////////// // Add v to u and return u. // // The expression // // u += v; // // results in a call to operator+=( u, v ). // // Returning u by reference allows the result of the // assignment to be used in a larger expression. Vector_3d &operator+=( Vector_3d &u, Vector_3d const &v ) { u = u + v; return u; } // Subtract v from u and return u. // // This implementation reuses the overloaded binary - // operator rather than repeating the component calculations. Vector_3d &operator-=( Vector_3d &u, Vector_3d const &v ) { u = u - v; return u; } // Multiply u by the scalar a and return u. // // This implementation reuses the overloaded * operator. Vector_3d &operator*=( Vector_3d &u, double const a ) { u = u*a; return u; } // Divide u by the non-zero scalar a and return u. // // This implementation reuses the overloaded / operator. Vector_3d &operator/=( Vector_3d &u, double const a ) { assert( a != 0.0 ); u = u/a; return u; }