// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-FileCopyrightText: Copyright (c) 2006 Atamai, Inc. // SPDX-License-Identifier: BSD-3-Clause #include "vtkMNITagPointWriter.h" #include "vtkObjectFactory.h" #include "vtkCommand.h" #include "vtkDoubleArray.h" #include "vtkErrorCode.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkIntArray.h" #include "vtkMath.h" #include "vtkPointData.h" #include "vtkPointSet.h" #include "vtkPoints.h" #include "vtkStringArray.h" #include "vtkStringFormatter.h" #include "vtksys/FStream.hxx" #include #include #if !defined(_WIN32) || defined(__CYGWIN__) #include /* unlink */ #else #include /* unlink */ #endif //------------------------------------------------------------------------------ VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkMNITagPointWriter); vtkCxxSetObjectMacro(vtkMNITagPointWriter, LabelText, vtkStringArray); vtkCxxSetObjectMacro(vtkMNITagPointWriter, Weights, vtkDoubleArray); vtkCxxSetObjectMacro(vtkMNITagPointWriter, StructureIds, vtkIntArray); vtkCxxSetObjectMacro(vtkMNITagPointWriter, PatientIds, vtkIntArray); //------------------------------------------------------------------------------ vtkMNITagPointWriter::vtkMNITagPointWriter() { this->Points[0] = nullptr; this->Points[1] = nullptr; this->LabelText = nullptr; this->Weights = nullptr; this->StructureIds = nullptr; this->PatientIds = nullptr; this->Comments = nullptr; this->SetNumberOfInputPorts(2); this->SetNumberOfOutputPorts(0); this->FileName = nullptr; } //------------------------------------------------------------------------------ vtkMNITagPointWriter::~vtkMNITagPointWriter() { vtkObject* objects[6]; objects[0] = this->Points[0]; objects[1] = this->Points[1]; objects[2] = this->LabelText; objects[3] = this->Weights; objects[4] = this->StructureIds; objects[5] = this->PatientIds; for (int i = 0; i < 6; i++) { if (objects[i]) { objects[i]->Delete(); } } delete[] this->Comments; delete[] this->FileName; } //------------------------------------------------------------------------------ void vtkMNITagPointWriter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Points: " << this->Points[0] << " " << this->Points[1] << "\n"; os << indent << "LabelText: " << this->LabelText << "\n"; os << indent << "Weights: " << this->Weights << "\n"; os << indent << "StructureIds: " << this->StructureIds << "\n"; os << indent << "PatientIds: " << this->PatientIds << "\n"; os << indent << "Comments: " << (this->Comments ? this->Comments : "none") << "\n"; } //------------------------------------------------------------------------------ int vtkMNITagPointWriter::FillInputPortInformation(int, vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet"); info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1); return 1; } //------------------------------------------------------------------------------ vtkMTimeType vtkMNITagPointWriter::GetMTime() { vtkMTimeType mtime = this->Superclass::GetMTime(); vtkObject* objects[6]; objects[0] = this->Points[0]; objects[1] = this->Points[1]; objects[2] = this->LabelText; objects[3] = this->Weights; objects[4] = this->StructureIds; objects[5] = this->PatientIds; for (int i = 0; i < 6; i++) { if (objects[i]) { vtkMTimeType m = objects[i]->GetMTime(); mtime = std::max(m, mtime); } } return mtime; } //------------------------------------------------------------------------------ void vtkMNITagPointWriter::SetPoints(int port, vtkPoints* points) { if (port < 0 || port > 1) { return; } if (this->Points[port] == points) { return; } if (this->Points[port]) { this->Points[port]->Delete(); } this->Points[port] = points; if (this->Points[port]) { this->Points[port]->Register(this); } this->Modified(); } //------------------------------------------------------------------------------ vtkPoints* vtkMNITagPointWriter::GetPoints(int port) { if (port < 0 || port > 1) { return nullptr; } return this->Points[port]; } //------------------------------------------------------------------------------ void vtkMNITagPointWriter::WriteData(vtkPointSet* inputs[2]) { static const char* arrayNames[3] = { "Weights", "StructureIds", "PatientIds" }; vtkPoints* points[2]; vtkStringArray* labels = nullptr; vtkDataArray* darray[3]; darray[0] = nullptr; darray[1] = nullptr; darray[2] = nullptr; vtkDataArray* ivarArrays[3]; ivarArrays[0] = this->Weights; ivarArrays[1] = this->StructureIds; ivarArrays[2] = this->PatientIds; for (int ii = 1; ii >= 0; --ii) { points[ii] = nullptr; if (inputs[ii]) { points[ii] = inputs[ii]->GetPoints(); vtkStringArray* stringArray = vtkArrayDownCast(inputs[ii]->GetPointData()->GetAbstractArray("LabelText")); if (stringArray) { labels = stringArray; } for (int j = 0; j < 3; j++) { vtkDataArray* dataArray = inputs[ii]->GetPointData()->GetArray(arrayNames[j]); if (dataArray) { darray[j] = dataArray; } } } if (this->Points[ii]) { points[ii] = this->Points[ii]; } } if (this->LabelText) { labels = this->LabelText; } for (int j = 0; j < 3; j++) { if (ivarArrays[j]) { darray[j] = ivarArrays[j]; } } if (points[0] == nullptr) { vtkErrorMacro("No input points have been provided"); return; } // numVolumes is 1 if there is only one set of points int numVolumes = 1; vtkIdType n = points[0]->GetNumberOfPoints(); if (points[1]) { numVolumes = 2; if (points[1]->GetNumberOfPoints() != n) { vtkErrorMacro( "Input point counts do not match: " << n << " versus " << points[1]->GetNumberOfPoints()); return; } } // labels is null if there are no labels if (labels && labels->GetNumberOfValues() != n) { vtkErrorMacro("LabelText count does not match point count: " << labels->GetNumberOfValues() << " versus " << n); return; } // dataArrays is null if there are no data arrays vtkDataArray** dataArrays = nullptr; for (int jj = 0; jj < 3; jj++) { if (darray[jj]) { dataArrays = darray; if (darray[jj]->GetNumberOfTuples() != n) { vtkErrorMacro("" << arrayNames[jj] << " count does not match point count: " << darray[jj]->GetNumberOfTuples() << " versus " << n); return; } } } // If we got this far, the data seems to be okay ostream* outfilep = this->OpenFile(); if (!outfilep) { return; } ostream& outfile = *outfilep; // Write the header outfile << "MNI Tag Point File\n"; outfile << "Volumes = " << numVolumes << ";\n"; // Write user comments if (this->Comments) { char* cp = this->Comments; while (*cp) { if (*cp != '%') { outfile << "% "; } while (*cp && *cp != '\n') { if (isprint(*cp) || *cp == '\t') { outfile << *cp; } cp++; } outfile << "\n"; if (*cp == '\n') { cp++; } } } else { for (int k = 0; k < numVolumes; k++) { outfile << "% Volume " << (k + 1) << " produced by VTK\n"; } } // Add a blank line outfile << "\n"; // Write the points outfile << "Points =\n"; char text[256]; for (int i = 0; i < n; i++) { for (int kk = 0; kk < 2; kk++) { if (points[kk]) { double point[3]; points[kk]->GetPoint(i, point); auto result = vtk::format_to_n( text, sizeof(text), " {:.15g} {:.15g} {:.15g}", point[0], point[1], point[2]); *result.out = '\0'; outfile << text; } } if (dataArrays) { double w = 0.0; int s = -1; int p = -1; if (dataArrays[0]) { w = dataArrays[0]->GetComponent(i, 0); } if (dataArrays[1]) { s = static_cast(dataArrays[1]->GetComponent(i, 0)); } if (dataArrays[2]) { p = static_cast(dataArrays[2]->GetComponent(i, 0)); } auto result = vtk::format_to_n(text, sizeof(text), " {:.15g} {:d} {:d}", w, s, p); *result.out = '\0'; outfile << text; } if (labels) { std::string l = labels->GetValue(i); outfile << " \""; for (std::string::iterator si = l.begin(); si != l.end(); ++si) { if (isprint(*si) && *si != '\"' && *si != '\\') { outfile.put(*si); } else { outfile.put('\\'); char c = '\0'; static char ctrltable[] = { '\a', 'a', '\b', 'b', '\f', 'f', '\n', 'n', '\r', 'r', '\t', 't', '\v', 'v', '\\', '\\', '\"', '\"', '\0', '\0' }; for (int ci = 0; ctrltable[ci] != '\0'; ci += 2) { if (*si == ctrltable[ci]) { c = ctrltable[ci + 1]; break; } } if (c != '\0') { outfile.put(c); } else { auto result = vtk::format_to_n( text, sizeof(text), "x{:02x}", (static_cast(*si) & 0x00ff)); *result.out = '\0'; outfile << text; } } } outfile << "\""; } if (i < n - 1) { outfile << "\n"; } } outfile << ";\n"; outfile.flush(); // Close the file this->CloseFile(outfilep); // Delete the file if an error occurred if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError) { vtkErrorMacro("Ran out of disk space; deleting file: " << this->FileName); unlink(this->FileName); } } //------------------------------------------------------------------------------ int vtkMNITagPointWriter::Write() { // Allow writer to work when no inputs are provided this->Modified(); this->Update(); return 1; } //------------------------------------------------------------------------------ int vtkMNITagPointWriter::RequestData( vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector*) { this->SetErrorCode(vtkErrorCode::NoError); vtkInformation* inInfo[2]; inInfo[0] = inputVector[0]->GetInformationObject(0); inInfo[1] = inputVector[1]->GetInformationObject(0); vtkPointSet* input[2]; input[0] = nullptr; input[1] = nullptr; vtkMTimeType lastUpdateTime = 0; for (int idx = 0; idx < 2; ++idx) { if (inInfo[idx]) { input[idx] = vtkPointSet::SafeDownCast(inInfo[idx]->Get(vtkDataObject::DATA_OBJECT())); if (input[idx]) { vtkMTimeType updateTime = input[idx]->GetUpdateTime(); lastUpdateTime = std::max(updateTime, lastUpdateTime); } } } if (lastUpdateTime < this->WriteTime && this->GetMTime() < this->WriteTime) { // we are up to date return 1; } this->InvokeEvent(vtkCommand::StartEvent, nullptr); this->WriteData(input); this->InvokeEvent(vtkCommand::EndEvent, nullptr); this->WriteTime.Modified(); return 1; } //------------------------------------------------------------------------------ ostream* vtkMNITagPointWriter::OpenFile() { ostream* fptr; if (!this->FileName) { vtkErrorMacro(<< "No FileName specified! Can't write!"); this->SetErrorCode(vtkErrorCode::NoFileNameError); return nullptr; } vtkDebugMacro(<< "Opening file for writing..."); fptr = new vtksys::ofstream(this->FileName, ios::out); if (fptr->fail()) { vtkErrorMacro(<< "Unable to open file: " << this->FileName); this->SetErrorCode(vtkErrorCode::CannotOpenFileError); delete fptr; return nullptr; } return fptr; } //------------------------------------------------------------------------------ void vtkMNITagPointWriter::CloseFile(ostream* fp) { vtkDebugMacro(<< "Closing file\n"); delete fp; } VTK_ABI_NAMESPACE_END