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