00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "PreCompiled.h"
00025 #include <gp_Pnt.hxx>
00026 #include <BRepExtrema_DistShapeShape.hxx>
00027 #include <BRepBuilderAPI_MakeVertex.hxx>
00028 #include <TopoDS_Vertex.hxx>
00029
00030 #include <QEventLoop>
00031 #include <QFuture>
00032 #include <QFutureWatcher>
00033 #include <QtConcurrentMap>
00034
00035 #include <boost/signals.hpp>
00036 #include <boost/bind.hpp>
00037
00038 #include <Base/Console.h>
00039 #include <Base/Exception.h>
00040 #include <Base/FutureWatcherProgress.h>
00041 #include <Base/Parameter.h>
00042 #include <Base/Sequencer.h>
00043 #include <Base/Tools.h>
00044 #include <App/Application.h>
00045 #include <Mod/Mesh/App/Mesh.h>
00046 #include <Mod/Mesh/App/MeshFeature.h>
00047 #include <Mod/Mesh/App/Core/Algorithm.h>
00048 #include <Mod/Mesh/App/Core/Grid.h>
00049 #include <Mod/Mesh/App/Core/Iterator.h>
00050 #include <Mod/Mesh/App/Core/MeshKernel.h>
00051 #include <Mod/Points/App/PointsFeature.h>
00052 #include <Mod/Points/App/PointsGrid.h>
00053 #include <Mod/Part/App/PartFeature.h>
00054
00055 #include "InspectionFeature.h"
00056
00057
00058 using namespace Inspection;
00059
00060 InspectActualMesh::InspectActualMesh(const Mesh::MeshObject& rMesh) : _iter(rMesh.getKernel())
00061 {
00062 this->_count = rMesh.countPoints();
00063 this->_iter.Transform(rMesh.getTransform());
00064 }
00065
00066 InspectActualMesh::~InspectActualMesh()
00067 {
00068 }
00069
00070 unsigned long InspectActualMesh::countPoints() const
00071 {
00072 return this->_count;
00073 }
00074
00075 Base::Vector3f InspectActualMesh::getPoint(unsigned long index)
00076 {
00077 _iter.Set(index);
00078 return *_iter;
00079 }
00080
00081
00082
00083 InspectActualPoints::InspectActualPoints(const Points::PointKernel& rPoints) : _rKernel(rPoints)
00084 {
00085 }
00086
00087 unsigned long InspectActualPoints::countPoints() const
00088 {
00089 return _rKernel.size();
00090 }
00091
00092 Base::Vector3f InspectActualPoints::getPoint(unsigned long index)
00093 {
00094 Base::Vector3d p = _rKernel.getPoint(index);
00095 return Base::Vector3f((float)p.x,(float)p.y,(float)p.z);
00096 }
00097
00098
00099
00100 InspectActualShape::InspectActualShape(const Part::TopoShape& shape) : _rShape(shape)
00101 {
00102 ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
00103 ("User parameter:BaseApp/Preferences/Mod/Part");
00104 float deviation = hGrp->GetFloat("MeshDeviation",0.2);
00105
00106 Base::BoundBox3d bbox = _rShape.getBoundBox();
00107 Standard_Real deflection = (bbox.LengthX() + bbox.LengthY() + bbox.LengthZ())/300.0 * deviation;
00108
00109 std::vector<Data::ComplexGeoData::Facet> f;
00110 _rShape.getFaces(points, f, (float)deflection);
00111 }
00112
00113 unsigned long InspectActualShape::countPoints() const
00114 {
00115 return points.size();
00116 }
00117
00118 Base::Vector3f InspectActualShape::getPoint(unsigned long index)
00119 {
00120 return Base::toVector<float>(points[index]);
00121 }
00122
00123
00124
00125 namespace Inspection {
00126 class MeshInspectGrid : public MeshCore::MeshGrid
00127 {
00128 public:
00129 MeshInspectGrid (const MeshCore::MeshKernel &mesh, float fGridLen, const Base::Matrix4D& m)
00130 : MeshCore::MeshGrid(mesh), _transform(m)
00131 {
00132 Base::BoundBox3f clBBMesh = _pclMesh->GetBoundBox().Transformed(m);
00133 Rebuild(std::max<unsigned long>((unsigned long)(clBBMesh.LengthX() / fGridLen), 1),
00134 std::max<unsigned long>((unsigned long)(clBBMesh.LengthY() / fGridLen), 1),
00135 std::max<unsigned long>((unsigned long)(clBBMesh.LengthZ() / fGridLen), 1));
00136 }
00137
00138 void Validate (const MeshCore::MeshKernel&)
00139 {
00140
00141 }
00142
00143 void Validate (void)
00144 {
00145
00146 }
00147
00148 bool Verify() const
00149 {
00150
00151 return true;
00152 }
00153
00154 protected:
00155 void CalculateGridLength (unsigned long ulCtGrid, unsigned long ulMaxGrids)
00156 {
00157
00158 }
00159
00160 void CalculateGridLength (int iCtGridPerAxis)
00161 {
00162
00163 }
00164
00165 unsigned long HasElements (void) const
00166 {
00167 return _pclMesh->CountFacets();
00168 }
00169
00170 void Pos (const Base::Vector3f &rclPoint, unsigned long &rulX, unsigned long &rulY, unsigned long &rulZ) const
00171 {
00172 rulX = (unsigned long)((rclPoint.x - _fMinX) / _fGridLenX);
00173 rulY = (unsigned long)((rclPoint.y - _fMinY) / _fGridLenY);
00174 rulZ = (unsigned long)((rclPoint.z - _fMinZ) / _fGridLenZ);
00175
00176 assert((rulX < _ulCtGridsX) && (rulY < _ulCtGridsY) && (rulZ < _ulCtGridsZ));
00177 }
00178
00179 void AddFacet (const MeshCore::MeshGeomFacet &rclFacet, unsigned long ulFacetIndex)
00180 {
00181 unsigned long ulX, ulY, ulZ;
00182 unsigned long ulX1, ulY1, ulZ1, ulX2, ulY2, ulZ2;
00183
00184 Base::BoundBox3f clBB;
00185 clBB &= rclFacet._aclPoints[0];
00186 clBB &= rclFacet._aclPoints[1];
00187 clBB &= rclFacet._aclPoints[2];
00188
00189 Pos(Base::Vector3f(clBB.MinX,clBB.MinY,clBB.MinZ), ulX1, ulY1, ulZ1);
00190 Pos(Base::Vector3f(clBB.MaxX,clBB.MaxY,clBB.MaxZ), ulX2, ulY2, ulZ2);
00191
00192
00193 if ((ulX1 < ulX2) || (ulY1 < ulY2) || (ulZ1 < ulZ2)) {
00194 for (ulX = ulX1; ulX <= ulX2; ulX++) {
00195 for (ulY = ulY1; ulY <= ulY2; ulY++) {
00196 for (ulZ = ulZ1; ulZ <= ulZ2; ulZ++) {
00197 if (rclFacet.IntersectBoundingBox(GetBoundBox(ulX, ulY, ulZ)))
00198 _aulGrid[ulX][ulY][ulZ].insert(ulFacetIndex);
00199 }
00200 }
00201 }
00202 }
00203 else
00204 _aulGrid[ulX1][ulY1][ulZ1].insert(ulFacetIndex);
00205 }
00206
00207 void InitGrid (void)
00208 {
00209 unsigned long i, j;
00210
00211 Base::BoundBox3f clBBMesh = _pclMesh->GetBoundBox().Transformed(_transform);
00212
00213 float fLengthX = clBBMesh.LengthX();
00214 float fLengthY = clBBMesh.LengthY();
00215 float fLengthZ = clBBMesh.LengthZ();
00216
00217 _fGridLenX = (1.0f + fLengthX) / float(_ulCtGridsX);
00218 _fMinX = clBBMesh.MinX - 0.5f;
00219
00220 _fGridLenY = (1.0f + fLengthY) / float(_ulCtGridsY);
00221 _fMinY = clBBMesh.MinY - 0.5f;
00222
00223 _fGridLenZ = (1.0f + fLengthZ) / float(_ulCtGridsZ);
00224 _fMinZ = clBBMesh.MinZ - 0.5f;
00225
00226 _aulGrid.clear();
00227 _aulGrid.resize(_ulCtGridsX);
00228 for (i = 0; i < _ulCtGridsX; i++) {
00229 _aulGrid[i].resize(_ulCtGridsY);
00230 for (j = 0; j < _ulCtGridsY; j++)
00231 _aulGrid[i][j].resize(_ulCtGridsZ);
00232 }
00233 }
00234
00235 void RebuildGrid (void)
00236 {
00237 _ulCtElements = _pclMesh->CountFacets();
00238 InitGrid();
00239
00240 unsigned long i = 0;
00241 MeshCore::MeshFacetIterator clFIter(*_pclMesh);
00242 clFIter.Transform(_transform);
00243 for (clFIter.Init(); clFIter.More(); clFIter.Next()) {
00244 AddFacet(*clFIter, i++);
00245 }
00246 }
00247
00248 private:
00249 Base::Matrix4D _transform;
00250 };
00251 }
00252
00253 InspectNominalMesh::InspectNominalMesh(const Mesh::MeshObject& rMesh, float offset) : _iter(rMesh.getKernel())
00254 {
00255 const MeshCore::MeshKernel& kernel = rMesh.getKernel();
00256 _iter.Transform(rMesh.getTransform());
00257
00258
00259 float fMaxGridElements=8000000.0f;
00260 Base::BoundBox3f box = kernel.GetBoundBox().Transformed(rMesh.getTransform());
00261
00262
00263 float fMinGridLen = (float)pow((box.LengthX()*box.LengthY()*box.LengthZ()/fMaxGridElements), 0.3333f);
00264 float fGridLen = 5.0f * MeshCore::MeshAlgorithm(kernel).GetAverageEdgeLength();
00265
00266
00267
00268
00269
00270 fGridLen = std::max<float>(fMinGridLen, fGridLen);
00271
00272
00273 _pGrid = new MeshInspectGrid(kernel, fGridLen, rMesh.getTransform());
00274 _box = box;
00275 _box.Enlarge(offset);
00276 }
00277
00278 InspectNominalMesh::~InspectNominalMesh()
00279 {
00280 delete this->_pGrid;
00281 }
00282
00283 float InspectNominalMesh::getDistance(const Base::Vector3f& point)
00284 {
00285 if (!_box.IsInBox(point))
00286 return FLT_MAX;
00287
00288 std::vector<unsigned long> indices;
00289
00290 if (indices.empty()) {
00291 std::set<unsigned long> inds;
00292 _pGrid->MeshGrid::SearchNearestFromPoint(point, inds);
00293 indices.insert(indices.begin(), inds.begin(), inds.end());
00294 }
00295
00296 float fMinDist=FLT_MAX;
00297 bool positive = true;
00298 for (std::vector<unsigned long>::iterator it = indices.begin(); it != indices.end(); ++it) {
00299 _iter.Set(*it);
00300 float fDist = _iter->DistanceToPoint(point);
00301 if (fabs(fDist) < fabs(fMinDist)) {
00302 fMinDist = fDist;
00303 positive = point.DistanceToPlane(_iter->_aclPoints[0], _iter->GetNormal()) > 0;
00304 }
00305 }
00306
00307 if (!positive)
00308 fMinDist = -fMinDist;
00309 return fMinDist;
00310 }
00311
00312
00313
00314 InspectNominalFastMesh::InspectNominalFastMesh(const Mesh::MeshObject& rMesh, float offset) : _iter(rMesh.getKernel())
00315 {
00316 const MeshCore::MeshKernel& kernel = rMesh.getKernel();
00317 _iter.Transform(rMesh.getTransform());
00318
00319
00320 float fMaxGridElements=8000000.0f;
00321 Base::BoundBox3f box = kernel.GetBoundBox().Transformed(rMesh.getTransform());
00322
00323
00324 float fMinGridLen = (float)pow((box.LengthX()*box.LengthY()*box.LengthZ()/fMaxGridElements), 0.3333f);
00325 float fGridLen = 5.0f * MeshCore::MeshAlgorithm(kernel).GetAverageEdgeLength();
00326
00327
00328
00329
00330
00331 fGridLen = std::max<float>(fMinGridLen, fGridLen);
00332
00333
00334 _pGrid = new MeshInspectGrid(kernel, fGridLen, rMesh.getTransform());
00335 _box = box;
00336 _box.Enlarge(offset);
00337 max_level = (unsigned long)(offset/fGridLen);
00338 }
00339
00340 InspectNominalFastMesh::~InspectNominalFastMesh()
00341 {
00342 delete this->_pGrid;
00343 }
00344
00349 float InspectNominalFastMesh::getDistance(const Base::Vector3f& point)
00350 {
00351 if (!_box.IsInBox(point))
00352 return FLT_MAX;
00353
00354 std::set<unsigned long> indices;
00355 #if 0 // a point in a neighbour grid can be nearer
00356 std::vector<unsigned long> elements;
00357 _pGrid->GetElements(point, elements);
00358 indices.insert(elements.begin(), elements.end());
00359 #else
00360 unsigned long ulX, ulY, ulZ;
00361 _pGrid->Position(point, ulX, ulY, ulZ);
00362 unsigned long ulLevel = 0;
00363 while (indices.size() == 0 && ulLevel <= max_level)
00364 _pGrid->GetHull(ulX, ulY, ulZ, ulLevel++, indices);
00365 if (indices.size() == 0 || ulLevel==1)
00366 _pGrid->GetHull(ulX, ulY, ulZ, ulLevel, indices);
00367 #endif
00368
00369 float fMinDist=FLT_MAX;
00370 bool positive = true;
00371 for (std::set<unsigned long>::iterator it = indices.begin(); it != indices.end(); ++it) {
00372 _iter.Set(*it);
00373 float fDist = _iter->DistanceToPoint(point);
00374 if (fabs(fDist) < fabs(fMinDist)) {
00375 fMinDist = fDist;
00376 positive = point.DistanceToPlane(_iter->_aclPoints[0], _iter->GetNormal()) > 0;
00377 }
00378 }
00379
00380 if (!positive)
00381 fMinDist = -fMinDist;
00382 return fMinDist;
00383 }
00384
00385
00386
00387 InspectNominalPoints::InspectNominalPoints(const Points::PointKernel& Kernel, float offset) : _rKernel(Kernel)
00388 {
00389 int uGridPerAxis = 50;
00390 this->_pGrid = new Points::PointsGrid (Kernel, uGridPerAxis);
00391 }
00392
00393 InspectNominalPoints::~InspectNominalPoints()
00394 {
00395 delete this->_pGrid;
00396 }
00397
00398 float InspectNominalPoints::getDistance(const Base::Vector3f& point)
00399 {
00400
00401 std::set<unsigned long> indices;
00402 unsigned long x,y,z;
00403 Base::Vector3d pointd(point.x,point.y,point.z);
00404 _pGrid->Position(pointd, x, y, z);
00405 _pGrid->GetElements(x,y,z,indices);
00406
00407 double fMinDist=DBL_MAX;
00408 for (std::set<unsigned long>::iterator it = indices.begin(); it != indices.end(); ++it) {
00409 Base::Vector3d pt = _rKernel.getPoint(*it);
00410 double fDist = Base::Distance(pointd, pt);
00411 if (fDist < fMinDist) {
00412 fMinDist = fDist;
00413 }
00414 }
00415
00416 return (float)fMinDist;
00417 }
00418
00419
00420
00421 InspectNominalShape::InspectNominalShape(const TopoDS_Shape& shape, float radius) : _rShape(shape)
00422 {
00423 distss = new BRepExtrema_DistShapeShape();
00424 distss->LoadS1(_rShape);
00425
00426 }
00427
00428 InspectNominalShape::~InspectNominalShape()
00429 {
00430 delete distss;
00431 }
00432
00433 float InspectNominalShape::getDistance(const Base::Vector3f& point)
00434 {
00435 BRepBuilderAPI_MakeVertex mkVert(gp_Pnt(point.x,point.y,point.z));
00436 distss->LoadS2(mkVert.Vertex());
00437 float fMinDist=FLT_MAX;
00438 if (distss->Perform() && distss->NbSolution() > 0)
00439 fMinDist = (float)distss->Value();
00440 return fMinDist;
00441 }
00442
00443
00444
00445
00446 struct DistanceInspection
00447 {
00448
00449 DistanceInspection(float radius, InspectActualGeometry* a,
00450 std::vector<InspectNominalGeometry*> n)
00451 : radius(radius), actual(a), nominal(n)
00452 {
00453 }
00454 float mapped(unsigned long index)
00455 {
00456 Base::Vector3f pnt = actual->getPoint(index);
00457
00458 float fMinDist=FLT_MAX;
00459 for (std::vector<InspectNominalGeometry*>::iterator it = nominal.begin(); it != nominal.end(); ++it) {
00460 float fDist = (*it)->getDistance(pnt);
00461 if (fabs(fDist) < fabs(fMinDist))
00462 fMinDist = fDist;
00463 }
00464
00465 if (fMinDist > this->radius)
00466 fMinDist = FLT_MAX;
00467 else if (-fMinDist > this->radius)
00468 fMinDist = -FLT_MAX;
00469
00470 return fMinDist;
00471 }
00472
00473 float radius;
00474 InspectActualGeometry* actual;
00475 std::vector<InspectNominalGeometry*> nominal;
00476 };
00477
00478 PROPERTY_SOURCE(Inspection::Feature, App::DocumentObject)
00479
00480 Feature::Feature()
00481 {
00482 ADD_PROPERTY(SearchRadius,(0.05f));
00483 ADD_PROPERTY(Thickness,(0.0f));
00484 ADD_PROPERTY(Actual,(0));
00485 ADD_PROPERTY(Nominals,(0));
00486 ADD_PROPERTY(Distances,(0.0f));
00487 }
00488
00489 Feature::~Feature()
00490 {
00491 }
00492
00493 short Feature::mustExecute() const
00494 {
00495 if (SearchRadius.isTouched())
00496 return 1;
00497 if (Thickness.isTouched())
00498 return 1;
00499 if (Actual.isTouched())
00500 return 1;
00501 if (Nominals.isTouched())
00502 return 1;
00503 return 0;
00504 }
00505
00506 App::DocumentObjectExecReturn* Feature::execute(void)
00507 {
00508 App::DocumentObject* pcActual = Actual.getValue();
00509 if (!pcActual)
00510 throw Base::Exception("No actual geometry to inspect specified");
00511
00512 InspectActualGeometry* actual = 0;
00513 if (pcActual->getTypeId().isDerivedFrom(Mesh::Feature::getClassTypeId())) {
00514 Mesh::Feature* mesh = static_cast<Mesh::Feature*>(pcActual);
00515 actual = new InspectActualMesh(mesh->Mesh.getValue());
00516 }
00517 else if (pcActual->getTypeId().isDerivedFrom(Points::Feature::getClassTypeId())) {
00518 Points::Feature* pts = static_cast<Points::Feature*>(pcActual);
00519 actual = new InspectActualPoints(pts->Points.getValue());
00520 }
00521 else if (pcActual->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
00522 Part::Feature* part = static_cast<Part::Feature*>(pcActual);
00523 actual = new InspectActualShape(part->Shape.getShape());
00524 }
00525 else {
00526 throw Base::Exception("Unknown geometric type");
00527 }
00528
00529
00530 std::vector<InspectNominalGeometry*> inspectNominal;
00531 const std::vector<App::DocumentObject*>& nominals = Nominals.getValues();
00532 for (std::vector<App::DocumentObject*>::const_iterator it = nominals.begin(); it != nominals.end(); ++it) {
00533 InspectNominalGeometry* nominal = 0;
00534 if ((*it)->getTypeId().isDerivedFrom(Mesh::Feature::getClassTypeId())) {
00535 Mesh::Feature* mesh = static_cast<Mesh::Feature*>(*it);
00536 nominal = new InspectNominalMesh(mesh->Mesh.getValue(), this->SearchRadius.getValue());
00537 }
00538 else if ((*it)->getTypeId().isDerivedFrom(Points::Feature::getClassTypeId())) {
00539 Points::Feature* pts = static_cast<Points::Feature*>(*it);
00540 nominal = new InspectNominalPoints(pts->Points.getValue(), this->SearchRadius.getValue());
00541 }
00542 else if ((*it)->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
00543 Part::Feature* part = static_cast<Part::Feature*>(*it);
00544 nominal = new InspectNominalShape(part->Shape.getValue(), this->SearchRadius.getValue());
00545 }
00546
00547 if (nominal)
00548 inspectNominal.push_back(nominal);
00549 }
00550
00551 #if 0 // test with some huge data sets
00552 Standard::SetReentrant(Standard_True);
00553 std::vector<unsigned long> index(actual->countPoints());
00554 std::generate(index.begin(), index.end(), Base::iotaGen<unsigned long>(0));
00555 DistanceInspection check(this->SearchRadius.getValue(), actual, inspectNominal);
00556 QFuture<float> future = QtConcurrent::mapped
00557 (index, boost::bind(&DistanceInspection::mapped, &check, _1));
00558
00559 Base::FutureWatcherProgress progress("Inspecting...", actual->countPoints());
00560 QFutureWatcher<float> watcher;
00561 QObject::connect(&watcher, SIGNAL(progressValueChanged(int)),
00562 &progress, SLOT(progressValueChanged(int)));
00563 watcher.setFuture(future);
00564
00565
00566 QEventLoop loop;
00567 QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
00568 loop.exec();
00569
00570 std::vector<float> vals;
00571 vals.insert(vals.end(), future.begin(), future.end());
00572 #else
00573 unsigned long count = actual->countPoints();
00574 std::stringstream str;
00575 str << "Inspecting " << this->Label.getValue() << "...";
00576 Base::SequencerLauncher seq(str.str().c_str(), count);
00577
00578 std::vector<float> vals(count);
00579 for (unsigned long index = 0; index < count; index++) {
00580 Base::Vector3f pnt = actual->getPoint(index);
00581
00582 float fMinDist=FLT_MAX;
00583 for (std::vector<InspectNominalGeometry*>::iterator it = inspectNominal.begin(); it != inspectNominal.end(); ++it) {
00584 float fDist = (*it)->getDistance(pnt);
00585 if (fabs(fDist) < fabs(fMinDist))
00586 fMinDist = fDist;
00587 }
00588
00589 if (fMinDist > this->SearchRadius.getValue())
00590 fMinDist = FLT_MAX;
00591 else if (-fMinDist > this->SearchRadius.getValue())
00592 fMinDist = -FLT_MAX;
00593 vals[index] = fMinDist;
00594 seq.next();
00595 }
00596 #endif
00597
00598 Distances.setValues(vals);
00599
00600 float fRMS = 0;
00601 int countRMS = 0;
00602 for (std::vector<float>::iterator it = vals.begin(); it != vals.end(); ++it) {
00603 if (fabs(*it) < FLT_MAX) {
00604 fRMS += (*it) * (*it);
00605 countRMS++;
00606 }
00607 }
00608
00609 fRMS = fRMS / countRMS;
00610 fRMS = sqrt(fRMS);
00611 Base::Console().Message("RMS value for '%s' with search radius=%.4f is: %.4f\n",
00612 this->Label.getValue(), this->SearchRadius.getValue(), fRMS);
00613
00614 delete actual;
00615 for (std::vector<InspectNominalGeometry*>::iterator it = inspectNominal.begin(); it != inspectNominal.end(); ++it)
00616 delete *it;
00617
00618 return 0;
00619 }
00620
00621
00622
00623 PROPERTY_SOURCE(Inspection::Group, App::DocumentObjectGroup)
00624
00625
00626 Group::Group()
00627 {
00628 }
00629
00630 Group::~Group()
00631 {
00632 }