Salome HOME
Fix the problem with unexpected movement of features during Mirror constraint execution
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintMultiRotation.cpp
1 #include <SketchSolver_ConstraintMultiRotation.h>
2 #include <SketchSolver_Group.h>
3 #include <SketchSolver_Error.h>
4
5 #include <SketchPlugin_Arc.h>
6 #include <SketchPlugin_MultiRotation.h>
7
8 #include <ModelAPI_AttributeDouble.h>
9 #include <ModelAPI_AttributeRefAttr.h>
10 #include <ModelAPI_AttributeRefList.h>
11 #include <ModelAPI_ResultConstruction.h>
12
13 #include <GeomAPI_Dir2d.h>
14 #include <GeomAPI_XY.h>
15
16 #include <math.h>
17
18 void SketchSolver_ConstraintMultiRotation::getAttributes(
19     Slvs_hEntity& theCenter, double& theAngle,
20     std::vector<std::vector<Slvs_hEntity> >& thePoints,
21     std::vector<std::vector<Slvs_hEntity> >& theCircular)
22 {
23   DataPtr aData = myBaseConstraint->data();
24   theAngle = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
25       aData->attribute(SketchPlugin_MultiRotation::ANGLE_ID()))->value();
26
27   AttributePtr aCenterAttr = aData->attribute(SketchPlugin_MultiRotation::CENTER_ID());
28   if (!aCenterAttr || !aCenterAttr->isInitialized()) {
29     myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
30     return;
31   }
32   int aType = SLVS_E_UNKNOWN; // type of created entity
33   Slvs_hEntity anEntityID = myGroup->getAttributeId(aCenterAttr);
34   if (anEntityID == SLVS_E_UNKNOWN)
35     anEntityID = changeEntity(aCenterAttr, aType);
36   theCenter = anEntityID;
37
38   // Lists of objects and number of copies
39   AttributeRefListPtr anInitialRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
40       aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
41   myNumberOfObjects = anInitialRefList->size();
42   myNumberOfCopies = (size_t)std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
43       aData->attribute(SketchPlugin_MultiRotation::NUMBER_OF_COPIES_ID()))->value();
44   AttributeRefListPtr aRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
45       myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
46   if (!aRefList) {
47     myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
48     return;
49   }
50
51   // Obtain all points of initial features and store them into separate lists
52   // containing their translated copies.
53   // Also all circles and arc collected too, because they will be constrained by equal radii.
54   FeaturePtr aFeature;
55   ResultConstructionPtr aRC;
56   std::vector<Slvs_hEntity> aPoints[2]; // lists of points of features
57   std::vector<Slvs_hEntity> aCircs;     // list of circular objects
58   std::list<ObjectPtr> anObjectList = aRefList->list();
59   std::list<ObjectPtr>::iterator anObjectIter = anObjectList.begin();
60   while (anObjectIter != anObjectList.end()) {
61     aPoints[0].clear();
62     aPoints[1].clear();
63     aCircs.clear();
64
65     for (size_t i = 0; i <= myNumberOfCopies && anObjectIter != anObjectList.end(); i++, anObjectIter++) {
66       aFeature = ModelAPI_Feature::feature(*anObjectIter);
67       if (!aFeature)
68         continue;
69       anEntityID = changeEntity(aFeature, aType);
70       Slvs_Entity anEntity = myStorage->getEntity(anEntityID);
71       switch (aType) {
72       case SLVS_E_POINT_IN_2D:
73       case SLVS_E_POINT_IN_3D:
74         aPoints[0].push_back(anEntityID);
75         break;
76       case SLVS_E_LINE_SEGMENT:
77         aPoints[0].push_back(anEntity.point[0]); // start point of line
78         aPoints[1].push_back(anEntity.point[1]); // end point of line
79         break;
80       case SLVS_E_CIRCLE:
81         aPoints[0].push_back(anEntity.point[0]); // center of circle
82         aCircs.push_back(anEntityID);
83         break;
84       case SLVS_E_ARC_OF_CIRCLE:
85         aPoints[0].push_back(anEntity.point[1]); // start point of arc
86         aPoints[1].push_back(anEntity.point[2]); // end point of arc
87         aCircs.push_back(anEntityID);
88         break;
89       default:
90         myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
91         return;
92       }
93     }
94
95     if (!aPoints[0].empty())
96       thePoints.push_back(aPoints[0]);
97     if (!aPoints[1].empty())
98       thePoints.push_back(aPoints[1]);
99     if (!aCircs.empty())
100       theCircular.push_back(aCircs);
101   }
102 }
103
104 void SketchSolver_ConstraintMultiRotation::process()
105 {
106   cleanErrorMsg();
107   if (!myBaseConstraint || !myStorage || myGroup == 0) {
108     /// TODO: Put error message here
109     return;
110   }
111   if (!mySlvsConstraints.empty()) // some data is changed, update constraint
112     update(myBaseConstraint);
113
114   Slvs_hEntity aCenter;
115   std::vector<std::vector<Slvs_hEntity> > aPointsAndCopies;
116   std::vector<std::vector<Slvs_hEntity> > aCircsAndCopies;
117   getAttributes(aCenter, myAngle, aPointsAndCopies, aCircsAndCopies);
118   if (!myErrorMsg.empty())
119     return;
120
121   // Create lines between neighbor rotated points and make angle between them equal to anAngle.
122   // Also these lines should have equal lengths.
123   Slvs_Constraint aConstraint;
124   myRotationCenter = aCenter;
125   Slvs_Entity aPrevLine;
126   std::vector<std::vector<Slvs_hEntity> >::iterator aCopyIter = aPointsAndCopies.begin();
127   for (; aCopyIter != aPointsAndCopies.end(); aCopyIter++) {
128     size_t aSize = aCopyIter->size();
129     if (aSize <= 1) continue;
130
131     aPrevLine = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(),
132           myGroup->getWorkplaneId(), aCenter, (*aCopyIter)[0]);
133     aPrevLine.h = myStorage->addEntity(aPrevLine);
134     for (size_t i = 1; i < aSize; i++) {
135       Slvs_Entity aLine = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(),
136           myGroup->getWorkplaneId(), aCenter, (*aCopyIter)[i]);
137       aLine.h = myStorage->addEntity(aLine);
138       // Equal length constraint
139       aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(),
140           SLVS_C_EQUAL_LENGTH_LINES, myGroup->getWorkplaneId(), 0.0,
141           SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aPrevLine.h, aLine.h);
142       aConstraint.h = myStorage->addConstraint(aConstraint);
143       mySlvsConstraints.push_back(aConstraint.h);
144       // Angle constraint
145       aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(),
146           SLVS_C_ANGLE, myGroup->getWorkplaneId(), fabs(myAngle), SLVS_E_UNKNOWN, SLVS_E_UNKNOWN,
147           aPrevLine.h, aLine.h);
148       if (myAngle < 0.0) // clockwise rotation
149         aConstraint.other = true;
150       aConstraint.h = myStorage->addConstraint(aConstraint);
151       mySlvsConstraints.push_back(aConstraint.h);
152
153       aPrevLine = aLine;
154     }
155   }
156   // Equal radii constraints
157   for (aCopyIter = aCircsAndCopies.begin(); aCopyIter != aCircsAndCopies.end(); aCopyIter++) {
158     size_t aSize = aCopyIter->size();
159     for (size_t i = 0; i < aSize - 1; i++) {
160       aConstraint = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(),
161           SLVS_C_EQUAL_RADIUS, myGroup->getWorkplaneId(), 0.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN,
162           (*aCopyIter)[i], (*aCopyIter)[i+1]);
163       aConstraint.h = myStorage->addConstraint(aConstraint);
164       mySlvsConstraints.push_back(aConstraint.h);
165     }
166   }
167
168   // Set the rotation center unchanged during constraint recalculation
169   if (!myStorage->isPointFixed(myRotationCenter, aConstraint.h, true)) {
170     aConstraint = Slvs_MakeConstraint(
171         SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_WHERE_DRAGGED, myGroup->getWorkplaneId(), 0.0,
172         myRotationCenter, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
173     aConstraint.h = myStorage->addConstraint(aConstraint);
174     mySlvsConstraints.push_back(aConstraint.h);
175   }
176
177   adjustConstraint();
178 }
179
180 void SketchSolver_ConstraintMultiRotation::update(ConstraintPtr theConstraint)
181 {
182   cleanErrorMsg();
183   if (!theConstraint || theConstraint == myBaseConstraint) {
184     AttributeRefListPtr anInitialRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
185         myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
186     AttributeDoublePtr aNbCopies = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
187         myBaseConstraint->attribute(SketchPlugin_MultiRotation::NUMBER_OF_COPIES_ID()));
188     if (anInitialRefList->size() != myNumberOfObjects ||
189         (size_t)aNbCopies->value() != myNumberOfCopies) {
190       remove(myBaseConstraint);
191       process();
192       return;
193     }
194   }
195
196   // update angle value
197   myAngle = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
198       myBaseConstraint->attribute(SketchPlugin_MultiRotation::ANGLE_ID()))->value();
199
200   SketchSolver_Constraint::update();
201 }
202
203 bool SketchSolver_ConstraintMultiRotation::remove(ConstraintPtr theConstraint)
204 {
205   cleanErrorMsg();
206   if (theConstraint && theConstraint != myBaseConstraint)
207     return false;
208   bool isFullyRemoved = true;
209   std::vector<Slvs_hEntity>::iterator aCIter = mySlvsConstraints.begin();
210   for (; aCIter != mySlvsConstraints.end(); aCIter++)
211    isFullyRemoved = myStorage->removeConstraint(*aCIter) && isFullyRemoved;
212   mySlvsConstraints.clear();
213
214   std::map<FeaturePtr, Slvs_hEntity>::iterator aFeatIt = myFeatureMap.begin();
215   for (; aFeatIt != myFeatureMap.end(); aFeatIt++)
216     myStorage->removeEntity(aFeatIt->second);
217
218   if (isFullyRemoved) {
219     myFeatureMap.clear();
220     myAttributeMap.clear();
221     myValueMap.clear();
222   } else
223     cleanRemovedEntities();
224   return true;
225 }
226
227 void SketchSolver_ConstraintMultiRotation::adjustConstraint()
228 {
229   if (abs(myAngle) < tolerance) {
230     myStorage->setNeedToResolve(false);
231     return;
232   }
233
234   // Obtain coordinates of rotation center
235   Slvs_Entity aRotCenter = myStorage->getEntity(myRotationCenter);
236   double aCenterXY[2];
237   for (int i = 0; i < 2; i++)
238     aCenterXY[i] = myStorage->getParameter(aRotCenter.param[i]).val;
239
240   double cosA = cos(myAngle * PI / 180.0);
241   double sinA = sin(myAngle * PI / 180.0);
242
243   // Update positions of all points to satisfy angles
244   std::list<Slvs_Constraint> aConstrAngle = myStorage->getConstraintsByType(SLVS_C_ANGLE);
245   std::list<Slvs_Constraint>::iterator anAngIt = aConstrAngle.begin();
246   std::vector<Slvs_hConstraint>::iterator aCIt;
247   Slvs_hConstraint aFixed; // temporary variable
248   double aVec[2]; // coordinates of vector defining a line
249   Slvs_Param aTarget[2]; // parameter to be changed
250   for (; anAngIt != aConstrAngle.end(); anAngIt++) {
251     for (aCIt = mySlvsConstraints.begin(); aCIt != mySlvsConstraints.end(); aCIt++)
252       if (anAngIt->h == *aCIt)
253         break;
254     if (aCIt == mySlvsConstraints.end())
255       continue;
256     Slvs_Entity aLineA = myStorage->getEntity(anAngIt->entityA);
257     Slvs_Entity aLineB = myStorage->getEntity(anAngIt->entityB);
258     if (myStorage->isPointFixed(aLineB.point[1], aFixed))
259       continue;
260     Slvs_Entity aPointA = myStorage->getEntity(aLineA.point[1]);
261     Slvs_Entity aPointB = myStorage->getEntity(aLineB.point[1]);
262     for (int i = 0; i < 2; i++) {
263       aVec[i] = myStorage->getParameter(aPointA.param[i]).val - aCenterXY[i];
264       aTarget[i] = myStorage->getParameter(aPointB.param[i]);
265     }
266     aTarget[0].val = aCenterXY[0] + aVec[0] * cosA - aVec[1] * sinA;
267     aTarget[1].val = aCenterXY[1] + aVec[0] * sinA + aVec[1] * cosA;
268     myStorage->updateParameter(aTarget[0]);
269     myStorage->updateParameter(aTarget[1]);
270
271     anAngIt->valA = myAngle;
272     myStorage->updateConstraint(*anAngIt);
273   }
274
275   // update positions of centers of arcs for correct radius calculation
276   std::list<Slvs_Constraint> aRadii = myStorage->getConstraintsByType(SLVS_C_EQUAL_RADIUS);
277   std::map<FeaturePtr, Slvs_hEntity>::iterator aFeatIt;
278   for (anAngIt = aRadii.begin(); anAngIt != aRadii.end(); anAngIt++) {
279     int aNbFound = 0; // number of arcs used in translation
280     for (aFeatIt = myFeatureMap.begin(); aFeatIt != myFeatureMap.end(); aFeatIt++)
281       if (aFeatIt->second == anAngIt->entityA || aFeatIt->second == anAngIt->entityB) {
282         if (aFeatIt->first->getKind() == SketchPlugin_Arc::ID())
283           aNbFound++;
284         else
285           break;
286       }
287     if (aNbFound != 2)
288       continue;
289     // two arcs were found, update their centers
290     Slvs_Entity anArcA = myStorage->getEntity(anAngIt->entityA);
291     Slvs_Entity anArcB = myStorage->getEntity(anAngIt->entityB);
292     if (myStorage->isPointFixed(anArcB.point[0], aFixed))
293       continue;
294     Slvs_Entity aCenterA = myStorage->getEntity(anArcA.point[0]);
295     Slvs_Entity aCenterB = myStorage->getEntity(anArcB.point[0]);
296     for (int i = 0; i < 2; i++) {
297       aVec[i] = myStorage->getParameter(aCenterA.param[i]).val - aCenterXY[i];
298       aTarget[i] = myStorage->getParameter(aCenterB.param[i]);
299     }
300     aTarget[0].val = aCenterXY[0] + aVec[0] * cosA - aVec[1] * sinA;
301     aTarget[1].val = aCenterXY[1] + aVec[0] * sinA + aVec[1] * cosA;
302     myStorage->updateParameter(aTarget[0]);
303     myStorage->updateParameter(aTarget[1]);
304   }
305 }