Skip to content

Commit

Permalink
Sketcher: UI Constraint Creation - PointOnObject + Tangency on edge c…
Browse files Browse the repository at this point in the history
…onstraint substitution

============================================================================================

On creation of a constraint from the UI (toolbar/menu):

1. if a PointOnObject constraint preexisted the addition of an
edge-to-edge tangency, substitute it with a point-to-edge tangency.

2. if an edge-to-edge tangency preexisted, addition of a PointOnObject
results in a substitution of the edge-to-edge tangency with an edge-to-curve
tangency.

Bonus:
- Refactor of this with preexisting coincident+tangent substitution.
- Activate both substitutions in continuous constraint addition mode.
  • Loading branch information
abdullahtahiriyo committed Mar 20, 2021
1 parent 477e0d3 commit 3a656d8
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 69 deletions.
253 changes: 185 additions & 68 deletions src/Mod/Sketcher/Gui/CommandConstraints.cpp
Expand Up @@ -642,9 +642,9 @@ bool SketcherGui::checkConstraint(const std::vector< Sketcher::Constraint * > &v
return false;
}


void SketcherGui::doEndpointTangency(Sketcher::SketchObject* Obj, Gui::SelectionObject &selection,
int GeoId1, int GeoId2, PointPos PosId1, PointPos PosId2){
void SketcherGui::doEndpointTangency(Sketcher::SketchObject* Obj,
int GeoId1, int GeoId2, PointPos PosId1, PointPos PosId2)
{
// This code supports simple B-spline endpoint tangency to any other geometric curve
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
Expand All @@ -660,10 +660,16 @@ void SketcherGui::doEndpointTangency(Sketcher::SketchObject* Obj, Gui::Selection
// GeoId1 is the B-spline now
} // end of code supports simple B-spline endpoint tangency

Gui::cmdAppObjectArgs(selection.getObject(), "addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d,%d)) ",
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d,%d)) ",
GeoId1,PosId1,GeoId2,PosId2);
}

void SketcherGui::doEndpointToEdgeTangency( Sketcher::SketchObject* Obj, int GeoId1, PointPos PosId1, int GeoId2)
{
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d)) ",
GeoId1,PosId1,GeoId2);
}

void SketcherGui::notifyConstraintSubstitutions(const QString & message)
{
Gui::Dialog::DlgCheckableMessageBox::showMessage( QObject::tr("Sketcher Constraint Substitution"),
Expand Down Expand Up @@ -2066,6 +2072,10 @@ class CmdSketcherConstrainCoincident : public CmdSketcherConstraint
protected:
virtual void activated(int iMsg);
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
// returns true if a substitution took place
bool substituteConstraintCombinations(SketchObject * Obj,
int GeoId1, PointPos PosId1,
int GeoId2, PointPos PosId2);
};

CmdSketcherConstrainCoincident::CmdSketcherConstrainCoincident()
Expand All @@ -2084,6 +2094,48 @@ CmdSketcherConstrainCoincident::CmdSketcherConstrainCoincident()
allowedSelSequences = {{SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex}};
}

bool CmdSketcherConstrainCoincident::substituteConstraintCombinations(SketchObject * Obj,
int GeoId1, PointPos PosId1,
int GeoId2, PointPos PosId2)
{
// checks for direct and indirect coincidence constraints
bool constraintExists = Obj->arePointsCoincident(GeoId1,PosId1,GeoId2,PosId2);

const std::vector< Constraint * > &cvals = Obj->Constraints.getValues();

int j=0;
for (std::vector<Constraint *>::const_iterator it = cvals.begin(); it != cvals.end(); ++it,++j) {
if( (*it)->Type == Sketcher::Tangent &&
(*it)->FirstPos == Sketcher::none && (*it)->SecondPos == Sketcher::none &&
(*it)->Third == Constraint::GeoUndef &&
(((*it)->First == GeoId1 && (*it)->Second == GeoId2) ||
((*it)->Second == GeoId1 && (*it)->First == GeoId2)) ) {

Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Swap edge tangency with ptp tangency"));

if( constraintExists ) {
// try to remove any pre-existing direct coincident constraints
Gui::cmdAppObjectArgs(Obj, "delConstraintOnPoint(%i,%i)", GeoId1, PosId1);
}

Gui::cmdAppObjectArgs(Obj, "delConstraint(%i)", j);

doEndpointTangency(Obj, GeoId1, GeoId2, PosId1, PosId2);

commitCommand();
Obj->solve(); // The substitution requires a solve() so that the autoremove redundants works when Autorecompute not active.
tryAutoRecomputeIfNotSolve(Obj);

notifyConstraintSubstitutions(QObject::tr("Endpoint to endpoint tangency was applied instead."));

getSelection().clearSelection();
return true;
}
}

return false;
}

