/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program 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 General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ /**************************************************************************** History $Log: not supported by cvs2svn $ ****************************************************************************/ #ifndef __VCGLIB_TRI_CLIP #define __VCGLIB_TRI_CLIP #include #include namespace vcg { namespace tri { template class TriMeshClipper { public: typedef TriMeshClipper ClassType; typedef TRIMESHTYPE TriMeshType; typedef typename TriMeshType::FaceType FaceType; typedef typename FaceType::VertexType VertexType; typedef typename VertexType::CoordType CoordType; typedef typename CoordType::ScalarType ScalarType; /* static inline void Box(const Box3 & b, VERTEXINTEPOLATOR & vInterp, TriMeshType & m); Clip mesh "m" against an axis aligned box (in place version); Notes: 1) faces marked as deleted are skipped; 2) faces completely outside box are marked as deleted; 3) faces completely inside box are left unchanged; 4) faces intersecting box's sides are marked as deleted: they are replaced with proper tesselation; new vertices and faces are created, so reallocation could occour; previously saved pointers could not to be valid anymore, thus they should be updated; 5) vInterp functor must implement a n operator with signature void operator () (const VERTEX & v0, const VERTEX & v1, const VERTEX & v2, const Scalar & a, const Scalar & b, VERTEX & r); its semantic is to intepolate vertex attribute across triangle; a typical implementation is; r.P() = v0.P() + a * (v1.P() - v0.P()) + b * (v2.P() - v0.P()); // interpolate position r.N() = v0.N() + a * (v1.N() - v0.N()) + b * (v2.N() - v0.N()); // interpolate normal ... // interpolate other vertex attributes */ template static inline void Box(const Box3 & b, VERTEXINTEPOLATOR & vInterp, TriMeshType & m) { std::vector facesToDelete; ClassType::Box(b, vInterp, m, facesToDelete); for (size_t i=0; i static inline void Box(const Box3 & b, VERTEXINTEPOLATOR & vInterp, TriMeshType & m, FACEINDEXCONTAINER & facesToDelete) { if (m.fn <= 0) { return; } class VertexInfo { public: typedef VertexInfo ClassType; ScalarType fU; ScalarType fV; unsigned int idx; unsigned int tref; }; typedef std::vector VertexInfoVec; class TriangleInfo { public: typedef TriangleInfo ClassType; unsigned int v[3]; unsigned int idx; }; typedef std::vector TriangleInfoVec; class EdgeIsect { public: CoordType p; unsigned int idx; }; class EdgeIntersections { public: unsigned int n; EdgeIsect isects[6]; EdgeIntersections(void) { this->n = 0; } }; typedef stdext::hash_map UIntHMap; typedef typename UIntHMap::iterator UIntHMap_i; typedef typename UIntHMap::value_type UIntHMap_v; typedef stdext::hash_map EdgeMap; typedef typename EdgeMap::iterator EdgeMap_i; typedef typename EdgeMap::value_type EdgeMap_v; typedef typename TriMeshType::FaceIterator FaceIterator; EdgeMap edges; VertexInfoVec vInfos; TriangleInfoVec tInfos; CoordType vTriangle[4]; CoordType vClipped[64]; CoordType pvP0[64]; CoordType pvP1[64]; unsigned int numDeletedTris = 0; unsigned int numTriangles = 0; unsigned int numVertices = 0; unsigned int vIdx = (unsigned int)(m.vn); unsigned int tIdx = (unsigned int)(m.fn); ScalarType boxOffsets[6]; boxOffsets[0] = b.min[0]; boxOffsets[1] = -b.max[0]; boxOffsets[2] = b.min[1]; boxOffsets[3] = -b.max[1]; boxOffsets[4] = b.min[2]; boxOffsets[5] = -b.max[2]; UIntHMap emptyMap; EdgeIntersections emptyIsects; const ScalarType eps = (ScalarType)(1e-6); for (FaceIterator it=m.face.begin(); it!=m.face.end(); ++it) { if ((*it).IsD()) { continue; } unsigned int cc[3]; cc[0] = ClassType::BoxClipCode(boxOffsets, (*it).V(0)->P()); cc[1] = ClassType::BoxClipCode(boxOffsets, (*it).V(1)->P()); cc[2] = ClassType::BoxClipCode(boxOffsets, (*it).V(2)->P()); if ((cc[0] | cc[1] | cc[2]) == 0) { continue; } const unsigned int refT = (unsigned int)(std::distance(m.face.begin(), it)); if ((cc[0] & cc[1] & cc[2]) != 0) { facesToDelete.push_back(refT); (*it).SetD(); numDeletedTris++; continue; } facesToDelete.push_back(refT); vTriangle[0] = (*it).V(0)->P(); vTriangle[1] = (*it).V(1)->P(); vTriangle[2] = (*it).V(2)->P(); vTriangle[3] = (*it).V(0)->P(); unsigned int n, n0, n1; ClipPolygonLine(0, b.min[0], vTriangle, 4, pvP1, n1); ClipPolygonLine(1, b.max[0], pvP1, n1, pvP0, n0); ClipPolygonLine(2, b.min[1], pvP0, n0, pvP1, n1); ClipPolygonLine(3, b.max[1], pvP1, n1, pvP0, n0); ClipPolygonLine(4, b.min[2], pvP0, n0, pvP1, n1); ClipPolygonLine(5, b.max[2], pvP1, n1, vClipped, n); assert(n < 64); unsigned int firstV, lastV; if (n > 2) { if (vClipped[0] == vClipped[n - 1]) { n--; } const CoordType vU = vTriangle[1] - vTriangle[0]; const CoordType vV = vTriangle[2] - vTriangle[0]; const ScalarType tArea = (vU ^ vV).SquaredNorm(); if (tArea < eps) { continue; } unsigned int tvidx[3]; tvidx[0] = (*it).V(0) - &(*(m.vert.begin())); tvidx[1] = (*it).V(1) - &(*(m.vert.begin())); tvidx[2] = (*it).V(2) - &(*(m.vert.begin())); numTriangles += n - 2; size_t vBegin = vInfos.size(); VertexInfo vnfo; TriangleInfo tnfo; unsigned int vmin[3]; unsigned int vmax[3]; if (tvidx[0] < tvidx[1]) { vmin[0] = tvidx[0]; vmax[0] = tvidx[1]; } else { vmin[0] = tvidx[1]; vmax[0] = tvidx[0]; } if (tvidx[0] < tvidx[2]) { vmin[1] = tvidx[0]; vmax[1] = tvidx[2]; } else { vmin[1] = tvidx[2]; vmax[1] = tvidx[0]; } if (tvidx[1] < tvidx[2]) { vmin[2] = tvidx[1]; vmax[2] = tvidx[2]; } else { vmin[2] = tvidx[2]; vmax[2] = tvidx[1]; } for (unsigned int i=0; i mi = edges.insert(std::make_pair(vmin[1], emptyMap)); std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[1], emptyIsects)); bool found = false; for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) { if (vClipped[i] == (*(hi.first)).second.isects[s].p) { found = true; vnfo.idx = (*(hi.first)).second.isects[s].idx; break; } } if (!found) { vnfo.idx = vIdx++; numVertices++; vInfos.push_back(vnfo); (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vnfo.idx; (*(hi.first)).second.n++; } } else if (vnfo.fU < eps) { std::pair mi = edges.insert(std::make_pair(vmin[0], emptyMap)); std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[0], emptyIsects)); bool found = false; for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) { if (vClipped[i] == (*(hi.first)).second.isects[s].p) { found = true; vnfo.idx = (*(hi.first)).second.isects[s].idx; break; } } if (!found) { vnfo.idx = vIdx++; numVertices++; vInfos.push_back(vnfo); (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vnfo.idx; (*(hi.first)).second.n++; } } else if ((vnfo.fU + vnfo.fV) >= ((ScalarType)(1.0 - 1e-5))) { std::pair mi = edges.insert(std::make_pair(vmin[2], emptyMap)); std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[2], emptyIsects)); bool found = false; for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) { if (vClipped[i] == (*(hi.first)).second.isects[s].p) { found = true; vnfo.idx = (*(hi.first)).second.isects[s].idx; break; } } if (!found) { vnfo.idx = vIdx++; numVertices++; vInfos.push_back(vnfo); (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vnfo.idx; (*(hi.first)).second.n++; } } else { vnfo.idx = vIdx++; numVertices++; vInfos.push_back(vnfo); } if (i == 0) { firstV = vnfo.idx; } if (i > 1) { tnfo.idx = tIdx++; tnfo.v[0] = firstV; tnfo.v[1] = lastV; tnfo.v[2] = vnfo.idx; tInfos.push_back(tnfo); } lastV = vnfo.idx; } } } if (numTriangles == 0) { return; } const unsigned int vSize = (unsigned int)(m.vn); const unsigned int tSize = (unsigned int)(m.fn); typedef Allocator TriMeshAllocatorType; TriMeshAllocatorType::AddVertices(m, numVertices); TriMeshAllocatorType::AddFaces(m, numTriangles); unsigned int j = vSize; for (size_t i=0; i= vSize) { const unsigned int tref = vInfos[i].tref; vInterp(*(m.face[tref].V(0)), *(m.face[tref].V(1)), *(m.face[tref].V(2)), vInfos[i].fV, vInfos[i].fU, m.vert[j]); j++; } } j = tSize; for (size_t i=0; i & b, VERTEXINTEPOLATOR & vInterp, const TriMeshType & m, TriMeshType & r); Clip mesh "m" against an axis aligned box and put resulting data in mesh "r" (out of place version); Notes: 1) input mesh is not modified; 2) faces marked as deleted are skipped; 3) vInterp functor must implement a n operator with signature void operator () (const VERTEX & v0, const VERTEX & v1, const VERTEX & v2, const Scalar & a, const Scalar & b, VERTEX & r); its semantic is to intepolate vertex attribute across triangle; a typical implementation is; r.P() = v0.P() + a * (v1.P() - v0.P()) + b * (v2.P() - v0.P()); // interpolate position r.N() = v0.N() + a * (v1.N() - v0.N()) + b * (v2.N() - v0.N()); // interpolate normal ... // interpolate other vertex attributes */ template static inline void Box(const Box3 & b, VERTEXINTEPOLATOR & vInterp, const TriMeshType & m, TriMeshType & r) { r.Clear(); if (m.fn <= 0) { return; } class VertexInfo { public: typedef VertexInfo ClassType; ScalarType fU; ScalarType fV; unsigned int idx; unsigned int tref; }; typedef std::vector VertexInfoVec; class TriangleInfo { public: typedef TriangleInfo ClassType; unsigned int v[3]; unsigned int idx; }; typedef std::vector TriangleInfoVec; class EdgeIsect { public: CoordType p; unsigned int idx; }; class EdgeIntersections { public: unsigned int n; EdgeIsect isects[6]; EdgeIntersections(void) { this->n = 0; } }; typedef stdext::hash_map UIntHMap; typedef typename UIntHMap::iterator UIntHMap_i; typedef typename UIntHMap::value_type UIntHMap_v; typedef stdext::hash_map EdgeMap; typedef typename EdgeMap::iterator EdgeMap_i; typedef typename EdgeMap::value_type EdgeMap_v; typedef stdext::hash_map UIHMap; typedef typename UIHMap::iterator UIHMap_i; typedef typename TriMeshType::ConstFaceIterator ConstFaceIterator; UIHMap origVertsMap; EdgeMap edges; VertexInfoVec vInfos; TriangleInfoVec tInfos; CoordType vTriangle[4]; CoordType vClipped[64]; CoordType pvP0[64]; CoordType pvP1[64]; unsigned int numDeletedTris = 0; unsigned int numTriangles = 0; unsigned int numVertices = 0; unsigned int vIdx = 0; unsigned int tIdx = 0; ScalarType boxOffsets[6]; boxOffsets[0] = b.min[0]; boxOffsets[1] = -b.max[0]; boxOffsets[2] = b.min[1]; boxOffsets[3] = -b.max[1]; boxOffsets[4] = b.min[2]; boxOffsets[5] = -b.max[2]; UIntHMap emptyMap; EdgeIntersections emptyIsects; const ScalarType eps = (ScalarType)(1e-6); for (ConstFaceIterator it=m.face.begin(); it!=m.face.end(); ++it) { if ((*it).IsD()) { continue; } unsigned int cc[3]; cc[0] = ClassType::BoxClipCode(boxOffsets, (*it).V(0)->P()); cc[1] = ClassType::BoxClipCode(boxOffsets, (*it).V(1)->P()); cc[2] = ClassType::BoxClipCode(boxOffsets, (*it).V(2)->P()); if ((cc[0] | cc[1] | cc[2]) == 0) { TriangleInfo tnfo; VertexInfo vnfo; tnfo.idx = tIdx++; for (int i=0; i<3; ++i) { const unsigned int v = (*it).V(i) - &(*(m.vert.begin())); std::pair hi = origVertsMap.insert(std::make_pair(v, vIdx)); if (hi.second) { vnfo.idx = v; vInfos.push_back(vnfo); tnfo.v[i] = vIdx++; } else { tnfo.v[i] = (*(hi.first)).second; } } tInfos.push_back(tnfo); continue; } if ((cc[0] & cc[1] & cc[2]) != 0) { numDeletedTris++; continue; } vTriangle[0] = (*it).V(0)->P(); vTriangle[1] = (*it).V(1)->P(); vTriangle[2] = (*it).V(2)->P(); vTriangle[3] = (*it).V(0)->P(); unsigned int n, n0, n1; ClipPolygonLine(0, b.min[0], vTriangle, 4, pvP1, n1); ClipPolygonLine(1, b.max[0], pvP1, n1, pvP0, n0); ClipPolygonLine(2, b.min[1], pvP0, n0, pvP1, n1); ClipPolygonLine(3, b.max[1], pvP1, n1, pvP0, n0); ClipPolygonLine(4, b.min[2], pvP0, n0, pvP1, n1); ClipPolygonLine(5, b.max[2], pvP1, n1, vClipped, n); assert(n < 64); unsigned int firstV, lastV; if (n > 2) { if (vClipped[0] == vClipped[n - 1]) { n--; } const CoordType vU = vTriangle[1] - vTriangle[0]; const CoordType vV = vTriangle[2] - vTriangle[0]; const ScalarType tArea = (vU ^ vV).SquaredNorm(); if (tArea < eps) { continue; } unsigned int tvidx[3]; tvidx[0] = (*it).V(0) - &(*(m.vert.begin())); tvidx[1] = (*it).V(1) - &(*(m.vert.begin())); tvidx[2] = (*it).V(2) - &(*(m.vert.begin())); unsigned int refT = (unsigned int)(std::distance(m.face.begin(), it)); numTriangles += n - 2; size_t vBegin = vInfos.size(); VertexInfo vnfo; TriangleInfo tnfo; unsigned int vmin[3]; unsigned int vmax[3]; if (tvidx[0] < tvidx[1]) { vmin[0] = tvidx[0]; vmax[0] = tvidx[1]; } else { vmin[0] = tvidx[1]; vmax[0] = tvidx[0]; } if (tvidx[0] < tvidx[2]) { vmin[1] = tvidx[0]; vmax[1] = tvidx[2]; } else { vmin[1] = tvidx[2]; vmax[1] = tvidx[0]; } if (tvidx[1] < tvidx[2]) { vmin[2] = tvidx[1]; vmax[2] = tvidx[2]; } else { vmin[2] = tvidx[2]; vmax[2] = tvidx[1]; } for (unsigned int i=0; i hi = origVertsMap.insert(std::make_pair(tvidx[0], vIdx)); if (hi.second) { vnfo.idx = tvidx[0]; vInfos.push_back(vnfo); currVIdx = vIdx++; } else { currVIdx = (*(hi.first)).second; } } else if (vClipped[i] == vTriangle[1]) { std::pair hi = origVertsMap.insert(std::make_pair(tvidx[1], vIdx)); if (hi.second) { vnfo.idx = tvidx[1]; vInfos.push_back(vnfo); currVIdx = vIdx++; } else { currVIdx = (*(hi.first)).second; } } else if (vClipped[i] == vTriangle[2]) { std::pair hi = origVertsMap.insert(std::make_pair(tvidx[2], vIdx)); if (hi.second) { vnfo.idx = tvidx[2]; vInfos.push_back(vnfo); currVIdx = vIdx++; } else { currVIdx = (*(hi.first)).second; } } else if (vnfo.fV < eps) { std::pair mi = edges.insert(std::make_pair(vmin[1], emptyMap)); std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[1], emptyIsects)); bool found = false; for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) { if (vClipped[i] == (*(hi.first)).second.isects[s].p) { found = true; vnfo.idx = (unsigned int)(-1); currVIdx = (*(hi.first)).second.isects[s].idx; break; } } if (!found) { (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vIdx; (*(hi.first)).second.n++; vnfo.idx = (unsigned int)(-1); numVertices++; vInfos.push_back(vnfo); currVIdx = vIdx++; } } else if (vnfo.fU < eps) { std::pair mi = edges.insert(std::make_pair(vmin[0], emptyMap)); std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[0], emptyIsects)); bool found = false; for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) { if (vClipped[i] == (*(hi.first)).second.isects[s].p) { found = true; vnfo.idx = (unsigned int)(-1); currVIdx = (*(hi.first)).second.isects[s].idx; break; } } if (!found) { (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vIdx; (*(hi.first)).second.n++; vnfo.idx = (unsigned int)(-1); numVertices++; vInfos.push_back(vnfo); currVIdx = vIdx++; } } else if ((vnfo.fU + vnfo.fV) >= ((ScalarType)(1.0 - 1e-5))) { std::pair mi = edges.insert(std::make_pair(vmin[2], emptyMap)); std::pair hi = (*(mi.first)).second.insert(std::make_pair(vmax[2], emptyIsects)); bool found = false; for (unsigned int s=0; s<(*(hi.first)).second.n; ++s) { if (vClipped[i] == (*(hi.first)).second.isects[s].p) { found = true; vnfo.idx = (unsigned int)(-1); currVIdx = (*(hi.first)).second.isects[s].idx; break; } } if (!found) { (*(hi.first)).second.isects[(*(hi.first)).second.n].p = vClipped[i]; (*(hi.first)).second.isects[(*(hi.first)).second.n].idx = vIdx; (*(hi.first)).second.n++; vnfo.idx = (unsigned int)(-1); numVertices++; vInfos.push_back(vnfo); currVIdx = vIdx++; } } else { vnfo.idx = (unsigned int)(-1); numVertices++; vInfos.push_back(vnfo); currVIdx = vIdx++; } if (i == 0) { firstV = currVIdx; } if (i > 1) { tnfo.idx = tIdx++; tnfo.v[0] = firstV; tnfo.v[1] = lastV; tnfo.v[2] = currVIdx; tInfos.push_back(tnfo); } lastV = currVIdx; } } } if (tInfos.empty()) { return; } const unsigned int vSize = (unsigned int)(m.vn); const unsigned int tSize = (unsigned int)(m.fn); typedef Allocator TriMeshAllocatorType; TriMeshAllocatorType::AddVertices(r, (int)(vInfos.size())); TriMeshAllocatorType::AddFaces(r, (int)(tInfos.size())); for (size_t i=0; i value + eps; break; case 2: flag = p_in[1] + eps < value; break; case 3: flag = p_in[1] > value + eps; break; case 4: flag = p_in[2] + eps < value; break; case 5: flag = p_in[2] > value + eps; break; default: break; } return (flag); } static inline void CrossPoint(int mode, const ScalarType & value, const CoordType & SP, const CoordType & PP, CoordType & p_out) { switch(mode) { case 0: case 1: p_out[0] = value; if ((PP[0] - SP[0]) == ((ScalarType)(0))) { p_out[1] = PP[1]; p_out[2] = PP[2]; } else { p_out[1] = SP[1] + (value - SP[0]) * (PP[1] - SP[1]) / (PP[0] - SP[0]); p_out[2] = SP[2] + (value - SP[0]) * (PP[2] - SP[2]) / (PP[0] - SP[0]); } break; case 2: case 3: p_out[1] = value; if ((PP[1] - SP[1]) == ((ScalarType)(0))) { p_out[0] = PP[0]; p_out[2] = PP[2]; } else { p_out[0] = SP[0] + (value - SP[1]) * (PP[0] - SP[0]) / (PP[1] - SP[1]); p_out[2] = SP[2] + (value - SP[1]) * (PP[2] - SP[2]) / (PP[1] - SP[1]); } break; case 4: case 5: p_out[2] = value; if ((PP[2] - SP[2]) == ((ScalarType)(0))) { p_out[0] = PP[0]; p_out[1] = PP[1]; } else { p_out[0] = SP[0] + (value - SP[2]) * (PP[0] - SP[0]) / (PP[2] - SP[2]); p_out[1] = SP[1] + (value - SP[2]) * (PP[1] - SP[1]) / (PP[2] - SP[2]); } break; default: break; } } static inline void ClipPolygonLine(int mode, const ScalarType & value, CoordType * P_in, unsigned int n_in, CoordType * P_out, unsigned int & n_out) { unsigned int ps; CoordType * SP; CoordType * PP; n_out = 0; SP = &P_in[n_in-1]; if (ClassType::InRegion(mode, value, *SP)) { ps = 0; } else { ps = 2; } for(unsigned int i=0; i> 1) | ((ClassType::InRegion(mode, value, *PP)) ? (0) : (2)); switch(ps) { case 0: break; case 1: ClassType::CrossPoint(mode, value, *SP, *PP, P_out[n_out]); n_out++; break; case 2: ClassType::CrossPoint(mode, value, *SP, *PP, P_out[n_out]); n_out++; P_out[n_out] = *PP; n_out++; break; case 3: P_out[n_out] = *PP; n_out++; break; default: break; } SP = PP; } } }; } // end namespace tri } // end namespace vcg #endif // __VCGLIB_TRI_CLIP