Skip to content

Commit

Permalink
Turn newly created redundant constraints with a label into references.
Browse files Browse the repository at this point in the history
This is a fairly standard CAD feature; it conveys the same
information and has the same recovery path, without erroring out,
so seems like an obvious win.
  • Loading branch information
Evil-Spirit authored and whitequark committed Jan 12, 2017
1 parent c00ab25 commit 43db220
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 23 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ New sketch features:
drag the point from the source sketch.
* When dragging an arc or rectangle point, it will be automatically
constrained to other points with a click.
* When adding a constraint which has a label and is redundant with another
constraint, the constraint is added as a reference, avoiding an error.

New export/import features:
* Three.js: allow configuring projection for exported model, and initially
Expand Down
8 changes: 8 additions & 0 deletions src/constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,14 @@ void Constraint::MenuConstrain(Command id) {
default: ssassert(false, "Unexpected menu ID");
}

if(SK.constraint.FindByIdNoOops(c.h)) {
Constraint *constraint = SK.GetConstraint(c.h);
if(SS.TestRankForGroup(c.group) == SolveResult::REDUNDANT_OKAY &&
constraint->HasLabel()) {
constraint->reference = true;
}
}

SS.GW.ClearSelection();
InvalidateGraphics();
}
Expand Down
16 changes: 15 additions & 1 deletion src/generate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ void SolveSpaceUI::SolveGroupAndReport(hGroup hg, bool andFindFree) {
}
}

void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) {
int i;
// Clear out the system to be solved.
sys.entity.Clear();
Expand Down Expand Up @@ -528,6 +528,11 @@ void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
}

MarkDraggedParams();
}

void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
WriteEqSystemForGroup(hg);
Group *g = SK.GetGroup(hg);
g->solved.remove.Clear();
SolveResult how = sys.Solve(g, &(g->solved.dof),
&(g->solved.remove),
Expand All @@ -541,6 +546,15 @@ void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
FreeAllTemporary();
}

SolveResult SolveSpaceUI::TestRankForGroup(hGroup hg) {
WriteEqSystemForGroup(hg);
Group *g = SK.GetGroup(hg);
SolveResult result = sys.SolveRank(g, NULL, NULL, false, false,
/*forceDofCheck=*/!g->dofCheckOk);
FreeAllTemporary();
return result;
}

bool SolveSpaceUI::ActiveGroupsOkay() {
for(int i = 0; i < SK.groupOrder.n; i++) {
Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
Expand Down
8 changes: 8 additions & 0 deletions src/solvespace.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,15 @@ class System {

bool NewtonSolve(int tag);

void MarkParamsFree(bool findFree);
int CalculateDof();

SolveResult Solve(Group *g, int *dof, List<hConstraint> *bad,
bool andFindBad, bool andFindFree, bool forceDofCheck = false);

SolveResult SolveRank(Group *g, int *dof, List<hConstraint> *bad,
bool andFindBad, bool andFindFree, bool forceDofCheck = false);

void Clear();
};

Expand Down Expand Up @@ -847,6 +853,8 @@ class SolveSpaceUI {
bool genForBBox = false);
void SolveGroup(hGroup hg, bool andFindFree);
void SolveGroupAndReport(hGroup hg, bool andFindFree);
SolveResult TestRankForGroup(hGroup hg);
void WriteEqSystemForGroup(hGroup hg);
void MarkDraggedParams();
void ForceReferences();

Expand Down
86 changes: 64 additions & 22 deletions src/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,28 +475,8 @@ SolveResult System::Solve(Group *g, int *dof, List<hConstraint> *bad,
// This is not the full Jacobian, but any substitutions or single-eq
// solves removed one equation and one unknown, therefore no effect
// on the number of DOF.
if(dof) *dof = mat.n - mat.m;

// If requested, find all the free (unbound) variables. This might be
// more than the number of degrees of freedom. Don't always do this,
// because the display would get annoying and it's slow.
for(i = 0; i < param.n; i++) {
Param *p = &(param.elem[i]);
p->free = false;

if(andFindFree) {
if(p->tag == 0) {
p->tag = VAR_DOF_TEST;
WriteJacobian(0);
EvalJacobian();
int rank = CalculateRank();
if(rank == mat.m) {
p->free = true;
}
p->tag = 0;
}
}
}
if(dof) *dof = CalculateDof();
MarkParamsFree(andFindFree);
}
// System solved correctly, so write the new values back in to the
// main parameter table.
Expand Down Expand Up @@ -537,9 +517,71 @@ SolveResult System::Solve(Group *g, int *dof, List<hConstraint> *bad,
return rankOk ? SolveResult::DIDNT_CONVERGE : SolveResult::REDUNDANT_DIDNT_CONVERGE;
}

SolveResult System::SolveRank(Group *g, int *dof, List<hConstraint> *bad,
bool andFindBad, bool andFindFree, bool forceDofCheck)
{
WriteEquationsExceptFor(Constraint::NO_CONSTRAINT, g);

// All params and equations are assigned to group zero.
param.ClearTags();
eq.ClearTags();

if(!forceDofCheck) {
SolveBySubstitution();
}

// Now write the Jacobian, and do a rank test; that
// tells us if the system is inconsistently constrained.
if(!WriteJacobian(0)) {
return SolveResult::TOO_MANY_UNKNOWNS;
}

bool rankOk = TestRank();
if(!rankOk) {
if(!g->allowRedundant) {
if(andFindBad) FindWhichToRemoveToFixJacobian(g, bad, forceDofCheck);
}
} else {
// This is not the full Jacobian, but any substitutions or single-eq
// solves removed one equation and one unknown, therefore no effect
// on the number of DOF.
if(dof) *dof = CalculateDof();
MarkParamsFree(andFindFree);
}
return rankOk ? SolveResult::OKAY : SolveResult::REDUNDANT_OKAY;
}

void System::Clear() {
entity.Clear();
param.Clear();
eq.Clear();
dragged.Clear();
}

void System::MarkParamsFree(bool find) {
// If requested, find all the free (unbound) variables. This might be
// more than the number of degrees of freedom. Don't always do this,
// because the display would get annoying and it's slow.
for(int i = 0; i < param.n; i++) {
Param *p = &(param.elem[i]);
p->free = false;

if(find) {
if(p->tag == 0) {
p->tag = VAR_DOF_TEST;
WriteJacobian(0);
EvalJacobian();
int rank = CalculateRank();
if(rank == mat.m) {
p->free = true;
}
p->tag = 0;
}
}
}
}

int System::CalculateDof() {
return mat.n - mat.m;
}

0 comments on commit 43db220

Please sign in to comment.