CustusX  15.3.4-beta
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 
54 #include "cxImage.h"
55 #include "cxView.h"
56 #include "cxImageLUT2D.h"
57 #include "cxSliceProxy.h"
58 #include "cxTypeConversions.h"
59 #include "cxGPUImageBuffer.h"
60 #include <vtkOpenGLRenderWindow.h>
61 
62 
63 //---------------------------------------------------------
64 namespace cx
65 {
66 //---------------------------------------------------------
67 
68 #ifdef WIN32
69 
71 {
72  return Texture3DSlicerProxyPtr(new Texture3DSlicerProxy());
73 }
74 
76 {
77  return false;
78 }
79 
80 #else
81 
83 {
84  vtkOpenGLRenderWindow *context = vtkOpenGLRenderWindow::SafeDownCast(window);
85  bool success = context && TextureSlicePainter::LoadRequiredExtensions(context->GetExtensionManager());
86  return success;
87 }
88 
90 {
92 }
93 
94 
96 {
97  mTargetSpaceIsR = true;
98 }
99 
101 {
102  mTargetSpaceIsR = false;
103  mActor = vtkActorPtr::New();
104  mPainter = TextureSlicePainterPtr::New();
105  mPainterPolyDatamapper = vtkPainterPolyDataMapperPtr::New();
106 
107  mPlaneSource = vtkPlaneSourcePtr::New();
108 
109  vtkTriangleFilterPtr triangleFilter = vtkTriangleFilterPtr::New(); //create triangle polygons from input polygons
110  triangleFilter->SetInputConnection(mPlaneSource->GetOutputPort()); //in this case a Planesource
111 
112  vtkStripperPtr stripper = vtkStripperPtr::New();
113  stripper->SetInputConnection(triangleFilter->GetOutputPort());
114 // stripper->Update();
115  mPolyDataAlgorithm = stripper;
116  mPolyDataAlgorithm->Update();
117 
118  mPolyData = mPolyDataAlgorithm->GetOutput();
119  mPolyData->GetPointData()->SetNormals(NULL);
120 
121  mPainter->SetDelegatePainter(mPainterPolyDatamapper->GetPainter());
122  mPainterPolyDatamapper->SetPainter(mPainter);
123  mPainterPolyDatamapper->SetInputData(mPolyData);
124  mActor->SetMapper(mPainterPolyDatamapper);
125 }
126 
128 {
129  mImages.clear();
130 }
131 
133 {
135 }
136 
138 {
139  return mActor;
140 }
141 
143 {
144  mPainter->setShaderPath(shaderFile);
145 }
146 
147 //void Texture3DSlicerProxyImpl::viewChanged()
148 //{
149 // if (!mView)
150 // return;
151 // if (!mView->getZoomFactor() < 0)
152 // return; // ignore if zoom is invalid
153 // this->setViewportData(mView->get_vpMs(), mView->getViewport());
154 //}
155 
157 {
158  if (!mTargetSpaceIsR)
159  {
160  mBB_s = transform(vpMs.inv(), vp);
161  }
162 
163  this->resetGeometryPlane();
164 }
165 
166 void Texture3DSlicerProxyImpl::resetGeometryPlane()
167 {
168  if (mTargetSpaceIsR)
169  {
170  // use largest bb from all volume box vertices projected onto s:
171  Transform3D rMs = mSliceProxy->get_sMr().inv();
172  DoubleBoundingBox3D bb_d = mImages[0]->boundingBox();
173  Transform3D sMd = rMs.inv()*mImages[0]->get_rMd();
174  std::vector<Vector3D> pp_s;
175  for (unsigned x=0; x<2; ++x)
176  for (unsigned y=0; y<2; ++y)
177  for (unsigned z=0; z<2; ++z)
178  pp_s.push_back(sMd.coord(bb_d.corner(x,y,x)));
179 
180  mBB_s = DoubleBoundingBox3D::fromCloud(pp_s);
181  mBB_s[4] = 0;
182  mBB_s[5] = 0;
183 // double extent = 100;
184 // mBB_s = DoubleBoundingBox3D(-extent, extent, -extent, extent, 0, 0);
185  }
186 
187  Vector3D origin(mBB_s[0], mBB_s[2], 0);
188  Vector3D p1(mBB_s[1], mBB_s[2], 0);
189  Vector3D p2(mBB_s[0], mBB_s[3], 0);
190 
191  if (mTargetSpaceIsR)
192  {
193  Transform3D rMs = mSliceProxy->get_sMr().inv();
194  p1 = rMs.coord(p1);
195  p2 = rMs.coord(p2);
196  origin = rMs.coord(origin);
197  }
198 
199  if (similar(mBB_s.range()[0] * mBB_s.range()[1], 0.0))
200  {
201 // std::cout << "zero-size bounding box in texture slicer- ignoring" << std::endl;
202  return;
203  }
204 
205  createGeometryPlane(p1, p2, origin);
206 }
207 
209 {
210 // std::cout << "createGeometryPlane " << point1_s << ", " << point2_s << ", " << origin_s << std::endl;
211  mPlaneSource->SetPoint1( point1_s.begin() );
212  mPlaneSource->SetPoint2( point2_s.begin() );
213  mPlaneSource->SetOrigin( origin_s.begin() );
214 // std::cout << "createGeometryPlane update begin" << std::endl;
215  mPolyDataAlgorithm->Update();
216 // mPolyData->Update();
217 // std::cout << "createGeometryPlane update end" << std::endl;
218  // each stripper->update() resets the contents of polydata, thus we must reinsert the data here.
219  for (unsigned i=0; i<mImages.size(); ++i)
220  {
221  updateCoordinates(i);
222  }
223 }
224 
225 void Texture3DSlicerProxyImpl::setImages(std::vector<ImagePtr> images_raw)
226 {
227  std::vector<ImagePtr> images(images_raw.size());
228  for (unsigned i=0; i<images.size(); ++i)
229  {
230  images[i] = images_raw[i]->getUnsigned(images_raw[i]);
231  }
232 
233  if (mImages.size() == images.size())
234  {
235  bool equal = true;
236  for (unsigned i = 0; i < mImages.size(); ++i)
237  equal &= (mImages[i] == images[i]);
238  if (equal)
239  return;
240  }
241 
242  for (unsigned i = 0; i < mImages.size(); ++i)
243  {
244  disconnect(mImages[i].get(), SIGNAL(transformChanged()), this, SLOT(transformChangedSlot()));
245  disconnect(mImages[i].get(), SIGNAL(transferFunctionsChanged()), this, SLOT(updateColorAttributeSlot()));
246  disconnect(mImages[i].get(), SIGNAL(vtkImageDataChanged()), this, SLOT(imageChanged()));
247  }
248 
249  mImages = images;
250 
251  for (unsigned i = 0; i < mImages .size(); ++i)
252  {
253  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData();//
254 
256  inputImage);
257 
258  mPainter->SetVolumeBuffer(i, dataBuffer);
259 
260  connect(mImages[i].get(), SIGNAL(transformChanged()), this, SLOT(transformChangedSlot()));
261  connect(mImages[i].get(), SIGNAL(transferFunctionsChanged()), this, SLOT(updateColorAttributeSlot()));
262  connect(mImages[i].get(), SIGNAL(vtkImageDataChanged()), this, SLOT(imageChanged()));
263  this->updateCoordinates(i);
264  }
265  this->updateColorAttributeSlot();
266 
267  for (unsigned i = 0; i < mImages.size(); ++i)
268  {
269  mPainterPolyDatamapper->MapDataArrayToMultiTextureAttribute(2 * i,
270  cstring_cast(this->getTCoordName(i)),
271  vtkDataObject::FIELD_ASSOCIATION_POINTS);
272  }
273 }
274 
275 
277 {
278  if (mSliceProxy)
279  disconnect(mSliceProxy.get(), SIGNAL(transformChanged(Transform3D)), this, SLOT(transformChangedSlot()));
280  mSliceProxy = slicer;
281  if (mSliceProxy)
282  {
283  connect(mSliceProxy.get(), SIGNAL(transformChanged(Transform3D)), this, SLOT(transformChangedSlot()));
284  for (unsigned i=0; i < mImages.size(); ++i)
285  {
286  updateCoordinates(i);
287  }
288  }
289 }
290 
291 QString Texture3DSlicerProxyImpl::getTCoordName(int index)
292 {
293  return "texture"+qstring_cast(index);
294 }
295 
296 void Texture3DSlicerProxyImpl::updateCoordinates(int index)
297 {
298  if (!mPolyData || !mSliceProxy)
299  return;
300 
301  vtkImageDataPtr volume = mImages[index]->getBaseVtkImageData();
302  // create a bb describing the volume in physical (raw data) space
303  Vector3D origin(volume->GetOrigin());
304  Vector3D spacing(volume->GetSpacing());
305  DoubleBoundingBox3D imageSize(volume->GetExtent());
306 
307  for (int i = 0; i < 3; ++i)
308  {
309  imageSize[2 * i] = origin[i] + spacing[i] * (imageSize[2 * i] - 0.5);
310  imageSize[2 * i + 1] = origin[i] + spacing[i] * (imageSize[2 * i + 1] + 0.5);
311  }
312 
313  // identity bb
314  DoubleBoundingBox3D textureSpace(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
315 
316  // create transform from slice space to raw data space
317  Transform3D iMs = mImages[index]->get_rMd().inv() * mSliceProxy->get_sMr().inv();
318  // create transform from image space to texture normalized space
319  Transform3D nMi = createTransformNormalize(imageSize, textureSpace);
320  // total transform from slice space to texture space
321  Transform3D nMs = nMi * iMs;
322  // transform the viewport to texture coordinates (must use coords since bb3D doesnt handle non-axis-aligned transforms)
323  std::vector<Vector3D> plane(4);
324  plane[0] = mBB_s.corner(0, 0, 0);
325  plane[1] = mBB_s.corner(1, 0, 0);
326  plane[2] = mBB_s.corner(0, 1, 0);
327  plane[3] = mBB_s.corner(1, 1, 0);
328 
329  for (unsigned i = 0; i < plane.size(); ++i)
330  {
331  plane[i] = nMs.coord(plane[i]);
332  }
333 
334  vtkFloatArrayPtr TCoords = vtkFloatArray::SafeDownCast(mPolyData->GetPointData()->GetArray(
335  cstring_cast(getTCoordName(index))));
336 
337  if (!TCoords) // create the TCoords
338  {
339  TCoords = vtkFloatArrayPtr::New();
340  TCoords->SetNumberOfComponents(3);
341  TCoords->Allocate(4 * 3);
342  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
343  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
344  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
345  TCoords->InsertNextTuple3(0.0, 0.0, 0.0);
346  TCoords->SetName(cstring_cast(getTCoordName(index)));
347  mPolyData->GetPointData()->AddArray(TCoords);
348  }
349 
350  for (unsigned i = 0; i < plane.size(); ++i)
351  {
352  TCoords->SetTuple3(i, plane[i][0], plane[i][1], plane[i][2]);
353  }
354 
355  mPolyData->Modified();
356 }
357 
358 void Texture3DSlicerProxyImpl::updateColorAttributeSlot()
359 {
360  for (unsigned i = 0; i < mImages.size(); ++i)
361  {
362  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData() ;
363 
364  vtkLookupTablePtr lut = mImages[i]->getLookupTable2D()->getOutputLookupTable();
365  lut->GetTable()->Modified();
367 
368  // no lut indicates to the fragment shader that RGBA should be used
369  if (inputImage->GetNumberOfScalarComponents()==1)
370  {
371  mPainter->SetLutBuffer(i, lutBuffer);
372  }
373 
374  int scalarTypeMax = (int)inputImage->GetScalarTypeMax();
375  double imin = lut->GetRange()[0];
376  double imax = lut->GetRange()[1];
377 
378  float window = (float) (imax-imin) / scalarTypeMax;
379  float llr = (float) mImages[i]->getLookupTable2D()->getLLR() / scalarTypeMax;
380  float level = (float) imin/scalarTypeMax + window/2;
381  float alpha = (float) mImages[i]->getLookupTable2D()->getAlpha();
382 
383  mPainter->SetColorAttribute(i, window, level, llr, alpha);
384  }
385  mActor->Modified();
386 }
387 
388 void Texture3DSlicerProxyImpl::transformChangedSlot()
389 {
390  if (mTargetSpaceIsR)
391  this->resetGeometryPlane();
392  this->update();
393 }
394 
395 void Texture3DSlicerProxyImpl::imageChanged()
396 {
397  mActor->Modified();
398 
399  for (unsigned i = 0; i < mImages .size(); ++i)
400  {
401  vtkImageDataPtr inputImage = mImages[i]->getBaseVtkImageData();//
402 
404  inputImage);
405 
406  mPainter->SetVolumeBuffer(i, dataBuffer);
407  }
408 }
409 
411 {
412  for (unsigned i=0; i<mImages.size(); ++i)
413  {
414  updateCoordinates(i);
415  }
416 }
417 
418 #endif // WIN32
419 
420 
421 //---------------------------------------------------------
422 }//end namespace
423 //---------------------------------------------------------
424 
QString qstring_cast(const T &val)
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
vtkSmartPointer< class vtkActor > vtkActorPtr
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