// Copyright (C) 1999-2017 // Smithsonian Astrophysical Observatory, Cambridge, MA, USA // For conditions of distribution and use, see copyright notice in "copyright" #include #include "vector.h" #include "vector3d.h" #include "fuzzy.h" // Vector::manip int Vector::separator = ios_base::xalloc(); int Vector::unit = ios_base::xalloc(); Vector::Vector(const Vector3d& a) { v[0]=a.v[0]; v[1]=a.v[1]; v[2]=1; } Vector& Vector::operator=(const Vector3d& a) { v[0]=a.v[0]; v[1]=a.v[1]; v[2]=1; return *this; } Vector& Vector::clip(const BBox& bb) { // restrict vector by bbox Vector ll(bb.ll); Vector ur(bb.ur); if (v[0]ur[0]) v[0]=ur[0]; if (v[1]ur[1]) v[1]=ur[1]; return *this; } Vector Vector::TkCanvasPs(void* canvas) { return Vector(v[0], Tk_CanvasPsY((Tk_Canvas)canvas, v[1])); } ostream& operator<<(ostream& os, const Vector& v) { unsigned char sep = (unsigned char)os.iword(Vector::separator); if (!sep) sep = ' '; unsigned char unit = (unsigned char)os.iword(Vector::unit); if (!unit) os << v.v[0] << sep << v.v[1]; else os << v.v[0] << unit << sep << v.v[1] << unit; // reset unit os.iword(Vector::unit) = '\0'; return os; } istream& operator>>(istream& s, Vector& v) { s >> v.v[0] >> v.v[1]; return s; } // Vertex ostream& operator<<(ostream& os, const Vertex& v) { os << v.vector; return os; } // Matrix Matrix& Matrix::operator*=(const Matrix& a) { Matrix r; for (int i=0; i<3; i++) for (int j=0; j<3; j++) r.m[i][j] = m[i][0]*a.m[0][j] + m[i][1]*a.m[1][j] + m[i][2]*a.m[2][j]; return *this=r; } Matrix Matrix::invert() { Matrix cc = this->cofactor(); Matrix aa = cc.adjoint(); double dd = m[0][0]*aa.m[0][0] + m[0][1]*aa.m[1][0] + m[0][2]*aa.m[2][0]; Matrix rr; for (int ii=0; ii<3; ii++ ) for (int jj=0; jj<3; jj++) rr.m[ii][jj] = aa.m[ii][jj]/dd; return rr; } Matrix Matrix::cofactor() { Matrix rr; rr.m[0][0] = +(m[1][1]*m[2][2]-m[1][2]*m[2][1]); rr.m[0][1] = -(m[1][0]*m[2][2]-m[1][2]*m[2][0]); rr.m[0][2] = +(m[1][0]*m[2][1]-m[1][1]*m[2][0]); rr.m[1][0] = -(m[0][1]*m[2][2]-m[0][2]*m[2][1]); rr.m[1][1] = +(m[0][0]*m[2][2]-m[0][2]*m[2][0]); rr.m[1][2] = -(m[0][0]*m[2][1]-m[0][1]*m[2][0]); rr.m[2][0] = +(m[0][1]*m[1][2]-m[0][2]*m[1][1]); rr.m[2][1] = -(m[0][0]*m[1][2]-m[0][2]*m[1][0]); rr.m[2][2] = +(m[0][0]*m[1][1]-m[0][1]*m[1][0]); return rr; } double Matrix::det() { return + m[0][0]*(m[1][1]*m[2][2]-m[1][2]*m[2][1]) - m[0][1]*(m[1][0]*m[2][2]-m[1][2]*m[2][0]) + m[0][2]*(m[1][0]*m[2][1]-m[1][1]*m[2][0]); } Matrix Matrix::adjoint() { Matrix rr; for (int ii=0; ii<3; ii++) for (int jj=0; jj<3; jj++) rr.m[jj][ii] = m[ii][jj]; return rr; } ostream& operator<<(ostream& os, const Matrix& m) { os << ' '; for (int i=0; i<3; i++) for (int j=0; j<2; j++) os << m.m[i][j] << ' '; return os; } istream& operator>>(istream& s, Matrix& m) { for (int i=0; i<3; i++ ) for (int j=0; j<2; j++) s >> m.m[i][j]; return s; } // Translate ostream& operator<<(ostream& os, const Translate& m) { os << ' ' << m.m[2][0] << ' ' << m.m[2][1] << ' '; return os; } istream& operator>>(istream& s, Translate& m) { s >> m.m[2][0] >> m.m[2][1]; return s; } // Scale ostream& operator<<(ostream& os, const Scale& m) { os << ' ' << m.m[0][0] << ' ' << m.m[1][1] << ' '; return os; } istream& operator>>(istream& s, Scale& m) { s >> m.m[0][0] >> m.m[1][1]; return s; } // Rotate Rotate::Rotate(double a) : Matrix() { // note: signs reverse for X-Windows (origin is upper left) m[0][0] = cos(a); m[0][1] = -sin(a); m[1][0] = sin(a); m[1][1] = cos(a); // this fixes a problem with numbers too small and tring to invert the matrix tzero(&m[0][0]); tzero(&m[0][1]); tzero(&m[1][0]); tzero(&m[1][1]); } ostream& operator<<(ostream& os, const Rotate& m) { os << ' ' << m.m[0][0] << ' ' << m.m[0][1] << ' ' << m.m[1][0] << ' ' << m.m[1][1] << ' '; return os; } istream& operator>>(istream& s, Rotate& m) { s >> m.m[0][0] >> m.m[0][1] >> m.m[1][0] >> m.m[1][1]; return s; } // BBox BBox::BBox(double a, double b, double c, double d) { // we want a 'positive' box ll.v[0] = a < c ? a : c; ll.v[1] = b < d ? b : d; ur.v[0] = a < c ? c : a; ur.v[1] = b < d ? d : b; } BBox::BBox(const Vector& l, const Vector& h) { // we want a 'positive' box ll.v[0] = l.v[0] < h.v[0] ? l.v[0] : h.v[0]; ll.v[1] = l.v[1] < h.v[1] ? l.v[1] : h.v[1]; ur.v[0] = l.v[0] < h.v[0] ? h.v[0] : l.v[0]; ur.v[1] = l.v[1] < h.v[1] ? h.v[1] : l.v[1]; } int BBox::isIn(const Vector& v) const { return !(v.v[0] < ll.v[0] || v.v[1] < ll.v[1] || v.v[0] > ur.v[0] || v.v[1] > ur.v[1]); } int BBox::isIn(const BBox& bb) const { // return 0 if outside, > 0 if intersection // = 4 if inside BBox b = bb; return isIn(b.ll) + isIn(b.ur) + isIn(b.ul()) + isIn(b.lr()); } BBox& BBox::bound(const Vector& v) { if (v.v[0] < ll[0]) ll[0] = v.v[0]; if (v.v[1] < ll[1]) ll[1] = v.v[1]; if (v.v[0] > ur[0]) ur[0] = v.v[0]; if (v.v[1] > ur[1]) ur[1] = v.v[1]; return *this; } BBox& BBox::bound(BBox b) { this->bound(b.ll); this->bound(b.lr()); this->bound(b.ur); this->bound(b.ul()); return *this; } BBox intersect(const BBox& a, const BBox& b) { // test for obvious int ab = a.isIn(b); int ba = b.isIn(a); // no intersection? if (ab==0 && ba == 0) { // maybe they are just crossed, check the centers int abc = a.isIn(((BBox&)b).center()); int bac = b.isIn(((BBox&)a).center()); if (abc==0 && bac==0) return BBox(); } if (ab == 4) // b is inside a return b; if (ba == 4) // a is inside b return a; // else, there seems to be some overlap BBox r; r.ll.v[0] = (a.ll.v[0] > b.ll.v[0]) ? a.ll.v[0] : b.ll.v[0]; r.ll.v[1] = (a.ll.v[1] > b.ll.v[1]) ? a.ll.v[1] : b.ll.v[1]; r.ur.v[0] = (a.ur.v[0] < b.ur.v[0]) ? a.ur.v[0] : b.ur.v[0]; r.ur.v[1] = (a.ur.v[1] < b.ur.v[1]) ? a.ur.v[1] : b.ur.v[1]; return r; } ostream& operator<<(ostream& os, const BBox& b) { os << b.ll << ' ' << b.ur; return os; } // Cohen–Sutherland clipping algorithm // bounded diagonally by (0,0), and (width, height) const int INSIDE = 0; // 0000 const int LEFT = 1; // 0001 const int RIGHT = 2; // 0010 const int BOTTOM = 4; // 0100 const int TOP = 8; // 1000 static int calcOutCode(Vector vv, int width, int height) { int code = INSIDE; if (vv[0] < 0) // to the left of clip window code |= LEFT; else if (vv[0] > width) // to the right of clip window code |= RIGHT; if (vv[1] < 0) // below the clip window code |= BOTTOM; else if (vv[1] > height) // above the clip window code |= TOP; return code; } int clip(Vector* v0, Vector* v1, int width, int height) { int outcode0 = calcOutCode(*v0, width, height); int outcode1 = calcOutCode(*v1, width, height); int accept = false; while (true) { if (!(outcode0 | outcode1)) { // Bitwise OR is 0. Trivially accept and get out of loop accept = true; break; } else if (outcode0 & outcode1) { // Bitwise AND is not 0. Trivially reject and get out of loop break; } else { // At least one endpoint is outside the clip rectangle; pick it. int outcodeOut = outcode0 ? outcode0 : outcode1; // Now find the intersection point // y = y0 + slope * (x - x0) // x = x0 + (1 / slope) * (y - y0) double x, y; if (outcodeOut & TOP) { // point is above the clip rectangle x = (*v0)[0] + ((*v1)[0]-(*v0)[0]) * (height-(*v0)[1]) / ((*v1)[1]-(*v0)[1]); y = height; } else if (outcodeOut & BOTTOM) { // point is below the clip rectangle x = (*v0)[0] + ((*v1)[0]-(*v0)[0]) * (0-(*v0)[1]) / ((*v1)[1]-(*v0)[1]); y = 0; } else if (outcodeOut & RIGHT) { // point is to the right of clip rectangle y = (*v0)[1] + ((*v1)[1]-(*v0)[1]) * (width-(*v0)[0]) / ((*v1)[0]-(*v0)[0]); x = width; } else if (outcodeOut & LEFT) { // point is to the left of clip rectangle y = (*v0)[1] + ((*v1)[1]-(*v0)[1]) * (0-(*v0)[0]) / ((*v1)[0]-(*v0)[0]); x = 0; } // Now we move outside point to intersection point to clip // and get ready for next pass. if (outcodeOut == outcode0) { (*v0)[0] = x; (*v0)[1] = y; outcode0 = calcOutCode(*v0, width, height); } else { (*v1)[0] = x; (*v1)[1] = y; outcode1 = calcOutCode(*v1, width, height); } } } return accept; }