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