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