Salome HOME
Issue #1860: fix end lines with spaces
[modules/shaper.git] / src / SketchSolver / PlaneGCSSolver / PlaneGCSSolver_Storage.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:    PlaneGCSSolver_Storage.cpp
4 // Created: 14 Dec 2015
5 // Author:  Artem ZHIDKOV
6
7 #include <PlaneGCSSolver_Storage.h>
8 #include <PlaneGCSSolver_Builder.h>
9 #include <PlaneGCSSolver_Solver.h>
10 #include <PlaneGCSSolver_ConstraintWrapper.h>
11 #include <PlaneGCSSolver_EntityWrapper.h>
12 #include <PlaneGCSSolver_PointWrapper.h>
13 #include <PlaneGCSSolver_ScalarWrapper.h>
14 #include <PlaneGCSSolver_ParameterWrapper.h>
15
16 #include <GeomAPI_Edge.h>
17 #include <GeomAPI_Dir2d.h>
18 #include <GeomAPI_Pnt2d.h>
19 #include <GeomAPI_XY.h>
20 #include <GeomDataAPI_Point2D.h>
21 #include <SketchPlugin_Arc.h>
22 #include <SketchPlugin_ConstraintTangent.h>
23
24 #include <cmath>
25
26
27 PlaneGCSSolver_Storage::PlaneGCSSolver_Storage(const GroupID& theGroup)
28   : SketchSolver_Storage(theGroup),
29     myEntityLastID(EID_SKETCH),
30     myConstraintLastID(CID_UNKNOWN)
31 {
32 }
33
34 void PlaneGCSSolver_Storage::addConstraint(
35     ConstraintPtr                   theConstraint,
36     std::list<ConstraintWrapperPtr> theSolverConstraints)
37 {
38   SketchSolver_Storage::addConstraint(theConstraint, theSolverConstraints);
39
40   // update point-point coincidence
41   if (!theSolverConstraints.empty() &&
42       theSolverConstraints.front()->type() == CONSTRAINT_PT_PT_COINCIDENT) {
43     std::list<ConstraintWrapperPtr>::iterator aCIt = theSolverConstraints.begin();
44     for (; aCIt != theSolverConstraints.end(); ++aCIt)
45       update(*aCIt);
46   }
47 }
48
49
50 bool PlaneGCSSolver_Storage::update(ConstraintWrapperPtr theConstraint)
51 {
52   bool isUpdated = false;
53   std::shared_ptr<PlaneGCSSolver_ConstraintWrapper> aConstraint =
54       std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(theConstraint);
55
56   // point-Line distance should be positive
57   if (aConstraint->type() == CONSTRAINT_PT_LINE_DISTANCE && aConstraint->value() < 0.0)
58     aConstraint->setValue(-aConstraint->value());
59
60   // make value of constraint unchangeable
61   ParameterWrapperPtr aValue = aConstraint->valueParameter();
62   if (aValue)
63     isUpdated = update(aValue) || isUpdated;
64
65   // update constrained entities
66   std::list<EntityWrapperPtr> anEntities = theConstraint->entities();
67   std::list<EntityWrapperPtr>::iterator anIt = anEntities.begin();
68   for (; anIt != anEntities.end(); ++anIt)
69     isUpdated = update(*anIt) || isUpdated;
70
71   if (aConstraint->id() == CID_UNKNOWN) {
72     const std::list<EntityWrapperPtr>& aSubs = aConstraint->entities();
73     // check middle-point constraint conflicts with point-on-line
74     if (aConstraint->type() == CONSTRAINT_MIDDLE_POINT) {
75       std::map<ConstraintPtr, std::list<ConstraintWrapperPtr> >::const_iterator
76           anIt = myConstraintMap.begin();
77       for (; anIt != myConstraintMap.end(); ++anIt) {
78         EntityWrapperPtr aPoint, aLine;
79         if (anIt->second.empty())
80           continue;
81         ConstraintWrapperPtr aCurrentConstr = anIt->second.front();
82         if (aCurrentConstr->type() != CONSTRAINT_PT_ON_LINE)
83           continue;
84         const std::list<EntityWrapperPtr>& aCurSubs = aCurrentConstr->entities();
85         std::list<EntityWrapperPtr>::const_iterator aSIt1, aSIt2;
86         for (aSIt1 = aSubs.begin(); aSIt1 != aSubs.end(); ++aSIt1) {
87           if ((*aSIt1)->type() == ENTITY_POINT)
88             aPoint = *aSIt1;
89           else if((*aSIt1)->type() == ENTITY_LINE)
90             aLine = *aSIt1;
91           else
92             continue;
93           for (aSIt2 = aCurSubs.begin(); aSIt2 != aCurSubs.end(); ++aSIt2)
94             if ((*aSIt1)->id() == (*aSIt2)->id())
95               break;
96           if (aSIt2 == aCurSubs.end())
97             break;
98         }
99         // point-on-line found, change it to bisector
100         if (aSIt1 == aSubs.end()) {
101           std::list<GCSConstraintPtr> aConstrList = aConstraint->constraints();
102           aConstrList.pop_front();
103           aConstraint->setConstraints(aConstrList);
104           break;
105         }
106       }
107     }
108
109     // Change ID of constraints
110     aConstraint->setId(++myConstraintLastID);
111   }
112
113   return isUpdated;
114 }
115
116 /// \brief Update coordinates of the point or scalar using its base attribute
117 static bool updateValues(EntityWrapperPtr& theEntity)
118 {
119   const double aTol = 1000. * tolerance;
120   bool isUpdated = false;
121   AttributePtr anAttr = theEntity->baseAttribute();
122   const std::list<ParameterWrapperPtr> aParams = theEntity->parameters();
123
124   double aCoord[2];
125
126   std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
127       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
128   if (aPoint2D) {
129     aCoord[0] = aPoint2D->x();
130     aCoord[1] = aPoint2D->y();
131   } else {
132     AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(anAttr);
133     if (aScalar)
134       aCoord[0] = aScalar->value();
135   }
136
137   std::list<ParameterWrapperPtr>::const_iterator anIt = aParams.begin();
138   for (int i = 0; anIt != aParams.end(); ++anIt, ++i)
139     if (fabs((*anIt)->value() - aCoord[i]) > aTol) {
140       (*anIt)->setValue(aCoord[i]);
141       isUpdated = true;
142     }
143   return isUpdated;
144 }
145
146 bool PlaneGCSSolver_Storage::update(EntityWrapperPtr theEntity)
147 {
148   if (theEntity->type() == ENTITY_SKETCH)
149     return true; // sketch is not necessary for PlaneGCS, so it is always says true
150
151   bool isUpdated = false;
152
153   if (theEntity->baseAttribute()) {
154     isUpdated = updateValues(theEntity);
155     if (isUpdated) {
156       setNeedToResolve(true);
157       if (theEntity->type() == ENTITY_POINT && theEntity->group() != myGroupID)
158         updateCoincident(theEntity);
159     }
160   }
161
162   // update parameters
163   std::list<ParameterWrapperPtr> aParams = theEntity->parameters();
164   std::list<ParameterWrapperPtr>::iterator aPIt = aParams.begin();
165   for (; aPIt != aParams.end(); ++aPIt)
166     isUpdated = update(*aPIt) || isUpdated;
167
168   // update sub-entities
169   std::list<EntityWrapperPtr> aSubEntities = theEntity->subEntities();
170   std::list<EntityWrapperPtr>::iterator aSIt = aSubEntities.begin();
171   for (; aSIt != aSubEntities.end(); ++aSIt)
172     isUpdated = update(*aSIt) || isUpdated;
173
174   // additional constraints for the arc processing
175   if (theEntity->type() == ENTITY_ARC)
176     processArc(theEntity);
177
178   // Change entity's ID, if necessary
179   if (theEntity->id() == EID_UNKNOWN) {
180     if (theEntity->type() == ENTITY_POINT) {
181       std::shared_ptr<PlaneGCSSolver_PointWrapper> aPoint =
182           std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theEntity);
183       if (!aPoint) {
184         aPoint = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(
185             theEntity->subEntities().front());
186       }
187       aPoint->setId(++myEntityLastID);
188     } else if (theEntity->type() == ENTITY_SCALAR) {
189       std::shared_ptr<PlaneGCSSolver_ScalarWrapper> aScalar =
190           std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(theEntity);
191       aScalar->setId(++myEntityLastID);
192     } else {
193       std::shared_ptr<PlaneGCSSolver_EntityWrapper> aGCSEnt =
194           std::dynamic_pointer_cast<PlaneGCSSolver_EntityWrapper>(theEntity);
195       aGCSEnt->setId(++myEntityLastID);
196     }
197   }
198   return isUpdated;
199 }
200
201 bool PlaneGCSSolver_Storage::update(ParameterWrapperPtr theParameter)
202 {
203   std::shared_ptr<PlaneGCSSolver_ParameterWrapper> aParam =
204       std::dynamic_pointer_cast<PlaneGCSSolver_ParameterWrapper>(theParameter);
205   if (aParam->isProcessed())
206     return false;
207   if (theParameter->group() != myGroupID || theParameter->isParametric())
208     myConst.push_back(aParam->parameter());
209   else
210     myParameters.push_back(aParam->parameter());
211   aParam->setProcessed(true);
212   return true;
213 }
214
215
216 bool PlaneGCSSolver_Storage::remove(ConstraintWrapperPtr theConstraint)
217 {
218   std::shared_ptr<PlaneGCSSolver_ConstraintWrapper> aConstraint =
219     std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(theConstraint);
220
221   bool isFullyRemoved = true;
222   // remove point-point coincidence
223   if (aConstraint->type() == CONSTRAINT_PT_PT_COINCIDENT)
224     isFullyRemoved = removeCoincidence(theConstraint) && isFullyRemoved;
225   // remove sub-entities
226   const std::list<EntityWrapperPtr>& aSubs = aConstraint->entities();
227   std::list<EntityWrapperPtr>::const_iterator aSIt = aSubs.begin();
228   for (; aSIt != aSubs.end(); ++ aSIt)
229     isFullyRemoved = remove(*aSIt) && isFullyRemoved;
230
231   if (aConstraint->valueParameter())
232     isFullyRemoved = remove(aConstraint->valueParameter()) && isFullyRemoved;
233   if (!isFullyRemoved && aConstraint->baseConstraint() &&
234      (!aConstraint->baseConstraint()->data() || !aConstraint->baseConstraint()->data()->isValid()))
235     isFullyRemoved = true;
236   setNeedToResolve(true);
237   myRemovedConstraints.insert(myRemovedConstraints.end(),
238       aConstraint->constraints().begin(), aConstraint->constraints().end());
239
240   if (isFullyRemoved && theConstraint->id() == myConstraintLastID)
241     --myConstraintLastID;
242
243   return isFullyRemoved;
244 }
245
246 bool PlaneGCSSolver_Storage::remove(EntityWrapperPtr theEntity)
247 {
248   // do not remove entity, if it is used by constraints or other entities
249   if ((theEntity->baseFeature() && isUsed(theEntity->baseFeature())) ||
250       (theEntity->baseAttribute() && isUsed(theEntity->baseAttribute())))
251     return false;
252
253   bool isFullyRemoved = SketchSolver_Storage::remove(theEntity);
254   if (isFullyRemoved) {
255     if (theEntity->type() == ENTITY_ARC) {
256       // remove arc additional constraints
257       std::map<EntityWrapperPtr, std::vector<GCSConstraintPtr> >::iterator
258           aFound = myArcConstraintMap.find(theEntity);
259       if (aFound != myArcConstraintMap.end()) {
260         myRemovedConstraints.insert(myRemovedConstraints.end(),
261             aFound->second.begin(), aFound->second.end());
262         myArcConstraintMap.erase(aFound);
263       }
264     }
265     if (theEntity->id() == myEntityLastID)
266       --myEntityLastID;
267   }
268   return isFullyRemoved;
269 }
270
271 bool PlaneGCSSolver_Storage::remove(ParameterWrapperPtr theParameter)
272 {
273   std::shared_ptr<PlaneGCSSolver_ParameterWrapper> aParam =
274       std::dynamic_pointer_cast<PlaneGCSSolver_ParameterWrapper>(theParameter);
275   if (aParam->isProcessed()) {
276     double* aValPtr = aParam->parameter();
277     GCS::VEC_pD::iterator anIt =  myParameters.begin();
278     for (; anIt != myParameters.end(); ++anIt)
279       if (*anIt == aValPtr)
280         break;
281     if (anIt != myParameters.end()) {
282       myParameters.erase(anIt);
283       setNeedToResolve(true);
284       aParam->setProcessed(false);
285     }
286     else {
287       for (anIt = myConst.begin(); anIt != myConst.end(); ++anIt)
288         if (*anIt == aValPtr)
289           break;
290       if (anIt != myConst.end()) {
291         myConst.erase(anIt);
292         setNeedToResolve(true);
293         aParam->setProcessed(false);
294       }
295     }
296   }
297   return true;
298 }
299
300
301 void PlaneGCSSolver_Storage::addCoincidentPoints(
302     EntityWrapperPtr theMaster, EntityWrapperPtr theSlave)
303 {
304   if (theMaster->type() != ENTITY_POINT || theSlave->type() != ENTITY_POINT)
305     return;
306
307   std::shared_ptr<PlaneGCSSolver_PointWrapper> aMaster =
308       std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theMaster);
309   if (!aMaster)
310     aMaster = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(
311       std::dynamic_pointer_cast<PlaneGCSSolver_EntityWrapper>(theMaster)->subEntities().front());
312   std::shared_ptr<PlaneGCSSolver_PointWrapper> aSlave =
313       std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSlave);
314   if (!aSlave)
315     aSlave = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(
316       std::dynamic_pointer_cast<PlaneGCSSolver_EntityWrapper>(theSlave)->subEntities().front());
317
318   // Search available coincidence
319   CoincidentPointsMap::iterator aMasterFound = myCoincidentPoints.find(aMaster);
320   CoincidentPointsMap::iterator aSlaveFound = myCoincidentPoints.find(aSlave);
321   if (aMasterFound == myCoincidentPoints.end() &&  aSlaveFound == myCoincidentPoints.end()) {
322     // try to find master and slave points in the lists of slaves of already existent coincidences
323     CoincidentPointsMap::iterator anIt = myCoincidentPoints.begin();
324     for (; anIt != myCoincidentPoints.end(); ++anIt) {
325       if (anIt->second.find(aMaster) != anIt->second.end())
326         aMasterFound = anIt;
327       else if (anIt->second.find(aSlave) != anIt->second.end())
328         aSlaveFound = anIt;
329
330       if (aMasterFound != myCoincidentPoints.end() &&  aSlaveFound != myCoincidentPoints.end())
331         break;
332     }
333   }
334
335   if (aMasterFound == myCoincidentPoints.end()) {
336     // create new group
337     myCoincidentPoints[aMaster] = std::set<EntityWrapperPtr>();
338     aMasterFound = myCoincidentPoints.find(aMaster);
339   } else if (aMasterFound == aSlaveFound)
340     return; // already coincident
341
342   if (aSlaveFound != myCoincidentPoints.end()) {
343     // A slave has been found, we need to attach all points coincident with it to the new master
344     std::set<EntityWrapperPtr> aNewSlaves = aSlaveFound->second;
345     aNewSlaves.insert(aSlaveFound->first);
346     myCoincidentPoints.erase(aSlaveFound);
347
348     std::set<EntityWrapperPtr>::const_iterator aSlIt = aNewSlaves.begin();
349     for (; aSlIt != aNewSlaves.end(); ++aSlIt)
350       addCoincidentPoints(aMaster, *aSlIt);
351   } else {
352     //std::list<ParameterWrapperPtr> aSlaveParams = aSlave->parameters();
353     //aSlave->setParameters(aMaster->parameters());
354
355     //// Remove slave's parameters
356     //std::list<ParameterWrapperPtr>::iterator aParIt = aSlaveParams.begin();
357     //for (; aParIt != aSlaveParams.end(); ++aParIt)
358     //  remove(*aParIt);
359
360     aMasterFound->second.insert(aSlave);
361   }
362 }
363
364
365 void PlaneGCSSolver_Storage::changeGroup(EntityWrapperPtr theEntity, const GroupID& theGroup)
366 {
367   theEntity->setGroup(theGroup);
368   if (theGroup == myGroupID)
369     makeVariable(theEntity);
370   else {
371     if (theEntity->type() == ENTITY_POINT)
372       update(theEntity);
373     makeConstant(theEntity);
374   }
375 }
376
377 void PlaneGCSSolver_Storage::changeGroup(ParameterWrapperPtr theParam, const GroupID& theGroup)
378 {
379   // TODO
380 }
381
382 void PlaneGCSSolver_Storage::verifyFixed()
383 {
384   // TODO
385 }
386
387 void PlaneGCSSolver_Storage::processArc(const EntityWrapperPtr& theArc)
388 {
389   // Calculate additional parameters necessary for PlaneGCS
390   const std::list<EntityWrapperPtr>& aSubs = theArc->subEntities();
391   std::list<EntityWrapperPtr>::const_iterator aSubIt = aSubs.begin();
392   while ((*aSubIt)->type() == ENTITY_POINT) // search scalar entities
393     ++aSubIt;
394   double* aStartAngle =
395     std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(*aSubIt++)->scalar();
396   double* aEndAngle =
397     std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(*aSubIt++)->scalar();
398   double* aRadius = std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(*aSubIt)->scalar();
399
400   std::shared_ptr<SketchPlugin_Feature> anArcFeature =
401       std::dynamic_pointer_cast<SketchPlugin_Feature>(theArc->baseFeature());
402   std::shared_ptr<GeomDataAPI_Point2D> aCenterAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
403       anArcFeature->attribute(SketchPlugin_Arc::CENTER_ID()));
404   std::shared_ptr<GeomDataAPI_Point2D> aStartAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
405       anArcFeature->attribute(SketchPlugin_Arc::START_ID()));
406   std::shared_ptr<GeomDataAPI_Point2D> aEndAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
407       anArcFeature->attribute(SketchPlugin_Arc::END_ID()));
408   if (!aCenterAttr || !aStartAttr || !aEndAttr)
409     return;
410   std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = aCenterAttr->pnt();
411   std::shared_ptr<GeomAPI_Pnt2d> aStartPnt  = aStartAttr->pnt();
412   std::shared_ptr<GeomAPI_Pnt2d> aEndPnt    = aEndAttr->pnt();
413
414   *aRadius = aCenterPnt->distance(aStartPnt);
415   if (!anArcFeature->lastResult())
416     return;
417   std::shared_ptr<GeomAPI_Edge> anArcEdge =
418       std::dynamic_pointer_cast<GeomAPI_Edge>(anArcFeature->lastResult()->shape());
419   if (!anArcEdge)
420     return;
421   anArcEdge->getRange(*aStartAngle, *aEndAngle);
422   // verify the range is correct and not shifted to an angle
423   std::shared_ptr<GeomAPI_Dir2d> aDir(new GeomAPI_Dir2d(cos(*aStartAngle), sin(*aStartAngle)));
424   std::shared_ptr<GeomAPI_Pnt2d> aCalcStartPnt(
425       new GeomAPI_Pnt2d(aCenterPnt->xy()->added(aDir->xy()->multiplied(*aRadius))));
426   if (aCalcStartPnt->distance(aStartPnt) > tolerance) {
427     std::shared_ptr<GeomAPI_Dir2d> aDirToStart(
428         new GeomAPI_Dir2d(aStartPnt->xy()->decreased(aCenterPnt->xy())));
429     double anAngle = aDir->angle(aDirToStart);
430     *aStartAngle += anAngle;
431     *aEndAngle += anAngle;
432   }
433
434   // no need to constraint a fixed or a copied arc
435   if (theArc->group() == GID_OUTOFGROUP || anArcFeature->isCopy())
436     return;
437   // No need to add constraints if they are already exist
438   std::map<EntityWrapperPtr, std::vector<GCSConstraintPtr> >::const_iterator
439       aFound = myArcConstraintMap.find(theArc);
440   if (aFound != myArcConstraintMap.end())
441     return;
442
443   // Prepare additional constraints to produce the arc
444   std::vector<GCSConstraintPtr> anArcConstraints;
445   std::shared_ptr<PlaneGCSSolver_EntityWrapper> anArcEnt =
446       std::dynamic_pointer_cast<PlaneGCSSolver_EntityWrapper>(theArc);
447   std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anArcEnt->entity());
448   // Distances from center till start and end points are equal to radius
449   GCSConstraintPtr aNew = GCSConstraintPtr(new GCS::ConstraintP2PDistance(
450       anArc->center, anArc->start, anArc->rad));
451 //  aNew->setTag((int)(++myConstraintLastID));
452   anArcConstraints.push_back(aNew);
453   aNew = GCSConstraintPtr(new GCS::ConstraintP2PDistance(
454       anArc->center, anArc->end, anArc->rad));
455 //  aNew->setTag((int)myConstraintLastID);
456   anArcConstraints.push_back(aNew);
457   // Angles of start and end points should be equal to given angles
458   aNew = GCSConstraintPtr(new GCS::ConstraintP2PAngle(
459       anArc->center, anArc->start, anArc->startAngle));
460 //  aNew->setTag((int)myConstraintLastID);
461   anArcConstraints.push_back(aNew);
462   aNew = GCSConstraintPtr(new GCS::ConstraintP2PAngle(
463       anArc->center, anArc->end, anArc->endAngle));
464 //  aNew->setTag((int)myConstraintLastID);
465   anArcConstraints.push_back(aNew);
466
467   myArcConstraintMap[theArc] = anArcConstraints;
468 }
469
470
471 void PlaneGCSSolver_Storage::makeConstant(const EntityWrapperPtr& theEntity)
472 {
473   toggleEntity(theEntity, myParameters, myConst);
474   if (theEntity->type() == ENTITY_POINT)
475     updateCoincident(theEntity);
476 }
477
478 void PlaneGCSSolver_Storage::makeVariable(const EntityWrapperPtr& theEntity)
479 {
480   toggleEntity(theEntity, myConst, myParameters);
481 }
482
483 static void getParametersToMove(const EntityWrapperPtr& theEntity, std::set<double*>& theParamList)
484 {
485   const std::list<ParameterWrapperPtr> aParams = theEntity->parameters();
486   std::list<ParameterWrapperPtr>::const_iterator aPIt = aParams.begin();
487   for (; aPIt != aParams.end(); ++aPIt)
488     theParamList.insert(
489         std::dynamic_pointer_cast<PlaneGCSSolver_ParameterWrapper>(*aPIt)->parameter());
490
491   const std::list<EntityWrapperPtr> aSubs = theEntity->subEntities();
492   std::list<EntityWrapperPtr>::const_iterator aSIt = aSubs.begin();
493
494   if (theEntity->type() == ENTITY_ARC) {
495     // workaround for the arc processing, because the arc is fixed by a set of constraints,
496     // which will conflict with all parameters fixed:
497     // 1. take center
498     getParametersToMove(*aSIt++, theParamList);
499     // 2. skip start and end points
500     ++aSIt;
501     // 3. take radius, start angle and end angle parameters
502     getParametersToMove(*(++aSIt), theParamList);
503     getParametersToMove(*(++aSIt), theParamList);
504     getParametersToMove(*(++aSIt), theParamList);
505   } else {
506     for (; aSIt != aSubs.end(); ++aSIt)
507       getParametersToMove(*aSIt, theParamList);
508   }
509 }
510
511 void PlaneGCSSolver_Storage::toggleEntity(
512     const EntityWrapperPtr& theEntity, GCS::VEC_pD& theFrom, GCS::VEC_pD& theTo)
513 {
514   std::set<double*> aParamsToMove;
515   getParametersToMove(theEntity, aParamsToMove);
516
517   GCS::VEC_pD::iterator anIt = theFrom.begin();
518   while (anIt != theFrom.end()) {
519     if (aParamsToMove.find(*anIt) == aParamsToMove.end()) {
520       ++anIt;
521       continue;
522     }
523
524     theTo.push_back(*anIt);
525     int aShift = int(anIt - theFrom.begin());
526     theFrom.erase(anIt);
527     anIt = theFrom.begin() + aShift;
528   }
529 }
530
531 void PlaneGCSSolver_Storage::updateCoincident(const EntityWrapperPtr& thePoint)
532 {
533   CoincidentPointsMap::iterator anIt = myCoincidentPoints.begin();
534   for (; anIt != myCoincidentPoints.end(); ++anIt) {
535     if (anIt->first == thePoint || anIt->second.find(thePoint) != anIt->second.end()) {
536       std::set<EntityWrapperPtr> aCoincident = anIt->second;
537       aCoincident.insert(anIt->first);
538
539       const std::list<ParameterWrapperPtr>& aBaseParams = thePoint->parameters();
540       std::list<ParameterWrapperPtr> aParams;
541       std::list<ParameterWrapperPtr>::const_iterator aBaseIt, anUpdIt;
542
543       std::set<EntityWrapperPtr>::const_iterator aCoincIt = aCoincident.begin();
544       for (; aCoincIt != aCoincident.end(); ++aCoincIt)
545         if (*aCoincIt != thePoint && (*aCoincIt)->group() != GID_OUTOFGROUP) {
546           aParams = (*aCoincIt)->parameters();
547           aBaseIt = aBaseParams.begin();
548           for (anUpdIt = aParams.begin(); anUpdIt != aParams.end(); ++anUpdIt, ++aBaseIt)
549             (*anUpdIt)->setValue((*aBaseIt)->value());
550         }
551
552       break;
553     }
554   }
555 }
556
557
558 bool PlaneGCSSolver_Storage::isRedundant(
559     GCSConstraintPtr theCheckedConstraint,
560     ConstraintWrapperPtr theParentConstraint,
561     std::list<std::set<double*> >& theCoincidentPoints) const
562 {
563   if (theParentConstraint->type() == CONSTRAINT_SYMMETRIC) {
564     if (theCheckedConstraint->getTypeId() == GCS::Perpendicular) {
565       BuilderPtr aBuilder = PlaneGCSSolver_Builder::getInstance();
566       // check the initial point is placed on the mirror line
567       std::list<EntityWrapperPtr> aSubs = theParentConstraint->entities();
568       std::shared_ptr<GeomAPI_Pnt2d> aPoint = aBuilder->point(aSubs.front());
569       std::shared_ptr<GeomAPI_Lin2d> aLine = aBuilder->line(aSubs.back());
570       return aLine->distance(aPoint) < tolerance;
571     }
572   }
573   else if (theParentConstraint->type() == CONSTRAINT_PT_PT_COINCIDENT) {
574     // Verify that the coincidence between points is already added
575     GCS::VEC_pD aParams = theCheckedConstraint->params();
576
577     std::list<std::set<double*> >::iterator aCoincIt, aFound1, aFound2;
578     aFound1 = aFound2 = theCoincidentPoints.end();
579     for (aCoincIt = theCoincidentPoints.begin();
580          aCoincIt != theCoincidentPoints.end(); ++aCoincIt) {
581       if (aFound1 == theCoincidentPoints.end() && aCoincIt->find(aParams[0]) != aCoincIt->end())
582         aFound1 = aCoincIt;
583       if (aFound2 == theCoincidentPoints.end() && aCoincIt->find(aParams[1]) != aCoincIt->end())
584         aFound2 = aCoincIt;
585       if (aFound1 != theCoincidentPoints.end() && aFound2 != theCoincidentPoints.end())
586         break;
587     }
588     if (aCoincIt != theCoincidentPoints.end()) { // both point are found
589       if (aFound1 == aFound2)
590         return true;
591       // merge two groups of coincidence
592       aFound1->insert(aFound2->begin(), aFound2->end());
593       theCoincidentPoints.erase(aFound2);
594     } else {
595       if (aFound1 != theCoincidentPoints.end())
596         aFound1->insert(aParams[1]);
597       else if (aFound2 != theCoincidentPoints.end())
598         aFound2->insert(aParams[0]);
599       else {
600         std::set<double*> aNewCoincidence;
601         aNewCoincidence.insert(aParams[0]);
602         aNewCoincidence.insert(aParams[1]);
603         theCoincidentPoints.push_back(aNewCoincidence);
604       }
605     }
606   }
607
608   return false;
609 }
610
611 void PlaneGCSSolver_Storage::initializeSolver(SolverPtr theSolver)
612 {
613   std::shared_ptr<PlaneGCSSolver_Solver> aSolver =
614       std::dynamic_pointer_cast<PlaneGCSSolver_Solver>(theSolver);
615   if (!aSolver)
616     return;
617   aSolver->clear();
618
619   if (myExistArc)
620     processArcs();
621
622   // initialize constraints
623   std::map<ConstraintPtr, std::list<ConstraintWrapperPtr> >::const_iterator
624       aCIt = myConstraintMap.begin();
625   GCS::SET_I aTangentIDs;
626   std::list<std::set<double*> > aCoincidentPoints;
627   for (; aCIt != myConstraintMap.end(); ++aCIt) {
628     std::list<ConstraintWrapperPtr>::const_iterator aCWIt = aCIt->second.begin();
629     for (; aCWIt != aCIt->second.end(); ++ aCWIt) {
630       std::shared_ptr<PlaneGCSSolver_ConstraintWrapper> aGCS =
631           std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(*aCWIt);
632       std::list<GCSConstraintPtr>::const_iterator anIt = aGCS->constraints().begin();
633       for (; anIt != aGCS->constraints().end(); ++anIt)
634         if (!isRedundant(*anIt, aGCS, aCoincidentPoints))
635           aSolver->addConstraint(*anIt, aGCS->type());
636     }
637     // store IDs of tangent constraints to avoid incorrect report of redundant constraints
638     if (aCIt->first && aCIt->first->getKind() == SketchPlugin_ConstraintTangent::ID())
639       for (aCWIt = aCIt->second.begin(); aCWIt != aCIt->second.end(); ++ aCWIt)
640         aTangentIDs.insert((int)(*aCWIt)->id());
641   }
642   // additional constraints for arcs
643   std::map<EntityWrapperPtr, std::vector<GCSConstraintPtr> >::const_iterator
644       anArcIt = myArcConstraintMap.begin();
645   for (; anArcIt != myArcConstraintMap.end(); ++anArcIt) {
646     std::vector<GCSConstraintPtr>::const_iterator anIt = anArcIt->second.begin();
647     for (; anIt != anArcIt->second.end(); ++anIt)
648       aSolver->addConstraint(*anIt, CONSTRAINT_UNKNOWN);
649   }
650   // removed waste constraints
651   std::list<GCSConstraintPtr>::const_iterator aRemIt = myRemovedConstraints.begin();
652   for (; aRemIt != myRemovedConstraints.end(); ++aRemIt)
653     aSolver->removeConstraint(*aRemIt);
654   myRemovedConstraints.clear();
655   // set list of tangent constraints
656   aSolver->setTangent(aTangentIDs);
657   // initialize unknowns
658   aSolver->setParameters(myParameters);
659 }
660
661 // indicates attribute containing in the external feature
662 bool isExternalAttribute(const AttributePtr& theAttribute)
663 {
664   if (!theAttribute)
665     return false;
666   std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
667       std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
668   return aSketchFeature.get() && aSketchFeature->isExternal();
669 }
670
671 void PlaneGCSSolver_Storage::refresh(bool theFixedOnly) const
672 {
673   //blockEvents(true);
674
675   std::map<AttributePtr, EntityWrapperPtr>::const_iterator anIt = myAttributeMap.begin();
676   std::list<ParameterWrapperPtr> aParams;
677   std::list<ParameterWrapperPtr>::const_iterator aParIt;
678   for (; anIt != myAttributeMap.end(); ++anIt) {
679     // the external feature always should keep the up to date values, so,
680     // refresh from the solver is never needed
681     bool isExternal = isExternalAttribute(anIt->first);
682
683     // update parameter wrappers and obtain values of attributes
684     aParams = anIt->second->parameters();
685     double aCoords[3];
686     bool isUpd[3] = {false};
687     int i = 0;
688     for (aParIt = aParams.begin(); i < 3 && aParIt != aParams.end(); ++aParIt, ++i) {
689       if (!theFixedOnly || isExternal ||
690           (*aParIt)->group() == GID_OUTOFGROUP || (*aParIt)->isParametric()) {
691         aCoords[i] = (*aParIt)->value();
692         isUpd[i] = true;
693       }
694     }
695     if (!isUpd[0] && !isUpd[1] && !isUpd[2])
696       continue; // nothing is updated
697
698     std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
699         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anIt->first);
700     if (aPoint2D) {
701       if ((isUpd[0] && fabs(aPoint2D->x() - aCoords[0]) > tolerance) ||
702           (isUpd[1] && fabs(aPoint2D->y() - aCoords[1]) > tolerance) || isExternal) {
703         // Find points coincident with this one (probably not in GID_OUTOFGROUP)
704         CoincidentPointsMap::const_iterator aCoincIt = myCoincidentPoints.begin();
705         for (; aCoincIt != myCoincidentPoints.end(); ++aCoincIt)
706           if (aCoincIt->first == anIt->second ||
707               aCoincIt->second.find(anIt->second) != aCoincIt->second.end())
708             break;
709         // get coordinates of "master"-point
710         std::shared_ptr<GeomDataAPI_Point2D> aMaster = aCoincIt != myCoincidentPoints.end() ?
711             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aCoincIt->first->baseAttribute()) :
712             aPoint2D;
713         if (!isUpd[0] || isExternal) aCoords[0] = aMaster->x();
714         if (!isUpd[1] || isExternal) aCoords[1] = aMaster->y();
715         if (!isExternal)
716           aPoint2D->setValue(aCoords[0], aCoords[1]);
717         if (aCoincIt != myCoincidentPoints.end()) {
718           if (aMaster && !isExternalAttribute(aMaster))
719             aMaster->setValue(aCoords[0], aCoords[1]);
720           std::set<EntityWrapperPtr>::const_iterator aSlaveIt = aCoincIt->second.begin();
721           for (; aSlaveIt != aCoincIt->second.end(); ++aSlaveIt) {
722             aPoint2D = std::dynamic_pointer_cast<GeomDataAPI_Point2D>((*aSlaveIt)->baseAttribute());
723             if (aPoint2D && !isExternalAttribute(aPoint2D))
724               aPoint2D->setValue(aCoords[0], aCoords[1]);
725           }
726         }
727       }
728       continue;
729     }
730     AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(anIt->first);
731     if (aScalar && !isExternal) {
732       if (isUpd[0] && fabs(aScalar->value() - aCoords[0]) > tolerance)
733         aScalar->setValue(aCoords[0]);
734       continue;
735     }
736   }
737
738   //blockEvents(false);
739 }
740
741 EntityWrapperPtr PlaneGCSSolver_Storage::calculateMiddlePoint(
742     EntityWrapperPtr theBase, double theCoeff)
743 {
744   std::shared_ptr<PlaneGCSSolver_Builder> aBuilder =
745       std::dynamic_pointer_cast<PlaneGCSSolver_Builder>(PlaneGCSSolver_Builder::getInstance());
746
747   std::shared_ptr<GeomAPI_XY> aMidPoint;
748   if (theBase->type() == ENTITY_LINE) {
749     std::shared_ptr<GeomAPI_Pnt2d> aPoints[2];
750     const std::list<EntityWrapperPtr>& aSubs = theBase->subEntities();
751     std::list<EntityWrapperPtr>::const_iterator anIt = aSubs.begin();
752     for (int i = 0; i < 2; ++i, ++anIt)
753       aPoints[i] = aBuilder->point(*anIt);
754     aMidPoint = aPoints[0]->xy()->multiplied(1.0 - theCoeff)->added(
755         aPoints[1]->xy()->multiplied(theCoeff));
756   }
757   else if (theBase->type() == ENTITY_ARC) {
758     double theX, theY;
759     double anArcPoint[3][2];
760     const std::list<EntityWrapperPtr>& aSubs = theBase->subEntities();
761     std::list<EntityWrapperPtr>::const_iterator anIt = aSubs.begin();
762     for (int i = 0; i < 3; ++i, ++anIt) {
763       std::shared_ptr<GeomAPI_Pnt2d> aPoint = aBuilder->point(*anIt);
764       anArcPoint[i][0] = aPoint->x();
765       anArcPoint[i][1] = aPoint->y();
766     }
767     // project last point of arc on the arc
768     double x = anArcPoint[1][0] - anArcPoint[0][0];
769     double y = anArcPoint[1][1] - anArcPoint[0][1];
770     double aRad = sqrt(x*x + y*y);
771     x = anArcPoint[2][0] - anArcPoint[0][0];
772     y = anArcPoint[2][1] - anArcPoint[0][1];
773     double aNorm = sqrt(x*x + y*y);
774     if (aNorm >= tolerance) {
775       anArcPoint[2][0] = x * aRad / aNorm;
776       anArcPoint[2][1] = y * aRad / aNorm;
777     }
778     anArcPoint[1][0] -= anArcPoint[0][0];
779     anArcPoint[1][1] -= anArcPoint[0][1];
780     if (theCoeff < tolerance) {
781       theX = anArcPoint[0][0] + anArcPoint[1][0];
782       theY = anArcPoint[0][1] + anArcPoint[1][1];
783     } else if (1 - theCoeff < tolerance) {
784       theX = anArcPoint[0][0] + anArcPoint[2][0];
785       theY = anArcPoint[0][1] + anArcPoint[2][1];
786     } else {
787       std::shared_ptr<GeomAPI_Dir2d>
788         aStartDir(new GeomAPI_Dir2d(anArcPoint[1][0], anArcPoint[1][1]));
789       std::shared_ptr<GeomAPI_Dir2d>
790         aEndDir(new GeomAPI_Dir2d(anArcPoint[2][0], anArcPoint[2][1]));
791       double anAngle = aStartDir->angle(aEndDir);
792       if (anAngle < 0)
793         anAngle += 2.0 * PI;
794       anAngle *= theCoeff;
795       double aCos = cos(anAngle);
796       double aSin = sin(anAngle);
797       theX = anArcPoint[0][0] + anArcPoint[1][0] * aCos - anArcPoint[1][1] * aSin;
798       theY = anArcPoint[0][1] + anArcPoint[1][0] * aSin + anArcPoint[1][1] * aCos;
799     }
800     aMidPoint = std::shared_ptr<GeomAPI_XY>(new GeomAPI_XY(theX, theY));
801   }
802
803   if (!aMidPoint)
804     return EntityWrapperPtr();
805
806   std::list<ParameterWrapperPtr> aParameters;
807   aParameters.push_back(aBuilder->createParameter(myGroupID, aMidPoint->x()));
808   aParameters.push_back(aBuilder->createParameter(myGroupID, aMidPoint->y()));
809   // Create entity (parameters are not filled)
810   GCSPointPtr aPnt(new GCS::Point);
811   aPnt->x =
812     std::dynamic_pointer_cast<PlaneGCSSolver_ParameterWrapper>(aParameters.front())->parameter();
813   aPnt->y =
814     std::dynamic_pointer_cast<PlaneGCSSolver_ParameterWrapper>(aParameters.back())->parameter();
815
816   EntityWrapperPtr aResult(new PlaneGCSSolver_PointWrapper(AttributePtr(), aPnt));
817   aResult->setGroup(myGroupID);
818   aResult->setParameters(aParameters);
819
820   update(aResult);
821   return aResult;
822 }