vtkMoleculeMapper.cxx 30.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkMoleculeMapper.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkMoleculeMapper.h"

#include "vtkActor.h"
#include "vtkColor.h"
#include "vtkCylinderSource.h"
#include "vtkDataArray.h"
21
#include "vtkDoubleArray.h"
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "vtkEventForwarderCommand.h"
#include "vtkExecutive.h"
#include "vtkFloatArray.h"
#include "vtkGlyph3DMapper.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkLookupTable.h"
#include "vtkMath.h"
#include "vtkMolecule.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPeriodicTable.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
36
#include "vtkPolyDataMapper.h"
37
38
39
40
41
42
43
44
45
46
47
#include "vtkSelection.h"
#include "vtkSelectionNode.h"
#include "vtkSphereSource.h"
#include "vtkTransform.h"
#include "vtkTransformPolyDataFilter.h"
#include "vtkTrivialProducer.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnsignedShortArray.h"
#include "vtkVector.h"
#include "vtkVectorOperators.h"

48
49
50
51
52

// Note this class may have an accelerated subclass ala
// vtkOpenGLMoleculeMapper. If you change this class please
// also check that class for impacts.
vtkObjectFactoryNewMacro( vtkMoleculeMapper );
53
54
55
56
57
58

//----------------------------------------------------------------------------
vtkMoleculeMapper::vtkMoleculeMapper()
  : RenderAtoms(true),
    AtomicRadiusType(VDWRadius),
    AtomicRadiusScaleFactor(0.3),
59
    AtomicRadiusArrayName(nullptr),
60
    RenderBonds(true),
61
    BondColorMode(DiscreteByAtom),
62
    UseMultiCylindersForBonds(true),
63
64
    BondRadius(0.075),
    RenderLattice(true)
65
66
{
  // Initialize ivars:
67
  this->BondColor[0] = this->BondColor[1] = this->BondColor[2] = 50;
68
  this->LatticeColor[0] = this->LatticeColor[1] = this->LatticeColor[2] = 255;
69
  this->SetAtomicRadiusArrayName("radii");
70
71
72
73
74
75
76

  // Setup glyph sources
  vtkNew<vtkSphereSource> sphere;
  sphere->SetRadius(1.0);
  sphere->SetPhiResolution(50);
  sphere->SetThetaResolution(50);
  sphere->Update();
77
  this->AtomGlyphMapper->SetSourceConnection(sphere->GetOutputPort());
78
79
80
81
82

  vtkNew<vtkCylinderSource> cylinder;
  cylinder->SetRadius(1.0);
  cylinder->SetResolution(20);
  cylinder->SetHeight(1.0);
83
  cylinder->CappingOff();
84
85
86
87
88
89
  cylinder->Update();
  // Rotate the glyph so that the cylinder is aligned with the x-axis,
  // rather than the y-axis. This makes glyph orientation much easier.
  vtkNew<vtkTransform> cylXform;
  cylXform->RotateWXYZ(90, 0.0, 0.0, 1.0);
  vtkNew<vtkTransformPolyDataFilter> cylXformFilter;
90
  cylXformFilter->SetInputConnection(cylinder->GetOutputPort());
91
  cylXformFilter->SetTransform(cylXform);
92
  cylXformFilter->Update();
93
  this->BondGlyphMapper->SetSourceConnection(cylXformFilter->GetOutputPort());
94

95
  // Configure default LookupTable
96
  vtkNew<vtkLookupTable> lut;
97
98
  this->PeriodicTable->GetDefaultLUT(lut);
  this->SetLookupTable(lut);
99
100

  // Setup glyph mappers
101
102
103
104
105
106
107
108
109
110
111
112
  this->AtomGlyphMapper->SetScalarRange
    (0, this->PeriodicTable->GetNumberOfElements());
  this->AtomGlyphMapper->SetColorModeToMapScalars();
  this->AtomGlyphMapper->SetScalarModeToUsePointFieldData();
  this->AtomGlyphMapper->SetScaleModeToScaleByMagnitude();
  this->BondGlyphMapper->SetScaleModeToScaleByVectorComponents();
  // Bond color mode is setup during updates

  // Forward commands to instance mappers
  vtkNew<vtkEventForwarderCommand> cb;
  cb->SetTarget(this);

113
114
  this->AtomGlyphMapper->AddObserver(vtkCommand::StartEvent, cb);
  this->AtomGlyphMapper->AddObserver(vtkCommand::EndEvent, cb);
115
  this->AtomGlyphMapper->AddObserver(vtkCommand::ProgressEvent,
116
                                     cb);
117

118
119
  this->BondGlyphMapper->AddObserver(vtkCommand::StartEvent, cb);
  this->BondGlyphMapper->AddObserver(vtkCommand::EndEvent, cb);
120
  this->BondGlyphMapper->AddObserver(vtkCommand::ProgressEvent,
121
                                     cb);
122
123

  // Connect the trivial producers to forward the glyph polydata
124
  this->AtomGlyphPointOutput->SetOutput(this->AtomGlyphPolyData);
125
126
127
  this->AtomGlyphMapper->SetInputConnection
    (this->AtomGlyphPointOutput->GetOutputPort());

128
  this->BondGlyphPointOutput->SetOutput(this->BondGlyphPolyData);
129
130
131
  this->BondGlyphMapper->SetInputConnection
    (this->BondGlyphPointOutput->GetOutputPort());

132
  this->LatticeMapper->SetInputData(this->LatticePolyData);
133
134
  this->LatticeMapper->SetColorModeToDefault();

135
136
  // Force the glyph data to be generated on the next render:
  this->GlyphDataInitialized = false;
137
138
139
140

  this->SetInputArrayToProcess(0, 0, 0,
                               vtkDataObject::FIELD_ASSOCIATION_VERTICES,
                               "Atomic Numbers");
141
142
143
144
145
}

