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