//################################################################################################## // // Custom Visualization Core library // Copyright (C) 2011-2012 Ceetron AS // // This library 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 3 of the License, or // (at your option) any later version. // // 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 General Public License at <> // for more details. // //################################################################################################## #include "cvfBase.h" #include "cvfOutlineEdgeExtractor.h" #include "cvfEdgeKey.h" #include "cvfGeometryUtils.h" namespace cvf { //================================================================================================== /// /// \class cvf::OutlineEdgeExtractor /// \ingroup Geometry /// /// /// //================================================================================================== static const size_t OEE_OUTLINE_EDGE = cvf::UNDEFINED_SIZE_T; // This is an outline edge static const size_t OEE_NON_OUTLINE_EDGE = cvf::UNDEFINED_SIZE_T - 1; // Marked as an interior edge static const size_t OEE_MULTIREF_EDGE = cvf::UNDEFINED_SIZE_T - 2; // An edge with more than two faces connected to it. //-------------------------------------------------------------------------------------------------- /// Constructor /// /// creaseAngle is specified in radians //-------------------------------------------------------------------------------------------------- OutlineEdgeExtractor::OutlineEdgeExtractor(double creaseAngle, const Vec3fValueArray& vertexArray) : m_creaseAngle(creaseAngle), m_vertexArray(vertexArray) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void OutlineEdgeExtractor::addPrimitives(uint verticesPerPrimitive, const uint* indices, size_t indexCount) { CVF_ASSERT(verticesPerPrimitive > 0); CVF_ASSERT(indices); CVF_ASSERT(indexCount > 0); // Points will never become edges if (verticesPerPrimitive < 2) { return; } size_t numPrimitives = indexCount/verticesPerPrimitive; size_t ip; for (ip = 0; ip < numPrimitives; ip++) { size_t myFaceIndex = m_faceNormals.size(); size_t firstIdxInPrimitive = ip*verticesPerPrimitive; // Normal computation accepts points and lines, but in that case returns a zero vector Vec3f faceNormal = computeFaceNormal(&indices[firstIdxInPrimitive], verticesPerPrimitive); m_faceNormals.push_back(faceNormal); uint i; for (i = 0; i < verticesPerPrimitive; i++) { const uint vertexIdx1 = indices[firstIdxInPrimitive + i]; const uint vertexIdx2 = (i < verticesPerPrimitive - 1) ? indices[firstIdxInPrimitive + i + 1] : indices[firstIdxInPrimitive]; // Never add collapsed edges if (vertexIdx1 == vertexIdx2) { continue; } int64 edgeKeyVal = EdgeKey(vertexIdx1, vertexIdx2).toKeyVal(); std::map::iterator it = m_edgeToFaceIndexMap.find(edgeKeyVal); if (it == m_edgeToFaceIndexMap.end()) { // Not found, so add and register face index m_edgeToFaceIndexMap[edgeKeyVal] = myFaceIndex; } else { size_t otherFaceIdx = it->second; if (otherFaceIdx < OEE_MULTIREF_EDGE) { // An edge is already there, check angle if (isFaceAngleAboveThreshold(myFaceIndex, otherFaceIdx)) { m_edgeToFaceIndexMap[edgeKeyVal] = OEE_OUTLINE_EDGE; } else { m_edgeToFaceIndexMap[edgeKeyVal] = OEE_NON_OUTLINE_EDGE; } } else { // Three or more faces share an edge m_edgeToFaceIndexMap[edgeKeyVal] = OEE_MULTIREF_EDGE; } } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void OutlineEdgeExtractor::addPrimitives(uint verticesPerPrimitive, const UIntArray& indices) { CVF_ASSERT(verticesPerPrimitive > 0); size_t indexCount = indices.size(); size_t numPrimitives = indexCount/verticesPerPrimitive; if (numPrimitives > 0) { const uint* indexPtr = indices.ptr(); addPrimitives(verticesPerPrimitive, indexPtr, indexCount); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void OutlineEdgeExtractor::addFaceList(const UIntArray& faceList) { size_t numFaceListEntries = faceList.size(); size_t i = 0; while (i < numFaceListEntries) { uint numVerticesInFace = faceList[i++]; CVF_ASSERT(numVerticesInFace > 0); CVF_ASSERT(i + numVerticesInFace <= numFaceListEntries); const uint* indexPtr = &faceList[i]; addPrimitives(numVerticesInFace, indexPtr, numVerticesInFace); i += numVerticesInFace; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- ref OutlineEdgeExtractor::lineIndices() const { ref indices = new UIntArray; size_t numEdges = m_edgeToFaceIndexMap.size(); if (numEdges == 0) { return indices; } indices->reserve(2*numEdges); std::map::const_iterator it; for (it = m_edgeToFaceIndexMap.begin(); it != m_edgeToFaceIndexMap.end(); ++it) { size_t otherFaceIdx = it->second; if (otherFaceIdx != OEE_NON_OUTLINE_EDGE) { EdgeKey ek = EdgeKey::fromkeyVal(it->first); indices->add(ek.index1()); indices->add(ek.index2()); } } return indices; } //-------------------------------------------------------------------------------------------------- /// Returns the worker array with one normal per face (primitive) //-------------------------------------------------------------------------------------------------- const std::vector& OutlineEdgeExtractor::faceNormals() { return m_faceNormals; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- Vec3f OutlineEdgeExtractor::computeFaceNormal(const uint* faceVertexIndices, uint numVerticesInFace) const { // Init to zero so that points and lines return a zero normal Vec3f normal(Vec3f::ZERO); if (numVerticesInFace == 3) { const Vec3f& v0 = m_vertexArray.val(faceVertexIndices[0]); Vec3f v1 = m_vertexArray.val(faceVertexIndices[1]) - v0; Vec3f v2 = m_vertexArray.val(faceVertexIndices[2]) - v0; normal = v1 ^ v2; normal.normalize(); } else if (numVerticesInFace == 4) { // Quad surface normal, // From "Real Time Collision Detection" p 495 const Vec3f& A = m_vertexArray.val(faceVertexIndices[0]); const Vec3f& B = m_vertexArray.val(faceVertexIndices[1]); const Vec3f& C = m_vertexArray.val(faceVertexIndices[2]); const Vec3f& D = m_vertexArray.val(faceVertexIndices[3]); normal = GeometryUtils::quadNormal(A, B, C, D); } else if (numVerticesInFace > 4) { normal = GeometryUtils::polygonNormal(m_vertexArray, faceVertexIndices, numVerticesInFace); } return normal; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool OutlineEdgeExtractor::isFaceAngleAboveThreshold(size_t faceIdx1, size_t faceIdx2) const { const Vec3f& n1 = m_faceNormals[faceIdx1]; const Vec3f& n2 = m_faceNormals[faceIdx2]; // If either vector is 0, the face is probably a line // Therefore just return true to flag this as an outline edge if (n1.isZero() || n2.isZero()) { return true; } // Guard acos against out-of-domain input const double dotProduct = Math::clamp(static_cast(n1*n2), -1.0, 1.0); const double angle = Math::acos(dotProduct); if (Math::abs(angle) > m_creaseAngle) { return true; } else { return false; } } } // namespace cvf