1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkParallelopipedRepresentation.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 "vtkParallelopipedRepresentation.h"
16 
17 #include "vtkSmartPointer.h"
18 #include "vtkActor.h"
19 #include "vtkCamera.h"
20 #include "vtkCellArray.h"
21 #include "vtkDoubleArray.h"
22 #include "vtkMath.h"
23 #include "vtkObjectFactory.h"
24 #include "vtkPolyData.h"
25 #include "vtkPolyDataMapper.h"
26 #include "vtkProperty.h"
27 #include "vtkRenderWindowInteractor.h"
28 #include "vtkRenderer.h"
29 #include "vtkInteractorObserver.h"
30 #include "vtkEvent.h"
31 #include "vtkSphereHandleRepresentation.h"
32 #include "vtkLine.h"
33 #include "vtkClosedSurfacePointPlacer.h"
34 #include "vtkPlaneCollection.h"
35 #include "vtkPlane.h"
36 #include <vector>
37 #include <set>
38 #include <algorithm>
39 
40 //----------------------------------------------------------------------------
41 // This class manages topological information for a parallelopiped with a
42 // chair etched out at any node.
43 // README : Uncomment the line that reads "PrintTopology(cout) to
44 //          understand what the class does. The goal of the class is succintly
45 //          described in that one line.
46 class vtkParallelopipedTopology
47 {
48 public:
49   typedef struct Line { vtkIdType Id[2];
LinevtkParallelopipedTopology::Line50           Line(vtkIdType a, vtkIdType b) { Id[0]=a; Id[1]=b; } }  LineType;
51   typedef std::vector< vtkIdType >                             CellType;
52   typedef std::vector< CellType  >                             CliqueType;
53 
54   // Diametric opposite of Corner 0 = 6, 1 = 7, 2 = 4, 3 = 5.
55   // Mathematically, if a diametric corner is represented by a 3 bit value:
56   // abc, its diametric opposite = a'b'c.
GetDiametricOppositeOfCorner(int i)57   static int GetDiametricOppositeOfCorner( int i )
58   {
59     return ((~i) & 0x6) | (i & 0x1);
60   }
61 
62   // Get the corners connected to corner 'i'. There will be three such corners
GetNeighbors(int c,vtkIdType neighborPtIds[3],int configuration=0) const63   void GetNeighbors( int c, vtkIdType neighborPtIds[3], int configuration = 0 ) const
64   {
65     std::set< vtkIdType > neighbors;
66     const CliqueType & clique = m_Topology[configuration];
67     for (CliqueType::const_iterator clit = clique.begin();
68          clit != clique.end(); ++clit)
69     {
70       if (std::find(clit->begin(), clit->end(), c) != clit->end())
71       {
72         const CellType cell = RotateCell( *clit, c );
73         neighbors.insert(cell[0]);
74         neighbors.insert(cell[cell.size()-2]);
75       }
76     }
77     int i = 0;
78     for (std::set< vtkIdType >::const_iterator it = neighbors.begin();
79          it != neighbors.end(); neighborPtIds[i++] = *it, ++it)
80     {
81       ;
82     }
83   }
84 
GetNeighbors(vtkIdType node,vtkIdType neighborPtIds[3],vtkCellArray * neighborCells,std::vector<LineType> & lines)85   void GetNeighbors( vtkIdType node, vtkIdType neighborPtIds[3],
86       vtkCellArray *neighborCells, std::vector< LineType > & lines )
87   {
88     GetNeighbors( 8 + GetDiametricOppositeOfCorner(node), neighborPtIds, node+1 );
89     vtkIdType opposingNeighborPtIds[3],
90               opposite = GetDiametricOppositeOfCorner(node);
91     GetNeighbors( opposite, opposingNeighborPtIds );
92 
93     std::vector< vtkIdType > nodes(2);
94     for (int i = 0; i < 3; i++)
95     {
96       nodes[0] = neighborPtIds[i];
97       for (int j = 0; j < 3; j++)
98       {
99         nodes[1] = opposingNeighborPtIds[j];
100         const CliqueType cells =
101           FindCellsContainingNodes( m_Topology[node+1], nodes );
102         if (!cells.empty())
103         {
104           PopulateTopology( cells, neighborCells );
105           lines.push_back( LineType(opposite, nodes[1]) );
106         }
107       }
108     }
109   }
110 
FindCellsContainingNodes(int configuration,vtkCellArray * cellArray,const std::vector<vtkIdType> & nodes) const111   void FindCellsContainingNodes( int configuration, vtkCellArray *cellArray,
112                         const std::vector< vtkIdType > & nodes ) const
113   {
114     vtkParallelopipedTopology::PopulateTopology(
115       FindCellsContainingNodes( m_Topology[configuration], nodes), cellArray );
116   }
117 
FindCellsContainingNodes(int configuration,const std::vector<vtkIdType> & nodes)118   std::vector< CellType > FindCellsContainingNodes(
119        int configuration, const std::vector< vtkIdType > & nodes )
120     { return FindCellsContainingNodes( m_Topology[configuration], nodes); }
121 
vtkParallelopipedTopology()122   vtkParallelopipedTopology()
123   {
124 
125     // The topology of a parallelopiped.
126     CliqueType clique;
127     AddCellToClique(clique, 3,0,4,7);
128     AddCellToClique(clique, 1,2,6,5);
129     AddCellToClique(clique, 0,1,5,4);
130     AddCellToClique(clique, 2,3,7,6);
131     AddCellToClique(clique, 0,3,2,1);
132     AddCellToClique(clique, 4,5,6,7);
133     m_Topology.push_back(clique);
134 
135     for ( vtkIdType i = 0; i < 8;
136           m_Topology.push_back( GetChairClique( i++, clique ) ) )
137     {
138       ;
139     }
140 
141     // README : The goal of the class is succintly described by the line below
142     // PrintTopology( cout );
143   }
144 
145   // Populate topoplogy into a vtkCellArray.
146   // If configuration is 0, the topoology populated is that of a parallelopiped.
147   // If configuration > 0, the topology populated is that of a parallelopiped
148   // with a chair at node = (configuration - 1).
PopulateTopology(int configuration,vtkCellArray * cellArray) const149   void PopulateTopology( int configuration, vtkCellArray * cellArray ) const
150     { vtkParallelopipedTopology::PopulateTopology(
151                           m_Topology[configuration], cellArray ); }
152 
PrintTopology(ostream & os) const153   void PrintTopology(ostream &os) const
154   {
155     os << "Connectivity of Point Ids in a parallelopiped: " << endl;
156     PrintClique(m_Topology[0], os);
157     for (int i = 0; i < 8; i++)
158     {
159       os << "Connectivity of Point Ids in a parallelopiped "
160          << "with chair carved out at node: " << i << endl;
161       PrintClique(m_Topology[i+1], os);
162     }
163   }
164 
165 private:
166 
AddCellToClique(CliqueType & clique,vtkIdType a,vtkIdType b,vtkIdType c,vtkIdType d)167   void AddCellToClique( CliqueType & clique,
168       vtkIdType a, vtkIdType b, vtkIdType c, vtkIdType d)
169   {
170     CellType v(4);
171     v[0] = a; v[1] = b; v[2] = c; v[3] = d;
172     clique.push_back(v);
173   }
174 
RotateCell(const CellType & cell,vtkIdType endval)175   static CellType RotateCell( const CellType & cell, vtkIdType endval )
176   {
177     CellType outputCell;
178     for (CellType::const_iterator cit = cell.begin(); cit != cell.end(); ++cit)
179     {
180       outputCell.push_back(*cit);
181       if (*cit == endval) break;
182     }
183     for (CellType::const_reverse_iterator cit = cell.rbegin(); cit != cell.rend(); ++cit)
184     {
185       if (*cit == endval) break;
186       outputCell.insert(outputCell.begin(), *cit);
187     }
188     return outputCell;
189   }
190 
ReverseCell(const CellType & cell)191   static CellType ReverseCell( const CellType & cell )
192   {
193     CellType outputCell;
194     for (CellType::const_reverse_iterator cit = cell.rbegin(); cit != cell.rend(); ++cit)
195       outputCell.push_back(*cit);
196     return outputCell;
197   }
198 
ChairCell(const CellType & cell)199   static CellType ChairCell( const CellType & cell )
200   {
201     CellType outputCell = ReverseCell(cell);
202     for (CellType::iterator cit = outputCell.begin(); cit != outputCell.end(); ++cit)
203       *cit += 8;
204     return outputCell;
205   }
206 
ChairCell(vtkIdType c,const CellType & cell)207   static CellType ChairCell( vtkIdType c, const CellType & cell )
208   {
209     CellType tmpCell = RotateCell(cell, c);
210     CellType::iterator it = tmpCell.end();
211     tmpCell.erase(--it);
212     CellType outputCell = tmpCell;
213     for (CellType::reverse_iterator cit = tmpCell.rbegin(); cit != tmpCell.rend(); ++cit)
214       outputCell.push_back(*cit + 8);
215     return outputCell;
216   }
217 
GetChairClique(vtkIdType c,const CliqueType & clique)218   static CliqueType GetChairClique( vtkIdType c, const CliqueType & clique )
219   {
220     CliqueType outputClique;
221     for (CliqueType::const_iterator clit = clique.begin();
222          clit != clique.end(); ++clit)
223     {
224       if (std::find(clit->begin(), clit->end(), c) == clit->end())
225       {
226         outputClique.insert( outputClique.begin(), *clit );
227         outputClique.push_back( ChairCell(*clit) );
228       }
229       else
230       {
231         outputClique.insert( outputClique.begin(), ChairCell(c, *clit) );
232       }
233     }
234     return outputClique;
235   }
236 
PopulateTopology(const CliqueType & clique,vtkCellArray * cellArray)237   static void PopulateTopology( const CliqueType & clique, vtkCellArray * cellArray )
238   {
239     for (CliqueType::const_iterator clit = clique.begin();
240          clit != clique.end(); ++clit)
241     {
242       vtkIdType *ids = new vtkIdType[clit->size()];
243       int i = 0;
244       for (CellType::const_iterator cit = clit->begin();
245            cit != clit->end(); ids[i++] = *cit, ++cit )
246       {
247         ;
248       }
249       cellArray->InsertNextCell( static_cast<vtkIdType>(clit->size()), ids );
250       delete [] ids;
251     }
252   }
253 
254   // Find all the cells in a given a configuration (specified by the clique)
255   // that contain the nodes. (specified by nodes). Each cell returned must
256   // contain all the nodes specified.
FindCellsContainingNodes(const CliqueType & clique,const std::vector<vtkIdType> & nodes)257   static std::vector< CellType > FindCellsContainingNodes(
258        const CliqueType & clique, const std::vector< vtkIdType > & nodes )
259   {
260     std::vector< CellType > cells;
261     for (CliqueType::const_iterator clit = clique.begin();
262          clit != clique.end(); ++clit)
263     {
264       bool found = true;
265       for (std::vector< vtkIdType >::const_iterator nit = nodes.begin();
266             nit != nodes.end(); ++nit)
267         found &= (std::find(clit->begin(), clit->end(), *nit) != clit->end());
268       if (found) cells.push_back(*clit);
269     }
270     return cells;
271   }
272 
PrintCell(const CellType & cell,ostream & os)273   static void PrintCell( const CellType & cell, ostream &os )
274   {
275     for (CellType::const_iterator cit = cell.begin();
276          cit != cell.end(); os << *cit << " ", ++cit )
277     {
278       ;
279     }
280   }
281 
PrintClique(const CliqueType & clique,ostream & os)282   static void PrintClique( const CliqueType & clique, ostream &os )
283   {
284     os << "  Clique has " << clique.size() << " cells." << endl;
285     for (CliqueType::const_iterator clit = clique.begin();
286          clit != clique.end(); ++clit)
287     {
288       os << "  Cell PtIds: ";
289       PrintCell( *clit, os );
290       os << endl;
291     }
292   }
293 
294   std::vector< CliqueType > m_Topology;
295 };
296 
297 //----------------------------------------------------------------------------
298 vtkStandardNewMacro(vtkParallelopipedRepresentation);
299 
300 vtkCxxSetObjectMacro(vtkParallelopipedRepresentation,
301                      HandleProperty, vtkProperty);
302 vtkCxxSetObjectMacro(vtkParallelopipedRepresentation,
303                      SelectedHandleProperty, vtkProperty);
304 vtkCxxSetObjectMacro(vtkParallelopipedRepresentation,
305                      HoveredHandleProperty, vtkProperty);
306 
307 //----------------------------------------------------------------------------
vtkParallelopipedRepresentation()308 vtkParallelopipedRepresentation::vtkParallelopipedRepresentation()
309 {
310   // This contains all the connectivity information.
311   this->Topology = new vtkParallelopipedTopology;
312 
313   this->LastEventPosition[0] = this->LastEventPosition[1] = 0.0;
314 
315   // Construct the poly data representing the hex
316   this->HexPolyData       = vtkPolyData::New();
317   this->HexMapper         = vtkPolyDataMapper::New();
318   this->HexActor          = vtkActor::New();
319   this->HexMapper->SetInputData(HexPolyData);
320   this->HexActor->SetMapper(this->HexMapper);
321 
322   // 16 points from the parallelopiped and the chair (also modelled as a
323   // parallelopiped).
324   this->Points            = vtkPoints::New(VTK_DOUBLE);
325   this->Points->SetNumberOfPoints(16);
326   this->HexPolyData->SetPoints(this->Points);
327 
328   vtkCellArray *cellArray = vtkCellArray::New();
329   this->Topology->PopulateTopology( 0, cellArray );
330   this->HexPolyData->SetPolys(cellArray);
331   this->HexPolyData->BuildCells();
332   cellArray->Delete();
333 
334   // The face of the polyhedron
335   vtkIdType pts[4] = { 4, 5, 6, 7 };
336   vtkCellArray * cells = vtkCellArray::New();
337   cells->Allocate(cells->EstimateSize(1,4));
338   cells->InsertNextCell(4,pts); //temporary, replaced later
339   this->HexFacePolyData = vtkPolyData::New();
340   this->HexFaceMapper   = vtkPolyDataMapper::New();
341   this->HexFaceActor    = vtkActor::New();
342   this->HexFacePolyData->SetPoints(this->Points);
343   this->HexFacePolyData->SetPolys(cells);
344   this->HexFaceMapper->SetInputData(HexFacePolyData);
345   this->HexFaceActor->SetMapper(this->HexFaceMapper);
346   cells->Delete();
347 
348   // Set some default properties.
349   // Handle properties
350   this->HandleProperty          = vtkProperty::New();
351   this->SelectedHandleProperty  = vtkProperty::New();
352   this->HoveredHandleProperty   = vtkProperty::New();
353   this->HandleProperty        ->SetColor(1.0,1.0,0.7);
354   this->SelectedHandleProperty->SetColor(1.0,0.2,0.1);
355   this->HoveredHandleProperty ->SetColor(1.0,0.7,0.5);
356 
357   // Face properties
358   this->FaceProperty            = vtkProperty::New();
359   this->SelectedFaceProperty    = vtkProperty::New();
360   this->FaceProperty        ->SetColor(1,1,1);
361   this->SelectedFaceProperty->SetColor(0,0,1);
362   this->FaceProperty->SetOpacity(0.0);
363   this->SelectedFaceProperty->SetOpacity(0.25);
364 
365   // Outline properties (for the hex and the chair)
366   this->OutlineProperty = vtkProperty::New();
367   this->OutlineProperty->SetRepresentationToWireframe();
368   this->OutlineProperty->SetAmbient(1.0);
369   this->OutlineProperty->SetAmbientColor(1.0,1.0,1.0);
370   this->OutlineProperty->SetLineWidth(2.0);
371   this->SelectedOutlineProperty = vtkProperty::New();
372   this->SelectedOutlineProperty->SetRepresentationToWireframe();
373   this->SelectedOutlineProperty->SetAmbient(1.0);
374   this->SelectedOutlineProperty->SetAmbientColor(0.0,0.0,1.0);
375   this->SelectedOutlineProperty->SetLineWidth(2.0);
376   this->HexActor->SetProperty(this->OutlineProperty);
377   this->HexFaceActor->SetProperty(this->FaceProperty);
378 
379   // Handle looks like a sphere.
380   this->HandleRepresentation  = nullptr;
381   this->HandleRepresentations = nullptr;
382   vtkSphereHandleRepresentation * hRep = vtkSphereHandleRepresentation::New();
383   this->SetHandleRepresentation(hRep);
384   hRep->Delete();
385 
386   this->CurrentHandleIdx  = -1;
387   this->LastResizeAxisIdx = -1;
388   this->ChairHandleIdx    = -1;
389 
390   // Point placer to dictate placement of the chair point inside the
391   // parallelopiped.
392   this->ChairPointPlacer  = vtkClosedSurfacePointPlacer::New();
393 
394   this->InitialChairDepth = 0.25;
395   this->MinimumThickness  = 0.05;
396   this->AbsoluteMinimumThickness  = 0.05;
397   this->PlaceFactor       = 1.0;
398 
399   // Define the point coordinates and initial placement of the widget
400   double bounds[6] = { -0.5, 0.5, -0.5, 0.5, -0.5, 0.5 };
401   this->PlaceWidget(bounds);
402 }
403 
404 //----------------------------------------------------------------------------
~vtkParallelopipedRepresentation()405 vtkParallelopipedRepresentation::~vtkParallelopipedRepresentation()
406 {
407   this->HexActor->Delete();
408   this->HexMapper->Delete();
409   this->HexPolyData->Delete();
410   this->Points->Delete();
411   this->HexFaceActor->Delete();
412   this->HexFaceMapper->Delete();
413   this->HexFacePolyData->Delete();
414 
415   this->SetHandleRepresentation(nullptr);
416 
417   this->FaceProperty->Delete();
418   this->SelectedFaceProperty->Delete();
419   this->OutlineProperty->Delete();
420   this->SelectedOutlineProperty->Delete();
421 
422   this->SetHandleProperty         ( nullptr );
423   this->SetSelectedHandleProperty ( nullptr );
424   this->SetHoveredHandleProperty  ( nullptr );
425   this->ChairPointPlacer->Delete();
426   delete this->Topology;
427 }
428 
429 //----------------------------------------------------------------------------
430 vtkHandleRepresentation* vtkParallelopipedRepresentation
GetHandleRepresentation(int handleIndex)431 ::GetHandleRepresentation( int handleIndex )
432 {
433   return (handleIndex > 7) ? nullptr : this->HandleRepresentations[handleIndex];
434 }
435 
436 //----------------------------------------------------------------------
437 // You can swap the handle representation to one that you like.
438 void vtkParallelopipedRepresentation
SetHandleRepresentation(vtkHandleRepresentation * handle)439 ::SetHandleRepresentation(vtkHandleRepresentation *handle)
440 {
441   if ( handle == this->HandleRepresentation )
442   {
443     return;
444   }
445 
446   vtkSetObjectBodyMacro( HandleRepresentation,
447                       vtkHandleRepresentation, handle );
448 
449   if (this->HandleRepresentation)
450   {
451     // Allocate the 8 handles if they haven't been allocated.
452     if (!this->HandleRepresentations)
453     {
454       this->HandleRepresentations = new vtkHandleRepresentation* [8];
455       for (int i=0; i<8; this->HandleRepresentations[i++] = nullptr)
456       {
457         ;
458       }
459     }
460   }
461   else
462   {
463     // Free the 8 handles if they haven't been freed.
464     if (this->HandleRepresentations)
465     {
466       for (int i=0; i<8; this->HandleRepresentations[i++]->Delete())
467       {
468         ;
469       }
470       delete [] this->HandleRepresentations;
471       this->HandleRepresentations = nullptr;
472     }
473   }
474 
475   for (int i=0; i<8; i++)
476   {
477 
478     // We will remove the old handle, in anticipation of the new user-
479     // provided handle type that we are going to set a few lines later.
480     if (this->HandleRepresentations && this->HandleRepresentations[i])
481     {
482       this->HandleRepresentations[i]->Delete();
483       this->HandleRepresentations[i] = nullptr;
484     }
485 
486     // Copy the new user-provided handle.
487     if (this->HandleRepresentation)
488     {
489       this->HandleRepresentations[i] = this->HandleRepresentation->NewInstance();
490       this->HandleRepresentations[i]->ShallowCopy(this->HandleRepresentation);
491     }
492   }
493 }
494 
495 //----------------------------------------------------------------------
496 // Remove any existing chairs in the parallelopiped.
RemoveExistingChairs()497 void vtkParallelopipedRepresentation::RemoveExistingChairs()
498 {
499   // If we have a chair. A chair has 9 faces as opposed to a parallelopiped
500   // which has 6 faces.
501   if (this->HexPolyData->GetPolys()->GetNumberOfCells() == 9)
502   {
503 
504     // Go back to the topology of a parallelopiped.
505     vtkCellArray *parallelopipedcells = vtkCellArray::New();
506     this->Topology->PopulateTopology( 0, parallelopipedcells );
507     this->HexPolyData->SetPolys(parallelopipedcells);
508     this->HexPolyData->BuildCells();
509     parallelopipedcells->Delete();
510 
511 
512     // Bring the node that had the chair back to the 4th corner of the
513     // parallelopiped. We will use vector addition by finding the 4th point of
514     // a parallelogram from the other 3 points.
515     vtkIdType neighborPtIds[3], npts = 0, *cellPtIds = nullptr;
516     this->Topology->GetNeighbors( this->ChairHandleIdx, neighborPtIds );
517 
518     // First find 4 points that form a parallelogram and contain the chaired
519     // handle. The pointIds shall be stored in "nodes"
520     vtkParallelopipedTopology::CellType nodes(3);
521     nodes[0] = this->ChairHandleIdx;
522     nodes[1] = neighborPtIds[0];
523     nodes[2] = neighborPtIds[1];
524 
525     vtkSmartPointer< vtkCellArray > cells = vtkSmartPointer<vtkCellArray>::New();
526     this->Topology->FindCellsContainingNodes( 0, cells, nodes );
527 
528     cells->InitTraversal();
529     cells->GetNextCell(npts, cellPtIds);
530 
531     // Find the 4th pointId.
532     int j = 0;
533     while (cellPtIds[j] == nodes[0]
534         || cellPtIds[j] == nodes[1]
535         || cellPtIds[j] == nodes[2]) ++j;
536     nodes.push_back(cellPtIds[j]);
537 
538     // Now go about finding the 4th point (Index 0) in the parallelogram..
539     //     0 ------ 1
540     //     |        |
541     //     2 ------ 3
542     //
543     double p[4][3];  // for 4 points.. 3 we know, 4th to find..
544     this->Points->GetPoint( nodes[3], p[0] );
545     this->Points->GetPoint( nodes[1], p[1] );
546     this->Points->GetPoint( nodes[2], p[2] );
547     p[3][0] = p[1][0] + p[2][0] - p[0][0];
548     p[3][1] = p[1][1] + p[2][1] - p[0][1];
549     p[3][2] = p[1][2] + p[2][2] - p[0][2];
550     this->Points->SetPoint( nodes[0], p[3] );
551 
552     this->ChairHandleIdx = -1;
553   }
554 }
555 
556 //----------------------------------------------------------------------
557 // Node can be an integer within [0,7]. This will create a chair one one of
558 // the handle corners. The '0 < scale < 1' value dicates the starting
559 // depth of the cavity.
UpdateChairAtNode(int node)560 void vtkParallelopipedRepresentation::UpdateChairAtNode( int node )
561 {
562   vtkIdType npts = 0, *cellPtIds = nullptr;
563 
564   // If we have a chair somewhere else, remove it. We can have only one
565   // chair at a time.
566   if (this->CurrentHandleIdx != this->ChairHandleIdx &&
567       this->HexPolyData->GetPolys()->GetNumberOfCells() == 9)
568   {
569     this->RemoveExistingChairs();
570   }
571 
572   this->ChairHandleIdx = node;
573 
574   // If we already don't have a chair, create one. (a chair has 6 faces,
575   // unlike a parallelopiped).
576   if (this->HexPolyData->GetPolys()->GetNumberOfCells() != 9)
577   {
578     // chair has 14 points, but we will model this with 2 parallelopipeds.
579     // Hence 16 points. Look at vtkParallelopipedTopology for details.
580 
581     // Scale points with respect to the node.
582     double origin[3], d[3];
583     this->Points->GetPoint( node, origin );
584 
585     for (int i = 0; i < 8 ; i++)
586     {
587       this->Points->GetPoint(i, d);
588       d[0] = (d[0] - origin[0]) * this->InitialChairDepth + origin[0];
589       d[1] = (d[1] - origin[1]) * this->InitialChairDepth + origin[1];
590       d[2] = (d[2] - origin[2]) * this->InitialChairDepth + origin[2];
591       this->Points->SetPoint(i+8, d);
592     }
593 
594     this->Points->SetPoint( node, this->Points->GetPoint(
595         vtkParallelopipedTopology::GetDiametricOppositeOfCorner(node) + 8));
596 
597     vtkSmartPointer< vtkCellArray > cells = vtkSmartPointer<vtkCellArray>::New();
598     this->Topology->PopulateTopology( node + 1, cells );
599     this->HexPolyData->SetPolys(cells);
600     this->HexPolyData->BuildCells();
601 
602     // Synchronize the handle representations with our recently updated
603     // "Points" data-structure.
604     this->PositionHandles();
605   }
606   else
607   {
608     // We do have a chair. Update the points in the chair by taking the
609     // projection of the chaired node onto the axes of the parallelopiped.
610 
611     // These three PtIds are those that lie on the chair and are connected via
612     // a line to the "Chair node" in question. It is the position of these 3
613     // points that we seek to find in the next few lines.
614     vtkIdType neighborPtIds[3];
615 
616     // This will contain the 3 faces that lie on the parallelopiped and have
617     // a chair carved out in them. As you know, we are about to compute the
618     // points at the carved out locations.
619     vtkSmartPointer< vtkCellArray > neighborCells = vtkSmartPointer<vtkCellArray>::New();
620 
621     // Handle PointID is the diametric opposite of the chair corner on the
622     // higher order parallelopiped (the chair parallelopiped).
623     const vtkIdType chairHandleId = 8 + vtkParallelopipedTopology::
624                   GetDiametricOppositeOfCorner(this->CurrentHandleIdx);
625 
626     // Get the world position of the chair handle.
627     double chairPoint[3];
628     this->Points->GetPoint( chairHandleId, chairPoint );
629 
630     std::vector< vtkParallelopipedTopology::LineType > lines;
631     this->Topology->GetNeighbors( node, neighborPtIds, neighborCells, lines );
632 
633     neighborCells->InitTraversal();
634 
635     for (int i = 0; i < 3; i++)
636     {
637       double lineEndPt[2][3];
638       this->Points->GetPoint( lines[i].Id[0], lineEndPt[0] );
639       this->Points->GetPoint( lines[i].Id[1], lineEndPt[1] );
640 
641       double t, neighborPt[3]; // "x" is the point that we are trying to find.
642 
643       neighborCells->GetNextCell(npts, cellPtIds);
644 
645       vtkIdType planePtIds[3];
646 
647       // For each point in the cell
648       for (int j = 0, idx = 0; j < npts && idx < 3; j++)
649       {
650         // Avoid the points that are on the chair as these are the ones we seek
651         // to find.
652         if ( cellPtIds[j] < 8 )
653         {
654           planePtIds[idx++] = cellPtIds[j];
655         }
656       }
657 
658       // Construct a plane from the cell.
659       vtkPlane *plane = vtkPlane::New();
660       this->DefinePlane(plane, planePtIds[0], planePtIds[1], planePtIds[2]);
661 
662       double endPoint[3] = { chairPoint[0] + lineEndPt[1][0] - lineEndPt[0][0],
663                              chairPoint[1] + lineEndPt[1][1] - lineEndPt[0][1],
664                              chairPoint[2] + lineEndPt[1][2] - lineEndPt[0][2] };
665 
666       vtkPlane::IntersectWithLine( chairPoint, endPoint,
667           plane->GetNormal(), plane->GetOrigin(), t, neighborPt );
668       plane->Delete();
669 
670       vtkDebugMacro( << "ChairPoint: (" << chairPoint[0] << "," << chairPoint[1]
671         << "," << chairPoint[2] << ") lineEndPts [" << lines[i].Id[0] << "("
672         << lineEndPt[0][0] << "," << lineEndPt[0][1] << "," << lineEndPt[0][2]
673         << ")-" << lines[i].Id[1] << "(" << lineEndPt[1][0] << ","
674         << lineEndPt[1][1] << "," << lineEndPt[1][2] << ")]"
675         << " Intersection at: (" << neighborPt[0] << "," << neighborPt[1]
676         << "," << neighborPt[2] << ")" );
677 
678       this->Points->SetPoint( neighborPtIds[i], neighborPt );
679     }
680 
681     // Now that we have found the 3 neighbors, we need to compute the other
682     // points in the chair. Note that we have 4 so far (3 neighbors + the
683     // chair node). There are 2 more to be found. Given that they will
684     // have to satisfy a parallelogram relationship, we can easily use
685     // vector addition to evaluate them.
686 
687     for (int i = 0; i < 3; i++)
688     {
689       std::vector< vtkIdType > nodes(3);
690       vtkSmartPointer< vtkCellArray > cells
691           = vtkSmartPointer<vtkCellArray>::New();
692       nodes[0] = 8 + vtkParallelopipedTopology::
693         GetDiametricOppositeOfCorner(this->CurrentHandleIdx);
694       nodes[1] = neighborPtIds[i];
695       nodes[2] = neighborPtIds[(i+1)%3];
696       vtkDebugMacro( << "Looking for cells containing nodes: " << nodes[0]
697         << "," << nodes[1] << "," << nodes[2] << " in topology "
698         << (this->CurrentHandleIdx+1) );
699       this->Topology->FindCellsContainingNodes(
700           this->CurrentHandleIdx + 1, cells, nodes );
701 
702       npts = 0; cellPtIds = nullptr;
703       cells->InitTraversal();
704       cells->GetNextCell(npts, cellPtIds);
705 
706       // Find the 4th pointId. The pointIds shall be stored in "nodes"
707       int j = 0;
708       while (cellPtIds[j] == nodes[0]
709           || cellPtIds[j] == nodes[1]
710           || cellPtIds[j] == nodes[2]) ++j;
711       nodes.push_back(cellPtIds[j]);
712 
713       // Now go about finding the 4th point (Index 3) in the parallelogram..
714       //     0 ------ 1
715       //     |        |
716       //     2 ------ 3
717       //
718       double p[4][3];  // for 4 points.. 3 we know, 4th to find..
719       this->Points->GetPoint( nodes[0], p[0] );
720       this->Points->GetPoint( nodes[1], p[1] );
721       this->Points->GetPoint( nodes[2], p[2] );
722       p[3][0] = p[1][0] + p[2][0] - p[0][0];
723       p[3][1] = p[1][1] + p[2][1] - p[0][1];
724       p[3][2] = p[1][2] + p[2][2] - p[0][2];
725 
726       vtkDebugMacro( << "Parallelogram built from (nodes and points): \n"
727        << "(" << nodes[0] << ") = [" << p[0][0] << "," << p[0][1] << "," << p[0][2] << "]\n"
728        << "(" << nodes[1] << ") = [" << p[1][0] << "," << p[1][1] << "," << p[1][2] << "]\n"
729        << "(" << nodes[2] << ") = [" << p[2][0] << "," << p[2][1] << "," << p[2][2] << "]\n"
730        << "(" << cellPtIds[j] << ") = [" << p[3][0] << "," << p[3][1] << "," << p[3][2] << "]\n");
731 
732       this->Points->SetPoint( nodes[3], p[3] );
733     }
734 
735     this->Points->SetPoint( 8 + vtkParallelopipedTopology::
736         GetDiametricOppositeOfCorner(this->CurrentHandleIdx),
737         this->Points->GetPoint(this->CurrentHandleIdx));
738   }
739 }
740 
741 //----------------------------------------------------------------------
742 // This is where the bulk of the work is done.
743 int vtkParallelopipedRepresentation
ComputeInteractionState(int X,int Y,int vtkNotUsed (modify))744 ::ComputeInteractionState(int X, int Y, int vtkNotUsed(modify))
745 {
746   int oldInteractionState = this->InteractionState;
747 
748   // (A) -----------------------------------------------------------
749   // Handle the request methods. These are mere requests and will not cause
750   // any change in the position of the handles or the shape of the
751   // parallelopiped. The representation will, within this IF block change its
752   // state from a request to a concrete state.
753 
754   if ( this->InteractionState == vtkParallelopipedRepresentation::RequestResizeParallelopiped
755     || this->InteractionState == vtkParallelopipedRepresentation::RequestResizeParallelopipedAlongAnAxis
756     || this->InteractionState == vtkParallelopipedRepresentation::RequestChairMode )
757   {
758     this->CurrentHandleIdx = -1;
759 
760     // We are trying to perform user interaction that might potentially
761     // select a handle. Check if we are really near a handle, so it
762     // can be selected.
763 
764     // Loop over all the handles and check if one of them is selected
765     for(int i = 0; i< 8; i++)
766     {
767       this->HandleRepresentations[i]->ComputeInteractionState(X, Y, 0);
768 
769        if (this->HandleRepresentations[i]->GetInteractionState() ==
770                                  vtkHandleRepresentation::Selecting)
771        {
772         // The selected handle.
773         this->CurrentHandleIdx = i;
774 
775         // The shift modifier determines if the handles are going to be
776         // translated along an axes of the parallelopiped.
777         switch (this->InteractionState)
778         {
779           case vtkParallelopipedRepresentation::RequestResizeParallelopiped:
780             this->InteractionState = (this->CurrentHandleIdx == this->ChairHandleIdx)
781                ? ChairMode : ResizingParallelopiped;
782             break;
783           case vtkParallelopipedRepresentation::RequestResizeParallelopipedAlongAnAxis:
784             this->InteractionState = (this->CurrentHandleIdx == this->ChairHandleIdx)
785                ? ChairMode : ResizingParallelopipedAlongAnAxis;
786             break;
787           case vtkParallelopipedRepresentation::RequestChairMode:
788           {
789 
790             // Toggle chair mode if we already have a chair here.. We are
791             // trying to toggle of course.. In this case remove all chairs,
792             if (this->CurrentHandleIdx == this->ChairHandleIdx &&
793                 this->HexPolyData->GetPolys()->GetNumberOfCells() == 9)
794             {
795               this->RemoveExistingChairs();
796               this->LastEventPosition[0] = X;
797               this->LastEventPosition[1] = Y;
798               this->InteractionState = vtkParallelopipedRepresentation::Inside;
799 
800               // Synchronize the handle representations with our recently updated
801               // "Points" data-structure.
802               this->PositionHandles();
803               return this->InteractionState;
804             }
805 
806             // We aren't trying to toggle. Create one
807             // Create a chair with a default cavity depth of 0.1
808             this->UpdateChairAtNode( this->CurrentHandleIdx );
809 
810             // We are in chair mode. Use the placer to dictate where the
811             // "chaired" handle can move. (It can only move within the
812             // parallelopiped). First set some parameters on the placer.
813 
814             vtkPlaneCollection *pc = vtkPlaneCollection::New();
815             this->GetParallelopipedBoundingPlanes( pc );
816             this->ChairPointPlacer->SetBoundingPlanes( pc );
817             pc->Delete();
818 
819             this->InteractionState = ChairMode;
820             break;
821           }
822         }
823 
824         // Highlight the selected handle and unhighlight all others.
825         this->SetHandleHighlight(-1, this->HandleProperty);
826         this->SetHandleHighlight(
827             this->CurrentHandleIdx, this->SelectedHandleProperty);
828 
829         break;
830        }
831     }
832 
833     if (this->CurrentHandleIdx == -1)
834     {
835       // We are near none of the handles.
836 
837       // Now check if we are within the parallelopiped or outside the
838       // parallelopiped. We will use the pointplacer to evaluate this.
839       vtkPlaneCollection *pc = vtkPlaneCollection::New();
840       this->GetParallelopipedBoundingPlanes( pc );
841       this->ChairPointPlacer->SetBoundingPlanes( pc );
842       pc->Delete();
843 
844       // Use any random handle as a reference for the point placer.
845       double eventDisplayPos[3] = {static_cast<double>(X),
846                                    static_cast<double>(Y),
847                                    0.0};
848       double dummy[4], worldOrient[9], handleWorldPos[4];
849       this->HandleRepresentations[0]->GetWorldPosition(handleWorldPos);
850 
851       this->InteractionState = (this->ChairPointPlacer->ComputeWorldPosition(
852         this->Renderer, eventDisplayPos, handleWorldPos, dummy, worldOrient )
853             ? vtkParallelopipedRepresentation::Inside
854             : vtkParallelopipedRepresentation::Outside);
855     }
856 
857     if (this->InteractionState == vtkParallelopipedRepresentation::Inside &&
858         oldInteractionState == vtkParallelopipedRepresentation::
859                                 RequestResizeParallelopipedAlongAnAxis)
860     {
861       this->HighlightAllFaces();
862     }
863     else
864     {
865       // UnHighlight all faces
866       this->UnHighlightAllFaces();
867     }
868 
869     // Reset any cached "resize along that axis" stuff.
870     this->LastResizeAxisIdx = -1;
871   }
872 
873 
874   // (B) -----------------------------------------------------------
875   // Handle the resizing operations (along the axis or arbitrarily).
876 
877   else if (this->InteractionState ==
878         vtkParallelopipedRepresentation::ResizingParallelopipedAlongAnAxis ||
879       this->InteractionState ==
880         vtkParallelopipedRepresentation::ResizingParallelopiped)
881   {
882     // Ensure that a handle has been picked.
883     if (this->CurrentHandleIdx != -1)
884     {
885       // Compute world positions corresponding to the current event position
886       // (X,Y) and the last event positions such that they lie at the same
887       // depth that the handle lies on.
888 
889       double axis[3][3], eventWorldPos[4], handleWorldPos[4],
890         handleDisplayPos[4], neighborWorldPos[3][4], neighborDisplayPos[3][4];
891 
892       this->HandleRepresentations[this->CurrentHandleIdx]
893                                  ->GetWorldPosition(handleWorldPos);
894 
895       vtkInteractorObserver::ComputeWorldToDisplay( this->Renderer,
896         handleWorldPos[0], handleWorldPos[1], handleWorldPos[2],
897         handleDisplayPos);
898 
899       // Now find and get the display positions of the three neighbors of the
900       // current handle. We have to rescale along one of the three edges.
901 
902       vtkIdType neighborIndices[3];
903       this->Topology->GetNeighbors( this->CurrentHandleIdx, neighborIndices,
904               (this->ChairHandleIdx == -1) ? 0 : this->ChairHandleIdx + 1 );
905 
906       // The motion vector in display coords
907       const double motionVector[3] = { X - this->LastEventPosition[0],
908                                        Y - this->LastEventPosition[1],
909                                        0.0                            };
910 
911       double maxConfidence = VTK_DOUBLE_MIN;
912 
913       // The next few lines attempt to find the axis should we scale along.
914       // The axis is the axis of the parallelopiped that is most aligned with
915       // the direction of mouse motion.
916 
917       int axisIdx = this->LastResizeAxisIdx; // To be found out ..
918 
919       // loop over the 3 neighbors of the current handle
920       for (int i = 0; i < 3; i++)
921       {
922 
923         // Compute display position of this neighbor
924         this->Points->GetPoint(neighborIndices[i], neighborWorldPos[i]);
925         vtkInteractorObserver::ComputeWorldToDisplay( this->Renderer,
926             neighborWorldPos[i][0], neighborWorldPos[i][1],
927             neighborWorldPos[i][2], neighborDisplayPos[i]);
928 
929         // Dot product of the motion vector (in display coords) with each
930         // of the three edges (in display coords). The maximum of the three
931         // will determine which axis of the parallelopiped we will rescale along
932 
933         axis[i][0] = neighborDisplayPos[i][0] - handleDisplayPos[0];
934         axis[i][1] = neighborDisplayPos[i][1] - handleDisplayPos[1];
935         axis[i][2] = 0.0;
936         vtkMath::Normalize2D(axis[i]);
937 
938         // If we did not compute the resize axis Idx already the last time,
939         // we were in this method, compute it now, by checking which axis
940         // the motion vector is most aligned with.
941         if (this->LastResizeAxisIdx == -1 ||
942             this->InteractionState ==
943                 vtkParallelopipedRepresentation::ResizingParallelopiped)
944         {
945           const double confidence
946             = fabs(vtkMath::Dot2D( axis[i], motionVector ));
947           if (confidence > maxConfidence)
948           {
949             axisIdx = i;
950             maxConfidence = confidence;
951           }
952         }
953       }
954 
955 
956       // Now that we know the axis to translate along, find the amount we should
957       // translate by. The new handle position must lie somewhere along the
958       // line joining the currently selected handle and the neighbor that lies
959       // along the rescale axis. We will evaluate 't E [-inf, 1.0]', the
960       // parametric position along the line. This point will simply be the
961       // point on the aforementioned line that the current event position is
962       // closest to.
963 
964       double directionOfProjection[3], closestPt1[3], closestPt2[3], t1, t;
965 
966       this->Renderer->GetActiveCamera()->
967             GetDirectionOfProjection(directionOfProjection);
968       vtkInteractorObserver::ComputeDisplayToWorld( this->Renderer,
969                             X, Y, handleDisplayPos[2], eventWorldPos);
970 
971       double l0[3] = {eventWorldPos[0] - directionOfProjection[0],
972                       eventWorldPos[1] - directionOfProjection[1],
973                       eventWorldPos[2] - directionOfProjection[2] };
974       double l1[3] = {eventWorldPos[0] + directionOfProjection[0],
975                       eventWorldPos[1] + directionOfProjection[1],
976                       eventWorldPos[2] + directionOfProjection[2] };
977 
978       vtkLine::DistanceBetweenLines( handleWorldPos, neighborWorldPos[axisIdx],
979                                                  l0, l1,
980                                          closestPt1, closestPt2,
981                                                   t, t1 );
982       t = (t > 1.0 ? 1.0 : t); // clamp 't'
983 
984       vtkDebugMacro( << "Currently selected handle is at : (" <<
985         handleWorldPos[0] << "," << handleWorldPos[1] << "," << handleWorldPos[2] <<
986         ")\n Pt2 (the selected handle will be moved along the axis represented by"
987         << " itself and Pt2) is at: (" << neighborWorldPos[axisIdx][0]
988         << "," << neighborWorldPos[axisIdx][1] << "," << neighborWorldPos[axisIdx][2]
989         << ")\n The selected handle will be moved to parametric location t = " << t
990         << "with the line being specified by the above 2 points.");
991 
992 
993       // This is the amount by which the face will move towards
994       // (or away from if t < 0.0) the other face. We know that the face has
995       // the following PointIds.
996       //   1) CurrentHandleIdx
997       //   2) Neighbor 1 of currentHandleIdx
998       //   3) Neighbor 2 of CurrentHandleIdx
999       // It will be our job in the next few lines to find the other points in
1000       // the face and translate the face.
1001 
1002       // "nodes" contains the 3 pointIds that we know are present on the face.
1003       std::vector< vtkIdType > nodes(3);
1004       nodes[0] = this->CurrentHandleIdx;
1005 
1006       for (int i = 0, j = 1; i < 3; i++)
1007       {
1008         if (i != axisIdx)
1009         {
1010           nodes[j++] = neighborIndices[i];
1011         }
1012       }
1013 
1014       // "cells" below contains the face to be translated.
1015       vtkSmartPointer< vtkCellArray > cells = vtkSmartPointer<vtkCellArray>::New();
1016       this->Topology->FindCellsContainingNodes(
1017         (this->ChairHandleIdx == -1) ? 0 :
1018           this->ChairHandleIdx + 1, cells, nodes );
1019 
1020       vtkIdType npts = 0, *cellPtIds = nullptr;
1021       cells->InitTraversal();
1022       cells->GetNextCell(npts, cellPtIds);
1023 
1024       // The translation vector
1025       double handleTranslation[3] =
1026         { t * neighborWorldPos[axisIdx][0] - t * handleWorldPos[0],
1027           t * neighborWorldPos[axisIdx][1] - t * handleWorldPos[1],
1028           t * neighborWorldPos[axisIdx][2] - t * handleWorldPos[2]  };
1029 
1030       double newHandleWorldPos[3] = { handleWorldPos[0] + handleTranslation[0],
1031                                       handleWorldPos[1] + handleTranslation[1],
1032                                       handleWorldPos[2] + handleTranslation[2]};
1033 
1034       if (   t > 0.0  // We are moving towards the other handle
1035           && (vtkMath::Distance2BetweenPoints(
1036               neighborWorldPos[axisIdx], newHandleWorldPos)) < (
1037               this->AbsoluteMinimumThickness * this->AbsoluteMinimumThickness))
1038       {
1039         // Too close. We don't want the parallelopiped collapsing, do we ?
1040         vtkDebugMacro( << "AbsoluteMaximumThickness = "
1041           << this->AbsoluteMinimumThickness << " This move will bring us "
1042           << sqrt(vtkMath::Distance2BetweenPoints(
1043               neighborWorldPos[axisIdx], newHandleWorldPos)) << " far away to ("
1044           << newHandleWorldPos[0] << "," << newHandleWorldPos[1] << ","
1045           << newHandleWorldPos[2] << "). We can\'t do that." );
1046 
1047         // Revise 't' so as to maintain minimum thickness. The bottom line is
1048         // that although 't E [-inf, 1.0]', 't' will never hit 1.0 unless
1049         // AbsoluteMinimumThickness is 0.0.
1050         t = 1.0 - this->AbsoluteMinimumThickness / sqrt(
1051             vtkMath::Distance2BetweenPoints(
1052               neighborWorldPos[axisIdx], handleWorldPos));
1053 
1054         // Recompute these 2 positions with our revised 't' value.
1055         handleTranslation[0] = t * neighborWorldPos[axisIdx][0] - t * handleWorldPos[0];
1056         handleTranslation[1] = t * neighborWorldPos[axisIdx][1] - t * handleWorldPos[1];
1057         handleTranslation[2] = t * neighborWorldPos[axisIdx][2] - t * handleWorldPos[2];
1058 
1059         newHandleWorldPos[0] = handleWorldPos[0] + handleTranslation[0];
1060         newHandleWorldPos[1] = handleWorldPos[1] + handleTranslation[1];
1061         newHandleWorldPos[2] = handleWorldPos[2] + handleTranslation[2];
1062 
1063         if (t < 0.0)
1064         {
1065           // Sanity check. We should never get here in the first place.
1066           this->LastEventPosition[0] = X;
1067           this->LastEventPosition[1] = Y;
1068           return this->InteractionState;
1069         }
1070 
1071         vtkDebugMacro( "So we are revising the value of t to " << t
1072           << " and newHandleWorldPos to (" << newHandleWorldPos[0] << ","
1073           << newHandleWorldPos[1] << "," << newHandleWorldPos[2] << ")" );
1074       }
1075 
1076       // If we have a chair, prevent the handle from being translated beyond
1077       // the plane of the chair, otherwise it will cause the chair to turn
1078       // inside out. So we will do some dot-product stuff and revise the
1079       // "neighborWorldPos", if we have a chair.
1080       if (this->ChairHandleIdx != -1)
1081       {
1082         std::vector< vtkIdType > nodes2(1);
1083         nodes2[0] = vtkParallelopipedTopology::GetDiametricOppositeOfCorner(this->ChairHandleIdx)+8;
1084         const vtkParallelopipedTopology::CliqueType cells2 = this->
1085           Topology->FindCellsContainingNodes( this->ChairHandleIdx+1, nodes2 );
1086 
1087         for (vtkParallelopipedTopology::CliqueType::const_iterator clit = cells2.begin();
1088              clit != cells2.end(); ++clit)
1089         {
1090           vtkSmartPointer< vtkPlane > plane = vtkSmartPointer< vtkPlane >::New();
1091           this->DefinePlane(plane, (*clit)[0], (*clit)[1], (*clit)[2] );
1092           double distance = plane->EvaluateFunction(newHandleWorldPos);
1093 
1094           // Ensure that the handle is on the right side of the chair's plane,
1095           // and that it is at least 'MinimumThickness' away from any of the
1096           // planes of the chair.
1097           if (fabs(distance) < this->MinimumThickness ||
1098                (distance * (std::find(clit->begin(), clit->end(),
1099                   this->CurrentHandleIdx+8) != clit->end() ? -1 : 1) > 0))
1100           {
1101             this->LastEventPosition[0] = X;
1102             this->LastEventPosition[1] = Y;
1103             return this->InteractionState;
1104           }
1105         }
1106       }
1107 
1108       // Highlight this face...
1109       this->SetFaceHighlight( cells, this->SelectedFaceProperty );
1110 
1111       // Translate this face...
1112       for (vtkIdType i = 0; i < npts;
1113            this->TranslatePoint( cellPtIds[i++], handleTranslation ))
1114       {
1115         ;
1116       }
1117 
1118       // Cache the axis along which we resized the previous time, so we don't
1119       // have to recompute it.
1120       this->LastResizeAxisIdx = axisIdx;
1121 
1122       // Update the bounding planes.
1123       vtkPlaneCollection *pc = vtkPlaneCollection::New();
1124       this->GetParallelopipedBoundingPlanes( pc );
1125       this->ChairPointPlacer->SetBoundingPlanes( pc );
1126       pc->Delete();
1127 
1128     }
1129     else
1130     {
1131       // In theory, we should never get there.
1132       this->InteractionState = vtkParallelopipedRepresentation::Outside;
1133     }
1134   }
1135 
1136 
1137   // (C) -----------------------------------------------------------
1138   // Default method for all other states.
1139 
1140   else if (this->InteractionState == vtkParallelopipedRepresentation::ChairMode)
1141   {
1142     // Ensure that a handle has been picked.
1143     if (this->CurrentHandleIdx != -1)
1144     {
1145       double handleWorldPos[4];
1146 
1147       this->HandleRepresentations[this->CurrentHandleIdx]
1148                                  ->GetWorldPosition(handleWorldPos);
1149 
1150       // The new handle position, will lie on a plane that passes through the
1151       // current world position and is parallel to the focal plane.
1152       // To compute this, we will use the help of the focal plane point placer,
1153       // and supply it with the offset of the handle's distance from the
1154       // focal plane.
1155 
1156       double eventDisplayPos[3] = {static_cast<double>(X),
1157                                    static_cast<double>(Y),
1158                                    0.0};
1159       double newHandlePos[4], worldOrient[9];
1160 
1161       if (this->ChairPointPlacer->ComputeWorldPosition(
1162             this->Renderer, eventDisplayPos, handleWorldPos,
1163             newHandlePos, worldOrient ))
1164       {
1165         const double handleTranslation[3] =
1166               { newHandlePos[0] - handleWorldPos[0],
1167                 newHandlePos[1] - handleWorldPos[1],
1168                 newHandlePos[2] - handleWorldPos[2] };
1169         this->TranslatePoint( this->CurrentHandleIdx, handleTranslation);
1170       }
1171 
1172       this->UpdateChairAtNode( this->CurrentHandleIdx );
1173 
1174     }
1175     else
1176     {
1177       // In theory, we should never get there.
1178       this->InteractionState = vtkParallelopipedRepresentation::Outside;
1179     }
1180   }
1181 
1182 
1183   // (D) -----------------------------------------------------------
1184   // Default method for all other states.
1185 
1186   else
1187   {
1188     this->InteractionState = vtkParallelopipedRepresentation::Outside;
1189 
1190     // Loop over all the handles and check if we are near one of them.
1191     for(int i = 0; i< 8; i++)
1192     {
1193       this->HandleRepresentations[i]->ComputeInteractionState(X, Y, 0);
1194       if (this->HandleRepresentations[i]->GetInteractionState() ==
1195                                     vtkHandleRepresentation::Selecting)
1196       {
1197         this->SetHandleHighlight( i, this->HoveredHandleProperty );
1198         this->InteractionState = vtkParallelopipedRepresentation::Inside;
1199         break;
1200       }
1201     }
1202 
1203     if (this->InteractionState == vtkParallelopipedRepresentation::Outside)
1204     {
1205       // Unhighlight all handles and faces.
1206       this->SetHandleHighlight( -1, this->HandleProperty );
1207       this->UnHighlightAllFaces();
1208     }
1209   }
1210 
1211   // Cache the last event position.
1212   this->LastEventPosition[0] = X;
1213   this->LastEventPosition[1] = Y;
1214   return this->InteractionState;
1215 }
1216 
1217 //----------------------------------------------------------------------------
1218 void vtkParallelopipedRepresentation
TranslatePoint(int id,const double translation[3])1219 ::TranslatePoint( int id, const double translation[3] )
1220 {
1221   double p[3];
1222   this->Points->GetPoint(id, p);
1223   p[0] += translation[0];
1224   p[1] += translation[1];
1225   p[2] += translation[2];
1226   this->Points->SetPoint(id, p);
1227   if (id < 8)
1228   {
1229     this->HandleRepresentations[id]->SetWorldPosition(p);
1230   }
1231 
1232   // Update our records.
1233   this->PositionHandles();
1234 }
1235 
1236 //----------------------------------------------------------------------------
1237 // Get the bounding planes of the object. The first 6 planes will
1238 // be bounding planes of the parallelopiped. If in chair mode, three
1239 // additional planes will be present. The last three planes will be those
1240 // of the chair. The Normals of all the planes will point into the object.
1241 //
GetBoundingPlanes(vtkPlaneCollection * pc)1242 void vtkParallelopipedRepresentation::GetBoundingPlanes( vtkPlaneCollection *pc )
1243 {
1244   vtkSmartPointer< vtkCellArray > cellArray = vtkSmartPointer<vtkCellArray>::New();
1245   this->Topology->PopulateTopology( this->ChairHandleIdx + 1, cellArray );
1246 
1247   vtkIdType npts = 0, *ptIds = nullptr;
1248 
1249   // For each planar cell in our object, we need to find the plane it lies on
1250   for (cellArray->InitTraversal(); cellArray->GetNextCell(npts, ptIds); )
1251   {
1252     vtkIdType planePtIds[3];
1253 
1254     // For each cell, get the point ids that comprise the planar cell.
1255     for (int i = 0, idx = 0; i < npts && idx < 3; i++)
1256     {
1257       if (this->CurrentHandleIdx != ptIds[i])
1258       {
1259         planePtIds[idx++] = ptIds[i];
1260       }
1261     }
1262 
1263     // Construct a plane from the cell.
1264     vtkPlane *plane = vtkPlane::New();
1265     this->DefinePlane(plane, planePtIds[0], planePtIds[1], planePtIds[2]);
1266     pc->AddItem(plane);
1267     plane->Delete();
1268   }
1269 }
1270 
1271 //----------------------------------------------------------------------------
1272 // Convenience method to get just the planes that define the parallelopiped.
1273 // If we aren't in chair mode, this will be the same as GetBoundingPlanes().
1274 // If we are in chair mode, this will be the first 6 planes from amongst
1275 // those returned by "GetBoundingPlanes".
1276 // All planes have their normals pointing inwards.
1277 //
1278 void vtkParallelopipedRepresentation
GetParallelopipedBoundingPlanes(vtkPlaneCollection * pc)1279 ::GetParallelopipedBoundingPlanes( vtkPlaneCollection * pc )
1280 {
1281   vtkPlaneCollection * pc2 = vtkPlaneCollection::New();
1282   this->GetBoundingPlanes( pc2 );
1283   vtkPlane *p;
1284   int i = 0;
1285   for (pc2->InitTraversal(); ((p = pc2->GetNextItem()) && i < 6); ++i )
1286   {
1287     pc->AddItem(p);
1288   }
1289   pc2->Delete();
1290 }
1291 
1292 //----------------------------------------------------------------------------
1293 // Convenience method to populate a plane from 3 pointIds
DefinePlane(vtkPlane * plane,vtkIdType id1,vtkIdType id2,vtkIdType id3)1294 void vtkParallelopipedRepresentation::DefinePlane( vtkPlane *plane,
1295       vtkIdType id1, vtkIdType id2, vtkIdType id3)
1296 {
1297   double p[3][3];
1298   this->Points->GetPoint(id1, p[0]);
1299   this->Points->GetPoint(id2, p[1]);
1300   this->Points->GetPoint(id3, p[2]);
1301   this->DefinePlane(plane, p);
1302 }
1303 
1304 //----------------------------------------------------------------------------
1305 // Convenience method to populate a plane from 3 points.
DefinePlane(vtkPlane * plane,double p[3][3])1306 void vtkParallelopipedRepresentation::DefinePlane( vtkPlane *plane, double p[3][3])
1307 {
1308   plane->SetOrigin( p[0] );
1309   double v1[3] = { p[1][0] - p[0][0], p[1][1] - p[0][1], p[1][2] - p[0][2] };
1310   double v2[3] = { p[2][0] - p[0][0], p[2][1] - p[0][1], p[2][2] - p[0][2] };
1311   double normal[3];
1312   vtkMath::Cross( v1, v2, normal );
1313   vtkMath::Normalize(normal);
1314   plane->SetNormal( normal );
1315 }
1316 
1317 //----------------------------------------------------------------------
GetActors(vtkPropCollection * pc)1318 void vtkParallelopipedRepresentation::GetActors(vtkPropCollection *pc)
1319 {
1320   for (int i=0; i<8; i++)
1321   {
1322     this->HandleRepresentations[i]->GetActors(pc);
1323   }
1324   this->HexActor->GetActors(pc);
1325   this->HexFaceActor->GetActors(pc);
1326 }
1327 
1328 //----------------------------------------------------------------------
ReleaseGraphicsResources(vtkWindow * w)1329 void vtkParallelopipedRepresentation::ReleaseGraphicsResources(vtkWindow *w)
1330 {
1331   this->HexActor->ReleaseGraphicsResources(w);
1332   this->HexFaceActor->ReleaseGraphicsResources(w);
1333   for (int i=0; i<8; i++)
1334   {
1335     this->HandleRepresentations[i]->ReleaseGraphicsResources(w);
1336   }
1337 }
1338 
1339 //----------------------------------------------------------------------
RenderOverlay(vtkViewport * v)1340 int vtkParallelopipedRepresentation::RenderOverlay(vtkViewport *v)
1341 {
1342   int count = 0;
1343   count+=this->HexActor->RenderOverlay(v);
1344   count+=this->HexFaceActor->RenderOverlay(v);
1345   for (int i=0; i<8; i++)
1346   {
1347     count+=this->HandleRepresentations[i]->RenderOverlay(v);
1348   }
1349   return count;
1350 }
1351 
1352 //----------------------------------------------------------------------------
RenderOpaqueGeometry(vtkViewport * viewport)1353 int vtkParallelopipedRepresentation::RenderOpaqueGeometry(vtkViewport *viewport)
1354 {
1355   int count = 0;
1356   this->BuildRepresentation();
1357   count+=this->HexActor->RenderOpaqueGeometry(viewport);
1358   count+=this->HexFaceActor->RenderOpaqueGeometry(viewport);
1359   for (int i=0; i<8; i++)
1360   {
1361     count += this->HandleRepresentations[i]->RenderOpaqueGeometry(viewport);
1362   }
1363   return count;
1364 }
1365 
1366 //----------------------------------------------------------------------------
PositionHandles()1367 void vtkParallelopipedRepresentation::PositionHandles()
1368 {
1369   for (int i = 0; i < 8; ++i)
1370   {
1371     this->HandleRepresentations[i]->SetWorldPosition(this->Points->GetPoint(i));
1372   }
1373 
1374   this->Points->GetData()->Modified();
1375   this->HexFacePolyData->Modified();
1376   this->HexPolyData->Modified();
1377 }
1378 
1379 //----------------------------------------------------------------------------
HandlesOn()1380 void vtkParallelopipedRepresentation::HandlesOn()
1381 {
1382   for (int i=0; i<8; this->HandleRepresentations[i++]->SetVisibility(1))
1383   {
1384     ;
1385   }
1386 }
1387 
1388 //----------------------------------------------------------------------------
HandlesOff()1389 void vtkParallelopipedRepresentation::HandlesOff()
1390 {
1391   for (int i=0; i<8; this->HandleRepresentations[i++]->SetVisibility(0))
1392   {
1393     ;
1394   }
1395 }
1396 
1397 //----------------------------------------------------------------------------
SetHandleHighlight(int handleIdx,vtkProperty * property)1398 void vtkParallelopipedRepresentation::SetHandleHighlight(
1399                       int handleIdx, vtkProperty *property )
1400 {
1401   if ( handleIdx == -1)
1402   {
1403     // Do for all handles
1404     for (int i = 0; i < 8; i++)
1405     {
1406       static_cast< vtkSphereHandleRepresentation * >(
1407           this->HandleRepresentations[i])->SetProperty(property);
1408       static_cast< vtkSphereHandleRepresentation * >(
1409           this->HandleRepresentations[i])->SetSelectedProperty(property);
1410     }
1411   }
1412   else
1413   {
1414     static_cast< vtkSphereHandleRepresentation * >(
1415         this->HandleRepresentations[handleIdx])->SetProperty(property);
1416     static_cast< vtkSphereHandleRepresentation * >(
1417         this->HandleRepresentations[handleIdx])->SetSelectedProperty(property);
1418   }
1419 }
1420 
1421 //----------------------------------------------------------------------------
1422 void vtkParallelopipedRepresentation
SetFaceHighlight(vtkCellArray * face,vtkProperty * p)1423 ::SetFaceHighlight( vtkCellArray * face, vtkProperty *p )
1424 {
1425   if (face)
1426   {
1427     this->HexFacePolyData->SetPolys(face);
1428   }
1429   this->HexFaceActor->SetProperty( p );
1430 }
1431 
1432 //----------------------------------------------------------------------------
HighlightAllFaces()1433 void vtkParallelopipedRepresentation::HighlightAllFaces()
1434 {
1435   vtkSmartPointer< vtkCellArray > cells = vtkSmartPointer<vtkCellArray>::New();
1436   this->Topology->PopulateTopology( this->ChairHandleIdx + 1, cells );
1437   this->SetFaceHighlight( cells, this->SelectedFaceProperty );
1438 }
1439 
1440 //----------------------------------------------------------------------------
UnHighlightAllFaces()1441 void vtkParallelopipedRepresentation::UnHighlightAllFaces()
1442 {
1443   this->SetFaceHighlight( nullptr, this->FaceProperty );
1444 }
1445 
1446 //----------------------------------------------------------------------------
1447 // Translate by a vector to be computed from the last Pick position and the
1448 // supplied event position
Translate(int X,int Y)1449 void vtkParallelopipedRepresentation::Translate( int X, int Y )
1450 {
1451   double eventPos[2] = { static_cast<double>(X),
1452                          static_cast<double>(Y)};
1453   double lastEventPos[2] =
1454     { this->LastEventPosition[0], this->LastEventPosition[1] };
1455 
1456   // First compute the centroid. Its only use is to determine a reference
1457   // plane, on which we will assume lastEventPos and eventPos lie.
1458   double *pts =
1459          static_cast<vtkDoubleArray *>(this->Points->GetData())->GetPointer(0);
1460   double center[3] = {0.0, 0.0, 0.0};
1461   for (int i=0; i<8; i++)
1462   {
1463     center[0] += *pts++;
1464     center[1] += *pts++;
1465     center[2] += *pts++;
1466   }
1467   center[0] /= 8.0;
1468   center[1] /= 8.0;
1469   center[2] /= 8.0;
1470 
1471   // Now convert the event positions to world positions as if they lay at the
1472   // same plane as the center.
1473 
1474   double fp[4], lastEventWorldPos[4], eventWorldPos[4];
1475 
1476   vtkInteractorObserver::ComputeWorldToDisplay( this->Renderer,
1477     center[0], center[1], center[2], fp );
1478 
1479   vtkInteractorObserver::ComputeDisplayToWorld( this->Renderer,
1480       lastEventPos[0], lastEventPos[1], fp[2], lastEventWorldPos);
1481   vtkInteractorObserver::ComputeDisplayToWorld( this->Renderer,
1482       eventPos[0], eventPos[1], fp[2], eventWorldPos);
1483 
1484   // Compute the offset from the last event position and translate.
1485   double translation[3] = { eventWorldPos[0] - lastEventWorldPos[0],
1486                             eventWorldPos[1] - lastEventWorldPos[1],
1487                             eventWorldPos[2] - lastEventWorldPos[2] };
1488   this->Translate( translation );
1489 
1490   // Update our records
1491   this->LastEventPosition[0] = X;
1492   this->LastEventPosition[1] = Y;
1493 }
1494 
1495 //----------------------------------------------------------------------------
1496 // Loop through all points and translate them
Translate(double translation[3])1497 void vtkParallelopipedRepresentation::Translate(double translation[3])
1498 {
1499   double *pts =
1500          static_cast<vtkDoubleArray *>(this->Points->GetData())->GetPointer(0);
1501   for (int i=0; i<16; i++)
1502   {
1503     *pts++ += translation[0];
1504     *pts++ += translation[1];
1505     *pts++ += translation[2];
1506   }
1507 
1508   // Synchronize the handle representations with our recently updated
1509   // "Points" data-structure.
1510   this->PositionHandles();
1511 }
1512 
1513 //----------------------------------------------------------------------------
Scale(int vtkNotUsed (X),int Y)1514 void vtkParallelopipedRepresentation::Scale( int vtkNotUsed(X), int Y )
1515 {
1516   double *pts =
1517           static_cast<vtkDoubleArray *>(this->Points->GetData())->GetPointer(0);
1518   double *center
1519     = static_cast<vtkDoubleArray *>(this->Points->GetData())->GetPointer(3*14);
1520   double sf = ( Y > this->LastEventPosition[1] ? 1.03 : 0.97 );
1521 
1522   for (int i=0; i<16; i++, pts+=3)
1523   {
1524     pts[0] = sf * (pts[0] - center[0]) + center[0];
1525     pts[1] = sf * (pts[1] - center[1]) + center[1];
1526     pts[2] = sf * (pts[2] - center[2]) + center[2];
1527   }
1528 
1529   // Synchronize the handle representations with our recently updated
1530   // "Points" data-structure.
1531   this->PositionHandles();
1532 }
1533 
1534 //----------------------------------------------------------------------------
PlaceWidget(double bounds[6])1535 void vtkParallelopipedRepresentation::PlaceWidget(double bounds[6])
1536 {
1537   double corners[8][3] =
1538     { { bounds[0], bounds[2], bounds[4] },
1539       { bounds[1], bounds[2], bounds[4] },
1540       { bounds[1], bounds[3], bounds[4] },
1541       { bounds[0], bounds[3], bounds[4] },
1542       { bounds[0], bounds[2], bounds[5] },
1543       { bounds[1], bounds[2], bounds[5] },
1544       { bounds[1], bounds[3], bounds[5] },
1545       { bounds[0], bounds[3], bounds[5] } };
1546 
1547   this->PlaceWidget(corners);
1548 }
1549 
1550 //----------------------------------------------------------------------------
PlaceWidget(double corners[8][3])1551 void vtkParallelopipedRepresentation::PlaceWidget(double corners[8][3])
1552 {
1553   // Scale the corners of parallelopiped according to the place factor.
1554   // Note that the default place factor is 0.5. So if your corners
1555   // appear half way in, don't be surprised.
1556   //
1557   double center[3] = {0.0, 0.0, 0.0}, newCorners[8][3];
1558   for (int j = 0; j < 3; j++)
1559   {
1560     for (int i = 0; i < 8; center[j] += corners[i][j], i++)
1561     {
1562       ;
1563     }
1564     center[j] /= 8.0;
1565 
1566     for (int i = 0; i < 8; i++)
1567     {
1568       newCorners[i][j] = center[j] +
1569         this->PlaceFactor*(corners[i][j]-center[j]);
1570     }
1571   }
1572 
1573   for (int i = 0; i < 8; i++)
1574   {
1575     this->Points->SetPoint(i, newCorners[i]);
1576   }
1577   this->AbsoluteMinimumThickness =
1578     this->HexPolyData->GetLength()*this->MinimumThickness;
1579 
1580   this->ChairPointPlacer->SetMinimumDistance( 0.5 * this->AbsoluteMinimumThickness );
1581 
1582   // Initialize the chair points too
1583   for (int i = 8; i < 16; i++)
1584   {
1585     this->Points->SetPoint(i, newCorners[0]);
1586   }
1587 
1588   this->PositionHandles();
1589 }
1590 
1591 //----------------------------------------------------------------------------
GetPolyData(vtkPolyData * pd)1592 void vtkParallelopipedRepresentation::GetPolyData(vtkPolyData *pd)
1593 {
1594   pd->SetPoints(this->HexPolyData->GetPoints());
1595   pd->SetPolys(this->HexPolyData->GetPolys());
1596 }
1597 
1598 //----------------------------------------------------------------------------
GetBounds()1599 double *vtkParallelopipedRepresentation::GetBounds()
1600 {
1601   return this->Points->GetBounds();
1602 }
1603 
1604 //----------------------------------------------------------------------------
BuildRepresentation()1605 void vtkParallelopipedRepresentation::BuildRepresentation()
1606 {
1607   this->Points->Modified();
1608 }
1609 
1610 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)1611 void vtkParallelopipedRepresentation::PrintSelf(ostream& os, vtkIndent indent)
1612 {
1613   this->Superclass::PrintSelf(os,indent);
1614 
1615   os << indent << "Minimum Thickness: " << this->MinimumThickness << "\n";
1616 
1617   if ( this->HandleProperty )
1618   {
1619     os << indent << "Handle Property: " << this->HandleProperty << "\n";
1620   }
1621   else
1622   {
1623     os << indent << "Handle Property: (none)\n";
1624   }
1625 
1626   if ( this->HoveredHandleProperty )
1627   {
1628     os << indent << "Hovered Handle Property: " << this->HoveredHandleProperty << "\n";
1629   }
1630   else
1631   {
1632     os << indent << "Hovered Handle Property: (none)\n";
1633   }
1634 
1635   if ( this->FaceProperty )
1636   {
1637     os << indent << "Face Property: " << this->FaceProperty << "\n";
1638   }
1639   else
1640   {
1641     os << indent << "Face Property: (none)\n";
1642   }
1643 
1644   if ( this->OutlineProperty )
1645   {
1646     os << indent << "Outline Property: " << this->OutlineProperty << "\n";
1647   }
1648   else
1649   {
1650     os << indent << "Outline Property: (none)\n";
1651   }
1652 
1653   if ( this->SelectedHandleProperty )
1654   {
1655     os << indent << "Selected Handle Property: " << this->SelectedHandleProperty << "\n";
1656   }
1657   else
1658   {
1659     os << indent << "Selected Handle Property: (none)\n";
1660   }
1661 
1662   if ( this->SelectedFaceProperty )
1663   {
1664     os << indent << "Selected Face Property: " << this->SelectedFaceProperty << "\n";
1665   }
1666   else
1667   {
1668     os << indent << "Selected Face Property: (none)\n";
1669   }
1670 
1671   if ( this->SelectedOutlineProperty )
1672   {
1673     os << indent << "Selected Outline Property: " << this->SelectedOutlineProperty << "\n";
1674   }
1675   else
1676   {
1677     os << indent << "Selected Outline Property: (none)\n";
1678   }
1679 
1680   // this->InteractionState is printed in superclass
1681   // this is commented to avoid PrintSelf errors
1682 }
1683