Salome HOME
Code cleanup in SketchPlugin and SketchSolver.
[modules/shaper.git] / src / SketchSolver / SolveSpaceSolver / SolveSpaceSolver_Builder.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:    SolveSpaceSolver_Builder.cpp
4 // Created: 25 Mar 2015
5 // Author:  Artem ZHIDKOV
6
7 #include <SolveSpaceSolver_Builder.h>
8 #include <SolveSpaceSolver_Solver.h>
9 #include <SolveSpaceSolver_Storage.h>
10 #include <SolveSpaceSolver_ParameterWrapper.h>
11 #include <SolveSpaceSolver_EntityWrapper.h>
12 #include <SolveSpaceSolver_ConstraintWrapper.h>
13 #include <SolveSpaceSolver_ConstraintType.h>
14
15 #include <SketchSolver_Manager.h>
16
17 #include <GeomAPI_Dir2d.h>
18 #include <GeomAPI_Pnt2d.h>
19 #include <GeomAPI_XY.h>
20 #include <GeomDataAPI_Dir.h>
21 #include <GeomDataAPI_Point.h>
22 #include <GeomDataAPI_Point2D.h>
23 #include <ModelAPI_Attribute.h>
24 #include <ModelAPI_AttributeRefAttr.h>
25
26 #include <SketchPlugin_Arc.h>
27 #include <SketchPlugin_Circle.h>
28 #include <SketchPlugin_Line.h>
29 #include <SketchPlugin_Point.h>
30
31 #include <math.h>
32
33
34 static EntityWrapperPtr createLine(FeaturePtr theFeature,
35                                    const std::list<EntityWrapperPtr>& theAttributes,
36                                    const GroupID& theGroupID,
37                                    const EntityID& theSketchID);
38 static EntityWrapperPtr createCircle(FeaturePtr theFeature,
39                                      const std::list<EntityWrapperPtr>& theAttributes,
40                                      const GroupID& theGroupID,
41                                      const EntityID& theSketchID);
42 static EntityWrapperPtr createArc(FeaturePtr theFeature,
43                                   const std::list<EntityWrapperPtr>& theAttributes,
44                                   const GroupID& theGroupID,
45                                   const EntityID& theSketchID);
46
47 /// \brief Set flags of constraint to identify which points are coincident in the Tangency
48 ///        (for more information, see SolveSpace documentation)
49 static void adjustTangency(ConstraintWrapperPtr theConstraint);
50 /// \brief Set flags for angle constraint
51 static void adjustAngle(ConstraintWrapperPtr theConstraint);
52 /// \brief Update mirror points
53 static void adjustMirror(ConstraintWrapperPtr theConstraint);
54
55 /// \brief Transform points to be symmetric regarding to the mirror line
56 static void makeMirrorPoints(EntityWrapperPtr theOriginal,
57                              EntityWrapperPtr theMirrored,
58                              EntityWrapperPtr theMirrorLine);
59
60
61
62 // Initialization of constraint builder self pointer
63 BuilderPtr SolveSpaceSolver_Builder::mySelf = SolveSpaceSolver_Builder::getInstance();
64
65 BuilderPtr SolveSpaceSolver_Builder::getInstance()
66 {
67   if (!mySelf) {
68     mySelf = BuilderPtr(new SolveSpaceSolver_Builder);
69     SketchSolver_Manager::instance()->setBuilder(mySelf);
70   }
71   return mySelf;
72 }
73
74 StoragePtr SolveSpaceSolver_Builder::createStorage(const GroupID& theGroup) const
75 {
76   return StoragePtr(new SolveSpaceSolver_Storage(theGroup));
77 }
78
79 SolverPtr SolveSpaceSolver_Builder::createSolver() const
80 {
81   return SolverPtr(new SolveSpaceSolver_Solver);
82 }
83
84
85 std::list<ConstraintWrapperPtr> SolveSpaceSolver_Builder::createConstraint(
86     ConstraintPtr theConstraint,
87     const GroupID& theGroupID,
88     const EntityID& theSketchID,
89     const SketchSolver_ConstraintType& theType,
90     const double& theValue,
91     const EntityWrapperPtr& thePoint1,
92     const EntityWrapperPtr& thePoint2,
93     const EntityWrapperPtr& theEntity1,
94     const EntityWrapperPtr& theEntity2) const
95 {
96   if (theType == CONSTRAINT_SYMMETRIC)
97     return createMirror(theConstraint, theGroupID, theSketchID,
98                         thePoint1, thePoint2, theEntity1);
99
100   int aType = ConstraintType::toSolveSpace(theType);
101   if (aType == SLVS_C_UNKNOWN)
102     return std::list<ConstraintWrapperPtr>();
103
104   Slvs_hEntity aSlvsEntities[4] = {SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN};
105   EntityWrapperPtr anOriginal[4] = {thePoint1, thePoint2, theEntity1, theEntity2};
106   std::list<EntityWrapperPtr> aConstrAttrList; // to be filled
107   for (int i = 0; i < 4; ++i) {
108     if (!anOriginal[i])
109       continue;
110     aSlvsEntities[i] = (Slvs_hEntity)anOriginal[i]->id();
111     if (aSlvsEntities[i] == SLVS_E_UNKNOWN)
112       return std::list<ConstraintWrapperPtr>(); // entity is not added into a storage, constraint can not be created
113     aConstrAttrList.push_back(anOriginal[i]);
114   }
115
116   Slvs_Constraint aConstraint = Slvs_MakeConstraint(
117       SLVS_C_UNKNOWN, (Slvs_hGroup)theGroupID, aType, (Slvs_hEntity)theSketchID,
118       theValue, aSlvsEntities[0], aSlvsEntities[1], aSlvsEntities[2], aSlvsEntities[3]);
119   ConstraintWrapperPtr aResult(new SolveSpaceSolver_ConstraintWrapper(theConstraint, aConstraint));
120   aResult->setGroup(theGroupID);
121   aResult->setValue(theValue);
122   aResult->setEntities(aConstrAttrList);
123   adjustConstraint(aResult);
124
125   return std::list<ConstraintWrapperPtr>(1, aResult);
126 }
127
128 std::list<ConstraintWrapperPtr> SolveSpaceSolver_Builder::createConstraint(
129     ConstraintPtr theConstraint,
130     const GroupID& theGroupID,
131     const EntityID& theSketchID,
132     const SketchSolver_ConstraintType& theType,
133     const double& theValue,
134     const bool theFullValue,
135     const EntityWrapperPtr& thePoint1,
136     const EntityWrapperPtr& thePoint2,
137     const std::list<EntityWrapperPtr>& theTrsfEnt) const
138 {
139   if (theType != CONSTRAINT_MULTI_ROTATION && theType != CONSTRAINT_MULTI_TRANSLATION)
140     return std::list<ConstraintWrapperPtr>();
141
142   int aType = ConstraintType::toSolveSpace(theType);
143   if (aType == SLVS_C_UNKNOWN)
144     return std::list<ConstraintWrapperPtr>();
145
146   Slvs_Constraint aConstraint =
147       Slvs_MakeConstraint(SLVS_C_UNKNOWN, (Slvs_hGroup)theGroupID, aType, (Slvs_hEntity)theSketchID,
148       theValue, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
149
150   std::list<EntityWrapperPtr> aConstrAttrList = theTrsfEnt;
151   if (thePoint2)
152     aConstrAttrList.push_front(thePoint2);
153   aConstrAttrList.push_front(thePoint1);
154
155   ConstraintWrapperPtr aResult(new SolveSpaceSolver_ConstraintWrapper(theConstraint, aConstraint));
156   aResult->setGroup(theGroupID);
157   aResult->setValue(theValue);
158   aResult->setIsFullValue(theFullValue);
159   aResult->setEntities(aConstrAttrList);
160   return std::list<ConstraintWrapperPtr>(1, aResult);
161 }
162
163
164 std::list<ConstraintWrapperPtr> SolveSpaceSolver_Builder::createMirror(
165     ConstraintPtr theConstraint,
166     const GroupID& theGroupID,
167     const EntityID& theSketchID,
168     const EntityWrapperPtr& theEntity1,
169     const EntityWrapperPtr& theEntity2,
170     const EntityWrapperPtr& theMirrorLine) const
171 {
172   Slvs_Constraint aConstraint;
173   std::list<ConstraintWrapperPtr> aResult;
174   std::list<EntityWrapperPtr> aConstrAttrList;
175   if (theEntity1->type() == ENTITY_POINT) {
176     if (theEntity2->group() == theGroupID) // theEntity2 is not fixed
177       makeMirrorPoints(theEntity1, theEntity2, theMirrorLine);
178
179     aConstraint = Slvs_MakeConstraint(
180         SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID, SLVS_C_SYMMETRIC_LINE, (Slvs_hEntity)theSketchID,
181         0.0, (Slvs_hEntity)theEntity1->id(), (Slvs_hEntity)theEntity2->id(),
182         (Slvs_hEntity)theMirrorLine->id(), SLVS_E_UNKNOWN);
183
184     aConstrAttrList.push_back(theEntity1);
185     aConstrAttrList.push_back(theEntity2);
186     aConstrAttrList.push_back(theMirrorLine);
187
188     ConstraintWrapperPtr aWrapper(new SolveSpaceSolver_ConstraintWrapper(
189         theConstraint, aConstraint));
190     aWrapper->setGroup(theGroupID);
191     aWrapper->setEntities(aConstrAttrList);
192     aResult.push_back(aWrapper);
193   }
194   else if (theEntity1->type() == ENTITY_LINE) {
195     const std::list<EntityWrapperPtr>& aPoints1 = theEntity1->subEntities();
196     const std::list<EntityWrapperPtr>& aPoints2 = theEntity2->subEntities();
197     std::list<EntityWrapperPtr>::const_iterator anIt1 = aPoints1.begin();
198     std::list<EntityWrapperPtr>::const_iterator anIt2 = aPoints2.begin();
199     for (; anIt1 != aPoints1.end() && anIt2 != aPoints2.end(); ++anIt1, ++anIt2) {
200       std::list<ConstraintWrapperPtr> aMrrList =
201           createMirror(theConstraint, theGroupID, theSketchID, *anIt1, *anIt2, theMirrorLine);
202       aResult.insert(aResult.end(), aMrrList.begin(), aMrrList.end());
203     }
204   }
205   else if (theEntity1->type() == ENTITY_CIRCLE) {
206     const std::list<EntityWrapperPtr>& aPoints1 = theEntity1->subEntities();
207     std::list<EntityWrapperPtr>::const_iterator anIt1 = aPoints1.begin();
208     for (; anIt1 != aPoints1.end(); ++anIt1)
209       if ((*anIt1)->type() == ENTITY_POINT)
210         break;
211     const std::list<EntityWrapperPtr>& aPoints2 = theEntity2->subEntities();
212     std::list<EntityWrapperPtr>::const_iterator anIt2 = aPoints2.begin();
213     for (; anIt2 != aPoints2.end(); ++anIt2)
214       if ((*anIt2)->type() == ENTITY_POINT)
215         break;
216
217     std::list<ConstraintWrapperPtr> aMrrList =
218         createMirror(theConstraint, theGroupID, theSketchID, *anIt1, *anIt2, theMirrorLine);
219     aResult.insert(aResult.end(), aMrrList.begin(), aMrrList.end());
220
221     // Additional constraint for equal radii
222     aMrrList = createConstraint(theConstraint, theGroupID, theSketchID, CONSTRAINT_EQUAL_RADIUS,
223         0.0, EntityWrapperPtr(), EntityWrapperPtr(), theEntity1, theEntity2);
224     aResult.insert(aResult.end(), aMrrList.begin(), aMrrList.end());
225   }
226   else if (theEntity1->type() == ENTITY_ARC) {
227     // Do not allow mirrored arc recalculate its position until coordinated of all points recalculated
228     FeaturePtr aMirrArc = theEntity2->baseFeature();
229     aMirrArc->data()->blockSendAttributeUpdated(true);
230
231     std::list<ConstraintWrapperPtr> aMrrList;
232     std::list<EntityWrapperPtr>::const_iterator anIt1 = theEntity1->subEntities().begin();
233     std::list<EntityWrapperPtr>::const_iterator anIt2 = theEntity2->subEntities().begin();
234     if ((*anIt2)->group() == theGroupID) // mirrored point is not fixed
235       makeMirrorPoints(*anIt1, *anIt2, theMirrorLine);
236
237     // Workaround to avoid problems in SolveSpace.
238     // The symmetry of two arcs will be done using symmetry of three points on these arcs:
239     // start point, end point, and any other point on the arc
240     std::list<EntityWrapperPtr> aBaseArcPoints(++anIt1, theEntity1->subEntities().end());
241     std::list<EntityWrapperPtr> aMirrorArcPoints(++anIt2, theEntity2->subEntities().end());
242     // indices of points of arc, center corresponds center, first point corresponds last point
243     aMirrorArcPoints.reverse();
244
245     anIt1 = aBaseArcPoints.begin();
246     anIt2 = aMirrorArcPoints.begin();
247     for (; anIt1 != aBaseArcPoints.end(); ++anIt1, ++anIt2) {
248       aMrrList = createMirror(theConstraint, theGroupID, theSketchID, *anIt1, *anIt2, theMirrorLine);
249       aResult.insert(aResult.end(), aMrrList.begin(), aMrrList.end());
250     }
251     // Restore event sending
252     aMirrArc->data()->blockSendAttributeUpdated(false);
253   }
254   return aResult;
255 }
256
257 void SolveSpaceSolver_Builder::adjustConstraint(ConstraintWrapperPtr theConstraint) const
258 {
259   SketchSolver_ConstraintType aType = theConstraint->type();
260   // Update flags in constraints
261   if (aType == CONSTRAINT_TANGENT_ARC_ARC || aType == CONSTRAINT_TANGENT_ARC_LINE)
262     adjustTangency(theConstraint);
263   else if (aType == CONSTRAINT_ANGLE)
264     adjustAngle(theConstraint);
265   else if (aType == CONSTRAINT_SYMMETRIC)
266     adjustMirror(theConstraint);
267 }
268
269 EntityWrapperPtr SolveSpaceSolver_Builder::createFeature(
270     FeaturePtr theFeature,
271     const std::list<EntityWrapperPtr>& theAttributes,
272     const GroupID& theGroupID,
273     const EntityID& theSketchID) const
274 {
275   static EntityWrapperPtr aDummy;
276   if (!theFeature->data()->isValid())
277     return aDummy;
278
279   // Sketch
280   CompositeFeaturePtr aSketch = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
281   if (aSketch)
282     return createSketchEntity(aSketch, theGroupID);
283
284   // SketchPlugin features
285   std::shared_ptr<SketchPlugin_Feature> aFeature =
286       std::dynamic_pointer_cast<SketchPlugin_Feature>(theFeature);
287   if (!aFeature)
288     return aDummy;
289
290   // Verify the feature by its kind
291   const std::string& aFeatureKind = aFeature->getKind();
292   // Line
293   if (aFeatureKind == SketchPlugin_Line::ID())
294     return createLine(theFeature, theAttributes, theGroupID, theSketchID);
295   // Circle
296   else if (aFeatureKind == SketchPlugin_Circle::ID())
297     return createCircle(theFeature, theAttributes,theGroupID, theSketchID);
298   // Arc
299   else if (aFeatureKind == SketchPlugin_Arc::ID())
300     return createArc(theFeature, theAttributes,theGroupID, theSketchID);
301   // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
302   else if (aFeatureKind == SketchPlugin_Point::ID()) {
303     AttributePtr aPoint = theFeature->attribute(SketchPlugin_Point::COORD_ID());
304     if (!aPoint->isInitialized())
305       return aDummy;
306     EntityWrapperPtr aSub = theAttributes.empty() ? createAttribute(aPoint, theGroupID, theSketchID) :
307                             theAttributes.front();
308     if (!aSub)
309       return aDummy;
310
311     const Slvs_Entity& aSubEnt =
312         std::dynamic_pointer_cast<SolveSpaceSolver_EntityWrapper>(aSub)->entity();
313     EntityWrapperPtr aResult(new SolveSpaceSolver_EntityWrapper(theFeature, aPoint, aSubEnt));
314     aResult->setSubEntities(theAttributes);
315     return aResult;
316   }
317
318   // wrong entity
319   return aDummy;
320 }
321
322 EntityWrapperPtr SolveSpaceSolver_Builder::createAttribute(
323     AttributePtr theAttribute,
324     const GroupID& theGroupID,
325     const EntityID& theSketchID) const
326 {
327   AttributePtr anAttribute = theAttribute;
328   AttributeRefAttrPtr aRefAttr =
329       std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttribute);
330   if (aRefAttr) {
331     if (aRefAttr->isObject()) {
332       // do not create features here
333       return EntityWrapperPtr();
334     } else
335       anAttribute = aRefAttr->attr();
336   }
337
338   std::list<ParameterWrapperPtr> aParameters;
339   Slvs_Entity anEntity;
340   anEntity.type = 0;
341
342   // Point in 3D
343   std::shared_ptr<GeomDataAPI_Point> aPoint =
344       std::dynamic_pointer_cast<GeomDataAPI_Point>(theAttribute);
345   if (aPoint) {
346     aParameters.push_back(createParameter(theGroupID, aPoint->x(), !aPoint->textX().empty()));
347     aParameters.push_back(createParameter(theGroupID, aPoint->y(), !aPoint->textY().empty()));
348     aParameters.push_back(createParameter(theGroupID, aPoint->z(), !aPoint->textZ().empty()));
349     // Create entity (parameters are not filled)
350     anEntity = Slvs_MakePoint3d(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID,
351         SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
352   } else {
353     // Point in 2D
354     std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
355       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
356     if (aPoint2D) {
357       aParameters.push_back(createParameter(theGroupID, aPoint2D->x(), !aPoint2D->textX().empty()));
358       aParameters.push_back(createParameter(theGroupID, aPoint2D->y(), !aPoint2D->textY().empty()));
359       // Create entity (parameters are not filled)
360       anEntity = Slvs_MakePoint2d(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID,
361           (Slvs_hEntity)theSketchID, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
362     } else {
363       // Scalar value (used for the distance entities)
364       AttributeDoublePtr aScalar =
365           std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
366       if (aScalar) {
367         aParameters.push_back(createParameter(theGroupID, aScalar->value(), !aScalar->text().empty()));
368         // Create entity (parameter is not filled)
369         anEntity = Slvs_MakeDistance(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID,
370           (Slvs_hEntity)theSketchID, SLVS_E_UNKNOWN);
371       }
372     }
373   }
374
375   if (anEntity.type == 0) {
376     // unknown attribute type
377     return EntityWrapperPtr();
378   }
379
380   EntityWrapperPtr aResult(new SolveSpaceSolver_EntityWrapper(theAttribute, anEntity));
381   aResult->setParameters(aParameters);
382   return aResult;
383 }
384
385
386
387 EntityWrapperPtr SolveSpaceSolver_Builder::createSketchEntity(
388     CompositeFeaturePtr theSketch,
389     const GroupID& theGroupID) const
390 {
391   DataPtr aSketchData = theSketch->data();
392   if (!aSketchData || !aSketchData->isValid())
393     return EntityWrapperPtr(); // the sketch is incorrect
394
395   // Get parameters of workplane
396   AttributePtr aDirX    = aSketchData->attribute(SketchPlugin_Sketch::DIRX_ID());
397   AttributePtr aNorm    = aSketchData->attribute(SketchPlugin_Sketch::NORM_ID());
398   AttributePtr anOrigin = aSketchData->attribute(SketchPlugin_Sketch::ORIGIN_ID());
399   if (!anOrigin->isInitialized() || !aNorm->isInitialized() || !aDirX->isInitialized())
400     return EntityWrapperPtr();
401
402   EntityWrapperPtr aNewEnt;
403   std::list<EntityWrapperPtr> aSubs;
404
405   // Create SolveSpace entity corresponding to the sketch origin
406   aNewEnt = createAttribute(anOrigin, theGroupID);
407   if (!aNewEnt)
408     return EntityWrapperPtr();
409   aSubs.push_back(aNewEnt);
410
411   // Create SolveSpace entity corresponding the the sketch normal
412   aNewEnt = createNormal(aNorm, aDirX, theGroupID);
413   if (!aNewEnt)
414     return EntityWrapperPtr();
415   aSubs.push_back(aNewEnt);
416
417   // Create workplane
418   Slvs_Entity aWorkplane = Slvs_MakeWorkplane(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID,
419       SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
420
421   aNewEnt = EntityWrapperPtr(
422       new SolveSpaceSolver_EntityWrapper(FeaturePtr(theSketch), aWorkplane));
423   aNewEnt->setSubEntities(aSubs);
424   return aNewEnt;
425 }
426
427 EntityWrapperPtr SolveSpaceSolver_Builder::createNormal(
428     AttributePtr theNormal,
429     AttributePtr theDirX,
430     const GroupID& theGroupID) const
431 {
432   std::shared_ptr<GeomDataAPI_Dir> aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(theNormal);
433   std::shared_ptr<GeomDataAPI_Dir> aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(theDirX);
434   if (!aDirX || !aNorm ||
435       (fabs(aDirX->x()) + fabs(aDirX->y()) + fabs(aDirX->z()) < tolerance) || 
436       !aNorm->isInitialized())
437     return EntityWrapperPtr();
438   // calculate Y direction
439   std::shared_ptr<GeomAPI_Dir> aDirY(new GeomAPI_Dir(aNorm->dir()->cross(aDirX->dir())));
440
441   // quaternion parameters of normal vector
442   double qw, qx, qy, qz;
443   Slvs_MakeQuaternion(aDirX->x(), aDirX->y(), aDirX->z(), aDirY->x(), aDirY->y(), aDirY->z(), &qw,
444                       &qx, &qy, &qz);
445   double aNormCoord[4] = { qw, qx, qy, qz };
446
447   // Create parameters of the normal
448   std::list<ParameterWrapperPtr> aParameters;
449   for (int i = 0; i < 4; i++)
450     aParameters.push_back(createParameter(theGroupID, aNormCoord[i]));
451
452   // Create a normal with empty parameters
453   Slvs_Entity aNormalEnt = Slvs_MakeNormal3d(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID,
454       SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
455   EntityWrapperPtr aNormal(new SolveSpaceSolver_EntityWrapper(theNormal, aNormalEnt));
456   aNormal->setParameters(aParameters);
457   return aNormal;
458 }
459
460 ParameterWrapperPtr SolveSpaceSolver_Builder::createParameter(
461     const GroupID& theGroup, const double theValue, const bool theExpr) const
462 {
463   Slvs_Param aParam = Slvs_MakeParam(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroup, theValue);
464   ParameterWrapperPtr aWrapper(new SolveSpaceSolver_ParameterWrapper(aParam));
465   aWrapper->setIsParametric(theExpr);
466   return aWrapper;
467 }
468
469
470
471
472
473 // ================   Auxiliary functions   ==========================
474 EntityWrapperPtr createLine(FeaturePtr theFeature,
475                             const std::list<EntityWrapperPtr>& theAttributes,
476                             const GroupID& theGroupID,
477                             const EntityID& theSketchID)
478 {
479   EntityWrapperPtr aNewEntity;
480   std::list<EntityWrapperPtr> aSubs;
481
482   AttributePtr aStart = theFeature->attribute(SketchPlugin_Line::START_ID());
483   AttributePtr aEnd = theFeature->attribute(SketchPlugin_Line::END_ID());
484   if (!aStart->isInitialized() || !aEnd->isInitialized())
485     return aNewEntity;
486
487   EntityWrapperPtr aStartEnt, aEndEnt;
488   std::list<EntityWrapperPtr>::const_iterator anIt = theAttributes.begin();
489   for (; anIt != theAttributes.end(); ++anIt) {
490     std::shared_ptr<SolveSpaceSolver_EntityWrapper> aSlvsEntity = 
491         std::dynamic_pointer_cast<SolveSpaceSolver_EntityWrapper>(*anIt);
492     if (aSlvsEntity->isBase(aStart))
493       aStartEnt = aSlvsEntity;
494     else if (aSlvsEntity->isBase(aEnd))
495       aEndEnt = aSlvsEntity;
496   }
497   if (!aStartEnt || !aEndEnt)
498     return aNewEntity;
499
500   aSubs.push_back(aStartEnt);
501   aSubs.push_back(aEndEnt);
502   Slvs_Entity anEntity = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID,
503       (Slvs_hEntity)theSketchID, (Slvs_hEntity)aStartEnt->id(), (Slvs_hEntity)aEndEnt->id());
504
505   aNewEntity = EntityWrapperPtr(new SolveSpaceSolver_EntityWrapper(theFeature, anEntity));
506   aNewEntity->setSubEntities(aSubs);
507   return aNewEntity;
508 }
509
510 EntityWrapperPtr createCircle(FeaturePtr theFeature,
511                               const std::list<EntityWrapperPtr>& theAttributes,
512                               const GroupID& theGroupID,
513                               const EntityID& theSketchID)
514 {
515   EntityWrapperPtr aNewEntity;
516   std::list<EntityWrapperPtr> aSubs;
517
518   AttributePtr aCenter = theFeature->attribute(SketchPlugin_Circle::CENTER_ID());
519   AttributePtr aRadius = theFeature->attribute(SketchPlugin_Circle::RADIUS_ID());
520   if (!aCenter->isInitialized() || !aRadius->isInitialized())
521     return aNewEntity;
522
523   EntityWrapperPtr aCenterEnt, aRadiusEnt, aNormalEnt;
524   std::list<EntityWrapperPtr>::const_iterator anIt = theAttributes.begin();
525   for (; anIt != theAttributes.end(); ++anIt) {
526     std::shared_ptr<SolveSpaceSolver_EntityWrapper> aSlvsEntity = 
527         std::dynamic_pointer_cast<SolveSpaceSolver_EntityWrapper>(*anIt);
528     if (aSlvsEntity->isBase(aCenter))
529       aCenterEnt = aSlvsEntity;
530     else if (aSlvsEntity->isBase(aRadius))
531       aRadiusEnt = aSlvsEntity;
532     else if (aSlvsEntity->type() == ENTITY_NORMAL)
533       aNormalEnt = aSlvsEntity;
534   }
535   if (!aCenterEnt || !aRadiusEnt || !aNormalEnt)
536     return aNewEntity;
537
538   aSubs.push_back(aCenterEnt);
539   aSubs.push_back(aRadiusEnt);
540   Slvs_Entity anEntity = Slvs_MakeCircle(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID,
541       (Slvs_hEntity)theSketchID, (Slvs_hEntity)aCenterEnt->id(),
542       (Slvs_hEntity)aNormalEnt->id(), (Slvs_hEntity)aRadiusEnt->id());
543
544   aNewEntity = EntityWrapperPtr(new SolveSpaceSolver_EntityWrapper(theFeature, anEntity));
545   aNewEntity->setSubEntities(aSubs);
546   return aNewEntity;
547 }
548
549 EntityWrapperPtr createArc(FeaturePtr theFeature,
550                            const std::list<EntityWrapperPtr>& theAttributes,
551                            const GroupID& theGroupID,
552                            const EntityID& theSketchID)
553 {
554   EntityWrapperPtr aNewEntity;
555   std::list<EntityWrapperPtr> aSubs;
556
557   AttributePtr aCenter = theFeature->attribute(SketchPlugin_Arc::CENTER_ID());
558   AttributePtr aStart = theFeature->attribute(SketchPlugin_Arc::START_ID());
559   AttributePtr aEnd = theFeature->attribute(SketchPlugin_Arc::END_ID());
560   if (!aCenter->isInitialized() || !aStart->isInitialized() || !aEnd->isInitialized())
561     return aNewEntity;
562
563   EntityWrapperPtr aCenterEnt, aStartEnt, aEndEnt, aNormalEnt;
564   std::list<EntityWrapperPtr>::const_iterator anIt = theAttributes.begin();
565   for (; anIt != theAttributes.end(); ++anIt) {
566     std::shared_ptr<SolveSpaceSolver_EntityWrapper> aSlvsEntity = 
567         std::dynamic_pointer_cast<SolveSpaceSolver_EntityWrapper>(*anIt);
568     if (aSlvsEntity->isBase(aCenter))
569       aCenterEnt = aSlvsEntity;
570     else if (aSlvsEntity->isBase(aStart))
571       aStartEnt = aSlvsEntity;
572     else if (aSlvsEntity->isBase(aEnd))
573       aEndEnt = aSlvsEntity;
574     else if (aSlvsEntity->type() == ENTITY_NORMAL)
575       aNormalEnt = aSlvsEntity;
576   }
577   if (!aCenterEnt || !aStartEnt || !aEndEnt || !aNormalEnt)
578     return aNewEntity;
579
580   aSubs.push_back(aCenterEnt);
581   aSubs.push_back(aStartEnt);
582   aSubs.push_back(aEndEnt);
583   Slvs_Entity anEntity = Slvs_MakeArcOfCircle(SLVS_E_UNKNOWN, (Slvs_hGroup)theGroupID,
584       (Slvs_hEntity)theSketchID, (Slvs_hEntity)aNormalEnt->id(),
585       (Slvs_hEntity)aCenterEnt->id(), (Slvs_hEntity)aStartEnt->id(), (Slvs_hEntity)aEndEnt->id());
586
587   aNewEntity = EntityWrapperPtr(new SolveSpaceSolver_EntityWrapper(theFeature, anEntity));
588   aNewEntity->setSubEntities(aSubs);
589   return aNewEntity;
590 }
591
592
593 void adjustTangency(ConstraintWrapperPtr theConstraint)
594 {
595   BuilderPtr aBuilder = SolveSpaceSolver_Builder::getInstance();
596
597   std::shared_ptr<SolveSpaceSolver_ConstraintWrapper> aConstraint =
598     std::dynamic_pointer_cast<SolveSpaceSolver_ConstraintWrapper>(theConstraint);
599
600   // Collect start, end points of entities
601   std::shared_ptr<GeomAPI_Pnt2d> aStartEntPoints[2][2];
602   bool isCoinc[2][2] = {false};
603   const std::list<EntityWrapperPtr>& aSubs = aConstraint->entities();
604   std::list<EntityWrapperPtr>::const_iterator aSIt = aSubs.begin();
605   for (int i = 0; aSIt != aSubs.end(); ++aSIt, ++i) {
606     const std::list<EntityWrapperPtr>& aPoints = (*aSIt)->subEntities();
607     std::list<EntityWrapperPtr>::const_iterator aPIt = aPoints.begin();
608     if ((*aSIt)->type() == ENTITY_ARC)
609       ++aPIt;
610     for (int j = 0; aPIt != aPoints.end(); ++aPIt, ++j) {
611       aStartEntPoints[i][j] = aBuilder->point(*aPIt);
612       if (i > 0) { // check coincidence
613         for (int k = 0; k < 2; ++k)
614           if (aStartEntPoints[i][j]->distance(aStartEntPoints[0][k]) < tolerance)
615             isCoinc[0][k] = isCoinc[i][j] = true;
616       }
617     }
618   }
619
620   Slvs_Constraint& aSlvsConstraint = aConstraint->changeConstraint();
621   if (isCoinc[0][0] == false && isCoinc[0][1] == true)
622     aSlvsConstraint.other = 1;
623   else aSlvsConstraint.other = 0;
624   if (isCoinc[1][0] == false && isCoinc[1][1] == true)
625     aSlvsConstraint.other2 = 1;
626   else aSlvsConstraint.other2 = 0;
627 }
628
629 void adjustAngle(ConstraintWrapperPtr theConstraint)
630 {
631   BuilderPtr aBuilder = SolveSpaceSolver_Builder::getInstance();
632
633   std::shared_ptr<SolveSpaceSolver_ConstraintWrapper> aConstraint =
634     std::dynamic_pointer_cast<SolveSpaceSolver_ConstraintWrapper>(theConstraint);
635
636   bool isFixed[2][2];
637   std::shared_ptr<GeomAPI_Pnt2d> aPoints[2][2]; // start and end points of lines
638   const std::list<EntityWrapperPtr>& aConstrLines = aConstraint->entities();
639   std::list<EntityWrapperPtr>::const_iterator aCLIt = aConstrLines.begin();
640   for (int i = 0; aCLIt != aConstrLines.end(); ++i, ++aCLIt) {
641     const std::list<EntityWrapperPtr>& aLinePoints = (*aCLIt)->subEntities();
642     std::list<EntityWrapperPtr>::const_iterator aLPIt = aLinePoints.begin();
643     for (int j = 0; aLPIt != aLinePoints.end(); ++j, ++aLPIt) {
644       isFixed[i][j] = ((*aLPIt)->group() != theConstraint->group());
645       aPoints[i][j] = aBuilder->point(*aLPIt);
646     }
647   }
648
649   if (isFixed[0][0] && isFixed[0][1] && isFixed[1][0] && isFixed[1][1])
650     return; // both lines are fixed => no need to update them
651
652   std::shared_ptr<GeomAPI_Lin2d> aLine[2] = {
653     std::shared_ptr<GeomAPI_Lin2d>(new GeomAPI_Lin2d(aPoints[0][0], aPoints[0][1])),
654     std::shared_ptr<GeomAPI_Lin2d>(new GeomAPI_Lin2d(aPoints[1][0], aPoints[1][1]))
655   };
656   std::shared_ptr<GeomAPI_Pnt2d> anIntersection = aLine[0]->intersect(aLine[1]);
657   if (!anIntersection)
658     return;
659   double aDist[2][2];
660   for (int i = 0; i < 2; i++) {
661     for (int j = 0; j < 2; j++) {
662       aDist[i][j] = anIntersection->distance(aPoints[i][j]);
663       if (fabs(aDist[i][j]) <= tolerance)
664         aDist[i][j] = 0.0;
665     }
666     if (aDist[i][0] > tolerance && aDist[i][1] > tolerance &&
667         aDist[i][0] + aDist[i][1] < aPoints[i][0]->distance(aPoints[i][1]) + 2.0 * tolerance) {
668       // the intersection point is an inner point of the line,
669       // we change the sign of distance till start point to calculate correct coordinates
670       // after rotation
671       aDist[i][0] *= -1.0;
672     }
673   }
674   std::shared_ptr<GeomAPI_Dir2d> aDir[2];
675   for (int i = 0; i < 2; i++) {
676     if (aDist[i][1] > fabs(aDist[i][0]))
677       aDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(
678           aPoints[i][1]->xy()->decreased(anIntersection->xy())));
679     else {
680       aDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(
681           aPoints[i][0]->xy()->decreased(anIntersection->xy())));
682       // main direction is opposite => change signs
683       if (aDist[i][0] < 0.0) {
684         aDist[i][0] *= -1.0;
685         aDist[i][1] *= -1.0;
686       }
687     }
688   }
689
690   Slvs_Constraint& aSlvsConstraint = aConstraint->changeConstraint();
691   aSlvsConstraint.other = false;
692   for (int i = 0; i < 2; i++)
693     if (aLine[i]->direction()->dot(aDir[i]) < 0.0)
694       aSlvsConstraint.other = !aSlvsConstraint.other;
695
696   // Recalculate positions of lines to avoid conflicting constraints
697   // while changing angle value several times
698   double cosA = cos(aConstraint->value() * PI / 180.0);
699   double sinA = sin(aConstraint->value() * PI / 180.0);
700   if (aDir[0]->cross(aDir[1]) < 0.0)
701     sinA *= -1.0;
702   int aLineToUpd = 1;
703   if (isFixed[1][0] && isFixed[1][1]) {
704     sinA *= -1.0;
705     aLineToUpd = 0;
706   }
707   double x = aDir[1-aLineToUpd]->x() * cosA - aDir[1-aLineToUpd]->y() * sinA;
708   double y = aDir[1-aLineToUpd]->x() * sinA + aDir[1-aLineToUpd]->y() * cosA;
709
710   std::shared_ptr<GeomAPI_Pnt2d> aNewPoints[2];
711   for (int i = 0; i < 2; i++) {
712     aNewPoints[i] = std::shared_ptr<GeomAPI_Pnt2d>(
713         new GeomAPI_Pnt2d(anIntersection->x() + x * aDist[aLineToUpd][i],
714                           anIntersection->y() + y * aDist[aLineToUpd][i]));
715   }
716
717   std::shared_ptr<GeomAPI_XY> aDelta;
718   if (isFixed[aLineToUpd][0] && !isFixed[aLineToUpd][1])
719     aDelta = aPoints[aLineToUpd][0]->xy()->decreased(aNewPoints[0]->xy());
720   else if (!isFixed[aLineToUpd][0] && isFixed[aLineToUpd][1])
721     aDelta = aPoints[aLineToUpd][1]->xy()->decreased(aNewPoints[1]->xy());
722   if (aDelta) {
723     for (int i = 0; i < 2; i++) {
724       aNewPoints[i]->setX(aNewPoints[i]->x() + aDelta->x());
725       aNewPoints[i]->setY(aNewPoints[i]->y() + aDelta->y());
726     }
727   }
728
729   // Update positions of points
730   std::list<EntityWrapperPtr>::const_iterator anUpdLine = aConstrLines.begin();
731   if (aLineToUpd > 0) ++anUpdLine;
732   const std::list<EntityWrapperPtr>& anUpdPoints = (*anUpdLine)->subEntities();
733   std::list<EntityWrapperPtr>::const_iterator aPIt = anUpdPoints.begin();
734   for (int i = 0; aPIt != anUpdPoints.end(); ++aPIt, ++i) {
735     double aCoord[2] = {aNewPoints[i]->x(), aNewPoints[i]->y()};
736     const std::list<ParameterWrapperPtr>& aParams = (*aPIt)->parameters();
737     std::list<ParameterWrapperPtr>::const_iterator aParIt = aParams.begin();
738     for (int j = 0; aParIt != aParams.end(); ++j, ++aParIt)
739       (*aParIt)->setValue(aCoord[j]);
740   }
741 }
742
743 void adjustMirror(ConstraintWrapperPtr theConstraint)
744 {
745   std::vector<EntityWrapperPtr> aPoints;
746   EntityWrapperPtr aMirrorLine;
747
748   const std::list<EntityWrapperPtr>& aSubs = theConstraint->entities();
749   std::list<EntityWrapperPtr>::const_iterator anIt = aSubs.begin();
750   for (; anIt != aSubs.end(); ++anIt) {
751     if ((*anIt)->type() == ENTITY_POINT)
752       aPoints.push_back(*anIt);
753     else if ((*anIt)->type() == ENTITY_LINE)
754       aMirrorLine = *anIt;
755   }
756
757   makeMirrorPoints(aPoints[0], aPoints[1], aMirrorLine);
758 }
759
760 void makeMirrorPoints(EntityWrapperPtr theOriginal,
761                       EntityWrapperPtr theMirrored,
762                       EntityWrapperPtr theMirrorLine)
763 {
764   BuilderPtr aBuilder = SolveSpaceSolver_Builder::getInstance();
765
766   std::shared_ptr<GeomAPI_Lin2d> aMirrorLine = aBuilder->line(theMirrorLine);
767   std::shared_ptr<GeomAPI_Dir2d> aMLDir = aMirrorLine->direction();
768   // orthogonal direction
769   aMLDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aMLDir->y(), -aMLDir->x()));
770
771   std::shared_ptr<GeomAPI_Pnt2d> aPoint = aBuilder->point(theOriginal);
772   std::shared_ptr<GeomAPI_XY> aVec = aPoint->xy()->decreased(aMirrorLine->location()->xy());
773   double aDist = aVec->dot(aMLDir->xy());
774   aVec = aPoint->xy()->added(aMLDir->xy()->multiplied(-2.0 * aDist));
775   double aCoord[2] = {aVec->x(), aVec->y()};
776   std::list<ParameterWrapperPtr>::const_iterator aMIt = theMirrored->parameters().begin();
777   for (int i = 0; aMIt != theMirrored->parameters().end(); ++aMIt, ++i)
778     (*aMIt)->setValue(aCoord[i]);
779
780   // update corresponding attribute
781   AttributePtr anAttr = std::dynamic_pointer_cast<SolveSpaceSolver_EntityWrapper>(theMirrored)->baseAttribute();
782   if (anAttr) {
783     std::shared_ptr<GeomDataAPI_Point2D> aMirroredPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
784     aMirroredPnt->setValue(aCoord[0], aCoord[1]);
785   }
786 }