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