Salome HOME
Merge remote-tracking branch 'remotes/origin/HigherLevelObjectsHistory'
[modules/shaper.git] / src / SketcherPrs / SketcherPrs_PositionMgr.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "SketcherPrs_PositionMgr.h"
21 #include "SketcherPrs_Tools.h"
22
23 #include <GeomAPI_Edge.h>
24 #include <GeomAPI_Curve.h>
25 #include <GeomAPI_Vertex.h>
26 #include <GeomAPI_Dir.h>
27 #include <GeomAPI_Ax3.h>
28 #include <GeomAPI_Circ.h>
29 #include <GeomAPI_Lin2d.h>
30
31 #include <GeomDataAPI_Point2D.h>
32
33 #include <SketchPlugin_Line.h>
34 #include <SketchPlugin_Circle.h>
35 #include <SketchPlugin_Arc.h>
36 #include <SketchPlugin_ConstraintTangent.h>
37 #include <SketchPlugin_ConstraintPerpendicular.h>
38
39 #include <TopoDS_Vertex.hxx>
40 #include <Geom_Curve.hxx>
41 #include <GeomAPI_ProjectPointOnCurve.hxx>
42 #include <TColGeom_SequenceOfCurve.hxx>
43 #include <gp_Dir.hxx>
44
45 #include <array>
46
47 static SketcherPrs_PositionMgr* MyPosMgr = NULL;
48
49 #define PI 3.1415926535897932
50
51 // The class is implemented as a singlton
52 SketcherPrs_PositionMgr* SketcherPrs_PositionMgr::get()
53 {
54   if (MyPosMgr == NULL)
55     MyPosMgr = new SketcherPrs_PositionMgr();
56   return MyPosMgr;
57 }
58
59 SketcherPrs_PositionMgr::SketcherPrs_PositionMgr()
60 {
61 }
62
63 int SketcherPrs_PositionMgr::getPositionIndex(ObjectPtr theLine,
64                                               const SketcherPrs_SymbolPrs* thePrs)
65 {
66   if (myShapes.count(theLine) == 1) {
67     // Find the map and add new [Presentation - Index] pair
68     PositionsMap& aPosMap = myShapes[theLine];
69     if (aPosMap.count(thePrs) == 1) {
70       // return existing index
71       return aPosMap[thePrs];
72     } else {
73       // Add a new [Presentation - Index] pair
74       int aInd = int(aPosMap.size());
75       aPosMap[thePrs] = aInd;
76       return aInd;
77     }
78   } else {
79     // Create a new map with initial index
80     PositionsMap aPosMap;
81     aPosMap[thePrs] = 0;
82     myShapes[theLine] = aPosMap;
83     return 0;
84   }
85 }
86
87
88 bool SketcherPrs_PositionMgr::isPntConstraint(const std::string& theName)
89 {
90   return ((theName == SketchPlugin_ConstraintTangent::ID()) ||
91     (theName == SketchPlugin_ConstraintPerpendicular::ID()));
92 }
93
94 bool containsPoint(const FeaturePtr& theFeature, GeomPnt2dPtr thePnt2d, GeomPointPtr thePos)
95 {
96   if (theFeature->getKind() == SketchPlugin_Line::ID()) {
97     AttributePoint2DPtr aSPnt1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
98       theFeature->data()->attribute(SketchPlugin_Line::START_ID()));
99     AttributePoint2DPtr aSPnt2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
100       theFeature->data()->attribute(SketchPlugin_Line::END_ID()));
101
102     GeomPnt2dPtr aPnt1 = aSPnt1->pnt();
103     GeomPnt2dPtr aPnt2 = aSPnt2->pnt();
104
105     if (aPnt1->isEqual(thePnt2d) || aPnt2->isEqual(thePnt2d))
106       return true;
107   } else if ((theFeature->getKind() == SketchPlugin_Circle::ID()) ||
108              (theFeature->getKind() == SketchPlugin_Arc::ID())) {
109     GeomCurvePtr aCurve;
110     ObjectPtr aResObj;
111     std::list<ResultPtr> aResults = theFeature->results();
112     std::list<ResultPtr>::const_iterator aIt;
113     for (aIt = aResults.cbegin(); aIt != aResults.cend(); aIt++) {
114       GeomShapePtr aShp = SketcherPrs_Tools::getShape((*aIt));
115       if (aShp->isEdge()) {
116         aResObj = (*aIt);
117         aCurve = std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShp));
118         break;
119       }
120     }
121     if (aCurve.get()) {
122       double aStart = aCurve->startParam();
123       double aEnd = aCurve->endParam();
124       GeomCirclePtr  aCircle = GeomCirclePtr(new GeomAPI_Circ(aCurve));
125       double aParam;
126       if (aCircle->parameter(thePos, 1.e-4, aParam) && (aParam >= aStart) && (aParam <= aEnd))
127         return true;
128     }
129   }
130   return false;
131 }
132
133 const std::array<int, 2>& SketcherPrs_PositionMgr::getPositionIndex(GeomPointPtr thePos,
134                                               const SketcherPrs_SymbolPrs* thePrs)
135 {
136   if (myPntShapes.count(thePrs->feature()) == 0) {
137     // Renumerate positions around the specified constraint point for all constraints
138     GeomAx3Ptr aAx3 = thePrs->plane();
139     ModelAPI_CompositeFeature* aOwner = thePrs->sketcher();
140     GeomPnt2dPtr aPnt2d = thePos->to2D(aAx3->origin(), aAx3->dirX(), aAx3->dirY());
141
142     int aNbSubs = aOwner->numberOfSubs();
143     int aId = 0;
144     std::list<const ModelAPI_Feature*> aFeaList;
145     for (int i = 0; i < aNbSubs; i++) {
146       FeaturePtr aFeature = aOwner->subFeature(i);
147
148       bool aUseFeature = ((myPntShapes.count(aFeature.get()) == 1) ||
149                          (isPntConstraint(aFeature->getKind())));
150       if (aUseFeature) {
151         DataPtr aData = aFeature->data();
152         AttributeRefAttrPtr aObjRef = aData->refattr(SketchPlugin_Constraint::ENTITY_A());
153         FeaturePtr aObj = ModelAPI_Feature::feature(aObjRef->object());
154         bool aContains = false;
155         if (containsPoint(aObj, aPnt2d, thePos)) {
156           aContains = true;
157         } else {
158           aObjRef = aData->refattr(SketchPlugin_Constraint::ENTITY_B());
159           aObj = ModelAPI_Feature::feature(aObjRef->object());
160           if (containsPoint(aObj, aPnt2d, thePos)) {
161             aContains = true;
162           }
163         }
164         if (aContains) {
165           myPntShapes[aFeature.get()][0] = aId;
166           aId++;
167           aFeaList.push_back(aFeature.get());
168         }
169       }
170     }
171     int aSize = (int) aFeaList.size();
172     std::list<const ModelAPI_Feature*>::const_iterator aIt;
173     for (aIt = aFeaList.cbegin(); aIt != aFeaList.cend(); aIt++) {
174       myPntShapes[*aIt][1] = aSize;
175     }
176   }
177   return myPntShapes[thePrs->feature()];
178 }
179
180 //*****************************************************************
181 gp_Vec getVector(ObjectPtr theShape, GeomDirPtr theDir, gp_Pnt theP)
182 {
183   gp_Vec aVec;
184   std::shared_ptr<GeomAPI_Shape> aShape = SketcherPrs_Tools::getShape(theShape);
185   if (aShape->isEdge()) {
186     std::shared_ptr<GeomAPI_Curve> aCurve =
187       std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShape));
188
189     if (aCurve->isCircle()) {
190       Handle(Geom_Curve) aCurv = aCurve->impl<Handle_Geom_Curve>();
191       GeomAPI_ProjectPointOnCurve anExtr(theP, aCurv);
192       double aParam = anExtr.LowerDistanceParameter();
193       gp_Pnt aP;
194       aCurv->D1(aParam, aP, aVec);
195       // 2458: check correct orientation of the vector
196       if (aVec.SquareMagnitude() > Precision::Confusion()) {
197         std::shared_ptr<GeomAPI_Edge> aCircEdge(new GeomAPI_Edge(aShape));
198         double aFirstParam, aLastParam;
199         aCircEdge->getRange(aFirstParam, aLastParam);
200         // if parameter is near the LastParam, make the vector go inside (reverse)
201         double aDelta = aLastParam - aParam;
202         while (aDelta < -Precision::Confusion())
203           aDelta += 2. * M_PI;
204         while (aDelta > 2. * M_PI - Precision::Confusion())
205           aDelta -= 2. * M_PI;
206         if (fabs(aDelta) < Precision::Confusion())
207           aVec.Reverse();
208       }
209     } else {
210       GeomPointPtr aPnt1 = aCurve->getPoint(aCurve->endParam());
211       GeomPointPtr aPnt2 = aCurve->getPoint(aCurve->startParam());
212
213       gp_Pnt aPn2 = aPnt2->impl<gp_Pnt>();
214       if (aPn2.IsEqual(theP, Precision::Confusion()))
215         aVec = gp_Vec(aPn2, aPnt1->impl<gp_Pnt>());
216       else
217         aVec = gp_Vec(aPnt1->impl<gp_Pnt>(), aPn2);
218     }
219   } else {
220     aVec = gp_Vec(theDir->impl<gp_Dir>());
221   }
222   return aVec;
223 }
224
225 //*****************************************************************
226 gp_Pnt SketcherPrs_PositionMgr::getPosition(ObjectPtr theShape,
227                                             const SketcherPrs_SymbolPrs* thePrs,
228                                             double theStep, GeomPointPtr thePnt)
229 {
230   std::shared_ptr<GeomAPI_Shape> aShape = SketcherPrs_Tools::getShape(theShape);
231   gp_Pnt aP; // Central point
232
233   if (thePnt.get()) {
234     return getPointPosition(theShape, thePrs, theStep, thePnt);
235   } else {
236     if (aShape->isEdge()) {
237       std::shared_ptr<GeomAPI_Curve> aCurve =
238         std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShape));
239       // this is a circle or arc
240       double aMidParam = (aCurve->startParam() + aCurve->endParam()) / 2.;
241       std::shared_ptr<GeomAPI_Pnt> aPnt = aCurve->getPoint(aMidParam);
242       aP = aPnt->impl<gp_Pnt>();
243     } else {
244       // This is a point
245       std::shared_ptr<GeomAPI_Vertex> aVertex =
246         std::shared_ptr<GeomAPI_Vertex>(new GeomAPI_Vertex(aShape));
247       std::shared_ptr<GeomAPI_Pnt> aPnt = aVertex->point();
248       aP = aPnt->impl<gp_Pnt>();
249     }
250   }
251   // main vector
252   gp_Vec aVec1 = getVector(theShape, thePrs->plane()->dirX(), aP);
253
254   // Compute shifting vector for a one symbol
255   gp_Vec aShift = aVec1.Crossed(thePrs->plane()->normal()->impl<gp_Dir>());
256   aShift.Normalize();
257   aShift.Multiply(theStep * 0.8);
258
259   // Shift the position coordinate according to position index
260   int aPos = getPositionIndex(theShape, thePrs);
261   int aM = 1;
262   if ((aPos % 2) == 0) {
263     // Even position
264     aP.Translate(aShift);
265     if (aPos > 0) {
266       if (aPos % 4 == 0)
267         aM = aPos / 4;
268       else
269         aM = -(aPos + 2) / 4;
270     }
271   } else {
272     // Odd position
273     aP.Translate(-aShift);
274     if (aPos > 1) {
275       if ((aPos - 1) % 4 == 0)
276         aM = (aPos - 1) / 4;
277       else
278         aM = -(aPos + 1) / 4;
279     }
280   }
281   if (aPos > 1) {
282     // Normalize vector along the line
283     aVec1.Normalize();
284     aVec1.Multiply(theStep);
285     aP.Translate(aVec1.Multiplied(aM));
286   }
287   return aP;
288 }
289
290
291 //*****************************************************************
292 //! Returns curves connected to the given point
293 std::list<ObjectPtr> getCurves(const GeomPointPtr& thePnt, const SketcherPrs_SymbolPrs* thePrs)
294 {
295   std::list<ObjectPtr> aList;
296   GeomAx3Ptr aAx3 = thePrs->plane();
297   ModelAPI_CompositeFeature* aOwner = thePrs->sketcher();
298   GeomPnt2dPtr aPnt2d = thePnt->to2D(aAx3->origin(), aAx3->dirX(), aAx3->dirY());
299
300   int aNbSubs = aOwner->numberOfSubs();
301   for (int i = 0; i < aNbSubs; i++) {
302     FeaturePtr aFeature = aOwner->subFeature(i);
303     if (!aFeature->firstResult().get() || aFeature->firstResult()->isDisabled())
304       continue;  // There is no result
305
306     if (aFeature->getKind() == SketchPlugin_Line::ID()) {
307       AttributePoint2DPtr aSPnt1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
308         aFeature->data()->attribute(SketchPlugin_Line::START_ID()));
309       AttributePoint2DPtr aSPnt2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
310         aFeature->data()->attribute(SketchPlugin_Line::END_ID()));
311
312       GeomPnt2dPtr aPnt1 = aSPnt1->pnt();
313       GeomPnt2dPtr aPnt2 = aSPnt2->pnt();
314
315       if (aPnt1->distance(aPnt2d) <= Precision::Confusion() ||
316           aPnt2->distance(aPnt2d) <= Precision::Confusion()) {
317         // a point corresponds to one of the line end
318         GeomShapePtr aShp = SketcherPrs_Tools::getShape(aFeature->firstResult());
319         GeomCurvePtr aCurv = std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShp));
320         aList.push_back(aFeature->firstResult());
321       } else {
322         // Check that a point belongs to the curve
323         GeomAPI_Lin2d aLin2d(aPnt1, aPnt2);
324         double aDist = aLin2d.distance(aPnt2d);
325         if (aDist <= Precision::Confusion())
326           aList.push_back(aFeature->firstResult());
327       }
328     } else if ((aFeature->getKind() == SketchPlugin_Circle::ID()) ||
329               (aFeature->getKind() == SketchPlugin_Arc::ID())) {
330       GeomCurvePtr aCurve;
331       ObjectPtr aResObj;
332       std::list<ResultPtr> aResults = aFeature->results();
333       std::list<ResultPtr>::const_iterator aIt;
334       for (aIt = aResults.cbegin(); aIt != aResults.cend(); aIt++) {
335         GeomShapePtr aShp = SketcherPrs_Tools::getShape((*aIt));
336         if (aShp->isEdge()) {
337           aResObj = (*aIt);
338           aCurve = std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShp));
339           break;
340         }
341       }
342       if (aCurve.get()) {
343         double aStart = aCurve->startParam();
344         double aEnd = aCurve->endParam();
345         GeomCirclePtr  aCircle = GeomCirclePtr(new GeomAPI_Circ(aCurve));
346         GeomPointPtr aProjPnt = aCircle->project(thePnt);
347         if (aProjPnt && thePnt->distance(aProjPnt) <= Precision::Confusion())
348           aList.push_back(aResObj);
349       }
350     }
351   }
352   return aList;
353 }
354
355 //*****************************************************************
356 gp_Pnt SketcherPrs_PositionMgr::getPointPosition(
357   ObjectPtr theLine, const SketcherPrs_SymbolPrs* thePrs,
358   double theStep, GeomPointPtr thePnt)
359 {
360   gp_Pnt aP = thePnt->impl<gp_Pnt>();
361   GeomDirPtr aNormal = thePrs->plane()->normal();
362   gp_Dir aNormDir = aNormal->impl<gp_Dir>();
363
364   std::list<ObjectPtr> aCurves = getCurves(thePnt, thePrs);
365   std::list<ObjectPtr>::const_iterator aItCurv;
366   std::list<gp_Vec> aVectorsList;
367   // Calculate all vectors
368   for (aItCurv = aCurves.cbegin(); aItCurv != aCurves.cend(); aItCurv++) {
369     aVectorsList.push_back(getVector((*aItCurv), thePrs->plane()->dirX(), aP));
370   }
371
372   // Position of the symbol
373   const std::array<int, 2>& aPos = getPositionIndex(thePnt, thePrs);
374
375   // Angle size of a symbol
376   //double aAngleStep = PI * 50./180.;
377   double aAngleStep = PI/4.;
378
379   std::list<gp_Vec>::const_iterator aItVec;
380   std::list<double> aAngles;
381   std::list<gp_Vec> aVectors;
382   // Select closest vectors and calculate angles between base vector and closest vector
383   for (aItVec = aVectorsList.cbegin(); aItVec != aVectorsList.cend(); aItVec++) {
384     std::list<gp_Vec>::const_iterator aIt;
385     double aMinAng = 0;
386     gp_Vec aVec = *aItVec;
387     for (aIt = aVectorsList.cbegin(); aIt != aVectorsList.cend(); aIt++) {
388       double aAng = aVec.AngleWithRef(*aIt, aNormDir);
389       if (aAng != 0) {
390         if (aAng < 0)
391           aAng = 2 * PI + aAng;
392
393         if (aMinAng == 0)
394           aMinAng = aAng;
395         else if (aAng < aMinAng) {
396           aMinAng = aAng;
397         }
398       }
399     }
400     if (aMinAng >= aAngleStep) {
401       aVectors.push_back(aVec);
402       aAngles.push_back(aMinAng);
403     }
404   }
405
406   gp_Ax1 aRotAx(aP, aNormDir);
407   gp_Vec aVecPos;
408   // If number of angle less then number of symbols then each symbol can be placed
409   // directly inside of the angle
410   if (aAngles.size() >= aPos[1] && !aVectors.empty()) {
411     int aId = aPos[0];
412     aVecPos = *(std::next(aVectors.begin(), aId));
413
414     gp_Vec aShift = aVecPos.Rotated(aRotAx, aAngleStep);
415     aShift.Normalize();
416     aShift.Multiply(theStep * 1.5);
417     return aP.Translated(aShift);
418   }
419
420   // A case when there are a lot of symbols
421   int aPosCount = 0;
422   double aAng;
423   std::list<double>::const_iterator aItAng;
424
425   double aAngPos;
426   bool aHasPlace = false;
427   //int aIntId = 0; // a position inside a one sector
428   while (aPosCount < aPos[1]) {
429     for (aItAng = aAngles.cbegin(), aItVec = aVectors.cbegin();
430          aItAng != aAngles.cend(); ++aItAng, ++aItVec) {
431       aAng = (*aItAng);
432       int Nb = int(aAng / aAngleStep);
433       aPosCount += Nb;
434
435       if ((!aHasPlace) && (aPosCount >= (aPos[0] + 1))) {
436         aHasPlace = true;
437         aAngPos = (*aItAng);
438         aVecPos = (*aItVec);
439         //aIntId = aPos[0] - (aPosCount - Nb);
440       }
441     }
442     if (aPosCount < aPos[1]) {
443       aAngleStep -= 0.1;
444       aHasPlace = false;
445       aPosCount = 0;
446     }
447     if (aAngleStep <= 0)
448       break;
449   }
450
451   if (aHasPlace) {
452     // rotate base vector on a necessary angle
453     gp_Vec aShift = aVecPos.Rotated(aRotAx, aAngleStep + aAngleStep * aPos[0]);
454     aShift.Normalize();
455     aShift.Multiply(theStep * 1.5);
456     return aP.Translated(aShift);
457   }
458   return aP;
459 }
460
461 //*****************************************************************
462 void SketcherPrs_PositionMgr::deleteConstraint(const SketcherPrs_SymbolPrs* thePrs)
463 {
464   std::map<ObjectPtr, PositionsMap>::iterator aIt;
465   std::list<ObjectPtr> aToDel;
466   // Clear map for deleted presentation
467   for (aIt = myShapes.begin(); aIt != myShapes.end(); ++aIt) {
468     PositionsMap& aPosMap = aIt->second;
469     if (aPosMap.count(thePrs) > 0) {
470       // Erase index
471       aPosMap.erase(aPosMap.find(thePrs));
472       if (aPosMap.size() == 0)
473         // Delete the map
474         aToDel.push_back(aIt->first);
475       else {
476         // Reindex objects positions in order to avoid spaces
477         PositionsMap::iterator aIt;
478         int i = 0;
479         for (aIt = aPosMap.begin(); aIt != aPosMap.end(); aIt++, i++)
480           aIt->second = i;
481       }
482     }
483   }
484   std::list<ObjectPtr>::const_iterator aListIt;
485   for (aListIt = aToDel.cbegin(); aListIt != aToDel.cend(); ++aListIt) {
486     myShapes.erase(*aListIt);
487   }
488 }