Salome HOME
Prevent crash on sketcher plane change
[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;
154         if (aObjRef)
155           aObj = ModelAPI_Feature::feature(aObjRef->object());
156         bool aContains = false;
157         if (aObj && containsPoint(aObj, aPnt2d, thePos)) {
158           aContains = true;
159         } else {
160           aObjRef = aData->refattr(SketchPlugin_Constraint::ENTITY_B());
161           if (aObjRef)
162             aObj = ModelAPI_Feature::feature(aObjRef->object());
163           if (aObj && containsPoint(aObj, aPnt2d, thePos)) {
164             aContains = true;
165           }
166         }
167         if (aContains) {
168           myPntShapes[aFeature.get()][0] = aId;
169           aId++;
170           aFeaList.push_back(aFeature.get());
171         }
172       }
173     }
174     int aSize = (int) aFeaList.size();
175     std::list<const ModelAPI_Feature*>::const_iterator aIt;
176     for (aIt = aFeaList.cbegin(); aIt != aFeaList.cend(); aIt++) {
177       myPntShapes[*aIt][1] = aSize;
178     }
179   }
180   return myPntShapes[thePrs->feature()];
181 }
182
183 //*****************************************************************
184 gp_Vec getVector(ObjectPtr theShape, GeomDirPtr theDir, gp_Pnt theP)
185 {
186   gp_Vec aVec;
187   std::shared_ptr<GeomAPI_Shape> aShape = SketcherPrs_Tools::getShape(theShape);
188   if (aShape->isEdge()) {
189     std::shared_ptr<GeomAPI_Curve> aCurve =
190       std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShape));
191
192     if (aCurve->isCircle()) {
193       Handle(Geom_Curve) aCurv = aCurve->impl<Handle_Geom_Curve>();
194       GeomAPI_ProjectPointOnCurve anExtr(theP, aCurv);
195       double aParam = anExtr.LowerDistanceParameter();
196       gp_Pnt aP;
197       aCurv->D1(aParam, aP, aVec);
198       // 2458: check correct orientation of the vector
199       if (aVec.SquareMagnitude() > Precision::Confusion()) {
200         std::shared_ptr<GeomAPI_Edge> aCircEdge(new GeomAPI_Edge(aShape));
201         double aFirstParam, aLastParam;
202         aCircEdge->getRange(aFirstParam, aLastParam);
203         // if parameter is near the LastParam, make the vector go inside (reverse)
204         double aDelta = aLastParam - aParam;
205         while (aDelta < -Precision::Confusion())
206           aDelta += 2. * M_PI;
207         while (aDelta > 2. * M_PI - Precision::Confusion())
208           aDelta -= 2. * M_PI;
209         if (fabs(aDelta) < Precision::Confusion())
210           aVec.Reverse();
211       }
212     } else {
213       GeomPointPtr aPnt1 = aCurve->getPoint(aCurve->endParam());
214       GeomPointPtr aPnt2 = aCurve->getPoint(aCurve->startParam());
215
216       gp_Pnt aPn2 = aPnt2->impl<gp_Pnt>();
217       if (aPn2.IsEqual(theP, Precision::Confusion()))
218         aVec = gp_Vec(aPn2, aPnt1->impl<gp_Pnt>());
219       else
220         aVec = gp_Vec(aPnt1->impl<gp_Pnt>(), aPn2);
221     }
222   } else {
223     aVec = gp_Vec(theDir->impl<gp_Dir>());
224   }
225   return aVec;
226 }
227
228 //*****************************************************************
229 gp_Pnt SketcherPrs_PositionMgr::getPosition(ObjectPtr theShape,
230                                             const SketcherPrs_SymbolPrs* thePrs,
231                                             double theStep, GeomPointPtr thePnt)
232 {
233   std::shared_ptr<GeomAPI_Shape> aShape = SketcherPrs_Tools::getShape(theShape);
234   gp_Pnt aP; // Central point
235
236   if (thePnt.get()) {
237     return getPointPosition(theShape, thePrs, theStep, thePnt);
238   } else {
239     if (aShape->isEdge()) {
240       std::shared_ptr<GeomAPI_Curve> aCurve =
241         std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShape));
242       // this is a circle or arc
243       double aMidParam = (aCurve->startParam() + aCurve->endParam()) / 2.;
244       std::shared_ptr<GeomAPI_Pnt> aPnt = aCurve->getPoint(aMidParam);
245       aP = aPnt->impl<gp_Pnt>();
246     } else {
247       // This is a point
248       std::shared_ptr<GeomAPI_Vertex> aVertex =
249         std::shared_ptr<GeomAPI_Vertex>(new GeomAPI_Vertex(aShape));
250       std::shared_ptr<GeomAPI_Pnt> aPnt = aVertex->point();
251       aP = aPnt->impl<gp_Pnt>();
252     }
253   }
254   // main vector
255   gp_Vec aVec1 = getVector(theShape, thePrs->plane()->dirX(), aP);
256
257   // Compute shifting vector for a one symbol
258   gp_Vec aShift = aVec1.Crossed(thePrs->plane()->normal()->impl<gp_Dir>());
259   aShift.Normalize();
260   aShift.Multiply(theStep * 0.8);
261
262   // Shift the position coordinate according to position index
263   int aPos = getPositionIndex(theShape, thePrs);
264   int aM = 1;
265   if ((aPos % 2) == 0) {
266     // Even position
267     aP.Translate(aShift);
268     if (aPos > 0) {
269       if (aPos % 4 == 0)
270         aM = aPos / 4;
271       else
272         aM = -(aPos + 2) / 4;
273     }
274   } else {
275     // Odd position
276     aP.Translate(-aShift);
277     if (aPos > 1) {
278       if ((aPos - 1) % 4 == 0)
279         aM = (aPos - 1) / 4;
280       else
281         aM = -(aPos + 1) / 4;
282     }
283   }
284   if (aPos > 1) {
285     // Normalize vector along the line
286     aVec1.Normalize();
287     aVec1.Multiply(theStep);
288     aP.Translate(aVec1.Multiplied(aM));
289   }
290   return aP;
291 }
292
293
294 //*****************************************************************
295 //! Returns curves connected to the given point
296 std::list<ObjectPtr> getCurves(const GeomPointPtr& thePnt, const SketcherPrs_SymbolPrs* thePrs)
297 {
298   std::list<ObjectPtr> aList;
299   GeomAx3Ptr aAx3 = thePrs->plane();
300   ModelAPI_CompositeFeature* aOwner = thePrs->sketcher();
301   GeomPnt2dPtr aPnt2d = thePnt->to2D(aAx3->origin(), aAx3->dirX(), aAx3->dirY());
302
303   int aNbSubs = aOwner->numberOfSubs();
304   for (int i = 0; i < aNbSubs; i++) {
305     FeaturePtr aFeature = aOwner->subFeature(i);
306     if (!aFeature->firstResult().get() || aFeature->firstResult()->isDisabled())
307       continue;  // There is no result
308
309     if (aFeature->getKind() == SketchPlugin_Line::ID()) {
310       AttributePoint2DPtr aSPnt1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
311         aFeature->data()->attribute(SketchPlugin_Line::START_ID()));
312       AttributePoint2DPtr aSPnt2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
313         aFeature->data()->attribute(SketchPlugin_Line::END_ID()));
314
315       GeomPnt2dPtr aPnt1 = aSPnt1->pnt();
316       GeomPnt2dPtr aPnt2 = aSPnt2->pnt();
317
318       if (aPnt1->distance(aPnt2d) <= Precision::Confusion() ||
319           aPnt2->distance(aPnt2d) <= Precision::Confusion()) {
320         // a point corresponds to one of the line end
321         GeomShapePtr aShp = SketcherPrs_Tools::getShape(aFeature->firstResult());
322         GeomCurvePtr aCurv = std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShp));
323         aList.push_back(aFeature->firstResult());
324       } else {
325         // Check that a point belongs to the curve
326         GeomAPI_Lin2d aLin2d(aPnt1, aPnt2);
327         double aDist = aLin2d.distance(aPnt2d);
328         if (aDist <= Precision::Confusion())
329           aList.push_back(aFeature->firstResult());
330       }
331     } else if ((aFeature->getKind() == SketchPlugin_Circle::ID()) ||
332               (aFeature->getKind() == SketchPlugin_Arc::ID())) {
333       GeomCurvePtr aCurve;
334       ObjectPtr aResObj;
335       std::list<ResultPtr> aResults = aFeature->results();
336       std::list<ResultPtr>::const_iterator aIt;
337       for (aIt = aResults.cbegin(); aIt != aResults.cend(); aIt++) {
338         GeomShapePtr aShp = SketcherPrs_Tools::getShape((*aIt));
339         if (aShp->isEdge()) {
340           aResObj = (*aIt);
341           aCurve = std::shared_ptr<GeomAPI_Curve>(new GeomAPI_Curve(aShp));
342           break;
343         }
344       }
345       if (aCurve.get()) {
346         double aStart = aCurve->startParam();
347         double aEnd = aCurve->endParam();
348         GeomCirclePtr  aCircle = GeomCirclePtr(new GeomAPI_Circ(aCurve));
349         GeomPointPtr aProjPnt = aCircle->project(thePnt);
350         if (aProjPnt && thePnt->distance(aProjPnt) <= Precision::Confusion())
351           aList.push_back(aResObj);
352       }
353     }
354   }
355   return aList;
356 }
357
358 //*****************************************************************
359 gp_Pnt SketcherPrs_PositionMgr::getPointPosition(
360   ObjectPtr theLine, const SketcherPrs_SymbolPrs* thePrs,
361   double theStep, GeomPointPtr thePnt)
362 {
363   gp_Pnt aP = thePnt->impl<gp_Pnt>();
364   if (!thePrs->plane().get())
365     return aP;
366   GeomDirPtr aNormal = thePrs->plane()->normal();
367   gp_Dir aNormDir = aNormal->impl<gp_Dir>();
368
369   std::list<ObjectPtr> aCurves = getCurves(thePnt, thePrs);
370   std::list<ObjectPtr>::const_iterator aItCurv;
371   std::list<gp_Vec> aVectorsList;
372   // Calculate all vectors
373   for (aItCurv = aCurves.cbegin(); aItCurv != aCurves.cend(); aItCurv++) {
374     aVectorsList.push_back(getVector((*aItCurv), thePrs->plane()->dirX(), aP));
375   }
376
377   // Position of the symbol
378   const std::array<int, 2>& aPos = getPositionIndex(thePnt, thePrs);
379
380   // Angle size of a symbol
381   //double aAngleStep = PI * 50./180.;
382   double aAngleStep = PI/4.;
383
384   std::list<gp_Vec>::const_iterator aItVec;
385   std::list<double> aAngles;
386   std::list<gp_Vec> aVectors;
387   // Select closest vectors and calculate angles between base vector and closest vector
388   for (aItVec = aVectorsList.cbegin(); aItVec != aVectorsList.cend(); aItVec++) {
389     std::list<gp_Vec>::const_iterator aIt;
390     double aMinAng = 0;
391     gp_Vec aVec = *aItVec;
392     for (aIt = aVectorsList.cbegin(); aIt != aVectorsList.cend(); aIt++) {
393       double aAng = aVec.AngleWithRef(*aIt, aNormDir);
394       if (aAng != 0) {
395         if (aAng < 0)
396           aAng = 2 * PI + aAng;
397
398         if (aMinAng == 0)
399           aMinAng = aAng;
400         else if (aAng < aMinAng) {
401           aMinAng = aAng;
402         }
403       }
404     }
405     if (aMinAng >= aAngleStep) {
406       aVectors.push_back(aVec);
407       aAngles.push_back(aMinAng);
408     }
409   }
410
411   gp_Ax1 aRotAx(aP, aNormDir);
412   gp_Vec aVecPos;
413   // If number of angle less then number of symbols then each symbol can be placed
414   // directly inside of the angle
415   if (aAngles.size() >= aPos[1] && !aVectors.empty()) {
416     int aId = aPos[0];
417     aVecPos = *(std::next(aVectors.begin(), aId));
418
419     gp_Vec aShift = aVecPos.Rotated(aRotAx, aAngleStep);
420     aShift.Normalize();
421     aShift.Multiply(theStep * 1.5);
422     return aP.Translated(aShift);
423   }
424
425   // A case when there are a lot of symbols
426   int aPosCount = 0;
427   double aAng;
428   std::list<double>::const_iterator aItAng;
429
430   double aAngPos;
431   bool aHasPlace = false;
432   //int aIntId = 0; // a position inside a one sector
433   while (aPosCount < aPos[1]) {
434     for (aItAng = aAngles.cbegin(), aItVec = aVectors.cbegin();
435          aItAng != aAngles.cend(); ++aItAng, ++aItVec) {
436       aAng = (*aItAng);
437       int Nb = int(aAng / aAngleStep);
438       aPosCount += Nb;
439
440       if ((!aHasPlace) && (aPosCount >= (aPos[0] + 1))) {
441         aHasPlace = true;
442         aAngPos = (*aItAng);
443         aVecPos = (*aItVec);
444         //aIntId = aPos[0] - (aPosCount - Nb);
445       }
446     }
447     if (aPosCount < aPos[1]) {
448       aAngleStep -= 0.1;
449       aHasPlace = false;
450       aPosCount = 0;
451     }
452     if (aAngleStep <= 0)
453       break;
454   }
455
456   if (aHasPlace) {
457     // rotate base vector on a necessary angle
458     gp_Vec aShift = aVecPos.Rotated(aRotAx, aAngleStep + aAngleStep * aPos[0]);
459     aShift.Normalize();
460     aShift.Multiply(theStep * 1.5);
461     return aP.Translated(aShift);
462   }
463   return aP;
464 }
465
466 //*****************************************************************
467 void SketcherPrs_PositionMgr::deleteConstraint(const SketcherPrs_SymbolPrs* thePrs)
468 {
469   std::map<ObjectPtr, PositionsMap>::iterator aIt;
470   std::list<ObjectPtr> aToDel;
471   // Clear map for deleted presentation
472   for (aIt = myShapes.begin(); aIt != myShapes.end(); ++aIt) {
473     PositionsMap& aPosMap = aIt->second;
474     if (aPosMap.count(thePrs) > 0) {
475       // Erase index
476       aPosMap.erase(aPosMap.find(thePrs));
477       if (aPosMap.size() == 0)
478         // Delete the map
479         aToDel.push_back(aIt->first);
480       else {
481         // Reindex objects positions in order to avoid spaces
482         PositionsMap::iterator aIt;
483         int i = 0;
484         for (aIt = aPosMap.begin(); aIt != aPosMap.end(); aIt++, i++)
485           aIt->second = i;
486       }
487     }
488   }
489   std::list<ObjectPtr>::const_iterator aListIt;
490   for (aListIt = aToDel.cbegin(); aListIt != aToDel.cend(); ++aListIt) {
491     myShapes.erase(*aListIt);
492   }
493 }