1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
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 email : webmaster.salome@opencascade.com
20 #include <Standard_Stream.hxx>
22 #include <GEOMImpl_Fillet1dDriver.hxx>
24 #include <GEOMImpl_Fillet1d.hxx>
25 #include <GEOMImpl_IFillet1d.hxx>
26 #include <GEOMImpl_Types.hxx>
27 #include <GEOMImpl_HealingDriver.hxx>
29 #include <GEOM_Function.hxx>
31 #include <GEOMUtils.hxx>
33 #include <ShapeFix_Wire.hxx>
34 #include <StdFail_NotDone.hxx>
35 #include <Standard_ConstructionError.hxx>
39 #include <TopoDS_Edge.hxx>
40 #include <TopoDS_Wire.hxx>
41 #include <TopoDS_Shape.hxx>
43 #include <TopExp_Explorer.hxx>
44 #include <TopTools_DataMapOfShapeShape.hxx>
45 #include <TopTools_ListOfShape.hxx>
46 #include <TopTools_ListIteratorOfListOfShape.hxx>
47 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
48 #include <TopTools_IndexedMapOfShape.hxx>
50 #include <BRep_Tool.hxx>
51 #include <BRep_Builder.hxx>
52 #include <BRepTools.hxx>
53 #include <BRepBuilderAPI_MakeWire.hxx>
54 #include <BRepCheck_Analyzer.hxx>
60 #include <Precision.hxx>
62 //=======================================================================
65 //=======================================================================
66 const Standard_GUID& GEOMImpl_Fillet1dDriver::GetID()
68 static Standard_GUID aFillet1dDriver("FF60908B-AB2E-4b71-B098-5C256C37D961");
69 return aFillet1dDriver;
72 //=======================================================================
73 //function : GEOMImpl_Fillet1dDriver
75 //=======================================================================
76 GEOMImpl_Fillet1dDriver::GEOMImpl_Fillet1dDriver()
80 //=======================================================================
81 //function : anotherVertex
82 //purpose : local function to get vertex from edge
83 //=======================================================================
84 static TopoDS_Vertex anotherVertex( const TopoDS_Edge& theE,
85 const TopoDS_Vertex& theV )
87 // here is an assumption that edge has different vertices
89 TopExp_Explorer anExp( theE, TopAbs_VERTEX );
90 for ( ; anExp.More(); anExp.Next() )
92 if ( BRepTools::Compare(theV,TopoDS::Vertex(anExp.Current())) /*theV.IsSame(anExp.Current())*/ )
94 aV = TopoDS::Vertex( anExp.Current() );
100 //=======================================================================
101 //function : takePlane
102 //purpose : local function returns plane of given edges
103 //=======================================================================
104 static Standard_Boolean takePlane( const TopoDS_Edge& theE1,
105 const TopoDS_Edge& theE2,
106 const TopoDS_Vertex& theV,
109 TopoDS_Vertex aV12 = anotherVertex( theE1, theV );
110 TopoDS_Vertex aV22 = anotherVertex( theE2, theV );
111 // check can closed wire be created by two initial edges
112 if ( aV12.IsNull() || aV22.IsNull() || aV12.IsSame( aV22 ) )
115 // create plane by 3 points
116 gp_XYZ aXYZ = BRep_Tool::Pnt( theV ).XYZ();
117 gp_XYZ aXYZ1 = BRep_Tool::Pnt( aV12 ).XYZ();
118 gp_XYZ aXYZ2 = BRep_Tool::Pnt( aV22 ).XYZ();
120 gp_Dir aDir1( aXYZ - aXYZ1 );
121 gp_Dir aDir2( aXYZ2 - aXYZ );
122 Standard_Real anAngle = aDir1.Angle(aDir2);
123 if ( fabs(anAngle) <= gp::Resolution() ||
124 fabs(anAngle - M_PI) <= gp::Resolution() )
126 thePlane = gp_Pln( gp_Pnt(aXYZ), aDir1^ aDir2);
128 catch (Standard_Failure) {
134 //=======================================================================
135 //function : addEdgeRelation
136 //purpose : local function to remember relation between initial and modified edge
137 //=======================================================================
138 static void addEdgeRelation(TopTools_DataMapOfShapeShape& theMap,
139 const TopoDS_Edge& theInitE,
140 const TopoDS_Edge& theResE)
142 if ( theMap.IsBound( theInitE ) )
143 theMap.ChangeFind( theInitE ) = theResE;
145 theMap.Bind( theInitE, theResE );
148 //=======================================================================
151 //=======================================================================
152 Standard_Integer GEOMImpl_Fillet1dDriver::Execute(LOGBOOK& log) const
154 if (Label().IsNull()) return 0;
155 Handle(GEOM_Function) aFunction = GEOM_Function::GetFunction(Label());
157 GEOMImpl_IFillet1d aCI (aFunction);
159 Handle(GEOM_Function) aRefShape = aCI.GetShape();
160 TopoDS_Shape aShape = aRefShape->GetValue();
163 if (aShape.ShapeType() != TopAbs_WIRE)
164 Standard_ConstructionError::Raise("Wrong arguments: polyline as wire must be given");
166 TopoDS_Wire aWire = TopoDS::Wire(aShape);
168 bool doIgnoreSecantPoints = aCI.GetFlag();
170 double rad = aCI.GetR();
171 if (rad < Precision::Confusion())
174 // collect vertices for make fillet
175 TopTools_ListOfShape aVertexList;
176 TopTools_IndexedMapOfShape anIndices;
177 TopExp::MapShapes(aWire, anIndices);
178 int aLen = aCI.GetLength();
180 for (int ii = 1; ii <= aLen; ii++) {
181 int ind = aCI.GetVertex(ii);
182 if (1 <= ind && ind <= anIndices.Extent()) {
183 TopoDS_Shape aShapeVertex = anIndices.FindKey(ind);
184 if (aShapeVertex.ShapeType() == TopAbs_VERTEX)
185 aVertexList.Append(aShapeVertex);
189 else { // get all vertices from wire
190 TopTools_MapOfShape mapShape;
191 TopExp_Explorer anExp (aWire, TopAbs_VERTEX);
192 for (; anExp.More(); anExp.Next()) {
193 if (mapShape.Add(anExp.Current()))
194 aVertexList.Append(anExp.Current());
197 if (aVertexList.IsEmpty())
198 Standard_ConstructionError::Raise("Invalid input: no vertices to make fillet");
200 // at first we try to make fillet on the initial wire (without edges fusing)
201 bool isFinalPass = !doIgnoreSecantPoints;
203 bool isAllStepsOk = MakeFillet(aWire, aVertexList, rad, isFinalPass, aResult);
205 // try to fuse collinear edges to allow bigger radius
206 if (!isFinalPass && !isAllStepsOk) {
208 TopoDS_Shape aShapeNew;
209 Handle(TColStd_HSequenceOfTransient) aVerts;
210 GEOMImpl_HealingDriver::FuseCollinearEdges(aWire, aVerts, aShapeNew);
211 TopoDS_Wire aWireNew = TopoDS::Wire(aShapeNew);
213 // 2. Rebuild the list of vertices (by coincidence)
214 Standard_Real tol, tolMax = Precision::Confusion();
215 for (TopExp_Explorer ExV (aWireNew, TopAbs_VERTEX); ExV.More(); ExV.Next()) {
216 TopoDS_Vertex Vertex = TopoDS::Vertex(ExV.Current());
217 tol = BRep_Tool::Tolerance(Vertex);
222 TopTools_ListOfShape aVertexListNew;
223 TopTools_IndexedMapOfShape anIndicesNew;
224 TopExp::MapShapes(aWireNew, anIndicesNew);
225 TopTools_ListIteratorOfListOfShape anIt (aVertexList);
226 for (; anIt.More(); anIt.Next()) {
227 TopoDS_Vertex aV = TopoDS::Vertex(anIt.Value());
228 if (anIndicesNew.Contains(aV))
229 aVertexListNew.Append(aV);
231 // try to find by coords in the new wire
232 gp_Pnt aP = BRep_Tool::Pnt(aV);
234 bool isFound = false;
235 TopTools_MapOfShape mapShape;
236 TopExp_Explorer exp (aWireNew, TopAbs_VERTEX);
237 for (; exp.More() && !isFound; exp.Next()) {
238 if (mapShape.Add(exp.Current())) {
239 TopoDS_Vertex aVi = TopoDS::Vertex(exp.Current());
240 gp_Pnt aPi = BRep_Tool::Pnt(aVi);
241 if (aPi.Distance(aP) < tolMax) {
242 aVertexListNew.Append(aVi);
250 // 3. Repeat the fillet algorithm
252 MakeFillet(aWireNew, aVertexListNew, rad, isFinalPass, aResult);
255 if (!GEOMUtils::CheckShape(aResult, true) &&
256 !GEOMUtils::FixShapeTolerance(aResult)) {
257 Standard_ConstructionError::Raise("Non valid shape result");
260 aFunction->SetValue(aResult);
261 #if OCC_VERSION_MAJOR < 7
262 log.SetTouched(Label());
264 log->SetTouched(Label());
270 //=======================================================================
271 //function : MakeFillet
273 //=======================================================================
274 bool GEOMImpl_Fillet1dDriver::MakeFillet(const TopoDS_Wire& theWire,
275 const TopTools_ListOfShape& theVertexList,
276 const Standard_Real theRadius,
278 TopoDS_Wire& theResult) const
280 // this variable is needed to break execution
281 // in case of fillet failure and try to fuse edges
282 bool isAllStepsOk = true;
284 //INFO: this algorithm implemented in assumption that user can select both
285 // vertices of some edges to make fillet. In this case we should remember
286 // already modified initial edges to take care in next fillet step
287 TopTools_DataMapOfShapeShape anEdgeToEdgeMap;
289 //iterates on vertices, and make fillet on each couple of edges
290 //collect result fillet edges in list
291 TopTools_ListOfShape aListOfNewEdge;
292 // remember relation between initial and modified map
293 TopTools_IndexedDataMapOfShapeListOfShape aMapVToEdges;
294 TopExp::MapShapesAndAncestors( theWire, TopAbs_VERTEX, TopAbs_EDGE, aMapVToEdges );
295 TopTools_ListIteratorOfListOfShape anIt( theVertexList );
296 for ( ; anIt.More(); anIt.Next() ) {
297 TopoDS_Vertex aV = TopoDS::Vertex( anIt.Value() );
298 if ( aV.IsNull() || !aMapVToEdges.Contains( aV ) )
300 const TopTools_ListOfShape& aVertexEdges = aMapVToEdges.FindFromKey( aV );
301 if ( aVertexEdges.Extent() != 2 )
302 continue; // no input data to make fillet
303 TopoDS_Edge anEdge1 = TopoDS::Edge( aVertexEdges.First() );
304 TopoDS_Edge anEdge2 = TopoDS::Edge( aVertexEdges.Last() );
305 // check if initial edges already modified in previous fillet operation
306 if ( anEdgeToEdgeMap.IsBound( anEdge1 ) ) anEdge1 = TopoDS::Edge(anEdgeToEdgeMap.Find( anEdge1 ));
307 if ( anEdgeToEdgeMap.IsBound( anEdge2 ) ) anEdge2 = TopoDS::Edge(anEdgeToEdgeMap.Find( anEdge2 ));
308 if ( anEdge1.IsNull() || anEdge2.IsNull() || anEdge1.IsSame( anEdge2 ) )
309 continue; //no input data to make fillet
311 // create plane on 2 edges
313 if ( !takePlane(anEdge1, anEdge2, aV, aPlane) )
314 continue; // seems edges does not belong to same plane or parallel (fillet can not be build)
316 GEOMImpl_Fillet1d aFilletAlgo (anEdge1, anEdge2, aPlane);
317 if (!aFilletAlgo.Perform(theRadius)) {
319 continue; // can not create fillet with given radius
321 isAllStepsOk = false;
322 break; // can not create fillet with given radius
326 // take fillet result in given vertex
327 TopoDS_Edge aModifE1, aModifE2;
328 TopoDS_Edge aNewE = aFilletAlgo.Result(BRep_Tool::Pnt(aV), aModifE1, aModifE2);
329 if (aNewE.IsNull()) {
331 continue; // no result found
333 isAllStepsOk = false;
334 break; // no result found
338 // add new created edges and take modified edges
339 aListOfNewEdge.Append(aNewE);
341 // check if wire edges modified,
342 // if yes, then map to original edges (from vertex-edges list), because edges can be modified before
343 if (aModifE1.IsNull() || !anEdge1.IsSame( aModifE1 ))
344 addEdgeRelation( anEdgeToEdgeMap, TopoDS::Edge(aVertexEdges.First()), aModifE1 );
345 if (aModifE2.IsNull() || !anEdge2.IsSame( aModifE2 ))
346 addEdgeRelation( anEdgeToEdgeMap, TopoDS::Edge(aVertexEdges.Last()), aModifE2 );
349 if (anEdgeToEdgeMap.IsEmpty() && aListOfNewEdge.IsEmpty()) {
351 StdFail_NotDone::Raise("1D Fillet can't be computed on the given shape with the given radius");
353 isAllStepsOk = false;
359 // create new wire instead of original
361 Standard_Real aVertMaxTol = -RealLast();
362 for (TopExp_Explorer anExp (theWire, TopAbs_EDGE); anExp.More(); anExp.Next()) {
363 TopoDS_Shape anEdge = anExp.Current();
364 if (!anEdgeToEdgeMap.IsBound(anEdge))
365 aListOfNewEdge.Append(anEdge);
366 else if (!anEdgeToEdgeMap.Find(anEdge).IsNull())
367 aListOfNewEdge.Append(anEdgeToEdgeMap.Find(anEdge));
369 // calculate maximum vertex tolerance of the initial wire
370 // to be used for resulting wire fixing (some gaps are possible)
371 for (TopExp_Explorer anExV (anEdge, TopAbs_VERTEX); anExV.More(); anExV.Next()) {
372 TopoDS_Vertex aVert = TopoDS::Vertex(anExV.Current());
373 aTol = BRep_Tool::Tolerance(aVert);
374 if (aTol > aVertMaxTol)
379 // Fix for Mantis issue 0023411: BEGIN
381 TopoDS_Wire aResWire;
382 B.MakeWire(aResWire);
383 TopTools_ListIteratorOfListOfShape anItNewEdge (aListOfNewEdge);
384 for (; anItNewEdge.More(); anItNewEdge.Next()) {
385 B.Add(aResWire, TopoDS::Edge(anItNewEdge.Value()));
387 Handle(ShapeFix_Wire) aFW = new ShapeFix_Wire;
390 aFW->ClosedWireMode() = theWire.Closed();
391 // Fix for Mantis issue 0023411
392 // We forced to do this because of fillet 1d algorithm feature
393 // (see function DivideEdge in file GEOMImpl_Fillet1d.cxx):
394 // a distance (gap) from new arc end to a vertex of original wire
395 // can reach (aVertexTolerance + Precision::Confusion()),
396 // so a distance between two adjacent arcs will be covered by value below
397 aFW->FixConnected(aVertMaxTol*2.0 + Precision::Confusion()*3.0);
398 theResult = aFW->WireAPIMake();
399 GEOMUtils::FixShapeTolerance(theResult, TopAbs_VERTEX, Precision::Confusion());
401 // In OCCT 7.0.0 and earlier this gap was successfully covered by
402 // implementation of BRepBuilderAPI_MakeWire::Add(TopTools_ListOfShape)
403 // (in the case described in Mantis issue 0023411)
405 //GEOMUtils::SortShapes(aListOfNewEdge);
406 //BRepBuilderAPI_MakeWire aWireTool;
407 //aWireTool.Add(aListOfNewEdge);
409 //if (!aWireTool.IsDone())
411 //theResult = aWireTool.Wire();
412 // Fix for Mantis issue 0023411: END
416 //================================================================================
418 * \brief Returns a name of creation operation and names and values of creation parameters
420 //================================================================================
422 bool GEOMImpl_Fillet1dDriver::
423 GetCreationInformation(std::string& theOperationName,
424 std::vector<GEOM_Param>& theParams)
426 if (Label().IsNull()) return 0;
427 Handle(GEOM_Function) function = GEOM_Function::GetFunction(Label());
429 GEOMImpl_IFillet1d aCI( function );
430 Standard_Integer aType = function->GetType();
432 theOperationName = "FILLET_1D";
436 AddParam( theParams, "Wire", aCI.GetShape() );
437 AddParam( theParams, "Vertexes");
438 if ( aCI.GetLength() > 1 )
439 theParams[1] << aCI.GetLength() << " vertexes: ";
440 for (int i = 1; i <= aCI.GetLength(); ++i )
441 theParams[1] << aCI.GetVertex( i ) << " ";
442 AddParam( theParams, "Radius", aCI.GetR() );
443 AddParam( theParams, "Fuse collinear edges", aCI.GetFlag() );
452 OCCT_IMPLEMENT_STANDARD_RTTIEXT (GEOMImpl_Fillet1dDriver,GEOM_BaseDriver);