From 46ab541444bdf4c9a16af3057c043ca4507bc63e Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 21 Jan 2016 15:01:43 +0000 Subject: [PATCH] Add a setting that permits a group to include redundant constraints. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This setting is generally useful, but it especially shines when assembling, since the "same orientation" and "parallel" constraints remove three and two rotational degrees of freedom, which makes them impossible to use with 3d "point on line" constraint that removes two spatial and two rotational degrees of freedom. The setting is not enabled for all imported groups by default because it exhibits some edge case failures. For example: * draw two line segments sharing a point, * constrain lengths of line segments, * constrain line segments perpendicular, * constrain line segments to a 90° angle. This is a truly degenerate case and so it is not considered very important. However, we can fix this later by using Eigen::SparseQR. --- src/file.cpp | 1 + src/generate.cpp | 18 ++++++++---------- src/group.cpp | 5 +++++ src/sketch.h | 2 ++ src/system.cpp | 11 +++++++---- src/textscreens.cpp | 26 ++++++++++++++++++++++++-- src/ui.h | 2 ++ 7 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/file.cpp b/src/file.cpp index 95eefe5..a436a85 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -109,6 +109,7 @@ const SolveSpaceUI::SaveTable SolveSpaceUI::SAVED[] = { { 'g', "Group.visible", 'b', &(SS.sv.g.visible) }, { 'g', "Group.suppress", 'b', &(SS.sv.g.suppress) }, { 'g', "Group.relaxConstraints", 'b', &(SS.sv.g.relaxConstraints) }, + { 'g', "Group.allowRedundant", 'b', &(SS.sv.g.allowRedundant) }, { 'g', "Group.allDimsReference", 'b', &(SS.sv.g.allDimsReference) }, { 'g', "Group.scale", 'f', &(SS.sv.g.scale) }, { 'g', "Group.remap", 'M', &(SS.sv.g.remap) }, diff --git a/src/generate.cpp b/src/generate.cpp index 53cca0b..10d0573 100644 --- a/src/generate.cpp +++ b/src/generate.cpp @@ -469,8 +469,9 @@ void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) { g->solved.remove.Clear(); int how = sys.Solve(g, &(g->solved.dof), &(g->solved.remove), true, andFindFree); - if((how != System::SOLVED_OKAY) || - (how == System::SOLVED_OKAY && g->solved.how != System::SOLVED_OKAY)) + bool isOkay = how == System::SOLVED_OKAY || + (g->allowRedundant && how == System::REDUNDANT_OKAY); + if(!isOkay || (isOkay && !g->IsSolvedOkay())) { TextWindow::ReportHowGroupSolved(g->h); } @@ -478,14 +479,11 @@ void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) { FreeAllTemporary(); } -bool SolveSpaceUI::AllGroupsOkay(void) { - int i; - bool allOk = true; - for(i = 0; i < SK.group.n; i++) { - if(SK.group.elem[i].solved.how != System::SOLVED_OKAY) { - allOk = false; - } +bool SolveSpaceUI::AllGroupsOkay() { + for(int i = 0; i < SK.group.n; i++) { + if(!SK.group.elem[i].IsSolvedOkay()) + return false; } - return allOk; + return true; } diff --git a/src/group.cpp b/src/group.cpp index 2babcba..f736774 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -513,6 +513,11 @@ void Group::Generate(IdList *entity, } } +bool Group::IsSolvedOkay() { + return this->solved.how == System::SOLVED_OKAY || + this->allowRedundant && this->solved.how == System::REDUNDANT_OKAY; +} + void Group::AddEq(IdList *l, Expr *expr, int index) { Equation eq; eq.e = expr; diff --git a/src/sketch.h b/src/sketch.h index c14bd43..23abe5a 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -110,6 +110,7 @@ public: bool visible; bool suppress; bool relaxConstraints; + bool allowRedundant; bool allDimsReference; double scale; @@ -204,6 +205,7 @@ public: static void AddParam(ParamList *param, hParam hp, double v); void Generate(EntityList *entity, ParamList *param); + bool IsSolvedOkay(); void TransformImportedBy(Vector t, Quaternion q); // When a request generates entities from entities, and the source // entities may have come from multiple requests, it's necessary to diff --git a/src/system.cpp b/src/system.cpp index 3a04ec4..4fe704d 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -459,9 +459,12 @@ int System::Solve(Group *g, int *dof, List *bad, goto didnt_converge; } - if(!TestRank()) { - if(andFindBad) FindWhichToRemoveToFixJacobian(g, bad); - return System::REDUNDANT_OKAY; + rankOk = TestRank(); + if(!rankOk) { + if(!g->allowRedundant) { + if(andFindBad) FindWhichToRemoveToFixJacobian(g, bad); + return System::REDUNDANT_OKAY; + } } // This is not the full Jacobian, but any substitutions or single-eq @@ -505,7 +508,7 @@ int System::Solve(Group *g, int *dof, List *bad, pp->known = true; pp->free = p->free; } - return System::SOLVED_OKAY; + return rankOk ? System::SOLVED_OKAY : System::REDUNDANT_OKAY; didnt_converge: SK.constraint.ClearTags(); diff --git a/src/textscreens.cpp b/src/textscreens.cpp index 807113e..fecb8b6 100644 --- a/src/textscreens.cpp +++ b/src/textscreens.cpp @@ -112,7 +112,7 @@ void TextWindow::ShowListOfGroups(void) { std::string s = g->DescriptionString(); bool active = (g->h.v == SS.GW.activeGroup.v); bool shown = g->visible; - bool ok = (g->solved.how == System::SOLVED_OKAY); + bool ok = g->IsSolvedOkay(); bool ref = (g->h.v == Group::HGROUP_REFERENCES.v); Printf(false, "%Bp%Fd " "%Ft%s%Fb%D%f%Ll%s%E " @@ -205,6 +205,8 @@ void TextWindow::ScreenChangeGroupOption(int link, uint32_t v) { case 'r': g->relaxConstraints = !(g->relaxConstraints); break; + case 'e': g->allowRedundant = !(g->allowRedundant); break; + case 'v': g->visible = !(g->visible); break; case 'd': g->allDimsReference = !(g->allDimsReference); break; @@ -405,6 +407,10 @@ void TextWindow::ShowGroupInfo(void) { &TextWindow::ScreenChangeGroupOption, g->relaxConstraints ? CHECK_TRUE : CHECK_FALSE); + Printf(false, " %f%Le%Fd%s allow redundant constraints", + &TextWindow::ScreenChangeGroupOption, + g->allowRedundant ? CHECK_TRUE : CHECK_FALSE); + Printf(false, " %f%Ld%Fd%s treat all dimensions as reference", &TextWindow::ScreenChangeGroupOption, g->allDimsReference ? CHECK_TRUE : CHECK_FALSE); @@ -459,9 +465,18 @@ list_items: // what failed, and (if the problem is a singular Jacobian) a list of // constraints that could be removed to fix it. //----------------------------------------------------------------------------- +void TextWindow::ScreenAllowRedundant(int link, uint32_t v) { + SS.UndoRemember(); + + Group *g = SK.GetGroup(SS.TW.shown.group); + g->allowRedundant = true; + + SS.TW.shown.screen = SCREEN_GROUP_INFO; + SS.TW.Show(); +} void TextWindow::ShowGroupSolveInfo(void) { Group *g = SK.group.FindById(shown.group); - if(g->solved.how == System::SOLVED_OKAY) { + if(g->IsSolvedOkay()) { // Go back to the default group info screen shown.screen = SCREEN_GROUP_INFO; Show(); @@ -504,6 +519,13 @@ void TextWindow::ShowGroupSolveInfo(void) { Printf(true, "It may be possible to fix the problem "); Printf(false, "by selecting Edit -> Undo."); + + if(g->solved.how == System::REDUNDANT_OKAY) { + Printf(true, "It is possible to suppress this error "); + Printf(false, "by %Fl%f%Llallowing redundant constraints%E in ", + &TextWindow::ScreenAllowRedundant); + Printf(false, "this group."); + } } //----------------------------------------------------------------------------- diff --git a/src/ui.h b/src/ui.h index 705a2e8..028cba7 100644 --- a/src/ui.h +++ b/src/ui.h @@ -278,6 +278,8 @@ public: static void ScreenChangeCanvasSize(int link, uint32_t v); static void ScreenChangeShadedTriangles(int link, uint32_t v); + static void ScreenAllowRedundant(int link, uint32_t v); + static void ScreenStepDimSteps(int link, uint32_t v); static void ScreenStepDimFinish(int link, uint32_t v); static void ScreenStepDimGo(int link, uint32_t v);