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