From 0e92e6356fe1778cef3e47b2b3d475bebff68bd0 Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Wed, 3 Jun 2015 17:01:14 +0200 Subject: [PATCH] Sketcher: Major re-structuration: New Solving Model: General Sketch Solve call reduction ======================================================================================= ActSketch in ViewProvider dissapears. The temporal sketch (sketch.cpp) for solving is now a data member of SketchObject.cpp (hereinafter solvedSketch). All the solving is concentrated in SketchObject.cpp. SketchObject provides an interface to expose its solver, as it is still currently needed for some UI operations from ViewProviderSketch, like dragging points (solving rubber bands). ViewProviderSketch still can select whether to draw the solvedSketch geometry (previously ActSketch) geometry (UI staff) or the SketchObject geometry. Performancewise, it makes sense to separate this two geometries, as the SketchObject one involves modifying the Geometry and Constraint Properties (including all the undo related functionality), which would mess the undo functinality and incur in a big peformance penalisation while dragging. One characteristic of solvedSketch is that its geometry is solved, whereas the geometry of SketchObject may not have been solved yet. These geometries may differ at for the following reasons: 1. The geometry corresponds to an ongoing dragging operation, so solvedSketch has the last calculated dragging geometry, while SketchObject has the one corresponding to initial position. 2. Geometry/constraints were added to the sketch, but no solve/execute was triggered yet (envisioned situation is the future group creation functionality not in this commit). What do I gain? - Inserting a (simple) geometry element now costs 1 solver execution. - Inserting a constraint now costs 1 solver executions. For reference, in previous versions inserting a constraint involved 5 solver executions. The following information provide a historical review of the coding of this commit (formed of 10 squashed commits): This is a general sketch solve call reduction, not only during geometry creation (this commit does not include until here any specific measure to reduce calls on geometry creation, that is another branch) After a lot of profiling, it seems that the "cause"(tm) that creates so many solver calls is that every update generates a solving in ViewProviderSketch, regardless of the need for that update, many times with the only aim of providing the DoF for the message dialog and keeping ActSketch in sync with SketchObject in case it is needed (currently UI moving points, constraints,...). Sketch solver is now a data member of SketchObject instead of a temporal object that gets initilized and destroyed. This allows: 1. Potentially more synergy reducing calls to setUpSketch (still to be seen, still to be optimized) 2. Allowing SketchObject to access the latest geometry that has been solved => In future, allow objects that use SketchObject to obtain the latest solved geometry instead the geometry of SketchObject that may still be unsolved. This is relevant for drawing the geometry No more solving in ViewProviderSketch. Solving a Sketch is now an exclusive competence of SketchObject. There is however a lot of cleaning to do in ViewProviderSketch (I mean, not that these commits are making a mess in VPSketch, but that as a consequence of the changes, it should be possible to optimize VPSketch, specially moving and drawing methods) Very useful comment for future developers that may wonder why a solve per constraint just upon addition is necessary. Added a new function to get the size of the geometry of the instance of the solver object (Sketch.cpp). The previous way was to extract the geometry, which is costly and error prone, as you have to delete it afterwards. Inserted comment about the necessity of triggering a Part2D update during edit mode --- src/Mod/PartDesign/Gui/CMakeLists.txt | 1 + src/Mod/Sketcher/App/Sketch.h | 3 + src/Mod/Sketcher/App/SketchObject.cpp | 118 ++++++++++---- src/Mod/Sketcher/App/SketchObject.h | 32 +++- src/Mod/Sketcher/App/SketchObjectPyImp.cpp | 15 +- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 58 ++++--- src/Mod/Sketcher/Gui/CommandSketcherTools.cpp | 4 +- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 150 +++++++++--------- src/Mod/Sketcher/Gui/ViewProviderSketch.h | 12 +- 9 files changed, 254 insertions(+), 139 deletions(-) diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt index 15fc66dd8..06eb433fb 100644 --- a/src/Mod/PartDesign/Gui/CMakeLists.txt +++ b/src/Mod/PartDesign/Gui/CMakeLists.txt @@ -15,6 +15,7 @@ include_directories( ${ZLIB_INCLUDE_DIR} ${PYTHON_INCLUDE_PATH} ${XERCESC_INCLUDE_DIR} + ${EIGEN3_INCLUDE_DIR} ) link_directories(${OCC_LIBRARY_DIR}) diff --git a/src/Mod/Sketcher/App/Sketch.h b/src/Mod/Sketcher/App/Sketch.h index 244db2d19..7a3972ad0 100644 --- a/src/Mod/Sketcher/App/Sketch.h +++ b/src/Mod/Sketcher/App/Sketch.h @@ -315,6 +315,9 @@ public: //see more info in respective function in GCS. double calculateConstraintError(int icstr) { return GCSsys.calculateConstraintErrorByTag(icstr);} + /// Returns the size of the Geometry + int getGeometrySize(void) {return Geoms.size();} + enum GeoType { None = 0, Point = 1, // 1 Point(start), 2 Parameters(x,y) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 7548d950f..3f087acfe 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -82,6 +82,12 @@ SketchObject::SketchObject() ExternalGeo.push_back(HLine); ExternalGeo.push_back(VLine); rebuildVertexIndex(); + + lastDoF=0; + lastHasConflict=false; + lastHasRedundancies=false; + lastSolverStatus=0; + lastSolveTime=0; } SketchObject::~SketchObject() @@ -110,49 +116,69 @@ App::DocumentObjectExecReturn *SketchObject::execute(void) delConstraintsToExternal(); } - Sketch sketch; - - int dofs = sketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), + // We should have an updated Sketcher geometry or this execute should not have happened + // therefore we update our sketch object geometry with the SketchObject one. + // + // set up a sketch (including dofs counting and diagnosing of conflicts) + lastDoF = solvedSketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), getExternalGeometryCount()); - if (dofs < 0) { // over-constrained sketch + lastHasConflict = solvedSketch.hasConflicts(); + lastHasRedundancies = solvedSketch.hasRedundancies(); + lastConflicting=solvedSketch.getConflicting(); + lastRedundant=solvedSketch.getRedundant(); + + if (lastDoF < 0) { // over-constrained sketch std::string msg="Over-constrained sketch\n"; - appendConflictMsg(sketch.getConflicting(), msg); + appendConflictMsg(lastConflicting, msg); return new App::DocumentObjectExecReturn(msg.c_str(),this); } - if (sketch.hasConflicts()) { // conflicting constraints + if (lastHasConflict) { // conflicting constraints std::string msg="Sketch with conflicting constraints\n"; - appendConflictMsg(sketch.getConflicting(), msg); + appendConflictMsg(lastConflicting, msg); return new App::DocumentObjectExecReturn(msg.c_str(),this); } - if (sketch.hasRedundancies()) { // redundant constraints + if (lastHasRedundancies) { // redundant constraints std::string msg="Sketch with redundant constraints\n"; - appendRedundantMsg(sketch.getRedundant(), msg); + appendRedundantMsg(lastRedundant, msg); return new App::DocumentObjectExecReturn(msg.c_str(),this); } - // solve the sketch - if (sketch.solve() != 0) + lastSolverStatus=solvedSketch.solve(); + lastSolveTime=solvedSketch.SolveTime; + + if (lastSolverStatus != 0) return new App::DocumentObjectExecReturn("Solving the sketch failed",this); - std::vector geomlist = sketch.extractGeometry(); + std::vector geomlist = solvedSketch.extractGeometry(); Geometry.setValues(geomlist); for (std::vector::iterator it=geomlist.begin(); it != geomlist.end(); ++it) if (*it) delete *it; - Shape.setValue(sketch.toShape()); + // this is not necessary for sketch representation in edit mode, unless we want to trigger an update of + // the objects that depend on this sketch (like pads) + Shape.setValue(solvedSketch.toShape()); return App::DocumentObject::StdReturn; } -int SketchObject::hasConflicts(void) const +int SketchObject::hasConflicts(void) { + // TODO: This must be reviewed to see if we are not setting an already set geometry again (and calculating again) + // it is unclear if we need to know if there are conflicts of an updated geometry that has not been already solved + // or not. + // set up a sketch (including dofs counting and diagnosing of conflicts) - Sketch sketch; - int dofs = sketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), + /*lastDoF = solvedSketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), getExternalGeometryCount()); - if (dofs < 0) // over-constrained sketch + + lastHasConflict = solvedSketch.hasConflicts(); + lastHasRedundancies = solvedSketch.hasRedundancies(); + lastConflicting=solvedSketch.getConflicting(); + lastRedundant=solvedSketch.getRedundant();*/ + + if (lastDoF < 0) // over-constrained sketch return -2; - if (sketch.hasConflicts()) // conflicting constraints + if (solvedSketch.hasConflicts()) // conflicting constraints return -1; return 0; @@ -160,21 +186,35 @@ int SketchObject::hasConflicts(void) const int SketchObject::solve() { + // We should have an updated Sketcher geometry or this solver should not have happened + // therefore we update our sketch object geometry with the SketchObject one. + // // set up a sketch (including dofs counting and diagnosing of conflicts) - Sketch sketch; - int dofs = sketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), + lastDoF = solvedSketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), getExternalGeometryCount()); + + lastHasConflict = solvedSketch.hasConflicts(); + int err=0; - if (dofs < 0) // over-constrained sketch + if (lastDoF < 0) // over-constrained sketch err = -3; - else if (sketch.hasConflicts()) // conflicting constraints + else if (lastHasConflict) // conflicting constraints err = -3; - else if (sketch.solve() != 0) // solving - err = -2; + else { + lastSolverStatus=solvedSketch.solve(); + if (lastSolverStatus != 0) // solving + err = -2; + } + + lastHasRedundancies = solvedSketch.hasRedundancies(); + + lastConflicting=solvedSketch.getConflicting(); + lastRedundant=solvedSketch.getRedundant(); + lastSolveTime=solvedSketch.SolveTime; if (err == 0) { // set the newly solved geometry - std::vector geomlist = sketch.extractGeometry(); + std::vector geomlist = solvedSketch.extractGeometry(); Geometry.setValues(geomlist); for (std::vector::iterator it = geomlist.begin(); it != geomlist.end(); ++it) if (*it) delete *it; @@ -306,18 +346,30 @@ int SketchObject::toggleDriving(int ConstrId) int SketchObject::movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toPoint, bool relative) { - Sketch sketch; - int dofs = sketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), + // if we are moving a point, we need to start from a solved sketch + // if we have conflicts we can forget about moving + + /*lastDoF = solvedSketch.setUpSketch(getCompleteGeometry(), Constraints.getValues(), getExternalGeometryCount()); - if (dofs < 0) // over-constrained sketch + + lastHasConflict = solvedSketch.hasConflicts(); + lastHasRedundancies = solvedSketch.hasRedundancies(); + lastConflicting=solvedSketch.getConflicting(); + lastRedundant=solvedSketch.getRedundant();*/ + + if (lastDoF < 0) // over-constrained sketch return -1; - if (sketch.hasConflicts()) // conflicting constraints + if (lastHasConflict) // conflicting constraints return -1; // move the point and solve - int ret = sketch.movePoint(GeoId, PosId, toPoint, relative); - if (ret == 0) { - std::vector geomlist = sketch.extractGeometry(); + lastSolverStatus = solvedSketch.movePoint(GeoId, PosId, toPoint, relative); + + // moving the point can not result in a conflict that we did not have + // or a redundancy that we did not have before, or a change of DoF + + if (lastSolverStatus == 0) { + std::vector geomlist = solvedSketch.extractGeometry(); Geometry.setValues(geomlist); //Constraints.acceptGeometry(getCompleteGeometry()); for (std::vector::iterator it=geomlist.begin(); it != geomlist.end(); ++it) { @@ -325,7 +377,7 @@ int SketchObject::movePoint(int GeoId, PointPos PosId, const Base::Vector3d& toP } } - return ret; + return lastSolverStatus; } Base::Vector3d SketchObject::getPoint(int GeoId, PointPos PosId) const diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 89ba9bd63..c8f4bf241 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -32,6 +32,8 @@ #include #include +#include "Sketch.h" + namespace Sketcher { @@ -104,7 +106,7 @@ public: std::vector getCompleteGeometry(void) const; /// returns non zero if the sketch contains conflicting constraints - int hasConflicts(void) const; + int hasConflicts(void); /// solves the sketch and updates the Geometry int solve(); @@ -190,6 +192,23 @@ public: bool evaluateConstraints() const; /// Remove constraints with invalid indexes void validateConstraints(); + + /// gets DoF of last solver execution + int getLastDoF() {return lastDoF;} + /// gets HasConflicts status of last solver execution + bool getLastHasConflicts() {return lastHasConflict;} + /// gets HasRedundancies status of last solver execution + bool getLastHasRedundancies() {return lastHasRedundancies;} + /// gets solver status of last solver execution + int getLastSolverStatus() {return lastSolverStatus;} + /// gets solver SolveTime of last solver execution + float getLastSolveTime() {return lastSolveTime;} + /// gets the conflicting constraints of the last solver execution + const std::vector &getLastConflicting(void) const { return lastConflicting; } + /// gets the redundant constraints of last solver execution + const std::vector &getLastRedundant(void) const { return lastRedundant; } + + Sketch &getSolvedSketch(void) {return solvedSketch;} protected: /// get called by the container when a property has changed @@ -201,6 +220,17 @@ private: std::vector VertexId2GeoId; std::vector VertexId2PosId; + + Sketch solvedSketch; + + int lastDoF; + bool lastHasConflict; + bool lastHasRedundancies; + int lastSolverStatus; + float lastSolveTime; + + std::vector lastConflicting; + std::vector lastRedundant; bool AutoLockTangencyAndPerpty(Constraint* cstr, bool bForce = false, bool bLock = true); }; diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 924c08bc8..7e7d2c5fa 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -241,7 +241,20 @@ PyObject* SketchObjectPy::addConstraint(PyObject *args) return 0; } int ret = this->getSketchObjectPtr()->addConstraint(constr); - this->getSketchObjectPtr()->solve(); + // this solve is necessary because: + // 1. The addition of constraint is part of a command addition + // 2. This solve happens before the command is committed + // 3. A constraint, may effect a geometry change (think of coincident, + // a line's point moves to meet the other line's point + // 4. The transaction is comitted before any other solve, for example + // the one of execute() triggered by a recompute (UpdateActive) is generated. + // 5. Upon "undo", the constraint is removed (it was before the command was committed) + // however, the geometry changed after the command was committed, so the point that + // moved do not go back to the position where it was. + // + // N.B.: However, the solve itself may be inhibited in cases where groups of geometry/constraints + // are added together, because in that case undoing will also make the geometry disappear. + this->getSketchObjectPtr()->solve(); return Py::new_reference_to(Py::Int(ret)); } else if (PyObject_TypeCheck(pcObj, &(PyList_Type)) || diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 3b67757dc..44fee1a8d 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -160,6 +160,7 @@ void openEditDatumDialog(Sketcher::SketchObject* sketch, int ConstrNbr) catch (const Base::Exception& e) { QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what())); Gui::Command::abortCommand(); + Gui::Command::updateActive(); } } } @@ -866,7 +867,7 @@ void CmdSketcherConstrainCoincident::activated(int iMsg) else abortCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes // clear the selection (convenience) getSelection().clearSelection(); @@ -1552,7 +1553,8 @@ void CmdSketcherConstrainParallel::activated(int iMsg) } // finish the transaction and update commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + // clear the selection (convenience) getSelection().clearSelection(); @@ -1673,7 +1675,8 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) } commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + getSelection().clearSelection(); return; @@ -1697,7 +1700,8 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d,%d)) ", selection[0].getFeatName(),GeoId1,PosId1,GeoId2,PosId2); commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + getSelection().clearSelection(); return; } @@ -1719,7 +1723,7 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d)) ", selection[0].getFeatName(),GeoId1,PosId1,GeoId2); commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes getSelection().clearSelection(); return; } @@ -1806,7 +1810,7 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) } commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes getSelection().clearSelection(); return; @@ -1817,7 +1821,7 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Perpendicular',%d,%d)) ", selection[0].getFeatName(),GeoId1,GeoId2); commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes getSelection().clearSelection(); return; } @@ -1945,7 +1949,7 @@ void CmdSketcherConstrainTangent::activated(int iMsg) } commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes getSelection().clearSelection(); return; @@ -1969,7 +1973,7 @@ void CmdSketcherConstrainTangent::activated(int iMsg) Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d,%d)) ", selection[0].getFeatName(),GeoId1,PosId1,GeoId2,PosId2); commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes getSelection().clearSelection(); return; } @@ -1991,7 +1995,7 @@ void CmdSketcherConstrainTangent::activated(int iMsg) Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d)) ", selection[0].getFeatName(),GeoId1,PosId1,GeoId2); commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes getSelection().clearSelection(); return; } @@ -2051,7 +2055,7 @@ void CmdSketcherConstrainTangent::activated(int iMsg) Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ", selection[0].getFeatName(),GeoId1,GeoId2); commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes getSelection().clearSelection(); return; } @@ -2301,13 +2305,14 @@ void CmdSketcherConstrainRadius::activated(int iMsg) } } commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes commitNeeded=false; updateNeeded=false; } catch (const Base::Exception& e) { QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what())); abortCommand(); + updateActive(); } } else { @@ -2327,8 +2332,9 @@ void CmdSketcherConstrainRadius::activated(int iMsg) if(commitNeeded) commitCommand(); - if(updateNeeded) - updateActive(); + //if(updateNeeded) + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + } void CmdSketcherConstrainRadius::updateAction(int mode) @@ -2715,7 +2721,8 @@ void CmdSketcherConstrainEqual::activated(int iMsg) } // finish the transaction and update commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + // clear the selection (convenience) getSelection().clearSelection(); @@ -2795,7 +2802,8 @@ void CmdSketcherConstrainSymmetric::activated(int iMsg) // finish the transaction and update commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + // clear the selection (convenience) getSelection().clearSelection(); @@ -2846,7 +2854,8 @@ void CmdSketcherConstrainSymmetric::activated(int iMsg) // finish the transaction and update commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + // clear the selection (convenience) getSelection().clearSelection(); @@ -2862,7 +2871,8 @@ void CmdSketcherConstrainSymmetric::activated(int iMsg) // finish the transaction and update commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + // clear the selection (convenience) getSelection().clearSelection(); @@ -3003,7 +3013,8 @@ void CmdSketcherConstrainSnellsLaw::activated(int iMsg) }*/ commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + // clear the selection (convenience) getSelection().clearSelection(); @@ -3245,7 +3256,8 @@ void CmdSketcherConstrainInternalAlignment::activated(int iMsg) // finish the transaction and update commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + if(extra_elements){ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Extra elements"), @@ -3401,7 +3413,8 @@ void CmdSketcherConstrainInternalAlignment::activated(int iMsg) // finish the transaction and update commitCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + if(extra_elements){ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Extra elements"), @@ -3540,7 +3553,8 @@ void CmdSketcherToggleDrivingConstraint::activated(int iMsg) else abortCommand(); - updateActive(); + //updateActive(); on adding constraints the updateActive's are unnecessary and introduce extra recomputes + // clear the selection (convenience) getSelection().clearSelection(); diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index 8509328cb..22153ef00 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -477,7 +477,7 @@ void CmdSketcherSelectRedundantConstraints::activated(int iMsg) std::stringstream ss; // get the needed lists and objects - const std::vector< int > &solverredundant = dynamic_cast(doc->getInEdit())->getRedundant(); + const std::vector< int > &solverredundant = dynamic_cast(doc->getInEdit())->getSketchObject()->getLastRedundant(); const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues(); getSelection().clearSelection(); @@ -532,7 +532,7 @@ void CmdSketcherSelectConflictingConstraints::activated(int iMsg) std::stringstream ss; // get the needed lists and objects - const std::vector< int > &solverconflicting = dynamic_cast(doc->getInEdit())->getConflicting(); + const std::vector< int > &solverconflicting = dynamic_cast(doc->getInEdit())->getSketchObject()->getLastConflicting(); const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues(); getSelection().clearSelection(); diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 7d6bf3c29..b5eb47589 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -152,7 +152,7 @@ struct EditData { PreselectCross(-1), blockedPreselection(false), FullyConstrained(false), - //ActSketch(0), + //ActSketch(0), // if you are wondering, it went to SketchObject, accessible via getSketchObject()->getSolvedSketch() EditRoot(0), PointsMaterials(0), CurvesMaterials(0), @@ -183,8 +183,6 @@ struct EditData { bool FullyConstrained; bool visibleBeforeEdit; - // instance of the solver - Sketcher::Sketch ActSketch; // container to track our own selected parts std::set SelPointSet; std::set SelCurvSet; // also holds cross axes at -1 and -2 @@ -997,7 +995,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor preselectChanged = detectPreselection(pp, viewer, cursorPos); delete pp; } - + switch (Mode) { case STATUS_NONE: if (preselectChanged) { @@ -1007,7 +1005,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor } return false; case STATUS_SELECT_Point: - if (!edit->ActSketch.hasConflicts() && + if (!getSketchObject()->getSolvedSketch().hasConflicts() && edit->PreselectPoint != -1 && edit->DragPoint != edit->PreselectPoint) { Mode = STATUS_SKETCH_DragPoint; edit->DragPoint = edit->PreselectPoint; @@ -1015,7 +1013,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor Sketcher::PointPos PosId; getSketchObject()->getGeoVertexIndex(edit->DragPoint, GeoId, PosId); if (GeoId != Sketcher::Constraint::GeoUndef && PosId != Sketcher::none) { - edit->ActSketch.initMove(GeoId, PosId, false); + getSketchObject()->getSolvedSketch().initMove(GeoId, PosId, false); relative = false; xInit = 0; yInit = 0; @@ -1029,11 +1027,11 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor edit->PreselectConstraintSet.clear(); return true; case STATUS_SELECT_Edge: - if (!edit->ActSketch.hasConflicts() && + if (!getSketchObject()->getSolvedSketch().hasConflicts() && edit->PreselectCurve != -1 && edit->DragCurve != edit->PreselectCurve) { Mode = STATUS_SKETCH_DragCurve; edit->DragCurve = edit->PreselectCurve; - edit->ActSketch.initMove(edit->DragCurve, Sketcher::none, false); + getSketchObject()->getSolvedSketch().initMove(edit->DragCurve, Sketcher::none, false); const Part::Geometry *geo = getSketchObject()->getGeometry(edit->DragCurve); if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { relative = true; @@ -1074,12 +1072,12 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor getSketchObject()->getGeoVertexIndex(edit->DragPoint, GeoId, PosId); Base::Vector3d vec(x-xInit,y-yInit,0); if (GeoId != Sketcher::Constraint::GeoUndef && PosId != Sketcher::none) { - if (edit->ActSketch.movePoint(GeoId, PosId, vec, relative) == 0) { + if (getSketchObject()->getSolvedSketch().movePoint(GeoId, PosId, vec, relative) == 0) { setPositionText(Base::Vector2D(x,y)); draw(true); - signalSolved(QString::fromLatin1("Solved in %1 sec").arg(edit->ActSketch.SolveTime)); + signalSolved(QString::fromLatin1("Solved in %1 sec").arg(getSketchObject()->getSolvedSketch().SolveTime)); } else { - signalSolved(QString::fromLatin1("Unsolved (%1 sec)").arg(edit->ActSketch.SolveTime)); + signalSolved(QString::fromLatin1("Unsolved (%1 sec)").arg(getSketchObject()->getSolvedSketch().SolveTime)); //Base::Console().Log("Error solving:%d\n",ret); } } @@ -1088,12 +1086,12 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor case STATUS_SKETCH_DragCurve: if (edit->DragCurve != -1) { Base::Vector3d vec(x-xInit,y-yInit,0); - if (edit->ActSketch.movePoint(edit->DragCurve, Sketcher::none, vec, relative) == 0) { + if (getSketchObject()->getSolvedSketch().movePoint(edit->DragCurve, Sketcher::none, vec, relative) == 0) { setPositionText(Base::Vector2D(x,y)); draw(true); - signalSolved(QString::fromLatin1("Solved in %1 sec").arg(edit->ActSketch.SolveTime)); + signalSolved(QString::fromLatin1("Solved in %1 sec").arg(getSketchObject()->getSolvedSketch().SolveTime)); } else { - signalSolved(QString::fromLatin1("Unsolved (%1 sec)").arg(edit->ActSketch.SolveTime)); + signalSolved(QString::fromLatin1("Unsolved (%1 sec)").arg(getSketchObject()->getSolvedSketch().SolveTime)); } } return true; @@ -1144,8 +1142,9 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2D &toPo int intGeoCount = getSketchObject()->getHighestCurveIndex() + 1; int extGeoCount = getSketchObject()->getExternalGeometryCount(); + // with memory allocation - const std::vector geomlist = edit->ActSketch.extractGeometry(true, true); + const std::vector geomlist = getSketchObject()->getSolvedSketch().extractGeometry(true, true); assert(int(geomlist.size()) == extGeoCount + intGeoCount); assert((Constr->First >= -extGeoCount && Constr->First < intGeoCount) @@ -1156,10 +1155,10 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2D &toPo Base::Vector3d p1(0.,0.,0.), p2(0.,0.,0.); if (Constr->SecondPos != Sketcher::none) { // point to point distance - p1 = edit->ActSketch.getPoint(Constr->First, Constr->FirstPos); - p2 = edit->ActSketch.getPoint(Constr->Second, Constr->SecondPos); + p1 = getSketchObject()->getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); + p2 = getSketchObject()->getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos); } else if (Constr->Second != Constraint::GeoUndef) { // point to line distance - p1 = edit->ActSketch.getPoint(Constr->First, Constr->FirstPos); + p1 = getSketchObject()->getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); const Part::Geometry *geo = GeoById(geomlist, Constr->Second); if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { const Part::GeomLineSegment *lineSeg = dynamic_cast(geo); @@ -1171,7 +1170,7 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2D &toPo } else return; } else if (Constr->FirstPos != Sketcher::none) { - p2 = edit->ActSketch.getPoint(Constr->First, Constr->FirstPos); + p2 = getSketchObject()->getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); } else if (Constr->First != Constraint::GeoUndef) { const Part::Geometry *geo = GeoById(geomlist, Constr->First); if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { @@ -1263,11 +1262,11 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2D &toPo p0 = Base::Vector3d(x,y,0); } } else {//angle-via-point - Base::Vector3d p = edit->ActSketch.getPoint(Constr->Third, Constr->ThirdPos); + Base::Vector3d p = getSketchObject()->getSolvedSketch().getPoint(Constr->Third, Constr->ThirdPos); p0 = Base::Vector3d(p.x, p.y, 0); - dir1 = edit->ActSketch.calculateNormalAtPoint(Constr->First, p.x, p.y); + dir1 = getSketchObject()->getSolvedSketch().calculateNormalAtPoint(Constr->First, p.x, p.y); dir1.RotateZ(-M_PI/2);//convert to vector of tangency by rotating - dir2 = edit->ActSketch.calculateNormalAtPoint(Constr->Second, p.x, p.y); + dir2 = getSketchObject()->getSolvedSketch().calculateNormalAtPoint(Constr->Second, p.x, p.y); dir2.RotateZ(-M_PI/2); } @@ -2254,7 +2253,7 @@ void ViewProviderSketch::updateColor(void) hasMaterial = true; m = dynamic_cast(s->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); } - + if (edit->SelConstraintSet.find(i) != edit->SelConstraintSet.end()) { if (hasDatumLabel) { SoDatumLabel *l = dynamic_cast(s->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); @@ -2263,9 +2262,9 @@ void ViewProviderSketch::updateColor(void) m->diffuseColor = SelectColor; } else if (type == Sketcher::Coincident) { int index; - index = edit->ActSketch.getPointId(constraint->First, constraint->FirstPos) + 1; + index = getSketchObject()->getSolvedSketch().getPointId(constraint->First, constraint->FirstPos) + 1; if (index >= 0 && index < PtNum) pcolor[index] = SelectColor; - index = edit->ActSketch.getPointId(constraint->Second, constraint->SecondPos) + 1; + index = getSketchObject()->getSolvedSketch().getPointId(constraint->Second, constraint->SecondPos) + 1; if (index >= 0 && index < PtNum) pcolor[index] = SelectColor; } else if (type == Sketcher::InternalAlignment) { switch(constraint->AlignmentType) { @@ -2288,7 +2287,7 @@ void ViewProviderSketch::updateColor(void) case EllipseFocus1: case EllipseFocus2: { - int index = edit->ActSketch.getPointId(constraint->First, constraint->FirstPos) + 1; + int index = getSketchObject()->getSolvedSketch().getPointId(constraint->First, constraint->FirstPos) + 1; if (index >= 0 && index < PtNum) pcolor[index] = SelectColor; } break; @@ -2890,7 +2889,7 @@ void ViewProviderSketch::draw(bool temp) const std::vector *geomlist; std::vector tempGeo; if (temp) - tempGeo = edit->ActSketch.extractGeometry(true, true); // with memory allocation + tempGeo = getSketchObject()->getSolvedSketch().extractGeometry(true, true); // with memory allocation else tempGeo = getSketchObject()->getCompleteGeometry(); // without memory allocation geomlist = &tempGeo; @@ -3195,11 +3194,11 @@ Restart: assert(0);//no point found! } while (false); if (temp) - midpos1 = edit->ActSketch.getPoint(ptGeoId, ptPosId); + midpos1 = getSketchObject()->getSolvedSketch().getPoint(ptGeoId, ptPosId); else midpos1 = getSketchObject()->getPoint(ptGeoId, ptPosId); - norm1 = edit->ActSketch.calculateNormalAtPoint(Constr->Second, midpos1.x, midpos1.y); + norm1 = getSketchObject()->getSolvedSketch().calculateNormalAtPoint(Constr->Second, midpos1.x, midpos1.y); norm1.Normalize(); dir1 = norm1; dir1.RotateZ(-M_PI/2.0); @@ -3424,15 +3423,15 @@ Restart: Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.); if (Constr->SecondPos != Sketcher::none) { // point to point distance if (temp) { - pnt1 = edit->ActSketch.getPoint(Constr->First, Constr->FirstPos); - pnt2 = edit->ActSketch.getPoint(Constr->Second, Constr->SecondPos); + pnt1 = getSketchObject()->getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); + pnt2 = getSketchObject()->getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos); } else { pnt1 = getSketchObject()->getPoint(Constr->First, Constr->FirstPos); pnt2 = getSketchObject()->getPoint(Constr->Second, Constr->SecondPos); } } else if (Constr->Second != Constraint::GeoUndef) { // point to line distance if (temp) { - pnt1 = edit->ActSketch.getPoint(Constr->First, Constr->FirstPos); + pnt1 = getSketchObject()->getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); } else { pnt1 = getSketchObject()->getPoint(Constr->First, Constr->FirstPos); } @@ -3448,7 +3447,7 @@ Restart: break; } else if (Constr->FirstPos != Sketcher::none) { if (temp) { - pnt2 = edit->ActSketch.getPoint(Constr->First, Constr->FirstPos); + pnt2 = getSketchObject()->getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); } else { pnt2 = getSketchObject()->getPoint(Constr->First, Constr->FirstPos); } @@ -3522,9 +3521,9 @@ Restart: if (ptPosId != Sketcher::none) break; assert(0);//no point found! } while (false); - pos = edit->ActSketch.getPoint(ptGeoId, ptPosId); + pos = getSketchObject()->getSolvedSketch().getPoint(ptGeoId, ptPosId); - Base::Vector3d norm = edit->ActSketch.calculateNormalAtPoint(Constr->Second, pos.x, pos.y); + Base::Vector3d norm = getSketchObject()->getSolvedSketch().calculateNormalAtPoint(Constr->Second, pos.x, pos.y); norm.Normalize(); Base::Vector3d dir = norm; dir.RotateZ(-M_PI/2.0); @@ -3650,8 +3649,8 @@ Restart: assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); - Base::Vector3d pnt1 = edit->ActSketch.getPoint(Constr->First, Constr->FirstPos); - Base::Vector3d pnt2 = edit->ActSketch.getPoint(Constr->Second, Constr->SecondPos); + Base::Vector3d pnt1 = getSketchObject()->getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); + Base::Vector3d pnt2 = getSketchObject()->getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos); SbVec3f p1(pnt1.x,pnt1.y,zConstr); SbVec3f p2(pnt2.x,pnt2.y,zConstr); @@ -3730,11 +3729,11 @@ Restart: } } } else {//angle-via-point - Base::Vector3d p = edit->ActSketch.getPoint(Constr->Third, Constr->ThirdPos); + Base::Vector3d p = getSketchObject()->getSolvedSketch().getPoint(Constr->Third, Constr->ThirdPos); p0 = SbVec3f(p.x, p.y, 0); - dir1 = edit->ActSketch.calculateNormalAtPoint(Constr->First, p.x, p.y); + dir1 = getSketchObject()->getSolvedSketch().calculateNormalAtPoint(Constr->First, p.x, p.y); dir1.RotateZ(-M_PI/2);//convert to vector of tangency by rotating - dir2 = edit->ActSketch.calculateNormalAtPoint(Constr->Second, p.x, p.y); + dir2 = getSketchObject()->getSolvedSketch().calculateNormalAtPoint(Constr->Second, p.x, p.y); dir2.RotateZ(-M_PI/2); } @@ -4056,8 +4055,21 @@ void ViewProviderSketch::updateData(const App::Property *prop) if (edit && (prop == &(getSketchObject()->Geometry) || prop == &(getSketchObject()->Constraints))) { edit->FullyConstrained = false; - solveSketch(); - draw(true); + + // UpdateData is only called as a consequence of SketchObject having changed + // and triggered from SketchObject::onChanged. Therefore we have to assume that + // SketchObject is updated and solved , or will soon be updated and solved in the + // still to be processed recompute (SketchObject::execute() that will trigger a + // SketchObject::onChanged). + // + // At this point, we do not need to solve the Sketch and we can draw directly from + // the SketchObject geometry. + // + // This SketchObject:: + //solveSketch(); + //draw(true); + UpdateSolverInformation(); // just update the solver window with the last SketchObject solving information + draw(); } if (edit && &(getSketchObject()->Constraints)) { // send the signal for the TaskDlg. @@ -4182,8 +4194,8 @@ bool ViewProviderSketch::setEdit(int ModNum) else Gui::Control().showDialog(new TaskDlgEditSketch(this)); - solveSketch(); - draw(); + getSketchObject()->solve(); // This call to the solver is needed to initialize the DoF and solve time controls + //draw(); is not necessary, because a solve triggers an updateData. return true; } @@ -4225,72 +4237,66 @@ QString ViewProviderSketch::appendRedundantMsg(const std::vector &redundant return msg; } -const std::vector &ViewProviderSketch::getConflicting(void) const +void ViewProviderSketch::UpdateSolverInformation() { - return edit->ActSketch.getConflicting(); -} - -const std::vector &ViewProviderSketch::getRedundant(void) const -{ - return edit->ActSketch.getRedundant(); -} - -void ViewProviderSketch::solveSketch(void) -{ - // set up the sketch and diagnose possible conflicts - int dofs = edit->ActSketch.setUpSketch(getSketchObject()->getCompleteGeometry(), - getSketchObject()->Constraints.getValues(), - getSketchObject()->getExternalGeometryCount()); + // Updates Solver Information with the Last solver execution at SketchObject level + int dofs = getSketchObject()->getLastDoF(); + bool hasConflicts = getSketchObject()->getLastHasConflicts(); + bool hasRedundancies = getSketchObject()->getLastHasRedundancies(); + if (getSketchObject()->Geometry.getSize() == 0) { signalSetUp(tr("Empty sketch")); signalSolved(QString()); } else if (dofs < 0) { // over-constrained sketch std::string msg; - SketchObject::appendConflictMsg(edit->ActSketch.getConflicting(), msg); + SketchObject::appendConflictMsg(getSketchObject()->getLastConflicting(), msg); signalSetUp(QString::fromLatin1("%1%2
%3

