Sketcher: Geometry state synchronisation

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

The geometry state stored in the geometryFacade is modified following a mutable model
(without setting the Geometry property on Constraint change), in order to avoid coupling
the addition/removal of a constraint with a change of the Geometry Property.

This design decision however interferes with the ability of the Geometry property to restore
the correct geometry state upon redo/undo.

While such a situation is rare in the case of Internal Alignment geometry, because constraint
addition/removal is performed with the corresponding geometry addition/removal (within the same
transaction. That is not the case with the Block constraint (or another future general case where
the geometry state may be applied).

This commit leverages the synchronisation mechanism already in use for non-properties (e.g. external geometry or
vertex indices) to check and synchronise geometry state upon undo/redo and restore.

Bonus:
- addGeometryState is refactored to separate the checking logic from the setting logic.
This commit is contained in:
Abdullah Tahiri
2021-02-09 07:48:27 +01:00
parent 41a40c1c31
commit 36fdbc460a
2 changed files with 109 additions and 81 deletions

View File

@@ -1179,85 +1179,16 @@ void SketchObject::addGeometryState(const Constraint* cstr) const
{
const std::vector< Part::Geometry * > &vals = getInternalGeometry();
if(cstr->Type == InternalAlignment) {
Sketcher::InternalType::InternalType constraintInternalAlignment = InternalType::None;
bool constraintBlockedState = false;
switch(cstr->AlignmentType){
case Undef:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::None);
break;
}
case EllipseMajorDiameter:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::EllipseMajorDiameter);
break;
}
case EllipseMinorDiameter:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::EllipseMinorDiameter);
break;
}
case EllipseFocus1:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::EllipseFocus1);
break;
}
case EllipseFocus2:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::EllipseFocus2);
break;
}
case HyperbolaMajor:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::HyperbolaMajor);
break;
}
case HyperbolaMinor:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::HyperbolaMinor);
break;
}
case HyperbolaFocus:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::HyperbolaFocus);
break;
}
case ParabolaFocus:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::ParabolaFocus);
break;
}
case BSplineControlPoint:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::BSplineControlPoint);
// handle constraint as adimensional
break;
}
case BSplineKnotPoint:
{
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setInternalType(InternalType::BSplineKnotPoint);
break;
}
}
}
// Assign Blocked geometry mode
if(cstr->Type == Block){
if (getInternalTypeState(cstr, constraintInternalAlignment)) {
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setBlocked();
gf->setInternalType(constraintInternalAlignment);
}
else if (getBlockedState(cstr, constraintBlockedState)) {
auto gf = GeometryFacade::getFacade(vals[cstr->First]);
gf->setBlocked(constraintBlockedState);
}
}
@@ -7669,9 +7600,93 @@ void SketchObject::onUndoRedoFinished()
// such a recompute
Constraints.checkConstraintIndices(getHighestCurveIndex(),-getExternalGeometryCount()); // in case it is redoing an operation with invalid data.
acceptGeometry();
synchroniseGeometryState();
solve();
}
void SketchObject::synchroniseGeometryState()
{
const std::vector< Part::Geometry * > &vals = getInternalGeometry();
for(size_t i = 0 ; i < vals.size() ; i++) {
auto gf = GeometryFacade::getFacade(vals[i]);
auto facadeInternalAlignment = gf->getInternalType();
auto facadeBlockedState = gf->getBlocked();
Sketcher::InternalType::InternalType constraintInternalAlignment = InternalType::None;
bool constraintBlockedState = false;
for (auto cstr : Constraints.getValues()) {
getInternalTypeState(cstr, constraintInternalAlignment);
getBlockedState(cstr, constraintBlockedState);
}
if(constraintInternalAlignment != facadeInternalAlignment)
gf->setInternalType(constraintInternalAlignment);
if(constraintBlockedState != facadeBlockedState)
gf->setBlocked(constraintBlockedState);
}
}
bool SketchObject::getInternalTypeState(const Constraint * cstr, Sketcher::InternalType::InternalType & internaltypestate) const
{
if(cstr->Type == InternalAlignment) {
switch(cstr->AlignmentType){
case Undef:
internaltypestate = InternalType::None;
break;
case EllipseMajorDiameter:
internaltypestate = InternalType::EllipseMajorDiameter;
break;
case EllipseMinorDiameter:
internaltypestate = InternalType::EllipseMinorDiameter;
break;
case EllipseFocus1:
internaltypestate = InternalType::EllipseFocus1;
break;
case EllipseFocus2:
internaltypestate = InternalType::EllipseFocus2;
break;
case HyperbolaMajor:
internaltypestate = InternalType::HyperbolaMajor;
break;
case HyperbolaMinor:
internaltypestate = InternalType::HyperbolaMinor;
break;
case HyperbolaFocus:
internaltypestate = InternalType::HyperbolaFocus;
break;
case ParabolaFocus:
internaltypestate = InternalType::ParabolaFocus;
break;
case BSplineControlPoint:
internaltypestate = InternalType::BSplineControlPoint;
break;
case BSplineKnotPoint:
internaltypestate = InternalType::BSplineKnotPoint;
break;
}
return true;
}
return false;
}
bool SketchObject::getBlockedState(const Constraint * cstr, bool & blockedstate) const
{
if(cstr->Type == Block) {
blockedstate = true;
return true;
}
return false;
}
void SketchObject::onDocumentRestored()
{
try {
@@ -7690,6 +7705,7 @@ void SketchObject::restoreFinished()
validateExternalLinks();
rebuildExternalGeometry();
Constraints.acceptGeometry(getCompleteGeometry());
synchroniseGeometryState();
// this may happen when saving a sketch directly in edit mode
// but never performed a recompute before
if (Shape.getValue().IsNull() && hasConflicts() == 0) {

View File

@@ -65,8 +65,8 @@ public:
/// Property
/**
The Geometry list contains the non-external Part::Geometry objects in the sketch. The list
may be accessed directly, or indirectly via getInternalGeometry().
The Geometry list contains the non-external Part::Geometry objects in the sketch. The list
may be accessed directly, or indirectly via getInternalGeometry().
Many of the methods in this class take geoId and posId parameters. A GeoId is a unique identifier for
geometry in the Sketch. geoId >= 0 means an index in the Geometry list. geoId < 0 refers to sketch
@@ -242,7 +242,7 @@ public:
\param radius - fillet radius
\param trim - if false, leaves the original lines untouched
\param createCorner - keep geoId/pos as a Point and keep as many constraints as possible
\retval - 0 on success, -1 on failure
\retval - 0 on success, -1 on failure
*/
int fillet(int geoId, PointPos pos, double radius, bool trim=true, bool preserveCorner=false);
/*!
@@ -253,7 +253,7 @@ public:
\param trim - if false, leaves the original lines untouched
\param preserveCorner - if the lines are coincident, place a Point where they meet and keep as many
of the existing constraints as possible
\retval - 0 on success, -1 on failure
\retval - 0 on success, -1 on failure
*/
int fillet(int geoId1, int geoId2,
const Base::Vector3d& refPnt1, const Base::Vector3d& refPnt2,
@@ -544,6 +544,18 @@ protected:
// retrieves redundant, conflicting and malformed constraint information from the solver
void retrieveSolverDiagnostics();
// retrieves whether a geometry blocked state corresponds to this constraint
// returns true of the constraint is of Block type, false otherwise
bool getBlockedState(const Constraint * cstr, bool & blockedstate) const;
// retrieves the geometry blocked state corresponding to this constraint
// returns true of the constraint is of InternalAlignment type, false otherwise
bool getInternalTypeState(const Constraint * cstr, Sketcher::InternalType::InternalType & internaltypestate) const;
// Checks whether the geometry state stored in the geometry extension matches the current sketcher situation (e.g. constraints)
// and corrects the state if not matching.
void synchroniseGeometryState();
private:
/// Flag to allow external geometry from other bodies than the one this sketch belongs to
bool allowOtherBody;