Salome HOME
Copyright update 2022
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_NExplode.cpp
1 // Copyright (C) 2017-2022  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 "GeomAlgoAPI_NExplode.h"
21
22 #include <GeomAPI_Pnt.h>
23 #include <GeomAPI_ShapeExplorer.h>
24
25 #include <Bnd_Box.hxx>
26 #include <BRep_Tool.hxx>
27 #include <BRepBndLib.hxx>
28 #include <BRepGProp.hxx>
29 #include <GProp_GProps.hxx>
30 #include <Precision.hxx>
31 #include <TopoDS.hxx>
32 #include <TopoDS_Shape.hxx>
33
34 #include <algorithm>
35 #include <set>
36 #include <unordered_map>
37
38 namespace NExplodeTools
39 {
40   void dummy(gp_Pnt&)
41   {
42     // do nothing (new approach to order shapes)
43   }
44
45   void pointToDouble(gp_Pnt& thePoint)
46   {
47     // old approach to order shapes
48     double dMidXYZ = thePoint.X() * 999.0 + thePoint.Y() * 99.0 + thePoint.Z() * 0.9;
49     thePoint.SetCoord(dMidXYZ, 0.0, 0.0);
50   }
51
52   std::pair<GeomPointPtr, double> ShapeToDouble(const GeomShapePtr& theShape,
53                                                 void(*convertPoint)(gp_Pnt&))
54   {
55     // Computing of CentreOfMass
56     gp_Pnt GPoint;
57     double Len;
58
59     TopoDS_Shape S = theShape->impl<TopoDS_Shape>();
60     if (S.ShapeType() == TopAbs_VERTEX) {
61       GPoint = BRep_Tool::Pnt(TopoDS::Vertex(S));
62       Len = (double)S.Orientation();
63     }
64     else {
65       GProp_GProps GPr;
66       if (S.ShapeType() == TopAbs_EDGE || S.ShapeType() == TopAbs_WIRE) {
67         BRepGProp::LinearProperties(S, GPr);
68       }
69       else if (S.ShapeType() == TopAbs_FACE || S.ShapeType() == TopAbs_SHELL) {
70         BRepGProp::SurfaceProperties(S, GPr);
71       }
72       else {
73         BRepGProp::VolumeProperties(S, GPr);
74       }
75       GPoint = GPr.CentreOfMass();
76       Len = GPr.Mass();
77     }
78
79     (*convertPoint)(GPoint);
80     GeomPointPtr aMidPoint(new GeomAPI_Pnt(GPoint.X(), GPoint.Y(), GPoint.Z()));
81     return std::make_pair(aMidPoint, Len);
82   }
83
84   /*!
85   * \brief Sort shapes in the list by their coordinates.
86   */
87   struct CompareShapes : public std::binary_function<TopoDS_Shape, TopoDS_Shape, bool>
88   {
89     typedef std::unordered_map<GeomShapePtr,
90                                std::pair<GeomPointPtr, double>,
91                                GeomAPI_Shape::Hash,
92                                GeomAPI_Shape::Equal > DataMapOfShapeDouble;
93
94     CompareShapes(void(*convertPoint)(gp_Pnt&))
95       : myConvertPoint(convertPoint) {}
96
97     bool operator() (const GeomShapePtr& lhs, const GeomShapePtr& rhs);
98
99     DataMapOfShapeDouble myMap;
100     void(*myConvertPoint)(gp_Pnt&);
101   };
102 }
103
104 bool NExplodeTools::CompareShapes::operator() (const GeomShapePtr& lhs, const GeomShapePtr& rhs)
105 {
106   if (myMap.find(lhs) == myMap.end()) {
107     myMap[lhs] = ShapeToDouble(lhs, myConvertPoint);
108   }
109
110   if (myMap.find(rhs) == myMap.end()) {
111     myMap[rhs] = ShapeToDouble(rhs, myConvertPoint);
112   }
113
114   const std::pair<GeomPointPtr, double>& val1 = myMap.at(lhs);
115   const std::pair<GeomPointPtr, double>& val2 = myMap.at(rhs);
116
117   double tol = 10.0 * Precision::Confusion();
118   bool exchange = Standard_False;
119
120   // compare coordinates of center points
121   if (val2.first->isLess(val1.first, tol)) {
122     exchange = Standard_True;
123   }
124   else if (!val1.first->isLess(val2.first, tol)) {
125     double dLength = val1.second - val2.second;
126     if (dLength >= tol) {
127       exchange = Standard_True;
128     }
129     else if (Abs(dLength) < tol && lhs->shapeType() <= GeomAPI_Shape::FACE) {
130       // equal values possible on shapes such as two halves of a sphere and
131       // a membrane inside the sphere
132       Bnd_Box box1, box2;
133       BRepBndLib::Add(lhs->impl<TopoDS_Shape>(), box1);
134       if (!box1.IsVoid()) {
135         BRepBndLib::Add(rhs->impl<TopoDS_Shape>(), box2);
136         Standard_Real dSquareExtent = box1.SquareExtent() - box2.SquareExtent();
137         if (dSquareExtent >= tol) {
138           exchange = Standard_True;
139         }
140         else if (Abs(dSquareExtent) < tol) {
141           Standard_Real aXmin, aYmin, aZmin, aXmax, aYmax, aZmax, value1, value2;
142           box1.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
143           value1 = (aXmin + aXmax)*999.0 + (aYmin + aYmax)*99.0 + (aZmin + aZmax)*0.9;
144           box2.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
145           value2 = (aXmin + aXmax)*999.0 + (aYmin + aYmax)*99.0 + (aZmin + aZmax)*0.9;
146           if (value1 - value2 >= tol) {
147             exchange = Standard_True;
148           }
149           else { // compare adresses if shapes are geometrically equal
150             exchange = lhs->impl<TopoDS_Shape>().TShape().get() >
151                        rhs->impl<TopoDS_Shape>().TShape().get();
152           }
153         }
154       }
155     }
156     else { // compare adresses if shapes are geometrically equal
157       exchange = lhs->impl<TopoDS_Shape>().TShape().get() >
158                  rhs->impl<TopoDS_Shape>().TShape().get();
159      }
160   }
161
162   //return val1 < val2;
163   return !exchange;
164 }
165
166 GeomAlgoAPI_NExplode::GeomAlgoAPI_NExplode(const GeomShapePtr theContext,
167                                            const GeomAPI_Shape::ShapeType theShapeType,
168                                            const ShapeOrder theOrder)
169 {
170   std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aMapShape;
171   GeomAPI_ShapeExplorer anExp(theContext, theShapeType);
172   for (; anExp.more(); anExp.next()) {
173     GeomShapePtr aCurrent = anExp.current();
174     if (aMapShape.find(aCurrent) == aMapShape.end()) {
175       mySorted.push_back(aCurrent);
176       aMapShape.insert(aCurrent);
177     }
178   }
179   reorder(theOrder);
180 }
181
182 GeomAlgoAPI_NExplode::GeomAlgoAPI_NExplode(const ListOfShape& theShapes,
183                                            const ShapeOrder theOrder)
184   : mySorted(theShapes.begin(), theShapes.end())
185 {
186   reorder(theOrder);
187 }
188
189 int GeomAlgoAPI_NExplode::index(const GeomShapePtr theSubShape)
190 {
191   std::vector<GeomShapePtr>::iterator anIter = mySorted.begin();
192   for(int anIndex = 1; anIter != mySorted.end(); anIter++, anIndex++) {
193     if ((*anIter)->isSame(theSubShape))
194       return anIndex;
195   }
196   return 0; // not found
197 }
198
199 GeomShapePtr GeomAlgoAPI_NExplode::shape(const int theIndex)
200 {
201   std::vector<GeomShapePtr>::iterator anIter = mySorted.begin();
202   for(int anIndex = 1; anIter != mySorted.end(); anIter++, anIndex++) {
203     if (anIndex == theIndex)
204       return *anIter;
205   }
206   return GeomShapePtr(); // not found
207 }
208
209 void GeomAlgoAPI_NExplode::reorder(const ShapeOrder theNewOrder)
210 {
211   NExplodeTools::CompareShapes shComp(
212       theNewOrder == ORDER_BY_HASH_VALUE ? NExplodeTools::pointToDouble : NExplodeTools::dummy);
213   std::stable_sort(mySorted.begin(), mySorted.end(), shComp);
214 }