Salome HOME
Merge branch 'BR_H2018_3' into BR_2018_V8_5
[modules/hydro.git] / src / HYDROData / HYDROData_PolylineOperator.cxx
1 // Copyright (C) 2014-2015  EDF-R&D
2 // This library is free software; you can redistribute it and/or
3 // modify it under the terms of the GNU Lesser General Public
4 // License as published by the Free Software Foundation; either
5 // version 2.1 of the License, or (at your option) any later version.
6 //
7 // This library is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 // Lesser General Public License for more details.
11 //
12 // You should have received a copy of the GNU Lesser General Public
13 // License along with this library; if not, write to the Free Software
14 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
15 //
16 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
17 //
18
19 #include <HYDROData_PolylineOperator.h>
20 #include <HYDROData_Document.h>
21 #include <HYDROData_TopoCurve.h>
22 #include <HYDROData_Object.h>
23
24 #ifndef LIGHT_MODE
25 #include <CurveCreator_Utils.hxx>
26 #endif
27
28 #include <BRepAdaptor_Curve.hxx>
29 #include <BRep_Builder.hxx>
30 #include <BRep_Tool.hxx>
31 #include <BRepBuilderAPI_MakeEdge2d.hxx>
32 #include <BRepBuilderAPI_MakeEdge.hxx>
33 #include <BRepBuilderAPI_MakeWire.hxx>
34 #include <Extrema_ExtCC.hxx>
35 #include <Extrema_ExtPC.hxx>
36 #include <GeomAPI_Interpolate.hxx>
37 #include <NCollection_Vector.hxx>
38 #include <Precision.hxx>
39 #include <ShapeAnalysis_TransferParametersProj.hxx>
40 #include <ShapeBuild_Edge.hxx>
41 #include <TColgp_Array1OfVec.hxx>
42 #include <TColgp_HArray1OfPnt.hxx>
43 #include <TColStd_HArray1OfBoolean.hxx>
44 #include <TopoDS.hxx>
45 #include <TopoDS_Edge.hxx>
46 #include <TopoDS_Wire.hxx>
47 #include <TopExp.hxx>
48 #include <TopExp_Explorer.hxx>
49 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
50 #include <QStringList>
51 #include <QColor>
52 #include <Geom_BSplineCurve.hxx>
53 #include <TopTools_IndexedMapOfShape.hxx>
54 #include <BRepLib_MakeWire.hxx>
55
56 //#define _DEVDEBUG_
57 #include "HYDRO_trace.hxx"
58 #include <BRepTools.hxx>
59 #include <sstream>
60
61 template<class T> void append( std::vector<T>& theList, const std::vector<T>& theList2 )
62 {
63   int aSize = theList.size();
64   int aNewSize = aSize + theList2.size();
65
66   if( aSize==aNewSize )
67     return;
68
69   theList.resize( aNewSize );
70   for( int i=aSize, j=0; i<aNewSize; i++, j++ )
71     theList[i] = theList2[j];
72 }
73
74 bool HYDROData_PolylineOperator::Split( const Handle( HYDROData_Document )& theDoc,
75                                         const Handle( HYDROData_PolylineXY )& thePolyline,
76                                         const gp_Pnt2d& thePoint,
77                                         double theTolerance ) const
78 {
79   if (thePolyline.IsNull())
80   {
81     return false;
82   }
83
84   std::vector<gp_Pnt2d> aPointsList( 1 );
85   aPointsList[0] = thePoint;
86   std::vector<TopoDS_Wire> aCurves;
87   GetWires(thePolyline, aCurves);
88   bool isOK = true;
89
90   std::vector<QColor> aSectColors;
91   int nbSec = thePolyline->NbSections();
92   for (int i = 0; i < nbSec; i++)
93   {
94     QColor aColor;
95     thePolyline->GetSectionColor(i, aColor);
96     aSectColors.push_back(aColor);
97   }
98
99   bool bsetColor = aCurves.size() == aSectColors.size();
100   for( int i=0, n=aCurves.size(); i<n; i++ )
101   {
102     std::vector<TopoDS_Shape> aCurvesList;
103     Split( aCurves[i], thePoint, theTolerance, aCurvesList );
104     NCollection_IndexedDataMap<Handle(HYDROData_PolylineXY), TopoDS_Shape> outPoly2Sh;
105     bool isLocalOK = CreatePolylines( theDoc, thePolyline, aCurvesList, true, outPoly2Sh );
106     isOK = isOK && isLocalOK;
107     if (bsetColor)
108     {
109       QColor aColor = aSectColors[i];
110       if( aColor.isValid() )
111       {
112         for (int j=1;j<=outPoly2Sh.Extent();j++)
113         {
114           const Handle(HYDROData_PolylineXY)& Poly = outPoly2Sh.FindKey(j); 
115           int nbSec = Poly->NbSections();
116           for (int k = 0; k < nbSec; k++)
117             Poly->SetSectionColor(k, aColor);
118         }
119       }
120     }
121   }
122
123   return isOK;
124 }
125
126 bool HYDROData_PolylineOperator::Split( const Handle( HYDROData_Document )& theDoc,
127               const Handle( HYDROData_PolylineXY )& thePolyline,
128               const Handle( HYDROData_PolylineXY )& theTool,
129               double theTolerance,
130               bool& theIsIntersected) const
131 {
132   if (thePolyline.IsNull() || theTool.IsNull())
133   {
134     return false;
135   }
136
137   HYDROData_SequenceOfObjects aSeq;
138   aSeq.Append( theTool );
139   return split( theDoc, thePolyline, aSeq, theTolerance, -1, theIsIntersected);
140 }
141
142 bool HYDROData_PolylineOperator::Split( const Handle( HYDROData_Document )& theDoc,
143                                         const HYDROData_SequenceOfObjects& thePolylines,
144                                         double theTolerance )
145 {
146   int f = thePolylines.Lower(), l = thePolylines.Upper();
147   for( int i=f; i<=l; i++ )
148   {
149     Handle( HYDROData_PolylineXY ) aPolyline = Handle( HYDROData_PolylineXY )::DownCast( thePolylines.Value( i ) );
150     bool isIntersected;
151     if( !split( theDoc, aPolyline, thePolylines, theTolerance, i, isIntersected) )
152       return false;
153   }
154   return true;
155 }
156
157 bool HYDROData_PolylineOperator::Merge( const Handle( HYDROData_Document )& theDoc,
158                                         const QString& theName,
159                                         const HYDROData_SequenceOfObjects& thePolylines,
160                                         bool isConnectByNewSegment,
161                                         double theTolerance )
162 {
163   Handle( HYDROData_PolylineXY ) aNewPolyline =
164     Handle( HYDROData_PolylineXY )::DownCast( theDoc->CreateObject( KIND_POLYLINEXY ) );
165   int ins =0;
166
167   HYDROData_SequenceOfObjects::Iterator aPIt(thePolylines);
168
169   for (int ip=1; aPIt.More(); aPIt.Next(), ip++)
170   {
171     Handle(HYDROData_PolylineXY) aPolyline =
172       Handle(HYDROData_PolylineXY)::DownCast(aPIt.Value());
173     NCollection_Sequence<TCollection_AsciiString>           aSectNames;
174     NCollection_Sequence<HYDROData_PolylineXY::SectionType> aSectTypes;
175     NCollection_Sequence<bool>                              aSectClosures;
176     aPolyline->GetSections( aSectNames, aSectTypes, aSectClosures );
177
178     for ( int i = 1, n = aSectNames.Size(); i <= n; ++i )
179     {
180       const TCollection_AsciiString& aSectName = aSectNames.Value( i ) + "_" + ip;
181       const HYDROData_PolylineXY::SectionType& aSectType = aSectTypes.Value( i );
182       bool aSectClosure = aSectClosures.Value( i );
183       aNewPolyline->AddSection(aSectName, aSectType, aSectClosure);
184       HYDROData_PolylineXY::PointsList aPointsList = aPolyline->GetPoints(i-1, false);
185       aNewPolyline->SetPoints(ins++, aPointsList);
186     }
187   }
188   QString aName = theName;
189   if( aName.isEmpty() )
190     {
191       aName = "merged";
192       int anIndex = 1;
193       QString aNewName = aName + "_" + QString::number(anIndex);
194       while (!theDoc->FindObjectByName(aNewName).IsNull())  // the object with such a name is not found
195         {
196           anIndex++;
197           aNewName = aName + "_" + QString::number(anIndex);
198         }
199       aName = aNewName;
200     }
201
202   aNewPolyline->SetName(aName);
203   aNewPolyline->Update();
204   return true;
205 }
206
207 bool HYDROData_PolylineOperator::split( const Handle( HYDROData_Document )& theDoc,
208                                         const Handle( HYDROData_PolylineXY )& thePolyline,
209                                         const HYDROData_SequenceOfObjects& theTools,
210                                         double theTolerance,
211                                         int theIgnoreIndex,
212                                         bool& theIsIntersected) const
213 {
214   theIsIntersected = false;
215
216   if (thePolyline.IsNull())
217   {
218     return false;
219   }
220
221   std::vector<TopoDS_Wire> aCurves;
222   GetWires(thePolyline, aCurves);
223   std::vector<TopoDS_Wire> aToolCurves;
224   for( int i=theTools.Lower(), n=theTools.Upper(); i<=n; i++ )
225     if( i!=theIgnoreIndex )
226     {
227       Handle( HYDROData_PolylineXY ) aToolPolyline = 
228         Handle( HYDROData_PolylineXY )::DownCast( theTools.Value( i ) );
229       if (!aToolPolyline.IsNull())
230       {
231         std::vector<TopoDS_Wire> aTCurves;
232         GetWires(aToolPolyline, aTCurves);
233         append( aToolCurves, aTCurves);
234       }
235     }
236
237   if (aToolCurves.empty())
238   {
239     return false;
240   }
241
242   std::vector<QColor> aSectColors;
243   int nbSec = thePolyline->NbSections();
244
245
246
247   const int aPSCount = aCurves.size();
248   const int aTSCount = aToolCurves.size();
249   std::vector<TopoDS_Shape> aResult;
250
251   bool bSetColor = aPSCount == nbSec;
252   if (bSetColor)
253   {
254     for (int i = 0; i < nbSec; i++)
255     {
256       QColor aColor;
257       thePolyline->GetSectionColor(i, aColor);
258       aSectColors.push_back(aColor);
259     }
260   }
261   NCollection_DataMap <TopoDS_Shape, QColor, TopTools_ShapeMapHasher> W2Color;
262
263   for (int aPSI = 0; aPSI < aPSCount; ++aPSI)
264   {
265     HYDROData_TopoCurve aCurve;
266     DEBTRACE("Initialize curve " << aPSI);
267     if (!aCurve.Initialize(aCurves[aPSI]))
268     {
269       continue;
270     }
271
272     std::deque<std::list<double> > aParams;
273     for (int aTSI = 0; aTSI < aTSCount; ++aTSI)
274     {
275       aCurve.Intersect(aToolCurves[aTSI], aParams);
276     }
277
278     std::deque<HYDROData_TopoCurve> aSplitCurves;
279     theIsIntersected |= aCurve.Cut(aParams, aSplitCurves);
280     std::deque<HYDROData_TopoCurve>::const_iterator aCIt =
281       aSplitCurves.begin();
282     std::deque<HYDROData_TopoCurve>::const_iterator aLastCIt =
283       aSplitCurves.end();
284     for (int iw=0; aCIt != aLastCIt; ++aCIt, iw++)
285     {
286       //std::stringstream brepName;
287       //brepName << "theSplitWire_";
288       //brepName << iw;
289       //brepName << ".brep";
290       //BRepTools::Write(aCIt->Wire() , brepName.str().c_str() );
291       const TopoDS_Wire& aW = aCIt->Wire();
292       if (bSetColor)
293         W2Color.Bind(aW, aSectColors[aPSI]);
294       aResult.push_back(aW);
295     }
296   }
297
298   NCollection_IndexedDataMap<Handle(HYDROData_PolylineXY), TopoDS_Shape> outPoly2Sh;
299   CreatePolylines( theDoc, thePolyline, aResult, true, outPoly2Sh );
300   
301   for (int j=1;j<=outPoly2Sh.Extent();j++)
302   {
303     const Handle(HYDROData_PolylineXY)& Poly = outPoly2Sh.FindKey(j); 
304     const TopoDS_Shape& Sh = outPoly2Sh.FindFromIndex(j); 
305
306     int nbSec = Poly->NbSections();
307     for (int k = 0; k < nbSec; k++)
308     {
309       const QColor* color = W2Color.Seek(Sh);
310       if (color)
311         Poly->SetSectionColor(k, *color);
312     }
313   }
314   
315   return true;
316 }
317
318 void HYDROData_PolylineOperator::GetWires(
319   const Handle( HYDROData_PolylineXY )& thePolyline,
320   std::vector<TopoDS_Wire>& theWires)
321 {
322   TopoDS_Shape aShape = thePolyline->GetShape();
323   if (aShape.IsNull())
324     return;
325   if( aShape.ShapeType()==TopAbs_WIRE )
326   {
327     theWires.push_back( TopoDS::Wire( aShape ) );
328   }
329   else
330   {
331     TopExp_Explorer anExp( aShape, TopAbs_WIRE );
332     for( ; anExp.More(); anExp.Next() )
333     {
334       theWires.push_back( TopoDS::Wire( anExp.Current() ) );
335     }
336   }
337 }
338
339 void HYDROData_PolylineOperator::Split(
340   const TopoDS_Wire& theWire,
341   const gp_Pnt2d& thePoint,
342   double theTolerance,
343   std::vector<TopoDS_Shape>& theWires)
344 {
345   HYDROData_TopoCurve aCurve;
346   if (!aCurve.Initialize(theWire))
347   {
348     theWires.push_back(theWire);
349     return;
350   }
351
352   const gp_XYZ aP(thePoint.X(), thePoint.Y(), 0);
353   std::list<TopoDS_Edge>::const_iterator aEPos;
354   double aParam;
355   aCurve.Project(aP, aEPos, aParam);
356   HYDROData_TopoCurve aCurve1, aCurve2;
357   aCurve.Cut(aEPos, aParam, aCurve1, aCurve2);
358   theWires.push_back(aCurve1.Wire());
359   if (!aCurve2.IsEmpty())
360   {
361     theWires.push_back(aCurve2.Wire());
362   }
363 }
364
365 bool HYDROData_PolylineOperator::CreatePolylines( const Handle( HYDROData_Document )& theDoc,
366                                                   const Handle( HYDROData_PolylineXY )& theOldPolyline,
367                                                   const std::vector<TopoDS_Shape>& theShapes,
368                                                   bool isUseIndices,
369                                                   NCollection_IndexedDataMap<Handle(HYDROData_PolylineXY), TopoDS_Shape>& outPoly2Sh)
370 {
371   if( theDoc.IsNull() )
372     return false;
373
374   if ( theOldPolyline.IsNull() )
375     return false;
376   const QString& theNamePrefix = theOldPolyline->GetName();
377
378   int n = theShapes.size();
379   DEBTRACE("theShapes.size() "<< n);
380   int anIndex = 1;
381   for( int i=0; i<n; i++ )
382   {
383     Handle( HYDROData_PolylineXY ) aPolyline = 
384       Handle( HYDROData_PolylineXY )::DownCast( theDoc->CreateObject( KIND_POLYLINEXY ) );
385     if( aPolyline.IsNull() )
386       return false;
387
388     aPolyline->ImportShape(theShapes[i], false, theOldPolyline, false);
389
390     if( isUseIndices )
391     {
392       QString aNewName = theNamePrefix + "_" + QString::number( anIndex );
393       while( !theDoc->FindObjectByName( aNewName ).IsNull() )  // the object with such a name is not found
394       {
395         anIndex++;
396         aNewName = theNamePrefix + "_" + QString::number( anIndex );
397       }
398       aPolyline->SetName( aNewName );
399     }
400     else
401     {
402       aPolyline->SetName( theNamePrefix );
403     }
404
405     outPoly2Sh.Add(aPolyline, theShapes[i]);
406   }
407   return true;
408 }
409
410 double HYDROData_PolylineOperator::ReduceDeflection(
411   const double theDeflection,
412   HYDROData_TopoCurve& theCurve,
413   int& thePieceCount)
414 {
415   // Construct the approximating B-spline.
416   std::list<gp_XYZ> aPs;
417   if (!theCurve.ValuesInKnots(aPs))
418   {
419     return -1;
420   }
421
422   Handle(TColgp_HArray1OfPnt) aPs2 = new TColgp_HArray1OfPnt(1, aPs.size());
423   {
424     std::list<gp_XYZ>::const_iterator aLastPIt = aPs.end();
425     std::list<gp_XYZ>::const_iterator aPIt = aPs.begin();
426     for (int aPN = 1; aPIt != aLastPIt; ++aPN, ++aPIt)
427     {
428       aPs2->SetValue(aPN, *aPIt);
429     }
430   }
431   Handle(Geom_BSplineCurve) aBSpline2;
432   const bool isClosed = theCurve.IsClosed();
433 #ifndef LIGHT_MODE
434   if (!CurveCreator_Utils::constructBSpline(aPs2, isClosed, aBSpline2))
435 #endif
436   {
437     return -1;
438   }
439
440   // Calculate the piece deflections.
441   std::deque<double> aSqDefls;
442   double aMaxSqDefl = 0;
443   std::list<TopoDS_Edge>& aEdges = theCurve.Edges();
444   std::list<TopoDS_Edge>::const_iterator aLastEIt = aEdges.end();
445   {
446     std::list<TopoDS_Edge>::const_iterator aEIt = aEdges.begin();
447     for (int aPrevKCount = 0; aEIt != aLastEIt; ++aEIt)
448     {
449       TopLoc_Location aLoc;
450       double aParams[2];
451       Handle(Geom_BSplineCurve) aBSpline = Handle(Geom_BSplineCurve)::DownCast(
452         BRep_Tool::Curve(*aEIt, aLoc, aParams[0], aParams[1]));
453       const int aKCount = aBSpline->NbKnots();
454       for (int aKN = 1; aKN < aKCount; ++aKN)
455       {
456         const double aParam =
457           (aBSpline->Knot(aKN) + aBSpline->Knot(aKN + 1)) * 0.5;
458         const double aParam2 = (aBSpline2->Knot(aPrevKCount + aKN) +
459           aBSpline2->Knot(aPrevKCount + aKN + 1)) * 0.5;
460         const double aSqDefl = Abs(aBSpline->Value(aParam).
461           SquareDistance(aBSpline2->Value(aParam2)));
462         aSqDefls.push_back(aSqDefl);
463         if (aMaxSqDefl < aSqDefl)
464         {
465           aMaxSqDefl = aSqDefl;
466         }
467       }
468       aPrevKCount += aKCount - 1;
469     }
470   }
471
472   // Check whether the reducing is necessary.
473   const double aMaxDefl = Sqrt(aMaxSqDefl);
474   if (aMaxDefl <= theDeflection)
475   {
476     return aMaxDefl;
477   }
478
479   // Reduce the deflections.
480   const double aThresSqDefl =
481     Max(aMaxSqDefl * 0.25, theDeflection * theDeflection);
482   std::list<TopoDS_Edge>::iterator aEIt = aEdges.begin();
483   std::deque<double>::const_iterator aSqDIt = aSqDefls.begin();
484   thePieceCount = 0;
485   for (; aEIt != aLastEIt; ++aEIt)
486   {
487     TopLoc_Location aLoc;
488     double aParams[2];
489     Handle(Geom_BSplineCurve) aBSpline = Handle(Geom_BSplineCurve)::DownCast(
490       BRep_Tool::Curve(*aEIt, aLoc, aParams[0], aParams[1]));
491     Handle(Geom_BSplineCurve) aBSpline2 =
492       Handle(Geom_BSplineCurve)::DownCast(aBSpline->Copy());
493     const int aKCount = aBSpline->NbKnots();
494     for (int aKN = 1; aKN < aKCount; ++aSqDIt, ++aKN)
495     {
496       if (*aSqDIt > aThresSqDefl)
497       {
498         aBSpline2->InsertKnot(
499           (aBSpline->Knot(aKN) + aBSpline->Knot(aKN + 1)) * 0.5);
500       }
501     }
502     TopoDS_Edge aEdge;
503     BRep_Builder().MakeEdge(aEdge, aBSpline2, Precision::Confusion());
504     BRep_Builder().Add(aEdge, TopExp::FirstVertex(*aEIt));
505     BRep_Builder().Add(aEdge, TopExp::LastVertex(*aEIt));
506     thePieceCount += aBSpline2->NbKnots() - 1;
507     *aEIt = aEdge;
508   }
509   return aMaxDefl;
510 }
511
512 bool HYDROData_PolylineOperator::Extract( const Handle(HYDROData_Document)& theDocument,
513                                           const Handle(HYDROData_Object)& theObject )
514 {
515   if( theObject.IsNull() || theDocument.IsNull() )
516     return false;
517
518   QList<TopoDS_Shape> aBoundShapes;
519   QStringList aBoundNames;
520   QMap<QString, TopTools_IndexedMapOfShape> aNameToShMap;
521
522   theObject->GetBoundaries( aBoundShapes, aBoundNames );
523
524   for( int i=0, n=aBoundShapes.size(); i<n; i++ )
525   {
526     TopoDS_Shape aShape = aBoundShapes[i];
527     if( aShape.IsNull() )
528       continue;
529
530     QString aBoundName = i<aBoundNames.size() ? aBoundNames[i] : "";
531     
532     if (!aNameToShMap.contains(aBoundName))
533     {
534       TopTools_IndexedMapOfShape IM;
535       IM.Add(aShape);
536       aNameToShMap[aBoundName] = IM;
537     }
538     else
539       aNameToShMap[aBoundName].Add(aShape);
540      
541   }
542
543   foreach( QString K, aNameToShMap.keys() )
544   {
545     const TopTools_IndexedMapOfShape& IM = aNameToShMap.value(K);
546     TopTools_ListOfShape LSE;
547     for (int i = 1; i <= IM.Extent(); i++)
548     {
549       const TopoDS_Edge& E = TopoDS::Edge(IM(i));
550       if (E.IsNull())
551         continue;
552       LSE.Append(E);
553     }
554
555
556     TopoDS_Shape aShapeOut;
557     if (LSE.Extent() == 1)
558     {
559       aShapeOut = LSE.First();
560     }
561     else if (LSE.Extent() > 1)
562     {
563       BRepLib_MakeWire WM;
564       WM.Add(LSE);
565       if (WM.IsDone())
566         aShapeOut = WM.Wire();
567       else
568         continue;
569     }
570     else continue;
571
572     Handle( HYDROData_PolylineXY ) aPolyline = 
573       Handle( HYDROData_PolylineXY )::DownCast( theDocument->CreateObject( KIND_POLYLINEXY ) );
574
575     if( aPolyline.IsNull() )
576       return false;
577
578     aPolyline->SetShape( aShapeOut );
579
580     int anIndex = 0;
581     QString aName = K;
582     while( !theDocument->FindObjectByName( aName ).IsNull() )
583     {
584       anIndex++;
585       aName = K + "_" + QString::number( anIndex );
586     }
587     aPolyline->SetName( aName );
588   }
589
590   return true;
591 }