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