Salome HOME
Merge branch 'BR_v14_rc' of ssh://git.salome-platform.org/modules/hydro into HEAD
[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 <BRepAdaptor_Curve.hxx>
22 #include <BRep_Builder.hxx>
23 #include <BRepBuilderAPI_MakeEdge2d.hxx>
24 #include <BRepBuilderAPI_MakeWire.hxx>
25 #include <Extrema_ExtPC.hxx>
26 #include <NCollection_Vector.hxx>
27 #include <Precision.hxx>
28 #include <ShapeAnalysis_TransferParametersProj.hxx>
29 #include <ShapeBuild_Edge.hxx>
30 #include <TopoDS.hxx>
31 #include <TopoDS_Edge.hxx>
32 #include <TopoDS_Wire.hxx>
33 #include <TopExp.hxx>
34 #include <TopExp_Explorer.hxx>
35 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
36 #include <QString>
37
38 template<class T> void append( std::vector<T>& theList, const std::vector<T>& theList2 )
39 {
40   int aSize = theList.size();
41   int aNewSize = aSize + theList2.size();
42
43   if( aSize==aNewSize )
44     return;
45
46   theList.resize( aNewSize );
47   for( int i=aSize, j=0; i<aNewSize; i++, j++ )
48     theList[i] = theList2[j];
49 }
50
51 bool HYDROData_PolylineOperator::Split( const Handle( HYDROData_Document )& theDoc,
52                                         const Handle( HYDROData_PolylineXY )& thePolyline,
53                                         const gp_Pnt2d& thePoint,
54                                         double theTolerance ) const
55 {
56   std::vector<gp_Pnt2d> aPointsList( 1 );
57   aPointsList[0] = thePoint;
58   std::vector<TopoDS_Wire> aCurves = GetWires( thePolyline );
59   bool isOK = true;
60   for( int i=0, n=aCurves.size(); i<n; i++ )
61   {
62     std::vector<TopoDS_Shape> aCurvesList = Split( aCurves[i], thePoint, theTolerance );
63     bool isLocalOK = CreatePolylines( theDoc, thePolyline->GetName(), aCurvesList, true );
64     isOK = isOK && isLocalOK;
65   }
66   return isOK;
67 }
68
69 bool HYDROData_PolylineOperator::Split( const Handle( HYDROData_Document )& theDoc,
70               const Handle( HYDROData_PolylineXY )& thePolyline,
71               const Handle( HYDROData_PolylineXY )& theTool,
72               double theTolerance ) const
73 {
74   HYDROData_SequenceOfObjects aSeq;
75   aSeq.Append( theTool );
76   return split( theDoc, thePolyline, aSeq, theTolerance, -1 );
77 }
78
79 bool HYDROData_PolylineOperator::Split( const Handle( HYDROData_Document )& theDoc,
80                                         const HYDROData_SequenceOfObjects& thePolylines,
81                                         double theTolerance )
82 {
83   int f = thePolylines.Lower(), l = thePolylines.Upper();
84   for( int i=f; i<=l; i++ )
85   {
86     Handle( HYDROData_PolylineXY ) aPolyline = Handle( HYDROData_PolylineXY )::DownCast( thePolylines.Value( i ) );
87     if( !split( theDoc, aPolyline, thePolylines, theTolerance, i ) )
88       return false;
89   }
90   return true;
91 }
92
93 bool HYDROData_PolylineOperator::Merge( const Handle( HYDROData_Document )& theDoc,
94                                         const QString& theName,
95                                         const HYDROData_SequenceOfObjects& thePolylines,
96                                         bool isConnectByNewSegment,
97                                         double theTolerance )
98 {
99   TopoDS_Shape aMergedPolyline;
100
101   int f = thePolylines.Lower(), l = thePolylines.Upper();
102   for( int i=f; i<=l; i++ )
103   {
104     Handle( HYDROData_PolylineXY ) aPolyline = Handle( HYDROData_PolylineXY )::DownCast( thePolylines.Value( i ) );
105     std::vector<TopoDS_Wire> aCurves = GetWires( aPolyline );
106     for( int j=0, m=aCurves.size(); j<m; j++ )
107       if( !Merge( aMergedPolyline, aCurves[j], isConnectByNewSegment, theTolerance ) )
108         return false;
109   }
110
111   std::vector<TopoDS_Shape> aShapes( 1 );
112   aShapes[0] = aMergedPolyline;
113   CreatePolylines( theDoc, theName, aShapes, false );
114
115   return true;
116 }
117
118 bool HYDROData_PolylineOperator::Merge( TopoDS_Shape& theShape, const TopoDS_Wire& theWire, 
119                                         bool isConnectByNewSegment, double theTolerance )
120 {
121   //TODO
122   return true;
123 }
124
125 bool HYDROData_PolylineOperator::split( const Handle( HYDROData_Document )& theDoc,
126                                         const Handle( HYDROData_PolylineXY )& thePolyline,
127                                         const HYDROData_SequenceOfObjects& theTools,
128                                         double theTolerance,
129                                         int theIgnoreIndex ) const
130 {
131   std::vector<TopoDS_Wire> aCurves = GetWires( thePolyline );
132   std::vector<TopoDS_Wire> aToolCurves;
133   for( int i=theTools.Lower(), n=theTools.Upper(); i<=n; i++ )
134     if( i!=theIgnoreIndex )
135     {
136       Handle( HYDROData_PolylineXY ) aToolPolyline = 
137         Handle( HYDROData_PolylineXY )::DownCast( theTools.Value( i ) );
138       append( aToolCurves, GetWires( aToolPolyline ) );
139     }
140
141   bool isOK = true;
142
143   int n = aCurves.size();
144   std::vector<TopoDS_Shape> aResults( n );
145   for( int i=0; i<n; i++ )
146     aResults[i] = aCurves[i];
147
148   for( int j=0, m=aToolCurves.size(); j<m; j++ )
149   {
150     std::vector<TopoDS_Shape> aNewResults;
151     for( int k=0, q=aResults.size(); k<q; k++ )
152     {
153       std::vector<TopoDS_Shape> aCurvesList = Split( TopoDS::Wire( aResults[k] ), aToolCurves[j], theTolerance );
154       append( aNewResults, aCurvesList );
155     }
156     aResults = aNewResults;
157   }
158
159   CreatePolylines( theDoc, thePolyline->GetName(), aResults, true );
160
161   return isOK;
162 }
163
164 std::vector<TopoDS_Wire> HYDROData_PolylineOperator::GetWires( const Handle( HYDROData_PolylineXY )& thePolyline )
165 {
166   std::vector<TopoDS_Wire> aResult;
167
168   TopoDS_Shape aShape = thePolyline->GetShape();
169
170   if( aShape.ShapeType()==TopAbs_WIRE )
171   {
172     aResult.push_back( TopoDS::Wire( aShape ) );
173   }
174   else
175   {
176     TopExp_Explorer anExp( aShape, TopAbs_WIRE );
177     for( ; anExp.More(); anExp.Next() )
178     {
179       aResult.push_back( TopoDS::Wire( anExp.Current() ) );
180     }
181   }
182   return aResult;
183 }
184
185 static Standard_Boolean OrderCurveEdges(
186   const TopoDS_Wire& theWire,
187   NCollection_Vector<TopoDS_Edge>& theEdges,
188   Standard_Boolean& theIsClosed)
189 {
190   TopTools_IndexedDataMapOfShapeListOfShape aVertexToEdges;
191   TopExp::MapShapesAndAncestors(theWire,
192     TopAbs_VERTEX, TopAbs_EDGE, aVertexToEdges);
193   const Standard_Integer aVCount = aVertexToEdges.Extent();
194   if (aVCount == 0)
195   {
196     return Standard_False;
197   }
198
199   {
200     Standard_Integer aEndCount = 0;
201     for (Standard_Integer aVN = 1; aVN <= aVCount; ++aVN)
202     {
203       const Standard_Integer aEdgeCount =
204         aVertexToEdges.FindFromIndex(aVN).Extent();
205       if (aEdgeCount == 1)
206       {
207         ++aEndCount;
208       }
209       if (aEdgeCount > 2)
210       {
211         return Standard_False;
212       }
213     }
214     theIsClosed = (aEndCount == 0);
215     if (!theIsClosed && aEndCount != 2)
216     {
217       return Standard_False;
218     }
219   }
220
221   Standard_Integer aVN = 1;
222   if (!theIsClosed)
223   {
224     while (aVN <= aVCount &&
225       aVertexToEdges.FindFromIndex(aVN).Extent() == 2)
226     {
227       ++aVN;
228     }
229   }
230
231   TopTools_ListOfShape* aEdges = &aVertexToEdges.ChangeFromIndex(aVN);
232   while (!aEdges->IsEmpty())
233   {
234     const TopoDS_Edge aEdge = TopoDS::Edge(aEdges->First());
235     aEdges->RemoveFirst();
236     theEdges.Append(aEdge);
237     Standard_Integer aVN2 =
238       aVertexToEdges.FindIndex(TopExp::FirstVertex(aEdge));
239     if (aVN2 == aVN)
240     {
241       aVN2 = aVertexToEdges.FindIndex(TopExp::LastVertex(aEdge));
242     }
243     aVN = aVN2;
244
245     aEdges = &aVertexToEdges.ChangeFromIndex(aVN2);
246     if (aEdges->First().IsEqual(aEdge))
247     {
248       aEdges->RemoveFirst();
249     }
250     else
251     {
252       const TopoDS_Edge aEdge = TopoDS::Edge(aEdges->First());
253       aEdges->Clear();
254       aEdges->Append(aEdge);
255     }
256   }
257   return (!theIsClosed && theEdges.Size() == aVCount - 1) ||
258     (theIsClosed && theEdges.Size() == aVCount);
259 }
260
261 static Standard_Real ProjectPointToEdge(
262   const gp_Pnt& thePoint,
263   const TopoDS_Edge& theEdge,
264   Standard_Real& theParameter)
265 {
266   BRepAdaptor_Curve aECurve(theEdge);
267   Extrema_ExtPC aAlgo(thePoint, aECurve);
268   Standard_Integer aMinEN = -2;
269   Standard_Real aMinSqDist = DBL_MAX;
270   if (aAlgo.IsDone())
271   {
272     const Standard_Integer aECount = aAlgo.NbExt();
273     if (aECount != 0)
274     {
275       for (Standard_Integer aEN = 1; aEN <= aECount; ++aEN)
276       {
277         const gp_Pnt& aP = aAlgo.Point(aEN).Value();
278         const Standard_Real aSqDist = thePoint.SquareDistance(aP);
279         if (aMinSqDist > aSqDist)
280         {
281           aMinSqDist = aSqDist;
282           aMinEN = aEN;
283         }
284       }
285     }
286   }
287
288   const Standard_Real aParams[] =
289     {aECurve.FirstParameter(), aECurve.LastParameter()};
290   const gp_Pnt aEnds[] = {aECurve.Value(aParams[0]), aECurve.Value(aParams[1])};
291   const Standard_Real aSqDists[] =
292     {thePoint.SquareDistance(aEnds[0]), thePoint.SquareDistance(aEnds[1])};
293   for (Standard_Integer aEI = 0; aEI < 2; ++aEI)
294   {
295     if (aMinSqDist > aSqDists[aEI])
296     {
297       aMinSqDist = aSqDists[aEI];
298       aMinEN = -aEI;
299     }
300   }
301
302   if (aMinEN <= 0)
303   {
304     theParameter = aParams[-aMinEN];
305     return aMinSqDist;
306   }
307
308   const Extrema_POnCurv& aPOnC = aAlgo.Point(aMinEN);
309   const gp_Pnt& aPoint = aPOnC.Value();
310   theParameter = aPOnC.Parameter();
311   for (Standard_Integer aEI = 0; aEI < 2; ++aEI)
312   {
313     if (Abs(theParameter - aParams[aEI]) < Precision::PConfusion() ||
314       aPoint.SquareDistance(aEnds[aEI]) < Precision::SquareConfusion())
315     {
316       theParameter = aParams[aEI];
317     }
318   }
319   return aMinSqDist;
320 }
321
322 static void SplitCurveByPoint(
323   const NCollection_Vector<TopoDS_Edge>& theEdges,
324   const Standard_Integer theEdgeIndex,
325   const Standard_Real theParameter,
326   NCollection_Vector<TopoDS_Edge>& theEdges1,
327   NCollection_Vector<TopoDS_Edge>& theEdges2)
328 {
329   for (Standard_Integer aEI = 0; aEI < theEdgeIndex; ++aEI)
330   {
331     theEdges1.Append(theEdges(aEI));
332   }
333
334   const TopoDS_Edge& aEdge = theEdges(theEdgeIndex);
335   BRepAdaptor_Curve aECurve(aEdge);
336   Standard_Integer aParamI = -1;
337   const Standard_Real aEdgeEndParams[] =
338     {aECurve.FirstParameter(), aECurve.LastParameter()};
339   if (Abs(theParameter - aEdgeEndParams[0]) < Precision::PConfusion())
340   {
341     aParamI = 0;
342   }
343   else if (Abs(theParameter - aEdgeEndParams[1]) < Precision::PConfusion())
344   {
345     aParamI = 1;
346   }
347
348   const TopAbs_Orientation aOrient = aEdge.Orientation();
349   if (aOrient == TopAbs_REVERSED)
350   {
351     aParamI ^= 1;
352   }
353
354   const Standard_Integer aECount = theEdges.Size();
355   if (aParamI == 0)
356   {
357     NCollection_Vector<TopoDS_Edge>* aEdges = (theEdgeIndex != 0) ? &theEdges1 : &theEdges2;
358     for (Standard_Integer aEI = theEdgeIndex; aEI < aECount; ++aEI)
359     {
360       aEdges->Append(theEdges(aEI));
361     }
362   }
363   else if (aParamI == 1)
364   {
365     theEdges1.Append(aEdge);
366     for (Standard_Integer aEI = theEdgeIndex; aEI < aECount; ++aEI)
367     {
368       theEdges2.Append(theEdges(aEI));
369     }
370   }
371   else
372   {
373     TopoDS_Edge aFE = TopoDS::Edge(aEdge.Oriented(TopAbs_FORWARD));
374     ShapeAnalysis_TransferParametersProj aSATPP(aFE, TopoDS_Face());
375     aSATPP.SetMaxTolerance(Precision::Confusion());
376     TopoDS_Vertex aSplitV;
377     BRep_Builder().MakeVertex(
378       aSplitV, aECurve.Value(theParameter), Precision::Confusion());
379
380     TopoDS_Edge aEParts[] = {
381       ShapeBuild_Edge().CopyReplaceVertices(aFE, TopoDS_Vertex(),
382         TopoDS::Vertex(aSplitV.Oriented(TopAbs_REVERSED))),
383       ShapeBuild_Edge().CopyReplaceVertices(aFE, aSplitV, TopoDS_Vertex())};
384     ShapeBuild_Edge().CopyPCurves(aEParts[0], aFE);
385     ShapeBuild_Edge().CopyPCurves(aEParts[1], aFE);
386     BRep_Builder().SameRange(aEParts[0], Standard_False);
387     BRep_Builder().SameRange(aEParts[1], Standard_False);
388     BRep_Builder().SameParameter(aEParts[0], Standard_False);
389     BRep_Builder().SameParameter(aEParts[1], Standard_False);
390     aSATPP.TransferRange(aEParts[0],
391       aEdgeEndParams[0], theParameter, Standard_False);
392     aSATPP.TransferRange(aEParts[1],
393       theParameter, aEdgeEndParams[1], Standard_False);
394     aEParts[0].Orientation(aOrient);
395     aEParts[1].Orientation(aOrient);
396
397     const Standard_Integer aFirstPI = (aOrient != TopAbs_REVERSED) ? 0 : 1;
398     theEdges1.Append(aEParts[aFirstPI]);
399     theEdges2.Append(aEParts[1 - aFirstPI]);
400
401     for (Standard_Integer aEI = theEdgeIndex + 1; aEI < aECount; ++aEI)
402     {
403       theEdges2.Append(theEdges(aEI));
404     }
405   }
406 }
407
408 static void CollectCurveWire(
409   const NCollection_Vector<TopoDS_Edge>& theEdges, TopoDS_Wire& theWire)
410 {
411   BRep_Builder aBulder;
412   aBulder.MakeWire(theWire);
413   NCollection_Vector<TopoDS_Edge>::Iterator aEIt(theEdges);
414   for (; aEIt.More(); aEIt.Next())
415   {
416     aBulder.Add(theWire, aEIt.Value());
417   }
418 }
419
420 static void CollectCurveWire(
421   const NCollection_Vector<TopoDS_Edge>& theEdges1,
422   const NCollection_Vector<TopoDS_Edge>& theEdges2,
423   TopoDS_Wire& theWire)
424 {
425   BRep_Builder aBulder;
426   aBulder.MakeWire(theWire);
427   const NCollection_Vector<TopoDS_Edge>* aEdges[] = {&theEdges1, &theEdges2};
428   for (Standard_Integer aEI = 0; aEI < 2; ++aEI)
429   {
430     NCollection_Vector<TopoDS_Edge>::Iterator aEIt(*aEdges[aEI]);
431     for (; aEIt.More(); aEIt.Next())
432     {
433       aBulder.Add(theWire, aEIt.Value());
434     }
435   }
436 }
437
438 std::vector<TopoDS_Shape> HYDROData_PolylineOperator::Split( const TopoDS_Wire& theWire,
439                                                              const gp_Pnt2d& thePoint,
440                                                              double theTolerance )
441 {
442   std::vector<TopoDS_Shape> aResult;
443   NCollection_Vector<TopoDS_Edge> aEdges;
444   Standard_Boolean isClosed;
445   if (!OrderCurveEdges(theWire, aEdges, isClosed))
446   {
447     aResult.push_back(theWire);
448     return aResult;
449   }
450
451   const gp_Pnt aP(thePoint.X(), thePoint.Y(), 0);
452   Standard_Real aMinSqDist = DBL_MAX;
453   int aSEI = -1;
454   Standard_Real aSParam;
455   for (int aECount = aEdges.Size(), aEI = 0; aEI < aECount; ++aEI)
456   {
457     Standard_Real aParam;
458     const Standard_Real aSqDist =
459       ProjectPointToEdge(aP, aEdges(aEI), aParam);
460     if (aMinSqDist > aSqDist)
461     {
462       aMinSqDist = aSqDist;
463       aSEI = aEI;
464       aSParam = aParam;
465     }
466   }
467
468   NCollection_Vector<TopoDS_Edge> aEdges1, aEdges2;
469   SplitCurveByPoint(aEdges, aSEI, aSParam, aEdges1, aEdges2);
470   TopoDS_Wire aWire;
471   if (!isClosed)
472   {
473     CollectCurveWire(aEdges1, aWire);
474     if (!aEdges2.IsEmpty())
475     {
476       aResult.push_back(aWire);
477       CollectCurveWire(aEdges2, aWire);
478     }
479   }
480   else
481   {
482     CollectCurveWire(aEdges2, aEdges1, aWire);
483   }
484   aResult.push_back(aWire);
485   return aResult;
486 }
487
488 std::vector<TopoDS_Shape> HYDROData_PolylineOperator::Split( const TopoDS_Wire& theWire,
489                                                              const TopoDS_Wire& theTool,
490                                                              double theTolerance )
491 {
492   std::vector<TopoDS_Shape> aResult;
493   //TODO
494   return aResult;
495 }
496
497 std::vector<TopoDS_Shape> HYDROData_PolylineOperator::Split( const std::vector<TopoDS_Wire>& theWires,
498                                                              double theTolerance )
499 {
500   std::vector<TopoDS_Shape> aResult;
501   //TODO
502   return aResult;
503 }
504
505 bool HYDROData_PolylineOperator::CreatePolylines( const Handle( HYDROData_Document )& theDoc,
506                                                   const QString& theNamePrefix,
507                                                   const std::vector<TopoDS_Shape>& theShapes,
508                                                   bool isUseIndices )
509 {
510   if( theDoc.IsNull() )
511     return false;
512
513   int n = theShapes.size();
514   int anIndex = 1;
515   for( int i=0; i<n; i++ )
516   {
517     Handle( HYDROData_PolylineXY ) aPolyline = 
518       Handle( HYDROData_PolylineXY )::DownCast( theDoc->CreateObject( KIND_POLYLINEXY ) );
519     if( aPolyline.IsNull() )
520       return false;
521
522     aPolyline->SetShape( theShapes[i] );
523
524     if( isUseIndices )
525     {
526       QString aNewName = theNamePrefix + "_" + QString::number( anIndex );
527       if( theDoc->FindObjectByName( aNewName ).IsNull() )  // the object with such a name is not found
528         aPolyline->SetName( aNewName );
529       anIndex++;
530     }
531     else
532     {
533       aPolyline->SetName( theNamePrefix );
534     }
535   }
536   return true;
537 }