File: TransformationManager.h

package info (click to toggle)
camitk 6.0.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 389,508 kB
  • sloc: cpp: 103,476; sh: 2,448; python: 1,618; xml: 984; makefile: 128; perl: 84; sed: 20
file content (602 lines) | stat: -rw-r--r-- 33,366 bytes parent folder | download
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