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