diff --git a/src/Mod/Sketcher/App/SketchAnalysis.cpp b/src/Mod/Sketcher/App/SketchAnalysis.cpp index ff2a0433c0542..a868b1a4ec791 100644 --- a/src/Mod/Sketcher/App/SketchAnalysis.cpp +++ b/src/Mod/Sketcher/App/SketchAnalysis.cpp @@ -87,6 +87,15 @@ struct SketchAnalysis::Vertex_Less double tolerance; }; +struct SketchAnalysis::VertexID_Less +{ + bool operator()(const VertexIds& x, + const VertexIds& y) const + { + return (x.GeoId < y.GeoId || ((x.GeoId == y.GeoId) && (x.PosId < y. PosId))); + } +}; + struct SketchAnalysis::Vertex_EqualTo { Vertex_EqualTo(double tolerance) : tolerance(tolerance){} @@ -142,7 +151,9 @@ struct SketchAnalysis::Edge_EqualTo int SketchAnalysis::detectMissingPointOnPointConstraints(double precision, bool includeconstruction /*=true*/) { - std::vector vertexIds; + std::vector vertexIds; // Holds a list of all vertices in the sketch + + // Build the list of sketch vertices const std::vector& geom = sketch->getInternalGeometry(); for (std::size_t i=0; igetEndPoint(); vertexIds.push_back(id); } + // TODO take into account single vertices ? + } + + std::sort(vertexIds.begin(), vertexIds.end(), Vertex_Less(precision)); // Sort points in geographic order + + // Build a list of all coincidence in the sketch + + std::vector coincidences = sketch->Constraints.getValues(); + + for (auto &constraint:sketch->Constraints.getValues()) { + if (constraint->Type == Sketcher::Coincident || + constraint->Type == Sketcher::Tangent || + constraint->Type == Sketcher::Perpendicular) { + coincidences.push_back(constraint); + } + // TODO optimizing by removing constraints not applying on vertices ? } - std::sort(vertexIds.begin(), vertexIds.end(), Vertex_Less(precision)); + std::list missingCoincidences; //Holds the list of missing coincidences + std::vector::iterator vt = vertexIds.begin(); Vertex_EqualTo pred(precision); - std::list coincidences; - // Make a list of constraint we expect for coincident vertexes + // Comparing existing constraints and find missing ones + while (vt < vertexIds.end()) { - // get first item whose adjacent element has the same vertex coordinates + // Seeking for adjacent group of vertices vt = std::adjacent_find(vt, vertexIds.end(), pred); - if (vt < vertexIds.end()) { + if (vt < vertexIds.end()) { // If one found std::vector::iterator vn; - for (vn = vt+1; vn != vertexIds.end(); ++vn) { + std::set vertexGrp; // Holds a single group of adjacent vertices + // Extract the group of adjacent vertices + vertexGrp.insert(*vt); + for (vn = vt+1; vn < vertexIds.end(); ++vn) { if (pred(*vt,*vn)) { - ConstraintIds id; - id.Type = Coincident; // default point on point restriction - id.v = vt->v; - id.First = vt->GeoId; - id.FirstPos = vt->PosId; - id.Second = vn->GeoId; - id.SecondPos = vn->PosId; - coincidences.push_back(id); + vertexGrp.insert(*vn); } else { break; } } - vt = vn; - } - } + std::vector> coincVertexGrps; // Holds groups of coincident vertices + + // Decompose the group of adjacent vertices into groups of coincident vertices + for (auto &coincidence:coincidences) { // Going through existent coincidences + VertexIds v1, v2; + v1.GeoId = coincidence->First; + v1.PosId = coincidence->FirstPos; + v2.GeoId = coincidence->Second; + v2.PosId = coincidence->SecondPos; + + // Look if coincident vertices are in the group of adjacent ones we are processing + auto nv1 = vertexGrp.extract(v1); + auto nv2 = vertexGrp.extract(v2); + if (nv1.empty() && nv2.empty()) continue; + + // Look if one of the constrained vertices is already in a group of coincident vertices + for (std::set grp:coincVertexGrps) { + if ( (grp.find(v1) != grp.end()) || (grp.find(v2) != grp.end()) ) { + // If yes add them to the existing group + grp.insert(nv1.value()); + grp.insert(nv2.value()); + continue; + } + } + // If no, create a new group of coincident vertices + if (!nv1.empty() || !nv2.empty()) { + std::set newGrp; + newGrp.insert(nv1.value()); + newGrp.insert(nv2.value()); + coincVertexGrps.push_back(newGrp); + } + } - // Go through the available 'Coincident', 'Tangent' or 'Perpendicular' constraints - // and check which of them is forcing two vertexes to be coincident. - // If there is none but two vertexes can be considered equal a coincident constraint is missing. - std::vector constraint = sketch->Constraints.getValues(); - for (std::vector::iterator it = constraint.begin(); it != constraint.end(); ++it) { - if ((*it)->Type == Sketcher::Coincident || - (*it)->Type == Sketcher::Tangent || - (*it)->Type == Sketcher::Perpendicular) { - ConstraintIds id; - id.First = (*it)->First; - id.FirstPos = (*it)->FirstPos; - id.Second = (*it)->Second; - id.SecondPos = (*it)->SecondPos; - std::list::iterator pos = std::find_if - (coincidences.begin(), coincidences.end(), Constraint_Equal(id)); - if (pos != coincidences.end()) { - coincidences.erase(pos); + // If there are remaining vertices in the adjacent group (not in any existing constraint) + // add them as being each a separate coincident group + for (auto &lonept: vertexGrp) { + std::set newGrp; + newGrp.insert(lonept); + coincVertexGrps.push_back(newGrp); } + + // If there is more than 1 coincident group into adjacent group, constraint(s) is(are) missing + // Virtually generate the missing constraint(s) + if (coincVertexGrps.size() > 1) { + std::vector>::iterator vn; + // Starting from the 2nd coincident group, generate a constraint between + // this group first vertex, and previous group first vertex + for (vn = coincVertexGrps.begin()+1; vn < coincVertexGrps.end(); ++vn) { + ConstraintIds id; + id.Type = Coincident; // default point on point restriction + id.v = (vn-1)->begin()->v; + id.First = (vn-1)->begin()->GeoId; + id.FirstPos = (vn-1)->begin()->PosId; + id.Second = vn->begin()->GeoId; + id.SecondPos = vn->begin()->PosId; + missingCoincidences.push_back(id); + } + } + + vt = vn; } } + // Update list of missing constraints stored as member variable of sketch this->vertexConstraints.clear(); - this->vertexConstraints.reserve(coincidences.size()); + this->vertexConstraints.reserve(missingCoincidences.size()); - for (std::list::iterator it = coincidences.begin(); it != coincidences.end(); ++it) { - this->vertexConstraints.push_back(*it); + for (auto &coincidence:missingCoincidences) { + this->vertexConstraints.push_back(coincidence); } + // Return number of missing constraints return this->vertexConstraints.size(); } diff --git a/src/Mod/Sketcher/App/SketchAnalysis.h b/src/Mod/Sketcher/App/SketchAnalysis.h index 08b3bd488495b..94454a9c7a795 100644 --- a/src/Mod/Sketcher/App/SketchAnalysis.h +++ b/src/Mod/Sketcher/App/SketchAnalysis.h @@ -132,6 +132,7 @@ class SketcherExport SketchAnalysis struct VertexIds; struct Vertex_Less; + struct VertexID_Less; struct Vertex_EqualTo; struct EdgeIds; struct Edge_Less;