//----------------------------------------------------------------------------
vtkMoleculeMapper::~vtkMoleculeMapper()
{
146
  this->SetLookupTable(nullptr);
Nicolas Vuaille's avatar
Nicolas Vuaille committed
147
  this->SetAtomicRadiusArrayName(nullptr);
148
149
150
}

//----------------------------------------------------------------------------
151
void vtkMoleculeMapper::SetInputData(vtkMolecule *input)
152
{
153
  this->SetInputDataInternal(0, input);
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
}

//----------------------------------------------------------------------------
vtkMolecule *vtkMoleculeMapper::GetInput()
{
  return vtkMolecule::SafeDownCast(this->GetExecutive()->GetInputData(0, 0));
}

//----------------------------------------------------------------------------
void vtkMoleculeMapper::UseBallAndStickSettings()
{
  this->SetRenderAtoms(true);
  this->SetRenderBonds(true);
  this->SetAtomicRadiusType( VDWRadius );
  this->SetAtomicRadiusScaleFactor( 0.3 );
169
  this->SetBondColorMode( DiscreteByAtom );
170
171
172
173
174
175
176
177
178
179
180
  this->SetUseMultiCylindersForBonds( true );
  this->SetBondRadius( 0.075 );
}

//----------------------------------------------------------------------------
void vtkMoleculeMapper::UseVDWSpheresSettings()
{
  this->SetRenderAtoms(true);
  this->SetRenderBonds(true);
  this->SetAtomicRadiusType( VDWRadius );
  this->SetAtomicRadiusScaleFactor( 1.0 );
181
  this->SetBondColorMode( DiscreteByAtom );
182
183
184
185
186
  this->SetUseMultiCylindersForBonds( true );
  this->SetBondRadius( 0.075 );
}

//----------------------------------------------------------------------------
187
void vtkMoleculeMapper::UseLiquoriceStickSettings()
188
189
190
191
192
{
  this->SetRenderAtoms(true);
  this->SetRenderBonds(true);
  this->SetAtomicRadiusType( UnitRadius );
  this->SetAtomicRadiusScaleFactor( 0.15 );
193
  this->SetBondColorMode( DiscreteByAtom );
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  this->SetUseMultiCylindersForBonds( false );
  this->SetBondRadius( 0.15 );
}

//----------------------------------------------------------------------------
void vtkMoleculeMapper::UseFastSettings()
{
  this->SetRenderAtoms(true);
  this->SetRenderBonds(true);
  this->SetAtomicRadiusType( UnitRadius );
  this->SetAtomicRadiusScaleFactor( 0.60 );
  this->SetBondColorMode( SingleColor );
  this->SetBondColor( 50, 50, 50 );
  this->SetUseMultiCylindersForBonds( false );
  this->SetBondRadius( 0.075 );
}

//----------------------------------------------------------------------------
const char * vtkMoleculeMapper::GetAtomicRadiusTypeAsString()
{
  switch (this->AtomicRadiusType)
215
  {
216
217
218
219
220
221
    case CovalentRadius:
      return "CovalentRadius";
    case VDWRadius:
      return "VDWRadius";
    case UnitRadius:
      return "UnitRadius";
222
223
    case CustomArrayRadius:
      return "CustomArrayRadius";
224
225
    default:
      return "Invalid";
226
  }
227
228
229
230
231
232
}

//----------------------------------------------------------------------------
const char * vtkMoleculeMapper::GetBondColorModeAsString()
{
  switch (this->BondColorMode)
233
  {
234
235
236
237
238
239
    case SingleColor:
      return "SingleColor";
    case DiscreteByAtom:
      return "DiscreteByAtom";
    default:
      return "Invalid";
240
  }
241
242
243
244
245
246
247
248
}

