1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
|
/*****************************************************************************
* $CAMITK_LICENCE_BEGIN$
*
* CamiTK - Computer Assisted Medical Intervention ToolKit
* (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
*
* Visit http://camitk.imag.fr for more information
*
* This file is part of CamiTK.
*
* CamiTK is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* CamiTK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with CamiTK. If not, see <http://www.gnu.org/licenses/>.
*
* $CAMITK_LICENCE_END$
****************************************************************************/
#ifndef TRANSFORMATIONMANAGER_H
#define TRANSFORMATIONMANAGER_H
// -- Core stuff
#include "CamiTKAPI.h"
#include "FrameOfReference.h"
#include "Transformation.h"
// -- QT stuff
#include <QHash>
#include <QString>
#include <QVector>
#include <QPalette>
#include <QVariant>
#include <vtkSmartPointer.h>
#include <vtkTransform.h>
#include <memory>
#include <unordered_set>
namespace camitk {
class Transformation;
class FrameOfReference;
class Application;
/**
* @brief TransformationManager manages frames of reference and transformations for a CamiTK Application
*
* This class is the entry point to using FrameOfReference and Transformation system.
*
* Every Component that is displayable contains data which are located in space using coordinates.
* But two Components may not use the same origin in space, or the same axes.
*
* To manage that, the notion of Frame specifies an origin and axes, this is modeled by camitk::FrameOfReference.
* Each component has a FrameOfReference accessed by Component::getFrame()
*
* Two Components may share a common Frame, for example two meshes of two organs computed from the same image.
* In this case both components' getFrame() should return the same FrameOfReference.
*
* TransformationManager stores and manages all the FrameOfReference objects used in a CamiTK Application.
*
* When you need to display two Components, or to apply an Action that uses multiple Components,
* it is necessary to be able to transform the coordinates of one Component's data to another.
*
* These geometrical transformations are stored in the camitk::Transformation class.
* A Transformation stores the frame of origin, the frame of destination and the geometrical
* transformation itself.
* Currently it supports linear transformations (represented in a 4x4 homogeneous matrices).
*
* All Transformation objects are also stored and managed in the TransformationManager.
* The TransformationManager provides and manages a specific and unique "world frame".
* This is the frame used by VTK and the default for 3D viewers. Having a world frame
* simplifies the usage of frames.
*
* Alongside Component, Viewers also have a FrameOfReference, which determines the
* "point of view" they are using for the visualization.
* More precisely, it is used to set the camera and orient the vtkActors.
* The default frame of the 2D and 3D viewers is the world frame.
* Their is only one TransformationManager (all public methods are static).
*
* \note on shared and raw pointers
*
* Transformation and Frames can be manipulated locally using their raw pointer.
* Use the shared pointer only if you need to own the object as well, that if the Transformation
* or Frame is part of your class members and need to be kept alive for your code to work.
* Using a shared pointer ensures that the Frame or Transformation won't be removed from the system
* by the TransformationManager.
* If you only need to access or modify information on a frame or transformation, only use the raw pointer.
*
* \section TransformationManager_API
*
* The TransformationManager provides the following utilities:
* - addFrameOfReference(...) methods add FrameOfReference objects
* - addTransformation(...) methods add Transformation objects
* - getTransformationOwnership(..) methods returns the shared_ptr to a Transformation
* - getTransformation(...) method computes a Transformation between two Frames, if any path exists between them
* - updateTransformation(...) methods modify the transformation matrix values
*
* Two methods are provided for the manipulation of world frame:
* - getWorldFrame() returns the unique world frame, to which all frames should have a path to (composed of 1 or more transformations).
* - ensurePathToWorld() to add a default identity Transformation between the provided Frame and the worldFrame if no Transformation was defined.
*
* \section Transformation
*
* A Transformation can either be:
* - directly defined by the user using addTransformation() -> it holds a user defined 4x4 matrix. It has 0 sources.
* - an inverse of another Transformation. Inverse Transformation are automatically generated by the
* TransformationManager. It therefore has 1 source (the direct Transformation it is the inverse of).
* - a composite transformation defined automatically by the TransformationManager to pass from one source
* to another one over more than one frame. It is composed by a list of other transformations.
* It therefore has more than one sources.
*
* Information about a Transformation t can be obtained from the TransformationManager using:
* - hasSources(t) and getSources(t)
* - isCompositeTransformation(t)
* - isInverseTransformation(t)
* - isDefaultIdentityToWorld(t)
*
* There is three cases where TransformationManager will automatically create a transformation:
* - when a linear transformation is added using addTransformation(), the inverse transformation is automatically generated and stored in the system. The new transformation will therefore only have 1 source.
* - when getTransformation() is called and finds a new path of transformations between the two given frames, it will generate a new composed transformation (for optimization) from those transformations. The new transformation will therefore have all those transformations as sources.
* - when ensurePathToWorld(f) is called, and no path can be found between f and the world frame, then a new identity transformation from f to world frame is created. This new transformation has no source (note that TransformationManager will also create its inverse, which has 1 source). Use isDefaultIdentityToWorld() to check if a transformation was generated this way.
*
* Note that TransformationManager always generates an inverse transformation for any existing linear transformation if it does not exist already.
*
* \section Transformation_Sources
*
* The lists of Transformation sources are managed by the TransformationManager.
* Sources are the transformations that are used (composed) to compute a Transformation t.
* If any of the sources are modified, t is guaranteed to reflect this update, i.e., it is recomputed from its sources.
* If t has only one source, this means t is the inverse of this source.
*
* @see hasSources() getSources()
*
* \section FrameOfReference
* A FrameOfReference represents a specific system of coordinates in which component data's coordinates are expressed.
*
* It can have a name, a description, and anatomical labels associated to its axes: for example, the X axis
* may have label L on the lower values, and R on the higher values (for Left/Right anatomical orientation).
* You can add new Frames of reference using addFrameOfReference methods when creating a new Component (even though
* Component constructor creates a default one for you), or if you need to manage multiple frames in your component
* (e.g. for an articulated robot).
*
* If you need to get ownership of a specific FrameOfReference (e.g. you want your Component to store the same Frame
* as another Component), use getFrameOfReferenceOwnership()
*
* To edit anatomical information, name or description, refer to FrameOfReference class.
*
*
* \section Transformation_Path_management
*
* When you need a Transformation from one Frame to another, the method to call is getTransformation()
*
* This method first looks if a Transformation was added using addTransformation between those Frames,
* then if there is already a cached composite Transformation linking both Frames,
* and finally, it checks whether there is a path in the graph of Frames and Transformations
* linking those Frames using intermediary Frames.
*
* Private methods hasPath() and getPath() are used to search the graph for a suitable path.
* If there is one, a new cached composite Transformation is stored (it combines the path of
* Transformations into one). If there is no path, these methods return nullptr.
*
* When the user wants to ensure that a specific Frame has a Transformation to
* the WorldFrame, she/he should call ensurePathToWorld(). This will create a default identity
* Transformation to the WorldFrame if there is no existing path between the Frame and the WorldFrame.
*
* All default identity Transformations are marked, so that if a new Transformation is
* added using addTransformation, these Transformations can be automatically removed. This is needed
* to avoid creation of multiple path between Frames (there will therefore never be any cycle in
* the Frame/Transformation graph).
*
* \section TransformationMemoryManagement
*
* As FrameOfReference and Transformation constructors are private, all Frames and Transformations
* must be created through the TransformationManager.
*
* Internally, Transformation and FrameOfReference objects are stored using std::shared_ptr
*
* This means that ownership of these objects is shared between the TransformationManager and custom
* objects used in CamiTK such as Component (which owns its FrameOfReference), ImageComponent (which also
* owns its internal Transformation from raw to main).
*
* Most methods of this class return or use raw pointers, meaning they do not return or get ownership
* of the FrameOfReference or Transformation object. The raw pointers are meant to be used for immediate
* processing (e.g. getting the name of a Frame, transforming the coordinates of a point using a Transformation)
* but not to store the pointer.
* If you need to store the object, you must use getFrameOfReferenceOwnership() and getTransformationOwnership()
* This makes explicit who is owning Transformations and Frames.
*
* Note that you may not get ownership of a composite Transformation (computed from others) or
* a default Transformation, as those must be removable at all times.
*
* TransformationManager may delete any Transformation that is not owned outside its internal data structure
* (which mean they are not used anymore apart from internally).
* @see cleanupFramesAndTransformation(), removeDefaultPaths(), removeTransformation().
*
* To determine whether a Transformation or Frame is owned outside the TransformationManager, std::shared_ptr usage
* counter is used.
*
* \section TransformationManager_Usage "Using TransformationManager in your extensions: TransformationManager use cases"
*
* Most common use cases for CamiTK extension developers:
* - adding a new Component: use the default Component constructor which creates a new default FrameOfReference for the Component
* - adding a new Component created from another one:
* - if the new Component is using the same Frame, just set its Frame to the original component's frame using InterfaceFrame::setFrameFrom()
* - if you need to ensure a Component has its own independent frame, use InterfaceFrame::resetFrame()
* - Special cases for ImageComponent: ImageComponents have a data frame and a main transformation (from their data to their main frame)
* that needs to be taken care of. Be aware that ImageComponent::setFrameFrom() and ImageComponent::resetFrame() are reimplemented
* to take care of the data frame and main transformation.
* For instance:
* - your action creates an image outImage from an image inImage, just call `out->setFrameFrom(in)`
* - your action creates an mesh outMesh from an image inImage, call
* `outMesh->setFrame(TransformationManager::getFrameOfReferenceOwnership(inImage->getDataFrame()));`
* as the mesh is computed from the image data, the mesh is defined in the image component data frame
*
* - Registering a Component to another: for example, compute the registration between two meshes, then use addTransformation() between the frames of the two meshes.
* @warning: if there is already a Transformation path between the meshes and the world frame is not in this path, addTransformation() will return nullptr. This means that computed transformation (for example after previous registration) already gives a transformation between the two meshes. If you want to update previous registration, you should use updateTransformation() instead. If updateTransformation() also returns nullptr, this means you are trying to create a cycle in the transformation graph.
* - You can use multiple Frames and Transformation inside your own Component, use the TransformationExplorer to check if it is working as expected.
*
* In the case of an articulated robot, each part may have its own FrameOfReference, and each articulation its own Transformation. You can use
* methods addFrameOfReference, addTransformation, updateTransformation to create and update frames and transformations in your Action and Component extensions.
*
*/
class CAMITK_API TransformationManager {
public:
/// get current state as a QString
static QString toString();
///////////////////////////////////////////////////////////////////////
///@{ FrameOfReference Management
/**
* Get a FrameOfReference from its UUID
*/
static std::shared_ptr<FrameOfReference> getFrameOfReferenceOwnership(const QUuid& uuid);
/**
* Get the shared_ptr that owns the given FrameOfReference.
* This should only be used to take shared ownership of the given FrameOfReference.
*
* @return nullptr if the given FrameOfReference is unknown to the TransformationManager
*/
static std::shared_ptr<FrameOfReference> getFrameOfReferenceOwnership(const FrameOfReference*);
/**
* Add a FrameOfReference with a name and description
* This is the standard way to create a new FrameOfReference
*
* @return the corresponding shared_ptr (save it to keep ownership, ignore if ownership is not needed)
*/
static std::shared_ptr<FrameOfReference> addFrameOfReference(QString name, QString description = "");
/**
* Add a copy of the provided FrameOfReference (with a different UUID)
*
* @return the corresponding shared_ptr (save it to keep ownership, ignore if ownership is not needed)
*/
static std::shared_ptr<FrameOfReference> addFrameOfReference(const FrameOfReference&);
/**
* Get a list of all stored FrameOfReference
*/
static QVector<FrameOfReference*> getFramesOfReference();
/// @}
///////////////////////////////////////////////////////////////////////
///@{ Transformation Management
/**
* returns true if there a path of transformations between two frames of reference 'from' and 'to'
* or if `from` equals `to`.
*
* This will search for a path in the graph of frames/transformations.
*/
static bool hasPath(const FrameOfReference* from, const FrameOfReference* to);
/**
* Is the transformation a default one ?
* This means that it was created as Identity by default and might be replaced it another Transformation is set
* It is usually a Transformation which destination is worldFrame
*/
static bool isDefaultIdentityToWorld(const Transformation*);
/**
* Get a transformation if it exists or compute it if a path exists between the frames.
*/
static Transformation* getTransformation(const FrameOfReference* from, const FrameOfReference* to);
/**
* Get the shared_ptr that owns the given Transformation.
* This should only be used to take shared ownership of the given Transformation.
* @warning You cannot take ownership of a Composite or a default identity to world transformation.
*
* @warning use getTransformation() if you don't need to take ownership of a Transformation.
*
* @return nullptr if the given Transformation is unknown to the TransformationManager or
* is a composite or is a default identity to world.
*/
static std::shared_ptr<Transformation> getTransformationOwnership(const Transformation*);
/**
* Get the shared_ptr that owns the Transformation with given UUID.
* This should only be used to take shared ownership of the given Transformation.
* @warning You cannot take ownership of a Composite or a default identity to world transformation.
*
* @warning use getTransformation() if you don't need to take ownership of a Transformation.
*
* @return nullptr if the given Transformation is unknown to the TransformationManager or
* is a composite or is a default identity to world.
*/
static std::shared_ptr<Transformation> getTransformationOwnership(const QUuid& uuid);
/**
* Get the shared_ptr that owns the given Transformation between from and to.
* This should only be used to take shared ownership of the given Transformation.
* @warning You cannot take ownership of a Composite or a default identity to world transformation.
*
* @warning use getTransformation() if you don't need to take ownership of a Transformation.
*
* @return nullptr if the given Transformation is unknown to the TransformationManager or
* is a composite or is a default identity to world.
*/
static std::shared_ptr<Transformation> getTransformationOwnership(const FrameOfReference* from, const FrameOfReference* to);
/**
* Make sure there is a Transformation from the given Frame to the world Frame.
* This will create an identity (default) transformation if no path to world already exists.
*/
static void ensurePathToWorld(const FrameOfReference* frame);
/**
* Call this method when you prefer (for visualization purpose only) to have a direct link to world from
* the given frame instead of any other path. If another path to world exists from the frame and include
* a default identity transformation to world, it will be delete in favor of a new default identity
* transformation that directly links the given frame to world.
* @return false if there is a path to world that contains no default identity transformation
*/
static bool preferredDefaultIdentityToWorldLink(const FrameOfReference* frame);
/**
* @brief Get the WorldFrame
*
* This is the Frame that links all Frames so that there is a common space
* If a Component's frame is not linked by a Transformation to any other Frame,
* a default identity Transformation should be created between it and this worldFrame
*
* This is done by calling ensurePathToWorld
*/
static const FrameOfReference* getWorldFrame();
/**
* Returns the list of direct transformations, that is the transformations that are independent of any other. The inverse transformations (which have one source) and composite transformations (which have 2 or more sources) are not included.
*/
static QVector<Transformation*> getDirectTransformations();
/**
* Returns the list of all transformations managed in the system, independents or not.
*/
static QVector<Transformation*> getTransformations();
/// @brief Create and register a new Transformation from a QVariant (usually from a JSON representation in a .camitk file) if there is no corresponding transformation (uuid and from/to path)
/// @return a pointer to the new Transformation or nullptr if a transformation with the same UUid or the same from/to frames already exist in the system (use getTransformationOwnership() or getTransformation(..) instead)
static std::shared_ptr<Transformation> addTransformation(const QVariant&);
/// @brief Create and register a new identity Transformation between two frames if
/// there is no existing transformation between those frames.
/// @return a pointer to the new Transformation or nullptr if a transformation between those frames already exist (use getTransformation())
static std::shared_ptr<Transformation> addTransformation(const FrameOfReference* from, const FrameOfReference* to);
/// @copydoc addTransformation(const FrameOfReference*,const FrameOfReference*)
static std::shared_ptr<Transformation> addTransformation(const std::shared_ptr<FrameOfReference>& from, const std::shared_ptr<FrameOfReference>& to);
/// @brief Create and register a new Transformation between two frames and sets the transformation to the provided transform if
/// there is no existing transformation between those frames.
/// @param vtkTr The vtkTransform pointer that will be stored in the Transformation.
/// @warning if the given vtkTransform is updated later, it will affect the Transformation!
/// @return a pointer to the new Transformation or nullptr if a transformation between those frames already exist (use getTransformation())
static std::shared_ptr<Transformation> addTransformation(const FrameOfReference* from, const FrameOfReference* to, vtkSmartPointer<vtkTransform> vtkTr);
/// @copydoc addTransformation(const FrameOfReference*,const FrameOfReference*,vtkSmartPointer<vtkTransform>)
static std::shared_ptr<Transformation> addTransformation(const std::shared_ptr<FrameOfReference>& from, const std::shared_ptr<FrameOfReference>& to, vtkSmartPointer<vtkTransform> vtkTr);
/// @brief Create and register a new Transformation between two frames, copying the content of the provided matrix if there is no existing transformation between those frames.
/// A deep copy of the given matrix is used to initialize the new Transformation
/// @return a pointer to the new Transformation or nullptr if a transformation between those frames already exist
static std::shared_ptr<Transformation> addTransformation(const FrameOfReference* from, const FrameOfReference* to, const vtkMatrix4x4* matrix);
/// @copydoc addTransformation(const FrameOfReference*,const FrameOfReference*,const vtkMatrix4x4*)
static std::shared_ptr<Transformation> addTransformation(const std::shared_ptr<FrameOfReference>& from, const std::shared_ptr<FrameOfReference>& to, const vtkMatrix4x4* matrix);
/**
* Remove transformations and frames that are unused (i.e. only present in the TransformationManager)
*/
static void cleanupFramesAndTransformations();
/** Remove an existing transformation between the two frames.
*
* This method checks that the given shared_ptr<Transformation> has no other owner than the caller
* (i.e., only the caller of this method has ownership of the given shared_ptr). If this is true,
* then the shared_ptr will be set to nullptr, which will result in the deletion of the Transformation.
*
* If there is another owner, the shared_ptr is not modified, and the Transformation will not be removed.
*
* If the given shared_ptr is equal to nullptr (for instance when calling removeTransformation(getTransformationOwnership(t))
* with t being a composite transformation), this method returns false.
*
* @warning if successful, the shared_ptr must not be used anymore (as it is set to nullptr).
*
* @return true if the Transformation was removed from the transformation system, false otherwise.
*/
static bool removeTransformation(std::shared_ptr<Transformation>& tr);
/** Remove an existing transformation between the two frames.
* @see removeTransformation(std::shared_ptr<Transformation>&)
*
* @warning if successful, the shared_ptr must not be used anymore (as it is set to nullptr) after that.
*/
static bool removeTransformation(const FrameOfReference* from, const FrameOfReference* to);
/// @name updateTransformation
/// Updating a Transformation work only for Transformations created by addTransformation()
/// It will fail if the transformation was computed from others (e.g. inverse of another Transformation, or a composition)
/// If there is already path, but some Transformation are default identity Transformation, you should be using addTransformation.
/// If there is already a valid path using non-default Transformations you must break the path before using addTransformation.
/// @return nullptr if the transformation was not updated
///@{
/// Modify the Transformation between the two frames by setting its vtkTransform
/// @warning only the vtkMatrix of the given vtkTransform is duplicated (later modification of vtkTr will not update the transformations)
static bool updateTransformation(const FrameOfReference* from, const FrameOfReference* to, vtkSmartPointer<vtkTransform> vtkTr);
/// Modify the Transformation between the two frames by setting its vtkMatrix
static bool updateTransformation(const FrameOfReference* from, const FrameOfReference* to, vtkMatrix4x4* matrix);
/// Modify the Transformation by setting its vtkTransform
/// @warning only the vtkMatrix of the given vtkTransform is duplicated (later modification of vtkTr will not update the transformations)
static bool updateTransformation(Transformation* tr, vtkSmartPointer<vtkTransform> vtkTr);
/// Modify the Transformation by setting its matrix
static bool updateTransformation(Transformation* tr, vtkMatrix4x4* matrix);
///@}
/**
* Was this Transformation computed from others or not?
*/
static bool hasSources(const Transformation*);
/**
* Get the list of sources used to compute the provided Transformation
*/
static QVector<Transformation*> getSources(const Transformation*);
/// @return true if the given Transformation is an inverse, i.e., has one and only one source
static bool isInverseTransformation(const Transformation*);
/// @return the inverse transformation (creates it if it does not exists yet, that is if tr is a composite) or nullptr if tr is nullptr
static Transformation* getInverseTransformation(const Transformation* tr);
/**
* Is this transformation composed of two or more transformations ?
*/
static bool isCompositeTransformation(const Transformation* tr);
/// @}
/**
* Save Frame and Transformation data to a QVariant
*
* This creates a QVariantMap with the keys "frames" and "transformations"
* for saving the state of the TransformationManager
* @see InterfacePersistence
*/
static QVariant toVariant();
/**
* Load Frame and Transformation data from a QVariant
*
* This reads "frames" and "transformations" from the provided QVariantMap
* to add Frame and Transformation objects in the TransformationManager
* @see InterfacePersistence
*/
static void fromVariant(const QVariant&);
private:
/// @name Transformations and Frames of reference management
///@{
/// worldFrame : frame in which all actors and props are defined (maybe through their userTransform)
static std::shared_ptr<FrameOfReference> worldFrame;
/// Storing the frames
static std::vector<std::shared_ptr<FrameOfReference>> frames;
/// Storing the transformations
static std::vector<std::shared_ptr<Transformation>> transformations;
/// Cache to get FrameOfReference pointer from Uuid
static QHash<QUuid, FrameOfReference*> frameMap;
/// Cache to get Transformation pointer from Uuid
static QHash<QUuid, Transformation*> transformationMap;
/// Storing the sources of a transformation:
/// - [] means it is a direct transformations (created using addTransformation())
/// - [Transf1] means it was computed from on other transformation (it's an inverse)
/// - [Transf1, ..., Transfn] means that it was computed by combining n transformations.
static QHash<const Transformation*, QVector<std::shared_ptr<Transformation>>> transformationSources;
/// Finding a transform from Frame1 to Frame2 : transformationsFromTo[Frame1][Frame2]
static QHash<const FrameOfReference*, QHash<const FrameOfReference*, Transformation*>> transformationFromTo;
/// List of default Identity Transformations : these can be removed if another transform is added later and completes the desired paths
///\example
/// Two components C1 and C2 are loaded, create Frame1 and Frame2.
/// C1 is added to a 3D Viewer, which has the FrameOfReference WorldFrame
/// A default identity transformation is added to link Frame1 to WorldFrame,
/// because we need to set Actor1 in the viewer's frame, and there is no known Transformation
/// from Frame1 to WorldFrame.
/// We also add C2 to the 3D Viewer.
/// The problem is identical, a default identity transformation is added from Frame2 to WorldFrame.
/// Now C1 and C2 are registered, and a new Transformation Tr2_1(Frame2, Frame1) must be added.
/// But there is already a path Frame1 --> WorldFrame <-- Frame2 linking both Frames.
/// Because the current path contains default identity transformations, these can be deleted, so
/// that the new Frame2 --> Frame1 transformation can be added.
/// When the Viewer updates its content, it will try to find Frame1--> WorldFrame, which was removed, and
/// will create a default identity transformation again.
/// When trying to show C2, it will find the Transformation Frame2 --> Frame1 --> WorldFrame
static std::unordered_set<const Transformation*> defaultIdentityToWorldTransformations;
/**
* Private method to add a Transformation in the members.
*
* @warning: TransformationManager takes ownership of the provided pointer, therefore YOU MUST NOT DELETE IT
*
* @param tr the transformation to add
* @param sources List of Transformations that were used to compute this one. This list is empty if this method is called inside addTransformation().
*/
static std::shared_ptr<Transformation> registerTransformation(Transformation* tr, const QVector<std::shared_ptr<Transformation>>& sources = {});
/**
* If there is a path of transformations between two frames of reference, this will return it.
*
* This will search for a path in the graph of frames/transformations.
*
* \note if from or to are nullptr, returns an empty QVector
*
* @return The list of Transformations that once chained link Frame 'from' to Frame 'to' or an empty QVector if there is no path or if `from` equals `to`.
*/
static QVector<std::shared_ptr<Transformation>> getPath(const FrameOfReference* from, const FrameOfReference* to);
/**
* Create and register a Transformation which is the composition of the provided transformations
*/
static std::shared_ptr<Transformation> addCompositeTransformation(QVector<std::shared_ptr<Transformation>> transforms);
/**
* Add a FrameOfReference with all data
* @warning should be used only if you need a specific UUID (e.g. when reading a FrameOfReference from a file)
*/
static std::shared_ptr<FrameOfReference> addFrameOfReference(QUuid uuid, QString name, QString description, int dimensions = 3, const AnatomicalOrientation& ao = {}, std::vector<Unit> units = {"", "", "", "", ""});
/**
* Add a FrameOfReference from a QVariant (as stored using PersistenceManager)
*/
static std::shared_ptr<FrameOfReference> addFrameOfReference(const QVariant&);
///@}
/// Remove the default identity to world Transformations that are present in the path between from and to
/// @return true if and only if at least one default transformation was removed between from and to
static bool removeDefaultPaths(const FrameOfReference* from, const FrameOfReference* to);
/// Helper method that removes a transformation from all internal members except the owner (transformations)
/// @warning this is a utility methods, should only be called from cleanUp and removeTransformation methods.
static void removeTransformationFromInternalStructures(const Transformation* trToDelete);
/// Remove all composite from internal structures
static void cleanupCompositeTransformations();
/**
* Get the shared_ptr that owns the given Transformation (works for all transformation that are not composite)
* @warning This should never be a public method (see difference with getTransformationOwnership)
*/
static std::shared_ptr<Transformation> getTransformationSharedPtr(const Transformation*);
};
}
#endif // TRANSFORMATIONMANAGER_H
|