Salome HOME
50b2ef2b16cc004b3d1c5d3b197f87962958cd21
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_Prism.cpp
1 // Copyright (C) 2014-2020  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "GeomAlgoAPI_Prism.h"
21
22 #include <GeomAPI_Ax1.h>
23 #include <GeomAPI_Dir.h>
24 #include <GeomAPI_Face.h>
25 #include <GeomAPI_Pln.h>
26 #include <GeomAPI_Pnt.h>
27 #include <GeomAPI_ShapeExplorer.h>
28 #include <GeomAPI_XYZ.h>
29
30 #include <GeomAlgoAPI_CompoundBuilder.h>
31 #include <GeomAlgoAPI_DFLoader.h>
32 #include <GeomAlgoAPI_FaceBuilder.h>
33 #include <GeomAlgoAPI_Offset.h>
34 #include <GeomAlgoAPI_Partition.h>
35 #include <GeomAlgoAPI_ShapeTools.h>
36 #include <GeomAlgoAPI_Translation.h>
37
38 #include <Bnd_Box.hxx>
39 #include <BRep_Builder.hxx>
40 #include <BRepAlgoAPI_Cut.hxx>
41 #include <BRepBndLib.hxx>
42 #include <BRepBuilderAPI_FindPlane.hxx>
43 #include <BRepBuilderAPI_Transform.hxx>
44 #include <BRepTools.hxx>
45 #include <Geom_Curve.hxx>
46 #include <Geom2d_Curve.hxx>
47 #include <BRepLib_CheckCurveOnSurface.hxx>
48 #include <BRepPrimAPI_MakePrism.hxx>
49 #include <Geom_Plane.hxx>
50 #include <Geom_RectangularTrimmedSurface.hxx>
51 #include <gp_Pln.hxx>
52 #include <IntAna_IntConicQuad.hxx>
53 #include <IntAna_Quadric.hxx>
54 #include <IntTools_Context.hxx>
55 #include <TopExp_Explorer.hxx>
56 #include <TopoDS.hxx>
57 #include <TopoDS_Edge.hxx>
58 #include <TopoDS_Shell.hxx>
59 #include <TopoDS_Solid.hxx>
60 #include <TopTools_ListIteratorOfListOfShape.hxx>
61
62
63 /// Expand planar face to cover the bounding box if theOriginalShape is planar.
64 /// Otherwise, return the same shape;
65 static GeomShapePtr buildPlanarFace(const GeomShapePtr& theOriginalShape,
66                                     const Bnd_Box& theBaseShapeBB);
67
68 /// Build offset for the given shape.
69 /// If the offset algorithm failed, translate the shape along the direction.
70 static GeomShapePtr buildOffset(const GeomShapePtr& theShape,
71                                 const double theOffset,
72                                 const GeomDirPtr theDirection,
73                                 GeomAlgoAPI_MakeShapeList& theMakeShapeList);
74
75 /// Collect base faces of the prism.
76 static void collectPrismBases(const TopoDS_Shape& theBaseShape,
77                               BRepPrimAPI_MakePrism& thePrismAlgo,
78                               ListOfShape& theBoundaries,
79                               const GeomAPI_Shape::ShapeType theTypeToExp);
80
81 /// Collect all solids which contain boundaries but do not contain bases of prism.
82 static GeomShapePtr collectResults(const GeomMakeShapePtr& theOperation,
83                                    const ListOfShape& theBoundaries,
84                                    const ListOfShape& theShapesToExclude,
85                                    const GeomAPI_Shape::ShapeType theTypeToExp);
86
87 static void storeGenerationHistory(GeomAlgoAPI_Prism* thePrismAlgo,
88                                    const TopoDS_Shape& theBase,
89                                    const TopAbs_ShapeEnum theType,
90                                    BRepPrimAPI_MakePrism* thePrismBuilder);
91
92 static void storeGenerationHistory(GeomAlgoAPI_Prism* thePrismAlgo,
93                                    const TopoDS_Shape& theResult,
94                                    const TopAbs_ShapeEnum theType,
95                                    const TopoDS_Face& theToFace,
96                                    const TopoDS_Face& theFromFace);
97
98 static GeomShapePtr toShape(const TopoDS_Shape& theShape)
99 {
100   GeomShapePtr aShape(new GeomAPI_Shape());
101   aShape->setImpl(new TopoDS_Shape(theShape));
102   return aShape;
103 }
104
105 static void changeOrientationIfNeeded(const TopoDS_Shape& theShape, gp_Vec& theNormal)
106 {
107   TopExp_Explorer anExp(theShape, TopAbs_VERTEX);
108   gp_Pnt aPnt0 = BRep_Tool::Pnt(TopoDS::Vertex(anExp.Current()));
109   gp_Dir aDir01;
110   for (anExp.Next(); anExp.More(); anExp.Next()) {
111     gp_Pnt aPnt1 = BRep_Tool::Pnt(TopoDS::Vertex(anExp.Current()));
112     if (aPnt1.SquareDistance(aPnt0) > Precision::SquareConfusion()) {
113       aDir01 = gp_Dir(gp_Vec(aPnt0, aPnt1));
114       break;
115     }
116   }
117   gp_Vec aNormal;
118   for (; anExp.More(); anExp.Next()) {
119     gp_Pnt aPnt2 = BRep_Tool::Pnt(TopoDS::Vertex(anExp.Current()));
120     if (aPnt2.SquareDistance(aPnt0) > Precision::SquareConfusion()) {
121       aNormal = gp_Vec(aDir01) ^ gp_Vec(aPnt0, aPnt2);
122       if (aNormal.SquareMagnitude() > Precision::SquareConfusion())
123         break;
124     }
125   }
126   if (anExp.More() && aNormal.XYZ().Dot(theNormal.XYZ()) < -Precision::Confusion()) {
127     // directions differ, reverse the normal
128     theNormal.Reverse();
129   }
130 }
131
132 //==================================================================================================
133 GeomAlgoAPI_Prism::GeomAlgoAPI_Prism(const GeomShapePtr theBaseShape,
134                                      const GeomDirPtr   theDirection,
135                                      const GeomShapePtr theToShape,
136                                      const double       theToSize,
137                                      const GeomShapePtr theFromShape,
138                                      const double       theFromSize)
139 {
140   if(!theBaseShape.get() ||
141     (((!theFromShape.get() && !theToShape.get()) ||
142     (theFromShape.get() && theToShape.get() && theFromShape->isEqual(theToShape)))
143     && (theFromSize == -theToSize))) {
144     return;
145   }
146
147   // Getting base shape.
148   const TopoDS_Shape& aBaseShape = theBaseShape->impl<TopoDS_Shape>();
149   GeomAPI_Shape::ShapeType aShapeTypeToExp;
150   switch(aBaseShape.ShapeType()) {
151     case TopAbs_VERTEX:
152       aShapeTypeToExp = GeomAPI_Shape::VERTEX;
153       break;
154     case TopAbs_EDGE:
155     case TopAbs_WIRE:
156       aShapeTypeToExp = GeomAPI_Shape::EDGE;
157       break;
158     case TopAbs_FACE:
159     case TopAbs_SHELL:
160       aShapeTypeToExp = GeomAPI_Shape::FACE;
161       break;
162     case TopAbs_COMPOUND:
163       aShapeTypeToExp = GeomAPI_Shape::COMPOUND;
164       break;
165     default:
166       return;
167   }
168
169   // Getting direction.
170   gp_Vec aBaseVec;
171   std::shared_ptr<GeomAPI_Pnt> aBaseLoc;
172   std::shared_ptr<GeomAPI_Dir> aBaseDir;
173   BRepBuilderAPI_FindPlane aFindPlane(aBaseShape);
174   if(aFindPlane.Found() == Standard_True)
175   {
176     bool checkOrientation = false;
177     Handle(Geom_Plane) aPlane;
178     if(aBaseShape.ShapeType() == TopAbs_FACE || aBaseShape.ShapeType() == TopAbs_SHELL) {
179       TopExp_Explorer anExp(aBaseShape, TopAbs_FACE);
180       const TopoDS_Shape& aFace = anExp.Current();
181       Handle(Geom_Surface) aSurface = BRep_Tool::Surface(TopoDS::Face(aFace));
182       if(aSurface->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface)) {
183         Handle(Geom_RectangularTrimmedSurface) aTrimSurface =
184           Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurface);
185         aSurface = aTrimSurface->BasisSurface();
186       }
187       if(aSurface->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
188         return;
189       }
190       aPlane = Handle(Geom_Plane)::DownCast(aSurface);
191     } else {
192       aPlane = aFindPlane.Plane();
193       checkOrientation = true;
194     }
195     gp_Pnt aLoc = aPlane->Axis().Location();
196     aBaseVec = aPlane->Axis().Direction();
197
198     if (checkOrientation) {
199       // to stabilize the result of algorithm, if base shape is a wire, compare the orientation
200       // of calculated plane with the normal vector got iterating on vertices
201       changeOrientationIfNeeded(aBaseShape, aBaseVec);
202     }
203
204     aBaseLoc.reset(new GeomAPI_Pnt(aLoc.X(), aLoc.Y(), aLoc.Z()));
205     aBaseDir.reset(new GeomAPI_Dir(aBaseVec.X(), aBaseVec.Y(), aBaseVec.Z()));
206   }
207   else if (theDirection.get())
208   {
209     aBaseDir = theDirection;
210     aBaseVec = theDirection->impl<gp_Dir>();
211   }
212   else
213   {
214     return;
215   }
216
217   if(!aBaseLoc.get()) {
218     gp_Pnt aLoc;
219     gp_XYZ aDirXYZ = aBaseVec.XYZ();
220     Standard_Real aMinParam = Precision::Infinite();
221     for(TopExp_Explorer anExp(aBaseShape, TopAbs_VERTEX); anExp.More(); anExp.Next()) {
222       const TopoDS_Shape& aVertex = anExp.Current();
223       gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aVertex));
224       double aParam = aDirXYZ.Dot(aPnt.XYZ());
225       if(aParam < aMinParam) {
226         aMinParam = aParam;
227         aLoc = aPnt;
228       }
229     }
230     aBaseLoc.reset(new GeomAPI_Pnt(aLoc.X(), aLoc.Y(), aLoc.Z()));
231   }
232
233   gp_Vec anExtVec;
234   std::shared_ptr<GeomAPI_Dir> anExtDir;
235   if (theDirection.get())
236   {
237     anExtDir = theDirection;
238     anExtVec = theDirection->impl<gp_Dir>();
239   }
240   else
241   {
242     anExtDir = aBaseDir;
243     anExtVec = aBaseDir->impl<gp_Dir>();
244   }
245
246
247   TopoDS_Shape aResult;
248   const bool isBoundingShapesSet = theFromShape.get() || theToShape.get();
249   if(!isBoundingShapesSet) {
250     buildBySizes(theBaseShape, anExtDir, theToSize, theFromSize, aShapeTypeToExp);
251   } else {
252     GeomShapePtr aBasePlane = GeomAlgoAPI_FaceBuilder::squareFace(aBaseLoc, aBaseDir, 100.0);
253
254     GeomShapePtr aBoundingFromShape = theFromShape ? theFromShape : aBasePlane;
255     GeomShapePtr aBoundingToShape   = theToShape   ? theToShape   : aBasePlane;
256
257     bool isFromShapePlanar = aBoundingFromShape->isPlanar();
258     bool isToShapePlanar   = aBoundingToShape->isPlanar();
259
260     // Set signs of offsets if both bounding shapes are planar
261     if (isFromShapePlanar && isToShapePlanar) {
262       std::shared_ptr<GeomAPI_Pln> aFromPln = GeomAPI_Face(aBoundingFromShape).getPlane();
263       std::shared_ptr<GeomAPI_Pln> aToPln = GeomAPI_Face(aBoundingToShape).getPlane();
264       buildByPlanes(theBaseShape, anExtDir,
265                     aToPln, theToSize,
266                     aFromPln, theFromSize,
267                     aShapeTypeToExp);
268     }
269     else {
270       buildByFaces(theBaseShape, anExtDir,
271                    aBoundingToShape, theToSize, isToShapePlanar,
272                    aBoundingFromShape, theFromSize, isFromShapePlanar,
273                    aShapeTypeToExp);
274     }
275   }
276 }
277
278 //==================================================================================================
279 void GeomAlgoAPI_Prism::buildBySizes(const GeomShapePtr             theBaseShape,
280                                      const GeomDirPtr               theDirection,
281                                      const double                   theToSize,
282                                      const double                   theFromSize,
283                                      const GeomAPI_Shape::ShapeType theTypeToExp)
284 {
285   const TopoDS_Shape& aBaseShape = theBaseShape->impl<TopoDS_Shape>();
286   gp_Vec anExtVec = theDirection->impl<gp_Dir>();
287
288   // Moving base shape.
289   gp_Trsf aTrsf;
290   aTrsf.SetTranslation(anExtVec * -theFromSize);
291   BRepBuilderAPI_Transform* aTransformBuilder =
292       new BRepBuilderAPI_Transform(aBaseShape, aTrsf);
293   if (!aTransformBuilder || !aTransformBuilder->IsDone()) {
294     return;
295   }
296   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
297       new GeomAlgoAPI_MakeShape(aTransformBuilder)));
298   TopoDS_Shape aMovedBase = aTransformBuilder->Shape();
299
300   // Making prism.
301   BRepPrimAPI_MakePrism* aPrismBuilder =
302       new BRepPrimAPI_MakePrism(aMovedBase, anExtVec * (theFromSize + theToSize));
303   if (!aPrismBuilder || !aPrismBuilder->IsDone()) {
304     return;
305   }
306   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
307       new GeomAlgoAPI_MakeShape(aPrismBuilder)));
308   TopoDS_Shape aResult = aPrismBuilder->Shape();
309
310   // Setting naming.
311   if(theTypeToExp == GeomAPI_Shape::COMPOUND) {
312     storeGenerationHistory(this, aMovedBase, TopAbs_EDGE, aPrismBuilder);
313     storeGenerationHistory(this, aMovedBase, TopAbs_FACE, aPrismBuilder);
314   } else {
315     storeGenerationHistory(this, aMovedBase, (TopAbs_ShapeEnum)theTypeToExp, aPrismBuilder);
316   }
317
318   // Setting result.
319   if (!aResult.IsNull()) {
320     aResult = GeomAlgoAPI_DFLoader::refineResult(aResult);
321     this->setShape(toShape(aResult));
322     this->setDone(true);
323   }
324 }
325
326 //==================================================================================================
327 void GeomAlgoAPI_Prism::buildByPlanes(const GeomShapePtr             theBaseShape,
328                                       const GeomDirPtr               theDirection,
329                                       const GeomPlanePtr             theToPlane,
330                                       const double                   theToSize,
331                                       const GeomPlanePtr             theFromPlane,
332                                       const double                   theFromSize,
333                                       const GeomAPI_Shape::ShapeType theTypeToExp)
334 {
335   const TopoDS_Shape& aBaseShape = theBaseShape->impl<TopoDS_Shape>();
336   gp_Vec anExtVec = theDirection->impl<gp_Dir>();
337
338   // Moving prism bounding faces according to "from" and "to" sizes.
339   std::shared_ptr<GeomAPI_Pnt> aFromLoc = theFromPlane->location();
340   std::shared_ptr<GeomAPI_Dir> aFromDir = theFromPlane->direction();
341
342   std::shared_ptr<GeomAPI_Pnt> aToLoc = theToPlane->location();
343   std::shared_ptr<GeomAPI_Dir> aToDir = theToPlane->direction();
344
345   std::shared_ptr<GeomAPI_XYZ> anExtDir = theDirection->xyz();
346   bool aSign = aFromLoc->xyz()->dot(anExtDir) > aToLoc->xyz()->dot(anExtDir);
347
348   std::shared_ptr<GeomAPI_Pnt> aFromPnt(
349     new GeomAPI_Pnt(aFromLoc->xyz()->added(anExtDir->multiplied(
350                     aSign ? theFromSize : -theFromSize))));
351
352   std::shared_ptr<GeomAPI_Pnt> aToPnt(
353     new GeomAPI_Pnt(aToLoc->xyz()->added(anExtDir->multiplied(
354                     aSign ? -theToSize : theToSize))));
355
356   // Getting bounding box for base shape.
357   Bnd_Box aBndBox;
358   BRepBndLib::Add(aBaseShape, aBndBox);
359   Standard_Real aXArr[2] = {aBndBox.CornerMin().X(), aBndBox.CornerMax().X()};
360   Standard_Real aYArr[2] = {aBndBox.CornerMin().Y(), aBndBox.CornerMax().Y()};
361   Standard_Real aZArr[2] = {aBndBox.CornerMin().Z(), aBndBox.CornerMax().Z()};
362   gp_Pnt aPoints[8];
363   int aNum = 0;
364   for(int i = 0; i < 2; i++) {
365     for(int j = 0; j < 2; j++) {
366       for(int k = 0; k < 2; k++) {
367         aPoints[aNum] = gp_Pnt(aXArr[i], aYArr[j], aZArr[k]);
368         aNum++;
369       }
370     }
371   }
372
373   // Project points to bounding planes. Search max distance to them.
374   IntAna_Quadric aBndToQuadric(gp_Pln(aToPnt->impl<gp_Pnt>(), aToDir->impl<gp_Dir>()));
375   IntAna_Quadric aBndFromQuadric(gp_Pln(aFromPnt->impl<gp_Pnt>(), aFromDir->impl<gp_Dir>()));
376   Standard_Real aMaxToDist = 0, aMaxFromDist = 0;
377   for(int i = 0; i < 8; i++) {
378     gp_Lin aLine(aPoints[i], anExtVec);
379     IntAna_IntConicQuad aToIntAna(aLine, aBndToQuadric);
380     IntAna_IntConicQuad aFromIntAna(aLine, aBndFromQuadric);
381     if(aToIntAna.NbPoints() == 0 || aFromIntAna.NbPoints() == 0) {
382       return;
383     }
384     const gp_Pnt& aPntOnToFace = aToIntAna.Point(1);
385     const gp_Pnt& aPntOnFromFace = aFromIntAna.Point(1);
386     if(aPoints[i].Distance(aPntOnToFace) > aMaxToDist) {
387       aMaxToDist = aPoints[i].Distance(aPntOnToFace);
388     }
389     if(aPoints[i].Distance(aPntOnFromFace) > aMaxFromDist) {
390       aMaxFromDist = aPoints[i].Distance(aPntOnFromFace);
391     }
392   }
393
394   // We added 1 just to be sure that prism is long enough for boolean operation.
395   double aPrismLength = aMaxToDist + aMaxFromDist + 1;
396
397   // Moving base shape.
398   gp_Trsf aTrsf;
399   aTrsf.SetTranslation(anExtVec * -aPrismLength);
400   BRepBuilderAPI_Transform* aTransformBuilder = new BRepBuilderAPI_Transform(aBaseShape, aTrsf);
401   if(!aTransformBuilder || !aTransformBuilder->IsDone()) {
402     return;
403   }
404   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
405     new GeomAlgoAPI_MakeShape(aTransformBuilder)));
406   TopoDS_Shape aMovedBase = aTransformBuilder->Shape();
407
408   // Making prism.
409   BRepPrimAPI_MakePrism* aPrismBuilder =
410     new BRepPrimAPI_MakePrism(aMovedBase, anExtVec * 2 * aPrismLength);
411   if(!aPrismBuilder || !aPrismBuilder->IsDone()) {
412     return;
413   }
414   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
415     new GeomAlgoAPI_MakeShape(aPrismBuilder)));
416   TopoDS_Shape aResult = aPrismBuilder->Shape();
417
418   BRepBndLib::Add(aResult, aBndBox);
419   Standard_Real aBndBoxSize = aBndBox.CornerMin().Distance(aBndBox.CornerMax());
420
421   // Orienting bounding planes.
422   std::shared_ptr<GeomAPI_Pnt> aCentreOfMass = GeomAlgoAPI_ShapeTools::centreOfMass(theBaseShape);
423   const gp_Pnt& aCentrePnt = aCentreOfMass->impl<gp_Pnt>();
424   gp_Lin aLine(aCentrePnt, anExtVec);
425   IntAna_IntConicQuad aToIntAna(aLine, aBndToQuadric);
426   IntAna_IntConicQuad aFromIntAna(aLine, aBndFromQuadric);
427   Standard_Real aToParameter = aToIntAna.ParamOnConic(1);
428   Standard_Real aFromParameter = aFromIntAna.ParamOnConic(1);
429   if(aToParameter > aFromParameter) {
430     gp_Vec aVec = aToDir->impl<gp_Dir>();
431     if((aVec * anExtVec) > 0)
432       aToDir->setImpl(new gp_Dir(aVec.Reversed()));
433     aVec = aFromDir->impl<gp_Dir>();
434     if((aVec * anExtVec) < 0)
435       aFromDir->setImpl(new gp_Dir(aVec.Reversed()));
436   } else {
437     gp_Vec aVec = aToDir->impl<gp_Dir>();
438     if((aVec * anExtVec) < 0)
439       aToDir->setImpl(new gp_Dir(aVec.Reversed()));
440     aVec = aFromDir->impl<gp_Dir>();
441     if((aVec * anExtVec) > 0)
442       aFromDir->setImpl(new gp_Dir(aVec.Reversed()));
443   }
444
445   static const double THE_FACE_SIZE_COEFF = 10.0;
446   GeomShapePtr aBoundingFromShape =
447       GeomAlgoAPI_FaceBuilder::squareFace(aFromPnt, aFromDir, THE_FACE_SIZE_COEFF * aBndBoxSize);
448   GeomShapePtr aBoundingToShape =
449       GeomAlgoAPI_FaceBuilder::squareFace(aToPnt, aToDir, THE_FACE_SIZE_COEFF * aBndBoxSize);
450
451   // bounding planes
452   const TopoDS_Shape& aToShape   = aBoundingToShape->impl<TopoDS_Shape>();
453   const TopoDS_Shape& aFromShape = aBoundingFromShape->impl<TopoDS_Shape>();
454   TopoDS_Face aToFace   = TopoDS::Face(aToShape);
455   TopoDS_Face aFromFace = TopoDS::Face(aFromShape);
456
457   // Solid based on "To" bounding plane
458   gp_Vec aNormal = aToDir->impl<gp_Dir>();
459   BRepPrimAPI_MakePrism* aToPrismBuilder =
460       new BRepPrimAPI_MakePrism(aToShape, aNormal * (-2.0 * aBndBoxSize));
461   if (!aToPrismBuilder || !aToPrismBuilder->IsDone()) {
462     return;
463   }
464   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
465     new GeomAlgoAPI_MakeShape(aToPrismBuilder)));
466   TopoDS_Shape aToSolid = aToPrismBuilder->Shape();
467
468   // Cutting with to plane.
469   BRepAlgoAPI_Cut* aToCutBuilder = new BRepAlgoAPI_Cut(aResult, aToSolid);
470   aToCutBuilder->Build();
471   if(!aToCutBuilder->IsDone()) {
472     return;
473   }
474   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
475     new GeomAlgoAPI_MakeShape(aToCutBuilder)));
476   aResult = aToCutBuilder->Shape();
477   if(aResult.ShapeType() == TopAbs_COMPOUND) {
478     aResult = GeomAlgoAPI_DFLoader::refineResult(aResult);
479   }
480   if (theTypeToExp == GeomAPI_Shape::FACE || theTypeToExp == GeomAPI_Shape::COMPOUND) {
481     TopTools_ListOfShape aPrismShapes = aToPrismBuilder->Modified(aToShape);
482     if (aPrismShapes.IsEmpty())
483       aPrismShapes.Append(aToShape);
484     for (TopTools_ListIteratorOfListOfShape anIt1(aPrismShapes); anIt1.More(); anIt1.Next()) {
485       const TopTools_ListOfShape& aToShapes = aToCutBuilder->Modified(anIt1.Value());
486       for (TopTools_ListIteratorOfListOfShape anIt2(aToShapes); anIt2.More(); anIt2.Next()) {
487         GeomShapePtr aGeomSh = toShape(anIt2.Value());
488         fixOrientation(aGeomSh);
489         this->addToShape(aGeomSh);
490       }
491     }
492   }
493
494   // Solid based on "From" bounding plane
495   aNormal = aFromDir->impl<gp_Dir>();
496   BRepPrimAPI_MakePrism* aFromPrismBuilder =
497       new BRepPrimAPI_MakePrism(aFromShape, aNormal * (-2.0 * aBndBoxSize));
498   if (!aFromPrismBuilder || !aFromPrismBuilder->IsDone()) {
499     return;
500   }
501   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
502     new GeomAlgoAPI_MakeShape(aFromPrismBuilder)));
503   TopoDS_Shape aFromSolid = aFromPrismBuilder->Shape();
504
505   // Cutting with from plane.
506   BRepAlgoAPI_Cut* aFromCutBuilder = new BRepAlgoAPI_Cut(aResult, aFromSolid);
507   aFromCutBuilder->Build();
508   if(!aFromCutBuilder->IsDone()) {
509     return;
510   }
511   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
512     new GeomAlgoAPI_MakeShape(aFromCutBuilder)));
513   aResult = aFromCutBuilder->Shape();
514   TopoDS_Iterator aCheckIt(aResult);
515   if(!aCheckIt.More()) {
516     return;
517   }
518   if(aResult.ShapeType() == TopAbs_COMPOUND) {
519     aResult = GeomAlgoAPI_DFLoader::refineResult(aResult);
520   }
521   if (theTypeToExp == GeomAPI_Shape::FACE || theTypeToExp == GeomAPI_Shape::COMPOUND) {
522     TopTools_ListOfShape aPrismShapes = aFromPrismBuilder->Modified(aFromShape);
523     if (aPrismShapes.IsEmpty())
524       aPrismShapes.Append(aFromShape);
525     for (TopTools_ListIteratorOfListOfShape anIt1(aPrismShapes); anIt1.More(); anIt1.Next()) {
526       const TopTools_ListOfShape& aFromShapes = aFromCutBuilder->Modified(anIt1.Value());
527       for (TopTools_ListIteratorOfListOfShape anIt2(aFromShapes); anIt2.More(); anIt2.Next()) {
528         GeomShapePtr aGeomSh = toShape(anIt2.Value());
529         fixOrientation(aGeomSh);
530         this->addFromShape(aGeomSh);
531       }
532     }
533   }
534
535   // Naming for extrusion from vertex, edge.
536   if(theTypeToExp == GeomAPI_Shape::COMPOUND) {
537     storeGenerationHistory(this, aResult, TopAbs_EDGE, aToFace, aFromFace);
538     storeGenerationHistory(this, aResult, TopAbs_FACE, aToFace, aFromFace);
539   } else {
540     storeGenerationHistory(this, aResult, (TopAbs_ShapeEnum)theTypeToExp, aToFace, aFromFace);
541   }
542
543   if(aResult.ShapeType() == TopAbs_COMPOUND) {
544     GeomShapePtr aGeomShape = toShape(aResult);
545     ListOfShape aResults;
546     aGeomShape = GeomAlgoAPI_ShapeTools::combineShapes(aGeomShape,
547                                                         GeomAPI_Shape::COMPSOLID,
548                                                         aResults);
549     aResult = aGeomShape->impl<TopoDS_Shape>();
550   }
551
552   // Setting result.
553   if (!aResult.IsNull()) {
554     aResult = GeomAlgoAPI_DFLoader::refineResult(aResult);
555     this->setShape(toShape(aResult));
556     this->setDone(true);
557   }
558 }
559
560 //==================================================================================================
561 void GeomAlgoAPI_Prism::buildByFaces(const GeomShapePtr             theBaseShape,
562                                      const GeomDirPtr               theDirection,
563                                      const GeomShapePtr             theToShape,
564                                      const double                   theToSize,
565                                      const bool                     theToIsPlanar,
566                                      const GeomShapePtr             theFromShape,
567                                      const double                   theFromSize,
568                                      const bool                     theFromIsPlanar,
569                                      const GeomAPI_Shape::ShapeType theTypeToExp)
570 {
571   const TopoDS_Shape& aBaseShape = theBaseShape->impl<TopoDS_Shape>();
572   gp_Vec anExtVec = theDirection->impl<gp_Dir>();
573
574   // Moving prism bounding faces according to "from" and "to" sizes.
575   GeomShapePtr aBoundingFromShape = buildOffset(theFromShape, -theFromSize, theDirection, *this);
576   GeomShapePtr aBoundingToShape   = buildOffset(theToShape, theToSize, theDirection, *this);
577
578   // Bounding box for shapes used in prism building.
579   Bnd_Box aBndBox;
580   BRepBndLib::Add(aBaseShape, aBndBox);
581   BRepBndLib::Add(aBoundingFromShape->impl<TopoDS_Shape>(), aBndBox);
582   BRepBndLib::Add(aBoundingToShape->impl<TopoDS_Shape>(), aBndBox);
583   double aPrismLength = 2.0 * aBndBox.CornerMin().Distance(aBndBox.CornerMax());
584
585   // Prism building.
586   gp_Trsf aTrsf;
587   aTrsf.SetTranslation(anExtVec * -aPrismLength);
588   BRepBuilderAPI_Transform* aTransformBuilder = new BRepBuilderAPI_Transform(aBaseShape, aTrsf);
589   if (!aTransformBuilder || !aTransformBuilder->IsDone()) {
590     return;
591   }
592   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
593       new GeomAlgoAPI_MakeShape(aTransformBuilder)));
594   TopoDS_Shape aMovedBase = aTransformBuilder->Shape();
595
596   // Making prism.
597   BRepPrimAPI_MakePrism* aPrismBuilder =
598       new BRepPrimAPI_MakePrism(aMovedBase, anExtVec * 2 * aPrismLength);
599   if (!aPrismBuilder || !aPrismBuilder->IsDone()) {
600     return;
601   }
602   this->appendAlgo(std::shared_ptr<GeomAlgoAPI_MakeShape>(
603       new GeomAlgoAPI_MakeShape(aPrismBuilder)));
604
605   GeomShapePtr aResult = toShape(aPrismBuilder->Shape());
606
607   // Prism generatrix
608   ListOfShape aPrismBaseFaces;
609   collectPrismBases(aMovedBase, *aPrismBuilder, aPrismBaseFaces, theTypeToExp);
610
611   // Build planar faces intersecting the prism fully.
612   BRepBndLib::Add(aResult->impl<TopoDS_Shape>(), aBndBox);
613   aBoundingFromShape = buildPlanarFace(aBoundingFromShape, aBndBox);
614   aBoundingToShape   = buildPlanarFace(aBoundingToShape, aBndBox);
615
616   // Perform partition.
617   ListOfShape anObjects, aTools;
618   anObjects.push_back(aResult);
619   aTools.push_back(aBoundingFromShape);
620   aTools.push_back(aBoundingToShape);
621
622   GeomMakeShapePtr aPartition(new GeomAlgoAPI_Partition(anObjects, aTools));
623   if (!aPartition->isDone())
624     return;
625
626   this->appendAlgo(aPartition);
627
628   // Collect pieces of boundary shapes, split by Partition.
629   if (theFromIsPlanar) {
630     ListOfShape anImagesFrom;
631     aPartition->modified(aBoundingFromShape, anImagesFrom);
632     for (ListOfShape::iterator anIt = anImagesFrom.begin(); anIt != anImagesFrom.end(); ++anIt)
633       addFromShape(*anIt);
634   }
635
636   if (theToIsPlanar) {
637     ListOfShape anImagesTo;
638     aPartition->modified(aBoundingToShape, anImagesTo);
639     for (ListOfShape::iterator anIt = anImagesTo.begin(); anIt != anImagesTo.end(); ++anIt)
640       addToShape(*anIt);
641   }
642
643   // Collect results which have both boundaries, selected for extrusion,
644   // but which do not contain top and bottom faces of the prism
645   // (these faces are treated as infinitely distant).
646   aResult = collectResults(aPartition, aTools, aPrismBaseFaces, theTypeToExp);
647   if (aResult && aResult->shapeType() == GeomAPI_Shape::COMPOUND) {
648     ListOfShape aResults;
649     aResult = GeomAlgoAPI_ShapeTools::combineShapes(aResult,
650         theTypeToExp == GeomAPI_Shape::EDGE ? GeomAPI_Shape::SHELL : GeomAPI_Shape::COMPSOLID,
651         aResults);
652
653     if (aResults.size() > 1 &&
654        (GeomAlgoAPI_ShapeTools::hasSharedTopology(aResults, GeomAPI_Shape::EDGE) ||
655         GeomAlgoAPI_ShapeTools::hasSharedTopology(aResults, GeomAPI_Shape::VERTEX))) {
656       // results shuold not have shared topology
657       aResult = GeomShapePtr();
658     }
659   }
660
661   if (aResult) {
662     this->setShape(aResult);
663     this->setDone(true);
664   }
665 }
666
667
668 // Auxilary functions:
669 //==================================================================================================
670 GeomShapePtr buildPlanarFace(const GeomShapePtr& theOriginalShape,
671                              const Bnd_Box& theBaseShapeBB)
672 {
673   GeomPlanePtr aPlane = GeomAPI_Face(theOriginalShape).getPlane();
674   if (!aPlane)
675     return theOriginalShape;
676
677   gp_Pnt aCornerMin = theBaseShapeBB.CornerMin();
678   gp_Pnt aCornerMax = theBaseShapeBB.CornerMax();
679   double aSize = aCornerMin.SquareDistance(aCornerMax);
680
681   gp_Pnt aLocation = aPlane->location()->impl<gp_Pnt>();
682
683   gp_Pnt aCurPnt;
684   for (int x = 0; x < 2; ++x) {
685     aCurPnt.SetX(x == 0 ? aCornerMin.X() : aCornerMax.X());
686     for (int y = 0; y < 2; ++y) {
687       aCurPnt.SetY(y == 0 ? aCornerMin.Y() : aCornerMax.Y());
688       for (int z = 0; z < 2; ++z) {
689         aCurPnt.SetZ(z == 0 ? aCornerMin.Z() : aCornerMax.Z());
690         double aDist = aCurPnt.SquareDistance(aLocation);
691         if (aDist > aSize)
692           aSize = aDist;
693       }
694     }
695   }
696
697   aSize = Sqrt(aSize);
698   return GeomAlgoAPI_FaceBuilder::squareFace(aPlane, 2.0 * aSize);
699 }
700
701 //==================================================================================================
702 GeomShapePtr buildOffset(const GeomShapePtr& theShape,
703                          const double theOffset,
704                          const GeomDirPtr theDirection,
705                          GeomAlgoAPI_MakeShapeList& theMakeShapeList)
706 {
707   if (Abs(theOffset) < Precision::Confusion())
708     return theShape; // no need zero offset
709
710   GeomMakeShapePtr anAlgo(new GeomAlgoAPI_Offset(theShape, theOffset));
711   if (!anAlgo->isDone()) {
712     // offset not done, perform translation
713     std::shared_ptr<GeomAPI_Ax1> anAxis(new GeomAPI_Ax1());
714     anAxis->setDir(theDirection);
715     anAlgo.reset(new GeomAlgoAPI_Translation(theShape, anAxis, theOffset));
716   }
717
718   GeomShapePtr aResult = theShape;
719   if (anAlgo->isDone()) {
720     theMakeShapeList.appendAlgo(anAlgo);
721     aResult = anAlgo->shape();
722   }
723   return aResult;
724 }
725
726 //==================================================================================================
727 void collectPrismBases(const TopoDS_Shape& theBaseShape,
728                        BRepPrimAPI_MakePrism& thePrismAlgo,
729                        ListOfShape& theBoundaries,
730                        const GeomAPI_Shape::ShapeType theTypeToExp)
731 {
732   for (TopExp_Explorer anExp(theBaseShape, (TopAbs_ShapeEnum)theTypeToExp);
733        anExp.More(); anExp.Next()) {
734     theBoundaries.push_back(toShape(thePrismAlgo.FirstShape(anExp.Current())));
735     theBoundaries.push_back(toShape(thePrismAlgo.LastShape(anExp.Current())));
736   }
737 }
738
739 //==================================================================================================
740 typedef std::set<GeomShapePtr, GeomAPI_Shape::Comparator> SetOfShape;
741
742 bool isShapeApplicable(const GeomShapePtr& theSolid,
743                        const std::list<ListOfShape>& theShapesToExist,
744                        const SetOfShape& theShapesToExclude,
745                        const GeomAPI_Shape::ShapeType theTypeToExp)
746 {
747   SetOfShape aFaces;
748   for (GeomAPI_ShapeExplorer aFExp(theSolid, theTypeToExp);
749        aFExp.more(); aFExp.next()) {
750     GeomShapePtr aCurrent = aFExp.current();
751     if (theShapesToExclude.find(aCurrent) != theShapesToExclude.end())
752       return false;
753     aFaces.insert(aCurrent);
754   }
755
756   // check all faces are in solid
757   bool isApplicable = true;
758   for (std::list<ListOfShape>::const_iterator it1 = theShapesToExist.begin();
759        it1 != theShapesToExist.end() && isApplicable; ++it1) {
760     ListOfShape::const_iterator it2 = it1->begin();
761     for (; it2 != it1->end(); ++it2)
762       if (aFaces.find(*it2) != aFaces.end())
763         break;
764     isApplicable = it2 != it1->end();
765   }
766   return isApplicable;
767 }
768
769 void collectModified(const GeomMakeShapePtr& theOperation,
770                      const ListOfShape& theShapes,
771                      std::list<ListOfShape>& theModified)
772 {
773   for (ListOfShape::const_iterator anIt = theShapes.begin();
774        anIt != theShapes.end(); ++anIt) {
775     theModified.push_back(ListOfShape());
776     theOperation->modified(*anIt, theModified.back());
777     theOperation->generated(*anIt, theModified.back());
778     theModified.back().push_back(*anIt);
779   }
780 }
781
782 GeomShapePtr collectResults(const GeomMakeShapePtr& theOperation,
783                             const ListOfShape& theBoundaries,
784                             const ListOfShape& theShapesToExclude,
785                             const GeomAPI_Shape::ShapeType theTypeToExp)
786 {
787   ListOfShape aResults;
788
789   // collect modified shapes
790   std::list<ListOfShape> aModifiedBoundaries;
791   collectModified(theOperation, theBoundaries, aModifiedBoundaries);
792
793   std::list<ListOfShape> aModifiedExclude;
794   collectModified(theOperation, theShapesToExclude, aModifiedExclude);
795   SetOfShape aTabooShapes;
796   for (std::list<ListOfShape>::iterator anIt = aModifiedExclude.begin();
797        anIt != aModifiedExclude.end(); ++anIt)
798     aTabooShapes.insert(anIt->begin(), anIt->end());
799
800   // type of sub-shapes to explode
801   GeomAPI_Shape::ShapeType aSubshapeType;
802   switch (theTypeToExp) {
803   case GeomAPI_Shape::VERTEX:
804     aSubshapeType = GeomAPI_Shape::EDGE;
805     break;
806   case GeomAPI_Shape::EDGE:
807     aSubshapeType = GeomAPI_Shape::FACE;
808     break;
809   case GeomAPI_Shape::FACE:
810     aSubshapeType = GeomAPI_Shape::SOLID;
811     break;
812   default:
813     aSubshapeType = GeomAPI_Shape::COMPOUND;
814   }
815
816   // search applicable solids
817   GeomShapePtr anOperationResult = theOperation->shape();
818   for (GeomAPI_ShapeExplorer anExp(anOperationResult, aSubshapeType);
819         anExp.more(); anExp.next()) {
820     if (isShapeApplicable(anExp.current(), aModifiedBoundaries, aTabooShapes, theTypeToExp))
821       aResults.push_back(anExp.current());
822   }
823
824   GeomShapePtr aResult;
825   if (aResults.size() == 1)
826     aResult = aResults.front();
827   else if (!aResults.empty())
828     aResult = GeomAlgoAPI_CompoundBuilder::compound(aResults);
829   return aResult;
830 }
831
832 //==================================================================================================
833 void storeGenerationHistory(GeomAlgoAPI_Prism* thePrismAlgo,
834                             const TopoDS_Shape& theBase,
835                             const TopAbs_ShapeEnum theType,
836                             BRepPrimAPI_MakePrism* thePrismBuilder)
837 {
838   for(TopExp_Explorer anExp(theBase, theType); anExp.More(); anExp.Next()) {
839     const TopoDS_Shape& aShape = anExp.Current();
840     GeomShapePtr aFromShape(new GeomAPI_Shape), aToShape(new GeomAPI_Shape);
841     aFromShape->setImpl(new TopoDS_Shape(thePrismBuilder->FirstShape(aShape)));
842     aToShape->setImpl(new TopoDS_Shape(thePrismBuilder->LastShape(aShape)));
843     thePrismAlgo->fixOrientation(aFromShape);
844     thePrismAlgo->fixOrientation(aToShape);
845     thePrismAlgo->addFromShape(aFromShape);
846     thePrismAlgo->addToShape(aToShape);
847   }
848 }
849
850 //==================================================================================================
851 void storeGenerationHistory(GeomAlgoAPI_Prism* thePrismAlgo,
852                             const TopoDS_Shape& theResult,
853                             const TopAbs_ShapeEnum theType,
854                             const TopoDS_Face& theToFace,
855                             const TopoDS_Face& theFromFace)
856 {
857   for(TopExp_Explorer anExp(theResult, theType); anExp.More(); anExp.Next()) {
858     const TopoDS_Shape& aShape = anExp.Current();
859     GeomShapePtr aGeomSh(new GeomAPI_Shape());
860     if(theType == TopAbs_VERTEX) {
861       gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aShape));
862       IntTools_Context anIntTools;
863       if(anIntTools.IsValidPointForFace(aPnt,
864           theToFace, Precision::Confusion()) == Standard_True) {
865         aGeomSh->setImpl(new TopoDS_Shape(aShape));
866         thePrismAlgo->fixOrientation(aGeomSh);
867         thePrismAlgo->addToShape(aGeomSh);
868       }
869       if(anIntTools.IsValidPointForFace(aPnt,
870           theFromFace, Precision::Confusion()) == Standard_True) {
871         aGeomSh->setImpl(new TopoDS_Shape(aShape));
872         thePrismAlgo->fixOrientation(aGeomSh);
873         thePrismAlgo->addFromShape(aGeomSh);
874       }
875     } else if(theType == TopAbs_EDGE) {
876       TopoDS_Edge anEdge = TopoDS::Edge(aShape);
877       BRepLib_CheckCurveOnSurface anEdgeCheck(anEdge, theToFace);
878       anEdgeCheck.Perform();
879       if(anEdgeCheck.MaxDistance() < Precision::Confusion()) {
880         aGeomSh->setImpl(new TopoDS_Shape(aShape));
881         thePrismAlgo->fixOrientation(aGeomSh);
882         thePrismAlgo->addToShape(aGeomSh);
883       }
884       anEdgeCheck.Init(anEdge, theFromFace);
885       anEdgeCheck.Perform();
886       if(anEdgeCheck.MaxDistance() < Precision::Confusion()) {
887         aGeomSh->setImpl(new TopoDS_Shape(aShape));
888         thePrismAlgo->fixOrientation(aGeomSh);
889         thePrismAlgo->addFromShape(aGeomSh);
890       }
891     } else {
892       break;
893     }
894   }
895 }