From: azv Date: Tue, 27 May 2014 08:29:27 +0000 (+0400) Subject: Small refactoring of SketchSolver. X-Git-Tag: V_0.4.4~341^2~3^2~1 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=d3eb64c1bdb0d5ab02a5712ecf473b57ac3f8f9c;p=modules%2Fshaper.git Small refactoring of SketchSolver. SketchSolver_ConstraintGroup was detached into separate file. --- diff --git a/src/SketchSolver/CMakeLists.txt b/src/SketchSolver/CMakeLists.txt index 7c60c7f2e..2178882ab 100644 --- a/src/SketchSolver/CMakeLists.txt +++ b/src/SketchSolver/CMakeLists.txt @@ -5,12 +5,14 @@ SET(PROJECT_HEADERS SketchSolver.h SketchSolver_Solver.h SketchSolver_Constraint.h + SketchSolver_ConstraintGroup.h SketchSolver_ConstraintManager.h ) SET(PROJECT_SOURCES SketchSolver_Solver.cpp SketchSolver_Constraint.cpp + SketchSolver_ConstraintGroup.cpp SketchSolver_ConstraintManager.cpp ) diff --git a/src/SketchSolver/SketchSolver_ConstraintGroup.cpp b/src/SketchSolver/SketchSolver_ConstraintGroup.cpp new file mode 100644 index 000000000..1cbeafc89 --- /dev/null +++ b/src/SketchSolver/SketchSolver_ConstraintGroup.cpp @@ -0,0 +1,844 @@ +// File: SketchSolver_ConstraintGroup.cpp +// Created: 27 May 2014 +// Author: Artem ZHIDKOV + +#include "SketchSolver_ConstraintGroup.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +/// Tolerance for value of parameters +const double tolerance = 1.e-10; + +/// This value is used to give unique index to the groups +static Slvs_hGroup myGroupIndexer = 0; + +/** \brief Search the entity/parameter with specified ID in the list of elements + * \param[in] theEntityID unique ID of the element + * \param[in] theEntities list of elements + * \return position of the found element or -1 if the element is not found + */ +template +static int Search(const uint32_t& theEntityID, const std::vector& theEntities); + + +// ======================================================== +// ========= SketchSolver_ConstraintGroup =============== +// ======================================================== + +SketchSolver_ConstraintGroup:: + SketchSolver_ConstraintGroup(boost::shared_ptr theWorkplane) + : myID(++myGroupIndexer), + myParamMaxID(0), + myEntityMaxID(0), + myConstrMaxID(0), + myConstraintMap(), + myNeedToSolve(false), + myConstrSolver() +{ + myParams.clear(); + myEntities.clear(); + myConstraints.clear(); + + // Initialize workplane + myWorkplane.h = SLVS_E_UNKNOWN; +#ifndef NDEBUG + assert(addWorkplane(theWorkplane)); +#else + addWorkplane(theWorkplane); +#endif +} + +SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup() +{ + myParams.clear(); + myEntities.clear(); + myConstraints.clear(); + myConstraintMap.clear(); + + // If the group with maximal identifier is deleted, decrease the indexer + if (myID == myGroupIndexer) + myGroupIndexer--; +} + +// ============================================================================ +// Function: isBaseWorkplane +// Class: SketchSolver_ConstraintGroup +// Purpose: verify the group is based on the given workplane +// ============================================================================ +bool SketchSolver_ConstraintGroup::isBaseWorkplane( + boost::shared_ptr theWorkplane) const +{ + return theWorkplane == mySketch; +} + +// ============================================================================ +// Function: isInteract +// Class: SketchSolver_ConstraintGroup +// Purpose: verify are there any entities in the group used by given constraint +// ============================================================================ +bool SketchSolver_ConstraintGroup::isInteract( + boost::shared_ptr theConstraint) const +{ + // Check the group is empty + if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty()) + return true; + + // Go through constraint entities and verify if some of them already in the group + for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++) + { + boost::shared_ptr aCAttrRef = + boost::dynamic_pointer_cast( + theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i]) + ); + if (!aCAttrRef) continue; + if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end()) + return true; + } + + // Entities did not found + return false; +} + +// ============================================================================ +// Function: changeConstraint +// Class: SketchSolver_ConstraintGroup +// Purpose: create/update the constraint in the group +// ============================================================================ +bool SketchSolver_ConstraintGroup::changeConstraint( + boost::shared_ptr theConstraint) +{ + // There is no workplane yet, something wrong + if (myWorkplane.h == SLVS_E_UNKNOWN) + return false; + + // Search this constraint in the current group to update it + std::map, Slvs_hConstraint>::const_iterator + aConstrMapIter = myConstraintMap.find(theConstraint); + std::vector::iterator aConstrIter; + if (aConstrMapIter != myConstraintMap.end()) + { + int aConstrPos = Search(aConstrMapIter->second, myConstraints); + aConstrIter = myConstraints.begin() + aConstrPos; + } + + // Get constraint type and verify the constraint parameters are correct + SketchSolver_Constraint aConstraint(theConstraint); + int aConstrType = aConstraint.getType(); + if (aConstrType == SLVS_C_UNKNOWN || + (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType)) + return false; + const std::vector& aConstraintAttributes = aConstraint.getAttributes(); + + // Create constraint parameters + double aDistance = 0.0; // scalar value of the constraint + boost::shared_ptr aDistAttr = + boost::dynamic_pointer_cast(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE)); + if (aDistAttr) + { + aDistance = aDistAttr->value(); + if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance) + { + myNeedToSolve = true; + aConstrIter->valA = aDistance; + } + } + + Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint + for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++) + { + aConstrEnt[indAttr] = SLVS_E_UNKNOWN; + boost::shared_ptr aConstrAttr = + boost::dynamic_pointer_cast( + theConstraint->data()->attribute(aConstraintAttributes[indAttr]) + ); + if (!aConstrAttr) continue; + aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr()); + } + + if (aConstrMapIter == myConstraintMap.end()) + { + // Create SolveSpace constraint structure + Slvs_Constraint aConstraint = + Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h, + aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]); + myConstraints.push_back(aConstraint); + myConstraintMap[theConstraint] = aConstraint.h; + } + return true; +} + +// ============================================================================ +// Function: changeEntity +// Class: SketchSolver_ConstraintGroup +// Purpose: create/update the element affected by any constraint +// ============================================================================ +Slvs_hEntity SketchSolver_ConstraintGroup::changeEntity( + boost::shared_ptr theEntity) +{ + // If the entity is already in the group, try to find it + std::map, Slvs_hEntity>::const_iterator + aEntIter = myEntityMap.find(theEntity); + std::vector::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise + if (aEntIter == myEntityMap.end()) // no such entity => should be created + aParamIter = myParams.end(); + else + { // the entity already exists + int aEntPos = Search(aEntIter->second, myEntities); + int aParamPos = Search(myEntities[aEntPos].param[0], myParams); + aParamIter = myParams.begin() + aParamPos; + } + + // Look over supported types of entities + + // Point in 3D + boost::shared_ptr aPoint = + boost::dynamic_pointer_cast(theEntity); + if (aPoint) + { + Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter); + Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter); + Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter); + + if (aEntIter != myEntityMap.end()) // the entity already exists + return aEntIter->second; + + // New entity + Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ); + myEntities.push_back(aPtEntity); + myEntityMap[theEntity] = aPtEntity.h; + return aPtEntity.h; + } + + // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error + if (myWorkplane.h == SLVS_E_UNKNOWN) + return SLVS_E_UNKNOWN; + + // Point in 2D + boost::shared_ptr aPoint2D = + boost::dynamic_pointer_cast(theEntity); + if (aPoint2D) + { + Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter); + Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter); + + if (aEntIter != myEntityMap.end()) // the entity already exists + return aEntIter->second; + + // New entity + Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV); + myEntities.push_back(aPt2DEntity); + myEntityMap[theEntity] = aPt2DEntity.h; + return aPt2DEntity.h; + } + + // Scalar value (used for the distance entities) + boost::shared_ptr aScalar = + boost::dynamic_pointer_cast(theEntity); + if (aScalar) + { + Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter); + + if (aEntIter != myEntityMap.end()) // the entity already exists + return aEntIter->second; + + // New entity + Slvs_Entity aDistance = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue); + myEntities.push_back(aDistance); + myEntityMap[theEntity] = aDistance.h; + return aDistance.h; + } + + // SketchPlugin features + boost::shared_ptr aFeature = + boost::dynamic_pointer_cast(theEntity); + if (aFeature) + { // Verify the feature by its kind + const std::string& aFeatureKind = aFeature->getKind(); + + // Line + if (aFeatureKind.compare("SketchLine") == 0) + { + Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(LINE_ATTR_START)); + Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(LINE_ATTR_END)); + + if (aEntIter != myEntityMap.end()) // the entity already exists + return aEntIter->second; + + // New entity + Slvs_Entity aLineEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd); + myEntities.push_back(aLineEntity); + myEntityMap[theEntity] = aLineEntity.h; + return aLineEntity.h; + } + // Circle + else if (aFeatureKind.compare("SketchCircle") == 0) + { + Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_CENTER)); + Slvs_hEntity aRadius = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_RADIUS)); + + if (aEntIter != myEntityMap.end()) // the entity already exists + return aEntIter->second; + + // New entity + Slvs_Entity aCircleEntity = + Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter, myWorkplane.normal, aRadius); + myEntities.push_back(aCircleEntity); + myEntityMap[theEntity] = aCircleEntity.h; + return aCircleEntity.h; + } + // Arc + else if (aFeatureKind.compare("SketchArc") == 0) + { + Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(ARC_ATTR_CENTER)); + Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(ARC_ATTR_START)); + Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(ARC_ATTR_END)); + + if (aEntIter != myEntityMap.end()) // the entity already exists + return aEntIter->second; + + Slvs_Entity anArcEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID, + myWorkplane.h, myWorkplane.normal, aCenter, aStart, aEnd); + myEntities.push_back(anArcEntity); + myEntityMap[theEntity] = anArcEntity.h; + return anArcEntity.h; + } + // Point (it has low probability to be an attribute of constraint, so it is checked at the end) + else if (aFeatureKind.compare("SketchPoint") == 0) + { + Slvs_hEntity aPoint = changeEntity(aFeature->data()->attribute(POINT_ATTR_COORD)); + + if (aEntIter != myEntityMap.end()) // the entity already exists + return aEntIter->second; + + // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier + myEntityMap[theEntity] = aPoint; + return aPoint; + } + } + + /// \todo Other types of entities + + // Unsupported or wrong entity type + return SLVS_E_UNKNOWN; +} + +// ============================================================================ +// Function: changeNormal +// Class: SketchSolver_ConstraintGroup +// Purpose: create/update the normal of workplane +// ============================================================================ +Slvs_hEntity SketchSolver_ConstraintGroup::changeNormal( + boost::shared_ptr theDirX, + boost::shared_ptr theDirY, + boost::shared_ptr theNorm) +{ + boost::shared_ptr aDirX = + boost::dynamic_pointer_cast(theDirX); + boost::shared_ptr aDirY = + boost::dynamic_pointer_cast(theDirY); + if (!aDirX || !aDirY || + (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) || + (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance)) + return SLVS_E_UNKNOWN; + + // quaternion parameters of normal vector + double qw, qx, qy, qz; + Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(), + aDirY->x(), aDirY->y(), aDirY->z(), + &qw, &qx, &qy, &qz); + double aNormCoord[4] = {qw, qx, qy, qz}; + + // Try to find existent normal + std::map, Slvs_hEntity>::const_iterator + aEntIter = myEntityMap.find(theNorm); + std::vector::const_iterator aParamIter; // looks to the first parameter of already existent entity or to the end of vector otherwise + if (aEntIter == myEntityMap.end()) // no such entity => should be created + aParamIter = myParams.end(); + else + { // the entity already exists, update it + int aEntPos = Search(aEntIter->second, myEntities); + int aParamPos = Search(myEntities[aEntPos].param[0], myParams); + aParamIter = myParams.begin() + aParamPos; + } + + // Change parameters of the normal + Slvs_hParam aNormParams[4]; + for (int i = 0; i < 4; i++) + aNormParams[i] = changeParameter(aNormCoord[i], aParamIter); + + if (aEntIter != myEntityMap.end()) // the entity already exists + return aEntIter->second; + + // Create a normal + Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID, + aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]); + myEntities.push_back(aNormal); + myEntityMap[theNorm] = aNormal.h; + return aNormal.h; +} + + +// ============================================================================ +// Function: addWorkplane +// Class: SketchSolver_ConstraintGroup +// Purpose: create workplane for the group +// ============================================================================ +bool SketchSolver_ConstraintGroup::addWorkplane( + boost::shared_ptr theSketch) +{ + if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0) + return false; // the workplane already exists or the function parameter is not Sketch + + mySketch = theSketch; + updateWorkplane(); + return true; +} + +// ============================================================================ +// Function: updateWorkplane +// Class: SketchSolver_ConstraintGroup +// Purpose: update parameters of workplane +// ============================================================================ +bool SketchSolver_ConstraintGroup::updateWorkplane() +{ + // Get parameters of workplane + boost::shared_ptr aDirX = mySketch->data()->attribute(SKETCH_ATTR_DIRX); + boost::shared_ptr aDirY = mySketch->data()->attribute(SKETCH_ATTR_DIRY); + boost::shared_ptr aNorm = mySketch->data()->attribute(SKETCH_ATTR_NORM); + boost::shared_ptr anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN); + // Transform them into SolveSpace format + Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm); + if (!aNormalWP) return false; + Slvs_hEntity anOriginWP = changeEntity(anOrigin); + if (!anOriginWP) return false; + + if (!myWorkplane.h) + { + // Create workplane + myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP); + // Workplane should be added to the list of entities + myEntities.push_back(myWorkplane); + } + return true; +} + +// ============================================================================ +// Function: changeParameter +// Class: SketchSolver_ConstraintGroup +// Purpose: create/update value of parameter +// ============================================================================ +Slvs_hParam SketchSolver_ConstraintGroup::changeParameter( + const double& theParam, + std::vector::const_iterator& thePrmIter) +{ + if (thePrmIter != myParams.end()) + { // Parameter should be updated + int aParamPos = thePrmIter - myParams.begin(); + if (fabs(thePrmIter->val - theParam) > tolerance) + { + myNeedToSolve = true; // parameter is changed, need to resolve constraints + myParams[aParamPos].val = theParam; + } + thePrmIter++; + return myParams[aParamPos].h; + } + + // Newly created parameter + Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam); + myParams.push_back(aParam); + myNeedToSolve = true; + // The list of parameters is changed, move iterator to the end of the list to avoid problems + thePrmIter = myParams.end(); + return aParam.h; +} + +// ============================================================================ +// Function: resolveConstraints +// Class: SketchSolver_ConstraintGroup +// Purpose: solve the set of constraints for the current group +// ============================================================================ +void SketchSolver_ConstraintGroup::resolveConstraints() +{ + if (!myNeedToSolve) + return; + + myConstrSolver.setGroupID(myID); + myConstrSolver.setParameters(myParams); + myConstrSolver.setEntities(myEntities); + myConstrSolver.setConstraints(myConstraints); + + if (myConstrSolver.solve() == SLVS_RESULT_OKAY) + { // solution succeeded, store results into correspondent attributes + // Obtain result into the same list of parameters + if (!myConstrSolver.getResult(myParams)) + return; + + std::map, Slvs_hEntity>::iterator + anEntIter = myEntityMap.begin(); + for ( ; anEntIter != myEntityMap.end(); anEntIter++) + updateAttribute(anEntIter->first, anEntIter->second); + } + /// \todo Implement error handling + + removeTemporaryConstraints(); + myNeedToSolve = false; +} + +// ============================================================================ +// Function: mergeGroups +// Class: SketchSolver_ConstraintGroup +// Purpose: append specified group to the current group +// ============================================================================ +void SketchSolver_ConstraintGroup::mergeGroups( + const SketchSolver_ConstraintGroup& theGroup) +{ + // If specified group is empty, no need to merge + if (theGroup.myConstraintMap.empty()) + return ; + + // NOTE: The possibility, that some elements are placed into both groups, is around 0, + // so the objects should be copied with changing the indexes + + // Maps between old and new indexes of SolveSpace elements: + std::map aParamMap; + std::map anEntityMap; + std::map aConstrMap; + + // Go through copying constraints + std::vector::const_iterator aConstrIter = theGroup.myConstraints.begin(); + for ( ; aConstrIter != theGroup.myConstraints.end(); aConstrIter++) + { + Slvs_Constraint aConstraintCopy = *aConstrIter; + // Go through constraint entities + Slvs_hEntity* anEntities[CONSTRAINT_ATTR_SIZE] = { + &(aConstraintCopy.ptA), &(aConstraintCopy.ptB), + &(aConstraintCopy.entityA), &(aConstraintCopy.entityB) + }; + for (int indEnt = 0; indEnt < CONSTRAINT_ATTR_SIZE; indEnt++) + { + if (*(anEntities[indEnt]) == 0) + continue; + if (anEntityMap.find(*(anEntities[indEnt])) != anEntityMap.end()) + { // entity is already copied + *(anEntities[indEnt]) = anEntityMap[*(anEntities[indEnt])]; + continue; + } + + // Copy entity + Slvs_Entity anEntityCopy = theGroup.myEntities[Search(*(anEntities[indEnt]), theGroup.myEntities)]; + // Go through entity parameters + const int aNbEntParams = 4; // maximal number of entity parameters + for (int indPrm = 0; indPrm < aNbEntParams; indPrm++) + { + if (anEntityCopy.param[indPrm] == 0) + continue; + if (aParamMap.find(anEntityCopy.param[indPrm]) != aParamMap.end()) + { + anEntityCopy.param[indPrm] = aParamMap[anEntityCopy.param[indPrm]]; + continue; + } + + Slvs_Param aParamCopy = theGroup.myParams[Search(anEntityCopy.param[indPrm], theGroup.myParams)]; + aParamMap[aParamCopy.h] = ++myParamMaxID; + aParamCopy.h = myParamMaxID; + myParams.push_back(aParamCopy); + } + + anEntityMap[anEntityCopy.h] = ++myEntityMaxID; + anEntityCopy.h = myEntityMaxID; + myEntities.push_back(anEntityCopy); + *(anEntities[indEnt]) = anEntityCopy.h; + } + + aConstraintCopy.h = ++myConstrMaxID; + myConstraints.push_back(aConstraintCopy); + aConstrMap[aConstrIter->h] = aConstraintCopy.h; + } + + // Append maps of SketchPlugin to SolveSpace parameters + std::map, Slvs_hConstraint>::const_iterator + aSPConstrMapIter = theGroup.myConstraintMap.begin(); + for ( ; aSPConstrMapIter!= theGroup.myConstraintMap.end(); aSPConstrMapIter++) + { + std::map::iterator aFind = aConstrMap.find(aSPConstrMapIter->second); + if (aFind != aConstrMap.end()) + myConstraintMap[aSPConstrMapIter->first] = aFind->second; + } + + std::map, Slvs_hEntity>::const_iterator + aSPEntMapIter = theGroup.myEntityMap.begin(); + for ( ; aSPEntMapIter != theGroup.myEntityMap.end(); aSPEntMapIter++) { + std::map::iterator aFind = anEntityMap.find(aSPEntMapIter->second); + if (aFind != anEntityMap.end()) + myEntityMap[aSPEntMapIter->first] = aFind->second; + } + + // Add temporary constraints + std::list::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin(); + for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++) + { + std::map::iterator aFind = aConstrMap.find(*aTempConstrIter); + if (aFind != aConstrMap.end()) + myTempConstraints.push_back(aFind->second); + } + myTempConstraints.sort(); + + myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve; +} + +// ============================================================================ +// Function: updateGroup +// Class: SketchSolver_ConstraintGroup +// Purpose: search removed entities and constraints +// ============================================================================ +bool SketchSolver_ConstraintGroup::updateGroup() +{ + // Check for valid sketch + if (!mySketch->data()->isValid()) + return true; + + // Fast check for constraint validity. If all constraints are valid, no need to update the group + std::map, Slvs_hConstraint>::reverse_iterator + aConstrIter = myConstraintMap.rbegin(); + bool isAllValid = true; + for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++) + if (!aConstrIter->first->data()->isValid()) + isAllValid = false; + if (isAllValid) + return false; + + // Remove invalid constraints. + // There only constraint will be deleted (parameters and entities) will be removed below + std::list< boost::shared_ptr > aConstrToDelete; + std::map anEntToDelete; // entities will be removed if no valid constraints use them + for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++) + { + bool isValid = aConstrIter->first->data()->isValid(); + + int aConstrPos = Search(aConstrIter->second, myConstraints); + if (aConstrPos < (int)myConstraints.size()) + { + Slvs_hEntity aConstrEnt[] = { + myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB, + myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB}; + for (int i = 0; i < 4; i++) + if (aConstrEnt[i] != SLVS_E_UNKNOWN) + { + if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end()) + anEntToDelete[aConstrEnt[i]] = !isValid; + else if (isValid) // constraint is valid => no need to remove its entities + anEntToDelete[aConstrEnt[i]] = false; + } + if (!isValid) + { + myConstraints.erase(myConstraints.begin() + aConstrPos); + if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer + myConstrMaxID--; + aConstrToDelete.push_front(aConstrIter->first); + } + } + } + std::list< boost::shared_ptr >::iterator aDelIter; + for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++) + myConstraintMap.erase(*aDelIter); + + // Remove invalid and unused entities + std::map::reverse_iterator aEDelIter; + for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++) + if (aEDelIter->second) + { + int anEntPos = Search(aEDelIter->first, myEntities); + std::vector::iterator aEntIter = myEntities.begin() + anEntPos; + // Number of parameters for the entity + int aNbParams = 0; + while (aEntIter->param[aNbParams]) aNbParams++; + if (aNbParams == 0) continue; + // Decrease parameter indexer if there are deleted parameter with higher IDs + if (aEntIter->param[aNbParams-1] == myParamMaxID) + myParamMaxID -= aNbParams; + // Remove parameters of the entity + int aParamPos = Search(aEntIter->param[0], myParams); + myParams.erase(myParams.begin() + aParamPos, + myParams.begin() + aParamPos + aNbParams); + + // Remove entity + if (aEDelIter->first == myEntityMaxID) + myEntityMaxID--; + myEntities.erase(myEntities.begin() + anEntPos); + // Remove such entity from myEntityMap + std::map, Slvs_hEntity>::iterator + anEntMapIter = myEntityMap.begin(); + for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++) + if (anEntMapIter->second == aEDelIter->first) + break; + if (anEntMapIter != myEntityMap.end()) + myEntityMap.erase(anEntMapIter); + } + + return false; +} + +// ============================================================================ +// Function: updateAttribute +// Class: SketchSolver_ConstraintGroup +// Purpose: update features of sketch after resolving constraints +// ============================================================================ +void SketchSolver_ConstraintGroup::updateAttribute( + boost::shared_ptr theAttribute, + const Slvs_hEntity& theEntityID) +{ + // Search the position of the first parameter of the entity + int anEntPos = Search(theEntityID, myEntities); + int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams); + + // Look over supported types of entities + + // Point in 3D + boost::shared_ptr aPoint = + boost::dynamic_pointer_cast(theAttribute); + if (aPoint) + { + aPoint->setValue(myParams[aFirstParamPos].val, + myParams[aFirstParamPos+1].val, + myParams[aFirstParamPos+2].val); + return ; + } + + // Point in 2D + boost::shared_ptr aPoint2D = + boost::dynamic_pointer_cast(theAttribute); + if (aPoint2D) + { + aPoint2D->setValue(myParams[aFirstParamPos].val, + myParams[aFirstParamPos+1].val); + return ; + } + + // Scalar value + boost::shared_ptr aScalar = + boost::dynamic_pointer_cast(theAttribute); + if (aScalar) + { + aScalar->setValue(myParams[aFirstParamPos].val); + return ; + } + + /// \todo Support other types of entities +} + +// ============================================================================ +// Function: updateEntityIfPossible +// Class: SketchSolver_ConstraintGroup +// Purpose: search the entity in this group and update it +// ============================================================================ +void SketchSolver_ConstraintGroup::updateEntityIfPossible( + boost::shared_ptr theEntity) +{ + if (myEntityMap.find(theEntity) != myEntityMap.end()) + { + // If the attribute is a point and it is changed (the group needs to rebuild), + // probably user has dragged this point into this position, + // so it is necessary to add constraint which will guarantee the point will not change + + // Store myNeedToSolve flag to verify the entity is really changed + bool aNeedToSolveCopy = myNeedToSolve; + myNeedToSolve = false; + + changeEntity(theEntity); + + if (myNeedToSolve) // the entity is changed + { + // Verify the entity is a point and add temporary constraint of permanency + boost::shared_ptr aPoint = + boost::dynamic_pointer_cast(theEntity); + boost::shared_ptr aPoint2D = + boost::dynamic_pointer_cast(theEntity); + if (aPoint || aPoint2D) + addTemporaryConstraintWhereDragged(theEntity); + } + + // Restore flag of changes + myNeedToSolve = myNeedToSolve || aNeedToSolveCopy; + } +} + +// ============================================================================ +// Function: addTemporaryConstraintWhereDragged +// Class: SketchSolver_ConstraintGroup +// Purpose: add transient constraint SLVS_C_WHERE_DRAGGED for the entity, +// which was moved by user +// ============================================================================ +void SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged( + boost::shared_ptr theEntity) +{ + // Find identifier of the entity + std::map, Slvs_hEntity>::const_iterator + anEntIter = myEntityMap.find(theEntity); + + // Create SLVS_C_WHERE_DRAGGED constraint + Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED, + myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0); + myConstraints.push_back(aWDConstr); + myTempConstraints.push_back(aWDConstr.h); +} + +// ============================================================================ +// Function: removeTemporaryConstraints +// Class: SketchSolver_ConstraintGroup +// Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after +// resolving the set of constraints +// ============================================================================ +void SketchSolver_ConstraintGroup::removeTemporaryConstraints() +{ + std::list::reverse_iterator aTmpConstrIter; + for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++) + { + int aConstrPos = Search(*aTmpConstrIter, myConstraints); + myConstraints.erase(myConstraints.begin() + aConstrPos); + + // If the removing constraint has higher index, decrease the indexer + if (*aTmpConstrIter == myConstrMaxID) + myConstrMaxID--; + } + myTempConstraints.clear(); +} + + + +// ======================================================== +// ========= Auxiliary functions =============== +// ======================================================== + +template +int Search(const uint32_t& theEntityID, const std::vector& theEntities) +{ + int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0; + int aVecSize = theEntities.size(); + while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID) + aResIndex--; + while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID) + aResIndex++; + if (aResIndex == -1) + aResIndex = aVecSize; + return aResIndex; +} diff --git a/src/SketchSolver/SketchSolver_ConstraintGroup.h b/src/SketchSolver/SketchSolver_ConstraintGroup.h new file mode 100644 index 000000000..1d2974296 --- /dev/null +++ b/src/SketchSolver/SketchSolver_ConstraintGroup.h @@ -0,0 +1,169 @@ +// File: SketchSolver_ConstraintGroup.h +// Created: 27 May 2014 +// Author: Artem ZHIDKOV + +#ifndef SketchSolver_ConstraintGroup_Headerfile +#define SketchSolver_ConstraintGroup_Headerfile + +#include "SketchSolver.h" +#include + +#include + +#include +#include +#include +#include + + +/** \class SketchSolver_ConstraintGroup + * \ingroup DataModel + * \brief Keeps the group of constraints which based on the same entities + */ +class SketchSolver_ConstraintGroup +{ +public: + /** \brief New group based on specified workplane. + * Throws an exception if theWorkplane is not an object of SketchPlugin_Sketch type + * \remark Type of theSketch is not verified inside + */ + SketchSolver_ConstraintGroup(boost::shared_ptr theWorkplane); + + ~SketchSolver_ConstraintGroup(); + + /// \brief Returns group's unique identifier + inline const Slvs_hGroup& getId() const + {return myID;} + + /// \brief Returns true if the group has no constraints yet + inline bool isEmpty() const + {return myConstraints.empty();} + + /** \brief Adds or updates a constraint in the group + * \param[in] theConstraint constraint to be changed + * \return \c true if the constraint added or updated successfully + */ + bool changeConstraint(boost::shared_ptr theConstraint); + + /** \brief Verifies the constraint uses the objects from this group + * \param[in] theConstraint constraint for verification of interaction + * \return \c true if the constrained objects are used in current group + */ + bool isInteract(boost::shared_ptr theConstraint) const; + + /** \brief Verifies the specified feature is equal to the base workplane for this group + * \param[in] theWorkplane the feature to be compared with base workplane + * \return \c true if workplanes are the same + */ + bool isBaseWorkplane(boost::shared_ptr theWorkplane) const; + + boost::shared_ptr getWorkplane() const + { return mySketch; } + + /** \brief Update parameters of workplane. Should be called when Update event is coming. + * \return \c true if workplane updated successfully, \c false if workplane parameters are not consistent + */ + bool updateWorkplane(); + + /** \brief If the entity is in this group it will updated + * \param[in] theEntity attribute, which values should update SolveSpace entity + */ + void updateEntityIfPossible(boost::shared_ptr theEntity); + + /** \brief Searches invalid features and constraints in the group and avoids them + * \return \c true if the group's sketch is invalid and the group should be removed + */ + bool updateGroup(); + + /** \brief Add specified group to this one + * \param[in] theGroup group of constraint to be added + */ + void mergeGroups(const SketchSolver_ConstraintGroup& theGroup); + + /** \brief Start solution procedure if necessary and update attributes of features + */ + void resolveConstraints(); + +protected: + /** \brief Adds or updates an entity in the group + * + * The parameters of entity will be parsed and added to the list of SolveSpace parameters. + * Parameters of certain entity will be placed sequentially in the list. + * + * \param[in] theEntity the object of constraint + * \return identifier of changed entity or 0 if entity could not be changed + */ + Slvs_hEntity changeEntity(boost::shared_ptr theEntity); + + /** \brief Adds or updates a normal in the group + * + * Normal is a special entity in SolveSpace, which defines a direction in 3D and + * a rotation about this direction. So, SolveSpace represents normals as unit quaternions. + * + * To define a normal there should be specified two coordinate axis + * on the plane transversed to created normal. + * + * \param[in] theDirX first coordinate axis of the plane + * \param[in] theDirY second coordinate axis of the plane + * \param[in] theNorm attribute for the normal (used to identify newly created entity) + * \return identifier of created or updated normal + */ + Slvs_hEntity changeNormal(boost::shared_ptr theDirX, + boost::shared_ptr theDirY, + boost::shared_ptr theNorm); + + /** \brief Adds or updates a parameter in the group + * \param[in] theParam the value of parameter + * \param[in] thePrmIter the cell in the list of parameters which should be changed + * (the iterator will be increased if it does not reach the end of the list) + * \return identifier of changed parameter; when the parameter cannot be created, returned ID is 0 + */ + Slvs_hParam changeParameter(const double& theParam, + std::vector::const_iterator& thePrmIter); + + /** \brief Change values of attribute by parameters received from SolveSpace solver + * \param[in,out] theAttribute pointer to the attribute to be changed + * \param[in] theEntityID identifier of SolveSpace entity, which contains updated data + */ + void updateAttribute(boost::shared_ptr theAttribute, const Slvs_hEntity& theEntityID); + + /** \brief Adds a constraint for a point which should not be changed during computations + * \param[in] theEntity the base for the constraint + */ + void addTemporaryConstraintWhereDragged(boost::shared_ptr theEntity); + + /** \brief Remove all temporary constraint after computation finished + */ + void removeTemporaryConstraints(); + +private: + /** \brief Creates a workplane from the sketch parameters + * \param[in] theSketch parameters of workplane are the attributes of this sketch + * \return \c true if success, \c false if workplane parameters are not consistent + */ + bool addWorkplane(boost::shared_ptr theSketch); + +private: + // SolveSpace entities + Slvs_hGroup myID; ///< the index of the group + Slvs_Entity myWorkplane; ///< Workplane for the current group + std::vector myParams; ///< List of parameters of the constraints + Slvs_hParam myParamMaxID; ///< Actual maximal ID of parameters (not equal to myParams size) + std::vector myEntities; ///< List of entities of the constaints + Slvs_hEntity myEntityMaxID; ///< Actual maximal ID of entities (not equal to myEntities size) + std::vector myConstraints; ///< List of constraints in SolveSpace format + Slvs_hConstraint myConstrMaxID; ///< Actual maximal ID of constraints (not equal to myConstraints size) + bool myNeedToSolve; ///< Indicator that something changed in the group and constraint system need to be rebuilt + + SketchSolver_Solver myConstrSolver; ///< Solver for set of equations obtained by constraints + + // SketchPlugin entities + boost::shared_ptr mySketch; ///< Equivalent to workplane + std::map, Slvs_hConstraint> + myConstraintMap; ///< The map between SketchPlugin and SolveSpace constraints + std::list myTempConstraints; ///< The list of identifiers of temporary constraints + std::map, Slvs_hEntity> + myEntityMap; ///< The map between parameters of constraints and their equivalent SolveSpace entities +}; + +#endif diff --git a/src/SketchSolver/SketchSolver_ConstraintManager.cpp b/src/SketchSolver/SketchSolver_ConstraintManager.cpp index 4fd5b4f07..1933745be 100644 --- a/src/SketchSolver/SketchSolver_ConstraintManager.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintManager.cpp @@ -4,19 +4,13 @@ #include "SketchSolver_ConstraintManager.h" -#include - #include -#include -#include -#include #include #include #include #include #include -#include #include #include @@ -24,11 +18,6 @@ #include #include -#include -#include - -/// Tolerance for value of parameters -const double tolerance = 1.e-10; // Initialization of constraint manager self pointer SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0; @@ -36,18 +25,6 @@ SketchSolver_ConstraintManager* SketchSolver_ConstraintManager::_self = 0; /// Global constraint manager object SketchSolver_ConstraintManager* myManager = SketchSolver_ConstraintManager::Instance(); -/// This value is used to give unique index to the groups -static Slvs_hGroup myGroupIndexer = 0; - -/** \brief Search the entity/parameter with specified ID in the list of elements - * \param[in] theEntityID unique ID of the element - * \param[in] theEntities list of elements - * \return position of the found element or -1 if the element is not found - */ -template -static int Search(const uint32_t& theEntityID, const std::vector& theEntities); - - // ======================================================== // ========= SketchSolver_ConstraintManager =============== @@ -382,806 +359,3 @@ void SketchSolver_ConstraintManager::resolveConstraints() Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_FEATURE_UPDATED)); } - - -// ======================================================== -// ========= SketchSolver_ConstraintGroup =============== -// ======================================================== - -SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup:: - SketchSolver_ConstraintGroup(boost::shared_ptr theWorkplane) - : myID(++myGroupIndexer), - myParamMaxID(0), - myEntityMaxID(0), - myConstrMaxID(0), - myConstraintMap(), - myNeedToSolve(false), - myConstrSolver() -{ - myParams.clear(); - myEntities.clear(); - myConstraints.clear(); - - // Initialize workplane - myWorkplane.h = SLVS_E_UNKNOWN; -#ifndef NDEBUG - assert(addWorkplane(theWorkplane)); -#else - addWorkplane(theWorkplane); -#endif -} - -SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::~SketchSolver_ConstraintGroup() -{ - myParams.clear(); - myEntities.clear(); - myConstraints.clear(); - myConstraintMap.clear(); - - // If the group with maximal identifier is deleted, decrease the indexer - if (myID == myGroupIndexer) - myGroupIndexer--; -} - -// ============================================================================ -// Function: isBaseWorkplane -// Class: SketchSolver_ConstraintGroup -// Purpose: verify the group is based on the given workplane -// ============================================================================ -bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isBaseWorkplane( - boost::shared_ptr theWorkplane) const -{ - return theWorkplane == mySketch; -} - -// ============================================================================ -// Function: isInteract -// Class: SketchSolver_ConstraintGroup -// Purpose: verify are there any entities in the group used by given constraint -// ============================================================================ -bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::isInteract( - boost::shared_ptr theConstraint) const -{ - // Check the group is empty - if (myWorkplane.h != SLVS_E_UNKNOWN && myConstraints.empty()) - return true; - - // Go through constraint entities and verify if some of them already in the group - for (int i = 0; i < CONSTRAINT_ATTR_SIZE; i++) - { - boost::shared_ptr aCAttrRef = - boost::dynamic_pointer_cast( - theConstraint->data()->attribute(CONSTRAINT_ATTRIBUTES[i]) - ); - if (!aCAttrRef) continue; - if (myEntityMap.find(aCAttrRef->attr()) != myEntityMap.end()) - return true; - } - - // Entities did not found - return false; -} - -// ============================================================================ -// Function: changeConstraint -// Class: SketchSolver_ConstraintGroup -// Purpose: create/update the constraint in the group -// ============================================================================ -bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeConstraint( - boost::shared_ptr theConstraint) -{ - // There is no workplane yet, something wrong - if (myWorkplane.h == SLVS_E_UNKNOWN) - return false; - - // Search this constraint in the current group to update it - std::map, Slvs_hConstraint>::const_iterator - aConstrMapIter = myConstraintMap.find(theConstraint); - std::vector::iterator aConstrIter; - if (aConstrMapIter != myConstraintMap.end()) - { - int aConstrPos = Search(aConstrMapIter->second, myConstraints); - aConstrIter = myConstraints.begin() + aConstrPos; - } - - // Get constraint type and verify the constraint parameters are correct - SketchSolver_Constraint aConstraint(theConstraint); - int aConstrType = aConstraint.getType(); - if (aConstrType == SLVS_C_UNKNOWN || - (aConstrMapIter != myConstraintMap.end() && aConstrIter->type != aConstrType)) - return false; - const std::vector& aConstraintAttributes = aConstraint.getAttributes(); - - // Create constraint parameters - double aDistance = 0.0; // scalar value of the constraint - boost::shared_ptr aDistAttr = - boost::dynamic_pointer_cast(theConstraint->data()->attribute(CONSTRAINT_ATTR_VALUE)); - if (aDistAttr) - { - aDistance = aDistAttr->value(); - if (aConstrMapIter != myConstraintMap.end() && fabs(aConstrIter->valA - aDistance) > tolerance) - { - myNeedToSolve = true; - aConstrIter->valA = aDistance; - } - } - - Slvs_hEntity aConstrEnt[CONSTRAINT_ATTR_SIZE]; // parameters of the constraint - for (unsigned int indAttr = 0; indAttr < CONSTRAINT_ATTR_SIZE; indAttr++) - { - aConstrEnt[indAttr] = SLVS_E_UNKNOWN; - boost::shared_ptr aConstrAttr = - boost::dynamic_pointer_cast( - theConstraint->data()->attribute(aConstraintAttributes[indAttr]) - ); - if (!aConstrAttr) continue; - aConstrEnt[indAttr] = changeEntity(aConstrAttr->attr()); - } - - if (aConstrMapIter == myConstraintMap.end()) - { - // Create SolveSpace constraint structure - Slvs_Constraint aConstraint = - Slvs_MakeConstraint(++myConstrMaxID, myID, aConstrType, myWorkplane.h, - aDistance, aConstrEnt[0], aConstrEnt[1], aConstrEnt[2], aConstrEnt[3]); - myConstraints.push_back(aConstraint); - myConstraintMap[theConstraint] = aConstraint.h; - } - return true; -} - -// ============================================================================ -// Function: changeEntity -// Class: SketchSolver_ConstraintGroup -// Purpose: create/update the element affected by any constraint -// ============================================================================ -Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeEntity( - boost::shared_ptr theEntity) -{ - // If the entity is already in the group, try to find it - std::map, Slvs_hEntity>::const_iterator - aEntIter = myEntityMap.find(theEntity); - std::vector::const_iterator aParamIter; // looks at first parameter of already existent entity or at the end of vector otherwise - if (aEntIter == myEntityMap.end()) // no such entity => should be created - aParamIter = myParams.end(); - else - { // the entity already exists - int aEntPos = Search(aEntIter->second, myEntities); - int aParamPos = Search(myEntities[aEntPos].param[0], myParams); - aParamIter = myParams.begin() + aParamPos; - } - - // Look over supported types of entities - - // Point in 3D - boost::shared_ptr aPoint = - boost::dynamic_pointer_cast(theEntity); - if (aPoint) - { - Slvs_hParam aX = changeParameter(aPoint->x(), aParamIter); - Slvs_hParam aY = changeParameter(aPoint->y(), aParamIter); - Slvs_hParam aZ = changeParameter(aPoint->z(), aParamIter); - - if (aEntIter != myEntityMap.end()) // the entity already exists - return aEntIter->second; - - // New entity - Slvs_Entity aPtEntity = Slvs_MakePoint3d(++myEntityMaxID, myID, aX, aY, aZ); - myEntities.push_back(aPtEntity); - myEntityMap[theEntity] = aPtEntity.h; - return aPtEntity.h; - } - - // All entities except 3D points are created on workplane. So, if there is no workplane yet, then error - if (myWorkplane.h == SLVS_E_UNKNOWN) - return SLVS_E_UNKNOWN; - - // Point in 2D - boost::shared_ptr aPoint2D = - boost::dynamic_pointer_cast(theEntity); - if (aPoint2D) - { - Slvs_hParam aU = changeParameter(aPoint2D->x(), aParamIter); - Slvs_hParam aV = changeParameter(aPoint2D->y(), aParamIter); - - if (aEntIter != myEntityMap.end()) // the entity already exists - return aEntIter->second; - - // New entity - Slvs_Entity aPt2DEntity = Slvs_MakePoint2d(++myEntityMaxID, myID, myWorkplane.h, aU, aV); - myEntities.push_back(aPt2DEntity); - myEntityMap[theEntity] = aPt2DEntity.h; - return aPt2DEntity.h; - } - - // Scalar value (used for the distance entities) - boost::shared_ptr aScalar = - boost::dynamic_pointer_cast(theEntity); - if (aScalar) - { - Slvs_hParam aValue = changeParameter(aScalar->value(), aParamIter); - - if (aEntIter != myEntityMap.end()) // the entity already exists - return aEntIter->second; - - // New entity - Slvs_Entity aDistance = Slvs_MakeDistance(++myEntityMaxID, myID, myWorkplane.h, aValue); - myEntities.push_back(aDistance); - myEntityMap[theEntity] = aDistance.h; - return aDistance.h; - } - - // SketchPlugin features - boost::shared_ptr aFeature = - boost::dynamic_pointer_cast(theEntity); - if (aFeature) - { // Verify the feature by its kind - const std::string& aFeatureKind = aFeature->getKind(); - - // Line - if (aFeatureKind.compare("SketchLine") == 0) - { - Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(LINE_ATTR_START)); - Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(LINE_ATTR_END)); - - if (aEntIter != myEntityMap.end()) // the entity already exists - return aEntIter->second; - - // New entity - Slvs_Entity aLineEntity = Slvs_MakeLineSegment(++myEntityMaxID, myID, myWorkplane.h, aStart, aEnd); - myEntities.push_back(aLineEntity); - myEntityMap[theEntity] = aLineEntity.h; - return aLineEntity.h; - } - // Circle - else if (aFeatureKind.compare("SketchCircle") == 0) - { - Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_CENTER)); - Slvs_hEntity aRadius = changeEntity(aFeature->data()->attribute(CIRCLE_ATTR_RADIUS)); - - if (aEntIter != myEntityMap.end()) // the entity already exists - return aEntIter->second; - - // New entity - Slvs_Entity aCircleEntity = - Slvs_MakeCircle(++myEntityMaxID, myID, myWorkplane.h, aCenter, myWorkplane.normal, aRadius); - myEntities.push_back(aCircleEntity); - myEntityMap[theEntity] = aCircleEntity.h; - return aCircleEntity.h; - } - // Arc - else if (aFeatureKind.compare("SketchArc") == 0) - { - Slvs_hEntity aCenter = changeEntity(aFeature->data()->attribute(ARC_ATTR_CENTER)); - Slvs_hEntity aStart = changeEntity(aFeature->data()->attribute(ARC_ATTR_START)); - Slvs_hEntity aEnd = changeEntity(aFeature->data()->attribute(ARC_ATTR_END)); - - if (aEntIter != myEntityMap.end()) // the entity already exists - return aEntIter->second; - - Slvs_Entity anArcEntity = Slvs_MakeArcOfCircle(++myEntityMaxID, myID, - myWorkplane.h, myWorkplane.normal, aCenter, aStart, aEnd); - myEntities.push_back(anArcEntity); - myEntityMap[theEntity] = anArcEntity.h; - return anArcEntity.h; - } - // Point (it has low probability to be an attribute of constraint, so it is checked at the end) - else if (aFeatureKind.compare("SketchPoint") == 0) - { - Slvs_hEntity aPoint = changeEntity(aFeature->data()->attribute(POINT_ATTR_COORD)); - - if (aEntIter != myEntityMap.end()) // the entity already exists - return aEntIter->second; - - // Both the sketch point and its attribute (coordinates) link to the same SolveSpace point identifier - myEntityMap[theEntity] = aPoint; - return aPoint; - } - } - - /// \todo Other types of entities - - // Unsupported or wrong entity type - return SLVS_E_UNKNOWN; -} - -// ============================================================================ -// Function: changeNormal -// Class: SketchSolver_ConstraintGroup -// Purpose: create/update the normal of workplane -// ============================================================================ -Slvs_hEntity SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeNormal( - boost::shared_ptr theDirX, - boost::shared_ptr theDirY, - boost::shared_ptr theNorm) -{ - boost::shared_ptr aDirX = - boost::dynamic_pointer_cast(theDirX); - boost::shared_ptr aDirY = - boost::dynamic_pointer_cast(theDirY); - if (!aDirX || !aDirY || - (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) || - (fabs(aDirY->x()) + fabs(aDirY->y()) + fabs(aDirY->z()) < tolerance)) - return SLVS_E_UNKNOWN; - - // quaternion parameters of normal vector - double qw, qx, qy, qz; - Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(), - aDirY->x(), aDirY->y(), aDirY->z(), - &qw, &qx, &qy, &qz); - double aNormCoord[4] = {qw, qx, qy, qz}; - - // Try to find existent normal - std::map, Slvs_hEntity>::const_iterator - aEntIter = myEntityMap.find(theNorm); - std::vector::const_iterator aParamIter; // looks to the first parameter of already existent entity or to the end of vector otherwise - if (aEntIter == myEntityMap.end()) // no such entity => should be created - aParamIter = myParams.end(); - else - { // the entity already exists, update it - int aEntPos = Search(aEntIter->second, myEntities); - int aParamPos = Search(myEntities[aEntPos].param[0], myParams); - aParamIter = myParams.begin() + aParamPos; - } - - // Change parameters of the normal - Slvs_hParam aNormParams[4]; - for (int i = 0; i < 4; i++) - aNormParams[i] = changeParameter(aNormCoord[i], aParamIter); - - if (aEntIter != myEntityMap.end()) // the entity already exists - return aEntIter->second; - - // Create a normal - Slvs_Entity aNormal = Slvs_MakeNormal3d(++myEntityMaxID, myID, - aNormParams[0], aNormParams[1], aNormParams[2], aNormParams[3]); - myEntities.push_back(aNormal); - myEntityMap[theNorm] = aNormal.h; - return aNormal.h; -} - - -// ============================================================================ -// Function: addWorkplane -// Class: SketchSolver_ConstraintGroup -// Purpose: create workplane for the group -// ============================================================================ -bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addWorkplane( - boost::shared_ptr theSketch) -{ - if (myWorkplane.h || theSketch->getKind().compare("Sketch") != 0) - return false; // the workplane already exists or the function parameter is not Sketch - - mySketch = theSketch; - updateWorkplane(); - return true; -} - -// ============================================================================ -// Function: updateWorkplane -// Class: SketchSolver_ConstraintGroup -// Purpose: update parameters of workplane -// ============================================================================ -bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateWorkplane() -{ - // Get parameters of workplane - boost::shared_ptr aDirX = mySketch->data()->attribute(SKETCH_ATTR_DIRX); - boost::shared_ptr aDirY = mySketch->data()->attribute(SKETCH_ATTR_DIRY); - boost::shared_ptr aNorm = mySketch->data()->attribute(SKETCH_ATTR_NORM); - boost::shared_ptr anOrigin = mySketch->data()->attribute(SKETCH_ATTR_ORIGIN); - // Transform them into SolveSpace format - Slvs_hEntity aNormalWP = changeNormal(aDirX, aDirY, aNorm); - if (!aNormalWP) return false; - Slvs_hEntity anOriginWP = changeEntity(anOrigin); - if (!anOriginWP) return false; - - if (!myWorkplane.h) - { - // Create workplane - myWorkplane = Slvs_MakeWorkplane(++myEntityMaxID, myID, anOriginWP, aNormalWP); - // Workplane should be added to the list of entities - myEntities.push_back(myWorkplane); - } - return true; -} - -// ============================================================================ -// Function: changeParameter -// Class: SketchSolver_ConstraintGroup -// Purpose: create/update value of parameter -// ============================================================================ -Slvs_hParam SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::changeParameter( - const double& theParam, - std::vector::const_iterator& thePrmIter) -{ - if (thePrmIter != myParams.end()) - { // Parameter should be updated - int aParamPos = thePrmIter - myParams.begin(); - if (fabs(thePrmIter->val - theParam) > tolerance) - { - myNeedToSolve = true; // parameter is changed, need to resolve constraints - myParams[aParamPos].val = theParam; - } - thePrmIter++; - return myParams[aParamPos].h; - } - - // Newly created parameter - Slvs_Param aParam = Slvs_MakeParam(++myParamMaxID, myID, theParam); - myParams.push_back(aParam); - myNeedToSolve = true; - // The list of parameters is changed, move iterator to the end of the list to avoid problems - thePrmIter = myParams.end(); - return aParam.h; -} - -// ============================================================================ -// Function: resolveConstraints -// Class: SketchSolver_ConstraintGroup -// Purpose: solve the set of constraints for the current group -// ============================================================================ -void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::resolveConstraints() -{ - if (!myNeedToSolve) - return; - - myConstrSolver.setGroupID(myID); - myConstrSolver.setParameters(myParams); - myConstrSolver.setEntities(myEntities); - myConstrSolver.setConstraints(myConstraints); - - if (myConstrSolver.solve() == SLVS_RESULT_OKAY) - { // solution succeeded, store results into correspondent attributes - // Obtain result into the same list of parameters - if (!myConstrSolver.getResult(myParams)) - return; - - std::map, Slvs_hEntity>::iterator - anEntIter = myEntityMap.begin(); - for ( ; anEntIter != myEntityMap.end(); anEntIter++) - updateAttribute(anEntIter->first, anEntIter->second); - } - /// \todo Implement error handling - - removeTemporaryConstraints(); - myNeedToSolve = false; -} - -// ============================================================================ -// Function: mergeGroups -// Class: SketchSolver_ConstraintGroup -// Purpose: append specified group to the current group -// ============================================================================ -void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::mergeGroups( - const SketchSolver_ConstraintGroup& theGroup) -{ - // If specified group is empty, no need to merge - if (theGroup.myConstraintMap.empty()) - return ; - - // NOTE: The possibility, that some elements are placed into both groups, is around 0, - // so the objects should be copied with changing the indexes - - // Maps between old and new indexes of SolveSpace elements: - std::map aParamMap; - std::map anEntityMap; - std::map aConstrMap; - - // Go through copying constraints - std::vector::const_iterator aConstrIter = theGroup.myConstraints.begin(); - for ( ; aConstrIter != theGroup.myConstraints.end(); aConstrIter++) - { - Slvs_Constraint aConstraintCopy = *aConstrIter; - // Go through constraint entities - Slvs_hEntity* anEntities[CONSTRAINT_ATTR_SIZE] = { - &(aConstraintCopy.ptA), &(aConstraintCopy.ptB), - &(aConstraintCopy.entityA), &(aConstraintCopy.entityB) - }; - for (int indEnt = 0; indEnt < CONSTRAINT_ATTR_SIZE; indEnt++) - { - if (*(anEntities[indEnt]) == 0) - continue; - if (anEntityMap.find(*(anEntities[indEnt])) != anEntityMap.end()) - { // entity is already copied - *(anEntities[indEnt]) = anEntityMap[*(anEntities[indEnt])]; - continue; - } - - // Copy entity - Slvs_Entity anEntityCopy = theGroup.myEntities[Search(*(anEntities[indEnt]), theGroup.myEntities)]; - // Go through entity parameters - const int aNbEntParams = 4; // maximal number of entity parameters - for (int indPrm = 0; indPrm < aNbEntParams; indPrm++) - { - if (anEntityCopy.param[indPrm] == 0) - continue; - if (aParamMap.find(anEntityCopy.param[indPrm]) != aParamMap.end()) - { - anEntityCopy.param[indPrm] = aParamMap[anEntityCopy.param[indPrm]]; - continue; - } - - Slvs_Param aParamCopy = theGroup.myParams[Search(anEntityCopy.param[indPrm], theGroup.myParams)]; - aParamMap[aParamCopy.h] = ++myParamMaxID; - aParamCopy.h = myParamMaxID; - myParams.push_back(aParamCopy); - } - - anEntityMap[anEntityCopy.h] = ++myEntityMaxID; - anEntityCopy.h = myEntityMaxID; - myEntities.push_back(anEntityCopy); - *(anEntities[indEnt]) = anEntityCopy.h; - } - - aConstraintCopy.h = ++myConstrMaxID; - myConstraints.push_back(aConstraintCopy); - aConstrMap[aConstrIter->h] = aConstraintCopy.h; - } - - // Append maps of SketchPlugin to SolveSpace parameters - std::map, Slvs_hConstraint>::const_iterator - aSPConstrMapIter = theGroup.myConstraintMap.begin(); - for ( ; aSPConstrMapIter!= theGroup.myConstraintMap.end(); aSPConstrMapIter++) - { - std::map::iterator aFind = aConstrMap.find(aSPConstrMapIter->second); - if (aFind != aConstrMap.end()) - myConstraintMap[aSPConstrMapIter->first] = aFind->second; - } - - std::map, Slvs_hEntity>::const_iterator - aSPEntMapIter = theGroup.myEntityMap.begin(); - for ( ; aSPEntMapIter != theGroup.myEntityMap.end(); aSPEntMapIter++) { - std::map::iterator aFind = anEntityMap.find(aSPEntMapIter->second); - if (aFind != anEntityMap.end()) - myEntityMap[aSPEntMapIter->first] = aFind->second; - } - - // Add temporary constraints - std::list::const_iterator aTempConstrIter = theGroup.myTempConstraints.begin(); - for ( ; aTempConstrIter != theGroup.myTempConstraints.end(); aTempConstrIter++) - { - std::map::iterator aFind = aConstrMap.find(*aTempConstrIter); - if (aFind != aConstrMap.end()) - myTempConstraints.push_back(aFind->second); - } - myTempConstraints.sort(); - - myNeedToSolve = myNeedToSolve || theGroup.myNeedToSolve; -} - -// ============================================================================ -// Function: updateGroup -// Class: SketchSolver_ConstraintGroup -// Purpose: search removed entities and constraints -// ============================================================================ -bool SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateGroup() -{ - // Check for valid sketch - if (!mySketch->data()->isValid()) - return true; - - // Fast check for constraint validity. If all constraints are valid, no need to update the group - std::map, Slvs_hConstraint>::reverse_iterator - aConstrIter = myConstraintMap.rbegin(); - bool isAllValid = true; - for ( ; isAllValid && aConstrIter != myConstraintMap.rend(); aConstrIter++) - if (!aConstrIter->first->data()->isValid()) - isAllValid = false; - if (isAllValid) - return false; - - // Remove invalid constraints. - // There only constraint will be deleted (parameters and entities) will be removed below - std::list< boost::shared_ptr > aConstrToDelete; - std::map anEntToDelete; // entities will be removed if no valid constraints use them - for (aConstrIter = myConstraintMap.rbegin(); aConstrIter != myConstraintMap.rend(); aConstrIter++) - { - bool isValid = aConstrIter->first->data()->isValid(); - - int aConstrPos = Search(aConstrIter->second, myConstraints); - if (aConstrPos < (int)myConstraints.size()) - { - Slvs_hEntity aConstrEnt[] = { - myConstraints[aConstrPos].ptA, myConstraints[aConstrPos].ptB, - myConstraints[aConstrPos].entityA, myConstraints[aConstrPos].entityB}; - for (int i = 0; i < 4; i++) - if (aConstrEnt[i] != SLVS_E_UNKNOWN) - { - if (anEntToDelete.find(aConstrEnt[i]) == anEntToDelete.end()) - anEntToDelete[aConstrEnt[i]] = !isValid; - else if (isValid) // constraint is valid => no need to remove its entities - anEntToDelete[aConstrEnt[i]] = false; - } - if (!isValid) - { - myConstraints.erase(myConstraints.begin() + aConstrPos); - if (aConstrIter->second == myConstrMaxID) // When the constraint with highest ID is removed, decrease indexer - myConstrMaxID--; - aConstrToDelete.push_front(aConstrIter->first); - } - } - } - std::list< boost::shared_ptr >::iterator aDelIter; - for (aDelIter = aConstrToDelete.begin(); aDelIter != aConstrToDelete.end(); aDelIter++) - myConstraintMap.erase(*aDelIter); - - // Remove invalid and unused entities - std::map::reverse_iterator aEDelIter; - for (aEDelIter = anEntToDelete.rbegin(); aEDelIter != anEntToDelete.rend(); aEDelIter++) - if (aEDelIter->second) - { - int anEntPos = Search(aEDelIter->first, myEntities); - std::vector::iterator aEntIter = myEntities.begin() + anEntPos; - // Number of parameters for the entity - int aNbParams = 0; - while (aEntIter->param[aNbParams]) aNbParams++; - if (aNbParams == 0) continue; - // Decrease parameter indexer if there are deleted parameter with higher IDs - if (aEntIter->param[aNbParams-1] == myParamMaxID) - myParamMaxID -= aNbParams; - // Remove parameters of the entity - int aParamPos = Search(aEntIter->param[0], myParams); - myParams.erase(myParams.begin() + aParamPos, - myParams.begin() + aParamPos + aNbParams); - - // Remove entity - if (aEDelIter->first == myEntityMaxID) - myEntityMaxID--; - myEntities.erase(myEntities.begin() + anEntPos); - // Remove such entity from myEntityMap - std::map, Slvs_hEntity>::iterator - anEntMapIter = myEntityMap.begin(); - for ( ; anEntMapIter != myEntityMap.end(); anEntMapIter++) - if (anEntMapIter->second == aEDelIter->first) - break; - if (anEntMapIter != myEntityMap.end()) - myEntityMap.erase(anEntMapIter); - } - - return false; -} - -// ============================================================================ -// Function: updateAttribute -// Class: SketchSolver_ConstraintGroup -// Purpose: update features of sketch after resolving constraints -// ============================================================================ -void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateAttribute( - boost::shared_ptr theAttribute, - const Slvs_hEntity& theEntityID) -{ - // Search the position of the first parameter of the entity - int anEntPos = Search(theEntityID, myEntities); - int aFirstParamPos = Search(myEntities[anEntPos].param[0], myParams); - - // Look over supported types of entities - - // Point in 3D - boost::shared_ptr aPoint = - boost::dynamic_pointer_cast(theAttribute); - if (aPoint) - { - aPoint->setValue(myParams[aFirstParamPos].val, - myParams[aFirstParamPos+1].val, - myParams[aFirstParamPos+2].val); - return ; - } - - // Point in 2D - boost::shared_ptr aPoint2D = - boost::dynamic_pointer_cast(theAttribute); - if (aPoint2D) - { - aPoint2D->setValue(myParams[aFirstParamPos].val, - myParams[aFirstParamPos+1].val); - return ; - } - - // Scalar value - boost::shared_ptr aScalar = - boost::dynamic_pointer_cast(theAttribute); - if (aScalar) - { - aScalar->setValue(myParams[aFirstParamPos].val); - return ; - } - - /// \todo Support other types of entities -} - -// ============================================================================ -// Function: updateEntityIfPossible -// Class: SketchSolver_ConstraintGroup -// Purpose: search the entity in this group and update it -// ============================================================================ -void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::updateEntityIfPossible( - boost::shared_ptr theEntity) -{ - if (myEntityMap.find(theEntity) != myEntityMap.end()) - { - // If the attribute is a point and it is changed (the group needs to rebuild), - // probably user has dragged this point into this position, - // so it is necessary to add constraint which will guarantee the point will not change - - // Store myNeedToSolve flag to verify the entity is really changed - bool aNeedToSolveCopy = myNeedToSolve; - myNeedToSolve = false; - - changeEntity(theEntity); - - if (myNeedToSolve) // the entity is changed - { - // Verify the entity is a point and add temporary constraint of permanency - boost::shared_ptr aPoint = - boost::dynamic_pointer_cast(theEntity); - boost::shared_ptr aPoint2D = - boost::dynamic_pointer_cast(theEntity); - if (aPoint || aPoint2D) - addTemporaryConstraintWhereDragged(theEntity); - } - - // Restore flag of changes - myNeedToSolve = myNeedToSolve || aNeedToSolveCopy; - } -} - -// ============================================================================ -// Function: addTemporaryConstraintWhereDragged -// Class: SketchSolver_ConstraintGroup -// Purpose: add transient constraint SLVS_C_WHERE_DRAGGED for the entity, -// which was moved by user -// ============================================================================ -void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::addTemporaryConstraintWhereDragged( - boost::shared_ptr theEntity) -{ - // Find identifier of the entity - std::map, Slvs_hEntity>::const_iterator - anEntIter = myEntityMap.find(theEntity); - - // Create SLVS_C_WHERE_DRAGGED constraint - Slvs_Constraint aWDConstr = Slvs_MakeConstraint(++myConstrMaxID, myID, SLVS_C_WHERE_DRAGGED, - myWorkplane.h, 0.0, anEntIter->second, 0, 0, 0); - myConstraints.push_back(aWDConstr); - myTempConstraints.push_back(aWDConstr.h); -} - -// ============================================================================ -// Function: removeTemporaryConstraints -// Class: SketchSolver_ConstraintGroup -// Purpose: remove all transient SLVS_C_WHERE_DRAGGED constraints after -// resolving the set of constraints -// ============================================================================ -void SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup::removeTemporaryConstraints() -{ - std::list::reverse_iterator aTmpConstrIter; - for (aTmpConstrIter = myTempConstraints.rbegin(); aTmpConstrIter != myTempConstraints.rend(); aTmpConstrIter++) - { - int aConstrPos = Search(*aTmpConstrIter, myConstraints); - myConstraints.erase(myConstraints.begin() + aConstrPos); - - // If the removing constraint has higher index, decrease the indexer - if (*aTmpConstrIter == myConstrMaxID) - myConstrMaxID--; - } - myTempConstraints.clear(); -} - - - -// ======================================================== -// ========= Auxiliary functions =============== -// ======================================================== - -template -int Search(const uint32_t& theEntityID, const std::vector& theEntities) -{ - int aResIndex = theEntityID <= theEntities.size() ? theEntityID - 1 : 0; - int aVecSize = theEntities.size(); - while (aResIndex >= 0 && theEntities[aResIndex].h > theEntityID) - aResIndex--; - while (aResIndex < aVecSize && theEntities[aResIndex].h < theEntityID) - aResIndex++; - if (aResIndex == -1) - aResIndex = aVecSize; - return aResIndex; -} diff --git a/src/SketchSolver/SketchSolver_ConstraintManager.h b/src/SketchSolver/SketchSolver_ConstraintManager.h index 548d20f10..a12cc6d5d 100644 --- a/src/SketchSolver/SketchSolver_ConstraintManager.h +++ b/src/SketchSolver/SketchSolver_ConstraintManager.h @@ -7,6 +7,7 @@ #include "SketchSolver.h" #include +#include #include #include @@ -84,8 +85,6 @@ protected: void resolveConstraints(); private: - class SketchSolver_ConstraintGroup; - /** \brief Searches list of groups which interact with specified constraint * \param[in] theConstraint constraint to be found * \param[out] theGroups list of group indexes interacted with constraint @@ -105,155 +104,4 @@ private: std::vector myGroups; ///< Groups of constraints }; - -/** \class SketchSolver_ConstraintGroup - * \ingroup DataModel - * \brief Keeps the group of constraints which based on the same entities - */ -class SketchSolver_ConstraintManager::SketchSolver_ConstraintGroup -{ -public: - /** \brief New group based on specified workplane. - * Throws an exception if theWorkplane is not an object of SketchPlugin_Sketch type - * \remark Type of theSketch is not verified inside - */ - SketchSolver_ConstraintGroup(boost::shared_ptr theWorkplane); - - ~SketchSolver_ConstraintGroup(); - - /// \brief Returns group's unique identifier - inline const Slvs_hGroup& getId() const - {return myID;} - - /// \brief Returns true if the group has no constraints yet - inline bool isEmpty() const - {return myConstraints.empty();} - - /** \brief Adds or updates a constraint in the group - * \param[in] theConstraint constraint to be changed - * \return \c true if the constraint added or updated successfully - */ - bool changeConstraint(boost::shared_ptr theConstraint); - - /** \brief Verifies the constraint uses the objects from this group - * \param[in] theConstraint constraint for verification of interaction - * \return \c true if the constrained objects are used in current group - */ - bool isInteract(boost::shared_ptr theConstraint) const; - - /** \brief Verifies the specified feature is equal to the base workplane for this group - * \param[in] theWorkplane the feature to be compared with base workplane - * \return \c true if workplanes are the same - */ - bool isBaseWorkplane(boost::shared_ptr theWorkplane) const; - - boost::shared_ptr getWorkplane() const - { return mySketch; } - - /** \brief Update parameters of workplane. Should be called when Update event is coming. - * \return \c true if workplane updated successfully, \c false if workplane parameters are not consistent - */ - bool updateWorkplane(); - - /** \brief If the entity is in this group it will updated - * \param[in] theEntity attribute, which values should update SolveSpace entity - */ - void updateEntityIfPossible(boost::shared_ptr theEntity); - - /** \brief Searches invalid features and constraints in the group and avoids them - * \return \c true if the group's sketch is invalid and the group should be removed - */ - bool updateGroup(); - - /** \brief Add specified group to this one - * \param[in] theGroup group of constraint to be added - */ - void mergeGroups(const SketchSolver_ConstraintGroup& theGroup); - - /** \brief Start solution procedure if necessary and update attributes of features - */ - void resolveConstraints(); - -protected: - /** \brief Adds or updates an entity in the group - * - * The parameters of entity will be parsed and added to the list of SolveSpace parameters. - * Parameters of certain entity will be placed sequentially in the list. - * - * \param[in] theEntity the object of constraint - * \return identifier of changed entity or 0 if entity could not be changed - */ - Slvs_hEntity changeEntity(boost::shared_ptr theEntity); - - /** \brief Adds or updates a normal in the group - * - * Normal is a special entity in SolveSpace, which defines a direction in 3D and - * a rotation about this direction. So, SolveSpace represents normals as unit quaternions. - * - * To define a normal there should be specified two coordinate axis - * on the plane transversed to created normal. - * - * \param[in] theDirX first coordinate axis of the plane - * \param[in] theDirY second coordinate axis of the plane - * \param[in] theNorm attribute for the normal (used to identify newly created entity) - * \return identifier of created or updated normal - */ - Slvs_hEntity changeNormal(boost::shared_ptr theDirX, - boost::shared_ptr theDirY, - boost::shared_ptr theNorm); - - /** \brief Adds or updates a parameter in the group - * \param[in] theParam the value of parameter - * \param[in] thePrmIter the cell in the list of parameters which should be changed - * (the iterator will be increased if it does not reach the end of the list) - * \return identifier of changed parameter; when the parameter cannot be created, returned ID is 0 - */ - Slvs_hParam changeParameter(const double& theParam, - std::vector::const_iterator& thePrmIter); - - /** \brief Change values of attribute by parameters received from SolveSpace solver - * \param[in,out] theAttribute pointer to the attribute to be changed - * \param[in] theEntityID identifier of SolveSpace entity, which contains updated data - */ - void updateAttribute(boost::shared_ptr theAttribute, const Slvs_hEntity& theEntityID); - - /** \brief Adds a constraint for a point which should not be changed during computations - * \param[in] theEntity the base for the constraint - */ - void addTemporaryConstraintWhereDragged(boost::shared_ptr theEntity); - - /** \brief Remove all temporary constraint after computation finished - */ - void removeTemporaryConstraints(); - -private: - /** \brief Creates a workplane from the sketch parameters - * \param[in] theSketch parameters of workplane are the attributes of this sketch - * \return \c true if success, \c false if workplane parameters are not consistent - */ - bool addWorkplane(boost::shared_ptr theSketch); - -private: - // SolveSpace entities - Slvs_hGroup myID; ///< the index of the group - Slvs_Entity myWorkplane; ///< Workplane for the current group - std::vector myParams; ///< List of parameters of the constraints - Slvs_hParam myParamMaxID; ///< Actual maximal ID of parameters (not equal to myParams size) - std::vector myEntities; ///< List of entities of the constaints - Slvs_hEntity myEntityMaxID; ///< Actual maximal ID of entities (not equal to myEntities size) - std::vector myConstraints; ///< List of constraints in SolveSpace format - Slvs_hConstraint myConstrMaxID; ///< Actual maximal ID of constraints (not equal to myConstraints size) - bool myNeedToSolve; ///< Indicator that something changed in the group and constraint system need to be rebuilt - - SketchSolver_Solver myConstrSolver; ///< Solver for set of equations obtained by constraints - - // SketchPlugin entities - boost::shared_ptr mySketch; ///< Equivalent to workplane - std::map, Slvs_hConstraint> - myConstraintMap; ///< The map between SketchPlugin and SolveSpace constraints - std::list myTempConstraints; ///< The list of identifiers of temporary constraints - std::map, Slvs_hEntity> - myEntityMap; ///< The map between parameters of constraints and their equivalent SolveSpace entities -}; - #endif