diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index 645ee33621..87569bcf53 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -130,6 +130,85 @@ void Sketch::clear(void) malformedConstraints = false; } +bool Sketch::analyseBlockedGeometry( const std::vector &internalGeoList, + const std::vector &constraintList, + std::vector &onlyblockedGeometry, + std::vector &blockedGeoIds) const +{ + bool isSomethingBlocked = false; + bool doesBlockAffectOtherConstraints = false; + + int geoindex = 0; + for(auto g : internalGeoList) { + if(GeometryFacade::getBlocked(g)) { + // is it only affected by one constraint, the block constraint (and this is driving), or by any other driving constraint ? + bool blockOnly = true; + bool blockisDriving = false; + + for(auto c : constraintList) { + // is block driving + if( c->Type == Sketcher::Block && c->isDriving && c->First == geoindex) + blockisDriving = true; + + if( c->Type != Sketcher::Block && c->isDriving && + (c->First == geoindex || c->Second == geoindex || c->Third == geoindex) ) + blockOnly = false; + } + + if(blockisDriving) { + if(blockOnly) { + onlyblockedGeometry[geoindex] = true; // we pre-fix this geometry + isSomethingBlocked = true; + } + else { + // we will have to pos-analyse the first diagnose result for these geometries + // in order to avoid redundant constraints + isSomethingBlocked = true; + doesBlockAffectOtherConstraints = true; + blockedGeoIds.push_back(geoindex); + } + } + + } + geoindex++; + } + + if(isSomethingBlocked) { + + // look for internal geometry linked IAs + for(auto c : constraintList) { + if(c->Type == InternalAlignment) { + + auto geoit = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),c->Second); + + if(geoit != blockedGeoIds.end() || onlyblockedGeometry[c->Second]) { // internal alignment geometry found, add to list + // check if pre-fix or post-analyses + bool blockAffectedOnly = true; + + for(auto ic : constraintList) { + // there is another driving constraint + if( ic->Type != Sketcher::Block && ic->isDriving && + (ic->First == c->First || ic->Second == c->First || ic->Third == c->First)) + blockAffectedOnly = false; + } + + if(blockAffectedOnly) { + onlyblockedGeometry[c->Second] = true; // we pre-fix this geometry + } + else { + // we will have to post-analyse the first diagnose result for these geometries + // in order to avoid redundant constraints + doesBlockAffectOtherConstraints = true; + blockedGeoIds.push_back(*geoit); + } + } + } + } + } + + return doesBlockAffectOtherConstraints; +} + int Sketch::setUpSketch(const std::vector &GeoList, const std::vector &ConstraintList, int extGeoCount) @@ -144,13 +223,43 @@ int Sketch::setUpSketch(const std::vector &GeoList, for (int i=int(GeoList.size())-extGeoCount; i < int(GeoList.size()); i++) extGeoList.push_back(GeoList[i]); - std::vector blockedGeometry(intGeoList.size(),false); // these geometries are blocked, frozen and sent as fixed parameters to the solver + std::vector onlyBlockedGeometry(intGeoList.size(),false); // these geometries are blocked, frozen and sent as fixed parameters to the solver std::vector unenforceableConstraints(ConstraintList.size(),false); // these constraints are unenforceable due to a Blocked constraint + /* This implements the old block constraint. I have decided not to remove it at this time while the new is tested, just in case the change + * needs to be reverted */ /*if(!intGeoList.empty()) getBlockedGeometry(blockedGeometry, unenforceableConstraints, ConstraintList);*/ - addGeometry(intGeoList,blockedGeometry); + // Pre-analysis of blocked geometry (new block constraint) to fix geometry only affected by a block constraint (see comment in Sketch.h) + std::vector blockedGeoIds; + bool doesBlockAffectOtherConstraints = analyseBlockedGeometry( intGeoList, + ConstraintList, + onlyBlockedGeometry, + blockedGeoIds); + +#ifdef DEBUG_BLOCK_CONSTRAINT + if(doesBlockAffectOtherConstraints) + Base::Console().Log("\n Block interferes with other constraints: Post-analysis required"); + + Base::Console().Log("\nOnlyBlocked GeoIds:"); + size_t i = 0; + for(; i < onlyBlockedGeometry.size(); i++) { + if(onlyBlockedGeometry[i]) + Base::Console().Log("\n GeoId=%d", i); + } + if( i == 0) + Base::Console().Log("\n None"); + + Base::Console().Log("\nNotOnlyBlocked GeoIds:"); + i = 0; + for(; i < blockedGeoIds.size(); i++) + Base::Console().Log("\n GeoId=%d", blockedGeoIds[i]); + if( i == 0) + Base::Console().Log("\n None"); +#endif //DEBUG_BLOCK_CONSTRAINT + + addGeometry(intGeoList,onlyBlockedGeometry); int extStart=Geoms.size(); addGeometry(extGeoList, true); int extEnd=Geoms.size()-1; @@ -165,158 +274,43 @@ int Sketch::setUpSketch(const std::vector &GeoList, GCSsys.declareUnknowns(Parameters); GCSsys.declareDrivenParams(DrivenParameters); GCSsys.initSolution(defaultSolverRedundant); - GCSsys.getConflicting(Conflicting); - GCSsys.getRedundant(Redundant); - GCSsys.getDependentParams(pDependentParametersList); - calculateDependentParametersElements(); + // Post-analysis + // Now that we have all the parameters information, we deal properly with the block constraints if necessary + if(doesBlockAffectOtherConstraints) { - // Now that we have all the parameters information, we deal properly with the block constraint - - bool isSomethingBlocked = false; - std::vector blockedGeoIds; - - int geoindex = 0; - for(auto & g : Geoms) { - if(!g.external && GeometryFacade::getBlocked(g.geo)) { - isSomethingBlocked = true; - blockedGeoIds.push_back(geoindex); - } - geoindex++; - } - - if(isSomethingBlocked) { - - // 0. look for internal geometry linked IAs - for(auto c : ConstraintList) { - if(c->Type == InternalAlignment) { - - auto geoit = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),c->Second); - - if(geoit != blockedGeoIds.end()) { // internal alignment geometry found, add to list - blockedGeoIds.push_back(*geoit); - } - } - } - - - // 1. Look what needs blocking std::vector params_to_block; - std::vector < std::set < double*>> groups; - GCSsys.getDependentParamsGroups(groups); - if(GCSsys.isEmptyDiagnoseMatrix()) { // special case in which no other driving constraint is in the system - for(auto geoid : blockedGeoIds) { - switch(Geoms[geoid].type) { - case Point: - params_to_block.push_back(Points[Geoms[geoid].index].x); - params_to_block.push_back(Points[Geoms[geoid].index].y); - break; - case Line: - Lines[Geoms[geoid].index].PushOwnParams(params_to_block); - break; - case Arc: - Arcs[Geoms[geoid].index].PushOwnParams(params_to_block); - break; - case Circle: - Circles[Geoms[geoid].index].PushOwnParams(params_to_block); - break; - case Ellipse: - Ellipses[Geoms[geoid].index].PushOwnParams(params_to_block); - break; - case ArcOfEllipse: - ArcsOfEllipse[Geoms[geoid].index].PushOwnParams(params_to_block); - break; - case ArcOfHyperbola: - ArcsOfHyperbola[Geoms[geoid].index].PushOwnParams(params_to_block); - break; - case ArcOfParabola: - ArcsOfParabola[Geoms[geoid].index].PushOwnParams(params_to_block); - break; - case BSpline: - BSplines[Geoms[geoid].index].PushOwnParams(params_to_block); - break; - case None: - break; - } + bool unsatisfied_groups = analyseBlockedConstraintDependentParameters(blockedGeoIds, params_to_block); + + // I am unsure if more than one QR iterations are needed with the current implementation. + // + // With previous implementations mostly one QR iteration was enough, but if block constraint is abused, more + // iterations were needed. + int index = 0; + while(unsatisfied_groups) { + // We tried hard not to arrive to an unsatisfied group, so we try harder + // This loop has the advantage that the user will notice increased effort to solve, + // so may understand he is abusing the block constraint, while guaranteing that wrong + // behaviour of the block constraint is not undetected. + + // Another QR iteration + fixParametersAndDiagnose(params_to_block); + + unsatisfied_groups = analyseBlockedConstraintDependentParameters(blockedGeoIds,params_to_block); + + if (debugMode==GCS::IterationLevel) { + Base::Console().Log("Sketcher::setUpSketch()-BlockConstraint-PostAnalysis:%d\n",index); } - + index++; } - else { -#ifdef DEBUG_BLOCK_CONSTRAINT - for(size_t i = 0; i < groups.size(); i++) { - Base::Console().Log("\nDepParams: Group %d:",i); - for(size_t j = 0; j < groups[i].size(); j++) - Base::Console().Log("\n Param=%x ,GeoId=%d, GeoPos=%d", - param2geoelement.find(*std::next(groups[i].begin(), j))->first, - param2geoelement.find(*std::next(groups[i].begin(), j))->second.first, - param2geoelement.find(*std::next(groups[i].begin(), j))->second.second); - } -#endif //DEBUG_BLOCK_CONSTRAINT - - for(size_t i = 0; i < groups.size(); i++) { - for(size_t j = 0; j < groups[i].size(); j++) { - - double * thisparam = *std::next(groups[i].begin(), j); - - auto element = param2geoelement.find(thisparam); - - if (element != param2geoelement.end()) { - - auto blocked = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),element->second.first); - - if( blocked != blockedGeoIds.end()) { - // This dependent parameter group contains a parameter that should be blocked. - // - // One parameter per group is enough to fix the group, but it must be a different one for each group or - // it will create a different dependency group - auto already_in = std::find(params_to_block.begin(),params_to_block.end(), thisparam); - - if( already_in == params_to_block.end()) { - params_to_block.push_back(thisparam); - -#ifdef DEBUG_BLOCK_CONSTRAINT - Base::Console().Log("\nBlocking: Param=%x ,GeoId=%d, GeoPos=%d", - element->first, - element->second.first, - element->second.second); -#endif //DEBUG_BLOCK_CONSTRAINT - - break; - } - } - } - } - } - } // if(!GCSsys.isEmptyDiagnoseMatrix()) // 2. If something needs blocking, block-it - if(params_to_block.size() > 0) { - - for( auto p : params_to_block ) { - auto findparam = std::find(Parameters.begin(),Parameters.end(), p); - - if(findparam != Parameters.end()) { - FixParameters.push_back(*findparam); - Parameters.erase(findparam); - } - - } - - pDependencyGroups.clear(); - clearTemporaryConstraints(); - GCSsys.invalidatedDiagnosis(); - GCSsys.declareUnknowns(Parameters); - GCSsys.declareDrivenParams(DrivenParameters); - GCSsys.initSolution(defaultSolverRedundant); - GCSsys.getConflicting(Conflicting); - GCSsys.getRedundant(Redundant); - GCSsys.getDependentParams(pDependentParametersList); - - calculateDependentParametersElements(); + fixParametersAndDiagnose(params_to_block); #ifdef DEBUG_BLOCK_CONSTRAINT - std::vector < std::set < double*>> groups; + if(params_to_block.size() > 0) { + std::vector < std::vector < double*>> groups; GCSsys.getDependentParamsGroups(groups); // Debug code block @@ -328,10 +322,17 @@ int Sketch::setUpSketch(const std::vector &GeoList, param2geoelement.find(*std::next(groups[i].begin(), j))->second.first, param2geoelement.find(*std::next(groups[i].begin(), j))->second.second); } -#endif //DEBUG_BLOCK_CONSTRAINT } +#endif //DEBUG_BLOCK_CONSTRAINT } + // Now we set the Sketch status with the latest solver information + GCSsys.getConflicting(Conflicting); + GCSsys.getRedundant(Redundant); + GCSsys.getDependentParams(pDependentParametersList); + + calculateDependentParametersElements(); + if (debugMode==GCS::Minimal || debugMode==GCS::IterationLevel) { Base::TimeInfo end_time; @@ -341,6 +342,112 @@ int Sketch::setUpSketch(const std::vector &GeoList, return GCSsys.dofsNumber(); } +void Sketch::fixParametersAndDiagnose(std::vector ¶ms_to_block) +{ + if(params_to_block.size() > 0) { // only there are parameters to fix + for( auto p : params_to_block ) { + auto findparam = std::find(Parameters.begin(),Parameters.end(), p); + + if(findparam != Parameters.end()) { + FixParameters.push_back(*findparam); + Parameters.erase(findparam); + } + } + + pDependencyGroups.clear(); + clearTemporaryConstraints(); + GCSsys.invalidatedDiagnosis(); + GCSsys.declareUnknowns(Parameters); + GCSsys.declareDrivenParams(DrivenParameters); + GCSsys.initSolution(defaultSolverRedundant); + /*GCSsys.getConflicting(Conflicting); + GCSsys.getRedundant(Redundant); + GCSsys.getDependentParams(pDependentParametersList); + + calculateDependentParametersElements();*/ + } +} + +bool Sketch::analyseBlockedConstraintDependentParameters(std::vector &blockedGeoIds, std::vector ¶ms_to_block) const +{ + // 1. Retrieve solver information + std::vector < std::vector < double*>> groups; + GCSsys.getDependentParamsGroups(groups); + + // 2. Determine blockable parameters for each group (see documentation in header file). + struct group { + std::vector blockable_params_in_group; + double * blocking_param_in_group = nullptr; + }; + + std::vector prop_groups(groups.size()); + +#ifdef DEBUG_BLOCK_CONSTRAINT + for(size_t i = 0; i < groups.size(); i++) { + Base::Console().Log("\nDepParams: Group %d:",i); + for(size_t j = 0; j < groups[i].size(); j++) + Base::Console().Log("\n Param=%x ,GeoId=%d, GeoPos=%d", + param2geoelement.find(*std::next(groups[i].begin(), j))->first, + param2geoelement.find(*std::next(groups[i].begin(), j))->second.first, + param2geoelement.find(*std::next(groups[i].begin(), j))->second.second); + } +#endif //DEBUG_BLOCK_CONSTRAINT + + for(size_t i = 0; i < groups.size(); i++) { + for(size_t j = 0; j < groups[i].size(); j++) { + + double * thisparam = *std::next(groups[i].begin(), j); + + auto element = param2geoelement.find(thisparam); + + if (element != param2geoelement.end()) { + + auto blockable = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),element->second.first); + + if( blockable != blockedGeoIds.end()) { + // This dependent parameter group contains at least one parameter that should be blocked, so added to the blockable list. + prop_groups[i].blockable_params_in_group.push_back(thisparam); + } + } + } + } + + // 3. Apply heuristic - pick the last blockable param available to block the group, starting from the last group + for(size_t i = prop_groups.size(); i--> 0;) { + for(size_t j = prop_groups[i].blockable_params_in_group.size(); j-->0; ) { + // check if parameter is already satisfying one group + double * thisparam = prop_groups[i].blockable_params_in_group[j]; + auto pos = std::find(params_to_block.begin(), params_to_block.end(), thisparam); + + if( pos == params_to_block.end()) { // not found, so add + params_to_block.push_back(thisparam); + prop_groups[i].blocking_param_in_group = thisparam; +#ifdef DEBUG_BLOCK_CONSTRAINT + Base::Console().Log("\nTentatively blocking group %d, with param=%x", i, thisparam); +#endif //DEBUG_BLOCK_CONSTRAINT + break; + } + } + } + + // 4. Check if groups are satisfied or are licitly unsatisfiable and thus deemed as satisfied + bool unsatisfied_groups = false; + for(size_t i = 0; i < prop_groups.size(); i++) { + // 4.1. unsatisfiable group + if(prop_groups[i].blockable_params_in_group.size() == 0) { + // this group does not contain any blockable parameter, so it is by definition satisfied (or impossible to satisfy by block constraints) + continue; + } + // 4.2. satisfiable and not satisfied + if(prop_groups[i].blocking_param_in_group == nullptr) { + unsatisfied_groups = true; + } + } + + return unsatisfied_groups; +} + + void Sketch::clearTemporaryConstraints(void) { GCSsys.clearByTag(GCS::DefaultTemporaryConstraint); @@ -390,7 +497,7 @@ void Sketch::calculateDependentParametersElements(void) } } - std::vector < std::set < double*>> groups; + std::vector < std::vector < double*>> groups; GCSsys.getDependentParamsGroups(groups); pDependencyGroups.resize(groups.size()); @@ -399,7 +506,7 @@ void Sketch::calculateDependentParametersElements(void) for(size_t i = 0; i < groups.size(); i++) { for(size_t j = 0; j < groups[i].size(); j++) { - auto element = param2geoelement.find(*std::next(groups[i].begin(), j)); + auto element = param2geoelement.find(groups[i][j]); if (element != param2geoelement.end()) { pDependencyGroups[i].insert(element->second); diff --git a/src/Mod/Sketcher/App/Sketch.h b/src/Mod/Sketcher/App/Sketch.h index be04c5521e..f23150afda 100644 --- a/src/Mod/Sketcher/App/Sketch.h +++ b/src/Mod/Sketcher/App/Sketch.h @@ -499,6 +499,71 @@ private: int checkGeoId(int geoId) const; GCS::Curve* getGCSCurveByGeoId(int geoId); const GCS::Curve* getGCSCurveByGeoId(int geoId) const; + + // Block constraints + + /** This function performs a pre-analysis of blocked geometries, separating them into: + * + * 1) onlyblockedGeometry : Geometries affected exclusively by a block constraint. + * + * 2) blockedGeoIds : Geometries affected notonly by a block constraint. + * + * This is important because 1) can be pre-fixed when creating geometry and constraints + * before GCS::diagnose() via initSolution(). This is important because if no other constraint + * affect the geometry, the geometry parameters won't even appear in the Jacobian, and they won't + * be reported as dependent parameters. + * + * On the contrary 2) cannot be pre-fixed because it would lead to redundant constraints and requires + * a post-analysis, see analyseBlockedConstraintDependentParameters, to fix just the parameters that + * fulfil the dependacy groups. + */ + bool analyseBlockedGeometry( const std::vector &internalGeoList, + const std::vector &constraintList, + std::vector &onlyblockedGeometry, + std::vector &blockedGeoIds) const; + + /* This function performs a post-analysis of blocked geometries (see analyseBlockedGeometry for more detail + * on the pre-analysis). + * + * Basically identifies which parameters shall be fixed to make geometries having blocking constraints fixed, + * while not leading to redundant/conflicting constraints. These parameters must belong to blocked geometry. This + * is, groups may comprise parameters belonging to blocked geometry and parameters belonging to unconstrained geometry. + * It is licit that the latter remain as dependent parameters. The former are referred to as "blockable parameters". + * + * Extending this concept, there may be unsatisfiable groups (because they do not comprise any bloackable parameter), + * and it is the desired outcome NOT to satisfy such groups. + * + * There is not a single combination of fixed parameters from the blockable parameters that satisfy all the dependency + * groups. However: + * + * 1) some combinations do not satisfy all the dependency groups that must be satisfied (e.g. fixing one + * group containing two blockable parameters with a given one may result in another group, fixable only by the former, not + * to be satisfied). This leads, in a subsequent diagnosis, to satisfiable unsatisfied groups. + * + * 2) some combinations lead to partially redundant constraints, that the solver will silently drop in a subsequent diagnosis, + * thereby reducing the rank of the system fixing less than it should. + * + * Implementation rationale (at this time): + * + * The implementation is on the order of the groups provided by the QR decomposition used to reveal the parameters + * (see System::identifyDependentParameters in GCS). Zeros are made over the pilot of the full R matrix of the QR decomposition, + * which is a top triangular matrix.This, together with the permutation matrix, allow to know groups of dependent parameters + * (cols between rank and full size). Each group refers to a new parameter not affected by the rank in combination with other free + * parameters intervening in the rank (because of the triangular shape of the R matrix). This results in that each the first column + * between the rank and the full size, may only depend on a number of parameters, while the last full size colum may dependent on + * any amount of previously introduced parameters. + * + * Thus the rationale is start from the last group (having **potentially** the larger amount of parameters) and selecting as blocking + * for that group the latest blockable parameter. Because previous groups do not have access to the last parameter, this can never + * interfere with previous groups. However, because the last parameter may not be a blockable one, there is a risk of selecting a parameter + * common with other group, albeit the probability is reduced and probably (I have not demonstrated it though and I am not sure), it leads + * to the right solution in one iteration. + * + */ + bool analyseBlockedConstraintDependentParameters(std::vector &blockedGeoIds, std::vector ¶ms_to_block) const; + + /// utility function refactoring fixing the provided parameters and running a new diagnose + void fixParametersAndDiagnose(std::vector ¶ms_to_block); }; } //namespace Part diff --git a/src/Mod/Sketcher/App/planegcs/GCS.cpp b/src/Mod/Sketcher/App/planegcs/GCS.cpp index 16d7974eda..5d43564bba 100644 --- a/src/Mod/Sketcher/App/planegcs/GCS.cpp +++ b/src/Mod/Sketcher/App/planegcs/GCS.cpp @@ -226,6 +226,7 @@ public: void LogQRSystemInformation(const System &system, int paramsNum = 0, int constrNum = 0, int rank = 0); void LogGroupOfConstraints(const std::string & str, std::vector< std::vector > constraintgroups); + void LogGroupOfParameters(const std::string & str, std::vector< std::vector > parametergroups); void LogMatrix(const std::string str, Eigen::MatrixXd matrix); void LogMatrix(const std::string str, MatrixIndexType matrix); @@ -367,6 +368,23 @@ void SolverReportingManager::LogGroupOfConstraints(const std::string & str, std: LogString(tempstream.str()); } +void SolverReportingManager::LogGroupOfParameters(const std::string & str, std::vector< std::vector > parametergroups) +{ + std::stringstream tempstream; + + tempstream << str << ":" << '\n'; + + for(size_t i = 0; i < parametergroups.size(); i++) { + tempstream << "["; + + for(auto p : parametergroups[i]) + tempstream << std::hex << p << " "; + + tempstream << "]" << '\n'; + } + + LogString(tempstream.str()); +} #ifdef _GCS_DEBUG void SolverReportingManager::LogMatrix(const std::string str, Eigen::MatrixXd matrix) @@ -4017,9 +4035,9 @@ SolverReportingManager::Manager().LogToFile("GCS::System::diagnose()\n"); // // Debug: // auto fut = std::async(std::launch::deferred,&System::identifyDependentParametersSparseQR,this,J,jacobianconstraintmap, pdiagnoselist, false); - auto fut = std::async(&System::identifyDependentParametersSparseQR,this,J,jacobianconstraintmap, pdiagnoselist, false); + auto fut = std::async(&System::identifyDependentParametersSparseQR,this,J,jacobianconstraintmap, pdiagnoselist, /*silent=*/true); - makeSparseQRDecomposition( J, jacobianconstraintmap, SqrJT, rank, R); + makeSparseQRDecomposition( J, jacobianconstraintmap, SqrJT, rank, R, /*transposed=*/true, /*silent=*/false); int paramsNum = SqrJT.rows(); int constrNum = SqrJT.cols(); @@ -4260,21 +4278,31 @@ void System::identifyDependentParameters( T & qrJ, if(!silent) SolverReportingManager::Manager().LogMatrix("Rparams_nonzeros_over_pilot", Rparams); #endif + pDependentParametersGroups.resize(qrJ.cols()-rank); for (int j=rank; j < qrJ.cols(); j++) { for (int row=0; row < rank; row++) { if (fabs(Rparams(row,j)) > 1e-10) { int origCol = qrJ.colsPermutation().indices()[row]; - pDependentParametersGroups[j-rank].insert(pdiagnoselist[origCol]); + pDependentParametersGroups[j-rank].push_back(pdiagnoselist[origCol]); pDependentParameters.push_back(pdiagnoselist[origCol]); } } int origCol = qrJ.colsPermutation().indices()[j]; - pDependentParametersGroups[j-rank].insert(pdiagnoselist[origCol]); + pDependentParametersGroups[j-rank].push_back(pdiagnoselist[origCol]); pDependentParameters.push_back(pdiagnoselist[origCol]); } + +#ifdef _GCS_DEBUG + if(!silent) { + SolverReportingManager::Manager().LogMatrix("PermMatrix", (Eigen::MatrixXd)qrJ.colsPermutation()); + + SolverReportingManager::Manager().LogGroupOfParameters("ParameterGroups",pDependentParametersGroups); + } + +#endif } void System::identifyDependentGeometryParametersInTransposedJacobianDenseQRDecomposition( diff --git a/src/Mod/Sketcher/App/planegcs/GCS.h b/src/Mod/Sketcher/App/planegcs/GCS.h index 7c5f3ee916..506fd8f4a8 100644 --- a/src/Mod/Sketcher/App/planegcs/GCS.h +++ b/src/Mod/Sketcher/App/planegcs/GCS.h @@ -106,7 +106,7 @@ namespace GCS // This is a map of primary and secondary identifiers that are found dependent by the solver // GCS ignores from a type point - std::vector< std::set > pDependentParametersGroups; + std::vector< std::vector > pDependentParametersGroups; std::vector clist; std::map c2p; // constraint to parameter adjacency list @@ -363,7 +363,7 @@ namespace GCS { redundantOut = hasDiagnosis ? redundantTags : VEC_I(0); } void getDependentParams(VEC_pD &pdependentparameterlist) const { pdependentparameterlist = pDependentParameters;} - void getDependentParamsGroups(std::vector> &pdependentparametergroups) const + void getDependentParamsGroups(std::vector> &pdependentparametergroups) const { pdependentparametergroups = pDependentParametersGroups;} bool isEmptyDiagnoseMatrix() const {return emptyDiagnoseMatrix;} void invalidatedDiagnosis();