/Users/craigcornelius/Projects/SPRING Mac Release 0.2/face.cpp

Go to the documentation of this file.
00001 /* $Id: face.cpp,v 1.69 2006/02/01 20:18:01 craig Exp $ */
00002 #include "util.h"
00003 #include <assert.h>
00004 #include "face.h"
00005 #include "facearray.h"
00006 #include "object.h"
00007 #include <math.h>
00008 
00009 #ifdef _WIN32
00010 #include <glut.h>
00011 #else
00012 #include <GLUT/glut.h>
00013 #include <OpenGL/gl.h>
00014 #endif
00015 
00016 #include "intersection.h"
00017 #include "rasterfont.h"
00018 #include "point3d.h"
00019 
00020 const char* Face::rcsid = "@(#) $Id: face.cpp,v 1.69 2006/02/01 20:18:01 craig Exp $ $Copyright: (c)2001 National Biocomputation Center, Stanford University $";
00021 
00022 int Face::debug = 0;
00023 
00024 Face::Face()
00025 {
00026     facearray_p = NULL;
00027     for (int i=0; i<3; i++) {
00028         node[i] = NULL;
00029         edge[i] = NULL;
00030     }
00031                 
00032         splitNode[0] = splitNode[1] = NULL;
00033         fakeSplitNode[0] = fakeSplitNode[1] = NULL;
00034         splitEdge[0] = splitEdge[1] = NULL;
00035         num_split_edges = 0;
00036 
00037     marker = 0;
00038     boundingsphere = NULL;
00039 }
00040 
00041 Face::~Face()
00042 {
00043     facearray_p = NULL;
00044     for (int i=0; i<3; i++) {
00045         node[i] = NULL;
00046         edge[i] = NULL;
00047     }
00048 }
00049 
00050 void Face::init(NodeArray* nodearray_p, EdgeArray* edgearray_p,
00051                 FaceArray* facearray_p_in, int i0, int i1, int i2)  
00052 { 
00053     // Face knows where it is in facearray 
00054     facearray_p = facearray_p_in;
00055     
00056     // add nodes to this Face, add this Face to those nodes 
00057     node[0] = nodearray_p->getNode(i0);
00058     node[1] = nodearray_p->getNode(i1); 
00059     node[2] = nodearray_p->getNode(i2); 
00060     
00061     // call the real init
00062     init(nodearray_p, edgearray_p, facearray_p, node[0], node[1], node[2]);
00063 }
00064 
00065 void Face::init(NodeArray* nodearray_p, EdgeArray* edgearray_p,
00066                 FaceArray* facearray_p_in, Node* n0, Node* n1, Node* n2)  
00067 {
00068     // see if there's already an edge between n0 and n1, and if not, make one.
00069     Edge* e0 = n0->is_adjacent_to(n1);
00070     if (e0 == NULL)
00071         e0 = edgearray_p->getEdge(edgearray_p->addEdge(n0, n1));
00072     
00073     // ditto for n1 and n2
00074     Edge* e1 = n1->is_adjacent_to(n2);
00075     if (e1 == NULL)
00076         e1 = edgearray_p->getEdge(edgearray_p->addEdge(n1, n2));
00077     
00078     // and finally, n2 and n0
00079     Edge* e2 = n2->is_adjacent_to(n0);
00080     if (e2 == NULL)
00081         e2 = edgearray_p->getEdge(edgearray_p->addEdge(n2, n0));
00082     
00083     // and call the last init to setup links
00084     init(nodearray_p, edgearray_p, facearray_p_in, n0, n1, n2, e0, e1, e2);
00085 }
00086 
00087 void Face::init(NodeArray* nodearray_p, EdgeArray* edgearray_p,
00088                 FaceArray* facearray_p_in, Node* n0, Node* n1, Node* n2,
00089                 Edge* e0, Edge* e1, Edge* e2)  
00090 { 
00091     // set internal vars
00092     facearray_p = facearray_p_in;
00093     node[0] = n0; node[1] = n1; node[2] = n2;
00094     edge[0] = e0; edge[1] = e1; edge[2] = e2;
00095     
00096     // crosslink us into our node's and edge's lists
00097     for (int i=0; i<3; i++) {
00098         // add us to the node's face list
00099         node[i]->addFaceLink(this);
00100         
00101         // add us to the edge's face list
00102         edge[i]->addFaceLink(this);
00103     }
00104 }
00105 
00106 void Face::replaceNodeLink(Node* old_node, Node* new_node)
00107 {
00108     for (int i=0; i<3; i++)
00109         if (node[i] == old_node) node[i] = new_node;
00110 }
00111 
00112 void Face::replaceNodeLink(int which, Node* new_node)
00113 {
00114     node[which] = new_node;
00115 }
00116 
00117 
00118 Node *Face::getOppositeNode(Edge *e)
00119 {
00120     if(hasEdgeLink(e) == 0){
00121         cerr << "this face and this edge are not related" << endl;
00122         return NULL;
00123     }
00124     for(int i = 0; i < 3; i ++){
00125         Node *tempN = getNodeLink(i);
00126         if(tempN != e->getNodeLink(0) && tempN != e->getNodeLink(1))
00127             return tempN;
00128     }
00129     return NULL; //actually, should not be returned!
00130 }
00131 
00132 
00133 
00134 void Face::replaceEdgeLink(Edge* old_edge, Edge* new_edge)
00135 {
00136     for (int i=0; i<3; i++)
00137         if (edge[i] == old_edge) edge[i] = new_edge;
00138 }
00139 
00140 int Face::hasNodeLink(Node* n)
00141 {
00142     for (int i=0; i<3; i++)
00143         if (node[i] == n) return(1);
00144         return(0);
00145 }
00146 
00147 void Face::unlink()
00148 {int i;
00149     // unlink ourselves from our nodes and edges
00150     for ( i=0; i<3; i++) {
00151         // unlink from our nodes
00152         node[i]->deleteFaceLink(this);
00153         
00154         // unlink from edges
00155         edge[i]->deleteFaceLink(this);
00156     }
00157         for ( i=0; i<tetra.NumElements(); i++) {
00158                 tetra[i]->deleteFaceLink(this);
00159         }
00160 }
00161 
00162 int Face::hasEdgeLink(Edge* e)
00163 {
00164     for (int i=0; i<3; i++)
00165         if (edge[i] == e) return(1);
00166         return(0);
00167 }
00168 
00169 void Face::setNormal(Point3D normal_in)
00170 { normal = normal_in; }
00171 
00172 void Face::SaveAsMesh(ostream& os)
00173 {
00174     for (int i=0; i<3; i++)
00175         os << node[i]->getIndex() << " " ;
00176     os << endl;
00177 }
00178 
00179 void Face::draw(int do_texture) 
00180 { 
00181     for (int i=0; i<3; i++)
00182         node[i]->draw(1, 1, do_texture); // do_color, objectwide_color, do_texture
00183 }
00184 
00185 // make a label containing the face num
00186 void Face::drawlabel(float offset)
00187 {
00188     extern RasterFont* font_p;
00189     char s[20]; sprintf(s, "%d", getIndex());
00190     Point3D center = getCenter();
00191     
00192     Point3D ll = center + offset*normal;
00193     
00194     glBegin(GL_LINES);
00195     glVertex3d(center.x, center.y, center.z);
00196     glVertex3d(ll.x, ll.y, ll.z);
00197     glEnd();
00198     
00199     font_p->printString(s, ll.x,ll.y,ll.z);
00200 }
00201 
00202 void Face::drawnormal()
00203 {
00204     Point3D p = getCenter();
00205     glVertex3d(p.x, p.y, p.z);
00206     glVertex3d(p.x+normal.x, p.y+normal.y, p.z+normal.z);
00207 }
00208 
00209 // want some way to mark a face as special, say colliding
00210 void Face::drawmarker()
00211 {
00212     if (!marker) return;
00213 
00214     // try drawing outline in purple
00215     glColor3f(1.0, 0.0, 1.0);
00216     glBegin(GL_LINE_LOOP);
00217     for (int i = 0; i < 3; i++)
00218         getNodeLink(i)->drawover(0.05);
00219     glEnd();
00220 }
00221 
00222 int Face::getIndex()
00223 { return(facearray_p->getIndex(this)); }
00224 
00225 // ZVolume returns the exact volume that lies between the face
00226 // and its projection on the X-Y plane. It may be positive or
00227 // negative depending on the mutual orientations of the Z axis
00228 // and the normal of the face.
00229 double Face::ZVolume()
00230 {
00231     double Zvol1 = 0, Zvol2 = 0, Zvol3 = 0, Zvolume = 0;
00232     int minzz = getMinZNode();
00233     Point3D n = getNormal();
00234     
00235     // The point with the minimum Z
00236     Point3D a = node[minzz]->p;
00237     // The two other points of the face
00238     Point3D b = node[(minzz+1)%3]->p;
00239     Point3D c = node[(minzz+2)%3]->p;
00240     // Projections of b and c on the XY plane
00241     Point3D bp, cp;
00242     bp.x = b.x; bp.y = b.y; bp.z = a.z;
00243     cp.x = c.x; cp.y = c.y; cp.z = a.z;
00244     
00245     Zvol1 = TetraVol(a,b,c,bp);
00246     Zvol2 = TetraVol(a,bp,cp,c);
00247     Zvol3 = a.z * getZShadowArea();
00248     
00249     Zvolume = Zvol1 + Zvol2 + Zvol3;
00250     if(n.z < 0)
00251         Zvolume = -Zvolume;
00252     
00253     return Zvolume;
00254 }
00255 
00256 // TetraVol return the volume of the tetrahedron formed
00257 // by the four points passed in
00258 double Face::TetraVol(Point3D a, Point3D b,
00259                       Point3D c, Point3D d)
00260 {
00261     double vol = 0;
00262     vol = -a.z*b.y*c.x + a.y*b.z*c.x + a.z*b.x*c.y - a.x*b.z*c.y -
00263         a.y*b.x*c.z + a.x*b.y*c.z + a.z*b.y*d.x - a.y*b.z*d.x -
00264         a.z*c.y*d.x + b.z*c.y*d.x + a.y*c.z*d.x - b.y*c.z*d.x -
00265         a.z*b.x*d.y + a.x*b.z*d.y + a.z*c.x*d.y - b.z*c.x*d.y -
00266         a.x*c.z*d.y + b.x*c.z*d.y + a.y*b.x*d.z - a.x*b.y*d.z -
00267         a.y*c.x*d.z + b.y*c.x*d.z + a.x*c.y*d.z - b.x*c.y*d.z;
00268     vol = vol/6.0;
00269     return vol;
00270 }
00271 
00272 int Face::getMinZNode()
00273 {
00274     Point3D alpha = node[0]->p, beta = node[1]->p, 
00275         gamma = node[2]->p;
00276     if(alpha.z <= beta.z)
00277     {
00278         if(alpha.z <= gamma.z) return 0;
00279         else return 2;
00280     }
00281     else if(beta.z <= gamma.z)
00282     {
00283         if(beta.z <= alpha.z) return 1;
00284         else return 0;
00285     }
00286     else if(gamma.z <= alpha.z)
00287     {
00288         if(gamma.z <= beta.z) return 2;
00289         else return 1;
00290     }
00291     printf("/nPb determining MinZNode, returning 0");
00292     return 0;
00293 }
00294 
00295 // This function takes in two nodes in a given face, and returns the third
00296 Node *Face::getThirdNode(Node *n0, Node *n1)
00297 {
00298     if ((n0 == node[0] && n1 == node[1]) || (n0 == node[1] && n1 == node[0]))
00299         return node[2];
00300     else if ((n0 == node[0] && n1 == node[2]) || (n0 == node[2] && n1 == node[0]))
00301         return node[1];
00302     else if ((n0 == node[1] && n1 == node[2]) || (n0 == node[2] && n1 == node[1]))
00303         return node[0];
00304     else {
00305         printf("Input nodes are not both in this face\n");
00306         return NULL;
00307     }
00308 }
00309 
00310 // returns signed distance (positive on normal side, neg on other)
00311 double Face::PlaneDistanceToPoint(Point3D pt)
00312 {
00313     if (debug) cerr << "PlaneDistanceToPoint(" << pt << ")\n";
00314     
00315     // compute plane equation for plane PQR
00316     // have the normal, compute the constant
00317     double plane_constant = normal.Dot(node[0]->p); // note, this is -d
00318     
00319     // compute the constant for the given point
00320     double pt_constant = normal.Dot(pt);
00321 
00322     // return the diff
00323     return (pt_constant - plane_constant);
00324 }
00325 
00326 
00327 //find the projection of pt on the face plane 
00328 Point3D Face::Projection(Point3D pt)
00329 {
00330   double dist = PlaneDistanceToPoint(pt);
00331   Point3D proj = pt - normal*(dist);
00332   return(proj);
00333 
00334   //old way, inefficient, determinePlaneEq uses normal, why recompute normal?
00335   // also, normal is already a unit vector
00336   /*
00337     double a, b, c, d;
00338     Point3D proj, normal;
00339     
00340     determinePlaneEq(&a, &b, &c, &d); //this->determinePlaneEq(&a, &b, &c, &d);
00341     normal = Point3D(a, b, c);
00342     
00343     proj = pt - ((normal.Dot(pt) + d) / normal.Squared()) * normal;
00344     return(proj);
00345   */
00346 }
00347 
00348 // should check this code ??? !!!
00349 // computes the distance from a point to a face
00350 double Face::FaceDistanceToPoint(Point3D pt, int early_exit, Point3D *nearpt)
00351 {
00352     int i, test[3]; 
00353     double dist;
00354     Edge *e1, *e2;
00355     Point3D proj, normal;
00356     Node *p[3];
00357     
00358     normal = getNormal();
00359     p[0] = getNodeLink(0);
00360     p[1] = getNodeLink(1);
00361     p[2] = getNodeLink(2);
00362     
00363     proj = Projection(pt);
00364     
00365     //printf("projection = %g %g %g\n", proj.x, proj.y, proj.z);
00366     
00367     for(i = 0; i < 3; i ++)
00368         // tests the position of proj compared to the edge p[i]p[i+1] 
00369       test[i] = (((p[(i + 1) % 3]->p - p[i]->p).Cross(pt - p[i]->p)).Dot(normal)
00370                  >= 0) ? 0 : 1;
00371     
00372     assert(!(test[0] == 1 && test[1] == 1 && test[2] == 1));
00373     
00374     // this means that pt projects _inside_ the face, I hope
00375     if(test[0] == 0 && test[1] == 0 && test[2] == 0) {
00376       // want to return positive distance!!
00377       // otherwise ClosestFaceToPoint, etc. are wrong.
00378       if (early_exit)
00379         *nearpt = proj;
00380       return(fabs(PlaneDistanceToPoint(pt)));
00381     }
00382 
00383     if (early_exit)
00384       return(99999.9); // not inside face, want to return large value
00385 
00386     for(i = 0; i < 3; i ++){
00387         //printf("i = %d\n", i);
00388         if(test[i] == 1 && test[(i+1) % 3] == 0 && test[(i+2) % 3] == 0){
00389             //printf("i = %d\n", i);
00390             e1 = p[i]->findEdgeLink(p[(i+1) % 3]);
00391             dist = e1->EdgeDistanceToPoint(proj); 
00392             //printf("dist to edge = %g\n", dist);
00393             dist = sqrt(dist * dist + (pt - proj).Squared());
00394             return dist;
00395         }
00396     }
00397     
00398     for(i = 0; i < 3; i ++){
00399         //printf("i = %d\n", i);
00400         if(test[i] == 1 && test[(i+1) % 3] == 1 && test[(i+2) % 3] == 0){
00401             //printf("i = %d\n", i);
00402             e1 = p[i]->findEdgeLink(p[(i+1) % 3]);
00403             e2 = p[(i+1) % 3]->findEdgeLink(p[(i+2) % 3]);
00404             dist = MIN(e1->EdgeDistanceToPoint(proj),e2->EdgeDistanceToPoint(proj));
00405             //printf("dist to edge = %g\n", dist);
00406             dist = sqrt(dist * dist + (pt - proj).Squared());
00407             return dist;
00408         }
00409     }
00410     
00411     // if got here and nothing above hit, return large value, print error
00412     printf("problem in FaceDistanceToPoint\n");
00413     return(99999.9);
00414 }
00415 
00416 
00417 int Face::IsColliding(Point3D p)
00418 {
00419     if (debug) cerr << "Face::IsColliding(" << p << ")\n";
00420     
00421     // first, are we close enough to the plane that the face is in?
00422     const double max_dist = 0.1; // mm
00423     double dist = fabs(PlaneDistanceToPoint(p));
00424     if (dist > max_dist) return(0);
00425     
00426     if (debug) 
00427         cerr << "  distance =  " << dist << "(max_dist = " << max_dist << ")\n";
00428     
00429     // close enough to plane, are we within the triangle bounds?
00430     Point3D a = node[0]->p;
00431     Point3D b = node[1]->p;
00432     Point3D c = node[2]->p;
00433     
00434     // basically, if the point is in the triangle, approximately, as point may
00435     // be slightly out of plane, each of these cross products
00436     // will point in the direction of the normal, and the length of the sum
00437     // will be ~ 2 instead of ~ 0.  Special case if p is collinear with one side,
00438     // then cross product is 0 vector, so don't normalize, the sum will have
00439     // length 1 and will pass the test
00440     
00441     // do the ab edge
00442     Point3D ab = b - a, ap = p - a;
00443     Point3D n1 = ab.Cross(ap);
00444     if (!(n1.IsZero()))
00445         n1.Normalize(); 
00446     if ((n1 + normal).Squared() < 1.0) return(0);
00447     
00448     // do the bc edge
00449     Point3D bc = c - b, bp = p - b;
00450     Point3D n2 = bc.Cross(bp);
00451     if (!(n2.IsZero()))
00452         n2.Normalize(); 
00453     if ((n2 + normal).Squared() < 1.0) return(0);
00454     
00455     // do the ca edge
00456     Point3D ca = a - c, cp = p - c;
00457     Point3D n3 = ca.Cross(cp);
00458     if (!(n3.IsZero()))
00459         n3.Normalize(); 
00460     if ((n3 + normal).Squared() < 1.0) return(0);
00461     
00462     // debugging
00463     if (debug) 
00464         cerr << "  it passed all tests- is a collision!\n";
00465     
00466     // yay!!  it worked!
00467     return(1);
00468     
00469 #ifdef SLOW_WAY_TO_TEST_SIGNS
00470     // all-att-once way of doing it
00471     //Point3D ab = b - a, bc = c - b, ca = a - c;
00472     //Point3D ap = p - a, bp = p - b, cp = p - c;
00473     //Point3D n1 = ab.Cross(ap), n2 = bc.Cross(bp), n3 = ca.Cross(cp);
00474     
00475     n1.Normalize(); n2.Normalize(); n3.Normalize();
00476     if ((n1 + normal).Length() < 1.0) return(0);
00477     if ((n2 + normal).Length() < 1.0) return(0);
00478     if ((n3 + normal).Length() < 1.0) return(0);
00479     
00480 #elif defined(BAD_WAY_TO_TEST_SIGNS)
00481     // NOTE!  This did NOT work- why???
00482     cerr << n1.Sign() << " " << n2.Sign() << " " << n3.Sign() << "   " 
00483         << normal.Sign() << endl;
00484     if (n1.Sign() != normal.Sign()) return(0);
00485     if (n2.Sign() != normal.Sign()) return(0);
00486     if (n3.Sign() != normal.Sign()) return(0);
00487 #endif
00488 }
00489 
00490 Node* Face::getClosestNodeInFace(Point3D pt)
00491 {
00492     double dist0 = node[0]->p.SquaredDist(pt);
00493     double dist1 = node[1]->p.SquaredDist(pt);
00494     double dist2 = node[2]->p.SquaredDist(pt);
00495     if (dist0 < dist1) {
00496         if (dist0 < dist2) return(node[0]);
00497         else return(node[2]);
00498     } else {
00499         if (dist1 < dist2) return(node[1]);
00500         else return(node[2]);
00501     }
00502 }
00503 
00504 // computes area of the face
00505 // A = |p2-p1 * p3-p1|/2
00506 double Face::getArea()
00507 {
00508     Point3D p1 = node[0]->p;
00509     Point3D p2 = node[1]->p;
00510     Point3D p3 = node[2]->p;
00511     Point3D first_part = p2 - p1;
00512     Point3D last_part = p3 - p1;
00513     Point3D cross_part = first_part.Cross(last_part);
00514     
00515     return( cross_part.Length()/2.0 );
00516 }
00517 
00518 // computes area of projection of tri onto x-z plane
00519 // for use in volume calculations
00520 double Face::getShadowArea()
00521 {
00522     Point3D p1 = node[0]->p;
00523     Point3D p2 = node[1]->p;
00524     Point3D p3 = node[2]->p;
00525     p1.y = p2.y = p3.y = 0;     // shadow onto the x-z plane
00526     Point3D first_part = p2 - p1;
00527     Point3D last_part = p3 - p1;
00528     Point3D cross_part = first_part.Cross(last_part);
00529     
00530     return( cross_part.Length()/2.0 );
00531 }
00532 
00533 double Face::getZShadowArea()
00534 {
00535     Point3D p1 = node[0]->p;
00536     Point3D p2 = node[1]->p;
00537     Point3D p3 = node[2]->p;
00538     p1.z = p2.z = p3.z = 0;     // shadow onto the x-y plane
00539     Point3D first_part = p2 - p1;
00540     Point3D last_part = p3 - p1;
00541     Point3D cross_part = first_part.Cross(last_part);
00542     
00543     return( cross_part.Length()/2.0 );
00544 }
00545 
00546 void Face::getAdjacentFaces(Face** f1_p, Face** f2_p, Face** f3_p)
00547 {
00548     if (!f1_p || !f2_p || !f3_p) return;
00549     
00550     { // get face on other side of edge 0
00551         Face* efa = edge[0]->getFaceLink(0);
00552         Face* efb = edge[0]->getFaceLink(1);
00553         if (efa == this) *f1_p = efb;
00554         else *f1_p = efa;
00555     }
00556     
00557     { // get face on other side of edge 1
00558         Face* efa = edge[1]->getFaceLink(0);
00559         Face* efb = edge[1]->getFaceLink(1);
00560         if (efa == this) *f2_p = efb;
00561         else *f2_p = efa;
00562     }
00563     
00564     { // get face on other side of edge 2
00565         Face* efa = edge[2]->getFaceLink(0);
00566         Face* efb = edge[2]->getFaceLink(1);
00567         if (efa == this) *f3_p = efb;
00568         else *f3_p = efa;
00569     }
00570 }
00571 
00572 
00573 ostream& operator<<(ostream& os, const Face& f_in)
00574 {
00575     Face& f = (Face&)f_in;
00576     if (f.facearray_p)
00577         os << "Face#" << ((Face&)f).getIndex() << ": ";
00578     else os << "Face#UNLINKED: ";
00579     os << "Nodelinks: " ;
00580     { for (int i=0; i<3; i++) os << f.node[i]->getIndex() << " " ; }
00581     os << ", Edgelinks: ";
00582     { for (int i=0; i<3; i++) os << f.edge[i]->getIndex() << " " ; }
00583         os << ", Tetralinks: ";
00584   { for (int i=0; i<f.tetra.NumElements(); i++) os << f.tetra[i]->getIndex() << " " ; }
00585 
00586     // print out adjacent faces
00587     os << ", adjfaces: ";
00588     for (int i=0;i<3; i++) {
00589         Edge* cur_edge = ((Face)f).getEdgeLink(i);
00590         Face* other_face = cur_edge->getOtherFace((Face*)&f);
00591         if (other_face)
00592             os << other_face->getIndex() << " ";
00593         else os << "NULL ";
00594     }
00595     
00596     return(os);
00597 }
00598 
00599 int Face::SanityCheck(FaceArray* real_fa_p)
00600 {
00601     char s[1024]; s[0] = '\0';
00602     char tmps[1024]; tmps[0] = '\0';
00603     
00604     // test usual stuff
00605     int my_index = -1;
00606     if (!facearray_p) strcat(s, "facearray is NULL!");
00607     if (facearray_p != real_fa_p) strcat(s, "facearray is BAD!");
00608     else {
00609         my_index = getIndex();
00610         if (my_index < 0) {
00611            sprintf(tmps, "getIndex() = %d\n", my_index);
00612            strcat(s, tmps);
00613         }
00614     }
00615 
00616     // is this a problem???
00617     //if (normal.Length() != 1.0) strcat(s, "normal is not unit-length");
00618 
00619         // test that we have a boundingsphere leaf if our object has a tree
00620         Object* obj = getFaceArray()->getObject();
00621         if (obj->getBoundingSphereRoot() && !boundingsphere) 
00622                 strcat(s, "boundingsphere leaf is NULL");
00623     
00624     { // check nodelinks
00625         for (int i=0; i<3; i++) {
00626             if (!node[i]) { 
00627                 sprintf(tmps, "nodelink[%d] is NULL", i); 
00628                 strcat(s, tmps);
00629             }
00630             else if (!node[i]->hasFaceLink(this)) {
00631                 sprintf(tmps, "nodelink[%d] doesn't point back to us!", i); 
00632                 strcat(s, tmps);
00633             }
00634         }
00635     }
00636     
00637     { // check edgelinks
00638         for (int i=0; i<3; i++) {
00639             if (!edge[i]) {
00640                 sprintf(tmps, "edgelink[%d] is NULL!", i); 
00641                 strcat(s, tmps);
00642             }
00643             else if (!edge[i]->hasFaceLink(this)) {
00644                 sprintf(tmps, "edgelink[%d] doesn't point back to us!", i); 
00645                 strcat(s, tmps);
00646             }
00647         }
00648     }
00649 
00650     { // verify my tetra links
00651     for (int i=0; i<tetra.NumElements(); i++) {
00652         Tetra* t = tetra[i];
00653         if (!t) {
00654             sprintf(tmps, "tetralink[%d] is NULL!", i); 
00655             strcat(s, tmps);
00656         }
00657         else {
00658             if (!t->hasFaceLink(this)) {
00659                 sprintf(tmps, "tetralink[%d] does not have a link back to us!", i); 
00660                 strcat(s, tmps);
00661 
00662                 // look for duplicate facelinks
00663                 for (int j=0; j<i; j++) {
00664                     if (t == tetra[j]) {
00665                         sprintf(tmps, "tetralink[%d] is a duplicate of tetralink[%d]!", i, j); 
00666                         strcat(s, tmps);
00667                     }
00668                 } // for
00669            }
00670         } // else
00671     } // for
00672     } // verify tetra links
00673 
00674     // and if there's a problem, print it out
00675     if (strlen(s) > 1) {
00676         cerr << "Face[" << my_index << "]: is INSANE:" << s << endl;
00677         return(0);
00678     }
00679     
00680     // return ok
00681     return(1);
00682 }
00683 
00684 // Determines the equation of the plane which contains the two points S 
00685 // and E, and a vector n which is the mean of the 2 normals to the original 
00686 // surface in S and E
00687 void Face::determinePlaneEq(double *a_p, double *b_p, double *c_p, double *d_p)
00688 {
00689     Point3D pt = getNodeLink(0)->p;
00690     Point3D n = getNormal();
00691     *a_p = n.x;
00692     *b_p = n.y;
00693     *c_p = n.z;
00694     *d_p = - ((*a_p) * pt.x + (*b_p) * pt.y + (*c_p) * pt.z);
00695 }
00696 
00697 /* Determines if this face intersects the input edge, and if so
00698 * returns true and determines the intersection point */
00699 int Face::intersectsEdge(Edge *e_in, Point3D *intersection_p)
00700 {
00701     if (debug) 
00702         cerr << "Face::intersectsEdge(" << e_in << ", " << intersection_p 
00703         << ")\n";
00704     double a, b, c, d;
00705     int it_crosses;
00706     
00707     // find the equation of the plane containing this face
00708     determinePlaneEq(&a, &b, &c, &d);
00709     
00710     // and see if the input edge (line segment) crosses this plane while
00711     // determining point of intersection
00712     it_crosses = e_in->crossesPlane(a, b, c, d, intersection_p);
00713     
00714     if (!it_crosses) return 0;
00715     
00716     if (debug) cerr << "    it crosses plane!\n";
00717     
00718     // it does cross, is the intersection point within the face?
00719     return (IsColliding(*intersection_p));
00720 }
00721 
00722 // Determines if this face intersects the input face, and if so
00723 // returns true and determines (one) point of intersection (of many?)
00724 int Face::intersectsFace(Face *f_in, Point3D *intersection_p)
00725 {
00726     if (debug) 
00727         cerr << "Face::intersectsFace(" << f_in << ", " << intersection_p 
00728         << ")\n";
00729     
00730 #if (1)
00731     Edge *e;
00732     { // first check to see if this face's edges (eg the tool's edges) intersect
00733         // the input face (eg a face of the tube)
00734         // (we waste time by determining plane equation up to three times ???)
00735         for (int i = 0; i < 3; i++) {
00736             e = this->getEdgeLink(i);
00737             if (f_in->intersectsEdge(e, intersection_p)) return 1;
00738         }
00739     }
00740     
00741     { // now do vice versa in case input face is piercing this face
00742         for (int i = 0; i < 3; i++) {
00743             e = f_in->getEdgeLink(i);
00744             if (this->intersectsEdge(e, intersection_p)) return 1;
00745         }
00746     }
00747     
00748     // this face does not pierce/be-pierced-by input face
00749     return 0;
00750 #else
00751     // try and do some smarter cutouts
00752     Point3D thisNormal = this->getNormal();
00753     double thisPlaneConst = thisNormal.Dot(this->getNodeLink(0)->p);
00754     // distances from f_in vertices to this face
00755     double q0d = thisNormal.Dot(f_in->getNodeLink(0)->p) - thisPlaneConst;
00756     double q1d = thisNormal.Dot(f_in->getNodeLink(1)->p) - thisPlaneConst;
00757     double q2d = thisNormal.Dot(f_in->getNodeLink(2)->p) - thisPlaneConst;
00758     
00759     // no intersection if f_in is completely on one side of this face
00760     if (((q0d > 0) && (q1d > 0) && (q2d > 0)) ||
00761         ((q0d < 0) && (q1d < 0) && (q2d < 0)))
00762         return 0;
00763     
00764     Point3D otherNormal = f_in->getNormal();
00765     double otherPlaneConst = otherNormal.Dot(f_in->getNodeLink(0)->p);
00766     // distances from this face vertices to f_in
00767     double p0d = otherNormal.Dot(this->getNodeLink(0)->p) - otherPlaneConst;
00768     double p1d = otherNormal.Dot(this->getNodeLink(1)->p) - otherPlaneConst;
00769     double p2d = otherNormal.Dot(this->getNodeLink(2)->p) - otherPlaneConst;
00770     
00771     // no intersection if this face is completely on one side of f_in
00772     if (((p0d > 0) && (p1d > 0) && (p2d > 0)) ||
00773         ((p0d < 0) && (p1d < 0) && (p2d < 0)))
00774         return 0;
00775     
00776     // now check pairs of vertices of f_in on opposite sides of this face
00777     if ((q0d >= 0 && q1d < 0) || (q0d < 0 && q1d >= 0)) {
00778         // vertices 0 and 1 -- interpolate to find plane intersection point
00779         intersection_p->interp(f_in->getNodeLink(0)->p, f_in->getNodeLink(1)->p,
00780             fabs(q0d)/(fabs(q0d)+fabs(q1d)));
00781         // now see if this point is actually inside this face
00782         if (this->IsColliding(*intersection_p))
00783             return 1;
00784     }
00785     if ((q0d >= 0 && q2d < 0) || (q0d < 0 && q2d >= 0)) {
00786         // vertices 0 and 2 -- interpolate to find plane intersection point
00787         intersection_p->interp(f_in->getNodeLink(0)->p, f_in->getNodeLink(2)->p,
00788             fabs(q0d)/(fabs(q0d)+fabs(q2d)));
00789         // now see if this point is actually inside this face
00790         if (this->IsColliding(*intersection_p))
00791             return 1;
00792     }
00793     if ((q1d >= 0 && q2d < 0) || (q1d < 0 && q2d >= 0)) {
00794         // vertices 1 and 2 -- interpolate to find plane intersection point
00795         intersection_p->interp(f_in->getNodeLink(1)->p, f_in->getNodeLink(2)->p,
00796             fabs(q1d)/(fabs(q1d)+fabs(q2d)));
00797         // now see if this point is actually inside this face
00798         if (this->IsColliding(*intersection_p))
00799             return 1;
00800     }
00801     
00802     // now check pairs of vertices of this face on opposite sides of f_in
00803     if ((p0d >= 0 && p1d < 0) || (p0d < 0 && p1d >= 0)) {
00804         // vertices 0 and 1 -- interpolate to find plane intersection point
00805         intersection_p->interp(this->getNodeLink(0)->p, this->getNodeLink(1)->p,
00806             fabs(p0d)/(fabs(p0d)+fabs(p1d)));
00807         // now see if this point is actually inside f_in face
00808         if (f_in->IsColliding(*intersection_p))
00809             return 1;
00810     }
00811     if ((p0d >= 0 && p2d < 0) || (p0d < 0 && p2d >= 0)) {
00812         // vertices 0 and 2 -- interpolate to find plane intersection point
00813         intersection_p->interp(this->getNodeLink(0)->p, this->getNodeLink(2)->p,
00814             fabs(p0d)/(fabs(p0d)+fabs(p2d)));
00815         // now see if this point is actually inside f_in face
00816         if (f_in->IsColliding(*intersection_p))
00817             return 1;
00818     }
00819     if ((p1d >= 0 && p2d < 0) || (p1d < 0 && p2d >= 0)) {
00820         // vertices 1 and 2 -- interpolate to find plane intersection point
00821         intersection_p->interp(this->getNodeLink(1)->p, this->getNodeLink(2)->p,
00822             fabs(p1d)/(fabs(p1d)+fabs(p2d)));
00823         // now see if this point is actually inside f_in face
00824         if (f_in->IsColliding(*intersection_p))
00825             return 1;
00826     }
00827     
00828     return 0;
00829 #endif
00830 }
00831 
00832 // Determines if this face intersects the input edge (Chris)
00833 bool Face::intersectsEdge(Edge *edge)
00834 {
00835     double a, b, c, d;
00836     Point3D intersectionPoint;
00837 
00838     // find the equation of the plane containing this face
00839     determinePlaneEq(&a, &b, &c, &d);
00840     
00841     // and see if the input edge (line segment) crosses this plane while
00842     // determining point of intersection
00843     if (edge->crossesPlane(a, b, c, d, &intersectionPoint)) {
00844                 // it does cross, is the intersection point within the face?
00845                 return IsColliding(intersectionPoint);
00846         }
00847         else {
00848                 return false;
00849         }
00850 }
00851 
00852 // Determines if this face intersects the input face - Chris
00853 bool Face::intersectsFace(Face *otherFace)
00854 {
00855         // supposed to be a fast intersection test function!
00856         Intersection* intersection = Intersection::Instance();
00857         return intersection->intersects(this, otherFace);
00858 }
00859 
00860 // Determines if this face intersects with the input box (axis aligned box) - Chris
00861 bool Face::intersectsBox(Point3D *min, Point3D *max)
00862 {
00863         // supposed to be a fast intersection test function!
00864         Intersection* intersection = Intersection::Instance();
00865         return intersection->intersects(this, min, max);
00866 }
00867 
00868 // i move myself
00869 int Face::ResolveMyself(Face *f_in)
00870 {
00871     if (debug) cerr << "Face[" << getIndex() << "]:ResolveMyself()\n";
00872     
00873     // first check if the faces are intersecting
00874     
00875     // if they are, then
00876     // project the input face onto the plane defined by my face
00877     for (int i=0; i<3; i++) {
00878         Node* movee = node[i];
00879         Point3D b = movee->p - f_in->getCenter();
00880         double vd = f_in->getNormal().Dot(b); 
00881         //cerr << "vd: " << vd << endl;
00882         
00883         // scale up the force to make things happen faster
00884         //vd *= 4.0;
00885         
00886         if (vd < 0.0) {
00887             movee->setFext(-vd*f_in->getNormal());      
00888             
00889             // so at the next update, the node should move
00890             // but make sure we remove the external force when 
00891             // it is not applied anymore
00892             
00893         }       
00894     }
00895     
00896     // return success
00897     return(1);
00898 }
00899  void Face::addTetraLink(Tetra *t)
00900 {
00901   if (debug)
00902     cout << "\nFace " << getIndex() << " ::addTetraLink(" << t->getIndex() << ")\n";
00903   tetra.Append(t);
00904 }
00905 
00906  void Face::deleteTetraLink(Tetra* t)
00907 {
00908   if (debug)
00909     cout << "\nEdge " << getIndex() << " ::deleteTetraLink(" << t->getIndex() << ")\n";
00910 
00911   tetra.DeleteItem(t);
00912 
00913 }
00914 int Face::stepOffVerts(Point3D intPt) 
00915 {
00916         for (int i=0; i<3; i++) {
00917                 if (node[i]->p.Dist(intPt) < 0.00001) { 
00918                         //cout << "intersection on a vert" << endl;
00919                         intPt = .01*(node[(i+1)%3]->p - node[(i+0)%3]->p);                              
00920                         return i;
00921                 }
00922         }
00923         return -1;
00924 }
00925 
00926 int Face::stepOffEdges(Point3D intPt) 
00927 {
00928         for (int i=0; i<3; i++) {
00929                 if (edge[i]->EdgeDistanceToPoint(intPt) < 0.00001) { 
00930                  //     cout << "intersection on an edge" << endl;
00931                         Point3D vect = edge[i]->getNodeLink(1)->p - edge[i]->getNodeLink(0)->p;
00932                         intPt = .01*(normal.Cross(vect));       
00933                         return i;                               
00934                 }
00935         }
00936         return -1;
00937 }
00938 int Face::getCase(int (&v)[9]) 
00939 {
00940         //cout << "\nface: " << getIndex() << endl;
00941         
00942         int num_edges = 0;      
00943     {
00944       for (int i=0; i<3; i++) {
00945                 if (edge[i]->getSplitNode(0) != NULL)
00946                         num_edges++;
00947       }
00948         }
00949         
00950         int num_faces = 0;
00951         if (getSplitNode(0) != NULL) {
00952                 num_faces++;
00953         }
00954         
00955 //      cout << "number of edges split = " << num_edges << endl;
00956 //      cout << "number of faces split = " << num_faces << endl;
00957 
00958         Node* n[9];
00959         for (int i=0; i<9; i++) {
00960                 n[i] = NULL;
00961                 v[i] = -1;
00962         }
00963         
00964         if (num_edges == 1) {
00965         
00966                         if (num_faces == 0) {
00967                                 cerr << "entered and exited thru the same edge " << endl;
00968                                 return 2;
00969                         }
00970                         if (num_faces == 1) {                                                           
00971         
00972                                 n[0] = getOppositeNode(splitEdge[0]);
00973                                 int node0Idx = getNodeIndex(n[0]); cerr << "node[0] index: " << node0Idx << endl;
00974                                 n[1] = getNodeLink((node0Idx+1)%3); cerr << "node[0] index: " << (node0Idx+1)%3 << endl;
00975                                 n[2] = getNodeLink((node0Idx+2)%3); cerr << "node[0] index: " << (node0Idx+2)%3 << endl;
00976                                 
00977                                 n[3] = getSplitNode(0);
00978 
00979                                 for (int i=0; i<4; i++) v[i] = n[i]->getIndex();
00980 
00981                                 n[4] = splitEdge[0]->getSplitNode(0);
00982                                 n[5] = splitEdge[0]->getSplitNode(1);
00983 
00984                                 if (getFaceArray()->getNodeArray()->getNode(v[1])->isConnectedTo(n[5])){
00985                                         v[4] = n[5]->getIndex();
00986                                         v[5] = n[4]->getIndex();
00987                                 }
00988                                 else {
00989                                         v[4] = n[4]->getIndex();
00990                                         v[5] = n[5]->getIndex();                        
00991                                 }
00992                                 return 0;                       
00993                         }
00994                 }
00995                 if (num_edges == 2) {
00996                         if (num_faces == 0) {
00997                         
00998                         
00999                                 n[0] = splitEdge[1]->getNodeLink(0);
01000                                 n[1] = splitEdge[1]->getNodeLink(1);
01001 
01002                                 for (int i=0; i<2; i++) {                       
01003                                         int numsplit = 0;
01004                                         for (int j=0; j< n[i]->numEdgeLinks(); j++) {
01005                                                 Edge* myedge = n[i]->getEdgeLink(j);
01006                                                 if (myedge->getSplitNode(0) != NULL && hasEdgeLink(myedge)) {
01007                                                         numsplit++;
01008                                                 }
01009                                         }
01010                                         if (numsplit > 1) {
01011                                                 v[0] = n[i]->getIndex();
01012                                                 v[1] = n[(i+1)%2]->getIndex();
01013 
01014                                                 break;
01015                                         }
01016                                 }
01017                                 n[2] = getOtherNode(n[0],n[1]);
01018                                 v[2] = n[2]->getIndex();
01019 
01020                                 
01021                                 if ( getEdgeLinkIndex(splitEdge[1]) == (getEdgeLinkIndex(splitEdge[0])+1)%3 ) {
01022                                         n[3] = splitEdge[0]->getSplitNode(0);
01023                                         n[4] = splitEdge[0]->getSplitNode(1);
01024 
01025                                         n[5] = splitEdge[1]->getSplitNode(0);
01026                                         n[6] = splitEdge[1]->getSplitNode(1);
01027                                 }
01028                                 
01029                                 else {
01030                                         n[3] = splitEdge[1]->getSplitNode(0);
01031                                         n[4] = splitEdge[1]->getSplitNode(1);
01032 
01033                                         n[5] = splitEdge[0]->getSplitNode(0);
01034                                         n[6] = splitEdge[0]->getSplitNode(1);           
01035                                         
01036                                         int temp = v[1];
01037                                         v[1] = v[2];
01038                                         v[2] = temp;                            
01039                                 }
01040 
01041                                 if (getFaceArray()->getNodeArray()->getNode(v[2])->isConnectedTo(n[4])){
01042                                         v[3] = n[4]->getIndex();
01043                                         v[4] = n[3]->getIndex();
01044                                 }
01045                                 else {
01046                                         v[3] = n[3]->getIndex();
01047                                         v[4] = n[4]->getIndex();                        
01048                                 }
01049 
01050                                 if (getFaceArray()->getNodeArray()->getNode(v[1])->isConnectedTo(n[6])){
01051                                         v[5] = n[6]->getIndex();
01052                                         v[6] = n[5]->getIndex();
01053                                 }
01054                                 else {
01055                                         v[5] = n[5]->getIndex();
01056                                         v[6] = n[6]->getIndex();                        
01057                                 }
01058                                         
01059                                 if (getFakeSplitNode(0) == NULL) {
01060                                         Point3D mid; mid.interp(n[3]->p,n[5]->p,.5);
01061                                         n[7] = getFaceArray()->getNodeArray()->getNode(getFaceArray()->getNodeArray()->addNode(mid));
01062                                         n[8] = getFaceArray()->getNodeArray()->getNode(getFaceArray()->getNodeArray()->addNode(mid));
01063                                         setFakeSplitNodes(n[7],n[8]);
01064                 //                      getFaceArray()->getObject()->addToCutPath(n[7]);
01065                 //                      getFaceArray()->getObject()->addToCutPath(n[8]);
01066                                 }
01067                                 n[7] = getFakeSplitNode(0);
01068                                 n[8] = getFakeSplitNode(1);                             
01069                                 
01070                                 if (getFaceArray()->getNodeArray()->getNode(v[0])->isConnectedTo(n[7])) {
01071                                         v[7] = n[8]->getIndex();
01072                                         v[8] = n[7]->getIndex();
01073                                 }
01074                                 else {
01075                                         v[7] = n[7]->getIndex();
01076                                         v[8] = n[8]->getIndex();
01077                                 }
01078 
01079                                 return 1;
01080                         }
01081                 }       
01082                 
01083                 
01084                 // CBNOTE if you get here you have an invalid case so remove space nodes
01085                 
01086         //cout << "                     has an invalid case" << endl;
01087         return -1;
01088 }
01089 Node* Face::getOtherNode(Node* n0, Node* n1)
01090 {
01091         if (!hasNodeLink(n0) || !hasNodeLink(n1)) return NULL;
01092         
01093         for (int i=0; i<3; i++) {       
01094                 if (node[i] == n0 || node[i] == n1) continue;
01095                 return  node[i];
01096         }
01097         return NULL;
01098 }
01099 int Face::intersectsPlaneSweptByCuttingEdge(Edge* e_in, Point3D* intersection_p)
01100 {
01101 
01102         if (intersectsEdge(e_in,intersection_p)) return 1;      
01103 
01104 
01105         return 0;
01106 }
01107 
01108 int Face::vectorCrossesMe(Point3D P1, Point3D P2, Point3D* intPt) 
01109 {
01110         double a, b, c, d;
01111   determinePlaneEq(&a, &b, &c, &d);
01112 
01113         double alpha, t;
01114 
01115   alpha = a * (P1.x - P2.x) + b * (P1.y - P2.y) + c * (P1.z - P2.z);
01116   if(alpha == 0) return 0;
01117 
01118   t = -(a * P2.x + b * P2.y + c * P2.z + d) / alpha;
01119   if ((t < 0) || (t > 1)) return 0;
01120 
01121   intPt->x = (P1.x - P2.x) * t + P2.x;
01122   intPt->y = (P1.y - P2.y) * t + P2.y;
01123   intPt->z = (P1.z - P2.z) * t + P2.z;
01124         
01125         if (intPt->insideTriangle(node[0]->p, node[1]->p, node[2]->p)) return 1;
01126         return 0;
01127 
01128 }
01129 void Face::interpolateTextureCoords(Node* n0)
01130 {
01131         double d[3]; 
01132         double sum_dist = 0;
01133         
01134         Point3D nt[3];
01135         
01136         n0->setTextureCoords(Point3D(0,0,0));
01137         
01138         for (int i=0; i<3; i++) {
01139                 d[i] = node[i]->p.Dist(n0->p);
01140                 sum_dist += d[i];
01141                 nt[i] = node[i]->getTextureCoords();
01142         }
01143 
01144         Point3D text_coords;
01145 
01146         text_coords.x = (d[0]*nt[0].x + d[1]*nt[1].x + d[2]*nt[2].x)/sum_dist;
01147         text_coords.y = (d[0]*nt[0].y + d[1]*nt[1].y + d[2]*nt[2].y)/sum_dist;
01148         text_coords.z = (d[0]*nt[0].z + d[1]*nt[1].z + d[2]*nt[2].z)/sum_dist;
01149         
01150         n0->setTextureCoords(text_coords);
01151                         
01152 }
01153 int Face::getEdgeLinkIndex(Edge* e)
01154 {
01155   for (int i=0; i<3; i++)
01156     if (edge[i] == e) return(i);
01157   return(-1);
01158 }
01159 Edge* Face::getEdge(int n0, int n1)
01160 {
01161   for (int i=0; i<3; i++){
01162     if ( (edge[i]->getNodeLink(0)->getIndex() == n0 && edge[i]->getNodeLink(1)->getIndex() == n1) || 
01163                                  (edge[i]->getNodeLink(1)->getIndex() == n0 && edge[i]->getNodeLink(0)->getIndex() == n1) )
01164                                  return edge[i];
01165         }
01166   return(NULL);
01167 }
01168 
01169 
01170 //finds if faces share and edge and returns 1 if not then returns 0
01171 Edge* Face::isEdgeAdjacent(Face *f)
01172 {       
01173         for(int x=0; x<3; x++)
01174         for(int z=0; z<3; z++) {
01175         if(this->getEdgeLink(x) == f->getEdgeLink(z))
01176                 return f->getEdgeLink(z);
01177         }       
01178         return NULL;
01179 }
01180 
01181 //find other point shared by two faces, meaning not p
01182 Point3D Face::getSatelite(Face *f, Node *in)
01183 {
01184         Point3D         otherp;
01185         Edge                    *sharededge;
01186         Node                    *nearnode;
01187 
01188         sharededge = this->isEdgeAdjacent(f);
01189         if(sharededge != NULL)
01190                 otherp = sharededge->getOtherNode(in)->p;
01191         else //we have a gap or non-adjoining faces around a node
01192         {
01193                 nearnode        = this->getClosestNodeInFace(f->getCenter(), in);
01194                 otherp          = nearnode->p;
01195                 //cout << "Non-adjacent faces at node ";
01196         }
01197 
01198         return otherp;
01199 }
01200 
01201 //anil return closest one except notme
01202 Node* Face::getClosestNodeInFace(Point3D pt, Node* notme)
01203 {
01204   double dist0 = node[0]->p.SquaredDist(pt);
01205   double dist1 = node[1]->p.SquaredDist(pt);
01206   double dist2 = node[2]->p.SquaredDist(pt);
01207   double sum;
01208         int who = 0;
01209 
01210         //round about way to eliminat notme to reuse old code
01211         sum = dist0+dist1+dist2;
01212         for(int i=0; i<3; i++){
01213                 if(node[i] == notme) who = i;
01214         }
01215         if(who==0) dist0 = sum;
01216         if(who==1) dist1 = sum;
01217         if(who==2) dist2 = sum;
01218         
01219         if (dist0 < dist1) {
01220      if (dist0 < dist2) return(node[0]);
01221      else return(node[2]);
01222   } else {
01223      if (dist1 < dist2) return(node[1]);
01224      else return(node[2]);
01225   }
01226 }
01227 

Generated on Thu Aug 30 11:03:13 2007 for SPRING Mac by  doxygen 1.5.3