Salome HOME
Merge branch 'V9_2_2_BR'
[modules/paravis.git] / src / Plugins / DevelopedSurface / IO / vtkDevelopedSurface.cxx
1 // Copyright (C) 2017-2019  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 // Author : Anthony Geay (EDF R&D)
20
21 #include "vtkDevelopedSurface.h"
22 #include "VTKToMEDMem.hxx"
23
24 #include "vtkAdjacentVertexIterator.h"
25 #include "vtkIntArray.h"
26 #include "vtkCellData.h"
27 #include "vtkPointData.h"
28 #include "vtkCylinder.h"
29 #include "vtkNew.h"
30 #include "vtkCutter.h"
31 #include "vtkTransform.h"
32
33 #include "vtkStreamingDemandDrivenPipeline.h"
34 #include "vtkUnstructuredGrid.h"
35 #include  "vtkMultiBlockDataSet.h"
36
37 #include "vtkInformationStringKey.h"
38 #include "vtkAlgorithmOutput.h"
39 #include "vtkObjectFactory.h"
40 #include "vtkMutableDirectedGraph.h"
41 #include "vtkMultiBlockDataSet.h"
42 #include "vtkDataSet.h"
43 #include "vtkInformationVector.h"
44 #include "vtkInformation.h"
45 #include "vtkDataArraySelection.h"
46 #include "vtkTimeStamp.h"
47 #include "vtkInEdgeIterator.h"
48 #include "vtkInformationDataObjectKey.h"
49 #include "vtkExecutive.h"
50 #include "vtkVariantArray.h"
51 #include "vtkStringArray.h"
52 #include "vtkDoubleArray.h"
53 #include "vtkFloatArray.h"
54 #include "vtkCharArray.h"
55 #include "vtkUnsignedCharArray.h"
56 #include "vtkDataSetAttributes.h"
57 #include "vtkDemandDrivenPipeline.h"
58 #include "vtkDataObjectTreeIterator.h"
59 #include "vtkWarpScalar.h"
60
61 #include "MEDCouplingMemArray.hxx"
62
63 #include "VTKMEDTraits.hxx"
64
65 #ifdef WIN32
66 #define _USE_MATH_DEFINES
67 #endif
68 #include <math.h>
69
70 #include <map>
71 #include <deque>
72 #include <sstream>
73 #include <algorithm>
74
75 vtkStandardNewMacro(vtkDevelopedSurface);
76
77 ///////////////////
78
79 template<class T>
80 struct VTKTraits
81 {
82 };
83
84 template<>
85 struct VTKTraits<double>
86 {
87   typedef vtkDoubleArray ArrayType;
88 };
89
90 template<>
91 struct VTKTraits<float>
92 {
93   typedef vtkFloatArray ArrayType;
94 };
95
96 void ExtractInfo(vtkInformationVector *inputVector, vtkDataSet *& usgIn)
97 {
98   vtkInformation *inputInfo(inputVector->GetInformationObject(0));
99   vtkDataSet *input(0);
100   vtkDataSet *input0(vtkDataSet::SafeDownCast(inputInfo->Get(vtkDataObject::DATA_OBJECT())));
101   vtkMultiBlockDataSet *input1(vtkMultiBlockDataSet::SafeDownCast(inputInfo->Get(vtkDataObject::DATA_OBJECT())));
102   if(input0)
103     input=input0;
104   else
105     {
106       if(!input1)
107         throw MZCException("Input dataSet must be a DataSet or single elt multi block dataset expected !");
108       if(input1->GetNumberOfBlocks()!=1)
109         throw MZCException("Input dataSet is a multiblock dataset with not exactly one block ! Use MergeBlocks or ExtractBlocks filter before calling this filter !");
110       vtkDataObject *input2(input1->GetBlock(0));
111       if(!input2)
112         throw MZCException("Input dataSet is a multiblock dataset with exactly one block but this single element is NULL !");
113       vtkDataSet *input2c(vtkDataSet::SafeDownCast(input2));
114       if(!input2c)
115         throw MZCException("Input dataSet is a multiblock dataset with exactly one block but this single element is not a dataset ! Use MergeBlocks or ExtractBlocks filter before calling this filter !");
116       input=input2c;
117     }
118   if(!input)
119     throw MZCException("Input data set is NULL !");
120   vtkPointData *att(input->GetPointData());
121   if(!att)
122     throw MZCException("Input dataset has no point data attribute ! Impossible to deduce a developed surface on it !");
123   usgIn=input;
124 }
125
126 class vtkDevelopedSurface::vtkInternals
127 {
128 public:
129   vtkNew<vtkCutter> Cutter;
130 };
131
132 ////////////////////
133
134 vtkDevelopedSurface::vtkDevelopedSurface():_cyl(nullptr),Internal(new vtkInternals),InvertStatus(false),OffsetInRad(0.)
135 {
136   //this->RegisterFilter(this->Internal->Cutter.GetPointer());
137 }
138
139 vtkDevelopedSurface::~vtkDevelopedSurface()
140 {
141   delete this->Internal;
142 }
143
144 void vtkDevelopedSurface::SetInvertWay(bool invertStatus)
145 {
146   this->InvertStatus=invertStatus;
147   this->Modified();
148 }
149
150 void vtkDevelopedSurface::SetThetaOffset(double offsetInDegrees)
151 {
152   double tmp(std::min(offsetInDegrees,180.));
153   tmp=std::max(tmp,-180.);
154   this->OffsetInRad=tmp/180.*M_PI;
155   this->Modified();
156 }
157
158 int vtkDevelopedSurface::RequestInformation(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector)
159
160   //std::cerr << "########################################## vtkDevelopedSurface::RequestInformation ##########################################" << std::endl;
161   try
162     {
163       vtkDataSet *usgIn(0);
164       ExtractInfo(inputVector[0],usgIn);
165     }
166   catch(MZCException& e)
167     {
168       std::ostringstream oss;
169       oss << "Exception has been thrown in vtkDevelopedSurface::RequestInformation : " << e.what() << std::endl;
170       if(this->HasObserver("ErrorEvent") )
171         this->InvokeEvent("ErrorEvent",const_cast<char *>(oss.str().c_str()));
172       else
173         vtkOutputWindowDisplayErrorText(const_cast<char *>(oss.str().c_str()));
174       vtkObject::BreakOnError();
175       return 0;
176     }
177   return 1;
178 }
179
180 std::vector<int> UnWrapByDuplicatingNodes(vtkCellArray *ca, vtkIdType& offset, const MEDCoupling::DataArrayDouble *thetas)
181 {
182   std::vector<int> ret;
183   vtkIdType nbCells(ca->GetNumberOfCells());
184   vtkIdType *conn(ca->GetPointer());
185   const double *tptr(thetas->begin());
186   for(vtkIdType i=0;i<nbCells;i++)
187     {
188       vtkIdType sz(*conn++);
189       double ma(-std::numeric_limits<double>::max()),mi(std::numeric_limits<double>::max());
190       for(vtkIdType j=0;j<sz;j++)
191         {
192           double tmp_theta(tptr[conn[j]]);
193           ma=std::max(ma,tmp_theta);
194           mi=std::min(mi,tmp_theta);
195         }
196       if(ma-mi>M_PI)
197         {
198           for(vtkIdType j=0;j<sz;j++)
199             {
200               double tmp_theta(tptr[conn[j]]);
201               if(tmp_theta<=M_PI)
202                 {
203                   ret.push_back(conn[j]);
204                   conn[j]=offset++;
205                 }
206             }
207         }
208       conn+=sz;
209     }
210   return ret;
211 }
212
213 template<class T>
214 void DealArray(vtkDataSetAttributes *pd, int pos, typename MEDFileVTKTraits<T>::VtkType *arr, std::vector<int>& nodeSel)
215 {
216   int nbc(arr->GetNumberOfComponents());
217   std::size_t nbt(nodeSel.size());
218   vtkSmartPointer< typename MEDFileVTKTraits<T>::VtkType > newArr;
219   newArr.TakeReference(MEDFileVTKTraits<T>::VtkType::New());
220   newArr->SetNumberOfComponents(nbc);
221   newArr->SetNumberOfTuples(nbt);
222   T *ptr(newArr->GetPointer(0));
223   const T *inPtr(arr->GetPointer(0));
224   for(std::size_t i=0;i<nbt;i++)
225     {
226       ptr=std::copy(inPtr+nbc*nodeSel[i],inPtr+nbc*(nodeSel[i]+1),ptr);
227     }
228   newArr->SetName(arr->GetName());
229   arr->DeepCopy(newArr);
230 }
231
232 void ToDouble(vtkDataArray *coords, vtkSmartPointer<vtkDoubleArray>& coordsOut)
233 {
234   vtkDoubleArray *coords2(vtkDoubleArray::SafeDownCast(coords));
235   vtkFloatArray *coords3(vtkFloatArray::SafeDownCast(coords));
236   if(!coords2 && !coords3)
237     throw MZCException("Input coordinates are neither float64 or float32 !");
238   //
239   if(coords2)
240     {
241       coordsOut.TakeReference(coords2);
242       coords2->Register(0);
243     }
244   else
245     {
246       coordsOut.TakeReference(vtkDoubleArray::New());
247       coordsOut->SetNumberOfComponents(3);
248       vtkIdType nbTuples(coords3->GetNumberOfTuples());
249       coordsOut->SetNumberOfTuples(nbTuples);
250       std::copy(coords3->GetPointer(0),coords3->GetPointer(0)+3*nbTuples,coordsOut->GetPointer(0));
251     }
252 }
253
254 void dealWith(vtkPolyData *outdata, const double center[3], const double axis[3], double radius, double eps, bool invertThetaInc, double offsetInRad)
255 {
256   vtkDataArray *coords(outdata->GetPoints()->GetData());
257   if(coords->GetNumberOfComponents()!=3)
258     throw MZCException("Input coordinates are expected to have 3 components !");
259   //
260   vtkIdType nbNodes(coords->GetNumberOfTuples());
261   if(nbNodes==0)
262     throw MZCException("No points -> impossible to develop anything !");
263   //
264   vtkSmartPointer<vtkDoubleArray> zeCoords;
265   ToDouble(coords,zeCoords);
266   //
267   double axis_cross_Z[3]={axis[1],-axis[0],0.};
268   double n_axis(sqrt(axis_cross_Z[0]*axis_cross_Z[0]+axis_cross_Z[1]*axis_cross_Z[1]));
269   if(n_axis>eps)
270     {
271       double ang(asin(n_axis));
272       if(axis[2]<0.)
273         ang=M_PI-ang;
274       MEDCoupling::DataArrayDouble::Rotate3DAlg(center,axis_cross_Z,ang,nbNodes,zeCoords->GetPointer(0),zeCoords->GetPointer(0));
275     }
276   //
277   MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> c_cyl;
278   {
279     MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> cc(MEDCoupling::DataArrayDouble::New()); cc->alloc(nbNodes,3);
280     double *ccPtr(cc->getPointer());
281     const double *zeCoordsPtr(zeCoords->GetPointer(0));
282     for(vtkIdType i=0;i<nbNodes;i++,ccPtr+=3,zeCoordsPtr+=3)
283       {
284         std::transform(zeCoordsPtr,zeCoordsPtr+3,center,ccPtr,std::minus<double>());
285       }
286     c_cyl=cc->fromCartToCyl();
287   }
288   MEDCoupling::MCAuto<MEDCoupling::MEDFileData> mfd(MEDCoupling::MEDFileData::New());
289   WriteMEDFileFromVTKDataSet(mfd,outdata,{},0.,0);
290   bool a;
291   {
292     MEDCoupling::MEDFileMeshes *ms(mfd->getMeshes());
293     if(ms->getNumberOfMeshes()!=1)
294       throw MZCException("Unexpected number of meshes !");
295     MEDCoupling::MEDFileMesh *mm(ms->getMeshAtPos(0));
296     MEDCoupling::MEDFileUMesh *mmu(dynamic_cast<MEDCoupling::MEDFileUMesh *>(mm));
297     if(!mmu)
298       throw MZCException("Expecting unstructured one !");
299     MEDCoupling::MCAuto<MEDCoupling::MEDCouplingUMesh> m0(mmu->getMeshAtLevel(0));
300     {
301       int v(0);
302       MEDCoupling::MCAuto<MEDCoupling::DataArrayInt> c0s(m0->getCellIdsLyingOnNodes(&v,&v+1,false));
303       if(c0s->empty())
304         throw MZCException("Orphan node 0 !");
305       std::vector<int> nodes0;
306       m0->getNodeIdsOfCell(c0s->getIJ(0,0),nodes0);
307       MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> tmp0(c_cyl->selectByTupleIdSafe(nodes0.data(),nodes0.data()+nodes0.size()));
308       tmp0=tmp0->keepSelectedComponents({1});
309       double tmp(tmp0->getMaxAbsValueInArray());
310       a=tmp>0.;
311     }
312   }
313   //
314   constexpr double EPS_FOR_RADIUS=1e-2;
315   MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> rs(c_cyl->keepSelectedComponents({0}));
316   if(!rs->isUniform(radius,radius*EPS_FOR_RADIUS))
317     {
318       double mi(rs->getMinValueInArray()),ma(rs->getMaxValueInArray());
319       std::ostringstream oss; oss << "Looks not really a cylinder within given precision ! Range is [" << mi << "," << ma << "] expecting " << radius << " within precision of " << radius*EPS_FOR_RADIUS << " !";
320       throw MZCException(oss.str());
321     }
322   double tetha0(c_cyl->getIJ(0,1));
323   {
324     double *ccylptr(c_cyl->getPointer()+1);
325     double mi02(std::numeric_limits<double>::max());
326     for(vtkIdType i=0;i<nbNodes;i++,ccylptr+=3)
327       {
328         *ccylptr-=tetha0+offsetInRad;
329         mi02=std::min(mi02,*ccylptr);
330       }
331     if(mi02>0.)
332       {
333         ccylptr=c_cyl->getPointer()+1;
334         for(vtkIdType i=0;i<nbNodes;i++,ccylptr+=3)
335           {
336             if(*ccylptr>=2*M_PI)
337               {
338                 *ccylptr+=-2*M_PI;
339               }
340           }
341       }
342   }
343   {
344     MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> c_cyl_2(c_cyl->keepSelectedComponents({1}));
345     c_cyl_2->abs();
346     MEDCoupling::MCAuto<MEDCoupling::DataArrayInt> poses(c_cyl_2->findIdsInRange(0.,eps));
347     c_cyl->setPartOfValuesSimple3(0.,poses->begin(),poses->end(),1,2,1);
348   }
349   //
350   if(a ^ (!invertThetaInc))
351     {
352       MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> tmp(c_cyl->keepSelectedComponents({1}));
353       tmp=tmp->negate();
354       std::for_each(tmp->getPointer(),tmp->getPointer()+tmp->getNumberOfTuples(),[](double& v) { if(v==-0.) v=0.; });
355       c_cyl->setPartOfValues1(tmp,0,nbNodes,1,1,2,1);
356     }
357   MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> c_cyl_post(c_cyl->keepSelectedComponents({1}));
358   {
359     double *c_cyl_post_ptr(c_cyl_post->getPointer());
360     for(vtkIdType i=0;i<nbNodes;i++,c_cyl_post_ptr++)
361       {
362         if(*c_cyl_post_ptr<0.)
363           *c_cyl_post_ptr+=2*M_PI;
364       }
365   }
366   vtkCellArray *cb(outdata->GetPolys());
367   vtkIdType offset(nbNodes);
368   std::vector<int> dupNodes(UnWrapByDuplicatingNodes(cb,offset,c_cyl_post));
369   //
370   MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> c_cyl_post2(c_cyl_post->selectByTupleId(dupNodes.data(),dupNodes.data()+dupNodes.size()));
371   c_cyl_post2->applyLin(1.,2*M_PI);
372   c_cyl_post=MEDCoupling::DataArrayDouble::Aggregate(c_cyl_post,c_cyl_post2);
373   MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> z0(c_cyl->keepSelectedComponents({2}));
374   MEDCoupling::MCAuto<MEDCoupling::DataArrayDouble> z1(z0->selectByTupleId(dupNodes.data(),dupNodes.data()+dupNodes.size()));
375   z0=MEDCoupling::DataArrayDouble::Aggregate(z0,z1);
376   //
377   std::size_t outNbNodes(z0->getNumberOfTuples());
378   vtkSmartPointer<vtkDoubleArray> zeCoords2;
379   zeCoords2.TakeReference(vtkDoubleArray::New());
380   zeCoords2->SetNumberOfComponents(3);
381   zeCoords2->SetNumberOfTuples(outNbNodes);
382   {
383     const double *tptr(c_cyl_post->begin()),*zptr(z0->begin());
384     double *outPtr(zeCoords2->GetPointer(0));
385     for(std::size_t i=0;i<outNbNodes;i++,tptr++,zptr++,outPtr+=3)
386       {
387         outPtr[0]=radius*(*tptr);
388         outPtr[1]=(*zptr);
389         outPtr[2]=0.;
390       }
391   }
392   //
393   outdata->GetPoints()->SetData(zeCoords2);
394   // now post process nodes
395   std::vector<int> nodeSel(nbNodes+dupNodes.size());
396   {
397     int cnt(0);
398     std::for_each(nodeSel.begin(),nodeSel.begin()+nbNodes,[&cnt](int& v){ v=cnt++; });
399     std::copy(dupNodes.begin(),dupNodes.end(),nodeSel.begin()+nbNodes);
400   }
401   vtkDataSetAttributes *pd(outdata->GetPointData());
402   int nba(pd->GetNumberOfArrays());
403   for(int i=0;i<nba;i++)
404     {
405       vtkDataArray *arr(pd->GetArray(i));
406       {
407         vtkIntArray *arr0(vtkIntArray::SafeDownCast(arr));
408         if(arr0)
409           {
410             DealArray<int>(pd,i,arr0,nodeSel);
411             continue;
412           }
413       }
414       {
415         vtkFloatArray *arr0(vtkFloatArray::SafeDownCast(arr));
416         if(arr0)
417           {
418             DealArray<float>(pd,i,arr0,nodeSel);
419             continue;
420           }
421       }
422       {
423         vtkDoubleArray *arr0(vtkDoubleArray::SafeDownCast(arr));
424         if(arr0)
425           {
426             DealArray<double>(pd,i,arr0,nodeSel);
427             continue;
428           }
429       }
430     }
431 }
432
433 int vtkDevelopedSurface::RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector)
434 {
435   //std::cerr << "########################################## vtkDevelopedSurface::RequestData        ##########################################" << std::endl;
436   try
437     {
438       if(!_cyl)
439         throw MZCException("No cylinder object as cut function !");
440       double center[3],axis[3],radius;
441       vtkAbstractTransform* trf(_cyl->GetTransform());
442       {
443         _cyl->GetCenter(center);
444         _cyl->GetAxis(axis[0],axis[1],axis[2]);
445         radius=_cyl->GetRadius();
446       }
447       if(trf)
448         {
449           double axis3[3]={center[0]+0.,center[1]+1.,center[2]+0.},axis4[3];
450           trf->TransformPoint(axis3,axis4);
451           std::transform(axis4,axis4+3,center,axis,[](double a, double b) { return b-a; });
452           axis[1]=-axis[1];
453           if(std::isnan(axis[0]) && std::isnan(axis[1]) && std::isnan(axis[2]))
454             { axis[0]=0.; axis[1]=-1.; axis[2]=0.; }
455         }
456       //std::cerr << trf << " jjj " << axis[0] << " " << axis[1] << " " << axis[2]  << " : " << center[0] <<  " " << center[1] << " " << center[2] <<  " " " " << " -> " << radius << std::endl;
457       vtkDataSet *usgIn(0);
458       ExtractInfo(inputVector[0],usgIn);
459       vtkSmartPointer<vtkPolyData> outData;
460       {
461         vtkNew<vtkCutter> Cutter;
462         Cutter->SetInputData(usgIn);
463         Cutter->SetCutFunction(_cyl);
464         Cutter->Update();
465         vtkDataSet *zeComputedOutput(Cutter->GetOutput());
466         vtkPolyData *zeComputedOutput2(vtkPolyData::SafeDownCast(zeComputedOutput));
467         if(!zeComputedOutput2)
468           throw MZCException("Unexpected output of cutter !");
469         outData.TakeReference(zeComputedOutput2);
470         zeComputedOutput2->Register(0);
471       }
472       if(outData->GetNumberOfCells()==0)
473         return 1;// no cells -> nothing to do
474       //
475       dealWith(outData,center,axis,radius,1e-7,this->InvertStatus,this->OffsetInRad);
476       //finish
477       vtkInformation *outInfo(outputVector->GetInformationObject(0));
478       vtkPolyData *output(vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())));
479       output->ShallowCopy(outData);
480     }
481   catch(MZCException& e)
482     {
483       std::ostringstream oss;
484       oss << "Exception has been thrown in vtkDevelopedSurface::RequestInformation : " << e.what() << std::endl;
485       if(this->HasObserver("ErrorEvent") )
486         this->InvokeEvent("ErrorEvent",const_cast<char *>(oss.str().c_str()));
487       else
488         vtkOutputWindowDisplayErrorText(const_cast<char *>(oss.str().c_str()));
489       vtkObject::BreakOnError();
490       return 0;
491     }
492   return 1;
493 }
494
495 int vtkDevelopedSurface::FillOutputPortInformation( int vtkNotUsed(port), vtkInformation* info)
496 {
497   info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkPolyData");
498   return 1;
499 }
500
501
502 void vtkDevelopedSurface::PrintSelf(ostream& os, vtkIndent indent)
503 {
504   this->Superclass::PrintSelf(os, indent);
505 }
506
507 void vtkDevelopedSurface::SetCutFunction(vtkImplicitFunction* func)
508 {
509   vtkCylinder *cyl(vtkCylinder::SafeDownCast(func));
510   if(cyl)
511     {
512       _cyl=cyl;
513       this->Modified();
514     }
515 }
516
517 vtkMTimeType vtkDevelopedSurface::GetMTime()
518 {
519   vtkMTimeType maxMTime = this->Superclass::GetMTime(); // My MTime
520   if(_cyl)
521     {
522       maxMTime=std::max(maxMTime,_cyl->GetMTime());
523     }
524   return maxMTime;
525 }