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