1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkImageResliceMapper.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 side copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkImageResliceMapper.h"
16 
17 #include "vtkImageSliceMapper.h"
18 #include "vtkRenderer.h"
19 #include "vtkCamera.h"
20 #include "vtkImageSlice.h"
21 #include "vtkImageData.h"
22 #include "vtkImageProperty.h"
23 #include "vtkLookupTable.h"
24 #include "vtkMath.h"
25 #include "vtkPoints.h"
26 #include "vtkMatrix4x4.h"
27 #include "vtkLinearTransform.h"
28 #include "vtkPlane.h"
29 #include "vtkStreamingDemandDrivenPipeline.h"
30 #include "vtkGarbageCollector.h"
31 #include "vtkInformation.h"
32 #include "vtkInformationVector.h"
33 #include "vtkImageResliceToColors.h"
34 #include "vtkAbstractImageInterpolator.h"
35 #include "vtkObjectFactory.h"
36 
37 // A tolerance to compensate for roundoff errors
38 #define VTK_RESLICE_MAPPER_VOXEL_TOL 7.62939453125e-06
39 
40 vtkStandardNewMacro(vtkImageResliceMapper);
41 
42 //----------------------------------------------------------------------------
vtkImageResliceMapper()43 vtkImageResliceMapper::vtkImageResliceMapper()
44 {
45   this->SliceMapper = vtkImageSliceMapper::New();
46   this->ImageReslice = vtkImageResliceToColors::New();
47   this->ResliceMatrix = vtkMatrix4x4::New();
48   this->WorldToDataMatrix = vtkMatrix4x4::New();
49   this->SliceToWorldMatrix = vtkMatrix4x4::New();
50 
51   this->JumpToNearestSlice = 0;
52   this->AutoAdjustImageQuality = 1;
53   this->SeparateWindowLevelOperation = 1;
54   this->SlabType = VTK_IMAGE_SLAB_MEAN;
55   this->SlabThickness = 0.0;
56   this->SlabSampleFactor = 2;
57   this->ImageSampleFactor = 1;
58   this->ResampleToScreenPixels = 1;
59   this->InternalResampleToScreenPixels = 0;
60   this->ResliceNeedUpdate = 0;
61 
62   // streaming requires an output port
63   this->SetNumberOfOutputPorts(1);
64 }
65 
66 //----------------------------------------------------------------------------
~vtkImageResliceMapper()67 vtkImageResliceMapper::~vtkImageResliceMapper()
68 {
69   if (this->SliceMapper)
70   {
71     this->SliceMapper->Delete();
72   }
73   if (this->ImageReslice)
74   {
75     this->ImageReslice->Delete();
76   }
77   if (this->ResliceMatrix)
78   {
79     this->ResliceMatrix->Delete();
80   }
81   if (this->WorldToDataMatrix)
82   {
83     this->WorldToDataMatrix->Delete();
84   }
85   if (this->SliceToWorldMatrix)
86   {
87     this->SliceToWorldMatrix->Delete();
88   }
89 }
90 
91 //----------------------------------------------------------------------------
SetSlicePlane(vtkPlane * plane)92 void vtkImageResliceMapper::SetSlicePlane(vtkPlane *plane)
93 {
94   if (this->SlicePlane == plane)
95   {
96     return;
97   }
98   if (this->SlicePlane)
99   {
100     this->SlicePlane->Delete();
101   }
102   if (!plane)
103   {
104     this->SlicePlane = vtkPlane::New();
105   }
106   else
107   {
108     this->SlicePlane = plane;
109     plane->Register(this);
110   }
111 
112   this->Modified();
113 }
114 
115 //----------------------------------------------------------------------------
SetInterpolator(vtkAbstractImageInterpolator * interpolator)116 void vtkImageResliceMapper::SetInterpolator(
117   vtkAbstractImageInterpolator *interpolator)
118 {
119   vtkMTimeType mtime = this->ImageReslice->GetMTime();
120 
121   this->ImageReslice->SetInterpolator(interpolator);
122 
123   if (this->ImageReslice->GetMTime() > mtime)
124   {
125     this->Modified();
126   }
127 }
128 
129 //----------------------------------------------------------------------------
GetInterpolator()130 vtkAbstractImageInterpolator *vtkImageResliceMapper::GetInterpolator()
131 {
132   return this->ImageReslice->GetInterpolator();
133 }
134 
135 //----------------------------------------------------------------------------
ReleaseGraphicsResources(vtkWindow * win)136 void vtkImageResliceMapper::ReleaseGraphicsResources(vtkWindow *win)
137 {
138   this->SliceMapper->ReleaseGraphicsResources(win);
139 }
140 
141 //----------------------------------------------------------------------------
Render(vtkRenderer * ren,vtkImageSlice * prop)142 void vtkImageResliceMapper::Render(vtkRenderer *ren, vtkImageSlice *prop)
143 {
144   if (this->ResliceNeedUpdate)
145   {
146     this->ImageReslice->SetInputConnection(
147       this->GetInputConnection(0, 0));
148     this->ImageReslice->UpdateWholeExtent();
149     this->ResliceNeedUpdate = 0;
150   }
151 
152   // apply checkerboard pattern (should have timestamps)
153   vtkImageProperty *property = prop->GetProperty();
154   if (property && property->GetCheckerboard() &&
155       this->InternalResampleToScreenPixels &&
156       !this->SeparateWindowLevelOperation &&
157       this->SliceFacesCamera)
158   {
159     this->CheckerboardImage(this->ImageReslice->GetOutput(),
160       ren->GetActiveCamera(), property);
161   }
162 
163   // delegate to vtkImageSliceMapper
164   this->SliceMapper->SetInputConnection(
165     this->ImageReslice->GetOutputPort());
166   this->SliceMapper->GetDataToWorldMatrix()->DeepCopy(
167     this->SliceToWorldMatrix);
168   // the mapper uses SliceFacesCamera to decide whether to use a polygon
169   // for the texture versus using a quad the size of the window
170   this->SliceMapper->SetSliceFacesCamera(
171     (this->SliceFacesCamera && !this->SeparateWindowLevelOperation));
172   this->SliceMapper->SetExactPixelMatch(this->InternalResampleToScreenPixels);
173   this->SliceMapper->SetBorder( (this->Border ||
174                                  this->InternalResampleToScreenPixels) );
175   this->SliceMapper->SetBackground( (this->Background &&
176     !(this->SliceFacesCamera && this->InternalResampleToScreenPixels &&
177       !this->SeparateWindowLevelOperation) ) );
178   this->SliceMapper->SetPassColorData(!this->SeparateWindowLevelOperation);
179   this->SliceMapper->SetDisplayExtent(this->ImageReslice->GetOutputExtent());
180 
181   // render pass info for members of vtkImageStack
182   this->SliceMapper->MatteEnable = this->MatteEnable;
183   this->SliceMapper->ColorEnable = this->ColorEnable;
184   this->SliceMapper->DepthEnable = this->DepthEnable;
185 
186   // let vtkImageSliceMapper do the rest of the work
187   this->SliceMapper->SetNumberOfThreads(this->NumberOfThreads);
188   this->SliceMapper->SetClippingPlanes(this->ClippingPlanes);
189   this->SliceMapper->Render(ren, prop);
190 }
191 
192 //----------------------------------------------------------------------------
Update(int port)193 void vtkImageResliceMapper::Update(int port)
194 {
195   // I don't like to override Update, or call Modified() in Update,
196   // but this allows updates to be forced where MTimes can't be used
197   bool resampleToScreenPixels = (this->ResampleToScreenPixels != 0);
198   vtkRenderer *ren = nullptr;
199 
200   if (this->AutoAdjustImageQuality && resampleToScreenPixels)
201   {
202     // only use image-size texture if image is smaller than render window,
203     // since otherwise there is far less advantage in doing so
204     vtkImageSlice *prop = this->GetCurrentProp();
205     ren = this->GetCurrentRenderer();
206     if (ren && prop)
207     {
208       int *rsize = ren->GetSize();
209       int maxrsize = (rsize[0] > rsize[1] ? rsize[0] : rsize[1]);
210       int *isize = this->GetInput()->GetDimensions();
211       int maxisize = (isize[0] > isize[1] ? isize[0] : isize[1]);
212       maxisize = (isize[2] > maxisize ? isize[2] : maxisize);
213       if (maxisize <= maxrsize && maxisize <= 1024)
214       {
215         resampleToScreenPixels = (prop->GetAllocatedRenderTime() >= 1.0);
216       }
217     }
218   }
219 
220   if (resampleToScreenPixels)
221   {
222     // force update if quality has increased to "ResampleToScreenPixels"
223     if (!this->InternalResampleToScreenPixels)
224     {
225       this->Modified();
226     }
227     else
228     {
229       // force update if renderer size has changes, since the texture
230       // size is equal to the renderer size for "ResampleToScreenPixels"
231       if (!ren)
232       {
233         ren = this->GetCurrentRenderer();
234       }
235       if (ren)
236       {
237         int *extent = this->ImageReslice->GetOutputExtent();
238         int *size = ren->GetSize();
239         if (size[0] != (extent[1] - extent[0] + 1) ||
240             size[1] != (extent[3] - extent[2] + 1))
241         {
242           this->Modified();
243         }
244       }
245     }
246   }
247   else if (this->InternalResampleToScreenPixels)
248   {
249     // if execution reaches this point in the code, then the
250     // rendering has just switched to interactive quality, and it is
251     // necessary to force update if modified since the last update
252     if (this->GetMTime() > this->UpdateTime.GetMTime())
253     {
254       this->Modified();
255     }
256     else
257     {
258       // don't switch yet: wait until the camera changes position,
259       // which will cause the MTime to change
260       resampleToScreenPixels = true;
261     }
262   }
263 
264   this->InternalResampleToScreenPixels = resampleToScreenPixels;
265 
266   // Always update if something else caused the input to update
267   vtkImageData *input = this->GetInput();
268   if (input && input->GetUpdateTime() > this->UpdateTime.GetMTime())
269   {
270     this->Modified();
271   }
272 
273   this->Superclass::Update(port);
274   this->UpdateTime.Modified();
275 }
276 
277 //----------------------------------------------------------------------------
Update()278 void vtkImageResliceMapper::Update()
279 {
280   this->Superclass::Update();
281 }
282 
283 //----------------------------------------------------------------------------
Update(int port,vtkInformationVector *)284 int vtkImageResliceMapper::Update(
285   int port, vtkInformationVector*)
286 {
287   // One can't really make requests of a mapper so default to regular
288   // update.
289   this->Update(port);
290   return 1;
291 }
292 
293 //----------------------------------------------------------------------------
Update(vtkInformation *)294 int vtkImageResliceMapper::Update(vtkInformation*)
295 {
296   // One can't really make requests of a mapper so default to regular
297   // update.
298   this->Update();
299   return 1;
300 }
301 
302 //----------------------------------------------------------------------------
ProcessRequest(vtkInformation * request,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)303 int vtkImageResliceMapper::ProcessRequest(
304   vtkInformation* request, vtkInformationVector** inputVector,
305   vtkInformationVector* outputVector)
306 {
307   if (request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_INFORMATION()))
308   {
309     // use superclass method to update some info
310     this->Superclass::ProcessRequest(request, inputVector, outputVector);
311 
312     // need the prop and renderer
313     vtkImageSlice *prop = this->GetCurrentProp();
314     vtkRenderer *ren = this->GetCurrentRenderer();
315 
316     if (ren && prop)
317     {
318       vtkImageProperty *property = prop->GetProperty();
319 
320       // Get point/normal from camera
321       if (this->SliceFacesCamera || this->SliceAtFocalPoint)
322       {
323         vtkCamera *camera = ren->GetActiveCamera();
324 
325         if (this->SliceFacesCamera)
326         {
327           double normal[3];
328           camera->GetDirectionOfProjection(normal);
329           normal[0] = -normal[0];
330           normal[1] = -normal[1];
331           normal[2] = -normal[2];
332           this->SlicePlane->SetNormal(normal);
333         }
334 
335         if (this->SliceAtFocalPoint)
336         {
337           double point[4];
338           camera->GetFocalPoint(point);
339 
340           if (this->JumpToNearestSlice)
341           {
342             double normal[4];
343             this->SlicePlane->GetNormal(normal);
344             normal[3] = -vtkMath::Dot(point, normal);
345             point[3] = 1.0;
346 
347             // convert normal to data coordinates
348             double worldToData[16];
349             vtkMatrix4x4 *dataToWorld = this->GetDataToWorldMatrix();
350             vtkMatrix4x4::Transpose(*dataToWorld->Element, worldToData);
351             vtkMatrix4x4::MultiplyPoint(worldToData, normal, normal);
352 
353             // find the slice orientation from the normal
354             int k = 0;
355             double maxsq = 0;
356             double sumsq = 0;
357             for (int i = 0; i < 3; i++)
358             {
359               double tmpsq = normal[i]*normal[i];
360               sumsq += tmpsq;
361               if (tmpsq > maxsq)
362               {
363                 maxsq = tmpsq;
364                 k = i;
365               }
366             }
367 
368             // if the slice is not oblique
369             if ((1.0 - maxsq/sumsq) < 1e-12)
370             {
371               // get the point in data coordinates
372               vtkMatrix4x4::Invert(*dataToWorld->Element, worldToData);
373               vtkMatrix4x4::MultiplyPoint(worldToData, point, point);
374 
375               // set the point to lie exactly on a slice
376               double z = (point[k] - this->DataOrigin[k])/this->DataSpacing[k];
377               if (z > VTK_INT_MIN && z < VTK_INT_MAX)
378               {
379                 int j = vtkMath::Floor(z + 0.5);
380                 point[k] = j*this->DataSpacing[k] + this->DataOrigin[k];
381               }
382 
383               // convert back to world coordinates
384               dataToWorld->MultiplyPoint(point, point);
385             }
386           }
387 
388           this->SlicePlane->SetOrigin(point);
389         }
390 
391       } // end of "Get point/normal from camera"
392 
393       // set the matrices
394       this->UpdateResliceMatrix(ren, prop);
395 
396       // update the coords for the polygon to be textured
397       this->UpdatePolygonCoords(ren);
398 
399       // set the reslice spacing/origin/extent/axes
400       this->UpdateResliceInformation(ren);
401 
402       // set the reslice bits related to the property
403       this->UpdateResliceInterpolation(property);
404 
405       // update anything related to the image coloring
406       this->UpdateColorInformation(property);
407     }
408 
409     // set the number of threads to use when executing
410     this->ImageReslice->SetNumberOfThreads(this->NumberOfThreads);
411 
412     // delegate request to vtkImageReslice (generally not a good thing to
413     // do, but I'm familiar with the vtkImageReslice code that gets called).
414     return this->ImageReslice->ProcessRequest(
415       request, inputVector, outputVector);
416   }
417 
418   if(request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_UPDATE_EXTENT()))
419   {
420     if (this->Streaming)
421     {
422       // delegate request to vtkImageReslice (generally not a good thing to
423       // do, but I'm familiar with the vtkImageReslice code that gets called).
424       return this->ImageReslice->ProcessRequest(
425         request, inputVector, outputVector);
426     }
427     else
428     {
429       vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
430       int ext[6];
431       inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), ext);
432       inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), ext, 6);
433     }
434     return 1;
435   }
436 
437   if(request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_DATA()))
438   {
439     vtkInformation *outInfo = outputVector->GetInformationObject(0);
440     vtkImageData *output = vtkImageData::SafeDownCast(
441       outInfo->Get(vtkDataObject::DATA_OBJECT()));
442 
443     // set output extent to avoid re-execution
444     output->GetInformation()->Set(vtkDataObject::DATA_EXTENT(),
445       outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()), 6);
446 
447     // do an update of Reslice on the next render
448     this->ResliceNeedUpdate = 1;
449 
450     return 1;
451   }
452 
453   return this->Superclass::ProcessRequest(request, inputVector, outputVector);
454 }
455 
456 //----------------------------------------------------------------------------
457 // Update the WorldToData transformation matrix, which is just the
458 // inverse of the vtkProp3D matrix.
UpdateWorldToDataMatrix(vtkImageSlice * prop)459 void vtkImageResliceMapper::UpdateWorldToDataMatrix(vtkImageSlice *prop)
460 {
461   // copy the matrix, but only if it has changed (we do this to
462   // preserve the modified time of the matrix)
463   double tmpmat[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
464   if (!prop->GetIsIdentity())
465   {
466     vtkMatrix4x4::Invert(*prop->GetMatrix()->Element, tmpmat);
467   }
468   double *mat = *this->WorldToDataMatrix->Element;
469   for (int i = 0; i < 16; i++)
470   {
471     if (mat[i] != tmpmat[i])
472     {
473       this->WorldToDataMatrix->DeepCopy(tmpmat);
474       break;
475     }
476   }
477 }
478 
479 //----------------------------------------------------------------------------
480 // Update the SliceToWorld transformation matrix
UpdateSliceToWorldMatrix(vtkCamera * camera)481 void vtkImageResliceMapper::UpdateSliceToWorldMatrix(vtkCamera *camera)
482 {
483   // Get slice plane in world coords by passing null as the prop matrix
484   double plane[4];
485   this->GetSlicePlaneInDataCoords(nullptr, plane);
486 
487   // Make sure normal is facing towards camera
488   vtkMatrix4x4 *viewMatrix = camera->GetViewTransformMatrix();
489   double *ndop = viewMatrix->Element[2];
490   if (vtkMath::Dot(ndop, plane) < 0)
491   {
492     plane[0] = -plane[0];
493     plane[1] = -plane[1];
494     plane[2] = -plane[2];
495     plane[3] = -plane[3];
496   }
497 
498   // The normal is the first three elements
499   double *normal = plane;
500 
501   // The last element is -dot(normal, origin)
502   double dp = -plane[3];
503 
504   // Compute rotation angle between camera axis and slice normal
505   double vec[3];
506   vtkMath::Cross(ndop, normal, vec);
507   double costheta = vtkMath::Dot(ndop, normal);
508   double sintheta = vtkMath::Norm(vec);
509   double theta = atan2(sintheta, costheta);
510   if (sintheta != 0)
511   {
512     vec[0] /= sintheta;
513     vec[1] /= sintheta;
514     vec[2] /= sintheta;
515   }
516   // convert to quaternion
517   costheta = cos(0.5*theta);
518   sintheta = sin(0.5*theta);
519   double quat[4];
520   quat[0] = costheta;
521   quat[1] = vec[0]*sintheta;
522   quat[2] = vec[1]*sintheta;
523   quat[3] = vec[2]*sintheta;
524   // convert to matrix
525   double mat[3][3];
526   vtkMath::QuaternionToMatrix3x3(quat, mat);
527 
528   // Create a slice-to-world transform matrix
529   // The columns are v1, v2, normal
530   vtkMatrix4x4 *sliceToWorld = this->SliceToWorldMatrix;
531 
532   double v1[3], v2[3];
533   vtkMath::Multiply3x3(mat, viewMatrix->Element[0], v1);
534   vtkMath::Multiply3x3(mat, viewMatrix->Element[1], v2);
535 
536   sliceToWorld->Element[0][0] = v1[0];
537   sliceToWorld->Element[1][0] = v1[1];
538   sliceToWorld->Element[2][0] = v1[2];
539   sliceToWorld->Element[3][0] = 0.0;
540 
541   sliceToWorld->Element[0][1] = v2[0];
542   sliceToWorld->Element[1][1] = v2[1];
543   sliceToWorld->Element[2][1] = v2[2];
544   sliceToWorld->Element[3][1] = 0.0;
545 
546   sliceToWorld->Element[0][2] = normal[0];
547   sliceToWorld->Element[1][2] = normal[1];
548   sliceToWorld->Element[2][2] = normal[2];
549   sliceToWorld->Element[3][2] = 0.0;
550 
551   sliceToWorld->Element[0][3] = -dp*normal[0];
552   sliceToWorld->Element[1][3] = -dp*normal[1];
553   sliceToWorld->Element[2][3] = dp-dp*normal[2];
554   sliceToWorld->Element[3][3] = 1.0;
555 }
556 
557 //----------------------------------------------------------------------------
558 // Update the reslice matrix, which is the slice-to-data matrix.
UpdateResliceMatrix(vtkRenderer * ren,vtkImageSlice * prop)559 void vtkImageResliceMapper::UpdateResliceMatrix(
560   vtkRenderer *ren, vtkImageSlice *prop)
561 {
562   // Save the old matrix
563   double *matrixElements = *this->ResliceMatrix->Element;
564   double oldMatrixElements[16];
565   vtkMatrix4x4::DeepCopy(oldMatrixElements, matrixElements);
566 
567   // Get world-to-data matrix from the prop matrix
568   this->UpdateWorldToDataMatrix(prop);
569 
570   // Check if prop matrix is orthonormal
571   bool propMatrixIsOrthonormal = false;
572   vtkMatrix4x4 *propMatrix = nullptr;
573   if (!this->InternalResampleToScreenPixels)
574   {
575     static double tol = 1e-12;
576     propMatrix = prop->GetMatrix();
577     double *row0 = propMatrix->Element[0];
578     double *row1 = propMatrix->Element[1];
579     double *row2 = propMatrix->Element[2];
580     propMatrixIsOrthonormal = (
581       fabs(vtkMath::Dot(row0, row0) - 1.0) < tol &&
582       fabs(vtkMath::Dot(row1, row1) - 1.0) < tol &&
583       fabs(vtkMath::Dot(row2, row2) - 1.0) < tol &&
584       fabs(vtkMath::Dot(row0, row1)) < tol &&
585       fabs(vtkMath::Dot(row0, row2)) < tol &&
586       fabs(vtkMath::Dot(row1, row2)) < tol);
587   }
588 
589   // Compute SliceToWorld matrix from camera if prop matrix is not
590   // orthonormal or if InternalResampleToScreenPixels is set
591   if (this->InternalResampleToScreenPixels ||
592       !propMatrixIsOrthonormal)
593   {
594     this->UpdateSliceToWorldMatrix(ren->GetActiveCamera());
595     vtkMatrix4x4::Multiply4x4(
596       this->WorldToDataMatrix, this->SliceToWorldMatrix, this->ResliceMatrix);
597   }
598   else
599   {
600     // Get the matrices used to compute the reslice matrix
601     vtkMatrix4x4 *resliceMatrix = this->ResliceMatrix;
602     vtkMatrix4x4 *viewMatrix =
603       ren->GetActiveCamera()->GetViewTransformMatrix();
604 
605     // Get slice plane in world coords by passing null as the matrix
606     double wplane[4];
607     this->GetSlicePlaneInDataCoords(nullptr, wplane);
608 
609     // Check whether normal is facing towards camera, the "ndop" is
610     // the negative of the direction of projection for the camera
611     double *ndop = viewMatrix->Element[2];
612     double dotprod = vtkMath::Dot(ndop, wplane);
613 
614     // Get slice plane in data coords by passing the prop matrix, flip
615     // normal to face the camera
616     double plane[4];
617     this->GetSlicePlaneInDataCoords(propMatrix, plane);
618     if (dotprod < 0)
619     {
620       plane[0] = -plane[0];
621       plane[1] = -plane[1];
622       plane[2] = -plane[2];
623       plane[3] = -plane[3];
624 
625       wplane[0] = -wplane[0];
626       wplane[1] = -wplane[1];
627       wplane[2] = -wplane[2];
628       wplane[3] = -wplane[3];
629     }
630 
631     // Find the largest component of the normal
632     int maxi = 0;
633     double maxv = 0.0;
634     for (int i = 0; i < 3; i++)
635     {
636       double tmp = plane[i]*plane[i];
637       if (tmp > maxv)
638       {
639         maxi = i;
640         maxv = tmp;
641       }
642     }
643 
644     // Create the corresponding axis
645     double axis[3];
646     axis[0] = 0.0;
647     axis[1] = 0.0;
648     axis[2] = 0.0;
649     axis[maxi] = ((plane[maxi] < 0.0) ? -1.0 : 1.0);
650 
651     // Create two orthogonal axes
652     double saxis[3], taxis[3];
653     taxis[0] = 0.0;
654     taxis[1] = 1.0;
655     taxis[2] = 0.0;
656     if (maxi == 1)
657     {
658       taxis[1] = 0.0;
659       taxis[2] = 1.0;
660     }
661     vtkMath::Cross(taxis, axis, saxis);
662 
663     // The normal is the first three elements
664     double *normal = plane;
665 
666     // The last element is -dot(normal, origin)
667     double dp = (-plane[3] +
668                  wplane[0]*propMatrix->Element[0][3] +
669                  wplane[1]*propMatrix->Element[1][3] +
670                  wplane[2]*propMatrix->Element[2][3]);
671 
672     // Compute the rotation angle between the axis and the normal
673     double vec[3];
674     vtkMath::Cross(axis, normal, vec);
675     double costheta = vtkMath::Dot(axis, normal);
676     double sintheta = vtkMath::Norm(vec);
677     double theta = atan2(sintheta, costheta);
678     if (sintheta != 0)
679     {
680       vec[0] /= sintheta;
681       vec[1] /= sintheta;
682       vec[2] /= sintheta;
683     }
684     // convert to quaternion
685     costheta = cos(0.5*theta);
686     sintheta = sin(0.5*theta);
687     double quat[4];
688     quat[0] = costheta;
689     quat[1] = vec[0]*sintheta;
690     quat[2] = vec[1]*sintheta;
691     quat[3] = vec[2]*sintheta;
692     // convert to matrix
693     double mat[3][3];
694     vtkMath::QuaternionToMatrix3x3(quat, mat);
695 
696     // Create a slice-to-data transform matrix
697     // The columns are v1, v2, normal
698     double v1[3], v2[3];
699     vtkMath::Multiply3x3(mat, saxis, v1);
700     vtkMath::Multiply3x3(mat, taxis, v2);
701 
702     resliceMatrix->Element[0][0] = v1[0];
703     resliceMatrix->Element[1][0] = v1[1];
704     resliceMatrix->Element[2][0] = v1[2];
705     resliceMatrix->Element[3][0] = 0.0;
706 
707     resliceMatrix->Element[0][1] = v2[0];
708     resliceMatrix->Element[1][1] = v2[1];
709     resliceMatrix->Element[2][1] = v2[2];
710     resliceMatrix->Element[3][1] = 0.0;
711 
712     resliceMatrix->Element[0][2] = normal[0];
713     resliceMatrix->Element[1][2] = normal[1];
714     resliceMatrix->Element[2][2] = normal[2];
715     resliceMatrix->Element[3][2] = 0.0;
716 
717     resliceMatrix->Element[0][3] = dp*(propMatrix->Element[2][0] - normal[0]) -
718       (propMatrix->Element[0][3]*propMatrix->Element[0][0] +
719        propMatrix->Element[1][3]*propMatrix->Element[1][0] +
720        propMatrix->Element[2][3]*propMatrix->Element[2][0]);
721     resliceMatrix->Element[1][3] = dp*(propMatrix->Element[2][1] - normal[1]) -
722       (propMatrix->Element[0][3]*propMatrix->Element[0][1] +
723        propMatrix->Element[1][3]*propMatrix->Element[1][1] +
724        propMatrix->Element[2][3]*propMatrix->Element[2][1]);
725     resliceMatrix->Element[2][3] = dp*(propMatrix->Element[2][2] - normal[2]) -
726       (propMatrix->Element[0][3]*propMatrix->Element[0][2] +
727        propMatrix->Element[1][3]*propMatrix->Element[1][2] +
728        propMatrix->Element[2][3]*propMatrix->Element[2][2]);
729     resliceMatrix->Element[3][3] = 1.0;
730 
731     // Compute the SliceToWorldMatrix
732     vtkMatrix4x4::Multiply4x4(propMatrix, resliceMatrix,
733       this->SliceToWorldMatrix);
734   }
735 
736   // If matrix changed, mark as modified so that Reslice will update
737   int matrixChanged = 0;
738   for (int j = 0; j < 16; j++)
739   {
740     matrixChanged |= (matrixElements[j] != oldMatrixElements[j]);
741   }
742   if (matrixChanged)
743   {
744     this->ResliceMatrix->Modified();
745   }
746 }
747 
748 //----------------------------------------------------------------------------
749 // Do all the fancy math to set up the reslicing
UpdateResliceInformation(vtkRenderer * ren)750 void vtkImageResliceMapper::UpdateResliceInformation(vtkRenderer *ren)
751 {
752   vtkMatrix4x4 *resliceMatrix = this->ResliceMatrix;
753   vtkImageResliceToColors *reslice = this->ImageReslice;
754 
755   int extent[6];
756   double spacing[3];
757   double origin[3];
758 
759   // Get current spacing and origin
760   reslice->GetOutputSpacing(spacing);
761   reslice->GetOutputOrigin(origin);
762   reslice->GetOutputExtent(extent);
763 
764   // Get the view matrix
765   vtkCamera *camera = ren->GetActiveCamera();
766   vtkMatrix4x4 *viewMatrix = camera->GetViewTransformMatrix();
767 
768   // Get slice plane in world coords by passing null as the matrix
769   double plane[4];
770   this->GetSlicePlaneInDataCoords(nullptr, plane);
771 
772   // Check whether normal is facing towards camera, the "ndop" is
773   // the negative of the direction of projection for the camera
774   double *ndop = viewMatrix->Element[2];
775   if (vtkMath::Dot(ndop, plane) < 0)
776   {
777     plane[0] = -plane[0];
778     plane[1] = -plane[1];
779     plane[2] = -plane[2];
780     plane[3] = -plane[3];
781   }
782 
783   // Get the z position of the slice in slice coords
784   // (requires plane to be normalized by GetSlicePlaneInDataCoords)
785   double z = (plane[2] - 2.0)*plane[3];
786 
787   if (this->InternalResampleToScreenPixels)
788   {
789     // Get the projection matrix
790     double aspect = ren->GetTiledAspectRatio();
791     vtkMatrix4x4 *projMatrix = camera->GetProjectionTransformMatrix(
792                                  aspect, 0, 1);
793 
794     // Compute other useful matrices
795     double worldToView[16];
796     double viewToWorld[16];
797     double planeWorldToView[16];
798     vtkMatrix4x4::Multiply4x4(
799       *projMatrix->Element, *viewMatrix->Element, worldToView);
800     vtkMatrix4x4::Invert(worldToView, viewToWorld);
801     vtkMatrix4x4::Transpose(viewToWorld, planeWorldToView);
802 
803     double worldToSlice[16];
804     double viewToSlice[16];
805     vtkMatrix4x4::Invert(*this->SliceToWorldMatrix->Element, worldToSlice);
806     vtkMatrix4x4::Multiply4x4(worldToSlice, viewToWorld, viewToSlice);
807 
808     // Transform the plane into view coordinates, using the transpose
809     // of the inverse of the world-to-view matrix
810     vtkMatrix4x4::MultiplyPoint(planeWorldToView, plane, plane);
811 
812     // Compute the bounds in slice coords
813     double xmin = VTK_DOUBLE_MAX;
814     double xmax = -VTK_DOUBLE_MAX;
815     double ymin = VTK_DOUBLE_MAX;
816     double ymax = -VTK_DOUBLE_MAX;
817 
818     for (int i = 0; i < 4; i++)
819     {
820       // The four corners of the view
821       double x = (((i & 1) == 0) ? -1.0 : 1.0);
822       double y = (((i & 2) == 0) ? -1.0 : 1.0);
823 
824       double hpoint[4];
825       hpoint[0] = x;
826       hpoint[1] = y;
827       hpoint[2] = 0.0;
828       hpoint[3] = 1.0;
829 
830       if (fabs(plane[2]) < 1e-6)
831       {
832         // Looking at plane edge-on, just put some
833         // points at front clipping plane, others at back plane
834         hpoint[2] = (((i & 1) == 0) ? 0.0 : 1.0);
835       }
836       else
837       {
838         // Intersect with the slice plane
839         hpoint[2] = - (x*plane[0] + y*plane[1] + plane[3])/plane[2];
840 
841         // Clip to the front and back clipping planes
842         if (hpoint[2] < 0)
843         {
844           hpoint[2] = 0.0;
845         }
846         else if (hpoint[2] > 1)
847         {
848           hpoint[2] = 1.0;
849         }
850       }
851 
852       // Transform into slice coords
853       vtkMatrix4x4::MultiplyPoint(viewToSlice, hpoint, hpoint);
854 
855       x = hpoint[0]/hpoint[3];
856       y = hpoint[1]/hpoint[3];
857 
858       // Find min/max in slice coords
859       if (x < xmin) { xmin = x; }
860       if (x > xmax) { xmax = x; }
861       if (y < ymin) { ymin = y; }
862       if (y > ymax) { ymax = y; }
863     }
864 
865     // The ResliceExtent is always set to the renderer size,
866     // this is the maximum size ever required and sticking to
867     // this size avoids any memory reallocation on GPU or CPU
868     int *size = ren->GetSize();
869     int xsize = ((size[0] <= 0) ? 1 : size[0]);
870     int ysize = ((size[1] <= 0) ? 1 : size[1]);
871 
872     extent[0] = 0;
873     extent[1] = xsize - 1;
874     extent[2] = 0;
875     extent[3] = ysize - 1;
876     extent[4] = 0;
877     extent[5] = 0;
878 
879     // Find the spacing
880     spacing[0] = (xmax - xmin)/xsize;
881     spacing[1] = (ymax - ymin)/ysize;
882 
883     // Corner of resliced plane, including half-pixel offset to
884     // exactly match texels to pixels in the final rendering
885     origin[0] = xmin + 0.5*spacing[0];
886     origin[1] = ymin + 0.5*spacing[1];
887     origin[2] = z;
888   }
889   else
890   {
891     // Compute texel spacing from image spacing
892     double inputSpacing[3];
893     this->GetInput()->GetSpacing(inputSpacing);
894     inputSpacing[0] = fabs(inputSpacing[0]);
895     inputSpacing[1] = fabs(inputSpacing[1]);
896     inputSpacing[2] = fabs(inputSpacing[2]);
897     for (int j = 0; j < 2; j++)
898     {
899       double xc = this->ResliceMatrix->Element[j][0];
900       double yc = this->ResliceMatrix->Element[j][1];
901       double zc = this->ResliceMatrix->Element[j][2];
902       double s = (xc*xc*inputSpacing[0] +
903                   yc*yc*inputSpacing[1] +
904                   zc*zc*inputSpacing[2])/sqrt(xc*xc + yc*yc + zc*zc);
905       s /= this->ImageSampleFactor;
906       // only modify if difference is greater than roundoff tolerance
907       if (fabs((s - spacing[j])/s) > 1e-12)
908       {
909         spacing[j] = s;
910       }
911     }
912 
913     // Find the bounds for the texture
914     double xmin = VTK_DOUBLE_MAX;
915     double xmax = -VTK_DOUBLE_MAX;
916     double ymin = VTK_DOUBLE_MAX;
917     double ymax = -VTK_DOUBLE_MAX;
918 
919     vtkPoints *points = this->SliceMapper->GetPoints();
920     vtkIdType n = points->GetNumberOfPoints();
921     if (n == 0)
922     {
923       double inputOrigin[3];
924       this->GetInput()->GetOrigin(inputOrigin);
925       xmin = inputOrigin[0];
926       xmax = inputOrigin[0];
927       ymin = inputOrigin[1];
928       ymax = inputOrigin[1];
929     }
930 
931     for (vtkIdType k = 0; k < n; k++)
932     {
933       double point[3];
934       points->GetPoint(k, point);
935 
936       xmin = ((xmin < point[0]) ? xmin : point[0]);
937       xmax = ((xmax > point[0]) ? xmax : point[0]);
938       ymin = ((ymin < point[1]) ? ymin : point[1]);
939       ymax = ((ymax > point[1]) ? ymax : point[1]);
940     }
941 
942     double tol = VTK_RESLICE_MAPPER_VOXEL_TOL;
943     int xsize = vtkMath::Floor((xmax - xmin)/spacing[0] + tol);
944     int ysize = vtkMath::Floor((ymax - ymin)/spacing[1] + tol);
945     if (this->Border == 0)
946     {
947       xsize += 1;
948       ysize += 1;
949     }
950     if (xsize < 1) { xsize = 1; }
951     if (ysize < 1) { ysize = 1; }
952 
953     // Keep old size if possible, to avoid memory reallocation
954     if ((xsize - 1) > extent[1] || (ysize - 1) > extent[3] ||
955         (0.9*extent[1]/xsize) > 1.0 || (0.9*extent[3]/ysize) > 1.0)
956     {
957       extent[1] = xsize - 1;
958       extent[3] = ysize - 1;
959     }
960     extent[0] = 0;
961     extent[2] = 0;
962     extent[4] = 0;
963     extent[5] = 0;
964 
965     double x0 = xmin + 0.5*spacing[0]*(this->Border != 0);
966     double y0 = ymin + 0.5*spacing[1]*(this->Border != 0);
967 
968     double dx = x0 - origin[0];
969     double dy = y0 - origin[1];
970     double dz = z - origin[2];
971 
972     // only modify origin if it has changed by tolerance
973     if (dx*dx + dy*dy + dz*dz > tol*tol*spacing[0]*spacing[1])
974     {
975       origin[0] = x0;
976       origin[1] = y0;
977       origin[2] = z;
978     }
979   }
980 
981   // Prepare for reslicing
982   reslice->SetResliceAxes(resliceMatrix);
983   reslice->SetOutputExtent(extent);
984   reslice->SetOutputSpacing(spacing);
985   reslice->SetOutputOrigin(origin);
986 
987   if ((this->SliceFacesCamera &&
988        this->InternalResampleToScreenPixels &&
989        !this->SeparateWindowLevelOperation) ||
990       this->SlabThickness > 0)
991   {
992     // if slice follows camera, use reslice to set the border
993     reslice->SetBorder(this->Border);
994   }
995   else
996   {
997     // tell reslice to use a double-thickness border,
998     // since the polygon geometry will dictate the actual size
999     reslice->SetBorder(true);
1000     reslice->SetBorderThickness(1.0);
1001   }
1002 }
1003 
1004 //----------------------------------------------------------------------------
1005 // Do all the fancy math to set up the reslicing
UpdateColorInformation(vtkImageProperty * property)1006 void vtkImageResliceMapper::UpdateColorInformation(vtkImageProperty *property)
1007 {
1008   vtkScalarsToColors *lookupTable = this->DefaultLookupTable;
1009 
1010   if (property)
1011   {
1012     double colorWindow = property->GetColorWindow();
1013     double colorLevel = property->GetColorLevel();
1014     if (property->GetLookupTable())
1015     {
1016       lookupTable = property->GetLookupTable();
1017       if (!property->GetUseLookupTableScalarRange())
1018       {
1019         lookupTable->SetRange(colorLevel - 0.5*colorWindow,
1020                               colorLevel + 0.5*colorWindow);
1021       }
1022     }
1023     else
1024     {
1025       lookupTable->SetRange(colorLevel - 0.5*colorWindow,
1026                             colorLevel + 0.5*colorWindow);
1027     }
1028   }
1029   else
1030   {
1031     lookupTable->SetRange(0, 255);
1032   }
1033   this->ImageReslice->SetBypass(this->SeparateWindowLevelOperation != 0);
1034   this->ImageReslice->SetLookupTable(lookupTable);
1035   double backgroundColor[4] = { 0.0, 0.0, 0.0, 0.0 };
1036   if (this->Background)
1037   {
1038     this->GetBackgroundColor(property, backgroundColor);
1039     backgroundColor[0] *= 255;
1040     backgroundColor[1] *= 255;
1041     backgroundColor[2] *= 255;
1042     backgroundColor[3] *= 255;
1043   }
1044   this->ImageReslice->SetBackgroundColor(backgroundColor);
1045 }
1046 
1047 //----------------------------------------------------------------------------
1048 // Set the reslice interpolation and slab thickness
UpdateResliceInterpolation(vtkImageProperty * property)1049 void vtkImageResliceMapper::UpdateResliceInterpolation(
1050   vtkImageProperty *property)
1051 {
1052   // set the interpolation mode and border
1053   int interpMode = VTK_RESLICE_NEAREST;
1054   int slabSlices = 1;
1055 
1056   if (property)
1057   {
1058     switch(property->GetInterpolationType())
1059     {
1060       case VTK_NEAREST_INTERPOLATION:
1061         interpMode = VTK_RESLICE_NEAREST;
1062         break;
1063       case VTK_LINEAR_INTERPOLATION:
1064         interpMode = VTK_RESLICE_LINEAR;
1065         break;
1066       case VTK_CUBIC_INTERPOLATION:
1067         interpMode = VTK_RESLICE_CUBIC;
1068         break;
1069     }
1070   }
1071 
1072   // set up the slice spacing for slab views
1073   double spacing[3], inputSpacing[3];
1074   this->ImageReslice->GetOutputSpacing(spacing);
1075   this->GetInput()->GetSpacing(inputSpacing);
1076   inputSpacing[0] = fabs(inputSpacing[0]);
1077   inputSpacing[1] = fabs(inputSpacing[1]);
1078   inputSpacing[2] = fabs(inputSpacing[2]);
1079   double xc = this->ResliceMatrix->Element[2][0];
1080   double yc = this->ResliceMatrix->Element[2][1];
1081   double zc = this->ResliceMatrix->Element[2][2];
1082   spacing[2] = (xc*xc*inputSpacing[0] +
1083                 yc*yc*inputSpacing[1] +
1084                 zc*zc*inputSpacing[2])/sqrt(xc*xc + yc*yc + zc*zc);
1085 
1086   // slab slice spacing is half the input slice spacing
1087   int n = vtkMath::Ceil(this->SlabThickness/spacing[2]);
1088   slabSlices = 1 + this->SlabSampleFactor*n;
1089   if (slabSlices > 1)
1090   {
1091     spacing[2] = this->SlabThickness/(slabSlices - 1);
1092   }
1093   this->ImageReslice->SetOutputSpacing(spacing);
1094   int slabMode = this->SlabType;
1095   double scalarScale = 1.0;
1096   if (slabMode == VTK_IMAGE_SLAB_SUM)
1097   {
1098     // "sum" means integrating over the path length of each ray through
1099     // the volume, so we need to include the sample spacing as a factor
1100     scalarScale = spacing[2];
1101   }
1102 
1103   this->ImageReslice->SetInterpolationMode(interpMode);
1104   this->ImageReslice->SetSlabMode(slabMode);
1105   this->ImageReslice->SetSlabNumberOfSlices(slabSlices);
1106   this->ImageReslice->SetScalarScale(scalarScale);
1107   this->ImageReslice->SlabTrapezoidIntegrationOn();
1108 }
1109 
1110 //----------------------------------------------------------------------------
CheckerboardImage(vtkImageData * input,vtkCamera * camera,vtkImageProperty * property)1111 void vtkImageResliceMapper::CheckerboardImage(
1112   vtkImageData *input, vtkCamera *camera, vtkImageProperty *property)
1113 {
1114   // Use focal point as center of checkerboard pattern.  This guarantees
1115   // exactly the same checkerboard for all images in the scene, which is
1116   // useful when doing multiple overlays.
1117   double focalPoint[4];
1118   camera->GetFocalPoint(focalPoint);
1119   focalPoint[3] = 1.0;
1120 
1121   double worldToSlice[16];
1122   vtkMatrix4x4::Invert(*this->SliceToWorldMatrix->Element, worldToSlice);
1123 
1124   vtkMatrix4x4::MultiplyPoint(worldToSlice, focalPoint, focalPoint);
1125   if (focalPoint[3] != 0.0)
1126   {
1127     focalPoint[0] /= focalPoint[3];
1128     focalPoint[1] /= focalPoint[3];
1129     focalPoint[2] /= focalPoint[3];
1130   }
1131 
1132   // Get the checkerboard spacing and apply the offset fraction
1133   double checkSpacing[2], checkOffset[2];
1134   property->GetCheckerboardSpacing(checkSpacing);
1135   property->GetCheckerboardOffset(checkOffset);
1136   checkOffset[0] = checkOffset[0]*checkSpacing[0] + focalPoint[0];
1137   checkOffset[1] = checkOffset[1]*checkSpacing[1] + focalPoint[1];
1138 
1139   // Adjust according to the origin and spacing of the slice data
1140   double origin[3], spacing[3];
1141   input->GetSpacing(spacing);
1142   input->GetOrigin(origin);
1143   checkOffset[0] = (checkOffset[0] - origin[0])/spacing[0];
1144   checkOffset[1] = (checkOffset[1] - origin[1])/spacing[1];
1145   checkSpacing[0] /= spacing[0];
1146   checkSpacing[1] /= spacing[1];
1147 
1148   // Apply the checkerboard to the data
1149   int extent[6];
1150   input->GetExtent(extent);
1151   unsigned char *data = static_cast<unsigned char *>(
1152     input->GetScalarPointerForExtent(extent));
1153 
1154   vtkImageMapper3D::CheckerboardRGBA(
1155     data, extent[1] - extent[0] + 1, extent[3] - extent[2] + 1,
1156     checkOffset[0], checkOffset[1], checkSpacing[0], checkSpacing[1]);
1157 }
1158 
1159 //----------------------------------------------------------------------------
1160 // Compute the vertices of the polygon in the slice coordinate system
1161 #define VTK_IRM_MAX_VERTS 32
1162 #define VTK_IRM_MAX_COORDS 96
UpdatePolygonCoords(vtkRenderer * ren)1163 void vtkImageResliceMapper::UpdatePolygonCoords(vtkRenderer *ren)
1164 {
1165   // Get the projection matrix
1166   double aspect = ren->GetTiledAspectRatio();
1167   vtkCamera *camera = ren->GetActiveCamera();
1168   vtkMatrix4x4 *viewMatrix = camera->GetViewTransformMatrix();
1169   vtkMatrix4x4 *projMatrix = camera->GetProjectionTransformMatrix(
1170                                aspect, 0, 1);
1171 
1172   // Compute other useful matrices
1173   double worldToView[16];
1174   double viewToWorld[16];
1175   vtkMatrix4x4::Multiply4x4(
1176     *projMatrix->Element, *viewMatrix->Element, worldToView);
1177   vtkMatrix4x4::Invert(worldToView, viewToWorld);
1178 
1179   double worldToSlice[16];
1180   double viewToSlice[16];
1181   vtkMatrix4x4::Invert(*this->SliceToWorldMatrix->Element, worldToSlice);
1182   vtkMatrix4x4::Multiply4x4(worldToSlice, viewToWorld, viewToSlice);
1183 
1184   // Get slice plane in world coords by passing null as the matrix
1185   double plane[4];
1186   this->GetSlicePlaneInDataCoords(nullptr, plane);
1187 
1188   // Check whether normal is facing towards camera, the "ndop" is
1189   // the negative of the direction of projection for the camera
1190   double *ndop = viewMatrix->Element[2];
1191   if (vtkMath::Dot(ndop, plane) < 0)
1192   {
1193     plane[0] = -plane[0];
1194     plane[1] = -plane[1];
1195     plane[2] = -plane[2];
1196     plane[3] = -plane[3];
1197   }
1198 
1199   // Get the z position of the slice in slice coords
1200   // (requires plane to be normalized by GetSlicePlaneInDataCoords)
1201   double z = (plane[2] - 2.0)*plane[3];
1202 
1203   // Generate a tolerance based on the screen pixel size
1204   double fpoint[4];
1205   camera->GetFocalPoint(fpoint);
1206   fpoint[3] = 1.0;
1207   vtkMatrix4x4::MultiplyPoint(worldToView, fpoint, fpoint);
1208   fpoint[0] /= fpoint[3];
1209   fpoint[1] /= fpoint[3];
1210   fpoint[2] /= fpoint[3];
1211   fpoint[3] = 1.0;
1212 
1213   double topOfScreen[4], botOfScreen[4];
1214   fpoint[1] -= 1.0;
1215   vtkMatrix4x4::MultiplyPoint(viewToWorld, fpoint, topOfScreen);
1216   fpoint[1] += 2.0;
1217   vtkMatrix4x4::MultiplyPoint(viewToWorld, fpoint, botOfScreen);
1218 
1219   topOfScreen[0] /= topOfScreen[3];
1220   topOfScreen[1] /= topOfScreen[3];
1221   topOfScreen[2] /= topOfScreen[3];
1222   topOfScreen[3] = 1.0;
1223 
1224   botOfScreen[0] /= botOfScreen[3];
1225   botOfScreen[1] /= botOfScreen[3];
1226   botOfScreen[2] /= botOfScreen[3];
1227   botOfScreen[3] = 1.0;
1228 
1229   // height of view in world coords at focal point
1230   double viewHeight =
1231     sqrt(vtkMath::Distance2BetweenPoints(topOfScreen, botOfScreen));
1232 
1233   // height of view in pixels
1234   int height = ren->GetSize()[1];
1235 
1236   double tol = (height == 0 ? 0.5 : viewHeight*0.5/height);
1237 
1238   // make the data bounding box (with or without border)
1239   double b = (this->Border ? 0.5 : VTK_RESLICE_MAPPER_VOXEL_TOL);
1240   double bounds[6];
1241   for (int ii = 0; ii < 3; ii++)
1242   {
1243     double c = b*this->DataSpacing[ii];
1244     int lo = this->DataWholeExtent[2*ii];
1245     int hi = this->DataWholeExtent[2*ii+1];
1246     if (lo == hi && tol > c)
1247     { // apply tolerance to avoid degeneracy
1248       c = tol;
1249     }
1250     bounds[2*ii]   = lo*this->DataSpacing[ii] + this->DataOrigin[ii] - c;
1251     bounds[2*ii+1] = hi*this->DataSpacing[ii] + this->DataOrigin[ii] + c;
1252   }
1253 
1254   // transform the vertices to the slice coord system
1255   double xpoints[8], ypoints[8];
1256   double weights1[8], weights2[8];
1257   bool above[8], below[8];
1258   double mat[16];
1259   vtkMatrix4x4::Multiply4x4(*this->WorldToDataMatrix->Element,
1260                             *this->SliceToWorldMatrix->Element, mat);
1261   vtkMatrix4x4::Invert(mat, mat);
1262 
1263   // arrays for the list of polygon points
1264   int n = 0;
1265   double newxpoints[VTK_IRM_MAX_VERTS];
1266   double newypoints[VTK_IRM_MAX_VERTS];
1267   double cx = 0.0;
1268   double cy = 0.0;
1269 
1270   for (int i = 0; i < 8; i++)
1271   {
1272     double point[4];
1273     point[0] = bounds[0 + ((i>>0)&1)];
1274     point[1] = bounds[2 + ((i>>1)&1)];
1275     point[2] = bounds[4 + ((i>>2)&1)];
1276     point[3] = 1.0;
1277     vtkMatrix4x4::MultiplyPoint(mat, point, point);
1278     xpoints[i] = point[0]/point[3];
1279     ypoints[i] = point[1]/point[3];
1280     weights1[i] = point[2]/point[3] - z - 0.5*this->SlabThickness;
1281     weights2[i] = weights1[i] + this->SlabThickness;
1282     below[i] = (weights1[i] < 0);
1283     above[i] = (weights2[i] >= 0);
1284 
1285     if (this->SlabThickness > 0 && above[i] && below[i])
1286     {
1287       newxpoints[n] = xpoints[i];
1288       newypoints[n] = ypoints[i];
1289       cx += xpoints[i];
1290       cy += ypoints[i];
1291       n++;
1292     }
1293   }
1294 
1295   // go through the edges and find the new points
1296   for (int j = 0; j < 12; j++)
1297   {
1298     // verts from edges (sorry about this..)
1299     int i1 = (j & 3) | (((j<<1) ^ (j<<2)) & 4);
1300     int i2 = (i1 ^ (1 << (j>>2)));
1301 
1302     double *weights = weights2;
1303     bool *side = above;
1304     int m = 1 + (this->SlabThickness > 0);
1305     for (int k = 0; k < m; k++)
1306     {
1307       if (side[i1] ^ side[i2])
1308       {
1309         double w1 = weights[i2];
1310         double w2 = -weights[i1];
1311         double x = (w1*xpoints[i1] + w2*xpoints[i2])/(w1 + w2);
1312         double y = (w1*ypoints[i1] + w2*ypoints[i2])/(w1 + w2);
1313         newxpoints[n] = x;
1314         newypoints[n] = y;
1315         cx += x;
1316         cy += y;
1317         n++;
1318       }
1319       weights = weights1;
1320       side = below;
1321     }
1322   }
1323 
1324   double coords[VTK_IRM_MAX_COORDS];
1325 
1326   if (n > 0)
1327   {
1328     // centroid
1329     cx /= n;
1330     cy /= n;
1331 
1332     // sort the points to make a convex polygon
1333     double angles[VTK_IRM_MAX_VERTS];
1334     for (int k = 0; k < n; k++)
1335     {
1336       double x = newxpoints[k];
1337       double y = newypoints[k];
1338       double t = atan2(y - cy, x - cx);
1339       int kk;
1340       for (kk = 0; kk < k; kk++)
1341       {
1342         if (t < angles[kk]) { break; }
1343       }
1344       for (int jj = k; jj > kk; --jj)
1345       {
1346         int jj3 = jj*3;
1347         angles[jj] = angles[jj-1];
1348         coords[jj3] = coords[jj3-3];
1349         coords[jj3+1] = coords[jj3-2];
1350         coords[jj3+2] = coords[jj3-1];
1351       }
1352       int kk3 = kk*3;
1353       angles[kk] = t;
1354       coords[kk3] = x;
1355       coords[kk3+1] = y;
1356       coords[kk3+2] = z;
1357     }
1358   }
1359 
1360   // remove degenerate points
1361   if (n > 0)
1362   {
1363     bool found = true;
1364     int m = 0;
1365     do
1366     {
1367       m = 0;
1368       double xl = coords[3*(n-1)+0];
1369       double yl = coords[3*(n-1)+1];
1370       for (int k = 0; k < n; k++)
1371       {
1372         double x = coords[3*k+0];
1373         double y = coords[3*k+1];
1374 
1375         if (((x - xl)*(x - xl) + (y - yl)*(y - yl)) > tol*tol)
1376         {
1377           coords[3*m+0] = x;
1378           coords[3*m+1] = y;
1379           xl = x;
1380           yl = y;
1381           m++;
1382         }
1383       }
1384       found = (m < n);
1385       n = m;
1386     }
1387     while (found && n > 0);
1388   }
1389 
1390   // find convex hull
1391   if (this->SlabThickness > 0 && n > 0)
1392   {
1393     bool found = true;
1394     int m = 0;
1395     do
1396     {
1397       m = 0;
1398       double xl = coords[3*(n-1)+0];
1399       double yl = coords[3*(n-1)+1];
1400       for (int k = 0; k < n; k++)
1401       {
1402         double x = coords[3*k+0];
1403         double y = coords[3*k+1];
1404         int k1 = (k + 1) % n;
1405         double xn = coords[3*k1+0];
1406         double yn = coords[3*k1+1];
1407 
1408         if ((xn-xl)*(y-yl) - (yn-yl)*(x-xl) < tol*tol)
1409         {
1410           coords[3*m+0] = x;
1411           coords[3*m+1] = y;
1412           xl = x;
1413           yl = y;
1414           m++;
1415         }
1416       }
1417       found = (m < n);
1418       n = m;
1419     }
1420     while (found && n > 0);
1421   }
1422 
1423   vtkPoints *points = this->SliceMapper->GetPoints();
1424   if (!points)
1425   {
1426     points = vtkPoints::New();
1427     points->SetDataTypeToDouble();
1428     this->SliceMapper->SetPoints(points);
1429     points->Delete();
1430   }
1431 
1432   points->SetNumberOfPoints(n);
1433   for (int k = 0; k < n; k++)
1434   {
1435     points->SetPoint(k, &coords[3*k]);
1436   }
1437   points->Modified();
1438 }
1439 
1440 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)1441 void vtkImageResliceMapper::PrintSelf(ostream& os, vtkIndent indent)
1442 {
1443   this->Superclass::PrintSelf(os,indent);
1444 
1445   os << indent << "JumpToNearestSlice: "
1446      << (this->JumpToNearestSlice ? "On\n" : "Off\n");
1447   os << indent << "AutoAdjustImageQuality: "
1448      << (this->AutoAdjustImageQuality ? "On\n" : "Off\n");
1449   os << indent << "SeparateWindowLevelOperation: "
1450      << (this->SeparateWindowLevelOperation ? "On\n" : "Off\n");
1451   os << indent << "ResampleToScreenPixels: "
1452      << (this->ResampleToScreenPixels ? "On\n" : "Off\n");
1453   os << indent << "SlabThickness: " << this->SlabThickness << "\n";
1454   os << indent << "SlabType: " << this->GetSlabTypeAsString() << "\n";
1455   os << indent << "SlabSampleFactor: " << this->SlabSampleFactor << "\n";
1456   os << indent << "ImageSampleFactor: " << this->ImageSampleFactor << "\n";
1457   os << indent << "Interpolator: " << this->GetInterpolator() << "\n";
1458 }
1459 
1460 //----------------------------------------------------------------------------
GetSlabTypeAsString()1461 const char *vtkImageResliceMapper::GetSlabTypeAsString()
1462 {
1463   switch (this->SlabType)
1464   {
1465     case VTK_IMAGE_SLAB_MIN:
1466       return "Min";
1467     case VTK_IMAGE_SLAB_MAX:
1468       return "Max";
1469     case VTK_IMAGE_SLAB_MEAN:
1470       return "Mean";
1471     case VTK_IMAGE_SLAB_SUM:
1472       return "Sum";
1473   }
1474   return "";
1475 }
1476 
1477 //----------------------------------------------------------------------------
GetMTime()1478 vtkMTimeType vtkImageResliceMapper::GetMTime()
1479 {
1480   vtkMTimeType mTime = this->Superclass::GetMTime();
1481 
1482   // Check whether interpolator has changed
1483   vtkAbstractImageInterpolator *interpolator =
1484     this->ImageReslice->GetInterpolator();
1485   if (interpolator)
1486   {
1487     vtkMTimeType mTime2 = interpolator->GetMTime();
1488     if (mTime2 > mTime)
1489     {
1490       mTime = mTime2;
1491     }
1492   }
1493 
1494   // Include camera in MTime so that REQUEST_INFORMATION
1495   // will be called if the camera changes
1496   if (this->SliceFacesCamera || this->SliceAtFocalPoint ||
1497       this->InternalResampleToScreenPixels)
1498   {
1499     vtkRenderer *ren = this->GetCurrentRenderer();
1500     if (ren)
1501     {
1502       vtkCamera *camera = ren->GetActiveCamera();
1503       vtkMTimeType mTime2 = camera->GetMTime();
1504       mTime = (mTime2 > mTime ? mTime2 : mTime);
1505     }
1506   }
1507 
1508   if (!this->SliceFacesCamera || !this->SliceAtFocalPoint)
1509   {
1510     vtkMTimeType sTime = this->SlicePlane->GetMTime();
1511     mTime = (sTime > mTime ? sTime : mTime);
1512   }
1513 
1514   vtkImageSlice *prop = this->GetCurrentProp();
1515   if (prop != nullptr)
1516   {
1517     vtkMTimeType mTime2 = prop->GetUserTransformMatrixMTime();
1518     mTime = (mTime2 > mTime ? mTime2 : mTime);
1519 
1520     vtkImageProperty *property = prop->GetProperty();
1521     if (property != nullptr)
1522     {
1523       bool useMTime = true;
1524       if (this->SeparateWindowLevelOperation)
1525       {
1526         // only care about property if interpolation mode has changed,
1527         // since interpolation is the only property-related operation
1528         // done by vtkImageReslice if SeparateWindowLevelOperation is on
1529         int imode = this->ImageReslice->GetInterpolationMode();
1530         this->UpdateResliceInterpolation(property);
1531         useMTime = (imode != this->ImageReslice->GetInterpolationMode());
1532       }
1533       if (useMTime)
1534       {
1535         mTime2 = property->GetMTime();
1536         mTime = (mTime2 > mTime ? mTime2 : mTime);
1537 
1538         vtkScalarsToColors *lookupTable = property->GetLookupTable();
1539         if (lookupTable != nullptr)
1540         {
1541           // check the lookup table mtime
1542           mTime2 = lookupTable->GetMTime();
1543           mTime = (mTime2 > mTime ? mTime2 : mTime);
1544         }
1545       }
1546     }
1547   }
1548 
1549   return mTime;
1550 }
1551 
1552 //----------------------------------------------------------------------------
GetBounds()1553 double *vtkImageResliceMapper::GetBounds()
1554 {
1555   // Modify to give just the slice bounds
1556   if (!this->GetInput())
1557   {
1558     vtkMath::UninitializeBounds(this->Bounds);
1559     return this->Bounds;
1560   }
1561   else
1562   {
1563     this->UpdateInformation();
1564     double *spacing = this->DataSpacing;
1565     double *origin = this->DataOrigin;
1566     int *extent = this->DataWholeExtent;
1567 
1568     // expand by half a pixel if border is on
1569     double border = 0.5*(this->Border != 0);
1570 
1571     // swap the extent if the spacing is negative
1572     int swapX = (spacing[0] < 0);
1573     int swapY = (spacing[1] < 0);
1574     int swapZ = (spacing[2] < 0);
1575 
1576     this->Bounds[0+swapX] = origin[0] + (extent[0] - border) * spacing[0];
1577     this->Bounds[2+swapY] = origin[1] + (extent[2] - border) * spacing[1];
1578     this->Bounds[4+swapZ] = origin[2] + (extent[4] - border) * spacing[2];
1579 
1580     this->Bounds[1-swapX] = origin[0] + (extent[1] + border) * spacing[0];
1581     this->Bounds[3-swapY] = origin[1] + (extent[3] + border) * spacing[1];
1582     this->Bounds[5-swapZ] = origin[2] + (extent[5] + border) * spacing[2];
1583 
1584     return this->Bounds;
1585   }
1586 }
1587 
1588 //----------------------------------------------------------------------------
ReportReferences(vtkGarbageCollector * collector)1589 void vtkImageResliceMapper::ReportReferences(vtkGarbageCollector* collector)
1590 {
1591   this->Superclass::ReportReferences(collector);
1592   // These filters share our input and are therefore involved in a
1593   // reference loop.
1594   vtkGarbageCollectorReport(collector, this->ImageReslice, "ImageReslice");
1595   vtkGarbageCollectorReport(collector, this->SliceMapper, "SliceMapper");
1596 }
1597