/*===========================================================================*\ * * * IsoEx * * Copyright (C) 2002 by Computer Graphics Group, RWTH Aachen * * www.rwth-graphics.de * * * *---------------------------------------------------------------------------* * * * License * * * * This library 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, version 2. * * * * This library 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 this library; if not, write to the Free Software * * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * \*===========================================================================*/ //============================================================================= // // CLASS ExtendedMarchingCubesT - IMPLEMENTATION // //============================================================================= #define ISOEX_EXTMARCHINGCUBEST_C //== INCLUDES ================================================================= #include #include #include #include #include #include #include //== NAMESPACES =============================================================== namespace IsoEx { //== IMPLEMENTATION ========================================================== template ExtendedMarchingCubesT:: ExtendedMarchingCubesT(const Grid& _grid, Mesh& _mesh, double _iso_value, double _feature_angle) : grid_(_grid), mesh_(_mesh), feature_angle_(_feature_angle / 180.0 * M_PI), n_edges_(0), n_corners_(0) { CubeIterator cube_it(grid_.begin()), cube_end(grid_.end()); for (; cube_it!=cube_end; ++cube_it) process_cube(*cube_it, _iso_value); flip_edges(); std::cerr << "Found " << n_edges_ << " edge features, " << n_corners_ << " corner features\n"; } //----------------------------------------------------------------------------- template void ExtendedMarchingCubesT:: process_cube(CubeIdx _idx, double _iso_value) { PointIdx corner[8]; VertexHandle samples[12]; unsigned char cubetype(0); unsigned int i, j; unsigned int n_components, n_vertices; int *indices; VertexHandle vh; std::vector vhandles; // get corner vertices for (i=0; i<8; ++i) corner[i] = grid_.point_idx(_idx, i); // determine cube type for (i=0; i<8; ++i) if (!grid_.is_inside(corner[i], _iso_value)) cubetype |= (1< create triangle fan around feature vertex if (vh.is_valid()) { vhandles.push_back(vhandles[0]); for (j=0; j old marching cubes triangle table else { for (j=0; polyTable[n_vertices][j] != -1; j+=3) mesh_.add_face( samples[indices[polyTable[n_vertices][ j]]], samples[indices[polyTable[n_vertices][j+1]]], samples[indices[polyTable[n_vertices][j+2]]] ); } indices += n_vertices; } } //----------------------------------------------------------------------------- template typename ExtendedMarchingCubesT::VertexHandle ExtendedMarchingCubesT:: add_vertex(PointIdx _p0, PointIdx _p1, double _iso_value) { // find vertex if it has been computed already VertexHandle vh = edge2vertex_.find(_p0, _p1); if (vh.is_valid()) return vh; // generate new vertex const typename Mesh::Point p0(grid_.point(_p0)); const typename Mesh::Point p1(grid_.point(_p1)); typename Mesh::Point point, normal(0,0,0); typename Mesh::Point::value_type distance; bool ok = grid_.directed_distance(p0, p1, point, normal, distance, _iso_value); if (!ok) { // should not happen, just in case of precision errors... float s0 = fabs(grid_.scalar_distance(_p0)); float s1 = fabs(grid_.scalar_distance(_p1)); float t = s0 / (s0+s1); point = (1.0f-t)*p0 + t*p1; } // add vertex vh = mesh_.add_vertex(point); mesh_.set_normal(vh, normal); edge2vertex_.insert(_p0, _p1, vh); return vh; } //----------------------------------------------------------------------------- template typename ExtendedMarchingCubesT::VertexHandle ExtendedMarchingCubesT:: find_feature(const VertexHandleVector& _vhandles) { unsigned int i, j, nV = _vhandles.size(), rank; // collect point & normals; std::vector p, n; p.resize(nV); n.resize(nV); for (i=0; i return invalid vertex handle if (min_c > cos(feature_angle_)) return Mesh::InvalidVertexHandle; // ok, we have a feature // is it edge or corner, i.e. rank 2 or 3 ? axis.normalize(); for (min_c=1.0, max_c=-1.0, i=0; i max_c) max_c = c; } c = std::max(fabs(min_c),fabs(max_c)); c = sqrt(1.0-c*c); rank = (c > cos(feature_angle_) ? 2 : 3); if (rank == 2) ++n_edges_; else ++n_corners_; // setup linear system (find intersection of tangent planes) Math::MatrixT A(nV, 3); Math::VectorT b(nV); for (i = 0; i < nV; ++i) { A(i, 0) = n[i][0]; A(i, 1) = n[i][1]; A(i, 2) = n[i][2]; b(i) = (p[i] | n[i]); } // SVD of matrix A Math::MatrixT V(3, 3); Math::VectorT S(nV); Math::svd_decomp(A, S, V); // rank == 2 -> suppress smallest singular value if (rank == 2) { double smin = FLT_MAX; unsigned int sminid = 0; unsigned int srank = std::min(nV, 3u); for (i = 0; i < srank; ++i) if (S(i) < smin) { smin = S(i); sminid = i; } S(sminid) = 0.0; } // SVD backsubstitution -> least squares, least norm solution x Math::VectorT x(3); Math::svd_backsub(A, S, V, b, x); // transform x to world coords typename Mesh::Point point(x(0), x(1), x(2)); point += cog; // insert the feature-point VertexHandle vh = mesh_.add_vertex(point); mesh_.status(vh).set_feature(true); return vh; } //----------------------------------------------------------------------------- template void ExtendedMarchingCubesT::flip_edges() { VertexHandle v0, v1, v2, v3; typename Mesh::HalfedgeHandle he; typename Mesh::EdgeIter e_it(mesh_.edges_begin()), e_end(mesh_.edges_end()); for (; e_it != e_end; ++e_it) { if (mesh_.is_flip_ok(*e_it)) { he = mesh_.halfedge_handle(*e_it, 0); v0 = mesh_.to_vertex_handle(he); he = mesh_.next_halfedge_handle(he); v1 = mesh_.to_vertex_handle(he); he = mesh_.halfedge_handle(*e_it, 1); v2 = mesh_.to_vertex_handle(he); he = mesh_.next_halfedge_handle(he); v3 = mesh_.to_vertex_handle(he); // flip edge if it would connect two features (v1, v3) // and not disconnect two others (v0, v2) afterwards // (maybe we should check for flipping triangle normals) if (mesh_.status(v1).feature() && mesh_.status(v3).feature() && !mesh_.status(v0).feature() && !mesh_.status(v2).feature()) mesh_.flip(*e_it); } } } //============================================================================= } // namespace IsoEx //=============================================================================