Fraxinus  17.12
An IGT application
cxCustomMetric.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 "cxCustomMetric.h"
34 
35 #include <vtkImageData.h>
36 #include "cxBoundingBox3D.h"
37 #include "cxTypeConversions.h"
38 #include "cxPatientModelService.h"
39 #include "cxTypeConversions.h"
40 #include "cxSpaceProvider.h"
41 #include "cxSpaceListener.h"
42 #include "cxData.h"
43 #include "cxMesh.h"
44 #include "cxImage.h"
45 #include "cxLogger.h"
46 
47 namespace cx
48 {
49 
50 CustomMetric::CustomMetric(const QString& uid, const QString& name, PatientModelServicePtr dataManager, SpaceProviderPtr spaceProvider) :
51  DataMetric(uid, name, dataManager, spaceProvider), mShowDistanceMarkers(false), mDistanceMarkerVisibility(50)
52 {
53  mArguments.reset(new MetricReferenceArgumentList(QStringList() << "position" << "direction"));
54  mArguments->setValidArgumentTypes(QStringList() << "pointMetric" << "frameMetric");
55  connect(mArguments.get(), SIGNAL(argumentsChanged()), this, SIGNAL(transformChanged()));
56  connect(this, &CustomMetric::propertiesChanged, this, &CustomMetric::onPropertiesChanged);
57  mDefineVectorUpMethod = mDefineVectorUpMethods.table;
58  mModelUid = "";
59  mScaleToP1 = false;
60  mOffsetFromP0 = 0.0;
61  mOffsetFromP1 = 0.0;
62  mRepeatDistance = 0.0;
63  mTranslationOnly = false;
64  mTextureFollowTool = false;
65 }
66 
67 bool CustomMetric::needForToolListenerHasChanged() const
68 {
69  bool toolDefinesUp = mDefineVectorUpMethod == mDefineVectorUpMethods.tool;
70  bool toolListenerDefined = mToolListener.get()!=NULL;
71 
72  if (mTextureFollowTool != toolListenerDefined || toolDefinesUp != toolListenerDefined)
73  return true;
74  else
75  return false;
76 }
77 
78 
79 void CustomMetric::createOrDestroyToolListener()
80 {
81  if (this->needForToolListenerHasChanged())
82  {
83  bool toolDefinesUp = mDefineVectorUpMethod == mDefineVectorUpMethods.tool;
84  if (mTextureFollowTool || toolDefinesUp)
85  {
86  mToolListener = mSpaceProvider->createListener();
87  mToolListener->setSpace(CoordinateSystem(csTOOL_OFFSET, "active"));
88  connect(mToolListener.get(), &SpaceListener::changed, this, &CustomMetric::transformChanged);
89  }
90  else
91  {
92  disconnect(mToolListener.get(), &SpaceListener::changed, this, &CustomMetric::transformChanged);
93  mToolListener.reset();
94  }
95  }
96 }
97 
98 void CustomMetric::onPropertiesChanged()
99 {
100  this->createOrDestroyToolListener();
101 }
102 
103 CustomMetric::DefineVectorUpMethods CustomMetric::getDefineVectorUpMethods() const
104 {
105  return mDefineVectorUpMethods;
106 }
107 
108 
109 CustomMetricPtr CustomMetric::create(QString uid, QString name, PatientModelServicePtr dataManager, SpaceProviderPtr spaceProvider)
110 {
111  return CustomMetricPtr(new CustomMetric(uid, name, dataManager, spaceProvider));
112 }
113 
115 {
116 }
117 
118 void CustomMetric::addXml(QDomNode& dataNode)
119 {
120  DataMetric::addXml(dataNode);
121 
122  mArguments->addXml(dataNode);
123 
124  QDomElement elem = dataNode.toElement();
125  elem.setAttribute("definevectorup", mDefineVectorUpMethod);
126  elem.setAttribute("meshUid", mModelUid);
127 
128  elem.setAttribute("scaleToP1", mScaleToP1);
129  elem.setAttribute("offsetFromP0", mOffsetFromP0);
130  elem.setAttribute("offsetFromP1", mOffsetFromP1);
131  elem.setAttribute("repeatDistance", mRepeatDistance);
132  elem.setAttribute("showDistance", mShowDistanceMarkers);
133  elem.setAttribute("distanceMarkerVisibility", mDistanceMarkerVisibility);
134  elem.setAttribute("translationOnly", mTranslationOnly);
135  elem.setAttribute("textureFollowTool", mTextureFollowTool);
136 }
137 
138 void CustomMetric::parseXml(QDomNode& dataNode)
139 {
140  DataMetric::parseXml(dataNode);
141 
142  mArguments->parseXml(dataNode, mDataManager->getDatas());
143 
144  QDomElement elem = dataNode.toElement();
145  mDefineVectorUpMethod = elem.attribute("definevectorup", qstring_cast(mDefineVectorUpMethod));
146  mModelUid = elem.attribute("meshUid", qstring_cast(mModelUid));
147  mScaleToP1 = elem.attribute("scaleToP1", QString::number(mScaleToP1)).toInt();
148  mOffsetFromP0 = elem.attribute("offsetFromP0", QString::number(mOffsetFromP0)).toDouble();
149  mOffsetFromP1 = elem.attribute("offsetFromP1", QString::number(mOffsetFromP1)).toDouble();
150  mRepeatDistance = elem.attribute("repeatDistance", QString::number(mRepeatDistance)).toDouble();
151  mShowDistanceMarkers = elem.attribute("showDistance", QString::number(mShowDistanceMarkers)).toInt();
152  mDistanceMarkerVisibility = elem.attribute("distanceMarkerVisibility", QString::number(mDistanceMarkerVisibility)).toDouble();
153  mTranslationOnly = elem.attribute("translationOnly", QString::number(mTranslationOnly)).toInt();
154  mTextureFollowTool = elem.attribute("textureFollowTool", QString::number(mTextureFollowTool)).toInt();
155 
156  this->onPropertiesChanged();
157 }
158 
160 {
161  return !mArguments->getRefCoords().empty();
162 }
163 
165 {
166  return mArguments->getRefCoords().front();
167 }
168 
170 {
171  return DoubleBoundingBox3D::fromCloud(mArguments->getRefCoords());
172 }
173 
174 std::vector<Vector3D> CustomMetric::getPointCloud() const
175 {
176  std::vector<Vector3D> retval;
177 
178  DataPtr model = this->getModel();
179 
180  std::vector<Transform3D> pos = this->calculateOrientations();
181  std::vector<Vector3D> cloud;
182  Transform3D rrMd;
183 
184  if (model)
185  {
186  rrMd = model->get_rMd();
187  cloud = model->getPointCloud();
188  }
189  else
190  {
191  cloud.push_back(Vector3D::Zero());
192  rrMd = Transform3D::Identity();
193  }
194 
195  for (unsigned i=0; i<pos.size(); ++i)
196  {
197  Transform3D rMd = pos[i] * rrMd;
198 
199  for (unsigned j=0; j<cloud.size(); ++j)
200  {
201  Vector3D p_r = rMd.coord(cloud[j]);
202  retval.push_back(p_r);
203  }
204  }
205 
206  return retval;
207 }
208 
210 {
211  Vector3D zeroPos;
212  std::vector<Vector3D> coords = mArguments->getRefCoords();
213  if (coords.empty())
214  return zeroPos;
215  Vector3D p0 = coords[0];
216 
217  Vector3D dir = this->getDirection();
218  Vector3D vup = this->getVectorUp();
219  Vector3D scale = this->getScale();
220 
221  zeroPos = this->calculateOrientation(p0, dir, vup, scale).coord(Vector3D(0,0,0));
222 
223  return zeroPos;
224 }
225 
226 std::vector<Vector3D> CustomMetric::getPositions() const
227 {
228  std::vector<Vector3D> retval;
229  std::vector<Vector3D> coords = mArguments->getRefCoords();
230  if (coords.size() < 2)
231  return retval;
232 
233  Vector3D p0 = coords[0];
234  Vector3D p1 = coords[1];
235  Vector3D dir = getDirection();
236  double fullDist = dot(dir, p1-p0);
237 
238  int reps = this->getRepeatCount();
239  for (int i=0; i<reps; ++i)
240  {
241  double dist = mOffsetFromP0 + mRepeatDistance*i;
242  Vector3D p = p0 + dir*dist;
243  retval.push_back(p);
244  }
245 
246  return retval;
247 }
248 
250 {
251  std::vector<Vector3D> coords = mArguments->getRefCoords();
252  if (coords.size() < 2)
253  return 0;
254 
255  Vector3D p0 = coords[0];
256  Vector3D p1 = coords[1];
257  Vector3D dir = getDirection();
258 
259  int reps = 1;
260  if (!similar(mRepeatDistance, 0.0))
261  reps = (dot(p1-p0, dir)-mOffsetFromP0-mOffsetFromP1)/mRepeatDistance + 1;
262  reps = std::min(100, reps);
263  reps = std::max(reps, 1);
264 
265  return reps;
266 }
267 
268 
269 Vector3D CustomMetric::getDirection() const
270 {
271  std::vector<Vector3D> coords = mArguments->getRefCoords();
272  if (coords.size()<2)
273  return Vector3D::UnitZ();
274  Vector3D diff = (coords[1]-coords[0]);
275  if (similar(diff.length(), 0.0))
276  return Vector3D(0,1,0);
277  return diff.normal();
278 }
279 
280 Vector3D CustomMetric::getVectorUp() const
281 {
282  if(mDefineVectorUpMethod == mDefineVectorUpMethods.connectedFrameInP1)
283  {
284  std::vector<Transform3D> transforms = mArguments->getRefFrames();
285  if (transforms.size()<2)
286  return Vector3D::UnitZ();
287 
288  Transform3D rMframe = transforms[1];
289  Vector3D upVector = rMframe.vector(Vector3D(-1,0,0));
290 
291  return upVector;
292  }
293  else if (mDefineVectorUpMethod == mDefineVectorUpMethods.tool)
294  {
295  Transform3D rMt = mSpaceProvider->getActiveToolTipTransform(CoordinateSystem::reference(), true);
296  Vector3D toolUp = -Vector3D::UnitX();
297  return rMt.vector(toolUp);
298  }
299  else
300  {
301  return mDataManager->getOperatingTable().getVectorUp();
302  }
303 }
304 
305 
307 {
308  if (!model)
309  return;
310 
311  if (!this->getTextureFollowTool() || !model->hasTexture())
312  return;
313 
314  // special case:
315  // Project tool position down to the model, then set that position as
316  // the texture x pos.
317 
318  Transform3D rMt = mSpaceProvider->getActiveToolTipTransform(CoordinateSystem::reference());
319  Transform3D rMd = rMrr * model->get_rMd();
320  Vector3D t_r = rMt.coord(Vector3D::Zero());
321  Vector3D td_r = rMt.vector(Vector3D::UnitZ());
322 
323  DoubleBoundingBox3D bb_d = model->boundingBox();
324  Vector3D bl = bb_d.bottomLeft();
325  Vector3D tr = bb_d.topRight();
326  Vector3D c = (bl+tr)/2;
327  Vector3D x_min_r(c[0], bl[1], c[2]);
328  Vector3D x_max_r(c[0], tr[1], c[2]);
329  x_min_r = rMd.coord(x_min_r);
330  x_max_r = rMd.coord(x_max_r);
331 
332  double t_x = dot(t_r, td_r);
333  double bbmin_x = dot(x_min_r, td_r);
334  double bbmax_x = dot(x_max_r, td_r);
335  double range = bbmax_x-bbmin_x;
336  if (similar(range, 0.0))
337  range = 1.0E-6;
338  double s = (t_x-bbmin_x)/range;
339  model->getTextureData().getPositionY()->setValue(s);
340 }
341 
342 Vector3D CustomMetric::getScale() const
343 {
344  if (!mScaleToP1 || !this->getModel() || this->getModel()->getType() == "image")
345  return Vector3D::Ones();
346 
347  DoubleBoundingBox3D bounds = this->getModel()->boundingBox();
348  bounds = transform(this->getModel()->get_rMd(), bounds);
349 
350  std::vector<Vector3D> coords = mArguments->getRefCoords();
351  double height = (coords[1] - coords[0]).length();
352 
353  Vector3D dir = this->getDirection();
354  double p0 = dot(coords[0], dir);
355  double p1 = dot(coords[1], dir);
356 
357  height = p1 - p0;
358  height -= (mOffsetFromP0 + mOffsetFromP1);
359 
360  Vector3D scale(1,
361  height/bounds.range()[1],
362  1);
363  return scale;
364 }
365 
367 {
368  return mDefineVectorUpMethod;
369 }
370 
371 void CustomMetric::setDefineVectorUpMethod(QString defineVectorUpMethod)
372 {
373  mDefineVectorUpMethod = defineVectorUpMethod;
374 }
375 
376 void CustomMetric::setModelUid(QString val)
377 {
378  mModelUid = val;
379  emit propertiesChanged();
380 }
381 
383 {
384  return mModelUid;
385 }
386 
388 {
389  return mDataManager->getData(mModelUid);
390 }
391 
393 {
394  if (mScaleToP1 == val)
395  return;
396  mScaleToP1 = val;
397  emit propertiesChanged();
398 }
399 
401 {
402  return mScaleToP1;
403 }
404 
406 {
407  if (mOffsetFromP0 == val)
408  return;
409  mOffsetFromP0 = val;
410  emit propertiesChanged();
411 }
412 
414 {
415  return mOffsetFromP0;
416 }
417 
419 {
420  if (mOffsetFromP1 == val)
421  return;
422  mOffsetFromP1 = val;
423  emit propertiesChanged();
424 }
425 
427 {
428  return mOffsetFromP1;
429 }
430 
432 {
433  if (mRepeatDistance == val)
434  return;
435  mRepeatDistance = val;
436  emit propertiesChanged();
437 }
438 
440 {
441  return mRepeatDistance;
442 }
443 
445 {
446  if(mShowDistanceMarkers == show)
447  return;
448  mShowDistanceMarkers = show;
449  emit propertiesChanged();
450 }
451 
453 {
454  return mShowDistanceMarkers;
455 }
457 {
458  if (mTranslationOnly == val)
459  return;
460  mTranslationOnly = val;
461  emit propertiesChanged();
462 }
463 
465 {
466  if (mDistanceMarkerVisibility == val)
467  return;
468  mDistanceMarkerVisibility = val;
469  emit propertiesChanged();
470 }
471 
473 {
474  return mDistanceMarkerVisibility;
475 }
476 
478 {
479  return mTranslationOnly;
480 }
481 
483 {
484  if (mTextureFollowTool == val)
485  return;
486  mTextureFollowTool = val;
487  emit propertiesChanged();
488 }
489 
491 {
492  return mTextureFollowTool;
493 }
494 
495 QStringList CustomMetric::DefineVectorUpMethods::getAvailableDefineVectorUpMethods() const
496 {
497  QStringList retval;
498  retval << table;
499  retval << connectedFrameInP1;
500  retval << tool;
501  return retval;
502 }
503 
504 std::map<QString, QString> CustomMetric::DefineVectorUpMethods::getAvailableDefineVectorUpMethodsDisplayNames() const
505 {
506  std::map<QString, QString> names;
507  names[table] = "The operating table";
508  names[connectedFrameInP1] = "The connected frame in p1";
509  names[tool] = "The active tool";
510  return names;
511 }
512 
513 
514 std::vector<Transform3D> CustomMetric::calculateOrientations() const
515 {
516  std::vector<Vector3D> pos = this->getPositions();
517  Vector3D dir = this->getDirection();
518  Vector3D vup = this->getVectorUp();
519  Vector3D scale = this->getScale();
520 
521  std::vector<Transform3D> retval(pos.size());
522  for (unsigned i=0; i<retval.size(); ++i)
523  {
524  if (mTranslationOnly)
525  {
526  retval[i] = createTransformTranslate(pos[i]);
527  }
528  else
529  {
530  retval[i] = this->calculateOrientation(pos[i], dir, vup, scale);
531  }
532  }
533 
534  return retval;
535 }
536 
541 Transform3D CustomMetric::calculateOrientation(Vector3D pos, Vector3D dir, Vector3D vup, Vector3D scale) const
542 {
543  Transform3D R = this->calculateRotation(dir, vup);
544 
545  Transform3D center2DImage = this->calculateTransformTo2DImageCenter();
546 
549  Transform3D M = T*R*S*center2DImage;
550  return M;
551 }
552 
553 Transform3D CustomMetric::calculateTransformTo2DImageCenter() const
554 {
555  Transform3D position2DImage = Transform3D::Identity();
556  if(this->modelIsImage())
557  {
558  DataPtr model = this->getModel();
559  ImagePtr imageModel = boost::dynamic_pointer_cast<Image>(model);
560  vtkImageDataPtr vtkImage = imageModel->getBaseVtkImageData();
561  int xSize = vtkImage->GetExtent()[1] - vtkImage->GetExtent()[0];
562  int ySize = vtkImage->GetExtent()[3] - vtkImage->GetExtent()[2];
563  Eigen::Array3d spacing(vtkImage->GetSpacing());
564 
565  position2DImage = createTransformTranslate(Vector3D(-xSize*spacing[0]/2.0, -ySize*spacing[1]/2.0, 0));
566  }
567  return position2DImage;
568 }
569 
571 {
572  DataPtr model = this->getModel();
573 
574  return (model && model->getType() == "image");
575 }
576 
577 Transform3D CustomMetric::calculateRotation(Vector3D dir, Vector3D vup) const
578 {
579  Transform3D R = Transform3D::Identity();
580  bool directionAlongUp = similar(dot(vup, dir.normal()), 1.0);
581  if (!directionAlongUp)
582  {
583  Vector3D jvec = dir.normal();
584  Vector3D kvec = cross(vup, dir).normal();
585  Vector3D ivec = cross(jvec, kvec).normal();
586  Vector3D center = Vector3D::Zero();
587  R = createTransformIJC(ivec, jvec, center);
588 
589  Transform3D rotateY = cx::createTransformRotateY(M_PI_2);
590  R = R*rotateY;//Let the models X-axis align with patient X-axis
591 
592  if(this->modelIsImage())
593  {
594  Transform3D rotateX = cx::createTransformRotateX(M_PI_2);
595  R = R*rotateX;
596  }
597  }
598  return R;
599 }
600 
601 }
boost::shared_ptr< class SpaceProvider > SpaceProviderPtr
QString qstring_cast(const T &val)
void setOffsetFromP1(double val)
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
Transform3D createTransformRotateY(const double angle)
virtual Transform3D get_rMd() const
Definition: cxData.cpp:107
Vector3D topRight() const
void addXml(QDomNode &dataNode)
adds xml information about the data and its variabels
void setScaleToP1(bool val)
bool getTranslationOnly() const
Transform3D createTransformScale(const Vector3D &scale_)
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
void propertiesChanged()
emitted when one of the metadata properties (uid, name etc) changes
Vector3D getZeroPosition() const
void transformChanged()
emitted when transform is changed
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
void setDefineVectorUpMethod(QString defineVectorUpMethod)
Vector3D bottomLeft() const
void setDistanceMarkerVisibility(double val)
void updateTexture(MeshPtr model, Transform3D rMrr)
virtual void parseXml(QDomNode &dataNode)
Use a XML node to load data.
PatientModelServicePtr mDataManager
Definition: cxDataMetric.h:88
static CoordinateSystem reference()
virtual vtkImageDataPtr getBaseVtkImageData()
Definition: cxImage.cpp:356
SpaceProviderPtr mSpaceProvider
Definition: cxDataMetric.h:89
static DoubleBoundingBox3D fromCloud(std::vector< Vector3D > cloud)
Vector3D cross(const Vector3D &a, const Vector3D &b)
compute cross product of a and b.
Definition: cxVector3D.cpp:62
std::vector< Transform3D > calculateOrientations() const
void setTranslationOnly(bool val)
CustomMetric::DefineVectorUpMethods getDefineVectorUpMethods() const
boost::shared_ptr< class Data > DataPtr
QString getDefineVectorUpMethod() const
Transform3D createTransformIJC(const Vector3D &ivec, const Vector3D &jvec, const Vector3D &center)
virtual QString getType() const
Definition: cxAngleMetric.h:84
bool getShowDistanceMarkers() const
void parseXml(QDomNode &dataNode)
Use a XML node to load data.
double getRepeatDistance() const
A volumetric data set.
Definition: cxImage.h:66
Data class that represents a custom.
void setOffsetFromP0(double val)
boost::shared_ptr< class PatientModelService > PatientModelServicePtr
virtual Vector3D getRefCoord() const
static CustomMetricPtr create(QString uid, QString name, PatientModelServicePtr dataManager, SpaceProviderPtr spaceProvider)
DataPtr getModel() const
Transform3D createTransformTranslate(const Vector3D &translation)
csTOOL_OFFSET
the tool space t with a virtual offset added along the z axis. (to)
double dot(const Vector3D &a, const Vector3D &b)
compute inner product (or dot product) of a and b.
Definition: cxVector3D.cpp:67
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.
bool getTextureFollowTool() const
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
void setModelUid(QString val)
RealScalar length() const
std::vector< Vector3D > getPointCloud() const
virtual DoubleBoundingBox3D boundingBox() const
double getOffsetFromP0() const
void setTextureFollowTool(bool val)
int getRepeatCount() const
void setShowDistanceMarkers(bool show)
Transform3D createTransformRotateX(const double angle)
bool modelIsImage() const
boost::shared_ptr< class Mesh > MeshPtr
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
double getOffsetFromP1() const
double getDistanceMarkerVisibility() const
void setRepeatDistance(double val)
boost::shared_ptr< class CustomMetric > CustomMetricPtr
virtual bool isValid() const
virtual void addXml(QDomNode &dataNode)
adds xml information about the data and its variabels
bool getScaleToP1() const
QString getModelUid() const
Namespace for all CustusX production code.