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