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