MeshKit
1.0
|
00001 #include "meshkit/AF2Front.hpp" 00002 00003 // C++ 00004 #include <algorithm> 00005 #include <cmath> 00006 00007 // MeshKit 00008 #include "meshkit/Error.hpp" 00009 #include "meshkit/AF2LocalTransform.hpp" 00010 00011 bool EndPointLess::operator()(const AF2Edge3D* const & oneEdge, 00012 const AF2Edge3D* const & otherEdge) const 00013 { 00014 if (oneEdge->getStart()->getLocalId() < otherEdge->getStart()->getLocalId()) 00015 { 00016 return true; 00017 } 00018 if (oneEdge->getStart()->getLocalId() > otherEdge->getStart()->getLocalId()) 00019 { 00020 return false; 00021 } 00022 // otherwise the start endpoints are equal 00023 if (oneEdge->getEnd()->getLocalId() < otherEdge->getEnd()->getLocalId()) 00024 { 00025 return true; 00026 } 00027 return false; 00028 } 00029 00030 AF2Front::AF2Front() : points(), edges(), qualityCount(4, 0u) 00031 { 00032 } 00033 00034 AF2Front::~AF2Front() 00035 { 00036 typedef std::set<AF2Edge3D*>::iterator EdgeSetItr; 00037 for (EdgeSetItr edgeItr = edges.begin(); edgeItr != edges.end(); ++edgeItr) 00038 { 00039 delete *edgeItr; 00040 } 00041 } 00042 00043 AF2Front::AF2Front(const AF2Front & toCopy) 00044 { 00045 MeshKit::Error notImpl(MeshKit::MK_NOT_IMPLEMENTED); 00046 notImpl.set_string("AF2Front copy construction is not supported."); 00047 throw notImpl; 00048 } 00049 00050 AF2Front& AF2Front::operator=(const AF2Front & rhs) 00051 { 00052 MeshKit::Error notImpl(MeshKit::MK_NOT_IMPLEMENTED); 00053 notImpl.set_string("AF2Front assignment operator is not supported."); 00054 throw notImpl; 00055 } 00056 00057 void AF2Front::qualityDecreased(const AF2Edge3D* const & anEdge) 00058 { 00059 unsigned int curQual = anEdge->getQualityLevel(); 00060 if (curQual > qualityCount.size()) 00061 { 00062 qualityCount.resize(curQual, 0u); 00063 } 00064 ++qualityCount[curQual - 1]; 00065 --qualityCount[curQual - 2]; 00066 } 00067 00068 void AF2Front::addPoint(AF2Point3D* pointPtr) 00069 { 00070 if (points.find(pointPtr) != points.end()) 00071 { 00072 MeshKit::Error duplicate(MeshKit::MK_BAD_INPUT); 00073 duplicate.set_string("The point is already on the advancing front."); 00074 throw duplicate; 00075 } 00076 points[pointPtr] = 0; 00077 } 00078 00079 void AF2Front::advanceFront(std::list<AF2Edge3D*> edgeList) 00080 { 00081 typedef std::map<AF2Point3D*, int>::iterator PointCountItr; 00082 typedef std::set<AF2Edge3D*>::iterator EdgeSetItr; 00083 typedef std::list<AF2Edge3D*>::iterator EdgeListItr; 00084 00085 // verify validity of endpoints, which could throw exception, 00086 // before doing any other processing 00087 for (EdgeListItr itr = edgeList.begin(); itr != edgeList.end(); ++itr) 00088 { 00089 PointCountItr startPntItr = points.find((*itr)->getStart()); 00090 PointCountItr endPntItr = points.find((*itr)->getEnd()); 00091 if (startPntItr == points.end() || endPntItr == points.end()) 00092 { 00093 MeshKit::Error badArg(MeshKit::MK_BAD_INPUT); 00094 badArg.set_string( 00095 "One or both endpoints of an edge are not on the advancing front."); 00096 throw badArg; 00097 } 00098 if ((*itr)->getQualityLevel() != 1u) 00099 { 00100 MeshKit::Error badArg(MeshKit::MK_BAD_INPUT); 00101 badArg.set_string( 00102 "Edges added to the advancing front must have quality level 1."); 00103 throw badArg; 00104 } 00105 } 00106 00107 // decide which half edges need to be added 00108 // and which (reverse) half edges need to be removed 00109 std::list<AF2Edge3D*> edgesToAdd; 00110 std::list<EdgeSetItr> edgesToRemove; 00111 00112 for (EdgeListItr itr = edgeList.begin(); itr != edgeList.end(); ++itr) 00113 { 00114 AF2Edge3D reverseEdge((*itr)->getEnd(), (*itr)->getStart()); 00115 EdgeSetItr revEdgeItr = edges.find(&reverseEdge); 00116 if (revEdgeItr != edges.end()) 00117 { 00118 // prepare for later processing of the reverse half edge that was 00119 // allocated on the heatp to remove it from the edge set and delete it 00120 // don't delete it now, since that would make it more difficult to 00121 // determine whether its endpoints should be deleted 00122 edgesToRemove.push_back(revEdgeItr); 00123 // right now delete the forward half edge that was allocated on the heap 00124 delete *itr; 00125 } 00126 else 00127 { 00128 // prepare for later processing of the forward half edge 00129 // don't add it now, since that would prevent adding both half edges 00130 // of an "isolated" edge 00131 edgesToAdd.push_back(*itr); 00132 } 00133 } 00134 00135 // add the half edges that need to be added 00136 for (EdgeListItr itr = edgesToAdd.begin(); itr != edgesToAdd.end(); ++itr) 00137 { 00138 // add it to the set of edges 00139 edges.insert(*itr); 00140 // start tracking the edge's quality level 00141 ++qualityCount[0]; 00142 (*itr)->setObserver(this); 00143 // increase the count of the number of edges incident to each endpoint 00144 ++points[(*itr)->getStart()]; 00145 ++points[(*itr)->getEnd()]; 00146 // update the distance to the boundary (locally) 00147 // * Note * The distance to the boundary may be globally inaccurate 00148 // after this update, but this agrees with how NetGen did the update. 00149 // It would be possible to cascade the distance to boundary updates 00150 // and ensure globally accuracy, but that might affect a lot of points. 00151 unsigned int maxDist = 1 + std::min( 00152 (*itr)->getStart()->getDistanceToBoundary(), 00153 (*itr)->getEnd()->getDistanceToBoundary()); 00154 (*itr)->getStart()->limitDistanceToBoundary(maxDist); 00155 (*itr)->getEnd()->limitDistanceToBoundary(maxDist); 00156 } 00157 00158 // remove the half edges that need to be removed and any endpoints of 00159 // those edges that have no incident edges after the edge is removed 00160 for (std::list<EdgeSetItr>::iterator itr = edgesToRemove.begin(); 00161 itr != edgesToRemove.end(); ++itr) 00162 { 00163 EdgeSetItr edgeItr = *itr; 00164 // decrease the count of the number of edges incident to each endpoint 00165 --points[(*edgeItr)->getStart()]; 00166 --points[(*edgeItr)->getEnd()]; 00167 // remove endpoints from points if they now have zero incident edges 00168 if (points[(*edgeItr)->getStart()] == 0u) 00169 { 00170 points.erase((*edgeItr)->getStart()); 00171 } 00172 if (points[(*edgeItr)->getEnd()] == 0u) 00173 { 00174 points.erase((*edgeItr)->getEnd()); 00175 } 00176 // stop tracking the quality level of the edge 00177 --qualityCount[(*edgeItr)->getQualityLevel() - 1u]; 00178 // delete the edge object that was allocated on the heap 00179 delete *edgeItr; 00180 // remove the (now dangling) pointer to the edge from the edge set 00181 edges.erase(edgeItr); 00182 } 00183 } 00184 00185 bool AF2Front::isEmpty() const 00186 { 00187 return edges.empty() && points.empty(); 00188 } 00189 00190 unsigned int AF2Front::getMaximumQuality() const 00191 { 00192 for (unsigned int qualityLevel = 1; 00193 qualityLevel <= qualityCount.size(); ++qualityLevel) 00194 { 00195 if (qualityCount[qualityLevel - 1] > 0) 00196 { 00197 return qualityLevel; 00198 } 00199 } 00200 00201 return 0u; 00202 } 00203 00204 AF2Neighborhood* AF2Front::selectNeighborhood( 00205 const AF2LocalTransformMaker* const & transformMaker) const 00206 { 00207 typedef std::set<AF2Edge3D*>::const_iterator EdgeSetItr; 00208 typedef std::map<AF2Point3D*, int>::const_iterator PointCountItr; 00209 00210 // Select a baseline edge that minimizes a metric value for the metric 00211 // edge quality level + sum of endpoint distances to boundary. 00212 // The current implementation loops through all of the edges 00213 // to find an edge with minimal metric value. This could be made faster 00214 // using a C++ priority queue, updating each time the metric value changes. 00215 AF2Edge3D* baselineEdge = NULL; 00216 unsigned int minMetricVal; 00217 for (EdgeSetItr edgeItr = edges.begin(); edgeItr != edges.end(); ++edgeItr) 00218 { 00219 if (baselineEdge == NULL) 00220 { 00221 minMetricVal = (*edgeItr)->getQualityLevel() + 00222 (*edgeItr)->getStart()->getDistanceToBoundary() + 00223 (*edgeItr)->getEnd()->getDistanceToBoundary(); 00224 baselineEdge = *edgeItr; 00225 } 00226 else 00227 { 00228 unsigned int metricVal = (*edgeItr)->getQualityLevel() + 00229 (*edgeItr)->getStart()->getDistanceToBoundary() + 00230 (*edgeItr)->getEnd()->getDistanceToBoundary(); 00231 if (metricVal < minMetricVal) 00232 { 00233 metricVal = minMetricVal; 00234 baselineEdge = *edgeItr; 00235 } 00236 } 00237 } 00238 00239 // throw an exception if no baseline edge was found 00240 if (baselineEdge == NULL) 00241 { 00242 MeshKit::Error fail(MeshKit::MK_FAILURE); 00243 fail.set_string("The advancing front has no edges."); 00244 throw fail; 00245 } 00246 00247 // determine the neighborhood size from the length and quality 00248 // level of the baseline edge 00249 AF2Point3D* baseStart = baselineEdge->getStart(); 00250 AF2Point3D* baseEnd = baselineEdge->getEnd(); 00251 double xDiff = baseEnd->getX() - baseStart->getX(); 00252 double yDiff = baseEnd->getY() - baseStart->getY(); 00253 double zDiff = baseEnd->getZ() - baseStart->getZ(); 00254 double sqLen = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; 00255 unsigned int qLevelFactor = std::min(baselineEdge->getQualityLevel(), 7u); 00256 double ngbhdSize = 4.0 * (3u + qLevelFactor) * (3u + qLevelFactor) * sqLen; 00257 00258 // create lists to hold the points and (other) edges in the neighborhood 00259 std::list<AF2Point3D*> ngbhdPoints; 00260 std::list<const AF2Edge3D*> ngbhdEdges; 00261 // create a set to track points included in the neighorhood and avoid 00262 // duplicating them if they are endpoints of multiple neighborhood edges 00263 std::set<AF2Point3D*> ngbhdPointSet; 00264 00265 // find edges that are close enough to the baseline edge 00266 // to be included in the neighborhood 00267 AF2Point3D* edgeStart = NULL; 00268 AF2Point3D* edgeEnd = NULL; 00269 double edgeXDiff = 0.0; 00270 double edgeYDiff = 0.0; 00271 double edgeZDiff = 0.0; 00272 double edgeSqLen = 0.0; 00273 double dotProd = 0.0; 00274 for (EdgeSetItr edgeItr = edges.begin(); edgeItr != edges.end(); ++edgeItr) 00275 { 00276 // skip the baseline edge to avoid duplicating it in the neighborhood 00277 if ((*edgeItr) == baselineEdge) 00278 { 00279 continue; 00280 } 00281 00282 // calculate components of vector along the direction of the edge 00283 // and the squared length of the edge 00284 edgeStart = (*edgeItr)->getStart(); 00285 edgeEnd = (*edgeItr)->getEnd(); 00286 edgeXDiff = edgeEnd->getX() - edgeStart->getX(); 00287 edgeYDiff = edgeEnd->getY() - edgeStart->getY(); 00288 edgeZDiff = edgeEnd->getZ() - edgeStart->getZ(); 00289 edgeSqLen = edgeXDiff * edgeXDiff + 00290 edgeYDiff * edgeYDiff + edgeZDiff * edgeZDiff; 00291 00292 // calculate components of vector from edge start point to base start point 00293 xDiff = baseStart->getX() - edgeStart->getX(); 00294 yDiff = baseStart->getY() - edgeStart->getY(); 00295 zDiff = baseStart->getZ() - edgeStart->getZ(); 00296 00297 // calculate the dot product of the two vectors 00298 dotProd = edgeXDiff * xDiff + edgeYDiff * yDiff + edgeZDiff * zDiff; 00299 00300 // calculate the components of the vector from the baseline edge 00301 // start point to the closest point on the edge, placing the results 00302 // in (and reusing the variables) xDiff, yDiff, and zDiff 00303 if (dotProd < 0) 00304 { 00305 // the orthogonal projection of the baseline edge start point onto 00306 // the line that contains the current edge lies outside the edge 00307 // and the edge start point is closest to the baseline edge start point 00308 00309 // the vector from the edge start point to the baseline edge start 00310 // point is correct, and xDiff, yDiff, and zDiff already has that vector 00311 // --> do nothing 00312 } 00313 else if (dotProd > edgeSqLen) 00314 { 00315 // the orthogonal projection of the baseline edge start point onto 00316 // the line that contains the current edge lies outside the edge 00317 // and the edge end point is closest to the baseline edge start point 00318 00319 // the vector from the edge end point to the baseline edge start 00320 // point is the one that is needed 00321 xDiff = baseStart->getX() - edgeEnd->getX(); 00322 yDiff = baseStart->getY() - edgeEnd->getY(); 00323 zDiff = baseStart->getZ() - edgeEnd->getZ(); 00324 } 00325 else 00326 { 00327 // the orthogonal projection of the baseline edge start point onto 00328 // the line that contains the current edge lies on the edge 00329 // and is the closest point on the edge to the baseline edge start point 00330 00331 // calculate the components of the projection of the vector from the 00332 // edge start point to the baseline edge start point onto the vector 00333 // in the direction of the edge, reusing edge difference variables 00334 edgeXDiff = dotProd * edgeXDiff / edgeSqLen; 00335 edgeYDiff = dotProd * edgeYDiff / edgeSqLen; 00336 edgeZDiff = dotProd * edgeZDiff / edgeSqLen; 00337 00338 // calculate the components of the projection of the vector from the 00339 // edge start point to the baseline edge start point onto the plane 00340 // orthogonal to the direction of the edge 00341 xDiff = xDiff - edgeXDiff; 00342 yDiff = yDiff - edgeYDiff; 00343 zDiff = zDiff - edgeZDiff; 00344 } 00345 00346 // calculate the square of the length of said vector, which is also the 00347 // square of the distance from the edge to the baseline edge start point 00348 sqLen = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; 00349 00350 // if the edge is close enough, add it and its endpoints 00351 // to the neighborhood 00352 if (sqLen < ngbhdSize) 00353 { 00354 ngbhdEdges.push_back(*edgeItr); 00355 if (ngbhdPointSet.find(edgeStart) == ngbhdPointSet.end()) 00356 { 00357 ngbhdPointSet.insert(edgeStart); 00358 ngbhdPoints.push_back(edgeStart); 00359 } 00360 if (ngbhdPointSet.find(edgeEnd) == ngbhdPointSet.end()) 00361 { 00362 ngbhdPointSet.insert(edgeEnd); 00363 ngbhdPoints.push_back(edgeEnd); 00364 } 00365 } 00366 } 00367 00368 // find points that are close enough to the baseline edge 00369 // to be included in the neighborhood 00370 for (PointCountItr itr = points.begin(); itr != points.end(); ++itr) 00371 { 00372 AF2Point3D* frontPoint = itr->first; 00373 00374 // calculate the square of the distance from the baseline edge start point 00375 xDiff = frontPoint->getX() - baseStart->getX(); 00376 yDiff = frontPoint->getY() - baseStart->getY(); 00377 zDiff = frontPoint->getZ() - baseStart->getZ(); 00378 sqLen = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff; 00379 00380 // if the distance is small enough and the point has not been added 00381 // to the neighborhood yet, add it to the neighborhood 00382 if (sqLen < ngbhdSize && 00383 ngbhdPointSet.find(frontPoint) == ngbhdPointSet.end()) 00384 { 00385 ngbhdPointSet.insert(frontPoint); 00386 ngbhdPoints.push_back(frontPoint); 00387 } 00388 } 00389 00390 // use the transform maker to make a local transform appropriate 00391 // to the neighborhood 00392 AF2LocalTransform* localTransform = 00393 transformMaker->makeLocalTransform(ngbhdPoints, baselineEdge, ngbhdEdges); 00394 00395 // build and return the neighborhood object 00396 return new AF2Neighborhood( 00397 ngbhdPoints, baselineEdge, ngbhdEdges, localTransform); 00398 }