//----------------------------------------------------------------------------
void vtkMoleculeMapper::GetSelectedAtomsAndBonds(vtkSelection *selection,
                                                 vtkIdTypeArray *atomIds,
                                                 vtkIdTypeArray *bondIds)
{
  // Sanity check
249
  if (selection == nullptr || (atomIds == nullptr && bondIds == nullptr) )
250
  {
251
    return;
252
  }
253
254

  // Clear the inputs
255
  if (atomIds != nullptr)
256
  {
257
    atomIds->Reset();
258
  }
259
  if (bondIds != nullptr)
260
  {
261
    bondIds->Reset();
262
  }
263
264
265
266
267
268
269
270

  const vtkIdType numAtoms = this->GetInput()->GetNumberOfAtoms();
  const vtkIdType numBonds = this->GetInput()->GetNumberOfBonds();
  const vtkIdType numAtomsAndBonds = numAtoms + numBonds;

  // Find selection node that we're interested in:
  const vtkIdType numNodes = selection->GetNumberOfNodes();
  for (vtkIdType nodeId = 0; nodeId < numNodes; ++nodeId)
271
  {
272
273
274
275
276
277
    vtkSelectionNode *node = selection->GetNode(nodeId);

    // Check if the mapper is this instance of MoleculeMapper
    vtkActor *selActor = vtkActor::SafeDownCast(
               node->GetProperties()->Get(vtkSelectionNode::PROP()));
    if (selActor && (selActor->GetMapper() == this))
278
    {
279
      // Separate the selection ids into atoms and bonds
280
      vtkIdTypeArray *selIds = vtkArrayDownCast<vtkIdTypeArray>(
281
282
            node->GetSelectionList());
      if (selIds)
283
      {
284
285
        vtkIdType numIds = selIds->GetNumberOfTuples();
        for (vtkIdType i = 0; i < numIds; ++i)
286
        {
287
          vtkIdType curId = selIds->GetValue(i);
288
          if (atomIds != nullptr && curId < numAtoms) // atoms
289
          {
290
            atomIds->InsertNextValue(curId);
291
          }
292
          else if (bondIds != nullptr && curId < numAtomsAndBonds)// bonds
293
          {
294
295
296
297
298
299
300
            // Remove offset
            curId -= numAtoms;
            bondIds->InsertNextValue(curId);
          }
        }
      }
    }
301
  }
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
}

//----------------------------------------------------------------------------
void vtkMoleculeMapper::Render(vtkRenderer *ren, vtkActor *act )
{
  // If we add more rendering backend (e.g. point sprites), add a switch here.
  this->GlyphRender(ren, act);
}

//----------------------------------------------------------------------------
void vtkMoleculeMapper::GlyphRender(vtkRenderer *ren, vtkActor *act)
{
  // Update cached polydata if needed
  this->UpdateGlyphPolyData();

  // Pass rendering call on
  if (this->RenderAtoms)
319
  {
320
    this->AtomGlyphMapper->Render(ren, act);
321
  }
322
323

  if (this->RenderBonds)
324
  {
325
    this->BondGlyphMapper->Render(ren, act);
326
  }
327
328

  if (this->RenderLattice)
329
  {
330
    this->LatticeMapper->Render(ren, act);
331
  }
332
333
334
335
336
337
338
339
}

//----------------------------------------------------------------------------
void vtkMoleculeMapper::UpdateGlyphPolyData()
{
  vtkMolecule *molecule = this->GetInput();

  if (!this->GlyphDataInitialized || (
340
        (molecule->GetMTime() > this->AtomGlyphPolyData->GetMTime() ||
341
342
         this->GetMTime() > this->AtomGlyphPolyData->GetMTime() ||
         this->LookupTable->GetMTime() > this->AtomGlyphPolyData->GetMTime()) &&
343
        this->RenderAtoms))
344
  {
345
    this->UpdateAtomGlyphPolyData();
346
  }
347
348

  if (!this->GlyphDataInitialized || (
349
        (molecule->GetMTime() > this->BondGlyphPolyData->GetMTime() ||
350
351
         this->GetMTime() > this->BondGlyphPolyData->GetMTime() ||
         this->LookupTable->GetMTime() > this->BondGlyphPolyData->GetMTime()) &&
352
        this->RenderBonds))
353
  {
354
    this->UpdateBondGlyphPolyData();
355
  }
356

357
358
359
360
  if (!this->GlyphDataInitialized || (
        (molecule->GetMTime() > this->LatticePolyData->GetMTime() ||
         this->GetMTime() > this->LatticePolyData->GetMTime()) &&
        this->RenderLattice))
361
  {
362
    this->UpdateLatticePolyData();
363
  }
364

365
366
367
368
369
370
371
372
373
374
375
  this->GlyphDataInitialized = true;
}

