8013367c |
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> |
8574e329 |
24 | #include <BRepOffset.hxx> |
8013367c |
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, |
8574e329 |
46 | const Standard_Real theOffsetValue, |
47 | const Standard_Real theTolerance) |
48 | : myOffsetValue(theOffsetValue), |
49 | myTolerance(theTolerance) |
8013367c |
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); |
8574e329 |
229 | aS = BRepOffset::CollapseSingularities (aS, theFace, myTolerance); |
8013367c |
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 | aNFD.myOffsetS = new Geom_OffsetSurface(aS, aMult * myOffsetValue, Standard_True); |
237 | aNFD.myL = TopLoc_Location(); // Null transformation. |
238 | |
239 | // Save offset surface in map. |
240 | myFaceInfo.Bind(theFace, aNFD); |
241 | } |
242 | |
243 | //============================================================================= |
244 | //function : FillEdgeData |
245 | //purpose : |
246 | //============================================================================= |
247 | void BRepOffset_SimpleOffset::FillEdgeData(const TopoDS_Edge& theEdge, |
248 | const TopTools_IndexedDataMapOfShapeListOfShape& theEdgeFaceMap, |
249 | const Standard_Integer theIdx) |
250 | { |
251 | const TopTools_ListOfShape& aFacesList = theEdgeFaceMap(theIdx); |
252 | |
253 | if (aFacesList.Size() == 0) |
254 | return; // Free edges are skipped. |
255 | |
256 | // Get offset surface. |
257 | const TopoDS_Face& aCurrFace = TopoDS::Face(aFacesList.First()); |
258 | |
259 | if (!myFaceInfo.IsBound(aCurrFace)) |
260 | return; |
261 | |
262 | // No need to deal with transformation - it is applied in fill faces data method. |
263 | const NewFaceData & aNFD = myFaceInfo.Find(aCurrFace); |
264 | Handle(Geom_Surface) anOffsetSurf = aNFD.myOffsetS; |
265 | |
266 | // Compute offset 3d curve. |
267 | Standard_Real aF, aL; |
268 | Handle(Geom2d_Curve) aC2d = BRep_Tool::CurveOnSurface(theEdge, aCurrFace, aF, aL); |
269 | |
270 | BRepBuilderAPI_MakeEdge anEdgeMaker(aC2d, anOffsetSurf, aF, aL); |
271 | TopoDS_Edge aNewEdge = anEdgeMaker.Edge(); |
272 | |
273 | // Compute max tolerance. Vertex tolerance usage is taken from existing offset computation algorithm. |
274 | // This piece of code significantly influences resulting performance. |
275 | Standard_Real aTol = BRep_Tool::MaxTolerance(theEdge, TopAbs_VERTEX); |
276 | BRepLib::BuildCurves3d(aNewEdge, aTol); |
277 | |
278 | NewEdgeData aNED; |
279 | aNED.myOffsetC = BRep_Tool::Curve(aNewEdge, aNED.myL, aF, aL); |
280 | |
281 | // Iterate over adjacent faces for the current edge and compute max deviation. |
282 | Standard_Real anEdgeTol = 0.0; |
283 | TopTools_ListOfShape::Iterator anIter(aFacesList); |
284 | for ( ; !aNED.myOffsetC.IsNull() && anIter.More() ; anIter.Next()) |
285 | { |
286 | const TopoDS_Face& aCurFace = TopoDS::Face(anIter.Value()); |
287 | |
288 | if (!myFaceInfo.IsBound(aCurFace)) |
289 | continue; |
290 | |
291 | // Create offset curve on surface. |
4d973358 |
292 | const Handle(Geom2d_Curve) aC2dNew = BRep_Tool::CurveOnSurface(theEdge, aCurFace, aF, aL); |
293 | const Handle(Adaptor2d_HCurve2d) aHCurve2d = new Geom2dAdaptor_HCurve(aC2dNew, aF, aL); |
8013367c |
294 | const Handle(Adaptor3d_HSurface) aHSurface = new GeomAdaptor_HSurface(myFaceInfo.Find(aCurFace).myOffsetS); |
295 | Adaptor3d_CurveOnSurface aCurveOnSurf(aHCurve2d, aHSurface); |
296 | |
297 | // Extract 3d-curve (it is not null). |
298 | const GeomAdaptor_Curve aCurve3d(aNED.myOffsetC, aF, aL); |
299 | |
300 | // It is necessary to compute maximal deviation (tolerance). |
301 | Standard_Real aMaxTol = 0.0; |
302 | ShapeAnalysis_Edge::ComputeDeviation(aCurve3d, aCurveOnSurf, Standard_True, aMaxTol, NCONTROL); |
303 | anEdgeTol = Max (anEdgeTol, aMaxTol); |
304 | } |
305 | aNED.myTol = Max(BRep_Tool::Tolerance(aNewEdge), anEdgeTol); |
306 | |
307 | // Save computed 3d curve in map. |
308 | myEdgeInfo.Bind(theEdge, aNED); |
309 | } |
310 | |
311 | //============================================================================= |
312 | //function : FillVertexData |
313 | //purpose : |
314 | //============================================================================= |
315 | void BRepOffset_SimpleOffset::FillVertexData(const TopoDS_Vertex& theVertex, |
316 | const TopTools_IndexedDataMapOfShapeListOfShape& theVertexEdgeMap, |
317 | const Standard_Integer theIdx) |
318 | { |
319 | // Algorithm: |
320 | // Find adjacent edges for the given vertex. |
321 | // Find corresponding end on the each adjacent edge. |
322 | // Get offset points for founded end. |
323 | // Set result vertex position as barycenter of founded points. |
324 | |
325 | gp_Pnt aCurrPnt = BRep_Tool::Pnt(theVertex); |
326 | |
327 | const TopTools_ListOfShape& aEdgesList = theVertexEdgeMap(theIdx); |
328 | |
329 | if (aEdgesList.Size() == 0) |
330 | return; // Free verices are skipped. |
331 | |
332 | // Array to store offset points. |
333 | NCollection_Vector<gp_Pnt> anOffsetPointVec; |
334 | |
335 | Standard_Real aMaxEdgeTol = 0.0; |
336 | |
337 | // Iterate over adjacent edges. |
338 | TopTools_ListOfShape::Iterator anIterEdges(aEdgesList); |
339 | for (; anIterEdges.More() ; anIterEdges.Next() ) |
340 | { |
341 | const TopoDS_Edge& aCurrEdge = TopoDS::Edge(anIterEdges.Value()); |
342 | |
343 | if (!myEdgeInfo.IsBound(aCurrEdge)) |
344 | continue; // Skip shared edges with wrong orientation. |
345 | |
346 | // Find the closest bound. |
347 | Standard_Real aF, aL; |
348 | Handle(Geom_Curve) aC3d = BRep_Tool::Curve(aCurrEdge, aF, aL); |
349 | |
350 | // Protection from degenerated edges. |
351 | if (aC3d.IsNull()) |
352 | continue; |
353 | |
354 | const gp_Pnt aPntF = aC3d->Value(aF); |
355 | const gp_Pnt aPntL = aC3d->Value(aL); |
356 | |
357 | const Standard_Real aSqDistF = aPntF.SquareDistance(aCurrPnt); |
358 | const Standard_Real aSqDistL = aPntL.SquareDistance(aCurrPnt); |
359 | |
360 | Standard_Real aMinParam = aF, aMaxParam = aL; |
361 | if (aSqDistL < aSqDistF) |
362 | { |
363 | // Square distance to last point is closer. |
364 | aMinParam = aL; aMaxParam = aF; |
365 | } |
366 | |
367 | // Compute point on offset edge. |
368 | const NewEdgeData& aNED = myEdgeInfo.Find(aCurrEdge); |
369 | const Handle(Geom_Curve) &anOffsetCurve = aNED.myOffsetC; |
370 | const gp_Pnt anOffsetPoint = anOffsetCurve->Value(aMinParam); |
371 | anOffsetPointVec.Append(anOffsetPoint); |
372 | |
373 | // Handle situation when edge is closed. |
374 | TopoDS_Vertex aV1, aV2; |
375 | TopExp::Vertices(aCurrEdge, aV1, aV2); |
376 | if (aV1.IsSame(aV2)) |
377 | { |
4d973358 |
378 | const gp_Pnt anOffsetPointLast = anOffsetCurve->Value(aMaxParam); |
379 | anOffsetPointVec.Append(anOffsetPointLast); |
8013367c |
380 | } |
381 | |
382 | aMaxEdgeTol = Max(aMaxEdgeTol, aNED.myTol); |
383 | } |
384 | |
385 | // NCollection_Vector starts from 0 by default. |
386 | // It's better to use lower() and upper() in this case instead of direct indexes range. |
387 | gp_Pnt aCenter(0.0, 0.0, 0.0); |
388 | for(Standard_Integer i = anOffsetPointVec.Lower(); |
389 | i <= anOffsetPointVec.Upper(); |
390 | ++i) |
391 | { |
392 | aCenter.SetXYZ(aCenter.XYZ() + anOffsetPointVec.Value(i).XYZ()); |
393 | } |
394 | aCenter.SetXYZ(aCenter.XYZ() / anOffsetPointVec.Size()); |
395 | |
396 | // Compute max distance. |
397 | Standard_Real aSqMaxDist = 0.0; |
398 | for(Standard_Integer i = anOffsetPointVec.Lower(); |
399 | i <= anOffsetPointVec.Upper(); |
400 | ++i) |
401 | { |
402 | const Standard_Real aSqDist = aCenter.SquareDistance(anOffsetPointVec.Value(i)); |
403 | if (aSqDist > aSqMaxDist) |
404 | aSqMaxDist = aSqDist; |
405 | } |
406 | |
407 | const Standard_Real aResTol = Max(aMaxEdgeTol, Sqrt(aSqMaxDist)); |
408 | |
409 | const Standard_Real aMultCoeff = 1.001; // Avoid tolernace problems. |
410 | NewVertexData aNVD; |
411 | aNVD.myP = aCenter; |
412 | aNVD.myTol = aResTol * aMultCoeff; |
413 | |
414 | // Save computed vertex info. |
415 | myVertexInfo.Bind(theVertex, aNVD); |
416 | } |