void CmdSketcherConstrainCoincident::activated(int iMsg)
{
Q_UNUSED(iMsg);
Expand Down Expand Up @@ -2145,42 +2197,16 @@ void CmdSketcherConstrainCoincident::activated(int iMsg)
return;
}

// check if as a consequence of this command undesirable combinations of constraints would
// arise and substitute them with more appropriate counterparts, examples:
// - coincidence + tangency on edge
// - point on object + tangency on edge
if(substituteConstraintCombinations(Obj, GeoId1, PosId1,GeoId2, PosId2))
return;

// check if this coincidence is already enforced (even indirectly)
bool constraintExists=Obj->arePointsCoincident(GeoId1,PosId1,GeoId2,PosId2);

// check for a preexisting edge-to-edge tangency
const std::vector< Constraint * > &cvals = Obj->Constraints.getValues();

int j=0;
for (std::vector<Constraint *>::const_iterator it = cvals.begin(); it != cvals.end(); ++it,++j) {
if( (*it)->Type == Sketcher::Tangent &&
(*it)->FirstPos == Sketcher::none && (*it)->SecondPos == Sketcher::none &&
(*it)->Third == Constraint::GeoUndef &&
(((*it)->First == GeoId1 && (*it)->Second == GeoId2) ||
((*it)->Second == GeoId1 && (*it)->First == GeoId2)) ) {

Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Swap edge tangency with ptp tangency"));

if(constraintExists) {
// try to remove any pre-existing direct coincident constraints
Gui::cmdAppObjectArgs(Obj, "delConstraintOnPoint(%i,%i)", GeoId1, PosId1);
}

Gui::cmdAppObjectArgs(Obj, "delConstraint(%i)", j);

doEndpointTangency(Obj, selection[0], GeoId1, GeoId2, PosId1, PosId2);

commitCommand();
Obj->solve(); // The substitution requires a solve() so that the autoremove redundants works when Autorecompute not active.
tryAutoRecomputeIfNotSolve(Obj);

notifyConstraintSubstitutions(QObject::tr("Endpoint to endpoint tangency was applied instead."));

getSelection().clearSelection();
return;
}
}

if (!constraintExists) {
constraintsAdded = true;
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
Expand Down Expand Up @@ -2218,6 +2244,13 @@ void CmdSketcherConstrainCoincident::applyConstraint(std::vector<SelIdPair> &sel
return;
}

// check if as a consequence of this command undesirable combinations of constraints would
// arise and substitute them with more appropriate counterparts, examples:
// - coincidence + tangency on edge
// - point on object + tangency on edge
if(substituteConstraintCombinations(Obj, GeoId1, PosId1,GeoId2, PosId2))
return;

// undo command open
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add coincident constraint"));

Expand Down Expand Up @@ -2595,10 +2628,11 @@ class CmdSketcherConstrainPointOnObject : public CmdSketcherConstraint
protected:
virtual void activated(int iMsg);
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
// returns true if a substitution took place
bool substituteConstraintCombinations(SketchObject * Obj,
int GeoId1, PointPos PosId1, int GeoId2);
};

//DEF_STD_CMD_A(CmdSketcherConstrainPointOnObject);

CmdSketcherConstrainPointOnObject::CmdSketcherConstrainPointOnObject()
:CmdSketcherConstraint("Sketcher_ConstrainPointOnObject")
{
Expand All @@ -2619,6 +2653,36 @@ CmdSketcherConstrainPointOnObject::CmdSketcherConstrainPointOnObject()

}