//----------------------------------------------------------------------------
// Generate scale and position information for each atom sphere
void vtkMoleculeMapper::UpdateAtomGlyphPolyData()
{
  this->AtomGlyphPolyData->Initialize();

  vtkMolecule *molecule = this->GetInput();

376
  int assoc; // output var for GetInputAbstractArrayToProcess
377
  vtkAbstractArray *wholeColorArray =
378
      this->GetInputAbstractArrayToProcess(0, molecule, assoc);
379
380
381
382
383
384
385
386
387

  vtkAbstractArray *colorArray = wholeColorArray != nullptr ? wholeColorArray->NewInstance() : nullptr;

  vtkNew<vtkUnsignedShortArray> atomicNbWithoutGhostArray;
  vtkUnsignedShortArray* atomicNbFullArray = molecule->GetAtomicNumberArray();
  vtkNew<vtkPoints> points;
  vtkPoints* allPoints = molecule->GetAtomicPositionArray();
  vtkUnsignedCharArray* ghosts = molecule->GetAtomGhostArray();
  for (vtkIdType i = 0; i < molecule->GetNumberOfAtoms(); i++)
388
  {
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    /**
     * Skip ghost atoms but not ghost bonds:
     *  - each atom is non-ghost for exactly one MPI node, that will handle it.
     *  - a ghost bond links an atom and a ghost atom. So there is exactly two MPI nodes
     *    that contain this ghost bond and no one that contains this bond as non-ghost.
     *    We let this two MPI nodes handle the ghost bond, as we cannot know if the bond
     *    was already handled.
     */
    if (ghosts && ghosts->GetValue(i) == 1)
    {
      continue;
    }
    atomicNbWithoutGhostArray->InsertNextValue(atomicNbFullArray->GetValue(i));
    points->InsertNextPoint(allPoints->GetPoint(i));
    if (colorArray != nullptr)
    {
      colorArray->InsertNextTuple(i, wholeColorArray);
    }
  }
  const vtkIdType numAtoms = points->GetNumberOfPoints();

  if (colorArray != nullptr)
  {
    if (colorArray->GetNumberOfTuples() != numAtoms)
413
    {
414
      vtkErrorMacro("Color array size does not match number of atoms.");
415
    }
416
    else
417
    {
418
419
420
421
      int colorArrayIdx =
          this->AtomGlyphPolyData->GetPointData()->AddArray(colorArray);
      this->AtomGlyphMapper->SelectColorArray(colorArrayIdx);
    }
422
    colorArray->Delete();
423
  }
424

425
  this->AtomGlyphPolyData->SetPoints(points.Get());
426
  this->AtomGlyphMapper->SetLookupTable(this->LookupTable);
427
428
429
430
431
432
433

  vtkNew<vtkFloatArray> scaleFactors;
  scaleFactors->SetNumberOfComponents(1);
  scaleFactors->SetName("Scale Factors");
  scaleFactors->Allocate(numAtoms);

  switch (this->AtomicRadiusType)
434
  {
435
436
437
    default:
      vtkWarningMacro(<<"Unknown radius type: " << this->AtomicRadiusType
                      <<". Falling back to 'VDWRadius' ("<<VDWRadius<<").");
438
      VTK_FALLTHROUGH;
439
440
    case VDWRadius:
      for (vtkIdType i = 0; i < numAtoms; ++i)
441
      {
442
        scaleFactors->InsertNextValue(this->AtomicRadiusScaleFactor *
443
          this->PeriodicTable->GetVDWRadius(atomicNbWithoutGhostArray->GetValue(i)));
444
      }
445
446
447
      break;
    case CovalentRadius:
      for (vtkIdType i = 0; i < numAtoms; ++i)
448
      {
449
        scaleFactors->InsertNextValue(this->AtomicRadiusScaleFactor *
450
          this->PeriodicTable->GetCovalentRadius(atomicNbWithoutGhostArray->GetValue(i)));
451
      }
452
453
454
      break;
    case UnitRadius:
      for (vtkIdType i = 0; i < numAtoms; ++i)
455
      {
456
        scaleFactors->InsertNextValue(this->AtomicRadiusScaleFactor);
457
      }
458
      break;
459
    case CustomArrayRadius: {
460
      vtkDataArray *allRadii = molecule->GetVertexData()->GetArray(this->AtomicRadiusArrayName);
461
      if (!allRadii)
462
      {
463
        vtkWarningMacro("AtomicRadiusType set to CustomArrayRadius, but no "
464
                        "array named " << this->AtomicRadiusArrayName << " found in input VertexData.");
465
466
        scaleFactors->SetNumberOfTuples(numAtoms);
        scaleFactors->FillComponent(0, this->AtomicRadiusScaleFactor);
467
468
469
470
471
472
473
474
475
476
        break;
      }
      vtkNew<vtkDoubleArray> radii;
      for (vtkIdType i = 0; i < molecule->GetNumberOfAtoms(); i++)
      {
        if (ghosts && ghosts->GetValue(i) == 1)
        {
          continue;
        }
        radii->InsertNextValue(allRadii->GetTuple1(i));
477
      }
478
      if (radii->GetNumberOfTuples() != numAtoms)
479
      {
480
481
482
483
        vtkWarningMacro("'radii' array contains " << radii->GetNumberOfTuples()
                        << " entries, but there are " << numAtoms << " atoms.");
        scaleFactors->SetNumberOfTuples(numAtoms);
        scaleFactors->FillComponent(0, this->AtomicRadiusScaleFactor);
484
      }
485
      else
486
      {
487
488
489
        scaleFactors->DeepCopy(radii);
        scaleFactors->SetName("Scale Factors"); // copy resets name.
      }
490
      break;
491
    }
492
  }
493

494
  this->AtomGlyphPolyData->GetPointData()->AddArray(scaleFactors);
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
  this->AtomGlyphMapper->SetScaleArray("Scale Factors");
}

