1 // Created on: 2016-10-14
2 // Created by: Alexander MALYSHEV
3 // Copyright (c) 1995-1999 Matra Datavision
4 // Copyright (c) 1999-2016 OPEN CASCADE SAS
5 //
6 // This file is part of Open CASCADE Technology software library.
7 //
8 // This library is free software; you can redistribute it and/or modify it under
9 // the terms of the GNU Lesser General Public License version 2.1 as published
10 // by the Free Software Foundation, with special exception defined in the file
11 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
12 // distribution for complete text of the license and disclaimer of any warranty.
13 //
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
16 
17 // Include self.
18 #include <BRepOffset_SimpleOffset.hxx>
19 
20 #include <Adaptor3d_CurveOnSurface.hxx>
21 #include <BRepBuilderAPI_MakeEdge.hxx>
22 #include <BRepLib.hxx>
23 #include <BRepLib_ValidateEdge.hxx>
24 #include <BRep_Tool.hxx>
25 #include <BRepOffset.hxx>
26 #include <Geom_OffsetSurface.hxx>
27 #include <GeomAdaptor_Curve.hxx>
28 #include <Geom2dAdaptor_Curve.hxx>
29 #include <GeomAdaptor_Surface.hxx>
30 #include <NCollection_Vector.hxx>
31 #include <ShapeAnalysis_Edge.hxx>
32 #include <TopExp.hxx>
33 #include <TopExp_Explorer.hxx>
34 #include <TopoDS.hxx>
35 #include <TopoDS_Face.hxx>
36 #include <TopoDS_Edge.hxx>
37 #include <TopoDS_Vertex.hxx>
38 
39 //=============================================================================
40 //function : BRepOffset_SimpleOffset
41 //purpose  : Constructor
42 //=============================================================================
BRepOffset_SimpleOffset(const TopoDS_Shape & theInputShape,const Standard_Real theOffsetValue,const Standard_Real theTolerance)43 BRepOffset_SimpleOffset::BRepOffset_SimpleOffset(const TopoDS_Shape& theInputShape,
44                                                  const Standard_Real theOffsetValue,
45                                                  const Standard_Real theTolerance)
46 : myOffsetValue(theOffsetValue),
47   myTolerance(theTolerance)
48 {
49   FillOffsetData(theInputShape);
50 }
51 
52 //=============================================================================
53 //function : NewSurface
54 //purpose  :
55 //=============================================================================
NewSurface(const TopoDS_Face & F,Handle (Geom_Surface)& S,TopLoc_Location & L,Standard_Real & Tol,Standard_Boolean & RevWires,Standard_Boolean & RevFace)56 Standard_Boolean BRepOffset_SimpleOffset::NewSurface(const TopoDS_Face& F,
57                                                      Handle(Geom_Surface)& S,
58                                                      TopLoc_Location& L,
59                                                      Standard_Real& Tol,
60                                                      Standard_Boolean& RevWires,
61                                                      Standard_Boolean& RevFace)
62 {
63   if (!myFaceInfo.IsBound(F))
64     return Standard_False;
65 
66   const NewFaceData& aNFD = myFaceInfo.Find(F);
67 
68   S = aNFD.myOffsetS;
69   L = aNFD.myL;
70   Tol = aNFD.myTol;
71   RevWires = aNFD.myRevWires;
72   RevFace = aNFD.myRevFace;
73 
74   return Standard_True;
75 }
76 
77 //=============================================================================
78 //function : NewCurve
79 //purpose  :
80 //=============================================================================
NewCurve(const TopoDS_Edge & E,Handle (Geom_Curve)& C,TopLoc_Location & L,Standard_Real & Tol)81 Standard_Boolean BRepOffset_SimpleOffset::NewCurve(const TopoDS_Edge& E,
82                                                    Handle(Geom_Curve)& C,
83                                                    TopLoc_Location& L,
84                                                    Standard_Real& Tol)
85 {
86   if (!myEdgeInfo.IsBound(E))
87     return Standard_False;
88 
89   const NewEdgeData& aNED = myEdgeInfo.Find(E);
90 
91   C = aNED.myOffsetC;
92   L = aNED.myL;
93   Tol = aNED.myTol;
94 
95   return Standard_True;
96 }
97 
98 //=============================================================================
99 //function : NewPoint
100 //purpose  :
101 //=============================================================================
NewPoint(const TopoDS_Vertex & V,gp_Pnt & P,Standard_Real & Tol)102 Standard_Boolean BRepOffset_SimpleOffset::NewPoint (const TopoDS_Vertex& V,
103                                                     gp_Pnt& P,
104                                                     Standard_Real& Tol)
105 {
106   if (!myVertexInfo.IsBound(V))
107     return Standard_False;
108 
109   const NewVertexData& aNVD = myVertexInfo.Find(V);
110 
111   P = aNVD.myP;
112   Tol = aNVD.myTol;
113 
114   return Standard_True;
115 }
116 
117 //=============================================================================
118 //function : NewCurve2d
119 //purpose  :
120 //=============================================================================
NewCurve2d(const TopoDS_Edge & E,const TopoDS_Face & F,const TopoDS_Edge &,const TopoDS_Face &,Handle (Geom2d_Curve)& C,Standard_Real & Tol)121 Standard_Boolean BRepOffset_SimpleOffset::NewCurve2d (const TopoDS_Edge& E,
122                                                       const TopoDS_Face& F,
123                                                       const TopoDS_Edge& /*NewE*/,
124                                                       const TopoDS_Face& /*NewF*/,
125                                                       Handle(Geom2d_Curve)& C,
126                                                       Standard_Real& Tol)
127 {
128   // Use original pcurve.
129   Standard_Real aF, aL;
130   C = BRep_Tool::CurveOnSurface(E, F, aF, aL);
131   Tol = BRep_Tool::Tolerance(E);
132 
133   if (myEdgeInfo.IsBound(E))
134     Tol = myEdgeInfo.Find(E).myTol;
135 
136   return Standard_True;
137 }
138 
139 //=============================================================================
140 //function : NewParameter
141 //purpose  :
142 //=============================================================================
NewParameter(const TopoDS_Vertex & V,const TopoDS_Edge & E,Standard_Real & P,Standard_Real & Tol)143 Standard_Boolean BRepOffset_SimpleOffset::NewParameter (const TopoDS_Vertex& V,
144                                                         const TopoDS_Edge& E,
145                                                         Standard_Real& P,
146                                                         Standard_Real& Tol)
147 {
148   // Use original parameter.
149   P = BRep_Tool::Parameter(V, E);
150   Tol = BRep_Tool::Tolerance(V);
151 
152   if (myVertexInfo.IsBound(V))
153     Tol = myVertexInfo.Find(V).myTol;
154 
155   return Standard_True;
156 }
157 
158 //=============================================================================
159 //function : NewParameter
160 //purpose  :
161 //=============================================================================
Continuity(const TopoDS_Edge & E,const TopoDS_Face & F1,const TopoDS_Face & F2,const TopoDS_Edge &,const TopoDS_Face &,const TopoDS_Face &)162 GeomAbs_Shape BRepOffset_SimpleOffset::Continuity (const TopoDS_Edge& E,
163                                                    const TopoDS_Face& F1,
164                                                    const TopoDS_Face& F2,
165                                                    const TopoDS_Edge& /*NewE*/,
166                                                    const TopoDS_Face& /*NewF1*/,
167                                                    const TopoDS_Face& /*NewF2*/)
168 {
169   // Compute result using original continuity.
170   return BRep_Tool::Continuity(E, F1, F2);
171 }
172 
173 //=============================================================================
174 //function : FillOffsetData
175 //purpose  :
176 //=============================================================================
FillOffsetData(const TopoDS_Shape & theShape)177 void BRepOffset_SimpleOffset::FillOffsetData(const TopoDS_Shape& theShape)
178 {
179   // Clears old data.
180   myFaceInfo.Clear();
181   myEdgeInfo.Clear();
182   myVertexInfo.Clear();
183 
184   // Faces loop. Compute offset surface for each face.
185   TopExp_Explorer anExpSF(theShape, TopAbs_FACE);
186   for(; anExpSF.More(); anExpSF.Next())
187   {
188     const TopoDS_Face& aCurrFace = TopoDS::Face(anExpSF.Current());
189     FillFaceData(aCurrFace);
190   }
191 
192   // Iterate over edges to compute 3d curve.
193   TopTools_IndexedDataMapOfShapeListOfShape aEdgeFaceMap;
194   TopExp::MapShapesAndAncestors(theShape, TopAbs_EDGE, TopAbs_FACE, aEdgeFaceMap);
195   for (Standard_Integer anIdx = 1; anIdx <= aEdgeFaceMap.Size(); ++anIdx)
196   {
197     const TopoDS_Edge& aCurrEdge = TopoDS::Edge(aEdgeFaceMap.FindKey(anIdx));
198     FillEdgeData(aCurrEdge, aEdgeFaceMap, anIdx);
199   }
200 
201   // Iterate over vertices to compute new vertex.
202   TopTools_IndexedDataMapOfShapeListOfShape aVertexEdgeMap;
203   TopExp::MapShapesAndAncestors(theShape, TopAbs_VERTEX, TopAbs_EDGE, aVertexEdgeMap);
204   for (Standard_Integer anIdx = 1; anIdx <= aVertexEdgeMap.Size(); ++anIdx)
205   {
206     const TopoDS_Vertex & aCurrVertex = TopoDS::Vertex(aVertexEdgeMap.FindKey(anIdx));
207     FillVertexData(aCurrVertex, aVertexEdgeMap, anIdx);
208   }
209 }
210 
211 //=============================================================================
212 //function : FillFaceData
213 //purpose  :
214 //=============================================================================
FillFaceData(const TopoDS_Face & theFace)215 void BRepOffset_SimpleOffset::FillFaceData(const TopoDS_Face& theFace)
216 {
217   NewFaceData aNFD;
218   aNFD.myRevWires = Standard_False;
219   aNFD.myRevFace = Standard_False;
220   aNFD.myTol = BRep_Tool::Tolerance(theFace);
221 
222   // Create offset surface.
223 
224   // Any existing transformation is applied to the surface.
225   // New face will have null transformation.
226   Handle(Geom_Surface) aS = BRep_Tool::Surface(theFace);
227   aS = BRepOffset::CollapseSingularities (aS, theFace, myTolerance);
228 
229   // Take into account face orientation.
230   Standard_Real aMult = 1.0;
231   if (theFace.Orientation() == TopAbs_REVERSED)
232     aMult = -1.0;
233 
234   BRepOffset_Status aStatus; // set by BRepOffset::Surface(), could be used to check result...
235   aNFD.myOffsetS = BRepOffset::Surface (aS, aMult * myOffsetValue, aStatus, Standard_True);
236   aNFD.myL = TopLoc_Location(); // Null transformation.
237 
238   // Save offset surface in map.
239   myFaceInfo.Bind(theFace, aNFD);
240 }
241 
242 //=============================================================================
243 //function : FillEdgeData
244 //purpose  :
245 //=============================================================================
FillEdgeData(const TopoDS_Edge & theEdge,const TopTools_IndexedDataMapOfShapeListOfShape & theEdgeFaceMap,const Standard_Integer theIdx)246 void BRepOffset_SimpleOffset::FillEdgeData(const TopoDS_Edge& theEdge,
247                                            const TopTools_IndexedDataMapOfShapeListOfShape& theEdgeFaceMap,
248                                            const Standard_Integer theIdx)
249 {
250   const TopTools_ListOfShape& aFacesList = theEdgeFaceMap(theIdx);
251 
252   if (aFacesList.Size() == 0)
253     return; // Free edges are skipped.
254 
255   // Get offset surface.
256   const TopoDS_Face& aCurrFace = TopoDS::Face(aFacesList.First());
257 
258   if (!myFaceInfo.IsBound(aCurrFace))
259     return;
260 
261   // No need to deal with transformation - it is applied in fill faces data method.
262   const NewFaceData & aNFD = myFaceInfo.Find(aCurrFace);
263   Handle(Geom_Surface) anOffsetSurf = aNFD.myOffsetS;
264 
265   // Compute offset 3d curve.
266   Standard_Real aF, aL;
267   Handle(Geom2d_Curve) aC2d = BRep_Tool::CurveOnSurface(theEdge, aCurrFace, aF, aL);
268 
269   BRepBuilderAPI_MakeEdge anEdgeMaker(aC2d, anOffsetSurf, aF, aL);
270   TopoDS_Edge aNewEdge = anEdgeMaker.Edge();
271 
272   // Compute max tolerance. Vertex tolerance usage is taken from existing offset computation algorithm.
273   // This piece of code significantly influences resulting performance.
274   Standard_Real aTol = BRep_Tool::MaxTolerance(theEdge, TopAbs_VERTEX);
275   BRepLib::BuildCurves3d(aNewEdge, aTol);
276 
277   NewEdgeData aNED;
278   aNED.myOffsetC = BRep_Tool::Curve(aNewEdge, aNED.myL, aF, aL);
279 
280   // Iterate over adjacent faces for the current edge and compute max deviation.
281   Standard_Real anEdgeTol = 0.0;
282   TopTools_ListOfShape::Iterator anIter(aFacesList);
283   for ( ; !aNED.myOffsetC.IsNull() && anIter.More() ; anIter.Next())
284   {
285     const TopoDS_Face& aCurFace = TopoDS::Face(anIter.Value());
286 
287     if (!myFaceInfo.IsBound(aCurFace))
288       continue;
289 
290     // Create offset curve on surface.
291     const Handle(Geom2d_Curve) aC2dNew = BRep_Tool::CurveOnSurface(theEdge, aCurFace, aF, aL);
292     const Handle(Adaptor2d_Curve2d) aHCurve2d = new Geom2dAdaptor_Curve(aC2dNew, aF, aL);
293     const Handle(Adaptor3d_Surface) aHSurface = new GeomAdaptor_Surface(myFaceInfo.Find(aCurFace).myOffsetS);
294     const Handle(Adaptor3d_CurveOnSurface) aCurveOnSurf = new Adaptor3d_CurveOnSurface(aHCurve2d, aHSurface);
295 
296     // Extract 3d-curve (it is not null).
297     const Handle(Adaptor3d_Curve) aCurve3d = new GeomAdaptor_Curve(aNED.myOffsetC, aF, aL);
298 
299     // It is necessary to compute maximal deviation (tolerance).
300     BRepLib_ValidateEdge aValidateEdge(aCurve3d, aCurveOnSurf, Standard_True);
301     aValidateEdge.Process();
302     if (aValidateEdge.IsDone())
303     {
304       Standard_Real aMaxTol1 = aValidateEdge.GetMaxDistance();
305       anEdgeTol = Max (anEdgeTol, aMaxTol1);
306     }
307   }
308   aNED.myTol = Max(BRep_Tool::Tolerance(aNewEdge), anEdgeTol);
309 
310   // Save computed 3d curve in map.
311   myEdgeInfo.Bind(theEdge, aNED);
312 }
313 
314 //=============================================================================
315 //function : FillVertexData
316 //purpose  :
317 //=============================================================================
FillVertexData(const TopoDS_Vertex & theVertex,const TopTools_IndexedDataMapOfShapeListOfShape & theVertexEdgeMap,const Standard_Integer theIdx)318 void BRepOffset_SimpleOffset::FillVertexData(const TopoDS_Vertex& theVertex,
319                                              const TopTools_IndexedDataMapOfShapeListOfShape& theVertexEdgeMap,
320                                              const Standard_Integer theIdx)
321 {
322   // Algorithm:
323   // Find adjacent edges for the given vertex.
324   // Find corresponding end on the each adjacent edge.
325   // Get offset points for founded end.
326   // Set result vertex position as barycenter of founded points.
327 
328   gp_Pnt aCurrPnt = BRep_Tool::Pnt(theVertex);
329 
330   const TopTools_ListOfShape& aEdgesList = theVertexEdgeMap(theIdx);
331 
332   if (aEdgesList.Size() == 0)
333     return; // Free verices are skipped.
334 
335   // Array to store offset points.
336   NCollection_Vector<gp_Pnt> anOffsetPointVec;
337 
338   Standard_Real aMaxEdgeTol = 0.0;
339 
340   // Iterate over adjacent edges.
341   TopTools_ListOfShape::Iterator anIterEdges(aEdgesList);
342   for (; anIterEdges.More() ; anIterEdges.Next() )
343   {
344     const TopoDS_Edge& aCurrEdge = TopoDS::Edge(anIterEdges.Value());
345 
346     if (!myEdgeInfo.IsBound(aCurrEdge))
347       continue; // Skip shared edges with wrong orientation.
348 
349     // Find the closest bound.
350     Standard_Real aF, aL;
351     Handle(Geom_Curve) aC3d = BRep_Tool::Curve(aCurrEdge, aF, aL);
352 
353     // Protection from degenerated edges.
354     if (aC3d.IsNull())
355       continue;
356 
357     const gp_Pnt aPntF = aC3d->Value(aF);
358     const gp_Pnt aPntL = aC3d->Value(aL);
359 
360     const Standard_Real aSqDistF = aPntF.SquareDistance(aCurrPnt);
361     const Standard_Real aSqDistL = aPntL.SquareDistance(aCurrPnt);
362 
363     Standard_Real aMinParam = aF, aMaxParam = aL;
364     if (aSqDistL < aSqDistF)
365     {
366       // Square distance to last point is closer.
367       aMinParam = aL; aMaxParam = aF;
368     }
369 
370     // Compute point on offset edge.
371     const NewEdgeData& aNED = myEdgeInfo.Find(aCurrEdge);
372     const Handle(Geom_Curve) &anOffsetCurve = aNED.myOffsetC;
373     const gp_Pnt anOffsetPoint = anOffsetCurve->Value(aMinParam);
374     anOffsetPointVec.Append(anOffsetPoint);
375 
376     // Handle situation when edge is closed.
377     TopoDS_Vertex aV1, aV2;
378     TopExp::Vertices(aCurrEdge, aV1, aV2);
379     if (aV1.IsSame(aV2))
380     {
381       const gp_Pnt anOffsetPointLast = anOffsetCurve->Value(aMaxParam);
382       anOffsetPointVec.Append(anOffsetPointLast);
383     }
384 
385     aMaxEdgeTol = Max(aMaxEdgeTol, aNED.myTol);
386   }
387 
388   // NCollection_Vector starts from 0 by default.
389   // It's better to use lower() and upper() in this case instead of direct indexes range.
390   gp_Pnt aCenter(0.0, 0.0, 0.0);
391   for(Standard_Integer i  = anOffsetPointVec.Lower();
392                        i <= anOffsetPointVec.Upper();
393                        ++i)
394   {
395     aCenter.SetXYZ(aCenter.XYZ() + anOffsetPointVec.Value(i).XYZ());
396   }
397   aCenter.SetXYZ(aCenter.XYZ() / anOffsetPointVec.Size());
398 
399   // Compute max distance.
400   Standard_Real aSqMaxDist = 0.0;
401   for(Standard_Integer i  = anOffsetPointVec.Lower();
402                        i <= anOffsetPointVec.Upper();
403                        ++i)
404   {
405     const Standard_Real aSqDist = aCenter.SquareDistance(anOffsetPointVec.Value(i));
406     if (aSqDist > aSqMaxDist)
407       aSqMaxDist = aSqDist;
408   }
409 
410   const Standard_Real aResTol = Max(aMaxEdgeTol, Sqrt(aSqMaxDist));
411 
412   const Standard_Real aMultCoeff = 1.001; // Avoid tolernace problems.
413   NewVertexData aNVD;
414   aNVD.myP = aCenter;
415   aNVD.myTol = aResTol * aMultCoeff;
416 
417   // Save computed vertex info.
418   myVertexInfo.Bind(theVertex, aNVD);
419 }
420