// // surf.cc // // Copyright (C) 1996 Limit Point Systems, Inc. // // Author: Curtis Janssen // Maintainer: LPS // // This file is part of the SC Toolkit. // // The SC Toolkit is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published by // the Free Software Foundation; either version 2, or (at your option) // any later version. // // The SC Toolkit is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with the SC Toolkit; see the file COPYING.LIB. If not, write to // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // // The U.S. Government is granted a limited license as per AL 91-7. // #ifdef __GNUC__ #pragma implementation #endif #include #include #include #include #include #include #include using namespace std; using namespace sc; #ifndef WRITE_OOGL #define WRITE_OOGL 1 #endif #if WRITE_OOGL #include #endif ///////////////////////////////////////////////////////////////////////// // TriangulatedSurface static ClassDesc TriangulatedSurface_cd( typeid(TriangulatedSurface),"TriangulatedSurface",1,"public DescribedClass", create, create, 0); TriangulatedSurface::TriangulatedSurface(): _verbose(0), _debug(0), _triangle_vertex(0), _triangle_edge(0), _edge_vertex(0), _integrator(new GaussTriangleIntegrator(1)) { clear(); } TriangulatedSurface::TriangulatedSurface(const Ref& keyval): _triangle_vertex(0), _triangle_edge(0), _edge_vertex(0) { _verbose = keyval->booleanvalue("verbose"); _debug = keyval->booleanvalue("debug"); Ref triint; triint << keyval->describedclassvalue("integrator"); if (triint.null()) { triint = new GaussTriangleIntegrator(1); } set_integrator(triint); triint << keyval->describedclassvalue("fast_integrator"); set_fast_integrator(triint); triint << keyval->describedclassvalue("accurate_integrator"); set_accurate_integrator(triint); clear(); } TriangulatedSurface::~TriangulatedSurface() { clear(); } void TriangulatedSurface::topology_info(ostream&o) { topology_info(nvertex(), nedge(), ntriangle(), o); } void TriangulatedSurface::topology_info(int v, int e, int t, ostream&o) { // Given v vertices i expect 2*v - 4*n_surface triangles // and 3*v - 6*n_surface edges o << indent << scprintf("n_vertex = %d, n_edge = %d, n_triangle = %d:", v, e, t) << endl; int nsurf_e = ((3*v - e)%6 == 0)? (3*v - e)/6 : -1; int nsurf_t = ((2*v - t)%4 == 0)? (2*v - t)/4 : -1; if ((nsurf_e!=-1) && (nsurf_e == nsurf_t)) { o << indent << scprintf(" this is consistent with n_closed_surface - n_hole = %d", nsurf_e) << endl; } else { o << indent << scprintf(" this implies that some surfaces are not closed") << endl; } } void TriangulatedSurface::set_integrator(const Ref& i) { _integrator = i; } void TriangulatedSurface::set_fast_integrator(const Ref& i) { _fast_integrator = i; } void TriangulatedSurface::set_accurate_integrator(const Ref& i) { _accurate_integrator = i; } Ref TriangulatedSurface::integrator(int) { // currently the argument, the integer index of the triangle, is ignored return _integrator; } Ref TriangulatedSurface::fast_integrator(int) { // currently the argument, the integer index of the triangle, is ignored return _fast_integrator.null()?_integrator:_fast_integrator; } Ref TriangulatedSurface::accurate_integrator(int) { // currently the argument, the integer index of the triangle, is ignored return _accurate_integrator.null()?_integrator:_accurate_integrator; } void TriangulatedSurface::clear_int_arrays() { if (_triangle_vertex) { for (int i=0; i<_triangles.size(); i++) { delete[] _triangle_vertex[i]; } delete[] _triangle_vertex; } _triangle_vertex = 0; if (_triangle_edge) { for (int i=0; i<_triangles.size(); i++) { delete[] _triangle_edge[i]; } delete[] _triangle_edge; } _triangle_edge = 0; if (_edge_vertex) { for (int i=0; i<_edges.size(); i++) { delete[] _edge_vertex[i]; } delete[] _edge_vertex; } _edge_vertex = 0; _completed_surface = 0; } void TriangulatedSurface::clear() { _completed_surface = 0; clear_int_arrays(); _have_values = 0; _values.clear(); _vertices.clear(); _edges.clear(); _triangles.clear(); _tmp_edges.clear(); } void TriangulatedSurface::complete_surface() { complete_ref_arrays(); complete_int_arrays(); _completed_surface = 1; } void TriangulatedSurface::complete_ref_arrays() { _tmp_edges.clear(); _index_to_edge.clear(); _edge_to_index.clear(); int i; int ntri = ntriangle(); _edges.clear(); for (i=0; i tri = triangle(i); add_edge(tri->edge(0)); add_edge(tri->edge(1)); add_edge(tri->edge(2)); } int ne = nedge(); _vertices.clear(); _index_to_vertex.clear(); _vertex_to_index.clear(); for (i=0; i e = edge(i); add_vertex(e->vertex(0)); add_vertex(e->vertex(1)); } } void TriangulatedSurface::complete_int_arrays() { clear_int_arrays(); int i; int ntri = ntriangle(); int ne = nedge(); // construct the array that converts the triangle number and vertex // number within the triangle to the overall vertex number _triangle_vertex = new int*[ntri]; for (i=0; i v = triangle(i)->vertex(j); _triangle_vertex[i][j] = _vertex_to_index[v]; } } // construct the array that converts the triangle number and edge number // within the triangle to the overall edge number _triangle_edge = new int*[ntri]; for (i=0; i e = triangle(i)->edge(j); _triangle_edge[i][j] = _edge_to_index[e]; } } // construct the array that converts the edge number and vertex number // within the edge to the overall vertex number _edge_vertex = new int*[ne]; for (i=0; i v = edge(i)->vertex(j); _edge_vertex[i][j] = _vertex_to_index[v]; } } } void TriangulatedSurface::compute_values(Ref&vol) { int n = _vertices.size(); _values.resize(n); for (int i=0; iset_x(vertex(i)->point()); _values[i] = vol->value(); } _have_values = 1; } double TriangulatedSurface::flat_area() { double result = 0.0; for (std::set >::iterator i=_triangles.begin(); i!=_triangles.end(); i++) { result += (*i)->flat_area(); } return result; } double TriangulatedSurface::flat_volume() { double result = 0.0; for (int i=0; i<_triangles.size(); i++) { // get the vertices of the triangle SCVector3 A(vertex(triangle_vertex(i,0))->point()); SCVector3 B(vertex(triangle_vertex(i,1))->point()); SCVector3 C(vertex(triangle_vertex(i,2))->point()); // project the vertices onto the xy plane SCVector3 Axy(A); Axy[2] = 0.0; SCVector3 Bxy(B); Bxy[2] = 0.0; SCVector3 Cxy(C); Cxy[2] = 0.0; // construct the legs of the triangle in the xy plane SCVector3 BAxy = Bxy - Axy; SCVector3 CAxy = Cxy - Axy; // find the lengths of the legs of the triangle in the xy plane double baxy = sqrt(BAxy.dot(BAxy)); double caxy = sqrt(CAxy.dot(CAxy)); // if one of the legs is of length zero, then there is // no contribution from this triangle if (baxy < 1.e-16 || caxy < 1.e-16) continue; // find the sine of the angle between the legs of the triangle // in the xy plane double costheta = BAxy.dot(CAxy)/(baxy*caxy); double sintheta = sqrt(1.0 - costheta*costheta); // the area of the triangle in the xy plane double areaxy = 0.5 * baxy * caxy * sintheta; // the height of the three corners of the triangle // (relative to the z plane) double hA = A[2]; double hB = B[2]; double hC = C[2]; // the volume of the space under the triangle double volume = areaxy * (hA + (hB + hC - 2.0*hA)/3.0); // the orientation of the triangle along the projection axis (z) SCVector3 BA(B-A); SCVector3 CA(C-A); double z_orientation = BA.cross(CA)[2]; if (z_orientation > 0.0) { result += volume; } else { result -= volume; } } // If the volume is negative, then the surface gradients were // opposite in sign to the direction assumed. Flip the sign // to fix. return fabs(result); } double TriangulatedSurface::area() { double area = 0.0; TriangulatedSurfaceIntegrator triint(this); for (triint = 0; triint.update(); triint++) { area += triint.w(); } return area; } double TriangulatedSurface::volume() { double volume = 0.0; TriangulatedSurfaceIntegrator triint(this); for (triint = 0; triint.update(); triint++) { volume += triint.weight()*triint.dA()[2]*triint.current()->point()[2]; } return volume; } void TriangulatedSurface::add_vertex(const Ref&t) { int i = _vertices.size(); _vertices.insert(t); if (i != _vertices.size()) { _index_to_vertex.push_back(t); _vertex_to_index[t] = i; if (_index_to_vertex.size() != _vertex_to_index.size()) { ExEnv::errn() << "TriangulatedSurface::add_vertex: length mismatch" << endl; abort(); } } } void TriangulatedSurface::add_edge(const Ref&t) { int i = _edges.size(); _edges.insert(t); if (i != _edges.size()) { _index_to_edge.push_back(t); _edge_to_index[t] = i; if (_index_to_edge.size() != _edge_to_index.size()) { ExEnv::errn() << "TriangulatedSurface::add_edge: length mismatch" << endl; abort(); } } } void TriangulatedSurface::add_triangle(const Ref&t) { if (_completed_surface) clear(); int i = _triangles.size(); _triangles.insert(t); if (i != _triangles.size()) { _index_to_triangle.push_back(t); _triangle_to_index[t] = i; if (_index_to_triangle.size() != _triangle_to_index.size()) { ExEnv::errn() << "TriangulatedSurface::add_triangle: length mismatch" << endl; abort(); } } } void TriangulatedSurface::add_triangle(const Ref& v1, const Ref& v2, const Ref& v3) { // Find this triangle's edges if they have already be created // for some other triangle. Ref e0, e1, e2; const std::set > &v1edges = _tmp_edges[v1]; const std::set > &v2edges = _tmp_edges[v2]; std::set >::const_iterator ix; for (ix = v1edges.begin(); ix != v1edges.end(); ix++) { const Ref& e = *ix; if (e->vertex(0) == v2 || e->vertex(1) == v2) { e0 = e; } else if (e->vertex(0) == v3 || e->vertex(1) == v3) { e2 = e; } } for (ix = v2edges.begin(); ix != v2edges.end(); ix++) { const Ref& e = *ix; if (e->vertex(0) == v3 || e->vertex(1) == v3) { e1 = e; } } if (e0.null()) { e0 = newEdge(v1,v2); _tmp_edges[v1].insert(e0); _tmp_edges[v2].insert(e0); } if (e1.null()) { e1 = newEdge(v2,v3); _tmp_edges[v2].insert(e1); _tmp_edges[v3].insert(e1); } if (e2.null()) { e2 = newEdge(v3,v1); _tmp_edges[v3].insert(e2); _tmp_edges[v1].insert(e2); } int orientation; if (e0->vertex(0) == v1) { orientation = 0; } else { orientation = 1; } add_triangle(newTriangle(e0,e1,e2,orientation)); } // If a user isn't keeping track of edges while add_triangle is being // used to build the surface, then this can be called to see if an edge // already exists (at a great performance cost). Ref TriangulatedSurface::find_edge(const Ref& v1, const Ref& v2) { std::set >::iterator i; for (i=_triangles.begin(); i!=_triangles.end(); i++) { Ref t = *i; Ref e1 = t->edge(0); Ref e2 = t->edge(1); Ref e3 = t->edge(2); if (e1->vertex(0) == v1 && e1->vertex(1) == v2) return e1; if (e1->vertex(1) == v1 && e1->vertex(0) == v2) return e1; if (e2->vertex(0) == v1 && e2->vertex(1) == v2) return e2; if (e2->vertex(1) == v1 && e2->vertex(0) == v2) return e2; if (e3->vertex(0) == v1 && e3->vertex(1) == v2) return e3; if (e3->vertex(1) == v1 && e3->vertex(0) == v2) return e3; } return 0; } void TriangulatedSurface::print(ostream&o) const { o << indent << "TriangulatedSurface:" << endl; int i; int np = nvertex(); o << indent << scprintf(" %3d Vertices:",np) << endl; for (i=0; i p = vertex(i); o << indent << scprintf(" %3d:",i); for (int j=0; j<3; j++) { o << scprintf(" % 15.10f", p->point()[j]); } o << endl; } int ne = nedge(); o << indent << scprintf(" %3d Edges:",ne) << endl; for (i=0; i e = edge(i); Ref v0 = e->vertex(0); Ref v1 = e->vertex(1); std::map,int>::const_iterator v0i=_vertex_to_index.find(v0); std::map,int>::const_iterator v1i=_vertex_to_index.find(v1); int v0int = v0i==_vertex_to_index.end()? -1: v0i->second; int v1int = v1i==_vertex_to_index.end()? -1: v1i->second; o << indent << scprintf(" %3d: %3d %3d",i, v0int, v1int) << endl; } int nt = ntriangle(); o << indent << scprintf(" %3d Triangles:",nt) << endl; for (i=0; i tri = triangle(i); Ref e0 = tri->edge(0); Ref e1 = tri->edge(1); Ref e2 = tri->edge(2); std::map,int>::const_iterator e0i = _edge_to_index.find(e0); std::map,int>::const_iterator e1i = _edge_to_index.find(e1); std::map,int>::const_iterator e2i = _edge_to_index.find(e2); int e0int = e0i==_edge_to_index.end()? -1: e0i->second; int e1int = e1i==_edge_to_index.end()? -1: e1i->second; int e2int = e2i==_edge_to_index.end()? -1: e2i->second; o << indent << scprintf(" %3d: %3d %3d %3d",i, e0int, e1int, e2int) << endl; } } void TriangulatedSurface::print_vertices_and_triangles(ostream&o) const { o << indent << "TriangulatedSurface:" << endl; int i; int np = nvertex(); o << indent << scprintf(" %3d Vertices:",np) << endl; for (i=0; i p = vertex(i); o << indent << scprintf(" %3d:",i); for (int j=0; j<3; j++) { o << scprintf(" % 15.10f", p->point()[j]); } o << endl; } int nt = ntriangle(); o << indent << scprintf(" %3d Triangles:",nt) << endl; for (i=0; i tri = triangle(i); o << indent << scprintf(" %3d: %3d %3d %3d",i, _triangle_vertex[i][0], _triangle_vertex[i][1], _triangle_vertex[i][2]) << endl; } } void TriangulatedSurface::render(const Ref &render) { Ref poly = new RenderedPolygons; poly->initialize(_vertices.size(), _triangles.size(), RenderedPolygons::Vertex); std::set >::iterator iv; std::set >::iterator it; std::map, int> vertex_to_index; int i = 0; for (iv = _vertices.begin(); iv != _vertices.end(); iv++, i++) { Ref v = *iv; vertex_to_index[v] = i; poly->set_vertex(i, v->point()[0], v->point()[1], v->point()[2]); poly->set_vertex_rgb(i, 0.3, 0.3, 0.3); } i = 0; for (it = _triangles.begin(); it != _triangles.end(); it++, i++) { Ref t = *it; poly->set_face(i, vertex_to_index[t->vertex(0)], vertex_to_index[t->vertex(1)], vertex_to_index[t->vertex(2)]); } render->render(poly.pointer()); } void TriangulatedSurface::print_geomview_format(ostream&o) const { o << "OFF" << endl; o << nvertex() << " " << ntriangle() << " " << nedge() << endl; int i; int np = nvertex(); for (i=0; i p = vertex(i); for (int j=0; j<3; j++) { o << scprintf(" % 15.10f", p->point()[j]); } o << endl; } int nt = ntriangle(); for (i=0; i tri = triangle(i); o << scprintf(" 3 %3d %3d %3d", _triangle_vertex[i][0], _triangle_vertex[i][1], _triangle_vertex[i][2]) << endl; } } void TriangulatedSurface::recompute_index_maps() { int i; std::set >::iterator iv; std::set >::iterator ie; std::set >::iterator it; // fix the index maps _vertex_to_index.clear(); _edge_to_index.clear(); _triangle_to_index.clear(); _index_to_vertex.clear(); _index_to_edge.clear(); _index_to_triangle.clear(); _index_to_vertex.resize(_vertices.size()); for (i=0, iv = _vertices.begin(); iv != _vertices.end(); i++, iv++) { _vertex_to_index[*iv] = i; _index_to_vertex[i] = *iv; } _index_to_edge.resize(_edges.size()); for (i=0, ie = _edges.begin(); ie != _edges.end(); i++, ie++) { _edge_to_index[*ie] = i; _index_to_edge[i] = *ie; } _index_to_triangle.resize(_triangles.size()); for (i=0, it = _triangles.begin(); it != _triangles.end(); i++, it++) { _triangle_to_index[*it] = i; _index_to_triangle[i] = *it; } } Edge* TriangulatedSurface::newEdge(const Ref& v0, const Ref& v1) const { return new Edge(v0,v1); } Triangle* TriangulatedSurface::newTriangle(const Ref& e0, const Ref& e1, const Ref& e2, int orientation) const { return new Triangle(e0,e1,e2,orientation); } ////////////////////////////////////////////////////////////////////// // TriangulatedSurfaceIntegrator TriangulatedSurfaceIntegrator:: TriangulatedSurfaceIntegrator(const Ref&ts) { set_surface(ts); use_default_integrator(); _itri = 0; _irs = 0; } TriangulatedSurfaceIntegrator:: TriangulatedSurfaceIntegrator() { use_default_integrator(); } void TriangulatedSurfaceIntegrator:: operator =(const TriangulatedSurfaceIntegrator&i) { set_surface(i._ts); _integrator = i._integrator; _itri = i._itri; _irs = i._irs; } TriangulatedSurfaceIntegrator:: ~TriangulatedSurfaceIntegrator() { } void TriangulatedSurfaceIntegrator::set_surface(const Ref&s) { _ts = s; _current = new Vertex(); } int TriangulatedSurfaceIntegrator:: vertex_number(int i) { return _ts->triangle_vertex(_itri,i); } Ref TriangulatedSurfaceIntegrator:: current() { return _current; } int TriangulatedSurfaceIntegrator::n() { int result = 0; int ntri = _ts->ntriangle(); for (int i=0; i*_integrator)(i)->n(); } return result; } int TriangulatedSurfaceIntegrator::update() { if (_itri < 0 || _itri >= _ts->ntriangle()) return 0; TriangleIntegrator* i = (_ts.pointer()->*_integrator)(_itri).pointer(); _s = i->s(_irs); _r = i->r(_irs); _weight = i->w(_irs); Ref t = _ts->triangle(_itri); Ref coef = i->coef(t->order(),_irs); t->interpolate(coef, _r, _s, _current, _dA); _surface_element = _dA.norm(); static double cum; if (_irs == 0) cum = 0.0; cum += _surface_element * _weight; //ExEnv::outn() << scprintf("%2d dA = %12.8f, w = %12.8f, Sum wdA = %12.8f", // _irs, _surface_element, _weight, cum) // << endl; return (int) 1; } void TriangulatedSurfaceIntegrator:: operator ++() { int n = (_ts.pointer()->*_integrator)(_itri)->n(); if (_irs == n-1) { _irs = 0; if (_grp.null()) _itri++; else _itri += _grp->n(); } else { _irs++; } } void TriangulatedSurfaceIntegrator::distribute(const Ref &grp) { _grp = grp; } int TriangulatedSurfaceIntegrator:: operator = (int i) { _itri = i; _irs = 0; return i; } void TriangulatedSurfaceIntegrator::use_default_integrator() { _integrator = &TriangulatedSurface::integrator; } void TriangulatedSurfaceIntegrator::use_fast_integrator() { _integrator = &TriangulatedSurface::fast_integrator; } void TriangulatedSurfaceIntegrator::use_accurate_integrator() { _integrator = &TriangulatedSurface::accurate_integrator; } ///////////////////////////////////////////////////////////////////////// // TriangulatedImplicitSurface static ClassDesc TriangulatedImplicitSurface_cd( typeid(TriangulatedImplicitSurface),"TriangulatedImplicitSurface",1,"public TriangulatedSurface", 0, create, 0); TriangulatedImplicitSurface:: TriangulatedImplicitSurface(const Ref&keyval): TriangulatedSurface(keyval) { inited_ = 0; vol_ << keyval->describedclassvalue("volume"); if (keyval->error() != KeyVal::OK) { ExEnv::errn() << "TriangulatedImplicitSurface(const Ref&keyval): " << "requires \"volume\"" << endl; abort(); } isovalue_ = keyval->doublevalue("value"); if (keyval->error() != KeyVal::OK) isovalue_ = 0.0; fix_orientation_ = keyval->booleanvalue("fix_orientation"); if (keyval->error() != KeyVal::OK) fix_orientation_ = 1; remove_short_edges_ = keyval->booleanvalue("remove_short_edges"); if (keyval->error() != KeyVal::OK) remove_short_edges_ = 1; remove_slender_triangles_ = keyval->booleanvalue("remove_slender_triangles"); if (keyval->error() != KeyVal::OK) remove_slender_triangles_ = 0; remove_small_triangles_ = keyval->booleanvalue("remove_small_triangles"); if (keyval->error() != KeyVal::OK) remove_small_triangles_ = 0; short_edge_factor_ = keyval->doublevalue("short_edge_factor"); if (keyval->error() != KeyVal::OK) short_edge_factor_ = 0.4; slender_triangle_factor_ = keyval->doublevalue("slender_triangle_factor"); if (keyval->error() != KeyVal::OK) slender_triangle_factor_ = 0.2; small_triangle_factor_ = keyval->doublevalue("small_triangle_factor"); if (keyval->error() != KeyVal::OK) small_triangle_factor_ = 0.2; resolution_ = keyval->doublevalue("resolution"); if (keyval->error() != KeyVal::OK) resolution_ = 1.0; order_ = keyval->intvalue("order"); if (keyval->error() != KeyVal::OK) order_ = 1; int initialize = keyval->booleanvalue("initialize"); if (initialize) init(); } void TriangulatedImplicitSurface::init() { if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: init start" << endl; ImplicitSurfacePolygonizer isogen(vol_); isogen.set_resolution(resolution_); if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: isosurface" << endl; isogen.isosurface(isovalue_,*this); #if WRITE_OOGL if (_debug) { render(new OOGLRender("surfiso.oogl")); } #endif if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: orientation" << endl; if (fix_orientation_) fix_orientation(); #if WRITE_OOGL if (_debug) { render(new OOGLRender("surffix.oogl")); } #endif if (remove_short_edges_) { if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: short edges" << endl; remove_short_edges(short_edge_factor_*resolution_,vol_,isovalue_); if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: orientation" << endl; if (fix_orientation_) fix_orientation(); } if (remove_slender_triangles_ || remove_small_triangles_) { if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: slender" << endl; double height_cutoff = slender_triangle_factor_ * resolution_; double area_cutoff = small_triangle_factor_*resolution_*resolution_*0.5; remove_slender_triangles(remove_slender_triangles_, height_cutoff, remove_small_triangles_, area_cutoff, vol_,isovalue_); if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: orientation" << endl; if (fix_orientation_) fix_orientation(); } // see if a higher order approximation to the surface is required if (order_ > 1) { if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: order" << endl; int i; for (i=0; iset_order(order_, vol_, isovalue_); } for (i=0; iset_order(order_, vol_, isovalue_); } } inited_ = 1; if (_verbose) ExEnv::outn() << "TriangulatedImplicitSurface: init done" << endl; } TriangulatedImplicitSurface::~TriangulatedImplicitSurface() { } ///////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: c++ // c-file-style: "CLJ" // End: