CustusX  15.3.4-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxEraserWidget.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 #include <cxEraserWidget.h>
34 
35 #include <QTimer>
36 #include <QCheckBox>
37 
38 #include "vtkSphereWidget.h"
39 #include "vtkSplineWidget.h"
40 #include "vtkSplineWidget2.h"
41 #include "vtkSplineRepresentation.h"
42 #include "vtkRenderWindow.h"
43 #include <vtkSphere.h>
44 #include <vtkClipPolyData.h>
45 #include <vtkImageData.h>
46 
47 #include "cxMesh.h"
48 #include "cxStringPropertyBase.h"
50 #include "cxDefinitionStrings.h"
51 #include "cxUtilHelpers.h"
52 
54 #include "cxImageAlgorithms.h"
55 #include "cxDoubleWidgets.h"
56 #include "cxImage.h"
57 #include "cxVolumeHelpers.h"
58 #include "cxPatientModelService.h"
59 #include "cxViewService.h"
60 #include "cxViewGroupData.h"
61 
62 namespace cx
63 {
64 
65 EraserWidget::EraserWidget(QWidget* parent) :
66  BaseWidget(parent, "EraserWidget", "Eraser"),
67  mPreviousCenter(0,0,0),
68  mPreviousRadius(0)
69 {
70 
71  QVBoxLayout* layout = new QVBoxLayout(this);
72 
73  mContinousEraseTimer = new QTimer(this);
74  connect(mContinousEraseTimer, SIGNAL(timeout()), this, SLOT(continousRemoveSlot())); // this signal will be executed in the thread of THIS, i.e. the main thread.
75 
76 // this->setToolTip(this->defaultWhatsThis());
77 
78 // layout->addWidget(new QLabel(QString("<font size=4 color=red><b>%1</b></font><br>Erase parts of active volume using a sphere.").arg("Experimental Widget!!")));
79 // layout->addStretch();
80 
81  QHBoxLayout* buttonLayout = new QHBoxLayout;
82  layout->addLayout(buttonLayout);
83  QHBoxLayout* buttonLayout2 = new QHBoxLayout;
84  layout->addLayout(buttonLayout2);
85 
86  mShowEraserCheckBox = new QCheckBox("Show");
87  mShowEraserCheckBox->setToolTip("Show eraser sphere in the views.");
88  connect(mShowEraserCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleShowEraser(bool)));
89  buttonLayout->addWidget(mShowEraserCheckBox);
90 
91  mContinousEraseCheckBox = new QCheckBox("Continous");
92  mContinousEraseCheckBox->setToolTip("Erase continously using the sphere. (might be slow)");
93  connect(mContinousEraseCheckBox, SIGNAL(toggled(bool)), this, SLOT(toggleContinous(bool)));
94  buttonLayout2->addWidget(mContinousEraseCheckBox);
95 
96  mDuplicateAction = this->createAction(this, QIcon(), "Duplicate", "Duplicate active volume - do this before erasing!",
97  SLOT(duplicateSlot()), buttonLayout);
98 
99  mSaveAction = this->createAction(this, QIcon(), "Save", "Save modified image to disk",
100  SLOT(saveSlot()), buttonLayout);
101 
102  mRemoveAction = this->createAction(this, QIcon(), "Erase", "Erase everything inside sphere",
103  SLOT(removeSlot()), buttonLayout2);
104 
105 
106  double sphereRadius = 10;
107  mSphereSizeAdapter = DoubleProperty::initialize("SphereSize", "Sphere Size", "Radius of Eraser Sphere", sphereRadius, DoubleRange(1,200,1), 0, QDomNode());
108  connect(mSphereSizeAdapter.get(), SIGNAL(changed()), this, SLOT(sphereSizeChangedSlot()));
109  mSphereSize = new SpinBoxAndSliderGroupWidget(this, mSphereSizeAdapter);
110  layout->addWidget(mSphereSize);
111 
112  layout->addStretch();
113 
114  this->enableButtons();
115 }
116 
117 void EraserWidget::enableButtons()
118 {
119  bool e = mShowEraserCheckBox->isChecked();
120 
121  mContinousEraseCheckBox->setEnabled(e);
122 // mDuplicateAction->setEnabled(e);
123 // mSaveAction->setEnabled(e);
124  mRemoveAction->setEnabled(e);
125  mSphereSize->setEnabled(e);
126 }
127 
129 {
130 }
131 
133 {
134  return "<html>"
135  "<h3>Functionality for erasing parts of volumes/meshes.</h3>"
136  "<p>"
137  "</p>"
138  "<p><i></i></p>"
139  "</html>";
140 }
141 
142 void EraserWidget::toggleContinous(bool on)
143 {
144  if (on)
145  {
146  mContinousEraseTimer->start(300);
147  }
148  else
149  {
150  mContinousEraseTimer->stop();
151  }
152 }
153 
154 void EraserWidget::continousRemoveSlot()
155 {
156  Transform3D rMd = viewService()->getGroup(0)->getOptions().mPickerGlyph->get_rMd();
157  //Transform3D rMd = viewService()->getViewGroupDatas().front()->getData()->getOptions().mPickerGlyph->get_rMd();
158  Vector3D c(mSphere->GetCenter());
159  c = rMd.coord(c);
160  double r = mSphere->GetRadius();
161 
162  // optimization: dont remove if idle
163  if (similar(mPreviousCenter, c) && similar(mPreviousRadius, r))
164  return;
165 
166  this->removeSlot();
167 }
168 
169 void EraserWidget::duplicateSlot()
170 {
171  ImagePtr original = patientService()->getActiveImage();
172 
173  ImagePtr duplicate = duplicateImage(patientService(), original);
174  patientService()->insertData(duplicate);
175  patientService()->setActiveImage(duplicate);
176 
177  // replace viz of original with duplicate
178 // std::vector<ViewGroupPtr> viewGroups = viewService()->getViewGroupDatas();
179  for (unsigned i = 0; i < viewService()->groupCount(); ++i)
180  {
181  if (viewService()->getGroup(i)->removeData(original->getUid()))
182  viewService()->getGroup(i)->addData(duplicate->getUid());
183  }
184 }
185 
186 void EraserWidget::sphereSizeChangedSlot()
187 {
188  if (mSphere)
189  {
190  mSphere->SetRadius(mSphereSizeAdapter->getValue());
191  mSphere->Update();
192  }
193 }
194 
199 void EraserWidget::saveSlot()
200 {
201  patientService()->insertData(patientService()->getActiveImage());
202 }
203 
204 
205 template <class TYPE>
206 void EraserWidget::eraseVolume(TYPE* volumePointer, TYPE replaceVal)
207 {
208  ImagePtr image = patientService()->getActiveImage();
209  vtkImageDataPtr img = image->getBaseVtkImageData();
210 
211  Eigen::Array3i dim(img->GetDimensions());
212  Vector3D spacing(img->GetSpacing());
213 
214  Transform3D rMd = viewService()->getGroup(0)->getOptions().mPickerGlyph->get_rMd();
215  Vector3D c(mSphere->GetCenter());
216  c = rMd.coord(c);
217  double r = mSphere->GetRadius();
218  mPreviousCenter = c;
219  mPreviousRadius = r;
220 
221  DoubleBoundingBox3D bb_r(c[0]-r, c[0]+r, c[1]-r, c[1]+r, c[2]-r, c[2]+r);
222 
223  Transform3D dMr = image->get_rMd().inv();
224  Transform3D rawMd = createTransformScale(spacing).inv();
225  Transform3D rawMr = rawMd * dMr;
226  Vector3D c_d = dMr.coord(c);
227  double r_d = dMr.vector(r * Vector3D::UnitX()).length();
228  c = rawMr.coord(c);
229  r = rawMr.vector(r * Vector3D::UnitX()).length();
230  DoubleBoundingBox3D bb0_raw = transform(rawMr, bb_r);
231  IntBoundingBox3D bb1_raw(0, dim[0], 0, dim[1], 0, dim[2]);
232 
233 // std::cout << " sphere: " << bb0_raw << std::endl;
234 // std::cout << " raw: " << bb1_raw << std::endl;
235 
236  for (int i=0; i<3; ++i)
237  {
238  bb1_raw[2*i] = std::max<double>(bb1_raw[2*i], bb0_raw[2*i]);
239  bb1_raw[2*i+1] = std::min<double>(bb1_raw[2*i+1], bb0_raw[2*i+1]);
240  }
241 
242  for (int x = bb1_raw[0]; x < bb1_raw[1]; ++x)
243  for (int y = bb1_raw[2]; y < bb1_raw[3]; ++y)
244  for (int z = bb1_raw[4]; z < bb1_raw[5]; ++z)
245  {
246  int index = x + y * dim[0] + z * dim[0] * dim[1];
247  if ((Vector3D(x*spacing[0], y*spacing[1], z*spacing[2]) - c_d).length() < r_d)
248  volumePointer[index] = replaceVal;
249  }
250 }
251 
252 //#define VTK_VOID 0
253 //#define VTK_BIT 1
254 //#define VTK_CHAR 2
255 //#define VTK_SIGNED_CHAR 15
256 //#define VTK_UNSIGNED_CHAR 3
257 //#define VTK_SHORT 4
258 //#define VTK_UNSIGNED_SHORT 5
259 //#define VTK_INT 6
260 //#define VTK_UNSIGNED_INT 7
261 //#define VTK_LONG 8
262 //#define VTK_UNSIGNED_LONG 9
263 //#define VTK_FLOAT 10
264 //#define VTK_DOUBLE 11
265 //#define VTK_ID_TYPE 12
266 
267 void EraserWidget::removeSlot()
268 {
269  if (!mSphere)
270  return;
271 
272  ImagePtr image = patientService()->getActiveImage();
273  vtkImageDataPtr img = image->getBaseVtkImageData();
274 
275  if (img->GetScalarType()==VTK_CHAR)
276  this->eraseVolume(static_cast<char*> (img->GetScalarPointer()), VTK_CHAR_MIN);
277  if (img->GetScalarType()==VTK_UNSIGNED_CHAR)
278  this->eraseVolume(static_cast<unsigned char*> (img->GetScalarPointer()), VTK_UNSIGNED_CHAR_MIN);
279  if (img->GetScalarType()==VTK_UNSIGNED_SHORT)
280  this->eraseVolume(static_cast<unsigned short*> (img->GetScalarPointer()), VTK_UNSIGNED_SHORT_MIN);
281  if (img->GetScalarType()==VTK_SHORT)
282  this->eraseVolume(static_cast<short*> (img->GetScalarPointer()), VTK_SHORT_MIN);
283  if (img->GetScalarType()==VTK_UNSIGNED_INT)
284  this->eraseVolume(static_cast<unsigned int*> (img->GetScalarPointer()), VTK_UNSIGNED_INT_MIN);
285  if (img->GetScalarType()==VTK_INT)
286  this->eraseVolume(static_cast<int*> (img->GetScalarPointer()), VTK_INT_MIN);
287 
288  ImageLUT2DPtr tf2D = image->getLookupTable2D();
289  ImageTF3DPtr tf3D = image->getTransferFunctions3D();
290 
291 // img->Modified();
292  setDeepModified(img);
293  image->setVtkImageData(img);
294 
295  // keep existing transfer functions
296  image->setLookupTable2D(tf2D);
297  image->setTransferFunctions3D(tf3D);
298 }
299 
300 void EraserWidget::toggleShowEraser(bool on)
301 {
302  if (on)
303  {
304 // std::vector<ViewGroupPtr> viewGroups = viewService()->getViewGroups();
305  mSphere = vtkSphereSourcePtr::New();
306 
307  mSphere->SetRadius(40);
308  mSphere->SetThetaResolution(16);
309  mSphere->SetPhiResolution(12);
310  mSphere->LatLongTessellationOn(); // more natural wireframe view
311 
312  double a = mSphereSizeAdapter->getValue();
313  mSphere->SetRadius(a);
314  mSphere->Update();
315  MeshPtr glyph = viewService()->getGroup(0)->getOptions().mPickerGlyph;
316  glyph->setVtkPolyData(mSphere->GetOutput());
317  glyph->setColor(QColor(255, 204, 0)); // same as tool
318  glyph->setIsWireframe(true);
319 
320  // set same glyph in all groups
321  for (unsigned i=0; i<viewService()->groupCount(); ++i)
322  {
323  ViewGroupData::Options options = viewService()->getGroup(i)->getOptions();
324  options.mPickerGlyph = glyph;
325  viewService()->getGroup(i)->setOptions(options);
326  }
327  }
328  else
329  {
330  viewService()->getGroup(0)->getOptions().mPickerGlyph->setVtkPolyData(NULL);
331  mContinousEraseCheckBox->setChecked(false);
332  }
333 
334  this->enableButtons();
335 }
336 
337 }
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
Transform3D createTransformScale(const Vector3D &scale_)
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
Utility class for describing a bounded numeric range.
Definition: cxDoubleRange.h:53
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
QAction * createAction(QObject *parent, QIcon iconName, QString text, QString tip, T slot, QLayout *layout=NULL, QToolButton *button=new QToolButton())
Definition: cxBaseWidget.h:130
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
EraserWidget(QWidget *parent)
Composite widget for scalar data manipulation.
ImagePtr duplicateImage(PatientModelServicePtr dataManager, ImagePtr image)
boost::shared_ptr< class ImageLUT2D > ImageLUT2DPtr
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:108
cxLogicManager_EXPORT ViewServicePtr viewService()
void setDeepModified(vtkImageDataPtr image)
cxLogicManager_EXPORT PatientModelServicePtr patientService()
static DoublePropertyPtr initialize(const QString &uid, QString name, QString help, double value, DoubleRange range, int decimals, QDomNode root=QDomNode())
RealScalar length() const
virtual QString defaultWhatsThis() const
Returns a short description of what this widget will do for you.
boost::shared_ptr< class Mesh > MeshPtr
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
boost::shared_ptr< class ImageTF3D > ImageTF3DPtr