// -*- C++ -*- // $RCSfile: amintersection.C,v $ // $Revision: 1.7 $ // $Author: langer $ // $Date: 2000/11/02 13:59:21 $ /* This software was produced by NIST, an agency of the U.S. government, * and by statute is not subject to copyright in the United States. * Recipients of this software assume all responsibilities associated * with its operation, modification and maintenance. However, to * facilitate maintenance we ask that before distributing modifed * versions of this software, you first contact the authors at * oof_manager@ctcms.nist.gov. */ // Find the intersection of a mesh triangle and a pixel. #include "amtriangle.h" #include "amtriangleiterator.h" // only used for DEBUG code #include "adaptmesh.h" #include "stdlib.h" //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// class ConvexPolygon { private: Vec vertex; static int compare(const void*, const void*); // for sorting vertices static int compare0(const void*, const void*); static MeshCoord referencept; // for sorting vertices bool sorted; int sort(); public: ConvexPolygon(int n=0) // n is number of vertices to preallocate : sorted(0) { vertex.setphysicalsize(n); } const MeshCoord &operator()(int i) const { return vertex[i]; } int npts() const { return vertex.capacity(); } void add(const MeshCoord &pt); double area(); }; void ConvexPolygon::add(const MeshCoord &pt) { // Don't bother checking for duplicate points here. That is done by // ConvexPolygon::sort(), which is called by ConvexPolygon::area(). vertex.grow(1, pt); sorted = 0; } double ConvexPolygon::area() { if(vertex.capacity() < 3) return 0.0; if(!sort()) return 0; // can't sort vertices if polygon is degenerate double a = 0; for(int j=2; j dup(vertex.capacity(), 0); int dups = 0; for(int k=1; k dup(vertex.capacity(), 0); for(int i=1; i 0) return -1; if(a < 0) return 1; return 0; } int ConvexPolygon::compare0(const void *p1, const void *p2) { MeshCoord &pp1 = *(MeshCoord*) p1; MeshCoord &pp2 = *(MeshCoord*) p2; if(pp1 < pp2) return -1; if(pp1 > pp2) return 1; return 0; } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // Find the area of intersection between a pixel and a triangle // does a pixel contain a point? inline int pixelcontains(const Cell_coordinate &pixel, const MeshCoord &pt) { return (pixel.x <= pt.x && pixel.x + 1 >= pt.x && pixel.y <= pt.y && pixel.y + 1 >= pt.y); } // find intersection of a pixel side (p1, p2) with a triangle side // (t1, t2) assuming that the pixel side is vertical and that the // triangle side is not. ***The segments are closed (contain their // endpoints).*** static int yintercept(const MeshCoord &t1, const MeshCoord &t2, const MeshCoord &p1, const MeshCoord &p2, MeshCoord &intercept) { // rename triangle corners so that ta is to the left of tb const MeshCoord &ta = (t1.x < t2.x ? t1 : t2); const MeshCoord &tb = (&ta == &t1 ? t2 : t1); // check that triangle side straddles the (extended) pixel side if(ta.x > p1.x || tb.x < p1.x) return 0; // rename pixel corners so that pa is below pb const MeshCoord &pa = (p1.y < p2.y ? p1 : p2); const MeshCoord &pb = (&pa == &p1 ? p2 : p1); intercept.x = pa.x; intercept.y = ta.y + (tb.y - ta.y)*(pa.x - ta.x)/(tb.x - ta.x); if(intercept.y > pb.y || intercept.y < pa.y) return 0; return 1; } // find intersection of a pixel side (p1, p2) with a triangle side // (t1, t2) assuming that the pixel side is horizontal and that the // triangle side is not. ***The segments are closed (contain their // endpoints).*** static int xintercept(const MeshCoord &t1, const MeshCoord &t2, const MeshCoord &p1, const MeshCoord &p2, MeshCoord &intercept) { // rename triangle corners so that ta is below tb const MeshCoord &ta = (t1.y < t2.y ? t1 : t2); const MeshCoord &tb = (&ta == &t1 ? t2 : t1); // check that triangle side straddles the (extended) pixel side if(ta.y > p1.y || tb.y < p1.y) return 0; // rename pixel corners so that pa is to the left of pb const MeshCoord &pa = (p1.x < p2.x ? p1 : p2); const MeshCoord &pb = (&pa == &p1 ? p2 : p1); intercept.y = pa.y; intercept.x = ta.x + (tb.x - ta.x)*(pa.y - ta.y)/(tb.y - ta.y); if(intercept.x > pb.x || intercept.x < pa.x) return 0; return 1; } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // find the area of intersection of the given pixel with this triangle double AMTriangle::intersection(const Cell_coordinate &pixel) const { if(area() == 0.0) return 0; int i; MeshCoord pixelcorner[4]; ConvexPolygon intersection(30); // The argument to the ConvexPolygon constructor is a guess at the // maximum number of vertices that a polygon will have. It doesn't // hurt to make it too big. The intersection of a triangle and a // square can have at most 7 vertices, but during its construction a // vertex may be added more than once. A vertex certainly can't be // added more than 4 times, though, so 30 is a safe upper bound. // labels for corners of pixel static const int LL = 0; // lower left static const int LR = 1; // lower right static const int UR = 2; // upper right static const int UL = 3; // upper left pixelcorner[LL].x = pixel.x; pixelcorner[LL].y = pixel.y; pixelcorner[LR].x = pixel.x + 1; pixelcorner[LR].y = pixel.y; pixelcorner[UR].x = pixel.x + 1; pixelcorner[UR].y = pixel.y + 1; pixelcorner[UL].x = pixel.x; pixelcorner[UL].y = pixel.y + 1; // Identify corners of the intersection region // When checking vertices, use <= and >= to see if triangle vertices // are inside the pixel (and vice versa), but < and > to see if triangle // edges intersect pixel edges. This prevents vertices of one shape // that lie on an edge of the other shape from being included in the // list twice. // check corners of pixel to see if they're in the triangle for(i=0; i<4; i++) // loop over pixel corners if(this->contains(pixelcorner[i])) intersection.add(pixelcorner[i]); int np = intersection.npts(); // no. of pixel corners w/in triangle if(np == 4) // pixel is entirely w/in triangle return 1.0; // check corners of triangle to see if they're in the pixel for(i=0; i<3; i++) // loop over triangle corners if(pixelcontains(pixel, node[i]->coord())) intersection.add(node[i]->coord()); int nt = intersection.npts() - np; // no. of triangle vertices w/in pixel if(nt == 3) // triangle is completely inside pixel return area(); // look for intersections of edges for(i=0; i<3; i++) { // loop over triangle edges const MeshCoord &t1 = node[i]->coord(); const MeshCoord &t2 = node[(i+1)%3]->coord(); MeshCoord intercept; // intersections with horizontal pixel edges if(t1.y != t2.y) { // triangle edge not horizontal // Don't have to look at horizontal triangle edges, since if // they lie along a horizontal pixel edge, the intersections // will have been detected when checking to see if the triangle // corners are inside the pixel. if(xintercept(t1, t2, pixelcorner[LL], pixelcorner[LR], intercept)) intersection.add(intercept); if(xintercept(t1, t2, pixelcorner[UR], pixelcorner[UL], intercept)) intersection.add(intercept); } // intersections with vertical pixel edges if(t1.x != t2.x) { // triangle edge not vertical // Don't have to look at vertical triangle edges. if(yintercept(t1, t2, pixelcorner[LR], pixelcorner[UR], intercept)) intersection.add(intercept); if(yintercept(t1, t2, pixelcorner[LL], pixelcorner[UL], intercept)) intersection.add(intercept); } } return intersection.area(); } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// #ifdef DEBUG void AMTriangle::dump_intersections() const { // cerr << "-----------------" << endl // << "Nodes: " << *node[0] << " " << *node[1] << " " << *node[2] << endl; // double total = 0; // cerr << "Intersections:" << endl; // for(AMTriangleIterator it(*this); !it.end(); ++it) { // const Cell_coordinate pixel = (*this)[it]; // double intersect = intersection(pixel); // total += intersect; // cerr << pixel << " " << intersect << endl; // } // cerr << "Total area = " << total << endl; } #endif // DEBUG