CustusX  15.8
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxTexture3DSlicerProxy.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 
34 /*
35  * sscTexture3DSlicerProxyImpl.cpp
36  *
37  * Created on: Oct 13, 2011
38  * Author: christiana
39  */
40 
41 #include "cxTexture3DSlicerProxy.h"
42 #include "cxTextureSlicePainter.h"
43 
44 #include <vtkRenderer.h>
45 #include <vtkFloatArray.h>
46 #include <vtkPlaneSource.h>
47 #include <vtkPointData.h>
48 #include <vtkTriangleFilter.h>
49 #include <vtkStripper.h>
50 #include <vtkImageData.h>
51 #include <vtkPainterPolyDataMapper.h>
52 #include <vtkLookupTable.h>
53 #include <vtkOpenGLRenderWindow.h>
54 
55 #include "cxImage.h"
56 #include "cxView.h"
57 #include "cxImageLUT2D.h"
58 #include "cxSliceProxy.h"
59 #include "cxTypeConversions.h"
60 #include "cxGPUImageBuffer.h"
61 #include "cxReporter.h"
62 
63 
64 //---------------------------------------------------------
65 namespace cx
66 {
67 //---------------------------------------------------------
68 
69 #ifdef WIN32
70 
72 {
73  return Texture3DSlicerProxyPtr(new Texture3DSlicerProxy());
74 }
75 
77 {
78  return false;
79 }
80 
81 #else
82 
84 {
85  vtkOpenGLRenderWindow *context = vtkOpenGLRenderWindow::SafeDownCast(window);
86  bool success = context && TextureSlicePainter::LoadRequiredExtensions(context->GetExtensionManager());
87  return success;
88 }
89 
91 {
93 }
94 
95 
97 {
98  mTargetSpaceIsR = true;
99 }
100 
102 {
103  mTargetSpaceIsR = false;
104  mActor = vtkActorPtr::New();
105  mPainter = TextureSlicePainterPtr::New();
106  mPainterPolyDatamapper = vtkPainterPolyDataMapperPtr::New();
107 
108  mPlaneSource = vtkPlaneSourcePtr::New();
109 
110  vtkTriangleFilterPtr triangleFilter = vtkTriangleFilterPtr::New(); //create triangle polygons from input polygons
111  triangleFilter->SetInputConnection(mPlaneSource->GetOutputPort()); //in this case a Planesource
112 
113  vtkStripperPtr stripper = vtkStripperPtr::New();
114  stripper->SetInputConnection(triangleFilter->GetOutputPort());
115 // stripper->Update();
116  mPolyDataAlgorithm = stripper;
117  mPolyDataAlgorithm->Update();
118 
119  mPolyData = mPolyDataAlgorithm->GetOutput();
120  mPolyData->GetPointData()->SetNormals(NULL);
121 
122  mPainter->SetDelegatePainter(mPainterPolyDatamapper->GetPainter());
123  mPainterPolyDatamapper->SetPainter(mPainter);
124  mPainterPolyDatamapper->SetInputData(mPolyData);
125  mActor->SetMapper(mPainterPolyDatamapper);
126 }
127 
129 {
130  mImages.clear();
131 }
132 
134 {
136 }
137 
139 {
140  return mActor;
141 }
142 
144 {
145  mPainter->setShaderPath(shaderFile);
146 }
147 
148 //void Texture3DSlicerProxyImpl::viewChanged()
149 //{
150 // if (!mView)
151 // return;
152 // if (!mView->getZoomFactor() < 0)
153 // return; // ignore if zoom is invalid
154 // this->setViewportData(mView->get_vpMs(), mView->getViewport());
155 //}
156 
158 {
159  if (!mTargetSpaceIsR)
160  {
161  mBB_s = transform(vpMs.inv(), vp);
162  }
163 
164  this->resetGeometryPlane();
165 }
166 
167 void Texture3DSlicerProxyImpl::resetGeometryPlane()
168 {
169  if (mTargetSpaceIsR)
170  {
171  // use largest bb from all volume box vertices projected onto s:
172  Transform3D rMs = mSliceProxy->get_sMr().inv();
173  DoubleBoundingBox3D bb_d = mImages[0]->boundingBox();
174  Transform3D sMd = rMs.inv()*mImages[0]->get_rMd();
175  std::vector<Vector3D> pp_s;
176  for (unsigned x=0; x<2; ++x)
177  for (unsigned y=0; y<2; ++y)
178  for (unsigned z=0; z<2; ++z)
179  pp_s.push_back(sMd.coord(bb_d.corner(x,y,x)));
180 
181  mBB_s = DoubleBoundingBox3D::fromCloud(pp_s);
182  mBB_s[4] = 0;
183  mBB_s[5] = 0;
184 // double extent = 100;
185 // mBB_s = DoubleBoundingBox3D(-extent, extent, -extent, extent, 0, 0);
186  }
187 
188  Vector3D origin(mBB_s[0], mBB_s[2], 0);
189  Vector3D p1(mBB_s[1], mBB_s[2], 0);
190  Vector3D p2(mBB_s[0], mBB_s[3], 0);
191 
192  if (mTargetSpaceIsR)
193  {
194  Transform3D rMs = mSliceProxy->get_sMr().inv();
195  p1 = rMs.coord(p1);
196  p2 = rMs.coord(p2);
197  origin = rMs.coord(origin);
198  }
199 
200  if (similar(mBB_s.range()[0] * mBB_s.range()[1], 0.0))
201  {
202 // std::cout << "zero-size bounding box in texture slicer- ignoring" << std::endl;
203  return;
204  }
205 
206  createGeometryPlane(p1, p2, origin);
207 }
208 
210 {
211 // std::cout << "createGeometryPlane " << point1_s << ", " << point2_s << ", " << origin_s << std::endl;
212  mPlaneSource->SetPoint1( point1_s.begin() );
213  mPlaneSource->SetPoint2( point2_s.begin() );
214  mPlaneSource->SetOrigin( origin_s.begin() );
215 // std::cout << "createGeometryPlane update begin" << std::endl;
216  mPolyDataAlgorithm->Update();
217 // mPolyData->Update();
218 // std::cout << "createGeometryPlane update end" << std::endl;
219  // each stripper->update() resets the contents of polydata, thus we must reinsert the data here.
220  for (unsigned i=0; i<mImages.size(); ++i)
221  {
222  updateCoordinates(i);
223  }
224 }
225 
226 void Texture3DSlicerProxyImpl::setImages(std::vector<ImagePtr> images_raw)
227 {
228  std::vector<ImagePtr> images = processImages(images_raw);
229 
230  for (unsigned i = 0; i < mImages.size(); ++i)
231  {
232  disconnect(mImages[i].get(), SIGNAL(transformChanged()), this, SLOT(transformChangedSlot()));
233  disconnect(mImages[i].get(), SIGNAL(transferFunctionsChanged()), this, SLOT(updateColorAttributeSlot()));
234  disconnect(mImages[i].get(), SIGNAL(vtkImageDataChanged()), this, SLOT(imageChanged()));
235  }
236 
237  mImages = images;
238 
239  for (unsigned i = 0; i < mImages .size(); ++i)
240  {
241  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData();
242 
244  inputImage);
245 
246  mPainter->SetVolumeBuffer(i, dataBuffer);
247 
248  connect(mImages[i].get(), SIGNAL(transformChanged()), this, SLOT(transformChangedSlot()));
249  connect(mImages[i].get(), SIGNAL(transferFunctionsChanged()), this, SLOT(updateColorAttributeSlot()));
250  connect(mImages[i].get(), SIGNAL(vtkImageDataChanged()), this, SLOT(imageChanged()));
251  this->updateCoordinates(i);
252  }
253  this->updateColorAttributeSlot();
254 
255  for (unsigned i = 0; i < mImages.size(); ++i)
256  {
257  mPainterPolyDatamapper->MapDataArrayToMultiTextureAttribute(2 * i,
258  cstring_cast(this->getTCoordName(i)),
259  vtkDataObject::FIELD_ASSOCIATION_POINTS);
260  }
261 }
262 
263 std::vector<ImagePtr> Texture3DSlicerProxyImpl::processImages(std::vector<ImagePtr> images_raw)
264 {
265  if(images_raw.size() > mMaxImages)
266  {
267  QString errorText = QString("Texture3DSlicerProxyImpl: GPU multislicer can't handle more than %1 images. Additional images are not shown.").arg(mMaxImages);
268  reportError(errorText);
269  images_raw.resize(mMaxImages);
270  }
271 
272  std::vector<ImagePtr> images(images_raw.size());
273  for (unsigned i=0; i<images.size(); ++i)
274  images[i] = images_raw[i]->getUnsigned(images_raw[i]);
275 
276  return images;
277 }
278 
279 
281 {
282  if (mSliceProxy)
283  disconnect(mSliceProxy.get(), SIGNAL(transformChanged(Transform3D)), this, SLOT(transformChangedSlot()));
284  mSliceProxy = slicer;
285  if (mSliceProxy)
286  {
287  connect(mSliceProxy.get(), SIGNAL(transformChanged(Transform3D)), this, SLOT(transformChangedSlot()));
288  for (unsigned i=0; i < mImages.size(); ++i)
289  {
290  updateCoordinates(i);
291  }
292  }
293 }
294 
295 QString Texture3DSlicerProxyImpl::getTCoordName(int index)
296 {
297  return "texture"+qstring_cast(index);
298 }
299 
300 void Texture3DSlicerProxyImpl::updateCoordinates(int index)
301 {
302  if (!mPolyData || !mSliceProxy)
303  return;
304 
305  vtkImageDataPtr volume = mImages[index]->getBaseVtkImageData();
306  // create a bb describing the volume in physical (raw data) space
307  Vector3D origin(volume->GetOrigin());
308  Vector3D spacing(volume->GetSpacing());
309  DoubleBoundingBox3D imageSize(volume->GetExtent());
310 
311  for (int i = 0; i < 3; ++i)
312  {
313  imageSize[2 * i] = origin[i] + spacing[i] * (imageSize[2 * i] - 0.5);
314  imageSize[2 * i + 1] = origin[i] + spacing[i] * (imageSize[2 * i + 1] + 0.5);
315  }
316 
317  // identity bb
318  DoubleBoundingBox3D textureSpace(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
319 
320  // create transform from slice space to raw data space
321  Transform3D iMs = mImages[index]->get_rMd().inv() * mSliceProxy->get_sMr().inv();
322  // create transform from image space to texture normalized space
323  Transform3D nMi = createTransformNormalize(imageSize, textureSpace);
324  // total transform from slice space to texture space
325  Transform3D nMs = nMi * iMs;
326  // transform the viewport to texture coordinates (must use coords since bb3D doesnt handle non-axis-aligned transforms)
327  std::vector<Vector3D> plane(4);
328  plane[0] = mBB_s.corner(0, 0, 0);
329  plane[1] = mBB_s.corner(1, 0, 0);
330  plane[2] = mBB_s.corner(0, 1, 0);
331  plane[3] = mBB_s.corner(1, 1, 0);
332 
333  for (unsigned i = 0; i < plane.size(); ++i)
334  {
335  plane[i] = nMs.coord(plane[i]);
336  }
337 
338  vtkFloatArrayPtr TCoords = vtkFloatArray::SafeDownCast(mPolyData->GetPointData()->GetArray(
339  cstring_cast(getTCoordName(index))));
340 
341  if (!TCoords) // create the TCoords
342  {
343  TCoords = vtkFloatArrayPtr::New();
344  TCoords->SetNumberOfComponents(3);
345  TCoords->Allocate(4 * 3);
346  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
347  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
348  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
349  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
350  TCoords->SetName(cstring_cast(getTCoordName(index)));
351  mPolyData->GetPointData()->AddArray(TCoords);
352  }
353 
354  for (unsigned i = 0; i < plane.size(); ++i)
355  {
356  TCoords->SetTuple3(i, plane[i][0], plane[i][1], plane[i][2]);
357  }
358 
359  mPolyData->Modified();
360 }
361 
362 void Texture3DSlicerProxyImpl::updateColorAttributeSlot()
363 {
364  for (unsigned i = 0; i < mImages.size(); ++i)
365  {
366  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData() ;
367 
368  vtkLookupTablePtr lut = mImages[i]->getLookupTable2D()->getOutputLookupTable();
369  lut->GetTable()->Modified();
371 
372  // no lut indicates to the fragment shader that RGBA should be used
373  if (inputImage->GetNumberOfScalarComponents()==1)
374  {
375  mPainter->SetLutBuffer(i, lutBuffer);
376  }
377 
378  int scalarTypeMax = (int)inputImage->GetScalarTypeMax();
379  double imin = lut->GetRange()[0];
380  double imax = lut->GetRange()[1];
381 
382  float window = (float) (imax-imin) / scalarTypeMax;
383  float llr = (float) mImages[i]->getLookupTable2D()->getLLR() / scalarTypeMax;
384  float level = (float) imin/scalarTypeMax + window/2;
385  float alpha = (float) mImages[i]->getLookupTable2D()->getAlpha();
386 
387  mPainter->SetColorAttribute(i, window, level, llr, alpha);
388  }
389  mActor->Modified();
390 }
391 
392 void Texture3DSlicerProxyImpl::transformChangedSlot()
393 {
394  if (mTargetSpaceIsR)
395  this->resetGeometryPlane();
396  this->update();
397 }
398 
399 void Texture3DSlicerProxyImpl::imageChanged()
400 {
401  mActor->Modified();
402 
403  for (unsigned i = 0; i < mImages .size(); ++i)
404  {
405  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData();//
406 
408  inputImage);
409 
410  mPainter->SetVolumeBuffer(i, dataBuffer);
411  }
412 }
413 
415 {
416  for (unsigned i=0; i<mImages.size(); ++i)
417  {
418  updateCoordinates(i);
419  }
420 }
421 
422 #endif // WIN32
423 
424 
425 //---------------------------------------------------------
426 }//end namespace
427 //---------------------------------------------------------
428 
QString qstring_cast(const T &val)
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
vtkSmartPointer< class vtkActor > vtkActorPtr
void reportError(QString msg)
Definition: cxLogger.cpp:92
Vector3D corner(int x, int y, int z) const
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
boost::shared_ptr< class SliceProxy > SliceProxyPtr
static Texture3DSlicerProxyPtr New()
vtkSmartPointer< class vtkFloatArray > vtkFloatArrayPtr
vtkSmartPointer< class vtkRenderWindow > vtkRenderWindowPtr
cstring_cast_Placeholder cstring_cast(const T &val)
void setTargetSpaceToR()
use to draw the slice in 3D r space instead of in 2D s space.
void setShaderPath(QString shaderFile)
static DoubleBoundingBox3D fromCloud(std::vector< Vector3D > cloud)
Transform3D createTransformNormalize(const DoubleBoundingBox3D &in, const DoubleBoundingBox3D &out)
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
GPUImageLutBufferPtr getGPUImageLutBuffer(vtkUnsignedCharArrayPtr lut)
void setViewportData(const Transform3D &vpMs, const DoubleBoundingBox3D &vp)
void setSliceProxy(SliceProxyPtr slicer)
static Texture3DSlicerProxyPtr New()
static bool LoadRequiredExtensions(vtkOpenGLExtensionManager *mgr)
vtkSmartPointer< class vtkTriangleFilter > vtkTriangleFilterPtr
void createGeometryPlane(Vector3D point1_s, Vector3D point2_s, Vector3D origin_s)
void setImages(std::vector< ImagePtr > images)
vtkSmartPointer< class vtkLookupTable > vtkLookupTablePtr
boost::shared_ptr< class Texture3DSlicerProxy > Texture3DSlicerProxyPtr
boost::shared_ptr< class GPUImageDataBuffer > GPUImageDataBufferPtr
vtkSmartPointer< class vtkStripper > vtkStripperPtr
Representation of a floating-point bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
boost::shared_ptr< class GPUImageLutBuffer > GPUImageLutBufferPtr
GPUImageDataBufferPtr getGPUImageDataBuffer(vtkImageDataPtr volume)
static GPUImageBufferRepository * getInstance()
static bool isSupported(vtkRenderWindowPtr window)
vtkSmartPointer< class vtkImageData > vtkImageDataPtr