bool CmdSketcherConstrainPointOnObject::substituteConstraintCombinations( SketchObject * Obj,
int GeoId1, PointPos PosId1, int GeoId2)
{
const std::vector< Constraint * > &cvals = Obj->Constraints.getValues();

int cid = 0;
for (std::vector<Constraint *>::const_iterator it = cvals.begin(); it != cvals.end(); ++it, ++cid) {
if( (*it)->Type == Sketcher::Tangent &&
(*it)->FirstPos == Sketcher::none && (*it)->SecondPos == Sketcher::none &&
(*it)->Third == Constraint::GeoUndef &&
(((*it)->First == GeoId1 && (*it)->Second == GeoId2) ||
((*it)->Second == GeoId1 && (*it)->First == GeoId2)) ) {

// NOTE: This function does not either open or commit a command as it is used for group addition
// it relies on such infrastructure being provided by the caller.

Gui::cmdAppObjectArgs(Obj, "delConstraint(%i)", cid);

doEndpointToEdgeTangency(Obj, GeoId1, PosId1, GeoId2);

notifyConstraintSubstitutions(QObject::tr("Endpoint to edge tangency was applied instead."));

getSelection().clearSelection();
return true;
}
}

return false;
}

void CmdSketcherConstrainPointOnObject::activated(int iMsg)
{
Q_UNUSED(iMsg);
Expand Down Expand Up @@ -2689,6 +2753,11 @@ void CmdSketcherConstrainPointOnObject::activated(int iMsg)
continue;
}

if(substituteConstraintCombinations(Obj, points[iPnt].GeoId, points[iPnt].PosId, curves[iCrv].GeoId)) {
cnt++;
continue;
}

cnt++;
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
points[iPnt].GeoId, points[iPnt].PosId, curves[iCrv].GeoId);
Expand Down Expand Up @@ -2773,6 +2842,12 @@ void CmdSketcherConstrainPointOnObject::applyConstraint(std::vector<SelIdPair> &
return;
}

if(substituteConstraintCombinations(Obj, GeoIdVt, PosIdVt, GeoIdCrv)) {
commitCommand();
tryAutoRecompute(Obj);
return;
}

if (allOK) {
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
GeoIdVt, PosIdVt, GeoIdCrv);
Expand Down Expand Up @@ -4090,6 +4165,8 @@ class CmdSketcherConstrainTangent : public CmdSketcherConstraint
protected:
virtual void activated(int iMsg);
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
// returns true if a substitution took place
bool substituteConstraintCombinations(SketchObject * Obj, int GeoId1, int GeoId2);
};

CmdSketcherConstrainTangent::CmdSketcherConstrainTangent()
Expand Down Expand Up @@ -4118,6 +4195,62 @@ CmdSketcherConstrainTangent::CmdSketcherConstrainTangent()
{SelVertexOrRoot, SelVertex} /*Two Endpoints*/ /*No Place for One Endpoint and One Curve*/};
}

bool CmdSketcherConstrainTangent::substituteConstraintCombinations(SketchObject * Obj, int GeoId1, int GeoId2)
{
const std::vector< Constraint * > &cvals = Obj->Constraints.getValues();

int cid = 0;
for (std::vector<Constraint *>::const_iterator it = cvals.begin(); it != cvals.end(); ++it, ++cid) {
if( (*it)->Type == Sketcher::Coincident &&
(((*it)->First == GeoId1 && (*it)->Second == GeoId2) ||
((*it)->Second == GeoId1 && (*it)->First == GeoId2)) ) {

// save values because 'doEndpointTangency' changes the
// constraint property and thus invalidates this iterator
int first = (*it)->First;
int firstpos = static_cast<int>((*it)->FirstPos);

Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Swap coincident+tangency with ptp tangency"));

doEndpointTangency(Obj, (*it)->First, (*it)->Second, (*it)->FirstPos, (*it)->SecondPos);

Gui::cmdAppObjectArgs(Obj, "delConstraintOnPoint(%i,%i)", first, firstpos);

commitCommand();
Obj->solve(); // The substitution requires a solve() so that the autoremove redundants works when Autorecompute not active.
tryAutoRecomputeIfNotSolve(Obj);

notifyConstraintSubstitutions(QObject::tr("Endpoint to endpoint tangency was applied. The coincident constraint was deleted."));

getSelection().clearSelection();
return true;
}
else if( (*it)->Type == Sketcher::PointOnObject &&
(((*it)->First == GeoId1 && (*it)->Second == GeoId2) ||
((*it)->Second == GeoId1 && (*it)->First == GeoId2)) ) {

Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Swap PointOnObject+tangency with point to curve tangency"));

doEndpointToEdgeTangency(Obj, (*it)->First, (*it)->FirstPos, (*it)->Second);

Gui::cmdAppObjectArgs(Obj, "delConstraint(%i)", cid); // remove the preexisting point on object constraint.

commitCommand();

// A substitution requires a solve() so that the autoremove redundants works when Autorecompute not active. However,
// delConstraint includes such solve() internally. So at this point it is already solved.
tryAutoRecomputeIfNotSolve(Obj);

notifyConstraintSubstitutions(QObject::tr("Endpoint to edge tangency was applied. The point on object constraint was deleted."));

getSelection().clearSelection();
return true;
}
}

return false;
}

