1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
3 // File: GeomAlgoAPI_Revolution.cpp
4 // Created: 12 May 2015
5 // Author: Dmitry Bobylev
7 #include "GeomAlgoAPI_Revolution.h"
9 #include <GeomAPI_Face.h>
10 #include <GeomAPI_ShapeExplorer.h>
11 #include <GeomAlgoAPI_DFLoader.h>
12 #include <GeomAlgoAPI_MakeShapeList.h>
13 #include <GeomAlgoAPI_ShapeTools.h>
15 #include <BRep_Builder.hxx>
16 #include <BRep_Tool.hxx>
17 #include <BRepAlgoAPI_Cut.hxx>
18 #include <BRepBuilderAPI_MakeFace.hxx>
19 #include <BRepBuilderAPI_Transform.hxx>
20 #include <BRepCheck_Analyzer.hxx>
21 #include <BRepPrimAPI_MakeRevol.hxx>
22 #include <BRepGProp.hxx>
23 #include <Geom_Plane.hxx>
24 #include <GeomLib_IsPlanarSurface.hxx>
26 #include <GProp_GProps.hxx>
27 #include <TopExp_Explorer.hxx>
29 #include <TopTools_ListIteratorOfListOfShape.hxx>
31 /// \brief Constructs infinite face from thePlane, and with axis located on the same side
32 /// of the plane as thePoint. Modifies thePlane axis direction.
33 /// \param[in,out] thePlane plane to construct face.
34 /// \param[in] thePoint point to locate plane axis.
35 /// \return constructed face.
36 static TopoDS_Face makeFaceFromPlane(gp_Pln& thePlane, const gp_Pnt& thePoint);
38 /// \return solid created from face or shell.
39 static TopoDS_Solid makeSolidFromShape(const TopoDS_Shape& theShape);
41 /// \brief Selects solid from theShape with closest center of mass to thePoint
42 /// \param[in] theShape compound with solids.
43 /// \param[in] thePoint point.
45 static TopoDS_Shape findClosest(const TopoDS_Shape& theShape, const gp_Pnt& thePoint);
47 //=================================================================================================
48 GeomAlgoAPI_Revolution::GeomAlgoAPI_Revolution(std::shared_ptr<GeomAPI_Shape> theBaseShape,
49 std::shared_ptr<GeomAPI_Ax1> theAxis,
53 build(theBaseShape, theAxis, std::shared_ptr<GeomAPI_Shape>(), theToAngle, std::shared_ptr<GeomAPI_Shape>(), theFromAngle);
56 //=================================================================================================
57 GeomAlgoAPI_Revolution::GeomAlgoAPI_Revolution(std::shared_ptr<GeomAPI_Shape> theBaseShape,
58 std::shared_ptr<GeomAPI_Ax1> theAxis,
59 std::shared_ptr<GeomAPI_Shape> theToShape,
61 std::shared_ptr<GeomAPI_Shape> theFromShape,
64 build(theBaseShape, theAxis, theToShape, theToAngle, theFromShape, theFromAngle);
67 //=================================================================================================
68 void GeomAlgoAPI_Revolution::build(const std::shared_ptr<GeomAPI_Shape>& theBaseShape,
69 const std::shared_ptr<GeomAPI_Ax1>& theAxis,
70 const std::shared_ptr<GeomAPI_Shape>& theToShape,
72 const std::shared_ptr<GeomAPI_Shape>& theFromShape,
75 if(!theBaseShape || !theAxis ||
76 (((!theFromShape && !theToShape) || (theFromShape && theToShape && theFromShape->isEqual(theToShape)))
77 && (theFromAngle == -theToAngle))) {
82 const TopoDS_Shape& aBaseShape = theBaseShape->impl<TopoDS_Shape>();
83 TopoDS_Face aBaseFace;
84 if(theBaseShape->shapeType() == GeomAPI_Shape::FACE) {
85 aBaseFace = TopoDS::Face(theBaseShape->impl<TopoDS_Shape>());
86 } else if(theBaseShape->shapeType() == GeomAPI_Shape::SHELL) {
87 GeomAPI_ShapeExplorer anExp(theBaseShape, GeomAPI_Shape::FACE);
89 std::shared_ptr<GeomAPI_Shape> aFaceOnShell = anExp.current();
90 aBaseFace = TopoDS::Face(aFaceOnShell->impl<TopoDS_Shape>());
93 if(aBaseFace.IsNull()) {
96 GeomLib_IsPlanarSurface isBasePlanar(BRep_Tool::Surface(aBaseFace));
97 gp_Pln aBasePln = isBasePlanar.Plan();
98 Geom_Plane aBasePlane(aBasePln);
99 gp_Ax1 anAxis = theAxis->impl<gp_Ax1>();
100 if(aBasePlane.Axis().Angle(anAxis) < Precision::Confusion()) {
104 gp_Pnt aBaseCentre = GeomAlgoAPI_ShapeTools::centreOfMass(theBaseShape)->impl<gp_Pnt>();
106 TopoDS_Shape aResult;
107 if(!theFromShape && !theToShape) { // Case 1: When only angles was set.
108 // Rotating base face with the negative value of "from angle".
110 aBaseTrsf.SetRotation(anAxis, -theFromAngle / 180.0 * M_PI);
111 BRepBuilderAPI_Transform* aBaseTransform = new BRepBuilderAPI_Transform(aBaseShape,
114 if(!aBaseTransform) {
117 this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(new GeomAlgoAPI_MakeShape(aBaseTransform)));
118 if(!aBaseTransform->IsDone()) {
121 TopoDS_Shape aRotatedBase = aBaseTransform->Shape();
123 // Making revolution to the angle equal to the sum of "from angle" and "to angle".
124 BRepPrimAPI_MakeRevol* aRevolBuilder = new BRepPrimAPI_MakeRevol(aRotatedBase,
126 (theFromAngle + theToAngle) / 180 * M_PI,
131 this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(new GeomAlgoAPI_MakeShape(aRevolBuilder)));
132 if(!aRevolBuilder->IsDone()) {
135 aResult = aRevolBuilder->Shape();
138 for(TopExp_Explorer anExp(aRotatedBase, TopAbs_FACE); anExp.More(); anExp.Next()) {
139 const TopoDS_Shape& aFace = anExp.Current();
140 std::shared_ptr<GeomAPI_Shape> aFromShape(new GeomAPI_Shape), aToShape(new GeomAPI_Shape);
141 aFromShape->setImpl(new TopoDS_Shape(aRevolBuilder->FirstShape(aFace)));
142 aToShape->setImpl(new TopoDS_Shape(aRevolBuilder->LastShape(aFace)));
143 this->addFromFace(aFromShape);
144 this->addToFace(aToShape);
146 } else if(theFromShape && theToShape) { // Case 2: When both bounding planes were set.
147 // Making revolution to the 360 angle.
148 BRepPrimAPI_MakeRevol* aRevolBuilder = new BRepPrimAPI_MakeRevol(aBaseShape, anAxis, 2 * M_PI, Standard_True);
152 this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(new GeomAlgoAPI_MakeShape(aRevolBuilder)));
153 if(!aRevolBuilder->IsDone()) {
156 aResult = aRevolBuilder->Shape();
158 // Getting bounding faces.
159 TopoDS_Face aFromFace = TopoDS::Face(theFromShape->impl<TopoDS_Shape>());
160 TopoDS_Face aToFace = TopoDS::Face(theToShape->impl<TopoDS_Shape>());
162 // Getting planes from bounding face.
163 GeomLib_IsPlanarSurface isFromPlanar(BRep_Tool::Surface(aFromFace));
164 GeomLib_IsPlanarSurface isToPlanar(BRep_Tool::Surface(aToFace));
165 if(!isFromPlanar.IsPlanar() || !isToPlanar.IsPlanar()) {// non-planar shapes is not supported for revolution bounding
168 gp_Pln aFromPln = isFromPlanar.Plan();
169 gp_Pln aToPln = isToPlanar.Plan();
171 // Orienting bounding planes properly so that the center of mass of the base face stays
172 // on the result shape after cut.
173 aFromFace = makeFaceFromPlane(aFromPln, aBaseCentre);
174 aToFace = makeFaceFromPlane(aToPln, aBaseCentre);
176 // Making solids from bounding planes and putting them in compound.
177 TopoDS_Shape aFromSolid = makeSolidFromShape(aFromFace);
178 TopoDS_Shape aToSolid = makeSolidFromShape(aToFace);
180 // Rotating bounding planes to the specified angle.
183 double aFromRotAngle = ((aFromPln.Axis().Direction() * aBasePln.Axis().Direction()) > 0) ? -theFromAngle : theFromAngle;
184 double aToRotAngle = ((aToPln.Axis().Direction() * aBasePln.Axis().Direction()) > 0) ? -theToAngle : theToAngle;
185 aFromTrsf.SetRotation(anAxis,aFromRotAngle / 180.0 * M_PI);
186 aToTrsf.SetRotation(anAxis, aToRotAngle / 180.0 * M_PI);
187 BRepBuilderAPI_Transform aFromTransform(aFromSolid, aFromTrsf, true);
188 BRepBuilderAPI_Transform aToTransform(aToSolid, aToTrsf, true);
189 TopoDS_Shape aRotatedFromFace = aFromTransform.Modified(aFromFace).First();
190 TopoDS_Shape aRotatedToFace = aToTransform.Modified(aToFace).First();
191 aFromSolid = aFromTransform.Shape();
192 aToSolid = aToTransform.Shape();
194 // Cutting revolution with from plane.
195 BRepAlgoAPI_Cut* aFromCutBuilder = new BRepAlgoAPI_Cut(aResult, aFromSolid);
196 aFromCutBuilder->Build();
197 if(!aFromCutBuilder->IsDone()) {
200 this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(new GeomAlgoAPI_MakeShape(aFromCutBuilder)));
201 aResult = aFromCutBuilder->Shape();
203 // Cutting revolution with to plane.
204 BRepAlgoAPI_Cut* aToCutBuilder = new BRepAlgoAPI_Cut(aResult, aToSolid);
205 aToCutBuilder->Build();
206 if(!aToCutBuilder->IsDone()) {
209 this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(new GeomAlgoAPI_MakeShape(aToCutBuilder)));
210 aResult = aToCutBuilder->Shape();
212 TopExp_Explorer anExp(aResult, TopAbs_SOLID);
216 if(aResult.ShapeType() == TopAbs_COMPOUND) {
217 aResult = GeomAlgoAPI_DFLoader::refineResult(aResult);
219 if(aResult.ShapeType() == TopAbs_COMPOUND) {
220 std::shared_ptr<GeomAPI_Shape> aCompound(new GeomAPI_Shape);
221 aCompound->setImpl(new TopoDS_Shape(aResult));
222 ListOfShape aCompSolids, aFreeSolids;
223 GeomAlgoAPI_ShapeTools::combineShapes(aCompound, GeomAPI_Shape::COMPSOLID, aCompSolids, aFreeSolids);
224 if(aCompSolids.size() == 1 && aFreeSolids.size() == 0) {
225 aResult = aCompSolids.front()->impl<TopoDS_Shape>();
226 } else if (aCompSolids.size() > 1 || (aCompSolids.size() >= 1 && aFreeSolids.size() >= 1)) {
227 TopoDS_Compound aResultComp;
228 TopoDS_Builder aBuilder;
229 aBuilder.MakeCompound(aResultComp);
230 for(ListOfShape::const_iterator anIter = aCompSolids.cbegin(); anIter != aCompSolids.cend(); anIter++) {
231 aBuilder.Add(aResultComp, (*anIter)->impl<TopoDS_Shape>());
233 for(ListOfShape::const_iterator anIter = aFreeSolids.cbegin(); anIter != aFreeSolids.cend(); anIter++) {
234 aBuilder.Add(aResultComp, (*anIter)->impl<TopoDS_Shape>());
236 aResult = aResultComp;
240 // If after cut we got more than one solids then take closest to the center of mass of the base face.
241 aResult = findClosest(aResult, aBaseCentre);
244 for(TopExp_Explorer anExp(aResult, TopAbs_FACE); anExp.More (); anExp.Next ()) {
245 const TopoDS_Shape& aFaceOnResult = anExp.Current();
246 Handle(Geom_Surface) aFaceSurface = BRep_Tool::Surface(TopoDS::Face(aFaceOnResult));
247 Handle(Geom_Surface) aFromSurface = BRep_Tool::Surface(TopoDS::Face(aRotatedFromFace));
248 Handle(Geom_Surface) aToSurface = BRep_Tool::Surface(TopoDS::Face(aRotatedToFace));
249 if(aFaceSurface == aFromSurface) {
250 std::shared_ptr<GeomAPI_Shape> aFSHape(new GeomAPI_Shape);
251 aFSHape->setImpl(new TopoDS_Shape(aFaceOnResult));
252 this->addFromFace(aFSHape);
254 if(aFaceSurface == aToSurface) {
255 std::shared_ptr<GeomAPI_Shape> aTSHape(new GeomAPI_Shape);
256 aTSHape->setImpl(new TopoDS_Shape(aFaceOnResult));
257 this->addToFace(aTSHape);
260 } else { //Case 3: When only one bounding plane was set.
261 // Making revolution to the 360 angle.
262 BRepPrimAPI_MakeRevol* aRevolBuilder = new BRepPrimAPI_MakeRevol(aBaseShape, anAxis, 2 * M_PI, Standard_True);
266 this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(new GeomAlgoAPI_MakeShape(aRevolBuilder)));
267 if(!aRevolBuilder->IsDone()) {
270 aResult = aRevolBuilder->Shape();
272 // Getting bounding face.
273 TopoDS_Face aBoundingFace;
274 bool isFromFaceSet = false;
276 aBoundingFace = TopoDS::Face(theFromShape->impl<TopoDS_Shape>());
277 isFromFaceSet = true;
278 } else if(theToShape) {
279 aBoundingFace = TopoDS::Face(theToShape->impl<TopoDS_Shape>());
282 // Getting plane from bounding face.
283 GeomLib_IsPlanarSurface isBoundingPlanar(BRep_Tool::Surface(aBoundingFace));
284 if(!isBoundingPlanar.IsPlanar()) { // non-planar shapes is not supported for revolution bounding
287 gp_Pln aBoundingPln = isBoundingPlanar.Plan();
289 // Orienting bounding plane properly so that the center of mass of the base face stays
290 // on the result shape after cut.
291 aBoundingFace = makeFaceFromPlane(aBoundingPln, aBaseCentre);
293 // Making solid from bounding plane.
294 TopoDS_Shape aBoundingSolid = makeSolidFromShape(aBoundingFace);
296 // Rotating bounding plane to the specified angle.
297 double aBoundingRotAngle = isFromFaceSet ? theFromAngle : theToAngle;
298 if(aBoundingPln.Axis().IsParallel(aBasePln.Axis(), Precision::Confusion())) {
299 if(isFromFaceSet) aBoundingRotAngle = -aBoundingRotAngle;
301 double aSign = (aBoundingPln.Axis().Direction() ^ aBasePln.Axis().Direction()) *
303 if((aSign <= 0 && !isFromFaceSet) || (aSign > 0 && isFromFaceSet)) {
304 aBoundingRotAngle = -aBoundingRotAngle;
307 gp_Trsf aBoundingTrsf;
308 aBoundingTrsf.SetRotation(anAxis, aBoundingRotAngle / 180.0 * M_PI);
309 BRepBuilderAPI_Transform aBoundingTransform(aBoundingSolid, aBoundingTrsf, true);
310 TopoDS_Shape aRotatedBoundingFace = aBoundingTransform.Modified(aBoundingFace).First();
311 aBoundingSolid = aBoundingTransform.Shape();
313 // Cutting revolution with bounding plane.
314 BRepAlgoAPI_Cut* aBoundingCutBuilder = new BRepAlgoAPI_Cut(aResult, aBoundingSolid);
315 aBoundingCutBuilder->Build();
316 if(!aBoundingCutBuilder->IsDone()) {
319 this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(new GeomAlgoAPI_MakeShape(aBoundingCutBuilder)));
320 aResult = aBoundingCutBuilder->Shape();
323 const TopTools_ListOfShape& aBndShapes = aBoundingCutBuilder->Modified(aBoundingFace);
324 for(TopTools_ListIteratorOfListOfShape anIt(aBndShapes); anIt.More(); anIt.Next()) {
325 std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape());
326 aShape->setImpl(new TopoDS_Shape(anIt.Value()));
327 isFromFaceSet ? this->addFromFace(aShape) : this->addToFace(aShape);
330 // Try to cut with base face. If it can not be done then keep result of cut with bounding plane.
331 TopoDS_Shape aModifiedBaseShape = aBaseShape;
333 if(aModifiedBaseShape.ShapeType() == TopAbs_FACE) {
334 aModifiedBaseShape.Orientation(TopAbs_REVERSED);
337 aMirrorTrsf.SetMirror(aBasePlane.Position().Ax2());
338 BRepBuilderAPI_Transform aMirrorTransform(aModifiedBaseShape, aMirrorTrsf, true);
339 aModifiedBaseShape = aMirrorTransform.Shape();
343 // Making solid from base face.
344 TopoDS_Shape aBaseSolid = makeSolidFromShape(aModifiedBaseShape);
346 // Rotating base face to the specified angle.
348 double aBaseRotAngle = isFromFaceSet ? theToAngle : -theFromAngle;
349 aBaseTrsf.SetRotation(anAxis, aBaseRotAngle / 180.0 * M_PI);
350 BRepBuilderAPI_Transform aBaseTransform(aBaseSolid, aBaseTrsf, true);
351 aBaseSolid = aBaseTransform.Shape();
353 // Cutting revolution with base.
354 BRepAlgoAPI_Cut* aBaseCutBuilder = new BRepAlgoAPI_Cut(aResult, aBaseSolid);
355 aBaseCutBuilder->Build();
356 if(aBaseCutBuilder->IsDone()) {
357 TopoDS_Shape aCutResult = aBaseCutBuilder->Shape();
358 TopExp_Explorer anExp(aCutResult, TopAbs_SOLID);
360 this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(new GeomAlgoAPI_MakeShape(aBaseCutBuilder)));
361 aResult = aCutResult;
365 const TopTools_ListOfShape& aBsShapes = aBaseCutBuilder->Modified(aBoundingFace);
366 for(TopTools_ListIteratorOfListOfShape anIt(aBsShapes); anIt.More(); anIt.Next()) {
367 std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape());
368 aShape->setImpl(new TopoDS_Shape(anIt.Value()));
369 isFromFaceSet ? this->addToFace(aShape) : this->addFromFace(aShape);
372 TopExp_Explorer anExp(aResult, TopAbs_SOLID);
376 if(aResult.ShapeType() == TopAbs_COMPOUND) {
377 aResult = GeomAlgoAPI_DFLoader::refineResult(aResult);
379 if(aResult.ShapeType() == TopAbs_COMPOUND) {
380 std::shared_ptr<GeomAPI_Shape> aCompound(new GeomAPI_Shape);
381 aCompound->setImpl(new TopoDS_Shape(aResult));
382 ListOfShape aCompSolids, aFreeSolids;
383 GeomAlgoAPI_ShapeTools::combineShapes(aCompound, GeomAPI_Shape::COMPSOLID, aCompSolids, aFreeSolids);
384 if(aCompSolids.size() == 1 && aFreeSolids.size() == 0) {
385 aResult = aCompSolids.front()->impl<TopoDS_Shape>();
386 } else if (aCompSolids.size() > 1 || (aCompSolids.size() >= 1 && aFreeSolids.size() >= 1)) {
387 TopoDS_Compound aResultComp;
388 TopoDS_Builder aBuilder;
389 aBuilder.MakeCompound(aResultComp);
390 for(ListOfShape::const_iterator anIter = aCompSolids.cbegin(); anIter != aCompSolids.cend(); anIter++) {
391 aBuilder.Add(aResultComp, (*anIter)->impl<TopoDS_Shape>());
393 for(ListOfShape::const_iterator anIter = aFreeSolids.cbegin(); anIter != aFreeSolids.cend(); anIter++) {
394 aBuilder.Add(aResultComp, (*anIter)->impl<TopoDS_Shape>());
396 aResult = aResultComp;
400 // If after cut we got more than one solids then take closest to the center of mass of the base face.
401 aResult = findClosest(aResult, aBaseCentre);
404 for(TopExp_Explorer anExp(aResult, TopAbs_FACE); anExp.More (); anExp.Next ()) {
405 const TopoDS_Shape& aFaceOnResult = anExp.Current();
406 Handle(Geom_Surface) aFaceSurface = BRep_Tool::Surface(TopoDS::Face(aFaceOnResult));
407 Handle(Geom_Surface) aBoundingSurface = BRep_Tool::Surface(TopoDS::Face(aRotatedBoundingFace));
408 if(aFaceSurface == aBoundingSurface) {
409 std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape());
410 aShape->setImpl(new TopoDS_Shape(aFaceOnResult));
411 isFromFaceSet ? this->addFromFace(aShape) : this->addToFace(aShape);
417 if(aResult.IsNull()) {
420 std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape());
421 aShape->setImpl(new TopoDS_Shape(aResult));
422 this->setShape(aShape);
426 //=================================================================================================
427 TopoDS_Face makeFaceFromPlane(gp_Pln& thePlane, const gp_Pnt& thePoint)
429 if(!thePlane.Contains(thePoint, Precision::Confusion())) {
430 gp_XYZ aVec = thePoint.XYZ() - thePlane.Location().XYZ();
431 double aSign = aVec * thePlane.Axis().Direction().XYZ();
432 if(aSign < 0) thePlane.SetAxis(thePlane.Axis().Reversed());
435 BRepBuilderAPI_MakeFace aMakeFace(thePlane);
436 TopoDS_Face aResultFace = TopoDS::Face(aMakeFace.Shape());
441 //=================================================================================================
442 TopoDS_Solid makeSolidFromShape(const TopoDS_Shape& theShape)
447 BRep_Builder aBoundingBuilder;
448 if(theShape.ShapeType() == TopAbs_SHELL) {
449 aShell = TopoDS::Shell(theShape);
451 aBoundingBuilder.MakeShell(aShell);
452 aBoundingBuilder.Add(aShell, theShape);
454 aBoundingBuilder.MakeSolid(aSolid);
455 aBoundingBuilder.Add(aSolid, aShell);
460 //=================================================================================================
461 TopoDS_Shape findClosest(const TopoDS_Shape& theShape, const gp_Pnt& thePoint)
463 TopoDS_Shape aResult = theShape;
465 if(theShape.ShapeType() == TopAbs_COMPOUND) {
466 double aMinDistance = Precision::Infinite();
468 GProp_GProps aGProps;
471 for (TopoDS_Iterator anItr(theShape); anItr.More(); anItr.Next()) {
472 TopoDS_Shape aValue = anItr.Value();
473 BRepGProp::VolumeProperties(aValue, aGProps);
474 aCentr = aGProps.CentreOfMass();
475 aCurDistance = aCentr.Distance(thePoint);
477 if(aCurDistance < aMinDistance) {
478 aMinDistance = aCurDistance;