1 // Copyright (C) 2014-2017 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
21 #include <GeomAlgoAPI_SketchBuilder.h>
22 #include <GeomAPI_PlanarEdges.h>
24 #include <BOPAlgo_Builder.hxx>
25 #include <BRep_Builder.hxx>
26 #include <BRepTopAdaptor_FClass2d.hxx>
27 #include <Geom_Plane.hxx>
28 #include <Geom_TrimmedCurve.hxx>
29 #include <Precision.hxx>
31 #include <TopExp_Explorer.hxx>
33 #include <TopoDS_Edge.hxx>
34 #include <TopTools_ListIteratorOfListOfShape.hxx>
35 #include <GProp_GProps.hxx>
36 #include <BRepGProp.hxx>
42 static TopoDS_Vertex findStartVertex(const TopoDS_Shape& theShape)
44 static const double aTol = Precision::PConfusion();
46 TopExp_Explorer anExp(theShape, TopAbs_VERTEX);
47 TopoDS_Vertex aStart = TopoDS::Vertex(anExp.Current());
48 gp_Pnt aStartPnt(BRep_Tool::Pnt(aStart));
49 TopoDS_Vertex aCurrent;
52 for (anExp.Next(); anExp.More(); anExp.Next()) {
53 aCurrent = TopoDS::Vertex(anExp.Current());
54 aCurrentPnt = BRep_Tool::Pnt(aCurrent);
55 if ((aCurrentPnt.X() > aStartPnt.X() + aTol) ||
56 (aCurrentPnt.X() > aStartPnt.X() - aTol && aCurrentPnt.Y() > aStartPnt.Y() + aTol) ||
57 (aCurrentPnt.X() > aStartPnt.X() - aTol && aCurrentPnt.Y() > aStartPnt.Y() - aTol &&
58 aCurrentPnt.Z() > aStartPnt.Z() + aTol)) {
60 aStartPnt = aCurrentPnt;
66 static TopoDS_Vertex findStartVertex(const TopoDS_Shape& theShape,
67 const std::list<std::shared_ptr<GeomAPI_Shape> >& theInitialShapes)
69 // Try to find edge lying on the one of original edges.
70 // First found edge will be taken as a start edge for the result wire
71 std::list<std::shared_ptr<GeomAPI_Shape> >::const_iterator aFeatIt = theInitialShapes.begin();
72 for (; aFeatIt != theInitialShapes.end(); aFeatIt++) {
73 std::shared_ptr<GeomAPI_Shape> aShape(*aFeatIt);
74 const TopoDS_Edge& anEdge = aShape->impl<TopoDS_Edge>();
75 if (anEdge.ShapeType() != TopAbs_EDGE)
79 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
80 if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
81 aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
83 TopExp_Explorer anExp(theShape, TopAbs_EDGE);
84 for (; anExp.More(); anExp.Next()) {
85 const TopoDS_Edge& aShapeEdge = TopoDS::Edge(anExp.Current());
87 Handle(Geom_Curve) aShapeCurve = BRep_Tool::Curve(aShapeEdge, aF, aL);
88 if (aShapeCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
89 aShapeCurve = Handle(Geom_TrimmedCurve)::DownCast(aShapeCurve)->BasisCurve();
91 if (aCurve != aShapeCurve)
94 // the edge is found, search vertex
95 TopoDS_Vertex aV1, aV2;
96 TopExp::Vertices(aShapeEdge, aV1, aV2);
97 return fabs(aF - aFirst) <= fabs(aL - aFirst) ? aV1 : aV2;
101 // start vertex is not found, use algorithm to search vertex with the greatest coordinates
102 return findStartVertex(theShape);
105 // returns true if the first shape must be located earlier than the second
106 bool isFirst(const TopoDS_Shape& theFirst, const TopoDS_Shape& theSecond,
107 NCollection_DataMap<TopoDS_Shape, NCollection_Array1<int> >& theAreaToIndex,
108 const NCollection_DataMap<Handle(Geom_Curve), int>& theCurveToIndex)
110 // fill theAreaToIndex for both shapes if needed
111 for(int aShapeNum = 1; aShapeNum <= 2; aShapeNum++) {
112 TopoDS_Shape aShape = aShapeNum == 1 ? theFirst : theSecond;
113 if (!theAreaToIndex.IsBound(aShape)) { // fill the list of curve indices
114 NCollection_List<int> aNewList;
115 TopExp_Explorer anEdgesExp(aShape, TopAbs_EDGE);
116 for(; anEdgesExp.More(); anEdgesExp.Next()) {
117 double aFirst, aLast;
118 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(
119 TopoDS::Edge(anEdgesExp.Current()), aFirst, aLast);
120 if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
121 aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
122 if (theCurveToIndex.IsBound(aCurve)) {
123 aNewList.Append(theCurveToIndex.Find(aCurve));
126 NCollection_Array1<int> aNewArray(1, aNewList.Extent());
127 NCollection_List<int>::Iterator aListIter(aNewList);
128 for(int anIndex = 1; aListIter.More(); aListIter.Next(), anIndex++) {
129 aNewArray.SetValue(anIndex, aListIter.Value());
131 std::sort(aNewArray.begin(), aNewArray.end());
132 theAreaToIndex.Bind(aShape, aNewArray);
135 // compare lists of indices one by one to find chich list indices are lower
136 NCollection_Array1<int>::Iterator aFirstList(theAreaToIndex.ChangeFind(theFirst));
137 NCollection_Array1<int>::Iterator aSecondList(theAreaToIndex.ChangeFind(theSecond));
138 for(; aFirstList.More() && aSecondList.More(); aFirstList.Next(), aSecondList.Next()) {
139 if (aFirstList.Value() < aSecondList.Value()) return true;
140 if (aFirstList.Value() > aSecondList.Value()) return false;
142 // if faces are identical by curves names (circle splitted by line in seam-point), use parameters
143 if (!aFirstList.More() && !aSecondList.More()) {
144 GProp_GProps aGProps;
145 BRepGProp::SurfaceProperties(theFirst, aGProps);
146 gp_Pnt aCentre1 = aGProps.CentreOfMass();
147 BRepGProp::SurfaceProperties(theSecond, aGProps);
148 gp_Pnt aCentre2 = aGProps.CentreOfMass();
149 return aCentre1.X() + aCentre1.Y() + aCentre1.Z() < aCentre2.X() + aCentre2.Y() + aCentre2.Z();
151 // if in first list there is no elements left, it is the first
152 return !aFirstList.More();
155 // sorts faces (in theAreas list) to make persistent order: by initial shapes edges
156 static void sortFaces(TopTools_ListOfShape& theAreas,
157 const std::list<std::shared_ptr<GeomAPI_Shape> >& theInitialShapes)
159 // collect indices of all edges to operate them quickly
160 NCollection_DataMap<Handle(Geom_Curve), int> aCurveToIndex; // curve -> index in initial shapes
161 std::list<std::shared_ptr<GeomAPI_Shape> >::const_iterator aFeatIt = theInitialShapes.begin();
162 for (int anIndex = 0; aFeatIt != theInitialShapes.end(); aFeatIt++) {
163 std::shared_ptr<GeomAPI_Shape> aShape(*aFeatIt);
164 const TopoDS_Edge& anEdge = aShape->impl<TopoDS_Edge>();
165 if (anEdge.ShapeType() != TopAbs_EDGE)
168 double aFirst, aLast;
169 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
170 if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
171 aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
172 if (!aCurveToIndex.IsBound(aCurve))
173 aCurveToIndex.Bind(aCurve, anIndex++);
175 // map from area to the most first indices of curves (to compare) in it
176 NCollection_DataMap<TopoDS_Shape, NCollection_Array1<int> > anAreaToIndex;
178 TopTools_ListOfShape::Iterator anArea1(theAreas);
179 for(; anArea1.More(); anArea1.Next()) {
180 TopTools_ListOfShape::Iterator anArea2 = anArea1;
181 for(anArea2.Next(); anArea2.More(); anArea2.Next()) {
182 if (!isFirst(anArea1.Value(), anArea2.Value(), anAreaToIndex, aCurveToIndex)) { // exchange
183 TopoDS_Shape aTmp = anArea1.Value();
184 anArea1.ChangeValue() = anArea2.Value();
185 anArea2.ChangeValue() = aTmp;
191 void GeomAlgoAPI_SketchBuilder::createFaces(
192 const std::shared_ptr<GeomAPI_Pnt>& theOrigin,
193 const std::shared_ptr<GeomAPI_Dir>& theDirX,
194 const std::shared_ptr<GeomAPI_Dir>& theNorm,
195 const std::list<std::shared_ptr<GeomAPI_Shape> >& theFeatures,
196 std::list<std::shared_ptr<GeomAPI_Shape> >& theResultFaces)
198 if (theFeatures.empty())
201 BRep_Builder aBuilder;
202 // Planar face, where the sketch was built
203 Handle(Geom_Surface) aPlane(new Geom_Plane(theOrigin->impl<gp_Pnt>(), theNorm->impl<gp_Dir>()));
204 TopoDS_Face aPlnFace;
205 aBuilder.MakeFace(aPlnFace, aPlane, Precision::Confusion());
207 // Use General Fuse algorithm to prepare all subfaces, bounded by given list of edges
209 aBB.AddArgument(aPlnFace);
211 BOPCol_ListOfShape anEdges;
212 BOPCol_ListIteratorOfListOfShape aShapeIt;
213 std::list<std::shared_ptr<GeomAPI_Shape> >::const_iterator aFeatIt = theFeatures.begin();
214 for (; aFeatIt != theFeatures.end(); aFeatIt++) {
215 std::shared_ptr<GeomAPI_Shape> aShape(*aFeatIt);
216 const TopoDS_Edge& anEdge = aShape->impl<TopoDS_Edge>();
217 if (anEdge.ShapeType() == TopAbs_EDGE)
218 aBB.AddArgument(anEdge);
221 if (aBB.ErrorStatus())
225 TopTools_ListOfShape anAreas = aBB.Modified(aPlnFace);
226 sortFaces(anAreas, theFeatures); // sort faces by the edges in them
227 TopTools_ListIteratorOfListOfShape anIt(anAreas);
228 for (; anIt.More(); anIt.Next()) {
229 TopoDS_Face aFace = TopoDS::Face(anIt.Value());
230 // avoid infinite faces
231 BRepTopAdaptor_FClass2d aFClass(aFace, Precision::Confusion());
232 if (aFClass.PerformInfinitePoint() == TopAbs_IN)
236 TopoDS_Face aNewFace;
237 aBuilder.MakeFace(aNewFace, aPlane, Precision::Confusion());
240 TopExp_Explorer aWireExp(aFace, TopAbs_WIRE);
241 for (; aWireExp.More(); aWireExp.Next()) {
242 TopoDS_Wire aWire = TopoDS::Wire(aWireExp.Current());
244 // to make faces equal on different platforms, we will find
245 // a vertex lying on an edge with the lowest index in the list of initial edges
246 TopoDS_Vertex aStartVertex = findStartVertex(aWire, theFeatures);
248 TopoDS_Wire aNewWire;
249 aBuilder.MakeWire(aNewWire);
250 std::list<TopoDS_Edge> aSkippedEdges;
251 bool aStartFound = false;
253 // remove internal edges from faces and make wire start from found vertex
254 TopExp_Explorer anExp(aWire, TopAbs_EDGE);
255 for (; anExp.More(); anExp.Next()) {
256 if (anExp.Current().Orientation() == TopAbs_INTERNAL)
259 const TopoDS_Edge& anEdge = TopoDS::Edge(anExp.Current());
260 TopoDS_Vertex aV1, aV2;
261 TopExp::Vertices(anEdge, aV1, aV2, Standard_True);
262 if (aV1.IsSame(aStartVertex) == Standard_True)
265 aSkippedEdges.push_back(anEdge);
268 aBuilder.Add(aNewWire, anExp.Current());
270 // add skipped edges to the end of wire
271 std::list<TopoDS_Edge>::const_iterator aSkIt = aSkippedEdges.begin();
272 for (; aSkIt != aSkippedEdges.end(); ++aSkIt)
273 aBuilder.Add(aNewWire, *aSkIt);
275 // check the wire is empty
276 anExp.Init(aNewWire, TopAbs_EDGE);
278 aBuilder.Add(aNewFace, aNewWire);
283 std::shared_ptr<GeomAPI_Shape> aResFace(new GeomAPI_Shape);
284 aResFace->setImpl(new TopoDS_Face(aFace));
285 theResultFaces.push_back(aResFace);
289 void GeomAlgoAPI_SketchBuilder::createFaces(const std::shared_ptr<GeomAPI_Pnt>& theOrigin,
290 const std::shared_ptr<GeomAPI_Dir>& theDirX,
291 const std::shared_ptr<GeomAPI_Dir>& theNorm,
292 const std::shared_ptr<GeomAPI_Shape>& theWire,
293 std::list<std::shared_ptr<GeomAPI_Shape> >& theResultFaces)
295 std::shared_ptr<GeomAPI_PlanarEdges> aWire =
296 std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theWire);
298 // Filter wires, return only faces.
299 createFaces(theOrigin, theDirX, theNorm, aWire->getEdges(), theResultFaces);
300 } else { // it may be only one circle
301 std::shared_ptr<GeomAPI_Edge> anEdge = std::dynamic_pointer_cast<GeomAPI_Edge>(theWire);
303 std::list<std::shared_ptr<GeomAPI_Shape> > aList;
304 aList.push_back(anEdge);
305 createFaces(theOrigin, theDirX, theNorm, aList, theResultFaces);