1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkImplicitImageRepresentation.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkImplicitImageRepresentation.h"
16 
17 #include "vtkActor.h"
18 #include "vtkAlgorithmOutput.h"
19 #include "vtkCutter.h"
20 #include "vtkFeatureEdges.h"
21 #include "vtkImageData.h"
22 #include "vtkImageMapToColors.h"
23 #include "vtkImageReslice.h"
24 #include "vtkInformation.h"
25 #include "vtkLookupTable.h"
26 #include "vtkMatrix4x4.h"
27 #include "vtkObjectFactory.h"
28 #include "vtkPlaneSource.h"
29 #include "vtkPolyDataMapper.h"
30 #include "vtkProperty.h"
31 #include "vtkRenderWindow.h"
32 #include "vtkRenderWindowInteractor.h"
33 #include "vtkRenderer.h"
34 #include "vtkStreamingDemandDrivenPipeline.h"
35 #include "vtkTexture.h"
36 #include "vtkTextureMapToPlane.h"
37 
38 #include <iostream>
39 
40 vtkStandardNewMacro(vtkImplicitImageRepresentation);
41 vtkCxxSetObjectMacro(vtkImplicitImageRepresentation, ColorMap, vtkImageMapToColors);
42 
43 //------------------------------------------------------------------------------
vtkImplicitImageRepresentation()44 vtkImplicitImageRepresentation::vtkImplicitImageRepresentation()
45 {
46   this->UserControlledLookupTable = false;
47   this->TextureInterpolate = true;
48   this->ResliceInterpolate = VTK_LINEAR_RESLICE;
49   this->OriginalWindow = 1.0;
50   this->OriginalLevel = 0.5;
51 
52   this->ImageData = nullptr;
53   this->Reslice = vtkImageReslice::New();
54   this->Reslice->TransformInputSamplingOff();
55   this->ResliceAxes = vtkMatrix4x4::New();
56   this->ColorMap = vtkImageMapToColors::New();
57   this->Texture = vtkTexture::New();
58   this->LookupTable = nullptr;
59 
60   // Setup the image / texture pipeline
61   this->GenerateTexturePlane();
62 }
63 
64 //------------------------------------------------------------------------------
~vtkImplicitImageRepresentation()65 vtkImplicitImageRepresentation::~vtkImplicitImageRepresentation()
66 {
67   if (this->LookupTable)
68   {
69     this->LookupTable->UnRegister(this);
70   }
71   if (this->ImageData)
72   {
73     this->ImageData = nullptr;
74   }
75   this->Reslice->Delete();
76   this->ResliceAxes->Delete();
77   this->ColorMap->Delete();
78   this->Texture->Delete();
79   this->TextureMapToPlane->Delete();
80 }
81 
82 //------------------------------------------------------------------------------
PlaceImage(vtkAlgorithmOutput * aout)83 void vtkImplicitImageRepresentation::PlaceImage(vtkAlgorithmOutput* aout)
84 {
85   vtkImageData* img =
86     vtkImageData::SafeDownCast(aout->GetProducer()->GetOutputDataObject(aout->GetIndex()));
87 
88   this->PlaceImage(img);
89   this->Reslice->SetInputConnection(aout);
90 }
91 
92 //------------------------------------------------------------------------------
PlaceImage(vtkImageData * img)93 void vtkImplicitImageRepresentation::PlaceImage(vtkImageData* img)
94 {
95   this->ImageData = img;
96   if (!this->ImageData)
97   {
98     // If nullptr is passed, remove any reference that Reslice had
99     // on the old ImageData
100     //
101     this->Reslice->SetInputData(nullptr);
102     return;
103   }
104 
105   // Place the widget
106   double bounds[6];
107   img->GetBounds(bounds);
108   this->Superclass::PlaceWidget(bounds);
109 
110   // Now update the pipeline
111   double range[2];
112   this->ImageData->GetScalarRange(range);
113 
114   if (!this->UserControlledLookupTable)
115   {
116     this->LookupTable->SetTableRange(range[0], range[1]);
117     this->LookupTable->Build();
118   }
119 
120   this->OriginalWindow = range[1] - range[0];
121   this->OriginalLevel = 0.5 * (range[0] + range[1]);
122 
123   if (fabs(this->OriginalWindow) < 0.001)
124   {
125     this->OriginalWindow = 0.001 * (this->OriginalWindow < 0.0 ? -1 : 1);
126   }
127   if (fabs(this->OriginalLevel) < 0.001)
128   {
129     this->OriginalLevel = 0.001 * (this->OriginalLevel < 0.0 ? -1 : 1);
130   }
131 
132   this->Reslice->SetInputData(img);
133   int interpolate = this->ResliceInterpolate;
134   this->ResliceInterpolate = -1; // Force change
135   this->SetResliceInterpolate(interpolate);
136 
137   this->ColorMap->SetInputConnection(this->Reslice->GetOutputPort());
138 
139   this->Texture->SetInputConnection(this->ColorMap->GetOutputPort());
140   this->Texture->SetInterpolate(this->TextureInterpolate);
141 }
142 
143 //------------------------------------------------------------------------------
SetLookupTable(vtkLookupTable * table)144 void vtkImplicitImageRepresentation::SetLookupTable(vtkLookupTable* table)
145 {
146   if (this->LookupTable != table)
147   {
148     // to avoid destructor recursion
149     vtkLookupTable* temp = this->LookupTable;
150     this->LookupTable = table;
151     if (temp != nullptr)
152     {
153       temp->UnRegister(this);
154     }
155     if (this->LookupTable != nullptr)
156     {
157       this->LookupTable->Register(this);
158     }
159     else // create a default lut
160     {
161       this->LookupTable = this->CreateDefaultLookupTable();
162     }
163   }
164 
165   this->ColorMap->SetLookupTable(this->LookupTable);
166   this->Texture->SetLookupTable(this->LookupTable);
167 
168   if (this->ImageData && !this->UserControlledLookupTable)
169   {
170     double range[2];
171     this->ImageData->GetScalarRange(range);
172 
173     this->LookupTable->SetTableRange(range[0], range[1]);
174     this->LookupTable->Build();
175 
176     this->OriginalWindow = range[1] - range[0];
177     this->OriginalLevel = 0.5 * (range[0] + range[1]);
178   }
179 }
180 
181 //------------------------------------------------------------------------------
SetResliceInterpolate(int i)182 void vtkImplicitImageRepresentation::SetResliceInterpolate(int i)
183 {
184   if (this->ResliceInterpolate == i)
185   {
186     return;
187   }
188   this->ResliceInterpolate = i;
189   this->Modified();
190 
191   if (!this->Reslice)
192   {
193     return;
194   }
195 
196   if (i == VTK_NEAREST_RESLICE)
197   {
198     this->Reslice->SetInterpolationModeToNearestNeighbor();
199   }
200   else if (i == VTK_LINEAR_RESLICE)
201   {
202     this->Reslice->SetInterpolationModeToLinear();
203   }
204   else
205   {
206     this->Reslice->SetInterpolationModeToCubic();
207   }
208   this->Texture->SetInterpolate(this->TextureInterpolate);
209 }
210 
211 //------------------------------------------------------------------------------
CreateDefaultProperties()212 void vtkImplicitImageRepresentation::CreateDefaultProperties()
213 {
214   // Use what's defined in the superclass
215   this->Superclass::CreateDefaultProperties();
216 
217   // Plane properties need to modified for best appearance due to texture
218   this->PlaneProperty->SetAmbient(1.0);
219   this->PlaneProperty->SetAmbientColor(1.0, 1.0, 1.0);
220   this->PlaneProperty->SetOpacity(1.0);
221   this->CutActor->SetProperty(this->PlaneProperty);
222 
223   this->SelectedPlaneProperty->SetAmbient(1.0);
224   this->SelectedPlaneProperty->SetAmbientColor(0.0, 1.0, 0.0);
225   this->SelectedPlaneProperty->SetOpacity(1.0);
226 }
227 
228 //------------------------------------------------------------------------------
CreateDefaultLookupTable()229 vtkLookupTable* vtkImplicitImageRepresentation::CreateDefaultLookupTable()
230 {
231   vtkLookupTable* lut = vtkLookupTable::New();
232   lut->Register(this);
233   lut->Delete();
234   lut->SetNumberOfColors(256);
235   lut->SetHueRange(0, 0);
236   lut->SetSaturationRange(0, 0);
237   lut->SetValueRange(0, 1);
238   lut->SetAlphaRange(1, 1);
239   lut->Build();
240   return lut;
241 }
242 
243 //------------------------------------------------------------------------------
GenerateTexturePlane()244 void vtkImplicitImageRepresentation::GenerateTexturePlane()
245 {
246   this->TextureMapToPlane = vtkTextureMapToPlane::New();
247   this->TextureMapToPlane->AutomaticPlaneGenerationOff();
248   this->TextureMapToPlane->SetOrigin(this->PlaneSource->GetOrigin());
249   this->TextureMapToPlane->SetPoint1(this->PlaneSource->GetPoint1());
250   this->TextureMapToPlane->SetPoint2(this->PlaneSource->GetPoint2());
251 
252   // Modify superclasses' pipeline to add in texture mapping
253   this->TextureMapToPlane->SetInputConnection(this->Cutter->GetOutputPort());
254   this->CutMapper->SetInputConnection(this->TextureMapToPlane->GetOutputPort());
255   this->Edges->SetInputConnection(this->Cutter->GetOutputPort());
256 
257   this->SetResliceInterpolate(this->ResliceInterpolate);
258 
259   this->LookupTable = this->CreateDefaultLookupTable();
260 
261   this->ColorMap->SetLookupTable(this->LookupTable);
262   this->ColorMap->SetOutputFormatToRGBA();
263   this->ColorMap->PassAlphaToOutputOn();
264 
265   this->Texture->SetQualityTo32Bit();
266   this->Texture->SetColorMode(VTK_COLOR_MODE_DEFAULT);
267   this->Texture->SetInterpolate(this->TextureInterpolate);
268   this->Texture->RepeatOff();
269   this->Texture->SetLookupTable(this->LookupTable);
270 
271   // Note using the superclasses' actor for texturing, this may mean
272   // modifying the pipeline.
273   this->CutActor->SetTexture(this->Texture);
274 }
275 
276 //------------------------------------------------------------------------------
UpdatePlane()277 void vtkImplicitImageRepresentation::UpdatePlane()
278 {
279   if (!this->Reslice || !this->ImageData)
280   {
281     return;
282   }
283 
284   // Calculate appropriate pixel spacing for the reslicing
285   //
286   vtkAlgorithm* inpAlg = this->Reslice->GetInputAlgorithm();
287   inpAlg->UpdateInformation();
288   vtkInformation* outInfo = inpAlg->GetOutputInformation(0);
289   double spacing[3];
290   outInfo->Get(vtkDataObject::SPACING(), spacing);
291   double origin[3];
292   outInfo->Get(vtkDataObject::ORIGIN(), origin);
293   int extent[6];
294   outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent);
295 
296   int i;
297 
298   for (i = 0; i < 3; i++)
299   {
300     if (extent[2 * i] > extent[2 * i + 1])
301     {
302       vtkErrorMacro("Invalid extent [" << extent[0] << ", " << extent[1] << ", " << extent[2]
303                                        << ", " << extent[3] << ", " << extent[4] << ", "
304                                        << extent[5] << "]."
305                                        << " Perhaps the input data is empty?");
306       break;
307     }
308   }
309 
310   // Update texture coordinate generation
311   this->TextureMapToPlane->SetOrigin(this->PlaneSource->GetOrigin());
312   this->TextureMapToPlane->SetPoint1(this->PlaneSource->GetPoint1());
313   this->TextureMapToPlane->SetPoint2(this->PlaneSource->GetPoint2());
314 
315   // Get the plane axes and related informaion
316   double planeAxis1[3], planeAxis2[3];
317   this->PlaneSource->GetAxis1(planeAxis1);
318   this->PlaneSource->GetAxis2(planeAxis2);
319 
320   // The x,y dimensions of the plane
321   //
322   double planeSizeX = vtkMath::Normalize(planeAxis1);
323   double planeSizeY = vtkMath::Normalize(planeAxis2);
324 
325   double normal[3];
326   this->PlaneSource->GetNormal(normal);
327 
328   // Generate the slicing matrix
329   //
330   this->ResliceAxes->Identity();
331   for (i = 0; i < 3; i++)
332   {
333     this->ResliceAxes->SetElement(0, i, planeAxis1[i]);
334     this->ResliceAxes->SetElement(1, i, planeAxis2[i]);
335     this->ResliceAxes->SetElement(2, i, normal[i]);
336   }
337 
338   double planeOrigin[4];
339   this->PlaneSource->GetOrigin(planeOrigin);
340 
341   planeOrigin[3] = 1.0;
342 
343   this->ResliceAxes->Transpose();
344   this->ResliceAxes->SetElement(0, 3, planeOrigin[0]);
345   this->ResliceAxes->SetElement(1, 3, planeOrigin[1]);
346   this->ResliceAxes->SetElement(2, 3, planeOrigin[2]);
347 
348   this->Reslice->SetResliceAxes(this->ResliceAxes);
349 
350   double spacingX = fabs(planeAxis1[0] * spacing[0]) + fabs(planeAxis1[1] * spacing[1]) +
351     fabs(planeAxis1[2] * spacing[2]);
352 
353   double spacingY = fabs(planeAxis2[0] * spacing[0]) + fabs(planeAxis2[1] * spacing[1]) +
354     fabs(planeAxis2[2] * spacing[2]);
355 
356   // Pad extent up to a power of two for efficient texture mapping
357 
358   // make sure we're working with valid values
359   double realExtentX = (spacingX == 0) ? VTK_INT_MAX : planeSizeX / spacingX;
360 
361   int extentX;
362   // Sanity check the input data:
363   // * if realExtentX is too large, extentX will wrap
364   // * if spacingX is 0, things will blow up.
365   if (realExtentX > (VTK_INT_MAX >> 1))
366   {
367     vtkErrorMacro(<< "Invalid X extent: " << realExtentX);
368     extentX = 0;
369   }
370   else
371   {
372     extentX = 1;
373     while (extentX < realExtentX)
374     {
375       extentX = extentX << 1;
376     }
377   }
378 
379   // make sure extentY doesn't wrap during padding
380   double realExtentY = (spacingY == 0) ? VTK_INT_MAX : planeSizeY / spacingY;
381 
382   int extentY;
383   if (realExtentY > (VTK_INT_MAX >> 1))
384   {
385     vtkErrorMacro(<< "Invalid Y extent: " << realExtentY);
386     extentY = 0;
387   }
388   else
389   {
390     extentY = 1;
391     while (extentY < realExtentY)
392     {
393       extentY = extentY << 1;
394     }
395   }
396 
397   double outputSpacingX = (extentX == 0) ? 1.0 : planeSizeX / extentX;
398   double outputSpacingY = (extentY == 0) ? 1.0 : planeSizeY / extentY;
399   this->Reslice->SetOutputSpacing(outputSpacingX, outputSpacingY, 1);
400   this->Reslice->SetOutputOrigin(0.5 * outputSpacingX, 0.5 * outputSpacingY, 0);
401   this->Reslice->SetOutputExtent(0, extentX - 1, 0, extentY - 1, 0, 0);
402 }
403 
404 //------------------------------------------------------------------------------
BuildRepresentation()405 void vtkImplicitImageRepresentation::BuildRepresentation()
406 {
407   // Make sure we're in a valid state
408   if (!this->Renderer || !this->Renderer->GetRenderWindow())
409   {
410     return;
411   }
412 
413   // Build the geometry
414   this->Superclass::BuildRepresentation();
415 
416   // Now setup the pipeline
417   this->UpdatePlane();
418 }
419 
420 //------------------------------------------------------------------------------
SetCropPlaneToBoundingBox(bool val)421 void vtkImplicitImageRepresentation::SetCropPlaneToBoundingBox(bool val)
422 {
423   if (this->CropPlaneToBoundingBox == val)
424   {
425     return;
426   }
427 
428   this->CropPlaneToBoundingBox = val;
429   if (val)
430   {
431     this->TextureMapToPlane->SetInputConnection(this->Cutter->GetOutputPort());
432     this->CutMapper->SetInputConnection(this->TextureMapToPlane->GetOutputPort());
433     this->Edges->SetInputConnection(this->Cutter->GetOutputPort());
434   }
435   else
436   {
437     this->CutMapper->SetInputConnection(this->PlaneSource->GetOutputPort());
438     this->Edges->SetInputConnection(this->PlaneSource->GetOutputPort());
439   }
440   this->Modified();
441 }
442 
443 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)444 void vtkImplicitImageRepresentation::PrintSelf(ostream& os, vtkIndent indent)
445 {
446   this->Superclass::PrintSelf(os, indent);
447 
448   os << indent << "Reslice Interpolate: " << this->ResliceInterpolate << "\n";
449   os << indent
450      << "User Controlled Lookup Table: " << (this->UserControlledLookupTable ? "On\n" : "Off\n");
451 
452   os << indent
453      << "User Controlled Lookup Table: " << (this->UserControlledLookupTable ? "On\n" : "Off\n");
454   if (this->LookupTable)
455   {
456     os << indent << "LookupTable:\n";
457     this->LookupTable->PrintSelf(os, indent.GetNextIndent());
458   }
459   else
460   {
461     os << indent << "LookupTable: (none)\n";
462   }
463 
464   if (this->ColorMap)
465   {
466     os << indent << "ColorMap:\n";
467     this->ColorMap->PrintSelf(os, indent.GetNextIndent());
468   }
469   else
470   {
471     os << indent << "ColorMap: (none)\n";
472   }
473 }
474