//----------------------------------------------------------------------------
// Generate position, scale, and orientation vectors for each bond cylinder
void vtkMoleculeMapper::UpdateBondGlyphPolyData()
{
  this->BondGlyphPolyData->Initialize();

  vtkMolecule *molecule = this->GetInput();
  const vtkIdType numBonds = molecule->GetNumberOfBonds();

  // For selection ID offset:
  const vtkIdType numAtoms = molecule->GetNumberOfAtoms();

  // Create arrays
  vtkNew<vtkPoints> cylCenters;
  vtkNew<vtkFloatArray> cylScales;
  vtkNew<vtkFloatArray> orientationVectors;
  // Since vtkHardwareSelector won't distinguish between the internal instance
  // mappers of vtkMoleculeMapper, use a custom selection ID range. This also
  // fixes the issue of bonds that are colored-by-atom, as these are rendered
  // as two glyphs.
  vtkNew<vtkIdTypeArray> selectionIds;

  // Setup arrays
  // vtkPoints uses three components by default
  cylScales->SetNumberOfComponents(3);
  orientationVectors->SetNumberOfComponents(3);
  selectionIds->SetNumberOfComponents(1);

  // Name arrays
  // Can't set name for points
  cylScales->SetName("Scale Factors");
  orientationVectors->SetName("Orientation Vectors");
  selectionIds->SetName("Selection Ids");

  // Allocate memory -- find out how many cylinders are needed
  vtkIdType numCylinders = numBonds;
  // Up to three cylinders per bond if multicylinders are enabled:
  if (this->UseMultiCylindersForBonds)
536
  {
537
    numCylinders *= 3;
538
  }
539
540
  // If DiscreteByAtom coloring is used, each cylinder is represented
  // by two individual cylinders
541
  if (this->BondColorMode == DiscreteByAtom)
542
  {
543
    numCylinders *= 2;
544
  }
545
546
547
548
549
550
551
552

  // Allocate memory. Multiply numCylinders by number of components in array.
  cylCenters->Allocate(3*numCylinders);
  cylScales->Allocate(3*numCylinders);
  orientationVectors->Allocate(3*numCylinders);
  selectionIds->Allocate(numCylinders);

  // Add arrays to BondGlyphPolyData
553
554
  this->BondGlyphPolyData->SetPoints(cylCenters);
  this->BondGlyphPolyData->GetPointData()->AddArray(cylScales);
555
  this->BondGlyphPolyData->GetPointData()->AddArray(
556
    orientationVectors);
557
  this->BondGlyphPolyData->GetPointData()->AddArray(
558
        selectionIds);
559
560

  // Set up coloring mode
561
  vtkDataArray *cylColors = nullptr;
562
  switch(this->BondColorMode)
563
  {
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
    case SingleColor:
      cylColors = vtkUnsignedCharArray::New();
      cylColors->SetNumberOfComponents(3);
      cylColors->Allocate(3*numCylinders);
      cylColors->SetName("Colors");
      this->BondGlyphPolyData->GetPointData()->SetScalars(cylColors);
      this->BondGlyphMapper->SetColorModeToDefault();
      this->BondGlyphMapper->SetScalarModeToUsePointData();
      break;
    default:
    case DiscreteByAtom:
      cylColors = vtkUnsignedShortArray::New();
      cylColors->SetNumberOfComponents(1);
      cylColors->Allocate(numCylinders);
      cylColors->SetName("Colors");
      this->BondGlyphPolyData->GetPointData()->SetScalars(cylColors);
580
      this->BondGlyphMapper->SetLookupTable(this->LookupTable);
581
582
583
584
585
      this->BondGlyphMapper->SetScalarRange
        (0, this->PeriodicTable->GetNumberOfElements());
      this->BondGlyphMapper->SetScalarModeToUsePointData();
      this->AtomGlyphMapper->SetColorModeToMapScalars();
      break;
586
  }
587
588
589

  // Set up pointers to the specific color arrays
  vtkUnsignedCharArray *singleColorArray =
590
    vtkArrayDownCast<vtkUnsignedCharArray>(cylColors);
591
  vtkUnsignedShortArray *discreteColorArray =
592
    vtkArrayDownCast<vtkUnsignedShortArray>(cylColors);
593
594
595
596
597
598
599

  // Remove reference to cylColors
  cylColors->Delete();

  // Declare some variables for later
  unsigned short bondOrder;
  float bondLength;
luz.paz's avatar
luz.paz committed
600
  // Since the input cylinder is oriented along the z axis, the
601
602
603
604
605
606
607
608
609
  // scale vector should be [radius, radius, bondLength]
  vtkVector3f scale;
  // Current cylinder's selection id
  vtkIdType selectionId;
  // Distance between multicylinder surfaces is approx. 1/3 of the
  // diameter:
  const float deltaLength = this->BondRadius * 2.6;
  // Vector between centers cylinders in a multibond:
  vtkVector3f delta;
610
  delta.Set(0.0, 0.0, 0.0);
611
612
  // The initial displacement when generating a multibond
  vtkVector3f initialDisp;
613
  initialDisp.Set(0.0, 0.0, 0.0);
614
615
616
617
618
619
620
621
622
623
624
625
626
  // The geometric center of the bond
  vtkVector3f bondCenter;
  // The center of the current cylinder
  vtkVector3f cylinderCenter;
  // Used in DiscreteByAtom color mode:
  vtkVector3f halfCylinderCenter;
  // The positions of the start and end atoms
  vtkVector3f pos1, pos2;
  // Array containing the atomic numbers of {begin, end} atoms
  unsigned short atomicNumbers[2];
  // Normalized vector pointing along bond from begin->end atom
  vtkVector3f bondVec;
  // Unit z vector -- used for multicylinder orientation
627
  static const vtkVector3f unitZ (0.0, 0.0, 1.0);
628
629
630
631
632
633
634
635
636
  // Can't use InsertNextTuple(unsigned char *) in a
  // vtkUnsignedCharArray. This float array is used instead.
  // Initialize with bondColor for SingleColor mode
  vtkColor3f bondColorf (static_cast<float>(this->BondColor[0]),
                         static_cast<float>(this->BondColor[1]),
                         static_cast<float>(this->BondColor[2]));

  // Generate the scale, orientation, and position of each cylinder
  for (vtkIdType bondInd = 0; bondInd < numBonds; ++bondInd)
637
  {
638
639
640
    selectionId = numAtoms + bondInd; // mixing 1 and 0 indexed ids on purpose
    // Extract bond info
    vtkBond bond = molecule->GetBond(bondInd);
641
642
643
    bondOrder = bond.GetOrder();
    pos1 = bond.GetBeginAtom().GetPosition();
    pos2 = bond.GetEndAtom().GetPosition();
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
    atomicNumbers[0] = bond.GetBeginAtom().GetAtomicNumber();
    atomicNumbers[1] = bond.GetEndAtom().GetAtomicNumber();

    // Compute additional bond info
    // - Normalized vector in direction of bond
    bondVec = pos2 - pos1;
    bondLength = bondVec.Normalize();
    // - Center of bond for translation
    // TODO vtkVector scalar multiplication
//    bondCenter = (pos1 + pos2) * 0.5;
    bondCenter[0] = (pos1[0] + pos2[0]) * 0.5;
    bondCenter[1] = (pos1[1] + pos2[1]) * 0.5;
    bondCenter[2] = (pos1[2] + pos2[2]) * 0.5;
    // end vtkVector TODO

    // Set up delta step vector and bond radius from bond order:
    if (this->UseMultiCylindersForBonds)
661
    {
662
      switch (bondOrder)
663
      {
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
        case 1:
        default:
          delta.Set(0.0, 0.0, 0.0);
          initialDisp.Set(0.0, 0.0, 0.0);
          break;
        case 2:
          // TODO vtkVector scalar multiplication
//          delta = bondVec.Cross(unitZ).Normalized() * deltaLength;
//          initialDisp = delta * (-0.5);
          delta = bondVec.Cross(unitZ).Normalized();
          delta[0] *= deltaLength;
          delta[1] *= deltaLength;
          delta[2] *= deltaLength;
          initialDisp.Set(delta[0]*(-0.5), delta[1]*(-0.5), delta[2]*(-0.5));
          // End vtkVector TODO
          break;
        case 3:
          // TODO vtkVector scalar multiplication, negation
//          delta = bondVec.Cross(unitZ).Normalized() * deltaLength;
//          initialDisp = -delta;
          delta = bondVec.Cross(unitZ).Normalized();
          delta[0] *= deltaLength;
          delta[1] *= deltaLength;
          delta[2] *= deltaLength;
          initialDisp.Set(-delta[0], -delta[1], -delta[2]);
          // End vtkVector TODO
          break;
      }
692
    }
693
694
695

    // Set up cylinder scale factors
    switch (this->BondColorMode)
696
    {
697
698
699
700
701
702
703
      case SingleColor:
        scale.Set( bondLength, this->BondRadius, this->BondRadius);
        break;
      default:
      case DiscreteByAtom:
        scale.Set( 0.5 * bondLength, this->BondRadius, this->BondRadius);
        break;
704
    }
705
706

    if (this->UseMultiCylindersForBonds)
707
    {
708
      cylinderCenter = bondCenter + initialDisp;
709
    }
710
    else
711
    {
712
      cylinderCenter = bondCenter;
713
    }
714
715
716
717

    // For each bond order, add a point to the glyph points, translate
    // by delta, and repeat.
    for (unsigned short iter = 0; iter < bondOrder; ++iter)
718
    {
719
720
721
722
      // Single color mode adds a single cylinder, while
      // DiscreteByAtom adds two differently colored and positioned
      // cylinders.
      switch (this->BondColorMode)
723
      {
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
        case SingleColor:
          cylCenters->InsertNextPoint(cylinderCenter.GetData());
          cylScales->InsertNextTuple(scale.GetData());
          singleColorArray->InsertNextTuple(bondColorf.GetData());
          orientationVectors->InsertNextTuple(bondVec.GetData());
          selectionIds->InsertNextValue(selectionId);
          break;
        default:
        case DiscreteByAtom:
          // Cache some scaling factors
          const float quarterLength = 0.25 * bondLength;

          // Add cylinder for begin atom:
          // TODO vtkVector subtraction, scalar multiplication
//          halfCylinderCenter = cylinderCenter - (bondVec * quarterLength);
          halfCylinderCenter[0] = cylinderCenter[0] -
              (bondVec[0] * quarterLength);
          halfCylinderCenter[1] = cylinderCenter[1] -
              (bondVec[1] * quarterLength);
          halfCylinderCenter[2] = cylinderCenter[2] -
              (bondVec[2] * quarterLength);
          // end vtkVector TODO

          cylCenters->InsertNextPoint(halfCylinderCenter.GetData());
          cylScales->InsertNextTuple(scale.GetData());
          discreteColorArray->InsertNextValue(atomicNumbers[0]);
          orientationVectors->InsertNextTuple(bondVec.GetData());
          selectionIds->InsertNextValue(selectionId);

          // Add cylinder for begin atom:
          // TODO vtkVector addition, scalar multiplication
//          halfCylinderCenter = cylinderCenter + (bondVec * quarterLength);
          halfCylinderCenter[0] = cylinderCenter[0] +
              (bondVec[0] * quarterLength);
          halfCylinderCenter[1] = cylinderCenter[1] +
              (bondVec[1] * quarterLength);
          halfCylinderCenter[2] = cylinderCenter[2] +
              (bondVec[2] * quarterLength);
          // end vtkVector TODO

          cylCenters->InsertNextPoint(halfCylinderCenter.GetData());
          cylScales->InsertNextTuple(scale.GetData());
          discreteColorArray->InsertNextValue(atomicNumbers[1]);
          orientationVectors->InsertNextTuple(bondVec.GetData());
          selectionIds->InsertNextValue(selectionId);
769
      }
770
771
772

      // Prepare for next multicylinder
      if (this->UseMultiCylindersForBonds && bondOrder != 1)
773
      {
774
775
776
777
778
779
780
781
        // TODO vtkVector in-place addition
//        cylinderCenter += delta;
        cylinderCenter[0] += delta[0];
        cylinderCenter[1] += delta[1];
        cylinderCenter[2] += delta[2];
        // end vtkVector TODO
      }
    }
782
  }
783
784
785
786
787
788
789
790

  // Free up some space
  this->BondGlyphPolyData->Squeeze();

  // Setup glypher
  this->BondGlyphMapper->SetScaleArray("Scale Factors");
  this->BondGlyphMapper->SetOrientationArray("Orientation Vectors");
  this->BondGlyphMapper->SetSelectionIdArray("Selection Ids");
791
  this->BondGlyphMapper->UseSelectionIdsOn();
792
793
}