") .arg(tr("Over-constrained sketch ")) .arg(tr("(click to select)")) .arg(QString::fromStdString(msg))); signalSolved(QString()); } - else if (edit->ActSketch.hasConflicts()) { // conflicting constraints + else if (hasConflicts) { // conflicting constraints signalSetUp(QString::fromLatin1("%1%2
%3

") .arg(tr("Sketch contains conflicting constraints ")) .arg(tr("(click to select)")) - .arg(appendConflictMsg(edit->ActSketch.getConflicting()))); + .arg(appendConflictMsg(getSketchObject()->getLastConflicting()))); signalSolved(QString()); } else { - if (edit->ActSketch.hasRedundancies()) { // redundant constraints + if (hasRedundancies) { // redundant constraints signalSetUp(QString::fromLatin1("%1%2
%3

") .arg(tr("Sketch contains redundant constraints ")) .arg(tr("(click to select)")) - .arg(appendRedundantMsg(edit->ActSketch.getRedundant()))); + .arg(appendRedundantMsg(getSketchObject()->getLastRedundant()))); } - if (edit->ActSketch.solve() == 0) { // solving the sketch + if (getSketchObject()->getLastSolverStatus() == 0) { if (dofs == 0) { - // color the sketch as fully constrained - edit->FullyConstrained = true; - if (!edit->ActSketch.hasRedundancies()) { + // color the sketch as fully constrained if it has geometry (other than the axes) + if(getSketchObject()->getSolvedSketch().getGeometrySize()>2) + edit->FullyConstrained = true; + + if (!hasRedundancies) { signalSetUp(QString::fromLatin1("%1").arg(tr("Fully constrained sketch"))); } } - else if (!edit->ActSketch.hasRedundancies()) { + else if (!hasRedundancies) { if (dofs == 1) signalSetUp(tr("Under-constrained sketch with 1 degree of freedom")); else signalSetUp(tr("Under-constrained sketch with %1 degrees of freedom").arg(dofs)); } - signalSolved(tr("Solved in %1 sec").arg(edit->ActSketch.SolveTime)); + signalSolved(tr("Solved in %1 sec").arg(getSketchObject()->getLastSolveTime())); } else { - signalSolved(tr("Unsolved (%1 sec)").arg(edit->ActSketch.SolveTime)); + signalSolved(tr("Unsolved (%1 sec)").arg(getSketchObject()->getLastSolveTime())); } } } + void ViewProviderSketch::createEditInventorNodes(void) { assert(edit); @@ -4737,6 +4743,8 @@ bool ViewProviderSketch::onDelete(const std::vector &subList) Base::Console().Error("%s\n", e.what()); } } + + Gui::Command::updateActive(); this->drawConstraintIcons(); this->updateColor(); diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index ac30c83ec..f3cbfb25f 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -217,21 +217,15 @@ public: boost::signal signalSolved; /// signals if the elements list has changed boost::signal signalElementsChanged; - - /** @name Act sketch interface */ - //@{ - const std::vector &getConflicting(void) const; - const std::vector &getRedundant(void) const; - //@} - + protected: virtual bool setEdit(int ModNum); virtual void unsetEdit(int ModNum); virtual void setEditViewer(Gui::View3DInventorViewer*, int ModNum); virtual void unsetEditViewer(Gui::View3DInventorViewer*); void deactivateHandler(); - /// set up and solve the sketch - void solveSketch(void); + /// update solver information based on last solving at SketchObject + void UpdateSolverInformation(void); /// helper to detect whether the picked point lies on the sketch bool isPointOnSketch(const SoPickedPoint *pp) const; /// get called by the container whenever a property has been changed