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