794
795
796
797
798
799
800
//----------------------------------------------------------------------------
void vtkMoleculeMapper::UpdateLatticePolyData()
{
  this->LatticePolyData->Initialize();

  vtkMolecule *molecule = this->GetInput();
  if (!molecule->HasLattice())
801
  {
802
    return;
803
  }
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821

  vtkVector3d a;
  vtkVector3d b;
  vtkVector3d c;
  vtkVector3d origin;

  molecule->GetLattice(a, b, c, origin);

  vtkNew<vtkPoints> points;
  points->SetNumberOfPoints(8);
  points->SetPoint(0, origin.GetData());
  points->SetPoint(1, (origin + a).GetData());
  points->SetPoint(2, (origin + b).GetData());
  points->SetPoint(3, (origin + c).GetData());
  points->SetPoint(4, (origin + a + b).GetData());
  points->SetPoint(5, (origin + a + c).GetData());
  points->SetPoint(6, (origin + b + c).GetData());
  points->SetPoint(7, (origin + a + b + c).GetData());
822
  this->LatticePolyData->SetPoints(points);
823
824
825
826
827

  vtkNew<vtkUnsignedCharArray> latticeColors;
  latticeColors->SetNumberOfComponents(3);
  latticeColors->SetNumberOfTuples(8);
  for (vtkIdType i = 0; i < 8; ++i)
828
  {
829
    latticeColors->SetTypedTuple(i, this->LatticeColor);
830
  }
831
  this->LatticePolyData->GetPointData()->SetScalars(latticeColors);
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872

  vtkNew<vtkCellArray> lines;
  vtkIdType line[2];

  line[0] = 0;
  line[1] = 1;
  lines->InsertNextCell(2, line);
  line[0] = 1;
  line[1] = 4;
  lines->InsertNextCell(2, line);
  line[0] = 4;
  line[1] = 2;
  lines->InsertNextCell(2, line);
  line[0] = 2;
  line[1] = 0;
  lines->InsertNextCell(2, line);
  line[0] = 0;
  line[1] = 3;
  lines->InsertNextCell(2, line);
  line[0] = 2;
  line[1] = 6;
  lines->InsertNextCell(2, line);
  line[0] = 4;
  line[1] = 7;
  lines->InsertNextCell(2, line);
  line[0] = 1;
  line[1] = 5;
  lines->InsertNextCell(2, line);
  line[0] = 6;
  line[1] = 3;
  lines->InsertNextCell(2, line);
  line[0] = 5;
  line[1] = 3;
  lines->InsertNextCell(2, line);
  line[0] = 5;
  line[1] = 7;
  lines->InsertNextCell(2, line);
  line[0] = 6;
  line[1] = 7;
  lines->InsertNextCell(2, line);

873
  this->LatticePolyData->SetLines(lines);
874
875
}

