CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxMetricManager.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 "cxMetricManager.h"
13 #include "cxManualTool.h"
14 #include "cxViewGroupData.h"
15 #include "cxTrackingService.h"
16 #include <QFile>
17 #include <QTextStream>
18 #include <QDialog>
19 #include <QPushButton>
20 #include "vtkMNITagPointReader.h"
21 #include "vtkStringArray.h"
22 #include "cxLogger.h"
24 #include "cxPointMetric.h"
25 #include "cxDistanceMetric.h"
26 #include "cxFrameMetric.h"
27 #include "cxToolMetric.h"
28 #include "cxPlaneMetric.h"
29 #include "cxShapedMetric.h"
30 #include "cxCustomMetric.h"
31 #include "cxAngleMetric.h"
32 #include "cxSphereMetric.h"
34 #include "cxDataFactory.h"
35 #include "cxSpaceProvider.h"
36 #include "cxTypeConversions.h"
37 #include "cxPatientModelService.h"
38 #include "cxViewService.h"
39 #include "cxOrderedQDomDocument.h"
40 #include "cxXmlFileHandler.h"
41 #include "cxTime.h"
42 #include "cxErrorObserver.h"
43 #include "cxHelperWidgets.h"
44 #include "cxFileHelpers.h"
45 #include "cxSpaceProperty.h"
46 #include "cxSpaceEditWidget.h"
48 #include "cxFileManagerService.h"
49 
50 namespace cx
51 {
52 
53 MetricManager::MetricManager(ViewServicePtr viewService, PatientModelServicePtr patientModelService, TrackingServicePtr trackingService, SpaceProviderPtr spaceProvider, FileManagerServicePtr filemanager) :
54  QObject(NULL),
55  mViewService(viewService),
56  mPatientModelService(patientModelService),
57  mTrackingService(trackingService),
58  mSpaceProvider(spaceProvider),
59  mFileManager(filemanager)
60 {
61  connect(trackingService.get(), &TrackingService::stateChanged, this, &MetricManager::metricsChanged);
62  connect(patientModelService.get(), SIGNAL(dataAddedOrRemoved()), this, SIGNAL(metricsChanged()));
63 
65  mUserSettings.imageRefs.push_back("");
66  mUserSettings.imageRefs.push_back("");
67 }
68 
70 {
71  DataPtr data = mPatientModelService->getData(uid);
72  DataMetricPtr metric = boost::dynamic_pointer_cast<DataMetric>(data);
73  return metric;
74 }
75 
77 {
78  return this->getAllMetrics().size();
79 }
80 
81 std::vector<DataMetricPtr> MetricManager::getAllMetrics() const
82 {
83  std::vector<DataMetricPtr> retval;
84  std::map<QString, DataPtr> all = mPatientModelService->getDatas();
85  for (std::map<QString, DataPtr>::iterator iter=all.begin(); iter!=all.end(); ++iter)
86  {
87  DataMetricPtr metric = boost::dynamic_pointer_cast<DataMetric>(iter->second);
88  if (metric)
89  retval.push_back(metric);
90  }
91  return retval;
92 }
93 
94 void MetricManager::setSelection(std::set<QString> selection)
95 {
96  mSelection = selection;
97 }
98 
99 void MetricManager::setActiveUid(QString uid)
100 {
101  mActiveLandmark = uid;
102  emit activeMetricChanged();
103 }
104 
106 {
107  DataMetricPtr metric = this->getMetric(uid);
108  if (!metric)
109  return;
110  Vector3D p_r = metric->getRefCoord();;
111  this->setManualToolPosition(p_r);
112 }
113 
114 void MetricManager::setManualToolPosition(Vector3D p_r)
115 {
116  Transform3D rMpr = mPatientModelService->get_rMpr();
117  Vector3D p_pr = rMpr.inv().coord(p_r);
118 
119  // set the picked point as offset tip
120  ToolPtr tool = mTrackingService->getManualTool();
121  Vector3D offset = tool->get_prMt().vector(Vector3D(0, 0, tool->getTooltipOffset()));
122  p_pr -= offset;
123  p_r = rMpr.coord(p_pr);
124 
125  // TODO set center here will not do: must handle
126  mPatientModelService->setCenter(p_r);
127  Vector3D p0_pr = tool->get_prMt().coord(Vector3D(0, 0, 0));
128  tool->set_prMt(createTransformTranslate(p_pr - p0_pr) * tool->get_prMt());
129 }
130 
131 PointMetricPtr MetricManager::addPoint(Vector3D point, CoordinateSystem space, QString uid, QColor color)
132 {
133  PointMetricPtr p1 = mPatientModelService->createSpecificData<PointMetric>(uid);
134  p1->get_rMd_History()->setParentSpace("reference");
135  p1->setSpace(space);
136  p1->setCoordinate(point);
137  p1->setColor(color);
138  mPatientModelService->insertData(p1);
139 
140  mViewService->getGroup(0)->addData(p1->getUid());
141  this->setActiveUid(p1->getUid());
142 
143  return p1;
144 }
145 
147 {
148  DistanceMetricPtr d0 = mPatientModelService->createSpecificData<DistanceMetric>(uid);
149  d0->get_rMd_History()->setParentSpace("reference");
150 
151  std::vector<DataPtr> args = this->getSpecifiedNumberOfValidArguments(d0->getArguments());
152  for (unsigned i=0; i<args.size(); ++i)
153  d0->getArguments()->set(i, args[i]);
154 
155  this->installNewMetric(d0);
156 
157  return d0;
158 }
159 
161 {
162  this->addPointInDefaultPosition();
163 }
164 
165 PointMetricPtr MetricManager::addPointInDefaultPosition()
166 {
168  QColor color = QColor(240, 170, 255, 255);
169  Vector3D p_ref = mSpaceProvider->getActiveToolTipPoint(ref, true);
170 
171  DataPtr data = mPatientModelService->getData(mActiveLandmark);
172  if(!data)
173 
174  return this->addPoint(p_ref, ref,"point%1", color);
175 
176  PointMetricPtr pointMetric = boost::dynamic_pointer_cast<PointMetric>(data);
177  if(pointMetric)
178  {
179  ref = pointMetric->getSpace();
180  p_ref = mSpaceProvider->getActiveToolTipPoint(ref, true);
181  }
182 
183  DataMetricPtr metric = boost::dynamic_pointer_cast<DataMetric>(data);
184  if(metric)
185  color = metric->getColor();
186 
187 
188 
189  return this->addPoint(p_ref, ref,"point%1", color);
190 }
191 
193 {
194  FrameMetricPtr frame = mPatientModelService->createSpecificData<FrameMetric>("frame%1");
195  frame->get_rMd_History()->setParentSpace("reference");
196 
198  Transform3D rMt = mSpaceProvider->getActiveToolTipTransform(ref, true);
199 
200  frame->setSpace(ref);
201  frame->setFrame(rMt);
202 
203  this->installNewMetric(frame);
204 }
205 
207 {
208  ToolMetricPtr frame = mPatientModelService->createSpecificData<ToolMetric>("tool%1");
209  frame->get_rMd_History()->setParentSpace("reference");
210 
212  Transform3D rMt = mSpaceProvider->getActiveToolTipTransform(ref, true);
213 
214  frame->setSpace(ref);
215  frame->setFrame(rMt);
216  frame->setToolName(mTrackingService->getActiveTool()->getName());
217  frame->setToolOffset(mTrackingService->getActiveTool()->getTooltipOffset());
218 
219  this->installNewMetric(frame);
220 }
221 
223 {
224  PlaneMetricPtr p1 = mPatientModelService->createSpecificData<PlaneMetric>("plane%1");
225  p1->get_rMd_History()->setParentSpace("reference");
226 
227  std::vector<DataPtr> args = this->getSpecifiedNumberOfValidArguments(p1->getArguments());
228  for (unsigned i=0; i<args.size(); ++i)
229  p1->getArguments()->set(i, args[i]);
230 
231  this->installNewMetric(p1);
232 }
233 
238 std::vector<DataPtr> MetricManager::refinePointArguments(std::vector<DataPtr> args, unsigned argNo)
239 {
240  // erase non-selected arguments if we have more than enough
241  //std::set<QString> selectedUids = this->getSelectedUids();
242  for (unsigned i=0; i<args.size();)
243  {
244  if (args.size() <= argNo)
245  break;
246  if (!mSelection.count(args[i]->getUid()))
247  args.erase(args.begin()+i);
248  else
249  ++i;
250  }
251 
252  while (args.size() > argNo)
253  args.erase(args.begin());
254 
255  while (args.size() < argNo)
256  {
257  PointMetricPtr p0 = this->addPointInDefaultPosition();
258  args.push_back(p0);
259  }
260 
261  return args;
262 }
263 
265 {
266  RegionOfInterestMetricPtr d0 = mPatientModelService->createSpecificData<RegionOfInterestMetric>("roi%1");
267  d0->get_rMd_History()->setParentSpace("reference");
268  this->installNewMetric(d0);
269 }
270 
272 {
273  this->addDistance();
274 }
275 
277 {
278  AngleMetricPtr d0 = mPatientModelService->createSpecificData<AngleMetric>("angle%1");
279  d0->get_rMd_History()->setParentSpace("reference");
280 
281  std::vector<DataPtr> args = this->getSpecifiedNumberOfValidArguments(d0->getArguments(), 3);
282  d0->getArguments()->set(0, args[0]);
283  d0->getArguments()->set(1, args[1]);
284  d0->getArguments()->set(2, args[1]);
285  d0->getArguments()->set(3, args[2]);
286 
287  this->installNewMetric(d0);
288 }
289 
290 std::vector<DataPtr> MetricManager::getSpecifiedNumberOfValidArguments(MetricReferenceArgumentListPtr arguments, int numberOfRequiredArguments)
291 {
292  // first try to reuse existing points as distance arguments, otherwise create new ones.
293 
294  std::vector<DataMetricPtr> metrics = this->getAllMetrics();
295 
296  std::vector<DataPtr> args;
297  for (unsigned i=0; i<metrics.size(); ++i)
298  {
299  if (arguments->validArgument(metrics[i]))
300  args.push_back(metrics[i]);
301  }
302 
303  if (numberOfRequiredArguments<0)
304  numberOfRequiredArguments = arguments->getCount();
305  args = this->refinePointArguments(args, numberOfRequiredArguments);
306 
307  return args;
308 }
309 
311 {
312  SphereMetricPtr d0 = mPatientModelService->createSpecificData<SphereMetric>("sphere%1");
313  d0->get_rMd_History()->setParentSpace("reference");
314  std::vector<DataPtr> args = this->getSpecifiedNumberOfValidArguments(d0->getArguments());
315  d0->getArguments()->set(0, args[0]);
316 
317  this->installNewMetric(d0);
318 }
319 
321 {
322  DonutMetricPtr d0 = mPatientModelService->createSpecificData<DonutMetric>("donut%1");
323  d0->get_rMd_History()->setParentSpace("reference");
324  std::vector<DataPtr> args = this->getSpecifiedNumberOfValidArguments(d0->getArguments());
325  d0->getArguments()->set(0, args[0]);
326  d0->getArguments()->set(1, args[1]);
327 
328  this->installNewMetric(d0);
329 }
330 
332 {
333  CustomMetricPtr d0 = mPatientModelService->createSpecificData<CustomMetric>("Custom%1");
334  d0->get_rMd_History()->setParentSpace("reference");
335  std::vector<DataPtr> args = this->getSpecifiedNumberOfValidArguments(d0->getArguments());
336  d0->getArguments()->set(0, args[0]);
337  d0->getArguments()->set(1, args[1]);
338 
339  this->installNewMetric(d0);
340 }
341 
342 void MetricManager::installNewMetric(DataMetricPtr metric)
343 {
344  DataMetricPtr prevMetric = this->getMetric(mActiveLandmark);
345  if(prevMetric)
346  {
347  metric->setColor(prevMetric->getColor());
348  }
349 
350  mPatientModelService->insertData(metric);
351  this->setActiveUid(metric->getUid());
352  mViewService->getGroup(0)->addData(metric->getUid());
353 }
354 
356 {
357  ToolPtr refTool = mTrackingService->getReferenceTool();
358  if(!refTool) // we only load reference points from reference tools
359  {
360  reportDebug("No reference tool, cannot load reference points into the pointsampler");
361  return;
362  }
363 
364  std::map<QString, Vector3D> referencePoints_s = refTool->getReferencePoints();
365  if(referencePoints_s.empty())
366  {
367  reportWarning("No referenceppoints in reference tool "+refTool->getName());
368  return;
369  }
370 
372  CoordinateSystem sensor = mSpaceProvider->getS(refTool);
373 
374  std::map<QString, Vector3D>::iterator it = referencePoints_s.begin();
375  for(; it != referencePoints_s.end(); ++it)
376  {
377  Vector3D P_ref = mSpaceProvider->get_toMfrom(sensor, ref).coord(it->second);
378  this->addPoint(P_ref, CoordinateSystem(csREF), "ref%1");
379  }
380 }
381 
383 {
384  OrderedQDomDocument orderedDoc;
385  QDomDocument& doc = orderedDoc.doc();
386  doc.appendChild(doc.createProcessingInstruction("xml version =", "'1.0'"));
387  QDomElement patientNode = doc.createElement("patient");
388  doc.appendChild(patientNode);
389  QDomElement managersNode = doc.createElement("managers");
390  patientNode.appendChild(managersNode);
391  QDomElement datamanagerNode = doc.createElement("datamanager");
392  managersNode.appendChild(datamanagerNode);
393  std::map<QString, DataPtr> dataMap = mPatientModelService->getDatas();
394 
395  std::map<QString, DataPtr>::iterator iter;
396  for (iter = dataMap.begin(); iter != dataMap.end(); ++iter)
397  {
398  DataMetricPtr metric = boost::dynamic_pointer_cast<DataMetric>(iter->second);
399  if(metric)
400  {
401  QDomElement dataNode = doc.createElement("data");
402  metric->addXml(dataNode);
403  datamanagerNode.appendChild(dataNode);
404  }
405  }
406 
407  XmlFileHandler::writeXmlFile(doc, filename);
408 }
409 
410 void MetricManager::resolveUnknownParentSpacesForPointMetrics(QDomNode dataNode, std::map<QString, QString> mapping_of_unknown_to_known_spaces, DataPtr data)
411 {
412  QString uid = data->getUid();
413  PointMetricPtr point_metric = boost::static_pointer_cast<PointMetric>(data);
414  if(!point_metric)
415  return;
416 
417  QString string_space = dataNode.toElement().attribute("space");
418  CoordinateSystem parentSpace = CoordinateSystem::fromString(string_space);
419  bool need_parent = parentSpace.isValid() && (parentSpace.mId == csDATA);
420  bool parent_found = mPatientModelService->getData(parentSpace.mRefObject) != DataPtr();
421  if(need_parent && !parent_found)
422  {
423  if(mapping_of_unknown_to_known_spaces.find(string_space) == mapping_of_unknown_to_known_spaces.end())
424  {
425  SpacePropertyPtr space_property;
426  space_property = SpaceProperty::initialize("selectSpace",
427  "Space",
428  "Select parent coordinate system of metric with uid: "+uid);
429  space_property->setSpaceProvider(mSpaceProvider);
430  QWidget* widget = new QWidget;
431  widget->setFocusPolicy(Qt::StrongFocus); // needed for help system: focus is used to display help text
432  QVBoxLayout *layout = new QVBoxLayout();
433  layout->addWidget(new QLabel("Select parent space for Point metric: "+uid+"."));
434  layout->addWidget(new SpaceEditWidget(widget, space_property));
435  QDialog dialog;
436  dialog.setWindowTitle("Space "+string_space+" does not exist.");
437  dialog.setLayout(layout);
438  QPushButton *okButton = new QPushButton(tr("Ok"));
439  layout->addWidget(okButton);
440  connect(okButton, &QAbstractButton::clicked, &dialog, &QWidget::close);
441  dialog.exec();
442  CX_LOG_DEBUG() << "New space is now: " << space_property->getValue().mId << " " << space_property->getValue().mRefObject;
443  CoordinateSystem new_parentspace = space_property->getValue();
444  mapping_of_unknown_to_known_spaces[string_space] = new_parentspace.toString();
445  }
446  parentSpace = CoordinateSystem::fromString(mapping_of_unknown_to_known_spaces[string_space]);
447  point_metric->setSpace(parentSpace);
448  point_metric->setCoordinate(Vector3D::fromString(dataNode.toElement().attribute("coord")));
449  dataNode.toElement().setAttribute("space", parentSpace.toString());
450  }
451 }
452 
454 {
455  QDomDocument xml = XmlFileHandler::readXmlFile(filename);
456  QDomElement patientNode = xml.documentElement();
457 
458  std::map<DataPtr, QDomNode> datanodes;
459 
460  QDomNode managersNode = patientNode.firstChildElement("managers");
461  QDomNode datamanagerNode = managersNode.firstChildElement("datamanager");
462  QDomNode dataNode = datamanagerNode.firstChildElement("data");
463 
464 
465  std::map<QString, QString> mapping_of_unknown_to_known_spaces;
466 
467  for (; !dataNode.isNull(); dataNode = dataNode.nextSibling())
468  {
469  QDomNamedNodeMap attributes = dataNode.attributes();
470  QDomNode typeAttribute = attributes.namedItem("type");
471  bool isMetric = false;
472  if(typeAttribute.isAttr())
473  {
474  isMetric = typeAttribute.toAttr().value().contains("Metric");
475  }
476 
477  if (dataNode.nodeName() == "data" && isMetric)
478  {
479 
480  QString uid = dataNode.toElement().attribute("uid");
481  if(mPatientModelService->getData(uid))
482  {
483  QString name = dataNode.toElement().attribute("name");
484  reportWarning("Metric: " + name + ", is already in the model with Uid: " + uid + ". Import skipped.");
485  continue;
486  }
487 
488  DataPtr data = this->loadDataFromXMLNode(dataNode.toElement());
489  if (data)
490  datanodes[data] = dataNode.toElement();
491 
492  //If point metrics space is uknown to the system, user needs to select a new parent -> POPUP DIALOG
493  this->resolveUnknownParentSpacesForPointMetrics(dataNode, mapping_of_unknown_to_known_spaces, data);
494  }
495  }
496 
497  // parse xml data separately: we want to first load all data
498  // because there might be interdependencies (cx::DistanceMetric)
499  for (std::map<DataPtr, QDomNode>::iterator iter = datanodes.begin(); iter != datanodes.end(); ++iter)
500  {
501  iter->first->parseXml(iter->second);
502  }
503 }
504 
505 
506 
507 
508 DataPtr MetricManager::loadDataFromXMLNode(QDomElement node)
509 {
510  QString uid = node.toElement().attribute("uid");
511  QString name = node.toElement().attribute("name");
512  QString type = node.toElement().attribute("type");
513 
514  return this->createData(type, uid, name);
515 
516 }
517 
518 DataPtr MetricManager::createData(QString type, QString uid, QString name)
519 {
520  DataPtr data = mPatientModelService->createData(type, uid, name);
521  if (!data)
522  {
523  reportError(QString("Not able to create metric with name: %1, uid: %2, type: %3").arg(name).arg(uid).arg(type));
524  return DataPtr();
525  }
526 
527  if (!name.isEmpty())
528  data->setName(name);
529 
530  mPatientModelService->insertData(data);
531 
532  return data;
533 
534 }
535 
536 
537 } // namespace cx
538 
539 
boost::shared_ptr< class SpaceProvider > SpaceProviderPtr
static void writeXmlFile(QDomDocument &doc, QString &filename)
pcsRAS
Right-Anterior-Superior, used by Slicer3D, ITK-Snap, nifti, MINC.
boost::shared_ptr< class FileManagerService > FileManagerServicePtr
MetricManager(ViewServicePtr viewService, PatientModelServicePtr patientModelService, TrackingServicePtr trackingService, SpaceProviderPtr spaceProvider, FileManagerServicePtr filemanager)
void reportError(QString msg)
Definition: cxLogger.cpp:71
void exportMetricsToXMLFile(QString &filename)
boost::shared_ptr< class DistanceMetric > DistanceMetricPtr
void addXml(QDomNode &dataNode)
adds xml information about the data and its variabels
Composite widget for string selection.
boost::shared_ptr< class FrameMetric > FrameMetricPtr
Definition: cxFrameMetric.h:23
boost::shared_ptr< class ToolMetric > ToolMetricPtr
Definition: cxToolMetric.h:24
boost::shared_ptr< class DonutMetric > DonutMetricPtr
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
boost::shared_ptr< DataMetric > DataMetricPtr
Definition: cxDataMetric.h:73
boost::shared_ptr< class TrackingService > TrackingServicePtr
static SpacePropertyPtr initialize(const QString &uid, QString name, QString help, Space value=Space(), std::vector< Space > range=std::vector< Space >(), QDomNode root=QDomNode())
boost::shared_ptr< class SpaceProperty > SpacePropertyPtr
csREF
the data reference space (r) using LPS (left-posterior-superior) coordinates.
Definition: cxDefinitions.h:90
boost::shared_ptr< class SphereMetric > SphereMetricPtr
DistanceMetricPtr addDistance(QString uid="distance%1")
Data class that represents a single frame (transform).
Definition: cxFrameMetric.h:33
boost::shared_ptr< class ViewService > ViewServicePtr
Data class representing a plane.
Definition: cxPlaneMetric.h:48
static CoordinateSystem reference()
boost::shared_ptr< class AngleMetric > AngleMetricPtr
Definition: cxAngleMetric.h:33
csDATA
a datas space (d)
Definition: cxDefinitions.h:90
COORDINATE_SYSTEM mId
the type of coordinate system
Data class that represents a donut.
boost::shared_ptr< class PlaneMetric > PlaneMetricPtr
Definition: cxPlaneMetric.h:34
boost::shared_ptr< class Data > DataPtr
void setSelection(std::set< QString > selection)
virtual RegistrationHistoryPtr get_rMd_History()
Definition: cxData.cpp:91
void reportWarning(QString msg)
Definition: cxLogger.cpp:70
Data class that represents a custom.
ImportMNIuserSettings mUserSettings
Data class that represents a donut.
boost::shared_ptr< class PatientModelService > PatientModelServicePtr
boost::shared_ptr< class MetricReferenceArgumentList > MetricReferenceArgumentListPtr
DataMetricPtr getMetric(QString uid)
Transform3D createTransformTranslate(const Vector3D &translation)
Identification of a Coordinate system.
QColor getColor()
Data class that represents a distance between two points, or a point and a plane. ...
boost::shared_ptr< class RegionOfInterestMetric > RegionOfInterestMetricPtr
QString mRefObject
for tool, sensor and data we need a object uid to define the coordinate system
Data class that represents a single point.
Definition: cxPointMetric.h:42
void importMetricsFromXMLFile(QString &filename)
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:42
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
static QDomDocument readXmlFile(QString &filename)
PointMetricPtr addPoint(Vector3D point, CoordinateSystem space=CoordinateSystem(csREF), QString uid="point%1", QColor color=QColor(240, 170, 255, 255))
void activeMetricChanged()
void addDistanceButtonClickedSlot()
int getNumberOfMetrics() const
Base class for all Data Metrics.
Definition: cxDataMetric.h:43
boost::shared_ptr< class CustomMetric > CustomMetricPtr
Data class that represents an angle between two lines.
Definition: cxAngleMetric.h:46
void moveToMetric(QString uid)
static CoordinateSystem fromString(QString text)
void reportDebug(QString msg)
Definition: cxLogger.cpp:68
void setActiveUid(QString uid)
Namespace for all CustusX production code.
boost::shared_ptr< class PointMetric > PointMetricPtr
boost::shared_ptr< class Tool > ToolPtr