1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkMoleculeAppend.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 #include "vtkMoleculeAppend.h"
16 
17 #include "vtkAlgorithmOutput.h"
18 #include "vtkDataArray.h"
19 #include "vtkDataSetAttributes.h"
20 #include "vtkExecutive.h"
21 #include "vtkInformation.h"
22 #include "vtkInformationVector.h"
23 #include "vtkMergePoints.h"
24 #include "vtkMolecule.h"
25 #include "vtkNew.h"
26 #include "vtkObjectFactory.h"
27 #include "vtkPoints.h"
28 #include "vtkUnsignedCharArray.h"
29 
30 #include <set>
31 #include <utility>
32 
33 vtkStandardNewMacro(vtkMoleculeAppend);
34 
35 //----------------------------------------------------------------------------
vtkMoleculeAppend()36 vtkMoleculeAppend::vtkMoleculeAppend() : MergeCoincidentAtoms(true)
37 {
38 }
39 
40 //----------------------------------------------------------------------------
GetInput(int idx)41 vtkDataObject* vtkMoleculeAppend::GetInput(int idx)
42 {
43   if (this->GetNumberOfInputConnections(0) <= idx)
44   {
45     return nullptr;
46   }
47   return vtkMolecule::SafeDownCast(this->GetExecutive()->GetInputData(0, idx));
48 }
49 
50 //----------------------------------------------------------------------------
RequestData(vtkInformation *,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)51 int vtkMoleculeAppend::RequestData(vtkInformation*,
52   vtkInformationVector** inputVector,
53   vtkInformationVector* outputVector)
54 {
55   vtkMolecule* output = vtkMolecule::GetData(outputVector, 0);
56   vtkDataSetAttributes* outputAtomData = output->GetAtomData();
57   vtkDataSetAttributes* outputBondData = output->GetBondData();
58 
59   // ********************
60   // Create output data arrays following first input arrays.
61   vtkMolecule* mol0 = vtkMolecule::SafeDownCast(this->GetInput());
62   outputAtomData->CopyStructure(mol0->GetAtomData());
63   outputBondData->CopyStructure(mol0->GetBondData());
64   output->SetAtomicNumberArrayName(mol0->GetAtomicNumberArrayName());
65   output->SetBondOrdersArrayName(mol0->GetBondOrdersArrayName());
66   vtkUnsignedCharArray* outputGhostAtoms = output->GetAtomGhostArray();
67   vtkUnsignedCharArray* outputGhostBonds = output->GetBondGhostArray();
68 
69   // ********************
70   // Initialize unique atoms/bonds containers
71   vtkNew<vtkMergePoints> uniquePoints;
72   vtkNew<vtkPoints> uniquePointsList;
73   double bounds[6] = { 0., 0., 0., 0., 0., 0. };
74   uniquePoints->InitPointInsertion(uniquePointsList, bounds, 0);
75   std::set<std::pair<vtkIdType, vtkIdType> > uniqueBonds;
76 
77   // ********************
78   // Process each input
79   for (int idx = 0; idx < this->GetNumberOfInputConnections(0); ++idx)
80   {
81     vtkMolecule* input = vtkMolecule::GetData(inputVector[0], idx);
82 
83     // --------------------
84     // Sanity check on input
85     int inputNbAtomArrays = input->GetAtomData()->GetNumberOfArrays();
86     if (inputNbAtomArrays != outputAtomData->GetNumberOfArrays())
87     {
88       vtkErrorMacro(<< "Input " << idx << ": Wrong number of atom array. Has " << inputNbAtomArrays
89                     << " instead of "
90                     << outputAtomData->GetNumberOfArrays());
91       return 0;
92     }
93 
94     int inputNbBondArrays = input->GetBondData()->GetNumberOfArrays();
95     if (input->GetNumberOfBonds() > 0 && inputNbBondArrays != outputBondData->GetNumberOfArrays())
96     {
97       vtkErrorMacro(<< "Input " << idx << ": Wrong number of bond array. Has " << inputNbBondArrays
98                     << " instead of "
99                     << outputBondData->GetNumberOfArrays());
100       return 0;
101     }
102 
103     for (vtkIdType ai = 0; ai < inputNbAtomArrays; ai++)
104     {
105       vtkDataArray* inArray = input->GetAtomData()->GetArray(ai);
106       if (!this->CheckArrays(inArray, outputAtomData->GetArray(inArray->GetName())))
107       {
108         vtkErrorMacro(<< "Input " << idx << ": atoms arrays do not match with output");
109         return 0;
110       }
111     }
112 
113     for (vtkIdType ai = 0; ai < inputNbBondArrays; ai++)
114     {
115       vtkDataArray* inArray = input->GetBondData()->GetArray(ai);
116       if (!this->CheckArrays(inArray, outputBondData->GetArray(inArray->GetName())))
117       {
118         vtkErrorMacro(<< "Input " << idx << ": bonds arrays do not match with output");
119         return 0;
120       }
121     }
122 
123     // --------------------
124     // add atoms and bonds without duplication
125 
126     // map from 'input molecule atom ids' to 'output molecule atom ids'
127     std::vector<vtkIdType> atomIdMap(input->GetNumberOfAtoms(), -1);
128 
129     vtkIdType previousNbOfAtoms = output->GetNumberOfAtoms();
130     int nbOfAtoms = 0;
131     for (vtkIdType i = 0; i < input->GetNumberOfAtoms(); i++)
132     {
133       double pt[3];
134       input->GetAtomicPositionArray()->GetPoint(i, pt);
135       bool addAtom = true;
136       if (this->MergeCoincidentAtoms)
137       {
138         addAtom = uniquePoints->InsertUniquePoint(pt, atomIdMap[i]) == 1;
139       }
140       else
141       {
142         atomIdMap[i] = previousNbOfAtoms + nbOfAtoms;
143       }
144 
145       if (addAtom)
146       {
147         nbOfAtoms++;
148         vtkAtom atom = input->GetAtom(i);
149         output->AppendAtom(atom.GetAtomicNumber(), atom.GetPosition()).GetId();
150         if (outputGhostAtoms)
151         {
152           outputGhostAtoms->InsertValue(atomIdMap[i], 255);
153         }
154       }
155     }
156     vtkIdType previousNbOfBonds = output->GetNumberOfBonds();
157     int nbOfBonds = 0;
158     for (vtkIdType i = 0; i < input->GetNumberOfBonds(); i++)
159     {
160       vtkBond bond = input->GetBond(i);
161       // as bonds are undirected, put min atom number at first to avoid duplication.
162       vtkIdType atom1 = atomIdMap[bond.GetBeginAtomId()];
163       vtkIdType atom2 = atomIdMap[bond.GetEndAtomId()];
164       auto result =
165         uniqueBonds.insert(std::make_pair(std::min(atom1, atom2), std::max(atom1, atom2)));
166       if (result.second)
167       {
168         nbOfBonds++;
169         output->AppendBond(atom1, atom2, bond.GetOrder());
170       }
171     }
172 
173     // --------------------
174     // Reset arrays size (and allocation if needed)
175     for (vtkIdType ai = 0; ai < input->GetAtomData()->GetNumberOfArrays(); ai++)
176     {
177       vtkDataArray* inArray = input->GetAtomData()->GetArray(ai);
178       vtkDataArray* outArray = output->GetAtomData()->GetArray(inArray->GetName());
179       outArray->Resize(previousNbOfAtoms + nbOfAtoms);
180     }
181 
182     for (vtkIdType ai = 0; ai < input->GetBondData()->GetNumberOfArrays(); ai++)
183     {
184       // skip bond orders array as it is auto-filled by AppendBond method
185       vtkDataArray* inArray = input->GetBondData()->GetArray(ai);
186       if (!strcmp(inArray->GetName(), input->GetBondOrdersArrayName()))
187       {
188         continue;
189       }
190       vtkDataArray* outArray = output->GetBondData()->GetArray(inArray->GetName());
191       outArray->Resize(previousNbOfBonds + nbOfBonds);
192     }
193 
194     // --------------------
195     // Fill DataArrays
196     for (vtkIdType i = 0; i < input->GetNumberOfAtoms(); i++)
197     {
198       for (vtkIdType ai = 0; ai < input->GetAtomData()->GetNumberOfArrays(); ai++)
199       {
200         vtkDataArray* inArray = input->GetAtomData()->GetArray(ai);
201         vtkDataArray* outArray = output->GetAtomData()->GetArray(inArray->GetName());
202         // Use Value of non-ghost atom.
203         if (outputGhostAtoms && outputGhostAtoms->GetValue(atomIdMap[i]) == 0)
204         {
205           continue;
206         }
207         outArray->InsertTuple(atomIdMap[i], inArray->GetTuple(i));
208       }
209     }
210     for (vtkIdType i = 0; i < input->GetNumberOfBonds(); i++)
211     {
212       vtkBond bond = input->GetBond(i);
213       vtkIdType outputBondId =
214         output->GetBondId(atomIdMap[bond.GetBeginAtomId()], atomIdMap[bond.GetBeginAtomId()]);
215 
216       for (vtkIdType ai = 0; ai < input->GetBondData()->GetNumberOfArrays(); ai++)
217       {
218         // skip bond orders array as it is auto-filled by AppendBond method
219         vtkDataArray* inArray = input->GetBondData()->GetArray(ai);
220         if (!strcmp(inArray->GetName(), input->GetBondOrdersArrayName()))
221         {
222           continue;
223         }
224         vtkDataArray* outArray = output->GetBondData()->GetArray(inArray->GetName());
225         outArray->InsertTuple(outputBondId, inArray->GetTuple(i));
226       }
227     }
228   }
229 
230   if (outputGhostBonds)
231   {
232     outputGhostBonds->SetNumberOfTuples(output->GetNumberOfBonds());
233     outputGhostBonds->Fill(0);
234     for (vtkIdType bondId = 0; bondId < output->GetNumberOfBonds(); bondId++)
235     {
236       vtkIdType atom1 = output->GetBond(bondId).GetBeginAtomId();
237       vtkIdType atom2 = output->GetBond(bondId).GetEndAtomId();
238       if (outputGhostAtoms->GetValue(atom1) == 1 || outputGhostAtoms->GetValue(atom2) == 1)
239       {
240         outputGhostBonds->SetValue(bondId, 1);
241       }
242     }
243   }
244 
245   return 1;
246 }
247 
248 //----------------------------------------------------------------------------
FillInputPortInformation(int i,vtkInformation * info)249 int vtkMoleculeAppend::FillInputPortInformation(int i, vtkInformation* info)
250 {
251   info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
252   return this->Superclass::FillInputPortInformation(i, info);
253 }
254 
255 //----------------------------------------------------------------------------
CheckArrays(vtkDataArray * array1,vtkDataArray * array2)256 bool vtkMoleculeAppend::CheckArrays(vtkDataArray* array1, vtkDataArray* array2)
257 {
258   if (strcmp(array1->GetName(), array2->GetName()))
259   {
260     vtkErrorMacro(<< "Execute: input name (" << array1->GetName() << "), must match output name ("
261                   << array2->GetName()
262                   << ")");
263     return false;
264   }
265 
266   if (array1->GetDataType() != array2->GetDataType())
267   {
268     vtkErrorMacro(<< "Execute: input ScalarType (" << array1->GetDataType()
269                   << "), must match output ScalarType ("
270                   << array2->GetDataType()
271                   << ")");
272     return false;
273   }
274 
275   if (array1->GetNumberOfComponents() != array2->GetNumberOfComponents())
276   {
277     vtkErrorMacro("Components of the inputs do not match");
278     return false;
279   }
280 
281   return true;
282 }
283