diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index 608d9da0e..1898955f3 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -96,8 +96,9 @@ void Sketch::clear(void) Conflicting.clear(); } -int Sketch::setUpSketch(const std::vector &GeoList, const std::vector &ConstraintList, - bool withDiagnose, int extGeoCount) +int Sketch::setUpSketch(const std::vector &GeoList, + const std::vector &ConstraintList, + int extGeoCount) { clear(); @@ -119,13 +120,11 @@ int Sketch::setUpSketch(const std::vector &GeoList, const std: addConstraints(ConstraintList); GCSsys.clearByTag(-1); - GCSsys.clearByTag(-2); - GCSsys.initSolution(Parameters); - - if (withDiagnose) - return diagnose(); - else - return 0; + GCSsys.declareUnknowns(Parameters); + GCSsys.initSolution(); + GCSsys.getConflicting(Conflicting); + GCSsys.getRedundant(Redundant); + return GCSsys.dofsNumber(); } const char* nameByType(Sketch::GeoType type) @@ -744,10 +743,7 @@ int Sketch::addPointCoincidentConstraint(int geoId1, PointPos pos1, int geoId2, GCS::Point &p1 = Points[pointId1]; GCS::Point &p2 = Points[pointId2]; int tag = ++ConstraintsCounter; - // trick: we do not tag coincidence constraints in order to exclude - // them from the diagnosing of conflicts - //GCSsys.addConstraintP2PCoincident(p1, p2, tag); - GCSsys.addConstraintP2PCoincident(p1, p2); + GCSsys.addConstraintP2PCoincident(p1, p2, tag); return ConstraintsCounter; } return -1; @@ -1569,7 +1565,6 @@ int Sketch::solve(void) Base::TimeInfo start_time; if (!isInitMove) { // make sure we are in single subsystem mode GCSsys.clearByTag(-1); - GCSsys.clearByTag(-2); isFine = true; } @@ -1593,32 +1588,33 @@ int Sketch::solve(void) break; case 3: // last resort: augment the system with a second subsystem and use the SQP solver solvername = "SQP(augmented system)"; - GCSsys.clearByTag(-1); - GCSsys.clearByTag(-2); InitParameters.resize(Parameters.size()); int i=0; for (std::vector::iterator it = Parameters.begin(); it != Parameters.end(); ++it, i++) { InitParameters[i] = **it; - GCSsys.addConstraintEqual(*it, &InitParameters[i], -2); + GCSsys.addConstraintEqual(*it, &InitParameters[i], -1); } - GCSsys.initSolution(Parameters); + GCSsys.initSolution(); ret = GCSsys.solve(isFine); break; } - // if successfully solved try write the parameters back + // if successfully solved try to write the parameters back if (ret == GCS::Success) { GCSsys.applySolution(); valid_solution = updateGeometry(); - if (!valid_solution) + if (!valid_solution) { + GCSsys.undoSolution(); + updateGeometry(); Base::Console().Warning("Invalid solution from %s solver.\n", solvername.c_str()); + } } else { valid_solution = false; //Base::Console().Log("NotSolved "); } if (soltype == 3) // cleanup temporary constraints of the augmented system - GCSsys.clearByTag(-2); + GCSsys.clearByTag(-1); if (valid_solution) { if (soltype == 1) @@ -1637,11 +1633,6 @@ int Sketch::solve(void) } } // soltype - if (!valid_solution) { // undo any changes - GCSsys.undoSolution(); - updateGeometry(); - } - Base::TimeInfo end_time; //Base::Console().Log("T:%s\n",Base::TimeInfo::diffTime(start_time,end_time).c_str()); SolveTime = Base::TimeInfo::diffTimeF(start_time,end_time); @@ -1655,7 +1646,6 @@ int Sketch::initMove(int geoId, PointPos pos, bool fine) geoId = checkGeoId(geoId); GCSsys.clearByTag(-1); - GCSsys.clearByTag(-2); // don't try to move sketches that contain conflicting constraints if (hasConflicts()) { @@ -1760,7 +1750,7 @@ int Sketch::initMove(int geoId, PointPos pos, bool fine) } InitParameters = MoveParameters; - GCSsys.initSolution(Parameters); + GCSsys.initSolution(); isInitMove = true; return 0; } @@ -1838,18 +1828,6 @@ Base::Vector3d Sketch::getPoint(int geoId, PointPos pos) return Base::Vector3d(); } -int Sketch::diagnose(void) -{ - Conflicting.clear(); - if (GCSsys.isInit()) { - int dofs = GCSsys.diagnose(Parameters, Conflicting); - return dofs; - } - else { - return -1; - } -} - TopoShape Sketch::toShape(void) const diff --git a/src/Mod/Sketcher/App/Sketch.h b/src/Mod/Sketcher/App/Sketch.h index ec108421b..3ea95bc4f 100644 --- a/src/Mod/Sketcher/App/Sketch.h +++ b/src/Mod/Sketcher/App/Sketch.h @@ -53,9 +53,21 @@ public: int solve(void); /// delete all geometry and constraints, leave an empty sketch void clear(void); - /// set the sketch up with geoms and constraints + /** set the sketch up with geoms and constraints + * + * returns the degree of freedom of a sketch and calculates a list of + * conflicting constraints + * + * 0 degrees of freedom correspond to a fully constrained sketch + * -1 degrees of freedom correspond to an over-constrained sketch + * positive degrees of freedom correspond to an under-constrained sketch + * + * an over-constrained sketch will always contain conflicting constraints + * a fully constrained or under-constrained sketch may contain conflicting + * constraints or may not + */ int setUpSketch(const std::vector &GeoList, const std::vector &ConstraintList, - bool withDiagnose=true, int extGeoCount=0); + int extGeoCount=0); /// return the actual geometry of the sketch a TopoShape Part::TopoShape toShape(void) const; /// add unspecified geometry @@ -71,20 +83,10 @@ public: /// retrieves a point Base::Vector3d getPoint(int geoId, PointPos pos); - /** returns the degree of freedom of a sketch and calculates a list of - * conflicting constraints - * - * 0 degrees of freedom correspond to a fully constrained sketch - * -1 degrees of freedom correspond to an over-constrained sketch - * positive degrees of freedom correspond to an under-constrained sketch - * - * an over-constrained sketch will always contain conflicting constraints - * a fully constrained or under-constrained sketch may contain conflicting - * constraints or may not - */ - int diagnose(void); - bool hasConflicts(void) const { return (Conflicting.size() > 0); }; - const std::vector &getConflicting(void) const { return Conflicting; }; + bool hasConflicts(void) const { return (Conflicting.size() > 0); } + const std::vector &getConflicting(void) const { return Conflicting; } + bool hasRedundancies(void) const { return (Redundant.size() > 0); } + const std::vector &getRedundant(void) const { return Redundant; } /** set the datum of a distance or angle constraint to a certain value and solve * This can cause the solving to fail! @@ -201,6 +203,7 @@ protected: GCS::System GCSsys; int ConstraintsCounter; std::vector Conflicting; + std::vector Redundant; // solving parameters std::vector Parameters; // with memory allocation diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 95aa455a7..c09cd6963 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -97,7 +97,7 @@ App::DocumentObjectExecReturn *SketchObject::execute(void) rebuildExternalGeometry(); Sketch sketch; int dofs = sketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), - true, getExternalGeometryCount()); + getExternalGeometryCount()); if (dofs < 0) { // over-constrained sketch std::string msg="Over-constrained sketch\n"; appendConflictMsg(sketch.getConflicting(), msg); @@ -108,6 +108,11 @@ App::DocumentObjectExecReturn *SketchObject::execute(void) appendConflictMsg(sketch.getConflicting(), msg); return new App::DocumentObjectExecReturn(msg.c_str(),this); } + if (sketch.hasRedundancies()) { // redundant constraints + std::string msg="Sketch with redundant constraints\n"; + appendRedundantMsg(sketch.getRedundant(), msg); + return new App::DocumentObjectExecReturn(msg.c_str(),this); + } // solve the sketch if (sketch.solve() != 0) @@ -128,7 +133,7 @@ int SketchObject::hasConflicts(void) const // set up a sketch (including dofs counting and diagnosing of conflicts) Sketch sketch; int dofs = sketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), - true, getExternalGeometryCount()); + getExternalGeometryCount()); if (dofs < 0) // over-constrained sketch return -2; if (sketch.hasConflicts()) // conflicting constraints @@ -166,7 +171,7 @@ int SketchObject::setDatum(int ConstrId, double Datum) // set up a sketch (including dofs counting and diagnosing of conflicts) Sketch sketch; int dofs = sketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), - true, getExternalGeometryCount()); + getExternalGeometryCount()); int err=0; if (dofs < 0) // over-constrained sketch err = -3; @@ -192,7 +197,7 @@ int SketchObject::movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toP { Sketch sketch; int dofs = sketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), - true, getExternalGeometryCount()); + getExternalGeometryCount()); if (dofs < 0) // over-constrained sketch return -1; if (sketch.hasConflicts()) // conflicting constraints @@ -1388,10 +1393,32 @@ void SketchObject::appendConflictMsg(const std::vector &conflicting, std::s if (msg.length() > 0) ss << msg; if (conflicting.size() > 0) { - ss << "Please remove at least one of the constraints (" << conflicting[0]; + if (conflicting.size() == 1) + ss << "Please remove the following constraint:\n"; + else + ss << "Please remove at least one of the following constraints:\n"; + ss << conflicting[0]; for (unsigned int i=1; i < conflicting.size(); i++) ss << ", " << conflicting[i]; - ss << ")\n"; + ss << "\n"; + } + msg = ss.str(); +} + +void SketchObject::appendRedundantMsg(const std::vector &redundant, std::string &msg) +{ + std::stringstream ss; + if (msg.length() > 0) + ss << msg; + if (redundant.size() > 0) { + if (redundant.size() == 1) + ss << "Please remove the following redundant constraint:\n"; + else + ss << "Please remove the following redundant constraints:\n"; + ss << redundant[0]; + for (unsigned int i=1; i < redundant.size(); i++) + ss << ", " << redundant[i]; + ss << "\n"; } msg = ss.str(); } diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 83802d83a..cf865d405 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -136,6 +136,8 @@ public: /// generates a warning message about constraint conflicts and appends it to the given message static void appendConflictMsg(const std::vector &conflicting, std::string &msg); + /// generates a warning message about redundant constraints and appends it to the given message + static void appendRedundantMsg(const std::vector &redundant, std::string &msg); // from base class virtual PyObject *getPyObject(void); diff --git a/src/Mod/Sketcher/App/freegcs/GCS.cpp b/src/Mod/Sketcher/App/freegcs/GCS.cpp index c608c9bd0..ffab4c579 100644 --- a/src/Mod/Sketcher/App/freegcs/GCS.cpp +++ b/src/Mod/Sketcher/App/freegcs/GCS.cpp @@ -41,19 +41,20 @@ typedef boost::adjacency_list Gra // System System::System() -: clist(0), +: plist(0), clist(0), c2p(), p2c(), - subsyslist(0), - reference(), - init(false) + subSystems(0), subSystemsAux(0), + reference(0), + hasUnknowns(false), hasDiagnosis(false), isInit(false) { } System::System(std::vector clist_) -: c2p(), p2c(), - subsyslist(0), - reference(), - init(false) +: plist(0), + c2p(), p2c(), + subSystems(0), subSystemsAux(0), + reference(0), + hasUnknowns(false), hasDiagnosis(false), isInit(false) { // create own (shallow) copy of constraints for (std::vector::iterator constr=clist_.begin(); @@ -125,7 +126,16 @@ System::~System() void System::clear() { - clearReference(); + plist.clear(); + pIndex.clear(); + hasUnknowns = false; + hasDiagnosis = false; + + redundant.clear(); + conflictingTags.clear(); + redundantTags.clear(); + + reference.clear(); clearSubSystems(); free(clist); c2p.clear(); @@ -148,7 +158,9 @@ void System::clearByTag(int tagId) int System::addConstraint(Constraint *constr) { - clearReference(); + isInit = false; + if (constr->getTag() >= 0) // negatively tagged constraints have no impact + hasDiagnosis = false; // on the diagnosis clist.push_back(constr); VEC_pD constr_params = constr->params(); @@ -163,12 +175,15 @@ int System::addConstraint(Constraint *constr) void System::removeConstraint(Constraint *constr) { - clearReference(); - clearSubSystems(); - std::vector::iterator it; it = std::find(clist.begin(), clist.end(), constr); + if (it == clist.end()) + return; + clist.erase(it); + if (constr->getTag() >= 0) + hasDiagnosis = false; + clearSubSystems(); VEC_pD constr_params = c2p[constr]; for (VEC_pD::const_iterator param=constr_params.begin(); @@ -545,9 +560,18 @@ void System::rescaleConstraint(int id, double coeff) clist[id]->rescale(coeff); } - -void System::initSolution(VEC_pD ¶ms) +void System::declareUnknowns(VEC_pD ¶ms) { + plist = params; + pIndex.clear(); + for (int i=0; i < int(plist.size()); ++i) + pIndex[plist[i]] = i; + hasUnknowns = true; +} + +void System::initSolution() +{ + // - Stores the current parameters values in the vector "reference" // - identifies any decoupled subsystems and partitions the original // system into corresponding components // - Stores the current parameters in the vector "reference" @@ -556,18 +580,38 @@ void System::initSolution(VEC_pD ¶ms) // - Organizes the rest of constraints into two subsystems for // tag ids >=0 and < 0 respectively and applies the // system reduction specified in the previous step - MAP_pD_I pIndex; - for (int i=0; i < int(params.size()); ++i) - pIndex[params[i]] = i; + + isInit = false; + if (!hasUnknowns) + return; + + // storing reference configuration + setReference(); + + // diagnose conflicting or redundant constraints + if (!hasDiagnosis) { + diagnose(); + if (!hasDiagnosis) + return; + } + std::vector clistR; + if (redundant.size()) { + for (std::vector::const_iterator constr=clist.begin(); + constr != clist.end(); ++constr) + if (redundant.count(*constr) == 0) + clistR.push_back(*constr); + } + else + clistR = clist; // partitioning into decoupled components Graph g; - for (int i=0; i < int(params.size() + clist.size()); i++) + for (int i=0; i < int(plist.size() + clistR.size()); i++) boost::add_vertex(g); - int cvtid = int(params.size()); - for (std::vector::const_iterator constr=clist.begin(); - constr != clist.end(); ++constr, cvtid++) { + int cvtid = int(plist.size()); + for (std::vector::const_iterator constr=clistR.begin(); + constr != clistR.end(); ++constr, cvtid++) { VEC_pD &cparams = c2p[*constr]; for (VEC_pD::const_iterator param=cparams.begin(); param != cparams.end(); ++param) { @@ -580,113 +624,131 @@ void System::initSolution(VEC_pD ¶ms) VEC_I components(boost::num_vertices(g)); int componentsSize = boost::connected_components(g, &components[0]); - clearReference(); - for (VEC_pD::const_iterator param=params.begin(); - param != params.end(); ++param) - reference[*param] = **param; - // identification of equality constraints and parameter reduction - std::set eliminated; // constraints that will be eliminated through reduction - reductionmap.clear(); + std::set reducedConstrs; // constraints that will be eliminated through reduction + reductionmaps.clear(); // destroy any maps + reductionmaps.resize(componentsSize); // create empty maps to be filled in { - VEC_pD reduced_params=params; + VEC_pD reducedParams=plist; - for (std::vector::const_iterator constr=clist.begin(); - constr != clist.end(); ++constr) { + for (std::vector::const_iterator constr=clistR.begin(); + constr != clistR.end(); ++constr) { if ((*constr)->getTag() >= 0 && (*constr)->getTypeId() == Equal) { MAP_pD_I::const_iterator it1,it2; it1 = pIndex.find((*constr)->params()[0]); it2 = pIndex.find((*constr)->params()[1]); if (it1 != pIndex.end() && it2 != pIndex.end()) { - eliminated.insert(*constr); - double *p_kept = reduced_params[it1->second]; - double *p_replaced = reduced_params[it2->second]; - for (int i=0; i < int(params.size()); ++i) - if (reduced_params[i] == p_replaced) - reduced_params[i] = p_kept; + reducedConstrs.insert(*constr); + double *p_kept = reducedParams[it1->second]; + double *p_replaced = reducedParams[it2->second]; + for (int i=0; i < int(plist.size()); ++i) + if (reducedParams[i] == p_replaced) + reducedParams[i] = p_kept; } } } - for (int i=0; i < int(params.size()); ++i) - if (params[i] != reduced_params[i]) - reductionmap[params[i]] = reduced_params[i]; + for (int i=0; i < int(plist.size()); ++i) + if (plist[i] != reducedParams[i]) { + int cid = components[i]; + reductionmaps[cid][plist[i]] = reducedParams[i]; + } } - std::vector< std::vector > clists0(componentsSize), - clists1(componentsSize), - clists2(componentsSize); - int i = int(params.size()); - for (std::vector::const_iterator constr=clist.begin(); - constr != clist.end(); ++constr, i++) { - if (eliminated.count(*constr) == 0) { - int id = components[i]; - if ((*constr)->getTag() >= 0) - clists0[id].push_back(*constr); - else if ((*constr)->getTag() == -1) // move constraints - clists1[id].push_back(*constr); - else // distance from reference constraints - clists2[id].push_back(*constr); + clists.clear(); // destroy any lists + clists.resize(componentsSize); // create empty lists to be filled in + int i = int(plist.size()); + for (std::vector::const_iterator constr=clistR.begin(); + constr != clistR.end(); ++constr, i++) { + if (reducedConstrs.count(*constr) == 0) { + int cid = components[i]; + clists[cid].push_back(*constr); } } - std::vector< std::vector > plists(componentsSize); - for (int i=0; i < int(params.size()); ++i) { - int id = components[i]; - plists[id].push_back(params[i]); + plists.clear(); // destroy any lists + plists.resize(componentsSize); // create empty lists to be filled in + for (int i=0; i < int(plist.size()); ++i) { + int cid = components[i]; + plists[cid].push_back(plist[i]); } + // calculates subSystems and subSystemsAux from clists, plists and reductionmaps clearSubSystems(); - for (int cid=0; cid < componentsSize; cid++) { - subsyslist.push_back(std::vector(0)); - if (clists0[cid].size() > 0) - subsyslist[cid].push_back(new SubSystem(clists0[cid], plists[cid], reductionmap)); - if (clists1[cid].size() > 0) - subsyslist[cid].push_back(new SubSystem(clists1[cid], plists[cid], reductionmap)); - if (clists2[cid].size() > 0) - subsyslist[cid].push_back(new SubSystem(clists2[cid], plists[cid], reductionmap)); + for (int cid=0; cid < clists.size(); cid++) { + std::vector clist0, clist1; + for (std::vector::const_iterator constr=clists[cid].begin(); + constr != clists[cid].end(); ++constr) { + if ((*constr)->getTag() >= 0) + clist0.push_back(*constr); + else // move or distance from reference constraints + clist1.push_back(*constr); + } + + subSystems.push_back(NULL); + subSystemsAux.push_back(NULL); + if (clist0.size() > 0) + subSystems[cid] = new SubSystem(clist0, plists[cid], reductionmaps[cid]); + if (clist1.size() > 0) + subSystemsAux[cid] = new SubSystem(clist1, plists[cid], reductionmaps[cid]); } - init = true; + + isInit = true; } -void System::clearReference() +void System::setReference() { - init = false; reference.clear(); + reference.reserve(plist.size()); + for (VEC_pD::const_iterator param=plist.begin(); + param != plist.end(); ++param) + reference.push_back(**param); } void System::resetToReference() { - for (MAP_pD_D::const_iterator it=reference.begin(); - it != reference.end(); ++it) - *(it->first) = it->second; + if (reference.size() == plist.size()) { + VEC_D::const_iterator ref=reference.begin(); + VEC_pD::iterator param=plist.begin(); + for (; ref != reference.end(); ++ref, ++param) + **param = *ref; + } } int System::solve(VEC_pD ¶ms, bool isFine, Algorithm alg) { - initSolution(params); + declareUnknowns(params); + initSolution(); return solve(isFine, alg); } int System::solve(bool isFine, Algorithm alg) { + if (!isInit) + return Failed; + bool isReset = false; // return success by default in order to permit coincidence constraints to be applied // even if no other system has to be solved int res = Success; - for (int cid=0; cid < int(subsyslist.size()); cid++) { - if (subsyslist[cid].size() > 0 && !isReset) { + for (int cid=0; cid < int(subSystems.size()); cid++) { + if ((subSystems[cid] || subSystemsAux[cid]) && !isReset) { resetToReference(); isReset = true; } - if (subsyslist[cid].size() == 1) - res = std::max(res, solve(subsyslist[cid][0], isFine, alg)); - else if (subsyslist[cid].size() == 2) - res = std::max(res, solve(subsyslist[cid][0], subsyslist[cid][1], isFine)); - else if (subsyslist[cid].size() > 2) - // subsystem 1 has higher priority than subsystems 2,3,... - // these subsystems act like a preconditioner - for (int i=subsyslist[cid].size()-1; i > 0; i--) - res = std::max(res, solve(subsyslist[cid][0], subsyslist[cid][i], isFine)); + if (subSystems[cid] && subSystemsAux[cid]) + res = std::max(res, solve(subSystems[cid], subSystemsAux[cid], isFine)); + else if (subSystems[cid]) + res = std::max(res, solve(subSystems[cid], isFine, alg)); + else if (subSystemsAux[cid]) + res = std::max(res, solve(subSystemsAux[cid], isFine, alg)); + } + if (res == Success) { + for (std::set::const_iterator constr=redundant.begin(); + constr != redundant.end(); constr++) + if ((*constr)->error() > XconvergenceFine) { + res = Converged; + return res; + } } return res; } @@ -1068,7 +1130,7 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) int xsizeB = subsysB->pSize(); int csizeA = subsysA->cSize(); - VEC_pD plist(xsizeA+xsizeB); + VEC_pD plistAB(xsizeA+xsizeB); { VEC_pD plistA, plistB; subsysA->getParamList(plistA); @@ -1079,10 +1141,10 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) VEC_pD::const_iterator it; it = std::set_union(plistA.begin(),plistA.end(), - plistB.begin(),plistB.end(),plist.begin()); - plist.resize(it-plist.begin()); + plistB.begin(),plistB.end(),plistAB.begin()); + plistAB.resize(it-plistAB.begin()); } - int xsize = plist.size(); + int xsize = plistAB.size(); Eigen::MatrixXd B = Eigen::MatrixXd::Identity(xsize, xsize); Eigen::MatrixXd JA(csizeA, xsize); @@ -1100,12 +1162,12 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) subsysA->redirectParams(); subsysB->redirectParams(); - subsysB->getParams(plist,x); - subsysA->getParams(plist,x); - subsysB->setParams(plist,x); // just to ensure that A and B are synchronized + subsysB->getParams(plistAB,x); + subsysA->getParams(plistAB,x); + subsysB->setParams(plistAB,x); // just to ensure that A and B are synchronized - subsysB->calcGrad(plist,grad); - subsysA->calcJacobi(plist,JA); + subsysB->calcGrad(plistAB,grad); + subsysA->calcJacobi(plistAB,JA); subsysA->calcResidual(resA); double convergence = isFine ? XconvergenceFine : XconvergenceRough; @@ -1130,7 +1192,7 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) double tau=0.5; double rho=0.5; double alpha=1; - alpha = std::min(alpha, subsysA->maxStep(plist,xdir)); + alpha = std::min(alpha, subsysA->maxStep(plistAB,xdir)); // Eq. 18.32 // double mu = lambda.lpNorm() + 0.01; @@ -1148,8 +1210,8 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) double deriv = grad.dot(xdir) - mu * resA.lpNorm<1>(); x = x0 + alpha * xdir; - subsysA->setParams(plist,x); - subsysB->setParams(plist,x); + subsysA->setParams(plistAB,x); + subsysB->setParams(plistAB,x); subsysA->calcResidual(resA); double f = subsysB->error() + mu * resA.lpNorm<1>(); @@ -1161,8 +1223,8 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) // Eigen::ComputeThinV).solve(-resA); xdir1 = -Y*resA; x += xdir1; // = x0 + alpha * xdir + xdir1 - subsysA->setParams(plist,x); - subsysB->setParams(plist,x); + subsysA->setParams(plistAB,x); + subsysB->setParams(plistAB,x); subsysA->calcResidual(resA); f = subsysB->error() + mu * resA.lpNorm<1>(); if (f < f0 + eta * alpha * deriv) @@ -1172,8 +1234,8 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) if (alpha < 1e-8) // let the linesearch fail alpha = 0.; x = x0 + alpha * xdir; - subsysA->setParams(plist,x); - subsysB->setParams(plist,x); + subsysA->setParams(plistAB,x); + subsysB->setParams(plistAB,x); subsysA->calcResidual(resA); f = subsysB->error() + mu * resA.lpNorm<1>(); if (alpha < 1e-8) // let the linesearch fail @@ -1186,8 +1248,8 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) y = grad - JA.transpose() * lambda; { - subsysB->calcGrad(plist,grad); - subsysA->calcJacobi(plist,JA); + subsysB->calcGrad(plistAB,grad); + subsysA->calcJacobi(plistAB,JA); subsysA->calcResidual(resA); } y = grad - JA.transpose() * lambda - y; // Eq. 18.13 @@ -1225,13 +1287,15 @@ int System::solve(SubSystem *subsysA, SubSystem *subsysB, bool isFine) void System::applySolution() { - for (int cid=0; cid < int(subsyslist.size()); cid++) - for (int i=subsyslist[cid].size()-1; i >= 0; i--) - subsyslist[cid][i]->applySolution(); - - for (MAP_pD_pD::const_iterator it=reductionmap.begin(); - it != reductionmap.end(); ++it) - *(it->first) = *(it->second); + for (int cid=0; cid < int(subSystems.size()); cid++) { + if (subSystemsAux[cid]) + subSystemsAux[cid]->applySolution(); + if (subSystems[cid]) + subSystems[cid]->applySolution(); + for (MAP_pD_pD::const_iterator it=reductionmaps[cid].begin(); + it != reductionmaps[cid].end(); ++it) + *(it->first) = *(it->second); + } } void System::undoSolution() @@ -1239,7 +1303,7 @@ void System::undoSolution() resetToReference(); } -int System::diagnose(VEC_pD ¶ms, VEC_I &conflictingTags) +int System::diagnose() { // Analyses the constrainess grad of the system and provides feedback // The vector "conflictingTags" will hold a group of conflicting constraints @@ -1251,22 +1315,24 @@ int System::diagnose(VEC_pD ¶ms, VEC_I &conflictingTags) // will provide no feedback about possible conflicts between // two high priority constraints. For this reason, tagging // constraints with 0 should be used carefully. - if (!isInit()) - return -1; + hasDiagnosis = false; + if (!hasUnknowns) { + dofs = -1; + return dofs; + } + redundant.clear(); conflictingTags.clear(); - std::vector conflictingIndex; - VEC_I tags; - Eigen::MatrixXd J(clist.size(), params.size()); + redundantTags.clear(); + Eigen::MatrixXd J(clist.size(), plist.size()); int count=0; for (std::vector::iterator constr=clist.begin(); constr != clist.end(); ++constr) { (*constr)->revertParams(); if ((*constr)->getTag() >= 0) { count++; - tags.push_back((*constr)->getTag()); - for (int j=0; j < int(params.size()); j++) - J(count-1,j) = (*constr)->grad(params[j]); + for (int j=0; j < int(plist.size()); j++) + J(count-1,j) = (*constr)->grad(plist[j]); } } @@ -1284,7 +1350,7 @@ int System::diagnose(VEC_pD ¶ms, VEC_I &conflictingTags) R = qrJT.matrixQR().topRows(constrNum) .triangularView(); - if (constrNum > rank) { // conflicting constraints + if (constrNum > rank) { // conflicting or redundant constraints for (int i=1; i < rank; i++) { // eliminate non zeros above pivot assert(R(i,i) != 0); @@ -1296,43 +1362,142 @@ int System::diagnose(VEC_pD ¶ms, VEC_I &conflictingTags) } } } - conflictingIndex.resize(constrNum-rank); + std::vector< std::vector > conflictGroups(constrNum-rank); for (int j=rank; j < constrNum; j++) { for (int row=0; row < rank; row++) { if (fabs(R(row,j)) > 1e-10) { int origCol = qrJT.colsPermutation().indices()[row]; - conflictingIndex[j-rank].push_back(origCol); + conflictGroups[j-rank].push_back(clist[origCol]); } } int origCol = qrJT.colsPermutation().indices()[j]; - conflictingIndex[j-rank].push_back(origCol); + conflictGroups[j-rank].push_back(clist[origCol]); } - SET_I tags_set; - for (int i=0; i < conflictingIndex.size(); i++) { - for (int j=0; j < conflictingIndex[i].size(); j++) { - tags_set.insert(tags[conflictingIndex[i][j]]); + // try to remove the conflicting constraints and solve the + // system in order to check if the removed constraints were + // just redundant but not really conflicting + std::set skipped; + SET_I satisfiedGroups; + while (1) { + std::map< Constraint *, SET_I > conflictingMap; + for (int i=0; i < conflictGroups.size(); i++) { + if (satisfiedGroups.count(i) == 0) { + for (int j=0; j < conflictGroups[i].size(); j++) { + Constraint *constr = conflictGroups[i][j]; + if (constr->getTag() != 0) // exclude constraints tagged with zero + conflictingMap[constr].insert(i); + } + } + } + if (conflictingMap.empty()) + break; + + int maxPopularity = 0; + Constraint *mostPopular = NULL; + for (std::map< Constraint *, SET_I >::const_iterator it=conflictingMap.begin(); + it != conflictingMap.end(); it++) { + if (it->second.size() > maxPopularity || + (it->second.size() == maxPopularity && mostPopular && + it->first->getTag() > mostPopular->getTag())) { + mostPopular = it->first; + maxPopularity = it->second.size(); + } + } + if (maxPopularity > 0) { + skipped.insert(mostPopular); + for (SET_I::const_iterator it=conflictingMap[mostPopular].begin(); + it != conflictingMap[mostPopular].end(); it++) + satisfiedGroups.insert(*it); } } - tags_set.erase(0); // exclude constraints tagged with zero - conflictingTags.resize(tags_set.size()); - std::copy(tags_set.begin(), tags_set.end(), conflictingTags.begin()); - if (paramsNum == rank) // over-constrained - return paramsNum - constrNum; + std::vector clistTmp; + clistTmp.reserve(clist.size()); + for (std::vector::iterator constr=clist.begin(); + constr != clist.end(); ++constr) + if (skipped.count(*constr) == 0) + clistTmp.push_back(*constr); + + SubSystem *subSysTmp = new SubSystem(clistTmp, plist); + int res = solve(subSysTmp); + if (res == Success) { + subSysTmp->applySolution(); + for (std::set::const_iterator constr=skipped.begin(); + constr != skipped.end(); constr++) { + double err = (*constr)->error(); + if (err * err < XconvergenceFine) + redundant.insert(*constr); + } + resetToReference(); + + std::vector< std::vector > conflictGroupsOrig=conflictGroups; + conflictGroups.clear(); + for (int i=conflictGroupsOrig.size()-1; i >= 0; i--) { + bool isRedundant = false; + for (int j=0; j < conflictGroupsOrig[i].size(); j++) { + if (redundant.count(conflictGroupsOrig[i][j]) > 0) { + isRedundant = true; + break; + } + } + if (!isRedundant) + conflictGroups.push_back(conflictGroupsOrig[i]); + else + constrNum--; + } + } + delete subSysTmp; + + // simplified output of conflicting tags + SET_I conflictingTagsSet; + for (int i=0; i < conflictGroups.size(); i++) { + for (int j=0; j < conflictGroups[i].size(); j++) { + conflictingTagsSet.insert(conflictGroups[i][j]->getTag()); + } + } + conflictingTagsSet.erase(0); // exclude constraints tagged with zero + conflictingTags.resize(conflictingTagsSet.size()); + std::copy(conflictingTagsSet.begin(), conflictingTagsSet.end(), + conflictingTags.begin()); + + // output of redundant tags + SET_I redundantTagsSet; + for (std::set::iterator constr=redundant.begin(); + constr != redundant.end(); ++constr) + redundantTagsSet.insert((*constr)->getTag()); + // remove tags represented at least in one non-redundant constraint + for (std::vector::iterator constr=clist.begin(); + constr != clist.end(); ++constr) + if (redundant.count(*constr) == 0) + redundantTagsSet.erase((*constr)->getTag()); + redundantTags.resize(redundantTagsSet.size()); + std::copy(redundantTagsSet.begin(), redundantTagsSet.end(), + redundantTags.begin()); + + if (paramsNum == rank && constrNum > rank) { // over-constrained + hasDiagnosis = true; + dofs = paramsNum - constrNum; + return dofs; + } } - return paramsNum - rank; + hasDiagnosis = true; + dofs = paramsNum - rank; + return dofs; } - return params.size(); + hasDiagnosis = true; + dofs = plist.size(); + return dofs; } void System::clearSubSystems() { - init = false; - for (int i=0; i < int(subsyslist.size()); i++) - free(subsyslist[i]); - subsyslist.clear(); + isInit = false; + free(subSystems); + free(subSystemsAux); + subSystems.clear(); + subSystemsAux.clear(); } double lineSearch(SubSystem *subsys, Eigen::VectorXd &xdir) diff --git a/src/Mod/Sketcher/App/freegcs/GCS.h b/src/Mod/Sketcher/App/freegcs/GCS.h index 5cd1466ea..f0d87906f 100644 --- a/src/Mod/Sketcher/App/freegcs/GCS.h +++ b/src/Mod/Sketcher/App/freegcs/GCS.h @@ -49,25 +49,31 @@ namespace GCS // This is the main class. It holds all constraints and information // about partitioning into subsystems and solution strategies private: - std::vector clist; + VEC_pD plist; // list of the unknown parameters + MAP_pD_I pIndex; + std::vector clist; std::map c2p; // constraint to parameter adjacency list std::map > p2c; // parameter to constraint adjacency list - // each row of subsyslist contains up to 3 subsystems. - // the first one has the highest priority, always used as the primary subsystem - // the second one is used as secondary subsystem - // the third one is used as secondary system and serves as a preconditioner - std::vector< std::vector > subsyslist; + std::vector subSystems, subSystemsAux; void clearSubSystems(); - MAP_pD_D reference; - void clearReference(); - void resetToReference(); + VEC_D reference; + void setReference(); // copies the current parameter values to reference + void resetToReference(); // reverts all parameter values to the stored reference - MAP_pD_pD reductionmap; // for simplification of equality constraints + std::vector< VEC_pD > plists; // partitioned plist except equality constraints + std::vector< std::vector > clists; // partitioned clist except equality constraints + std::vector< MAP_pD_pD > reductionmaps; // for simplification of equality constraints - bool init; + int dofs; + std::set redundant; + VEC_I conflictingTags, redundantTags; + + bool hasUnknowns; // if plist is filled with the unknown parameters + bool hasDiagnosis; // if dofs, conflictingTags, redundantTags are up to date + bool isInit; // if plists, clists, reductionmaps are up to date int solve_BFGS(SubSystem *subsys, bool isFine); int solve_LM(SubSystem *subsys); @@ -147,7 +153,8 @@ namespace GCS int addConstraintP2PSymmetric(Point &p1, Point &p2, Line &l, int tagId=0); void rescaleConstraint(int id, double coeff); - void initSolution(VEC_pD ¶ms); + void declareUnknowns(VEC_pD ¶ms); + void initSolution(); int solve(bool isFine=true, Algorithm alg=DogLeg); int solve(VEC_pD ¶ms, bool isFine=true, Algorithm alg=DogLeg); @@ -157,9 +164,12 @@ namespace GCS void applySolution(); void undoSolution(); - bool isInit() const { return init; } - - int diagnose(VEC_pD ¶ms, VEC_I &conflictingTags); + int diagnose(); + int dofsNumber() { return hasDiagnosis ? dofs : -1; } + void getConflicting(VEC_I &conflictingOut) const + { conflictingOut = hasDiagnosis ? conflictingTags : VEC_I(0); } + void getRedundant(VEC_I &redundantOut) const + { redundantOut = hasDiagnosis ? redundantTags : VEC_I(0); } }; /////////////////////////////////////// diff --git a/src/Mod/Sketcher/Gui/TaskSketcherMessages.cpp b/src/Mod/Sketcher/Gui/TaskSketcherMessages.cpp index 7061f3a97..dc006117a 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherMessages.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherMessages.cpp @@ -54,8 +54,8 @@ TaskSketcherMessages::TaskSketcherMessages(ViewProviderSketch *sketchView) this->groupLayout()->addWidget(proxy); - connectionSetUp = sketchView->signalSetUp.connect(boost::bind(&SketcherGui::TaskSketcherMessages::slotSetUp, this,_1,_2,_3)); - connectionSolved = sketchView->signalSolved.connect(boost::bind(&SketcherGui::TaskSketcherMessages::slotSolved, this,_1,_2)); + connectionSetUp = sketchView->signalSetUp.connect(boost::bind(&SketcherGui::TaskSketcherMessages::slotSetUp, this,_1)); + connectionSolved = sketchView->signalSolved.connect(boost::bind(&SketcherGui::TaskSketcherMessages::slotSolved, this,_1)); } TaskSketcherMessages::~TaskSketcherMessages() @@ -65,43 +65,14 @@ TaskSketcherMessages::~TaskSketcherMessages() delete ui; } -void TaskSketcherMessages::slotSetUp(int type, int dofs, const std::string &msg) +void TaskSketcherMessages::slotSetUp(QString msg) { - switch(type){ - case -1: - ui->labelConstrainStatus->setText(QString::fromLatin1("Empty sketch")); - break; - case 0: - ui->labelConstrainStatus->setText(QString::fromLatin1("Fully constrained sketch ")); - break; - case 1: - if (dofs==1) - ui->labelConstrainStatus->setText(QString::fromLatin1("Under-constrained sketch with 1 degree of freedom")); - else - ui->labelConstrainStatus->setText(QString::fromLatin1("Under-constrained sketch with %1 degrees of freedom").arg(dofs)); - break; - case 2: - ui->labelConstrainStatus->setText(QString::fromLatin1("Sketch contains conflicting constraints
%1
").arg(QString::fromStdString(msg))); - break; - case 3: - ui->labelConstrainStatus->setText(QString::fromLatin1("Over-constrained sketch
%1
").arg(QString::fromStdString(msg))); - break; - } + ui->labelConstrainStatus->setText(msg); } -void TaskSketcherMessages::slotSolved(int type, float time) +void TaskSketcherMessages::slotSolved(QString msg) { - switch(type){ - case -1: - ui->labelSolverStatus->setText(QString()); - break; - case 0: - ui->labelSolverStatus->setText(QString::fromLatin1("Solved in %1 sec").arg(time)); - break; - case 1: - ui->labelSolverStatus->setText(QString::fromLatin1("Unsolved (%1)").arg(time)); - break; - } + ui->labelSolverStatus->setText(msg); } #include "moc_TaskSketcherMessages.cpp" diff --git a/src/Mod/Sketcher/Gui/TaskSketcherMessages.h b/src/Mod/Sketcher/Gui/TaskSketcherMessages.h index 7cdc104a7..cdf5f907b 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherMessages.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherMessages.h @@ -47,8 +47,8 @@ public: TaskSketcherMessages(ViewProviderSketch *sketchView); ~TaskSketcherMessages(); - void slotSetUp(int type, int dofs, const std::string &msg); - void slotSolved(int type, float time); + void slotSetUp(QString msg); + void slotSolved(QString msg); private Q_SLOTS: diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index ee289f1d7..4b6cc1988 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -768,7 +768,7 @@ bool ViewProviderSketch::mouseMove(const SbVec3f &point, const SbVec3f &normal, int GeoId; Sketcher::PointPos PosId; getSketchObject()->getGeoVertexIndex(edit->DragPoint, GeoId, PosId); - edit->ActSketch.initMove(GeoId, PosId); + edit->ActSketch.initMove(GeoId, PosId, false); relative = false; xInit = 0; yInit = 0; @@ -785,7 +785,7 @@ bool ViewProviderSketch::mouseMove(const SbVec3f &point, const SbVec3f &normal, edit->PreselectCurve != -1 && edit->DragCurve != edit->PreselectCurve) { Mode = STATUS_SKETCH_DragCurve; edit->DragCurve = edit->PreselectCurve; - edit->ActSketch.initMove(edit->DragCurve, Sketcher::none); + edit->ActSketch.initMove(edit->DragCurve, Sketcher::none, false); const Part::Geometry *geo = getSketchObject()->getGeometry(edit->DragCurve); if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { relative = true; @@ -822,9 +822,9 @@ bool ViewProviderSketch::mouseMove(const SbVec3f &point, const SbVec3f &normal, if (edit->ActSketch.movePoint(GeoId, PosId, vec, relative) == 0) { setPositionText(Base::Vector2D(x,y)); draw(true); - signalSolved(0, edit->ActSketch.SolveTime); + signalSolved(QString::fromLatin1("Solved in %1 sec").arg(edit->ActSketch.SolveTime)); } else { - signalSolved(1, edit->ActSketch.SolveTime); + signalSolved(QString::fromLatin1("Unsolved (%1 sec)").arg(edit->ActSketch.SolveTime)); //Base::Console().Log("Error solving:%d\n",ret); } } @@ -835,9 +835,9 @@ bool ViewProviderSketch::mouseMove(const SbVec3f &point, const SbVec3f &normal, if (edit->ActSketch.movePoint(edit->DragCurve, Sketcher::none, vec, relative) == 0) { setPositionText(Base::Vector2D(x,y)); draw(true); - signalSolved(0, edit->ActSketch.SolveTime); + signalSolved(QString::fromLatin1("Solved in %1 sec").arg(edit->ActSketch.SolveTime)); } else { - signalSolved(1, edit->ActSketch.SolveTime); + signalSolved(QString::fromLatin1("Unsolved (%1 sec)").arg(edit->ActSketch.SolveTime)); } } return true; @@ -2694,42 +2694,7 @@ void ViewProviderSketch::updateData(const App::Property *prop) if (edit && (prop == &(getSketchObject()->Geometry) || &(getSketchObject()->Constraints))) { edit->FullyConstrained = false; - int dofs = edit->ActSketch.setUpSketch(getSketchObject()->getCompleteGeometry(), - getSketchObject()->Constraints.getValues(), - true, getSketchObject()->getExternalGeometryCount()); - std::string msg; - if (getSketchObject()->Geometry.getSize() == 0) { - signalSetUp(-1, 0, msg); - signalSolved(-1, 0); - } - else if (dofs < 0) { // over-constrained sketch - SketchObject::appendConflictMsg(edit->ActSketch.getConflicting(), msg); - //Base::Console().Warning("Over-constrained sketch\n%s",msg.c_str()); - signalSetUp(3, 0, msg); - signalSolved(-1,0); - } - else if (edit->ActSketch.hasConflicts()) { // conflicting constraints - SketchObject::appendConflictMsg(edit->ActSketch.getConflicting(), msg); - //Base::Console().Warning("Sketch with conflicting constraints\n%s",msg.c_str()); - signalSetUp(2, dofs, msg); - signalSolved(-1,0); - } - else if (edit->ActSketch.solve() == 0) { // solving the sketch - if (dofs == 0) { - // color the sketch as fully constrained - edit->FullyConstrained = true; - //Base::Console().Message("Fully constrained sketch\n"); - signalSetUp(0, 0, msg); - } - else { - //Base::Console().Message("Under-constrained sketch with %d degrees of freedom\n", dofs); - signalSetUp(1, dofs, msg); - } - signalSolved(0,edit->ActSketch.SolveTime); - } - else { - signalSolved(1,edit->ActSketch.SolveTime); - } + solveSketch(); draw(true); } if (edit && &(getSketchObject()->Constraints)) { @@ -2831,49 +2796,64 @@ bool ViewProviderSketch::setEdit(int ModNum) else Gui::Control().showDialog(new TaskDlgEditSketch(this)); - // set up the sketch and diagnose possible conflicts - int dofs = edit->ActSketch.setUpSketch(getSketchObject()->getCompleteGeometry(), - getSketchObject()->Constraints.getValues(), - true, getSketchObject()->getExternalGeometryCount()); - std::string msg; - if (getSketchObject()->Geometry.getSize() == 0) { - signalSetUp(-1, 0, msg); - signalSolved(-1, 0); - } - else if (dofs < 0) { // over-constrained sketch - SketchObject::appendConflictMsg(edit->ActSketch.getConflicting(), msg); - //Base::Console().Warning("Over-constrained sketch\n%s",msg.c_str()); - signalSetUp(3, 0, msg); - signalSolved(-1, 0); - } - else if (edit->ActSketch.hasConflicts()) { // conflicting constraints - SketchObject::appendConflictMsg(edit->ActSketch.getConflicting(), msg); - //Base::Console().Warning("Sketch with conflicting constraints\n%s",msg.c_str()); - signalSetUp(2, dofs, msg); - signalSolved(-1, 0); - } - else if (edit->ActSketch.solve() == 0) { // solving the sketch - if (dofs == 0) { - // color the sketch as fully constrained - edit->FullyConstrained = true; - //Base::Console().Message("Fully constrained sketch\n"); - signalSetUp(0, 0, msg); - } - else { - //Base::Console().Message("Under-constrained sketch with %d degrees of freedom\n", dofs); - signalSetUp(1, dofs, msg); - } - signalSolved(0, edit->ActSketch.SolveTime); - } - else { - signalSolved(1, edit->ActSketch.SolveTime); - } - + solveSketch(); draw(); return true; } +void ViewProviderSketch::solveSketch(void) +{ + // set up the sketch and diagnose possible conflicts + int dofs = edit->ActSketch.setUpSketch(getSketchObject()->getCompleteGeometry(), + getSketchObject()->Constraints.getValues(), + getSketchObject()->getExternalGeometryCount()); + if (getSketchObject()->Geometry.getSize() == 0) { + signalSetUp(QString::fromLatin1("Empty sketch")); + signalSolved(QString()); + } + else if (dofs < 0) { // over-constrained sketch + std::string msg; + SketchObject::appendConflictMsg(edit->ActSketch.getConflicting(), msg); + signalSetUp(QString::fromLatin1("Over-constrained sketch
%1
") + .arg(QString::fromStdString(msg))); + signalSolved(QString()); + } + else if (edit->ActSketch.hasConflicts()) { // conflicting constraints + std::string msg; + SketchObject::appendConflictMsg(edit->ActSketch.getConflicting(), msg); + signalSetUp(QString::fromLatin1("Sketch contains conflicting constraints
%1
") + .arg(QString::fromStdString(msg))); + signalSolved(QString()); + } + else { + if (edit->ActSketch.hasRedundancies()) { // redundant constraints + std::string msg; + SketchObject::appendRedundantMsg(edit->ActSketch.getRedundant(), msg); + signalSetUp(QString::fromLatin1("Sketch contains redundant constraints
%1
") + .arg(QString::fromStdString(msg))); + } + if (edit->ActSketch.solve() == 0) { // solving the sketch + if (dofs == 0) { + // color the sketch as fully constrained + edit->FullyConstrained = true; + if (!edit->ActSketch.hasRedundancies()) + signalSetUp(QString::fromLatin1("Fully constrained sketch ")); + } + else if (!edit->ActSketch.hasRedundancies()) { + if (dofs == 1) + signalSetUp(QString::fromLatin1("Under-constrained sketch with 1 degree of freedom")); + else + signalSetUp(QString::fromLatin1("Under-constrained sketch with %1 degrees of freedom").arg(dofs)); + } + signalSolved(QString::fromLatin1("Solved in %1 sec").arg(edit->ActSketch.SolveTime)); + } + else { + signalSolved(QString::fromLatin1("Unsolved (%1 sec)").arg(edit->ActSketch.SolveTime)); + } + } +} + void ViewProviderSketch::createEditInventorNodes(void) { assert(edit); diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index 572547186..2c98d71eb 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -174,15 +174,17 @@ public: /// signals if the constraints list has changed boost::signal signalConstraintsChanged; /// signals if the sketch has been set up - boost::signal signalSetUp; + boost::signal signalSetUp; /// signals if the sketch has been solved - boost::signal signalSolved; + boost::signal signalSolved; protected: virtual bool setEdit(int ModNum); virtual void unsetEdit(int ModNum); virtual void setEditViewer(Gui::View3DInventorViewer*, int ModNum); virtual void unsetEditViewer(Gui::View3DInventorViewer*); + /// set up and solve the sketch + void solveSketch(void); /// helper to detect whether the picked point lies on the sketch bool isPointOnSketch(const SoPickedPoint *pp) const; /// get called by the container whenever a property has been changed