//--------------------------------------------------------------------------- // // CS488 -- Introduction to Computer Graphics // // algebra.hpp/algebra.cpp // // Classes and functions for manipulating points, vectors, matrices, // and colours. You probably won't need to modify anything in these // two files. // // University of Waterloo Computer Graphics Lab / 2003 // //--------------------------------------------------------------------------- #ifndef CS488_ALGEBRA_HPP #define CS488_ALGEBRA_HPP #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif class Point2D { public: Point2D() { v_[0] = 0.0; v_[1] = 0.0; } Point2D(double x, double y) { v_[0] = x; v_[1] = y; } Point2D(const Point2D& other) { v_[0] = other.v_[0]; v_[1] = other.v_[1]; } Point2D& operator =(const Point2D& other) { v_[0] = other.v_[0]; v_[1] = other.v_[1]; return *this; } double& operator[](size_t idx) { return v_[ idx ]; } double operator[](size_t idx) const { return v_[ idx ]; } private: double v_[2]; }; class Point3D { public: Point3D() { v_[0] = 0.0; v_[1] = 0.0; v_[2] = 0.0; } Point3D(double x, double y, double z) { v_[0] = x; v_[1] = y; v_[2] = z; } Point3D(const Point3D& other) { v_[0] = other.v_[0]; v_[1] = other.v_[1]; v_[2] = other.v_[2]; } double dot(const Point3D& other) const { return v_[0]*other.v_[0] + v_[1]*other.v_[1] + v_[2]*other.v_[2]; } Point3D& operator =(const Point3D& other) { v_[0] = other.v_[0]; v_[1] = other.v_[1]; v_[2] = other.v_[2]; return *this; } double& operator[](size_t idx) { return v_[ idx ]; } double operator[](size_t idx) const { return v_[ idx ]; } double *begin() const { return (double*)v_; } Point3D cross(const Point3D& other) const { return Point3D( v_[1]*other[2] - v_[2]*other[1], v_[2]*other[0] - v_[0]*other[2], v_[0]*other[1] - v_[1]*other[0]); } private: double v_[3]; }; class Vector3D { public: Vector3D() { v_[0] = 0.0; v_[1] = 0.0; v_[2] = 0.0; } Vector3D(double x, double y, double z) { v_[0] = x; v_[1] = y; v_[2] = z; } Vector3D(const Vector3D& other) { v_[0] = other.v_[0]; v_[1] = other.v_[1]; v_[2] = other.v_[2]; } Vector3D& operator =(const Vector3D& other) { v_[0] = other.v_[0]; v_[1] = other.v_[1]; v_[2] = other.v_[2]; return *this; } double& operator[](size_t idx) { return v_[ idx ]; } double operator[](size_t idx) const { return v_[ idx ]; } double dot(const Vector3D& other) const { return v_[0]*other.v_[0] + v_[1]*other.v_[1] + v_[2]*other.v_[2]; } double length2() const { return v_[0]*v_[0] + v_[1]*v_[1] + v_[2]*v_[2]; } double length() const { return sqrt(length2()); } double normalize(); Vector3D cross(const Vector3D& other) const { return Vector3D( v_[1]*other[2] - v_[2]*other[1], v_[2]*other[0] - v_[0]*other[2], v_[0]*other[1] - v_[1]*other[0]); } private: double v_[3]; }; inline Point3D operator *(double s, const Point3D& v) { return Point3D(s*v[0], s*v[1], s*v[2]); } inline Vector3D operator *(double s, const Vector3D& v) { return Vector3D(s*v[0], s*v[1], s*v[2]); } inline Vector3D operator +(const Vector3D& a, const Vector3D& b) { return Vector3D(a[0]+b[0], a[1]+b[1], a[2]+b[2]); } inline Point3D operator +(const Point3D& a, const Point3D& b) { return Point3D(a[0]+b[0], a[1]+b[1], a[2]+b[2]); } inline Point3D operator +(const Point3D& a, const Vector3D& b) { return Point3D(a[0]+b[0], a[1]+b[1], a[2]+b[2]); } /*inline Point3D operator -(const Point3D& a, const Point3D& b) { return Point3D(a[0]-b[0], a[1]-b[1], a[2]-b[2]); }*/ inline Vector3D operator -(const Point3D& a, const Point3D& b) { return Vector3D(a[0]-b[0], a[1]-b[1], a[2]-b[2]); } inline Vector3D operator -(const Vector3D& a, const Vector3D& b) { return Vector3D(a[0]-b[0], a[1]-b[1], a[2]-b[2]); } inline Vector3D operator -(const Vector3D& a) { return Vector3D(-a[0], -a[1], -a[2]); } inline Point3D operator -(const Point3D& a, const Vector3D& b) { return Point3D(a[0]-b[0], a[1]-b[1], a[2]-b[2]); } inline Vector3D cross(const Vector3D& a, const Vector3D& b) { return a.cross(b); } inline Point3D cross(const Point3D& a, const Point3D& b) { return a.cross(b); } inline std::ostream& operator <<(std::ostream& os, const Point2D& p) { return os << "p<" << p[0] << "," << p[1] << ">"; } inline std::ostream& operator <<(std::ostream& os, const Point3D& p) { return os << "p<" << p[0] << "," << p[1] << "," << p[2] << ">"; } inline std::ostream& operator <<(std::ostream& os, const Vector3D& v) { return os << "v<" << v[0] << "," << v[1] << "," << v[2] << ">"; } class Matrix4x4; class Vector4D { public: Vector4D() { v_[0] = 0.0; v_[1] = 0.0; v_[2] = 0.0; v_[3] = 0.0; } Vector4D(double x, double y, double z, double w) { v_[0] = x; v_[1] = y; v_[2] = z; v_[3] = w; } Vector4D(const Vector4D& other) { v_[0] = other.v_[0]; v_[1] = other.v_[1]; v_[2] = other.v_[2]; v_[3] = other.v_[3]; } Vector4D& operator =(const Vector4D& other) { v_[0] = other.v_[0]; v_[1] = other.v_[1]; v_[2] = other.v_[2]; v_[3] = other.v_[3]; return *this; } double& operator[](size_t idx) { return v_[ idx ]; } double operator[](size_t idx) const { return v_[ idx ]; } private: double v_[4]; }; typedef Vector4D Point4D; class Matrix4x4 { public: Matrix4x4() { // Construct an identity matrix std::fill(v_, v_+16, 0.0); v_[0] = 1.0; v_[5] = 1.0; v_[10] = 1.0; v_[15] = 1.0; } Matrix4x4(const Matrix4x4& other) { std::copy(other.v_, other.v_+16, v_); } Matrix4x4(const Vector4D row1, const Vector4D row2, const Vector4D row3, const Vector4D row4) { v_[0] = row1[0]; v_[1] = row1[1]; v_[2] = row1[2]; v_[3] = row1[3]; v_[4] = row2[0]; v_[5] = row2[1]; v_[6] = row2[2]; v_[7] = row2[3]; v_[8] = row3[0]; v_[9] = row3[1]; v_[10] = row3[2]; v_[11] = row3[3]; v_[12] = row4[0]; v_[13] = row4[1]; v_[14] = row4[2]; v_[15] = row4[3]; } Matrix4x4(double *vals) { std::copy(vals, vals + 16, (double*)v_); } Matrix4x4& operator=(const Matrix4x4& other) { std::copy(other.v_, other.v_+16, v_); return *this; } Vector4D getRow(size_t row) const { return Vector4D(v_[4*row], v_[4*row+1], v_[4*row+2], v_[4*row+3]); } double *getRow(size_t row) { return (double*)v_ + 4*row; } Vector4D getColumn(size_t col) const { return Vector4D(v_[col], v_[4+col], v_[8+col], v_[12+col]); } Vector4D operator[](size_t row) const { return getRow(row); } double *operator[](size_t row) { return getRow(row); } Matrix4x4 transpose() const { return Matrix4x4(getColumn(0), getColumn(1), getColumn(2), getColumn(3)); } Matrix4x4 invert() const; const double *begin() const { return (double*)v_; } const double *end() const { return begin() + 16; } private: double v_[16]; }; inline Vector4D operator -(const Vector4D& a, const Vector4D& b) { return Vector4D(a[0]-b[0], a[1]-b[1], a[2]-b[2], a[3]-b[3]); } inline Matrix4x4 operator *(const Matrix4x4& a, const Matrix4x4& b) { Matrix4x4 ret; for(size_t i = 0; i < 4; ++i) { Vector4D row = a.getRow(i); for(size_t j = 0; j < 4; ++j) { ret[i][j] = row[0] * b[0][j] + row[1] * b[1][j] + row[2] * b[2][j] + row[3] * b[3][j]; } } return ret; } inline Point2D operator *(const Matrix4x4& M, const Point2D& p) { return Point2D( p[0] * M[0][0] + p[1] * M[0][1], p[0] * M[1][0] + p[1] * M[1][1]); } inline Vector3D operator *(const Matrix4x4& M, const Vector3D& v) { return Vector3D( v[0] * M[0][0] + v[1] * M[0][1] + v[2] * M[0][2], v[0] * M[1][0] + v[1] * M[1][1] + v[2] * M[1][2], v[0] * M[2][0] + v[1] * M[2][1] + v[2] * M[2][2]); } inline Point3D operator *(const Matrix4x4& M, const Point3D& p) { return Point3D( p[0] * M[0][0] + p[1] * M[0][1] + p[2] * M[0][2] + M[0][3], p[0] * M[1][0] + p[1] * M[1][1] + p[2] * M[1][2] + M[1][3], p[0] * M[2][0] + p[1] * M[2][1] + p[2] * M[2][2] + M[2][3]); } inline Vector3D transNorm(const Matrix4x4& M, const Vector3D& n) { return Vector3D( n[0] * M[0][0] + n[1] * M[1][0] + n[2] * M[2][0], n[0] * M[0][1] + n[1] * M[1][1] + n[2] * M[2][1], n[0] * M[0][2] + n[1] * M[1][2] + n[2] * M[2][2]); } inline std::ostream& operator <<(std::ostream& os, const Matrix4x4& M) { return os << "[" << M[0][0] << " " << M[0][1] << " " << M[0][2] << " " << M[0][3] << "]" << std::endl << "[" << M[1][0] << " " << M[1][1] << " " << M[1][2] << " " << M[1][3] << "]" << std::endl << "[" << M[2][0] << " " << M[2][1] << " " << M[2][2] << " " << M[2][3] << "]" << std::endl << "[" << M[3][0] << " " << M[3][1] << " " << M[3][2] << " " << M[3][3] << "]"; } class Colour { public: Colour(double r, double g, double b) : r_(r) , g_(g) , b_(b) {} Colour(double c) : r_(c) , g_(c) , b_(c) {} Colour(const Colour& other) : r_(other.r_) , g_(other.g_) , b_(other.b_) {} Colour& operator =(const Colour& other) { r_ = other.r_; g_ = other.g_; b_ = other.b_; return *this; } double R() const { return r_; } double G() const { return g_; } double B() const { return b_; } private: double r_; double g_; double b_; }; inline Colour operator *(double s, const Colour& a) { return Colour(s*a.R(), s*a.G(), s*a.B()); } inline Colour operator *(const Colour& a, const Colour& b) { return Colour(a.R()*b.R(), a.G()*b.G(), a.B()*b.B()); } inline Colour operator +(const Colour& a, const Colour& b) { return Colour(a.R()+b.R(), a.G()+b.G(), a.B()+b.B()); } inline std::ostream& operator <<(std::ostream& os, const Colour& c) { return os << "c<" << c.R() << "," << c.G() << "," << c.B() << ">"; } // some matrix multplier helpers inline Matrix4x4 translate(const Vector3D& amount) { //std::cerr << "Stub: Translate " << m_name << " by " << amount << std::endl; Vector4D row1, row2, row3, row4; row1 = Vector4D(1.0, 0.0, 0.0, amount[0]); row2 = Vector4D(0.0, 1.0, 0.0, amount[1]); row3 = Vector4D(0.0, 0.0, 1.0, amount[2]); row4 = Vector4D(0.0, 0.0, 0.0, 1.0); return Matrix4x4(row1, row2, row3, row4); } inline Matrix4x4 scale(const Vector3D& amount) { //std::cerr << "Stub: Translate " << m_name << " by " << amount << std::endl; Vector4D row1, row2, row3, row4; row1 = Vector4D(amount[0], 0.0, 0.0, 0.0); row2 = Vector4D(0.0, amount[1], 0.0, 0.0); row3 = Vector4D(0.0, 0.0, amount[2], 0.0); row4 = Vector4D(0.0, 0.0, 0.0, 1.0); return Matrix4x4(row1, row2, row3, row4); } // used ONLY by the model rotation // (we need to call the main rotation people directly to ensure we don't // get a wrong rotation value) inline Matrix4x4 rotate(char axis, double angle) { //std::cerr << "Stub: Joint Rotate " << m_name << " around " << axis << " by " << angle << std::endl; Matrix4x4 r; Vector4D row1, row2, row3, row4; angle = angle * 2.0 * M_PI / 360.0; // convert to radians row4 = Vector4D(0.0, 0.0, 0.0, 1.0); switch(axis) { case 'x': row1 = Vector4D(1.0, 0.0, 0.0, 0.0); row2 = Vector4D(0.0, cos(angle), -sin(angle), 0.0); row3 = Vector4D(0.0, sin(angle), cos(angle), 0.0); r = Matrix4x4(row1, row2, row3, row4); break; case 'y': row1 = Vector4D(cos(angle), 0.0, sin(angle), 0.0); row2 = Vector4D(0.0, 1.0, 0.0, 0.0); row3 = Vector4D(-sin(angle), 0.0, cos(angle), 0.0); r = Matrix4x4(row1, row2, row3, row4); break; case 'z': row1 = Vector4D(cos(angle), -sin(angle), 0.0, 0.0); row2 = Vector4D(sin(angle), cos(angle), 0.0, 0.0); row3 = Vector4D(0.0, 0.0, 1.0, 0.0); r = Matrix4x4(row1, row2, row3, row4); break; default: // identity transformation r = Matrix4x4(); break; } return r; } #endif // CS488_ALGEBRA_HPP