Warnings on vc14 were eliminated
[occt.git] / src / ShapeFix / ShapeFix_FixSmallSolid.cxx
CommitLineData
8422b578 1// Copyright (c) 2014 OPEN CASCADE SAS
2//
3// This file is part of Open CASCADE Technology software library.
4//
5// This library is free software; you can redistribute it and/or modify it under
6// the terms of the GNU Lesser General Public License version 2.1 as published
7// by the Free Software Foundation, with special exception defined in the file
8// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9// distribution for complete text of the license and disclaimer of any warranty.
10//
11// Alternatively, this file may be used under the terms of Open CASCADE
12// commercial license or contractual agreement.
13
8422b578 14
42cf5bc1 15#include <BRep_Builder.hxx>
16#include <BRepGProp.hxx>
17#include <GProp_GProps.hxx>
18#include <Message_Msg.hxx>
8422b578 19#include <Precision.hxx>
42cf5bc1 20#include <ShapeBuild_ReShape.hxx>
21#include <ShapeFix_FixSmallSolid.hxx>
22#include <Standard_Type.hxx>
23#include <TopExp_Explorer.hxx>
8422b578 24#include <TopoDS_Builder.hxx>
25#include <TopoDS_Compound.hxx>
26#include <TopoDS_Iterator.hxx>
42cf5bc1 27#include <TopoDS_Shape.hxx>
28#include <TopTools_DataMapIteratorOfDataMapOfShapeListOfShape.hxx>
29#include <TopTools_DataMapOfShapeListOfShape.hxx>
8422b578 30#include <TopTools_DataMapOfShapeReal.hxx>
31#include <TopTools_DataMapOfShapeShape.hxx>
42cf5bc1 32#include <TopTools_ListIteratorOfListOfShape.hxx>
33#include <TopTools_ListOfShape.hxx>
34#include <TopTools_MapIteratorOfMapOfShape.hxx>
35#include <TopTools_MapOfShape.hxx>
8422b578 36
92efcf78 37IMPLEMENT_STANDARD_RTTIEXT(ShapeFix_FixSmallSolid,ShapeFix_Root)
38
8422b578 39//=======================================================================
40//function : ShapeFix_FixSmallSolid
41//purpose : Construct
42//=======================================================================
43ShapeFix_FixSmallSolid::ShapeFix_FixSmallSolid()
df515f16 44 : myFixMode (0)
45 , myVolumeThreshold (Precision::Infinite())
8422b578 46 , myWidthFactorThreshold (Precision::Infinite()) {}
47
df515f16 48//=======================================================================
49//function : SetFixMode
50//purpose : Set the mode for applying fixes of small solids.
51//=======================================================================
52void ShapeFix_FixSmallSolid::SetFixMode (
53 const Standard_Integer theMode)
54{
55 myFixMode = (theMode < 0 || theMode > 2) ? 0 : theMode;
56}
57
8422b578 58//=======================================================================
59//function : SetVolumeThreshold
60//purpose : Set or clear volume threshold for small solids
61//=======================================================================
62void ShapeFix_FixSmallSolid::SetVolumeThreshold (
63 const Standard_Real theThreshold)
64{
65 myVolumeThreshold =
66 theThreshold >= 0.0 ? theThreshold : Precision::Infinite();
67}
68
69//=======================================================================
70//function : SetWidthFactorThreshold
71//purpose : Set or clear width factor threshold for small solids
72//=======================================================================
73void ShapeFix_FixSmallSolid::SetWidthFactorThreshold (
74 const Standard_Real theThreshold)
75{
76 myWidthFactorThreshold =
77 theThreshold >= 0.0 ? theThreshold : Precision::Infinite();
78}
79
80//=======================================================================
81//function : IsValidInput
82//purpose : auxiliary
83//=======================================================================
84// Check if an input shape is valid
85static Standard_Boolean IsValidInput (const TopoDS_Shape& theShape)
86{
87 if (theShape.IsNull())
88 return Standard_False;
89
90 switch (theShape.ShapeType())
91 {
92 case TopAbs_COMPOUND:
93 case TopAbs_COMPSOLID:
94 case TopAbs_SOLID:
95 return Standard_True;
96 default:
97 return Standard_False;
98 }
99}
100
101//=======================================================================
102//function : Remove
103//purpose : Remove small solids from the given shape
104//=======================================================================
105TopoDS_Shape ShapeFix_FixSmallSolid::Remove (
106 const TopoDS_Shape& theShape,
107 const Handle(ShapeBuild_ReShape)& theContext) const
108{
109 // Check if at least one smallness criterion is set and the shape is valid
110 if (!IsThresholdsSet() || !IsValidInput (theShape)) return theShape;
111
112 // Find and remove all small solids
113 TopExp_Explorer aSolidIter (theShape, TopAbs_SOLID);
114 for (; aSolidIter.More(); aSolidIter.Next())
115 {
116 const TopoDS_Shape& aSolid = aSolidIter.Current();
117 if (IsSmall (aSolid))
7c8996f4 118 {
8422b578 119 theContext->Remove (aSolid);
7c8996f4 120 SendWarning ( aSolid, Message_Msg( "ShapeFix.FixSmallSolid.MSG0" ));
121 }
8422b578 122 }
123
124 // Return updated shape
125 return theContext->Apply (theShape);
126}
127
128//=======================================================================
129//function : ShapeArea
130//purpose : auxiliary
131//=======================================================================
132// Calculate surface area of a shape
133static Standard_Real ShapeArea (const TopoDS_Shape& theShape)
134{
135 GProp_GProps aProps;
136 BRepGProp::SurfaceProperties (theShape, aProps);
137 return aProps.Mass();
138}
139
140//=======================================================================
141//function : ShapeVolume
142//purpose : auxiliary
143//=======================================================================
144// Calculate volume of a shape
145static Standard_Real ShapeVolume (const TopoDS_Shape& theShape)
146{
147 GProp_GProps aProps;
148 BRepGProp::VolumeProperties (theShape, aProps);
149 return aProps.Mass();
150}
151
152//=======================================================================
153//function : AddToMap
154//purpose : auxiliary
155//=======================================================================
156// Append an item to a list of shapes mapped to a shape
157static void AddToMap (TopTools_DataMapOfShapeListOfShape& theMap,
158 const TopoDS_Shape& theKey,
159 const TopoDS_Shape& theItem)
160{
b7c077b9 161 TopTools_ListOfShape* aListPtr = theMap.ChangeSeek (theKey);
8422b578 162 if (aListPtr == NULL)
163 {
164 TopTools_ListOfShape aList;
165 aList.Append (theItem);
166 theMap.Bind (theKey, aList);
167 }
168 else
b7c077b9 169 aListPtr->Append (theItem);
8422b578 170}
171
172//=======================================================================
173//function : AddToMap
174//purpose : auxiliary
175//=======================================================================
176// Append items to a list of shapes mapped to a shape
177static void AddToMap (TopTools_DataMapOfShapeListOfShape& theMap,
178 const TopoDS_Shape& theKey,
179 TopTools_ListOfShape& theItems)
180{
181 if (theItems.IsEmpty()) return;
182
b7c077b9 183 TopTools_ListOfShape* aListPtr = theMap.ChangeSeek (theKey);
8422b578 184 if (aListPtr == NULL)
185 theMap.Bind (theKey, theItems);
186 else
b7c077b9 187 aListPtr->Append (theItems);
8422b578 188}
189
190//=======================================================================
191//function : MapFacesToShells
192//purpose : auxiliary
193//=======================================================================
194// Map faces from a solid with their shells;
195// unmap faces shared between two shells
196static void MapFacesToShells (const TopoDS_Shape& theSolid,
197 TopTools_DataMapOfShapeShape& theMap)
198{
199 TopoDS_Iterator aShellIter (theSolid);
200 for (; aShellIter.More(); aShellIter.Next())
201 {
202 const TopoDS_Shape& aShell = aShellIter.Value();
203 if (aShell.ShapeType() != TopAbs_SHELL) continue;
204
205 TopoDS_Iterator aFaceIter (aShell);
206 for (; aFaceIter.More(); aFaceIter.Next())
207 {
208 const TopoDS_Shape& aFace = aFaceIter.Value();
209 if (aFace.ShapeType() != TopAbs_FACE) continue;
210
211 if (!theMap.Bind (aFace, aShell))
212 theMap.UnBind (aFace);
213 }
214 }
215}
216
217//=======================================================================
218//function : FindMostSharedShell
219//purpose : auxiliary
220//=======================================================================
221// Find an outer shell having greatest sum area of
222// all faces shared with the solid
223static Standard_Boolean FindMostSharedShell (
224 const TopoDS_Shape& theSolid,
225 const TopTools_DataMapOfShapeShape& theMapFacesToOuterShells,
226 TopoDS_Shape& theMostSharedOuterShell,
227 TopoDS_Shape& theMostSharedSolidShell,
228 TopTools_ListOfShape& theOtherSolidShells)
229{
230 TopTools_DataMapOfShapeReal aSharedAreas;
231 Standard_Real aMaxSharedArea = 0.0;
232 const TopoDS_Shape* aMostSharedOuterShellPtr = NULL;
233 const TopoDS_Shape* aMostSharedSolidShellPtr = NULL;
234
235 // check every shell in the solid for faces shared with outer shells
236 TopoDS_Iterator aShellIter (theSolid);
237 for (; aShellIter.More(); aShellIter.Next())
238 {
239 const TopoDS_Shape& aSolidShell = aShellIter.Value();
240 if (aSolidShell.ShapeType() != TopAbs_SHELL) continue;
241
242 theOtherSolidShells.Append (aSolidShell);
243
244 TopoDS_Iterator aFaceIter (aSolidShell);
245 for (; aFaceIter.More(); aFaceIter.Next())
246 {
247 const TopoDS_Shape& aFace = aFaceIter.Value();
248 if (aFace.ShapeType() != TopAbs_FACE) continue;
249
250 // find an outer shell that shares the current face
b7c077b9 251 const TopoDS_Shape* anOuterShellPtr = theMapFacesToOuterShells.Seek (aFace);
8422b578 252 if (anOuterShellPtr == NULL) continue;
b7c077b9 253 const TopoDS_Shape& anOuterShell = *anOuterShellPtr;
8422b578 254
255 // add the face area to the sum shared area for the outer shell
256 Standard_Real anArea = ShapeArea (aFace);
b7c077b9 257 Standard_Real* aSharedAreaPtr = aSharedAreas.ChangeSeek (anOuterShell);
8422b578 258 if (aSharedAreaPtr == NULL)
259 aSharedAreas.Bind (anOuterShell, anArea);
260 else
b7c077b9 261 anArea = (*aSharedAreaPtr) += anArea;
8422b578 262
263 // if this outer shell currently has maximum shared area,
264 // remember it and the current solid's shell
265 if (aMaxSharedArea < anArea)
266 {
267 aMaxSharedArea = anArea;
268 aMostSharedOuterShellPtr = &anOuterShell;
269 aMostSharedSolidShellPtr = &aSolidShell;
270 }
271 }
272 }
273
274 // return nothing if no adjanced outer shells were found
275 if (aMostSharedSolidShellPtr == NULL)
276 return Standard_False;
277
278 // compose return values
279 theMostSharedOuterShell = *aMostSharedOuterShellPtr;
280 theMostSharedSolidShell = *aMostSharedSolidShellPtr;
281
282 // remove the most shared solid's shell from the returned list of its other shells
283 TopTools_ListIteratorOfListOfShape anOtherShellIter (theOtherSolidShells);
284 while (!anOtherShellIter.Value().IsSame (theMostSharedSolidShell))
285 anOtherShellIter.Next();
286 theOtherSolidShells.Remove (anOtherShellIter);
287
288 return Standard_True;
289}
290
291//=======================================================================
292//function : MergeShells
293//purpose : auxiliary
294//=======================================================================
295// Merge some shells to a base shell
296static TopoDS_Shape MergeShells (
297 const TopoDS_Shape& theBaseShell,
298 TopTools_ListOfShape& theShellsToMerge,
299 const TopTools_DataMapOfShapeShape& theMapFacesToOuterShells,
300 TopTools_DataMapOfShapeShape& theMapNewFreeFacesToShells)
301{
302 // Create a new shell
303 BRep_Builder aBuilder;
304 TopoDS_Shape aNewShell = theBaseShell.EmptyCopied();
305
306 // Sort the faces belogning to the merged shells:
307 // - faces shared with the base shell:
308 // keep to remove from the base shell;
309 // - faces shared with other outer shells, non-face elements:
310 // add to the new shell;
311 // - faces not shared with any outer or any merged shell:
312 // keep to add to the new shell and to the new map.
313 TopTools_MapOfShape aRemoveFaces;
314 TopTools_MapOfShape aNewFreeFaces;
315
316 TopTools_ListIteratorOfListOfShape aShellIter (theShellsToMerge);
317 for (; aShellIter.More(); aShellIter.Next())
318 {
319 TopoDS_Iterator aFaceIter (aShellIter.Value());
320 for (; aFaceIter.More(); aFaceIter.Next())
321 {
322 const TopoDS_Shape& aFace = aFaceIter.Value();
323
324 // non-face element in a shell - just add it to the new shell
325 if (aFace.ShapeType() != TopAbs_FACE)
326 {
327 aBuilder.Add (aNewShell, aFace);
328 continue;
329 }
330
331 // classify the face
b7c077b9 332 const TopoDS_Shape* anOuterShellPtr = theMapFacesToOuterShells.Seek (aFace);
8422b578 333 if (anOuterShellPtr != NULL)
334 {
b7c077b9 335 if (anOuterShellPtr->IsSame (theBaseShell))
8422b578 336 aRemoveFaces.Add (aFace); // face shared with the base shell
337 else
338 aBuilder.Add (aNewShell, aFace); // face shared with another outer shell
339 }
340 else
341 {
342 if (aNewFreeFaces.Contains (aFace))
343 aNewFreeFaces.Remove (aFace); // face shared with another merged shell
344 else
345 aNewFreeFaces.Add (aFace); // face not shared
346 }
347 }
348 }
349 theShellsToMerge.Clear();
350
351 // Add the kept faces from the merged shells to the new shell
352 TopTools_MapIteratorOfMapOfShape aNewFaceIter (aNewFreeFaces);
353 for (; aNewFaceIter.More(); aNewFaceIter.Next())
354 {
355 const TopoDS_Shape& aFace = aNewFaceIter.Key();
356 aBuilder.Add (aNewShell, aFace);
357 theMapNewFreeFacesToShells.Bind (aFace, aNewShell);
358 }
359 aNewFreeFaces.Clear();
360
361 // Add needed faces from the base shell to the new shell
362 TopoDS_Iterator aBaseFaceIter (theBaseShell);
363 for (; aBaseFaceIter.More(); aBaseFaceIter.Next())
364 {
365 const TopoDS_Shape& aFace = aBaseFaceIter.Value();
366 if (!aRemoveFaces.Contains (aFace))
367 aBuilder.Add (aNewShell, aFace);
368 }
369
370 // If there are no elements in the new shell, return null shape
371 if (!TopoDS_Iterator (aNewShell).More())
372 return TopoDS_Shape();
373
374 return aNewShell;
375}
376
377//=======================================================================
378//function : AddShells
379//purpose : auxiliary
380//=======================================================================
381// Add some shells to a base shell
382static TopoDS_Compound AddShells (
383 const TopoDS_Shape& theBaseShell,
384 TopTools_ListOfShape& theShellsToAdd)
385{
386 // Create a compound
387 BRep_Builder aBuilder;
388 TopoDS_Compound aCompound;
389 aBuilder.MakeCompound (aCompound);
390
391 // Add the base shell to the compound
392 if (!theBaseShell.IsNull())
393 aBuilder.Add (aCompound, theBaseShell);
394
395 // Add other shells to the compound
396 TopTools_ListIteratorOfListOfShape aShellIter (theShellsToAdd);
397 for (; aShellIter.More(); aShellIter.Next())
398 aBuilder.Add (aCompound, aShellIter.Value());
399
400 theShellsToAdd.Clear();
401
402 return aCompound;
403}
404
405TopoDS_Shape ShapeFix_FixSmallSolid::Merge (
406 const TopoDS_Shape& theShape,
407 const Handle(ShapeBuild_ReShape)& theContext) const
408{
409 // Check if at least one smallness criterion is set and the shape is valid
410 if (!IsThresholdsSet() || !IsValidInput (theShape)) return theShape;
411
412 // Find all small solids and put them in a list;
413 // Build a map of faces belonging to non-small solids
414 // but not shared between two non-small solids
415 TopTools_ListOfShape aSmallSolids;
416 TopTools_DataMapOfShapeShape aMapFacesToShells;
417
418 TopExp_Explorer aSolidIter (theShape, TopAbs_SOLID);
419 for (; aSolidIter.More(); aSolidIter.Next())
420 {
421 const TopoDS_Shape& aSolid = aSolidIter.Current();
422 if (IsSmall (aSolid))
423 aSmallSolids.Append (aSolid);
424 else
425 MapFacesToShells (aSolid, aMapFacesToShells);
426 }
427
428 // Merge all small solids adjacent to at least one non-small one;
429 // repeat this until no small solids remain or no new solids can be merged
430 TopTools_DataMapOfShapeShape aNewMapFacesToShells;
431 TopTools_DataMapOfShapeShape* aMapFacesToShellsPtr = &aMapFacesToShells;
432 TopTools_DataMapOfShapeShape* aNewMapFacesToShellsPtr = &aNewMapFacesToShells;
433 while (!aSmallSolids.IsEmpty())
434 {
435 // find small solids that may be merged on the current iteration;
436 // compose their shells in lists associated with non-small solids' shells
437 // which they should be merged to
438 TopTools_DataMapOfShapeListOfShape aShellsToMerge, aShellsToAdd;
51740958 439 TopTools_ListIteratorOfListOfShape aSmallIter(aSmallSolids);
440 while (aSmallIter.More())
8422b578 441 {
51740958 442 const TopoDS_Shape& aSmallSolid = aSmallIter.Value();
8422b578 443
444 // find a non-small solid's shell having greatest sum area of
445 // all faces shared with the current small solid
446 TopoDS_Shape aNonSmallSolidShell;
447 TopoDS_Shape anAdjacentShell;
448 TopTools_ListOfShape aNotAdjacentShells;
449 if (FindMostSharedShell (aSmallSolid, *aMapFacesToShellsPtr,
450 aNonSmallSolidShell, anAdjacentShell, aNotAdjacentShells))
451 {
452 // add the small solid's shells to appropriate lists
453 // associated with the selected non-small solid's shell
454 AddToMap (aShellsToMerge, aNonSmallSolidShell, anAdjacentShell);
455 AddToMap (aShellsToAdd , aNonSmallSolidShell, aNotAdjacentShells);
456
457 // remove the small solid
458 theContext->Remove (aSmallSolid);
7c8996f4 459 SendWarning ( aSmallSolid, Message_Msg( "ShapeFix.FixSmallSolid.MSG1" ));
81a023ab 460
51740958 461 aSmallSolids.Remove (aSmallIter);
8422b578 462 }
463 else
51740958 464 aSmallIter.Next();
8422b578 465 }
466
467 // stop if no solids can be merged
468 if (aShellsToMerge.IsEmpty()) break;
469
470 // update needed non-small solids' shells by
471 // merging and adding the listed small solids' shells to them
472 TopTools_DataMapIteratorOfDataMapOfShapeListOfShape
473 aShellIter (aShellsToMerge);
474 for (; aShellIter.More(); aShellIter.Next())
475 {
476 // get the current non-small solid's shell
477 // and corresponding small solids' shells
478 const TopoDS_Shape& aBaseShell = aShellIter.Key();
479 TopTools_ListOfShape& aShellsToBeMerged =
480 (TopTools_ListOfShape&)aShellIter.Value();
481 TopTools_ListOfShape* aShellsToBeAddedPtr =
b7c077b9 482 aShellsToAdd.ChangeSeek (aBaseShell);
8422b578 483
484 // merge needed shells
485 TopoDS_Shape aNewShell = MergeShells (aBaseShell, aShellsToBeMerged,
486 *aMapFacesToShellsPtr, *aNewMapFacesToShellsPtr);
487
488 // add new shells if needed
489 if (aShellsToBeAddedPtr != NULL)
490 aNewShell = AddShells (aNewShell, *aShellsToBeAddedPtr);
491
492 // replace the current non-small solid's shell with the new one(s)
493 theContext->Replace (aBaseShell, aNewShell);
494 }
495
496 // clear the old faces map and start using the new one
497 aMapFacesToShellsPtr->Clear();
498 std::swap (aMapFacesToShellsPtr, aNewMapFacesToShellsPtr);
499 }
500
501 // Return updated shape
502 return theContext->Apply (theShape);
503}
504
505//=======================================================================
506//function : IsThresholdsSet
507//purpose : Check if at least one smallness criterion is set
508//=======================================================================
509Standard_Boolean ShapeFix_FixSmallSolid::IsThresholdsSet() const
510{
df515f16 511 return (IsUsedVolumeThreshold() && myVolumeThreshold < Precision::Infinite()) ||
512 (IsUsedWidthFactorThreshold() && myWidthFactorThreshold < Precision::Infinite());
8422b578 513}
514
515//=======================================================================
516//function : IsSmall
517//purpose : Check if a solid meets the smallness criteria
518//=======================================================================
519Standard_Boolean ShapeFix_FixSmallSolid::IsSmall (const TopoDS_Shape& theSolid)
520 const
521{
df515f16 522 // If the volume threshold is used and set, and the solid's volume exceeds
523 // threshold value, consider the solid as not small
8422b578 524 Standard_Real aVolume = ShapeVolume (theSolid);
df515f16 525 if (IsUsedVolumeThreshold() && aVolume > myVolumeThreshold)
8422b578 526 return Standard_False;
527
df515f16 528 // If the width factor threshold is used and set,
529 // and the solid's width factor exceeds threshold value,
8422b578 530 // consider the solid as not small
df515f16 531 if (IsUsedWidthFactorThreshold() && myWidthFactorThreshold < Precision::Infinite())
8422b578 532 {
533 Standard_Real anArea = ShapeArea (theSolid);
534 if (aVolume > myWidthFactorThreshold * anArea * 0.5)
535 return Standard_False;
536 }
537
538 // Both thresholds are met - consider the solid as small
539 return Standard_True;
540}
df515f16 541//=======================================================================
542//function : IsUsedWidthFactorThreshold
543//purpose : Check if width factor threshold criterion is used
544//=======================================================================
545Standard_Boolean ShapeFix_FixSmallSolid::IsUsedWidthFactorThreshold() const
546{
547 return myFixMode == 0 || myFixMode == 1;
548}
549//=======================================================================
550//function : IsUsedVolumeThreshold
551//purpose : Check if volume threshold criterion is used
552//=======================================================================
553Standard_Boolean ShapeFix_FixSmallSolid::IsUsedVolumeThreshold() const
554{
555 return myFixMode == 0 || myFixMode == 2;
556}