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