1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkMNITagPointWriter.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 /*=========================================================================
16 
17 Copyright (c) 2006 Atamai, Inc.
18 
19 Use, modification and redistribution of the software, in source or
20 binary forms, are permitted provided that the following terms and
21 conditions are met:
22 
23 1) Redistribution of the source code, in verbatim or modified
24    form, must retain the above copyright notice, this license,
25    the following disclaimer, and any notices that refer to this
26    license and/or the following disclaimer.
27 
28 2) Redistribution in binary form must include the above copyright
29    notice, a copy of this license and the following disclaimer
30    in the documentation or with other materials provided with the
31    distribution.
32 
33 3) Modified copies of the source code must be clearly marked as such,
34    and must not be misrepresented as verbatim copies of the source code.
35 
36 THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS"
37 WITHOUT EXPRESSED OR IMPLIED WARRANTY INCLUDING, BUT NOT LIMITED TO,
38 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39 PURPOSE.  IN NO EVENT SHALL ANY COPYRIGHT HOLDER OR OTHER PARTY WHO MAY
40 MODIFY AND/OR REDISTRIBUTE THE SOFTWARE UNDER THE TERMS OF THIS LICENSE
41 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES
42 (INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA OR DATA BECOMING INACCURATE
43 OR LOSS OF PROFIT OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF
44 THE USE OR INABILITY TO USE THE SOFTWARE, EVEN IF ADVISED OF THE
45 POSSIBILITY OF SUCH DAMAGES.
46 
47 =========================================================================*/
48 
49 #include "vtkMNITagPointWriter.h"
50 
51 #include "vtkObjectFactory.h"
52 
53 #include "vtkInformationVector.h"
54 #include "vtkInformation.h"
55 #include "vtkPointSet.h"
56 #include "vtkPointData.h"
57 #include "vtkPoints.h"
58 #include "vtkStringArray.h"
59 #include "vtkDoubleArray.h"
60 #include "vtkIntArray.h"
61 #include "vtkMath.h"
62 #include "vtkErrorCode.h"
63 #include "vtkCommand.h"
64 
65 #include <cctype>
66 #include <cmath>
67 
68 #if !defined(_WIN32) || defined(__CYGWIN__)
69 # include <unistd.h> /* unlink */
70 #else
71 # include <io.h> /* unlink */
72 #endif
73 
74 //--------------------------------------------------------------------------
75 vtkStandardNewMacro(vtkMNITagPointWriter);
76 
77 vtkCxxSetObjectMacro(vtkMNITagPointWriter, LabelText, vtkStringArray);
78 vtkCxxSetObjectMacro(vtkMNITagPointWriter, Weights, vtkDoubleArray);
79 vtkCxxSetObjectMacro(vtkMNITagPointWriter, StructureIds, vtkIntArray);
80 vtkCxxSetObjectMacro(vtkMNITagPointWriter, PatientIds, vtkIntArray);
81 
82 //-------------------------------------------------------------------------
vtkMNITagPointWriter()83 vtkMNITagPointWriter::vtkMNITagPointWriter()
84 {
85   this->Points[0] = nullptr;
86   this->Points[1] = nullptr;
87 
88   this->LabelText = nullptr;
89   this->Weights = nullptr;
90   this->StructureIds = nullptr;
91   this->PatientIds = nullptr;
92 
93   this->Comments = nullptr;
94 
95   this->SetNumberOfInputPorts(2);
96   this->SetNumberOfOutputPorts(0);
97 
98   this->FileName = nullptr;
99 }
100 
101 //-------------------------------------------------------------------------
~vtkMNITagPointWriter()102 vtkMNITagPointWriter::~vtkMNITagPointWriter()
103 {
104   vtkObject *objects[6];
105   objects[0] = this->Points[0];
106   objects[1] = this->Points[1];
107   objects[2] = this->LabelText;
108   objects[3] = this->Weights;
109   objects[4] = this->StructureIds;
110   objects[5] = this->PatientIds;
111 
112   for (int i = 0; i < 6; i++)
113   {
114     if (objects[i])
115     {
116       objects[i]->Delete();
117     }
118   }
119 
120   delete [] this->Comments;
121 
122   delete[] this->FileName;
123 }
124 
125 //-------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)126 void vtkMNITagPointWriter::PrintSelf(ostream& os, vtkIndent indent)
127 {
128   this->Superclass::PrintSelf(os,indent);
129 
130   os << indent << "Points: " << this->Points[0] << " "
131      << this->Points[1] << "\n";
132   os << indent << "LabelText: " << this->LabelText << "\n";
133   os << indent << "Weights: " << this->Weights << "\n";
134   os << indent << "StructureIds: " << this->StructureIds << "\n";
135   os << indent << "PatientIds: " << this->PatientIds << "\n";
136 
137   os << indent << "Comments: "
138      << (this->Comments ? this->Comments : "none") << "\n";
139 }
140 
141 //----------------------------------------------------------------------------
FillInputPortInformation(int,vtkInformation * info)142 int vtkMNITagPointWriter::FillInputPortInformation(
143   int, vtkInformation* info)
144 {
145   info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet");
146   info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
147   return 1;
148 }
149 
150 //-------------------------------------------------------------------------
GetMTime()151 vtkMTimeType vtkMNITagPointWriter::GetMTime()
152 {
153   vtkMTimeType mtime = this->Superclass::GetMTime();
154 
155   vtkObject *objects[6];
156   objects[0] = this->Points[0];
157   objects[1] = this->Points[1];
158   objects[2] = this->LabelText;
159   objects[3] = this->Weights;
160   objects[4] = this->StructureIds;
161   objects[5] = this->PatientIds;
162 
163   for (int i = 0; i < 6; i++)
164   {
165     if (objects[i])
166     {
167       vtkMTimeType m = objects[i]->GetMTime();
168       if (m > mtime)
169       {
170         mtime = m;
171       }
172     }
173   }
174 
175   return mtime;
176 }
177 
178 //-------------------------------------------------------------------------
SetPoints(int port,vtkPoints * points)179 void vtkMNITagPointWriter::SetPoints(int port, vtkPoints *points)
180 {
181   if (port < 0 || port > 1)
182   {
183     return;
184   }
185   if (this->Points[port] == points)
186   {
187     return;
188   }
189   if (this->Points[port])
190   {
191     this->Points[port]->Delete();
192   }
193   this->Points[port] = points;
194   if (this->Points[port])
195   {
196     this->Points[port]->Register(this);
197   }
198   this->Modified();
199 }
200 
201 //-------------------------------------------------------------------------
GetPoints(int port)202 vtkPoints *vtkMNITagPointWriter::GetPoints(int port)
203 {
204   if (port < 0 || port > 1)
205   {
206     return nullptr;
207   }
208   return this->Points[port];
209 }
210 
211 //-------------------------------------------------------------------------
WriteData(vtkPointSet * inputs[2])212 void vtkMNITagPointWriter::WriteData(vtkPointSet *inputs[2])
213 {
214   static const char *arrayNames[3] = {
215     "Weights",
216     "StructureIds",
217     "PatientIds"
218   };
219 
220   vtkPoints *points[2];
221 
222   vtkStringArray *labels = nullptr;
223   vtkDataArray *darray[3];
224   darray[0] = nullptr;
225   darray[1] = nullptr;
226   darray[2] = nullptr;
227 
228   vtkDataArray *ivarArrays[3];
229   ivarArrays[0] = this->Weights;
230   ivarArrays[1] = this->StructureIds;
231   ivarArrays[2] = this->PatientIds;
232 
233   for (int ii = 1; ii >= 0; --ii)
234   {
235     points[ii] = nullptr;
236     if (inputs[ii])
237     {
238       points[ii] = inputs[ii]->GetPoints();
239 
240       vtkStringArray *stringArray = vtkArrayDownCast<vtkStringArray>(
241         inputs[ii]->GetPointData()->GetAbstractArray("LabelText"));
242       if (stringArray)
243       {
244         labels = stringArray;
245       }
246 
247       for (int j = 0; j < 3; j++)
248       {
249         vtkDataArray *dataArray =
250           inputs[ii]->GetPointData()->GetArray(arrayNames[j]);
251         if (dataArray)
252         {
253           darray[j] = dataArray;
254         }
255       }
256     }
257 
258     if (this->Points[ii])
259     {
260       points[ii] = this->Points[ii];
261     }
262   }
263 
264   if (this->LabelText)
265   {
266     labels = this->LabelText;
267   }
268 
269   for (int j = 0; j < 3; j++)
270   {
271     if (ivarArrays[j])
272     {
273       darray[j] = ivarArrays[j];
274     }
275   }
276 
277   if (points[0] == nullptr)
278   {
279     vtkErrorMacro("No input points have been provided");
280     return;
281   }
282 
283   // numVolumes is 1 if there is only one set of points
284   int numVolumes = 1;
285   vtkIdType n = points[0]->GetNumberOfPoints();
286   if (points[1])
287   {
288     numVolumes = 2;
289     if (points[1]->GetNumberOfPoints() != n)
290     {
291       vtkErrorMacro("Input point counts do not match: " << n << " versus "
292                     << points[1]->GetNumberOfPoints());
293       return;
294     }
295   }
296 
297   // labels is null if there are no labels
298   if (labels && labels->GetNumberOfValues() != n)
299   {
300     vtkErrorMacro("LabelText count does not match point count: "
301                   << labels->GetNumberOfValues() << " versus " << n);
302     return;
303   }
304 
305   // dataArrays is null if there are no data arrays
306   vtkDataArray **dataArrays = nullptr;
307   for (int jj = 0; jj < 3; jj++)
308   {
309     if (darray[jj])
310     {
311       dataArrays = darray;
312       if (darray[jj]->GetNumberOfTuples() != n)
313       {
314         vtkErrorMacro("" << arrayNames[jj]
315                       << " count does not match point count: "
316                       << darray[jj]->GetNumberOfTuples() << " versus " << n);
317         return;
318       }
319     }
320   }
321 
322   // If we got this far, the data seems to be okay
323   ostream *outfilep = this->OpenFile();
324   if (!outfilep)
325   {
326     return;
327   }
328 
329   ostream &outfile = *outfilep;
330 
331   // Write the header
332   outfile << "MNI Tag Point File\n";
333   outfile << "Volumes = " << numVolumes << ";\n";
334 
335   // Write user comments
336   if (this->Comments)
337   {
338     char *cp = this->Comments;
339     while (*cp)
340     {
341       if (*cp != '%')
342       {
343         outfile << "% ";
344       }
345       while (*cp && *cp != '\n')
346       {
347         if (isprint(*cp) || *cp == '\t')
348         {
349           outfile << *cp;
350         }
351         cp++;
352       }
353       outfile << "\n";
354       if (*cp == '\n')
355       {
356         cp++;
357       }
358     }
359   }
360   else
361   {
362     for (int k = 0; k < numVolumes; k++)
363     {
364       outfile << "% Volume " << (k + 1) << " produced by VTK\n";
365     }
366   }
367 
368   // Add a blank line
369   outfile << "\n";
370 
371   // Write the points
372   outfile << "Points =\n";
373 
374   char text[256];
375   for (int i = 0; i < n; i++)
376   {
377     for (int kk = 0; kk < 2; kk++)
378     {
379       if (points[kk])
380       {
381         double point[3];
382         points[kk]->GetPoint(i, point);
383         snprintf(text, sizeof(text), " %.15g %.15g %.15g", point[0], point[1], point[2]);
384         outfile << text;
385       }
386     }
387 
388     if (dataArrays)
389     {
390       double w = 0.0;
391       int s = -1;
392       int p = -1;
393       if (dataArrays[0])
394       {
395         w = dataArrays[0]->GetComponent(i, 0);
396       }
397       if (dataArrays[1])
398       {
399         s = static_cast<int>(dataArrays[1]->GetComponent(i, 0));
400       }
401       if (dataArrays[2])
402       {
403         p = static_cast<int>(dataArrays[2]->GetComponent(i, 0));
404       }
405 
406       snprintf(text, sizeof(text), " %.15g %d %d", w, s, p);
407       outfile << text;
408     }
409 
410     if (labels)
411     {
412       vtkStdString l = labels->GetValue(i);
413       outfile << " \"";
414       for (std::string::iterator si = l.begin(); si != l.end(); ++si)
415       {
416         if (isprint(*si) && *si != '\"' && *si != '\\')
417         {
418           outfile.put(*si);
419         }
420         else
421         {
422           outfile.put('\\');
423           char c = '\0';
424           static char ctrltable[] = {
425             '\a', 'a', '\b', 'b', '\f', 'f', '\n', 'n', '\r', 'r',
426             '\t', 't', '\v', 'v', '\\', '\\', '\"', '\"', '\0', '\0'
427             };
428           for (int ci = 0; ctrltable[ci] != '\0'; ci += 2)
429           {
430             if (*si == ctrltable[ci])
431             {
432               c = ctrltable[ci + 1];
433               break;
434             }
435           }
436           if (c != '\0')
437           {
438             outfile.put(c);
439           }
440           else
441           {
442             snprintf(text, sizeof(text), "x%2.2x", (static_cast<int>(*si) & 0x00ff));
443             outfile << text;
444           }
445         }
446       }
447 
448       outfile << "\"";
449     }
450 
451     if (i < n-1)
452     {
453       outfile << "\n";
454     }
455   }
456 
457   outfile << ";\n";
458   outfile.flush();
459 
460   // Close the file
461   this->CloseFile(outfilep);
462 
463   // Delete the file if an error occurred
464   if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError)
465   {
466     vtkErrorMacro("Ran out of disk space; deleting file: "
467                   << this->FileName);
468     unlink(this->FileName);
469   }
470 }
471 
472 //-------------------------------------------------------------------------
Write()473 int vtkMNITagPointWriter::Write()
474 {
475   // Allow writer to work when no inputs are provided
476   this->Modified();
477   this->Update();
478   return 1;
479 }
480 
481 //-------------------------------------------------------------------------
RequestData(vtkInformation *,vtkInformationVector ** inputVector,vtkInformationVector *)482 int vtkMNITagPointWriter::RequestData(
483   vtkInformation *, vtkInformationVector **inputVector,
484   vtkInformationVector *)
485 {
486   this->SetErrorCode(vtkErrorCode::NoError);
487 
488   vtkInformation *inInfo[2];
489   inInfo[0] = inputVector[0]->GetInformationObject(0);
490   inInfo[1] = inputVector[1]->GetInformationObject(0);
491 
492   vtkPointSet *input[2];
493   input[0] = nullptr;
494   input[1] = nullptr;
495 
496   vtkMTimeType lastUpdateTime = 0;
497   for (int idx = 0; idx < 2; ++idx)
498   {
499     if (inInfo[idx])
500     {
501       input[idx] = vtkPointSet::SafeDownCast(
502         inInfo[idx]->Get(vtkDataObject::DATA_OBJECT()));
503       if (input[idx])
504       {
505         vtkMTimeType updateTime = input[idx]->GetUpdateTime();
506         if (updateTime > lastUpdateTime)
507         {
508           lastUpdateTime = updateTime;
509         }
510       }
511     }
512   }
513 
514   if (lastUpdateTime < this->WriteTime && this->GetMTime() < this->WriteTime)
515   {
516     // we are up to date
517     return 1;
518   }
519 
520   this->InvokeEvent(vtkCommand::StartEvent, nullptr);
521   this->WriteData(input);
522   this->InvokeEvent(vtkCommand::EndEvent, nullptr);
523 
524   this->WriteTime.Modified();
525 
526   return 1;
527 }
528 
529 //-------------------------------------------------------------------------
OpenFile()530 ostream *vtkMNITagPointWriter::OpenFile()
531 {
532   ostream *fptr;
533 
534   if (!this->FileName )
535   {
536     vtkErrorMacro(<< "No FileName specified! Can't write!");
537     this->SetErrorCode(vtkErrorCode::NoFileNameError);
538     return nullptr;
539   }
540 
541   vtkDebugMacro(<<"Opening file for writing...");
542 
543   fptr = new ofstream(this->FileName, ios::out);
544 
545   if (fptr->fail())
546   {
547     vtkErrorMacro(<< "Unable to open file: "<< this->FileName);
548     this->SetErrorCode(vtkErrorCode::CannotOpenFileError);
549     delete fptr;
550     return nullptr;
551   }
552 
553   return fptr;
554 }
555 
556 //-------------------------------------------------------------------------
CloseFile(ostream * fp)557 void vtkMNITagPointWriter::CloseFile(ostream *fp)
558 {
559   vtkDebugMacro(<<"Closing file\n");
560 
561   delete fp;
562 }
563