876
877
878
879
880
881
882
883
884
885
886
//----------------------------------------------------------------------------
void vtkMoleculeMapper::ReleaseGraphicsResources(vtkWindow *w)
{
  this->AtomGlyphMapper->ReleaseGraphicsResources(w);
  this->BondGlyphMapper->ReleaseGraphicsResources(w);
}

double *vtkMoleculeMapper::GetBounds()
{
  vtkMolecule *input = this->GetInput();
  if (!input)
887
  {
888
    vtkMath::UninitializeBounds(this->Bounds);
889
  }
890
  else
891
  {
892
    if (!this->Static)
893
    {
894
      this->Update();
895
    }
896
897
898
899
900
901
902
903
    input->GetBounds(this->Bounds);
    // Pad bounds by 3 Angstrom to contain spheres, etc
    this->Bounds[0] -= 3.0;
    this->Bounds[1] += 3.0;
    this->Bounds[2] -= 3.0;
    this->Bounds[3] += 3.0;
    this->Bounds[4] -= 3.0;
    this->Bounds[5] += 3.0;
904
  }
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
  return this->Bounds;
}

//----------------------------------------------------------------------------
int vtkMoleculeMapper::FillInputPortInformation(int vtkNotUsed(port),
                                                vtkInformation* info)
{
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkMolecule");
  return 1;
}

//----------------------------------------------------------------------------
void vtkMoleculeMapper::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "AtomGlyphMapper:\n";
  this->AtomGlyphMapper->PrintSelf(os, indent.GetNextIndent());

  os << indent << "BondGlyphMapper:\n";
  this->BondGlyphMapper->PrintSelf(os, indent.GetNextIndent());
}