Salome HOME
Update constrain Mirror to keep non-zero the length of mirror line (issue #809)
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintMirror.cpp
1 #include <SketchSolver_ConstraintMirror.h>
2 #include <SketchSolver_Group.h>
3 #include <SketchSolver_Error.h>
4
5 #include <ModelAPI_AttributeDouble.h>
6 #include <ModelAPI_AttributeRefAttr.h>
7 #include <ModelAPI_AttributeRefList.h>
8 #include <ModelAPI_ResultConstruction.h>
9
10 #include <GeomAPI_Dir2d.h>
11 #include <GeomAPI_XY.h>
12
13 #include <cmath>
14
15 void SketchSolver_ConstraintMirror::getAttributes(
16     Slvs_Entity& theMirrorLine,
17     std::vector<Slvs_Entity>& theBaseEntities,
18     std::vector<Slvs_Entity>& theMirrorEntities)
19 {
20   DataPtr aData = myBaseConstraint->data();
21   AttributeRefAttrPtr aMirLineAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
22       myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
23   if (!aMirLineAttr || !aMirLineAttr->isInitialized() || !aMirLineAttr->isObject()) {
24     myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
25     return;
26   }
27   int aType = SLVS_E_UNKNOWN; // type of created entity
28   Slvs_hEntity anEntity = myGroup->getAttributeId(aMirLineAttr);
29   if (anEntity == SLVS_E_UNKNOWN)
30     anEntity = changeEntity(aMirLineAttr, aType);
31   theMirrorLine = myStorage->getEntity(anEntity);
32
33   // Create SolveSpace entity for all features
34   AttributeRefListPtr aBaseRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
35       myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
36   AttributeRefListPtr aMirroredRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
37       myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_C()));
38   myNumberOfObjects = aMirroredRefList->size();
39   if (!aBaseRefList || !aMirroredRefList) {
40     myErrorMsg = SketchSolver_Error::INCORRECT_MIRROR_ATTRIBUTE();
41     return;
42   }
43
44   std::list<ObjectPtr> aBaseList = aBaseRefList->list();
45   std::list<ObjectPtr> aMirroredList = aMirroredRefList->list();
46
47   FeaturePtr aFeature;
48   ResultConstructionPtr aRC;
49   for (int i = 0; i < 2; i++) {
50     std::list<ObjectPtr>::iterator anIter = i == 0 ? aBaseList.begin() : aMirroredList.begin();
51     std::list<ObjectPtr>::iterator aEndIter = i == 0 ? aBaseList.end() : aMirroredList.end();
52     std::vector<Slvs_Entity>* aList = i == 0 ? &theBaseEntities : & theMirrorEntities;
53     for ( ; anIter != aEndIter; anIter++) {
54       aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*anIter);
55       aFeature = aRC ? aRC->document()->feature(aRC) :
56         std::dynamic_pointer_cast<ModelAPI_Feature>(*anIter);
57       if (!aFeature)
58         continue;
59
60       anEntity = changeEntity(aFeature, aType);
61       // Sort entities by their type
62       std::vector<Slvs_Entity>::iterator anIt = aList->begin();
63       for (; anIt != aList->end(); anIt++)
64         if (aType < anIt->type)
65           break;
66 //      aList->push_back(myStorage->getEntity(anEntity));
67       aList->insert(anIt, myStorage->getEntity(anEntity));
68     }
69   }
70
71   if (theBaseEntities.size() > theMirrorEntities.size())
72     myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
73 }
74
75 void SketchSolver_ConstraintMirror::process()
76 {
77   cleanErrorMsg();
78   if (!myBaseConstraint || !myStorage || myGroup == 0) {
79     /// TODO: Put error message here
80     return;
81   }
82   if (!mySlvsConstraints.empty()) // some data is changed, update constraint
83     update(myBaseConstraint);
84
85   Slvs_Entity aMirrorLine;
86   std::vector<Slvs_Entity> aBaseList;
87   std::vector<Slvs_Entity> aMirrorList;
88   getAttributes(aMirrorLine, aBaseList, aMirrorList);
89   if (!myErrorMsg.empty())
90     return;
91
92   if (aBaseList.size() != aMirrorList.size()) {
93     myErrorMsg = SketchSolver_Error::INCORRECT_MIRROR_ATTRIBUTE();
94     return;
95   }
96
97   Slvs_Constraint aConstraint;
98   // Get coordinates of mirror line points for speed up
99   double aStartEnd[4];
100   for (int i = 0; i < 2; i++) {
101     Slvs_Entity aPoint = myStorage->getEntity(aMirrorLine.point[i]);
102     for (int j = 0; j < 2; j++)
103       aStartEnd[2*i+j] = myStorage->getParameter(aPoint.param[j]).val;
104   }
105
106   std::vector<Slvs_Entity>::iterator aBaseIter = aBaseList.begin();
107   std::vector<Slvs_Entity>::iterator aMirrorIter = aMirrorList.begin();
108   for (; aBaseIter != aBaseList.end(); aBaseIter++, aMirrorIter++) {
109     // Make aMirrorEnt parameters to be symmetric with aBaseEnt
110     makeMirrorEntity(*aBaseIter, *aMirrorIter, aStartEnd);
111
112     if (aBaseIter->type == SLVS_E_POINT_IN_2D) {
113       aConstraint = Slvs_MakeConstraint(
114           SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(),
115           0.0, aBaseIter->h, aMirrorIter->h, aMirrorLine.h, SLVS_E_UNKNOWN);
116       aConstraint.h = myStorage->addConstraint(aConstraint);
117       mySlvsConstraints.push_back(aConstraint.h);
118     } else if (aBaseIter->type == SLVS_E_LINE_SEGMENT) {
119       for (int i = 0; i < 2; i++) {
120         aConstraint = Slvs_MakeConstraint(
121             SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
122             aBaseIter->point[i], aMirrorIter->point[i], aMirrorLine.h, SLVS_E_UNKNOWN);
123         aConstraint.h = myStorage->addConstraint(aConstraint);
124         mySlvsConstraints.push_back(aConstraint.h);
125       }
126     } else if (aBaseIter->type == SLVS_E_CIRCLE) {
127       aConstraint = Slvs_MakeConstraint(
128           SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
129           aBaseIter->point[0], aMirrorIter->point[0], aMirrorLine.h, SLVS_E_UNKNOWN);
130       aConstraint.h = myStorage->addConstraint(aConstraint);
131       mySlvsConstraints.push_back(aConstraint.h);
132       // Additional constraint for equal radii
133       Slvs_Constraint anEqRadConstr = Slvs_MakeConstraint(
134           SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_EQUAL_RADIUS, myGroup->getWorkplaneId(),
135           0.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aBaseIter->h, aMirrorIter->h);
136       anEqRadConstr.h = myStorage->addConstraint(anEqRadConstr);
137       mySlvsConstraints.push_back(anEqRadConstr.h);
138     } else if (aBaseIter->type == SLVS_E_ARC_OF_CIRCLE) {
139       // Workaround to avoid problems in SolveSpace.
140       // The symmetry of two arcs will be done using symmetry of three points on these arcs:
141       // start point, end point, and any other point on the arc
142       Slvs_hEntity aBaseArcPoints[3] = {
143           aBaseIter->point[1],
144           aBaseIter->point[2],
145           SLVS_E_UNKNOWN};
146       Slvs_hEntity aMirrorArcPoints[3] = { // indices of points of arc, center corresponds center, first point corresponds last point
147           aMirrorIter->point[2],
148           aMirrorIter->point[1],
149           SLVS_E_UNKNOWN};
150
151       Slvs_Entity aBothArcs[2] = {*aBaseIter, *aMirrorIter};
152       Slvs_hEntity aBothMiddlePoints[2];
153       for (int i = 0; i < 2; i++) {
154         double x, y;
155         calculateMiddlePoint(aBothArcs[i], 0.5, x, y);
156         Slvs_Param aParamX = Slvs_MakeParam(SLVS_E_UNKNOWN, myGroup->getId(), x);
157         Slvs_Param aParamY = Slvs_MakeParam(SLVS_E_UNKNOWN, myGroup->getId(), y);
158         aParamX.h = myStorage->addParameter(aParamX);
159         aParamY.h = myStorage->addParameter(aParamY);
160         Slvs_Entity aPoint = Slvs_MakePoint2d(SLVS_E_UNKNOWN, myGroup->getId(),
161             myGroup->getWorkplaneId(), aParamX.h, aParamY.h);
162         aBothMiddlePoints[i] = myStorage->addEntity(aPoint);
163         // additional constraint point-on-curve
164         Slvs_Constraint aPonCircConstr = Slvs_MakeConstraint(
165             SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_PT_ON_CIRCLE, myGroup->getWorkplaneId(),
166             0.0, aBothMiddlePoints[i], SLVS_E_UNKNOWN, aBothArcs[i].h, SLVS_E_UNKNOWN);
167         aPonCircConstr.h = myStorage->addConstraint(aPonCircConstr);
168         mySlvsConstraints.push_back(aPonCircConstr.h);
169         if (i == 0) {
170           // additional constraint for the point to be in the middle of a base arc
171           Slvs_Entity aLine1 = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(),
172               myGroup->getWorkplaneId(), aBothArcs[i].point[1], aBothMiddlePoints[i]);
173           aLine1.h = myStorage->addEntity(aLine1);
174           Slvs_Entity aLine2 = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(),
175               myGroup->getWorkplaneId(), aBothArcs[i].point[2], aBothMiddlePoints[i]);
176           aLine2.h = myStorage->addEntity(aLine2);
177           Slvs_Constraint aMiddleConstr = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(),
178               SLVS_C_EQUAL_LENGTH_LINES, myGroup->getWorkplaneId(),
179               0.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aLine1.h, aLine2.h);
180           aMiddleConstr.h = myStorage->addConstraint(aMiddleConstr);
181           mySlvsConstraints.push_back(aMiddleConstr.h);
182         }
183       }
184
185       aBaseArcPoints[2] = aBothMiddlePoints[0];
186       aMirrorArcPoints[2] = aBothMiddlePoints[1];
187       for (int ind = 0; ind < 3; ind++) {
188         Slvs_Constraint aConstraint = Slvs_MakeConstraint(
189             SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
190             aBaseArcPoints[ind], aMirrorArcPoints[ind], aMirrorLine.h, SLVS_E_UNKNOWN);
191         aConstraint.h = myStorage->addConstraint(aConstraint);
192         mySlvsConstraints.push_back(aConstraint.h);
193       }
194     }
195   }
196 }
197
198
199 void SketchSolver_ConstraintMirror::update(ConstraintPtr theConstraint)
200 {
201   cleanErrorMsg();
202   if (!theConstraint || theConstraint == myBaseConstraint) {
203     AttributeRefListPtr aMirroredRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
204         myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_C()));
205     if (aMirroredRefList->size() != myNumberOfObjects) {
206       remove(myBaseConstraint);
207       process();
208       return;
209     }
210   }
211   SketchSolver_Constraint::update();
212 }
213
214 bool SketchSolver_ConstraintMirror::remove(ConstraintPtr theConstraint)
215 {
216   cleanErrorMsg();
217   if (theConstraint && theConstraint != myBaseConstraint)
218     return false;
219   bool isFullyRemoved = true;
220   std::vector<Slvs_hEntity>::iterator aCIter = mySlvsConstraints.begin();
221   for (; aCIter != mySlvsConstraints.end(); aCIter++)
222    isFullyRemoved = myStorage->removeConstraint(*aCIter) && isFullyRemoved;
223   mySlvsConstraints.clear();
224
225   std::map<FeaturePtr, Slvs_hEntity>::iterator aFeatIt = myFeatureMap.begin();
226   for (; aFeatIt != myFeatureMap.end(); aFeatIt++)
227     myStorage->removeEntity(aFeatIt->second);
228
229   if (isFullyRemoved) {
230     myFeatureMap.clear();
231     myAttributeMap.clear();
232     myValueMap.clear();
233   } else
234     cleanRemovedEntities();
235   return true;
236 }
237
238 bool SketchSolver_ConstraintMirror::checkAttributesChanged(ConstraintPtr theConstraint)
239 {
240   // First of all, check the mirror line is changed.
241   // It may be changed to one of mirrored lines, which is already in this constraint
242   // (this case is not marked as attribute changing)
243   ConstraintPtr aConstraint = theConstraint ? theConstraint : myBaseConstraint;
244   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
245       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
246   if (!aRefAttr || !aRefAttr->isObject() || !aRefAttr->object())
247     return true;
248   FeaturePtr aMirrorLine = ModelAPI_Feature::feature(aRefAttr->object());
249   if (!aMirrorLine)
250     return true;
251
252   std::map<FeaturePtr, Slvs_hEntity>::iterator aMirrorIter = myFeatureMap.find(aMirrorLine);
253   if (aMirrorIter == myFeatureMap.end())
254     return true;
255
256   // Check the entity is not used as mirror line
257   std::vector<Slvs_hConstraint>::iterator aCIter = mySlvsConstraints.begin();
258   for (; aCIter != mySlvsConstraints.end(); aCIter++) {
259     Slvs_Constraint aMirrorConstr = myStorage->getConstraint(*aCIter);
260     if (aMirrorConstr.type != SLVS_C_SYMMETRIC_LINE)
261       continue;
262     if (aMirrorConstr.entityA != aMirrorIter->second)
263       return true;
264     else break; // check just one symmetric constraint
265   }
266
267   // Base verification
268   return SketchSolver_Constraint::checkAttributesChanged(theConstraint);
269 }
270
271 void SketchSolver_ConstraintMirror::makeMirrorEntity(
272     const Slvs_Entity& theBase,
273     const Slvs_Entity& theMirror,
274     const double theMirrorLine[]) const
275 {
276   Slvs_hEntity aBasePoint[4];
277   Slvs_hEntity aMirrorPoint[4];
278   for (int i = 0; i < 4; i++) {
279     aBasePoint[i] = theBase.point[i];
280     aMirrorPoint[i] = theMirror.point[i];
281   }
282   if (theBase.type == SLVS_E_ARC_OF_CIRCLE) {
283     Slvs_hEntity aTmp = aMirrorPoint[2];
284     aMirrorPoint[2] = aMirrorPoint[1];
285     aMirrorPoint[1] = aTmp;
286     adjustArcPoints(theBase);
287   }
288   if (theBase.type == SLVS_E_POINT_IN_2D || theBase.type == SLVS_E_POINT_IN_3D) {
289     aBasePoint[0] = theBase.h;
290     aMirrorPoint[0] = theMirror.h;
291   }
292
293   // Mirror line parameters
294   std::shared_ptr<GeomAPI_XY> aLinePoints[2];
295   for (int i = 0; i < 2; i++)
296     aLinePoints[i] = std::shared_ptr<GeomAPI_XY>(
297         new GeomAPI_XY(theMirrorLine[2*i], theMirrorLine[2*i+1]));
298   // direction of a mirror line
299   std::shared_ptr<GeomAPI_Dir2d> aDir = std::shared_ptr<GeomAPI_Dir2d>(
300     new GeomAPI_Dir2d(aLinePoints[1]->added(aLinePoints[0]->multiplied(-1.0))));
301   // orthogonal direction
302   aDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir->y(), -aDir->x()));
303
304   Slvs_hConstraint aFixed; // transient variable
305   for (int i = 0; i < 4; i++) {
306     if (aBasePoint[i] == SLVS_E_UNKNOWN || aMirrorPoint[i] == SLVS_E_UNKNOWN)
307       continue;
308     // check the mirror point is not fixed
309     if (myStorage->isPointFixed(aMirrorPoint[i], aFixed, true))
310       continue;
311
312     Slvs_Entity aPointEnt = myStorage->getEntity(aBasePoint[i]);
313     double aBaseX = myStorage->getParameter(aPointEnt.param[0]).val;
314     double aBaseY = myStorage->getParameter(aPointEnt.param[1]).val;
315     std::shared_ptr<GeomAPI_XY> aPoint = std::shared_ptr<GeomAPI_XY>(new GeomAPI_XY(aBaseX, aBaseY));
316
317     std::shared_ptr<GeomAPI_XY> aVec = std::shared_ptr<GeomAPI_XY>(
318         new GeomAPI_XY(aPoint->x() - aLinePoints[0]->x(), aPoint->y() - aLinePoints[0]->y()));
319     double aDist = aVec->dot(aDir->xy());
320     aPoint = aPoint->added(aDir->xy()->multiplied(-2.0 * aDist));
321
322     Slvs_Entity aMirrorEnt = myStorage->getEntity(aMirrorPoint[i]);
323     Slvs_Param aParam = Slvs_MakeParam(aMirrorEnt.param[0], myGroup->getId(), aPoint->x());
324     myStorage->updateParameter(aParam);
325     aParam = Slvs_MakeParam(aMirrorEnt.param[1], myGroup->getId(), aPoint->y());
326     myStorage->updateParameter(aParam);
327   }
328 }
329
330 void SketchSolver_ConstraintMirror::adjustArcPoints(const Slvs_Entity& theArc) const
331 {
332   Slvs_Param aParam;
333   Slvs_Entity aPoint;
334   double anArcParams[3][2];
335   for (int i = 0; i < 3; i++) {
336     aPoint = myStorage->getEntity(theArc.point[i]);
337     for (int j = 0; j < 2; j++) {
338       aParam = myStorage->getParameter(aPoint.param[j]);
339       anArcParams[i][j] = aParam.val;
340       if (i > 0)
341         anArcParams[i][j] -= anArcParams[0][j];
342     }
343   }
344   double aRad2 = anArcParams[1][0] * anArcParams[1][0] + anArcParams[1][1] * anArcParams[1][1];
345   double aDist2 = anArcParams[2][0] * anArcParams[2][0] + anArcParams[2][1] * anArcParams[2][1];
346   if (std::fabs(aRad2 - aDist2) < tolerance)
347     return; // nothing to update (last point already on the arc)
348   if (aDist2 < tolerance)
349     return; // unable to update
350   double aCoeff = std::sqrt(aRad2 / aDist2);
351   anArcParams[2][0] *= aCoeff;
352   anArcParams[2][1] *= aCoeff;
353
354   // Update last point
355   aPoint = myStorage->getEntity(theArc.point[2]);
356   for (int i = 0; i < 2; i++) {
357     aParam = Slvs_MakeParam(aPoint.param[i], myGroup->getId(),
358         anArcParams[0][i] + anArcParams[2][i]);
359     myStorage->updateParameter(aParam);
360   }
361 }
362
363 void SketchSolver_ConstraintMirror::adjustConstraint()
364 {
365   AttributeRefAttrPtr aMirLineAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
366       myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
367   if (!aMirLineAttr || !aMirLineAttr->isInitialized() || !aMirLineAttr->isObject()) {
368     myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
369     return;
370   }
371   ResultConstructionPtr aRC =
372       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aMirLineAttr->object());
373   FeaturePtr aFeature = aRC ? aRC->document()->feature(aRC) :
374     std::dynamic_pointer_cast<ModelAPI_Feature>(aMirLineAttr->object());
375   std::map<FeaturePtr, Slvs_hEntity>::iterator aMirLineIter = myFeatureMap.find(aFeature);
376   if (aMirLineIter == myFeatureMap.end())
377     return;
378   Slvs_Entity aMirrorLine = myStorage->getEntity(aMirLineIter->second);
379
380   Slvs_Constraint aMirror;
381   double aStartEnd[4];
382   for (int i = 0; i < 2; i++) {
383     Slvs_Entity aPoint = myStorage->getEntity(aMirrorLine.point[i]);
384     for (int j = 0; j < 2; j++)
385       aStartEnd[2*i+j] = myStorage->getParameter(aPoint.param[j]).val;
386   }
387
388   // Calculate length of the mirror line and create temporary constraint
389   double aLength = sqrt((aStartEnd[2] - aStartEnd[0]) * (aStartEnd[2] - aStartEnd[0]) +
390                         (aStartEnd[3] - aStartEnd[1]) * (aStartEnd[3] - aStartEnd[1]));
391   if (aLength < tolerance) {
392     if (myMirrorLineLength < 1.0)
393       myMirrorLineLength = 1.0;
394   } else
395     myMirrorLineLength = aLength;
396   std::list<Slvs_Constraint> aDist = myStorage->getConstraintsByType(SLVS_C_PT_PT_DISTANCE);
397   std::list<Slvs_Constraint>::const_iterator aDIt = aDist.begin();
398   for (; aDIt != aDist.end(); ++aDIt)
399     if ((aDIt->ptA == aMirrorLine.point[0] && aDIt->ptB == aMirrorLine.point[1]) ||
400         (aDIt->ptA == aMirrorLine.point[1] && aDIt->ptB == aMirrorLine.point[0]))
401       break; // length of mirror line is already set
402   if (aDIt == aDist.end()) {
403     // check the points of mirror line is not fixed
404     Slvs_hConstraint aFixed;
405     if (!myStorage->isPointFixed(aMirrorLine.point[0], aFixed, true) ||
406         !myStorage->isPointFixed(aMirrorLine.point[1], aFixed, true)) {
407       // Add length constraint
408       aMirror = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_PT_PT_DISTANCE,
409           myGroup->getWorkplaneId(), aLength, aMirrorLine.point[0], aMirrorLine.point[1],
410           SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
411       aMirror.h = myStorage->addConstraint(aMirror);
412       myStorage->addTemporaryConstraint(aMirror.h);
413     }
414   }
415
416   // Search mirror between middle points on the arcs and recompute their coordinates
417   std::map<Slvs_hEntity, Slvs_hEntity> aPointsOnCircles;
418   std::list<Slvs_Constraint> aMirrorPonCirc;
419   std::list<Slvs_Constraint> aPonCirc = myStorage->getConstraintsByType(SLVS_C_PT_ON_CIRCLE);
420   std::vector<Slvs_hConstraint>::iterator aConstrIter = mySlvsConstraints.begin();
421   for (; aConstrIter != mySlvsConstraints.end(); aConstrIter++) {
422     aMirror = myStorage->getConstraint(*aConstrIter);
423     if (aMirror.type != SLVS_C_SYMMETRIC_LINE)
424       continue;
425     if (aMirror.entityA != aMirrorLine.h)
426       continue; // don't update another Mirror constraints
427     Slvs_Constraint aPonCircA, aPonCircB;
428     aPonCircA.h = SLVS_E_UNKNOWN;
429     aPonCircB.h = SLVS_E_UNKNOWN;
430     std::list<Slvs_Constraint>::iterator aPtIter = aPonCirc.begin();
431     for (; aPtIter != aPonCirc.end(); aPtIter++) {
432       if (aMirror.ptA == aPtIter->ptA)
433         aPonCircA = *aPtIter;
434       if (aMirror.ptB == aPtIter->ptA)
435         aPonCircB = *aPtIter;
436     }
437     if (aPonCircA.h == SLVS_E_UNKNOWN || aPonCircB.h == SLVS_E_UNKNOWN)
438       continue;
439     aMirrorPonCirc.push_back(aMirror);
440     // Store point IDs to avoid their recalculation twice
441     aPointsOnCircles[aPonCircA.ptA] = aPonCircA.entityA;
442     aPointsOnCircles[aPonCircB.ptA] = aPonCircB.entityA;
443   }
444
445   // Recalculate positions of mirroring points
446   std::list<Slvs_Constraint> aMirrorList = myStorage->getConstraintsByType(SLVS_C_SYMMETRIC_LINE);
447   std::list<Slvs_Constraint>::iterator aMirIter = aMirrorList.begin();
448   for (; aMirIter != aMirrorList.end(); aMirIter++) {
449     if (aMirIter->entityA != aMirrorLine.h)
450       continue; // don't update another Mirror constraints
451     if (aPointsOnCircles.find(aMirIter->ptA) != aPointsOnCircles.end())
452       continue; // Avoid mirroring points on circles
453     Slvs_Entity aBase = myStorage->getEntity(aMirIter->ptA);
454     Slvs_Entity aMirror = myStorage->getEntity(aMirIter->ptB);
455     makeMirrorEntity(aBase, aMirror, aStartEnd);
456   }
457
458   bool aNeedToResolve = myStorage->isNeedToResolve();
459   for (aMirIter = aMirrorPonCirc.begin(); aMirIter != aMirrorPonCirc.end(); aMirIter++) {
460     // Make centers of arcs symmetric
461     Slvs_Entity aBaseArc = myStorage->getEntity(aPointsOnCircles[aMirIter->ptA]);
462     Slvs_Entity aBasePoint = myStorage->getEntity(aBaseArc.point[0]);
463     Slvs_Entity aMirrorArc = myStorage->getEntity(aPointsOnCircles[aMirIter->ptB]);
464     Slvs_Entity aMirrorPoint = myStorage->getEntity(aMirrorArc.point[0]);
465     makeMirrorEntity(aBasePoint, aMirrorPoint, aStartEnd);
466     // Calculate middle point for base arc and mirrored point on mirror arc
467     aBasePoint = myStorage->getEntity(aMirIter->ptA);
468     Slvs_Param aParamX = myStorage->getParameter(aBasePoint.param[0]);
469     Slvs_Param aParamY = myStorage->getParameter(aBasePoint.param[1]);
470     calculateMiddlePoint(aBaseArc, 0.5, aParamX.val, aParamY.val);
471     myStorage->updateParameter(aParamX);
472     myStorage->updateParameter(aParamY);
473     aMirrorPoint = myStorage->getEntity(aMirIter->ptB);
474     aParamX = myStorage->getParameter(aMirrorPoint.param[0]);
475     aParamY = myStorage->getParameter(aMirrorPoint.param[1]);
476     calculateMiddlePoint(aMirrorArc, 0.5, aParamX.val, aParamY.val);
477     myStorage->updateParameter(aParamX);
478     myStorage->updateParameter(aParamY);
479   }
480   // Restore previous value to avoid looped recalculations of sketch
481   myStorage->setNeedToResolve(aNeedToResolve);
482 }