void CmdSketcherConstrainTangent::activated(int iMsg)
{
Q_UNUSED(iMsg);
Expand Down Expand Up @@ -4244,7 +4377,7 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
}

openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint"));
doEndpointTangency(Obj, selection[0], GeoId1, GeoId2, PosId1, PosId2);
doEndpointTangency(Obj, GeoId1, GeoId2, PosId1, PosId2);
commitCommand();
tryAutoRecompute(Obj);

Expand Down Expand Up @@ -4308,35 +4441,13 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
QObject::tr("Select an edge that is not a B-spline weight"));
return;
}
// check if there is a coincidence constraint on GeoId1, GeoId2
const std::vector< Constraint * > &cvals = Obj->Constraints.getValues();

for (std::vector<Constraint *>::const_iterator it = cvals.begin(); it != cvals.end(); ++it) {
if( (*it)->Type == Sketcher::Coincident &&
(((*it)->First == GeoId1 && (*it)->Second == GeoId2) ||
((*it)->Second == GeoId1 && (*it)->First == GeoId2)) ) {

// save values because 'doEndpointTangency' changes the
// constraint property and thus invalidates this iterator
int first = (*it)->First;
int firstpos = static_cast<int>((*it)->FirstPos);

Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Swap coincident+tangency with ptp tangency"));

doEndpointTangency(Obj, selection[0], (*it)->First, (*it)->Second, (*it)->FirstPos, (*it)->SecondPos);

Gui::cmdAppObjectArgs(Obj, "delConstraintOnPoint(%i,%i)", first, firstpos);

commitCommand();
Obj->solve(); // The substitution requires a solve() so that the autoremove redundants works when Autorecompute not active.
tryAutoRecomputeIfNotSolve(Obj);

notifyConstraintSubstitutions(QObject::tr("Endpoint to endpoint tangency was applied instead."));

getSelection().clearSelection();
return;
}
}
// check if as a consequence of this command undesirable combinations of constraints would
// arise and substitute them with more appropriate counterparts, examples:
// - coincidence + tangency on edge
// - point on object + tangency on edge
if(substituteConstraintCombinations(Obj, GeoId1, GeoId2))
return;

if( geom1 && geom2 &&
( geom1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
Expand Down Expand Up @@ -4529,6 +4640,12 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector<SelIdPair> &selSeq
return;
}

// check if as a consequence of this command undesirable combinations of constraints would
// arise and substitute them with more appropriate counterparts, examples:
// - coincidence + tangency on edge
// - point on object + tangency on edge
if(substituteConstraintCombinations(Obj, GeoId1, GeoId2))
return;

if( geom1 && geom2 &&
( geom1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
Expand Down
6 changes: 5 additions & 1 deletion src/Mod/Sketcher/Gui/CommandConstraints.h
Expand Up @@ -137,10 +137,14 @@ void tryAutoRecomputeIfNotSolve(Sketcher::SketchObject* obj);
bool checkConstraint(const std::vector< Sketcher::Constraint * > &vals, Sketcher::ConstraintType type, int geoid, Sketcher::PointPos pos);

/// Does an endpoint-to-endpoint tangency
void doEndpointTangency(Sketcher::SketchObject* Obj, Gui::SelectionObject &selection, int GeoId1, int GeoId2, Sketcher::PointPos PosId1, Sketcher::PointPos PosId2);
void doEndpointTangency(Sketcher::SketchObject* Obj, int GeoId1, int GeoId2, Sketcher::PointPos PosId1, Sketcher::PointPos PosId2);

/// Does an endpoint-edge tangency
void doEndpointToEdgeTangency( Sketcher::SketchObject* Obj, int GeoId1, Sketcher::PointPos PosId1, int GeoId2);

/// shows constraint substitution information dialog box, enabling the user to forgo further notifications
void notifyConstraintSubstitutions(const QString & message);

}
#endif // SKETCHERGUI_DrawSketchHandler_H

0 comments on commit 3a656d8

Please sign in to comment.